Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@insforge/cli",
"version": "0.1.36",
"version": "0.1.37",
"description": "InsForge CLI - Command line tool for InsForge platform",
"type": "module",
"bin": {
Expand Down
111 changes: 111 additions & 0 deletions src/commands/deployments/env-vars.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import type { Command } from 'commander';
import { ossFetch } from '../../lib/api/oss.js';
import { requireAuth } from '../../lib/credentials.js';
import { handleError, getRootOpts, ProjectNotLinkedError, CLIError } from '../../lib/errors.js';

Check warning on line 4 in src/commands/deployments/env-vars.ts

View workflow job for this annotation

GitHub Actions / Lint & Build

'CLIError' is defined but never used. Allowed unused vars must match /^_/u
import { getProjectConfig } from '../../lib/config.js';
import { outputJson, outputTable, outputSuccess } from '../../lib/output.js';
import { reportCliUsage } from '../../lib/skills.js';

interface EnvVar {
id: string;
key: string;
type: string;
updatedAt: number;
}

export function registerDeploymentsEnvVarsCommand(deploymentsCmd: Command): void {
const envCmd = deploymentsCmd.command('env').description('Manage deployment environment variables');

// list
envCmd
.command('list')
.description('List all deployment environment variables')
.action(async (_opts, cmd) => {
const { json } = getRootOpts(cmd);
try {
await requireAuth();
if (!getProjectConfig()) throw new ProjectNotLinkedError();

const res = await ossFetch('/api/deployments/env-vars');
const data = (await res.json()) as { envVars: EnvVar[] };
const envVars = data.envVars ?? [];

if (json) {
outputJson(data);
} else {
if (!envVars.length) {
console.log('No environment variables found.');
return;
}
outputTable(
['ID', 'Key', 'Type', 'Updated At'],
envVars.map((v) => [
v.id,
v.key,
v.type,
new Date(v.updatedAt).toLocaleString(),
]),
);
}
await reportCliUsage('cli.deployments.env.list', true);
} catch (err) {
await reportCliUsage('cli.deployments.env.list', false);
handleError(err, json);
}
});

// create / update
envCmd
.command('set <key> <value>')
.description('Create or update a deployment environment variable')
.action(async (key: string, value: string, _opts, cmd) => {
const { json } = getRootOpts(cmd);
try {
await requireAuth();
if (!getProjectConfig()) throw new ProjectNotLinkedError();

const res = await ossFetch('/api/deployments/env-vars', {
method: 'POST',
body: JSON.stringify({ envVars: [{ key, value }] }),
});
const data = (await res.json()) as { success: boolean; message: string; count: number };

if (json) {
outputJson(data);
} else {
outputSuccess(data.message);
}
await reportCliUsage('cli.deployments.env.set', true);
} catch (err) {
await reportCliUsage('cli.deployments.env.set', false);
handleError(err, json);
}
});

// delete
envCmd
.command('delete <id>')
.description('Delete a deployment environment variable by ID')
.action(async (id: string, _opts, cmd) => {
const { json } = getRootOpts(cmd);
try {
await requireAuth();
if (!getProjectConfig()) throw new ProjectNotLinkedError();

const res = await ossFetch(`/api/deployments/env-vars/${encodeURIComponent(id)}`, {
method: 'DELETE',
});
const data = (await res.json()) as { success: boolean; message: string };

if (json) {
outputJson(data);
} else {
outputSuccess(data.message);
}
await reportCliUsage('cli.deployments.env.delete', true);
} catch (err) {
await reportCliUsage('cli.deployments.env.delete', false);
handleError(err, json);
}
});
}
15 changes: 13 additions & 2 deletions src/commands/diagnose/logs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,15 @@ import { getProjectConfig } from '../../lib/config.js';
import { outputJson, outputTable } from '../../lib/output.js';
import { reportCliUsage } from '../../lib/skills.js';

const LOG_SOURCES = ['insforge.logs', 'postgREST.logs', 'postgres.logs', 'function.logs'] as const;
const LOG_SOURCES = ['insforge.logs', 'postgREST.logs', 'postgres.logs', 'function.logs', 'function-deploy.logs'] as const;

const ERROR_PATTERN = /\b(error|fatal|panic)\b/i;

/** Maps source names to their API paths. Most use /api/logs/{source}, but some have custom paths. */
const SOURCE_PATH: Record<string, string> = {
'function-deploy.logs': '/api/logs/functions/build-logs',
};

interface LogEntry {
timestamp: string;
message: string;
Expand All @@ -32,8 +37,14 @@ function parseLogEntry(entry: unknown): { ts: string; msg: string } {
return { ts, msg };
}

function getLogPath(source: string, limit: number): string {
const custom = SOURCE_PATH[source];
if (custom) return `${custom}?limit=${limit}`;
return `/api/logs/${encodeURIComponent(source)}?limit=${limit}`;
}

async function fetchSourceLogs(source: string, limit: number): Promise<SourceSummary> {
const res = await ossFetch(`/api/logs/${encodeURIComponent(source)}?limit=${limit}`);
const res = await ossFetch(getLogPath(source, limit));
const data = await res.json();
const logs = Array.isArray(data) ? data : ((data as Record<string, unknown>).logs as unknown[]) ?? [];

Expand Down
17 changes: 14 additions & 3 deletions src/commands/logs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,24 @@ import { requireAuth } from '../lib/credentials.js';
import { handleError, getRootOpts, CLIError } from '../lib/errors.js';
import { outputJson } from '../lib/output.js';

const VALID_SOURCES = ['insforge.logs', 'postgREST.logs', 'postgres.logs', 'function.logs'] as const;
const VALID_SOURCES = ['insforge.logs', 'postgREST.logs', 'postgres.logs', 'function.logs', 'function-deploy.logs'] as const;
const SOURCE_LOOKUP = new Map(VALID_SOURCES.map((s) => [s.toLowerCase(), s]));

/** Maps source names to their API paths. Most use /api/logs/{source}, but some have custom paths. */
const SOURCE_PATH: Record<string, string> = {
'function-deploy.logs': '/api/logs/functions/build-logs',
};

function getLogPath(source: string, limit: number): string {
const custom = SOURCE_PATH[source];
if (custom) return `${custom}?limit=${limit}`;
return `/api/logs/${encodeURIComponent(source)}?limit=${limit}`;
}

export function registerLogsCommand(program: Command): void {
program
.command('logs <source>')
.description('Fetch backend container logs (insforge.logs | postgREST.logs | postgres.logs | function.logs)')
.description('Fetch backend container logs (insforge.logs | postgREST.logs | postgres.logs | function.logs | function-deploy.logs)')
.option('--limit <n>', 'Number of log entries to return', '20')
.action(async (source: string, opts, cmd) => {
const { json } = getRootOpts(cmd);
Expand All @@ -23,7 +34,7 @@ export function registerLogsCommand(program: Command): void {
}

const limit = parseInt(opts.limit, 10) || 20;
const res = await ossFetch(`/api/logs/${encodeURIComponent(resolved)}?limit=${limit}`);
const res = await ossFetch(getLogPath(resolved, limit));
const data = await res.json();

if (json) {
Expand Down
2 changes: 2 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import { registerDeploymentsDeployCommand } from './commands/deployments/deploy.
import { registerDeploymentsListCommand } from './commands/deployments/list.js';
import { registerDeploymentsStatusCommand } from './commands/deployments/status.js';
import { registerDeploymentsCancelCommand } from './commands/deployments/cancel.js';
import { registerDeploymentsEnvVarsCommand } from './commands/deployments/env-vars.js';

import { registerDocsCommand } from './commands/docs.js';
import { registerSecretsListCommand } from './commands/secrets/list.js';
Expand Down Expand Up @@ -145,6 +146,7 @@ registerDeploymentsDeployCommand(deploymentsCmd);
registerDeploymentsListCommand(deploymentsCmd);
registerDeploymentsStatusCommand(deploymentsCmd);
registerDeploymentsCancelCommand(deploymentsCmd);
registerDeploymentsEnvVarsCommand(deploymentsCmd);
// registerDeploymentsMetadataCommand(deploymentsCmd);
// slug command doesn't work yet.
// registerDeploymentsSlugCommand(deploymentsCmd);
Expand Down
4 changes: 2 additions & 2 deletions src/lib/skills.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,13 @@ function updateGitignore(): void {
export async function installSkills(json: boolean): Promise<void> {
try {
if (!json) clack.log.info('Installing InsForge agent skills...');
await execAsync('npx skills add insforge/agent-skills -y -s insforge -s insforge-cli -a antigravity -a augment -a claude-code -a cline -a codex -a cursor -a gemini-cli -a github-copilot -a kilo -a qoder -a qwen-code -a roo -a trae -a windsurf', {
await execAsync('npx skills add insforge/agent-skills -y -a antigravity -a augment -a claude-code -a cline -a codex -a cursor -a gemini-cli -a github-copilot -a kilo -a qoder -a qwen-code -a roo -a trae -a windsurf', {
cwd: process.cwd(),
timeout: 60_000,
});
if (!json) clack.log.success('InsForge agent skills installed.');
} catch {
if (!json) clack.log.warn('Failed to install agent skills. You can run manually: npx skills add insforge/agent-skills -s insforge -s insforge-cli');
if (!json) clack.log.warn('Failed to install agent skills. You can run manually: npx skills add insforge/agent-skills');
}

// Install find-skills from vercel-labs for skill discovery
Expand Down
Loading