@mitsein-ai/cli 0.1.5 → 0.2.0
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/index.js +114 -111
- package/package.json +2 -2
- package/src/commands/agent.ts +126 -0
- package/src/commands/auth.ts +40 -1
- package/src/commands/files.ts +216 -0
- package/src/commands/thread.ts +1 -1
- package/src/core/client.ts +62 -2
- package/src/index.ts +18 -2
- package/src/commands/run.ts +0 -134
package/package.json
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
"ofetch": "^1.4.1",
|
|
9
9
|
"undici": "^8.0.2"
|
|
10
10
|
},
|
|
11
|
-
"description": "Mitsein CLI
|
|
11
|
+
"description": "Mitsein CLI \u2014 dev tooling, API helpers, and workflow automation",
|
|
12
12
|
"devDependencies": {
|
|
13
13
|
"@types/bun": "latest",
|
|
14
14
|
"typescript": "^5.3.3"
|
|
@@ -46,5 +46,5 @@
|
|
|
46
46
|
"typecheck": "tsc --noEmit"
|
|
47
47
|
},
|
|
48
48
|
"type": "module",
|
|
49
|
-
"version": "0.
|
|
49
|
+
"version": "0.2.0"
|
|
50
50
|
}
|
package/src/commands/agent.ts
CHANGED
|
@@ -88,4 +88,130 @@ export function registerAgent(program: Command): void {
|
|
|
88
88
|
}
|
|
89
89
|
})
|
|
90
90
|
);
|
|
91
|
+
|
|
92
|
+
/* ── running sub-group (formerly top-level `run` command) ── */
|
|
93
|
+
const running = agent.command('running').description('Agent run management');
|
|
94
|
+
|
|
95
|
+
running
|
|
96
|
+
.command('list <thread_id>')
|
|
97
|
+
.description('List agent runs for a thread')
|
|
98
|
+
.action(
|
|
99
|
+
handleErrors(async function runListAction(this: Command, threadId: string) {
|
|
100
|
+
const g = readGlobals(this);
|
|
101
|
+
setJsonMode(Boolean(g.json));
|
|
102
|
+
const client = ApiClient.fromOptions({
|
|
103
|
+
token: g.token,
|
|
104
|
+
endpoint: g.endpoint,
|
|
105
|
+
profile: g.profile ?? 'e2e',
|
|
106
|
+
real: g.real,
|
|
107
|
+
timeoutSec: httpTimeoutSec(g),
|
|
108
|
+
debug: g.debug,
|
|
109
|
+
});
|
|
110
|
+
const result = await client.post(`/api/thread/${threadId}/agent-runs`);
|
|
111
|
+
emit(result);
|
|
112
|
+
})
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
running
|
|
116
|
+
.command('status <run_id>')
|
|
117
|
+
.description('Get agent run status')
|
|
118
|
+
.action(
|
|
119
|
+
handleErrors(async function runStatusAction(this: Command, runId: string) {
|
|
120
|
+
const g = readGlobals(this);
|
|
121
|
+
setJsonMode(Boolean(g.json));
|
|
122
|
+
const client = ApiClient.fromOptions({
|
|
123
|
+
token: g.token,
|
|
124
|
+
endpoint: g.endpoint,
|
|
125
|
+
profile: g.profile ?? 'e2e',
|
|
126
|
+
real: g.real,
|
|
127
|
+
timeoutSec: httpTimeoutSec(g),
|
|
128
|
+
debug: g.debug,
|
|
129
|
+
});
|
|
130
|
+
const result = await client.post(`/api/agent-run/${runId}`);
|
|
131
|
+
emit(result);
|
|
132
|
+
})
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
running
|
|
136
|
+
.command('tail <run_id>')
|
|
137
|
+
.description('Stream agent run events (tail -f)')
|
|
138
|
+
.option('--filter <types>', 'Comma-separated event types')
|
|
139
|
+
.option('--timeout <sec>', 'Timeout (0 = no limit)', '120')
|
|
140
|
+
.action(
|
|
141
|
+
handleErrors(async function runTailAction(this: Command, runId: string) {
|
|
142
|
+
const g = readGlobals(this);
|
|
143
|
+
const opts = this.opts() as { filter?: string; timeout?: string };
|
|
144
|
+
setJsonMode(Boolean(g.json));
|
|
145
|
+
const creds = resolveCredentials({
|
|
146
|
+
token: g.token,
|
|
147
|
+
endpoint: g.endpoint,
|
|
148
|
+
profile: g.profile ?? 'e2e',
|
|
149
|
+
real: g.real,
|
|
150
|
+
});
|
|
151
|
+
const rawT = opts.timeout ?? '120';
|
|
152
|
+
const tNum = Number.parseFloat(rawT);
|
|
153
|
+
const effectiveTimeout = Number.isFinite(tNum) && tNum === 0 ? null : Number.isFinite(tNum) ? tNum : 120;
|
|
154
|
+
await waitForRun({
|
|
155
|
+
endpoint: creds.endpoint,
|
|
156
|
+
token: creds.token,
|
|
157
|
+
agent_run_id: runId,
|
|
158
|
+
timeout: effectiveTimeout ?? undefined,
|
|
159
|
+
stream_output: true,
|
|
160
|
+
json_mode: Boolean(g.json),
|
|
161
|
+
debug: g.debug,
|
|
162
|
+
event_filter: commaSet(opts.filter),
|
|
163
|
+
});
|
|
164
|
+
})
|
|
165
|
+
);
|
|
166
|
+
|
|
167
|
+
running
|
|
168
|
+
.command('wait <run_id>')
|
|
169
|
+
.description('Block until agent run completes')
|
|
170
|
+
.option('--timeout <sec>', 'Timeout (0 = no limit)', '120')
|
|
171
|
+
.action(
|
|
172
|
+
handleErrors(async function runWaitAction(this: Command, runId: string) {
|
|
173
|
+
const g = readGlobals(this);
|
|
174
|
+
const opts = this.opts() as { timeout?: string };
|
|
175
|
+
setJsonMode(Boolean(g.json));
|
|
176
|
+
const creds = resolveCredentials({
|
|
177
|
+
token: g.token,
|
|
178
|
+
endpoint: g.endpoint,
|
|
179
|
+
profile: g.profile ?? 'e2e',
|
|
180
|
+
real: g.real,
|
|
181
|
+
});
|
|
182
|
+
const rawT = opts.timeout ?? '120';
|
|
183
|
+
const tNum = Number.parseFloat(rawT);
|
|
184
|
+
const effectiveTimeout = Number.isFinite(tNum) && tNum === 0 ? null : Number.isFinite(tNum) ? tNum : 120;
|
|
185
|
+
const result = await waitForRun({
|
|
186
|
+
endpoint: creds.endpoint,
|
|
187
|
+
token: creds.token,
|
|
188
|
+
agent_run_id: runId,
|
|
189
|
+
timeout: effectiveTimeout ?? undefined,
|
|
190
|
+
stream_output: false,
|
|
191
|
+
json_mode: Boolean(g.json),
|
|
192
|
+
debug: g.debug,
|
|
193
|
+
});
|
|
194
|
+
emit(result);
|
|
195
|
+
})
|
|
196
|
+
);
|
|
197
|
+
|
|
198
|
+
running
|
|
199
|
+
.command('cancel <run_id>')
|
|
200
|
+
.description('Cancel a running agent')
|
|
201
|
+
.action(
|
|
202
|
+
handleErrors(async function runCancelAction(this: Command, runId: string) {
|
|
203
|
+
const g = readGlobals(this);
|
|
204
|
+
setJsonMode(Boolean(g.json));
|
|
205
|
+
const client = ApiClient.fromOptions({
|
|
206
|
+
token: g.token,
|
|
207
|
+
endpoint: g.endpoint,
|
|
208
|
+
profile: g.profile ?? 'e2e',
|
|
209
|
+
real: g.real,
|
|
210
|
+
timeoutSec: httpTimeoutSec(g),
|
|
211
|
+
debug: g.debug,
|
|
212
|
+
});
|
|
213
|
+
const result = await client.post(`/api/agent-run/${runId}/stop`);
|
|
214
|
+
emit(result);
|
|
215
|
+
})
|
|
216
|
+
);
|
|
91
217
|
}
|
package/src/commands/auth.ts
CHANGED
|
@@ -80,9 +80,47 @@ export function registerAuth(program: Command): void {
|
|
|
80
80
|
setJsonMode(Boolean(g.json));
|
|
81
81
|
void opts.endpoint;
|
|
82
82
|
const profile = opts.profile ?? 'default';
|
|
83
|
+
|
|
84
|
+
// Check env token first (MITSEIN_TOKEN or --token)
|
|
85
|
+
if (g.token) {
|
|
86
|
+
// Try to parse dev token format: #{"userid":"...", ...}
|
|
87
|
+
const raw = g.token.startsWith('#') ? g.token.slice(1) : g.token;
|
|
88
|
+
try {
|
|
89
|
+
const parsed = JSON.parse(raw) as Record<string, string>;
|
|
90
|
+
emit(
|
|
91
|
+
{
|
|
92
|
+
email: parsed.email ?? 'unknown',
|
|
93
|
+
user_id: parsed.userid ?? 'unknown',
|
|
94
|
+
username: parsed.username ?? 'unknown',
|
|
95
|
+
source: 'env/flag',
|
|
96
|
+
profile,
|
|
97
|
+
},
|
|
98
|
+
(payload) => {
|
|
99
|
+
const d = payload as Record<string, string>;
|
|
100
|
+
consola.success(`Logged in as ${d.email}`);
|
|
101
|
+
consola.log(` User ID: ${d.user_id}`);
|
|
102
|
+
consola.log(` Username: ${d.username}`);
|
|
103
|
+
consola.log(` Source: ${d.source}`);
|
|
104
|
+
}
|
|
105
|
+
);
|
|
106
|
+
return;
|
|
107
|
+
} catch {
|
|
108
|
+
// Not a parseable dev token, show raw info
|
|
109
|
+
emit(
|
|
110
|
+
{ token: '***', source: 'env/flag', profile },
|
|
111
|
+
(payload) => {
|
|
112
|
+
const d = payload as Record<string, string>;
|
|
113
|
+
consola.success('Authenticated via token');
|
|
114
|
+
consola.log(` Source: ${d.source}`);
|
|
115
|
+
}
|
|
116
|
+
);
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
83
121
|
const oauthPath = getOauthPath(profile);
|
|
84
122
|
if (!existsSync(oauthPath)) {
|
|
85
|
-
throw new CliError('Not logged in. Run `mitsein auth login
|
|
123
|
+
throw new CliError('Not logged in. Run `mitsein auth login` or set MITSEIN_TOKEN.', ExitCode.USAGE_ERROR);
|
|
86
124
|
}
|
|
87
125
|
const data = JSON.parse(readFileSync(oauthPath, 'utf8')) as Record<string, string>;
|
|
88
126
|
emit(
|
|
@@ -91,6 +129,7 @@ export function registerAuth(program: Command): void {
|
|
|
91
129
|
user_id: data.user_id ?? 'unknown',
|
|
92
130
|
profile,
|
|
93
131
|
endpoint: data.endpoint ?? '',
|
|
132
|
+
source: 'profile',
|
|
94
133
|
},
|
|
95
134
|
(payload) => {
|
|
96
135
|
const d = payload as Record<string, string>;
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import { basename, resolve } from 'node:path';
|
|
3
|
+
import { writeFile } from 'node:fs/promises';
|
|
4
|
+
import type { Command } from 'commander';
|
|
5
|
+
import { ApiClient } from '../core/client.js';
|
|
6
|
+
import { CliError, ExitCode, handleErrors } from '../core/errors.js';
|
|
7
|
+
import { emit, setJsonMode } from '../core/output.js';
|
|
8
|
+
import { httpTimeoutSec, readGlobals } from './command-opts.js';
|
|
9
|
+
|
|
10
|
+
function makeClient(cmd: Command): ApiClient {
|
|
11
|
+
const g = readGlobals(cmd);
|
|
12
|
+
setJsonMode(Boolean(g.json));
|
|
13
|
+
return ApiClient.fromOptions({
|
|
14
|
+
token: g.token,
|
|
15
|
+
endpoint: g.endpoint,
|
|
16
|
+
profile: g.profile ?? 'e2e',
|
|
17
|
+
real: g.real,
|
|
18
|
+
timeoutSec: httpTimeoutSec(g),
|
|
19
|
+
debug: g.debug,
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function registerFiles(program: Command): void {
|
|
24
|
+
const files = program.command('files').description('Sandbox file operations');
|
|
25
|
+
|
|
26
|
+
files
|
|
27
|
+
.command('list <thread_id>')
|
|
28
|
+
.description('List files in a thread sandbox')
|
|
29
|
+
.option('--path <dir>', 'Directory path', '')
|
|
30
|
+
.option('--filter <glob>', 'Glob filter', '*')
|
|
31
|
+
.action(
|
|
32
|
+
handleErrors(async function filesListAction(this: Command, threadId: string) {
|
|
33
|
+
const client = makeClient(this);
|
|
34
|
+
const opts = this.opts() as { path: string; filter: string };
|
|
35
|
+
const result = await client.get(`/api/files/${threadId}/list`, {
|
|
36
|
+
path: opts.path,
|
|
37
|
+
filter: opts.filter,
|
|
38
|
+
});
|
|
39
|
+
emit(result);
|
|
40
|
+
})
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
files
|
|
44
|
+
.command('read <thread_id> <path>')
|
|
45
|
+
.description('Read file content from sandbox')
|
|
46
|
+
.action(
|
|
47
|
+
handleErrors(async function filesReadAction(this: Command, threadId: string, filePath: string) {
|
|
48
|
+
const client = makeClient(this);
|
|
49
|
+
const result = await client.get(`/api/files/${threadId}/read`, {
|
|
50
|
+
path: filePath,
|
|
51
|
+
});
|
|
52
|
+
emit(result);
|
|
53
|
+
})
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
files
|
|
57
|
+
.command('info <thread_id> <path>')
|
|
58
|
+
.description('Get file metadata')
|
|
59
|
+
.action(
|
|
60
|
+
handleErrors(async function filesInfoAction(this: Command, threadId: string, filePath: string) {
|
|
61
|
+
const client = makeClient(this);
|
|
62
|
+
const result = await client.get(`/api/files/${threadId}/info`, {
|
|
63
|
+
path: filePath,
|
|
64
|
+
});
|
|
65
|
+
emit(result);
|
|
66
|
+
})
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
files
|
|
70
|
+
.command('upload <thread_id> <local_path>')
|
|
71
|
+
.description('Upload a local file to the sandbox')
|
|
72
|
+
.option('--remote-path <path>', 'Remote destination path (default: filename)')
|
|
73
|
+
.action(
|
|
74
|
+
handleErrors(async function filesUploadAction(
|
|
75
|
+
this: Command,
|
|
76
|
+
threadId: string,
|
|
77
|
+
localPath: string,
|
|
78
|
+
) {
|
|
79
|
+
const client = makeClient(this);
|
|
80
|
+
const opts = this.opts() as { remotePath?: string };
|
|
81
|
+
const absPath = resolve(localPath);
|
|
82
|
+
const bytes = await readFile(absPath);
|
|
83
|
+
const fileName = basename(absPath);
|
|
84
|
+
const remotePath = opts.remotePath ?? fileName;
|
|
85
|
+
const file = new File([bytes], fileName);
|
|
86
|
+
const result = await client.postMultipart(`/api/files/${threadId}/upload`, {
|
|
87
|
+
file,
|
|
88
|
+
path: remotePath,
|
|
89
|
+
});
|
|
90
|
+
emit(result);
|
|
91
|
+
})
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
files
|
|
95
|
+
.command('write <thread_id> <remote_path>')
|
|
96
|
+
.description('Write content to a file in the sandbox')
|
|
97
|
+
.option('--from <local_path>', 'Local file to read (reads stdin if omitted)')
|
|
98
|
+
.action(
|
|
99
|
+
handleErrors(async function filesWriteAction(
|
|
100
|
+
this: Command,
|
|
101
|
+
threadId: string,
|
|
102
|
+
remotePath: string,
|
|
103
|
+
) {
|
|
104
|
+
const client = makeClient(this);
|
|
105
|
+
const opts = this.opts() as { from?: string };
|
|
106
|
+
let bytes: Buffer;
|
|
107
|
+
if (opts.from) {
|
|
108
|
+
bytes = await readFile(resolve(opts.from));
|
|
109
|
+
} else {
|
|
110
|
+
bytes = await readStdin();
|
|
111
|
+
}
|
|
112
|
+
const fileName = basename(remotePath);
|
|
113
|
+
const content = new File([bytes], fileName);
|
|
114
|
+
const result = await client.postMultipart(`/api/files/${threadId}/write`, {
|
|
115
|
+
content,
|
|
116
|
+
path: remotePath,
|
|
117
|
+
});
|
|
118
|
+
emit(result);
|
|
119
|
+
})
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
files
|
|
123
|
+
.command('mkdir <thread_id> <path>')
|
|
124
|
+
.description('Create a directory in the sandbox')
|
|
125
|
+
.action(
|
|
126
|
+
handleErrors(async function filesMkdirAction(this: Command, threadId: string, dirPath: string) {
|
|
127
|
+
const client = makeClient(this);
|
|
128
|
+
const result = await client.post(`/api/files/${threadId}/folder`, {
|
|
129
|
+
path: dirPath,
|
|
130
|
+
});
|
|
131
|
+
emit(result);
|
|
132
|
+
})
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
files
|
|
136
|
+
.command('delete <thread_id> <path>')
|
|
137
|
+
.description('Delete a file from the sandbox')
|
|
138
|
+
.action(
|
|
139
|
+
handleErrors(async function filesDeleteAction(
|
|
140
|
+
this: Command,
|
|
141
|
+
threadId: string,
|
|
142
|
+
filePath: string,
|
|
143
|
+
) {
|
|
144
|
+
const client = makeClient(this);
|
|
145
|
+
const result = await client.delete(`/api/files/${threadId}/delete`, { path: filePath });
|
|
146
|
+
emit(result);
|
|
147
|
+
})
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
files
|
|
151
|
+
.command('download <thread_id>')
|
|
152
|
+
.description('Download file(s) from the sandbox')
|
|
153
|
+
.option('--path <path>', 'Single file path to download')
|
|
154
|
+
.option('--paths <paths...>', 'Multiple file paths (batch download)')
|
|
155
|
+
.option('--out <dir>', 'Output directory', '.')
|
|
156
|
+
.action(
|
|
157
|
+
handleErrors(async function filesDownloadAction(this: Command, threadId: string) {
|
|
158
|
+
const client = makeClient(this);
|
|
159
|
+
const opts = this.opts() as {
|
|
160
|
+
path?: string;
|
|
161
|
+
paths?: string[];
|
|
162
|
+
out: string;
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
if (!opts.path && (!opts.paths || opts.paths.length === 0)) {
|
|
166
|
+
throw new CliError(
|
|
167
|
+
'Either --path or --paths is required',
|
|
168
|
+
ExitCode.USAGE_ERROR,
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if (opts.path && opts.paths && opts.paths.length > 0) {
|
|
173
|
+
throw new CliError(
|
|
174
|
+
'--path and --paths are mutually exclusive',
|
|
175
|
+
ExitCode.USAGE_ERROR,
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if (opts.path) {
|
|
180
|
+
const result = await client.get(
|
|
181
|
+
`/api/files/${threadId}/read-binary`,
|
|
182
|
+
{ path: opts.path },
|
|
183
|
+
);
|
|
184
|
+
const outPath = resolve(opts.out, basename(opts.path));
|
|
185
|
+
if (result instanceof ArrayBuffer || Buffer.isBuffer(result)) {
|
|
186
|
+
await writeFile(outPath, Buffer.from(result as ArrayBuffer));
|
|
187
|
+
} else {
|
|
188
|
+
await writeFile(outPath, String(result));
|
|
189
|
+
}
|
|
190
|
+
emit({ downloaded: outPath });
|
|
191
|
+
} else {
|
|
192
|
+
const result = await client.post(
|
|
193
|
+
`/api/files/${threadId}/batch-download`,
|
|
194
|
+
{ paths: opts.paths },
|
|
195
|
+
);
|
|
196
|
+
emit(result);
|
|
197
|
+
}
|
|
198
|
+
})
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
async function readStdin(timeoutMs = 30_000): Promise<Buffer> {
|
|
203
|
+
const chunks: Buffer[] = [];
|
|
204
|
+
const timer = setTimeout(() => {
|
|
205
|
+
process.stderr.write('error: stdin read timed out (no input received)\n');
|
|
206
|
+
process.exit(1);
|
|
207
|
+
}, timeoutMs);
|
|
208
|
+
try {
|
|
209
|
+
for await (const chunk of process.stdin) {
|
|
210
|
+
chunks.push(Buffer.from(chunk as Uint8Array));
|
|
211
|
+
}
|
|
212
|
+
} finally {
|
|
213
|
+
clearTimeout(timer);
|
|
214
|
+
}
|
|
215
|
+
return Buffer.concat(chunks);
|
|
216
|
+
}
|
package/src/commands/thread.ts
CHANGED
|
@@ -73,7 +73,7 @@ export function registerThread(program: Command): void {
|
|
|
73
73
|
timeoutSec: httpTimeoutSec(g),
|
|
74
74
|
debug: g.debug,
|
|
75
75
|
});
|
|
76
|
-
const result = await client.post('/api/
|
|
76
|
+
const result = await client.post('/api/threads', { title: title ?? '' });
|
|
77
77
|
emit(result);
|
|
78
78
|
})
|
|
79
79
|
);
|
package/src/core/client.ts
CHANGED
|
@@ -168,6 +168,66 @@ export class ApiClient {
|
|
|
168
168
|
}
|
|
169
169
|
}
|
|
170
170
|
|
|
171
|
+
async postMultipart(
|
|
172
|
+
path: string,
|
|
173
|
+
fields: Record<string, string | File | Blob>,
|
|
174
|
+
): Promise<unknown> {
|
|
175
|
+
const p = path.startsWith('/') ? path : `/${path}`;
|
|
176
|
+
const formData = new FormData();
|
|
177
|
+
for (const [k, v] of Object.entries(fields)) {
|
|
178
|
+
formData.append(k, v);
|
|
179
|
+
}
|
|
180
|
+
if (this.debug) {
|
|
181
|
+
logDebug(this.debug, `POST ${p} (multipart)`);
|
|
182
|
+
logDebug(this.debug, `fields: ${Object.keys(fields).join(', ')}`);
|
|
183
|
+
}
|
|
184
|
+
const url = `${this.baseUrl}${p}`;
|
|
185
|
+
const controller = new AbortController();
|
|
186
|
+
const ms = this.timeoutMs;
|
|
187
|
+
const timer =
|
|
188
|
+
ms === undefined ? null : setTimeout(() => controller.abort(), ms);
|
|
189
|
+
try {
|
|
190
|
+
const r = await fetch(url, {
|
|
191
|
+
method: 'POST',
|
|
192
|
+
headers: {
|
|
193
|
+
Authorization: `Bearer ${this.bearerToken}`,
|
|
194
|
+
},
|
|
195
|
+
body: formData,
|
|
196
|
+
signal: controller.signal,
|
|
197
|
+
});
|
|
198
|
+
logDebug(this.debug, `→ ${r.status} (${r.headers.get('content-length') ?? '?'} bytes)`);
|
|
199
|
+
if (r.ok) {
|
|
200
|
+
const ct = r.headers.get('content-type') ?? '';
|
|
201
|
+
if (ct.includes('application/json')) {
|
|
202
|
+
return r.json();
|
|
203
|
+
}
|
|
204
|
+
return r.text();
|
|
205
|
+
}
|
|
206
|
+
let message = `HTTP ${r.status}`;
|
|
207
|
+
let detail: unknown;
|
|
208
|
+
try {
|
|
209
|
+
const j = (await r.json()) as Record<string, unknown>;
|
|
210
|
+
message = typeof j.message === 'string' ? j.message : message;
|
|
211
|
+
detail = j.detail ?? j.error;
|
|
212
|
+
} catch {
|
|
213
|
+
/* ignore */
|
|
214
|
+
}
|
|
215
|
+
throw new HttpError(r.status, message, detail);
|
|
216
|
+
} catch (e) {
|
|
217
|
+
if (e instanceof HttpError) {
|
|
218
|
+
throw e;
|
|
219
|
+
}
|
|
220
|
+
if (e instanceof Error && e.name === 'AbortError') {
|
|
221
|
+
throw new HttpError(599, 'Request aborted or timed out');
|
|
222
|
+
}
|
|
223
|
+
throw e;
|
|
224
|
+
} finally {
|
|
225
|
+
if (timer) {
|
|
226
|
+
clearTimeout(timer);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
171
231
|
async patch(path: string, jsonBody?: unknown | null): Promise<unknown> {
|
|
172
232
|
const p = path.startsWith('/') ? path : `/${path}`;
|
|
173
233
|
if (this.debug && jsonBody !== undefined && jsonBody !== null) {
|
|
@@ -194,8 +254,8 @@ export class ApiClient {
|
|
|
194
254
|
);
|
|
195
255
|
}
|
|
196
256
|
|
|
197
|
-
async delete(path: string): Promise<unknown> {
|
|
257
|
+
async delete(path: string, query?: Record<string, unknown> | null): Promise<unknown> {
|
|
198
258
|
const p = path.startsWith('/') ? path : `/${path}`;
|
|
199
|
-
return handleFetch(this.fetchImpl(p, { method: 'DELETE' }));
|
|
259
|
+
return handleFetch(this.fetchImpl(p, { method: 'DELETE', query: query ?? undefined }));
|
|
200
260
|
}
|
|
201
261
|
}
|
package/src/index.ts
CHANGED
|
@@ -6,9 +6,9 @@ import { registerAgent } from './commands/agent.js';
|
|
|
6
6
|
import { registerApi } from './commands/api-auto.js';
|
|
7
7
|
import { registerAuth } from './commands/auth.js';
|
|
8
8
|
import { registerDev } from './commands/dev.js';
|
|
9
|
+
import { registerFiles } from './commands/files.js';
|
|
9
10
|
import { registerMessages } from './commands/messages.js';
|
|
10
11
|
import { registerProject } from './commands/project.js';
|
|
11
|
-
import { registerRun } from './commands/run.js';
|
|
12
12
|
import { registerThread } from './commands/thread.js';
|
|
13
13
|
import { runVersion } from './commands/version.js';
|
|
14
14
|
import { CliError } from './core/errors.js';
|
|
@@ -49,12 +49,28 @@ program
|
|
|
49
49
|
registerDev(program);
|
|
50
50
|
registerApi(program);
|
|
51
51
|
registerThread(program);
|
|
52
|
-
registerRun(program);
|
|
53
52
|
registerMessages(program);
|
|
54
53
|
registerAgent(program);
|
|
55
54
|
registerProject(program);
|
|
55
|
+
registerFiles(program);
|
|
56
56
|
registerAuth(program);
|
|
57
57
|
|
|
58
|
+
// Deprecated: `mitsein run` was merged into `mitsein agent running` in v0.2.0
|
|
59
|
+
program
|
|
60
|
+
.command('run', { hidden: true })
|
|
61
|
+
.description('[deprecated] Use `mitsein agent running` instead')
|
|
62
|
+
.allowUnknownOption(true)
|
|
63
|
+
.action(() => {
|
|
64
|
+
process.stderr.write(
|
|
65
|
+
'error: `mitsein run` has been removed in v0.2.0.\n' +
|
|
66
|
+
'Use `mitsein agent running <subcommand>` instead.\n' +
|
|
67
|
+
'Examples:\n' +
|
|
68
|
+
' mitsein run list → mitsein agent running list\n' +
|
|
69
|
+
' mitsein run status → mitsein agent running status\n',
|
|
70
|
+
);
|
|
71
|
+
process.exit(1);
|
|
72
|
+
});
|
|
73
|
+
|
|
58
74
|
try {
|
|
59
75
|
await program.parseAsync(process.argv, { from: 'node' });
|
|
60
76
|
} catch (e) {
|
package/src/commands/run.ts
DELETED
|
@@ -1,134 +0,0 @@
|
|
|
1
|
-
import type { Command } from 'commander';
|
|
2
|
-
import { ApiClient } from '../core/client.js';
|
|
3
|
-
import { resolveCredentials } from '../core/credentials.js';
|
|
4
|
-
import { handleErrors } from '../core/errors.js';
|
|
5
|
-
import { emit, setJsonMode } from '../core/output.js';
|
|
6
|
-
import { waitForRun } from '../core/waiters.js';
|
|
7
|
-
import { commaSet, httpTimeoutSec, readGlobals } from './command-opts.js';
|
|
8
|
-
|
|
9
|
-
export function registerRun(program: Command): void {
|
|
10
|
-
const run = program.command('run').description('Agent run management');
|
|
11
|
-
|
|
12
|
-
run
|
|
13
|
-
.command('list <thread_id>')
|
|
14
|
-
.description('List agent runs for a thread')
|
|
15
|
-
.action(
|
|
16
|
-
handleErrors(async function runListAction(this: Command, threadId: string) {
|
|
17
|
-
const g = readGlobals(this);
|
|
18
|
-
setJsonMode(Boolean(g.json));
|
|
19
|
-
const client = ApiClient.fromOptions({
|
|
20
|
-
token: g.token,
|
|
21
|
-
endpoint: g.endpoint,
|
|
22
|
-
profile: g.profile ?? 'e2e',
|
|
23
|
-
real: g.real,
|
|
24
|
-
timeoutSec: httpTimeoutSec(g),
|
|
25
|
-
debug: g.debug,
|
|
26
|
-
});
|
|
27
|
-
const result = await client.post(`/api/thread/${threadId}/agent-runs`);
|
|
28
|
-
emit(result);
|
|
29
|
-
})
|
|
30
|
-
);
|
|
31
|
-
|
|
32
|
-
run
|
|
33
|
-
.command('get <run_id>')
|
|
34
|
-
.description('Get agent run status')
|
|
35
|
-
.action(
|
|
36
|
-
handleErrors(async function runGetAction(this: Command, runId: string) {
|
|
37
|
-
const g = readGlobals(this);
|
|
38
|
-
setJsonMode(Boolean(g.json));
|
|
39
|
-
const client = ApiClient.fromOptions({
|
|
40
|
-
token: g.token,
|
|
41
|
-
endpoint: g.endpoint,
|
|
42
|
-
profile: g.profile ?? 'e2e',
|
|
43
|
-
real: g.real,
|
|
44
|
-
timeoutSec: httpTimeoutSec(g),
|
|
45
|
-
debug: g.debug,
|
|
46
|
-
});
|
|
47
|
-
const result = await client.post(`/api/agent-run/${runId}`);
|
|
48
|
-
emit(result);
|
|
49
|
-
})
|
|
50
|
-
);
|
|
51
|
-
|
|
52
|
-
run
|
|
53
|
-
.command('tail <run_id>')
|
|
54
|
-
.description('Stream agent run events (tail -f)')
|
|
55
|
-
.option('--filter <types>', 'Comma-separated event types')
|
|
56
|
-
.option('--timeout <sec>', 'Timeout (0 = no limit)', '120')
|
|
57
|
-
.action(
|
|
58
|
-
handleErrors(async function runTailAction(this: Command, runId: string) {
|
|
59
|
-
const g = readGlobals(this);
|
|
60
|
-
const opts = this.opts() as { filter?: string; timeout?: string };
|
|
61
|
-
setJsonMode(Boolean(g.json));
|
|
62
|
-
const creds = resolveCredentials({
|
|
63
|
-
token: g.token,
|
|
64
|
-
endpoint: g.endpoint,
|
|
65
|
-
profile: g.profile ?? 'e2e',
|
|
66
|
-
real: g.real,
|
|
67
|
-
});
|
|
68
|
-
const rawT = opts.timeout ?? '120';
|
|
69
|
-
const tNum = Number.parseFloat(rawT);
|
|
70
|
-
const effectiveTimeout = Number.isFinite(tNum) && tNum === 0 ? null : Number.isFinite(tNum) ? tNum : 120;
|
|
71
|
-
await waitForRun({
|
|
72
|
-
endpoint: creds.endpoint,
|
|
73
|
-
token: creds.token,
|
|
74
|
-
agent_run_id: runId,
|
|
75
|
-
timeout: effectiveTimeout ?? undefined,
|
|
76
|
-
stream_output: true,
|
|
77
|
-
json_mode: Boolean(g.json),
|
|
78
|
-
debug: g.debug,
|
|
79
|
-
event_filter: commaSet(opts.filter),
|
|
80
|
-
});
|
|
81
|
-
})
|
|
82
|
-
);
|
|
83
|
-
|
|
84
|
-
run
|
|
85
|
-
.command('wait <run_id>')
|
|
86
|
-
.description('Block until agent run completes')
|
|
87
|
-
.option('--timeout <sec>', 'Timeout (0 = no limit)', '120')
|
|
88
|
-
.action(
|
|
89
|
-
handleErrors(async function runWaitAction(this: Command, runId: string) {
|
|
90
|
-
const g = readGlobals(this);
|
|
91
|
-
const opts = this.opts() as { timeout?: string };
|
|
92
|
-
setJsonMode(Boolean(g.json));
|
|
93
|
-
const creds = resolveCredentials({
|
|
94
|
-
token: g.token,
|
|
95
|
-
endpoint: g.endpoint,
|
|
96
|
-
profile: g.profile ?? 'e2e',
|
|
97
|
-
real: g.real,
|
|
98
|
-
});
|
|
99
|
-
const rawT = opts.timeout ?? '120';
|
|
100
|
-
const tNum = Number.parseFloat(rawT);
|
|
101
|
-
const effectiveTimeout = Number.isFinite(tNum) && tNum === 0 ? null : Number.isFinite(tNum) ? tNum : 120;
|
|
102
|
-
const result = await waitForRun({
|
|
103
|
-
endpoint: creds.endpoint,
|
|
104
|
-
token: creds.token,
|
|
105
|
-
agent_run_id: runId,
|
|
106
|
-
timeout: effectiveTimeout ?? undefined,
|
|
107
|
-
stream_output: false,
|
|
108
|
-
json_mode: Boolean(g.json),
|
|
109
|
-
debug: g.debug,
|
|
110
|
-
});
|
|
111
|
-
emit(result);
|
|
112
|
-
})
|
|
113
|
-
);
|
|
114
|
-
|
|
115
|
-
run
|
|
116
|
-
.command('cancel <run_id>')
|
|
117
|
-
.description('Cancel a running agent')
|
|
118
|
-
.action(
|
|
119
|
-
handleErrors(async function runCancelAction(this: Command, runId: string) {
|
|
120
|
-
const g = readGlobals(this);
|
|
121
|
-
setJsonMode(Boolean(g.json));
|
|
122
|
-
const client = ApiClient.fromOptions({
|
|
123
|
-
token: g.token,
|
|
124
|
-
endpoint: g.endpoint,
|
|
125
|
-
profile: g.profile ?? 'e2e',
|
|
126
|
-
real: g.real,
|
|
127
|
-
timeoutSec: httpTimeoutSec(g),
|
|
128
|
-
debug: g.debug,
|
|
129
|
-
});
|
|
130
|
-
const result = await client.post(`/api/agent-run/${runId}/stop`);
|
|
131
|
-
emit(result);
|
|
132
|
-
})
|
|
133
|
-
);
|
|
134
|
-
}
|