@sentropic/h2a-runtime 0.81.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/account-pool.d.ts +168 -0
- package/dist/account-pool.d.ts.map +1 -0
- package/dist/account-pool.js +385 -0
- package/dist/account-pool.js.map +1 -0
- package/dist/agents-projection.d.ts +56 -0
- package/dist/agents-projection.d.ts.map +1 -0
- package/dist/agents-projection.js +82 -0
- package/dist/agents-projection.js.map +1 -0
- package/dist/attach.d.ts +75 -0
- package/dist/attach.d.ts.map +1 -0
- package/dist/attach.js +432 -0
- package/dist/attach.js.map +1 -0
- package/dist/auth-bundle.d.ts +21 -0
- package/dist/auth-bundle.d.ts.map +1 -0
- package/dist/auth-bundle.js +112 -0
- package/dist/auth-bundle.js.map +1 -0
- package/dist/auth-diagnostics.d.ts +18 -0
- package/dist/auth-diagnostics.d.ts.map +1 -0
- package/dist/auth-diagnostics.js +23 -0
- package/dist/auth-diagnostics.js.map +1 -0
- package/dist/auth-login.d.ts +9 -0
- package/dist/auth-login.d.ts.map +1 -0
- package/dist/auth-login.js +21 -0
- package/dist/auth-login.js.map +1 -0
- package/dist/auth-refresh.d.ts +32 -0
- package/dist/auth-refresh.d.ts.map +1 -0
- package/dist/auth-refresh.js +83 -0
- package/dist/auth-refresh.js.map +1 -0
- package/dist/auth-tools.d.ts +55 -0
- package/dist/auth-tools.d.ts.map +1 -0
- package/dist/auth-tools.js +163 -0
- package/dist/auth-tools.js.map +1 -0
- package/dist/browser-bridge-local.d.ts +27 -0
- package/dist/browser-bridge-local.d.ts.map +1 -0
- package/dist/browser-bridge-local.js +51 -0
- package/dist/browser-bridge-local.js.map +1 -0
- package/dist/browser.d.ts +53 -0
- package/dist/browser.d.ts.map +1 -0
- package/dist/browser.js +61 -0
- package/dist/browser.js.map +1 -0
- package/dist/conductor-launch.d.ts +179 -0
- package/dist/conductor-launch.d.ts.map +1 -0
- package/dist/conductor-launch.js +414 -0
- package/dist/conductor-launch.js.map +1 -0
- package/dist/config.d.ts +182 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +334 -0
- package/dist/config.js.map +1 -0
- package/dist/conv-guard.d.ts +76 -0
- package/dist/conv-guard.d.ts.map +1 -0
- package/dist/conv-guard.js +131 -0
- package/dist/conv-guard.js.map +1 -0
- package/dist/convsync.d.ts +24 -0
- package/dist/convsync.d.ts.map +1 -0
- package/dist/convsync.js +108 -0
- package/dist/convsync.js.map +1 -0
- package/dist/cred-health.d.ts +96 -0
- package/dist/cred-health.d.ts.map +1 -0
- package/dist/cred-health.js +170 -0
- package/dist/cred-health.js.map +1 -0
- package/dist/delegate.d.ts +406 -0
- package/dist/delegate.d.ts.map +1 -0
- package/dist/delegate.js +739 -0
- package/dist/delegate.js.map +1 -0
- package/dist/enroll.d.ts +91 -0
- package/dist/enroll.d.ts.map +1 -0
- package/dist/enroll.js +248 -0
- package/dist/enroll.js.map +1 -0
- package/dist/fanout.d.ts +63 -0
- package/dist/fanout.d.ts.map +1 -0
- package/dist/fanout.js +115 -0
- package/dist/fanout.js.map +1 -0
- package/dist/forward.d.ts +66 -0
- package/dist/forward.d.ts.map +1 -0
- package/dist/forward.js +141 -0
- package/dist/forward.js.map +1 -0
- package/dist/gitdiff.d.ts +31 -0
- package/dist/gitdiff.d.ts.map +1 -0
- package/dist/gitdiff.js +133 -0
- package/dist/gitdiff.js.map +1 -0
- package/dist/h2a-bridge.d.ts +102 -0
- package/dist/h2a-bridge.d.ts.map +1 -0
- package/dist/h2a-bridge.js +333 -0
- package/dist/h2a-bridge.js.map +1 -0
- package/dist/h2a-jobs.d.ts +235 -0
- package/dist/h2a-jobs.d.ts.map +1 -0
- package/dist/h2a-jobs.js +388 -0
- package/dist/h2a-jobs.js.map +1 -0
- package/dist/h2a-ping.d.ts +38 -0
- package/dist/h2a-ping.d.ts.map +1 -0
- package/dist/h2a-ping.js +58 -0
- package/dist/h2a-ping.js.map +1 -0
- package/dist/index.d.ts +98 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5887 -0
- package/dist/index.js.map +1 -0
- package/dist/interactive-throttle.d.ts +164 -0
- package/dist/interactive-throttle.d.ts.map +1 -0
- package/dist/interactive-throttle.js +194 -0
- package/dist/interactive-throttle.js.map +1 -0
- package/dist/lineage-client.d.ts +77 -0
- package/dist/lineage-client.d.ts.map +1 -0
- package/dist/lineage-client.js +117 -0
- package/dist/lineage-client.js.map +1 -0
- package/dist/lineage-lease.d.ts +152 -0
- package/dist/lineage-lease.d.ts.map +1 -0
- package/dist/lineage-lease.js +303 -0
- package/dist/lineage-lease.js.map +1 -0
- package/dist/llm-gateway-runtime/accounts.d.ts +48 -0
- package/dist/llm-gateway-runtime/accounts.d.ts.map +1 -0
- package/dist/llm-gateway-runtime/accounts.js +226 -0
- package/dist/llm-gateway-runtime/accounts.js.map +1 -0
- package/dist/llm-gateway-runtime/index.d.ts +4 -0
- package/dist/llm-gateway-runtime/index.d.ts.map +1 -0
- package/dist/llm-gateway-runtime/index.js +58 -0
- package/dist/llm-gateway-runtime/index.js.map +1 -0
- package/dist/llm-gateway-runtime/model-catalog.d.ts +36 -0
- package/dist/llm-gateway-runtime/model-catalog.d.ts.map +1 -0
- package/dist/llm-gateway-runtime/model-catalog.js +146 -0
- package/dist/llm-gateway-runtime/model-catalog.js.map +1 -0
- package/dist/llm-gateway-runtime/proxy-anthropic.d.ts +3 -0
- package/dist/llm-gateway-runtime/proxy-anthropic.d.ts.map +1 -0
- package/dist/llm-gateway-runtime/proxy-anthropic.js +139 -0
- package/dist/llm-gateway-runtime/proxy-anthropic.js.map +1 -0
- package/dist/llm-gateway-runtime/proxy-openai.d.ts +117 -0
- package/dist/llm-gateway-runtime/proxy-openai.d.ts.map +1 -0
- package/dist/llm-gateway-runtime/proxy-openai.js +1132 -0
- package/dist/llm-gateway-runtime/proxy-openai.js.map +1 -0
- package/dist/llm-gateway-runtime/session-ledger.d.ts +32 -0
- package/dist/llm-gateway-runtime/session-ledger.d.ts.map +1 -0
- package/dist/llm-gateway-runtime/session-ledger.js +62 -0
- package/dist/llm-gateway-runtime/session-ledger.js.map +1 -0
- package/dist/llm-gateway-runtime/sticky.d.ts +40 -0
- package/dist/llm-gateway-runtime/sticky.d.ts.map +1 -0
- package/dist/llm-gateway-runtime/sticky.js +254 -0
- package/dist/llm-gateway-runtime/sticky.js.map +1 -0
- package/dist/llm-mesh.d.ts +102 -0
- package/dist/llm-mesh.d.ts.map +1 -0
- package/dist/llm-mesh.js +433 -0
- package/dist/llm-mesh.js.map +1 -0
- package/dist/migrate-candidates.d.ts +35 -0
- package/dist/migrate-candidates.d.ts.map +1 -0
- package/dist/migrate-candidates.js +128 -0
- package/dist/migrate-candidates.js.map +1 -0
- package/dist/migrate.d.ts +125 -0
- package/dist/migrate.d.ts.map +1 -0
- package/dist/migrate.js +561 -0
- package/dist/migrate.js.map +1 -0
- package/dist/plugin-manifest.d.ts +143 -0
- package/dist/plugin-manifest.d.ts.map +1 -0
- package/dist/plugin-manifest.js +233 -0
- package/dist/plugin-manifest.js.map +1 -0
- package/dist/plugin.d.ts +274 -0
- package/dist/plugin.d.ts.map +1 -0
- package/dist/plugin.js +919 -0
- package/dist/plugin.js.map +1 -0
- package/dist/pod-liveness.d.ts +51 -0
- package/dist/pod-liveness.d.ts.map +1 -0
- package/dist/pod-liveness.js +80 -0
- package/dist/pod-liveness.js.map +1 -0
- package/dist/profile-menu.d.ts +7 -0
- package/dist/profile-menu.d.ts.map +1 -0
- package/dist/profile-menu.js +52 -0
- package/dist/profile-menu.js.map +1 -0
- package/dist/profiles.d.ts +23 -0
- package/dist/profiles.d.ts.map +1 -0
- package/dist/profiles.js +66 -0
- package/dist/profiles.js.map +1 -0
- package/dist/protocol-local.d.ts +27 -0
- package/dist/protocol-local.d.ts.map +1 -0
- package/dist/protocol-local.js +12 -0
- package/dist/protocol-local.js.map +1 -0
- package/dist/pty.d.ts +26 -0
- package/dist/pty.d.ts.map +1 -0
- package/dist/pty.js +45 -0
- package/dist/pty.js.map +1 -0
- package/dist/readiness.d.ts +38 -0
- package/dist/readiness.d.ts.map +1 -0
- package/dist/readiness.js +169 -0
- package/dist/readiness.js.map +1 -0
- package/dist/registry.d.ts +258 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +570 -0
- package/dist/registry.js.map +1 -0
- package/dist/relaunch.d.ts +52 -0
- package/dist/relaunch.d.ts.map +1 -0
- package/dist/relaunch.js +76 -0
- package/dist/relaunch.js.map +1 -0
- package/dist/restore.d.ts +165 -0
- package/dist/restore.d.ts.map +1 -0
- package/dist/restore.js +530 -0
- package/dist/restore.js.map +1 -0
- package/dist/run.d.ts +29 -0
- package/dist/run.d.ts.map +1 -0
- package/dist/run.js +206 -0
- package/dist/run.js.map +1 -0
- package/dist/secrets.d.ts +25 -0
- package/dist/secrets.d.ts.map +1 -0
- package/dist/secrets.js +98 -0
- package/dist/secrets.js.map +1 -0
- package/dist/session-restore.d.ts +26 -0
- package/dist/session-restore.d.ts.map +1 -0
- package/dist/session-restore.js +122 -0
- package/dist/session-restore.js.map +1 -0
- package/dist/skills-sync.d.ts +126 -0
- package/dist/skills-sync.d.ts.map +1 -0
- package/dist/skills-sync.js +255 -0
- package/dist/skills-sync.js.map +1 -0
- package/dist/smoke.d.ts +23 -0
- package/dist/smoke.d.ts.map +1 -0
- package/dist/smoke.js +138 -0
- package/dist/smoke.js.map +1 -0
- package/dist/soft-refresh.d.ts +107 -0
- package/dist/soft-refresh.d.ts.map +1 -0
- package/dist/soft-refresh.js +426 -0
- package/dist/soft-refresh.js.map +1 -0
- package/dist/sync-status.d.ts +22 -0
- package/dist/sync-status.d.ts.map +1 -0
- package/dist/sync-status.js +41 -0
- package/dist/sync-status.js.map +1 -0
- package/dist/sync.d.ts +77 -0
- package/dist/sync.d.ts.map +1 -0
- package/dist/sync.js +302 -0
- package/dist/sync.js.map +1 -0
- package/dist/throttle-signatures.d.ts +43 -0
- package/dist/throttle-signatures.d.ts.map +1 -0
- package/dist/throttle-signatures.js +116 -0
- package/dist/throttle-signatures.js.map +1 -0
- package/dist/tmux.d.ts +286 -0
- package/dist/tmux.d.ts.map +1 -0
- package/dist/tmux.js +970 -0
- package/dist/tmux.js.map +1 -0
- package/dist/tunnel.d.ts +25 -0
- package/dist/tunnel.d.ts.map +1 -0
- package/dist/tunnel.js +127 -0
- package/dist/tunnel.js.map +1 -0
- package/dist/workspace-merge.d.ts +17 -0
- package/dist/workspace-merge.d.ts.map +1 -0
- package/dist/workspace-merge.js +153 -0
- package/dist/workspace-merge.js.map +1 -0
- package/dist/workspace-sync-incremental.d.ts +64 -0
- package/dist/workspace-sync-incremental.d.ts.map +1 -0
- package/dist/workspace-sync-incremental.js +199 -0
- package/dist/workspace-sync-incremental.js.map +1 -0
- package/dist/workspace-sync.d.ts +3 -0
- package/dist/workspace-sync.d.ts.map +1 -0
- package/dist/workspace-sync.js +187 -0
- package/dist/workspace-sync.js.map +1 -0
- package/dist/workspace.d.ts +70 -0
- package/dist/workspace.d.ts.map +1 -0
- package/dist/workspace.js +173 -0
- package/dist/workspace.js.map +1 -0
- package/package.json +44 -0
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plugin/MCP DESIRED-STATE manifest + drift diff (remote supervise slice 3) —
|
|
3
|
+
* ADDITIVE, PURE. No IO, no clock. The watch loop / `plugin sync --check` call
|
|
4
|
+
* these to RENDER the desired set, HASH it (to gate a sidecar push exactly like
|
|
5
|
+
* CREDS_HASH_FILE), and DIFF it against what a Pod actually has installed.
|
|
6
|
+
*
|
|
7
|
+
* Why a manifest at all: MCP servers + plugin CLIs (track, h2a, harness,
|
|
8
|
+
* graphify, skills) baked/installed into Pods drift away from the LOCAL
|
|
9
|
+
* source-of-truth — a Pod restart loses globally-installed plugins, a re-pin
|
|
10
|
+
* bumps the local version, and nothing today DETECTS the gap (`plugin ls`
|
|
11
|
+
* prints `REMOTE ?`). The manifest is the canonical "what every Pod SHOULD have"
|
|
12
|
+
* record; its sha256 is the cheap drift signal (one `kubectl exec cat`), and the
|
|
13
|
+
* diff is the per-item report.
|
|
14
|
+
*
|
|
15
|
+
* BIGGEST REMAINING DRIFT SOURCE (DEFERRED — converge-on-session-start): a
|
|
16
|
+
* freshly (re)created Pod boots with NO plugins until the next `plugin sync` /
|
|
17
|
+
* watch pass reconciles it, so its CLI starts WITHOUT track/h2a/etc. Closing
|
|
18
|
+
* that needs the session-agent / orchestrator to run the sync BEFORE the CLI
|
|
19
|
+
* launches — a session-agent change that is OUT OF SCOPE this slice. It hooks in
|
|
20
|
+
* at the session-agent startup path (packages/session-agent, the pane that
|
|
21
|
+
* launches `<cli>`): before exec'ing the CLI, run the equivalent of
|
|
22
|
+
* `buildPodSyncScript` for each desired plugin + write this manifest sidecar.
|
|
23
|
+
* TODO(slice 4): wire converge-on-session-start there; until then the watch-pass
|
|
24
|
+
* reconcile (compareManifestHash → re-run buildPodSyncScript) is the safety net.
|
|
25
|
+
*
|
|
26
|
+
* NOTHING here is a NEW push path: `plugin sync` (no --check) still converges via
|
|
27
|
+
* the EXISTING buildPodSyncScript, untouched. This module only adds a manifest
|
|
28
|
+
* artifact + a read-only drift report.
|
|
29
|
+
*/
|
|
30
|
+
import type { PluginEntry } from "./config.js";
|
|
31
|
+
/** One plugin package the Pods should carry (pkg + pinned version). */
|
|
32
|
+
export type ManifestPlugin = {
|
|
33
|
+
readonly pkg: string;
|
|
34
|
+
readonly version: string;
|
|
35
|
+
};
|
|
36
|
+
/** One MCP server the Pods should have registered (name + bin/script ref). */
|
|
37
|
+
export type ManifestMcp = {
|
|
38
|
+
readonly name: string;
|
|
39
|
+
readonly bin: string;
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* The desired-state manifest: the canonical set of plugins + MCP servers (+
|
|
43
|
+
* optional tracked skills) every live Pod should converge to. Field order /
|
|
44
|
+
* array order are NORMALIZED by `renderManifest` so the JSON — and therefore the
|
|
45
|
+
* hash — is independent of config key order.
|
|
46
|
+
*/
|
|
47
|
+
export type PluginManifest = {
|
|
48
|
+
readonly plugins: ReadonlyArray<ManifestPlugin>;
|
|
49
|
+
readonly mcp: ReadonlyArray<ManifestMcp>;
|
|
50
|
+
/** Tracked skills, if any are recorded as desired state (none today; reserved). */
|
|
51
|
+
readonly skills?: ReadonlyArray<string>;
|
|
52
|
+
};
|
|
53
|
+
/**
|
|
54
|
+
* Render the CANONICAL desired-state manifest from the configured plugins.
|
|
55
|
+
* Deterministic: plugins sorted by pkg, MCP servers sorted by name, skills
|
|
56
|
+
* sorted — so two configs that differ only in array/key ORDER render the SAME
|
|
57
|
+
* manifest (and hash). PURE. `skills` is emitted only when a non-empty tracked
|
|
58
|
+
* list is passed (none is tracked in config today — reserved for slice 4).
|
|
59
|
+
*/
|
|
60
|
+
export declare function renderManifest(plugins: ReadonlyArray<PluginEntry>, skills?: ReadonlyArray<string>): PluginManifest;
|
|
61
|
+
/**
|
|
62
|
+
* The CANONICAL JSON string of a manifest: keys in a FIXED order (plugins, mcp,
|
|
63
|
+
* skills) and every object's keys emitted in a fixed order, so the serialization
|
|
64
|
+
* is byte-stable regardless of how the manifest object was built. This is what
|
|
65
|
+
* gets written to `~/.remote-manifest.json` in the Pod AND what is hashed.
|
|
66
|
+
*/
|
|
67
|
+
export declare function canonicalManifestJson(manifest: PluginManifest): string;
|
|
68
|
+
/**
|
|
69
|
+
* sha256 (hex) of the canonical manifest JSON — the cheap drift signal. Same
|
|
70
|
+
* input → same hash; key-order / array-order independent (renderManifest
|
|
71
|
+
* normalizes order, canonicalManifestJson fixes serialization). Mirrors
|
|
72
|
+
* `hashAuthBundle` in soft-refresh.ts: the Pod records this hex in
|
|
73
|
+
* `~/.remote-manifest.sha256`, and a pass compares local vs Pod to gate the
|
|
74
|
+
* sidecar push + trigger a reconcile.
|
|
75
|
+
*/
|
|
76
|
+
export declare function manifestHash(manifest: PluginManifest): string;
|
|
77
|
+
/**
|
|
78
|
+
* What a Pod actually has installed, as probed by the thin pod-state reader:
|
|
79
|
+
* - `pluginVersions`: pkg -> installed version (from `npm ls -g --json`); a pkg
|
|
80
|
+
* absent from the map means it is NOT installed globally in the Pod.
|
|
81
|
+
* - `mcpRegistered`: the set of MCP server NAMES the Pod has registered (read
|
|
82
|
+
* from the claude/codex/agy config files the existing sync writes).
|
|
83
|
+
*/
|
|
84
|
+
export type PodPluginState = {
|
|
85
|
+
readonly pluginVersions: Readonly<Record<string, string>>;
|
|
86
|
+
readonly mcpRegistered: ReadonlyArray<string>;
|
|
87
|
+
};
|
|
88
|
+
/** Drift status for one desired item in one Pod. */
|
|
89
|
+
export type DriftStatus = "ok" | "version-drift" | "missing" | "mcp-unregistered";
|
|
90
|
+
/** One row of the drift report: a desired item's status in a Pod. */
|
|
91
|
+
export type DriftRow = {
|
|
92
|
+
readonly pod: string;
|
|
93
|
+
/** "plugin:<pkg>" or "mcp:<name>". */
|
|
94
|
+
readonly item: string;
|
|
95
|
+
readonly status: DriftStatus;
|
|
96
|
+
/** Human detail (e.g. "local@1.2.0 vs pod@1.1.0"); empty for ok. */
|
|
97
|
+
readonly detail: string;
|
|
98
|
+
};
|
|
99
|
+
/**
|
|
100
|
+
* DIFF the desired manifest against one Pod's actual state. PURE. One row per
|
|
101
|
+
* desired plugin + one per desired MCP server:
|
|
102
|
+
* - plugin MISSING → pkg not installed globally in the Pod.
|
|
103
|
+
* - plugin VERSION-DRIFT → installed but at a different version than desired.
|
|
104
|
+
* - plugin OK → installed at the desired version.
|
|
105
|
+
* - mcp MCP-UNREGISTERED → desired MCP name not in the Pod's registered set.
|
|
106
|
+
* - mcp OK → registered.
|
|
107
|
+
* Rows preserve the manifest's (already-canonical) order, plugins before mcp.
|
|
108
|
+
*/
|
|
109
|
+
export declare function diffManifest(manifest: PluginManifest, pod: string, state: PodPluginState): DriftRow[];
|
|
110
|
+
/** A row is drift when its status is anything other than `ok`. */
|
|
111
|
+
export declare function isDrift(row: DriftRow): boolean;
|
|
112
|
+
/**
|
|
113
|
+
* The process exit code for a `--check` run: 1 when ANY row across all Pods is
|
|
114
|
+
* drift, else 0. PURE — the command stays a thin printer around this. An empty
|
|
115
|
+
* report (no Pods / no desired items) is NOT drift → exit 0.
|
|
116
|
+
*/
|
|
117
|
+
export declare function checkExitCode(rows: ReadonlyArray<DriftRow>): number;
|
|
118
|
+
/**
|
|
119
|
+
* Parse `npm ls -g --json` output into pkg -> installed version, keeping ONLY
|
|
120
|
+
* the packages we care about (`wanted`). npm's JSON is
|
|
121
|
+
* `{ dependencies: { "<pkg>": { version, ... }, ... } }`; a missing/!
|
|
122
|
+
* unparseable blob yields an empty map (treated downstream as "nothing
|
|
123
|
+
* installed" → all desired plugins report missing, which is the safe drift
|
|
124
|
+
* signal). PURE, exported for tests. A pkg present but without a string version
|
|
125
|
+
* is skipped (can't assert a version → reported missing, never a false "ok").
|
|
126
|
+
*/
|
|
127
|
+
export declare function parseNpmLsVersions(json: string, wanted: ReadonlyArray<string>): Record<string, string>;
|
|
128
|
+
/**
|
|
129
|
+
* Parse the set of registered MCP server NAMES from the Pod's MCP config blobs.
|
|
130
|
+
* The existing sync writes either a Claude-style JSON (`{"mcpServers": {...}}` —
|
|
131
|
+
* ~/.claude.json and agy's mcp_config.json) or codex TOML
|
|
132
|
+
* (`[mcp_servers.<name>]` sections). We read whatever blobs are present and
|
|
133
|
+
* union their server names. PURE, exported for tests. A blob that is empty /
|
|
134
|
+
* unparseable contributes nothing (never throws — an unreadable config is just
|
|
135
|
+
* "no servers from there", surfacing as mcp-unregistered, the safe drift call).
|
|
136
|
+
*/
|
|
137
|
+
export declare function parseRegisteredMcpServers(blobs: {
|
|
138
|
+
/** ~/.claude.json and/or agy mcp_config.json bodies (Claude-style JSON). */
|
|
139
|
+
readonly json?: ReadonlyArray<string>;
|
|
140
|
+
/** ~/.codex/config.toml body. */
|
|
141
|
+
readonly toml?: string;
|
|
142
|
+
}): string[];
|
|
143
|
+
//# sourceMappingURL=plugin-manifest.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin-manifest.d.ts","sourceRoot":"","sources":["../src/plugin-manifest.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAIH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAM/C,uEAAuE;AACvE,MAAM,MAAM,cAAc,GAAG;IAAE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAEhF,8EAA8E;AAC9E,MAAM,MAAM,WAAW,GAAG;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC;AAE1E;;;;;GAKG;AACH,MAAM,MAAM,cAAc,GAAG;IAC3B,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC,cAAc,CAAC,CAAC;IAChD,QAAQ,CAAC,GAAG,EAAE,aAAa,CAAC,WAAW,CAAC,CAAC;IACzC,mFAAmF;IACnF,QAAQ,CAAC,MAAM,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;CACzC,CAAC;AAaF;;;;;;GAMG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,aAAa,CAAC,WAAW,CAAC,EACnC,MAAM,GAAE,aAAa,CAAC,MAAM,CAAM,GACjC,cAAc,CAmBhB;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,cAAc,GAAG,MAAM,CAStE;AAED;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,cAAc,GAAG,MAAM,CAE7D;AAMD;;;;;;GAMG;AACH,MAAM,MAAM,cAAc,GAAG;IAC3B,QAAQ,CAAC,cAAc,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAC1D,QAAQ,CAAC,aAAa,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;CAC/C,CAAC;AAEF,oDAAoD;AACpD,MAAM,MAAM,WAAW,GACnB,IAAI,GACJ,eAAe,GACf,SAAS,GACT,kBAAkB,CAAC;AAEvB,qEAAqE;AACrE,MAAM,MAAM,QAAQ,GAAG;IACrB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,sCAAsC;IACtC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;IAC7B,oEAAoE;IACpE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF;;;;;;;;;GASG;AACH,wBAAgB,YAAY,CAC1B,QAAQ,EAAE,cAAc,EACxB,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,cAAc,GACpB,QAAQ,EAAE,CAoCZ;AAED,kEAAkE;AAClE,wBAAgB,OAAO,CAAC,GAAG,EAAE,QAAQ,GAAG,OAAO,CAE9C;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,aAAa,CAAC,QAAQ,CAAC,GAAG,MAAM,CAEnE;AAMD;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,aAAa,CAAC,MAAM,CAAC,GAC5B,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAoBxB;AAED;;;;;;;;GAQG;AACH,wBAAgB,yBAAyB,CAAC,KAAK,EAAE;IAC/C,4EAA4E;IAC5E,QAAQ,CAAC,IAAI,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IACtC,iCAAiC;IACjC,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;CACxB,GAAG,MAAM,EAAE,CA2BX"}
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plugin/MCP DESIRED-STATE manifest + drift diff (remote supervise slice 3) —
|
|
3
|
+
* ADDITIVE, PURE. No IO, no clock. The watch loop / `plugin sync --check` call
|
|
4
|
+
* these to RENDER the desired set, HASH it (to gate a sidecar push exactly like
|
|
5
|
+
* CREDS_HASH_FILE), and DIFF it against what a Pod actually has installed.
|
|
6
|
+
*
|
|
7
|
+
* Why a manifest at all: MCP servers + plugin CLIs (track, h2a, harness,
|
|
8
|
+
* graphify, skills) baked/installed into Pods drift away from the LOCAL
|
|
9
|
+
* source-of-truth — a Pod restart loses globally-installed plugins, a re-pin
|
|
10
|
+
* bumps the local version, and nothing today DETECTS the gap (`plugin ls`
|
|
11
|
+
* prints `REMOTE ?`). The manifest is the canonical "what every Pod SHOULD have"
|
|
12
|
+
* record; its sha256 is the cheap drift signal (one `kubectl exec cat`), and the
|
|
13
|
+
* diff is the per-item report.
|
|
14
|
+
*
|
|
15
|
+
* BIGGEST REMAINING DRIFT SOURCE (DEFERRED — converge-on-session-start): a
|
|
16
|
+
* freshly (re)created Pod boots with NO plugins until the next `plugin sync` /
|
|
17
|
+
* watch pass reconciles it, so its CLI starts WITHOUT track/h2a/etc. Closing
|
|
18
|
+
* that needs the session-agent / orchestrator to run the sync BEFORE the CLI
|
|
19
|
+
* launches — a session-agent change that is OUT OF SCOPE this slice. It hooks in
|
|
20
|
+
* at the session-agent startup path (packages/session-agent, the pane that
|
|
21
|
+
* launches `<cli>`): before exec'ing the CLI, run the equivalent of
|
|
22
|
+
* `buildPodSyncScript` for each desired plugin + write this manifest sidecar.
|
|
23
|
+
* TODO(slice 4): wire converge-on-session-start there; until then the watch-pass
|
|
24
|
+
* reconcile (compareManifestHash → re-run buildPodSyncScript) is the safety net.
|
|
25
|
+
*
|
|
26
|
+
* NOTHING here is a NEW push path: `plugin sync` (no --check) still converges via
|
|
27
|
+
* the EXISTING buildPodSyncScript, untouched. This module only adds a manifest
|
|
28
|
+
* artifact + a read-only drift report.
|
|
29
|
+
*/
|
|
30
|
+
import { createHash } from "node:crypto";
|
|
31
|
+
/**
|
|
32
|
+
* The MCP "bin" reference recorded in the manifest. We use the package-relative
|
|
33
|
+
* script path (`scriptRel`, e.g. "dist/mcp.js") when known — it is the stable,
|
|
34
|
+
* Pod-portable identity (the realpath differs per npm-global root). When a
|
|
35
|
+
* plugin entry has no scriptRel recorded we fall back to the server name so the
|
|
36
|
+
* manifest still names the server (a plain presence check downstream).
|
|
37
|
+
*/
|
|
38
|
+
function manifestMcpBin(mcp) {
|
|
39
|
+
return mcp.scriptRel ?? mcp.name;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Render the CANONICAL desired-state manifest from the configured plugins.
|
|
43
|
+
* Deterministic: plugins sorted by pkg, MCP servers sorted by name, skills
|
|
44
|
+
* sorted — so two configs that differ only in array/key ORDER render the SAME
|
|
45
|
+
* manifest (and hash). PURE. `skills` is emitted only when a non-empty tracked
|
|
46
|
+
* list is passed (none is tracked in config today — reserved for slice 4).
|
|
47
|
+
*/
|
|
48
|
+
export function renderManifest(plugins, skills = []) {
|
|
49
|
+
const manifestPlugins = [...plugins]
|
|
50
|
+
.map((p) => ({ pkg: p.pkg, version: p.version }))
|
|
51
|
+
.sort((a, b) => (a.pkg < b.pkg ? -1 : a.pkg > b.pkg ? 1 : 0));
|
|
52
|
+
const mcp = [];
|
|
53
|
+
for (const p of plugins) {
|
|
54
|
+
for (const m of p.mcp) {
|
|
55
|
+
mcp.push({ name: m.name, bin: manifestMcpBin(m) });
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
mcp.sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0));
|
|
59
|
+
const manifest = { plugins: manifestPlugins, mcp };
|
|
60
|
+
const uniqueSkills = [...new Set(skills)].sort();
|
|
61
|
+
if (uniqueSkills.length > 0) {
|
|
62
|
+
return { ...manifest, skills: uniqueSkills };
|
|
63
|
+
}
|
|
64
|
+
return manifest;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* The CANONICAL JSON string of a manifest: keys in a FIXED order (plugins, mcp,
|
|
68
|
+
* skills) and every object's keys emitted in a fixed order, so the serialization
|
|
69
|
+
* is byte-stable regardless of how the manifest object was built. This is what
|
|
70
|
+
* gets written to `~/.remote-manifest.json` in the Pod AND what is hashed.
|
|
71
|
+
*/
|
|
72
|
+
export function canonicalManifestJson(manifest) {
|
|
73
|
+
const canonical = {
|
|
74
|
+
plugins: manifest.plugins.map((p) => ({ pkg: p.pkg, version: p.version })),
|
|
75
|
+
mcp: manifest.mcp.map((m) => ({ name: m.name, bin: m.bin })),
|
|
76
|
+
};
|
|
77
|
+
if (manifest.skills && manifest.skills.length > 0) {
|
|
78
|
+
canonical.skills = [...manifest.skills];
|
|
79
|
+
}
|
|
80
|
+
return JSON.stringify(canonical);
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* sha256 (hex) of the canonical manifest JSON — the cheap drift signal. Same
|
|
84
|
+
* input → same hash; key-order / array-order independent (renderManifest
|
|
85
|
+
* normalizes order, canonicalManifestJson fixes serialization). Mirrors
|
|
86
|
+
* `hashAuthBundle` in soft-refresh.ts: the Pod records this hex in
|
|
87
|
+
* `~/.remote-manifest.sha256`, and a pass compares local vs Pod to gate the
|
|
88
|
+
* sidecar push + trigger a reconcile.
|
|
89
|
+
*/
|
|
90
|
+
export function manifestHash(manifest) {
|
|
91
|
+
return createHash("sha256").update(canonicalManifestJson(manifest)).digest("hex");
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* DIFF the desired manifest against one Pod's actual state. PURE. One row per
|
|
95
|
+
* desired plugin + one per desired MCP server:
|
|
96
|
+
* - plugin MISSING → pkg not installed globally in the Pod.
|
|
97
|
+
* - plugin VERSION-DRIFT → installed but at a different version than desired.
|
|
98
|
+
* - plugin OK → installed at the desired version.
|
|
99
|
+
* - mcp MCP-UNREGISTERED → desired MCP name not in the Pod's registered set.
|
|
100
|
+
* - mcp OK → registered.
|
|
101
|
+
* Rows preserve the manifest's (already-canonical) order, plugins before mcp.
|
|
102
|
+
*/
|
|
103
|
+
export function diffManifest(manifest, pod, state) {
|
|
104
|
+
const rows = [];
|
|
105
|
+
for (const p of manifest.plugins) {
|
|
106
|
+
const installed = state.pluginVersions[p.pkg];
|
|
107
|
+
if (installed === undefined) {
|
|
108
|
+
rows.push({
|
|
109
|
+
pod,
|
|
110
|
+
item: `plugin:${p.pkg}`,
|
|
111
|
+
status: "missing",
|
|
112
|
+
detail: `not installed (desired ${p.version})`,
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
else if (installed !== p.version) {
|
|
116
|
+
rows.push({
|
|
117
|
+
pod,
|
|
118
|
+
item: `plugin:${p.pkg}`,
|
|
119
|
+
status: "version-drift",
|
|
120
|
+
detail: `local@${p.version} vs pod@${installed}`,
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
rows.push({ pod, item: `plugin:${p.pkg}`, status: "ok", detail: "" });
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
const registered = new Set(state.mcpRegistered);
|
|
128
|
+
for (const m of manifest.mcp) {
|
|
129
|
+
if (registered.has(m.name)) {
|
|
130
|
+
rows.push({ pod, item: `mcp:${m.name}`, status: "ok", detail: "" });
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
rows.push({
|
|
134
|
+
pod,
|
|
135
|
+
item: `mcp:${m.name}`,
|
|
136
|
+
status: "mcp-unregistered",
|
|
137
|
+
detail: "not registered in the pod's MCP config",
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
return rows;
|
|
142
|
+
}
|
|
143
|
+
/** A row is drift when its status is anything other than `ok`. */
|
|
144
|
+
export function isDrift(row) {
|
|
145
|
+
return row.status !== "ok";
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* The process exit code for a `--check` run: 1 when ANY row across all Pods is
|
|
149
|
+
* drift, else 0. PURE — the command stays a thin printer around this. An empty
|
|
150
|
+
* report (no Pods / no desired items) is NOT drift → exit 0.
|
|
151
|
+
*/
|
|
152
|
+
export function checkExitCode(rows) {
|
|
153
|
+
return rows.some(isDrift) ? 1 : 0;
|
|
154
|
+
}
|
|
155
|
+
// ---------------------------------------------------------------------------
|
|
156
|
+
// Pod-state probe PARSERS (pure — the executors that shell out stay thin).
|
|
157
|
+
// ---------------------------------------------------------------------------
|
|
158
|
+
/**
|
|
159
|
+
* Parse `npm ls -g --json` output into pkg -> installed version, keeping ONLY
|
|
160
|
+
* the packages we care about (`wanted`). npm's JSON is
|
|
161
|
+
* `{ dependencies: { "<pkg>": { version, ... }, ... } }`; a missing/!
|
|
162
|
+
* unparseable blob yields an empty map (treated downstream as "nothing
|
|
163
|
+
* installed" → all desired plugins report missing, which is the safe drift
|
|
164
|
+
* signal). PURE, exported for tests. A pkg present but without a string version
|
|
165
|
+
* is skipped (can't assert a version → reported missing, never a false "ok").
|
|
166
|
+
*/
|
|
167
|
+
export function parseNpmLsVersions(json, wanted) {
|
|
168
|
+
const want = new Set(wanted);
|
|
169
|
+
const out = {};
|
|
170
|
+
let parsed;
|
|
171
|
+
try {
|
|
172
|
+
parsed = JSON.parse(json);
|
|
173
|
+
}
|
|
174
|
+
catch {
|
|
175
|
+
return out;
|
|
176
|
+
}
|
|
177
|
+
if (!parsed || typeof parsed !== "object")
|
|
178
|
+
return out;
|
|
179
|
+
const deps = parsed.dependencies;
|
|
180
|
+
if (!deps || typeof deps !== "object")
|
|
181
|
+
return out;
|
|
182
|
+
for (const [pkg, info] of Object.entries(deps)) {
|
|
183
|
+
if (!want.has(pkg))
|
|
184
|
+
continue;
|
|
185
|
+
if (info && typeof info === "object") {
|
|
186
|
+
const v = info.version;
|
|
187
|
+
if (typeof v === "string")
|
|
188
|
+
out[pkg] = v;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
return out;
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Parse the set of registered MCP server NAMES from the Pod's MCP config blobs.
|
|
195
|
+
* The existing sync writes either a Claude-style JSON (`{"mcpServers": {...}}` —
|
|
196
|
+
* ~/.claude.json and agy's mcp_config.json) or codex TOML
|
|
197
|
+
* (`[mcp_servers.<name>]` sections). We read whatever blobs are present and
|
|
198
|
+
* union their server names. PURE, exported for tests. A blob that is empty /
|
|
199
|
+
* unparseable contributes nothing (never throws — an unreadable config is just
|
|
200
|
+
* "no servers from there", surfacing as mcp-unregistered, the safe drift call).
|
|
201
|
+
*/
|
|
202
|
+
export function parseRegisteredMcpServers(blobs) {
|
|
203
|
+
const names = new Set();
|
|
204
|
+
for (const body of blobs.json ?? []) {
|
|
205
|
+
if (!body || !body.trim())
|
|
206
|
+
continue;
|
|
207
|
+
let parsed;
|
|
208
|
+
try {
|
|
209
|
+
parsed = JSON.parse(body);
|
|
210
|
+
}
|
|
211
|
+
catch {
|
|
212
|
+
continue;
|
|
213
|
+
}
|
|
214
|
+
if (!parsed || typeof parsed !== "object")
|
|
215
|
+
continue;
|
|
216
|
+
const servers = parsed.mcpServers;
|
|
217
|
+
if (servers && typeof servers === "object" && !Array.isArray(servers)) {
|
|
218
|
+
for (const name of Object.keys(servers)) {
|
|
219
|
+
names.add(name);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
if (blobs.toml) {
|
|
224
|
+
// `[mcp_servers.<name>]` section headers — name is SAFE_NAME ([A-Za-z0-9_-]).
|
|
225
|
+
const re = /^\s*\[mcp_servers\.([A-Za-z0-9_-]+)\]\s*$/gm;
|
|
226
|
+
let m;
|
|
227
|
+
while ((m = re.exec(blobs.toml)) !== null) {
|
|
228
|
+
names.add(m[1]);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
return [...names].sort();
|
|
232
|
+
}
|
|
233
|
+
//# sourceMappingURL=plugin-manifest.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin-manifest.js","sourceRoot":"","sources":["../src/plugin-manifest.ts"],"names":[],"mappings":"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"}
|
package/dist/plugin.d.ts
ADDED
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `remote plugin` — install npm "plugin" packages (a CLI + an MCP server, e.g.
|
|
3
|
+
* @sentropic/track shipping bins `track` and `track-mcp`) for every agent CLI
|
|
4
|
+
* (claude, codex, agy), both LOCALLY (npm i -g + MCP registration) and inside
|
|
5
|
+
* live REMOTE session Pods (`remote plugin sync`: kubectl exec → npm i -g +
|
|
6
|
+
* per-profile MCP registration).
|
|
7
|
+
*
|
|
8
|
+
* agy (Antigravity CLI) has NO `agy mcp` subcommand: MCP servers are declared
|
|
9
|
+
* in ~/.gemini/config/mcp_config.json, a Claude-style `{"mcpServers": {…}}`
|
|
10
|
+
* JSON file (the agy changelog 1.0.3 calls this the "migrated" path; the old
|
|
11
|
+
* ~/.gemini/antigravity/mcp_config.json is legacy). We merge idempotently and
|
|
12
|
+
* keep a one-shot `.bak.<epoch>` backup the first time we touch a non-empty
|
|
13
|
+
* file.
|
|
14
|
+
*
|
|
15
|
+
* KNOWN PITFALL — broken entrypoint guard through the npm-global symlink:
|
|
16
|
+
* some packages (track@0.2.0) guard their entry script with a
|
|
17
|
+
* "was-I-run-directly?" check that compares argv[1] with the module path;
|
|
18
|
+
* invoked through the npm-global bin SYMLINK the two differ and the guard
|
|
19
|
+
* never fires (the CLI/MCP silently does nothing). So MCP servers are ALWAYS
|
|
20
|
+
* registered as `node <realpathSync(script)>` — never the bare bin name.
|
|
21
|
+
*
|
|
22
|
+
* Baking plugins into the session image is a separate TODO that belongs in
|
|
23
|
+
* packages/session-agent/Dockerfile (left untouched here): until then a Pod
|
|
24
|
+
* restart loses globally-installed plugins and `remote plugin sync` must be
|
|
25
|
+
* re-run.
|
|
26
|
+
*
|
|
27
|
+
* DRIFT (remote supervise slice 3, ADDITIVE): a desired-state manifest
|
|
28
|
+
* (plugin-manifest.ts) + a sidecar pushed to each Pod (~/.remote-manifest.json /
|
|
29
|
+
* ~/.remote-manifest.sha256, gated EXACTLY like CREDS_HASH_FILE), a read-only
|
|
30
|
+
* `plugin sync --check` drift report, and a watch-pass reconcile that re-runs
|
|
31
|
+
* the EXISTING buildPodSyncScript on a hash mismatch (an extra TRIGGER, not a
|
|
32
|
+
* new push path). The plain `plugin add`/`plugin sync` push is UNCHANGED.
|
|
33
|
+
*
|
|
34
|
+
* TODO(slice 4 — converge-on-session-start, the BIGGEST remaining drift source):
|
|
35
|
+
* a freshly (re)created Pod boots with NO plugins until the next sync/watch pass,
|
|
36
|
+
* so its CLI launches WITHOUT track/h2a/etc. Closing that needs the session-agent
|
|
37
|
+
* startup path (packages/session-agent, the pane that launches `<cli>`) to run
|
|
38
|
+
* the sync + write the manifest sidecar BEFORE exec'ing the CLI — OUT OF SCOPE
|
|
39
|
+
* here (do not touch session-agent this slice). The watch-pass reconcile is the
|
|
40
|
+
* interim safety net.
|
|
41
|
+
*/
|
|
42
|
+
import { type PluginEntry, type PluginInstall, type TunnelConfig } from "./config.js";
|
|
43
|
+
import { type DriftRow, type PluginManifest, type PodPluginState } from "./plugin-manifest.js";
|
|
44
|
+
/**
|
|
45
|
+
* The shell install command for a plugin + a one-line label. Pure, exported for
|
|
46
|
+
* tests. npm (default): `npm install -g <pkg>@<ver>`. curl: pipe an https
|
|
47
|
+
* installer (`curl -fsSL <url> | bash`) — the url is single-quoted and must be
|
|
48
|
+
* a clean https URL. script: an arbitrary shell line straight from the user's
|
|
49
|
+
* own config (no extra guard — it is the user's command). Same command runs
|
|
50
|
+
* locally (pluginAdd) and in the Pod (buildPodSyncScript).
|
|
51
|
+
*/
|
|
52
|
+
export declare function buildInstallCommand(plugin: PluginEntry): {
|
|
53
|
+
cmd: string;
|
|
54
|
+
label: string;
|
|
55
|
+
};
|
|
56
|
+
/** Split `pkg[@version]` (scope-aware: the leading @ is not a version sep). */
|
|
57
|
+
export declare function splitNpmSpec(spec: string): {
|
|
58
|
+
pkg: string;
|
|
59
|
+
version?: string;
|
|
60
|
+
};
|
|
61
|
+
export type McpRequest = {
|
|
62
|
+
name: string;
|
|
63
|
+
bin: string;
|
|
64
|
+
};
|
|
65
|
+
/** Parse one `--mcp <name>=<bin>` spec. */
|
|
66
|
+
export declare function parseMcpSpec(spec: string): McpRequest;
|
|
67
|
+
export declare function parseMcpSpecs(specs: readonly string[]): McpRequest[];
|
|
68
|
+
/**
|
|
69
|
+
* Heuristic when no --mcp is given: every bin ending in `-mcp` is an MCP
|
|
70
|
+
* server named after the bin minus the suffix (track-mcp → track).
|
|
71
|
+
*/
|
|
72
|
+
export declare function detectMcpBins(bins: Readonly<Record<string, string>>): McpRequest[];
|
|
73
|
+
/** Normalize a package.json `bin` field (string or map) to name -> rel path. */
|
|
74
|
+
export declare function normalizeBins(pkgName: string, bin: unknown): Record<string, string>;
|
|
75
|
+
/** The `[mcp_servers.<name>]` TOML section for ~/.codex/config.toml. */
|
|
76
|
+
export declare function codexMcpServerBlock(name: string, command: string, args: readonly string[]): string;
|
|
77
|
+
/**
|
|
78
|
+
* Idempotently upsert the `[mcp_servers.<name>]` section in a config.toml
|
|
79
|
+
* body: an existing section (up to the next `[…]` header) is replaced in
|
|
80
|
+
* place, otherwise the block is appended. Applying twice is a no-op.
|
|
81
|
+
*/
|
|
82
|
+
export declare function upsertCodexMcpServer(toml: string, name: string, command: string, args: readonly string[]): string;
|
|
83
|
+
/** Idempotent `mcpServers.<name>` merge for a ~/.claude.json body. */
|
|
84
|
+
export declare function mergeClaudeMcpServers(json: string, name: string, command: string, args: readonly string[]): string;
|
|
85
|
+
/**
|
|
86
|
+
* Idempotent `mcpServers.<name>` merge for agy's
|
|
87
|
+
* ~/.gemini/config/mcp_config.json (same Claude-style shape — the agy binary
|
|
88
|
+
* schema accepts command/args/env/url per server; we only emit command+args).
|
|
89
|
+
*/
|
|
90
|
+
export declare function mergeAgyMcpServers(json: string, name: string, command: string, args: readonly string[]): string;
|
|
91
|
+
/**
|
|
92
|
+
* Pure write plan for agy's mcp_config.json: the merged body, whether it
|
|
93
|
+
* differs from `before`, and whether a `.bak.<epoch>` backup is owed (only on
|
|
94
|
+
* the FIRST real modification of a non-empty file — an empty/absent file has
|
|
95
|
+
* nothing worth backing up, and an unchanged file is never rewritten).
|
|
96
|
+
*/
|
|
97
|
+
export declare function planAgyMcpConfigUpdate(before: string, name: string, command: string, args: readonly string[]): {
|
|
98
|
+
next: string;
|
|
99
|
+
changed: boolean;
|
|
100
|
+
needsBackup: boolean;
|
|
101
|
+
};
|
|
102
|
+
/**
|
|
103
|
+
* In-Pod ~/.claude.json merge, run as `node -e '<this>' <name> <scriptPath>`.
|
|
104
|
+
* Double quotes only — the snippet is single-quoted inside the bash script.
|
|
105
|
+
*/
|
|
106
|
+
export declare const POD_CLAUDE_MERGE_JS: string;
|
|
107
|
+
/**
|
|
108
|
+
* In-Pod ~/.gemini/config/mcp_config.json merge, run as
|
|
109
|
+
* `node -e '<this>' <name> <scriptPath>`. Double quotes only — the snippet is
|
|
110
|
+
* single-quoted inside the bash script. Same shape as the claude merge; the
|
|
111
|
+
* config dir may not exist yet in a fresh Pod, hence mkdirSync.
|
|
112
|
+
*/
|
|
113
|
+
export declare const POD_AGY_MERGE_JS: string;
|
|
114
|
+
/** claude / claude-code → claude; codex → codex; agy / antigravity → agy. */
|
|
115
|
+
export declare function mcpTargetForProfile(profile: string): "claude" | "codex" | "agy" | "todo";
|
|
116
|
+
/**
|
|
117
|
+
* Bash script run inside a session Pod (`kubectl exec … bash -lc`) by
|
|
118
|
+
* `remote plugin sync`: installs the plugin globally, recomputes each MCP
|
|
119
|
+
* script's realpath against the POD's npm global root (the local realpath is
|
|
120
|
+
* meaningless there), then registers the MCP server for the Pod's profile.
|
|
121
|
+
* Every step echoes one line — the CLI prints them as the per-session recap.
|
|
122
|
+
*/
|
|
123
|
+
export declare function buildPodSyncScript(plugin: PluginEntry, profile: string): string;
|
|
124
|
+
/** HOME-relative manifest sidecar in the Pod (the desired-state JSON). */
|
|
125
|
+
export declare const MANIFEST_FILE = ".remote-manifest.json";
|
|
126
|
+
/** HOME-relative file recording the sha256 of the last pushed manifest. */
|
|
127
|
+
export declare const MANIFEST_HASH_FILE = ".remote-manifest.sha256";
|
|
128
|
+
/** Render the desired-state manifest for the configured plugins (no tracked skills today). */
|
|
129
|
+
export declare function desiredManifest(): PluginManifest;
|
|
130
|
+
/**
|
|
131
|
+
* Injectable IO seams for the manifest sidecar + reconcile so the gate is
|
|
132
|
+
* unit-testable WITHOUT shelling out to kubectl. Production wires the real
|
|
133
|
+
* execPod against a tunnel (`manifestPodIo`); tests pass a stateful fake Pod and
|
|
134
|
+
* assert that a converged Pod re-reports NO drift (no resync) next pass.
|
|
135
|
+
*/
|
|
136
|
+
export type ManifestPodIo = {
|
|
137
|
+
/** Read the Pod's recorded manifest hash ("" when absent/unreadable). */
|
|
138
|
+
readHash: () => string;
|
|
139
|
+
/** Persist the manifest JSON + its hash in the Pod (the sidecar write). */
|
|
140
|
+
writeSidecar: (json: string, hash: string) => void;
|
|
141
|
+
/** Run one plugin's EXISTING buildPodSyncScript in the Pod; returns its stdout. */
|
|
142
|
+
runSync: (plugin: PluginEntry) => string;
|
|
143
|
+
};
|
|
144
|
+
/** Production wiring of `ManifestPodIo` against a live Pod (real kubectl exec). */
|
|
145
|
+
export declare function manifestPodIo(tunnel: TunnelConfig, pod: string, profile: string): ManifestPodIo;
|
|
146
|
+
/**
|
|
147
|
+
* Push the desired-state manifest into a Pod as ~/.remote-manifest.json +
|
|
148
|
+
* ~/.remote-manifest.sha256, GATED exactly like CREDS_HASH_FILE: read the Pod's
|
|
149
|
+
* recorded hash first and SKIP the write when it already matches (so a steady
|
|
150
|
+
* Pod is a single `cat`, no churn). Mirrors soft-refresh's hash-sidecar pattern.
|
|
151
|
+
* Returns whether the sidecar was (re)written. Best-effort: caller logs errors.
|
|
152
|
+
*/
|
|
153
|
+
export declare function pushManifestSidecar(tunnel: TunnelConfig, pod: string, manifest: PluginManifest): {
|
|
154
|
+
wrote: boolean;
|
|
155
|
+
hash: string;
|
|
156
|
+
};
|
|
157
|
+
/** Sidecar push over an injected `ManifestPodIo` (the testable core). PURE-ish. */
|
|
158
|
+
export declare function pushManifestSidecarVia(io: ManifestPodIo, manifest: PluginManifest): {
|
|
159
|
+
wrote: boolean;
|
|
160
|
+
hash: string;
|
|
161
|
+
};
|
|
162
|
+
/** The Pod's recorded manifest hash, or "" when absent/unreadable. */
|
|
163
|
+
export declare function readPodManifestHash(tunnel: TunnelConfig, pod: string): string;
|
|
164
|
+
/**
|
|
165
|
+
* Injectable seams so the pod-state probe is unit-testable WITHOUT kubectl.
|
|
166
|
+
* `npmLs` runs the argv-safe `npm ls -g --json` and returns its stdout;
|
|
167
|
+
* `readMcpConfigs` returns the raw MCP config blobs the existing sync writes.
|
|
168
|
+
*/
|
|
169
|
+
export type PodProbeDeps = {
|
|
170
|
+
/** Run `npm ls -g --json <pkgs…>` in the Pod, return stdout (may be non-zero exit). */
|
|
171
|
+
npmLs: (pkgs: ReadonlyArray<string>) => string;
|
|
172
|
+
/** Read the Pod's MCP config blobs: Claude-style JSON bodies + codex TOML. */
|
|
173
|
+
readMcpConfigs: () => {
|
|
174
|
+
json: string[];
|
|
175
|
+
toml: string;
|
|
176
|
+
};
|
|
177
|
+
};
|
|
178
|
+
/**
|
|
179
|
+
* Probe ONE Pod's actual plugin/MCP state into a `PodPluginState` (pure shape):
|
|
180
|
+
* installed versions for the desired pkgs (`npm ls -g --json` → parsed) + the
|
|
181
|
+
* registered MCP server names (claude/agy JSON + codex TOML → parsed). PURE-ish:
|
|
182
|
+
* all IO is injected via `deps` (tests never shell out). Exported for tests.
|
|
183
|
+
*/
|
|
184
|
+
export declare function probePodPluginState(desiredPkgs: ReadonlyArray<string>, deps: PodProbeDeps): PodPluginState;
|
|
185
|
+
/**
|
|
186
|
+
* Watch-pass RECONCILE for ONE live Pod (an EXTRA trigger, NOT a new push path):
|
|
187
|
+
* compare the local desired-state hash with the Pod's recorded
|
|
188
|
+
* ~/.remote-manifest.sha256 (one `cat`); on a MISMATCH, re-run the EXISTING
|
|
189
|
+
* buildPodSyncScript for every plugin (the same mechanism `plugin sync` uses)
|
|
190
|
+
* and re-push the manifest sidecar. A match is a zero-work no-op. Mirrors the
|
|
191
|
+
* slice-2 probe→push advisory structure. Best-effort: a per-plugin failure is
|
|
192
|
+
* logged; the others proceed. Returns what happened. Used by the supervise pass.
|
|
193
|
+
*/
|
|
194
|
+
export declare function reconcilePodManifest(tunnel: TunnelConfig, pod: string, profile: string, manifest: PluginManifest, plugins: ReadonlyArray<PluginEntry>, stderr?: {
|
|
195
|
+
write: (s: string) => unknown;
|
|
196
|
+
}): {
|
|
197
|
+
reconciled: boolean;
|
|
198
|
+
localHash: string;
|
|
199
|
+
podHash: string;
|
|
200
|
+
failures: number;
|
|
201
|
+
};
|
|
202
|
+
/**
|
|
203
|
+
* Reconcile core over an injected `ManifestPodIo` (the testable seam). PURE-ish:
|
|
204
|
+
* all Pod IO goes through `io`, so tests drive a stateful fake Pod and assert the
|
|
205
|
+
* gate — a matching hash is a zero-work no-op; an absent/mismatched hash resyncs
|
|
206
|
+
* ONCE then writes the sidecar so the NEXT pass converges (no re-sync).
|
|
207
|
+
*
|
|
208
|
+
* PERSISTENCE FIX (slice-3 regression): the sidecar is written via the SAME `io`
|
|
209
|
+
* (no second pod read, no `pushManifestSidecar` re-gate that could skip it) and
|
|
210
|
+
* is written even when some plugin syncs FAILED, so the Pod's recorded hash
|
|
211
|
+
* always converges to local after a reconcile — otherwise the next pass re-reads
|
|
212
|
+
* an absent hash and re-reports drift forever. (The dead-pod guard upstream keeps
|
|
213
|
+
* us off Evicted Pods where the write would throw and never persist at all.)
|
|
214
|
+
*/
|
|
215
|
+
export declare function reconcilePodManifestVia(io: ManifestPodIo, pod: string, manifest: PluginManifest, plugins: ReadonlyArray<PluginEntry>, stderr?: {
|
|
216
|
+
write: (s: string) => unknown;
|
|
217
|
+
}): {
|
|
218
|
+
reconciled: boolean;
|
|
219
|
+
localHash: string;
|
|
220
|
+
podHash: string;
|
|
221
|
+
failures: number;
|
|
222
|
+
};
|
|
223
|
+
/**
|
|
224
|
+
* Production wiring used by the supervise/watch pass: reconcile manifest drift
|
|
225
|
+
* for ONE session via the configured tunnel. Thin — the decision + the reused
|
|
226
|
+
* buildPodSyncScript live in `reconcilePodManifest`. No-op (and no error) when
|
|
227
|
+
* no plugins are configured. Best-effort; never throws into the caller.
|
|
228
|
+
*/
|
|
229
|
+
export declare function reconcileSessionPlugins(sessionId: string, profile: string, stderr?: NodeJS.WriteStream): {
|
|
230
|
+
reconciled: boolean;
|
|
231
|
+
};
|
|
232
|
+
/**
|
|
233
|
+
* `remote plugin add <npmPkg> [--mcp name=bin]...` — npm i -g, register the
|
|
234
|
+
* MCP server(s) with claude + codex + agy, persist in the config.
|
|
235
|
+
*/
|
|
236
|
+
export declare function pluginAdd(npmSpec: string, mcpSpecs: readonly string[], stderr?: NodeJS.WriteStream): PluginEntry;
|
|
237
|
+
/**
|
|
238
|
+
* `remote plugin add <name> --curl <url>` / `--install "<shell>"` — register a
|
|
239
|
+
* NON-npm plugin (a tool installed by piping an https script, or an arbitrary
|
|
240
|
+
* shell command). No local install is run (unlike the npm path, which must read
|
|
241
|
+
* the package to detect MCP bins): the installer runs in each Pod on
|
|
242
|
+
* `remote plugin sync`. Validates the command up front so a bad URL fails now.
|
|
243
|
+
*/
|
|
244
|
+
export declare function pluginAddInstaller(name: string, install: PluginInstall, stderr?: NodeJS.WriteStream): PluginEntry;
|
|
245
|
+
/**
|
|
246
|
+
* `remote plugin ls` — pkg / version / MCPs / where (local ok/missing, remote).
|
|
247
|
+
*
|
|
248
|
+
* The REMOTE column is now REAL per-Pod drift status (slice 3), not the old
|
|
249
|
+
* guess-`?`: when a `url` is passed (the command is connected to the control-
|
|
250
|
+
* plane) we probe each live Pod via the same drift mechanism `--check` uses and
|
|
251
|
+
* summarize each plugin's worst per-Pod status across all Pods
|
|
252
|
+
* (ok / version-drift / missing). Without a url (offline) we still print `?`
|
|
253
|
+
* with the documented hint — the `?` only remains when we genuinely can't reach
|
|
254
|
+
* the cluster, never as guesswork when we can.
|
|
255
|
+
*/
|
|
256
|
+
export declare function pluginLs(stdout?: NodeJS.WriteStream, url?: string): Promise<void>;
|
|
257
|
+
/**
|
|
258
|
+
* `remote plugin sync` — for every live remote session: kubectl exec into the
|
|
259
|
+
* Pod, `npm i -g <pkg>@<version>`, then register the MCP servers for the
|
|
260
|
+
* Pod's profile. Prints a per-session recap. Needs the configured tunnel.
|
|
261
|
+
*/
|
|
262
|
+
export declare function pluginSync(url: string, stderr?: NodeJS.WriteStream): Promise<void>;
|
|
263
|
+
/**
|
|
264
|
+
* `remote plugin sync --check` — a READ-ONLY drift report (converges NOTHING).
|
|
265
|
+
* For each live Pod it probes the actual installed state (npm ls -g --json for
|
|
266
|
+
* the desired pkgs + the registered MCP server names from the config files the
|
|
267
|
+
* existing sync writes) via the argv-safe pod exec, then the pure
|
|
268
|
+
* `diffManifest` rows it: ok / version-drift / missing / mcp-unregistered.
|
|
269
|
+
* Prints a table and EXITS 1 when ANY row is drift (via process.exitCode). This
|
|
270
|
+
* REPLACES the `plugin ls` `REMOTE ?` guesswork with REAL per-Pod status. Pure
|
|
271
|
+
* decisions live in plugin-manifest.ts; this is the thin probe + printer.
|
|
272
|
+
*/
|
|
273
|
+
export declare function pluginSyncCheck(url: string, stdout?: NodeJS.WriteStream, stderr?: NodeJS.WriteStream): Promise<DriftRow[]>;
|
|
274
|
+
//# sourceMappingURL=plugin.d.ts.map
|