wicked-crew 0.1.0 → 0.1.1
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/api/gate-cache.d.ts +38 -0
- package/dist/api/gate-cache.d.ts.map +1 -0
- package/dist/api/gate-cache.js +58 -0
- package/dist/api/gate-cache.js.map +1 -0
- package/dist/api/routes.d.ts +9 -0
- package/dist/api/routes.d.ts.map +1 -0
- package/dist/api/routes.js +250 -0
- package/dist/api/routes.js.map +1 -0
- package/dist/api/server.d.ts +10 -0
- package/dist/api/server.d.ts.map +1 -0
- package/dist/api/server.js +66 -0
- package/dist/api/server.js.map +1 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +134 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/core/adapter.d.ts +66 -0
- package/dist/core/adapter.d.ts.map +1 -0
- package/dist/core/adapter.js +145 -0
- package/dist/core/adapter.js.map +1 -0
- package/dist/core/types.d.ts +129 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +15 -0
- package/dist/core/types.js.map +1 -0
- package/dist/events/bus.d.ts +10 -0
- package/dist/events/bus.d.ts.map +1 -0
- package/dist/events/bus.js +48 -0
- package/dist/events/bus.js.map +1 -0
- package/dist/events/terminals.d.ts +42 -0
- package/dist/events/terminals.d.ts.map +1 -0
- package/dist/events/terminals.js +106 -0
- package/dist/events/terminals.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { CoreEvent, LaunchRunInput, RepoEntry, SessionView } from './types.js';
|
|
2
|
+
/** A parsed-CoreEvent listener. */
|
|
3
|
+
export type CoreEventListener = (event: CoreEvent) => void;
|
|
4
|
+
export interface CoreAdapterOptions {
|
|
5
|
+
/** Estate db path the core actor writes to (single writer). */
|
|
6
|
+
dbPath: string;
|
|
7
|
+
/** `true` → deterministic offline stub engine (tests); `false` → production engine. */
|
|
8
|
+
stub?: boolean;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* The single isolation boundary over wicked-core-ts. It holds the ONE `Core`
|
|
12
|
+
* handle, makes the ONE `subscribe()` call for the whole process, parses each
|
|
13
|
+
* CoreEvent, and re-emits it to registered in-daemon listeners. Every REST
|
|
14
|
+
* endpoint and the WS fan-out funnel through this stable API — so when the
|
|
15
|
+
* in-flight core-ts subscribe/teardown signature lands, only this file changes.
|
|
16
|
+
*/
|
|
17
|
+
export declare class CoreAdapter {
|
|
18
|
+
private readonly core;
|
|
19
|
+
private readonly subscription;
|
|
20
|
+
private readonly listeners;
|
|
21
|
+
private closed;
|
|
22
|
+
constructor(opts: CoreAdapterOptions);
|
|
23
|
+
/** Register a CoreEvent listener. Returns an unsubscribe function. */
|
|
24
|
+
onEvent(listener: CoreEventListener): () => void;
|
|
25
|
+
/** The production council roster (static), parsed to seats. */
|
|
26
|
+
static roster(): unknown[];
|
|
27
|
+
/** Liveness probe → `"ok"` (also proves the event pump). */
|
|
28
|
+
ping(): Promise<string>;
|
|
29
|
+
/** Launch an interactive, resumable run → the run id. */
|
|
30
|
+
launchRun(input: LaunchRunInput): Promise<string>;
|
|
31
|
+
/** Resume a run from its persisted cursor → the status token. */
|
|
32
|
+
resumeRun(runId: string): Promise<string>;
|
|
33
|
+
/** Resolve a human gate: approve (optional amend) or reject → the status token. */
|
|
34
|
+
confirmGate(runId: string, approve: boolean, amend?: string): Promise<string>;
|
|
35
|
+
/** Cancel a run → the status token. */
|
|
36
|
+
cancelRun(runId: string): Promise<string>;
|
|
37
|
+
/** Run ids on the store. */
|
|
38
|
+
sessions(): Promise<string[]>;
|
|
39
|
+
/** Every run + its ordered units. */
|
|
40
|
+
sessionsDetail(): Promise<SessionView[]>;
|
|
41
|
+
/** A unit's captured transcript (string, or `null`). */
|
|
42
|
+
workOutput(unitId: string): Promise<string | null>;
|
|
43
|
+
/** Register a git repo → the persisted `RepoEntry`. */
|
|
44
|
+
registerRepo(name: string, rootPath: string): Promise<RepoEntry>;
|
|
45
|
+
/** List every registered repo. */
|
|
46
|
+
listRepos(): Promise<RepoEntry[]>;
|
|
47
|
+
/**
|
|
48
|
+
* Open a PTY terminal session in `cwd` running `cmd` (or the login shell when
|
|
49
|
+
* omitted), sized `cols`x`rows`. `governed:true` keeps tool-calls routed through
|
|
50
|
+
* the gate-hook (the default); `governed:false` is the loud, opt-in **ungoverned
|
|
51
|
+
* operator shell** (DES-TERMINAL-001 §7). Resolves the new terminal id.
|
|
52
|
+
*/
|
|
53
|
+
openTerminal(cwd: string, cmd: string[] | undefined, cols: number, rows: number, governed: boolean): Promise<string>;
|
|
54
|
+
/** Write raw input bytes (keystrokes) to a terminal → `"ok"`. Rejects on an unknown id. */
|
|
55
|
+
writeTerminal(id: string, bytes: Buffer): Promise<string>;
|
|
56
|
+
/** Resize a terminal's PTY to `cols`x`rows` → `"ok"`. Rejects on an unknown id. */
|
|
57
|
+
resizeTerminal(id: string, cols: number, rows: number): Promise<string>;
|
|
58
|
+
/** Close a terminal (kill child, join reader) → `"ok"` after a `terminalExited` event. */
|
|
59
|
+
closeTerminal(id: string): Promise<string>;
|
|
60
|
+
/**
|
|
61
|
+
* Tear down the single subscription (stop delivery, release the pump thread +
|
|
62
|
+
* callback) so the process can exit cleanly. Idempotent.
|
|
63
|
+
*/
|
|
64
|
+
close(): void;
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=adapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../../src/core/adapter.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,cAAc,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAiBpF,mCAAmC;AACnC,MAAM,MAAM,iBAAiB,GAAG,CAAC,KAAK,EAAE,SAAS,KAAK,IAAI,CAAC;AAE3D,MAAM,WAAW,kBAAkB;IACjC,+DAA+D;IAC/D,MAAM,EAAE,MAAM,CAAC;IACf,uFAAuF;IACvF,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED;;;;;;GAMG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAa;IAClC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAe;IAC5C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAgC;IAC1D,OAAO,CAAC,MAAM,CAAS;gBAEX,IAAI,EAAE,kBAAkB;IAuBpC,sEAAsE;IACtE,OAAO,CAAC,QAAQ,EAAE,iBAAiB,GAAG,MAAM,IAAI;IAOhD,+DAA+D;IAC/D,MAAM,CAAC,MAAM,IAAI,OAAO,EAAE;IAI1B,4DAA4D;IAC5D,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC;IAIvB,yDAAyD;IACzD,SAAS,CAAC,KAAK,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC;IAYjD,iEAAiE;IACjE,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAIzC,mFAAmF;IACnF,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAI7E,uCAAuC;IACvC,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAIzC,4BAA4B;IACtB,QAAQ,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;IAInC,qCAAqC;IAC/B,cAAc,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC;IAI9C,wDAAwD;IAClD,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAIxD,uDAAuD;IACjD,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC;IAItE,kCAAkC;IAC5B,SAAS,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;IAUvC;;;;;OAKG;IACH,YAAY,CACV,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,MAAM,EAAE,GAAG,SAAS,EACzB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,OAAO,GAChB,OAAO,CAAC,MAAM,CAAC;IAIlB,2FAA2F;IAC3F,aAAa,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAIzD,mFAAmF;IACnF,cAAc,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAIvE,0FAA0F;IAC1F,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAI1C;;;OAGG;IACH,KAAK,IAAI,IAAI;CAMd"}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { createRequire } from 'node:module';
|
|
2
|
+
// The native addon is a CommonJS cdylib (`index.node`); load it with a CJS
|
|
3
|
+
// require even though this daemon is ESM. This module is the ONLY place that
|
|
4
|
+
// touches wicked-core-ts (DES-STUDIO-001 §5.2/§5.3), so the FINALIZING
|
|
5
|
+
// `subscribe` seam has a blast radius of exactly one file.
|
|
6
|
+
const require = createRequire(import.meta.url);
|
|
7
|
+
const { Core } = require('wicked-core-ts');
|
|
8
|
+
/**
|
|
9
|
+
* The single isolation boundary over wicked-core-ts. It holds the ONE `Core`
|
|
10
|
+
* handle, makes the ONE `subscribe()` call for the whole process, parses each
|
|
11
|
+
* CoreEvent, and re-emits it to registered in-daemon listeners. Every REST
|
|
12
|
+
* endpoint and the WS fan-out funnel through this stable API — so when the
|
|
13
|
+
* in-flight core-ts subscribe/teardown signature lands, only this file changes.
|
|
14
|
+
*/
|
|
15
|
+
export class CoreAdapter {
|
|
16
|
+
core;
|
|
17
|
+
subscription;
|
|
18
|
+
listeners = new Set();
|
|
19
|
+
closed = false;
|
|
20
|
+
constructor(opts) {
|
|
21
|
+
this.core = opts.stub ? Core.spawnStub(opts.dbPath) : Core.spawn(opts.dbPath);
|
|
22
|
+
// The ONE subscribe() for the process. Error-first callback (index.d.ts:56):
|
|
23
|
+
// one JSON string per CoreEvent, in emission order. A throw in a listener is
|
|
24
|
+
// isolated so one bad consumer can never stall the pump or the others.
|
|
25
|
+
this.subscription = this.core.subscribe((err, json) => {
|
|
26
|
+
if (err)
|
|
27
|
+
return;
|
|
28
|
+
let event;
|
|
29
|
+
try {
|
|
30
|
+
event = JSON.parse(json);
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return; // malformed frame — drop it rather than crash the pump
|
|
34
|
+
}
|
|
35
|
+
for (const listener of this.listeners) {
|
|
36
|
+
try {
|
|
37
|
+
listener(event);
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
/* isolate a faulty listener */
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
/** Register a CoreEvent listener. Returns an unsubscribe function. */
|
|
46
|
+
onEvent(listener) {
|
|
47
|
+
this.listeners.add(listener);
|
|
48
|
+
return () => {
|
|
49
|
+
this.listeners.delete(listener);
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
/** The production council roster (static), parsed to seats. */
|
|
53
|
+
static roster() {
|
|
54
|
+
return JSON.parse(Core.registryRoster());
|
|
55
|
+
}
|
|
56
|
+
/** Liveness probe → `"ok"` (also proves the event pump). */
|
|
57
|
+
ping() {
|
|
58
|
+
return this.core.ping();
|
|
59
|
+
}
|
|
60
|
+
/** Launch an interactive, resumable run → the run id. */
|
|
61
|
+
launchRun(input) {
|
|
62
|
+
const opts = {
|
|
63
|
+
problem: input.problem,
|
|
64
|
+
sessionId: input.sessionId,
|
|
65
|
+
clisJson: input.clisJson,
|
|
66
|
+
};
|
|
67
|
+
if (input.entityMode !== undefined)
|
|
68
|
+
opts.entityMode = input.entityMode;
|
|
69
|
+
if (input.humanConfirm !== undefined)
|
|
70
|
+
opts.humanConfirm = input.humanConfirm;
|
|
71
|
+
if (input.repoRef !== undefined)
|
|
72
|
+
opts.repoRef = input.repoRef;
|
|
73
|
+
return this.core.launchRun(opts);
|
|
74
|
+
}
|
|
75
|
+
/** Resume a run from its persisted cursor → the status token. */
|
|
76
|
+
resumeRun(runId) {
|
|
77
|
+
return this.core.resumeRun(runId);
|
|
78
|
+
}
|
|
79
|
+
/** Resolve a human gate: approve (optional amend) or reject → the status token. */
|
|
80
|
+
confirmGate(runId, approve, amend) {
|
|
81
|
+
return this.core.confirmGate(runId, approve, amend);
|
|
82
|
+
}
|
|
83
|
+
/** Cancel a run → the status token. */
|
|
84
|
+
cancelRun(runId) {
|
|
85
|
+
return this.core.cancelRun(runId);
|
|
86
|
+
}
|
|
87
|
+
/** Run ids on the store. */
|
|
88
|
+
async sessions() {
|
|
89
|
+
return JSON.parse(await this.core.sessions());
|
|
90
|
+
}
|
|
91
|
+
/** Every run + its ordered units. */
|
|
92
|
+
async sessionsDetail() {
|
|
93
|
+
return JSON.parse(await this.core.sessionsDetail());
|
|
94
|
+
}
|
|
95
|
+
/** A unit's captured transcript (string, or `null`). */
|
|
96
|
+
async workOutput(unitId) {
|
|
97
|
+
return JSON.parse(await this.core.workOutput(unitId));
|
|
98
|
+
}
|
|
99
|
+
/** Register a git repo → the persisted `RepoEntry`. */
|
|
100
|
+
async registerRepo(name, rootPath) {
|
|
101
|
+
return JSON.parse(await this.core.registerRepo(name, rootPath));
|
|
102
|
+
}
|
|
103
|
+
/** List every registered repo. */
|
|
104
|
+
async listRepos() {
|
|
105
|
+
return JSON.parse(await this.core.listRepos());
|
|
106
|
+
}
|
|
107
|
+
// ── PTY terminal sessions (DES-TERMINAL-001 §6) ────────────────────────────
|
|
108
|
+
// Thin wrappers over the four core-ts terminal methods. Output does NOT return
|
|
109
|
+
// here — it arrives as `terminalOutput` CoreEvents on the single subscription
|
|
110
|
+
// (routed to the owning browser socket by the WS layer, keyed on the id these
|
|
111
|
+
// resolve). Callers must already be attached via onEvent to catch the bytes.
|
|
112
|
+
/**
|
|
113
|
+
* Open a PTY terminal session in `cwd` running `cmd` (or the login shell when
|
|
114
|
+
* omitted), sized `cols`x`rows`. `governed:true` keeps tool-calls routed through
|
|
115
|
+
* the gate-hook (the default); `governed:false` is the loud, opt-in **ungoverned
|
|
116
|
+
* operator shell** (DES-TERMINAL-001 §7). Resolves the new terminal id.
|
|
117
|
+
*/
|
|
118
|
+
openTerminal(cwd, cmd, cols, rows, governed) {
|
|
119
|
+
return this.core.openTerminal(cwd, cmd ?? null, cols, rows, governed);
|
|
120
|
+
}
|
|
121
|
+
/** Write raw input bytes (keystrokes) to a terminal → `"ok"`. Rejects on an unknown id. */
|
|
122
|
+
writeTerminal(id, bytes) {
|
|
123
|
+
return this.core.writeTerminal(id, bytes);
|
|
124
|
+
}
|
|
125
|
+
/** Resize a terminal's PTY to `cols`x`rows` → `"ok"`. Rejects on an unknown id. */
|
|
126
|
+
resizeTerminal(id, cols, rows) {
|
|
127
|
+
return this.core.resizeTerminal(id, cols, rows);
|
|
128
|
+
}
|
|
129
|
+
/** Close a terminal (kill child, join reader) → `"ok"` after a `terminalExited` event. */
|
|
130
|
+
closeTerminal(id) {
|
|
131
|
+
return this.core.closeTerminal(id);
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Tear down the single subscription (stop delivery, release the pump thread +
|
|
135
|
+
* callback) so the process can exit cleanly. Idempotent.
|
|
136
|
+
*/
|
|
137
|
+
close() {
|
|
138
|
+
if (this.closed)
|
|
139
|
+
return;
|
|
140
|
+
this.closed = true;
|
|
141
|
+
this.listeners.clear();
|
|
142
|
+
this.subscription.close();
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
//# sourceMappingURL=adapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adapter.js","sourceRoot":"","sources":["../../src/core/adapter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Boundary types for the wicked-core-ts JSON surface.
|
|
3
|
+
*
|
|
4
|
+
* core-ts returns every complex result as a JSON string, which the adapter
|
|
5
|
+
* `JSON.parse`s. These interfaces mirror the serde representation of
|
|
6
|
+
* wicked-core's domain types (wicked-core/src/domain.rs, repo.rs, scope.rs) so
|
|
7
|
+
* the daemon never falls back to `any` at the core boundary. They are permissive
|
|
8
|
+
* about forward-additive fields — a newer core that adds fields still parses.
|
|
9
|
+
*
|
|
10
|
+
* ONLY `core/adapter.ts` imports from the native addon itself; the rest of the
|
|
11
|
+
* daemon speaks these daemon-owned shapes, which is what quarantines the
|
|
12
|
+
* FINALIZING core-ts `subscribe` seam to a single file (DES-STUDIO-001 §5.2).
|
|
13
|
+
*/
|
|
14
|
+
/** Run-level lifecycle status (`SessionStatus`, snake_case serde token). */
|
|
15
|
+
export type SessionStatus = 'planning' | 'distributing' | 'executing' | 'awaiting_human' | 'completed' | 'cancelled' | 'failed';
|
|
16
|
+
/** Per-unit lifecycle status (`UnitStatus`). */
|
|
17
|
+
export type UnitStatus = 'pending' | 'distributed' | 'done' | 'rejected';
|
|
18
|
+
/** The methodology stage badge on a unit (`StageKind`). */
|
|
19
|
+
export type StageKind = 'recon' | 'build' | 'review' | 'test';
|
|
20
|
+
/** Collection-scope mode (`EntityMode`). */
|
|
21
|
+
export type EntityMode = 'shared' | 'isolated';
|
|
22
|
+
/** Human-confirm gate policy. serde: `None`→"none", `All`→"all", `Before(n)`→{ before: n }. */
|
|
23
|
+
export type HumanConfirm = 'none' | 'all' | {
|
|
24
|
+
before: number;
|
|
25
|
+
};
|
|
26
|
+
/** Why a CLI was assigned to a unit (`RoutingInfo`, internally tagged on `method`). */
|
|
27
|
+
export type RoutingInfo = {
|
|
28
|
+
method: 'council';
|
|
29
|
+
winner: string;
|
|
30
|
+
agreement_pct: number;
|
|
31
|
+
returned: number;
|
|
32
|
+
dissent: number;
|
|
33
|
+
} | {
|
|
34
|
+
method: 'degraded';
|
|
35
|
+
reason: string;
|
|
36
|
+
} | {
|
|
37
|
+
method: 'evaluator_distinct';
|
|
38
|
+
winner: string;
|
|
39
|
+
was: string;
|
|
40
|
+
};
|
|
41
|
+
/** A run (`AgentSession`). */
|
|
42
|
+
export interface AgentSession {
|
|
43
|
+
id: string;
|
|
44
|
+
workflow_id: string;
|
|
45
|
+
problem: string;
|
|
46
|
+
entity_mode: EntityMode;
|
|
47
|
+
collection_scope: string | null;
|
|
48
|
+
clis: string[];
|
|
49
|
+
status: SessionStatus;
|
|
50
|
+
human_confirm: HumanConfirm;
|
|
51
|
+
unit_ix: number;
|
|
52
|
+
attempt: number;
|
|
53
|
+
workdir: string | null;
|
|
54
|
+
repo_ref: string | null;
|
|
55
|
+
}
|
|
56
|
+
/** An ordered unit of work within a run (`WorkUnit`). */
|
|
57
|
+
export interface WorkUnit {
|
|
58
|
+
id: string;
|
|
59
|
+
session_id: string;
|
|
60
|
+
ord: number;
|
|
61
|
+
description: string;
|
|
62
|
+
stage: StageKind;
|
|
63
|
+
assigned_cli: string | null;
|
|
64
|
+
assigned_invocation: string | null;
|
|
65
|
+
council_task_ref: string | null;
|
|
66
|
+
routing: RoutingInfo | null;
|
|
67
|
+
denial_reason: string | null;
|
|
68
|
+
phase_ref: string | null;
|
|
69
|
+
conformance_ref: string | null;
|
|
70
|
+
phase_status: string | null;
|
|
71
|
+
collection_scope: string | null;
|
|
72
|
+
status: UnitStatus;
|
|
73
|
+
}
|
|
74
|
+
/** A run plus its ordered units — the read a UI builds its project list from (`SessionView`). */
|
|
75
|
+
export interface SessionView {
|
|
76
|
+
session: AgentSession;
|
|
77
|
+
units: WorkUnit[];
|
|
78
|
+
}
|
|
79
|
+
/** A registered git repository (`RepoEntry`). */
|
|
80
|
+
export interface RepoEntry {
|
|
81
|
+
id: string;
|
|
82
|
+
name: string;
|
|
83
|
+
root_path: string;
|
|
84
|
+
default_branch: string;
|
|
85
|
+
registered_at: number;
|
|
86
|
+
}
|
|
87
|
+
/** The daemon's launch-run input (mapped by the adapter onto the addon's `LaunchOptions`). */
|
|
88
|
+
export interface LaunchRunInput {
|
|
89
|
+
/** Free-text problem, decomposed into ordered work units. */
|
|
90
|
+
problem: string;
|
|
91
|
+
/** Stable run id. */
|
|
92
|
+
sessionId: string;
|
|
93
|
+
/** JSON array of `AgenticCli` seats — the council roster. */
|
|
94
|
+
clisJson: string;
|
|
95
|
+
/** `shared` (default) | `isolated`. */
|
|
96
|
+
entityMode?: string;
|
|
97
|
+
/** Human-confirm gate policy: `none` (default) | `all` | `before:<ord>`. */
|
|
98
|
+
humanConfirm?: string;
|
|
99
|
+
/** Id of a registered repo to run within. Omit for a repo-less run. */
|
|
100
|
+
repoRef?: string;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* A CoreEvent frame as delivered by the live stream — a tagged-JSON object
|
|
104
|
+
* discriminated on `type` (wicked-core-ts `event_to_json`). Known optional
|
|
105
|
+
* fields are declared for the frames the daemon inspects; the index signature
|
|
106
|
+
* keeps the shape additive-safe so new variants pass through untouched.
|
|
107
|
+
*/
|
|
108
|
+
export interface CoreEvent {
|
|
109
|
+
type: string;
|
|
110
|
+
session?: string;
|
|
111
|
+
ord?: number;
|
|
112
|
+
prompt?: string;
|
|
113
|
+
chunk?: string;
|
|
114
|
+
allow?: boolean;
|
|
115
|
+
cli?: string;
|
|
116
|
+
description?: string;
|
|
117
|
+
problem?: string;
|
|
118
|
+
message?: string;
|
|
119
|
+
/** PTY terminal frames (`terminalOpened`/`terminalOutput`/`terminalExited`): the terminal id. */
|
|
120
|
+
id?: string;
|
|
121
|
+
/** `terminalOutput`: monotonic per-terminal sequence number. */
|
|
122
|
+
seq?: number;
|
|
123
|
+
/** `terminalOutput`: raw PTY bytes, base64-encoded (decode → send to the owning socket). */
|
|
124
|
+
bytesB64?: string;
|
|
125
|
+
/** `terminalOpened`: the working directory the PTY was opened in. */
|
|
126
|
+
cwd?: string;
|
|
127
|
+
[k: string]: unknown;
|
|
128
|
+
}
|
|
129
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/core/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Boundary types for the wicked-core-ts JSON surface.
|
|
3
|
+
*
|
|
4
|
+
* core-ts returns every complex result as a JSON string, which the adapter
|
|
5
|
+
* `JSON.parse`s. These interfaces mirror the serde representation of
|
|
6
|
+
* wicked-core's domain types (wicked-core/src/domain.rs, repo.rs, scope.rs) so
|
|
7
|
+
* the daemon never falls back to `any` at the core boundary. They are permissive
|
|
8
|
+
* about forward-additive fields — a newer core that adds fields still parses.
|
|
9
|
+
*
|
|
10
|
+
* ONLY `core/adapter.ts` imports from the native addon itself; the rest of the
|
|
11
|
+
* daemon speaks these daemon-owned shapes, which is what quarantines the
|
|
12
|
+
* FINALIZING core-ts `subscribe` seam to a single file (DES-STUDIO-001 §5.2).
|
|
13
|
+
*/
|
|
14
|
+
export {};
|
|
15
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/core/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { WebSocket } from 'ws';
|
|
2
|
+
import type { CoreEvent } from '../core/types.js';
|
|
3
|
+
export declare function registerClient(ws: WebSocket): void;
|
|
4
|
+
/** Fan one CoreEvent frame out to every connected client, verbatim. */
|
|
5
|
+
export declare function broadcast(event: CoreEvent): void;
|
|
6
|
+
/** Current live client count (diagnostics / tests). */
|
|
7
|
+
export declare function clientCount(): number;
|
|
8
|
+
/** Close every client socket and clear the set (graceful shutdown). */
|
|
9
|
+
export declare function closeAllClients(): void;
|
|
10
|
+
//# sourceMappingURL=bus.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bus.d.ts","sourceRoot":"","sources":["../../src/events/bus.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,IAAI,CAAC;AAC/B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAUlD,wBAAgB,cAAc,CAAC,EAAE,EAAE,SAAS,GAAG,IAAI,CAOlD;AAED,uEAAuE;AACvE,wBAAgB,SAAS,CAAC,KAAK,EAAE,SAAS,GAAG,IAAI,CAchD;AAED,uDAAuD;AACvD,wBAAgB,WAAW,IAAI,MAAM,CAEpC;AAED,uEAAuE;AACvE,wBAAgB,eAAe,IAAI,IAAI,CAStC"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { WebSocket } from 'ws';
|
|
2
|
+
// The daemon's WS fan-out surface. The adapter's single CoreEvent subscription
|
|
3
|
+
// pushes every parsed frame through `broadcast()` to all connected browser
|
|
4
|
+
// sockets, verbatim (the tagged-JSON `{ type, ...fields }` shape — additive-safe;
|
|
5
|
+
// DES-STUDIO-001 §2.1). No `{ type, payload, ts }` envelope: the studio switches
|
|
6
|
+
// directly on the CoreEvent discriminant.
|
|
7
|
+
const clients = new Set();
|
|
8
|
+
export function registerClient(ws) {
|
|
9
|
+
clients.add(ws);
|
|
10
|
+
ws.on('close', () => clients.delete(ws));
|
|
11
|
+
ws.on('error', () => {
|
|
12
|
+
clients.delete(ws);
|
|
13
|
+
ws.terminate();
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
/** Fan one CoreEvent frame out to every connected client, verbatim. */
|
|
17
|
+
export function broadcast(event) {
|
|
18
|
+
const msg = JSON.stringify(event);
|
|
19
|
+
for (const client of clients) {
|
|
20
|
+
if (client.readyState !== WebSocket.OPEN) {
|
|
21
|
+
clients.delete(client);
|
|
22
|
+
continue;
|
|
23
|
+
}
|
|
24
|
+
client.send(msg, (err) => {
|
|
25
|
+
if (err) {
|
|
26
|
+
clients.delete(client);
|
|
27
|
+
client.terminate();
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/** Current live client count (diagnostics / tests). */
|
|
33
|
+
export function clientCount() {
|
|
34
|
+
return clients.size;
|
|
35
|
+
}
|
|
36
|
+
/** Close every client socket and clear the set (graceful shutdown). */
|
|
37
|
+
export function closeAllClients() {
|
|
38
|
+
for (const client of clients) {
|
|
39
|
+
try {
|
|
40
|
+
client.close();
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
/* already closing */
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
clients.clear();
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=bus.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bus.js","sourceRoot":"","sources":["../../src/events/bus.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,IAAI,CAAC;AAG/B,+EAA+E;AAC/E,2EAA2E;AAC3E,kFAAkF;AAClF,iFAAiF;AACjF,0CAA0C;AAE1C,MAAM,OAAO,GAAmB,IAAI,GAAG,EAAE,CAAC;AAE1C,MAAM,UAAU,cAAc,CAAC,EAAa;IAC1C,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAChB,EAAE,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC;IACzC,EAAE,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;QAClB,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QACnB,EAAE,CAAC,SAAS,EAAE,CAAC;IACjB,CAAC,CAAC,CAAC;AACL,CAAC;AAED,uEAAuE;AACvE,MAAM,UAAU,SAAS,CAAC,KAAgB;IACxC,MAAM,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;IAClC,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,IAAI,MAAM,CAAC,UAAU,KAAK,SAAS,CAAC,IAAI,EAAE,CAAC;YACzC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;YACvB,SAAS;QACX,CAAC;QACD,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,GAAW,EAAE,EAAE;YAC/B,IAAI,GAAG,EAAE,CAAC;gBACR,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;gBACvB,MAAM,CAAC,SAAS,EAAE,CAAC;YACrB,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC;AACH,CAAC;AAED,uDAAuD;AACvD,MAAM,UAAU,WAAW;IACzB,OAAO,OAAO,CAAC,IAAI,CAAC;AACtB,CAAC;AAED,uEAAuE;AACvE,MAAM,UAAU,eAAe;IAC7B,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,IAAI,CAAC;YACH,MAAM,CAAC,KAAK,EAAE,CAAC;QACjB,CAAC;QAAC,MAAM,CAAC;YACP,qBAAqB;QACvB,CAAC;IACH,CAAC;IACD,OAAO,CAAC,KAAK,EAAE,CAAC;AAClB,CAAC"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { FastifyInstance } from 'fastify';
|
|
2
|
+
import { WebSocket } from 'ws';
|
|
3
|
+
import type { CoreAdapter } from '../core/adapter.js';
|
|
4
|
+
import type { CoreEvent } from '../core/types.js';
|
|
5
|
+
/**
|
|
6
|
+
* Per-terminal WS fan-out (DES-TERMINAL-001 §6). The daemon's single CoreEvent
|
|
7
|
+
* subscription is the source of truth; this hub routes the `terminalOutput` /
|
|
8
|
+
* `terminalExited` frames for a given terminal id to the ONE browser socket that
|
|
9
|
+
* opened it, keyed by id (a `Map<id, socket>`). Raw PTY output arrives
|
|
10
|
+
* base64-encoded (`bytesB64`); we decode and send it as a BINARY frame so
|
|
11
|
+
* xterm.js writes the exact bytes — control sequences and multi-byte UTF-8
|
|
12
|
+
* survive a text-frame round-trip.
|
|
13
|
+
*/
|
|
14
|
+
export declare class TerminalHub {
|
|
15
|
+
private readonly sockets;
|
|
16
|
+
/**
|
|
17
|
+
* ids whose `terminalExited` we've already routed — so the WS close handler
|
|
18
|
+
* skips a redundant `closeTerminal` (the engine already reaped that child).
|
|
19
|
+
*/
|
|
20
|
+
private readonly exited;
|
|
21
|
+
/** Bind a browser socket to a terminal id (later `terminalOutput` fans out to it). */
|
|
22
|
+
register(id: string, socket: WebSocket): void;
|
|
23
|
+
/** True once a `terminalExited` frame has been routed for this id. */
|
|
24
|
+
hasExited(id: string): boolean;
|
|
25
|
+
/** Drop the socket + exit bookkeeping for a terminal id. */
|
|
26
|
+
unregister(id: string): void;
|
|
27
|
+
/** Live terminal-socket count (diagnostics / tests). */
|
|
28
|
+
size(): number;
|
|
29
|
+
/**
|
|
30
|
+
* Route one CoreEvent frame to the terminal socket it belongs to. Called for
|
|
31
|
+
* EVERY core event; non-terminal frames and frames for an unknown id are no-ops.
|
|
32
|
+
*/
|
|
33
|
+
route(event: CoreEvent): void;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* The per-terminal WS channel: `GET /ws/terminals/:id`. Binds the browser socket
|
|
37
|
+
* to the terminal id in the hub (so `terminalOutput` for that id fans out to it),
|
|
38
|
+
* forwards every inbound browser frame to the PTY as raw stdin bytes, and closes
|
|
39
|
+
* the terminal when the socket drops (unless the PTY already exited on its own).
|
|
40
|
+
*/
|
|
41
|
+
export declare function registerTerminalWs(app: FastifyInstance, adapter: CoreAdapter, hub: TerminalHub): void;
|
|
42
|
+
//# sourceMappingURL=terminals.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"terminals.d.ts","sourceRoot":"","sources":["../../src/events/terminals.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAC/C,OAAO,EAAE,SAAS,EAAgB,MAAM,IAAI,CAAC;AAC7C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAElD;;;;;;;;GAQG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAgC;IACxD;;;OAGG;IACH,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAqB;IAE5C,sFAAsF;IACtF,QAAQ,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,GAAG,IAAI;IAI7C,sEAAsE;IACtE,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO;IAI9B,4DAA4D;IAC5D,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAK5B,wDAAwD;IACxD,IAAI,IAAI,MAAM;IAId;;;OAGG;IACH,KAAK,CAAC,KAAK,EAAE,SAAS,GAAG,IAAI;CAiB9B;AASD;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAChC,GAAG,EAAE,eAAe,EACpB,OAAO,EAAE,WAAW,EACpB,GAAG,EAAE,WAAW,GACf,IAAI,CAgCN"}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { WebSocket } from 'ws';
|
|
2
|
+
/**
|
|
3
|
+
* Per-terminal WS fan-out (DES-TERMINAL-001 §6). The daemon's single CoreEvent
|
|
4
|
+
* subscription is the source of truth; this hub routes the `terminalOutput` /
|
|
5
|
+
* `terminalExited` frames for a given terminal id to the ONE browser socket that
|
|
6
|
+
* opened it, keyed by id (a `Map<id, socket>`). Raw PTY output arrives
|
|
7
|
+
* base64-encoded (`bytesB64`); we decode and send it as a BINARY frame so
|
|
8
|
+
* xterm.js writes the exact bytes — control sequences and multi-byte UTF-8
|
|
9
|
+
* survive a text-frame round-trip.
|
|
10
|
+
*/
|
|
11
|
+
export class TerminalHub {
|
|
12
|
+
sockets = new Map();
|
|
13
|
+
/**
|
|
14
|
+
* ids whose `terminalExited` we've already routed — so the WS close handler
|
|
15
|
+
* skips a redundant `closeTerminal` (the engine already reaped that child).
|
|
16
|
+
*/
|
|
17
|
+
exited = new Set();
|
|
18
|
+
/** Bind a browser socket to a terminal id (later `terminalOutput` fans out to it). */
|
|
19
|
+
register(id, socket) {
|
|
20
|
+
this.sockets.set(id, socket);
|
|
21
|
+
}
|
|
22
|
+
/** True once a `terminalExited` frame has been routed for this id. */
|
|
23
|
+
hasExited(id) {
|
|
24
|
+
return this.exited.has(id);
|
|
25
|
+
}
|
|
26
|
+
/** Drop the socket + exit bookkeeping for a terminal id. */
|
|
27
|
+
unregister(id) {
|
|
28
|
+
this.sockets.delete(id);
|
|
29
|
+
this.exited.delete(id);
|
|
30
|
+
}
|
|
31
|
+
/** Live terminal-socket count (diagnostics / tests). */
|
|
32
|
+
size() {
|
|
33
|
+
return this.sockets.size;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Route one CoreEvent frame to the terminal socket it belongs to. Called for
|
|
37
|
+
* EVERY core event; non-terminal frames and frames for an unknown id are no-ops.
|
|
38
|
+
*/
|
|
39
|
+
route(event) {
|
|
40
|
+
const id = typeof event.id === 'string' ? event.id : undefined;
|
|
41
|
+
if (id === undefined)
|
|
42
|
+
return;
|
|
43
|
+
const socket = this.sockets.get(id);
|
|
44
|
+
if (!socket)
|
|
45
|
+
return;
|
|
46
|
+
if (event.type === 'terminalOutput') {
|
|
47
|
+
if (typeof event.bytesB64 === 'string' && socket.readyState === WebSocket.OPEN) {
|
|
48
|
+
socket.send(Buffer.from(event.bytesB64, 'base64'));
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
else if (event.type === 'terminalExited') {
|
|
52
|
+
// The child is gone. Mark it (so the socket-close handler skips a redundant
|
|
53
|
+
// closeTerminal), then close the browser socket so xterm stops cleanly.
|
|
54
|
+
this.exited.add(id);
|
|
55
|
+
if (socket.readyState === WebSocket.OPEN)
|
|
56
|
+
socket.close();
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/** Normalise a ws frame (Buffer | ArrayBuffer | Buffer[]) to a single Buffer. */
|
|
61
|
+
function toBuffer(data) {
|
|
62
|
+
if (Buffer.isBuffer(data))
|
|
63
|
+
return data;
|
|
64
|
+
if (Array.isArray(data))
|
|
65
|
+
return Buffer.concat(data);
|
|
66
|
+
return Buffer.from(data);
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* The per-terminal WS channel: `GET /ws/terminals/:id`. Binds the browser socket
|
|
70
|
+
* to the terminal id in the hub (so `terminalOutput` for that id fans out to it),
|
|
71
|
+
* forwards every inbound browser frame to the PTY as raw stdin bytes, and closes
|
|
72
|
+
* the terminal when the socket drops (unless the PTY already exited on its own).
|
|
73
|
+
*/
|
|
74
|
+
export function registerTerminalWs(app, adapter, hub) {
|
|
75
|
+
app.get('/ws/terminals/:id', { websocket: true }, (socket, req) => {
|
|
76
|
+
const { id } = req.params;
|
|
77
|
+
const ws = socket;
|
|
78
|
+
hub.register(id, ws);
|
|
79
|
+
// Browser keystrokes (xterm.onData → ws.send): text or binary, both are raw
|
|
80
|
+
// input bytes for the PTY. Failures (unknown/closed id) are swallowed — the
|
|
81
|
+
// exit/close path tears the socket down.
|
|
82
|
+
ws.on('message', (data) => {
|
|
83
|
+
void adapter.writeTerminal(id, toBuffer(data)).catch(() => {
|
|
84
|
+
/* terminal gone */
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
let torn = false;
|
|
88
|
+
const teardown = () => {
|
|
89
|
+
if (torn)
|
|
90
|
+
return;
|
|
91
|
+
torn = true;
|
|
92
|
+
const alreadyExited = hub.hasExited(id);
|
|
93
|
+
hub.unregister(id);
|
|
94
|
+
// Socket dropped by the browser (unmount) → close the PTY. Skip when the PTY
|
|
95
|
+
// already exited (engine reaped it; a second closeTerminal would reject).
|
|
96
|
+
if (!alreadyExited) {
|
|
97
|
+
void adapter.closeTerminal(id).catch(() => {
|
|
98
|
+
/* already closed / unknown id */
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
ws.on('close', teardown);
|
|
103
|
+
ws.on('error', teardown);
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
//# sourceMappingURL=terminals.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"terminals.js","sourceRoot":"","sources":["../../src/events/terminals.ts"],"names":[],"mappings":"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"}
|