@bitkyc08/opencodex 2.19.0 → 2.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/gui/dist/assets/index-DF_UFrGS.css +1 -0
- package/gui/dist/assets/index-DSK3S5HY.js +76 -0
- package/gui/dist/index.html +2 -2
- package/package.json +1 -1
- package/src/adapters/mimo-free.ts +17 -0
- package/src/adapters/openai-chat.ts +367 -32
- package/src/adapters/registry.ts +144 -0
- package/src/chat/inbound.ts +13 -7
- package/src/cli/claude.ts +2 -1
- package/src/cli/combo.ts +3 -0
- package/src/cli/dispatch.ts +8 -0
- package/src/cli/export-command.ts +2 -2
- package/src/cli/help.ts +2 -0
- package/src/cli/index.ts +3 -2
- package/src/cli/lab.ts +135 -1
- package/src/cli/minimax.ts +491 -0
- package/src/cli/models-runtime.ts +22 -1
- package/src/cli/models.ts +67 -2
- package/src/cli/opencode.ts +2 -1
- package/src/cli/registry.ts +22 -2
- package/src/clients/config-export.ts +125 -7
- package/src/codex/app-server-processes.ts +57 -2
- package/src/codex/app-server-restart-service.ts +232 -0
- package/src/codex/catalog/aggregation.ts +10 -1
- package/src/codex/catalog/effort.ts +15 -3
- package/src/codex/catalog/parsing.ts +3 -1
- package/src/codex/catalog/provider-fetch.ts +45 -5
- package/src/codex/catalog/sync.ts +74 -4
- package/src/codex/convergence.ts +2 -0
- package/src/combos/index.ts +1 -0
- package/src/combos/request.ts +30 -0
- package/src/combos/types.ts +6 -0
- package/src/config.ts +52 -0
- package/src/generated/compatibility-version.json +224 -76
- package/src/images/loop.ts +11 -1
- package/src/integrations/registry.ts +7 -0
- package/src/lab/conformance/jcs.ts +42 -2
- package/src/lab/conformance/negative-controls.ts +6 -2
- package/src/lab/conformance/runner.ts +16 -5
- package/src/lab/fabric/observe.ts +49 -14
- package/src/lab/index.ts +16 -0
- package/src/lab/ledger/purge.ts +152 -83
- package/src/lab/ledger/store.ts +168 -54
- package/src/lab/observe/from-conformance.ts +8 -6
- package/src/lab/observe/from-live.ts +8 -2
- package/src/lab/paths.ts +23 -0
- package/src/lab/public/bundle.ts +217 -0
- package/src/lab/public/community-authority.ts +175 -0
- package/src/lab/public/community-files.ts +29 -0
- package/src/lab/public/community.ts +479 -0
- package/src/lab/public/file-safety.ts +155 -0
- package/src/lab/public/ids.ts +26 -0
- package/src/lab/public/index.ts +16 -0
- package/src/lab/public/mutation-lock.ts +424 -0
- package/src/lab/public/operator.ts +353 -0
- package/src/lab/public/origin-purge.ts +79 -0
- package/src/lab/public/origin.ts +203 -0
- package/src/lab/public/privacy.ts +143 -0
- package/src/lab/public/private-file.ts +261 -0
- package/src/lab/public/project.ts +124 -0
- package/src/lab/public/purge-test-fault.ts +21 -0
- package/src/lab/public/purge.ts +223 -0
- package/src/lab/public/registry.ts +44 -0
- package/src/lab/public/revocation.ts +252 -0
- package/src/lab/public/signature.ts +219 -0
- package/src/lab/public/storage.ts +105 -0
- package/src/lab/public/strict-json.ts +206 -0
- package/src/lab/public/time.ts +26 -0
- package/src/lab/public/types.ts +172 -0
- package/src/lab/public/validate.ts +391 -0
- package/src/lib/codex-restart-contract.ts +120 -0
- package/src/lib/lab-activation.ts +109 -47
- package/src/lib/lab-live-pinned-sender.ts +16 -5
- package/src/lib/pinned-http.ts +70 -16
- package/src/lib/self-launch-argv.ts +15 -0
- package/src/lib/state-store-registrations.ts +2 -0
- package/src/lib/upstream-reachability.ts +4 -0
- package/src/lib/windows-elevation.ts +10 -1
- package/src/providers/derive.ts +24 -4
- package/src/providers/registry.ts +7 -3
- package/src/providers/request-pacing.ts +310 -0
- package/src/providers/service-tier.ts +143 -0
- package/src/providers/static-model-discovery.ts +86 -0
- package/src/reasoning-effort.ts +27 -1
- package/src/router.ts +23 -6
- package/src/routing/capability.ts +4 -2
- package/src/routing/compatibility/behavior.ts +5 -1
- package/src/server/adapter-resolve.ts +2 -32
- package/src/server/auth-cors.ts +8 -0
- package/src/server/chat-completions.ts +74 -36
- package/src/server/chat-native-sse.ts +331 -0
- package/src/server/chat-native.ts +371 -0
- package/src/server/management/combo-routes.ts +16 -2
- package/src/server/management/config-routes.ts +6 -4
- package/src/server/management/context.ts +17 -0
- package/src/server/management/lab-routes.ts +181 -19
- package/src/server/management/model-routes.ts +76 -2
- package/src/server/management/model-rows.ts +8 -0
- package/src/server/management/provider-capability-config.ts +48 -0
- package/src/server/management/provider-routes.ts +76 -4
- package/src/server/management/system-restart.ts +4 -2
- package/src/server/management/system-routes.ts +38 -0
- package/src/server/relay.ts +17 -3
- package/src/server/responses/compact.ts +4 -1
- package/src/server/responses/core.ts +257 -46
- package/src/server/responses/empty-completion-guard.ts +276 -0
- package/src/server/responses/fetch-helpers.ts +35 -4
- package/src/server/responses/pacing-overload.ts +13 -0
- package/src/server/responses/policy-fallback.ts +16 -2
- package/src/server/responses/terminal-guard.ts +1 -1
- package/src/server/responses/upstream-error.ts +5 -0
- package/src/server/responses.ts +17 -2
- package/src/types.ts +66 -4
- package/src/update/index.ts +6 -5
- package/src/update/job.ts +5 -6
- package/src/update/notify.ts +5 -3
- package/src/usage/log.ts +11 -1
- package/gui/dist/assets/index-CQ7bIKee.css +0 -1
- package/gui/dist/assets/index-D_JUZLEC.js +0 -76
package/src/chat/inbound.ts
CHANGED
|
@@ -8,11 +8,23 @@
|
|
|
8
8
|
export class ChatCompletionsRequestError extends Error {}
|
|
9
9
|
|
|
10
10
|
type Rec = Record<string, unknown>;
|
|
11
|
+
type ChatCompletionsRoutingBody = Rec & { model: string; messages: unknown[] };
|
|
11
12
|
|
|
12
13
|
function isRec(v: unknown): v is Rec {
|
|
13
14
|
return !!v && typeof v === "object" && !Array.isArray(v);
|
|
14
15
|
}
|
|
15
16
|
|
|
17
|
+
/** Validate only the fields needed before Chat routing, without projecting the body. */
|
|
18
|
+
export function assertChatCompletionsRoutingBody(raw: unknown): asserts raw is ChatCompletionsRoutingBody {
|
|
19
|
+
if (!isRec(raw)) throw new ChatCompletionsRequestError("request body must be a JSON object");
|
|
20
|
+
if (typeof raw.model !== "string" || raw.model.length === 0) {
|
|
21
|
+
throw new ChatCompletionsRequestError("model is required");
|
|
22
|
+
}
|
|
23
|
+
if (!Array.isArray(raw.messages) || raw.messages.length === 0) {
|
|
24
|
+
throw new ChatCompletionsRequestError("messages must be a non-empty array");
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
16
28
|
const OUTPUT_CONFIG_EFFORTS = new Set(["minimal", "low", "medium", "high", "xhigh", "max", "ultra"]);
|
|
17
29
|
const OUTPUT_CONFIG_SUMMARIES = new Set(["auto", "concise", "detailed", "none"]);
|
|
18
30
|
|
|
@@ -227,13 +239,7 @@ function resolveReasoningSummary(raw: Rec): string | undefined {
|
|
|
227
239
|
* Throws ChatCompletionsRequestError (-> 400) on malformed input.
|
|
228
240
|
*/
|
|
229
241
|
export function chatCompletionsToResponsesBody(raw: unknown): Rec {
|
|
230
|
-
|
|
231
|
-
if (typeof raw.model !== "string" || raw.model.length === 0) {
|
|
232
|
-
throw new ChatCompletionsRequestError("model is required");
|
|
233
|
-
}
|
|
234
|
-
if (!Array.isArray(raw.messages) || raw.messages.length === 0) {
|
|
235
|
-
throw new ChatCompletionsRequestError("messages must be a non-empty array");
|
|
236
|
-
}
|
|
242
|
+
assertChatCompletionsRoutingBody(raw);
|
|
237
243
|
|
|
238
244
|
const systemParts: string[] = [];
|
|
239
245
|
const input: Rec[] = [];
|
package/src/cli/claude.ts
CHANGED
|
@@ -19,6 +19,7 @@ import { configuredAdminToken } from "../lib/admin-secrets";
|
|
|
19
19
|
import { PROXY_MARKER, ownAdmissionTokens, defaultAuthDetectDeps, detectClaudeAuth, type AuthDetectDeps } from "../claude/auth-detect";
|
|
20
20
|
import { resolveClaudeAuthMode } from "../claude/auth-mode";
|
|
21
21
|
import { withProcessRuntimeProvenance } from "../lib/bun-runtime";
|
|
22
|
+
import { selfLaunchArgv } from "../lib/self-launch-argv";
|
|
22
23
|
import { ANTHROPIC_PARENT_ENV_SLOTS, trustedNodeLauncherContext, type AnthropicParentEnvSlot } from "./launcher-context";
|
|
23
24
|
|
|
24
25
|
export interface ClaudeLaunchEnv {
|
|
@@ -269,7 +270,7 @@ async function ensureProxyForClaude(): Promise<number | null> {
|
|
|
269
270
|
if (live) return live.port;
|
|
270
271
|
const cfgPort = loadConfig().port;
|
|
271
272
|
const pinPort = typeof cfgPort === "number" && cfgPort > 0 ? cfgPort : 10100;
|
|
272
|
-
const child = spawn(process.execPath, [
|
|
273
|
+
const child = spawn(process.execPath, selfLaunchArgv(["start", "--port", String(pinPort)]), {
|
|
273
274
|
detached: true,
|
|
274
275
|
stdio: "ignore",
|
|
275
276
|
windowsHide: true,
|
package/src/cli/combo.ts
CHANGED
|
@@ -91,6 +91,9 @@ async function set(argv: string[], deps: RuntimeApiDeps): Promise<void> {
|
|
|
91
91
|
if (alias !== undefined) combo.alias = alias === "-" ? "" : alias;
|
|
92
92
|
if (nativeAlias) combo.nativeAlias = true;
|
|
93
93
|
if (displayName !== undefined) combo.displayName = displayName === "-" ? "" : displayName;
|
|
94
|
+
const current = await runtimeRequest<{ combos?: ComboRow[] }>("/api/combos", {}, deps);
|
|
95
|
+
const existing = (current.combos ?? []).find(row => row.id === (renameFrom ?? id));
|
|
96
|
+
if (existing?.imageInput === "disabled") combo.imageInput = "disabled";
|
|
94
97
|
const result = await runtimeRequest("/api/combos", {
|
|
95
98
|
method: "PUT",
|
|
96
99
|
body: JSON.stringify({ id, combo, ...(renameFrom ? { renameFrom } : {}) }),
|
package/src/cli/dispatch.ts
CHANGED
|
@@ -488,6 +488,14 @@ const commandRunners: Record<string, CommandRunner> = {
|
|
|
488
488
|
const { cmdOpencode } = await import("./opencode");
|
|
489
489
|
return await cmdOpencode(deps.args.slice(1));
|
|
490
490
|
},
|
|
491
|
+
mcode: async deps => {
|
|
492
|
+
const { cmdMcode } = await import("./minimax");
|
|
493
|
+
return await cmdMcode(deps.args.slice(1));
|
|
494
|
+
},
|
|
495
|
+
mmx: async deps => {
|
|
496
|
+
const { cmdMmx } = await import("./minimax");
|
|
497
|
+
return await cmdMmx(deps.args.slice(1));
|
|
498
|
+
},
|
|
491
499
|
help: async () => {
|
|
492
500
|
printUsage();
|
|
493
501
|
return 0;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `ocx export --client <id>` — print a client config for the live proxy.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* are YAML
|
|
4
|
+
* Eight clients, four formats: OpenCode and Pi are JSON; OMP, Hermes, Gajae and
|
|
5
|
+
* MiniMax Code are YAML; OpenClaw is JSON5; Kimi is TOML.
|
|
6
6
|
*
|
|
7
7
|
* Two consumers, one payload (devlog 260731_client_config_export/020):
|
|
8
8
|
*
|
package/src/cli/help.ts
CHANGED
|
@@ -67,6 +67,8 @@ Usage:
|
|
|
67
67
|
ocx claude [args...] Launch Claude Code wired to the proxy (model discovery on)
|
|
68
68
|
ocx claude desktop [sub] Manage and apply Claude Desktop's four-family profile
|
|
69
69
|
ocx opencode [args...] Launch opencode wired to the proxy (runtime provider config)
|
|
70
|
+
ocx mcode [args...] Launch MiniMax Code through its managed provider
|
|
71
|
+
ocx mmx text <sub> [args] Launch MiniMax CLI text through the proxy
|
|
70
72
|
ocx help [command] Show help
|
|
71
73
|
ocx --version | -v Print version
|
|
72
74
|
|
package/src/cli/index.ts
CHANGED
|
@@ -60,6 +60,7 @@ import { setIntegrationEnabled, shouldSyncCodexOnStart, shouldSyncGrokOnStart, s
|
|
|
60
60
|
|
|
61
61
|
import { removeOwnedConfigState } from "../lib/config-ownership";
|
|
62
62
|
import { withProcessRuntimeProvenance } from "../lib/bun-runtime";
|
|
63
|
+
import { selfLaunchArgv } from "../lib/self-launch-argv";
|
|
63
64
|
import { initializeNodeLauncherContext } from "./launcher-context";
|
|
64
65
|
import { createLocalAttestationSecret } from "../lib/local-management-attestation";
|
|
65
66
|
import { MEMORY_DRAIN_RESTART_MS, REPLACEMENT_READY_TIMEOUT_MS } from "../lib/system-restart-contract";
|
|
@@ -121,11 +122,11 @@ function grokSyncFailureMessage(err: unknown): string {
|
|
|
121
122
|
|
|
122
123
|
/** Argv for detached `start`, optionally hard-pinning the listen port. */
|
|
123
124
|
function startArgv(port?: number): string[] {
|
|
124
|
-
const args = [
|
|
125
|
+
const args = ["start"];
|
|
125
126
|
if (typeof port === "number" && Number.isFinite(port) && port > 0 && port <= 65535) {
|
|
126
127
|
args.push("--port", String(Math.trunc(port)));
|
|
127
128
|
}
|
|
128
|
-
return args;
|
|
129
|
+
return selfLaunchArgv(args);
|
|
129
130
|
}
|
|
130
131
|
|
|
131
132
|
async function chooseListenPort(requestedPort?: number): Promise<number> {
|
package/src/cli/lab.ts
CHANGED
|
@@ -36,6 +36,7 @@ import {
|
|
|
36
36
|
queryPassiveProductionSignals,
|
|
37
37
|
type PassiveProductionQueryResultV1,
|
|
38
38
|
} from "../lab/query";
|
|
39
|
+
import { isLabRouteSubjectId } from "../usage/log";
|
|
39
40
|
import {
|
|
40
41
|
CliUsageError,
|
|
41
42
|
RuntimeApiError,
|
|
@@ -63,6 +64,14 @@ import { planManualLabRun } from "../lab/automation/planner";
|
|
|
63
64
|
import { listLabAutomationRuns } from "../lab/automation/runs-query";
|
|
64
65
|
import { LabAutomationError, type LabAutomationLayer } from "../lab/automation/types";
|
|
65
66
|
import { createProductionLabRouteExecutor } from "../lib/lab-live-route-production";
|
|
67
|
+
import {
|
|
68
|
+
exportLocalPublicEvidence,
|
|
69
|
+
importCommunityEvidenceFile,
|
|
70
|
+
listCommunityEvidenceContext,
|
|
71
|
+
previewLocalPublicEvidence,
|
|
72
|
+
verifyPublicEvidenceFile,
|
|
73
|
+
type PublicVerificationSummaryV1,
|
|
74
|
+
} from "../lab/public";
|
|
66
75
|
|
|
67
76
|
const USAGE = `Usage:
|
|
68
77
|
ocx lab status [--json]
|
|
@@ -76,6 +85,11 @@ const USAGE = `Usage:
|
|
|
76
85
|
ocx lab artifacts [--status <s>] [--artifact-class <c>] [--limit <n>] [--cursor <c>] [--json]
|
|
77
86
|
ocx lab artifact <digest> [--json]
|
|
78
87
|
ocx lab catalog [--layer <layer>] [--suite <id>] [--json]
|
|
88
|
+
ocx lab public preview --event <eventId> [--event <eventId> ...] [--json]
|
|
89
|
+
ocx lab public export --event <eventId> [--event <eventId> ...] [--json]
|
|
90
|
+
ocx lab public verify --file <bundle.json> [--json]
|
|
91
|
+
ocx lab public import --file <bundle.json> [--json]
|
|
92
|
+
ocx lab public community [--json]
|
|
79
93
|
ocx lab automation status [--json]
|
|
80
94
|
ocx lab automation enable [--protocol] [--live] [--json]
|
|
81
95
|
ocx lab automation disable [--json]
|
|
@@ -223,6 +237,119 @@ function runListLines(page: ReturnType<typeof listLabAutomationRuns>): string[]
|
|
|
223
237
|
return lines.length > 0 ? lines : ["No automation runs"];
|
|
224
238
|
}
|
|
225
239
|
|
|
240
|
+
function takeRepeatedOptions(args: string[], flag: string): string[] {
|
|
241
|
+
const values: string[] = [];
|
|
242
|
+
while (true) {
|
|
243
|
+
const index = args.indexOf(flag);
|
|
244
|
+
if (index < 0) break;
|
|
245
|
+
const value = args[index + 1];
|
|
246
|
+
if (!value || value.startsWith("--")) {
|
|
247
|
+
throw new CliUsageError(`${flag} requires a value`, USAGE);
|
|
248
|
+
}
|
|
249
|
+
values.push(value);
|
|
250
|
+
args.splice(index, 2);
|
|
251
|
+
}
|
|
252
|
+
return values;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
function publicPreviewLines(result: ReturnType<typeof previewLocalPublicEvidence>): string[] {
|
|
256
|
+
return [
|
|
257
|
+
`Public evidence preview: ${result.bundle.records.length} exportable record(s)`,
|
|
258
|
+
`Excluded: ${result.excluded.length}`,
|
|
259
|
+
"Unsigned local preview; no publisher key or remote publish is created.",
|
|
260
|
+
];
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
function publicExportLines(result: ReturnType<typeof exportLocalPublicEvidence>): string[] {
|
|
264
|
+
return [
|
|
265
|
+
"Public evidence exported locally",
|
|
266
|
+
`Bundle: ${result.bundle.bundleId}`,
|
|
267
|
+
`Publisher: ${result.bundle.publisher.keyId}`,
|
|
268
|
+
`Path: ${result.stored.path}`,
|
|
269
|
+
`Excluded: ${result.excluded.length}`,
|
|
270
|
+
"No remote publish occurred.",
|
|
271
|
+
];
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
function publicVerificationLines(result: PublicVerificationSummaryV1): string[] {
|
|
275
|
+
if (result.status !== "cryptographically_valid") {
|
|
276
|
+
return [
|
|
277
|
+
`Public evidence verification: ${result.status}`,
|
|
278
|
+
"Not locally verified.",
|
|
279
|
+
];
|
|
280
|
+
}
|
|
281
|
+
return [
|
|
282
|
+
"Public evidence verification: cryptographically valid",
|
|
283
|
+
`Bundle: ${result.bundleId}`,
|
|
284
|
+
`Publisher: ${result.publisherKeyId}`,
|
|
285
|
+
"Not locally verified. Signature validity proves integrity/continuity only.",
|
|
286
|
+
];
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
function handlePublicLabCommand(
|
|
290
|
+
argv: string[],
|
|
291
|
+
wantsJson: boolean,
|
|
292
|
+
configDir: string,
|
|
293
|
+
): void {
|
|
294
|
+
const [action, ...restInput] = argv;
|
|
295
|
+
const rest = [...restInput];
|
|
296
|
+
switch (action) {
|
|
297
|
+
case "preview": {
|
|
298
|
+
const eventIds = takeRepeatedOptions(rest, "--event");
|
|
299
|
+
rejectArgs(rest, USAGE);
|
|
300
|
+
const result = previewLocalPublicEvidence({ eventIds }, configDir);
|
|
301
|
+
printData(result, wantsJson, publicPreviewLines(result));
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
case "export": {
|
|
305
|
+
const eventIds = takeRepeatedOptions(rest, "--event");
|
|
306
|
+
rejectArgs(rest, USAGE);
|
|
307
|
+
const result = exportLocalPublicEvidence({ eventIds }, configDir);
|
|
308
|
+
printData(result, wantsJson, publicExportLines(result));
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
case "verify": {
|
|
312
|
+
const path = takeOption(rest, "--file");
|
|
313
|
+
if (!path) throw new CliUsageError("public verify requires --file", USAGE);
|
|
314
|
+
rejectArgs(rest, USAGE);
|
|
315
|
+
const result = verifyPublicEvidenceFile(path);
|
|
316
|
+
printData(result, wantsJson, publicVerificationLines(result));
|
|
317
|
+
if (result.status !== "cryptographically_valid") {
|
|
318
|
+
throw new RuntimeApiError(`public evidence verification failed: ${result.status}`, 422, result);
|
|
319
|
+
}
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
case "import": {
|
|
323
|
+
const path = takeOption(rest, "--file");
|
|
324
|
+
if (!path) throw new CliUsageError("public import requires --file", USAGE);
|
|
325
|
+
rejectArgs(rest, USAGE);
|
|
326
|
+
const result = importCommunityEvidenceFile(path, configDir);
|
|
327
|
+
printData(result, wantsJson, [
|
|
328
|
+
`Community evidence imported: ${result.bundleId}`,
|
|
329
|
+
`Publisher: ${result.publisherKeyId}`,
|
|
330
|
+
"Trust: community_untrusted_v1; not locally verified.",
|
|
331
|
+
]);
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
334
|
+
case "community": {
|
|
335
|
+
rejectArgs(rest, USAGE);
|
|
336
|
+
const result = listCommunityEvidenceContext(configDir);
|
|
337
|
+
const lines = result.evidence.length > 0
|
|
338
|
+
? result.evidence.map((row) =>
|
|
339
|
+
`${row.bundleId} publisher=${row.publisherKeyId} active=${row.activeRecordCount} revoked=${row.revokedRecordCount}`,
|
|
340
|
+
)
|
|
341
|
+
: ["No community evidence"];
|
|
342
|
+
printData(result, wantsJson, [
|
|
343
|
+
"Community evidence (untrusted, read-only context; not locally verified)",
|
|
344
|
+
...lines,
|
|
345
|
+
]);
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
default:
|
|
349
|
+
throw new CliUsageError("unknown public subcommand", USAGE);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
226
353
|
export async function handleLabCommand(argv: string[], deps: LabCliDeps = {}): Promise<number> {
|
|
227
354
|
return runCliAction(async () => {
|
|
228
355
|
const configDir = deps.configDir ?? getConfigDir();
|
|
@@ -232,6 +359,10 @@ export async function handleLabCommand(argv: string[], deps: LabCliDeps = {}): P
|
|
|
232
359
|
|
|
233
360
|
try {
|
|
234
361
|
switch (sub) {
|
|
362
|
+
case "public": {
|
|
363
|
+
handlePublicLabCommand(rest, wantsJson, configDir);
|
|
364
|
+
return;
|
|
365
|
+
}
|
|
235
366
|
case "status": {
|
|
236
367
|
rejectArgs(rest, USAGE);
|
|
237
368
|
const status = queryLabStatus(configDir);
|
|
@@ -243,6 +374,9 @@ export async function handleLabCommand(argv: string[], deps: LabCliDeps = {}): P
|
|
|
243
374
|
const limit = takeIntegerOption(rest, "--limit", { min: 1 });
|
|
244
375
|
rejectArgs(rest, USAGE);
|
|
245
376
|
if (!subjectId) throw new CliUsageError("--subject is required", USAGE);
|
|
377
|
+
if (!isLabRouteSubjectId(subjectId)) {
|
|
378
|
+
throw new CliUsageError("--subject must be an exact Lab route subject id", USAGE);
|
|
379
|
+
}
|
|
246
380
|
const result = queryPassiveProductionSignals(subjectId, limit, configDir);
|
|
247
381
|
printData(result, wantsJson, passiveProductionLines(result));
|
|
248
382
|
return;
|
|
@@ -461,7 +595,7 @@ export async function handleLabCommand(argv: string[], deps: LabCliDeps = {}): P
|
|
|
461
595
|
throw new CliUsageError(`unknown lab subcommand: ${sub}`, USAGE);
|
|
462
596
|
}
|
|
463
597
|
} catch (err) {
|
|
464
|
-
if (err instanceof CliUsageError) throw err;
|
|
598
|
+
if (err instanceof CliUsageError || err instanceof RuntimeApiError) throw err;
|
|
465
599
|
if (err instanceof LabProjectionUnavailableError || err instanceof LabProjectionIncompatibleError) {
|
|
466
600
|
throw new LabStateError(labErrorMessage(err));
|
|
467
601
|
}
|