codexteer 0.18.2 → 0.20.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/CHANGELOG.md +27 -0
- package/README.md +13 -5
- package/bin/codexteer.mjs +1 -1
- package/docs/distribution.md +23 -6
- package/docs/orchestration.md +4 -4
- package/package.json +1 -1
- package/src/app-server.mjs +4 -4
- package/src/checkpoint.mjs +10 -10
- package/src/cli.mjs +90 -79
- package/src/cloud-cli.mjs +29 -33
- package/src/connection-error.mjs +7 -0
- package/src/cursor.mjs +370 -0
- package/src/desktop-cloud-id.mjs +9 -1
- package/src/desktop-cloud-project.mjs +37 -4
- package/src/desktop-cloud.mjs +10 -2
- package/src/desktop.mjs +9 -9
- package/src/directive.mjs +7 -6
- package/src/distribution.mjs +58 -33
- package/src/evidence.mjs +3 -3
- package/src/findings.mjs +13 -8
- package/src/help-cloud-remote.mjs +399 -0
- package/src/help-errors.mjs +10 -0
- package/src/help-intervention.mjs +492 -0
- package/src/help-local.mjs +510 -0
- package/src/help-observation.mjs +200 -0
- package/src/help-supervision.mjs +257 -0
- package/src/help.mjs +126 -304
- package/src/journal.mjs +33 -12
- package/src/launcher.mjs +6 -6
- package/src/monitor.mjs +5 -4
- package/src/notify.mjs +3 -3
- package/src/observe.mjs +11 -40
- package/src/orchestrate-prompt.mjs +14 -12
- package/src/orchestrate.mjs +4 -7
- package/src/paged-read.mjs +10 -17
- package/src/prompt-command.mjs +45 -7
- package/src/prompt.mjs +15 -12
- package/src/resource.mjs +6 -6
- package/src/runner.mjs +5 -5
- package/src/store.mjs +2 -2
- package/src/supervise.mjs +5 -7
- package/src/task-id.mjs +29 -4
- package/src/thread-id.mjs +5 -5
- package/src/unknown-options.mjs +17 -0
- package/src/wrapper.mjs +2 -2
package/src/cli.mjs
CHANGED
|
@@ -6,7 +6,7 @@ import { listLocalThreads } from "./thread-store.mjs";
|
|
|
6
6
|
import { sendTrackedMessage, listMessages, getMessage, messageSummary, reconcileMessages, markMessage, listInstructions } from "./journal.mjs";
|
|
7
7
|
import { launchDesktopUpdate, desktopUpdateStatus } from "./update.mjs";
|
|
8
8
|
import { appServerDoctor, startDesktop } from "./launcher.mjs";
|
|
9
|
-
import { VERSION, helpData, renderHelp } from "./help.mjs";
|
|
9
|
+
import { VERSION, helpData, renderHelp, renderErrors, errorRemedy, topicFor } from "./help.mjs";
|
|
10
10
|
import { observeThread, printObservation } from "./observe.mjs";
|
|
11
11
|
import { monitorCommand } from "./monitor.mjs";
|
|
12
12
|
import { notificationOptions } from "./notify.mjs";
|
|
@@ -15,6 +15,8 @@ import { acquireResource, resourceStatus, renewResource, releaseResource, listRe
|
|
|
15
15
|
import { playSendSound } from "./sound.mjs";
|
|
16
16
|
import { prepareSupervisorPrompt, superviseAgent } from "./supervise.mjs";
|
|
17
17
|
import { prepareOrchestrationPrompt, orchestrateAgent } from "./orchestrate.mjs";
|
|
18
|
+
import { rejectRepeatedBinding } from "./prompt-command.mjs";
|
|
19
|
+
import { rejectUnknownOptions } from "./unknown-options.mjs";
|
|
18
20
|
import { supervisorId, registerSupervisor, getSupervisor, assertSupervisor, assertSupervisorRegistration, controlSupervisor, supervisorStatus, listSupervisors } from "./supervision.mjs";
|
|
19
21
|
import { createFinding, findingStatus, listFindings, resolveFinding, dismissFinding, reopenFinding, evaluateFinding, findingStats } from "./findings.mjs";
|
|
20
22
|
import { connectionMode, observationConnection, desktopReadOnly } from "./connection.mjs";
|
|
@@ -34,11 +36,13 @@ function success(command, data, json) {
|
|
|
34
36
|
return data;
|
|
35
37
|
}
|
|
36
38
|
|
|
37
|
-
export function fail(error, json) {
|
|
39
|
+
export function fail(error, json, argv = []) {
|
|
38
40
|
const message = error instanceof Error ? error.message : String(error);
|
|
41
|
+
const remedy = errorRemedy(argv, error.code);
|
|
39
42
|
if (json) console.log(JSON.stringify({ ok: false, ...(error.watch ? { command: "watch", data: error.watch } : {}), error: {
|
|
40
|
-
message, code: error.code, ...hostFields(), delivery_status: error.delivery_status,
|
|
43
|
+
message, code: error.code, ...remedy, ...hostFields(), delivery_status: error.delivery_status,
|
|
41
44
|
operation: error.operation, capability: error.capability,
|
|
45
|
+
expected_connection: error.expected_connection, connection: error.connection,
|
|
42
46
|
...((error.execution_environment === "cloud" || (typeof error.code === "string" && error.code.startsWith("CLOUD_"))) ? {
|
|
43
47
|
execution_environment: error.execution_environment, task_id: error.task_id, url: error.url,
|
|
44
48
|
submission_status: error.submission_status, uncertain: error.uncertain,
|
|
@@ -47,7 +51,7 @@ export function fail(error, json) {
|
|
|
47
51
|
} : {}),
|
|
48
52
|
sent: error.sent, thread_id: error.thread_id, rpc_code: error.rpc_code ?? error.rpcCode,
|
|
49
53
|
message_id: error.message_id, client_message_id: error.client_message_id, journal_update_required: error.journal_update_required,
|
|
50
|
-
existing_message_id: error.existing_message_id,
|
|
54
|
+
existing_message_id: error.existing_message_id, unknown_options: error.unknown_options,
|
|
51
55
|
existing_attempt_id: error.existing_attempt_id, existing_receipt_path: error.existing_receipt_path,
|
|
52
56
|
supervision_update_required: error.supervision_update_required,
|
|
53
57
|
history_changed: error.history_changed, backup_path: error.backup_path, receipt_path: error.receipt_path,
|
|
@@ -62,10 +66,12 @@ export function fail(error, json) {
|
|
|
62
66
|
} }));
|
|
63
67
|
else {
|
|
64
68
|
console.error(`codexteer: ${message}`);
|
|
69
|
+
for (const line of remedy?.usage ?? []) console.error(`使い方: codexteer ${line}`);
|
|
70
|
+
if (remedy?.action) console.error(`対処: ${remedy.action}`);
|
|
65
71
|
if (error.execution_environment === "cloud" || (typeof error.code === "string" && error.code.startsWith("DESKTOP_CLOUD_"))) {
|
|
66
72
|
// Recovery metadata must survive an uncertain submission or a stale
|
|
67
73
|
// on-disk receipt. Never print the child error, prompt, or diagnostics.
|
|
68
|
-
for (const key of ["creation_status", "submission_status", "delivery_status", "attempt_id", "receipt_path", "existing_attempt_id", "existing_receipt_path", "task_id", "thread_id", "url"]) {
|
|
74
|
+
for (const key of ["creation_status", "submission_status", "delivery_status", "attempt_id", "receipt_path", "existing_message_id", "existing_attempt_id", "existing_receipt_path", "task_id", "thread_id", "url"]) {
|
|
69
75
|
if (typeof error[key] === "string") console.error(`${key}: ${JSON.stringify(error[key])}`);
|
|
70
76
|
}
|
|
71
77
|
if (error.receipt_update_required) console.error("receipt_update_required: true");
|
|
@@ -76,11 +82,13 @@ export function fail(error, json) {
|
|
|
76
82
|
process.exitCode = 1;
|
|
77
83
|
}
|
|
78
84
|
|
|
85
|
+
const usage = message => Object.assign(new Error(message), { code: "OPTIONS_INVALID" });
|
|
86
|
+
|
|
79
87
|
function takeOption(args, name, defaultValue) {
|
|
80
88
|
const index = args.indexOf(name);
|
|
81
89
|
if (index === -1 || (args.includes("--") && index > args.indexOf("--"))) return defaultValue;
|
|
82
90
|
const value = args[index + 1];
|
|
83
|
-
if (value == null || value.startsWith("--")) throw
|
|
91
|
+
if (value == null || value.startsWith("--")) throw usage(`${name} requires a value.`);
|
|
84
92
|
args.splice(index, 2);
|
|
85
93
|
return value;
|
|
86
94
|
}
|
|
@@ -123,7 +131,7 @@ function readMessage(parts) {
|
|
|
123
131
|
|
|
124
132
|
function backendOption(args, fallback) {
|
|
125
133
|
const backend = takeOption(args, "--backend", fallback);
|
|
126
|
-
if (backend != null && !["app-server", "ui"].includes(backend)) throw
|
|
134
|
+
if (backend != null && !["app-server", "ui"].includes(backend)) throw usage("--backend must be app-server or ui.");
|
|
127
135
|
return backend;
|
|
128
136
|
}
|
|
129
137
|
|
|
@@ -159,9 +167,9 @@ function printSupervisor(data) {
|
|
|
159
167
|
export async function main(argv) {
|
|
160
168
|
const args = [...argv];
|
|
161
169
|
try {
|
|
170
|
+
rejectRepeatedBinding(args);
|
|
162
171
|
const transport = validateRemoteTransport(takeOption(args, "--remote-transport", "native"));
|
|
163
172
|
const explicitHost = takeOption(args, "--host", undefined);
|
|
164
|
-
if (args.slice(0, args.includes("--") ? args.indexOf("--") : args.length).includes("--host")) throw new Error("Specify --host only once.");
|
|
165
173
|
if (explicitHost !== undefined) validateHostId(explicitHost);
|
|
166
174
|
const probe = [...args];
|
|
167
175
|
takeFlag(probe, "--json");
|
|
@@ -182,7 +190,7 @@ export async function main(argv) {
|
|
|
182
190
|
const ownArguments = probe.slice(0, probe.includes("--") ? probe.indexOf("--") : probe.length);
|
|
183
191
|
if (isControlTransport(transport) && host === "local" && command !== "remote" && !["help", "--help", "--version"].some(value => ownArguments.includes(value))) throw Object.assign(new Error("--remote-transport control requires a remote hostId link or --host."), { code: "REMOTE_HOST_REQUIRED" });
|
|
184
192
|
return await withTarget(host, contextThread, () => mainOnTarget(args), { transport });
|
|
185
|
-
} catch (error) { fail(error, argv.includes("--json")); }
|
|
193
|
+
} catch (error) { fail(error, argv.includes("--json"), argv); }
|
|
186
194
|
}
|
|
187
195
|
|
|
188
196
|
async function mainOnTarget(argv) {
|
|
@@ -196,7 +204,7 @@ async function mainOnTarget(argv) {
|
|
|
196
204
|
const connectionDependencies = observationConnection(connection);
|
|
197
205
|
const requiredNode = takeOption(args, "--require-node-version", undefined);
|
|
198
206
|
if (requiredNode !== undefined) {
|
|
199
|
-
if (!/^v\d+\.\d+\.\d+(?:-[\w.-]+)?(?:\+[\w.-]+)?$/.test(requiredNode)) throw
|
|
207
|
+
if (!/^v\d+\.\d+\.\d+(?:-[\w.-]+)?(?:\+[\w.-]+)?$/.test(requiredNode)) throw usage("--require-node-version requires a Node version such as v25.1.0.");
|
|
200
208
|
if (requiredNode !== process.version) throw Object.assign(new Error(`This supervision command requires Node ${requiredNode}, but is running ${process.version}. Regenerate the supervisor prompt with the intended Node runtime.`), { code: "NODE_VERSION_MISMATCH" });
|
|
201
209
|
}
|
|
202
210
|
if (takeFlag(args, "--version")) {
|
|
@@ -206,16 +214,15 @@ async function mainOnTarget(argv) {
|
|
|
206
214
|
}
|
|
207
215
|
if (args.length === 0 || takeFlag(args, "--help") || args[0] === "help") {
|
|
208
216
|
if (args[0] === "help") args.shift();
|
|
209
|
-
const
|
|
210
|
-
const
|
|
211
|
-
: /^(codex:\/\/|[0-9a-f]{8}-)/i.test(candidate) ? "send" : candidate;
|
|
212
|
-
const data = helpData(topic);
|
|
217
|
+
const errors = takeFlag(args, "--errors");
|
|
218
|
+
const data = helpData(topicFor(args) ?? args[0] ?? "overview", { errors });
|
|
213
219
|
success("help", data, json);
|
|
214
|
-
if (!json) console.log(renderHelp(data));
|
|
220
|
+
if (!json) console.log(errors ? renderErrors(data) : renderHelp(data));
|
|
215
221
|
return;
|
|
216
222
|
}
|
|
217
223
|
|
|
218
224
|
let command = args.shift();
|
|
225
|
+
if (command === "diff") throw usage("diff reads a Web Cloud task's recorded patch and is not available for local or remote Desktop tasks. Use read --include-output to inspect file changes.");
|
|
219
226
|
const control = isControlTransport();
|
|
220
227
|
if (targetHost() !== "local" && ["desktop", "debug-ui"].includes(command)) throw Object.assign(new Error("Desktop process/update and Accessibility diagnostics cannot target the remote host. No local operation was run."), { code: "REMOTE_HOST_OPERATION_UNSUPPORTED" });
|
|
221
228
|
if (targetHost() !== "local" && !["remote", "projects", "read", "status", "watch", "doctor", "supervise", "findings", "history", "instructions", "thread", "open", ...(control ? ["send"] : []), ...(remoteTransport() === "paired" ? ["threads", "resource", "checkpoint", "orchestrate"] : [])].includes(command)
|
|
@@ -229,14 +236,14 @@ async function mainOnTarget(argv) {
|
|
|
229
236
|
if (command === "remote") {
|
|
230
237
|
if (supervisor) throw Object.assign(new Error("A bound supervisor cannot enroll, pair or manage account credentials."), { code: "SUPERVISOR_MISMATCH" });
|
|
231
238
|
const action = args.shift(), codeStdin = takeFlag(args, "--code-stdin"), provider = takeOption(args, "--key-provider", undefined);
|
|
232
|
-
if (provider !== undefined && action !== "prepare") throw
|
|
233
|
-
if (!["prepare", "recover-key", "enroll", "status", "identity", "pair", "verify"].includes(action) || args.length || (codeStdin && action !== "pair") || (action === "pair" && !codeStdin)) throw
|
|
234
|
-
if (["prepare", "enroll"].includes(action) && targetHost() !== "local") throw
|
|
239
|
+
if (provider !== undefined && action !== "prepare") throw usage("--key-provider is only available for remote prepare.");
|
|
240
|
+
if (!["prepare", "recover-key", "enroll", "status", "identity", "pair", "verify"].includes(action) || args.length || (codeStdin && action !== "pair") || (action === "pair" && !codeStdin)) throw usage("Expected: remote prepare|recover-key|enroll|status|identity|verify, or --host HOST remote pair --code-stdin.");
|
|
241
|
+
if (["prepare", "enroll"].includes(action) && targetHost() !== "local") throw usage("remote prepare/enroll registers this local controller; omit --host.");
|
|
235
242
|
// Code input is never an argv option or a chat message.
|
|
236
|
-
if (action === "pair" && !/^remote-control:env_[A-Za-z0-9_-]+$/.test(targetHost())) throw new Error("remote pair requires --host remote-control:env_...");
|
|
243
|
+
if (action === "pair" && !/^remote-control:env_[A-Za-z0-9_-]+$/.test(targetHost())) throw Object.assign(new Error("remote pair requires --host remote-control:env_..."), { code: "REMOTE_PAIR_INPUT_INVALID" });
|
|
237
244
|
if (action === "pair" && process.stdin.isTTY) console.error("codexteer: Enter the selected host's fresh pairing code (hidden), then press Enter.");
|
|
238
245
|
const code = action === "pair" ? await readPairingCode() : undefined;
|
|
239
|
-
if (code && code.length > 128) throw new Error("Invalid pairing code input.");
|
|
246
|
+
if (code && code.length > 128) throw Object.assign(new Error("Invalid pairing code input."), { code: "REMOTE_PAIR_INPUT_INVALID" });
|
|
240
247
|
const data = await withDeviceAccount(context => action === "prepare" ? prepareDevice(context, { ...(provider ? { provider } : {}) }) : action === "recover-key" ? recoverDeviceKey(context) : action === "enroll" ? enrollDevice(context, { progress: text => console.error(`codexteer: ${text}`) })
|
|
241
248
|
: action === "pair" ? pairDevice(context, targetHost(), code)
|
|
242
249
|
: action === "verify" ? verifyDevice(context) : action === "identity" ? deviceIdentityInfo(context) : readDevice(context.home, context.identity).then(deviceStatus));
|
|
@@ -244,12 +251,13 @@ async function mainOnTarget(argv) {
|
|
|
244
251
|
}
|
|
245
252
|
if (command === "supervise" && ["register", "status", "list", "pause", "resume", "stop"].includes(args[0])) {
|
|
246
253
|
const action = args.shift(), owner = action === "register" ? takeOption(args, "--owner", "external") : undefined;
|
|
247
|
-
if (args.length !== (action === "list" ? 0 : 1)) throw
|
|
254
|
+
if (args.length !== (action === "list" ? 0 : 1)) throw usage(`Expected: supervise ${action}${action === "list" ? "" : " <THREAD>"}.`);
|
|
248
255
|
let data;
|
|
249
256
|
if (action === "register") {
|
|
250
|
-
if (!supervisor) throw
|
|
257
|
+
if (!supervisor) throw usage("supervise register requires the --supervisor ID from a generated prompt.");
|
|
251
258
|
data = await registerSupervisor(args[0], supervisor, { owner, connection });
|
|
252
259
|
} else {
|
|
260
|
+
if (supervisor && action === "list") throw Object.assign(new Error("A bound supervisor cannot list other supervisor sessions. Use supervise status <THREAD> for its own task, or run supervise list from the user's terminal without --supervisor."), { code: "SUPERVISOR_CONTROL_FORBIDDEN" });
|
|
253
261
|
if (supervisor && action !== "status") throw Object.assign(new Error("Supervisors cannot change their own control state. Run this control command from the user's terminal without --supervisor."), { code: "SUPERVISOR_CONTROL_FORBIDDEN" });
|
|
254
262
|
if (supervisor) {
|
|
255
263
|
const record = await getSupervisor(args[0]);
|
|
@@ -280,7 +288,7 @@ async function mainOnTarget(argv) {
|
|
|
280
288
|
else if (action === "list") options = { all: takeFlag(args, "--all"), limit: Number(takeOption(args, "--limit", "50")) };
|
|
281
289
|
const handlers = { create: createFinding, show: findingStatus, list: listFindings, resolve: resolveFinding, dismiss: dismissFinding, reopen: reopenFinding, evaluate: evaluateFinding, stats: findingStats };
|
|
282
290
|
const single = ["create", "list", "stats"].includes(action);
|
|
283
|
-
if (!Object.hasOwn(handlers, action) || args.length !== (single ? 1 : 2) || args.some(arg => arg.startsWith("--"))) throw
|
|
291
|
+
if (!Object.hasOwn(handlers, action) || args.length !== (single ? 1 : 2) || args.some(arg => arg.startsWith("--"))) throw usage("See codexteer help findings.");
|
|
284
292
|
if (supervisor) await assertSupervisor(args[0], supervisor, { connection });
|
|
285
293
|
data = single ? await handlers[action](args[0], options) : await handlers[action](args[0], args[1], options);
|
|
286
294
|
success(`findings.${action}`, data, json);
|
|
@@ -291,7 +299,7 @@ async function mainOnTarget(argv) {
|
|
|
291
299
|
if (supervisor) throw Object.assign(new Error("Managed supervision is bound to its existing task and cannot orchestrate a project."), { code: "SUPERVISOR_CONTROL_FORBIDDEN" });
|
|
292
300
|
const promptOnly = args[0] === "prompt";
|
|
293
301
|
if (promptOnly) args.shift();
|
|
294
|
-
else if (json) throw
|
|
302
|
+
else if (json) throw usage("orchestrate inherits agent output and does not support --json. Use help orchestrate --json or orchestrate prompt --project ID|NAME <GOAL> --json.");
|
|
295
303
|
const separator = promptOnly ? -1 : args.indexOf("--");
|
|
296
304
|
const ownArgs = separator < 0 ? args : args.slice(0, separator);
|
|
297
305
|
const agentArgs = separator < 0 ? [] : args.slice(separator + 1);
|
|
@@ -299,8 +307,8 @@ async function mainOnTarget(argv) {
|
|
|
299
307
|
const agent = takeOption(ownArgs, "--agent", promptOnly ? undefined : "claude");
|
|
300
308
|
const usePathCli = takeFlag(ownArgs, "--use-path-cli");
|
|
301
309
|
if (promptOnly && ownArgs[0] === "--") ownArgs.shift();
|
|
302
|
-
else
|
|
303
|
-
if (ownArgs.length !== 1) throw
|
|
310
|
+
else rejectUnknownOptions(ownArgs, "orchestrate", "See help orchestrate; agent arguments belong after --.", usage);
|
|
311
|
+
if (ownArgs.length !== 1) throw usage("Expected: orchestrate [prompt] --project <ID-or-exact-name> <GOAL>. Quote the goal as one argument, or use - for stdin.");
|
|
304
312
|
const goal = ownArgs[0] === "-" ? readFileSync(0, "utf8") : ownArgs[0];
|
|
305
313
|
if (promptOnly) {
|
|
306
314
|
const data = await prepareOrchestrationPrompt(project, goal, { agent, connection, usePathCli });
|
|
@@ -313,47 +321,49 @@ async function mainOnTarget(argv) {
|
|
|
313
321
|
args.shift();
|
|
314
322
|
const agent = takeOption(args, "--agent", "claude");
|
|
315
323
|
const usePathCli = takeFlag(args, "--use-path-cli");
|
|
316
|
-
if (args.length < 1 || args.length > 2 || args.some(arg => arg.startsWith("--"))) throw
|
|
317
|
-
if (supervisor) throw new Error("A supervisor cannot generate a replacement session.");
|
|
324
|
+
if (args.length < 1 || args.length > 2 || args.some(arg => arg.startsWith("--"))) throw usage("Expected: supervise prompt <THREAD> [MESSAGE]. See codexteer help supervise prompt.");
|
|
325
|
+
if (supervisor) throw Object.assign(new Error("A supervisor cannot generate a replacement session."), { code: "SUPERVISOR_CONTROL_FORBIDDEN" });
|
|
318
326
|
const data = await prepareSupervisorPrompt(args[0], args[1], { agent, connection, usePathCli });
|
|
319
327
|
success("supervise.prompt", data, json);
|
|
320
328
|
if (!json) console.log(data.prompt);
|
|
321
329
|
return;
|
|
322
330
|
}
|
|
323
331
|
if (command === "supervise") {
|
|
324
|
-
if (supervisor) throw new Error("A supervisor cannot start a replacement session.");
|
|
325
|
-
if (json) throw
|
|
332
|
+
if (supervisor) throw Object.assign(new Error("A supervisor cannot start a replacement session."), { code: "SUPERVISOR_CONTROL_FORBIDDEN" });
|
|
333
|
+
if (json) throw usage("supervise inherits agent output and does not support --json. Use help supervise --json or supervise prompt <THREAD> --json.");
|
|
326
334
|
const separator = args.indexOf("--");
|
|
327
335
|
const ownArgs = separator < 0 ? args : args.slice(0, separator);
|
|
328
336
|
const agentArgs = separator < 0 ? [] : args.slice(separator + 1);
|
|
329
337
|
const agent = takeOption(ownArgs, "--agent", "claude");
|
|
330
338
|
const usePathCli = takeFlag(ownArgs, "--use-path-cli");
|
|
331
|
-
if (ownArgs.length < 1 || ownArgs.length > 2 || ownArgs.some(arg => arg.startsWith("--"))) throw
|
|
339
|
+
if (ownArgs.length < 1 || ownArgs.length > 2 || ownArgs.some(arg => arg.startsWith("--"))) throw usage("Expected: supervise <THREAD> [MESSAGE] [--agent claude|codex] [--use-path-cli] [-- <AGENT-ARGS...>].");
|
|
332
340
|
process.exitCode = await superviseAgent(ownArgs[0], { agent, agentArgs, policy: ownArgs[1], connection, usePathCli });
|
|
333
341
|
return;
|
|
334
342
|
}
|
|
335
343
|
if (command === "resource") {
|
|
336
|
-
const action = args.shift(); let data;
|
|
344
|
+
const threadId = takeOption(args, "--thread", undefined), action = args.shift(); let data;
|
|
345
|
+
if (supervisor && threadId === undefined) throw Object.assign(new Error("A supervisor resource command requires its --thread target."), { code: "SUPERVISOR_CONTROL_FORBIDDEN" });
|
|
346
|
+
// Like read/watch, inspecting leases and giving one up stay available while intervention is paused.
|
|
347
|
+
if (supervisor) await assertSupervisor(threadId, supervisor, { connection, sending: ["acquire", "renew", "run"].includes(action) });
|
|
337
348
|
if (["acquire", "run"].includes(action)) {
|
|
338
|
-
const options = { owner: takeOption(args, "--owner", undefined), ttlMs: Number(takeOption(args, "--ttl-ms", "600000")), reason: takeOption(args, "--reason", ""), condition: takeOption(args, "--condition", ""), threadId
|
|
349
|
+
const options = { owner: takeOption(args, "--owner", undefined), ttlMs: Number(takeOption(args, "--ttl-ms", "600000")), reason: takeOption(args, "--reason", ""), condition: takeOption(args, "--condition", ""), threadId, cwd: takeOption(args, "--cwd", undefined) };
|
|
339
350
|
if (action === "run") {
|
|
340
351
|
const timeoutMs = Number(takeOption(args, "--timeout-ms", "0")), includeOutput = takeFlag(args, "--include-output"), execBackend = takeOption(args, "--exec-backend", "app-server"), remoteShell = takeOption(args, "--shell", undefined);
|
|
341
|
-
if (
|
|
342
|
-
if (args[1] !== "--" || args.length < 3) throw new Error("Expected: resource run <NAME> --owner NAME -- <COMMAND> [ARGS...]");
|
|
352
|
+
if (args[1] !== "--" || args.length < 3) throw usage("Expected: resource run <NAME> --owner NAME -- <COMMAND> [ARGS...]");
|
|
343
353
|
data = await runWithResource(args[0], args.slice(2), { ...options, timeoutMs, execBackend, remoteShell, supervisor, connection });
|
|
344
354
|
if (!includeOutput) delete data.output_tail;
|
|
345
355
|
if (!data.valid) process.exitCode = 1;
|
|
346
356
|
} else {
|
|
347
|
-
if (args.length !== 1) throw
|
|
357
|
+
if (args.length !== 1) throw usage("Expected: resource acquire <NAME> --owner NAME.");
|
|
348
358
|
data = await acquireResource(args[0], options);
|
|
349
359
|
}
|
|
350
360
|
} else if (["renew", "release"].includes(action)) {
|
|
351
361
|
const token = takeOption(args, "--token", undefined), ttlMs = action === "renew" ? Number(takeOption(args, "--ttl-ms", "600000")) : undefined;
|
|
352
|
-
if (args.length !== 1) throw
|
|
362
|
+
if (args.length !== 1) throw usage(`Expected: resource ${action} <NAME> --token TOKEN.`);
|
|
353
363
|
data = action === "renew" ? await renewResource(args[0], token, { ttlMs }) : await releaseResource(args[0], token);
|
|
354
364
|
} else if (action === "status" && args.length === 1) data = await resourceStatus(args[0]);
|
|
355
365
|
else if (action === "list" && args.length === 0) data = await listResources();
|
|
356
|
-
else throw
|
|
366
|
+
else throw usage("See codexteer help resource.");
|
|
357
367
|
success(`resource.${action}`, data, json);
|
|
358
368
|
if (!json) console.log(JSON.stringify(data, null, 2));
|
|
359
369
|
return;
|
|
@@ -364,27 +374,27 @@ async function mainOnTarget(argv) {
|
|
|
364
374
|
let data;
|
|
365
375
|
if (action === "capture") {
|
|
366
376
|
const paths = takeOptions(args, "--path"), excludes = takeOptions(args, "--exclude");
|
|
367
|
-
if (args.length !== 2) throw
|
|
377
|
+
if (args.length !== 2) throw usage("Expected: checkpoint capture <THREAD> <NAME> --path PATH ...");
|
|
368
378
|
data = await createCheckpoint(args[0], args[1], { paths, excludes });
|
|
369
379
|
} else if (action === "run") {
|
|
370
380
|
const timeoutMs = Number(takeOption(args, "--timeout-ms", "0")), execBackend = takeOption(args, "--exec-backend", "app-server"), remoteShell = takeOption(args, "--shell", undefined);
|
|
371
381
|
if (supervisor && execBackend === "thread-shell") await assertSupervisor(args[0], supervisor, { connection, sending: true });
|
|
372
|
-
if (args[2] !== "--" || args.length < 4) throw
|
|
382
|
+
if (args[2] !== "--" || args.length < 4) throw usage("Expected: checkpoint run <THREAD> <ID> -- <COMMAND> [ARGS...]");
|
|
373
383
|
data = await runCheckpoint(args[0], args[1], args.slice(3), { timeoutMs, execBackend, remoteShell, supervisor, connection });
|
|
374
384
|
if (!includeOutput) delete data.output_tail;
|
|
375
385
|
if (!data.valid) process.exitCode = 1;
|
|
376
386
|
} else if (action === "attach") {
|
|
377
387
|
const refs = takeOptions(args, "--artifact");
|
|
378
|
-
if (args.length !== 2) throw
|
|
388
|
+
if (args.length !== 2) throw usage("Expected: checkpoint attach <THREAD> <ID> --artifact FILE ...");
|
|
379
389
|
data = await attachArtifacts(args[0], args[1], refs);
|
|
380
390
|
} else if (action === "list") {
|
|
381
|
-
if (args.length !== 1) throw
|
|
391
|
+
if (args.length !== 1) throw usage("Expected: checkpoint list <THREAD>.");
|
|
382
392
|
data = await listCheckpoints(args[0]);
|
|
383
393
|
} else if (["show", "verify"].includes(action)) {
|
|
384
|
-
if (args.length !== 2) throw
|
|
394
|
+
if (args.length !== 2) throw usage(`Expected: checkpoint ${action} <THREAD> <ID>.`);
|
|
385
395
|
data = action === "verify" ? await verifyCheckpoint(args[0], args[1]) : checkpointSummary(await getCheckpoint(args[0], args[1]), includeOutput);
|
|
386
396
|
if (action === "verify" && !data.valid) process.exitCode = 1;
|
|
387
|
-
} else throw
|
|
397
|
+
} else throw usage("See codexteer help checkpoint.");
|
|
388
398
|
success(`checkpoint.${action}`, data, json);
|
|
389
399
|
if (!json) console.log(JSON.stringify(data, null, 2));
|
|
390
400
|
return;
|
|
@@ -393,18 +403,18 @@ async function mainOnTarget(argv) {
|
|
|
393
403
|
const action = args.shift();
|
|
394
404
|
if (action === "list") {
|
|
395
405
|
const all = takeFlag(args, "--all");
|
|
396
|
-
if (args.length !== 1) throw
|
|
406
|
+
if (args.length !== 1) throw usage("Expected: instructions list <THREAD> [--all].");
|
|
397
407
|
if (supervisor) await assertSupervisor(args[0], supervisor, { connection });
|
|
398
408
|
const data = await listInstructions(args[0], { all });
|
|
399
409
|
success("instructions.list", data, json);
|
|
400
410
|
if (!json) { for (const entry of data.instructions) console.log(`${entry.id} ${entry.instruction_status} ${entry.metadata?.source ?? "unspecified"}\n${entry.body}`); if (data.pending_changes.length) console.log(`配送未確認: ${data.pending_changes.map(e => e.id).join(", ")}`); }
|
|
401
411
|
return;
|
|
402
412
|
}
|
|
403
|
-
if (action !== "retract") throw
|
|
413
|
+
if (action !== "retract") throw usage("See codexteer help instructions.");
|
|
404
414
|
if (connection === "desktop") throw desktopReadOnly();
|
|
405
415
|
const reason = takeOption(args, "--reason", undefined), source = takeOption(args, "--source", undefined), basedOn = takeOption(args, "--based-on", undefined);
|
|
406
416
|
const dryRun = takeFlag(args, "--dry-run"), newTurn = takeFlag(args, "--new-turn");
|
|
407
|
-
if (args.length !== 2 || !reason?.trim()) throw
|
|
417
|
+
if (args.length !== 2 || !reason?.trim()) throw usage("Expected: instructions retract <THREAD> <MESSAGE-ID> --reason TEXT.");
|
|
408
418
|
const data = await sendTrackedMessage(args[0], reason, { retracts: args[1], source, basedOn, dryRun, newTurn, supervisor, connection });
|
|
409
419
|
success("instructions.retract", data, json);
|
|
410
420
|
if (!json) console.log(dryRun ? "撤回の送信プレビューです。" : `撤回を受け付けました。message_id: ${data.message_id}`);
|
|
@@ -416,7 +426,7 @@ async function mainOnTarget(argv) {
|
|
|
416
426
|
const pending = takeFlag(args, "--pending");
|
|
417
427
|
const report = action === "mark" ? { status: takeOption(args, "--status", undefined), note: takeOption(args, "--note", undefined), evidence: takeOptions(args, "--evidence"), by: takeOption(args, "--by", "local") } : {};
|
|
418
428
|
const [threadId, id] = args;
|
|
419
|
-
if (!threadId || args.length > 2 || !["list", "show", "check", "mark"].includes(action) || (["show", "mark"].includes(action) && !id) || (action === "list" && id)) throw
|
|
429
|
+
if (!threadId || args.length > 2 || !["list", "show", "check", "mark"].includes(action) || (["show", "mark"].includes(action) && !id) || (action === "list" && id)) throw usage("See codexteer help history.");
|
|
420
430
|
if (supervisor) await assertSupervisor(threadId, supervisor, { connection });
|
|
421
431
|
const data = action === "list" ? await listMessages(threadId, { includeText, pending }) : action === "show" ? messageSummary(await getMessage(threadId, id), includeText) : action === "check" ? await reconcileMessages(threadId, id, { includeText }, connectionDependencies) : await markMessage(threadId, id, report);
|
|
422
432
|
success(`history.${action}`, data, json);
|
|
@@ -437,14 +447,14 @@ async function mainOnTarget(argv) {
|
|
|
437
447
|
if (stream) json = true;
|
|
438
448
|
if (command === "watch") {
|
|
439
449
|
const until = takeOption(args, "--until", undefined), timeout = takeOption(args, "--timeout-ms", undefined);
|
|
440
|
-
if (stream && (until !== undefined || timeout !== undefined)) throw
|
|
450
|
+
if (stream && (until !== undefined || timeout !== undefined)) throw usage("--stream runs until cancelled; do not combine it with --until or --timeout-ms. See codexteer help monitor.");
|
|
441
451
|
const notify = takeOption(args, "--notify", stream ? "digest" : "all"), settle = takeOption(args, "--settle-ms", undefined), maxHold = takeOption(args, "--max-hold-ms", undefined);
|
|
442
|
-
if (!stream && (notify === "digest" || settle !== undefined || maxHold !== undefined)) throw
|
|
443
|
-
if (notify !== "digest" && (settle !== undefined || maxHold !== undefined)) throw
|
|
452
|
+
if (!stream && (notify === "digest" || settle !== undefined || maxHold !== undefined)) throw usage("--notify digest, --settle-ms and --max-hold-ms require --stream.");
|
|
453
|
+
if (notify !== "digest" && (settle !== undefined || maxHold !== undefined)) throw usage("--settle-ms and --max-hold-ms require --notify digest.");
|
|
444
454
|
Object.assign(options, notificationOptions({ notify, ...(settle === undefined ? {} : { settleMs: Number(settle) }), ...(maxHold === undefined ? {} : { maxHoldMs: Number(maxHold) }) }));
|
|
445
455
|
Object.assign(options, { watch: true, until: until ?? "change", timeoutMs: Number(timeout ?? "30000"), pollMs: Number(takeOption(args, "--poll-ms", "1000")) });
|
|
446
456
|
}
|
|
447
|
-
if (args.length !== 1) throw
|
|
457
|
+
if (args.length !== 1) throw usage(`Expected: ${command} <THREAD>. See codexteer help ${command}.`);
|
|
448
458
|
if (stream) { await monitorCommand(args[0], options, connectionDependencies); return; }
|
|
449
459
|
const data = await observeThread(args[0], options, connectionDependencies);
|
|
450
460
|
data.connection_mode = connection;
|
|
@@ -457,10 +467,10 @@ async function mainOnTarget(argv) {
|
|
|
457
467
|
const backend = backendOption(args, "app-server");
|
|
458
468
|
if (connection === "desktop" && backend === "ui") throw desktopReadOnly();
|
|
459
469
|
const target = takeOption(args, "--thread", undefined);
|
|
460
|
-
if (target !== undefined && backend !== "app-server") throw
|
|
470
|
+
if (target !== undefined && backend !== "app-server") throw usage("doctor --thread requires --backend app-server.");
|
|
461
471
|
const threadId = target === undefined ? undefined : normalizeThreadId(target);
|
|
462
472
|
if (supervisor && threadId) await assertSupervisorRegistration(threadId, supervisor, { connection });
|
|
463
|
-
if (args.length) throw
|
|
473
|
+
if (args.length) throw usage(`Unexpected argument: ${args[0]}`);
|
|
464
474
|
if (targetHost() !== "local" && backend === "ui") throw remoteReadOnly();
|
|
465
475
|
const report = backend === "ui" ? desktopDoctor() : await appServerDoctor({ threadId, ...connectionDependencies });
|
|
466
476
|
if (report.connection) Object.assign(report.connection, { mode: connection, observation_only: connection === "desktop" || (targetHost() !== "local" && !control), ...hostFields() });
|
|
@@ -492,7 +502,7 @@ async function mainOnTarget(argv) {
|
|
|
492
502
|
if (command === "desktop") {
|
|
493
503
|
const action = args.shift();
|
|
494
504
|
if (action === "update-status") {
|
|
495
|
-
if (args.length) throw
|
|
505
|
+
if (args.length) throw usage(`Unexpected argument: ${args[0]}`);
|
|
496
506
|
const data = await desktopUpdateStatus();
|
|
497
507
|
success("desktop.update-status", data, json);
|
|
498
508
|
if (!json) console.log(JSON.stringify(data, null, 2));
|
|
@@ -502,15 +512,15 @@ async function mainOnTarget(argv) {
|
|
|
502
512
|
if (supervisor) throw Object.assign(new Error("A supervisor cannot update or restart Desktop. Run this command from the user's terminal without --supervisor."), { code: "SUPERVISOR_CONTROL_FORBIDDEN" });
|
|
503
513
|
const dryRun = takeFlag(args, "--dry-run");
|
|
504
514
|
const timeoutMs = Number(takeOption(args, "--timeout-ms", "600000"));
|
|
505
|
-
if (args.length) throw
|
|
515
|
+
if (args.length) throw usage(`Unexpected argument: ${args[0]}`);
|
|
506
516
|
const data = await launchDesktopUpdate({ dryRun, timeoutMs });
|
|
507
517
|
success("desktop.update", data, json);
|
|
508
518
|
if (!json) console.log(dryRun ? "Dry run: official update, relaunch, normal quit, wrapper startup, and verification." : `Update worker started (${data.worker_pid}). Check: ${data.status_command}`);
|
|
509
519
|
return;
|
|
510
520
|
}
|
|
511
|
-
if (action !== "start") throw
|
|
521
|
+
if (action !== "start") throw usage("Expected: codexteer desktop start|update|update-status");
|
|
512
522
|
const dryRun = takeFlag(args, "--dry-run");
|
|
513
|
-
if (args.length) throw
|
|
523
|
+
if (args.length) throw usage(`Unexpected argument: ${args[0]}`);
|
|
514
524
|
const data = await startDesktop({ dryRun });
|
|
515
525
|
success("desktop.start", data, json);
|
|
516
526
|
if (!json) console.log(dryRun ? "Dry run: would launch Desktop with the shared App Server wrapper." : "Desktop shared App Server is ready.");
|
|
@@ -519,9 +529,9 @@ async function mainOnTarget(argv) {
|
|
|
519
529
|
|
|
520
530
|
if (command === "projects") {
|
|
521
531
|
if (connection === "desktop") throw desktopReadOnly();
|
|
522
|
-
if (args.shift() !== "list") throw
|
|
532
|
+
if (args.shift() !== "list") throw usage("Expected: codexteer projects list [--remote | --all] [--host HOST-ID]");
|
|
523
533
|
const remote = takeFlag(args, "--remote"), all = takeFlag(args, "--all");
|
|
524
|
-
if (args.length || (remote && all) || (all && targetHost() !== "local")) throw
|
|
534
|
+
if (args.length || (remote && all) || (all && targetHost() !== "local")) throw usage("Use projects list [--remote | --all] or projects list --host HOST-ID.");
|
|
525
535
|
const saved = targetHost() !== "local" && remoteTransport() === "paired" ? await listRemoteProjects()
|
|
526
536
|
: remote || all || targetHost() !== "local" ? await registeredRemoteProjects() : [];
|
|
527
537
|
const projects = [...(!remote && (all || targetHost() === "local") ? await listProjects() : []),
|
|
@@ -532,15 +542,15 @@ async function mainOnTarget(argv) {
|
|
|
532
542
|
}
|
|
533
543
|
|
|
534
544
|
if (command === "threads") {
|
|
535
|
-
if (args.shift() !== "list") throw
|
|
545
|
+
if (args.shift() !== "list") throw usage("Expected: codexteer threads list");
|
|
536
546
|
const limit = Number.parseInt(takeOption(args, "--limit", "20"), 10);
|
|
537
547
|
const desktopOnly = takeFlag(args, "--desktop-only");
|
|
538
548
|
const cursor = takeOption(args, "--cursor", undefined), projectId = takeOption(args, "--project", undefined), search = takeOption(args, "--search", undefined);
|
|
539
|
-
if (args.length > 0) throw
|
|
549
|
+
if (args.length > 0) throw usage(`Unexpected argument: ${args[0]}`);
|
|
540
550
|
if (!Number.isInteger(limit) || limit < 1 || limit > 500) {
|
|
541
|
-
throw
|
|
551
|
+
throw usage("--limit must be an integer between 1 and 500.");
|
|
542
552
|
}
|
|
543
|
-
if (targetHost() === "local" && [cursor, projectId, search].some(value => value !== undefined)) throw
|
|
553
|
+
if (targetHost() === "local" && [cursor, projectId, search].some(value => value !== undefined)) throw usage("--cursor/--project/--search currently require a paired remote host.");
|
|
544
554
|
const data = targetHost() === "local" ? { threads: await listLocalThreads({ limit, desktopOnly }) } : await listRemoteThreads({ limit, desktopOnly, cursor, projectId, search });
|
|
545
555
|
const threads = data.threads;
|
|
546
556
|
success("threads.list", { ...data, count: threads.length }, json);
|
|
@@ -554,7 +564,7 @@ async function mainOnTarget(argv) {
|
|
|
554
564
|
if (connection === "desktop") throw desktopReadOnly();
|
|
555
565
|
if (supervisor) throw Object.assign(new Error("Managed supervision is bound to its existing task. Verify a project creation receipt without --supervisor."), { code: "SUPERVISOR_CONTROL_FORBIDDEN" });
|
|
556
566
|
const timeoutMs = Number(takeOption(args, "--timeout-ms", "120000"));
|
|
557
|
-
if (args.length !== 1 || args[0].startsWith("--")) throw
|
|
567
|
+
if (args.length !== 1 || args[0].startsWith("--")) throw usage("Expected: thread verify <ATTEMPT-ID> [--timeout-ms N].");
|
|
558
568
|
const data = await (targetHost() === "local" ? verifyProjectThread : verifyRemoteProjectThread)(args[0], { timeoutMs });
|
|
559
569
|
success("thread.verify", data, json);
|
|
560
570
|
if (!json) console.log(`Verified existing ${data.deep_link}; creation and initial instruction were not repeated.`);
|
|
@@ -572,7 +582,7 @@ async function mainOnTarget(argv) {
|
|
|
572
582
|
options.timeoutMs = Number(options.timeoutMs);
|
|
573
583
|
}
|
|
574
584
|
const literal = args.indexOf("--");
|
|
575
|
-
|
|
585
|
+
rejectUnknownOptions(args.slice(0, literal < 0 ? args.length : literal), "thread create", "Put an instruction that starts with -- after --.", usage);
|
|
576
586
|
const message = readMessage(args);
|
|
577
587
|
const data = await (targetHost() === "local" ? createProjectThread : createRemoteProjectThread)(selector, message, options);
|
|
578
588
|
success("thread.create", data, json);
|
|
@@ -581,7 +591,7 @@ async function mainOnTarget(argv) {
|
|
|
581
591
|
}
|
|
582
592
|
if (action === "repair") {
|
|
583
593
|
const apply = takeFlag(args, "--apply"), dryRun = takeFlag(args, "--dry-run");
|
|
584
|
-
if (args.length !== 1 || args[0].startsWith("--") || (apply && dryRun)) throw
|
|
594
|
+
if (args.length !== 1 || args[0].startsWith("--") || (apply && dryRun)) throw usage("Expected: thread repair <THREAD> [--dry-run | --apply].");
|
|
585
595
|
if (connection === "desktop") throw desktopReadOnly();
|
|
586
596
|
if (supervisor) throw Object.assign(new Error("A supervisor cannot repair task history. Run this command from the user's terminal without --supervisor."), { code: "SUPERVISOR_CONTROL_FORBIDDEN" });
|
|
587
597
|
const data = await repairTurns(args[0], { apply });
|
|
@@ -589,8 +599,8 @@ async function mainOnTarget(argv) {
|
|
|
589
599
|
if (!json) console.log(JSON.stringify(data, null, 2));
|
|
590
600
|
return;
|
|
591
601
|
}
|
|
592
|
-
if (action !== "resolve") throw
|
|
593
|
-
if (args.length !== 1) throw
|
|
602
|
+
if (action !== "resolve") throw usage("Expected: codexteer thread create|verify|resolve|repair. See help thread.");
|
|
603
|
+
if (args.length !== 1) throw usage("thread resolve requires exactly one thread ID or URL.");
|
|
594
604
|
const threadId = normalizeThreadId(args[0]);
|
|
595
605
|
const data = { thread_id: threadId, deep_link: threadDeepLink(threadId) };
|
|
596
606
|
success("thread.resolve", data, json);
|
|
@@ -599,7 +609,7 @@ async function mainOnTarget(argv) {
|
|
|
599
609
|
}
|
|
600
610
|
|
|
601
611
|
if (command === "open") {
|
|
602
|
-
if (args.length !== 1) throw
|
|
612
|
+
if (args.length !== 1) throw usage("open requires exactly one thread ID or URL.");
|
|
603
613
|
const threadId = normalizeThreadId(args[0]);
|
|
604
614
|
const data = targetHost() === "local" ? openDesktopThread(threadId) : await ensureRemoteDesktopSubscription(threadId);
|
|
605
615
|
success("open", data, json);
|
|
@@ -609,7 +619,7 @@ async function mainOnTarget(argv) {
|
|
|
609
619
|
|
|
610
620
|
if (command === "debug-ui") {
|
|
611
621
|
const waitMs = Number.parseInt(takeOption(args, "--wait-ms", "1500"), 10);
|
|
612
|
-
if (args.length !== 1) throw
|
|
622
|
+
if (args.length !== 1) throw usage("debug-ui requires exactly one thread ID or URL.");
|
|
613
623
|
const threadId = normalizeThreadId(args[0]);
|
|
614
624
|
const data = inspectDesktopUi(threadId, { waitMs });
|
|
615
625
|
success("debug-ui", data, json);
|
|
@@ -628,22 +638,23 @@ async function mainOnTarget(argv) {
|
|
|
628
638
|
if (remoteTransport() !== "paired") throw remoteReadOnly();
|
|
629
639
|
throw Object.assign(new Error("UI sending on the remote host is unsupported. No UI or other delivery route was used."), { code: "REMOTE_UI_UNSUPPORTED", sent: false, delivery_status: "not_sent" });
|
|
630
640
|
}
|
|
631
|
-
if (supervisor && backend !== "app-server") throw
|
|
641
|
+
if (supervisor && backend !== "app-server") throw usage("Managed supervision requires the app-server backend.");
|
|
632
642
|
const explicitSound = takeFlag(args, "--sound");
|
|
633
643
|
const noSound = takeFlag(args, "--no-sound");
|
|
634
|
-
if (explicitSound && noSound) throw
|
|
635
|
-
if (explicitSound && backend !== "app-server") throw
|
|
644
|
+
if (explicitSound && noSound) throw usage("--sound and --no-sound cannot be combined.");
|
|
645
|
+
if (explicitSound && backend !== "app-server") throw usage("--sound requires --backend app-server so receipt can be confirmed.");
|
|
636
646
|
const newTurn = takeFlag(args, "--new-turn");
|
|
637
647
|
const directive = { source: takeOption(args, "--source", undefined), kind: takeOption(args, "--kind", undefined), evidence: takeOptions(args, "--evidence"), basedOn: takeOption(args, "--based-on", undefined), supersedes: takeOption(args, "--supersedes", undefined), expiresAt: takeOption(args, "--expires-at", undefined), checkpoint: takeOption(args, "--checkpoint", undefined), finding: takeOption(args, "--finding", undefined) };
|
|
638
|
-
if (backend === "ui" && (directive.source || directive.kind || directive.evidence.length || directive.basedOn || directive.supersedes || directive.expiresAt || directive.checkpoint || directive.finding)) throw
|
|
648
|
+
if (backend === "ui" && (directive.source || directive.kind || directive.evidence.length || directive.basedOn || directive.supersedes || directive.expiresAt || directive.checkpoint || directive.finding)) throw usage("Directive metadata requires --backend app-server.");
|
|
639
649
|
const keepFocus = takeFlag(args, "--keep-focus");
|
|
640
650
|
const waitInput = takeOption(args, "--wait-ms", undefined);
|
|
641
|
-
if (backend !== "ui" && (keepFocus || waitInput != null)) throw
|
|
651
|
+
if (backend !== "ui" && (keepFocus || waitInput != null)) throw usage("--keep-focus and --wait-ms require --backend ui.");
|
|
642
652
|
const waitMs = Number(waitInput ?? "1500");
|
|
653
|
+
rejectUnknownOptions(args.slice(0, args.includes("--") ? args.indexOf("--") : args.length), "send", "Put instruction text that starts with -- after --.", usage);
|
|
643
654
|
const threadInput = args.shift();
|
|
644
|
-
if (!threadInput) throw
|
|
655
|
+
if (!threadInput) throw usage("send requires a thread ID or codex://threads URL.");
|
|
645
656
|
const message = readMessage(args);
|
|
646
|
-
if (message.trim() === "") throw
|
|
657
|
+
if (message.trim() === "") throw usage("send requires a non-empty message.");
|
|
647
658
|
const threadId = normalizeThreadId(threadInput);
|
|
648
659
|
const data = backend === "app-server"
|
|
649
660
|
? await sendTrackedMessage(threadId, message, { dryRun, newTurn, ...directive, supervisor, connection })
|
|
@@ -660,6 +671,6 @@ async function mainOnTarget(argv) {
|
|
|
660
671
|
if (!noSound && !dryRun && data.sent === true && !data.sound.played) console.error(`codexteer: Input was accepted, but the sound could not be played (${data.sound.reason}).`);
|
|
661
672
|
}
|
|
662
673
|
} catch (error) {
|
|
663
|
-
fail(error, json);
|
|
674
|
+
fail(error, json, argv);
|
|
664
675
|
}
|
|
665
676
|
}
|