@yansigit/opencodex 2.36.1-dev.20260829.48 → 2.36.1
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/{ApiKeys-LOOFiZfv.js → ApiKeys-CsmNyf4I.js} +1 -1
- package/gui/dist/assets/{Claude-BwKzpXe3.js → Claude-B4sHsGTD.js} +1 -1
- package/gui/dist/assets/{CodexSet-B5qi9KeE.js → CodexSet-BuAQ4YQn.js} +1 -1
- package/gui/dist/assets/{FileIntegrationPage-B0sUef6W.js → FileIntegrationPage-BC65sEaS.js} +1 -1
- package/gui/dist/assets/{Grok-bsb4n-f4.js → Grok-DNJWlISf.js} +1 -1
- package/gui/dist/assets/{Integrations-CSMwFDvM.js → Integrations-vGJshFZK.js} +2 -2
- package/gui/dist/assets/{IntegrationsOverview-CTORdKJA.js → IntegrationsOverview-DYMkDFHE.js} +1 -1
- package/gui/dist/assets/{Logs-BWYbfELf.js → Logs-Coc7_RPP.js} +1 -1
- package/gui/dist/assets/{Models-CexEtdT1.js → Models-CKVT8Meb.js} +1 -1
- package/gui/dist/assets/{NumberStepper-BapSFQnW.js → NumberStepper-Dug4wR8n.js} +1 -1
- package/gui/dist/assets/{Providers-BwUFeAgA.js → Providers-DyaCHRWL.js} +1 -1
- package/gui/dist/assets/{RestoreDialog-CD8piq90.js → RestoreDialog-BpuvVU2w.js} +1 -1
- package/gui/dist/assets/{Startup-BgX731C2.js → Startup-DAUQqNEN.js} +1 -1
- package/gui/dist/assets/{Storage-Bgq7HphP.js → Storage-DfP95SlJ.js} +1 -1
- package/gui/dist/assets/{Subagents-ChxItbeF.js → Subagents-CMNsZLfk.js} +1 -1
- package/gui/dist/assets/{Usage-BkylzP50.js → Usage-ESFRfBMO.js} +1 -1
- package/gui/dist/assets/{codex-stale-banner-D4gvSWnO.js → codex-stale-banner-W4FhikN7.js} +1 -1
- package/gui/dist/assets/{data-surface-BtO3lwam.js → data-surface-B8PX8rjm.js} +1 -1
- package/gui/dist/assets/{data-surface-pCXChiBf.js → data-surface-CETFXqiA.js} +1 -1
- package/gui/dist/assets/{index-CNopOid3.js → index-DHQHBRhb.js} +3 -3
- package/gui/dist/assets/{model-display-CiUpg8T9.js → model-display-OBTJhQwT.js} +1 -1
- package/gui/dist/assets/{provider-payload-b2jlS-On.js → provider-payload-CJRJUn7w.js} +1 -1
- package/gui/dist/assets/{section-tabs-BP7gEPK6.js → section-tabs-B8sLMiz6.js} +1 -1
- package/gui/dist/assets/shared-DI18-uF7.js +69 -0
- package/gui/dist/index.html +2 -2
- package/package.json +3 -1
- package/src/adapters/base.ts +26 -0
- package/src/adapters/cursor/catalog.ts +541 -0
- package/src/adapters/cursor/cursor-errors.ts +15 -0
- package/src/adapters/cursor/discovery.ts +34 -41
- package/src/adapters/cursor/envelope-echo.ts +128 -0
- package/src/adapters/cursor/request-builder.ts +19 -12
- package/src/adapters/cursor/tool-definitions.ts +2 -1
- package/src/adapters/cursor/tool-result-normalize.ts +23 -31
- package/src/adapters/cursor.ts +21 -2
- package/src/adapters/exec-tool-result-normalize.ts +99 -0
- package/src/adapters/google-antigravity-replay.ts +71 -2
- package/src/adapters/google.ts +19 -4
- package/src/adapters/kiro-constants.ts +12 -0
- package/src/adapters/kiro.ts +128 -11
- package/src/adapters/openai-chat.ts +16 -2
- package/src/adapters/openai-responses.ts +15 -2
- package/src/adapters/tool-catalog-nudge.ts +2 -1
- package/src/adapters/xai-web-search.ts +10 -14
- package/src/claude/outbound.ts +14 -3
- package/src/cli/access.ts +46 -3
- package/src/cli/account-api.ts +93 -16
- package/src/cli/account-extended.ts +262 -36
- package/src/cli/account-main.ts +12 -12
- package/src/cli/account.ts +40 -10
- package/src/cli/agent.ts +8 -1
- package/src/cli/capabilities-command.ts +94 -0
- package/src/cli/capabilities.ts +535 -0
- package/src/cli/claude-desktop.ts +31 -11
- package/src/cli/dispatch.ts +195 -27
- package/src/cli/doctor.ts +100 -1
- package/src/cli/help.ts +11 -2
- package/src/cli/index.ts +19 -3
- package/src/cli/inspect.ts +230 -0
- package/src/cli/observe.ts +11 -3
- package/src/cli/registry.ts +34 -2
- package/src/cli/runtime-api.ts +51 -7
- package/src/cli/status.ts +16 -0
- package/src/cli/storage.ts +234 -0
- package/src/cli/system-command.ts +16 -0
- package/src/cli/usage-report.ts +52 -2
- package/src/cli/version-skew.ts +46 -0
- package/src/codex/account-label.ts +21 -0
- package/src/codex/catalog/provider-fetch.ts +4 -0
- package/src/codex/transition-state.ts +12 -3
- package/src/compatibility/openai-responses.ts +9 -1
- package/src/generated/compatibility-version.json +94 -58
- package/src/integrations/ownership-policy.ts +24 -5
- package/src/integrations/ownership.ts +36 -2
- package/src/integrations/state.ts +40 -7
- package/src/integrations/writer.ts +21 -3
- package/src/lib/admin-secrets.ts +24 -0
- package/src/lib/errors.ts +25 -1
- package/src/lib/service-secrets.ts +15 -0
- package/src/oauth/store.ts +16 -6
- package/src/providers/label.ts +34 -1
- package/src/responses/turn-termination.ts +107 -0
- package/src/server/management/logs-usage-routes.ts +5 -27
- package/src/server/management/route-registry.ts +317 -0
- package/src/server/proxy-liveness.ts +27 -4
- package/src/server/request-log.ts +37 -17
- package/src/server/responses/core.ts +80 -4
- package/src/server/responses/policy-fallback.ts +1 -1
- package/src/service.ts +34 -0
- package/src/storage/policy-job.ts +14 -4
- package/src/storage/policy.ts +79 -33
- package/src/usage/log.ts +32 -6
- package/src/usage/summary.ts +22 -34
- package/gui/dist/assets/shared-BdYGV-yJ.js +0 -69
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `ocx system-surface` — the remaining routes that had no CLI caller at all (wp7).
|
|
3
|
+
*
|
|
4
|
+
* These are grouped by what an operator is trying to do, not by which server module owns them:
|
|
5
|
+
* inspecting effective configuration, reading the generated client-config snippet, toggling the
|
|
6
|
+
* native client integrations, checking request pacing and routing analytics, and reading the
|
|
7
|
+
* Codex system prompt.
|
|
8
|
+
*
|
|
9
|
+
* One route here is deliberately READ-ONLY forever. `POST /api/github/star` spends the operator's
|
|
10
|
+
* GitHub identity, and no CLI flag can carry that consent — the server requires a real dashboard
|
|
11
|
+
* session for it. So `star` reads status and says what it cannot do, rather than offering a
|
|
12
|
+
* `--yes` that would be a lie.
|
|
13
|
+
*/
|
|
14
|
+
import {
|
|
15
|
+
CliUsageError,
|
|
16
|
+
printData,
|
|
17
|
+
rejectArgs,
|
|
18
|
+
runCliAction,
|
|
19
|
+
runtimeRequest,
|
|
20
|
+
summaryLines,
|
|
21
|
+
takeFlag,
|
|
22
|
+
takeOption,
|
|
23
|
+
type RuntimeApiDeps,
|
|
24
|
+
} from "./runtime-api";
|
|
25
|
+
|
|
26
|
+
const USAGE = `Usage:
|
|
27
|
+
ocx inspect config [--json]
|
|
28
|
+
ocx inspect catalog [--json]
|
|
29
|
+
ocx inspect routing-analytics [--json]
|
|
30
|
+
ocx inspect pacing [--name <provider>] [--json]
|
|
31
|
+
ocx inspect key-providers [--json]
|
|
32
|
+
ocx inspect codex-prompt [--text] [--json]
|
|
33
|
+
ocx inspect client-config --client <id> [--json]
|
|
34
|
+
ocx inspect star [--json]
|
|
35
|
+
ocx inspect windows-tray [--json]
|
|
36
|
+
ocx integration native [list] [--json]
|
|
37
|
+
ocx integration native <claude|claude-desktop|codex|grok> <on|off> [--json]
|
|
38
|
+
ocx agent request-user-input [on|off] [--json]`;
|
|
39
|
+
|
|
40
|
+
/** A read that takes no arguments beyond `--json`. */
|
|
41
|
+
async function read(path: string, argv: string[], deps: RuntimeApiDeps): Promise<void> {
|
|
42
|
+
const args = [...argv];
|
|
43
|
+
const wantsJson = takeFlag(args, "--json");
|
|
44
|
+
rejectArgs(args, USAGE);
|
|
45
|
+
const result = await runtimeRequest(path, {}, deps);
|
|
46
|
+
printData(result, wantsJson, summaryLines(result));
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const NATIVE_CLIENTS = ["claude", "claude-desktop", "codex", "grok"] as const;
|
|
50
|
+
|
|
51
|
+
interface NativeClientRow {
|
|
52
|
+
clientId?: string;
|
|
53
|
+
state?: string;
|
|
54
|
+
installed?: boolean;
|
|
55
|
+
desiredEnabled?: boolean;
|
|
56
|
+
configPath?: string;
|
|
57
|
+
disableBlocked?: unknown;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* The shared depth-1 flattener renders an array as "N item(s)", so a bare
|
|
62
|
+
* `integration native list` printed `clients: 4 item(s)` -- the per-client state the operator
|
|
63
|
+
* asked for was in the payload and discarded before the terminal. Same defect class wp4 fixed
|
|
64
|
+
* for the account and key tables.
|
|
65
|
+
*/
|
|
66
|
+
function nativeLines(payload: unknown): string[] {
|
|
67
|
+
const clients = (payload as { clients?: NativeClientRow[] } | null)?.clients;
|
|
68
|
+
if (!Array.isArray(clients) || clients.length === 0) return ["No native client integrations reported."];
|
|
69
|
+
const lines = ["CLIENT STATE INSTALLED DESIRED CONFIG"];
|
|
70
|
+
for (const row of clients) {
|
|
71
|
+
lines.push([
|
|
72
|
+
(row.clientId ?? "?").padEnd(16),
|
|
73
|
+
(row.state ?? "?").padEnd(10),
|
|
74
|
+
(row.installed === true ? "yes" : row.installed === false ? "no" : "?").padEnd(10),
|
|
75
|
+
(row.desiredEnabled === true ? "on" : row.desiredEnabled === false ? "off" : "?").padEnd(8),
|
|
76
|
+
row.configPath ?? "",
|
|
77
|
+
].join(" ").trimEnd());
|
|
78
|
+
// A blocked disable is the reason a toggle did not take effect, so it is never silent.
|
|
79
|
+
if (row.disableBlocked !== null && row.disableBlocked !== undefined) {
|
|
80
|
+
lines.push(` disable blocked: ${typeof row.disableBlocked === "string" ? row.disableBlocked : JSON.stringify(row.disableBlocked)}`);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return lines;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
async function nativeIntegration(argv: string[], deps: RuntimeApiDeps): Promise<void> {
|
|
87
|
+
const action = argv[0] && !argv[0].startsWith("-") ? argv[0] : "list";
|
|
88
|
+
const rest = argv[0] && !argv[0].startsWith("-") ? argv.slice(1) : argv;
|
|
89
|
+
|
|
90
|
+
if (action === "list") {
|
|
91
|
+
const args = [...rest];
|
|
92
|
+
const wantsJson = takeFlag(args, "--json");
|
|
93
|
+
rejectArgs(args, USAGE);
|
|
94
|
+
const result = await runtimeRequest("/api/native-integrations", {}, deps);
|
|
95
|
+
printData(result, wantsJson, nativeLines(result));
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (!(NATIVE_CLIENTS as readonly string[]).includes(action)) {
|
|
100
|
+
throw new CliUsageError(`unknown native client ${action}; expected one of ${NATIVE_CLIENTS.join(", ")}`, USAGE);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const args = [...rest];
|
|
104
|
+
const wantsJson = takeFlag(args, "--json");
|
|
105
|
+
const state = args.shift();
|
|
106
|
+
rejectArgs(args, USAGE);
|
|
107
|
+
if (state !== "on" && state !== "off") {
|
|
108
|
+
throw new CliUsageError(`expected on or off after ${action}`, USAGE);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Toggling rewrites the client's own config file, which is why each client has its own route
|
|
112
|
+
// rather than one route with a client parameter.
|
|
113
|
+
const result = await runtimeRequest(`/api/native-integrations/${action}`, {
|
|
114
|
+
method: "PUT",
|
|
115
|
+
headers: { "content-type": "application/json" },
|
|
116
|
+
body: JSON.stringify({ enabled: state === "on" }),
|
|
117
|
+
}, deps);
|
|
118
|
+
printData(result, wantsJson, summaryLines(result));
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Exported unwrapped so `ocx agent request-user-input` can call it INSIDE its own
|
|
123
|
+
* `runCliAction`. Wrapping an already-wrapped handler reports one failure twice: the inner
|
|
124
|
+
* wrapper prints the error and returns a code, and the outer one prints again on the rethrow.
|
|
125
|
+
*/
|
|
126
|
+
export async function requestUserInputAction(argv: string[], deps: RuntimeApiDeps): Promise<void> {
|
|
127
|
+
const args = [...argv];
|
|
128
|
+
const wantsJson = takeFlag(args, "--json");
|
|
129
|
+
const state = args.shift();
|
|
130
|
+
rejectArgs(args, USAGE);
|
|
131
|
+
|
|
132
|
+
const path = "/api/codex-auth/features/default-mode-request-user-input";
|
|
133
|
+
// No argument means show. A read must not write the value it is reporting.
|
|
134
|
+
if (state === undefined) {
|
|
135
|
+
const result = await runtimeRequest(path, {}, deps);
|
|
136
|
+
printData(result, wantsJson, summaryLines(result));
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
if (state !== "on" && state !== "off") throw new CliUsageError("expected on or off", USAGE);
|
|
140
|
+
|
|
141
|
+
const result = await runtimeRequest(path, {
|
|
142
|
+
method: "PUT",
|
|
143
|
+
headers: { "content-type": "application/json" },
|
|
144
|
+
body: JSON.stringify({ enabled: state === "on" }),
|
|
145
|
+
}, deps);
|
|
146
|
+
printData(result, wantsJson, summaryLines(result));
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
async function clientConfig(argv: string[], deps: RuntimeApiDeps): Promise<void> {
|
|
150
|
+
const args = [...argv];
|
|
151
|
+
const wantsJson = takeFlag(args, "--json");
|
|
152
|
+
const client = takeOption(args, "--client");
|
|
153
|
+
rejectArgs(args, USAGE);
|
|
154
|
+
if (!client) throw new CliUsageError("--client is required", USAGE);
|
|
155
|
+
// The valid client list is not duplicated here: the route answers 400 naming every accepted id,
|
|
156
|
+
// which is more useful than a local list that can drift out of date.
|
|
157
|
+
const result = await runtimeRequest(`/api/client-config?client=${encodeURIComponent(client)}`, {}, deps);
|
|
158
|
+
printData(result, wantsJson, summaryLines(result));
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
async function pacing(argv: string[], deps: RuntimeApiDeps): Promise<void> {
|
|
162
|
+
const args = [...argv];
|
|
163
|
+
const wantsJson = takeFlag(args, "--json");
|
|
164
|
+
const name = takeOption(args, "--name");
|
|
165
|
+
rejectArgs(args, USAGE);
|
|
166
|
+
const suffix = name ? `?name=${encodeURIComponent(name)}` : "";
|
|
167
|
+
const result = await runtimeRequest(`/api/provider-request-pacing${suffix}`, {}, deps);
|
|
168
|
+
printData(result, wantsJson, summaryLines(result));
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
async function codexPrompt(argv: string[], deps: RuntimeApiDeps): Promise<void> {
|
|
172
|
+
const args = [...argv];
|
|
173
|
+
const wantsJson = takeFlag(args, "--json");
|
|
174
|
+
const asText = takeFlag(args, "--text");
|
|
175
|
+
rejectArgs(args, USAGE);
|
|
176
|
+
if (asText && wantsJson) throw new CliUsageError("--text and --json cannot be combined", USAGE);
|
|
177
|
+
if (asText) {
|
|
178
|
+
// The /text variant answers the prompt body itself, so it is printed verbatim rather than
|
|
179
|
+
// flattened through the summary renderer.
|
|
180
|
+
const result = await runtimeRequest<unknown>("/api/codex-prompt/text", {}, deps);
|
|
181
|
+
console.log(typeof result === "string" ? result : JSON.stringify(result, null, 2));
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
const result = await runtimeRequest("/api/codex-prompt", {}, deps);
|
|
185
|
+
printData(result, wantsJson, summaryLines(result));
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
async function star(argv: string[], deps: RuntimeApiDeps): Promise<void> {
|
|
189
|
+
const args = [...argv];
|
|
190
|
+
const wantsJson = takeFlag(args, "--json");
|
|
191
|
+
rejectArgs(args, USAGE);
|
|
192
|
+
const result = await runtimeRequest<Record<string, unknown>>("/api/github/star", {}, deps);
|
|
193
|
+
if (wantsJson) {
|
|
194
|
+
printData(result, true);
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
printData(result, false, summaryLines(result));
|
|
198
|
+
// Said plainly, because the natural next question is "then star it", and the answer is that no
|
|
199
|
+
// CLI invocation can: the POST requires a dashboard session precisely so an agent cannot spend
|
|
200
|
+
// the operator's identity on their behalf.
|
|
201
|
+
console.log("Starring is not available from the CLI: it uses your GitHub identity, so only you can do it from the dashboard.");
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
export async function handleInspectCommand(argv: string[], deps: RuntimeApiDeps = {}): Promise<number> {
|
|
205
|
+
return runCliAction(async () => {
|
|
206
|
+
const hasSub = argv[0] !== undefined && !argv[0].startsWith("-");
|
|
207
|
+
const sub = hasSub ? argv[0]! : "config";
|
|
208
|
+
const rest = hasSub ? argv.slice(1) : argv;
|
|
209
|
+
if (sub === "config") await read("/api/config", rest, deps);
|
|
210
|
+
else if (sub === "catalog") await read("/api/catalog", rest, deps);
|
|
211
|
+
else if (sub === "routing-analytics") await read("/api/routing-analytics", rest, deps);
|
|
212
|
+
else if (sub === "key-providers") await read("/api/key-providers", rest, deps);
|
|
213
|
+
else if (sub === "windows-tray") await read("/api/windows-tray", rest, deps);
|
|
214
|
+
else if (sub === "pacing") await pacing(rest, deps);
|
|
215
|
+
else if (sub === "client-config") await clientConfig(rest, deps);
|
|
216
|
+
else if (sub === "codex-prompt") await codexPrompt(rest, deps);
|
|
217
|
+
else if (sub === "star") await star(rest, deps);
|
|
218
|
+
else throw new CliUsageError(`unknown inspect command ${sub}`, USAGE);
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
export async function handleIntegrationCommand(argv: string[], deps: RuntimeApiDeps = {}): Promise<number> {
|
|
223
|
+
return runCliAction(async () => {
|
|
224
|
+
const [sub, ...rest] = argv;
|
|
225
|
+
if (sub === "native" || sub === undefined) await nativeIntegration(sub === undefined ? [] : rest, deps);
|
|
226
|
+
else throw new CliUsageError(`unknown integration command ${sub}`, USAGE);
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
export const INSPECT_USAGE = USAGE;
|
package/src/cli/observe.ts
CHANGED
|
@@ -15,7 +15,7 @@ import { USAGE_RANGES, USAGE_SURFACES } from "../usage/summary";
|
|
|
15
15
|
|
|
16
16
|
const USAGE = `Usage:
|
|
17
17
|
ocx observe logs [--provider <name>] [--model <id>] [--status <code>]
|
|
18
|
-
[--limit <n>] [--follow] [--json|--jsonl]
|
|
18
|
+
[--conversation <id>] [--limit <n>] [--follow] [--json|--jsonl]
|
|
19
19
|
ocx logs explain <request-id> [--json]
|
|
20
20
|
ocx logs rebuild-index
|
|
21
21
|
ocx logs index-status
|
|
@@ -50,7 +50,12 @@ function formatLog(row: LogEntry): string {
|
|
|
50
50
|
const route = [row.provider, row.model].filter(Boolean).join("/");
|
|
51
51
|
const status = row.status ?? row.statusCode ?? "?";
|
|
52
52
|
const duration = row.durationMs !== undefined ? `${String(row.durationMs)}ms` : "";
|
|
53
|
-
|
|
53
|
+
// The conversation id is shown because a conversation FILTER whose output never names the
|
|
54
|
+
// conversation is hard to trust: an empty result and a wrong-id result look identical (#2704).
|
|
55
|
+
const conversation = typeof row.conversationId === "string" && row.conversationId.length > 0
|
|
56
|
+
? `conv=${row.conversationId}`
|
|
57
|
+
: "";
|
|
58
|
+
return [time, String(status), route, duration, conversation].filter(Boolean).join(" ");
|
|
54
59
|
}
|
|
55
60
|
|
|
56
61
|
async function logs(argv: string[], deps: RuntimeApiDeps): Promise<void> {
|
|
@@ -61,13 +66,16 @@ async function logs(argv: string[], deps: RuntimeApiDeps): Promise<void> {
|
|
|
61
66
|
const provider = takeOption(args, "--provider");
|
|
62
67
|
const model = takeOption(args, "--model");
|
|
63
68
|
const status = takeOption(args, "--status");
|
|
69
|
+
// Both spellings, because the server accepts both (`request-log.ts:1032`) and an operator
|
|
70
|
+
// should not have to remember which one this surface wanted.
|
|
71
|
+
const conversationId = takeOption(args, "--conversation") ?? takeOption(args, "--conversationId");
|
|
64
72
|
const limit = takeIntegerOption(args, "--limit", { min: 1 }) ?? 200;
|
|
65
73
|
rejectArgs(args, USAGE);
|
|
66
74
|
if (wantsJson && wantsJsonl) throw new CliUsageError("--json and --jsonl cannot be combined", USAGE);
|
|
67
75
|
if (follow && wantsJson) throw new CliUsageError("--follow uses --jsonl, not --json", USAGE);
|
|
68
76
|
let seen = new Set<string>();
|
|
69
77
|
do {
|
|
70
|
-
const data = await runtimeRequest(`/api/logs${query({ provider, model, status, limit })}`, {}, deps);
|
|
78
|
+
const data = await runtimeRequest(`/api/logs${query({ provider, model, status, conversationId, limit })}`, {}, deps);
|
|
71
79
|
const rows = logRows(data);
|
|
72
80
|
if (!follow && wantsJson) printData(data, true);
|
|
73
81
|
else {
|
package/src/cli/registry.ts
CHANGED
|
@@ -213,13 +213,29 @@ export const CLI_COMMANDS: CliCommandEntry[] = [
|
|
|
213
213
|
usage: "ocx observe <logs|usage|storage|memory|debug|claude-inbound|injection> ...",
|
|
214
214
|
summary: "Inspect proxy requests, usage, storage, memory, and debug data.",
|
|
215
215
|
},
|
|
216
|
+
{
|
|
217
|
+
name: "inspect",
|
|
218
|
+
usage: "ocx inspect <config|catalog|routing-analytics|pacing|key-providers|codex-prompt|client-config|star|windows-tray> ...",
|
|
219
|
+
summary: "Read effective config, catalog, analytics, pacing, and the generated client-config snippet.",
|
|
220
|
+
details: [
|
|
221
|
+
"`inspect star` reads the repository star status only. Starring uses your GitHub identity and is available from the dashboard alone.",
|
|
222
|
+
],
|
|
223
|
+
},
|
|
216
224
|
{ name: "logs", usage: "ocx logs [filters] [--follow] [--json|--jsonl]", summary: "Alias of ocx observe logs." },
|
|
217
225
|
{
|
|
218
226
|
name: "usage",
|
|
219
227
|
usage: "ocx usage [--range <today|1d|7d|30d|all>] [--surface <all|codex|claude|grok>] [--provider <name>] [--model <id>] [--json]",
|
|
220
228
|
summary: "Alias of ocx observe usage.",
|
|
221
229
|
},
|
|
222
|
-
{
|
|
230
|
+
{
|
|
231
|
+
name: "storage",
|
|
232
|
+
usage: "ocx storage <report|cleanup|trash|policy> ...",
|
|
233
|
+
summary: "Storage report, archived-session cleanup, trash restore, and the cleanup policy.",
|
|
234
|
+
details: [
|
|
235
|
+
"A bare `ocx storage` prints the report, as it did when this was an alias of `observe storage`.",
|
|
236
|
+
"`cleanup` previews by default and only deletes under --yes; `trash restore` and `policy run` also require --yes.",
|
|
237
|
+
],
|
|
238
|
+
},
|
|
223
239
|
{ name: "memory", usage: "ocx memory [--json]", summary: "Alias of ocx observe memory." },
|
|
224
240
|
{
|
|
225
241
|
name: "access",
|
|
@@ -243,7 +259,14 @@ export const CLI_COMMANDS: CliCommandEntry[] = [
|
|
|
243
259
|
usage: "ocx grok <status|exclude|include|set|clear|apply> ...",
|
|
244
260
|
summary: "Manage and apply the Grok Build model fence.",
|
|
245
261
|
},
|
|
246
|
-
{
|
|
262
|
+
{
|
|
263
|
+
name: "integration",
|
|
264
|
+
usage: "ocx integration <claude|grok|client|native> ...",
|
|
265
|
+
summary: "Manage supported client integrations, and the native client toggles.",
|
|
266
|
+
details: [
|
|
267
|
+
"`native` shows or flips the native Claude/Claude Desktop/Codex/Grok toggles; the other subcommands manage the reversible file integrations.",
|
|
268
|
+
],
|
|
269
|
+
},
|
|
247
270
|
{
|
|
248
271
|
name: "system",
|
|
249
272
|
usage: "ocx system <status|settings|startup|diagnostics|sync|update> ...",
|
|
@@ -350,6 +373,15 @@ export const CLI_COMMANDS: CliCommandEntry[] = [
|
|
|
350
373
|
summary: "Check proxy health. Exits 0 if healthy, 1 otherwise.",
|
|
351
374
|
details: ["Use --json for structured output: {ok, pid, port}."],
|
|
352
375
|
},
|
|
376
|
+
{
|
|
377
|
+
name: "capabilities",
|
|
378
|
+
usage: "ocx capabilities [--json] [--mutating-only] [--route <path>]",
|
|
379
|
+
summary: "List the declared CLI capabilities and the management routes they drive.",
|
|
380
|
+
details: [
|
|
381
|
+
"The machine-readable surface index: start here when driving ocx programmatically instead of parsing help text.",
|
|
382
|
+
"--route <path> answers the inverse question: which commands drive this management route.",
|
|
383
|
+
],
|
|
384
|
+
},
|
|
353
385
|
{
|
|
354
386
|
name: "ready",
|
|
355
387
|
usage: "ocx ready [--json] [--wait [--timeout <seconds>]]",
|
package/src/cli/runtime-api.ts
CHANGED
|
@@ -47,15 +47,37 @@ export async function runtimeBaseUrl(deps: RuntimeApiDeps = {}): Promise<string>
|
|
|
47
47
|
return `http://${probeHostname(live.hostname)}:${live.port}`;
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
+
function stringField(record: Record<string, unknown>, key: string): string | undefined {
|
|
51
|
+
const value = record[key];
|
|
52
|
+
return typeof value === "string" && value.trim() ? value.trim() : undefined;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Compose the operator-facing message from a management error body.
|
|
57
|
+
*
|
|
58
|
+
* The server states WHY a request was refused under `reason` and WHAT TO DO under
|
|
59
|
+
* `hint` (see management-auth.ts, which sets both on a 503 when the management plane
|
|
60
|
+
* is unavailable). Both were dropped here, so a fenced management plane was
|
|
61
|
+
* indistinguishable from a generic failure and an operator had no way to tell a port
|
|
62
|
+
* collision from an ACL refusal from a stopped proxy (#2698).
|
|
63
|
+
*/
|
|
50
64
|
function responseMessage(body: unknown, status: number): string {
|
|
51
|
-
if (body && typeof body === "object") {
|
|
52
|
-
const record = body as Record<string, unknown>;
|
|
53
|
-
for (const key of ["error", "message", "detail"]) {
|
|
54
|
-
if (typeof record[key] === "string" && record[key]) return record[key];
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
65
|
if (typeof body === "string" && body.trim()) return body.trim().slice(0, 400);
|
|
58
|
-
return `Management request failed (${status})`;
|
|
66
|
+
if (!body || typeof body !== "object") return `Management request failed (${status})`;
|
|
67
|
+
const record = body as Record<string, unknown>;
|
|
68
|
+
let primary: string | undefined;
|
|
69
|
+
for (const key of ["error", "message", "detail"]) {
|
|
70
|
+
primary = stringField(record, key);
|
|
71
|
+
if (primary) break;
|
|
72
|
+
}
|
|
73
|
+
const parts = [primary ?? `Management request failed (${status})`];
|
|
74
|
+
const reason = stringField(record, "reason");
|
|
75
|
+
// A body of {ok:false, reason:"…"} with no `error` key used to degrade to the
|
|
76
|
+
// generic line, discarding the only actionable field.
|
|
77
|
+
if (reason && reason !== primary) parts.push(`reason: ${reason}`);
|
|
78
|
+
const hint = stringField(record, "hint");
|
|
79
|
+
if (hint && hint !== primary) parts.push(`hint: ${hint}`);
|
|
80
|
+
return parts.join("\n").slice(0, 1200);
|
|
59
81
|
}
|
|
60
82
|
|
|
61
83
|
export async function runtimeRequest<T = unknown>(
|
|
@@ -94,6 +116,28 @@ export function takeFlag(args: string[], flag: string): boolean {
|
|
|
94
116
|
return true;
|
|
95
117
|
}
|
|
96
118
|
|
|
119
|
+
/** Unicode dashes that copy-paste and IME input substitute for ASCII `-`. */
|
|
120
|
+
const DASH_CLASS = /[\u2010-\u2015\u2212]/g;
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* True for `--json`, `--json=true`, `-json`, and Unicode-dash spellings.
|
|
124
|
+
* Matching only the exact token `--json` is the same defect logout had: each
|
|
125
|
+
* spelling that slips through is a silent success (or, for doctor, prose on
|
|
126
|
+
* stdout after the caller asked for JSON).
|
|
127
|
+
*/
|
|
128
|
+
export function isJsonOption(arg: string): boolean {
|
|
129
|
+
const body = arg.replace(DASH_CLASS, "-").replace(/^-+/, "");
|
|
130
|
+
return body === "json" || body.startsWith("json=");
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/** Remove one JSON-request spelling from `args`. Returns whether one was present. */
|
|
134
|
+
export function takeJsonFlag(args: string[]): boolean {
|
|
135
|
+
const index = args.findIndex(isJsonOption);
|
|
136
|
+
if (index === -1) return false;
|
|
137
|
+
args.splice(index, 1);
|
|
138
|
+
return true;
|
|
139
|
+
}
|
|
140
|
+
|
|
97
141
|
export function takeOption(args: string[], flag: string): string | undefined {
|
|
98
142
|
const index = args.indexOf(flag);
|
|
99
143
|
if (index === -1) return undefined;
|
package/src/cli/status.ts
CHANGED
|
@@ -10,6 +10,8 @@ import { collectStartupHealth, type StartupHealth } from "../codex/autostart-hea
|
|
|
10
10
|
import { getCodexRoutingKind } from "../codex/inject";
|
|
11
11
|
import { diagnoseCodexShim } from "../codex/shim";
|
|
12
12
|
import { displayCodexRuntimePath, effortClampAppliesToRuntime, loadLastEffortClamp, resolveCodexRuntime } from "../codex/runtime";
|
|
13
|
+
import { packageVersion } from "./help";
|
|
14
|
+
import { computeVersionSkew, type VersionSkew } from "./version-skew";
|
|
13
15
|
import { redactSecretString, redactUserPath } from "../lib/redact";
|
|
14
16
|
import { collectOrcaCodexHomeDiagnostic, type OrcaCodexHomeDiagnostic } from "../codex/home";
|
|
15
17
|
import { grokFenceEndpointDrift, readGrokStatus } from "../grok/status";
|
|
@@ -70,6 +72,13 @@ export type CliStatusJson = {
|
|
|
70
72
|
};
|
|
71
73
|
};
|
|
72
74
|
codexHome: OrcaCodexHomeDiagnostic;
|
|
75
|
+
/**
|
|
76
|
+
* This CLI's version against the running proxy's (#2701).
|
|
77
|
+
*
|
|
78
|
+
* Additive and optional-by-value, so `schemaVersion` stays 1: an existing consumer that
|
|
79
|
+
* ignores the key is unaffected, and `proxyVersion` is null when nothing is live.
|
|
80
|
+
*/
|
|
81
|
+
versionSkew: VersionSkew;
|
|
73
82
|
};
|
|
74
83
|
|
|
75
84
|
export type CliStatusView = {
|
|
@@ -180,6 +189,9 @@ export async function collectStatus(): Promise<CliStatusView> {
|
|
|
180
189
|
const pidFile = readPid();
|
|
181
190
|
// Preserve an authoritative null from orphan/legacy liveness — do not restore pidFile.
|
|
182
191
|
const pid = resolveStatusPid(live, pidFile);
|
|
192
|
+
// No extra request: findLiveProxy's identity probe already parsed and validated the
|
|
193
|
+
// healthz body, so the version came back with the liveness result.
|
|
194
|
+
const versionSkew = computeVersionSkew(packageVersion(), live?.version);
|
|
183
195
|
const listen = live
|
|
184
196
|
? {
|
|
185
197
|
port: live.port,
|
|
@@ -347,6 +359,10 @@ export async function collectStatus(): Promise<CliStatusView> {
|
|
|
347
359
|
codexPlugins,
|
|
348
360
|
codexRuntime,
|
|
349
361
|
codexHome,
|
|
362
|
+
// Own field rather than a line in `codexRuntime.warning`: a stale ocx on PATH is a
|
|
363
|
+
// fact about this install, not about the Codex runtime, and filing it there would
|
|
364
|
+
// print it under the wrong heading (#2701).
|
|
365
|
+
versionSkew,
|
|
350
366
|
},
|
|
351
367
|
};
|
|
352
368
|
}
|