sessionpipe 0.1.1 → 0.2.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.
@@ -0,0 +1,4 @@
1
+ export declare const CONTROL_HELP = " sessionpipe control pair <receiver> [--folder DIR]\u2026 [--mode safe|auto] [--token T] [--name N] [--no-service]\n sessionpipe control status | keys [remove <id>] | off [<receiver>] | run\n sessionpipe wait [--session <harness>:<id>] (a session runs this in the background)";
2
+ export declare function controlMain(argv: string[], out: (s?: string) => void, distDir: string): Promise<void>;
3
+ export declare function waitMain(argv: string[], out: (s?: string) => void): Promise<void>;
4
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../src/control/cli.ts"],"names":[],"mappings":"AAgBA,eAAO,MAAM,YAAY,gSAEkE,CAAC;AAE5F,wBAAsB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAyE3G;AAwCD,wBAAsB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CASvF"}
@@ -0,0 +1,172 @@
1
+ import { claudeControl } from "@sessionpipe/core";
2
+ import { type ControlConfig, type PairedReceiver } from "./store.js";
3
+ type Caps = claudeControl.Caps;
4
+ type RunResult = claudeControl.RunResult;
5
+ /** The message as the receiver carried it (control.json). */
6
+ export interface Carried {
7
+ id: string;
8
+ cmd: string;
9
+ csig?: string | null;
10
+ grant?: {
11
+ str: string;
12
+ cred: string;
13
+ ad: string;
14
+ cd: string;
15
+ sig: string;
16
+ } | null;
17
+ confirm?: {
18
+ cred: string;
19
+ ad: string;
20
+ cd: string;
21
+ sig: string;
22
+ } | null;
23
+ at: string;
24
+ expires_at: string;
25
+ }
26
+ /** A session the Agent SDK holds for the daemon (sdk.ts; a fake in tests). */
27
+ export interface SdkSession {
28
+ /** Deliver one message; resolves with the agent's reply when the turn ends. */
29
+ send(text: string): Promise<{
30
+ reply: string;
31
+ error?: string;
32
+ }>;
33
+ interrupt(): Promise<void>;
34
+ close(): void;
35
+ readonly busy: boolean;
36
+ readonly lastUsed: number;
37
+ }
38
+ export interface SdkHost {
39
+ start(o: {
40
+ session: string;
41
+ cwd: string;
42
+ mode: "safe" | "auto";
43
+ configDir?: string;
44
+ }): SdkSession;
45
+ }
46
+ export interface DaemonDeps {
47
+ now: () => number;
48
+ fetch: typeof fetch;
49
+ log: (line: string) => void;
50
+ claudeDirs: () => string[];
51
+ claude: () => {
52
+ bin: string;
53
+ caps: Caps;
54
+ } | null;
55
+ run: (bin: string, args: string[], o: {
56
+ cwd: string;
57
+ env: NodeJS.ProcessEnv;
58
+ timeoutMs?: number;
59
+ }) => Promise<RunResult>;
60
+ sdk?: SdkHost | null;
61
+ /** How long to follow a live session's turn for an in-place answer (tests shorten it). */
62
+ answerWaitMs?: number;
63
+ /** Close an SDK session idle this long (a held session costs ~300 MB). */
64
+ sdkIdleMs?: number;
65
+ env?: NodeJS.ProcessEnv;
66
+ }
67
+ export interface Status {
68
+ version: string;
69
+ name: string;
70
+ receivers: {
71
+ url: string;
72
+ machine: string;
73
+ keys: number;
74
+ polling: boolean;
75
+ last_poll: number | null;
76
+ last_error: string | null;
77
+ }[];
78
+ waiting: string[];
79
+ mid_turn: string[];
80
+ attentions: {
81
+ session: string;
82
+ id: string;
83
+ }[];
84
+ sdk: string[];
85
+ nonces: number;
86
+ acks_pending: number;
87
+ delivered: Record<string, number>;
88
+ }
89
+ /** The machine's own caps (CONTROL.md §6 `start`): every command is the owner's,
90
+ * signed, but a burst of them must not start a crowd of agents. A start beyond the
91
+ * caps is refused `start_limit`; a headless run beyond `headless` waits its turn. */
92
+ export declare const LIMITS: {
93
+ readonly startConcurrent: 2;
94
+ readonly startPerHour: 10;
95
+ readonly headless: 3;
96
+ };
97
+ export declare class ControlDaemon {
98
+ private cfg;
99
+ private readonly dir;
100
+ private readonly sock;
101
+ private readonly deps;
102
+ private server;
103
+ private readonly live;
104
+ private readonly sdkSessions;
105
+ private readonly nonces;
106
+ private readonly acks;
107
+ private readonly recentAcks;
108
+ private readonly inflight;
109
+ private readonly queues;
110
+ private readonly lastEnd;
111
+ private state;
112
+ private running;
113
+ private readonly polling;
114
+ private readonly delivered;
115
+ private helloTimer;
116
+ private helloSoon;
117
+ private readonly aborts;
118
+ private startsRunning;
119
+ private startTimes;
120
+ private procs;
121
+ private readonly procWaiters;
122
+ constructor(cfg: ControlConfig, dir: string, sock: string, deps: DaemonDeps);
123
+ start(): Promise<void>;
124
+ stop(): Promise<void>;
125
+ reload(cfg: ControlConfig): void;
126
+ status(): Status;
127
+ private listen;
128
+ private onConn;
129
+ private reply;
130
+ private liveOf;
131
+ private onRequest;
132
+ /** A queued turn message that hasn't expired. */
133
+ private takeTurn;
134
+ private onEvent;
135
+ private pollLoop;
136
+ private scheduleHello;
137
+ private helloAll;
138
+ private post;
139
+ private finish;
140
+ private flushing;
141
+ private flushAcks;
142
+ handle(r: PairedReceiver, m: Carried): Promise<void>;
143
+ /** Where the session lives, and whether its folder is allowed — read from the
144
+ * session's own transcript (Claude Code) or the hooks' cwd, never the command. */
145
+ private place;
146
+ private allowed;
147
+ private permission;
148
+ private cancel;
149
+ private view;
150
+ private prompt;
151
+ /** A turn message whose Stop never took it: route again, now that it isn't mid-turn. */
152
+ private reroute;
153
+ /** Jobs for one session (and its copy) run one at a time, in order. */
154
+ private serial;
155
+ private headless;
156
+ private outcome;
157
+ /** Where the session's transcript ends right now: its answer starts after this. */
158
+ private answerFrom;
159
+ /** A message handed to a live session (a waiter, a Stop block) is `taken` now; the
160
+ * session answers in its own turn, which the daemon reads from the transcript and
161
+ * sends back as the final ack's `reply` — so the person sees the answer where they
162
+ * asked. A turn that doesn't end within ANSWER_WAIT_MS is acked delivered without. */
163
+ private followAnswer;
164
+ private limits;
165
+ /** One headless Claude Code process at a time per slot; the rest wait in order. */
166
+ private slot;
167
+ private startSession;
168
+ private startOne;
169
+ private closeIdleSdk;
170
+ }
171
+ export {};
172
+ //# sourceMappingURL=daemon.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"daemon.d.ts","sourceRoot":"","sources":["../../src/control/daemon.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,63 @@
1
+ export type LocalRequest =
2
+ /** A waiter parks until a message for its session arrives. */
3
+ {
4
+ op: "wait";
5
+ session: string;
6
+ pid?: number;
7
+ }
8
+ /** A Stop hook asks for a queued message (turn mode). */
9
+ | {
10
+ op: "stop";
11
+ session: string;
12
+ active: boolean;
13
+ }
14
+ /** A PermissionRequest hook parks until a verified answer, or until it's released. */
15
+ | {
16
+ op: "permission";
17
+ session: string;
18
+ attention: string;
19
+ tool?: string;
20
+ }
21
+ /** The worker reports a hook event: turn state and attention closure. */
22
+ | {
23
+ op: "event";
24
+ session: string;
25
+ event: string;
26
+ transcript?: string;
27
+ cwd?: string;
28
+ }
29
+ /** `sessionpipe control status`. */
30
+ | {
31
+ op: "status";
32
+ };
33
+ export type LocalReply = {
34
+ op: "message";
35
+ text: string;
36
+ id: string;
37
+ } | {
38
+ op: "block";
39
+ reason: string;
40
+ id: string;
41
+ } | {
42
+ op: "decision";
43
+ behavior: "allow" | "deny";
44
+ message?: string;
45
+ id: string;
46
+ } | {
47
+ op: "none";
48
+ }
49
+ /** A newer waiter for the same session took over; this one stops. */
50
+ | {
51
+ op: "replaced";
52
+ } | {
53
+ op: "ok";
54
+ } | {
55
+ op: "status";
56
+ status: unknown;
57
+ };
58
+ /** Where the daemon listens. `SESSIONPIPE_SOCKET` overrides (tests). */
59
+ export declare function socketPath(state: string, env?: NodeJS.ProcessEnv): string;
60
+ /** One request, one reply. `timeoutMs` bounds the whole exchange; null on any
61
+ * failure (no daemon, a timeout, a garbled reply) — callers treat that as "none". */
62
+ export declare function ask(sock: string, req: LocalRequest, timeoutMs: number): Promise<LocalReply | null>;
63
+ //# sourceMappingURL=local.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"local.d.ts","sourceRoot":"","sources":["../../src/control/local.ts"],"names":[],"mappings":"AAUA,MAAM,MAAM,YAAY;AACtB,8DAA8D;AAC5D;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE;AAC/C,yDAAyD;GACvD;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,OAAO,CAAA;CAAE;AAClD,sFAAsF;GACpF;IAAE,EAAE,EAAE,YAAY,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE;AACzE,yEAAyE;GACvE;IAAE,EAAE,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE;AACpF,oCAAoC;GAClC;IAAE,EAAE,EAAE,QAAQ,CAAA;CAAE,CAAC;AAErB,MAAM,MAAM,UAAU,GAClB;IAAE,EAAE,EAAE,SAAS,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,GAC3C;IAAE,EAAE,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,GAC3C;IAAE,EAAE,EAAE,UAAU,CAAC;IAAC,QAAQ,EAAE,OAAO,GAAG,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,GAC5E;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE;AAChB,qEAAqE;GACnE;IAAE,EAAE,EAAE,UAAU,CAAA;CAAE,GAClB;IAAE,EAAE,EAAE,IAAI,CAAA;CAAE,GACZ;IAAE,EAAE,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,OAAO,CAAA;CAAE,CAAC;AAEtC,wEAAwE;AACxE,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,MAAM,CAItF;AAED;sFACsF;AACtF,wBAAgB,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CA2BlG"}
@@ -0,0 +1,50 @@
1
+ import { type ControlConfig } from "./store.js";
2
+ export interface PairOptions {
3
+ url: string;
4
+ /** The person's sink token for this receiver (from `sink add`, or --token). */
5
+ token: string;
6
+ folders: string[];
7
+ mode: "safe" | "auto";
8
+ name: string;
9
+ out: (s: string) => void;
10
+ fetch?: typeof fetch;
11
+ sleep?: (ms: number) => Promise<void>;
12
+ env?: NodeJS.ProcessEnv;
13
+ }
14
+ export declare function discover(url: string, f: typeof fetch): Promise<{
15
+ control: string;
16
+ rpId: string;
17
+ }>;
18
+ /** Pair (or add a key to) this machine with one receiver. Returns the saved config. */
19
+ export declare function pair(o: PairOptions): Promise<ControlConfig>;
20
+ /** `control off [<receiver>]`: tell the receiver, forget its token and keys. */
21
+ export declare function off(o: {
22
+ url?: string;
23
+ out: (s: string) => void;
24
+ fetch?: typeof fetch;
25
+ env?: NodeJS.ProcessEnv;
26
+ }): Promise<void | ControlConfig>;
27
+ export declare function removeKey(id: string, env?: NodeJS.ProcessEnv): number;
28
+ export declare function serviceFiles(o: {
29
+ node: string;
30
+ cli: string;
31
+ logDir: string;
32
+ home?: string;
33
+ }): {
34
+ plist: {
35
+ file: string;
36
+ body: string;
37
+ };
38
+ unit: {
39
+ file: string;
40
+ body: string;
41
+ };
42
+ };
43
+ /** Write and start the service for this platform. Returns what it did, in words. */
44
+ export declare function installService(o: {
45
+ node: string;
46
+ cli: string;
47
+ env?: NodeJS.ProcessEnv;
48
+ }): string;
49
+ export declare function uninstallService(env?: NodeJS.ProcessEnv): void;
50
+ //# sourceMappingURL=pair.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pair.d.ts","sourceRoot":"","sources":["../../src/control/pair.ts"],"names":[],"mappings":"AAWA,OAAO,EAAE,KAAK,aAAa,EAAgE,MAAM,YAAY,CAAC;AAK9G,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,+EAA+E;IAC/E,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IACzB,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;IACrB,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACtC,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;CACzB;AAUD,wBAAsB,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,CAAC,EAAE,OAAO,KAAK,GAAG,OAAO,CAAC;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,CAavG;AAED,uFAAuF;AACvF,wBAAsB,IAAI,CAAC,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,aAAa,CAAC,CAkEjE;AAED,gFAAgF;AAChF,wBAAsB,GAAG,CAAC,CAAC,EAAE;IAC3B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IACzB,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;IACrB,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;CACzB,iCAYA;AAED,wBAAgB,SAAS,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,GAAG,MAAM,CAWrE;AAOD,wBAAgB,YAAY,CAAC,CAAC,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE;;;;;;;;;EAmC3F;AAED,oFAAoF;AACpF,wBAAgB,cAAc,CAAC,CAAC,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAA;CAAE,GAAG,MAAM,CA2BhG;AAED,wBAAgB,gBAAgB,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,GAAG,IAAI,CAgB9D"}
@@ -0,0 +1,19 @@
1
+ /** The transcript's size now: where its answer will start. */
2
+ export declare function transcriptEnd(file: string): number;
3
+ /** Claude Code records after the delivery → the turn's answer once the turn ended
4
+ * (an assistant record with `stop_reason: "end_turn"`), else null. The answer is the
5
+ * turn's assistant text, in order; sidechains (subagents) are not the session's words. */
6
+ export declare function answerIn(lines: string[]): {
7
+ done: boolean;
8
+ text: string;
9
+ };
10
+ /** Wait for the turn that follows a delivery to end, reading the transcript from
11
+ * `from` (its size at delivery). Resolves the answer, or null when the turn doesn't
12
+ * end within `timeoutMs` (an interrupted turn, a session closed mid-answer). */
13
+ export declare function awaitAnswer(file: string, from: number, o: {
14
+ timeoutMs: number;
15
+ pollMs?: number;
16
+ now?: () => number;
17
+ sleep?: (ms: number) => Promise<void>;
18
+ }): Promise<string | null>;
19
+ //# sourceMappingURL=reply.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reply.d.ts","sourceRoot":"","sources":["../../src/control/reply.ts"],"names":[],"mappings":"AASA,8DAA8D;AAC9D,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAMlD;AAED;;2FAE2F;AAC3F,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAqBzE;AAED;;iFAEiF;AACjF,wBAAsB,WAAW,CAC/B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,CAAC,EAAE;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;CAAE,GACnG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CA6BxB"}
@@ -0,0 +1,41 @@
1
+ import type { ControlKind } from "@sessionpipe/core/control";
2
+ export type DeliveryMode = "sdk" | "waiter" | "turn" | "api" | "resume" | "fork";
3
+ export interface SessionView {
4
+ harness: string;
5
+ /** The daemon holds this session through the Agent SDK right now. */
6
+ sdk: boolean;
7
+ /** A `sessionpipe wait` for it is parked on the socket. */
8
+ waiter: boolean;
9
+ /** Between a turn's start and its Stop (the Stop hook will ask). */
10
+ midTurn: boolean;
11
+ /** A live process holds the session (registry entry or a fresh transcript write). */
12
+ live: boolean;
13
+ /** The transcript exists on this machine. */
14
+ known: boolean;
15
+ /** This Claude Code can copy a session (`--fork-session`). */
16
+ canFork: boolean;
17
+ /** The harness has an input API (OpenCode, Codex app-server) — none is wired yet. */
18
+ api: boolean;
19
+ }
20
+ export type Route = {
21
+ mode: DeliveryMode;
22
+ } | {
23
+ unsupported: string;
24
+ } | {
25
+ refused: "no_session";
26
+ };
27
+ /** Where a prompt (or a start) goes. Never two writers on one transcript: a live
28
+ * session gets a fork, never a resume. */
29
+ export declare function routePrompt(s: SessionView): Route;
30
+ /** How each kind is delivered, before the session's state is looked at. */
31
+ export declare function kindPath(kind: ControlKind, harness: string): "prompt" | "permission" | "cancel" | "start" | {
32
+ unsupported: string;
33
+ };
34
+ /** The framed text a session receives (CONTROL.md §6): the model and the person can
35
+ * tell where it came from. */
36
+ /** Said with every message that reaches a live session: the answer goes back by
37
+ * itself (the daemon reads it from the transcript), so the model never invents a
38
+ * command to reply with (a real session tried \`sessionpipe send\`, 2026-10-01). */
39
+ export declare const IN_PLACE_ANSWER = "Answer it right here, as you would answer the person at the keyboard: your reply is sent back to them automatically. Don't run a command or call a tool just to send it.";
40
+ export declare function frame(text: string, receiver: string): string;
41
+ //# sourceMappingURL=route.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"route.d.ts","sourceRoot":"","sources":["../../src/control/route.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAE7D,MAAM,MAAM,YAAY,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;AAEjF,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,qEAAqE;IACrE,GAAG,EAAE,OAAO,CAAC;IACb,2DAA2D;IAC3D,MAAM,EAAE,OAAO,CAAC;IAChB,oEAAoE;IACpE,OAAO,EAAE,OAAO,CAAC;IACjB,qFAAqF;IACrF,IAAI,EAAE,OAAO,CAAC;IACd,6CAA6C;IAC7C,KAAK,EAAE,OAAO,CAAC;IACf,8DAA8D;IAC9D,OAAO,EAAE,OAAO,CAAC;IACjB,qFAAqF;IACrF,GAAG,EAAE,OAAO,CAAC;CACd;AAED,MAAM,MAAM,KAAK,GAAG;IAAE,IAAI,EAAE,YAAY,CAAA;CAAE,GAAG;IAAE,WAAW,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,OAAO,EAAE,YAAY,CAAA;CAAE,CAAC;AAEjG;2CAC2C;AAC3C,wBAAgB,WAAW,CAAC,CAAC,EAAE,WAAW,GAAG,KAAK,CAUjD;AAED,2EAA2E;AAC3E,wBAAgB,QAAQ,CACtB,IAAI,EAAE,WAAW,EACjB,OAAO,EAAE,MAAM,GACd,QAAQ,GAAG,YAAY,GAAG,QAAQ,GAAG,OAAO,GAAG;IAAE,WAAW,EAAE,MAAM,CAAA;CAAE,CAaxE;AAED;+BAC+B;AAC/B;;qFAEqF;AACrF,eAAO,MAAM,eAAe,6KACgJ,CAAC;AAE7K,wBAAgB,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAM5D"}
@@ -0,0 +1,7 @@
1
+ import type { SdkHost } from "./daemon.js";
2
+ export declare function loadSdk(o: {
3
+ claudeBin: string | null;
4
+ env?: NodeJS.ProcessEnv;
5
+ log: (s: string) => void;
6
+ }): Promise<SdkHost | null>;
7
+ //# sourceMappingURL=sdk.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sdk.d.ts","sourceRoot":"","sources":["../../src/control/sdk.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,OAAO,EAAc,MAAM,aAAa,CAAC;AAQvD,wBAAsB,OAAO,CAAC,CAAC,EAAE;IAC/B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,GAAG,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;CAC1B,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAwF1B"}
@@ -0,0 +1,76 @@
1
+ import { type TrustedKey } from "@sessionpipe/core/control";
2
+ export interface PairedReceiver {
3
+ /** The receiver's origin, as the person typed it. */
4
+ url: string;
5
+ /** Absolute control endpoint (from the well-known file). */
6
+ control: string;
7
+ rpId: string;
8
+ machine: string;
9
+ /** The machine's own bearer (polls, acks, hello, off): opens nothing else. */
10
+ token: string;
11
+ keys: (TrustedKey & {
12
+ name?: string;
13
+ added_at: string;
14
+ })[];
15
+ paired_at: string;
16
+ }
17
+ export interface ControlConfig {
18
+ name: string;
19
+ /** Folders sessions may run in; compared on whole path segments, real paths. */
20
+ folders: string[];
21
+ mode: "safe" | "auto";
22
+ receivers: PairedReceiver[];
23
+ /** Caps this machine keeps whatever a receiver sends (defaults in daemon.ts,
24
+ * LIMITS): new sessions running at once and per hour, headless Claude Code runs
25
+ * at once. */
26
+ limits?: {
27
+ start_concurrent?: number;
28
+ start_per_hour?: number;
29
+ headless?: number;
30
+ };
31
+ }
32
+ export declare const controlFile: (env?: NodeJS.ProcessEnv) => string;
33
+ export declare const controlState: (env?: NodeJS.ProcessEnv) => string;
34
+ export declare function readControl(env?: NodeJS.ProcessEnv): ControlConfig | null;
35
+ export declare const writeControl: (c: ControlConfig, env?: NodeJS.ProcessEnv) => void;
36
+ /** Nonces the machine has run, for NONCE_TTL_MS. `seen()` records a new nonce
37
+ * durably BEFORE it answers false; a write that fails throws, and the verifier
38
+ * refuses the command (`nonce_store`). */
39
+ export declare class NonceStore {
40
+ private readonly file;
41
+ private readonly now;
42
+ private readonly seenAt;
43
+ constructor(file: string, now?: () => number);
44
+ seen: (nonce: string) => boolean;
45
+ /** Rewrite the file with only the live window (on start, and daily). */
46
+ compact(): void;
47
+ get size(): number;
48
+ }
49
+ export interface Ack {
50
+ receiver: string;
51
+ id: string;
52
+ outcome: "taken" | "delivered" | "expired" | "unsupported" | "failed" | "refused";
53
+ at: string;
54
+ mode?: string;
55
+ session?: string;
56
+ code?: string;
57
+ detail?: string;
58
+ reply?: string;
59
+ }
60
+ /** Acks not yet accepted: appended before they are sent, rewritten when sent, so a
61
+ * daemon restart re-sends rather than forgets (every message is acked exactly once
62
+ * with a final outcome; a duplicate is tolerated by the receiver). */
63
+ export declare class AckOutbox {
64
+ private readonly file;
65
+ constructor(file: string);
66
+ all(): Ack[];
67
+ add(a: Ack): void;
68
+ keep(left: Ack[]): void;
69
+ }
70
+ export interface DaemonState {
71
+ /** Original session → the fork its messages go to. */
72
+ copies: Record<string, string>;
73
+ }
74
+ export declare function readState(file: string): DaemonState;
75
+ export declare const writeState: (file: string, s: DaemonState) => void;
76
+ //# sourceMappingURL=store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/control/store.ts"],"names":[],"mappings":"AAsBA,OAAO,EAAgB,KAAK,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAE1E,MAAM,WAAW,cAAc;IAC7B,qDAAqD;IACrD,GAAG,EAAE,MAAM,CAAC;IACZ,4DAA4D;IAC5D,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,8EAA8E;IAC9E,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,CAAC,UAAU,GAAG;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC,EAAE,CAAC;IAC3D,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,gFAAgF;IAChF,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IACtB,SAAS,EAAE,cAAc,EAAE,CAAC;IAC5B;;mBAEe;IACf,MAAM,CAAC,EAAE;QAAE,gBAAgB,CAAC,EAAE,MAAM,CAAC;QAAC,cAAc,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CACpF;AAED,eAAO,MAAM,WAAW,GAAI,MAAK,MAAM,CAAC,UAAwB,WACN,CAAC;AAC3D,eAAO,MAAM,YAAY,GAAI,MAAK,MAAM,CAAC,UAAwB,WAAwC,CAAC;AAY1G,wBAAgB,WAAW,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,aAAa,GAAG,IAAI,CAatF;AAED,eAAO,MAAM,YAAY,GAAI,GAAG,aAAa,EAAE,MAAK,MAAM,CAAC,UAAwB,SAChD,CAAC;AAEpC;;2CAE2C;AAC3C,qBAAa,UAAU;IAGnB,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,GAAG;IAHtB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA6B;gBAEjC,IAAI,EAAE,MAAM,EACZ,GAAG,GAAE,MAAM,MAAiB;IAiB/C,IAAI,GAAI,OAAO,MAAM,KAAG,OAAO,CAc7B;IAEF,wEAAwE;IACxE,OAAO,IAAI,IAAI;IAUf,IAAI,IAAI,IAAI,MAAM,CAEjB;CACF;AAED,MAAM,WAAW,GAAG;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,OAAO,GAAG,WAAW,GAAG,SAAS,GAAG,aAAa,GAAG,QAAQ,GAAG,SAAS,CAAC;IAClF,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;uEAEuE;AACvE,qBAAa,SAAS;IACR,OAAO,CAAC,QAAQ,CAAC,IAAI;gBAAJ,IAAI,EAAE,MAAM;IACzC,GAAG,IAAI,GAAG,EAAE;IAUZ,GAAG,CAAC,CAAC,EAAE,GAAG,GAAG,IAAI;IAIjB,IAAI,CAAC,IAAI,EAAE,GAAG,EAAE,GAAG,IAAI;CAOxB;AAED,MAAM,WAAW,WAAW;IAC1B,sDAAsD;IACtD,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAChC;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,CAOnD;AACD,eAAO,MAAM,UAAU,GAAI,MAAM,MAAM,EAAE,GAAG,WAAW,SAA0B,CAAC"}
@@ -0,0 +1,8 @@
1
+ export declare const RE_ARM = "(Answer it right here, as you would answer the person at the keyboard: your reply is sent back to them automatically, so don't run a command or call a tool just to send it. Then, without mentioning it, start `sessionpipe wait` again as a background command, with the longest timeout your harness allows, to keep receiving messages.)";
2
+ /** The session to wait for: --session, else the harness's own env. */
3
+ export declare function waitSession(flag: string | undefined, env?: NodeJS.ProcessEnv): string | null;
4
+ export declare function waitForMessage(session: string, o?: {
5
+ env?: NodeJS.ProcessEnv;
6
+ retryMs?: number;
7
+ }): Promise<string>;
8
+ //# sourceMappingURL=wait.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wait.d.ts","sourceRoot":"","sources":["../../src/control/wait.ts"],"names":[],"mappings":"AASA,eAAO,MAAM,MAAM,iVAC6T,CAAC;AAEjV,sEAAsE;AACtE,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,EAAE,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,MAAM,GAAG,IAAI,CAKzG;AAED,wBAAsB,cAAc,CAClC,OAAO,EAAE,MAAM,EACf,CAAC,GAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAO,GACpD,OAAO,CAAC,MAAM,CAAC,CAUjB"}