super-dsh 0.1.6 → 0.1.10-e
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/README.md +25 -3
- package/agent-agy/cordis.patch.yml +7 -4
- package/agent-agy/dist/agent.d.ts +12 -11
- package/agent-agy/dist/agy-auth-gateway.d.ts +124 -0
- package/agent-agy/dist/agy-auth-gateway.js +233 -0
- package/agent-agy/dist/agy-auth.d.ts +153 -0
- package/agent-agy/dist/agy-auth.js +179 -0
- package/agent-agy/dist/agy-cli-client.d.ts +39 -9
- package/agent-agy/dist/agy-cli-client.js +82 -18
- package/agent-agy/dist/agy-store.d.ts +8 -0
- package/agent-agy/dist/index.d.ts +46 -16
- package/agent-agy/dist/index.js +82 -56
- package/agent-agy/dist/models.d.ts +70 -0
- package/agent-agy/dist/models.js +102 -3
- package/agent-agy/package.json +7 -5
- package/agent-agy-sdk/cordis.patch.yml +58 -0
- package/agent-agy-sdk/dist/adapter.d.ts +15 -0
- package/agent-agy-sdk/dist/adapter.js +42 -0
- package/agent-agy-sdk/dist/agent-preset-agy.d.ts +27 -0
- package/agent-agy-sdk/dist/agent-preset-agy.js +32 -0
- package/agent-agy-sdk/dist/agent.d.ts +138 -0
- package/agent-agy-sdk/dist/agent.js +494 -0
- package/{agent-agy → agent-agy-sdk}/dist/agy-client.d.ts +26 -6
- package/{agent-agy → agent-agy-sdk}/dist/agy-client.js +43 -25
- package/agent-agy-sdk/dist/agy-sessions.d.ts +25 -0
- package/agent-agy-sdk/dist/agy-sessions.js +107 -0
- package/agent-agy-sdk/dist/agy-store.d.ts +39 -0
- package/agent-agy-sdk/dist/agy-store.js +83 -0
- package/agent-agy-sdk/dist/inbox.d.ts +26 -0
- package/agent-agy-sdk/dist/inbox.js +53 -0
- package/agent-agy-sdk/dist/index.d.ts +93 -0
- package/agent-agy-sdk/dist/index.js +476 -0
- package/agent-agy-sdk/dist/models.d.ts +41 -0
- package/agent-agy-sdk/dist/models.js +74 -0
- package/agent-agy-sdk/dist/sdk-venv.d.ts +76 -0
- package/agent-agy-sdk/dist/sdk-venv.js +173 -0
- package/agent-agy-sdk/dist/world-plugin.d.ts +10 -0
- package/agent-agy-sdk/dist/world-plugin.js +82 -0
- package/agent-agy-sdk/package.json +64 -0
- package/agent-claude/dist/adapter.js +106 -31
- package/agent-claude/dist/agent.js +154 -17
- package/agent-claude/dist/claude-client.js +84 -43
- package/agent-claude/dist/claude-launch.js +101 -0
- package/agent-claude/dist/claude-native-config.js +169 -0
- package/agent-claude/dist/claude-provider-gateway.js +340 -0
- package/agent-claude/dist/claude-providers.js +391 -0
- package/agent-claude/dist/claude-route.js +152 -0
- package/agent-claude/dist/index.js +214 -37
- package/agent-claude/dist/models.js +14 -0
- package/agent-claude/dist/sdk-runtime.js +320 -0
- package/agent-claude/package.json +6 -4
- package/agent-codex/README.md +141 -36
- package/agent-codex/cordis.patch.yml +14 -10
- package/agent-codex/dist/adapter.js +64 -28
- package/agent-codex/dist/agent.js +255 -81
- package/agent-codex/dist/app-server-approvals.js +165 -0
- package/agent-codex/dist/app-server-client.js +723 -0
- package/agent-codex/dist/app-server-events.js +432 -0
- package/agent-codex/dist/app-server-protocol.js +95 -0
- package/agent-codex/dist/app-server-wire.js +299 -0
- package/agent-codex/dist/codex-launch.js +78 -0
- package/agent-codex/dist/codex-native-config.js +392 -0
- package/agent-codex/dist/codex-provider-gateway.js +402 -0
- package/agent-codex/dist/codex-providers.js +376 -0
- package/agent-codex/dist/codex-route.js +86 -0
- package/agent-codex/dist/index.js +256 -115
- package/agent-codex/dist/permission.js +75 -1
- package/agent-codex/package.json +1 -2
- package/agent-codex-sdk/README.md +79 -0
- package/agent-codex-sdk/cordis.patch.yml +81 -0
- package/agent-codex-sdk/dist/adapter.js +91 -0
- package/agent-codex-sdk/dist/agent-preset-codex.js +32 -0
- package/agent-codex-sdk/dist/agent.js +1085 -0
- package/agent-codex-sdk/dist/codex-native-config.js +392 -0
- package/agent-codex-sdk/dist/codex-provider-gateway.js +402 -0
- package/agent-codex-sdk/dist/codex-providers.js +376 -0
- package/agent-codex-sdk/dist/codex-route.js +86 -0
- package/{agent-codex/dist/codex-client.js → agent-codex-sdk/dist/codex-sdk-client.js} +253 -32
- package/agent-codex-sdk/dist/codex-sdk-launch.js +92 -0
- package/agent-codex-sdk/dist/codex-store.js +180 -0
- package/agent-codex-sdk/dist/inbox.js +53 -0
- package/agent-codex-sdk/dist/index.js +739 -0
- package/agent-codex-sdk/dist/permission.js +71 -0
- package/agent-codex-sdk/dist/sdk-runtime.js +307 -0
- package/agent-codex-sdk/dist/session-map.js +167 -0
- package/agent-codex-sdk/dist/world-plugin.js +93 -0
- package/agent-codex-sdk/package.json +79 -0
- package/agent-codex-sdk/scripts/fetch-sdk-runtime.mjs +57 -0
- package/agent-omp/README.md +72 -55
- package/agent-omp/dist/agent.js +36 -11
- package/agent-omp/dist/index.js +92 -52
- package/agent-omp/dist/lazy-rpc.js +11 -4
- package/agent-omp/dist/omp-cli.js +114 -97
- package/agent-omp/dist/omp-store.js +12 -4
- package/agent-omp/dist/permission.js +70 -5
- package/agent-omp/dist/rpc-client.js +409 -0
- package/agent-omp/dist/world-plugin.js +36 -3
- package/agent-omp/package.json +2 -6
- package/agent-omp-sdk/README.md +103 -0
- package/agent-omp-sdk/cordis.patch.yml +108 -0
- package/agent-omp-sdk/dist/adapter.d.ts +21 -0
- package/agent-omp-sdk/dist/adapter.js +36 -0
- package/agent-omp-sdk/dist/agent-preset-omp.d.ts +31 -0
- package/agent-omp-sdk/dist/agent-preset-omp.js +32 -0
- package/agent-omp-sdk/dist/agent.d.ts +64 -0
- package/agent-omp-sdk/dist/agent.js +1178 -0
- package/agent-omp-sdk/dist/discovery.d.ts +57 -0
- package/agent-omp-sdk/dist/discovery.js +116 -0
- package/agent-omp-sdk/dist/inbox.d.ts +28 -0
- package/agent-omp-sdk/dist/inbox.js +53 -0
- package/agent-omp-sdk/dist/index.d.ts +60 -0
- package/agent-omp-sdk/dist/index.js +632 -0
- package/agent-omp-sdk/dist/knobs.d.ts +34 -0
- package/agent-omp-sdk/dist/knobs.js +50 -0
- package/agent-omp-sdk/dist/lazy-rpc.d.ts +98 -0
- package/agent-omp-sdk/dist/lazy-rpc.js +178 -0
- package/agent-omp-sdk/dist/mobile/zoom-guard.d.ts +187 -0
- package/agent-omp-sdk/dist/mobile/zoom-guard.js +294 -0
- package/agent-omp-sdk/dist/mobile-boot.d.ts +49 -0
- package/agent-omp-sdk/dist/mobile-boot.js +38 -0
- package/agent-omp-sdk/dist/models.d.ts +39 -0
- package/agent-omp-sdk/dist/models.js +299 -0
- package/agent-omp-sdk/dist/omp-cli.d.ts +67 -0
- package/agent-omp-sdk/dist/omp-cli.js +164 -0
- package/agent-omp-sdk/dist/omp-disk-discovery.d.ts +27 -0
- package/agent-omp-sdk/dist/omp-disk-discovery.js +178 -0
- package/agent-omp-sdk/dist/omp-executable.d.ts +2 -0
- package/agent-omp-sdk/dist/omp-executable.js +30 -0
- package/agent-omp-sdk/dist/omp-store.d.ts +134 -0
- package/agent-omp-sdk/dist/omp-store.js +526 -0
- package/agent-omp-sdk/dist/pairing.d.ts +7 -0
- package/agent-omp-sdk/dist/pairing.js +45 -0
- package/agent-omp-sdk/dist/permission.d.ts +138 -0
- package/agent-omp-sdk/dist/permission.js +161 -0
- package/agent-omp-sdk/dist/protocol.d.ts +189 -0
- package/agent-omp-sdk/dist/rpc-types.d.ts +77 -0
- package/agent-omp-sdk/dist/rpc-types.js +1 -0
- package/agent-omp-sdk/dist/sdk-client.d.ts +160 -0
- package/{agent-omp → agent-omp-sdk}/dist/sdk-client.js +108 -56
- package/agent-omp-sdk/dist/sdk-runtime.d.ts +211 -0
- package/agent-omp-sdk/dist/sdk-runtime.js +418 -0
- package/agent-omp-sdk/dist/sidecar-client.d.ts +87 -0
- package/agent-omp-sdk/dist/sidecar-client.js +197 -0
- package/agent-omp-sdk/dist/store/adapter.d.ts +17 -0
- package/agent-omp-sdk/dist/store/adapter.js +47 -0
- package/agent-omp-sdk/dist/store/db.d.ts +70 -0
- package/agent-omp-sdk/dist/store/db.js +210 -0
- package/agent-omp-sdk/dist/store/index.d.ts +23 -0
- package/agent-omp-sdk/dist/store/index.js +59 -0
- package/agent-omp-sdk/dist/store/reconcile.d.ts +15 -0
- package/agent-omp-sdk/dist/store/reconcile.js +51 -0
- package/agent-omp-sdk/dist/store/schema.d.ts +8 -0
- package/agent-omp-sdk/dist/store/schema.js +40 -0
- package/agent-omp-sdk/dist/transport.d.ts +49 -0
- package/agent-omp-sdk/dist/transport.js +35 -0
- package/agent-omp-sdk/dist/world-home.d.ts +4 -0
- package/agent-omp-sdk/dist/world-home.js +23 -0
- package/agent-omp-sdk/dist/world-plugin.d.ts +10 -0
- package/agent-omp-sdk/dist/world-plugin.js +137 -0
- package/agent-omp-sdk/package.json +87 -0
- package/client/index.js +1883 -287
- package/cordis.patch.yml +80 -0
- package/package.json +23 -9
- package/superd-hub/dist/a2a-delivery.d.ts +189 -0
- package/superd-hub/dist/a2a-delivery.js +240 -0
- package/superd-hub/dist/a2a-host.d.ts +76 -0
- package/superd-hub/dist/a2a-host.js +145 -0
- package/superd-hub/dist/a2a-ingest.d.ts +31 -0
- package/superd-hub/dist/a2a-ingest.js +636 -0
- package/superd-hub/dist/a2a-log.d.ts +17 -0
- package/superd-hub/dist/a2a-log.js +42 -0
- package/superd-hub/dist/a2a-row.d.ts +65 -0
- package/superd-hub/dist/a2a-row.js +162 -0
- package/superd-hub/dist/a2a-service.d.ts +357 -0
- package/superd-hub/dist/a2a-service.js +980 -0
- package/superd-hub/dist/a2a-wire.d.ts +220 -0
- package/superd-hub/dist/a2a-wire.js +305 -0
- package/superd-hub/dist/agent-ids.d.ts +10 -0
- package/superd-hub/dist/agent-ids.js +68 -0
- package/superd-hub/dist/agent-roster.d.ts +29 -18
- package/superd-hub/dist/agent-roster.js +54 -15
- package/superd-hub/dist/agent-status.d.ts +119 -0
- package/superd-hub/dist/agent-status.js +386 -0
- package/superd-hub/dist/client/agent-settings.d.ts +210 -0
- package/superd-hub/dist/client/agent-settings.js +300 -0
- package/superd-hub/dist/client/mesh-api.d.ts +134 -0
- package/superd-hub/dist/client/mesh-api.js +219 -0
- package/superd-hub/dist/client/mesh-groups.d.ts +63 -0
- package/superd-hub/dist/client/mesh-groups.js +66 -0
- package/superd-hub/dist/client/roster.d.ts +48 -0
- package/superd-hub/dist/client/roster.js +102 -0
- package/superd-hub/dist/client/runtime-settings-api.d.ts +76 -0
- package/superd-hub/dist/client/runtime-settings-api.js +137 -0
- package/superd-hub/dist/client/switch.d.ts +165 -0
- package/superd-hub/dist/client/switch.js +295 -0
- package/superd-hub/dist/exec-pty-channel.d.ts +58 -0
- package/superd-hub/dist/exec-pty-channel.js +162 -0
- package/superd-hub/dist/exec-pty-framing.d.ts +32 -0
- package/superd-hub/dist/exec-pty-framing.js +81 -0
- package/superd-hub/dist/exec-pty-rpc.d.ts +21 -0
- package/superd-hub/dist/exec-pty-rpc.js +128 -0
- package/superd-hub/dist/exec-pty-session.d.ts +76 -0
- package/superd-hub/dist/exec-pty-session.js +234 -0
- package/superd-hub/dist/exec.d.ts +56 -0
- package/superd-hub/dist/exec.js +225 -0
- package/superd-hub/dist/gateway.d.ts +39 -2
- package/superd-hub/dist/gateway.js +62 -3
- package/superd-hub/dist/hub-tool.d.ts +174 -0
- package/superd-hub/dist/hub-tool.js +464 -0
- package/superd-hub/dist/index.d.ts +15 -1
- package/superd-hub/dist/index.js +16 -0
- package/superd-hub/dist/machines.d.ts +45 -0
- package/superd-hub/dist/machines.js +125 -0
- package/superd-hub/dist/mesh-federate.d.ts +138 -0
- package/superd-hub/dist/mesh-federate.js +223 -0
- package/superd-hub/dist/mesh-gateway.d.ts +67 -0
- package/superd-hub/dist/mesh-gateway.js +408 -0
- package/superd-hub/dist/mesh-link.d.ts +274 -0
- package/superd-hub/dist/mesh-link.js +441 -0
- package/superd-hub/dist/mesh-rpc.d.ts +123 -0
- package/superd-hub/dist/mesh-rpc.js +250 -0
- package/superd-hub/dist/mux-client.d.ts +71 -0
- package/superd-hub/dist/mux-client.js +281 -0
- package/superd-hub/dist/pack-version.d.ts +2 -0
- package/superd-hub/dist/pack-version.js +30 -0
- package/superd-hub/dist/roster.d.ts +6 -0
- package/superd-hub/dist/rpc.d.ts +18 -2
- package/superd-hub/dist/rpc.js +34 -4
- package/superd-hub/dist/spawn-world.js +3 -1
- package/superd-hub/dist/switch-core.d.ts +62 -25
- package/superd-hub/dist/switch-core.js +53 -30
- package/superd-hub/dist/system-head.d.ts +22 -12
- package/superd-hub/dist/system-head.js +100 -84
- package/superd-hub/dist/wire-endpoints.d.ts +264 -0
- package/superd-hub/dist/wire-endpoints.js +180 -0
- package/superd-hub/lib/client/index.js +1883 -287
- package/superd-hub/package.json +11 -2
- package/agent-agy/dist/agent-preset-projection.d.ts +0 -31
- package/agent-agy/dist/agent-preset-projection.js +0 -33
- package/agent-claude/dist/agent-preset-projection.js +0 -30
- package/agent-codex/dist/agent-preset-projection.js +0 -37
- package/agent-codex/dist/app-home.js +0 -65
- package/agent-codex/dist/models.js +0 -152
- package/agent-codex/dist/spike.js +0 -46
- package/agent-hermes/dist/agent-preset-projection.js +0 -34
- package/agent-omp/dist/agent-preset-projection.js +0 -36
- package/agent-omp/dist/app-home.js +0 -86
- package/agent-omp/dist/sidecar-client.js +0 -195
- package/agent-pi/dist/agent-preset-projection.js +0 -29
- package/superd-hub/dist/client-shim.d.ts +0 -28
- package/superd-hub/dist/client-shim.js +0 -251
- package/superd-hub/dist/index-pass.d.ts +0 -22
- package/superd-hub/dist/index-pass.js +0 -47
- package/superd-hub/dist/labels.d.ts +0 -45
- package/superd-hub/dist/labels.js +0 -83
- /package/{agent-agy → agent-agy-sdk}/bridge/agy_bridge.py +0 -0
- /package/{agent-agy → agent-agy-sdk}/bridge/gemini_openai_server.mjs +0 -0
- /package/{agent-codex/dist/codex-events.js → agent-codex-sdk/dist/codex-sdk-events.js} +0 -0
- /package/{agent-omp → agent-omp-sdk}/dist/protocol.js +0 -0
- /package/{agent-omp → agent-omp-sdk}/sidecar/main.ts +0 -0
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime settings panels for the Hub's Agent Runtimes rows (P3, 2026-10-02
|
|
3
|
+
* user ruling).
|
|
4
|
+
*
|
|
5
|
+
* Interaction contract the UI follows (ruling, verbatim intent):
|
|
6
|
+
* - clicking the ROW BODY expands that runtime's OWN settings inline
|
|
7
|
+
* (accordion: at most one row open at a time);
|
|
8
|
+
* - the row's existing right-hand `switch` affordance is the ONLY switch —
|
|
9
|
+
* the panel never carries a second one.
|
|
10
|
+
*
|
|
11
|
+
* Config belongs to the adapter (agent-adapter-dev-rules §12), so this module
|
|
12
|
+
* owns only the presentation-side logic: which runtimes have a panel, the
|
|
13
|
+
* accordion transition, and a defensive parse of whatever the adapter's
|
|
14
|
+
* `*Auth/get` RPC returns. The adapter decides which auth methods exist, which
|
|
15
|
+
* are available on this machine, and which one is in force.
|
|
16
|
+
*
|
|
17
|
+
* Node-safe by construction (no DOM, no React, no fetch) — the host tsconfig
|
|
18
|
+
* dual-emits this file to `dist/client/agent-settings.js` so
|
|
19
|
+
* test/client-agent-settings.test.mjs can pin the parse and accordion rules
|
|
20
|
+
* under `node --test` (the roster.ts / mesh-api.ts precedent).
|
|
21
|
+
*
|
|
22
|
+
* @module superd-hub/client/agent-settings
|
|
23
|
+
*/
|
|
24
|
+
/** One selectable auth method as the adapter describes it. */
|
|
25
|
+
export interface AuthMethodOption {
|
|
26
|
+
/** Stable id the RPC round-trips (`apiKey` | `oauth` | `adc`). */
|
|
27
|
+
readonly key: string;
|
|
28
|
+
/** Human label rendered on the option. */
|
|
29
|
+
readonly label: string;
|
|
30
|
+
/** Detected on THIS machine. Drives the availability annotation. */
|
|
31
|
+
readonly available: boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Rendered but not selectable this round (the OAuth placeholder): the UI
|
|
34
|
+
* shows the shape and says why it is inert, instead of hiding it.
|
|
35
|
+
*/
|
|
36
|
+
readonly placeholder: boolean;
|
|
37
|
+
/** One short line explaining the option (or why it is inert). */
|
|
38
|
+
readonly hint: string;
|
|
39
|
+
}
|
|
40
|
+
/** The adapter's answer to `<runtime>Auth/get`. */
|
|
41
|
+
export interface RuntimeAuthView {
|
|
42
|
+
readonly methods: readonly AuthMethodOption[];
|
|
43
|
+
/** The user's explicit persisted choice; `null` when they have not made one. */
|
|
44
|
+
readonly selected: string | null;
|
|
45
|
+
/** What a new session would ACTUALLY use right now (ladder fallback applied). */
|
|
46
|
+
readonly effective: string | null;
|
|
47
|
+
/** Native file the adapter maintains for this runtime, when it owns one. */
|
|
48
|
+
readonly settingsPath?: string;
|
|
49
|
+
/** Free-form note from the adapter (e.g. why a mode is unavailable). */
|
|
50
|
+
readonly note?: string;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* A runtime's settings panel: which adapter Remote answers for it.
|
|
54
|
+
*
|
|
55
|
+
* The namespace is the adapter's own service key — the hub forwards
|
|
56
|
+
* `envelope.method` verbatim, so the client needs only the name. A runtime absent
|
|
57
|
+
* from this table has no panel yet: its row still expands, to an explicit empty
|
|
58
|
+
* state rather than a dead control (the interaction stays uniform, and a runtime
|
|
59
|
+
* with nothing to configure SAYS so).
|
|
60
|
+
*/
|
|
61
|
+
export interface SettingsPanelSpec {
|
|
62
|
+
/** The adapter's Remote namespace (`agyAuth`). */
|
|
63
|
+
readonly namespace: string;
|
|
64
|
+
/**
|
|
65
|
+
* Which body the expanded row mounts. A runtime's config is not always a
|
|
66
|
+
* choice among a fixed set: Antigravity picks one sign-in form from a ladder,
|
|
67
|
+
* while Codex carries a LIST of providers the user adds, edits and removes
|
|
68
|
+
* (2026-10-03 ruling; the former active/select toggle is gone — which provider
|
|
69
|
+
* a Codex session runs on is DSH's own per-session model selection, not a
|
|
70
|
+
* hub-side switch). One namespace, two shapes — the kind discriminates.
|
|
71
|
+
*/
|
|
72
|
+
readonly kind: 'auth' | 'providers';
|
|
73
|
+
}
|
|
74
|
+
/** The panel spec for a runtime, or `undefined` when it has none yet. */
|
|
75
|
+
export declare function settingsPanelFor(agentKey: string): SettingsPanelSpec | undefined;
|
|
76
|
+
/** Whether this runtime's row body expands into a real settings panel. */
|
|
77
|
+
export declare function hasSettingsPanel(agentKey: string): boolean;
|
|
78
|
+
/**
|
|
79
|
+
* Accordion transition: at most one row open. Clicking the open row closes it;
|
|
80
|
+
* clicking another moves the panel there in one click.
|
|
81
|
+
*/
|
|
82
|
+
export declare function toggleExpanded(current: string | null, key: string): string | null;
|
|
83
|
+
/**
|
|
84
|
+
* Defensive parse of the RPC answer. Returns `undefined` for anything that is
|
|
85
|
+
* not a usable view (no methods, wrong shape, adapter absent) so the panel can
|
|
86
|
+
* render an explicit failure instead of an empty list that looks like success.
|
|
87
|
+
*/
|
|
88
|
+
export declare function parseAuthView(raw: unknown): RuntimeAuthView | undefined;
|
|
89
|
+
/**
|
|
90
|
+
* The option the panel marks as in force: the effective one when the adapter
|
|
91
|
+
* named it, else the explicit selection. `undefined` when neither is known —
|
|
92
|
+
* the panel then marks nothing rather than guessing.
|
|
93
|
+
*/
|
|
94
|
+
export declare function activeMethod(view: RuntimeAuthView): AuthMethodOption | undefined;
|
|
95
|
+
/** Whether an option may be picked (placeholders are rendered, never selected). */
|
|
96
|
+
export declare function selectable(option: AuthMethodOption): boolean;
|
|
97
|
+
/** One model behind a provider, as the panel lists it. */
|
|
98
|
+
export interface ProviderModelOption {
|
|
99
|
+
readonly id: string;
|
|
100
|
+
/** Human label; the adapter sends one, else the id stands in. */
|
|
101
|
+
readonly label: string;
|
|
102
|
+
readonly contextWindow?: number;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* The auth shapes the provider faces use between them: `apiKey` is shared,
|
|
106
|
+
* codex adds `env` (the key lives in a named variable), claude adds
|
|
107
|
+
* `authToken` (a Bearer token). Each face's adapter rejects the other's
|
|
108
|
+
* addition on save, which is why the form offers kinds per view
|
|
109
|
+
* (`authKindOptions`) instead of one fixed list.
|
|
110
|
+
*/
|
|
111
|
+
export type ProviderAuthKind = 'apiKey' | 'env' | 'authToken';
|
|
112
|
+
/** One configured provider, listed but never carrying its key. */
|
|
113
|
+
export interface ProviderOption {
|
|
114
|
+
readonly id: string;
|
|
115
|
+
readonly label: string;
|
|
116
|
+
readonly baseUrl: string;
|
|
117
|
+
readonly protocol: string;
|
|
118
|
+
readonly models: readonly ProviderModelOption[];
|
|
119
|
+
/** Which key shape the entry stores — see `ProviderAuthKind`. */
|
|
120
|
+
readonly authKind: ProviderAuthKind;
|
|
121
|
+
/** The variable an env-shaped provider expects. */
|
|
122
|
+
readonly envName?: string;
|
|
123
|
+
/** Whether a key is available — the ONLY thing the wire says about it. */
|
|
124
|
+
readonly hasKey: boolean;
|
|
125
|
+
}
|
|
126
|
+
/** One wire protocol the adapter offers, and whether it may be picked. */
|
|
127
|
+
export interface ProviderProtocolOption {
|
|
128
|
+
readonly value: string;
|
|
129
|
+
readonly label: string;
|
|
130
|
+
readonly available: boolean;
|
|
131
|
+
readonly hint: string;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* The files this surface reads and writes, shown so where an edit lands is
|
|
135
|
+
* never a surprise. Only `store` is common: each face names its own native
|
|
136
|
+
* files (codex: config/auth/catalogs; claude: settings), and a face that
|
|
137
|
+
* lacks a slot simply omits it — the footer renders what is there.
|
|
138
|
+
*/
|
|
139
|
+
export interface ProviderPaths {
|
|
140
|
+
readonly store: string;
|
|
141
|
+
readonly config?: string;
|
|
142
|
+
readonly auth?: string;
|
|
143
|
+
readonly catalogs?: string;
|
|
144
|
+
readonly settings?: string;
|
|
145
|
+
}
|
|
146
|
+
/** The adapter's answer to `codexProviders/get` / `claudeProviders/get` (and to every mutation). */
|
|
147
|
+
export interface RuntimeProvidersView {
|
|
148
|
+
readonly providers: readonly ProviderOption[];
|
|
149
|
+
/**
|
|
150
|
+
* What the user's OWN native config currently names (codex: the provider
|
|
151
|
+
* `~/.codex/config.toml` selects; claude: the endpoint the `settings.json`
|
|
152
|
+
* env pins). Informational only — there is no action attached to it, and
|
|
153
|
+
* DSH's per-session model selection (not this list) decides what a session
|
|
154
|
+
* runs.
|
|
155
|
+
*/
|
|
156
|
+
readonly native: {
|
|
157
|
+
readonly providerId?: string;
|
|
158
|
+
readonly baseUrl?: string;
|
|
159
|
+
readonly model?: string;
|
|
160
|
+
} | null;
|
|
161
|
+
/**
|
|
162
|
+
* The wire protocols the adapter offers, in the order it offers them. Empty
|
|
163
|
+
* for a face with no protocol choice (claude) — the panel then omits the
|
|
164
|
+
* dropdown instead of inventing options the runtime cannot run.
|
|
165
|
+
*/
|
|
166
|
+
readonly protocols: readonly ProviderProtocolOption[];
|
|
167
|
+
readonly paths: ProviderPaths;
|
|
168
|
+
/** Epoch ms of the last reconcile against the native config, when one has happened. */
|
|
169
|
+
readonly syncedAt?: number;
|
|
170
|
+
readonly note?: string;
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Defensive parse of the adapter's providers answer.
|
|
174
|
+
*
|
|
175
|
+
* `undefined` for anything that is not a usable view — but an empty `providers`
|
|
176
|
+
* array is valid (a fresh install), and so is a provider whose `models` array
|
|
177
|
+
* is empty. `protocols` is the codex face's field: an ABSENT field (the claude
|
|
178
|
+
* face) reads as "no protocol choice" and never fails the parse, while a
|
|
179
|
+
* present-but-unusable list is a malformed codex answer and rejects as before.
|
|
180
|
+
*/
|
|
181
|
+
export declare function parseProvidersView(raw: unknown): RuntimeProvidersView | undefined;
|
|
182
|
+
/** One selectable auth shape for the add/edit form (`authKindOptions`). */
|
|
183
|
+
export interface ProviderAuthKindOption {
|
|
184
|
+
readonly value: ProviderAuthKind;
|
|
185
|
+
readonly label: string;
|
|
186
|
+
readonly hint: string;
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* The auth shapes the add/edit form may pick for a provider, in display order.
|
|
190
|
+
*
|
|
191
|
+
* The view carries no explicit vocabulary field, so the panel reads it off the
|
|
192
|
+
* one field that distinguishes the faces today: `protocols`. The codex face
|
|
193
|
+
* advertises wire protocols and offers key-or-env-var; the claude face carries
|
|
194
|
+
* neither and offers its two header shapes instead. The list is per-view,
|
|
195
|
+
* never a fixed union — each adapter rejects the other's kinds on save, so a
|
|
196
|
+
* union dropdown would miscode silently. A third face extends the table.
|
|
197
|
+
*/
|
|
198
|
+
export declare function authKindOptions(view: RuntimeProvidersView): readonly ProviderAuthKindOption[];
|
|
199
|
+
/**
|
|
200
|
+
* `provider / model` (codex) or `endpoint / model` (claude) — whichever halves
|
|
201
|
+
* the user's own native config names, joined for the one-line fact the panel
|
|
202
|
+
* states and never acts on.
|
|
203
|
+
*/
|
|
204
|
+
export declare function nativeDescription(native: RuntimeProvidersView['native']): string;
|
|
205
|
+
/**
|
|
206
|
+
* The non-store paths joined for the panel's second footer line, in a fixed
|
|
207
|
+
* slot order. `undefined` when the face named none — the line is omitted
|
|
208
|
+
* entirely rather than rendered with blank slots.
|
|
209
|
+
*/
|
|
210
|
+
export declare function nativePathsLine(paths: ProviderPaths): string | undefined;
|
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime settings panels for the Hub's Agent Runtimes rows (P3, 2026-10-02
|
|
3
|
+
* user ruling).
|
|
4
|
+
*
|
|
5
|
+
* Interaction contract the UI follows (ruling, verbatim intent):
|
|
6
|
+
* - clicking the ROW BODY expands that runtime's OWN settings inline
|
|
7
|
+
* (accordion: at most one row open at a time);
|
|
8
|
+
* - the row's existing right-hand `switch` affordance is the ONLY switch —
|
|
9
|
+
* the panel never carries a second one.
|
|
10
|
+
*
|
|
11
|
+
* Config belongs to the adapter (agent-adapter-dev-rules §12), so this module
|
|
12
|
+
* owns only the presentation-side logic: which runtimes have a panel, the
|
|
13
|
+
* accordion transition, and a defensive parse of whatever the adapter's
|
|
14
|
+
* `*Auth/get` RPC returns. The adapter decides which auth methods exist, which
|
|
15
|
+
* are available on this machine, and which one is in force.
|
|
16
|
+
*
|
|
17
|
+
* Node-safe by construction (no DOM, no React, no fetch) — the host tsconfig
|
|
18
|
+
* dual-emits this file to `dist/client/agent-settings.js` so
|
|
19
|
+
* test/client-agent-settings.test.mjs can pin the parse and accordion rules
|
|
20
|
+
* under `node --test` (the roster.ts / mesh-api.ts precedent).
|
|
21
|
+
*
|
|
22
|
+
* @module superd-hub/client/agent-settings
|
|
23
|
+
*/
|
|
24
|
+
const SETTINGS_PANELS = {
|
|
25
|
+
// The one runtime with a settings surface this round: the CLI adapter owns the
|
|
26
|
+
// auth ladder and is the configuration surface for the user's local Antigravity.
|
|
27
|
+
agy: { namespace: 'agyAuth', kind: 'auth' },
|
|
28
|
+
// Codex's own config.toml names exactly one `model_provider`, but that file
|
|
29
|
+
// is the USER's and no longer the hub's toggle: Super D hosts the list
|
|
30
|
+
// cc-switch otherwise holds (add / edit / remove) and the adapter owns both
|
|
31
|
+
// the list and whatever it does with the native file.
|
|
32
|
+
codex: { namespace: 'codexProviders', kind: 'providers' },
|
|
33
|
+
// Both Codex lines host the SAME provider list (2026-10-04): the SDK half is
|
|
34
|
+
// the same agent through a different transport, so it carries the same panel
|
|
35
|
+
// and the same Remote namespace. The two worlds keep separate stores under
|
|
36
|
+
// their own `agents/<key>/`, so neither sees the other's list.
|
|
37
|
+
'codex-sdk': { namespace: 'codexProviders', kind: 'providers' },
|
|
38
|
+
// Claude serves the same providers view shape as Codex with no protocol
|
|
39
|
+
// dropdown: its adapter's view carries no `protocols` field (claude has no
|
|
40
|
+
// wire-protocol choice to make), so the body degrades to what the view does
|
|
41
|
+
// carry — two header auth kinds and its own native fact (2026-10-04).
|
|
42
|
+
claude: { namespace: 'claudeProviders', kind: 'providers' },
|
|
43
|
+
};
|
|
44
|
+
/** The panel spec for a runtime, or `undefined` when it has none yet. */
|
|
45
|
+
export function settingsPanelFor(agentKey) {
|
|
46
|
+
return SETTINGS_PANELS[agentKey];
|
|
47
|
+
}
|
|
48
|
+
/** Whether this runtime's row body expands into a real settings panel. */
|
|
49
|
+
export function hasSettingsPanel(agentKey) {
|
|
50
|
+
return settingsPanelFor(agentKey) !== undefined;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Accordion transition: at most one row open. Clicking the open row closes it;
|
|
54
|
+
* clicking another moves the panel there in one click.
|
|
55
|
+
*/
|
|
56
|
+
export function toggleExpanded(current, key) {
|
|
57
|
+
return current === key ? null : key;
|
|
58
|
+
}
|
|
59
|
+
function str(value) {
|
|
60
|
+
return typeof value === 'string' && value !== '' ? value : undefined;
|
|
61
|
+
}
|
|
62
|
+
function methodOption(raw) {
|
|
63
|
+
if (typeof raw !== 'object' || raw === null)
|
|
64
|
+
return undefined;
|
|
65
|
+
const record = raw;
|
|
66
|
+
const key = str(record.key);
|
|
67
|
+
const label = str(record.label);
|
|
68
|
+
if (key === undefined || label === undefined)
|
|
69
|
+
return undefined;
|
|
70
|
+
return {
|
|
71
|
+
key,
|
|
72
|
+
label,
|
|
73
|
+
// Absent flags read as the conservative value: an older adapter that omits
|
|
74
|
+
// `available` must not render an option as usable when we cannot tell.
|
|
75
|
+
available: record.available === true,
|
|
76
|
+
placeholder: record.placeholder === true,
|
|
77
|
+
hint: str(record.hint) ?? '',
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Defensive parse of the RPC answer. Returns `undefined` for anything that is
|
|
82
|
+
* not a usable view (no methods, wrong shape, adapter absent) so the panel can
|
|
83
|
+
* render an explicit failure instead of an empty list that looks like success.
|
|
84
|
+
*/
|
|
85
|
+
export function parseAuthView(raw) {
|
|
86
|
+
if (typeof raw !== 'object' || raw === null)
|
|
87
|
+
return undefined;
|
|
88
|
+
const record = raw;
|
|
89
|
+
if (!Array.isArray(record.methods))
|
|
90
|
+
return undefined;
|
|
91
|
+
const methods = record.methods.map(methodOption).filter((m) => m !== undefined);
|
|
92
|
+
if (methods.length === 0)
|
|
93
|
+
return undefined;
|
|
94
|
+
const settingsPath = str(record.settingsPath);
|
|
95
|
+
const note = str(record.note);
|
|
96
|
+
return {
|
|
97
|
+
methods,
|
|
98
|
+
selected: str(record.selected) ?? null,
|
|
99
|
+
effective: str(record.effective) ?? null,
|
|
100
|
+
...(settingsPath === undefined ? {} : { settingsPath }),
|
|
101
|
+
...(note === undefined ? {} : { note }),
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* The option the panel marks as in force: the effective one when the adapter
|
|
106
|
+
* named it, else the explicit selection. `undefined` when neither is known —
|
|
107
|
+
* the panel then marks nothing rather than guessing.
|
|
108
|
+
*/
|
|
109
|
+
export function activeMethod(view) {
|
|
110
|
+
const wanted = view.effective ?? view.selected;
|
|
111
|
+
if (wanted === null)
|
|
112
|
+
return undefined;
|
|
113
|
+
return view.methods.find((method) => method.key === wanted);
|
|
114
|
+
}
|
|
115
|
+
/** Whether an option may be picked (placeholders are rendered, never selected). */
|
|
116
|
+
export function selectable(option) {
|
|
117
|
+
return !option.placeholder;
|
|
118
|
+
}
|
|
119
|
+
function nonEmpty(value) {
|
|
120
|
+
return typeof value === 'string' && value !== '' ? value : undefined;
|
|
121
|
+
}
|
|
122
|
+
function modelOption(raw) {
|
|
123
|
+
if (typeof raw !== 'object' || raw === null)
|
|
124
|
+
return undefined;
|
|
125
|
+
const record = raw;
|
|
126
|
+
const id = nonEmpty(record.id);
|
|
127
|
+
if (id === undefined)
|
|
128
|
+
return undefined;
|
|
129
|
+
const contextWindow = record.contextWindow;
|
|
130
|
+
return {
|
|
131
|
+
id,
|
|
132
|
+
label: nonEmpty(record.displayName) ?? id,
|
|
133
|
+
...(typeof contextWindow === 'number' && Number.isSafeInteger(contextWindow) && contextWindow > 0
|
|
134
|
+
? { contextWindow }
|
|
135
|
+
: {}),
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
function providerAuthKind(raw) {
|
|
139
|
+
// An unstated or unknown kind reads as the shared conservative default,
|
|
140
|
+
// never as the other face's shape.
|
|
141
|
+
return raw === 'env' ? 'env' : raw === 'authToken' ? 'authToken' : 'apiKey';
|
|
142
|
+
}
|
|
143
|
+
function providerOption(raw) {
|
|
144
|
+
if (typeof raw !== 'object' || raw === null)
|
|
145
|
+
return undefined;
|
|
146
|
+
const record = raw;
|
|
147
|
+
const id = nonEmpty(record.id);
|
|
148
|
+
const label = nonEmpty(record.label);
|
|
149
|
+
const baseUrl = nonEmpty(record.baseUrl);
|
|
150
|
+
if (id === undefined || label === undefined || baseUrl === undefined)
|
|
151
|
+
return undefined;
|
|
152
|
+
const models = Array.isArray(record.models)
|
|
153
|
+
? record.models.map(modelOption).filter((entry) => entry !== undefined)
|
|
154
|
+
: [];
|
|
155
|
+
const envName = nonEmpty(record.envName);
|
|
156
|
+
return {
|
|
157
|
+
id,
|
|
158
|
+
label,
|
|
159
|
+
baseUrl,
|
|
160
|
+
// An unknown protocol is never echoed into a control; the only value this
|
|
161
|
+
// harness can actually run is the safe default.
|
|
162
|
+
protocol: record.protocol === 'chat' ? 'chat' : 'responses',
|
|
163
|
+
models,
|
|
164
|
+
authKind: providerAuthKind(record.authKind),
|
|
165
|
+
...(envName === undefined ? {} : { envName }),
|
|
166
|
+
hasKey: record.hasKey === true,
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
function protocolOption(raw) {
|
|
170
|
+
if (typeof raw !== 'object' || raw === null)
|
|
171
|
+
return undefined;
|
|
172
|
+
const record = raw;
|
|
173
|
+
const value = nonEmpty(record.value);
|
|
174
|
+
const label = nonEmpty(record.label);
|
|
175
|
+
if (value === undefined || label === undefined)
|
|
176
|
+
return undefined;
|
|
177
|
+
return { value, label, available: record.available === true, hint: nonEmpty(record.hint) ?? '' };
|
|
178
|
+
}
|
|
179
|
+
function providerPaths(raw) {
|
|
180
|
+
if (typeof raw !== 'object' || raw === null)
|
|
181
|
+
return undefined;
|
|
182
|
+
const record = raw;
|
|
183
|
+
const store = nonEmpty(record.store);
|
|
184
|
+
// The store is the only slot every face promises; the native files are the
|
|
185
|
+
// face's own, and an absent one stays absent rather than blank.
|
|
186
|
+
if (store === undefined)
|
|
187
|
+
return undefined;
|
|
188
|
+
const config = nonEmpty(record.config);
|
|
189
|
+
const auth = nonEmpty(record.auth);
|
|
190
|
+
const catalogs = nonEmpty(record.catalogs);
|
|
191
|
+
const settings = nonEmpty(record.settings);
|
|
192
|
+
return {
|
|
193
|
+
store,
|
|
194
|
+
...(config === undefined ? {} : { config }),
|
|
195
|
+
...(auth === undefined ? {} : { auth }),
|
|
196
|
+
...(catalogs === undefined ? {} : { catalogs }),
|
|
197
|
+
...(settings === undefined ? {} : { settings }),
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Defensive parse of the adapter's providers answer.
|
|
202
|
+
*
|
|
203
|
+
* `undefined` for anything that is not a usable view — but an empty `providers`
|
|
204
|
+
* array is valid (a fresh install), and so is a provider whose `models` array
|
|
205
|
+
* is empty. `protocols` is the codex face's field: an ABSENT field (the claude
|
|
206
|
+
* face) reads as "no protocol choice" and never fails the parse, while a
|
|
207
|
+
* present-but-unusable list is a malformed codex answer and rejects as before.
|
|
208
|
+
*/
|
|
209
|
+
export function parseProvidersView(raw) {
|
|
210
|
+
if (typeof raw !== 'object' || raw === null)
|
|
211
|
+
return undefined;
|
|
212
|
+
const record = raw;
|
|
213
|
+
if (!Array.isArray(record.providers))
|
|
214
|
+
return undefined;
|
|
215
|
+
// Absent `protocols` (the claude face) → nothing to offer, the panel omits
|
|
216
|
+
// the dropdown. Present (the codex face) → must parse to at least one
|
|
217
|
+
// option, exactly as before.
|
|
218
|
+
let protocols = [];
|
|
219
|
+
if (record.protocols !== undefined) {
|
|
220
|
+
if (!Array.isArray(record.protocols))
|
|
221
|
+
return undefined;
|
|
222
|
+
protocols = record.protocols.map(protocolOption).filter((entry) => entry !== undefined);
|
|
223
|
+
if (protocols.length === 0)
|
|
224
|
+
return undefined;
|
|
225
|
+
}
|
|
226
|
+
const paths = providerPaths(record.paths);
|
|
227
|
+
if (paths === undefined)
|
|
228
|
+
return undefined;
|
|
229
|
+
const providers = record.providers.map(providerOption).filter((entry) => entry !== undefined);
|
|
230
|
+
const syncedAt = record.syncedAt;
|
|
231
|
+
const note = nonEmpty(record.note);
|
|
232
|
+
return {
|
|
233
|
+
providers,
|
|
234
|
+
native: nativeConfig(record.native),
|
|
235
|
+
protocols,
|
|
236
|
+
paths,
|
|
237
|
+
...(typeof syncedAt === 'number' && Number.isFinite(syncedAt) ? { syncedAt } : {}),
|
|
238
|
+
...(note === undefined ? {} : { note }),
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* The user's own native config as the adapter last read it. `null` when the
|
|
243
|
+
* adapter said nothing; otherwise an object carrying whichever halves were
|
|
244
|
+
* named — codex names a provider id, claude an endpoint URL — and an empty
|
|
245
|
+
* one is still news (the panel says the file names nothing).
|
|
246
|
+
*/
|
|
247
|
+
function nativeConfig(raw) {
|
|
248
|
+
if (typeof raw !== 'object' || raw === null)
|
|
249
|
+
return null;
|
|
250
|
+
const record = raw;
|
|
251
|
+
const providerId = nonEmpty(record.providerId);
|
|
252
|
+
const baseUrl = nonEmpty(record.baseUrl);
|
|
253
|
+
const model = nonEmpty(record.model);
|
|
254
|
+
return {
|
|
255
|
+
...(providerId === undefined ? {} : { providerId }),
|
|
256
|
+
...(baseUrl === undefined ? {} : { baseUrl }),
|
|
257
|
+
...(model === undefined ? {} : { model }),
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
/** The codex face: a stored key, or the name of a variable holding it. */
|
|
261
|
+
const KEY_OR_ENV = [
|
|
262
|
+
{ value: 'apiKey', label: 'API key', hint: 'Stored with the provider; injected at launch.' },
|
|
263
|
+
{ value: 'env', label: 'Environment variable', hint: 'The child reads the key from a named variable.' },
|
|
264
|
+
];
|
|
265
|
+
/** The claude face: both kinds are literal keys, differing only in the header. */
|
|
266
|
+
const TWO_HEADER_SHAPES = [
|
|
267
|
+
{ value: 'apiKey', label: 'API key (x-api-key)', hint: 'Sent as the x-api-key header.' },
|
|
268
|
+
{ value: 'authToken', label: 'Bearer token', hint: 'Sent as the Authorization header.' },
|
|
269
|
+
];
|
|
270
|
+
/**
|
|
271
|
+
* The auth shapes the add/edit form may pick for a provider, in display order.
|
|
272
|
+
*
|
|
273
|
+
* The view carries no explicit vocabulary field, so the panel reads it off the
|
|
274
|
+
* one field that distinguishes the faces today: `protocols`. The codex face
|
|
275
|
+
* advertises wire protocols and offers key-or-env-var; the claude face carries
|
|
276
|
+
* neither and offers its two header shapes instead. The list is per-view,
|
|
277
|
+
* never a fixed union — each adapter rejects the other's kinds on save, so a
|
|
278
|
+
* union dropdown would miscode silently. A third face extends the table.
|
|
279
|
+
*/
|
|
280
|
+
export function authKindOptions(view) {
|
|
281
|
+
return view.protocols.length > 0 ? KEY_OR_ENV : TWO_HEADER_SHAPES;
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* `provider / model` (codex) or `endpoint / model` (claude) — whichever halves
|
|
285
|
+
* the user's own native config names, joined for the one-line fact the panel
|
|
286
|
+
* states and never acts on.
|
|
287
|
+
*/
|
|
288
|
+
export function nativeDescription(native) {
|
|
289
|
+
const parts = [native?.providerId ?? native?.baseUrl, native?.model].filter((part) => part !== undefined);
|
|
290
|
+
return parts.length === 0 ? 'no provider' : parts.join(' / ');
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* The non-store paths joined for the panel's second footer line, in a fixed
|
|
294
|
+
* slot order. `undefined` when the face named none — the line is omitted
|
|
295
|
+
* entirely rather than rendered with blank slots.
|
|
296
|
+
*/
|
|
297
|
+
export function nativePathsLine(paths) {
|
|
298
|
+
const parts = [paths.config, paths.auth, paths.catalogs, paths.settings].filter((part) => part !== undefined);
|
|
299
|
+
return parts.length === 0 ? undefined : parts.join(' · ');
|
|
300
|
+
}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Machine-mesh data access for the Hub machines panel (mesh P1, Task 5):
|
|
3
|
+
* the client half of `/api/superd/machines` (host side: mesh-rpc.ts) —
|
|
4
|
+
* same-origin fetch behind the standard browser-trust + cookie fence, the
|
|
5
|
+
* roster face's house style (`credentials: 'same-origin'`). The localStorage
|
|
6
|
+
* ledger era (machines-store.ts, retired with this module) ended: the hub's
|
|
7
|
+
* registry is the single source of truth, this file only fetches it and
|
|
8
|
+
* coerces the wire answer into row views.
|
|
9
|
+
*
|
|
10
|
+
* Node-safe by construction — no DOM, no react, no @deepseek-ai imports —
|
|
11
|
+
* so the host tsc emits it to `dist/client/mesh-api.js` and
|
|
12
|
+
* test/client-mesh-view.test.mjs runs the pure halves (coercion, token-URL
|
|
13
|
+
* pre-check) under `node --test` without jsdom: the switch-core/toast-core
|
|
14
|
+
* precedent in reverse (pure logic kept beside the fetch wrappers, all under
|
|
15
|
+
* src/client). No fetch happens at import time.
|
|
16
|
+
*
|
|
17
|
+
* @module superd-hub/client/mesh-api
|
|
18
|
+
*/
|
|
19
|
+
/** Machine health as the hub reports it (mesh-rpc joins `'pending'` when the mesh-link status table has no entry for the row). */
|
|
20
|
+
export type MeshMachineStatus = 'pending' | 'reachable' | 'unreachable' | 'auth-expired';
|
|
21
|
+
/**
|
|
22
|
+
* One machine row for the panel: the wire's public projection (never token,
|
|
23
|
+
* never cookie) with `self` DERIVED from the response's self block — never
|
|
24
|
+
* stored per-row (the server's machines array carries remotes only; the
|
|
25
|
+
* flag exists so a self row surfacing in it still renders as self). Since
|
|
26
|
+
* I2 (mesh P3 5a) a reachable row may also carry `versionWarning` — the
|
|
27
|
+
* host's version-mismatch computation, coerced additively (absent stays
|
|
28
|
+
* absent; a non-string is dropped).
|
|
29
|
+
*/
|
|
30
|
+
export interface MeshMachineView {
|
|
31
|
+
readonly id: string;
|
|
32
|
+
readonly name: string;
|
|
33
|
+
readonly label: string;
|
|
34
|
+
readonly origin: string;
|
|
35
|
+
readonly status: MeshMachineStatus;
|
|
36
|
+
readonly lastProbeAt?: number;
|
|
37
|
+
/** I2: present exactly when the hub's probe found the peer's superd
|
|
38
|
+
* version incompatible with ours (diff major/minor, or unparsable). */
|
|
39
|
+
readonly versionWarning?: string;
|
|
40
|
+
readonly self: boolean;
|
|
41
|
+
}
|
|
42
|
+
/** The response's self block: this machine's minted m4 id + label. */
|
|
43
|
+
export interface MeshSelfView {
|
|
44
|
+
readonly id: string;
|
|
45
|
+
readonly label: string;
|
|
46
|
+
}
|
|
47
|
+
/** The coerced GET answer: self (when the wire carried a usable one) + the remote rows. */
|
|
48
|
+
export interface MeshList {
|
|
49
|
+
readonly self?: MeshSelfView;
|
|
50
|
+
readonly machines: readonly MeshMachineView[];
|
|
51
|
+
}
|
|
52
|
+
/** The pairing input — origin is parsed out of the token URL SERVER-side (mesh-link), never a separate field. */
|
|
53
|
+
export interface AddMachineInput {
|
|
54
|
+
readonly name: string;
|
|
55
|
+
readonly label?: string;
|
|
56
|
+
readonly tokenUrl: string;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Coerce a raw GET body into the panel's view list. Any bad shape degrades
|
|
60
|
+
* to the empty list (rows dropped individually, self dropped as a block) —
|
|
61
|
+
* the UI never crashes on a malformed answer.
|
|
62
|
+
*
|
|
63
|
+
* @param raw - the parsed JSON body (or anything else the wire produced).
|
|
64
|
+
* @returns self (when usable) + the coerced remote rows.
|
|
65
|
+
*/
|
|
66
|
+
export declare function coerceMachineList(raw: unknown): MeshList;
|
|
67
|
+
/**
|
|
68
|
+
* The version-warning chip for one machine row (I2): present EXACTLY when the
|
|
69
|
+
* coerced row carries a `versionWarning` — the Hub machines panel renders
|
|
70
|
+
* the chip iff this returns a value. Kept here beside the coercion so the
|
|
71
|
+
* presence contract stays unit-testable without jsdom (the HubPage seat is
|
|
72
|
+
* a one-line consumer following the existing chip patterns).
|
|
73
|
+
*/
|
|
74
|
+
export declare function machineVersionChip(machine: Pick<MeshMachineView, 'versionWarning'>): {
|
|
75
|
+
text: string;
|
|
76
|
+
} | undefined;
|
|
77
|
+
/**
|
|
78
|
+
* GET the machine list (self + remotes).
|
|
79
|
+
*
|
|
80
|
+
* @returns the coerced list — a malformed body degrades to the empty list, never a throw.
|
|
81
|
+
* @throws when the request fails or the face answers non-2xx.
|
|
82
|
+
*/
|
|
83
|
+
export declare function fetchMachines(): Promise<MeshList>;
|
|
84
|
+
/**
|
|
85
|
+
* POST a pairing `{name, label?, tokenUrl}` (label omitted → the server
|
|
86
|
+
* collapses it to the name).
|
|
87
|
+
*
|
|
88
|
+
* @throws Error(body.error) when the face answers non-2xx — the message is
|
|
89
|
+
* mesh-link's own, already token-redacted.
|
|
90
|
+
*/
|
|
91
|
+
export declare function addMachine(input: AddMachineInput): Promise<void>;
|
|
92
|
+
/**
|
|
93
|
+
* DELETE one remote row by id (`{removed: boolean}` answers even for
|
|
94
|
+
* unknown ids — a false removal is a reload, not an error).
|
|
95
|
+
*
|
|
96
|
+
* @throws Error(body.error) on a non-2xx answer.
|
|
97
|
+
*/
|
|
98
|
+
export declare function removeMachine(id: string): Promise<void>;
|
|
99
|
+
/**
|
|
100
|
+
* POST `{op: 'reprobe', id}` — a fresh probe of one remote; the outcome
|
|
101
|
+
* (even unreachable/auth-expired) lands in the status table server-side, so
|
|
102
|
+
* the caller reloads rather than interpreting the probe body.
|
|
103
|
+
*
|
|
104
|
+
* @throws Error(body.error) on a non-2xx answer (the op itself failed).
|
|
105
|
+
*/
|
|
106
|
+
export declare function reprobeMachine(id: string): Promise<void>;
|
|
107
|
+
/**
|
|
108
|
+
* The standing cadence note (D18): machine status refresh rides the hub's
|
|
109
|
+
* 5-minute sweep — the panel renders this verbatim beside its live-fetch
|
|
110
|
+
* control. English copy to match the DSH UI.
|
|
111
|
+
*/
|
|
112
|
+
export declare const AUTO_REFRESH_NOTE = "auto-refreshes every 5 min";
|
|
113
|
+
/**
|
|
114
|
+
* The per-machine last-fetch line, shaped from the row's `lastProbeAt`
|
|
115
|
+
* (the hub's status table stamp — mesh-rpc joins it on every GET).
|
|
116
|
+
* Pure by design (the UI passes its own `now`): headless-testable without
|
|
117
|
+
* jsdom, same presence contract as {@link machineVersionChip} — absent or
|
|
118
|
+
* non-finite stamp means "never probed" and the panel omits the line.
|
|
119
|
+
*
|
|
120
|
+
* @param lastProbeAt - epoch ms of the machine's last recorded probe.
|
|
121
|
+
* @param now - epoch ms the panel renders at.
|
|
122
|
+
* @returns "Updated 2m ago" / "Updated just now" / … , or undefined.
|
|
123
|
+
*/
|
|
124
|
+
export declare function lastFetchLabel(lastProbeAt: number | undefined, now: number): string | undefined;
|
|
125
|
+
/**
|
|
126
|
+
* Client-side pre-check before a pairing submit: a parseable absolute
|
|
127
|
+
* http(s) URL carrying a non-empty `token` query param. The server re-parses
|
|
128
|
+
* authoritatively (mesh-link's parseTokenUrl); this only keeps obviously
|
|
129
|
+
* dead drafts off the wire.
|
|
130
|
+
*
|
|
131
|
+
* @param input - the raw token-URL draft (trim before calling).
|
|
132
|
+
* @returns whether the draft is submit-shaped.
|
|
133
|
+
*/
|
|
134
|
+
export declare function validTokenUrl(input: string): boolean;
|