@agentuity/cli 1.0.30 → 1.0.32
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/dist/agent-detection.js +1 -1
- package/dist/agent-detection.js.map +1 -1
- package/dist/api.d.ts +1 -1
- package/dist/api.d.ts.map +1 -1
- package/dist/api.js +1 -1
- package/dist/api.js.map +1 -1
- package/dist/cache/agent-intro.d.ts.map +1 -1
- package/dist/cache/agent-intro.js.map +1 -1
- package/dist/cache/resource-region.d.ts.map +1 -1
- package/dist/cache/resource-region.js.map +1 -1
- package/dist/cache/user-cache.d.ts.map +1 -1
- package/dist/cache/user-cache.js.map +1 -1
- package/dist/cmd/ai/opencode/install.js +1 -1
- package/dist/cmd/ai/opencode/install.js.map +1 -1
- package/dist/cmd/build/ast.js +2 -2
- package/dist/cmd/build/ast.js.map +1 -1
- package/dist/cmd/build/patch/_util.d.ts +1 -1
- package/dist/cmd/build/patch/_util.d.ts.map +1 -1
- package/dist/cmd/build/patch/_util.js +2 -2
- package/dist/cmd/build/patch/_util.js.map +1 -1
- package/dist/cmd/build/vite/bun-dev-server.js +9 -1
- package/dist/cmd/build/vite/bun-dev-server.js.map +1 -1
- package/dist/cmd/build/vite/config-loader.js +9 -1
- package/dist/cmd/build/vite/config-loader.js.map +1 -1
- package/dist/cmd/build/vite/registry-generator.js.map +1 -1
- package/dist/cmd/build/vite/server-bundler.js +9 -1
- package/dist/cmd/build/vite/server-bundler.js.map +1 -1
- package/dist/cmd/build/vite/static-renderer.js +11 -3
- package/dist/cmd/build/vite/static-renderer.js.map +1 -1
- package/dist/cmd/build/vite/vite-asset-server-config.js +9 -1
- package/dist/cmd/build/vite/vite-asset-server-config.js.map +1 -1
- package/dist/cmd/build/vite/vite-asset-server.js +9 -1
- package/dist/cmd/build/vite/vite-asset-server.js.map +1 -1
- package/dist/cmd/build/vite/vite-builder.js +10 -2
- package/dist/cmd/build/vite/vite-builder.js.map +1 -1
- package/dist/cmd/build/vite/workbench-generator.d.ts +1 -1
- package/dist/cmd/build/vite/workbench-generator.d.ts.map +1 -1
- package/dist/cmd/build/vite/workbench-generator.js +1 -1
- package/dist/cmd/build/vite/workbench-generator.js.map +1 -1
- package/dist/cmd/cloud/keyvalue/repl.d.ts.map +1 -1
- package/dist/cmd/cloud/keyvalue/repl.js +4 -2
- package/dist/cmd/cloud/keyvalue/repl.js.map +1 -1
- package/dist/cmd/cloud/keyvalue/search.d.ts.map +1 -1
- package/dist/cmd/cloud/keyvalue/search.js.map +1 -1
- package/dist/cmd/cloud/sandbox/cp.js +2 -2
- package/dist/cmd/cloud/sandbox/cp.js.map +1 -1
- package/dist/cmd/cloud/storage/config.d.ts.map +1 -1
- package/dist/cmd/cloud/storage/config.js +1 -2
- package/dist/cmd/cloud/storage/config.js.map +1 -1
- package/dist/cmd/cloud/storage/list.d.ts.map +1 -1
- package/dist/cmd/cloud/storage/list.js.map +1 -1
- package/dist/cmd/cloud/task/create.d.ts.map +1 -1
- package/dist/cmd/cloud/task/create.js +15 -6
- package/dist/cmd/cloud/task/create.js.map +1 -1
- package/dist/cmd/cloud/task/delete.d.ts +8 -0
- package/dist/cmd/cloud/task/delete.d.ts.map +1 -0
- package/dist/cmd/cloud/task/delete.js +286 -0
- package/dist/cmd/cloud/task/delete.js.map +1 -0
- package/dist/cmd/cloud/task/get.d.ts.map +1 -1
- package/dist/cmd/cloud/task/get.js +10 -3
- package/dist/cmd/cloud/task/get.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 +2 -0
- package/dist/cmd/cloud/task/list.js.map +1 -1
- package/dist/cmd/dev/dev-lock.js +2 -2
- package/dist/cmd/dev/dev-lock.js.map +1 -1
- package/dist/cmd/dev/templates.d.ts.map +1 -1
- package/dist/cmd/dev/templates.js +0 -1
- package/dist/cmd/dev/templates.js.map +1 -1
- package/dist/cmd/project/download.js +1 -1
- package/dist/cmd/project/download.js.map +1 -1
- package/dist/domain.js +1 -1
- package/dist/domain.js.map +1 -1
- package/dist/schema-generator.d.ts +1 -1
- package/dist/schema-generator.d.ts.map +1 -1
- package/dist/schema-generator.js +1 -1
- package/dist/schema-generator.js.map +1 -1
- package/dist/schema-parser.js +1 -1
- package/dist/schema-parser.js.map +1 -1
- package/dist/terminal.d.ts.map +1 -1
- package/dist/terminal.js +12 -17
- package/dist/terminal.js.map +1 -1
- package/dist/tui.js +1 -1
- package/dist/tui.js.map +1 -1
- package/dist/utils/date.js +1 -1
- package/dist/utils/date.js.map +1 -1
- package/package.json +6 -6
- package/src/agent-detection.ts +1 -1
- package/src/api.ts +1 -1
- package/src/cache/agent-intro.ts +3 -4
- package/src/cache/resource-region.ts +3 -1
- package/src/cache/user-cache.ts +3 -4
- package/src/cmd/ai/opencode/install.ts +1 -1
- package/src/cmd/build/ast.ts +2 -2
- package/src/cmd/build/patch/_util.ts +2 -2
- package/src/cmd/build/vite/registry-generator.ts +4 -4
- package/src/cmd/build/vite/workbench-generator.ts +1 -1
- package/src/cmd/cloud/keyvalue/repl.ts +6 -2
- package/src/cmd/cloud/keyvalue/search.ts +2 -1
- package/src/cmd/cloud/sandbox/cp.ts +2 -2
- package/src/cmd/cloud/storage/config.ts +3 -8
- package/src/cmd/cloud/storage/list.ts +8 -3
- package/src/cmd/cloud/task/create.ts +17 -8
- package/src/cmd/cloud/task/delete.ts +342 -0
- package/src/cmd/cloud/task/get.ts +11 -3
- package/src/cmd/cloud/task/index.ts +10 -0
- package/src/cmd/cloud/task/list.ts +2 -0
- package/src/cmd/dev/dev-lock.ts +2 -2
- package/src/cmd/dev/templates.ts +0 -1
- package/src/cmd/project/download.ts +1 -1
- package/src/domain.ts +3 -3
- package/src/schema-generator.ts +1 -1
- package/src/schema-parser.ts +1 -1
- package/src/terminal.ts +12 -14
- package/src/tui.ts +1 -1
- package/src/utils/date.ts +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agentuity/cli",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.32",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"author": "Agentuity employees and contributors",
|
|
6
6
|
"type": "module",
|
|
@@ -41,9 +41,9 @@
|
|
|
41
41
|
"prepublishOnly": "bun run clean && bun run build"
|
|
42
42
|
},
|
|
43
43
|
"dependencies": {
|
|
44
|
-
"@agentuity/auth": "1.0.
|
|
45
|
-
"@agentuity/core": "1.0.
|
|
46
|
-
"@agentuity/server": "1.0.
|
|
44
|
+
"@agentuity/auth": "1.0.32",
|
|
45
|
+
"@agentuity/core": "1.0.32",
|
|
46
|
+
"@agentuity/server": "1.0.32",
|
|
47
47
|
"@datasert/cronjs-parser": "^1.4.0",
|
|
48
48
|
"@vitejs/plugin-react": "^5.1.2",
|
|
49
49
|
"acorn-loose": "^8.5.2",
|
|
@@ -60,10 +60,10 @@
|
|
|
60
60
|
"typescript": "^5.9.0",
|
|
61
61
|
"vite": "^7.2.7",
|
|
62
62
|
"zod": "^4.3.5",
|
|
63
|
-
"@agentuity/frontend": "1.0.
|
|
63
|
+
"@agentuity/frontend": "1.0.32"
|
|
64
64
|
},
|
|
65
65
|
"devDependencies": {
|
|
66
|
-
"@agentuity/test-utils": "1.0.
|
|
66
|
+
"@agentuity/test-utils": "1.0.32",
|
|
67
67
|
"@types/adm-zip": "^0.5.7",
|
|
68
68
|
"@types/bun": "latest",
|
|
69
69
|
"@types/tar-fs": "^2.0.4",
|
package/src/agent-detection.ts
CHANGED
|
@@ -294,7 +294,7 @@ function initLinuxFFI(): FFIFunctions {
|
|
|
294
294
|
const ppidField = fields[1]; // ppid is 2nd field after state
|
|
295
295
|
if (!ppidField) return null;
|
|
296
296
|
const ppid = parseInt(ppidField, 10);
|
|
297
|
-
return isNaN(ppid) || ppid <= 1 ? null : ppid;
|
|
297
|
+
return Number.isNaN(ppid) || ppid <= 1 ? null : ppid;
|
|
298
298
|
} catch {
|
|
299
299
|
// Ignore errors
|
|
300
300
|
}
|
package/src/api.ts
CHANGED
|
@@ -93,7 +93,7 @@ export function getAppBaseURL(config?: Config | null): string {
|
|
|
93
93
|
return baseGetAppBaseURL(config?.name, overrides);
|
|
94
94
|
}
|
|
95
95
|
|
|
96
|
-
export function getGravityDevModeURL(
|
|
96
|
+
export function getGravityDevModeURL(_region: string, config?: Config | null): string {
|
|
97
97
|
const overrides = config?.overrides as { gravity_url?: string } | undefined;
|
|
98
98
|
if (overrides?.gravity_url) {
|
|
99
99
|
return overrides.gravity_url;
|
package/src/cache/agent-intro.ts
CHANGED
|
@@ -33,10 +33,9 @@ function getDatabase(): Database {
|
|
|
33
33
|
export function hasAgentSeenIntro(agentId: string): boolean {
|
|
34
34
|
try {
|
|
35
35
|
const row = getDatabase()
|
|
36
|
-
.query<
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
>('SELECT agent_id FROM agent_intro_seen WHERE agent_id = ?')
|
|
36
|
+
.query<{ agent_id: string }, [string]>(
|
|
37
|
+
'SELECT agent_id FROM agent_intro_seen WHERE agent_id = ?'
|
|
38
|
+
)
|
|
40
39
|
.get(agentId);
|
|
41
40
|
return row !== null;
|
|
42
41
|
} catch {
|
|
@@ -92,7 +92,9 @@ export async function getResourceInfo(
|
|
|
92
92
|
.query<
|
|
93
93
|
{ region: string; org_id: string | null; project_id: string | null; last_updated: number },
|
|
94
94
|
[string, string, string]
|
|
95
|
-
>(
|
|
95
|
+
>(
|
|
96
|
+
'SELECT region, org_id, project_id, last_updated FROM resource_region_cache WHERE resource_type = ? AND profile = ? AND id = ?'
|
|
97
|
+
)
|
|
96
98
|
.get(type, profile, id);
|
|
97
99
|
|
|
98
100
|
if (!row) {
|
package/src/cache/user-cache.ts
CHANGED
|
@@ -37,10 +37,9 @@ export function getCachedUserInfo(
|
|
|
37
37
|
): { userId: string; firstName: string; lastName: string } | null {
|
|
38
38
|
try {
|
|
39
39
|
const row = getDatabase()
|
|
40
|
-
.query<
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
>('SELECT user_id, first_name, last_name FROM user_info_cache WHERE profile = ?')
|
|
40
|
+
.query<{ user_id: string; first_name: string; last_name: string }, [string]>(
|
|
41
|
+
'SELECT user_id, first_name, last_name FROM user_info_cache WHERE profile = ?'
|
|
42
|
+
)
|
|
44
43
|
.get(profile);
|
|
45
44
|
if (!row) return null;
|
|
46
45
|
return {
|
|
@@ -56,7 +56,7 @@ export const installSubcommand = createSubcommand({
|
|
|
56
56
|
const hasExactEntry = openCodeConfig.plugin.includes(pluginEntry);
|
|
57
57
|
|
|
58
58
|
// Check if there's an existing entry that needs updating
|
|
59
|
-
const existingIndex = openCodeConfig.plugin.
|
|
59
|
+
const existingIndex = openCodeConfig.plugin.indexOf('@agentuity/opencode');
|
|
60
60
|
|
|
61
61
|
if (hasExactEntry) {
|
|
62
62
|
if (!jsonMode) {
|
package/src/cmd/build/ast.ts
CHANGED
|
@@ -1269,9 +1269,9 @@ function extractValidatorSchemas(callExpr: ASTCallExpression): {
|
|
|
1269
1269
|
if (unary.argument?.type === 'Identifier') {
|
|
1270
1270
|
const identifier = unary.argument as ASTNodeIdentifier;
|
|
1271
1271
|
if (identifier.name === 'true') {
|
|
1272
|
-
result.stream = unary.operator
|
|
1272
|
+
result.stream = unary.operator !== '!';
|
|
1273
1273
|
} else if (identifier.name === 'false') {
|
|
1274
|
-
result.stream = unary.operator === '!'
|
|
1274
|
+
result.stream = unary.operator === '!';
|
|
1275
1275
|
}
|
|
1276
1276
|
}
|
|
1277
1277
|
}
|
|
@@ -43,7 +43,7 @@ ${inject}
|
|
|
43
43
|
|
|
44
44
|
export function generateGatewayEnvGuard(
|
|
45
45
|
apikey: string,
|
|
46
|
-
|
|
46
|
+
_apikeyval: string,
|
|
47
47
|
apibase: string,
|
|
48
48
|
provider: string
|
|
49
49
|
): string {
|
|
@@ -63,7 +63,7 @@ export function generateGatewayEnvGuard(
|
|
|
63
63
|
|
|
64
64
|
export function searchBackwards(contents: string, offset: number, val: string): number {
|
|
65
65
|
for (let i = offset; i >= 0; i--) {
|
|
66
|
-
if (contents.charAt(i)
|
|
66
|
+
if (contents.charAt(i) === val) {
|
|
67
67
|
return i;
|
|
68
68
|
}
|
|
69
69
|
}
|
|
@@ -1067,11 +1067,11 @@ export async function generateRouteRegistry(
|
|
|
1067
1067
|
const generatedContent = `// @generated
|
|
1068
1068
|
// Auto-generated by Agentuity - DO NOT EDIT
|
|
1069
1069
|
${importsStr}${typeImports}${
|
|
1070
|
-
|
|
1071
|
-
|
|
1070
|
+
shouldEmitFrontendClient
|
|
1071
|
+
? `
|
|
1072
1072
|
import { createClient } from '@agentuity/frontend';`
|
|
1073
|
-
|
|
1074
|
-
|
|
1073
|
+
: ''
|
|
1074
|
+
}
|
|
1075
1075
|
// ============================================================================
|
|
1076
1076
|
// Route Schema Type Exports
|
|
1077
1077
|
// ============================================================================
|
|
@@ -52,7 +52,8 @@ export const replSubcommand = createCommand({
|
|
|
52
52
|
const contentType = isPossiblyJSON(ctx.parsed.args[2]!)
|
|
53
53
|
? 'application/json'
|
|
54
54
|
: 'text/plain';
|
|
55
|
-
const ttl =
|
|
55
|
+
const ttl =
|
|
56
|
+
ctx.parsed.args.length > 3 ? parseInt(ctx.parsed.args[3]!, 10) : undefined;
|
|
56
57
|
await storage.set(ctx.parsed.args[0]!, ctx.parsed.args[1]!, ctx.parsed.args[2]!, {
|
|
57
58
|
contentType,
|
|
58
59
|
ttl,
|
|
@@ -172,7 +173,10 @@ export const replSubcommand = createCommand({
|
|
|
172
173
|
const item = results[key];
|
|
173
174
|
if (!item) continue;
|
|
174
175
|
const sizeMB = (item.size / (1024 * 1024)).toFixed(2);
|
|
175
|
-
const date =
|
|
176
|
+
const date =
|
|
177
|
+
item.lastUsed != null
|
|
178
|
+
? new Date(item.lastUsed).toLocaleString()
|
|
179
|
+
: 'unknown';
|
|
176
180
|
ctx.write(
|
|
177
181
|
` ${tui.bold(key)}: ${sizeMB} MB, ${item.contentType}, updated ${date}`
|
|
178
182
|
);
|
|
@@ -58,7 +58,8 @@ export const searchSubcommand = createCommand({
|
|
|
58
58
|
const item = results[key];
|
|
59
59
|
if (!item) continue;
|
|
60
60
|
const sizeMB = (item.size / (1024 * 1024)).toFixed(2);
|
|
61
|
-
const date =
|
|
61
|
+
const date =
|
|
62
|
+
item.lastUsed != null ? new Date(item.lastUsed).toLocaleString() : 'unknown';
|
|
62
63
|
tui.info(` ${tui.bold(key)}: ${sizeMB} MB, ${item.contentType}, updated ${date}`);
|
|
63
64
|
}
|
|
64
65
|
}
|
|
@@ -209,7 +209,7 @@ async function uploadToSandbox(
|
|
|
209
209
|
|
|
210
210
|
async function uploadSingleFile(
|
|
211
211
|
client: APIClient,
|
|
212
|
-
|
|
212
|
+
_logger: Logger,
|
|
213
213
|
orgId: string,
|
|
214
214
|
sandboxId: string,
|
|
215
215
|
resolvedPath: string,
|
|
@@ -327,7 +327,7 @@ async function downloadFromSandbox(
|
|
|
327
327
|
|
|
328
328
|
async function downloadSingleFile(
|
|
329
329
|
client: APIClient,
|
|
330
|
-
|
|
330
|
+
_logger: Logger,
|
|
331
331
|
orgId: string,
|
|
332
332
|
sandboxId: string,
|
|
333
333
|
remotePath: string,
|
|
@@ -18,20 +18,15 @@ import { getResourceInfo, setResourceInfo } from '../../../cache';
|
|
|
18
18
|
function displayConfig(config: BucketConfig) {
|
|
19
19
|
tui.newline();
|
|
20
20
|
console.log(tui.bold('Bucket: ') + config.bucket_name);
|
|
21
|
+
console.log(tui.bold('Storage Tier: ') + (config.storage_tier ?? tui.muted('default')));
|
|
21
22
|
console.log(
|
|
22
|
-
tui.bold('
|
|
23
|
-
);
|
|
24
|
-
console.log(
|
|
25
|
-
tui.bold('TTL: ') +
|
|
26
|
-
(config.ttl != null ? `${config.ttl}s` : tui.muted('default'))
|
|
23
|
+
tui.bold('TTL: ') + (config.ttl != null ? `${config.ttl}s` : tui.muted('default'))
|
|
27
24
|
);
|
|
28
25
|
console.log(
|
|
29
26
|
tui.bold('Public: ') +
|
|
30
27
|
(config.public != null ? String(config.public) : tui.muted('default'))
|
|
31
28
|
);
|
|
32
|
-
console.log(
|
|
33
|
-
tui.bold('Cache Control: ') + (config.cache_control ?? tui.muted('default'))
|
|
34
|
-
);
|
|
29
|
+
console.log(tui.bold('Cache Control: ') + (config.cache_control ?? tui.muted('default')));
|
|
35
30
|
|
|
36
31
|
if (config.cors) {
|
|
37
32
|
console.log(tui.bold('CORS:'));
|
|
@@ -260,15 +260,20 @@ export const listSubcommand = createSubcommand({
|
|
|
260
260
|
if (s3.region) console.log(` Region: ${tui.muted(s3.region)}`);
|
|
261
261
|
if (s3.endpoint) console.log(` Endpoint: ${tui.muted(s3.endpoint)}`);
|
|
262
262
|
if (s3.object_count != null) {
|
|
263
|
-
const sizeStr =
|
|
264
|
-
|
|
263
|
+
const sizeStr =
|
|
264
|
+
s3.total_size != null ? tui.formatBytes(s3.total_size) : 'unknown';
|
|
265
|
+
console.log(
|
|
266
|
+
` Objects: ${tui.muted(`${s3.object_count.toLocaleString()} (${sizeStr})`)}`
|
|
267
|
+
);
|
|
265
268
|
}
|
|
266
269
|
if (s3.last_event_at) {
|
|
267
270
|
const date = new Date(s3.last_event_at);
|
|
268
271
|
if (Number.isNaN(date.getTime())) {
|
|
269
272
|
console.log(` Activity: ${tui.muted('unknown')}`);
|
|
270
273
|
} else {
|
|
271
|
-
console.log(
|
|
274
|
+
console.log(
|
|
275
|
+
` Activity: ${tui.muted(date.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric', hour: '2-digit', minute: '2-digit' }))}`
|
|
276
|
+
);
|
|
272
277
|
}
|
|
273
278
|
}
|
|
274
279
|
tui.newline();
|
|
@@ -5,7 +5,7 @@ import * as tui from '../../../tui';
|
|
|
5
5
|
import { createStorageAdapter, parseMetadataFlag, cacheTaskId } from './util';
|
|
6
6
|
import { getCommand } from '../../../command-prefix';
|
|
7
7
|
import { whoami } from '@agentuity/server';
|
|
8
|
-
import type { TaskPriority, TaskStatus, TaskType } from '@agentuity/core';
|
|
8
|
+
import type { TaskPriority, TaskStatus, TaskType, UserType } from '@agentuity/core';
|
|
9
9
|
import { getCachedUserInfo, setCachedUserInfo } from '../../../cache';
|
|
10
10
|
import { defaultProfileName } from '../../../config';
|
|
11
11
|
|
|
@@ -70,6 +70,10 @@ export const createSubcommand = createCommand({
|
|
|
70
70
|
.min(1)
|
|
71
71
|
.optional()
|
|
72
72
|
.describe('the display name of the creator (used with --created-id)'),
|
|
73
|
+
createdType: z
|
|
74
|
+
.enum(['human', 'agent'])
|
|
75
|
+
.optional()
|
|
76
|
+
.describe('the type of the creator - human user or AI agent (default: human)'),
|
|
73
77
|
projectId: z.string().optional().describe('project ID to associate with the task'),
|
|
74
78
|
projectName: z
|
|
75
79
|
.string()
|
|
@@ -101,18 +105,23 @@ export const createSubcommand = createCommand({
|
|
|
101
105
|
|
|
102
106
|
// Resolve creator info
|
|
103
107
|
const createdId = opts.createdId ?? ctx.auth.userId;
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
creator
|
|
108
|
-
|
|
108
|
+
const createdType = (opts.createdType as UserType) ?? 'human';
|
|
109
|
+
let creator: { id: string; name: string; type?: UserType } | undefined;
|
|
110
|
+
if (opts.createdId) {
|
|
111
|
+
// Explicit creator — use createdId as name fallback (like project pattern)
|
|
112
|
+
creator = {
|
|
113
|
+
id: opts.createdId,
|
|
114
|
+
name: opts.createdName ?? opts.createdId,
|
|
115
|
+
type: createdType,
|
|
116
|
+
};
|
|
117
|
+
} else {
|
|
109
118
|
// Using auth userId — check cache first, then fall back to whoami API call
|
|
110
119
|
const profileName = ctx.config?.name ?? defaultProfileName;
|
|
111
120
|
const cached = getCachedUserInfo(profileName);
|
|
112
121
|
if (cached) {
|
|
113
122
|
const name = [cached.firstName, cached.lastName].filter(Boolean).join(' ');
|
|
114
123
|
if (name) {
|
|
115
|
-
creator = { id: createdId, name };
|
|
124
|
+
creator = { id: createdId, name, type: createdType };
|
|
116
125
|
}
|
|
117
126
|
} else {
|
|
118
127
|
// Fetch from API and cache
|
|
@@ -120,7 +129,7 @@ export const createSubcommand = createCommand({
|
|
|
120
129
|
const user = await whoami(ctx.apiClient);
|
|
121
130
|
const name = [user.firstName, user.lastName].filter(Boolean).join(' ');
|
|
122
131
|
if (name) {
|
|
123
|
-
creator = { id: createdId, name };
|
|
132
|
+
creator = { id: createdId, name, type: createdType };
|
|
124
133
|
}
|
|
125
134
|
setCachedUserInfo(profileName, createdId, user.firstName, user.lastName);
|
|
126
135
|
} catch {
|
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { createCommand } from '../../../types';
|
|
3
|
+
import * as tui from '../../../tui';
|
|
4
|
+
import { createStorageAdapter } from './util';
|
|
5
|
+
import { getCommand } from '../../../command-prefix';
|
|
6
|
+
import { isDryRunMode, outputDryRun } from '../../../explain';
|
|
7
|
+
import type { TaskPriority, TaskStatus, TaskType, BatchDeletedTask } from '@agentuity/core';
|
|
8
|
+
|
|
9
|
+
const DURATION_UNITS: Record<string, number> = {
|
|
10
|
+
s: 1000,
|
|
11
|
+
m: 60 * 1000,
|
|
12
|
+
h: 60 * 60 * 1000,
|
|
13
|
+
d: 24 * 60 * 60 * 1000,
|
|
14
|
+
w: 7 * 24 * 60 * 60 * 1000,
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Parse a human-friendly duration string (e.g. "30s", "7d", "24h", "30m", "2w")
|
|
19
|
+
* into milliseconds. Exported for testing.
|
|
20
|
+
*/
|
|
21
|
+
export function parseDuration(duration: string): number {
|
|
22
|
+
const match = duration.match(/^(\d+)([smhdw])$/);
|
|
23
|
+
if (!match) {
|
|
24
|
+
tui.fatal(
|
|
25
|
+
`Invalid duration format: "${duration}". Use a number followed by s (seconds), m (minutes), h (hours), d (days), or w (weeks). Examples: 30s, 30m, 24h, 7d, 2w`
|
|
26
|
+
);
|
|
27
|
+
// tui.fatal exits, but TypeScript doesn't know that
|
|
28
|
+
throw new Error('unreachable');
|
|
29
|
+
}
|
|
30
|
+
const value = parseInt(match[1]!, 10);
|
|
31
|
+
const unit = match[2]!;
|
|
32
|
+
const ms = DURATION_UNITS[unit];
|
|
33
|
+
if (!ms) {
|
|
34
|
+
tui.fatal(`Unknown duration unit: "${unit}"`);
|
|
35
|
+
throw new Error('unreachable');
|
|
36
|
+
}
|
|
37
|
+
return value * ms;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function truncate(s: string, max: number): string {
|
|
41
|
+
if (s.length <= max) return s;
|
|
42
|
+
return `${s.slice(0, max - 1)}…`;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const TaskDeleteResponseSchema = z.object({
|
|
46
|
+
success: z.boolean().describe('Whether the operation succeeded'),
|
|
47
|
+
deleted: z
|
|
48
|
+
.array(
|
|
49
|
+
z.object({
|
|
50
|
+
id: z.string().describe('Deleted task ID'),
|
|
51
|
+
title: z.string().describe('Deleted task title'),
|
|
52
|
+
})
|
|
53
|
+
)
|
|
54
|
+
.describe('List of deleted tasks'),
|
|
55
|
+
count: z.number().describe('Number of tasks deleted'),
|
|
56
|
+
durationMs: z.number().describe('Operation duration in milliseconds'),
|
|
57
|
+
dryRun: z.boolean().optional().describe('Whether this was a dry run'),
|
|
58
|
+
message: z.string().optional().describe('Status message'),
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
export const deleteSubcommand = createCommand({
|
|
62
|
+
name: 'delete',
|
|
63
|
+
aliases: ['del', 'rm'],
|
|
64
|
+
description: 'Soft-delete a task by ID or batch-delete tasks by filter',
|
|
65
|
+
tags: ['destructive', 'deletes-resource', 'slow', 'requires-auth'],
|
|
66
|
+
requires: { auth: true },
|
|
67
|
+
examples: [
|
|
68
|
+
{
|
|
69
|
+
command: getCommand('cloud task delete task_abc123'),
|
|
70
|
+
description: 'Delete a single task by ID',
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
command: getCommand('cloud task delete --status closed --older-than 7d'),
|
|
74
|
+
description: 'Delete closed tasks older than 7 days',
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
command: getCommand('cloud task delete --status done --limit 10 --dry-run'),
|
|
78
|
+
description: 'Preview which done tasks would be deleted (dry run)',
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
command: getCommand('cloud task delete --status cancelled --confirm'),
|
|
82
|
+
description: 'Delete all cancelled tasks without confirmation prompt',
|
|
83
|
+
},
|
|
84
|
+
],
|
|
85
|
+
schema: {
|
|
86
|
+
args: z.object({
|
|
87
|
+
id: z.string().optional().describe('Task ID to delete (for single delete)'),
|
|
88
|
+
}),
|
|
89
|
+
options: z.object({
|
|
90
|
+
status: z
|
|
91
|
+
.enum(['open', 'in_progress', 'done', 'closed', 'cancelled'])
|
|
92
|
+
.optional()
|
|
93
|
+
.describe('filter batch delete by status'),
|
|
94
|
+
type: z
|
|
95
|
+
.enum(['epic', 'feature', 'enhancement', 'bug', 'task'])
|
|
96
|
+
.optional()
|
|
97
|
+
.describe('filter batch delete by type'),
|
|
98
|
+
priority: z
|
|
99
|
+
.enum(['high', 'medium', 'low', 'none'])
|
|
100
|
+
.optional()
|
|
101
|
+
.describe('filter batch delete by priority'),
|
|
102
|
+
olderThan: z
|
|
103
|
+
.string()
|
|
104
|
+
.optional()
|
|
105
|
+
.describe('filter batch delete by age (e.g. 30s, 7d, 24h, 2w)'),
|
|
106
|
+
parentId: z.string().optional().describe('filter batch delete by parent task ID'),
|
|
107
|
+
createdId: z.string().optional().describe('filter batch delete by creator ID'),
|
|
108
|
+
limit: z.coerce
|
|
109
|
+
.number()
|
|
110
|
+
.int()
|
|
111
|
+
.min(1)
|
|
112
|
+
.max(200)
|
|
113
|
+
.default(50)
|
|
114
|
+
.describe('max tasks to delete in batch mode (default: 50, max: 200)'),
|
|
115
|
+
confirm: z.boolean().optional().default(false).describe('skip confirmation prompt'),
|
|
116
|
+
}),
|
|
117
|
+
response: TaskDeleteResponseSchema,
|
|
118
|
+
},
|
|
119
|
+
|
|
120
|
+
async handler(ctx) {
|
|
121
|
+
const { args, opts, options } = ctx;
|
|
122
|
+
const started = Date.now();
|
|
123
|
+
const storage = await createStorageAdapter(ctx);
|
|
124
|
+
|
|
125
|
+
// Determine mode: single delete or batch delete
|
|
126
|
+
const isSingleDelete = !!args.id;
|
|
127
|
+
const hasFilters =
|
|
128
|
+
opts.status ||
|
|
129
|
+
opts.type ||
|
|
130
|
+
opts.priority ||
|
|
131
|
+
opts.olderThan ||
|
|
132
|
+
opts.parentId ||
|
|
133
|
+
opts.createdId;
|
|
134
|
+
|
|
135
|
+
if (!isSingleDelete && !hasFilters) {
|
|
136
|
+
tui.fatal(
|
|
137
|
+
'Provide a task ID for single delete, or use --status, --type, --priority, --older-than, --parent-id, or --created-id for batch delete.'
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (isSingleDelete && hasFilters) {
|
|
142
|
+
tui.fatal(
|
|
143
|
+
'Cannot combine task ID with filter options. Use either single delete (by ID) or batch delete (by filters).'
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// ── Single delete mode ──────────────────────────────────────────────
|
|
148
|
+
if (isSingleDelete) {
|
|
149
|
+
if (isDryRunMode(options)) {
|
|
150
|
+
outputDryRun(`Would soft-delete task: ${args.id}`, options);
|
|
151
|
+
return {
|
|
152
|
+
success: true,
|
|
153
|
+
deleted: [{ id: args.id!, title: '(dry run)' }],
|
|
154
|
+
count: 1,
|
|
155
|
+
durationMs: Date.now() - started,
|
|
156
|
+
dryRun: true,
|
|
157
|
+
message: 'Dry run — no tasks were deleted',
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if (!opts.confirm) {
|
|
162
|
+
const confirmed = await tui.confirm(`Delete task "${args.id}"?`, false);
|
|
163
|
+
if (!confirmed) {
|
|
164
|
+
if (!options.json) tui.info('Cancelled');
|
|
165
|
+
return {
|
|
166
|
+
success: false,
|
|
167
|
+
deleted: [],
|
|
168
|
+
count: 0,
|
|
169
|
+
durationMs: Date.now() - started,
|
|
170
|
+
message: 'Cancelled',
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const task = await storage.softDelete(args.id!);
|
|
176
|
+
const durationMs = Date.now() - started;
|
|
177
|
+
|
|
178
|
+
if (!options.json) {
|
|
179
|
+
tui.success(`Deleted task ${tui.bold(task.id)} (${task.title}) in ${durationMs}ms`);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
return {
|
|
183
|
+
success: true,
|
|
184
|
+
deleted: [{ id: task.id, title: task.title }],
|
|
185
|
+
count: 1,
|
|
186
|
+
durationMs,
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// ── Batch delete mode ───────────────────────────────────────────────
|
|
191
|
+
// Validate older-than format early (before calling the API)
|
|
192
|
+
if (opts.olderThan) {
|
|
193
|
+
parseDuration(opts.olderThan); // will fatal on invalid format
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const batchParams = {
|
|
197
|
+
status: opts.status as TaskStatus | undefined,
|
|
198
|
+
type: opts.type as TaskType | undefined,
|
|
199
|
+
priority: opts.priority as TaskPriority | undefined,
|
|
200
|
+
parent_id: opts.parentId,
|
|
201
|
+
created_id: opts.createdId,
|
|
202
|
+
older_than: opts.olderThan,
|
|
203
|
+
limit: opts.limit,
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
// For dry-run and preview, first list what would be matched
|
|
207
|
+
// (we call batchDelete only when actually executing)
|
|
208
|
+
if (isDryRunMode(options) || !opts.confirm) {
|
|
209
|
+
// Use list() to preview matching tasks
|
|
210
|
+
const preview = await storage.list({
|
|
211
|
+
status: batchParams.status,
|
|
212
|
+
type: batchParams.type,
|
|
213
|
+
priority: batchParams.priority,
|
|
214
|
+
parent_id: batchParams.parent_id,
|
|
215
|
+
limit: batchParams.limit,
|
|
216
|
+
sort: 'created_at',
|
|
217
|
+
order: 'asc',
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
// Client-side filters for preview (server will apply these on actual delete)
|
|
221
|
+
let candidates = preview.tasks;
|
|
222
|
+
if (batchParams.created_id) {
|
|
223
|
+
candidates = candidates.filter(
|
|
224
|
+
(t: { created_id: string }) => t.created_id === batchParams.created_id
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
if (opts.olderThan) {
|
|
228
|
+
const durationMs = parseDuration(opts.olderThan);
|
|
229
|
+
const cutoff = new Date(Date.now() - durationMs);
|
|
230
|
+
candidates = candidates.filter(
|
|
231
|
+
(t: { created_at: string }) => new Date(t.created_at) < cutoff
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
if (candidates.length === 0) {
|
|
236
|
+
if (!options.json) tui.info('No tasks match the given filters');
|
|
237
|
+
return {
|
|
238
|
+
success: true,
|
|
239
|
+
deleted: [],
|
|
240
|
+
count: 0,
|
|
241
|
+
durationMs: Date.now() - started,
|
|
242
|
+
message: 'No matching tasks found',
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// Show preview table
|
|
247
|
+
if (!options.json) {
|
|
248
|
+
tui.warning(
|
|
249
|
+
`Found ${candidates.length} ${tui.plural(candidates.length, 'task', 'tasks')} to delete:`
|
|
250
|
+
);
|
|
251
|
+
tui.newline();
|
|
252
|
+
|
|
253
|
+
const tableData = candidates.map(
|
|
254
|
+
(task: {
|
|
255
|
+
id: string;
|
|
256
|
+
title: string;
|
|
257
|
+
status: string;
|
|
258
|
+
type: string;
|
|
259
|
+
created_at: string;
|
|
260
|
+
}) => ({
|
|
261
|
+
ID: tui.muted(truncate(task.id, 28)),
|
|
262
|
+
Title: truncate(task.title, 40),
|
|
263
|
+
Status: task.status,
|
|
264
|
+
Type: task.type,
|
|
265
|
+
Created: new Date(task.created_at).toLocaleDateString(),
|
|
266
|
+
})
|
|
267
|
+
);
|
|
268
|
+
|
|
269
|
+
tui.table(tableData, [
|
|
270
|
+
{ name: 'ID', alignment: 'left' },
|
|
271
|
+
{ name: 'Title', alignment: 'left' },
|
|
272
|
+
{ name: 'Status', alignment: 'left' },
|
|
273
|
+
{ name: 'Type', alignment: 'left' },
|
|
274
|
+
{ name: 'Created', alignment: 'left' },
|
|
275
|
+
]);
|
|
276
|
+
tui.newline();
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// Dry-run: return preview without executing
|
|
280
|
+
if (isDryRunMode(options)) {
|
|
281
|
+
outputDryRun(
|
|
282
|
+
`Would soft-delete ${candidates.length} ${tui.plural(candidates.length, 'task', 'tasks')}`,
|
|
283
|
+
options
|
|
284
|
+
);
|
|
285
|
+
return {
|
|
286
|
+
success: true,
|
|
287
|
+
deleted: candidates.map(
|
|
288
|
+
(t: { id: string; title: string }): BatchDeletedTask => ({
|
|
289
|
+
id: t.id,
|
|
290
|
+
title: t.title,
|
|
291
|
+
})
|
|
292
|
+
),
|
|
293
|
+
count: candidates.length,
|
|
294
|
+
durationMs: Date.now() - started,
|
|
295
|
+
dryRun: true,
|
|
296
|
+
message: 'Dry run — no tasks were deleted',
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// Confirmation prompt
|
|
301
|
+
if (!opts.confirm) {
|
|
302
|
+
const confirmed = await tui.confirm(
|
|
303
|
+
`Delete ${candidates.length} ${tui.plural(candidates.length, 'task', 'tasks')}?`,
|
|
304
|
+
false
|
|
305
|
+
);
|
|
306
|
+
if (!confirmed) {
|
|
307
|
+
if (!options.json) tui.info('Cancelled');
|
|
308
|
+
return {
|
|
309
|
+
success: false,
|
|
310
|
+
deleted: [],
|
|
311
|
+
count: 0,
|
|
312
|
+
durationMs: Date.now() - started,
|
|
313
|
+
message: 'Cancelled',
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// Execute batch delete via server-side API
|
|
320
|
+
const result = await storage.batchDelete(batchParams);
|
|
321
|
+
const durationMs = Date.now() - started;
|
|
322
|
+
|
|
323
|
+
if (!options.json) {
|
|
324
|
+
if (result.count > 0) {
|
|
325
|
+
tui.success(
|
|
326
|
+
`Deleted ${result.count} ${tui.plural(result.count, 'task', 'tasks')} in ${durationMs}ms`
|
|
327
|
+
);
|
|
328
|
+
} else {
|
|
329
|
+
tui.info('No tasks matched the given filters');
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
return {
|
|
334
|
+
success: true,
|
|
335
|
+
deleted: result.deleted,
|
|
336
|
+
count: result.count,
|
|
337
|
+
durationMs,
|
|
338
|
+
};
|
|
339
|
+
},
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
export default deleteSubcommand;
|