@agentuity/cli 1.0.37 β 1.0.39
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/cmd/build/vite/bundle-files.d.ts +12 -0
- package/dist/cmd/build/vite/bundle-files.d.ts.map +1 -0
- package/dist/cmd/build/vite/bundle-files.js +107 -0
- package/dist/cmd/build/vite/bundle-files.js.map +1 -0
- package/dist/cmd/build/vite/vite-builder.d.ts.map +1 -1
- package/dist/cmd/build/vite/vite-builder.js +9 -0
- package/dist/cmd/build/vite/vite-builder.js.map +1 -1
- package/dist/cmd/cloud/task/create.js +1 -1
- package/dist/cmd/cloud/task/create.js.map +1 -1
- package/dist/cmd/cloud/task/delete.js +3 -3
- 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 +14 -2
- 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 +1 -2
- package/dist/cmd/cloud/task/list.js.map +1 -1
- package/dist/cmd/cloud/task/project.d.ts +2 -0
- package/dist/cmd/cloud/task/project.d.ts.map +1 -0
- package/dist/cmd/cloud/task/project.js +206 -0
- package/dist/cmd/cloud/task/project.js.map +1 -0
- package/dist/cmd/cloud/task/update.d.ts.map +1 -1
- package/dist/cmd/cloud/task/update.js +4 -1
- package/dist/cmd/cloud/task/update.js.map +1 -1
- package/dist/cmd/cloud/task/user.d.ts +2 -0
- package/dist/cmd/cloud/task/user.d.ts.map +1 -0
- package/dist/cmd/cloud/task/user.js +179 -0
- package/dist/cmd/cloud/task/user.js.map +1 -0
- package/dist/types.d.ts +9 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +6 -6
- package/src/cmd/build/vite/bundle-files.ts +135 -0
- package/src/cmd/build/vite/vite-builder.ts +11 -0
- package/src/cmd/cloud/task/create.ts +1 -1
- package/src/cmd/cloud/task/delete.ts +3 -3
- package/src/cmd/cloud/task/index.ts +14 -2
- package/src/cmd/cloud/task/list.ts +1 -2
- package/src/cmd/cloud/task/project.ts +227 -0
- package/src/cmd/cloud/task/update.ts +4 -1
- package/src/cmd/cloud/task/user.ts +202 -0
- package/src/types.ts +9 -0
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { join, dirname } from 'node:path';
|
|
2
|
+
import { mkdirSync, cpSync } from 'node:fs';
|
|
3
|
+
import type { Logger } from '../../../types';
|
|
4
|
+
|
|
5
|
+
/** Paths that are always excluded regardless of .gitignore */
|
|
6
|
+
function isHardExcluded(match: string): boolean {
|
|
7
|
+
return (
|
|
8
|
+
match.startsWith('.agentuity/') ||
|
|
9
|
+
match.startsWith('.agentuity\\') ||
|
|
10
|
+
match.startsWith('node_modules/') ||
|
|
11
|
+
match.startsWith('node_modules\\') ||
|
|
12
|
+
match.startsWith('.git/') ||
|
|
13
|
+
match.startsWith('.git\\') ||
|
|
14
|
+
match === '.env' ||
|
|
15
|
+
match.startsWith('.env.')
|
|
16
|
+
);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Use `git check-ignore --stdin` to filter out files that are ignored by .gitignore.
|
|
21
|
+
* Returns the subset of `files` that are NOT gitignored.
|
|
22
|
+
* Falls back to returning all files if not in a git repo or git is unavailable.
|
|
23
|
+
*/
|
|
24
|
+
async function filterGitIgnored(
|
|
25
|
+
rootDir: string,
|
|
26
|
+
files: string[],
|
|
27
|
+
logger: Logger
|
|
28
|
+
): Promise<string[]> {
|
|
29
|
+
if (files.length === 0) return files;
|
|
30
|
+
|
|
31
|
+
try {
|
|
32
|
+
// Check if we're in a git repo
|
|
33
|
+
const gitCheck = Bun.spawnSync(['git', 'rev-parse', '--git-dir'], {
|
|
34
|
+
cwd: rootDir,
|
|
35
|
+
stderr: 'pipe',
|
|
36
|
+
});
|
|
37
|
+
if (gitCheck.exitCode !== 0) {
|
|
38
|
+
logger.debug('Not a git repository, skipping .gitignore filtering');
|
|
39
|
+
return files;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Use git check-ignore to find which files are ignored
|
|
43
|
+
const proc = Bun.spawn(['git', 'check-ignore', '--stdin'], {
|
|
44
|
+
cwd: rootDir,
|
|
45
|
+
stdin: 'pipe',
|
|
46
|
+
stdout: 'pipe',
|
|
47
|
+
stderr: 'pipe',
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
// Write all file paths to stdin, one per line
|
|
51
|
+
proc.stdin.write(files.join('\n'));
|
|
52
|
+
proc.stdin.end();
|
|
53
|
+
|
|
54
|
+
const output = await new Response(proc.stdout).text();
|
|
55
|
+
await proc.exited;
|
|
56
|
+
|
|
57
|
+
// git check-ignore exits 0 if some files are ignored, 1 if none are ignored.
|
|
58
|
+
// Both are fine. Other exit codes mean an error.
|
|
59
|
+
|
|
60
|
+
const ignoredFiles = output
|
|
61
|
+
.split('\n')
|
|
62
|
+
.map((line) => line.trim())
|
|
63
|
+
.filter(Boolean);
|
|
64
|
+
const ignoredSet = new Set(ignoredFiles);
|
|
65
|
+
|
|
66
|
+
if (ignoredSet.size > 0) {
|
|
67
|
+
logger.debug(`Filtered ${ignoredSet.size} gitignored file(s) from bundle`);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return files.filter((f) => !ignoredSet.has(f));
|
|
71
|
+
} catch {
|
|
72
|
+
logger.debug('git not available, skipping .gitignore filtering');
|
|
73
|
+
return files;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Copy files matching glob patterns into the build output directory.
|
|
79
|
+
* Files are copied preserving their relative directory structure from the project root.
|
|
80
|
+
* This runs BEFORE the build steps so that build output can overwrite any conflicts.
|
|
81
|
+
*
|
|
82
|
+
* Filtering layers:
|
|
83
|
+
* 1. Hard exclusions: .agentuity/, node_modules/, .git/, .env* (always skipped)
|
|
84
|
+
* 2. .gitignore: files ignored by git are skipped (falls back if not a git repo)
|
|
85
|
+
*/
|
|
86
|
+
export async function copyBundleFiles(
|
|
87
|
+
rootDir: string,
|
|
88
|
+
outDir: string,
|
|
89
|
+
patterns: string[],
|
|
90
|
+
logger: Logger
|
|
91
|
+
): Promise<number> {
|
|
92
|
+
let totalCopied = 0;
|
|
93
|
+
|
|
94
|
+
// Ensure output directory exists
|
|
95
|
+
mkdirSync(outDir, { recursive: true });
|
|
96
|
+
|
|
97
|
+
for (const pattern of patterns) {
|
|
98
|
+
const glob = new Bun.Glob(pattern);
|
|
99
|
+
const candidates: string[] = [];
|
|
100
|
+
|
|
101
|
+
// Phase 1: Glob match + hard exclusions
|
|
102
|
+
for await (const match of glob.scan({ cwd: rootDir, onlyFiles: true })) {
|
|
103
|
+
if (!isHardExcluded(match)) {
|
|
104
|
+
candidates.push(match);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Phase 2: Filter out gitignored files
|
|
109
|
+
const filesToCopy = await filterGitIgnored(rootDir, candidates, logger);
|
|
110
|
+
|
|
111
|
+
// Phase 3: Copy files
|
|
112
|
+
for (const match of filesToCopy) {
|
|
113
|
+
const src = join(rootDir, match);
|
|
114
|
+
const dest = join(outDir, match);
|
|
115
|
+
try {
|
|
116
|
+
mkdirSync(dirname(dest), { recursive: true });
|
|
117
|
+
cpSync(src, dest);
|
|
118
|
+
} catch (err) {
|
|
119
|
+
throw new Error(
|
|
120
|
+
`Failed to copy bundle file '${match}' (pattern '${pattern}'): ${(err as Error).message}`
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (filesToCopy.length === 0) {
|
|
126
|
+
logger.warn(`Bundle pattern '${pattern}' matched no files`);
|
|
127
|
+
} else {
|
|
128
|
+
logger.debug(`Bundle pattern '${pattern}': ${filesToCopy.length} file(s)`);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
totalCopied += filesToCopy.length;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return totalCopied;
|
|
135
|
+
}
|
|
@@ -325,6 +325,17 @@ export async function runAllBuilds(options: Omit<ViteBuildOptions, 'mode'>): Pro
|
|
|
325
325
|
// Load config to check if workbench is enabled (dev mode only)
|
|
326
326
|
const { loadAgentuityConfig, getWorkbenchConfig } = await import('./config-loader');
|
|
327
327
|
const config = await loadAgentuityConfig(rootDir, logger);
|
|
328
|
+
|
|
329
|
+
// Copy bundle files if configured (before build so build output takes priority)
|
|
330
|
+
if (config?.bundle?.length) {
|
|
331
|
+
const { copyBundleFiles } = await import('./bundle-files');
|
|
332
|
+
const outDir = join(rootDir, '.agentuity');
|
|
333
|
+
const count = await copyBundleFiles(rootDir, outDir, config.bundle, logger);
|
|
334
|
+
if (count > 0) {
|
|
335
|
+
logger.debug(`Copied ${count} bundle file(s) to .agentuity`);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
328
339
|
const workbenchConfig = getWorkbenchConfig(config, dev);
|
|
329
340
|
// Generate workbench files BEFORE any builds if enabled (dev mode only)
|
|
330
341
|
if (workbenchConfig.enabled) {
|
|
@@ -95,7 +95,7 @@ export const createSubcommand = createCommand({
|
|
|
95
95
|
.optional()
|
|
96
96
|
.describe('task priority (default: none)'),
|
|
97
97
|
status: z
|
|
98
|
-
.enum(['open', 'in_progress', '
|
|
98
|
+
.enum(['open', 'in_progress', 'done', 'cancelled'])
|
|
99
99
|
.optional()
|
|
100
100
|
.describe('initial task status (default: open)'),
|
|
101
101
|
parentId: z.string().optional().describe('parent task ID for subtasks'),
|
|
@@ -70,8 +70,8 @@ export const deleteSubcommand = createCommand({
|
|
|
70
70
|
description: 'Delete a single task by ID',
|
|
71
71
|
},
|
|
72
72
|
{
|
|
73
|
-
command: getCommand('cloud task delete --status
|
|
74
|
-
description: 'Delete
|
|
73
|
+
command: getCommand('cloud task delete --status done --older-than 7d'),
|
|
74
|
+
description: 'Delete done tasks older than 7 days',
|
|
75
75
|
},
|
|
76
76
|
{
|
|
77
77
|
command: getCommand('cloud task delete --status done --limit 10 --dry-run'),
|
|
@@ -88,7 +88,7 @@ export const deleteSubcommand = createCommand({
|
|
|
88
88
|
}),
|
|
89
89
|
options: z.object({
|
|
90
90
|
status: z
|
|
91
|
-
.enum(['open', 'in_progress', 'done', '
|
|
91
|
+
.enum(['open', 'in_progress', 'done', 'cancelled'])
|
|
92
92
|
.optional()
|
|
93
93
|
.describe('filter batch delete by status'),
|
|
94
94
|
type: z
|
|
@@ -6,6 +6,8 @@ import { listSubcommand } from './list';
|
|
|
6
6
|
import { deleteSubcommand } from './delete';
|
|
7
7
|
import { statsSubcommand } from './stats';
|
|
8
8
|
import { attachmentSubcommand } from './attachment';
|
|
9
|
+
import { userSubcommand } from './user';
|
|
10
|
+
import { projectSubcommand } from './project';
|
|
9
11
|
import { getCommand } from '../../../command-prefix';
|
|
10
12
|
|
|
11
13
|
export const taskCommand = createCommand({
|
|
@@ -34,13 +36,21 @@ export const taskCommand = createCommand({
|
|
|
34
36
|
description: 'Delete a task by ID',
|
|
35
37
|
},
|
|
36
38
|
{
|
|
37
|
-
command: getCommand('cloud task delete --status
|
|
38
|
-
description: 'Batch delete
|
|
39
|
+
command: getCommand('cloud task delete --status done --older-than 7d'),
|
|
40
|
+
description: 'Batch delete done tasks older than 7 days',
|
|
39
41
|
},
|
|
40
42
|
{
|
|
41
43
|
command: getCommand('cloud task attachment upload task_abc123 ./report.pdf'),
|
|
42
44
|
description: 'Upload a file attachment to a task',
|
|
43
45
|
},
|
|
46
|
+
{
|
|
47
|
+
command: getCommand('cloud task user list'),
|
|
48
|
+
description: 'List task users',
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
command: getCommand('cloud task project list'),
|
|
52
|
+
description: 'List task projects',
|
|
53
|
+
},
|
|
44
54
|
],
|
|
45
55
|
subcommands: [
|
|
46
56
|
getSubcommand,
|
|
@@ -50,6 +60,8 @@ export const taskCommand = createCommand({
|
|
|
50
60
|
deleteSubcommand,
|
|
51
61
|
statsSubcommand,
|
|
52
62
|
attachmentSubcommand,
|
|
63
|
+
userSubcommand,
|
|
64
|
+
projectSubcommand,
|
|
53
65
|
],
|
|
54
66
|
requires: { auth: true },
|
|
55
67
|
});
|
|
@@ -55,7 +55,6 @@ const STATUS_COLORS: Record<string, (s: string) => string> = {
|
|
|
55
55
|
open: tui.colorSuccess,
|
|
56
56
|
in_progress: tui.colorWarning,
|
|
57
57
|
done: tui.colorInfo,
|
|
58
|
-
closed: tui.muted,
|
|
59
58
|
cancelled: tui.muted,
|
|
60
59
|
};
|
|
61
60
|
|
|
@@ -108,7 +107,7 @@ export const listSubcommand = createCommand({
|
|
|
108
107
|
schema: {
|
|
109
108
|
options: z.object({
|
|
110
109
|
status: z
|
|
111
|
-
.enum(['open', 'in_progress', 'done', '
|
|
110
|
+
.enum(['open', 'in_progress', 'done', 'cancelled'])
|
|
112
111
|
.optional()
|
|
113
112
|
.describe('filter by status'),
|
|
114
113
|
type: z
|
|
@@ -0,0 +1,227 @@
|
|
|
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
|
+
|
|
7
|
+
// ββ List ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
8
|
+
|
|
9
|
+
const listProjectsSubcommand = createCommand({
|
|
10
|
+
name: 'list',
|
|
11
|
+
aliases: ['ls'],
|
|
12
|
+
description: 'List all task projects',
|
|
13
|
+
tags: ['read-only', 'slow', 'requires-auth'],
|
|
14
|
+
requires: { auth: true },
|
|
15
|
+
idempotent: true,
|
|
16
|
+
examples: [
|
|
17
|
+
{
|
|
18
|
+
command: getCommand('cloud task project list'),
|
|
19
|
+
description: 'List all projects',
|
|
20
|
+
},
|
|
21
|
+
],
|
|
22
|
+
schema: {
|
|
23
|
+
response: z.object({
|
|
24
|
+
success: z.boolean(),
|
|
25
|
+
projects: z.array(z.object({ id: z.string(), name: z.string() })),
|
|
26
|
+
total: z.number(),
|
|
27
|
+
durationMs: z.number(),
|
|
28
|
+
}),
|
|
29
|
+
},
|
|
30
|
+
|
|
31
|
+
async handler(ctx) {
|
|
32
|
+
const { options } = ctx;
|
|
33
|
+
const started = Date.now();
|
|
34
|
+
const storage = await createStorageAdapter(ctx);
|
|
35
|
+
const result = await storage.listProjects();
|
|
36
|
+
const durationMs = Date.now() - started;
|
|
37
|
+
|
|
38
|
+
if (!options.json) {
|
|
39
|
+
if (result.projects.length === 0) {
|
|
40
|
+
tui.info('No projects found.');
|
|
41
|
+
} else {
|
|
42
|
+
tui.table(
|
|
43
|
+
result.projects.map((p) => ({ id: p.id, name: p.name })),
|
|
44
|
+
['id', 'name']
|
|
45
|
+
);
|
|
46
|
+
tui.info(`${result.projects.length} project(s) (${durationMs.toFixed(1)}ms)`);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return {
|
|
51
|
+
success: true,
|
|
52
|
+
projects: result.projects,
|
|
53
|
+
total: result.projects.length,
|
|
54
|
+
durationMs,
|
|
55
|
+
};
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
// ββ Create ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
60
|
+
|
|
61
|
+
const createProjectSubcommand = createCommand({
|
|
62
|
+
name: 'create',
|
|
63
|
+
description: 'Create a new task project',
|
|
64
|
+
tags: ['mutating', 'slow', 'requires-auth'],
|
|
65
|
+
requires: { auth: true },
|
|
66
|
+
examples: [
|
|
67
|
+
{
|
|
68
|
+
command: getCommand('cloud task project create "My Project"'),
|
|
69
|
+
description: 'Create a new project',
|
|
70
|
+
},
|
|
71
|
+
],
|
|
72
|
+
schema: {
|
|
73
|
+
args: z.object({
|
|
74
|
+
name: z.string().min(1).describe('the project name'),
|
|
75
|
+
}),
|
|
76
|
+
response: z.object({
|
|
77
|
+
success: z.boolean(),
|
|
78
|
+
project: z.object({ id: z.string(), name: z.string() }),
|
|
79
|
+
durationMs: z.number(),
|
|
80
|
+
}),
|
|
81
|
+
},
|
|
82
|
+
|
|
83
|
+
async handler(ctx) {
|
|
84
|
+
const { args, options } = ctx;
|
|
85
|
+
const started = Date.now();
|
|
86
|
+
const storage = await createStorageAdapter(ctx);
|
|
87
|
+
const project = await storage.createProject({ name: args.name });
|
|
88
|
+
const durationMs = Date.now() - started;
|
|
89
|
+
|
|
90
|
+
if (!options.json) {
|
|
91
|
+
tui.success(`Created project: ${project.name} (${project.id})`);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return { success: true, project, durationMs };
|
|
95
|
+
},
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
// ββ Get βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
99
|
+
|
|
100
|
+
const getProjectSubcommand = createCommand({
|
|
101
|
+
name: 'get',
|
|
102
|
+
description: 'Get a task project by ID',
|
|
103
|
+
tags: ['read-only', 'slow', 'requires-auth'],
|
|
104
|
+
requires: { auth: true },
|
|
105
|
+
idempotent: true,
|
|
106
|
+
examples: [
|
|
107
|
+
{
|
|
108
|
+
command: getCommand('cloud task project get prj_abc123'),
|
|
109
|
+
description: 'Get project details',
|
|
110
|
+
},
|
|
111
|
+
],
|
|
112
|
+
schema: {
|
|
113
|
+
args: z.object({
|
|
114
|
+
id: z.string().min(1).describe('the project ID'),
|
|
115
|
+
}),
|
|
116
|
+
response: z.object({
|
|
117
|
+
success: z.boolean(),
|
|
118
|
+
project: z.object({ id: z.string(), name: z.string() }),
|
|
119
|
+
durationMs: z.number(),
|
|
120
|
+
}),
|
|
121
|
+
},
|
|
122
|
+
|
|
123
|
+
async handler(ctx) {
|
|
124
|
+
const { args, options } = ctx;
|
|
125
|
+
const started = Date.now();
|
|
126
|
+
const storage = await createStorageAdapter(ctx);
|
|
127
|
+
try {
|
|
128
|
+
const project = await storage.getProject(args.id);
|
|
129
|
+
const durationMs = Date.now() - started;
|
|
130
|
+
|
|
131
|
+
if (!options.json) {
|
|
132
|
+
tui.table([{ id: project.id, name: project.name }], ['id', 'name'], {
|
|
133
|
+
layout: 'vertical',
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return { success: true, project, durationMs };
|
|
138
|
+
} catch (_err) {
|
|
139
|
+
const durationMs = Date.now() - started;
|
|
140
|
+
if (!options.json) {
|
|
141
|
+
tui.error(`Project not found: ${args.id}`);
|
|
142
|
+
}
|
|
143
|
+
return { success: false, project: { id: args.id, name: '' }, durationMs };
|
|
144
|
+
}
|
|
145
|
+
},
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
// ββ Delete ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
149
|
+
|
|
150
|
+
const deleteProjectSubcommand = createCommand({
|
|
151
|
+
name: 'delete',
|
|
152
|
+
aliases: ['del', 'rm'],
|
|
153
|
+
description: 'Delete a task project',
|
|
154
|
+
tags: ['mutating', 'slow', 'requires-auth', 'destructive'],
|
|
155
|
+
requires: { auth: true },
|
|
156
|
+
examples: [
|
|
157
|
+
{
|
|
158
|
+
command: getCommand('cloud task project delete prj_abc123'),
|
|
159
|
+
description: 'Delete a project',
|
|
160
|
+
},
|
|
161
|
+
],
|
|
162
|
+
schema: {
|
|
163
|
+
args: z.object({
|
|
164
|
+
id: z.string().min(1).describe('the project ID'),
|
|
165
|
+
}),
|
|
166
|
+
response: z.object({
|
|
167
|
+
success: z.boolean(),
|
|
168
|
+
durationMs: z.number(),
|
|
169
|
+
}),
|
|
170
|
+
},
|
|
171
|
+
|
|
172
|
+
async handler(ctx) {
|
|
173
|
+
const { args, options } = ctx;
|
|
174
|
+
const started = Date.now();
|
|
175
|
+
const storage = await createStorageAdapter(ctx);
|
|
176
|
+
try {
|
|
177
|
+
await storage.deleteProject(args.id);
|
|
178
|
+
const durationMs = Date.now() - started;
|
|
179
|
+
|
|
180
|
+
if (!options.json) {
|
|
181
|
+
tui.success(`Deleted project: ${args.id}`);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
return { success: true, durationMs };
|
|
185
|
+
} catch (_err) {
|
|
186
|
+
const durationMs = Date.now() - started;
|
|
187
|
+
if (!options.json) {
|
|
188
|
+
tui.error(`Failed to delete project: ${args.id}`);
|
|
189
|
+
}
|
|
190
|
+
return { success: false, durationMs };
|
|
191
|
+
}
|
|
192
|
+
},
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
// ββ Parent command ββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
196
|
+
|
|
197
|
+
export const projectSubcommand = createCommand({
|
|
198
|
+
name: 'project',
|
|
199
|
+
aliases: ['proj'],
|
|
200
|
+
description: 'Manage task projects',
|
|
201
|
+
tags: ['requires-auth'],
|
|
202
|
+
requires: { auth: true },
|
|
203
|
+
examples: [
|
|
204
|
+
{
|
|
205
|
+
command: getCommand('cloud task project list'),
|
|
206
|
+
description: 'List all projects',
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
command: getCommand('cloud task project create "My Project"'),
|
|
210
|
+
description: 'Create a new project',
|
|
211
|
+
},
|
|
212
|
+
{
|
|
213
|
+
command: getCommand('cloud task project get prj_abc123'),
|
|
214
|
+
description: 'Get project details',
|
|
215
|
+
},
|
|
216
|
+
{
|
|
217
|
+
command: getCommand('cloud task project delete prj_abc123'),
|
|
218
|
+
description: 'Delete a project',
|
|
219
|
+
},
|
|
220
|
+
],
|
|
221
|
+
subcommands: [
|
|
222
|
+
listProjectsSubcommand,
|
|
223
|
+
createProjectSubcommand,
|
|
224
|
+
getProjectSubcommand,
|
|
225
|
+
deleteProjectSubcommand,
|
|
226
|
+
],
|
|
227
|
+
});
|
|
@@ -55,7 +55,10 @@ export const updateSubcommand = createCommand({
|
|
|
55
55
|
.enum(['epic', 'feature', 'enhancement', 'bug', 'task'])
|
|
56
56
|
.optional()
|
|
57
57
|
.describe('new task type'),
|
|
58
|
-
status: z
|
|
58
|
+
status: z
|
|
59
|
+
.enum(['open', 'in_progress', 'done', 'cancelled'])
|
|
60
|
+
.optional()
|
|
61
|
+
.describe('new task status'),
|
|
59
62
|
assignedId: z.string().optional().describe('new assigned agent or user ID'),
|
|
60
63
|
parentId: z.string().optional().describe('new parent task ID'),
|
|
61
64
|
closedId: z.string().optional().describe('ID of the closer (agent or user)'),
|
|
@@ -0,0 +1,202 @@
|
|
|
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
|
+
|
|
7
|
+
// ββ List ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
8
|
+
|
|
9
|
+
const listUsersSubcommand = createCommand({
|
|
10
|
+
name: 'list',
|
|
11
|
+
aliases: ['ls'],
|
|
12
|
+
description: 'List all task users',
|
|
13
|
+
tags: ['read-only', 'slow', 'requires-auth'],
|
|
14
|
+
requires: { auth: true },
|
|
15
|
+
idempotent: true,
|
|
16
|
+
examples: [
|
|
17
|
+
{
|
|
18
|
+
command: getCommand('cloud task user list'),
|
|
19
|
+
description: 'List all users',
|
|
20
|
+
},
|
|
21
|
+
],
|
|
22
|
+
schema: {
|
|
23
|
+
response: z.object({
|
|
24
|
+
success: z.boolean(),
|
|
25
|
+
users: z.array(
|
|
26
|
+
z.object({
|
|
27
|
+
id: z.string(),
|
|
28
|
+
name: z.string(),
|
|
29
|
+
type: z.string().optional(),
|
|
30
|
+
})
|
|
31
|
+
),
|
|
32
|
+
total: z.number(),
|
|
33
|
+
durationMs: z.number(),
|
|
34
|
+
}),
|
|
35
|
+
},
|
|
36
|
+
|
|
37
|
+
async handler(ctx) {
|
|
38
|
+
const { options } = ctx;
|
|
39
|
+
const started = Date.now();
|
|
40
|
+
const storage = await createStorageAdapter(ctx);
|
|
41
|
+
const result = await storage.listUsers();
|
|
42
|
+
const durationMs = Date.now() - started;
|
|
43
|
+
|
|
44
|
+
if (!options.json) {
|
|
45
|
+
if (result.users.length === 0) {
|
|
46
|
+
tui.info('No users found.');
|
|
47
|
+
} else {
|
|
48
|
+
tui.table(
|
|
49
|
+
result.users.map((u) => ({
|
|
50
|
+
id: u.id,
|
|
51
|
+
name: u.name,
|
|
52
|
+
type: (u as { type?: string }).type ?? 'human',
|
|
53
|
+
})),
|
|
54
|
+
['id', 'name', 'type']
|
|
55
|
+
);
|
|
56
|
+
tui.info(`${result.users.length} user(s) (${durationMs.toFixed(1)}ms)`);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return { success: true, users: result.users, total: result.users.length, durationMs };
|
|
61
|
+
},
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
// ββ Create ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
65
|
+
|
|
66
|
+
const createUserSubcommand = createCommand({
|
|
67
|
+
name: 'create',
|
|
68
|
+
description: 'Create a new task user',
|
|
69
|
+
tags: ['mutating', 'slow', 'requires-auth'],
|
|
70
|
+
requires: { auth: true },
|
|
71
|
+
examples: [
|
|
72
|
+
{
|
|
73
|
+
command: getCommand('cloud task user create "Jane Doe"'),
|
|
74
|
+
description: 'Create a human user',
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
command: getCommand('cloud task user create "My Agent" --type agent'),
|
|
78
|
+
description: 'Create an agent user',
|
|
79
|
+
},
|
|
80
|
+
],
|
|
81
|
+
schema: {
|
|
82
|
+
args: z.object({
|
|
83
|
+
name: z.string().min(1).describe('the user display name'),
|
|
84
|
+
}),
|
|
85
|
+
options: z.object({
|
|
86
|
+
type: z.enum(['human', 'agent']).optional().describe('user type (human or agent)'),
|
|
87
|
+
}),
|
|
88
|
+
response: z.object({
|
|
89
|
+
success: z.boolean(),
|
|
90
|
+
user: z.object({
|
|
91
|
+
id: z.string(),
|
|
92
|
+
name: z.string(),
|
|
93
|
+
type: z.string().optional(),
|
|
94
|
+
}),
|
|
95
|
+
durationMs: z.number(),
|
|
96
|
+
}),
|
|
97
|
+
},
|
|
98
|
+
|
|
99
|
+
async handler(ctx) {
|
|
100
|
+
const { args, opts, options } = ctx;
|
|
101
|
+
const started = Date.now();
|
|
102
|
+
const storage = await createStorageAdapter(ctx);
|
|
103
|
+
const user = await storage.createUser({ name: args.name, type: opts.type });
|
|
104
|
+
const durationMs = Date.now() - started;
|
|
105
|
+
|
|
106
|
+
if (!options.json) {
|
|
107
|
+
tui.success(`Created user: ${user.name} (${user.id})`);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return { success: true, user, durationMs };
|
|
111
|
+
},
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
// ββ Get βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
115
|
+
|
|
116
|
+
const getUserSubcommand = createCommand({
|
|
117
|
+
name: 'get',
|
|
118
|
+
description: 'Get a task user by ID',
|
|
119
|
+
tags: ['read-only', 'slow', 'requires-auth'],
|
|
120
|
+
requires: { auth: true },
|
|
121
|
+
idempotent: true,
|
|
122
|
+
examples: [
|
|
123
|
+
{
|
|
124
|
+
command: getCommand('cloud task user get usr_abc123'),
|
|
125
|
+
description: 'Get user details',
|
|
126
|
+
},
|
|
127
|
+
],
|
|
128
|
+
schema: {
|
|
129
|
+
args: z.object({
|
|
130
|
+
id: z.string().min(1).describe('the user ID'),
|
|
131
|
+
}),
|
|
132
|
+
response: z.object({
|
|
133
|
+
success: z.boolean(),
|
|
134
|
+
user: z.object({
|
|
135
|
+
id: z.string(),
|
|
136
|
+
name: z.string(),
|
|
137
|
+
type: z.string().optional(),
|
|
138
|
+
}),
|
|
139
|
+
durationMs: z.number(),
|
|
140
|
+
}),
|
|
141
|
+
},
|
|
142
|
+
|
|
143
|
+
async handler(ctx) {
|
|
144
|
+
const { args, options } = ctx;
|
|
145
|
+
const started = Date.now();
|
|
146
|
+
const storage = await createStorageAdapter(ctx);
|
|
147
|
+
try {
|
|
148
|
+
const user = await storage.getUser(args.id);
|
|
149
|
+
const durationMs = Date.now() - started;
|
|
150
|
+
|
|
151
|
+
if (!options.json) {
|
|
152
|
+
tui.table(
|
|
153
|
+
[
|
|
154
|
+
{
|
|
155
|
+
id: user.id,
|
|
156
|
+
name: user.name,
|
|
157
|
+
type: (user as { type?: string }).type ?? 'human',
|
|
158
|
+
},
|
|
159
|
+
],
|
|
160
|
+
['id', 'name', 'type'],
|
|
161
|
+
{ layout: 'vertical' }
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return { success: true, user, durationMs };
|
|
166
|
+
} catch (_err) {
|
|
167
|
+
const durationMs = Date.now() - started;
|
|
168
|
+
if (!options.json) {
|
|
169
|
+
tui.error(`User not found: ${args.id}`);
|
|
170
|
+
}
|
|
171
|
+
return {
|
|
172
|
+
success: false,
|
|
173
|
+
user: { id: args.id, name: '', type: 'human' as const },
|
|
174
|
+
durationMs,
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
},
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
// ββ Parent command ββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
181
|
+
|
|
182
|
+
export const userSubcommand = createCommand({
|
|
183
|
+
name: 'user',
|
|
184
|
+
description: 'Manage task users',
|
|
185
|
+
tags: ['requires-auth'],
|
|
186
|
+
requires: { auth: true },
|
|
187
|
+
examples: [
|
|
188
|
+
{
|
|
189
|
+
command: getCommand('cloud task user list'),
|
|
190
|
+
description: 'List all users',
|
|
191
|
+
},
|
|
192
|
+
{
|
|
193
|
+
command: getCommand('cloud task user create "Jane Doe"'),
|
|
194
|
+
description: 'Create a new user',
|
|
195
|
+
},
|
|
196
|
+
{
|
|
197
|
+
command: getCommand('cloud task user get usr_abc123'),
|
|
198
|
+
description: 'Get user details',
|
|
199
|
+
},
|
|
200
|
+
],
|
|
201
|
+
subcommands: [listUsersSubcommand, createUserSubcommand, getUserSubcommand],
|
|
202
|
+
});
|
package/src/types.ts
CHANGED
|
@@ -249,6 +249,15 @@ export interface AgentuityConfig {
|
|
|
249
249
|
* Note: Cannot override AGENTUITY_PUBLIC_* or process.env.NODE_ENV
|
|
250
250
|
*/
|
|
251
251
|
define?: Record<string, string>;
|
|
252
|
+
/**
|
|
253
|
+
* Glob patterns for additional files to include in the deployment bundle.
|
|
254
|
+
* Files matching these patterns will be copied into the .agentuity build
|
|
255
|
+
* output directory before the build runs, preserving their relative paths
|
|
256
|
+
* from the project root. Build output will overwrite any conflicting files.
|
|
257
|
+
*
|
|
258
|
+
* @example ['data/**', 'templates/*.json', 'models/weights.bin']
|
|
259
|
+
*/
|
|
260
|
+
bundle?: string[];
|
|
252
261
|
}
|
|
253
262
|
|
|
254
263
|
/**
|