@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
package/dist/h2a-jobs.js
ADDED
|
@@ -0,0 +1,388 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* h2a job callback + decision channel — P3 of cross-type agent delegation.
|
|
3
|
+
*
|
|
4
|
+
* Pure helpers (builders + parsing + instance derivation) plus a thin fs layer
|
|
5
|
+
* to DROP an envelope into a local h2a inbox. The bridge (`remote h2a bridge`)
|
|
6
|
+
* already transports envelopes pod↔local and is idempotent by file name, so
|
|
7
|
+
* everything here is best-effort: an undelivered envelope never blocks a job.
|
|
8
|
+
*
|
|
9
|
+
* - `job.done` agent → PARENT, when a delegated job terminates (body:
|
|
10
|
+
* {jobId, type, state, exitCode?, summary?, resultRef?}).
|
|
11
|
+
* - `decision.requested` agent → PARENT, when the job needs a human/parent
|
|
12
|
+
* decision (body: {jobId, question, options?}).
|
|
13
|
+
* - `decision.reply` PARENT → agent, the answer (body: {jobId, answer}).
|
|
14
|
+
*
|
|
15
|
+
* Envelopes mirror the on-disk h2a shape exactly (the format `h2a-bridge.ts`
|
|
16
|
+
* documents in the Pod README): {protocol, version, id, type, actor:{instance,
|
|
17
|
+
* role, scope}, to, body, createdAt}. They drop into
|
|
18
|
+
* `<localRoot>/inbox/<recipient-dir>/env__<ts>__<slug>.json`, where the dir is
|
|
19
|
+
* the recipient instance id with ":" → "__" — the bridge's SAFE_ENTRY shape.
|
|
20
|
+
*
|
|
21
|
+
* SECURITY: jobId / answer / question ride structured argv → the envelope body,
|
|
22
|
+
* NEVER a shell string. jobId is `assertSafeName`-checked before it becomes a
|
|
23
|
+
* file slug. The bridge itself re-validates every entry name (SAFE_ENTRY).
|
|
24
|
+
*
|
|
25
|
+
* KNOWN LIMIT — depends on agent behavior: a delegated interactive agent must
|
|
26
|
+
* CHOOSE to call its h2a MCP tool (h2a_inbox / write an envelope) to read a
|
|
27
|
+
* `decision.reply` or to emit a `decision.requested`. We build the channel and
|
|
28
|
+
* the transport; we cannot force the agent to use it. `job.done` for an
|
|
29
|
+
* interactive job is independent of that (it rides the SessionEnd hook), and
|
|
30
|
+
* for headless rides reconciliation — neither needs the agent to cooperate.
|
|
31
|
+
*/
|
|
32
|
+
import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync, } from "node:fs";
|
|
33
|
+
import { join } from "node:path";
|
|
34
|
+
import { assertSafeName } from "./delegate.js";
|
|
35
|
+
import { defaultLocalH2aRoot, instanceInboxDir } from "./h2a-bridge.js";
|
|
36
|
+
export const H2A_PROTOCOL = "sentropic.h2a";
|
|
37
|
+
export const H2A_VERSION = "0.1";
|
|
38
|
+
const DEFAULT_ROLE = "AGENTS";
|
|
39
|
+
const DEFAULT_SCOPE = "scope:default";
|
|
40
|
+
/**
|
|
41
|
+
* The h2a INSTANCE id of a delegated job's agent — the inbox a `decision.reply`
|
|
42
|
+
* must reach, and the `actor.instance` on envelopes the job emits.
|
|
43
|
+
* - remote (kind:"remote"): the Pod default mapping `<tool>:remote:<sessionId>`
|
|
44
|
+
* (same as `h2a-bridge.ts` defaultPodInstance), so the bridge already routes
|
|
45
|
+
* to/from it.
|
|
46
|
+
* - local: `<tool>:job:<jobId>` — a stable, predictable instance for the local
|
|
47
|
+
* job's h2a side-window. jobId already passed assertSafeName at delegate time.
|
|
48
|
+
* Pure, exported for tests.
|
|
49
|
+
*/
|
|
50
|
+
export function jobInstance(entry) {
|
|
51
|
+
if (entry.kind === "remote") {
|
|
52
|
+
return `${entry.tool}:remote:${entry.remoteId ?? entry.id}`;
|
|
53
|
+
}
|
|
54
|
+
return `${entry.tool}:job:${entry.id}`;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* The PARENT instance to address a callback to: the explicit `callbackTo`
|
|
58
|
+
* recorded on the job at delegate time (`--parent`/`--on-done`), else undefined
|
|
59
|
+
* (no parent → no callback is emitted; best-effort). Pure, exported for tests.
|
|
60
|
+
*/
|
|
61
|
+
export function parentInstance(entry) {
|
|
62
|
+
const to = entry.callbackTo;
|
|
63
|
+
return to && to.length > 0 ? to : undefined;
|
|
64
|
+
}
|
|
65
|
+
/** A monotonic-ish envelope id slug. Pure given (jobId, kind, ts). */
|
|
66
|
+
function envelopeId(kind, jobId, ts) {
|
|
67
|
+
return `env:${ts}:${kind}-${jobId}`;
|
|
68
|
+
}
|
|
69
|
+
// ---------------------------------------------------------------------------
|
|
70
|
+
// S1 — envelope AUTHENTICATION (shared RWX is multi-tenant).
|
|
71
|
+
//
|
|
72
|
+
// The h2a inbox lives on the shared RWX volume, writable by EVERY pod. Nothing
|
|
73
|
+
// signs an envelope, so any pod can DROP a file claiming to be a `job.done` for
|
|
74
|
+
// another job, or a `decision.requested`/`decision.reply` piloting a neighbour's
|
|
75
|
+
// agent. We can't make the file bus cryptographically trustworthy here, but we
|
|
76
|
+
// CAN reject envelopes whose claimed `actor.instance` doesn't match the instance
|
|
77
|
+
// the addressed job is KNOWN to use:
|
|
78
|
+
// - a `job.done`/`decision.requested` ABOUT job X must come FROM job X's own
|
|
79
|
+
// agent → actor.instance === jobInstance(X). A different sender is forged.
|
|
80
|
+
// - a `decision.reply` is PARENT→agent and is treated as ADVISORY: it is never
|
|
81
|
+
// used to gate a privileged action (the agent chooses to act on it), and a
|
|
82
|
+
// reply whose actor isn't the job's recorded parent is dropped from the
|
|
83
|
+
// "answered" set so it can't silently suppress a real pending decision.
|
|
84
|
+
//
|
|
85
|
+
// LIMIT: this is integrity-by-convention, not authentication — a pod that knows
|
|
86
|
+
// the victim's jobId AND can compute jobInstance() (a deterministic
|
|
87
|
+
// `<tool>:job:<id>` / `<tool>:remote:<id>`) could still forge a matching actor.
|
|
88
|
+
// Real trust needs signed envelopes (h2a_sign), tracked separately. This check
|
|
89
|
+
// stops the trivial cross-job spoof, which is the immediate exposure.
|
|
90
|
+
// ---------------------------------------------------------------------------
|
|
91
|
+
/** The `actor.instance` an envelope carries, or undefined when malformed. Pure. */
|
|
92
|
+
export function envelopeActorInstance(envelope) {
|
|
93
|
+
const inst = envelope.actor?.instance;
|
|
94
|
+
return typeof inst === "string" && inst.length > 0 ? inst : undefined;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Is `envelope`'s sender the instance we EXPECT for this job? `expectedInstance`
|
|
98
|
+
* is the job's `jobInstance(...)` (for job.done / decision.requested ABOUT the
|
|
99
|
+
* job) or the job's recorded parent (for decision.reply). An undefined expected
|
|
100
|
+
* instance means we can't verify (no job/parent on record) → reject (fail
|
|
101
|
+
* closed). Pure, exported for tests.
|
|
102
|
+
*/
|
|
103
|
+
export function isEnvelopeFromExpected(envelope, expectedInstance) {
|
|
104
|
+
if (!expectedInstance)
|
|
105
|
+
return false;
|
|
106
|
+
return envelopeActorInstance(envelope) === expectedInstance;
|
|
107
|
+
}
|
|
108
|
+
/** On-disk file name for an envelope — matches the bridge's SAFE_ENTRY shape. */
|
|
109
|
+
export function envelopeFileName(kind, jobId, ts) {
|
|
110
|
+
// "." in the type is not allowed in a SAFE_ENTRY dir/file segment lead, but
|
|
111
|
+
// is fine mid-name; we slugify it to "_" to stay maximally safe.
|
|
112
|
+
const slug = kind.replace(/\./g, "_");
|
|
113
|
+
return `env__${ts}__${slug}-${jobId}.json`;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* STABLE (timestamp-free) file name for a `job.done` callback: ONE per job, so
|
|
117
|
+
* the SessionEnd hook and the reconcile loop can BOTH attempt the emit and the
|
|
118
|
+
* second one is a no-op (dropEnvelope skips an existing file; the bridge skips
|
|
119
|
+
* by file name too). Idempotency by name, exactly the bridge's contract.
|
|
120
|
+
*/
|
|
121
|
+
export function jobDoneFileName(jobId) {
|
|
122
|
+
return `env__job_done-${jobId}.json`;
|
|
123
|
+
}
|
|
124
|
+
function nowIso(ms) {
|
|
125
|
+
return new Date(ms).toISOString();
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Build a `job.done` envelope: emitted BY the job's agent, addressed TO the
|
|
129
|
+
* parent. The body carries jobId/type/state and optional exitCode/summary/
|
|
130
|
+
* resultRef. Pure, exported for tests (ts/id are injected for determinism).
|
|
131
|
+
*/
|
|
132
|
+
export function buildJobDoneEnvelope(args) {
|
|
133
|
+
const ts = args.nowMs ?? Date.now();
|
|
134
|
+
const body = {
|
|
135
|
+
jobId: args.job.id,
|
|
136
|
+
type: args.job.tool,
|
|
137
|
+
state: args.state,
|
|
138
|
+
};
|
|
139
|
+
if (args.exitCode !== undefined)
|
|
140
|
+
body.exitCode = args.exitCode;
|
|
141
|
+
if (args.summary !== undefined)
|
|
142
|
+
body.summary = args.summary;
|
|
143
|
+
if (args.resultRef !== undefined)
|
|
144
|
+
body.resultRef = args.resultRef;
|
|
145
|
+
return {
|
|
146
|
+
protocol: H2A_PROTOCOL,
|
|
147
|
+
version: H2A_VERSION,
|
|
148
|
+
id: envelopeId("job-done", args.job.id, ts),
|
|
149
|
+
type: "job.done",
|
|
150
|
+
actor: { instance: jobInstance(args.job), role: DEFAULT_ROLE, scope: DEFAULT_SCOPE },
|
|
151
|
+
to: args.to,
|
|
152
|
+
body,
|
|
153
|
+
createdAt: nowIso(ts),
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Build a `decision.requested` envelope: emitted BY the job's agent, addressed
|
|
158
|
+
* TO the parent. Pure, exported for tests.
|
|
159
|
+
*/
|
|
160
|
+
export function buildDecisionRequested(args) {
|
|
161
|
+
const ts = args.nowMs ?? Date.now();
|
|
162
|
+
const body = {
|
|
163
|
+
jobId: args.job.id,
|
|
164
|
+
question: args.question,
|
|
165
|
+
};
|
|
166
|
+
if (args.options !== undefined)
|
|
167
|
+
body.options = args.options;
|
|
168
|
+
return {
|
|
169
|
+
protocol: H2A_PROTOCOL,
|
|
170
|
+
version: H2A_VERSION,
|
|
171
|
+
id: envelopeId("decision-req", args.job.id, ts),
|
|
172
|
+
type: "decision.requested",
|
|
173
|
+
actor: { instance: jobInstance(args.job), role: DEFAULT_ROLE, scope: DEFAULT_SCOPE },
|
|
174
|
+
to: args.to,
|
|
175
|
+
body,
|
|
176
|
+
createdAt: nowIso(ts),
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Build a `decision.reply` envelope: emitted BY the parent, addressed TO the
|
|
181
|
+
* job's agent inbox. The `answer` is opaque text — passed straight through, no
|
|
182
|
+
* shell concat. Pure, exported for tests.
|
|
183
|
+
*/
|
|
184
|
+
export function buildDecisionReply(args) {
|
|
185
|
+
const ts = args.nowMs ?? Date.now();
|
|
186
|
+
return {
|
|
187
|
+
protocol: H2A_PROTOCOL,
|
|
188
|
+
version: H2A_VERSION,
|
|
189
|
+
id: envelopeId("decision-reply", args.job.id, ts),
|
|
190
|
+
type: "decision.reply",
|
|
191
|
+
actor: {
|
|
192
|
+
instance: args.parentInstance,
|
|
193
|
+
role: DEFAULT_ROLE,
|
|
194
|
+
scope: DEFAULT_SCOPE,
|
|
195
|
+
},
|
|
196
|
+
to: jobInstance(args.job),
|
|
197
|
+
body: { jobId: args.job.id, answer: args.answer },
|
|
198
|
+
createdAt: nowIso(ts),
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Drop an envelope into `<localRoot>/inbox/<to-dir>/<fileName>`, where <to-dir>
|
|
203
|
+
* is `envelope.to` with ":" → "__". Idempotent by FILE NAME (an existing file is
|
|
204
|
+
* left untouched — same contract as the bridge). The jobId in the file name is
|
|
205
|
+
* already assertSafeName-checked by the caller. Returns where it landed.
|
|
206
|
+
*/
|
|
207
|
+
export function dropEnvelope(envelope, fileName, localRoot = defaultLocalH2aRoot()) {
|
|
208
|
+
const dir = join(localRoot, "inbox", instanceInboxDir(envelope.to));
|
|
209
|
+
const path = join(dir, fileName);
|
|
210
|
+
if (existsSync(path))
|
|
211
|
+
return { path, written: false };
|
|
212
|
+
mkdirSync(dir, { recursive: true });
|
|
213
|
+
writeFileSync(path, `${JSON.stringify(envelope, null, 2)}\n`, "utf8");
|
|
214
|
+
return { path, written: true };
|
|
215
|
+
}
|
|
216
|
+
/** Minimal parse of an on-disk envelope; tolerant of unrelated files. */
|
|
217
|
+
function parseEnvelope(raw) {
|
|
218
|
+
try {
|
|
219
|
+
const e = JSON.parse(raw);
|
|
220
|
+
if (e.protocol === H2A_PROTOCOL &&
|
|
221
|
+
typeof e.type === "string" &&
|
|
222
|
+
typeof e.id === "string" &&
|
|
223
|
+
e.body !== undefined) {
|
|
224
|
+
return e;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
catch {
|
|
228
|
+
// not an envelope / malformed → skip
|
|
229
|
+
}
|
|
230
|
+
return undefined;
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* AUTHENTICATE a flat list of job envelopes (S1): keep only those whose claimed
|
|
234
|
+
* `actor.instance` matches the instance the addressed job is known to use (per
|
|
235
|
+
* the resolver). A forged cross-job envelope (wrong actor) is DROPPED. Envelopes
|
|
236
|
+
* that aren't one of our job types pass through untouched (they're not our
|
|
237
|
+
* concern here). Pure over (envelopes, resolver), exported for tests.
|
|
238
|
+
*/
|
|
239
|
+
export function authenticateJobEnvelopes(envelopes, expectedInstanceOf) {
|
|
240
|
+
const out = [];
|
|
241
|
+
for (const e of envelopes) {
|
|
242
|
+
if (e.type !== "job.done" &&
|
|
243
|
+
e.type !== "decision.requested" &&
|
|
244
|
+
e.type !== "decision.reply") {
|
|
245
|
+
out.push(e);
|
|
246
|
+
continue;
|
|
247
|
+
}
|
|
248
|
+
const body = e.body;
|
|
249
|
+
const jobId = typeof body?.jobId === "string" ? body.jobId : undefined;
|
|
250
|
+
if (!jobId)
|
|
251
|
+
continue; // a job envelope without a jobId is unusable → drop
|
|
252
|
+
if (isEnvelopeFromExpected(e, expectedInstanceOf(jobId, e.type))) {
|
|
253
|
+
out.push(e);
|
|
254
|
+
}
|
|
255
|
+
// else: forged / unverifiable actor → dropped
|
|
256
|
+
}
|
|
257
|
+
return out;
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* From a flat list of (fileName, rawJson) envelopes found in the PARENT's inbox
|
|
261
|
+
* and the JOB inboxes, return the `decision.requested` that have NO matching
|
|
262
|
+
* `decision.reply` yet (same jobId). Pure, exported for tests — the fs read is
|
|
263
|
+
* done by the caller so this stays deterministic.
|
|
264
|
+
*
|
|
265
|
+
* A reply is matched to a request by jobId (the channel is one open decision per
|
|
266
|
+
* job at a time; P4 may add a per-request id). `repliedJobIds` is the set of
|
|
267
|
+
* jobIds for which a `decision.reply` exists anywhere we can see.
|
|
268
|
+
*/
|
|
269
|
+
export function pendingDecisions(envelopes, repliedJobIds) {
|
|
270
|
+
const out = [];
|
|
271
|
+
for (const e of envelopes) {
|
|
272
|
+
if (e.type !== "decision.requested")
|
|
273
|
+
continue;
|
|
274
|
+
const body = e.body;
|
|
275
|
+
if (typeof body.jobId !== "string" || typeof body.question !== "string") {
|
|
276
|
+
continue;
|
|
277
|
+
}
|
|
278
|
+
if (repliedJobIds.has(body.jobId))
|
|
279
|
+
continue;
|
|
280
|
+
const d = {
|
|
281
|
+
jobId: body.jobId,
|
|
282
|
+
question: body.question,
|
|
283
|
+
envelopeId: e.id,
|
|
284
|
+
};
|
|
285
|
+
if (Array.isArray(body.options))
|
|
286
|
+
d.options = body.options.map(String);
|
|
287
|
+
out.push(d);
|
|
288
|
+
}
|
|
289
|
+
return out;
|
|
290
|
+
}
|
|
291
|
+
/** The set of jobIds that already have a `decision.reply` in `envelopes`. Pure. */
|
|
292
|
+
export function repliedDecisionJobIds(envelopes) {
|
|
293
|
+
const set = new Set();
|
|
294
|
+
for (const e of envelopes) {
|
|
295
|
+
if (e.type !== "decision.reply")
|
|
296
|
+
continue;
|
|
297
|
+
const body = e.body;
|
|
298
|
+
if (typeof body.jobId === "string")
|
|
299
|
+
set.add(body.jobId);
|
|
300
|
+
}
|
|
301
|
+
return set;
|
|
302
|
+
}
|
|
303
|
+
/**
|
|
304
|
+
* Read every envelope under `<localRoot>/inbox/**` (one dir level deep, the h2a
|
|
305
|
+
* layout). The fs read is isolated HERE so the listing/matching above stays
|
|
306
|
+
* pure. Missing store → empty. Never throws on a bad file (skips it).
|
|
307
|
+
*/
|
|
308
|
+
export function readInboxEnvelopes(localRoot = defaultLocalH2aRoot()) {
|
|
309
|
+
const inbox = join(localRoot, "inbox");
|
|
310
|
+
if (!existsSync(inbox))
|
|
311
|
+
return [];
|
|
312
|
+
const out = [];
|
|
313
|
+
for (const dir of readdirSync(inbox, { withFileTypes: true })) {
|
|
314
|
+
if (!dir.isDirectory())
|
|
315
|
+
continue;
|
|
316
|
+
const dirPath = join(inbox, dir.name);
|
|
317
|
+
for (const f of readdirSync(dirPath, { withFileTypes: true })) {
|
|
318
|
+
if (!f.isFile() || !f.name.endsWith(".json"))
|
|
319
|
+
continue;
|
|
320
|
+
let raw;
|
|
321
|
+
try {
|
|
322
|
+
raw = readFileSync(join(dirPath, f.name), "utf8");
|
|
323
|
+
}
|
|
324
|
+
catch {
|
|
325
|
+
continue;
|
|
326
|
+
}
|
|
327
|
+
const env = parseEnvelope(raw);
|
|
328
|
+
if (env)
|
|
329
|
+
out.push(env);
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
return out;
|
|
333
|
+
}
|
|
334
|
+
/**
|
|
335
|
+
* Does the job have an UNANSWERED `decision.requested`? Used by `jobs status` to
|
|
336
|
+
* show `awaiting-decision`. Pure over the envelope list, exported for tests.
|
|
337
|
+
*/
|
|
338
|
+
export function isAwaitingDecision(jobId, envelopes) {
|
|
339
|
+
const replied = repliedDecisionJobIds(envelopes);
|
|
340
|
+
return pendingDecisions(envelopes, replied).some((d) => d.jobId === jobId);
|
|
341
|
+
}
|
|
342
|
+
/**
|
|
343
|
+
* One-line render of a pending decision for `jobs decisions` — jobId + question
|
|
344
|
+
* only (no raw envelope body beyond the essentials, per the security note).
|
|
345
|
+
* Pure, exported for tests.
|
|
346
|
+
*/
|
|
347
|
+
export function renderPendingDecisions(decisions) {
|
|
348
|
+
if (decisions.length === 0)
|
|
349
|
+
return "(no pending decisions)";
|
|
350
|
+
const lines = decisions.map((d) => {
|
|
351
|
+
const opts = d.options && d.options.length > 0 ? ` [${d.options.join(" | ")}]` : "";
|
|
352
|
+
return `${d.jobId}\t${d.question}${opts}`;
|
|
353
|
+
});
|
|
354
|
+
return ["JOB\tQUESTION", ...lines].join("\n");
|
|
355
|
+
}
|
|
356
|
+
/**
|
|
357
|
+
* Best-effort: emit a `job.done` envelope for a finished job into the parent's
|
|
358
|
+
* local inbox. No-op (emitted:false, "no-parent") when the job has no recorded
|
|
359
|
+
* `callbackTo` — a job delegated without a parent simply has nobody to notify.
|
|
360
|
+
* NEVER throws: any fs/build error is swallowed into emitted:false so a callback
|
|
361
|
+
* failure can't take down the SessionEnd hook or the reconcile loop.
|
|
362
|
+
*/
|
|
363
|
+
export function emitJobDone(job, args = { state: job.jobState ?? "done" }) {
|
|
364
|
+
try {
|
|
365
|
+
const to = parentInstance(job);
|
|
366
|
+
if (!to)
|
|
367
|
+
return { emitted: false, reason: "no-parent" };
|
|
368
|
+
assertSafeName(job.id);
|
|
369
|
+
// Prefer the job's terminal time so the envelope id is stable across the
|
|
370
|
+
// hook + reconcile paths; the FILE name is timestamp-free (one per job).
|
|
371
|
+
const ts = args.nowMs ?? (job.endedAt ? Date.parse(job.endedAt) : Date.now());
|
|
372
|
+
const envelope = buildJobDoneEnvelope({
|
|
373
|
+
job,
|
|
374
|
+
to,
|
|
375
|
+
state: args.state,
|
|
376
|
+
...(args.exitCode !== undefined ? { exitCode: args.exitCode } : {}),
|
|
377
|
+
...(args.summary !== undefined ? { summary: args.summary } : {}),
|
|
378
|
+
...(args.resultRef !== undefined ? { resultRef: args.resultRef } : {}),
|
|
379
|
+
nowMs: ts,
|
|
380
|
+
});
|
|
381
|
+
const { path, written } = dropEnvelope(envelope, jobDoneFileName(job.id), args.localRoot);
|
|
382
|
+
return { emitted: true, path, written, to };
|
|
383
|
+
}
|
|
384
|
+
catch (error) {
|
|
385
|
+
return { emitted: false, reason: "error", error: String(error) };
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
//# sourceMappingURL=h2a-jobs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"h2a-jobs.js","sourceRoot":"","sources":["../src/h2a-jobs.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { H2A_PROTOCOL, H2A_VERSION, type EnvelopeActor } from "./h2a-jobs.js";
|
|
2
|
+
export type H2aPingEnvelope = {
|
|
3
|
+
protocol: typeof H2A_PROTOCOL;
|
|
4
|
+
version: typeof H2A_VERSION;
|
|
5
|
+
id: string;
|
|
6
|
+
type: "h2a.ping";
|
|
7
|
+
actor: EnvelopeActor;
|
|
8
|
+
to: string;
|
|
9
|
+
body: {
|
|
10
|
+
message: string;
|
|
11
|
+
cwd?: string;
|
|
12
|
+
};
|
|
13
|
+
createdAt: string;
|
|
14
|
+
};
|
|
15
|
+
export type H2aPingResult = {
|
|
16
|
+
path: string;
|
|
17
|
+
written: boolean;
|
|
18
|
+
envelope: H2aPingEnvelope;
|
|
19
|
+
};
|
|
20
|
+
export declare function h2aPingFileName(ts: number): string;
|
|
21
|
+
export declare function remoteSessionIdFromInstance(instance: string): string | undefined;
|
|
22
|
+
export declare function buildH2aPingEnvelope(args: {
|
|
23
|
+
to: string;
|
|
24
|
+
from?: string;
|
|
25
|
+
message?: string;
|
|
26
|
+
cwd?: string;
|
|
27
|
+
nowMs?: number;
|
|
28
|
+
}): H2aPingEnvelope;
|
|
29
|
+
export declare function dropH2aPing(envelope: H2aPingEnvelope, localRoot?: string): H2aPingResult;
|
|
30
|
+
export declare function sendH2aPing(args: {
|
|
31
|
+
to: string;
|
|
32
|
+
from?: string;
|
|
33
|
+
message?: string;
|
|
34
|
+
cwd?: string;
|
|
35
|
+
localRoot?: string;
|
|
36
|
+
nowMs?: number;
|
|
37
|
+
}): H2aPingResult;
|
|
38
|
+
//# sourceMappingURL=h2a-ping.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"h2a-ping.d.ts","sourceRoot":"","sources":["../src/h2a-ping.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,KAAK,aAAa,EAAE,MAAM,eAAe,CAAC;AAI9E,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,EAAE,OAAO,YAAY,CAAC;IAC9B,OAAO,EAAE,OAAO,WAAW,CAAC;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,UAAU,CAAC;IACjB,KAAK,EAAE,aAAa,CAAC;IACrB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE;QACJ,OAAO,EAAE,MAAM,CAAC;QAChB,GAAG,CAAC,EAAE,MAAM,CAAC;KACd,CAAC;IACF,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,eAAe,CAAC;CAC3B,CAAC;AAcF,wBAAgB,eAAe,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAElD;AAED,wBAAgB,2BAA2B,CACzC,QAAQ,EAAE,MAAM,GACf,MAAM,GAAG,SAAS,CAGpB;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE;IACzC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,GAAG,eAAe,CAmBlB;AAED,wBAAgB,WAAW,CACzB,QAAQ,EAAE,eAAe,EACzB,SAAS,GAAE,MAA8B,GACxC,aAAa,CAUf;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,GAAG,aAAa,CAGhB"}
|
package/dist/h2a-ping.js
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { defaultLocalH2aRoot, instanceInboxDir } from "./h2a-bridge.js";
|
|
4
|
+
import { H2A_PROTOCOL, H2A_VERSION } from "./h2a-jobs.js";
|
|
5
|
+
const SAFE_INSTANCE = /^[A-Za-z0-9][A-Za-z0-9._:-]*$/;
|
|
6
|
+
function assertSafeInstance(instance) {
|
|
7
|
+
if (!SAFE_INSTANCE.test(instance)) {
|
|
8
|
+
throw new Error(`unsafe h2a instance "${instance}" (allowed: letters, digits, ".", "_", "-", ":")`);
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
function nowIso(ms) {
|
|
12
|
+
return new Date(ms).toISOString();
|
|
13
|
+
}
|
|
14
|
+
export function h2aPingFileName(ts) {
|
|
15
|
+
return `env__${ts}__h2a_ping.json`;
|
|
16
|
+
}
|
|
17
|
+
export function remoteSessionIdFromInstance(instance) {
|
|
18
|
+
const match = /^[A-Za-z0-9._-]+:remote:([A-Za-z0-9._-]+)$/.exec(instance);
|
|
19
|
+
return match?.[1];
|
|
20
|
+
}
|
|
21
|
+
export function buildH2aPingEnvelope(args) {
|
|
22
|
+
const ts = args.nowMs ?? Date.now();
|
|
23
|
+
const from = args.from ?? "remote:cli";
|
|
24
|
+
assertSafeInstance(args.to);
|
|
25
|
+
assertSafeInstance(from);
|
|
26
|
+
const body = {
|
|
27
|
+
message: args.message ?? "ping",
|
|
28
|
+
};
|
|
29
|
+
if (args.cwd !== undefined)
|
|
30
|
+
body.cwd = args.cwd;
|
|
31
|
+
return {
|
|
32
|
+
protocol: H2A_PROTOCOL,
|
|
33
|
+
version: H2A_VERSION,
|
|
34
|
+
id: `env:${ts}:h2a-ping`,
|
|
35
|
+
type: "h2a.ping",
|
|
36
|
+
actor: { instance: from, role: "AGENTS", scope: "scope:default" },
|
|
37
|
+
to: args.to,
|
|
38
|
+
body,
|
|
39
|
+
createdAt: nowIso(ts),
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
export function dropH2aPing(envelope, localRoot = defaultLocalH2aRoot()) {
|
|
43
|
+
assertSafeInstance(envelope.to);
|
|
44
|
+
const ts = Date.parse(envelope.createdAt);
|
|
45
|
+
const file = h2aPingFileName(Number.isFinite(ts) ? ts : Date.now());
|
|
46
|
+
const dir = join(localRoot, "inbox", instanceInboxDir(envelope.to));
|
|
47
|
+
const path = join(dir, file);
|
|
48
|
+
if (existsSync(path))
|
|
49
|
+
return { path, written: false, envelope };
|
|
50
|
+
mkdirSync(dir, { recursive: true });
|
|
51
|
+
writeFileSync(path, `${JSON.stringify(envelope, null, 2)}\n`, "utf8");
|
|
52
|
+
return { path, written: true, envelope };
|
|
53
|
+
}
|
|
54
|
+
export function sendH2aPing(args) {
|
|
55
|
+
const envelope = buildH2aPingEnvelope(args);
|
|
56
|
+
return dropH2aPing(envelope, args.localRoot);
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=h2a-ping.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"h2a-ping.js","sourceRoot":"","sources":["../src/h2a-ping.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { type RegistryEntry } from "./registry.js";
|
|
3
|
+
export declare const packageName = "@sentropic/remote-cli";
|
|
4
|
+
export { run } from "./run.js";
|
|
5
|
+
export type { RunOptions, RunResult } from "./run.js";
|
|
6
|
+
export { attach, createRemoteSession, getRemoteSession, listRemoteSessions, refreshRemoteSession, renameRemoteSession, stopRemoteSession, } from "./attach.js";
|
|
7
|
+
export type { AttachOptions, AttachResult } from "./attach.js";
|
|
8
|
+
export { inspectProfileAuth } from "./auth-diagnostics.js";
|
|
9
|
+
export type { AuthDiagnosticsResult, AuthDiagnosticsStatus, } from "./auth-diagnostics.js";
|
|
10
|
+
export { AuthRefreshError, ensureProfileAuthFresh } from "./auth-refresh.js";
|
|
11
|
+
export { AuthBundleMissingError, assertRequiredAuthBundle, collectProfileAuth, } from "./auth-bundle.js";
|
|
12
|
+
export type { AuthBundle } from "./auth-bundle.js";
|
|
13
|
+
export { resolveProfile, coerceCliProfileName, isCliProfile, withResume, type ProfileConfig, } from "./profiles.js";
|
|
14
|
+
export { smokeRemoteProfile } from "./smoke.js";
|
|
15
|
+
export type { SmokeRemoteProfileOptions, SmokeRemoteProfileResult, } from "./smoke.js";
|
|
16
|
+
export { migrateForward, migrateBack } from "./migrate.js";
|
|
17
|
+
export type { MigrateForwardOptions, MigrateForwardResult, MigrateBackOptions, MigrateBackResult, } from "./migrate.js";
|
|
18
|
+
export { pluginAdd, pluginAddInstaller, pluginLs, pluginSync, pluginSyncCheck, reconcileSessionPlugins, parseMcpSpec, parseMcpSpecs, detectMcpBins, upsertCodexMcpServer, mergeClaudeMcpServers, buildPodSyncScript, } from "./plugin.js";
|
|
19
|
+
/** Validate `--watch <minutes>`: a whole number of minutes >= 1. */
|
|
20
|
+
export declare function parseWatchMinutes(raw: string): number;
|
|
21
|
+
/**
|
|
22
|
+
* Soft-refresh EVERY live remote session (profile carried by each session).
|
|
23
|
+
* Per-session errors don't stop the pass; ends with a recap (ok / unchanged /
|
|
24
|
+
* failed) and returns the failure count. `hashes` carries the previous pass's
|
|
25
|
+
* bundle hashes (sessionId -> sha256) so unchanged creds are a no-op WITHOUT
|
|
26
|
+
* respawning the Pod CLI.
|
|
27
|
+
*/
|
|
28
|
+
export declare function softRefreshAllSessions(url: string, opts: {
|
|
29
|
+
authRefresh?: boolean;
|
|
30
|
+
}, hashes: Map<string, string>): Promise<{
|
|
31
|
+
failed: number;
|
|
32
|
+
}>;
|
|
33
|
+
/**
|
|
34
|
+
* Foreground refresh loop for `--watch <minutes>`: pass, sleep, repeat. NO
|
|
35
|
+
* daemonization, no pid file — the user runs it in a dedicated tmux window.
|
|
36
|
+
* Each pass is timestamped on stderr; SIGINT (Ctrl-C) stops it cleanly with a
|
|
37
|
+
* message and exit 0. Pass failures are logged and the loop keeps going.
|
|
38
|
+
*/
|
|
39
|
+
export declare function watchRefreshLoop(minutes: number, pass: () => Promise<{
|
|
40
|
+
failed: number;
|
|
41
|
+
}>, signals?: {
|
|
42
|
+
on(event: "SIGINT", listener: () => void): unknown;
|
|
43
|
+
removeListener(event: "SIGINT", listener: () => void): unknown;
|
|
44
|
+
}): Promise<number>;
|
|
45
|
+
/**
|
|
46
|
+
* P4 — the conductor's FOREGROUND watch loop (NO daemon, NO pid file — run it in
|
|
47
|
+
* a dedicated tmux window, exactly like `watchRefreshLoop` / `h2a bridge
|
|
48
|
+
* --watch`). Each pass is timestamped; SIGINT (Ctrl-C) stops it cleanly with a
|
|
49
|
+
* message and exit 0. A pass failure is logged and the loop keeps going. The
|
|
50
|
+
* `pass` is the conductor pass (reconcile + start `pending` jobs under the cap);
|
|
51
|
+
* `signals` is injectable so tests never emit a real SIGINT.
|
|
52
|
+
*/
|
|
53
|
+
export declare function conductLoop(minutes: number, pass: () => Promise<{
|
|
54
|
+
started: number;
|
|
55
|
+
finished: number;
|
|
56
|
+
}>, signals?: {
|
|
57
|
+
on(event: "SIGINT", listener: () => void): unknown;
|
|
58
|
+
removeListener(event: "SIGINT", listener: () => void): unknown;
|
|
59
|
+
}): Promise<number>;
|
|
60
|
+
export type StartJobResult = {
|
|
61
|
+
started: true;
|
|
62
|
+
target: "local" | "remote";
|
|
63
|
+
detail: string;
|
|
64
|
+
} | {
|
|
65
|
+
started: false;
|
|
66
|
+
error: string;
|
|
67
|
+
};
|
|
68
|
+
/**
|
|
69
|
+
* Launch a job that is enrolled in the registry (typically `pending`): spawn the
|
|
70
|
+
* agent (local detached tmux, or a Pod), advance the registry entry to
|
|
71
|
+
* `running`, mirror it under track (best-effort), and propagate the spawn-depth
|
|
72
|
+
* budget to the child via `REMOTE_DELEGATE_DEPTH`. The launch params are read
|
|
73
|
+
* from the entry's queued-launch fields (tool/task/headless/remoteTarget/
|
|
74
|
+
* originCwd/explicitCwd/depthBudget/trackWp), set at `delegate` time.
|
|
75
|
+
*
|
|
76
|
+
* Never throws: any spawn/registry error is returned as `{started:false}` so the
|
|
77
|
+
* conductor loop keeps going. For remote, the per-job workspace is created here
|
|
78
|
+
* (so a queued remote job doesn't hold a workspace while waiting).
|
|
79
|
+
*/
|
|
80
|
+
export declare function startJob(job: RegistryEntry): Promise<StartJobResult>;
|
|
81
|
+
/**
|
|
82
|
+
* Reliability slice 1 — RESUME a throttled HEADLESS LOCAL job. Relaunch the SAME
|
|
83
|
+
* job in the SAME `runCwd` it already ran in (its recorded `cwd` — NOT a fresh
|
|
84
|
+
* worktree, so it continues the prior conversation in place) with the tool's
|
|
85
|
+
* CONTINUE flag (`claude -p --continue` / `codex exec resume --last`, via the
|
|
86
|
+
* safe argv `buildThrottleResumeArgs` — never `bash -lc` concat), redirecting to
|
|
87
|
+
* the SAME result.json/output.log, then transition `throttled → running`. The
|
|
88
|
+
* throttle bookkeeping is PRESERVED so a re-throttle bumps `attempts` (the cap is
|
|
89
|
+
* enforced in reconcile). Never throws — a spawn error is returned as
|
|
90
|
+
* `{started:false}` so the conductor keeps going.
|
|
91
|
+
*
|
|
92
|
+
* SCOPE: headless local only. Interactive resume (send-keys) and remote resume
|
|
93
|
+
* (control-plane) are phase 2 — see the TODOs at the call site.
|
|
94
|
+
*/
|
|
95
|
+
export declare function resumeThrottledJob(job: RegistryEntry): StartJobResult;
|
|
96
|
+
export declare function main(argv: ReadonlyArray<string>): Promise<number>;
|
|
97
|
+
export { main as dispatch };
|
|
98
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|