@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
package/dist/keychain.js
CHANGED
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
*/
|
|
7
7
|
const SERVICE_PREFIX = 'com.agentuity.cli';
|
|
8
8
|
const KEY_ACCOUNT = 'aes-encryption-key';
|
|
9
|
+
const AUTH_ACCOUNT = 'auth-token';
|
|
10
|
+
const CODER_API_KEY_ACCOUNT = 'coder-hub-api-key';
|
|
9
11
|
/**
|
|
10
12
|
* Check if we're running on macOS
|
|
11
13
|
*/
|
|
@@ -65,20 +67,10 @@ async function decrypt(combined, keyBytes) {
|
|
|
65
67
|
const plaintext = await crypto.subtle.decrypt({ name: 'AES-GCM', iv }, key, ciphertext);
|
|
66
68
|
return new TextDecoder().decode(plaintext);
|
|
67
69
|
}
|
|
68
|
-
|
|
69
|
-
* Store auth data in macOS Keychain
|
|
70
|
-
*/
|
|
71
|
-
export async function saveAuthToKeychain(profileName, authData) {
|
|
72
|
-
const service = `${SERVICE_PREFIX}.${profileName}`;
|
|
73
|
-
const account = 'auth-token';
|
|
74
|
-
// Get or create encryption key
|
|
70
|
+
async function saveEncryptedValueToKeychain(service, account, value) {
|
|
75
71
|
const key = await ensureEncryptionKey(service);
|
|
76
|
-
|
|
77
|
-
const json = JSON.stringify(authData);
|
|
78
|
-
const encrypted = await encrypt(json, key);
|
|
72
|
+
const encrypted = await encrypt(value, key);
|
|
79
73
|
const b64 = Buffer.from(encrypted).toString('base64');
|
|
80
|
-
// Store encrypted auth in keychain
|
|
81
|
-
// First try to delete if exists, then add
|
|
82
74
|
const del = Bun.spawn(['security', 'delete-generic-password', '-s', service, '-a', account], {
|
|
83
75
|
stderr: 'ignore',
|
|
84
76
|
});
|
|
@@ -96,25 +88,39 @@ export async function saveAuthToKeychain(profileName, authData) {
|
|
|
96
88
|
]);
|
|
97
89
|
await add.exited;
|
|
98
90
|
}
|
|
91
|
+
async function getEncryptedValueFromKeychain(service, account) {
|
|
92
|
+
const find = Bun.spawn(['security', 'find-generic-password', '-s', service, '-a', account, '-w'], { stderr: 'ignore' });
|
|
93
|
+
const stdout = await new Response(find.stdout).text();
|
|
94
|
+
if (stdout.length === 0) {
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
const encrypted = Uint8Array.from(Buffer.from(stdout.trim(), 'base64'));
|
|
98
|
+
const key = await ensureEncryptionKey(service);
|
|
99
|
+
return decrypt(encrypted, key);
|
|
100
|
+
}
|
|
101
|
+
async function deleteValueFromKeychain(service, account) {
|
|
102
|
+
const del = Bun.spawn(['security', 'delete-generic-password', '-s', service, '-a', account], {
|
|
103
|
+
stderr: 'ignore',
|
|
104
|
+
});
|
|
105
|
+
await del.exited;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Store auth data in macOS Keychain
|
|
109
|
+
*/
|
|
110
|
+
export async function saveAuthToKeychain(profileName, authData) {
|
|
111
|
+
const service = `${SERVICE_PREFIX}.${profileName}`;
|
|
112
|
+
await saveEncryptedValueToKeychain(service, AUTH_ACCOUNT, JSON.stringify(authData));
|
|
113
|
+
}
|
|
99
114
|
/**
|
|
100
115
|
* Retrieve auth data from macOS Keychain
|
|
101
116
|
*/
|
|
102
117
|
export async function getAuthFromKeychain(profileName) {
|
|
103
118
|
const service = `${SERVICE_PREFIX}.${profileName}`;
|
|
104
|
-
const account = 'auth-token';
|
|
105
119
|
try {
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
const stdout = await new Response(find.stdout).text();
|
|
109
|
-
if (stdout.length === 0) {
|
|
120
|
+
const json = await getEncryptedValueFromKeychain(service, AUTH_ACCOUNT);
|
|
121
|
+
if (!json) {
|
|
110
122
|
return null;
|
|
111
123
|
}
|
|
112
|
-
const b64 = stdout.trim();
|
|
113
|
-
const encrypted = Uint8Array.from(Buffer.from(b64, 'base64'));
|
|
114
|
-
// Get the encryption key
|
|
115
|
-
const key = await ensureEncryptionKey(service);
|
|
116
|
-
// Decrypt the auth data
|
|
117
|
-
const json = await decrypt(encrypted, key);
|
|
118
124
|
return JSON.parse(json);
|
|
119
125
|
}
|
|
120
126
|
catch {
|
|
@@ -126,10 +132,23 @@ export async function getAuthFromKeychain(profileName) {
|
|
|
126
132
|
*/
|
|
127
133
|
export async function deleteAuthFromKeychain(profileName) {
|
|
128
134
|
const service = `${SERVICE_PREFIX}.${profileName}`;
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
}
|
|
133
|
-
await
|
|
135
|
+
await deleteValueFromKeychain(service, AUTH_ACCOUNT);
|
|
136
|
+
}
|
|
137
|
+
export async function saveCoderApiKeyToKeychain(profileName, apiKey) {
|
|
138
|
+
const service = `${SERVICE_PREFIX}.${profileName}`;
|
|
139
|
+
await saveEncryptedValueToKeychain(service, CODER_API_KEY_ACCOUNT, apiKey);
|
|
140
|
+
}
|
|
141
|
+
export async function getCoderApiKeyFromKeychain(profileName) {
|
|
142
|
+
const service = `${SERVICE_PREFIX}.${profileName}`;
|
|
143
|
+
try {
|
|
144
|
+
return await getEncryptedValueFromKeychain(service, CODER_API_KEY_ACCOUNT);
|
|
145
|
+
}
|
|
146
|
+
catch {
|
|
147
|
+
return null;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
export async function deleteCoderApiKeyFromKeychain(profileName) {
|
|
151
|
+
const service = `${SERVICE_PREFIX}.${profileName}`;
|
|
152
|
+
await deleteValueFromKeychain(service, CODER_API_KEY_ACCOUNT);
|
|
134
153
|
}
|
|
135
154
|
//# sourceMappingURL=keychain.js.map
|
package/dist/keychain.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"keychain.js","sourceRoot":"","sources":["../src/keychain.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,cAAc,GAAG,mBAAmB,CAAC;AAC3C,MAAM,WAAW,GAAG,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"keychain.js","sourceRoot":"","sources":["../src/keychain.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
CHANGED
|
@@ -716,10 +716,10 @@ export declare const BuildMetadataSchema: z.ZodObject<{
|
|
|
716
716
|
type: z.ZodEnum<{
|
|
717
717
|
email: "email";
|
|
718
718
|
stream: "stream";
|
|
719
|
+
websocket: "websocket";
|
|
719
720
|
api: "api";
|
|
720
721
|
sms: "sms";
|
|
721
722
|
cron: "cron";
|
|
722
|
-
websocket: "websocket";
|
|
723
723
|
sse: "sse";
|
|
724
724
|
}>;
|
|
725
725
|
agentIds: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agentuity/cli",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.8",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"author": "Agentuity employees and contributors",
|
|
6
6
|
"type": "module",
|
|
@@ -41,10 +41,11 @@
|
|
|
41
41
|
"prepublishOnly": "bun run clean && bun run build"
|
|
42
42
|
},
|
|
43
43
|
"dependencies": {
|
|
44
|
-
"@agentuity/auth": "2.0.
|
|
45
|
-
"@agentuity/
|
|
46
|
-
"@agentuity/
|
|
47
|
-
"@agentuity/
|
|
44
|
+
"@agentuity/auth": "2.0.8",
|
|
45
|
+
"@agentuity/coder-tui": "2.0.8",
|
|
46
|
+
"@agentuity/core": "2.0.8",
|
|
47
|
+
"@agentuity/frontend": "2.0.8",
|
|
48
|
+
"@agentuity/server": "2.0.8",
|
|
48
49
|
"@datasert/cronjs-parser": "^1.4.0",
|
|
49
50
|
"@vitejs/plugin-react": "^5.1.2",
|
|
50
51
|
"acorn-loose": "^8.5.2",
|
|
@@ -63,7 +64,7 @@
|
|
|
63
64
|
"zod": "^4.3.5"
|
|
64
65
|
},
|
|
65
66
|
"devDependencies": {
|
|
66
|
-
"@agentuity/test-utils": "2.0.
|
|
67
|
+
"@agentuity/test-utils": "2.0.8",
|
|
67
68
|
"@types/adm-zip": "^0.5.7",
|
|
68
69
|
"@types/archiver": "^7.0.0",
|
|
69
70
|
"@types/bun": "latest",
|
package/src/cli.ts
CHANGED
|
@@ -1195,8 +1195,10 @@ async function registerSubcommand(
|
|
|
1195
1195
|
// Add -y short alias for --confirm
|
|
1196
1196
|
flagSpec = `-y, --${flag}`;
|
|
1197
1197
|
} else if (optAliases.length > 0) {
|
|
1198
|
-
const
|
|
1199
|
-
|
|
1198
|
+
const aliasFlags = optAliases
|
|
1199
|
+
.map((a) => (a.length === 1 ? `-${a}` : `--${a}`))
|
|
1200
|
+
.join(', ');
|
|
1201
|
+
flagSpec = `${aliasFlags}, --${flag}`;
|
|
1200
1202
|
}
|
|
1201
1203
|
if (opt.type === 'boolean') {
|
|
1202
1204
|
if (opt.hasDefault) {
|
|
@@ -151,11 +151,11 @@ The handler receives a context object with:
|
|
|
151
151
|
|
|
152
152
|
```typescript
|
|
153
153
|
handler: async (ctx, input) => {
|
|
154
|
-
await ctx.kv.set('user:123', { name: 'Alice', age: 30 });
|
|
155
|
-
const
|
|
156
|
-
await ctx.kv.delete('user:123');
|
|
157
|
-
const keys = await ctx.kv.
|
|
158
|
-
return
|
|
154
|
+
await ctx.kv.set('users', 'user:123', { name: 'Alice', age: 30 });
|
|
155
|
+
const result = await ctx.kv.get('users', 'user:123');
|
|
156
|
+
await ctx.kv.delete('users', 'user:123');
|
|
157
|
+
const keys = await ctx.kv.getKeys('users');
|
|
158
|
+
return result.exists ? result.data : null;
|
|
159
159
|
};
|
|
160
160
|
```
|
|
161
161
|
|
|
@@ -188,7 +188,7 @@ handler: async (ctx, input) => {
|
|
|
188
188
|
handler: async (ctx, input) => {
|
|
189
189
|
// Schedule background work that continues after response
|
|
190
190
|
ctx.waitUntil(async () => {
|
|
191
|
-
await ctx.kv.set('processed', Date.now());
|
|
191
|
+
await ctx.kv.set('state', 'processed', Date.now());
|
|
192
192
|
ctx.logger.info('Background task complete');
|
|
193
193
|
});
|
|
194
194
|
|
|
@@ -180,6 +180,13 @@ export async function discoverRoutes(
|
|
|
180
180
|
version
|
|
181
181
|
);
|
|
182
182
|
|
|
183
|
+
// Extract type-specific config from route metadata
|
|
184
|
+
const meta =
|
|
185
|
+
typeof route.handler === 'function'
|
|
186
|
+
? (route.handler as any)[Symbol.for('agentuity:route-meta')]
|
|
187
|
+
: undefined;
|
|
188
|
+
const config = meta?.schedule ? { expression: meta.schedule } : undefined;
|
|
189
|
+
|
|
183
190
|
routes.push({
|
|
184
191
|
id,
|
|
185
192
|
filename: toForwardSlash(relative(rootDir, mount.routerFile)),
|
|
@@ -187,6 +194,7 @@ export async function discoverRoutes(
|
|
|
187
194
|
method,
|
|
188
195
|
version,
|
|
189
196
|
type: routeType,
|
|
197
|
+
config,
|
|
190
198
|
});
|
|
191
199
|
}
|
|
192
200
|
|
|
@@ -8,6 +8,7 @@ import { sandboxRmFile, sandboxResolve } from '@agentuity/server';
|
|
|
8
8
|
const RmFileResponseSchema = z.object({
|
|
9
9
|
success: z.boolean(),
|
|
10
10
|
path: z.string(),
|
|
11
|
+
found: z.boolean(),
|
|
11
12
|
});
|
|
12
13
|
|
|
13
14
|
export const rmSubcommand = createCommand({
|
|
@@ -40,17 +41,21 @@ export const rmSubcommand = createCommand({
|
|
|
40
41
|
|
|
41
42
|
const client = createSandboxClient(logger, auth, region);
|
|
42
43
|
|
|
43
|
-
await sandboxRmFile(client, {
|
|
44
|
+
const result = await sandboxRmFile(client, {
|
|
44
45
|
sandboxId: args.sandboxId,
|
|
45
46
|
path: args.path,
|
|
46
47
|
orgId,
|
|
47
48
|
});
|
|
48
49
|
|
|
49
50
|
if (!options.json) {
|
|
50
|
-
|
|
51
|
+
if (result.found) {
|
|
52
|
+
tui.success(`Removed file: ${args.path}`);
|
|
53
|
+
} else {
|
|
54
|
+
tui.warning(`File not found: ${args.path} (already removed)`);
|
|
55
|
+
}
|
|
51
56
|
}
|
|
52
57
|
|
|
53
|
-
return { success: true, path: args.path };
|
|
58
|
+
return { success: true, path: args.path, found: result.found };
|
|
54
59
|
},
|
|
55
60
|
});
|
|
56
61
|
|
|
@@ -8,6 +8,7 @@ import { sandboxRmDir, sandboxResolve } from '@agentuity/server';
|
|
|
8
8
|
const RmDirResponseSchema = z.object({
|
|
9
9
|
success: z.boolean(),
|
|
10
10
|
path: z.string(),
|
|
11
|
+
found: z.boolean(),
|
|
11
12
|
});
|
|
12
13
|
|
|
13
14
|
export const rmdirSubcommand = createCommand({
|
|
@@ -52,7 +53,7 @@ export const rmdirSubcommand = createCommand({
|
|
|
52
53
|
|
|
53
54
|
const client = createSandboxClient(logger, auth, region);
|
|
54
55
|
|
|
55
|
-
await sandboxRmDir(client, {
|
|
56
|
+
const result = await sandboxRmDir(client, {
|
|
56
57
|
sandboxId: args.sandboxId,
|
|
57
58
|
path: args.path,
|
|
58
59
|
recursive: opts.recursive,
|
|
@@ -60,10 +61,14 @@ export const rmdirSubcommand = createCommand({
|
|
|
60
61
|
});
|
|
61
62
|
|
|
62
63
|
if (!options.json) {
|
|
63
|
-
|
|
64
|
+
if (result.found) {
|
|
65
|
+
tui.success(`Removed directory: ${args.path}`);
|
|
66
|
+
} else {
|
|
67
|
+
tui.warning(`Directory not found: ${args.path} (already removed)`);
|
|
68
|
+
}
|
|
64
69
|
}
|
|
65
70
|
|
|
66
|
-
return { success: true, path: args.path };
|
|
71
|
+
return { success: true, path: args.path, found: result.found };
|
|
67
72
|
},
|
|
68
73
|
});
|
|
69
74
|
|
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { createCommand } from '../../../types';
|
|
3
|
+
import * as tui from '../../../tui';
|
|
4
|
+
import { createStorageAdapter, resolveMeId, parseDuration, truncate } from './util';
|
|
5
|
+
import { getCommand } from '../../../command-prefix';
|
|
6
|
+
import { isDryRunMode, outputDryRun } from '../../../explain';
|
|
7
|
+
import type { TaskPriority, TaskStatus, TaskType, BatchClosedTask } from '@agentuity/core';
|
|
8
|
+
|
|
9
|
+
const TaskCloseResponseSchema = z.object({
|
|
10
|
+
success: z.boolean().describe('Whether the operation succeeded'),
|
|
11
|
+
closed: z
|
|
12
|
+
.array(
|
|
13
|
+
z.object({
|
|
14
|
+
id: z.string().describe('Closed task ID'),
|
|
15
|
+
title: z.string().describe('Closed task title'),
|
|
16
|
+
status: z.string().describe('Task status'),
|
|
17
|
+
closed_date: z.string().optional().describe('ISO 8601 closed date'),
|
|
18
|
+
})
|
|
19
|
+
)
|
|
20
|
+
.describe('List of closed tasks'),
|
|
21
|
+
count: z.number().describe('Number of tasks closed'),
|
|
22
|
+
durationMs: z.number().describe('Operation duration in milliseconds'),
|
|
23
|
+
dryRun: z.boolean().optional().describe('Whether this was a dry run'),
|
|
24
|
+
message: z.string().optional().describe('Status message'),
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
export const closeSubcommand = createCommand({
|
|
28
|
+
name: 'close',
|
|
29
|
+
aliases: ['done', 'complete'],
|
|
30
|
+
description: 'Close a task by ID or batch-close tasks by filter',
|
|
31
|
+
tags: ['mutating', 'slow', 'requires-auth'],
|
|
32
|
+
requires: { auth: true },
|
|
33
|
+
examples: [
|
|
34
|
+
{
|
|
35
|
+
command: getCommand('cloud task close task_abc123'),
|
|
36
|
+
description: 'Close a single task by ID',
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
command: getCommand('cloud task close --status in_progress --older-than 7d'),
|
|
40
|
+
description: 'Close in-progress tasks older than 7 days',
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
command: getCommand('cloud task close --status open --limit 10 --dry-run'),
|
|
44
|
+
description: 'Preview which open tasks would be closed (dry run)',
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
command: getCommand('cloud task close --created-id me --confirm'),
|
|
48
|
+
description: 'Close all tasks created by me without confirmation prompt',
|
|
49
|
+
},
|
|
50
|
+
],
|
|
51
|
+
schema: {
|
|
52
|
+
args: z.object({
|
|
53
|
+
id: z.string().optional().describe('Task ID to close (for single close)'),
|
|
54
|
+
}),
|
|
55
|
+
options: z.object({
|
|
56
|
+
status: z
|
|
57
|
+
.enum(['open', 'in_progress', 'started', 'done', 'completed', 'closed', 'cancelled'])
|
|
58
|
+
.optional()
|
|
59
|
+
.describe('filter batch close by status'),
|
|
60
|
+
type: z
|
|
61
|
+
.enum(['epic', 'feature', 'enhancement', 'bug', 'task'])
|
|
62
|
+
.optional()
|
|
63
|
+
.describe('filter batch close by type'),
|
|
64
|
+
priority: z
|
|
65
|
+
.enum(['high', 'medium', 'low', 'none'])
|
|
66
|
+
.optional()
|
|
67
|
+
.describe('filter batch close by priority'),
|
|
68
|
+
olderThan: z
|
|
69
|
+
.string()
|
|
70
|
+
.optional()
|
|
71
|
+
.describe('filter batch close by age (e.g. 30s, 7d, 24h, 2w)'),
|
|
72
|
+
parentId: z.string().optional().describe('filter batch close by parent task ID'),
|
|
73
|
+
createdId: z
|
|
74
|
+
.string()
|
|
75
|
+
.optional()
|
|
76
|
+
.describe('filter batch close by creator ID (use "me" for current user)'),
|
|
77
|
+
assignedId: z.string().optional().describe('filter batch close by assigned user ID'),
|
|
78
|
+
projectId: z.string().optional().describe('filter batch close by project ID'),
|
|
79
|
+
tagId: z.string().optional().describe('filter batch close by tag ID'),
|
|
80
|
+
idsFile: z.string().optional().describe('path to JSON file containing task IDs to close'),
|
|
81
|
+
orgId: z.string().optional().describe('organization ID (uses default if not specified)'),
|
|
82
|
+
dryRun: z
|
|
83
|
+
.boolean()
|
|
84
|
+
.optional()
|
|
85
|
+
.default(false)
|
|
86
|
+
.describe('preview changes without executing'),
|
|
87
|
+
limit: z.coerce
|
|
88
|
+
.number()
|
|
89
|
+
.int()
|
|
90
|
+
.min(1)
|
|
91
|
+
.max(200)
|
|
92
|
+
.default(50)
|
|
93
|
+
.describe('max tasks to close in batch mode (default: 50, max: 200)'),
|
|
94
|
+
confirm: z.boolean().optional().default(false).describe('skip confirmation prompt'),
|
|
95
|
+
}),
|
|
96
|
+
response: TaskCloseResponseSchema,
|
|
97
|
+
},
|
|
98
|
+
|
|
99
|
+
async handler(ctx) {
|
|
100
|
+
const { args, opts, options } = ctx;
|
|
101
|
+
const started = Date.now();
|
|
102
|
+
const storage = await createStorageAdapter(ctx);
|
|
103
|
+
|
|
104
|
+
const isSingleClose = !!args.id;
|
|
105
|
+
const hasFilters =
|
|
106
|
+
opts.status ||
|
|
107
|
+
opts.type ||
|
|
108
|
+
opts.priority ||
|
|
109
|
+
opts.olderThan ||
|
|
110
|
+
opts.parentId ||
|
|
111
|
+
opts.createdId ||
|
|
112
|
+
opts.assignedId ||
|
|
113
|
+
opts.projectId ||
|
|
114
|
+
opts.tagId ||
|
|
115
|
+
opts.idsFile;
|
|
116
|
+
|
|
117
|
+
if (!isSingleClose && !hasFilters) {
|
|
118
|
+
tui.fatal(
|
|
119
|
+
'Provide a task ID for single close, or use --status, --type, --priority, --older-than, --parent-id, --created-id, --assigned-id, --project-id, --tag-id, or --ids-file for batch close.'
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (isSingleClose && hasFilters) {
|
|
124
|
+
tui.fatal(
|
|
125
|
+
'Cannot combine task ID with filter options. Use either single close (by ID) or batch close (by filters).'
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (isSingleClose) {
|
|
130
|
+
if (isDryRunMode(options)) {
|
|
131
|
+
outputDryRun(`Would close task: ${args.id}`, options);
|
|
132
|
+
return {
|
|
133
|
+
success: true,
|
|
134
|
+
closed: [{ id: args.id!, title: '(dry run)', status: 'done' }],
|
|
135
|
+
count: 1,
|
|
136
|
+
durationMs: Date.now() - started,
|
|
137
|
+
dryRun: true,
|
|
138
|
+
message: 'Dry run — no tasks were closed',
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
if (!opts.confirm) {
|
|
143
|
+
const confirmed = await tui.confirm(`Close task "${args.id}"?`, false);
|
|
144
|
+
if (!confirmed) {
|
|
145
|
+
if (!options.json) tui.info('Cancelled');
|
|
146
|
+
return {
|
|
147
|
+
success: false,
|
|
148
|
+
closed: [],
|
|
149
|
+
count: 0,
|
|
150
|
+
durationMs: Date.now() - started,
|
|
151
|
+
message: 'Cancelled',
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const task = await storage.close(args.id!);
|
|
157
|
+
const durationMs = Date.now() - started;
|
|
158
|
+
|
|
159
|
+
if (!options.json) {
|
|
160
|
+
tui.success(`Closed task ${tui.bold(task.id)} (${task.title}) in ${durationMs}ms`);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return {
|
|
164
|
+
success: true,
|
|
165
|
+
closed: [
|
|
166
|
+
{
|
|
167
|
+
id: task.id,
|
|
168
|
+
title: task.title,
|
|
169
|
+
status: task.status,
|
|
170
|
+
closed_date: task.closed_date,
|
|
171
|
+
},
|
|
172
|
+
],
|
|
173
|
+
count: 1,
|
|
174
|
+
durationMs,
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Batch close mode
|
|
179
|
+
if (opts.olderThan) {
|
|
180
|
+
parseDuration(opts.olderThan);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const createdId = resolveMeId(opts.createdId, ctx);
|
|
184
|
+
const assignedId = resolveMeId(opts.assignedId, ctx);
|
|
185
|
+
|
|
186
|
+
// Handle IDs file
|
|
187
|
+
let explicitIds: string[] | undefined;
|
|
188
|
+
if (opts.idsFile) {
|
|
189
|
+
const file = Bun.file(opts.idsFile);
|
|
190
|
+
if (!(await file.exists())) {
|
|
191
|
+
tui.fatal(`IDs file not found: ${opts.idsFile}`);
|
|
192
|
+
}
|
|
193
|
+
try {
|
|
194
|
+
const content = await file.json();
|
|
195
|
+
if (Array.isArray(content)) {
|
|
196
|
+
explicitIds = content.map((id) => String(id));
|
|
197
|
+
} else if (content && Array.isArray((content as { ids?: string[] }).ids)) {
|
|
198
|
+
explicitIds = (content as { ids: string[] }).ids;
|
|
199
|
+
} else {
|
|
200
|
+
tui.fatal(`Invalid IDs file format. Expected array of IDs or { ids: [...] }`);
|
|
201
|
+
}
|
|
202
|
+
} catch (err) {
|
|
203
|
+
tui.fatal(`Failed to parse IDs file: ${err}`);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const batchParams = {
|
|
208
|
+
status: opts.status as TaskStatus | undefined,
|
|
209
|
+
type: opts.type as TaskType | undefined,
|
|
210
|
+
priority: opts.priority as TaskPriority | undefined,
|
|
211
|
+
parent_id: opts.parentId,
|
|
212
|
+
created_id: createdId,
|
|
213
|
+
assigned_id: assignedId,
|
|
214
|
+
project_id: opts.projectId,
|
|
215
|
+
tag_id: opts.tagId,
|
|
216
|
+
older_than: opts.olderThan,
|
|
217
|
+
ids: explicitIds,
|
|
218
|
+
limit: opts.limit,
|
|
219
|
+
closed_id: ctx.auth.userId,
|
|
220
|
+
dry_run: isDryRunMode(options),
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
// For confirmation, run a dry-run first to preview
|
|
224
|
+
if (!isDryRunMode(options) && !opts.confirm) {
|
|
225
|
+
const preview = await storage.batchClose({ ...batchParams, dry_run: true });
|
|
226
|
+
|
|
227
|
+
if (preview.count === 0) {
|
|
228
|
+
if (!options.json) tui.info('No tasks match the given filters');
|
|
229
|
+
return {
|
|
230
|
+
success: true,
|
|
231
|
+
closed: [],
|
|
232
|
+
count: 0,
|
|
233
|
+
durationMs: Date.now() - started,
|
|
234
|
+
message: 'No matching tasks found',
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
if (!options.json) {
|
|
239
|
+
tui.warning(
|
|
240
|
+
`Found ${preview.count} ${tui.plural(preview.count, 'task', 'tasks')} to close:`
|
|
241
|
+
);
|
|
242
|
+
tui.newline();
|
|
243
|
+
|
|
244
|
+
const tableData = preview.closed.map((task: BatchClosedTask) => ({
|
|
245
|
+
ID: tui.muted(truncate(task.id, 28)),
|
|
246
|
+
Title: truncate(task.title, 40),
|
|
247
|
+
Status: task.status,
|
|
248
|
+
}));
|
|
249
|
+
|
|
250
|
+
tui.table(tableData, [
|
|
251
|
+
{ name: 'ID', alignment: 'left' },
|
|
252
|
+
{ name: 'Title', alignment: 'left' },
|
|
253
|
+
{ name: 'Status', alignment: 'left' },
|
|
254
|
+
]);
|
|
255
|
+
tui.newline();
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
const confirmed = await tui.confirm(
|
|
259
|
+
`Close ${preview.count} ${tui.plural(preview.count, 'task', 'tasks')}?`,
|
|
260
|
+
false
|
|
261
|
+
);
|
|
262
|
+
if (!confirmed) {
|
|
263
|
+
if (!options.json) tui.info('Cancelled');
|
|
264
|
+
return {
|
|
265
|
+
success: false,
|
|
266
|
+
closed: [],
|
|
267
|
+
count: 0,
|
|
268
|
+
durationMs: Date.now() - started,
|
|
269
|
+
message: 'Cancelled',
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// Execute batch close
|
|
275
|
+
const result = await storage.batchClose(batchParams);
|
|
276
|
+
const durationMs = Date.now() - started;
|
|
277
|
+
|
|
278
|
+
if (!options.json) {
|
|
279
|
+
if (result.dry_run) {
|
|
280
|
+
if (result.count > 0) {
|
|
281
|
+
tui.info(
|
|
282
|
+
`Dry run: would close ${result.count} ${tui.plural(result.count, 'task', 'tasks')}`
|
|
283
|
+
);
|
|
284
|
+
} else {
|
|
285
|
+
tui.info('No tasks match the given filters');
|
|
286
|
+
}
|
|
287
|
+
} else if (result.count > 0) {
|
|
288
|
+
tui.success(
|
|
289
|
+
`Closed ${result.count} ${tui.plural(result.count, 'task', 'tasks')} in ${durationMs}ms`
|
|
290
|
+
);
|
|
291
|
+
|
|
292
|
+
// Show which tasks were closed
|
|
293
|
+
if (result.closed.length > 0) {
|
|
294
|
+
tui.newline();
|
|
295
|
+
const closedTable = result.closed.map((task) => ({
|
|
296
|
+
ID: tui.muted(truncate(task.id, 28)),
|
|
297
|
+
Title: truncate(task.title, 40),
|
|
298
|
+
}));
|
|
299
|
+
tui.table(closedTable, [
|
|
300
|
+
{ name: 'ID', alignment: 'left' },
|
|
301
|
+
{ name: 'Title', alignment: 'left' },
|
|
302
|
+
]);
|
|
303
|
+
}
|
|
304
|
+
} else {
|
|
305
|
+
tui.info('No tasks matched the given filters');
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
return {
|
|
310
|
+
success: true,
|
|
311
|
+
closed: result.closed,
|
|
312
|
+
count: result.count,
|
|
313
|
+
durationMs,
|
|
314
|
+
dryRun: result.dry_run,
|
|
315
|
+
};
|
|
316
|
+
},
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
export default closeSubcommand;
|