harnery 0.44.0 → 0.45.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commander.js +1 -0
- package/dist/commands/files.d.ts +2 -2
- package/dist/commands/files.d.ts.map +1 -1
- package/dist/commands/files.js +7 -4
- package/dist/commands/servers.d.ts.map +1 -1
- package/dist/commands/servers.js +11 -17
- package/dist/commands/tunnel.d.ts +15 -0
- package/dist/commands/tunnel.d.ts.map +1 -1
- package/dist/commands/tunnel.js +453 -153
- package/dist/commands/turn-rollback.d.ts +5 -0
- package/dist/commands/turn-rollback.d.ts.map +1 -0
- package/dist/commands/turn-rollback.js +76 -0
- package/dist/commands/workflow.d.ts.map +1 -1
- package/dist/commands/workflow.js +44 -0
- package/dist/core/agents/coord-client.d.ts.map +1 -1
- package/dist/core/agents/coord-client.js +18 -0
- package/dist/core/agents/state/proc-start.d.ts +5 -6
- package/dist/core/agents/state/proc-start.d.ts.map +1 -1
- package/dist/core/agents/state/proc-start.js +13 -9
- package/dist/core/agents/state/process-handle.d.ts +26 -0
- package/dist/core/agents/state/process-handle.d.ts.map +1 -0
- package/dist/core/agents/state/process-handle.js +121 -0
- package/dist/core/artifacts/delivery-card.d.ts.map +1 -1
- package/dist/core/artifacts/delivery-card.js +2 -1
- package/dist/core/resources/service.d.ts +2 -16
- package/dist/core/resources/service.d.ts.map +1 -1
- package/dist/core/resources/service.js +1 -78
- package/dist/core/semantic/service.d.ts.map +1 -1
- package/dist/core/semantic/service.js +2 -9
- package/dist/core/servers/index.d.ts +12 -11
- package/dist/core/servers/index.d.ts.map +1 -1
- package/dist/core/servers/index.js +194 -81
- package/dist/core/servers/tunnels.d.ts +5 -3
- package/dist/core/servers/tunnels.d.ts.map +1 -1
- package/dist/core/servers/tunnels.js +45 -2
- package/dist/core/supervisor/service.d.ts +2 -4
- package/dist/core/supervisor/service.d.ts.map +1 -1
- package/dist/core/turn-rollback/content.d.ts +53 -0
- package/dist/core/turn-rollback/content.d.ts.map +1 -0
- package/dist/core/turn-rollback/content.js +396 -0
- package/dist/core/turn-rollback/index.d.ts +72 -0
- package/dist/core/turn-rollback/index.d.ts.map +1 -0
- package/dist/core/turn-rollback/index.js +443 -0
- package/dist/core/workflow/index.d.ts +4 -2
- package/dist/core/workflow/index.d.ts.map +1 -1
- package/dist/core/workflow/index.js +2 -1
- package/dist/core/workflow/recovery-audit.d.ts +30 -0
- package/dist/core/workflow/recovery-audit.d.ts.map +1 -0
- package/dist/core/workflow/recovery-audit.js +116 -0
- package/dist/core/workflow/run-state.d.ts +19 -2
- package/dist/core/workflow/run-state.d.ts.map +1 -1
- package/dist/core/workflow/run-state.js +113 -8
- package/dist/core/workflow/workspaces/local-git.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/local-git.js +70 -14
- package/dist/core/workflow/workspaces/reclaim.d.ts +6 -1
- package/dist/core/workflow/workspaces/reclaim.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/recovery.d.ts +2 -0
- package/dist/core/workflow/workspaces/recovery.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/recovery.js +24 -0
- package/dist/lib/tunnel/state.d.ts +13 -6
- package/dist/lib/tunnel/state.d.ts.map +1 -1
- package/dist/lib/tunnel/state.js +58 -12
- package/package.json +6 -1
- package/src/commander.ts +10 -0
- package/src/commands/files.ts +13 -5
- package/src/commands/servers.ts +12 -23
- package/src/commands/tunnel.ts +533 -166
- package/src/commands/turn-rollback.ts +111 -0
- package/src/commands/workflow.ts +59 -0
- package/src/core/agents/coord-client.ts +20 -0
- package/src/core/agents/state/proc-start.ts +14 -11
- package/src/core/agents/state/process-handle.ts +165 -0
- package/src/core/artifacts/delivery-card.ts +7 -1
- package/src/core/resources/service.ts +3 -88
- package/src/core/semantic/service.ts +2 -8
- package/src/core/servers/index.ts +234 -85
- package/src/core/servers/tunnels.ts +55 -3
- package/src/core/supervisor/service.ts +2 -3
- package/src/core/turn-rollback/content.ts +499 -0
- package/src/core/turn-rollback/index.ts +580 -0
- package/src/core/workflow/index.ts +12 -1
- package/src/core/workflow/recovery-audit.ts +181 -0
- package/src/core/workflow/run-state.ts +137 -8
- package/src/core/workflow/workspaces/local-git.ts +86 -15
- package/src/core/workflow/workspaces/reclaim.ts +4 -0
- package/src/core/workflow/workspaces/recovery.ts +23 -0
- package/src/lib/tunnel/state.ts +70 -15
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../../src/core/semantic/service.ts"],"names":[],"mappings":"AAoBA,OAAO,EAGL,iBAAiB,EAClB,MAAM,uBAAuB,CAAC;AAU/B,OAAO,EAAmB,KAAK,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAErE,OAAO,EAIL,KAAK,qBAAqB,EAC1B,KAAK,2BAA2B,EACjC,MAAM,qBAAqB,CAAC;AAc7B,OAAO,EACL,yBAAyB,EACzB,sCAAsC,EACtC,KAAK,wBAAwB,EAC7B,KAAK,oBAAoB,EACzB,KAAK,qBAAqB,EAC1B,KAAK,2BAA2B,GACjC,MAAM,qBAAqB,CAAC;AAE7B,eAAO,MAAM,oCAAoC,OAAQ,CAAC;AAC1D,eAAO,MAAM,gCAAgC,OAAQ,CAAC;AACtD,eAAO,MAAM,qCAAqC,OAAQ,CAAC;AAK3D,eAAO,MAAM,yCAAyC,QAAS,CAAC;AA2BhE,MAAM,WAAW,6BAA6B;IAC5C,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC;IACjB,SAAS,CAAC,EAAE,OAAO,iBAAiB,CAAC;IACrC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE;QAChB,SAAS,EAAE,MAAM,CAAC;QAClB,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,UAAU,EAAE,MAAM,CAAC;QACnB,UAAU,EAAE,MAAM,OAAO,CAAC;KAC3B,KAAK,OAAO,CAAC,kBAAkB,CAAC,CAAC;IAClC,WAAW,CAAC,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACvD;AAED,MAAM,MAAM,0BAA0B,GAClC,SAAS,GACT,SAAS,GACT,QAAQ,GACR,UAAU,GACV,aAAa,CAAC;AAElB,MAAM,WAAW,2BAA2B;IAC1C,KAAK,EAAE,0BAA0B,CAAC;IAClC,MAAM,CAAC,EAAE,qBAAqB,CAAC;IAC/B,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,iCAAiC;IAChD,UAAU,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,qBAAqB,CAAC;IACzD,KAAK,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,qBAAqB,CAAC,CAAC;IAC7D,QAAQ,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC;IACzC,QAAQ,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC;CAC1C;AAED,wBAAsB,oBAAoB,CACxC,YAAY,EAAE,MAAM,EACpB,OAAO,GAAE;IAAE,YAAY,CAAC,EAAE,MAAM,CAAA;CAAO,GACtC,OAAO,CAAC,qBAAqB,CAAC,CAgDhC;AAED;;;;;;GAMG;AACH,wBAAsB,4BAA4B,CAChD,YAAY,EAAE,MAAM,EACpB,SAAS,GAAE,OAAO,CAAC,iCAAiC,CAAM,GACzD,OAAO,CAAC,2BAA2B,CAAC,CAsBtC;AAED,wBAAgB,0BAA0B,CAAC,YAAY,EAAE,MAAM,GAAG,qBAAqB,
|
|
1
|
+
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../../src/core/semantic/service.ts"],"names":[],"mappings":"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"}
|
|
@@ -132,20 +132,13 @@ export async function ensureSemanticServiceRunning(coordRootRaw, overrides = {})
|
|
|
132
132
|
}
|
|
133
133
|
export function requestSemanticServiceStop(coordRootRaw) {
|
|
134
134
|
const coordRoot = resolve(coordRootRaw);
|
|
135
|
-
const status = readSemanticServiceStatus(coordRoot);
|
|
136
135
|
mkdirSync(semanticPaths(coordRoot).root, { recursive: true, mode: stateDirMode() });
|
|
137
136
|
writePrivateJsonAtomic(semanticPaths(coordRoot).stop, {
|
|
138
137
|
requested_at: new Date().toISOString(),
|
|
139
138
|
requested_by_pid: process.pid,
|
|
140
139
|
});
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
process.kill(status.record.pid, "SIGTERM");
|
|
144
|
-
}
|
|
145
|
-
catch {
|
|
146
|
-
// The durable stop request remains for a racing or restarted daemon.
|
|
147
|
-
}
|
|
148
|
-
}
|
|
140
|
+
// This record has no generation witness. The durable request is the stop
|
|
141
|
+
// authority; a numeric PID cannot authorize an additional signal.
|
|
149
142
|
return readSemanticServiceStatus(coordRoot);
|
|
150
143
|
}
|
|
151
144
|
export async function runSemanticServiceDaemon(input) {
|
|
@@ -21,15 +21,18 @@
|
|
|
21
21
|
*/
|
|
22
22
|
import { type ProcessGenerationSignalDependencies, type ProcessGenerationVerdict } from "../agents/state/proc-start.js";
|
|
23
23
|
import { type ScanSupport } from "./net.js";
|
|
24
|
+
export { processStartToken, signalIfSameGeneration, verifyProcessGeneration, } from "../agents/state/proc-start.js";
|
|
25
|
+
export { acquireNoClobberLease } from "../workflow/workspaces/leases.js";
|
|
24
26
|
export { ancestry, establishedConnectionCounts, listListeningPorts, listListeningSockets, lsofConnectionCounts, lsofListeningSockets, parseElapsed, parseLsofListen, parseProcNetTcp, processStartedAt, psProcessInfo, readProcessInfo, scanSupport, } from "./net.js";
|
|
25
|
-
export { tunnelServerId, tunnelServerInput } from "./tunnels.js";
|
|
26
|
-
export declare const SERVER_RECORD_SCHEMA_VERSION =
|
|
27
|
+
export { createTunnelReadinessVerifier, tunnelServerId, tunnelServerInput } from "./tunnels.js";
|
|
28
|
+
export declare const SERVER_RECORD_SCHEMA_VERSION = 2;
|
|
27
29
|
export declare const SERVER_DEFAULT_IDLE_HOURS = 2;
|
|
28
30
|
export declare const SERVER_DEFAULT_OWNER_STALE_HOURS = 24;
|
|
29
31
|
export type ServerKind = "service" | "session";
|
|
30
32
|
export interface ServerRecord {
|
|
31
33
|
schema_version: typeof SERVER_RECORD_SCHEMA_VERSION;
|
|
32
34
|
id: string;
|
|
35
|
+
registration_id: string;
|
|
33
36
|
kind: ServerKind;
|
|
34
37
|
/** Free-form family name: `preview`, `tunnel`, `dashboard`, ... */
|
|
35
38
|
type: string;
|
|
@@ -119,13 +122,11 @@ export declare function isValidServerId(id: string): boolean;
|
|
|
119
122
|
export declare function registerServer(input: RegisterServerInput, options?: ServerOptions): ServerRecord | null;
|
|
120
123
|
/** Best-effort registration that never throws (for server start paths). */
|
|
121
124
|
export declare function tryRegisterServer(input: RegisterServerInput, options?: ServerOptions): ServerRecord | null;
|
|
122
|
-
/**
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
export declare function
|
|
127
|
-
pid?: number;
|
|
128
|
-
}): boolean;
|
|
125
|
+
/** Remove only the exact registration receipt observed by the caller. */
|
|
126
|
+
export declare function unregisterServer(record: ServerRecord, options?: ServerOptions): boolean;
|
|
127
|
+
/** Hold the exact registration receipt until a synchronous or asynchronous effect settles. */
|
|
128
|
+
export declare function withServerRegistrationCustody<T>(record: ServerRecord, options: ServerOptions, effect: () => PromiseLike<T>): Promise<T>;
|
|
129
|
+
export declare function withServerRegistrationCustody<T>(record: ServerRecord, options: ServerOptions, effect: () => T): T;
|
|
129
130
|
export declare function readServer(id: string, options?: ServerOptions): ServerRecord | null;
|
|
130
131
|
/** Mark a server as used now. */
|
|
131
132
|
export declare function touchServer(id: string, options?: ServerOptions & {
|
|
@@ -186,8 +187,8 @@ export interface ServerProcessDependencies extends ProcessGenerationSignalDepend
|
|
|
186
187
|
}
|
|
187
188
|
/**
|
|
188
189
|
* Stop a verified server with SIGTERM, then SIGKILL after the grace period.
|
|
189
|
-
*
|
|
190
|
-
* a fresh generation check. Unknown or different generations retain the record.
|
|
190
|
+
* Every signal targets one retained process handle, including auxiliary processes.
|
|
191
|
+
* Each target requires a fresh generation check. Unknown or different generations retain the record.
|
|
191
192
|
*/
|
|
192
193
|
export declare function stopServer(record: ServerRecord, options?: ServerOptions & {
|
|
193
194
|
graceMs?: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/servers/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAQH,OAAO,EACL,KAAK,mCAAmC,EACxC,KAAK,wBAAwB,EAI9B,MAAM,+BAA+B,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/servers/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
* which `gc` never stops; a record without an owner is never stopped
|
|
20
20
|
* automatically.
|
|
21
21
|
*/
|
|
22
|
-
import { createHash } from "node:crypto";
|
|
22
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
23
23
|
import { existsSync, mkdirSync, readdirSync, readFileSync, rmSync, statSync } from "node:fs";
|
|
24
24
|
import { hostname } from "node:os";
|
|
25
25
|
import { basename, isAbsolute, join, relative, resolve } from "node:path";
|
|
@@ -31,10 +31,13 @@ import { readCoordinationViewV3, requireAuthoritySafeCoordinationViewV3, } from
|
|
|
31
31
|
import { liveInstanceIdV3 } from "../events/v3/live-route-observer.js";
|
|
32
32
|
import { resolveOwner } from "../hooks/resolve/owner.js";
|
|
33
33
|
import { writePrivateJsonAtomic } from "../storage/atomic-json.js";
|
|
34
|
+
import { acquireNoClobberLease } from "../workflow/workspaces/leases.js";
|
|
34
35
|
import { ancestry, establishedConnectionCounts, listListeningPorts, listListeningSockets, processStartedAt, readProcessInfo, scanSupport, } from "./net.js";
|
|
36
|
+
export { processStartToken, signalIfSameGeneration, verifyProcessGeneration, } from "../agents/state/proc-start.js";
|
|
37
|
+
export { acquireNoClobberLease } from "../workflow/workspaces/leases.js";
|
|
35
38
|
export { ancestry, establishedConnectionCounts, listListeningPorts, listListeningSockets, lsofConnectionCounts, lsofListeningSockets, parseElapsed, parseLsofListen, parseProcNetTcp, processStartedAt, psProcessInfo, readProcessInfo, scanSupport, } from "./net.js";
|
|
36
|
-
export { tunnelServerId, tunnelServerInput } from "./tunnels.js";
|
|
37
|
-
export const SERVER_RECORD_SCHEMA_VERSION =
|
|
39
|
+
export { createTunnelReadinessVerifier, tunnelServerId, tunnelServerInput } from "./tunnels.js";
|
|
40
|
+
export const SERVER_RECORD_SCHEMA_VERSION = 2;
|
|
38
41
|
export const SERVER_DEFAULT_IDLE_HOURS = 2;
|
|
39
42
|
export const SERVER_DEFAULT_OWNER_STALE_HOURS = 24;
|
|
40
43
|
const TOUCH_INTERVAL_MS = 60_000;
|
|
@@ -83,40 +86,54 @@ export function registerServer(input, options) {
|
|
|
83
86
|
!(Number.isInteger(input.port) && input.port > 0 && input.port < 65536))
|
|
84
87
|
throw new Error(`invalid server port: ${input.port}`);
|
|
85
88
|
const id = input.id ?? serverId(input.type, input.scope);
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
const
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
89
|
+
return withServerMutation(coordRoot, id, () => {
|
|
90
|
+
const path = recordPath(coordRoot, id);
|
|
91
|
+
const now = new Date().toISOString();
|
|
92
|
+
const startToken = input.start_token ?? processStartToken(pid);
|
|
93
|
+
const pids = [...new Set(input.pids?.filter((p) => Number.isSafeInteger(p) && p > 0))];
|
|
94
|
+
const pidsStartTokens = {};
|
|
95
|
+
for (const auxiliaryPid of pids) {
|
|
96
|
+
const token = processStartToken(auxiliaryPid);
|
|
97
|
+
if (token)
|
|
98
|
+
pidsStartTokens[auxiliaryPid] = token;
|
|
99
|
+
}
|
|
100
|
+
const owner = input.owner === undefined ? callingOwner(coordRoot) : (input.owner ?? undefined);
|
|
101
|
+
const record = {
|
|
102
|
+
schema_version: SERVER_RECORD_SCHEMA_VERSION,
|
|
103
|
+
id,
|
|
104
|
+
registration_id: randomUUID(),
|
|
105
|
+
kind: input.kind,
|
|
106
|
+
type: input.type,
|
|
107
|
+
label: input.label ?? id,
|
|
108
|
+
...(input.url ? { url: input.url } : {}),
|
|
109
|
+
...(input.port ? { port: input.port } : {}),
|
|
110
|
+
pid,
|
|
111
|
+
...(startToken ? { start_token: startToken } : {}),
|
|
112
|
+
...(pids.length ? { pids, pids_start_tokens: pidsStartTokens } : {}),
|
|
113
|
+
host: hostname(),
|
|
114
|
+
...(input.scope ? { scope: resolve(input.scope) } : {}),
|
|
115
|
+
...(input.cwd ? { cwd: resolve(input.cwd) } : {}),
|
|
116
|
+
...(input.log ? { log: resolve(input.log) } : {}),
|
|
117
|
+
...(owner ? { owner } : {}),
|
|
118
|
+
started_at: input.started_at ?? now,
|
|
119
|
+
registered_at: now,
|
|
120
|
+
last_active_at: now,
|
|
121
|
+
};
|
|
122
|
+
if (input.start_token !== undefined &&
|
|
123
|
+
verifyProcessGeneration(pid, input.start_token) !== "same")
|
|
124
|
+
throw new Error(`pid ${pid} generation could not be verified before registration`);
|
|
125
|
+
if (existsSync(path)) {
|
|
126
|
+
const existing = readRecord(path);
|
|
127
|
+
if (!existing)
|
|
128
|
+
throw new Error(`registration ${id} custody is unknown: invalid or older server record retained`);
|
|
129
|
+
if (sameProcessCustody(existing, record) && serverState(existing) === "running")
|
|
130
|
+
return existing;
|
|
131
|
+
if (existing.host !== hostname() || !allProcessesAbsent(existing))
|
|
132
|
+
throw new Error(`registration ${id} custody is held by a live or unverifiable server`);
|
|
133
|
+
}
|
|
134
|
+
writePrivateJsonAtomic(path, record);
|
|
135
|
+
return record;
|
|
136
|
+
});
|
|
120
137
|
}
|
|
121
138
|
/** Best-effort registration that never throws (for server start paths). */
|
|
122
139
|
export function tryRegisterServer(input, options) {
|
|
@@ -139,43 +156,106 @@ function callingOwner(coordRoot) {
|
|
|
139
156
|
return undefined;
|
|
140
157
|
}
|
|
141
158
|
}
|
|
142
|
-
/**
|
|
143
|
-
|
|
144
|
-
* is removed, so a replacement that re-registered the same id survives.
|
|
145
|
-
*/
|
|
146
|
-
export function unregisterServer(id, options) {
|
|
159
|
+
/** Remove only the exact registration receipt observed by the caller. */
|
|
160
|
+
export function unregisterServer(record, options) {
|
|
147
161
|
const coordRoot = rootOf(options);
|
|
148
|
-
if (!coordRoot || !isValidServerId(id))
|
|
162
|
+
if (!coordRoot || !record || !isValidServerId(record.id))
|
|
149
163
|
return false;
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
164
|
+
return withServerMutation(coordRoot, record.id, () => removeServerReceipt(coordRoot, record));
|
|
165
|
+
}
|
|
166
|
+
export function withServerRegistrationCustody(record, options, effect) {
|
|
167
|
+
const coordRoot = rootOf(options);
|
|
168
|
+
if (!coordRoot)
|
|
169
|
+
throw new Error("registration custody root is unavailable");
|
|
170
|
+
const lease = serverMutationLease(coordRoot, record.id);
|
|
171
|
+
try {
|
|
172
|
+
const current = readServer(record.id, { coordRoot });
|
|
173
|
+
if (!current ||
|
|
174
|
+
current.registration_id !== record.registration_id ||
|
|
175
|
+
!sameProcessCustody(current, record))
|
|
176
|
+
throw new Error(`registration ${record.id} custody changed`);
|
|
177
|
+
const result = effect();
|
|
178
|
+
if (result !== null &&
|
|
179
|
+
(typeof result === "object" || typeof result === "function") &&
|
|
180
|
+
typeof result.then === "function")
|
|
181
|
+
return Promise.resolve(result).finally(() => lease.release());
|
|
182
|
+
lease.release();
|
|
183
|
+
return result;
|
|
155
184
|
}
|
|
156
|
-
|
|
185
|
+
catch (error) {
|
|
186
|
+
lease.release();
|
|
187
|
+
throw error;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
function removeServerReceipt(coordRoot, record) {
|
|
191
|
+
const path = recordPath(coordRoot, record.id);
|
|
192
|
+
const current = readRecord(path);
|
|
193
|
+
if (!current ||
|
|
194
|
+
current.registration_id !== record.registration_id ||
|
|
195
|
+
!sameProcessCustody(current, record))
|
|
157
196
|
return false;
|
|
158
|
-
rmSync(path
|
|
197
|
+
rmSync(path);
|
|
159
198
|
return true;
|
|
160
199
|
}
|
|
200
|
+
function serverMutationLease(coordRoot, id) {
|
|
201
|
+
recordPath(coordRoot, id);
|
|
202
|
+
const scope = `server-registration-${id}`;
|
|
203
|
+
return acquireNoClobberLease({
|
|
204
|
+
path: join(serversDir(coordRoot), ".custody", id),
|
|
205
|
+
scope,
|
|
206
|
+
authoritySha256: createHash("sha256").update(scope).digest("hex"),
|
|
207
|
+
staleAfterMs: 1,
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
function withServerMutation(coordRoot, id, operation) {
|
|
211
|
+
const lease = serverMutationLease(coordRoot, id);
|
|
212
|
+
try {
|
|
213
|
+
return operation();
|
|
214
|
+
}
|
|
215
|
+
finally {
|
|
216
|
+
lease.release();
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
function sameProcessCustody(a, b) {
|
|
220
|
+
const helpers = (record) => [...new Set(record.pids ?? [])]
|
|
221
|
+
.sort((x, y) => x - y)
|
|
222
|
+
.map((pid) => [pid, record.pids_start_tokens?.[pid] ?? null]);
|
|
223
|
+
return (a.schema_version === SERVER_RECORD_SCHEMA_VERSION &&
|
|
224
|
+
b.schema_version === SERVER_RECORD_SCHEMA_VERSION &&
|
|
225
|
+
a.id === b.id &&
|
|
226
|
+
a.pid === b.pid &&
|
|
227
|
+
a.start_token === b.start_token &&
|
|
228
|
+
a.host === b.host &&
|
|
229
|
+
a.kind === b.kind &&
|
|
230
|
+
a.type === b.type &&
|
|
231
|
+
a.scope === b.scope &&
|
|
232
|
+
a.owner?.instance_id === b.owner?.instance_id &&
|
|
233
|
+
JSON.stringify(helpers(a)) === JSON.stringify(helpers(b)));
|
|
234
|
+
}
|
|
161
235
|
export function readServer(id, options) {
|
|
162
236
|
const coordRoot = rootOf(options);
|
|
163
237
|
if (!coordRoot || !isValidServerId(id))
|
|
164
238
|
return null;
|
|
165
|
-
|
|
239
|
+
const path = recordPath(coordRoot, id);
|
|
240
|
+
const record = readRecord(path);
|
|
241
|
+
if (!record && existsSync(path))
|
|
242
|
+
throw new Error(`server record ${id} is invalid or uses an older schema; retained without custody`);
|
|
243
|
+
return record;
|
|
166
244
|
}
|
|
167
245
|
/** Mark a server as used now. */
|
|
168
246
|
export function touchServer(id, options) {
|
|
169
247
|
const coordRoot = rootOf(options);
|
|
170
248
|
if (!coordRoot || !isValidServerId(id))
|
|
171
249
|
return;
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
250
|
+
withServerMutation(coordRoot, id, () => {
|
|
251
|
+
const path = recordPath(coordRoot, id);
|
|
252
|
+
const record = readRecord(path);
|
|
253
|
+
if (!record)
|
|
254
|
+
return;
|
|
255
|
+
writePrivateJsonAtomic(path, {
|
|
256
|
+
...record,
|
|
257
|
+
last_active_at: (options?.now ?? new Date()).toISOString(),
|
|
258
|
+
});
|
|
179
259
|
});
|
|
180
260
|
}
|
|
181
261
|
/**
|
|
@@ -204,6 +284,8 @@ function readRecord(path) {
|
|
|
204
284
|
const value = JSON.parse(readFileSync(path, "utf8"));
|
|
205
285
|
if (value?.schema_version !== SERVER_RECORD_SCHEMA_VERSION ||
|
|
206
286
|
typeof value.id !== "string" ||
|
|
287
|
+
typeof value.registration_id !== "string" ||
|
|
288
|
+
!/^[0-9a-f-]{36}$/.test(value.registration_id) ||
|
|
207
289
|
!Number.isSafeInteger(value.pid) ||
|
|
208
290
|
(value.kind !== "service" && value.kind !== "session"))
|
|
209
291
|
return null;
|
|
@@ -222,8 +304,12 @@ export function readServerRecords(options) {
|
|
|
222
304
|
return [];
|
|
223
305
|
return readdirSync(dir)
|
|
224
306
|
.filter((name) => name.endsWith(".json"))
|
|
225
|
-
.map((name) =>
|
|
226
|
-
|
|
307
|
+
.map((name) => {
|
|
308
|
+
const record = readRecord(join(dir, name));
|
|
309
|
+
if (!record)
|
|
310
|
+
throw new Error(`server record ${name} is invalid or uses an older schema; retained without custody`);
|
|
311
|
+
return record;
|
|
312
|
+
})
|
|
227
313
|
.sort((a, b) => a.kind.localeCompare(b.kind) || a.type.localeCompare(b.type) || a.id.localeCompare(b.id));
|
|
228
314
|
}
|
|
229
315
|
export function pidAlive(pid) {
|
|
@@ -294,7 +380,7 @@ export function listServers(options) {
|
|
|
294
380
|
records.forEach((record, i) => {
|
|
295
381
|
const state = states[i];
|
|
296
382
|
if (state === "dead" && allProcessesAbsent(record) && options?.prune !== false) {
|
|
297
|
-
unregisterServer(record
|
|
383
|
+
unregisterServer(record, { coordRoot });
|
|
298
384
|
pruned.push(record.id);
|
|
299
385
|
return;
|
|
300
386
|
}
|
|
@@ -427,11 +513,49 @@ export function planServerGc(views, policy) {
|
|
|
427
513
|
}
|
|
428
514
|
/**
|
|
429
515
|
* Stop a verified server with SIGTERM, then SIGKILL after the grace period.
|
|
430
|
-
*
|
|
431
|
-
* a fresh generation check. Unknown or different generations retain the record.
|
|
516
|
+
* Every signal targets one retained process handle, including auxiliary processes.
|
|
517
|
+
* Each target requires a fresh generation check. Unknown or different generations retain the record.
|
|
432
518
|
*/
|
|
433
519
|
export async function stopServer(record, options) {
|
|
434
520
|
const coordRoot = rootOf(options);
|
|
521
|
+
if (!coordRoot)
|
|
522
|
+
return {
|
|
523
|
+
id: record.id,
|
|
524
|
+
stopped: false,
|
|
525
|
+
method: "refused",
|
|
526
|
+
generation: "unknown",
|
|
527
|
+
detail: "registration custody root is unavailable",
|
|
528
|
+
};
|
|
529
|
+
let lease;
|
|
530
|
+
try {
|
|
531
|
+
lease = serverMutationLease(coordRoot, record.id);
|
|
532
|
+
}
|
|
533
|
+
catch {
|
|
534
|
+
return {
|
|
535
|
+
id: record.id,
|
|
536
|
+
stopped: false,
|
|
537
|
+
method: "refused",
|
|
538
|
+
generation: "unknown",
|
|
539
|
+
detail: "registration custody is unavailable",
|
|
540
|
+
};
|
|
541
|
+
}
|
|
542
|
+
try {
|
|
543
|
+
const current = readServer(record.id, { coordRoot });
|
|
544
|
+
if (!current || current.registration_id !== record.registration_id)
|
|
545
|
+
return {
|
|
546
|
+
id: record.id,
|
|
547
|
+
stopped: false,
|
|
548
|
+
method: "refused",
|
|
549
|
+
generation: "unknown",
|
|
550
|
+
detail: "registration custody changed; retained current server",
|
|
551
|
+
};
|
|
552
|
+
return await stopServerWithCustody(record, coordRoot, options);
|
|
553
|
+
}
|
|
554
|
+
finally {
|
|
555
|
+
lease.release();
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
async function stopServerWithCustody(record, coordRoot, options) {
|
|
435
559
|
const graceMs = options?.graceMs ?? 5_000;
|
|
436
560
|
const dependencies = options?.signalDependencies ?? {};
|
|
437
561
|
const targets = [
|
|
@@ -449,7 +573,7 @@ export async function stopServer(record, options) {
|
|
|
449
573
|
});
|
|
450
574
|
const finish = (result) => {
|
|
451
575
|
if (result.stopped || result.method === "not-running")
|
|
452
|
-
|
|
576
|
+
removeServerReceipt(coordRoot, record);
|
|
453
577
|
return result;
|
|
454
578
|
};
|
|
455
579
|
if (record.host !== hostname())
|
|
@@ -471,6 +595,9 @@ export async function stopServer(record, options) {
|
|
|
471
595
|
}
|
|
472
596
|
if (targets.every((target) => processPresence(target.pid, dependencies.probePid) === "absent"))
|
|
473
597
|
return finish({ id: record.id, stopped: false, method: "not-running" });
|
|
598
|
+
const current = readServer(record.id, { coordRoot });
|
|
599
|
+
if (!current || !sameProcessCustody(current, record))
|
|
600
|
+
return refuse(record.pid, "unknown");
|
|
474
601
|
for (const name of ["SIGTERM", "SIGKILL"]) {
|
|
475
602
|
for (const target of targets) {
|
|
476
603
|
const presence = processPresence(target.pid, dependencies.probePid);
|
|
@@ -478,11 +605,9 @@ export async function stopServer(record, options) {
|
|
|
478
605
|
continue;
|
|
479
606
|
if (presence === "unknown")
|
|
480
607
|
return refuse(target.pid, "unknown");
|
|
481
|
-
const result = target.pid
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
if (result.verdict !== "same")
|
|
485
|
-
return refuse(target.pid, result.verdict);
|
|
608
|
+
const result = signalIfSameGeneration(target.pid, target.token, name, dependencies);
|
|
609
|
+
if (!result.sent || result.verdict !== "same")
|
|
610
|
+
return refuse(target.pid, result.verdict === "same" ? "unknown" : result.verdict);
|
|
486
611
|
}
|
|
487
612
|
const observed = await waitForExit(targets, name === "SIGTERM" ? graceMs : 2_000, dependencies);
|
|
488
613
|
if (observed.refused)
|
|
@@ -498,18 +623,6 @@ export async function stopServer(record, options) {
|
|
|
498
623
|
detail: "process survived SIGKILL",
|
|
499
624
|
};
|
|
500
625
|
}
|
|
501
|
-
function signalServerLeader(pid, token, name, dependencies) {
|
|
502
|
-
// The leader's generation authorizes its group. A failed group signal must
|
|
503
|
-
// not reuse that observation for the direct-PID fallback.
|
|
504
|
-
const kill = dependencies.kill ?? process.kill;
|
|
505
|
-
const group = signalIfSameGeneration(pid, token, name, {
|
|
506
|
-
readToken: dependencies.readToken,
|
|
507
|
-
kill: (leader, signal) => kill(-leader, signal),
|
|
508
|
-
});
|
|
509
|
-
return group.sent || group.verdict !== "same"
|
|
510
|
-
? group
|
|
511
|
-
: signalIfSameGeneration(pid, token, name, dependencies);
|
|
512
|
-
}
|
|
513
626
|
async function waitForExit(targets, waitMs, dependencies) {
|
|
514
627
|
const deadline = Date.now() + waitMs;
|
|
515
628
|
while (true) {
|
|
@@ -2,10 +2,12 @@
|
|
|
2
2
|
* Tunnels as registry services.
|
|
3
3
|
*
|
|
4
4
|
* `tunnel up` and `tunnel reload` register the new gate; `tunnel down` removes
|
|
5
|
-
* its record.
|
|
5
|
+
* its record. Missing original witnesses remain unknown and cannot authorize actions.
|
|
6
6
|
*/
|
|
7
|
-
import { type TunnelState } from "../../lib/tunnel/state.js";
|
|
8
|
-
import type
|
|
7
|
+
import { type TunnelReadinessVerifier, type TunnelState } from "../../lib/tunnel/state.js";
|
|
8
|
+
import { type RegisterServerInput, type ServerOptions } from "./index.js";
|
|
9
|
+
/** Verify only witnesses saved by the original tunnel registration. */
|
|
10
|
+
export declare function createTunnelReadinessVerifier(options: ServerOptions): TunnelReadinessVerifier;
|
|
9
11
|
export declare function tunnelServerId(name: string): string;
|
|
10
12
|
/** Register the new gate; saved helper PIDs have no generation witnesses. */
|
|
11
13
|
export declare function tunnelServerInput(state: TunnelState, coordRoot: string): RegisterServerInput;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tunnels.d.ts","sourceRoot":"","sources":["../../../src/core/servers/tunnels.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;
|
|
1
|
+
{"version":3,"file":"tunnels.d.ts","sourceRoot":"","sources":["../../../src/core/servers/tunnels.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,EAEL,KAAK,uBAAuB,EAC5B,KAAK,WAAW,EACjB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EACL,KAAK,mBAAmB,EAExB,KAAK,aAAa,EAGnB,MAAM,YAAY,CAAC;AAEpB,uEAAuE;AACvE,wBAAgB,6BAA6B,CAAC,OAAO,EAAE,aAAa,GAAG,uBAAuB,CAiC7F;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEnD;AAED,6EAA6E;AAC7E,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,GAAG,mBAAmB,CAmB5F"}
|
|
@@ -2,15 +2,57 @@
|
|
|
2
2
|
* Tunnels as registry services.
|
|
3
3
|
*
|
|
4
4
|
* `tunnel up` and `tunnel reload` register the new gate; `tunnel down` removes
|
|
5
|
-
* its record.
|
|
5
|
+
* its record. Missing original witnesses remain unknown and cannot authorize actions.
|
|
6
6
|
*/
|
|
7
|
+
import { hostname } from "node:os";
|
|
7
8
|
import { resolve } from "node:path";
|
|
8
|
-
import { gateLogFile } from "../../lib/tunnel/state.js";
|
|
9
|
+
import { gateLogFile, } from "../../lib/tunnel/state.js";
|
|
10
|
+
import { readServer, verifyProcessGeneration, withServerRegistrationCustody, } from "./index.js";
|
|
11
|
+
/** Verify only witnesses saved by the original tunnel registration. */
|
|
12
|
+
export function createTunnelReadinessVerifier(options) {
|
|
13
|
+
return {
|
|
14
|
+
observeProcess(pid, originalToken) {
|
|
15
|
+
try {
|
|
16
|
+
process.kill(pid, 0);
|
|
17
|
+
}
|
|
18
|
+
catch (error) {
|
|
19
|
+
const code = error.code;
|
|
20
|
+
if (code === "ESRCH")
|
|
21
|
+
return "absent";
|
|
22
|
+
return "unknown";
|
|
23
|
+
}
|
|
24
|
+
return verifyProcessGeneration(pid, originalToken);
|
|
25
|
+
},
|
|
26
|
+
withRegistration(state, inspect) {
|
|
27
|
+
try {
|
|
28
|
+
const original = readServer(tunnelServerId(state.name), options);
|
|
29
|
+
if (!original ||
|
|
30
|
+
original.host !== hostname() ||
|
|
31
|
+
original.kind !== "service" ||
|
|
32
|
+
original.type !== "tunnel" ||
|
|
33
|
+
original.registration_id !== state.registration_id ||
|
|
34
|
+
original.pid !== state.gate_pid ||
|
|
35
|
+
original.start_token !== state.gate_start_token ||
|
|
36
|
+
original.port !== state.gate_port ||
|
|
37
|
+
original.url !== state.url)
|
|
38
|
+
return "unknown";
|
|
39
|
+
return withServerRegistrationCustody(original, options, inspect);
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
return "unknown";
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
}
|
|
9
47
|
export function tunnelServerId(name) {
|
|
10
48
|
return `tunnel-${name.toLowerCase().replace(/[^a-z0-9._-]+/g, "-")}`;
|
|
11
49
|
}
|
|
12
50
|
/** Register the new gate; saved helper PIDs have no generation witnesses. */
|
|
13
51
|
export function tunnelServerInput(state, coordRoot) {
|
|
52
|
+
if (Object.hasOwn(state, "provider_pid") || Object.hasOwn(state, "provider_start_token"))
|
|
53
|
+
throw new Error("obsolete tunnel provider authority is unknown; state retained");
|
|
54
|
+
if (!state.gate_start_token)
|
|
55
|
+
throw new Error("tunnel gate generation witness is unavailable; state retained");
|
|
14
56
|
return {
|
|
15
57
|
id: tunnelServerId(state.name),
|
|
16
58
|
kind: "service",
|
|
@@ -19,6 +61,7 @@ export function tunnelServerInput(state, coordRoot) {
|
|
|
19
61
|
url: state.url,
|
|
20
62
|
port: state.gate_port,
|
|
21
63
|
pid: state.gate_pid,
|
|
64
|
+
start_token: state.gate_start_token,
|
|
22
65
|
log: resolve(coordRoot, ".cache", "tunnel", gateLogFile(state.name)),
|
|
23
66
|
cwd: coordRoot,
|
|
24
67
|
started_at: state.started_at,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type ProcessGenerationVerdict } from "../agents/state/proc-start.js";
|
|
1
|
+
import { type ProcessGenerationSignalDependencies, type ProcessGenerationVerdict } from "../agents/state/proc-start.js";
|
|
2
2
|
import { type SupervisorServiceStatusRecord, type SupervisorStatus } from "./contract.js";
|
|
3
3
|
import { type SupervisorProcessDependencies } from "./status.js";
|
|
4
4
|
export declare const SUPERVISOR_DEFAULT_INTERVAL_MS = 2000;
|
|
@@ -30,8 +30,6 @@ export interface SupervisorStopResult extends SupervisorStatus {
|
|
|
30
30
|
signal_sent: boolean;
|
|
31
31
|
signal_verdict: ProcessGenerationVerdict;
|
|
32
32
|
}
|
|
33
|
-
export declare function requestSupervisorStop(coordRootRaw: string, dependencies?: SupervisorProcessDependencies &
|
|
34
|
-
kill?: (pid: number, signal: NodeJS.Signals | number) => unknown;
|
|
35
|
-
}): SupervisorStopResult;
|
|
33
|
+
export declare function requestSupervisorStop(coordRootRaw: string, dependencies?: SupervisorProcessDependencies & ProcessGenerationSignalDependencies): SupervisorStopResult;
|
|
36
34
|
export declare function runSupervisor(input: RunSupervisorInput): Promise<SupervisorServiceStatusRecord>;
|
|
37
35
|
//# sourceMappingURL=service.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../../src/core/supervisor/service.ts"],"names":[],"mappings":"AAkBA,OAAO,EACL,KAAK,wBAAwB,EAG9B,MAAM,+BAA+B,CAAC;AAevC,OAAO,EAML,KAAK,6BAA6B,EAElC,KAAK,gBAAgB,EACtB,MAAM,eAAe,CAAC;AAavB,OAAO,EAGL,KAAK,6BAA6B,EAEnC,MAAM,aAAa,CAAC;AASrB,eAAO,MAAM,8BAA8B,OAAQ,CAAC;AACpD,eAAO,MAAM,+BAA+B,QAAa,CAAC;AAe1D,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC;IACjB,IAAI,CAAC,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAChD;AAED,MAAM,MAAM,sBAAsB,GAC9B;IAAE,KAAK,EAAE,SAAS,GAAG,SAAS,CAAC;IAAC,MAAM,EAAE,gBAAgB,CAAA;CAAE,GAC1D;IAAE,KAAK,EAAE,aAAa,CAAC;IAAC,MAAM,EAAE,gBAAgB,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAEtE,wBAAsB,eAAe,CACnC,YAAY,EAAE,MAAM,EACpB,OAAO,GAAE;IAAE,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,OAAO,CAAA;CAAO,GAC9E,OAAO,CAAC,gBAAgB,CAAC,CAyD3B;AAED,wBAAsB,uBAAuB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,sBAAsB,CAAC,CAahG;AAED,MAAM,WAAW,oBAAqB,SAAQ,gBAAgB;IAC5D,WAAW,EAAE,OAAO,CAAC;IACrB,cAAc,EAAE,wBAAwB,CAAC;CAC1C;AAED,wBAAgB,qBAAqB,CACnC,YAAY,EAAE,MAAM,EACpB,YAAY,GAAE,6BAA6B,GAAG
|
|
1
|
+
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../../src/core/supervisor/service.ts"],"names":[],"mappings":"AAkBA,OAAO,EACL,KAAK,mCAAmC,EACxC,KAAK,wBAAwB,EAG9B,MAAM,+BAA+B,CAAC;AAevC,OAAO,EAML,KAAK,6BAA6B,EAElC,KAAK,gBAAgB,EACtB,MAAM,eAAe,CAAC;AAavB,OAAO,EAGL,KAAK,6BAA6B,EAEnC,MAAM,aAAa,CAAC;AASrB,eAAO,MAAM,8BAA8B,OAAQ,CAAC;AACpD,eAAO,MAAM,+BAA+B,QAAa,CAAC;AAe1D,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC;IACjB,IAAI,CAAC,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAChD;AAED,MAAM,MAAM,sBAAsB,GAC9B;IAAE,KAAK,EAAE,SAAS,GAAG,SAAS,CAAC;IAAC,MAAM,EAAE,gBAAgB,CAAA;CAAE,GAC1D;IAAE,KAAK,EAAE,aAAa,CAAC;IAAC,MAAM,EAAE,gBAAgB,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAEtE,wBAAsB,eAAe,CACnC,YAAY,EAAE,MAAM,EACpB,OAAO,GAAE;IAAE,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,OAAO,CAAA;CAAO,GAC9E,OAAO,CAAC,gBAAgB,CAAC,CAyD3B;AAED,wBAAsB,uBAAuB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,sBAAsB,CAAC,CAahG;AAED,MAAM,WAAW,oBAAqB,SAAQ,gBAAgB;IAC5D,WAAW,EAAE,OAAO,CAAC;IACrB,cAAc,EAAE,wBAAwB,CAAC;CAC1C;AAED,wBAAgB,qBAAqB,CACnC,YAAY,EAAE,MAAM,EACpB,YAAY,GAAE,6BAA6B,GAAG,mCAAwC,GACrF,oBAAoB,CA8BtB;AAED,wBAAsB,aAAa,CACjC,KAAK,EAAE,kBAAkB,GACxB,OAAO,CAAC,6BAA6B,CAAC,CA+QxC"}
|