@stringsync/splates 0.0.0-stage → 0.0.45

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.
Files changed (251) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +308 -2
  3. package/acp-extensions.d.ts +82 -0
  4. package/add.d.ts +53 -0
  5. package/adherence.d.ts +49 -0
  6. package/age.d.ts +2 -0
  7. package/agent-connection.d.ts +171 -0
  8. package/agent-errors.d.ts +2 -0
  9. package/agent-process.d.ts +65 -0
  10. package/agent-stream.d.ts +91 -0
  11. package/agents.d.ts +15 -0
  12. package/artifact-room.d.ts +47 -0
  13. package/artifact-viewers.d.ts +6 -0
  14. package/artifact.d.ts +30 -0
  15. package/artifacts.d.ts +161 -0
  16. package/asked.d.ts +146 -0
  17. package/asked.js +7 -0
  18. package/assumed.d.ts +34 -0
  19. package/attach.d.ts +30 -0
  20. package/attachments.d.ts +43 -0
  21. package/baselines.d.ts +55 -0
  22. package/claim.d.ts +17 -0
  23. package/cloudflare-api.d.ts +25 -0
  24. package/cloudflare-error.d.ts +5 -0
  25. package/cloudflared-tunnel-provider.d.ts +27 -0
  26. package/codex-limits.d.ts +19 -0
  27. package/compaction.d.ts +17 -0
  28. package/config-source.d.ts +13 -0
  29. package/config.d.ts +98 -0
  30. package/config.js +7 -0
  31. package/conversation-archive.d.ts +6 -0
  32. package/conversation-background.d.ts +21 -0
  33. package/conversation-choice.d.ts +4 -0
  34. package/conversation-files.d.ts +51 -0
  35. package/conversation-given.d.ts +8 -0
  36. package/conversation-host.d.ts +102 -0
  37. package/conversation-listing.d.ts +15 -0
  38. package/conversation-log.d.ts +301 -0
  39. package/conversation-messages.d.ts +18 -0
  40. package/conversation-name.d.ts +28 -0
  41. package/conversation-options.d.ts +73 -0
  42. package/conversation-reference.d.ts +2 -0
  43. package/conversation-state.d.ts +472 -0
  44. package/conversation-turns.d.ts +115 -0
  45. package/conversation.d.ts +209 -0
  46. package/conversations.d.ts +140 -0
  47. package/coordinator-state.d.ts +28 -0
  48. package/creds.d.ts +123 -0
  49. package/darwin-temperature.d.ts +7 -0
  50. package/decided.d.ts +18 -0
  51. package/demo.d.ts +29 -0
  52. package/dev/api.d.ts +209 -0
  53. package/dev/assets.d.ts +24 -0
  54. package/dev/conversation-address.d.ts +47 -0
  55. package/dev/conversation-feed.d.ts +166 -0
  56. package/dev/live-stream.d.ts +29 -0
  57. package/dev/worker.d.ts +33 -0
  58. package/dev-access.d.ts +61 -0
  59. package/dev-artifacts.d.ts +22 -0
  60. package/dev-budgets.d.ts +18 -0
  61. package/dev-conversation-events.d.ts +39 -0
  62. package/dev-conversation-routes.d.ts +90 -0
  63. package/dev-creds.d.ts +17 -0
  64. package/dev-dictation-connection.d.ts +18 -0
  65. package/dev-dictation.d.ts +17 -0
  66. package/dev-http.d.ts +76 -0
  67. package/dev-open.d.ts +15 -0
  68. package/dev-options.d.ts +133 -0
  69. package/dev-outcomes.d.ts +21 -0
  70. package/dev-pages.d.ts +16 -0
  71. package/dev-picker.d.ts +57 -0
  72. package/dev-port-socket.d.ts +26 -0
  73. package/dev-ports.d.ts +11 -0
  74. package/dev-project-watch.d.ts +44 -0
  75. package/dev-project.d.ts +168 -0
  76. package/dev-push.d.ts +59 -0
  77. package/dev-restart.d.ts +141 -0
  78. package/dev-servers.d.ts +30 -0
  79. package/dev-setup-native.d.ts +23 -0
  80. package/dev-setup-opencode.d.ts +29 -0
  81. package/dev-setup.d.ts +16 -0
  82. package/dev-shell.d.ts +70 -0
  83. package/dev-slot.d.ts +28 -0
  84. package/dev-sockets.d.ts +13 -0
  85. package/dev-task-ports.d.ts +73 -0
  86. package/dev-tasks.d.ts +20 -0
  87. package/dev-terminal-connection.d.ts +19 -0
  88. package/dev-terminal.d.ts +42 -0
  89. package/dev-todos.d.ts +35 -0
  90. package/dev-transcriber.d.ts +15 -0
  91. package/dev-tunnel.d.ts +38 -0
  92. package/dev-versions.d.ts +53 -0
  93. package/dev-workers.d.ts +55 -0
  94. package/dev.d.ts +24 -0
  95. package/diff.d.ts +29 -0
  96. package/dispatched.d.ts +16 -0
  97. package/editors.d.ts +22 -0
  98. package/escalate.d.ts +27 -0
  99. package/events.d.ts +311 -0
  100. package/everywhere.d.ts +41 -0
  101. package/exhibit.d.ts +21 -0
  102. package/exit.d.ts +54 -0
  103. package/fact-todos.d.ts +18 -0
  104. package/fake-agent.d.ts +107 -0
  105. package/fake-agent.js +284 -0
  106. package/finding-facts.d.ts +38 -0
  107. package/findings.d.ts +84 -0
  108. package/fold.d.ts +43 -0
  109. package/follow.d.ts +41 -0
  110. package/format.d.ts +68 -0
  111. package/git.d.ts +112 -0
  112. package/handed-out.d.ts +79 -0
  113. package/handoff.d.ts +23 -0
  114. package/heavy-run.d.ts +12 -0
  115. package/heavy.d.ts +28 -0
  116. package/heavy.js +16 -0
  117. package/held.d.ts +22 -0
  118. package/index-0tfvy823.js +272 -0
  119. package/index-43pq33n4.js +157 -0
  120. package/index-4776671w.js +698 -0
  121. package/index-d4hjrzaq.js +464 -0
  122. package/index-gcfq7awr.js +740 -0
  123. package/index-k73tsw5g.js +101 -0
  124. package/index-kaa44k7q.js +59 -0
  125. package/index-vcv9m93e.js +1265 -0
  126. package/index-xmfmy729.js +249 -0
  127. package/index.d.ts +2 -0
  128. package/index.js +26928 -0
  129. package/init.d.ts +28 -0
  130. package/journal.d.ts +57 -0
  131. package/landed.d.ts +14 -0
  132. package/lane-todos.d.ts +44 -0
  133. package/lane.d.ts +55 -0
  134. package/lanes-from-tags.d.ts +17 -0
  135. package/lanes.d.ts +96 -0
  136. package/legacy-names.d.ts +70 -0
  137. package/legacy-ps.d.ts +31 -0
  138. package/limits.d.ts +32 -0
  139. package/list.d.ts +18 -0
  140. package/load-config.d.ts +44 -0
  141. package/log.d.ts +14 -0
  142. package/machine.d.ts +42 -0
  143. package/merge.d.ts +35 -0
  144. package/migrate-config.d.ts +31 -0
  145. package/models.d.ts +24 -0
  146. package/needs.d.ts +115 -0
  147. package/notices.d.ts +39 -0
  148. package/offered.d.ts +39 -0
  149. package/opencode-usage.d.ts +37 -0
  150. package/outcomes.d.ts +114 -0
  151. package/package.json +74 -5
  152. package/path.d.ts +13 -0
  153. package/plan.d.ts +236 -0
  154. package/port-forward.d.ts +22 -0
  155. package/port-pipe.d.ts +36 -0
  156. package/ports-list.d.ts +19 -0
  157. package/probe-runtime.d.ts +5 -0
  158. package/probe-runtime.js +13 -0
  159. package/project-reaches.d.ts +23 -0
  160. package/projects.d.ts +136 -0
  161. package/prompts.d.ts +128 -0
  162. package/pty.d.ts +39 -0
  163. package/pulse.d.ts +8 -0
  164. package/recommend.d.ts +31 -0
  165. package/release.d.ts +41 -0
  166. package/remote-process.d.ts +26 -0
  167. package/remote-pty.d.ts +29 -0
  168. package/remote-worker.d.ts +77 -0
  169. package/remove.d.ts +31 -0
  170. package/repository.d.ts +91 -0
  171. package/retry.d.ts +34 -0
  172. package/roster.d.ts +63 -0
  173. package/saved-ports-host.d.ts +25 -0
  174. package/saved-tunnel.d.ts +44 -0
  175. package/server-worker-socket.d.ts +21 -0
  176. package/setup-areas.d.ts +7 -0
  177. package/setup-opencode-model-error.d.ts +3 -0
  178. package/setup-opencode-models.d.ts +54 -0
  179. package/setup-opencode-recipe.d.ts +26 -0
  180. package/setup-opencode.d.ts +46 -0
  181. package/setup.d.ts +102 -0
  182. package/shared-browser.d.ts +50 -0
  183. package/shell.d.ts +17 -0
  184. package/show.d.ts +106 -0
  185. package/skill-checks.d.ts +73 -0
  186. package/skill-checks.js +10 -0
  187. package/skills.d.ts +16 -0
  188. package/slot-tunnel.d.ts +27 -0
  189. package/slots.d.ts +135 -0
  190. package/snapshot.d.ts +75 -0
  191. package/splates-artifact.d.ts +3 -0
  192. package/splates-creds.d.ts +3 -0
  193. package/splates-dev.d.ts +22 -0
  194. package/splates-flags.d.ts +34 -0
  195. package/splates-lane.d.ts +3 -0
  196. package/splates-projects.d.ts +3 -0
  197. package/splates-setup.d.ts +17 -0
  198. package/splates-slots.d.ts +3 -0
  199. package/splates-todo.d.ts +3 -0
  200. package/splates-tunnel.d.ts +3 -0
  201. package/splates-worker.d.ts +3 -0
  202. package/splates.d.ts +28 -0
  203. package/stats.d.ts +186 -0
  204. package/supervise.d.ts +50 -0
  205. package/system-changes.d.ts +67 -0
  206. package/system.d.ts +28 -0
  207. package/systems.d.ts +119 -0
  208. package/systems.js +21 -0
  209. package/table.d.ts +5 -0
  210. package/task-log.d.ts +35 -0
  211. package/task-ports.d.ts +25 -0
  212. package/tell.d.ts +47 -0
  213. package/todo-edit.d.ts +60 -0
  214. package/todo-file.d.ts +6 -0
  215. package/todo-lanes.d.ts +82 -0
  216. package/todo-list.d.ts +26 -0
  217. package/todo-take.d.ts +27 -0
  218. package/todo.d.ts +146 -0
  219. package/todos.d.ts +157 -0
  220. package/tool-calls.d.ts +18 -0
  221. package/tunnel-provider.d.ts +37 -0
  222. package/tunnel-provider.js +0 -0
  223. package/tunnel-remove.d.ts +28 -0
  224. package/tunnel-setup.d.ts +104 -0
  225. package/unix-worker-listener.d.ts +23 -0
  226. package/unix-worker-socket.d.ts +30 -0
  227. package/update.d.ts +29 -0
  228. package/update.js +10 -0
  229. package/vocabulary.d.ts +9 -0
  230. package/wait.d.ts +36 -0
  231. package/watch.d.ts +46 -0
  232. package/watched.d.ts +43 -0
  233. package/websocket-worker-socket.d.ts +18 -0
  234. package/worker-call-host.d.ts +51 -0
  235. package/worker-channel.d.ts +83 -0
  236. package/worker-connection.d.ts +24 -0
  237. package/worker-daemon.d.ts +64 -0
  238. package/worker-git.d.ts +18 -0
  239. package/worker-list.d.ts +49 -0
  240. package/worker-machine.d.ts +34 -0
  241. package/worker-port-host.d.ts +21 -0
  242. package/worker-process-host.d.ts +29 -0
  243. package/worker-protocol.d.ts +104 -0
  244. package/worker-pty-host.d.ts +34 -0
  245. package/worker-registry.d.ts +68 -0
  246. package/worker-replay.d.ts +25 -0
  247. package/worker-run.d.ts +20 -0
  248. package/worker-session.d.ts +189 -0
  249. package/worker-socket.d.ts +15 -0
  250. package/worktree-service.d.ts +113 -0
  251. package/worktree.d.ts +232 -0
@@ -0,0 +1,104 @@
1
+ /**
2
+ * Turns frames into what a `WorkerSocket` carries and back, and settles which
3
+ * version two ends speak. A `WorkerSession` uses it for every frame; reach for
4
+ * it directly only to speak the protocol without one.
5
+ */
6
+ export declare class WorkerProtocol {
7
+ private constructor();
8
+ /** The version this end speaks, sent in every hello and welcome. */
9
+ static readonly version = "1.0";
10
+ static readonly headerBytes = 8;
11
+ static encode(frame: Frame): string | Uint8Array;
12
+ /** Throws on anything that is not a frame, which ends the connection. */
13
+ static decode(encoded: string | Uint8Array): Frame;
14
+ /**
15
+ * Settles on the lower minor and the capabilities both ends have; another
16
+ * major cannot be read, so it is refused with a reason the coordinator
17
+ * shows when it drains that worker.
18
+ */
19
+ static negotiate(ours: {
20
+ protocol: string;
21
+ capabilities: string[];
22
+ }, theirs: {
23
+ protocol: string;
24
+ capabilities: string[];
25
+ }): Negotiated;
26
+ }
27
+ /** A JSON value, as control frames carry them. */
28
+ export type Json = string | number | boolean | null | Json[] | {
29
+ [key: string]: Json;
30
+ };
31
+ /** Each channel's last seq one end holds, as `[channel, seq]` pairs. */
32
+ export type Held = [channel: number, seq: number][];
33
+ /**
34
+ * Every frame on the link between a coordinator and a worker. Those naming a
35
+ * `channel` carry its seq and are kept until acked, so they survive a
36
+ * reconnect; the rest belong to one connection and are never replayed.
37
+ */
38
+ export type Frame = {
39
+ type: "hello";
40
+ protocol: string;
41
+ capabilities: string[];
42
+ id: string;
43
+ info: {
44
+ [key: string]: Json;
45
+ };
46
+ /** The worker session's id, new each time a worker starts. */
47
+ session: string;
48
+ held: Held;
49
+ } | {
50
+ type: "welcome";
51
+ protocol: string;
52
+ capabilities: string[];
53
+ /** The coordinator session's id, new each time a coordinator starts. */
54
+ session: string;
55
+ held: Held;
56
+ } | {
57
+ type: "refuse";
58
+ reason: string;
59
+ } | {
60
+ type: "open";
61
+ channel: number;
62
+ seq: number;
63
+ kind: string;
64
+ params: Json;
65
+ } | {
66
+ type: "message";
67
+ channel: number;
68
+ seq: number;
69
+ body: Json;
70
+ } | {
71
+ type: "data";
72
+ channel: number;
73
+ seq: number;
74
+ bytes: Uint8Array;
75
+ } | {
76
+ type: "close";
77
+ channel: number;
78
+ seq: number;
79
+ } | {
80
+ type: "ack";
81
+ acks: Held;
82
+ } | {
83
+ type: "ping";
84
+ n: number;
85
+ } | {
86
+ type: "pong";
87
+ n: number;
88
+ };
89
+ /** A frame that belongs to a channel, so it has a seq and is replayed. */
90
+ export type ChannelFrame = Extract<Frame, {
91
+ channel: number;
92
+ }>;
93
+ /** What one end makes of the other's version, from its hello or welcome. */
94
+ export type Negotiated = {
95
+ ok: true;
96
+ protocol: string;
97
+ capabilities: string[];
98
+ }
99
+ /** `refused` when the other end turned this one away, rather than the link going. */
100
+ | {
101
+ ok: false;
102
+ reason: string;
103
+ refused: boolean;
104
+ };
@@ -0,0 +1,34 @@
1
+ import type { Resource } from "webappwiz/disposable";
2
+ import type { Ps } from "webappwiz/system";
3
+ import { z } from "zod";
4
+ import type { WorkerChannel } from "./worker-channel.js";
5
+ /** What a coordinator opens a `pty` channel with. */
6
+ export declare const PtyParams: z.ZodObject<{
7
+ argv: z.ZodArray<z.ZodString>;
8
+ cwd: z.ZodString;
9
+ cols: z.ZodNumber;
10
+ rows: z.ZodNumber;
11
+ env: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodString>>;
12
+ }, z.core.$strip>;
13
+ /** How much of what the shell last wrote is kept, to redraw a terminal that comes back. */
14
+ export declare const SCREEN_BYTES: number;
15
+ /**
16
+ * One shell a `pty` channel started on the worker, for a page's terminal:
17
+ * the terminal's bytes both ways, a resize as a message, and its pid and
18
+ * exit code as messages back. It keeps the last `SCREEN_BYTES` the shell
19
+ * wrote, and says its pid and writes them again when asked to `describe`
20
+ * itself, for a terminal taken back after a coordinator restart, or a page
21
+ * come back to it. Closing the channel hangs it up.
22
+ */
23
+ export declare class WorkerPtyHost implements Resource {
24
+ readonly channel: WorkerChannel;
25
+ private readonly pty;
26
+ /** What the shell last wrote, oldest first, `SCREEN_BYTES` at most in all. */
27
+ private readonly screen;
28
+ private screenBytes;
29
+ constructor(channel: WorkerChannel, ps: Ps);
30
+ /** Says the pid again, then writes the screen kept, to redraw a terminal that came back. */
31
+ private describe;
32
+ private keep;
33
+ dispose(): void;
34
+ }
@@ -0,0 +1,68 @@
1
+ import type { Resource } from "webappwiz/disposable";
2
+ import { type Eventful } from "webappwiz/events";
3
+ import type { Logger } from "webappwiz/log";
4
+ import { type Clock, Duration, type Timer, type WallClock } from "webappwiz/time";
5
+ import { RemoteWorker, type WorkerKind } from "./remote-worker.js";
6
+ import { type WorkerSessionOptions } from "./worker-session.js";
7
+ import type { WorkerSocket } from "./worker-socket.js";
8
+ /** A worker turned away, and why, so the coordinator can drain it. */
9
+ export interface RefusedWorker {
10
+ id: string;
11
+ protocol: string;
12
+ reason: string;
13
+ }
14
+ /** What a `WorkerRegistry` hears. */
15
+ export interface WorkerRegistryEvents extends Record<string, unknown> {
16
+ /** A worker connected, for the first time or again. */
17
+ joined: RemoteWorker;
18
+ refused: RefusedWorker;
19
+ }
20
+ /** How a `WorkerRegistry` paces the sessions it keeps. */
21
+ export interface WorkerRegistryOptions {
22
+ /** How long a new connection has to say hello. */
23
+ helloWithin?: Duration;
24
+ session?: Omit<WorkerSessionOptions, "timer" | "clock" | "wall" | "capabilities">;
25
+ timer?: Timer;
26
+ clock?: Clock;
27
+ wall?: WallClock;
28
+ }
29
+ /**
30
+ * The coordinator's roster of workers, in memory for now: it takes each
31
+ * connection, on whatever transport, reads its hello, and resumes the session
32
+ * of a worker it knows by its id, so what it was running carries on.
33
+ */
34
+ export declare class WorkerRegistry implements Eventful<WorkerRegistryEvents>, Resource {
35
+ private readonly log;
36
+ private readonly opts;
37
+ private readonly dispatcher;
38
+ readonly events: import("webappwiz/events").Events<WorkerRegistryEvents>;
39
+ private readonly byId;
40
+ private readonly refusals;
41
+ private readonly timer;
42
+ private readonly clock;
43
+ private readonly wall;
44
+ constructor(log: Logger, opts?: WorkerRegistryOptions);
45
+ get workers(): RemoteWorker[];
46
+ /** Workers turned away for another major, by id, last word first. */
47
+ get refused(): RefusedWorker[];
48
+ worker(id: string): RemoteWorker | undefined;
49
+ /**
50
+ * The worker known by `id`, made now if it has yet to join, so work can be
51
+ * given it before it does: its session keeps what it is sent until then.
52
+ * `kind` is how it will connect.
53
+ */
54
+ expect(id: string, kind?: WorkerKind): RemoteWorker;
55
+ /**
56
+ * Takes a worker's new connection, hanging up on one that says nothing in
57
+ * time. `kind` is how it reached us: `local` over this machine's socket.
58
+ */
59
+ accept(socket: WorkerSocket, kind?: WorkerKind): Promise<void>;
60
+ /**
61
+ * Hangs up on every worker for a restart, keeping what each runs for
62
+ * the next run to take back, once each holds everything it was sent, or
63
+ * `ACK_WAIT` is up.
64
+ */
65
+ detach(): Promise<void>;
66
+ private session;
67
+ dispose(): void;
68
+ }
@@ -0,0 +1,25 @@
1
+ import type { ChannelFrame, Held } from "./worker-protocol.js";
2
+ /**
3
+ * The channel frames one end has sent and the other has yet to ack, kept to
4
+ * send again on a reconnect. Its bound is in bytes: past it, `keep` still
5
+ * keeps the frame, so nothing is lost, but says the writer should stop.
6
+ */
7
+ export declare class WorkerReplay {
8
+ readonly limit: number;
9
+ private readonly channels;
10
+ private nextOrder;
11
+ private _bytes;
12
+ constructor(limit: number);
13
+ get bytes(): number;
14
+ get full(): boolean;
15
+ /** Keeps a frame until acked, and says whether there is room for more. */
16
+ keep(frame: ChannelFrame, encoded: string | Uint8Array): boolean;
17
+ /** Lets go of every frame, for an other end that will never ack them. */
18
+ clear(): void;
19
+ /** Lets go of `channel`'s frames, once both ends have closed it: nothing will ask for them. */
20
+ drop(channel: number): void;
21
+ /** Drops every frame of each channel up to the seq the other end holds. */
22
+ ack(acks: Held): void;
23
+ /** Every frame still kept, in the order it was first sent. */
24
+ unacked(): (string | Uint8Array)[];
25
+ }
@@ -0,0 +1,20 @@
1
+ import type { Logger } from "webappwiz/log";
2
+ import type { Fs, Ps } from "webappwiz/system";
3
+ /** Where the local worker finds the coordinator on this machine. */
4
+ export declare function workerSocketPath(ps: Ps): string;
5
+ /** What `splates worker run` is run with. */
6
+ export interface WorkerRunOptions {
7
+ /** Empty for this machine's coordinator, a URL for another's, or `unix:<path>`. */
8
+ coordinator: string;
9
+ slots: number;
10
+ id: string;
11
+ log: Logger;
12
+ ps: Ps;
13
+ fs: Fs;
14
+ }
15
+ /**
16
+ * `splates worker run`: a worker that serves its coordinator until a SIGINT
17
+ * or SIGTERM, or until the coordinator refuses it for speaking another
18
+ * protocol, which is a refusal to update one or the other.
19
+ */
20
+ export declare function workerRun(opts: WorkerRunOptions): Promise<void>;
@@ -0,0 +1,189 @@
1
+ import { type Resource } from "webappwiz/disposable";
2
+ import { type Eventful } from "webappwiz/events";
3
+ import { type Clock, Duration, type Timer, type WallClock } from "webappwiz/time";
4
+ import { WorkerChannel } from "./worker-channel.js";
5
+ import { type Frame, type Json, type Negotiated } from "./worker-protocol.js";
6
+ import type { WorkerSocket } from "./worker-socket.js";
7
+ /** A worker's hello, which the coordinator reads to pick its session. */
8
+ export type WorkerHello = Extract<Frame, {
9
+ type: "hello";
10
+ }>;
11
+ /** What a `WorkerSession` hears. */
12
+ export interface WorkerSessionEvents extends Record<string, unknown> {
13
+ /** The other end opened a channel. */
14
+ channel: WorkerChannel;
15
+ /** A handshake settled, and what the other end missed has been sent again. */
16
+ connected: undefined;
17
+ /** The connection went, by hang-up, missed heartbeats or a bad frame; channels stay. */
18
+ lost: {
19
+ reason: string;
20
+ };
21
+ /** Acks brought unacked bytes back under the bound: writers may go on. */
22
+ drain: undefined;
23
+ /** The other end acked something; `acked()` waits on it. */
24
+ acked: undefined;
25
+ }
26
+ /** How a `WorkerSession` paces and bounds itself. */
27
+ export interface WorkerSessionOptions {
28
+ /**
29
+ * What its hello or welcome names it by: the id a coordinator started
30
+ * again answers as, so its worker finds it unchanged and keeps every
31
+ * channel; new by default.
32
+ */
33
+ id?: string;
34
+ capabilities?: string[];
35
+ /** Unacked bytes kept for replay before writes return `false`. */
36
+ bufferBytes?: number;
37
+ /** How often each end pings the other. */
38
+ heartbeat?: Duration;
39
+ /** How long without a frame before the other end counts as gone. */
40
+ timeout?: Duration;
41
+ /** How long acks gather before they go, so a burst costs one ack. */
42
+ ackDelay?: Duration;
43
+ /**
44
+ * `delivery` acks data and messages once listeners have them; `manual`
45
+ * waits for `WorkerChannel.ack`, for an end that must first write them
46
+ * down. Opens and closes are acked on arrival either way.
47
+ */
48
+ acks?: "delivery" | "manual";
49
+ timer?: Timer;
50
+ clock?: Clock;
51
+ /** Stamps `lastHeard`. */
52
+ wall?: WallClock;
53
+ }
54
+ /**
55
+ * One end of the link between a coordinator and a worker, the same code on
56
+ * both. It outlives any one connection: channels stay open across a
57
+ * reconnect, every channel frame is kept until the other end acks it, and a
58
+ * new connection replays what was not acked, in order, once.
59
+ */
60
+ export declare class WorkerSession implements Eventful<WorkerSessionEvents>, Resource {
61
+ readonly role: "worker" | "coordinator";
62
+ /** What its hello or welcome names it by, so the other end can tell one started again. */
63
+ readonly id: string;
64
+ private readonly dispatcher;
65
+ readonly events: import("webappwiz/events").Events<WorkerSessionEvents>;
66
+ private readonly channels;
67
+ private readonly replay;
68
+ private readonly sent;
69
+ private readonly received;
70
+ /** Channels taken back without knowing where the other end's replay starts: its first frame says. */
71
+ private readonly unplaced;
72
+ /** Channels taken back from the session this one resumes, until the first handshake says the other end holds them. */
73
+ private readonly taken;
74
+ /**
75
+ * What was sent on a taken channel before that handshake, which says
76
+ * where its seqs carry on from: numbered and sent then.
77
+ */
78
+ private readonly deferred;
79
+ private readonly held;
80
+ private readonly pendingAcks;
81
+ /** Channels both ends closed, kept until a pong proves the other end has their ack. */
82
+ private readonly forgetting;
83
+ private readonly capabilities;
84
+ private readonly heartbeat;
85
+ private readonly timeout;
86
+ private readonly ackDelay;
87
+ private readonly acks;
88
+ private readonly timer;
89
+ private readonly clock;
90
+ private readonly wall;
91
+ private readonly link;
92
+ private connection;
93
+ private nextChannel;
94
+ private nextPing;
95
+ private wasFull;
96
+ private _negotiated;
97
+ private _lastHeard;
98
+ private disposed;
99
+ /** Hung up for a restart: its channels live on at the other end, for the next session to take back. */
100
+ private detached;
101
+ /** Made with an id, so the other end may hold channels of the session it resumes. */
102
+ private readonly resumed;
103
+ /** The other end's session at the last handshake; null before the first. */
104
+ private peer;
105
+ constructor(role: "worker" | "coordinator", opts?: WorkerSessionOptions);
106
+ /**
107
+ * Reads the first frame of a new connection, which must be a worker's
108
+ * hello, so the coordinator can pick the session it resumes.
109
+ */
110
+ static hello(socket: WorkerSocket): Promise<WorkerHello>;
111
+ /** What the last handshake settled on, once there has been one. */
112
+ get negotiated(): Negotiated | undefined;
113
+ /** When any frame last came from the other end, in epoch milliseconds; null before the first. */
114
+ get lastHeard(): number | null;
115
+ get connected(): boolean;
116
+ /** False while unacked bytes are at the bound; `drain` says when that ends. */
117
+ get writable(): boolean;
118
+ /** Settles once writes may go on: at once, or at the next `drain`. */
119
+ drained(): Promise<void>;
120
+ /** Opens a channel to the other end, which hears it as `channel`. */
121
+ open(kind: string, params?: Json): WorkerChannel;
122
+ /**
123
+ * Takes back channel `id`, which the other end still holds from the
124
+ * session this one resumes, before the handshake: `held` is the last seq
125
+ * of the other end's this end has, which its replay then follows, or null
126
+ * to take the replay from wherever it starts. What this end sends on it
127
+ * carries on from what the other end's hello says it received.
128
+ */
129
+ resume(id: number, kind: string, params: Json, held: number | null): WorkerChannel;
130
+ /**
131
+ * The worker's side of a handshake: says hello on `socket`, and on welcome
132
+ * replays what the coordinator does not hold. Resolves with what was
133
+ * negotiated, or why not: refused for another major, or the link went.
134
+ */
135
+ connect(socket: WorkerSocket, hello: {
136
+ id: string;
137
+ info?: {
138
+ [key: string]: Json;
139
+ };
140
+ }): Promise<Negotiated>;
141
+ /**
142
+ * The coordinator's side of a handshake, given the hello
143
+ * `WorkerSession.hello` read: welcomes the worker and replays what it
144
+ * does not hold, or refuses one on another major, hanging up, so the
145
+ * coordinator can drain it with the reason.
146
+ */
147
+ accept(socket: WorkerSocket, hello: WorkerHello): Negotiated;
148
+ /**
149
+ * What the worker's hello says it holds of the channels of the session
150
+ * this one resumes, at its first handshake: this end sends on each from
151
+ * past that, opens new ones past them all, and closes those nobody took
152
+ * back, which ends what they ran. One taken back that the worker does
153
+ * not hold, as after the worker too started again, ends here.
154
+ */
155
+ private takeBack;
156
+ /**
157
+ * Hangs up for a restart, keeping every channel open at the other end:
158
+ * a session with this id takes them back once it comes up.
159
+ */
160
+ detach(): void;
161
+ /** Settles once the other end has acked everything sent, or at once with nothing out. */
162
+ acked(): Promise<void>;
163
+ /** Hangs up and closes every channel; the other end hears them close. */
164
+ dispose(): void;
165
+ private attach;
166
+ private go;
167
+ private hear;
168
+ private handshake;
169
+ private receive;
170
+ /** Holds `frame` for the handshake that says what seq it goes out as. */
171
+ private defer;
172
+ private sendOnChannel;
173
+ private nextSeq;
174
+ private ack;
175
+ private flushAcks;
176
+ private acknowledged;
177
+ /**
178
+ * Ends every channel at this end, for another end started again, which
179
+ * knows none of them: what they ran stops, as when the old one hung up.
180
+ */
181
+ private forget;
182
+ private channelDone;
183
+ private ponged;
184
+ private beat;
185
+ private write;
186
+ private lose;
187
+ private assertRole;
188
+ private assertOpen;
189
+ }
@@ -0,0 +1,15 @@
1
+ import type { Resource } from "webappwiz/disposable";
2
+ import type { Eventful } from "webappwiz/events";
3
+ /** What a `WorkerSocket` hears: one whole frame, or that it is gone. */
4
+ export interface WorkerSocketEvents extends Record<string, unknown> {
5
+ frame: string | Uint8Array;
6
+ close: undefined;
7
+ }
8
+ /**
9
+ * One connection between a coordinator and a worker, carrying whole frames in
10
+ * order: a WebSocket as it is, or a Unix socket behind length-prefixed
11
+ * framing. Disposing it hangs up, and both ends then hear `close`.
12
+ */
13
+ export interface WorkerSocket extends Eventful<WorkerSocketEvents>, Resource {
14
+ send(frame: string | Uint8Array): void;
15
+ }
@@ -0,0 +1,113 @@
1
+ import { type Fs, type Ps } from "webappwiz/system";
2
+ import type { Adherence } from "./adherence.js";
3
+ import { Attachments } from "./attachments.js";
4
+ import type { Config } from "./config.js";
5
+ import type { Git, GitResult } from "./git.js";
6
+ import { type Outcomes } from "./outcomes.js";
7
+ import { type DiscardOptions, type TaskState, Worktree } from "./worktree.js";
8
+ export interface AddOptions {
9
+ /** Branch the new worktree starts from. Defaults to the trunk. */
10
+ base?: string;
11
+ }
12
+ /**
13
+ * Where tasks live and everything persistent about them: the worktree
14
+ * directories, the records under `.git/splates/tasks`, and the names of tasks
15
+ * already removed. One name, one lookup, whatever state it turns out to be in.
16
+ */
17
+ /** What a `WorktreeService` works through; the real ones by default. */
18
+ export interface WorktreeServiceOptions {
19
+ fs?: Fs;
20
+ ps?: Ps;
21
+ /** Where each task's ending is written down; none, and nothing is. */
22
+ outcomes?: Outcomes;
23
+ /** How closely conversations follow the skill, which an ending's findings read. */
24
+ adherence?: Adherence;
25
+ }
26
+ export declare class WorktreeService {
27
+ readonly git: Git;
28
+ readonly config: Config;
29
+ private readonly tasksDir;
30
+ private readonly removedDir;
31
+ private readonly conversationsDir;
32
+ private readonly fs;
33
+ private readonly outcomes;
34
+ private readonly adherence;
35
+ readonly ps: Ps;
36
+ constructor(git: Git, config: Config, splatesDir: string, opts?: WorktreeServiceOptions);
37
+ /**
38
+ * The `splates server` conversation whose agent runs this command, when it is
39
+ * one of this repo's: an agent from another project's page working here
40
+ * is, to this repo, an agent outside the page.
41
+ */
42
+ conversation(): Promise<string | undefined>;
43
+ /** Where the log of the conversation `id` is kept. */
44
+ conversationLog(id: string): string;
45
+ /** Whether the `splates server` conversation `id` is one of this repo's. */
46
+ holds(id: string): Promise<boolean>;
47
+ init(): Promise<void>;
48
+ get trunk(): string;
49
+ /** Where a task's worktree lives: a sibling of the repo, never inside it. */
50
+ pathFor(task: string): string;
51
+ branchFor(task: string): string;
52
+ taskFor(branch: string | null): string | null;
53
+ recordPath(task: string): string;
54
+ /**
55
+ * Where a task's files live, beside its record: the screenshots and videos
56
+ * its questions show, in `tasks/<task>/`. They go when the task does.
57
+ */
58
+ files(task: string): Attachments;
59
+ /**
60
+ * Where a task's files live when it attaches them from `conversation`:
61
+ * beside that conversation's artifacts, in `attached/<task>/`, so they
62
+ * outlive the task and a merged card's images still show.
63
+ */
64
+ attached(conversation: string, task: string): Attachments;
65
+ /**
66
+ * Every folder holding files `task` attached: its own, and one in each
67
+ * conversation that attached some.
68
+ */
69
+ filesOf(task: string): Promise<Attachments[]>;
70
+ /**
71
+ * The bytes `conversation` keeps on disk, its artifacts and attached
72
+ * files, which together are held to `artifactConversationCapacity`.
73
+ */
74
+ conversationBytes(conversation: string): Promise<number>;
75
+ /**
76
+ * Whether `path` is a file some task or conversation holds for a task:
77
+ * one level inside a task's folder, or inside a conversation's
78
+ * `attached/<task>/`, never a record or log, and not a way out of here.
79
+ */
80
+ owns(path: string): boolean;
81
+ /** Always answers; the returned worktree's status says what was found. */
82
+ find(task: string): Promise<Worktree>;
83
+ list(): Promise<Worktree[]>;
84
+ /** Adds the branch and the working directory. The record comes after. */
85
+ add(task: string, { base }?: AddOptions): Promise<GitResult>;
86
+ discard(worktree: Worktree, ended: "merged" | "removed", opts?: DiscardOptions): Promise<GitResult>;
87
+ /** Writes a task's record. A concurrent reader sees the old one or the new. */
88
+ saveRecord(state: TaskState): Promise<void>;
89
+ /**
90
+ * Writes down how a task with a record ended. One with none, a stray
91
+ * branch or a bare directory, never ran as a task, so says nothing.
92
+ */
93
+ /**
94
+ * What a task leaves to read as it ends: its plan's questions, and the
95
+ * systems its changes fall in, by the config in its own tree.
96
+ */
97
+ private ending;
98
+ private recordOutcome;
99
+ private readRecord;
100
+ /**
101
+ * Drops the files of every task with no record left: what a process that
102
+ * died between discarding a task and clearing its folder left behind.
103
+ */
104
+ private sweep;
105
+ private removedPath;
106
+ private removedAt;
107
+ /**
108
+ * Remembers a removed name so a second `remove` can say "already removed"
109
+ * instead of "never existed", then drops the oldest so the list of
110
+ * remembered names cannot grow without bound.
111
+ */
112
+ private rememberRemoved;
113
+ }