@agentuity/cli 2.0.6 → 2.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +4 -2
- package/dist/cli.js.map +1 -1
- package/dist/cmd/build/vite/route-discovery.d.ts.map +1 -1
- package/dist/cmd/build/vite/route-discovery.js +6 -0
- package/dist/cmd/build/vite/route-discovery.js.map +1 -1
- package/dist/cmd/cloud/sandbox/fs/rm.d.ts.map +1 -1
- package/dist/cmd/cloud/sandbox/fs/rm.js +9 -3
- package/dist/cmd/cloud/sandbox/fs/rm.js.map +1 -1
- package/dist/cmd/cloud/sandbox/fs/rmdir.d.ts.map +1 -1
- package/dist/cmd/cloud/sandbox/fs/rmdir.js +9 -3
- package/dist/cmd/cloud/sandbox/fs/rmdir.js.map +1 -1
- package/dist/cmd/cloud/task/close.d.ts +3 -0
- package/dist/cmd/cloud/task/close.d.ts.map +1 -0
- package/dist/cmd/cloud/task/close.js +286 -0
- package/dist/cmd/cloud/task/close.js.map +1 -0
- package/dist/cmd/cloud/task/delete.d.ts +1 -5
- package/dist/cmd/cloud/task/delete.d.ts.map +1 -1
- package/dist/cmd/cloud/task/delete.js +15 -38
- package/dist/cmd/cloud/task/delete.js.map +1 -1
- package/dist/cmd/cloud/task/index.d.ts.map +1 -1
- package/dist/cmd/cloud/task/index.js +10 -0
- package/dist/cmd/cloud/task/index.js.map +1 -1
- package/dist/cmd/cloud/task/list.d.ts.map +1 -1
- package/dist/cmd/cloud/task/list.js +97 -3
- package/dist/cmd/cloud/task/list.js.map +1 -1
- package/dist/cmd/cloud/task/util.d.ts +10 -0
- package/dist/cmd/cloud/task/util.d.ts.map +1 -1
- package/dist/cmd/cloud/task/util.js +47 -3
- package/dist/cmd/cloud/task/util.js.map +1 -1
- package/dist/cmd/coder/archive.d.ts +2 -0
- package/dist/cmd/coder/archive.d.ts.map +1 -0
- package/dist/cmd/coder/archive.js +57 -0
- package/dist/cmd/coder/archive.js.map +1 -0
- package/dist/cmd/coder/create.d.ts +2 -0
- package/dist/cmd/coder/create.d.ts.map +1 -0
- package/dist/cmd/coder/create.js +245 -0
- package/dist/cmd/coder/create.js.map +1 -0
- package/dist/cmd/coder/delete.d.ts +2 -0
- package/dist/cmd/coder/delete.d.ts.map +1 -0
- package/dist/cmd/coder/delete.js +64 -0
- package/dist/cmd/coder/delete.js.map +1 -0
- package/dist/cmd/coder/events.d.ts +2 -0
- package/dist/cmd/coder/events.d.ts.map +1 -0
- package/dist/cmd/coder/events.js +99 -0
- package/dist/cmd/coder/events.js.map +1 -0
- package/dist/cmd/coder/extension-path.d.ts +8 -0
- package/dist/cmd/coder/extension-path.d.ts.map +1 -0
- package/dist/cmd/coder/extension-path.js +59 -0
- package/dist/cmd/coder/extension-path.js.map +1 -0
- package/dist/cmd/coder/get.d.ts +2 -0
- package/dist/cmd/coder/get.d.ts.map +1 -0
- package/dist/cmd/coder/{inspect.js → get.js} +37 -33
- package/dist/cmd/coder/get.js.map +1 -0
- package/dist/cmd/coder/index.d.ts.map +1 -1
- package/dist/cmd/coder/index.js +54 -4
- package/dist/cmd/coder/index.js.map +1 -1
- package/dist/cmd/coder/list.d.ts.map +1 -1
- package/dist/cmd/coder/list.js +25 -34
- package/dist/cmd/coder/list.js.map +1 -1
- package/dist/cmd/coder/loop.d.ts +2 -0
- package/dist/cmd/coder/loop.d.ts.map +1 -0
- package/dist/cmd/coder/loop.js +78 -0
- package/dist/cmd/coder/loop.js.map +1 -0
- package/dist/cmd/coder/participants.d.ts +2 -0
- package/dist/cmd/coder/participants.d.ts.map +1 -0
- package/dist/cmd/coder/participants.js +93 -0
- package/dist/cmd/coder/participants.js.map +1 -0
- package/dist/cmd/coder/replay.d.ts +2 -0
- package/dist/cmd/coder/replay.d.ts.map +1 -0
- package/dist/cmd/coder/replay.js +53 -0
- package/dist/cmd/coder/replay.js.map +1 -0
- package/dist/cmd/coder/resolve-repo.d.ts +27 -0
- package/dist/cmd/coder/resolve-repo.d.ts.map +1 -0
- package/dist/cmd/coder/resolve-repo.js +97 -0
- package/dist/cmd/coder/resolve-repo.js.map +1 -0
- package/dist/cmd/coder/skill/buckets.d.ts +2 -0
- package/dist/cmd/coder/skill/buckets.d.ts.map +1 -0
- package/dist/cmd/coder/skill/buckets.js +174 -0
- package/dist/cmd/coder/skill/buckets.js.map +1 -0
- package/dist/cmd/coder/skill/delete.d.ts +2 -0
- package/dist/cmd/coder/skill/delete.d.ts.map +1 -0
- package/dist/cmd/coder/skill/delete.js +64 -0
- package/dist/cmd/coder/skill/delete.js.map +1 -0
- package/dist/cmd/coder/skill/index.d.ts +2 -0
- package/dist/cmd/coder/skill/index.d.ts.map +1 -0
- package/dist/cmd/coder/skill/index.js +33 -0
- package/dist/cmd/coder/skill/index.js.map +1 -0
- package/dist/cmd/coder/skill/list.d.ts +2 -0
- package/dist/cmd/coder/skill/list.d.ts.map +1 -0
- package/dist/cmd/coder/skill/list.js +93 -0
- package/dist/cmd/coder/skill/list.js.map +1 -0
- package/dist/cmd/coder/skill/save.d.ts +2 -0
- package/dist/cmd/coder/skill/save.d.ts.map +1 -0
- package/dist/cmd/coder/skill/save.js +77 -0
- package/dist/cmd/coder/skill/save.js.map +1 -0
- package/dist/cmd/coder/start.d.ts.map +1 -1
- package/dist/cmd/coder/start.js +88 -117
- package/dist/cmd/coder/start.js.map +1 -1
- package/dist/cmd/coder/tui-init.d.ts +4 -1
- package/dist/cmd/coder/tui-init.d.ts.map +1 -1
- package/dist/cmd/coder/tui-init.js +9 -3
- package/dist/cmd/coder/tui-init.js.map +1 -1
- package/dist/cmd/coder/update.d.ts +2 -0
- package/dist/cmd/coder/update.d.ts.map +1 -0
- package/dist/cmd/coder/update.js +126 -0
- package/dist/cmd/coder/update.js.map +1 -0
- package/dist/cmd/coder/users.d.ts +2 -0
- package/dist/cmd/coder/users.d.ts.map +1 -0
- package/dist/cmd/coder/users.js +97 -0
- package/dist/cmd/coder/users.js.map +1 -0
- package/dist/cmd/coder/workspace/create.d.ts +2 -0
- package/dist/cmd/coder/workspace/create.d.ts.map +1 -0
- package/dist/cmd/coder/workspace/create.js +97 -0
- package/dist/cmd/coder/workspace/create.js.map +1 -0
- package/dist/cmd/coder/workspace/delete.d.ts +2 -0
- package/dist/cmd/coder/workspace/delete.d.ts.map +1 -0
- package/dist/cmd/coder/workspace/delete.js +64 -0
- package/dist/cmd/coder/workspace/delete.js.map +1 -0
- package/dist/cmd/coder/workspace/get.d.ts +2 -0
- package/dist/cmd/coder/workspace/get.d.ts.map +1 -0
- package/dist/cmd/coder/workspace/get.js +109 -0
- package/dist/cmd/coder/workspace/get.js.map +1 -0
- package/dist/cmd/coder/workspace/index.d.ts +2 -0
- package/dist/cmd/coder/workspace/index.d.ts.map +1 -0
- package/dist/cmd/coder/workspace/index.js +38 -0
- package/dist/cmd/coder/workspace/index.js.map +1 -0
- package/dist/cmd/coder/workspace/list.d.ts +2 -0
- package/dist/cmd/coder/workspace/list.d.ts.map +1 -0
- package/dist/cmd/coder/workspace/list.js +93 -0
- package/dist/cmd/coder/workspace/list.js.map +1 -0
- package/dist/cmd/dev/sync.js +5 -5
- package/dist/cmd/dev/sync.js.map +1 -1
- package/dist/coder-hub-url.d.ts +3 -0
- package/dist/coder-hub-url.d.ts.map +1 -0
- package/dist/coder-hub-url.js +32 -0
- package/dist/coder-hub-url.js.map +1 -0
- package/dist/config.d.ts +1 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +14 -3
- package/dist/config.js.map +1 -1
- package/dist/internal-logger.d.ts +4 -0
- package/dist/internal-logger.d.ts.map +1 -1
- package/dist/internal-logger.js +64 -2
- package/dist/internal-logger.js.map +1 -1
- package/dist/keychain.d.ts +3 -0
- package/dist/keychain.d.ts.map +1 -1
- package/dist/keychain.js +47 -28
- package/dist/keychain.js.map +1 -1
- package/dist/types.d.ts +1 -1
- package/package.json +7 -6
- package/src/cli.ts +4 -2
- package/src/cmd/ai/prompt/agent.md +6 -6
- package/src/cmd/build/vite/route-discovery.ts +8 -0
- package/src/cmd/cloud/sandbox/fs/rm.ts +8 -3
- package/src/cmd/cloud/sandbox/fs/rmdir.ts +8 -3
- package/src/cmd/cloud/task/close.ts +319 -0
- package/src/cmd/cloud/task/delete.ts +15 -43
- package/src/cmd/cloud/task/index.ts +10 -0
- package/src/cmd/cloud/task/list.ts +111 -4
- package/src/cmd/cloud/task/util.ts +59 -5
- package/src/cmd/coder/archive.ts +59 -0
- package/src/cmd/coder/create.ts +268 -0
- package/src/cmd/coder/delete.ts +67 -0
- package/src/cmd/coder/events.ts +106 -0
- package/src/cmd/coder/extension-path.ts +71 -0
- package/src/cmd/coder/{inspect.ts → get.ts} +44 -45
- package/src/cmd/coder/index.ts +54 -4
- package/src/cmd/coder/list.ts +28 -65
- package/src/cmd/coder/loop.ts +85 -0
- package/src/cmd/coder/participants.ts +100 -0
- package/src/cmd/coder/replay.ts +58 -0
- package/src/cmd/coder/resolve-repo.ts +119 -0
- package/src/cmd/coder/skill/buckets.ts +191 -0
- package/src/cmd/coder/skill/delete.ts +67 -0
- package/src/cmd/coder/skill/index.ts +35 -0
- package/src/cmd/coder/skill/list.ts +97 -0
- package/src/cmd/coder/skill/save.ts +84 -0
- package/src/cmd/coder/start.ts +104 -141
- package/src/cmd/coder/tui-init.ts +13 -4
- package/src/cmd/coder/update.ts +128 -0
- package/src/cmd/coder/users.ts +101 -0
- package/src/cmd/coder/workspace/create.ts +104 -0
- package/src/cmd/coder/workspace/delete.ts +70 -0
- package/src/cmd/coder/workspace/get.ts +112 -0
- package/src/cmd/coder/workspace/index.ts +38 -0
- package/src/cmd/coder/workspace/list.ts +101 -0
- package/src/cmd/dev/sync.ts +5 -5
- package/src/coder-hub-url.ts +32 -0
- package/src/config.ts +17 -3
- package/src/internal-logger.ts +83 -2
- package/src/keychain.ts +68 -39
- package/dist/cmd/coder/hub-url.d.ts +0 -36
- package/dist/cmd/coder/hub-url.d.ts.map +0 -1
- package/dist/cmd/coder/hub-url.js +0 -106
- package/dist/cmd/coder/hub-url.js.map +0 -1
- package/dist/cmd/coder/inspect.d.ts +0 -2
- package/dist/cmd/coder/inspect.d.ts.map +0 -1
- package/dist/cmd/coder/inspect.js.map +0 -1
- package/src/cmd/coder/hub-url.ts +0 -111
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { CoderClient, type CoderCreateWorkspaceRequest } from '@agentuity/core/coder';
|
|
3
|
+
import { ValidationOutputError } from '@agentuity/core';
|
|
4
|
+
import { createSubcommand } from '../../../types';
|
|
5
|
+
import * as tui from '../../../tui';
|
|
6
|
+
import { getCommand } from '../../../command-prefix';
|
|
7
|
+
import { ErrorCode } from '../../../errors';
|
|
8
|
+
import { resolveGitHubRepo } from '../resolve-repo';
|
|
9
|
+
|
|
10
|
+
export const createWorkspaceSubcommand = createSubcommand({
|
|
11
|
+
name: 'create',
|
|
12
|
+
aliases: ['new', 'add'],
|
|
13
|
+
description: 'Create a new Coder workspace',
|
|
14
|
+
tags: ['mutating', 'requires-auth'],
|
|
15
|
+
requires: { auth: true, org: true },
|
|
16
|
+
examples: [
|
|
17
|
+
{
|
|
18
|
+
command: getCommand('coder workspace create "My Workspace"'),
|
|
19
|
+
description: 'Create a workspace with a name',
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
command: getCommand(
|
|
23
|
+
'coder workspace create "My Workspace" --description "For frontend work" --scope org'
|
|
24
|
+
),
|
|
25
|
+
description: 'Create an org-scoped workspace with description',
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
command: getCommand(
|
|
29
|
+
'coder workspace create "My Workspace" --repo https://github.com/org/repo --repo-branch main'
|
|
30
|
+
),
|
|
31
|
+
description: 'Create a workspace with a repository',
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
command: getCommand('coder workspace create "My Workspace" --json'),
|
|
35
|
+
description: 'Create a workspace and return JSON output',
|
|
36
|
+
},
|
|
37
|
+
],
|
|
38
|
+
schema: {
|
|
39
|
+
args: z.object({
|
|
40
|
+
name: z.string().describe('Workspace name'),
|
|
41
|
+
}),
|
|
42
|
+
options: z.object({
|
|
43
|
+
url: z.string().optional().describe('Coder API URL override'),
|
|
44
|
+
description: z.string().optional().describe('Workspace description'),
|
|
45
|
+
scope: z.string().optional().describe('Workspace scope: user or org'),
|
|
46
|
+
repo: z.string().optional().describe('Repository URL to add'),
|
|
47
|
+
repoBranch: z.string().optional().describe('Branch for the repository'),
|
|
48
|
+
}),
|
|
49
|
+
},
|
|
50
|
+
async handler(ctx) {
|
|
51
|
+
const { args, opts, options } = ctx;
|
|
52
|
+
const client = new CoderClient({
|
|
53
|
+
apiKey: ctx.auth.apiKey,
|
|
54
|
+
url: opts?.url,
|
|
55
|
+
orgId: ctx.orgId,
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
const body: CoderCreateWorkspaceRequest = {
|
|
59
|
+
name: args.name,
|
|
60
|
+
...(opts?.description && { description: opts.description }),
|
|
61
|
+
...(opts?.scope && { scope: opts.scope as 'user' | 'org' }),
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
if (opts?.repo) {
|
|
65
|
+
if (!options.json) tui.output('Resolving repository...');
|
|
66
|
+
try {
|
|
67
|
+
const resolved = await resolveGitHubRepo(client, opts.repo, opts?.repoBranch);
|
|
68
|
+
body.repos = [resolved];
|
|
69
|
+
} catch (err) {
|
|
70
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
71
|
+
tui.fatal(`Failed to resolve repository: ${msg}`, ErrorCode.VALIDATION_FAILED);
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
try {
|
|
77
|
+
const created = await client.createWorkspace(body);
|
|
78
|
+
|
|
79
|
+
if (options.json) {
|
|
80
|
+
return created;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
tui.success(`Workspace ${created.id} created.`);
|
|
84
|
+
tui.newline();
|
|
85
|
+
tui.output(` Name: ${tui.bold(created.name)}`);
|
|
86
|
+
if (created.description) {
|
|
87
|
+
tui.output(` Description: ${created.description}`);
|
|
88
|
+
}
|
|
89
|
+
tui.output(` Scope: ${created.scope}`);
|
|
90
|
+
tui.output(` Repos: ${created.repoCount}`);
|
|
91
|
+
tui.output(` Skills: ${created.selectionCount}`);
|
|
92
|
+
|
|
93
|
+
return created;
|
|
94
|
+
} catch (err) {
|
|
95
|
+
if (err instanceof ValidationOutputError) {
|
|
96
|
+
ctx.logger.trace('Validation response URL: %s', err.url ?? 'unknown');
|
|
97
|
+
ctx.logger.trace('Validation issues: %s', JSON.stringify(err.issues, null, 2));
|
|
98
|
+
tui.fatal(`Failed to create workspace: ${err.message}`, ErrorCode.VALIDATION_FAILED);
|
|
99
|
+
}
|
|
100
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
101
|
+
tui.fatal(`Failed to create workspace: ${msg}`, ErrorCode.NETWORK_ERROR);
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
});
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { CoderClient } from '@agentuity/core/coder';
|
|
3
|
+
import { ValidationOutputError } from '@agentuity/core';
|
|
4
|
+
import { createSubcommand } from '../../../types';
|
|
5
|
+
import * as tui from '../../../tui';
|
|
6
|
+
import { getCommand } from '../../../command-prefix';
|
|
7
|
+
import { ErrorCode } from '../../../errors';
|
|
8
|
+
|
|
9
|
+
export const deleteWorkspaceSubcommand = createSubcommand({
|
|
10
|
+
name: 'delete',
|
|
11
|
+
aliases: ['rm', 'del', 'remove'],
|
|
12
|
+
description: 'Delete a Coder workspace',
|
|
13
|
+
tags: ['destructive', 'deletes-resource', 'requires-auth'],
|
|
14
|
+
requires: { auth: true, org: true },
|
|
15
|
+
examples: [
|
|
16
|
+
{
|
|
17
|
+
command: getCommand('coder workspace delete ws_abc123'),
|
|
18
|
+
description: 'Delete a workspace',
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
command: getCommand('coder workspace delete ws_abc123 --json'),
|
|
22
|
+
description: 'Delete a workspace and return JSON output',
|
|
23
|
+
},
|
|
24
|
+
],
|
|
25
|
+
schema: {
|
|
26
|
+
args: z.object({
|
|
27
|
+
workspaceId: z.string().describe('Workspace ID to delete'),
|
|
28
|
+
}),
|
|
29
|
+
options: z.object({
|
|
30
|
+
url: z.string().optional().describe('Coder API URL override'),
|
|
31
|
+
}),
|
|
32
|
+
},
|
|
33
|
+
async handler(ctx) {
|
|
34
|
+
const { args, opts, options } = ctx;
|
|
35
|
+
const client = new CoderClient({
|
|
36
|
+
apiKey: ctx.auth.apiKey,
|
|
37
|
+
url: opts?.url,
|
|
38
|
+
orgId: ctx.orgId,
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
if (!options.json) {
|
|
42
|
+
const confirmed = await tui.confirm(`Delete workspace ${args.workspaceId}?`, false);
|
|
43
|
+
if (!confirmed) {
|
|
44
|
+
tui.info('Cancelled.');
|
|
45
|
+
return { deleted: false, workspaceId: args.workspaceId };
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
try {
|
|
50
|
+
await client.deleteWorkspace(args.workspaceId);
|
|
51
|
+
} catch (err) {
|
|
52
|
+
if (err instanceof ValidationOutputError) {
|
|
53
|
+
ctx.logger.trace('Validation response URL: %s', err.url ?? 'unknown');
|
|
54
|
+
ctx.logger.trace('Validation issues: %s', JSON.stringify(err.issues, null, 2));
|
|
55
|
+
}
|
|
56
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
57
|
+
tui.fatal(
|
|
58
|
+
`Failed to delete workspace ${args.workspaceId}: ${msg}`,
|
|
59
|
+
ErrorCode.NETWORK_ERROR
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (options.json) {
|
|
64
|
+
return { deleted: true, workspaceId: args.workspaceId };
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
tui.success(`Workspace ${args.workspaceId} deleted.`);
|
|
68
|
+
return { deleted: true, workspaceId: args.workspaceId };
|
|
69
|
+
},
|
|
70
|
+
});
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { CoderClient } from '@agentuity/core/coder';
|
|
3
|
+
import { ValidationOutputError } from '@agentuity/core';
|
|
4
|
+
import { createSubcommand } from '../../../types';
|
|
5
|
+
import * as tui from '../../../tui';
|
|
6
|
+
import { getCommand } from '../../../command-prefix';
|
|
7
|
+
import { ErrorCode } from '../../../errors';
|
|
8
|
+
|
|
9
|
+
function formatRelativeTime(isoDate: string): string {
|
|
10
|
+
const parsed = new Date(isoDate).getTime();
|
|
11
|
+
if (Number.isNaN(parsed)) return 'unknown';
|
|
12
|
+
const diffMs = Math.max(0, Date.now() - parsed);
|
|
13
|
+
const seconds = Math.floor(diffMs / 1000);
|
|
14
|
+
if (seconds < 60) return `${seconds}s ago`;
|
|
15
|
+
const minutes = Math.floor(seconds / 60);
|
|
16
|
+
if (minutes < 60) return `${minutes}m ago`;
|
|
17
|
+
const hours = Math.floor(minutes / 60);
|
|
18
|
+
if (hours < 24) return `${hours}h ago`;
|
|
19
|
+
const days = Math.floor(hours / 24);
|
|
20
|
+
return `${days}d ago`;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export const getWorkspaceSubcommand = createSubcommand({
|
|
24
|
+
name: 'get',
|
|
25
|
+
aliases: ['show', 'inspect'],
|
|
26
|
+
description: 'Show detailed information about a Coder workspace',
|
|
27
|
+
tags: ['read-only', 'fast', 'requires-auth'],
|
|
28
|
+
idempotent: true,
|
|
29
|
+
requires: { auth: true, org: true },
|
|
30
|
+
examples: [
|
|
31
|
+
{
|
|
32
|
+
command: getCommand('coder workspace get ws_abc123'),
|
|
33
|
+
description: 'Get a workspace by ID',
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
command: getCommand('coder workspace get ws_abc123 --json'),
|
|
37
|
+
description: 'Get workspace details as JSON',
|
|
38
|
+
},
|
|
39
|
+
],
|
|
40
|
+
schema: {
|
|
41
|
+
args: z.object({
|
|
42
|
+
workspaceId: z.string().describe('Workspace ID to inspect'),
|
|
43
|
+
}),
|
|
44
|
+
options: z.object({
|
|
45
|
+
url: z.string().optional().describe('Coder API URL override'),
|
|
46
|
+
}),
|
|
47
|
+
},
|
|
48
|
+
async handler(ctx) {
|
|
49
|
+
const { args, opts, options } = ctx;
|
|
50
|
+
const client = new CoderClient({
|
|
51
|
+
apiKey: ctx.auth.apiKey,
|
|
52
|
+
url: opts?.url,
|
|
53
|
+
orgId: ctx.orgId,
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
try {
|
|
57
|
+
const workspace = await client.getWorkspace(args.workspaceId);
|
|
58
|
+
|
|
59
|
+
if (options.json) {
|
|
60
|
+
return workspace;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
tui.header(`Workspace: ${workspace.name}`);
|
|
64
|
+
tui.newline();
|
|
65
|
+
tui.output(` ID: ${workspace.id}`);
|
|
66
|
+
tui.output(` Name: ${tui.bold(workspace.name)}`);
|
|
67
|
+
if (workspace.description) {
|
|
68
|
+
tui.output(` Description: ${workspace.description}`);
|
|
69
|
+
}
|
|
70
|
+
tui.output(` Scope: ${workspace.scope}`);
|
|
71
|
+
tui.output(` Owner: ${workspace.ownerUserId}`);
|
|
72
|
+
tui.output(` Created: ${formatRelativeTime(workspace.createdAt)}`);
|
|
73
|
+
tui.output(` Updated: ${formatRelativeTime(workspace.updatedAt)}`);
|
|
74
|
+
tui.newline();
|
|
75
|
+
|
|
76
|
+
if (workspace.repos.length > 0) {
|
|
77
|
+
tui.output(` Repositories (${workspace.repoCount}):`);
|
|
78
|
+
for (const repo of workspace.repos) {
|
|
79
|
+
const name = repo.fullName || repo.name || repo.url || repo.cloneUrl || 'unknown';
|
|
80
|
+
const branch = repo.branch || repo.defaultBranch || '';
|
|
81
|
+
tui.output(` - ${name}${branch ? ` (${branch})` : ''}`);
|
|
82
|
+
}
|
|
83
|
+
tui.newline();
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (workspace.savedSkillIds.length > 0) {
|
|
87
|
+
tui.output(` Saved Skill IDs (${workspace.savedSkillIds.length}):`);
|
|
88
|
+
for (const id of workspace.savedSkillIds) {
|
|
89
|
+
tui.output(` - ${id}`);
|
|
90
|
+
}
|
|
91
|
+
tui.newline();
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (workspace.skillBucketIds.length > 0) {
|
|
95
|
+
tui.output(` Skill Bucket IDs (${workspace.skillBucketIds.length}):`);
|
|
96
|
+
for (const id of workspace.skillBucketIds) {
|
|
97
|
+
tui.output(` - ${id}`);
|
|
98
|
+
}
|
|
99
|
+
tui.newline();
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return workspace;
|
|
103
|
+
} catch (err) {
|
|
104
|
+
if (err instanceof ValidationOutputError) {
|
|
105
|
+
ctx.logger.trace('Validation response URL: %s', err.url ?? 'unknown');
|
|
106
|
+
ctx.logger.trace('Validation issues: %s', JSON.stringify(err.issues, null, 2));
|
|
107
|
+
}
|
|
108
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
109
|
+
tui.fatal(`Failed to get workspace ${args.workspaceId}: ${msg}`, ErrorCode.NETWORK_ERROR);
|
|
110
|
+
}
|
|
111
|
+
},
|
|
112
|
+
});
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { createCommand } from '../../../types';
|
|
2
|
+
import { listSubcommand } from './list';
|
|
3
|
+
import { createWorkspaceSubcommand } from './create';
|
|
4
|
+
import { getWorkspaceSubcommand } from './get';
|
|
5
|
+
import { deleteWorkspaceSubcommand } from './delete';
|
|
6
|
+
import { getCommand } from '../../../command-prefix';
|
|
7
|
+
|
|
8
|
+
export const workspaceCommand = createCommand({
|
|
9
|
+
name: 'workspace',
|
|
10
|
+
aliases: ['workspaces', 'ws'],
|
|
11
|
+
description: 'Manage Coder workspaces',
|
|
12
|
+
tags: ['requires-auth'],
|
|
13
|
+
requires: { auth: true, org: true },
|
|
14
|
+
examples: [
|
|
15
|
+
{
|
|
16
|
+
command: getCommand('coder workspace list'),
|
|
17
|
+
description: 'List all workspaces',
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
command: getCommand('coder workspace create "My Workspace"'),
|
|
21
|
+
description: 'Create a new workspace',
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
command: getCommand('coder workspace get ws_abc123'),
|
|
25
|
+
description: 'Show workspace details',
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
command: getCommand('coder workspace delete ws_abc123'),
|
|
29
|
+
description: 'Delete a workspace',
|
|
30
|
+
},
|
|
31
|
+
],
|
|
32
|
+
subcommands: [
|
|
33
|
+
listSubcommand,
|
|
34
|
+
createWorkspaceSubcommand,
|
|
35
|
+
getWorkspaceSubcommand,
|
|
36
|
+
deleteWorkspaceSubcommand,
|
|
37
|
+
],
|
|
38
|
+
});
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import {
|
|
3
|
+
CoderClient,
|
|
4
|
+
type CoderWorkspaceDetail,
|
|
5
|
+
CoderWorkspaceDetailSchema,
|
|
6
|
+
} from '@agentuity/core/coder';
|
|
7
|
+
import { ValidationOutputError } from '@agentuity/core';
|
|
8
|
+
import { createSubcommand } from '../../../types';
|
|
9
|
+
import * as tui from '../../../tui';
|
|
10
|
+
import { getCommand } from '../../../command-prefix';
|
|
11
|
+
import { ErrorCode } from '../../../errors';
|
|
12
|
+
|
|
13
|
+
function formatRelativeTime(isoDate: string): string {
|
|
14
|
+
const parsed = new Date(isoDate).getTime();
|
|
15
|
+
if (Number.isNaN(parsed)) return 'unknown';
|
|
16
|
+
const diffMs = Math.max(0, Date.now() - parsed);
|
|
17
|
+
const seconds = Math.floor(diffMs / 1000);
|
|
18
|
+
if (seconds < 60) return `${seconds}s ago`;
|
|
19
|
+
const minutes = Math.floor(seconds / 60);
|
|
20
|
+
if (minutes < 60) return `${minutes}m ago`;
|
|
21
|
+
const hours = Math.floor(minutes / 60);
|
|
22
|
+
if (hours < 24) return `${hours}h ago`;
|
|
23
|
+
const days = Math.floor(hours / 24);
|
|
24
|
+
return `${days}d ago`;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export const listSubcommand = createSubcommand({
|
|
28
|
+
name: 'list',
|
|
29
|
+
aliases: ['ls'],
|
|
30
|
+
description: 'List Coder workspaces',
|
|
31
|
+
tags: ['read-only', 'fast', 'requires-auth'],
|
|
32
|
+
idempotent: true,
|
|
33
|
+
requires: { auth: true, org: true },
|
|
34
|
+
examples: [
|
|
35
|
+
{
|
|
36
|
+
command: getCommand('coder workspace list'),
|
|
37
|
+
description: 'List Coder workspaces',
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
command: getCommand('coder workspace list --json'),
|
|
41
|
+
description: 'Return workspaces as JSON',
|
|
42
|
+
},
|
|
43
|
+
],
|
|
44
|
+
schema: {
|
|
45
|
+
options: z.object({
|
|
46
|
+
url: z.string().optional().describe('Coder API URL override'),
|
|
47
|
+
}),
|
|
48
|
+
response: z.array(CoderWorkspaceDetailSchema),
|
|
49
|
+
},
|
|
50
|
+
async handler(ctx) {
|
|
51
|
+
const { options, opts } = ctx;
|
|
52
|
+
const client = new CoderClient({
|
|
53
|
+
apiKey: ctx.auth.apiKey,
|
|
54
|
+
url: opts?.url,
|
|
55
|
+
orgId: ctx.orgId,
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
let workspaces: CoderWorkspaceDetail[] = [];
|
|
59
|
+
try {
|
|
60
|
+
const response = await client.listWorkspaces();
|
|
61
|
+
workspaces = response.workspaces;
|
|
62
|
+
} catch (err) {
|
|
63
|
+
if (err instanceof ValidationOutputError) {
|
|
64
|
+
ctx.logger.trace('Validation response URL: %s', err.url ?? 'unknown');
|
|
65
|
+
ctx.logger.trace('Validation issues: %s', JSON.stringify(err.issues, null, 2));
|
|
66
|
+
}
|
|
67
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
68
|
+
tui.fatal(`Failed to list Coder workspaces: ${msg}`, ErrorCode.NETWORK_ERROR);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (options.json) {
|
|
72
|
+
return workspaces;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (workspaces.length === 0) {
|
|
76
|
+
tui.info('No Coder workspaces found.');
|
|
77
|
+
return [];
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
tui.table(
|
|
81
|
+
workspaces.map((w) => ({
|
|
82
|
+
ID: w.id,
|
|
83
|
+
Name: w.name,
|
|
84
|
+
Scope: w.scope,
|
|
85
|
+
Repos: String(w.repoCount),
|
|
86
|
+
Skills: String(w.selectionCount),
|
|
87
|
+
Created: formatRelativeTime(w.createdAt),
|
|
88
|
+
})),
|
|
89
|
+
[
|
|
90
|
+
{ name: 'ID', alignment: 'left' },
|
|
91
|
+
{ name: 'Name', alignment: 'left' },
|
|
92
|
+
{ name: 'Scope', alignment: 'center' },
|
|
93
|
+
{ name: 'Repos', alignment: 'right' },
|
|
94
|
+
{ name: 'Skills', alignment: 'right' },
|
|
95
|
+
{ name: 'Created', alignment: 'right' },
|
|
96
|
+
]
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
return workspaces;
|
|
100
|
+
},
|
|
101
|
+
});
|
package/src/cmd/dev/sync.ts
CHANGED
|
@@ -169,7 +169,7 @@ class DevmodeSyncService implements IDevmodeSyncService {
|
|
|
169
169
|
}
|
|
170
170
|
}
|
|
171
171
|
}
|
|
172
|
-
this.logger.debug('Previous metadata found with %d
|
|
172
|
+
this.logger.debug('Previous metadata found with %d evaluations', prevEvalCount);
|
|
173
173
|
} else {
|
|
174
174
|
this.logger.debug('No previous metadata, all evals will be treated as new');
|
|
175
175
|
}
|
|
@@ -182,7 +182,7 @@ class DevmodeSyncService implements IDevmodeSyncService {
|
|
|
182
182
|
if (agent.evals) {
|
|
183
183
|
currentEvalCount += agent.evals.length;
|
|
184
184
|
this.logger.debug(
|
|
185
|
-
'[CLI EVAL SYNC] Agent "%s" has %d
|
|
185
|
+
'[CLI EVAL SYNC] Agent "%s" has %d evaluations',
|
|
186
186
|
agent.name,
|
|
187
187
|
agent.evals.length
|
|
188
188
|
);
|
|
@@ -196,7 +196,7 @@ class DevmodeSyncService implements IDevmodeSyncService {
|
|
|
196
196
|
}
|
|
197
197
|
}
|
|
198
198
|
}
|
|
199
|
-
this.logger.debug('[CLI EVAL SYNC] Total current
|
|
199
|
+
this.logger.debug('[CLI EVAL SYNC] Total current evaluations: %d', currentEvalCount);
|
|
200
200
|
|
|
201
201
|
// Get agents and evals to sync using shared diff logic
|
|
202
202
|
const { create: agentsToCreate, delete: agentsToDelete } = getAgentsToSync(
|
|
@@ -241,7 +241,7 @@ class DevmodeSyncService implements IDevmodeSyncService {
|
|
|
241
241
|
}
|
|
242
242
|
if (evalsToCreate.length > 0 || evalsToDelete.length > 0) {
|
|
243
243
|
this.logger.debug(
|
|
244
|
-
'Successfully bulk synced %d
|
|
244
|
+
'Successfully bulk synced %d evaluations to create, %d evaluations to delete',
|
|
245
245
|
evalsToCreate.length,
|
|
246
246
|
evalsToDelete.length
|
|
247
247
|
);
|
|
@@ -369,7 +369,7 @@ class MockDevmodeSyncService implements IDevmodeSyncService {
|
|
|
369
369
|
|
|
370
370
|
if (evalsToCreate.length > 0 || evalsToDelete.length > 0) {
|
|
371
371
|
this.logger.debug(
|
|
372
|
-
'[MOCK] Would make request: POST /cli/devmode/eval with %d
|
|
372
|
+
'[MOCK] Would make request: POST /cli/devmode/eval with %d evaluations to create, %d evaluations to delete',
|
|
373
373
|
evalsToCreate.length,
|
|
374
374
|
evalsToDelete.length
|
|
375
375
|
);
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export function normalizeCoderHubHttpUrl(url: string): string {
|
|
2
|
+
let normalized = url.trim().replace(/\/+$/, '');
|
|
3
|
+
|
|
4
|
+
if (normalized.startsWith('ws://')) normalized = 'http://' + normalized.slice(5);
|
|
5
|
+
else if (normalized.startsWith('wss://')) normalized = 'https://' + normalized.slice(6);
|
|
6
|
+
|
|
7
|
+
normalized = normalized.replace(/\/api\/ws\b.*$/, '');
|
|
8
|
+
normalized = normalized.replace(/\/ws\b.*$/, '');
|
|
9
|
+
normalized = normalized.replace(/\/api\/hub\b.*$/, '');
|
|
10
|
+
|
|
11
|
+
return normalized.replace(/\/+$/, '');
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function toCoderHubWsUrl(hubHttpUrl: string): string {
|
|
15
|
+
let wsUrl = hubHttpUrl;
|
|
16
|
+
if (wsUrl.startsWith('http://')) wsUrl = 'ws://' + wsUrl.slice(7);
|
|
17
|
+
else if (wsUrl.startsWith('https://')) wsUrl = 'wss://' + wsUrl.slice(8);
|
|
18
|
+
|
|
19
|
+
try {
|
|
20
|
+
const parsed = new URL(wsUrl);
|
|
21
|
+
if (parsed.pathname !== '/api/ws') {
|
|
22
|
+
parsed.pathname = '/api/ws';
|
|
23
|
+
wsUrl = parsed.toString().replace(/\/$/, '');
|
|
24
|
+
}
|
|
25
|
+
} catch {
|
|
26
|
+
if (!wsUrl.endsWith('/api/ws')) {
|
|
27
|
+
wsUrl = wsUrl.replace(/\/?$/, '/api/ws');
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return wsUrl;
|
|
32
|
+
}
|
package/src/config.ts
CHANGED
|
@@ -28,6 +28,14 @@ import { ConfigSchema, ProjectSchema } from './types';
|
|
|
28
28
|
export const defaultProfileName = 'production';
|
|
29
29
|
|
|
30
30
|
export function getDefaultConfigDir(): string {
|
|
31
|
+
const configDirOverride = process.env.AGENTUITY_CONFIG_DIR?.trim();
|
|
32
|
+
if (configDirOverride) {
|
|
33
|
+
if (configDirOverride.startsWith('~/')) {
|
|
34
|
+
return resolve(join(homedir(), configDirOverride.slice(2)));
|
|
35
|
+
}
|
|
36
|
+
return resolve(configDirOverride);
|
|
37
|
+
}
|
|
38
|
+
|
|
31
39
|
return join(homedir(), '.config', 'agentuity');
|
|
32
40
|
}
|
|
33
41
|
|
|
@@ -144,16 +152,22 @@ let cachedConfig: Config | null | undefined;
|
|
|
144
152
|
// Track the resolved config path so saveConfig writes back to the same file
|
|
145
153
|
let cachedConfigPath: string | undefined;
|
|
146
154
|
|
|
155
|
+
export function resetConfigCache(): void {
|
|
156
|
+
cachedConfig = undefined;
|
|
157
|
+
cachedConfigPath = undefined;
|
|
158
|
+
}
|
|
159
|
+
|
|
147
160
|
export async function loadConfig(
|
|
148
161
|
customPath?: string,
|
|
149
162
|
skipCache = false,
|
|
150
163
|
profileFromFlag?: string
|
|
151
164
|
): Promise<Config | null> {
|
|
152
|
-
|
|
153
|
-
|
|
165
|
+
const configPath = customPath ? expandTilde(customPath) : await getProfile(profileFromFlag);
|
|
166
|
+
|
|
167
|
+
// Use cache if available and not skipped, but only if the path matches
|
|
168
|
+
if (!skipCache && cachedConfig !== undefined && cachedConfigPath === configPath) {
|
|
154
169
|
return cachedConfig;
|
|
155
170
|
}
|
|
156
|
-
const configPath = customPath ? expandTilde(customPath) : await getProfile(profileFromFlag);
|
|
157
171
|
|
|
158
172
|
try {
|
|
159
173
|
const file = Bun.file(configPath);
|
package/src/internal-logger.ts
CHANGED
|
@@ -43,6 +43,16 @@ const SENSITIVE_ENV_PATTERNS = [
|
|
|
43
43
|
/AUTH/i,
|
|
44
44
|
];
|
|
45
45
|
|
|
46
|
+
const MASKED_ARG_VALUE = '***MASKED***';
|
|
47
|
+
const SENSITIVE_ARG_PATTERNS = [
|
|
48
|
+
/^--?api[-_]?key$/i,
|
|
49
|
+
/^--?token$/i,
|
|
50
|
+
/^--?secret$/i,
|
|
51
|
+
/^--?password$/i,
|
|
52
|
+
/^--?client[-_]?secret$/i,
|
|
53
|
+
/^--?auth[-_]?value$/i,
|
|
54
|
+
];
|
|
55
|
+
|
|
46
56
|
interface SessionMetadata {
|
|
47
57
|
sessionId: string;
|
|
48
58
|
bucket: number;
|
|
@@ -102,6 +112,75 @@ function maskEnvironment(): Record<string, string> {
|
|
|
102
112
|
return masked;
|
|
103
113
|
}
|
|
104
114
|
|
|
115
|
+
function isSensitiveArgFlag(arg: string): boolean {
|
|
116
|
+
return SENSITIVE_ARG_PATTERNS.some((pattern) => pattern.test(arg));
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function sanitizeArgsForLogging(args: string[]): string[] {
|
|
120
|
+
const sanitized: string[] = [];
|
|
121
|
+
let maskNextValue = false;
|
|
122
|
+
|
|
123
|
+
for (let i = 0; i < args.length; i += 1) {
|
|
124
|
+
const arg = args[i]!;
|
|
125
|
+
|
|
126
|
+
if (maskNextValue) {
|
|
127
|
+
if (!arg.startsWith('-')) {
|
|
128
|
+
sanitized.push(MASKED_ARG_VALUE);
|
|
129
|
+
maskNextValue = false;
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
sanitized.push(MASKED_ARG_VALUE);
|
|
134
|
+
maskNextValue = false;
|
|
135
|
+
i -= 1;
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (!arg.startsWith('-')) {
|
|
140
|
+
sanitized.push(arg);
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const equalsIndex = arg.indexOf('=');
|
|
145
|
+
if (equalsIndex > 0) {
|
|
146
|
+
const flag = arg.slice(0, equalsIndex);
|
|
147
|
+
if (isSensitiveArgFlag(flag)) {
|
|
148
|
+
sanitized.push(`${flag}=${MASKED_ARG_VALUE}`);
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
sanitized.push(arg);
|
|
154
|
+
if (isSensitiveArgFlag(arg)) {
|
|
155
|
+
maskNextValue = true;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return sanitized;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export function sanitizeCliCommandForLogging(
|
|
163
|
+
command: string,
|
|
164
|
+
args: string[]
|
|
165
|
+
): { command: string; args: string[] } {
|
|
166
|
+
const commandTokens = command ? command.split(' ') : [];
|
|
167
|
+
|
|
168
|
+
if (
|
|
169
|
+
commandTokens.length >= 5 &&
|
|
170
|
+
commandTokens[0] === 'coder' &&
|
|
171
|
+
commandTokens[1] === 'config' &&
|
|
172
|
+
commandTokens[2] === 'set' &&
|
|
173
|
+
commandTokens[3] === 'apikey'
|
|
174
|
+
) {
|
|
175
|
+
commandTokens[4] = MASKED_ARG_VALUE;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
return {
|
|
179
|
+
command: commandTokens.join(' '),
|
|
180
|
+
args: sanitizeArgsForLogging(args),
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
|
|
105
184
|
/**
|
|
106
185
|
* Get the logs directory path
|
|
107
186
|
*/
|
|
@@ -266,14 +345,16 @@ export class InternalLogger implements Logger {
|
|
|
266
345
|
// Use workingDir as cwd in session metadata
|
|
267
346
|
const cwd = workingDir;
|
|
268
347
|
|
|
348
|
+
const sanitizedInvocation = sanitizeCliCommandForLogging(command, args);
|
|
349
|
+
|
|
269
350
|
// Gather session metadata
|
|
270
351
|
const sessionMetadata: SessionMetadata = {
|
|
271
352
|
sessionId: this.sessionId,
|
|
272
353
|
bucket: this.bucket,
|
|
273
354
|
pid: process.pid,
|
|
274
355
|
ppid: process.ppid,
|
|
275
|
-
command,
|
|
276
|
-
args,
|
|
356
|
+
command: sanitizedInvocation.command,
|
|
357
|
+
args: sanitizedInvocation.args,
|
|
277
358
|
timestamp: new Date().toISOString(),
|
|
278
359
|
cli: {
|
|
279
360
|
version: this.cliVersion,
|