@dharma-ai-labs/agent-fabric 0.2.102 → 0.2.104

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 (71) hide show
  1. package/dist/demoPackage.d.ts +2 -2
  2. package/dist/demoPackage.d.ts.map +1 -1
  3. package/dist/demoPackage.js +4 -1
  4. package/dist/demoPackage.js.map +1 -1
  5. package/dist/demoPackage.test.js +42 -0
  6. package/dist/demoPackage.test.js.map +1 -1
  7. package/dist/demoStatus.d.ts +14 -0
  8. package/dist/demoStatus.d.ts.map +1 -0
  9. package/dist/demoStatus.js +11 -0
  10. package/dist/demoStatus.js.map +1 -0
  11. package/dist/demoSupervisor.d.ts +34 -0
  12. package/dist/demoSupervisor.d.ts.map +1 -0
  13. package/dist/demoSupervisor.js +164 -0
  14. package/dist/demoSupervisor.js.map +1 -0
  15. package/dist/demoSupervisor.test.d.ts +2 -0
  16. package/dist/demoSupervisor.test.d.ts.map +1 -0
  17. package/dist/demoSupervisor.test.js +249 -0
  18. package/dist/demoSupervisor.test.js.map +1 -0
  19. package/dist/demoWatchControl.d.ts +60 -0
  20. package/dist/demoWatchControl.d.ts.map +1 -0
  21. package/dist/demoWatchControl.js +128 -0
  22. package/dist/demoWatchControl.js.map +1 -0
  23. package/dist/demoWatchControl.test.d.ts +2 -0
  24. package/dist/demoWatchControl.test.d.ts.map +1 -0
  25. package/dist/demoWatchControl.test.js +184 -0
  26. package/dist/demoWatchControl.test.js.map +1 -0
  27. package/dist/demoWatchHealth.d.ts +26 -0
  28. package/dist/demoWatchHealth.d.ts.map +1 -0
  29. package/dist/demoWatchHealth.js +177 -0
  30. package/dist/demoWatchHealth.js.map +1 -0
  31. package/dist/demoWatchHealth.test.d.ts +2 -0
  32. package/dist/demoWatchHealth.test.d.ts.map +1 -0
  33. package/dist/demoWatchHealth.test.js +175 -0
  34. package/dist/demoWatchHealth.test.js.map +1 -0
  35. package/dist/demoWatchRegistry.d.ts +17 -0
  36. package/dist/demoWatchRegistry.d.ts.map +1 -0
  37. package/dist/demoWatchRegistry.js +170 -0
  38. package/dist/demoWatchRegistry.js.map +1 -0
  39. package/dist/demoWatchRegistry.test.d.ts +2 -0
  40. package/dist/demoWatchRegistry.test.d.ts.map +1 -0
  41. package/dist/demoWatchRegistry.test.js +146 -0
  42. package/dist/demoWatchRegistry.test.js.map +1 -0
  43. package/dist/index.d.ts +5 -1
  44. package/dist/index.d.ts.map +1 -1
  45. package/dist/index.js +248 -82
  46. package/dist/index.js.map +1 -1
  47. package/dist/index.test.js +69 -5
  48. package/dist/index.test.js.map +1 -1
  49. package/dist/pidLock.test.d.ts +2 -0
  50. package/dist/pidLock.test.d.ts.map +1 -0
  51. package/dist/pidLock.test.js +172 -0
  52. package/dist/pidLock.test.js.map +1 -0
  53. package/dist/relayAutostart.d.ts +17 -6
  54. package/dist/relayAutostart.d.ts.map +1 -1
  55. package/dist/relayAutostart.js +124 -19
  56. package/dist/relayAutostart.js.map +1 -1
  57. package/dist/relayAutostart.test.js +158 -1
  58. package/dist/relayAutostart.test.js.map +1 -1
  59. package/dist/relaySupervisor.test.js +75 -1
  60. package/dist/relaySupervisor.test.js.map +1 -1
  61. package/dist/repositoryOnboardingCaller.test.js +21 -0
  62. package/dist/repositoryOnboardingCaller.test.js.map +1 -1
  63. package/dist/schemas/demo-watch-control.v1.schema.json +27 -0
  64. package/dist/schemas/demo-watch-health.v1.schema.json +24 -0
  65. package/dist/schemas/demo-watch-registration.v1.schema.json +18 -0
  66. package/dist/schemas/relay-autostart.v2.schema.json +23 -0
  67. package/dist/usage.d.ts +1 -1
  68. package/dist/usage.d.ts.map +1 -1
  69. package/dist/usage.js +1 -1
  70. package/dist/usage.js.map +1 -1
  71. package/package.json +1 -1
@@ -0,0 +1,184 @@
1
+ import assert from 'node:assert/strict';
2
+ import { mkdir, mkdtemp, readFile, realpath, rm, writeFile } from 'node:fs/promises';
3
+ import { tmpdir } from 'node:os';
4
+ import { join } from 'node:path';
5
+ import test from 'node:test';
6
+ import { disableDemoWatch, enableDemoWatch, demoWatchStatus, inspectDemoWatchSupervisor } from './demoWatchControl.js';
7
+ import { demoWatchRegistrationKey, listDemoWatchRegistrations, registerDemoWatch } from './demoWatchRegistry.js';
8
+ import { writeDemoWatchHealth } from './demoWatchHealth.js';
9
+ async function fixture() {
10
+ const home = await realpath(await mkdtemp(join(tmpdir(), 'dharma-watch-control-')));
11
+ const workspace = join(home, 'workspace');
12
+ await mkdir(workspace);
13
+ const registration = { schema: 'dharma.demo-watch/v1', hqUrl: 'https://example.com',
14
+ organizationId: 'org_test', repositoryId: '00000000-0000-4000-8000-000000000001',
15
+ normalizedRepository: 'github.com/example/repo', provider: 'codex', workspace };
16
+ const input = { home, registration, version: '0.2.103' };
17
+ let state = { state: 'stopped', pid: null };
18
+ const calls = [];
19
+ let tail = Promise.resolve();
20
+ const deps = {
21
+ verify: async () => { calls.push('verify'); },
22
+ prepareStartup: async () => { calls.push('prepare'); },
23
+ start: async () => { calls.push('start'); state = { state: 'running', pid: 123 }; },
24
+ supervisor: async () => state,
25
+ autostart: async () => 'enabled', wait: async () => { },
26
+ exclusive: operation => {
27
+ const result = tail.then(operation);
28
+ tail = result.catch(() => { });
29
+ return result;
30
+ },
31
+ };
32
+ return { input, deps, calls, setSupervisor: (value) => { state = value; },
33
+ cleanup: () => rm(home, { recursive: true, force: true }) };
34
+ }
35
+ test('enable verifies enrollment, registers one scope and starts only one shared supervisor', async () => {
36
+ const f = await fixture();
37
+ try {
38
+ const first = await enableDemoWatch(f.input, f.deps);
39
+ assert.equal(first.registered, true);
40
+ assert.equal(first.supervisor, 'running');
41
+ assert.equal(first.stage, 'demo_watch_pending');
42
+ assert.equal(first.fullWorkflowReady, false);
43
+ await enableDemoWatch(f.input, f.deps);
44
+ assert.equal(f.calls.filter(value => value === 'start').length, 1);
45
+ assert.equal((await listDemoWatchRegistrations(f.input.home)).length, 1);
46
+ assert.deepEqual(f.calls.slice(0, 3), ['verify', 'prepare', 'start']);
47
+ assert.doesNotMatch(JSON.stringify(first), /workspace|grant|credential|email|configPath/);
48
+ }
49
+ finally {
50
+ await f.cleanup();
51
+ }
52
+ });
53
+ test('dry-run and rejected authorization do not create a registry or startup entry', async () => {
54
+ const f = await fixture();
55
+ try {
56
+ assert.equal((await enableDemoWatch({ ...f.input, dryRun: true }, f.deps)).stage, 'demo_watch_plan');
57
+ assert.deepEqual(f.calls, []);
58
+ f.deps.verify = async () => { throw Object.assign(new Error('private secret'), { code: 'demo_fabric_device_revoked' }); };
59
+ await assert.rejects(enableDemoWatch(f.input, f.deps), /demo_fabric_device_revoked/);
60
+ assert.deepEqual(await listDemoWatchRegistrations(f.input.home), []);
61
+ assert.deepEqual(f.calls, []);
62
+ }
63
+ finally {
64
+ await f.cleanup();
65
+ }
66
+ });
67
+ test('ten concurrent enable requests converge without ten service starts', async () => {
68
+ const f = await fixture();
69
+ try {
70
+ await Promise.all(Array.from({ length: 10 }, () => enableDemoWatch(f.input, f.deps)));
71
+ assert.equal((await listDemoWatchRegistrations(f.input.home)).length, 1);
72
+ assert.equal(f.calls.filter(value => value === 'start').length, 1);
73
+ }
74
+ finally {
75
+ await f.cleanup();
76
+ }
77
+ });
78
+ test('startup failure preserves the registered scope for a grant-free retry', async () => {
79
+ const f = await fixture();
80
+ try {
81
+ const start = f.deps.start;
82
+ f.deps.start = async () => { throw new Error('private service details'); };
83
+ await assert.rejects(enableDemoWatch(f.input, f.deps), /^Error: demo_watch_start_failed:/);
84
+ assert.equal((await listDemoWatchRegistrations(f.input.home)).length, 1);
85
+ f.deps.start = start;
86
+ assert.equal((await enableDemoWatch(f.input, f.deps)).supervisor, 'running');
87
+ }
88
+ finally {
89
+ await f.cleanup();
90
+ }
91
+ });
92
+ test('an incompatible or unknown active supervisor is not killed or replaced', async () => {
93
+ const f = await fixture();
94
+ try {
95
+ for (const state of ['version_mismatch', 'legacy', 'unknown']) {
96
+ f.setSupervisor({ state, pid: 123 });
97
+ await assert.rejects(enableDemoWatch(f.input, f.deps), /demo_watch_supervisor_conflict/);
98
+ assert.equal((await listDemoWatchRegistrations(f.input.home)).length, 0);
99
+ assert.equal(f.calls.includes('prepare'), false);
100
+ assert.equal(f.calls.includes('start'), false);
101
+ }
102
+ }
103
+ finally {
104
+ await f.cleanup();
105
+ }
106
+ });
107
+ test('status uses only a current scoped observation and never equates a cycle with full readiness', async () => {
108
+ const f = await fixture();
109
+ try {
110
+ await enableDemoWatch(f.input, f.deps);
111
+ await writeFile(join(f.input.home, 'relay', 'supervisor.pid'), '123\n');
112
+ await writeDemoWatchHealth(f.input.home, { schema: 'dharma.demo-watch-health/v1',
113
+ key: demoWatchRegistrationKey(f.input.registration), pid: 123, version: f.input.version,
114
+ observedAt: new Date().toISOString(), state: 'completed', stage: 'demo_repository_package_installed',
115
+ sourceState: 'unchanged', code: null });
116
+ const observed = await demoWatchStatus(f.input, f.deps);
117
+ assert.equal(observed.stage, 'demo_watch_cycle_observed');
118
+ assert.equal(observed.fullWorkflowReady, false);
119
+ f.setSupervisor({ state: 'stopped', pid: null });
120
+ assert.equal((await demoWatchStatus(f.input, f.deps)).observation, null);
121
+ f.setSupervisor({ state: 'running', pid: 124 });
122
+ assert.equal((await demoWatchStatus(f.input, f.deps)).stage, 'demo_watch_pending');
123
+ }
124
+ finally {
125
+ await f.cleanup();
126
+ }
127
+ });
128
+ test('disable works after authorization revocation and preserves another scope and shared startup', async () => {
129
+ const f = await fixture();
130
+ try {
131
+ await enableDemoWatch(f.input, f.deps);
132
+ const other = { ...f.input.registration, repositoryId: '00000000-0000-4000-8000-000000000002' };
133
+ await registerDemoWatch(f.input.home, other);
134
+ f.deps.verify = async () => { throw new Error('revoked'); };
135
+ const calls = [...f.calls];
136
+ const result = await disableDemoWatch(f.input, f.deps);
137
+ assert.equal(result.stage, 'demo_watch_disabled');
138
+ assert.equal(result.registered, false);
139
+ assert.equal(result.autostart, 'enabled');
140
+ assert.deepEqual(await listDemoWatchRegistrations(f.input.home), [other]);
141
+ assert.deepEqual(f.calls, calls);
142
+ assert.equal((await readFile(join(f.input.home, 'relay', 'demo-watches', `${demoWatchRegistrationKey(other)}.json`), 'utf8')).includes('grant'), false);
143
+ }
144
+ finally {
145
+ await f.cleanup();
146
+ }
147
+ });
148
+ test('a foreign checkout cannot inspect or remove an existing scope', async () => {
149
+ const f = await fixture();
150
+ try {
151
+ await enableDemoWatch(f.input, f.deps);
152
+ const other = { ...f.input, registration: { ...f.input.registration, workspace: join(f.input.home, 'elsewhere') } };
153
+ await assert.rejects(demoWatchStatus(other, f.deps), /demo_watch_registration_conflict/);
154
+ await assert.rejects(disableDemoWatch(other, f.deps), /demo_watch_registration_conflict/);
155
+ assert.equal((await listDemoWatchRegistrations(f.input.home)).length, 1);
156
+ }
157
+ finally {
158
+ await f.cleanup();
159
+ }
160
+ });
161
+ test('supervisor inspection rejects stale bindings, invalid PID and legacy/version mismatch', async () => {
162
+ const f = await fixture();
163
+ try {
164
+ await mkdir(join(f.input.home, 'relay'));
165
+ const pidPath = join(f.input.home, 'relay', 'supervisor.pid');
166
+ const bindingPath = join(f.input.home, 'relay', 'supervisor-workspace.json');
167
+ await writeFile(pidPath, '123\n');
168
+ await writeFile(bindingPath, JSON.stringify({ pid: 124, version: '0.2.103', demoWatches: true }));
169
+ const inspect = () => inspectDemoWatchSupervisor(f.input.home, f.input.version, async () => 'running');
170
+ assert.equal((await inspect()).state, 'unknown');
171
+ await writeFile(bindingPath, JSON.stringify({ pid: 123, version: '0.2.102', demoWatches: true }));
172
+ assert.equal((await inspect()).state, 'version_mismatch');
173
+ await writeFile(bindingPath, JSON.stringify({ pid: 123, version: '0.2.103' }));
174
+ assert.equal((await inspect()).state, 'legacy');
175
+ await writeFile(bindingPath, JSON.stringify({ pid: 123, version: '0.2.103', demoWatches: true }));
176
+ assert.deepEqual(await inspect(), { state: 'running', pid: 123 });
177
+ await writeFile(pidPath, 'invalid');
178
+ assert.equal((await inspect()).state, 'unknown');
179
+ }
180
+ finally {
181
+ await f.cleanup();
182
+ }
183
+ });
184
+ //# sourceMappingURL=demoWatchControl.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"demoWatchControl.test.js","sourceRoot":"","sources":["../src/demoWatchControl.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ import type { DemoWatchObservation } from './demoSupervisor.js';
2
+ export type DemoWatchHealth = Omit<DemoWatchObservation, 'key'> & {
3
+ schema: 'dharma.demo-watch-health/v1';
4
+ key: string;
5
+ pid: number;
6
+ version: string;
7
+ observedAt: string;
8
+ };
9
+ export declare function writeDemoWatchHealth(home: string, value: DemoWatchHealth): Promise<void>;
10
+ export declare function readDemoWatchHealth(home: string, key: string, expected: {
11
+ pid: number;
12
+ version: string;
13
+ now?: number;
14
+ }): Promise<DemoWatchHealth | null>;
15
+ export declare function createDemoWatchHealthRecorder(input: {
16
+ home: string;
17
+ pid: number;
18
+ version: string;
19
+ now?: () => number;
20
+ write?: typeof writeDemoWatchHealth;
21
+ }): {
22
+ available: (key?: string) => boolean;
23
+ record(observation: DemoWatchObservation): void;
24
+ drain(): Promise<void>;
25
+ };
26
+ //# sourceMappingURL=demoWatchHealth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"demoWatchHealth.d.ts","sourceRoot":"","sources":["../src/demoWatchHealth.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAEhE,MAAM,MAAM,eAAe,GAAG,IAAI,CAAC,oBAAoB,EAAE,KAAK,CAAC,GAAG;IAChE,MAAM,EAAE,6BAA6B,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IACnD,GAAG,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;CAClD,CAAC;AAwDF,wBAAsB,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,eAAe,iBAqB9E;AAED,wBAAsB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EACjE,QAAQ,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CAsB3F;AAED,wBAAgB,6BAA6B,CAAC,KAAK,EAAE;IACnD,IAAI,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IAC/D,KAAK,CAAC,EAAE,OAAO,oBAAoB,CAAC;CACrC;sBAwBqB,MAAM;wBACJ,oBAAoB;;EAc3C"}
@@ -0,0 +1,177 @@
1
+ import { constants } from 'node:fs';
2
+ import { randomUUID } from 'node:crypto';
3
+ import { lstat, mkdir, open, readFile, rename, unlink } from 'node:fs/promises';
4
+ import { join, resolve } from 'node:path';
5
+ function assertKey(key) {
6
+ if (!/^[a-f0-9]{64}$/.test(key))
7
+ throw new Error('Demo watch health key is invalid.');
8
+ }
9
+ function validate(value, key) {
10
+ const row = value;
11
+ const keys = ['schema', 'key', 'pid', 'version', 'observedAt', 'state', 'stage', 'sourceState', 'code'];
12
+ const bounded = (value) => typeof value === 'string' && /^[a-z0-9_]{1,80}$/.test(value);
13
+ if (!row || typeof row !== 'object' || Array.isArray(row)
14
+ || Object.keys(row).length !== keys.length || keys.some(field => !Object.hasOwn(row, field))
15
+ || row.schema !== 'dharma.demo-watch-health/v1' || row.key !== key
16
+ || !Number.isSafeInteger(row.pid) || row.pid < 1
17
+ || typeof row.version !== 'string' || !/^(?=.{1,64}$)\d+\.\d+\.\d+(?:-[a-zA-Z0-9.-]+)?$/.test(row.version)
18
+ || typeof row.observedAt !== 'string' || !/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/.test(row.observedAt)
19
+ || !Number.isFinite(Date.parse(row.observedAt)) || new Date(row.observedAt).toISOString() !== row.observedAt
20
+ || !['completed', 'failed', 'timed_out'].includes(row.state)
21
+ || (row.stage !== null && !bounded(row.stage))
22
+ || (row.sourceState !== null && !bounded(row.sourceState))
23
+ || (row.state === 'completed' ? !bounded(row.stage) || row.code !== null
24
+ : row.stage !== null || row.sourceState !== null || !bounded(row.code))) {
25
+ throw new Error('Demo watch health receipt is invalid.');
26
+ }
27
+ assertKey(row.key);
28
+ return row;
29
+ }
30
+ async function directory(home, create) {
31
+ let path = resolve(home);
32
+ for (const segment of ['', 'relay', 'demo-watch-health']) {
33
+ if (segment)
34
+ path = join(path, segment);
35
+ if (create && segment)
36
+ await mkdir(path, { mode: 0o700 }).catch((error) => {
37
+ if (error.code !== 'EEXIST')
38
+ throw error;
39
+ });
40
+ const entry = await lstat(path).catch((error) => {
41
+ if (!create && error.code === 'ENOENT')
42
+ return null;
43
+ throw error;
44
+ });
45
+ if (!entry)
46
+ return null;
47
+ if (!entry.isDirectory() || entry.isSymbolicLink())
48
+ throw new Error('Demo watch health directory or symlink is invalid.');
49
+ }
50
+ return path;
51
+ }
52
+ async function fileEntry(path) {
53
+ const entry = await lstat(path).catch((error) => {
54
+ if (error.code === 'ENOENT')
55
+ return null;
56
+ throw error;
57
+ });
58
+ if (entry && (!entry.isFile() || entry.isSymbolicLink() || entry.size > 8192)) {
59
+ throw new Error('Demo watch health file or symlink is invalid.');
60
+ }
61
+ return entry;
62
+ }
63
+ export async function writeDemoWatchHealth(home, value) {
64
+ const row = validate(value, value?.key);
65
+ const root = (await directory(home, true));
66
+ const target = join(root, `${row.key}.json`);
67
+ await fileEntry(target);
68
+ const supervisor = Number((await readFile(join(home, 'relay', 'supervisor.pid'), 'utf8')).trim());
69
+ if (supervisor !== row.pid)
70
+ throw new Error('Demo watch health supervisor changed before publication.');
71
+ const temporary = join(root, `.${row.key}.${randomUUID()}.tmp`);
72
+ try {
73
+ const handle = await open(temporary, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | (constants.O_NOFOLLOW || 0), 0o600);
74
+ try {
75
+ await handle.writeFile(`${JSON.stringify(row)}\n`);
76
+ await handle.sync();
77
+ }
78
+ finally {
79
+ await handle.close();
80
+ }
81
+ // The caller retains the singleton lease until its serialized receipt writes finish.
82
+ if (Number((await readFile(join(home, 'relay', 'supervisor.pid'), 'utf8')).trim()) !== row.pid) {
83
+ throw new Error('Demo watch health supervisor changed before publication.');
84
+ }
85
+ await rename(temporary, target);
86
+ }
87
+ finally {
88
+ await unlink(temporary).catch((error) => { if (error.code !== 'ENOENT')
89
+ throw error; });
90
+ }
91
+ }
92
+ export async function readDemoWatchHealth(home, key, expected) {
93
+ assertKey(key);
94
+ const root = await directory(home, false);
95
+ if (!root)
96
+ return null;
97
+ const path = join(root, `${key}.json`);
98
+ const entry = await fileEntry(path);
99
+ if (!entry)
100
+ return null;
101
+ const handle = await open(path, constants.O_RDONLY | (constants.O_NOFOLLOW || 0));
102
+ let row;
103
+ try {
104
+ const actual = await handle.stat();
105
+ if (!actual.isFile() || actual.size > 8192 || (entry.ino !== 0 && actual.ino !== entry.ino)) {
106
+ throw new Error('Demo watch health file changed during validation.');
107
+ }
108
+ let parsed;
109
+ try {
110
+ parsed = JSON.parse(await handle.readFile({ encoding: 'utf8' }));
111
+ }
112
+ catch {
113
+ throw new Error('Demo watch health receipt is invalid.');
114
+ }
115
+ row = validate(parsed, key);
116
+ }
117
+ finally {
118
+ await handle.close();
119
+ }
120
+ const age = (expected.now ?? Date.now()) - Date.parse(row.observedAt);
121
+ if (row.pid !== expected.pid || row.version !== expected.version || age < -5_000 || age > 180_000)
122
+ return null;
123
+ return row;
124
+ }
125
+ export function createDemoWatchHealthRecorder(input) {
126
+ const pending = new Map();
127
+ const write = input.write ?? writeDemoWatchHealth;
128
+ let worker = null;
129
+ const failedScopes = new Set();
130
+ let invalid = false;
131
+ const flush = async () => {
132
+ while (pending.size) {
133
+ const [key, row] = pending.entries().next().value;
134
+ pending.delete(key);
135
+ try {
136
+ await write(input.home, row);
137
+ failedScopes.delete(key);
138
+ }
139
+ catch {
140
+ if (failedScopes.has(key) || failedScopes.size < 32)
141
+ failedScopes.add(key);
142
+ else
143
+ invalid = true;
144
+ }
145
+ }
146
+ };
147
+ const schedule = () => {
148
+ worker ??= Promise.resolve().then(flush).finally(() => {
149
+ worker = null;
150
+ if (pending.size)
151
+ schedule();
152
+ });
153
+ };
154
+ return {
155
+ available: (key) => !invalid && (key ? !failedScopes.has(key) : failedScopes.size === 0),
156
+ record(observation) {
157
+ if (observation.key === null)
158
+ return;
159
+ try {
160
+ const row = validate({ schema: 'dharma.demo-watch-health/v1', key: observation.key,
161
+ pid: input.pid, version: input.version, observedAt: new Date((input.now ?? Date.now)()).toISOString(),
162
+ state: observation.state, stage: observation.stage, sourceState: observation.sourceState,
163
+ code: observation.code }, observation.key);
164
+ if (!pending.has(row.key) && pending.size >= 32)
165
+ throw new Error('Health queue is full.');
166
+ pending.set(row.key, row);
167
+ schedule();
168
+ }
169
+ catch {
170
+ invalid = true;
171
+ }
172
+ },
173
+ async drain() { while (worker)
174
+ await worker; },
175
+ };
176
+ }
177
+ //# sourceMappingURL=demoWatchHealth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"demoWatchHealth.js","sourceRoot":"","sources":["../src/demoWatchHealth.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=demoWatchHealth.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"demoWatchHealth.test.d.ts","sourceRoot":"","sources":["../src/demoWatchHealth.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,175 @@
1
+ import assert from 'node:assert/strict';
2
+ import { mkdir, mkdtemp, readFile, rm, symlink, writeFile } from 'node:fs/promises';
3
+ import { tmpdir } from 'node:os';
4
+ import { join } from 'node:path';
5
+ import test from 'node:test';
6
+ import { createDemoWatchHealthRecorder, readDemoWatchHealth, writeDemoWatchHealth } from './demoWatchHealth.js';
7
+ const key = 'a'.repeat(64);
8
+ const now = Date.parse('2026-09-26T03:00:00.000Z');
9
+ const receipt = { schema: 'dharma.demo-watch-health/v1', key, pid: 123,
10
+ version: '0.2.103', observedAt: new Date(now).toISOString(), state: 'completed',
11
+ stage: 'demo_repository_package_installed', sourceState: 'unchanged', code: null };
12
+ test('health recorder serializes writes, coalesces pending scopes and drains before lease release', async () => {
13
+ const rows = [];
14
+ let finish;
15
+ let concurrent = 0;
16
+ let maximum = 0;
17
+ const recorder = createDemoWatchHealthRecorder({ home: '/unused', pid: 123, version: '0.2.103',
18
+ now: () => now, write: async (_home, row) => {
19
+ maximum = Math.max(maximum, ++concurrent);
20
+ if (!rows.length)
21
+ await new Promise(resolve => { finish = resolve; });
22
+ rows.push(row);
23
+ concurrent -= 1;
24
+ } });
25
+ recorder.record(receipt);
26
+ await new Promise(resolve => setImmediate(resolve));
27
+ recorder.record({ ...receipt, key: 'b'.repeat(64), sourceState: 'pending' });
28
+ recorder.record({ ...receipt, key: 'b'.repeat(64), sourceState: 'unchanged' });
29
+ let drained = false;
30
+ const drain = recorder.drain().then(() => { drained = true; });
31
+ await new Promise(resolve => setImmediate(resolve));
32
+ assert.equal(drained, false);
33
+ finish();
34
+ await drain;
35
+ assert.equal(maximum, 1);
36
+ assert.equal(rows.length, 2);
37
+ assert.equal(rows[1]?.sourceState, 'unchanged');
38
+ assert.equal(recorder.available(), true);
39
+ });
40
+ test('a receipt arriving while the writer closes is drained rather than stranded', async () => {
41
+ const rows = [];
42
+ const recorder = createDemoWatchHealthRecorder({ home: '/unused', pid: 123, version: '0.2.103',
43
+ now: () => now, write: async (_home, row) => {
44
+ rows.push(row);
45
+ if (rows.length === 1)
46
+ queueMicrotask(() => queueMicrotask(() => {
47
+ recorder.record({ ...receipt, key: 'b'.repeat(64) });
48
+ }));
49
+ } });
50
+ recorder.record(receipt);
51
+ await recorder.drain();
52
+ assert.equal(rows.length, 2);
53
+ assert.equal(rows[1]?.key, 'b'.repeat(64));
54
+ });
55
+ test('health write failure gates cycles and a later recorded observation restores the gate', async () => {
56
+ let fail = true;
57
+ const recorder = createDemoWatchHealthRecorder({ home: '/unused', pid: 123, version: '0.2.103',
58
+ now: () => now, write: async () => { if (fail)
59
+ throw new Error('private filesystem error'); } });
60
+ recorder.record(receipt);
61
+ await recorder.drain();
62
+ assert.equal(recorder.available(), false);
63
+ fail = false;
64
+ recorder.record({ ...receipt, state: 'failed', stage: null, sourceState: null, code: 'demo_watch_health_unavailable' });
65
+ await recorder.drain();
66
+ assert.equal(recorder.available(), true);
67
+ });
68
+ test('success on another repository cannot clear a failed scope receipt', async () => {
69
+ let fail = true;
70
+ const otherKey = 'b'.repeat(64);
71
+ const recorder = createDemoWatchHealthRecorder({ home: '/unused', pid: 123, version: '0.2.103',
72
+ now: () => now, write: async (_home, row) => {
73
+ if (row.key === key && fail)
74
+ throw new Error('Single-scope write failed');
75
+ } });
76
+ recorder.record(receipt);
77
+ await recorder.drain();
78
+ recorder.record({ ...receipt, key: otherKey });
79
+ await recorder.drain();
80
+ assert.equal(recorder.available(), false);
81
+ assert.equal(recorder.available(key), false);
82
+ assert.equal(recorder.available(otherKey), true);
83
+ fail = false;
84
+ recorder.record(receipt);
85
+ await recorder.drain();
86
+ assert.equal(recorder.available(key), true);
87
+ assert.equal(recorder.available(), true);
88
+ });
89
+ test('health recorder rejects malformed observations and ignores unscoped registry failures', async () => {
90
+ let writes = 0;
91
+ const recorder = createDemoWatchHealthRecorder({ home: '/unused', pid: 123, version: '0.2.103',
92
+ now: () => now, write: async () => { writes += 1; } });
93
+ recorder.record({ ...receipt, key: null });
94
+ recorder.record({ ...receipt, key: 'bad-key' });
95
+ await recorder.drain();
96
+ assert.equal(writes, 0);
97
+ assert.equal(recorder.available(), false);
98
+ });
99
+ async function fixture() {
100
+ const home = await mkdtemp(join(tmpdir(), 'dharma-demo-watch-health-'));
101
+ await mkdir(join(home, 'relay'));
102
+ await writeFile(join(home, 'relay', 'supervisor.pid'), '123\n');
103
+ return home;
104
+ }
105
+ test('health is credential-free and fresh only for its current supervisor/version', async () => {
106
+ const home = await fixture();
107
+ try {
108
+ await writeDemoWatchHealth(home, receipt);
109
+ assert.deepEqual(await readDemoWatchHealth(home, key, { pid: 123, version: '0.2.103', now }), receipt);
110
+ assert.equal(await readDemoWatchHealth(home, key, { pid: 124, version: '0.2.103', now }), null);
111
+ assert.equal(await readDemoWatchHealth(home, key, { pid: 123, version: '0.2.104', now }), null);
112
+ assert.equal(await readDemoWatchHealth(home, key, { pid: 123, version: '0.2.103', now: now + 180_001 }), null);
113
+ assert.equal(await readDemoWatchHealth(home, key, { pid: 123, version: '0.2.103', now: now - 10_000 }), null);
114
+ const raw = await readFile(join(home, 'relay', 'demo-watch-health', `${key}.json`), 'utf8');
115
+ assert.doesNotMatch(raw, /token|grant|password|workspace|email|Authorization/);
116
+ }
117
+ finally {
118
+ await rm(home, { recursive: true, force: true });
119
+ }
120
+ });
121
+ test('a departed supervisor cannot overwrite its successor receipt', async () => {
122
+ const home = await fixture();
123
+ try {
124
+ await writeDemoWatchHealth(home, receipt);
125
+ await writeFile(join(home, 'relay', 'supervisor.pid'), '124\n');
126
+ const newer = { ...receipt, pid: 124, state: 'timed_out',
127
+ stage: null, sourceState: null, code: 'demo_watch_cycle_timeout' };
128
+ await writeDemoWatchHealth(home, newer);
129
+ await assert.rejects(writeDemoWatchHealth(home, receipt), /supervisor changed/);
130
+ assert.deepEqual(await readDemoWatchHealth(home, key, { pid: 124, version: '0.2.103', now }), newer);
131
+ }
132
+ finally {
133
+ await rm(home, { recursive: true, force: true });
134
+ }
135
+ });
136
+ test('missing, malformed and foreign-key health cannot establish readiness', async () => {
137
+ const home = await fixture();
138
+ try {
139
+ const expected = { pid: 123, version: '0.2.103', now };
140
+ assert.equal(await readDemoWatchHealth(home, key, expected), null);
141
+ await writeDemoWatchHealth(home, receipt);
142
+ const path = join(home, 'relay', 'demo-watch-health', `${key}.json`);
143
+ await writeFile(path, JSON.stringify({ ...receipt, key: 'b'.repeat(64) }));
144
+ await assert.rejects(readDemoWatchHealth(home, key, expected), /invalid/);
145
+ await writeFile(path, 'private malformed payload');
146
+ await assert.rejects(readDemoWatchHealth(home, key, expected), /^Error: Demo watch health receipt is invalid\.$/);
147
+ await assert.rejects(writeDemoWatchHealth(home, { ...receipt, grant: 'never-persist' }), /invalid/);
148
+ await assert.rejects(writeDemoWatchHealth(home, { ...receipt, stage: 'private content' }), /invalid/);
149
+ await assert.rejects(writeDemoWatchHealth(home, { ...receipt, code: 'failure_on_completed' }), /invalid/);
150
+ }
151
+ finally {
152
+ await rm(home, { recursive: true, force: true });
153
+ }
154
+ });
155
+ test('health never follows directory or leaf symlinks', { skip: process.platform === 'win32' }, async () => {
156
+ const home = await fixture();
157
+ const outside = await mkdtemp(join(tmpdir(), 'dharma-demo-watch-health-outside-'));
158
+ try {
159
+ await symlink(outside, join(home, 'relay', 'demo-watch-health'));
160
+ await assert.rejects(writeDemoWatchHealth(home, receipt), /symlink/);
161
+ await rm(join(home, 'relay', 'demo-watch-health'));
162
+ await mkdir(join(home, 'relay', 'demo-watch-health'));
163
+ const target = join(outside, 'preserve.json');
164
+ await writeFile(target, JSON.stringify(receipt));
165
+ await symlink(target, join(home, 'relay', 'demo-watch-health', `${key}.json`));
166
+ await assert.rejects(readDemoWatchHealth(home, key, { pid: 123, version: '0.2.103', now }), /symlink/);
167
+ await assert.rejects(writeDemoWatchHealth(home, receipt), /symlink/);
168
+ assert.equal(await readFile(target, 'utf8'), JSON.stringify(receipt));
169
+ }
170
+ finally {
171
+ await rm(home, { recursive: true, force: true });
172
+ await rm(outside, { recursive: true, force: true });
173
+ }
174
+ });
175
+ //# sourceMappingURL=demoWatchHealth.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"demoWatchHealth.test.js","sourceRoot":"","sources":["../src/demoWatchHealth.test.ts"],"names":[],"mappings":"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"}