harnery 0.12.1 → 0.12.2
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/core/agents/coord-client.d.ts +8 -0
- package/dist/core/agents/coord-client.d.ts.map +1 -1
- package/dist/core/agents/coord-client.js +18 -2
- package/dist/core/scratch/index.d.ts.map +1 -1
- package/dist/core/scratch/index.js +3 -3
- package/package.json +1 -1
- package/src/core/agents/coord-client.ts +19 -2
- package/src/core/scratch/index.ts +3 -2
|
@@ -13,6 +13,14 @@
|
|
|
13
13
|
*/
|
|
14
14
|
export declare function isSafeInstanceId(value: unknown): value is string;
|
|
15
15
|
export declare function assertSafeInstanceId(value: unknown): asserts value is string;
|
|
16
|
+
/**
|
|
17
|
+
* Resolve one direct-child filename beneath a trusted coordination directory.
|
|
18
|
+
*
|
|
19
|
+
* This is a second boundary behind the instance-ID allowlist. Normalizing the
|
|
20
|
+
* candidate and proving its directory prefix prevents traversal even if a new
|
|
21
|
+
* caller constructs a filename from a different untrusted source.
|
|
22
|
+
*/
|
|
23
|
+
export declare function resolveContainedFile(directory: string, fileName: string): string;
|
|
16
24
|
export interface Heartbeat {
|
|
17
25
|
instance_id: string;
|
|
18
26
|
name?: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"coord-client.d.ts","sourceRoot":"","sources":["../../../src/core/agents/coord-client.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAWH;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAWhE;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,KAAK,IAAI,MAAM,CAI5E;AAED,MAAM,WAAW,SAAS;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,sEAAsE;IACtE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,oGAAoG;IACpG,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,4EAA4E;IAC5E,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,0IAA0I;IAC1I,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,sFAAsF;IACtF,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,mEAAmE;IACnE,uBAAuB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxC,wEAAwE;IACxE,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;;GASG;AACH,wBAAgB,YAAY,IAAI,MAAM,GAAG,IAAI,CA0C5C;AAED,qEAAqE;AACrE,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAIvD;AAED,+EAA+E;AAC/E,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAI1D;AAaD;;;;;;;;;;;GAWG;AACH,wBAAgB,YAAY,IAAI,MAAM,GAAG,IAAI,CAE5C;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,IAAI;IACxC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,MAAM,EAAE,KAAK,GAAG,QAAQ,GAAG,iBAAiB,GAAG,aAAa,GAAG,kBAAkB,GAAG,MAAM,CAAC;CAC5F,CAkEA;AA4CD;;;;;;;;;GASG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CA2BpE;AAED;;;;;;;;GAQG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAyBpE;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"coord-client.d.ts","sourceRoot":"","sources":["../../../src/core/agents/coord-client.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAWH;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAWhE;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,KAAK,IAAI,MAAM,CAI5E;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAOhF;AAED,MAAM,WAAW,SAAS;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,sEAAsE;IACtE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,oGAAoG;IACpG,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,4EAA4E;IAC5E,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,0IAA0I;IAC1I,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,sFAAsF;IACtF,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,mEAAmE;IACnE,uBAAuB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxC,wEAAwE;IACxE,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;;GASG;AACH,wBAAgB,YAAY,IAAI,MAAM,GAAG,IAAI,CA0C5C;AAED,qEAAqE;AACrE,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAIvD;AAED,+EAA+E;AAC/E,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAI1D;AAaD;;;;;;;;;;;GAWG;AACH,wBAAgB,YAAY,IAAI,MAAM,GAAG,IAAI,CAE5C;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,IAAI;IACxC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,MAAM,EAAE,KAAK,GAAG,QAAQ,GAAG,iBAAiB,GAAG,aAAa,GAAG,kBAAkB,GAAG,MAAM,CAAC;CAC5F,CAkEA;AA4CD;;;;;;;;;GASG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CA2BpE;AAED;;;;;;;;GAQG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAyBpE;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAMlE;AAgBD;;;;;;;;GAQG;AACH,wBAAgB,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAOtD;AAED;;;GAGG;AACH,wBAAgB,eAAe,IAAI,MAAM,CAIxC"}
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import { spawnSync } from "node:child_process";
|
|
10
10
|
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
11
|
-
import { resolve } from "node:path";
|
|
11
|
+
import { dirname, resolve, sep } from "node:path";
|
|
12
12
|
// NOTE: kept dependency-free (node builtins only); this file is vendored verbatim into
|
|
13
13
|
// a downstream consumer, so it cannot import the coordEnv helper.
|
|
14
14
|
const MAX_INSTANCE_ID_LENGTH = 128;
|
|
@@ -35,6 +35,21 @@ export function assertSafeInstanceId(value) {
|
|
|
35
35
|
throw new Error("instance_id must be 1-128 ASCII letters, digits, hyphens, or underscores");
|
|
36
36
|
}
|
|
37
37
|
}
|
|
38
|
+
/**
|
|
39
|
+
* Resolve one direct-child filename beneath a trusted coordination directory.
|
|
40
|
+
*
|
|
41
|
+
* This is a second boundary behind the instance-ID allowlist. Normalizing the
|
|
42
|
+
* candidate and proving its directory prefix prevents traversal even if a new
|
|
43
|
+
* caller constructs a filename from a different untrusted source.
|
|
44
|
+
*/
|
|
45
|
+
export function resolveContainedFile(directory, fileName) {
|
|
46
|
+
const root = resolve(directory);
|
|
47
|
+
const candidate = resolve(root, fileName);
|
|
48
|
+
if (!candidate.startsWith(`${root}${sep}`) || dirname(candidate) !== root) {
|
|
49
|
+
throw new Error("coordination filename must resolve directly beneath its root");
|
|
50
|
+
}
|
|
51
|
+
return candidate;
|
|
52
|
+
}
|
|
38
53
|
/**
|
|
39
54
|
* Resolve the monorepo root for coord-state purposes.
|
|
40
55
|
*
|
|
@@ -334,7 +349,8 @@ export function readHeartbeat(instanceId) {
|
|
|
334
349
|
const root = monorepoRoot();
|
|
335
350
|
if (!root)
|
|
336
351
|
return null;
|
|
337
|
-
|
|
352
|
+
const path = resolveContainedFile(resolve(root, ".harnery", "active"), `${instanceId}.json`);
|
|
353
|
+
return readJsonHeartbeatFile(path);
|
|
338
354
|
}
|
|
339
355
|
function readJsonHeartbeatFile(path) {
|
|
340
356
|
if (!existsSync(path))
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/scratch/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/scratch/index.ts"],"names":[],"mappings":"AAoBA;;;;;;;;;;;;GAYG;AAEH,eAAO,MAAM,kBAAkB,iFAQrB,CAAC;AAEX,MAAM,MAAM,eAAe,GAAG,CAAC,OAAO,kBAAkB,CAAC,CAAC,MAAM,CAAC,CAAC;AAElE,eAAO,MAAM,iBAAiB,QAAY,CAAC;AAC3C,eAAO,MAAM,kBAAkB,QAAY,CAAC;AAC5C,eAAO,MAAM,sBAAsB,IAAI,CAAC;AAExC,6DAA6D;AAC7D,eAAO,MAAM,eAAe,QACyF,CAAC;AAEtH,mFAAmF;AACnF,eAAO,MAAM,wBAAwB,yBAAyB,CAAC;AAE/D,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,eAAe,CAAC;IAC1B,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,aAAa,CAAC;IACtB,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,KAAK,EAAE,MAAM,CAAC;CACf;AAID,wBAAgB,UAAU,IAAI,MAAM,CAMnC;AAED,wBAAgB,UAAU,IAAI,MAAM,CAInC;AAED,wBAAgB,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAGtD;AAeD,8EAA8E;AAC9E,wBAAgB,aAAa,CAAC,CAAC,GAAE,IAAiB,GAAG,MAAM,CAW1D;AAID,wEAAwE;AACxE,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,UAAU,CAwEtE;AAED,+CAA+C;AAC/C,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,UAAU,GAAG,MAAM,CAkBxD;AAmBD,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,SAAoB,GAAG,SAAS,EAAE,CA2DrF;AAID,wBAAgB,WAAW,CACzB,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,eAAe,EACzB,IAAI,EAAE,MAAM,GACX,UAAU,CAgEZ;AAID,wGAAwG;AACxG,wBAAgB,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAQhE;AAED,iEAAiE;AACjE,wBAAgB,aAAa,CAAC,IAAI,SAAyB,GAAG,MAAM,CAkBnE;AAED,uFAAuF;AACvF,wBAAgB,sBAAsB,IAAI,MAAM,EAAE,CAajD;AAED,qDAAqD;AACrD,wBAAgB,YAAY,IAAI;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;CACf,EAAE,CAWF;AAID,wBAAgB,mBAAmB,IAAI,MAAM,CAM5C;AAED,wBAAgB,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CAIjE;AAED,2FAA2F;AAC3F,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAoB9D"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { existsSync, mkdirSync, readdirSync, readFileSync, renameSync, statSync, unlinkSync, writeFileSync, } from "node:fs";
|
|
2
2
|
import { dirname, resolve } from "node:path";
|
|
3
3
|
import { resolveMachineLabel } from "../../lib/machine.js";
|
|
4
|
-
import { assertSafeInstanceId, monorepoRoot, readHeartbeat, resolveOwner, } from "../agents/index.js";
|
|
4
|
+
import { assertSafeInstanceId, monorepoRoot, readHeartbeat, resolveContainedFile, resolveOwner, } from "../agents/index.js";
|
|
5
5
|
/**
|
|
6
6
|
* Agent scratchpad: per-agent markdown journal at `.harnery/scratch/<instance_id>.md`.
|
|
7
7
|
*
|
|
@@ -47,7 +47,7 @@ export function archiveDir() {
|
|
|
47
47
|
}
|
|
48
48
|
export function scratchPath(instanceId) {
|
|
49
49
|
assertSafeInstanceId(instanceId);
|
|
50
|
-
return
|
|
50
|
+
return resolveContainedFile(scratchDir(), `${instanceId}.md`);
|
|
51
51
|
}
|
|
52
52
|
// ─── Chicago time formatting ──────────────────────────────────────────────
|
|
53
53
|
const CHICAGO_FORMATTER = new Intl.DateTimeFormat("en-US", {
|
|
@@ -307,7 +307,7 @@ export function archiveScratch(instanceId) {
|
|
|
307
307
|
if (!existsSync(src))
|
|
308
308
|
return null;
|
|
309
309
|
const ts = new Date().toISOString().replace(/[:.]/g, "-");
|
|
310
|
-
const dest =
|
|
310
|
+
const dest = resolveContainedFile(archiveDir(), `${instanceId}-${ts}.md`);
|
|
311
311
|
renameSync(src, dest);
|
|
312
312
|
return dest;
|
|
313
313
|
}
|
package/package.json
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
import { spawnSync } from "node:child_process";
|
|
11
11
|
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
12
|
-
import { resolve } from "node:path";
|
|
12
|
+
import { dirname, resolve, sep } from "node:path";
|
|
13
13
|
|
|
14
14
|
// NOTE: kept dependency-free (node builtins only); this file is vendored verbatim into
|
|
15
15
|
// a downstream consumer, so it cannot import the coordEnv helper.
|
|
@@ -40,6 +40,22 @@ export function assertSafeInstanceId(value: unknown): asserts value is string {
|
|
|
40
40
|
}
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
+
/**
|
|
44
|
+
* Resolve one direct-child filename beneath a trusted coordination directory.
|
|
45
|
+
*
|
|
46
|
+
* This is a second boundary behind the instance-ID allowlist. Normalizing the
|
|
47
|
+
* candidate and proving its directory prefix prevents traversal even if a new
|
|
48
|
+
* caller constructs a filename from a different untrusted source.
|
|
49
|
+
*/
|
|
50
|
+
export function resolveContainedFile(directory: string, fileName: string): string {
|
|
51
|
+
const root = resolve(directory);
|
|
52
|
+
const candidate = resolve(root, fileName);
|
|
53
|
+
if (!candidate.startsWith(`${root}${sep}`) || dirname(candidate) !== root) {
|
|
54
|
+
throw new Error("coordination filename must resolve directly beneath its root");
|
|
55
|
+
}
|
|
56
|
+
return candidate;
|
|
57
|
+
}
|
|
58
|
+
|
|
43
59
|
export interface Heartbeat {
|
|
44
60
|
instance_id: string;
|
|
45
61
|
name?: string;
|
|
@@ -369,7 +385,8 @@ export function readHeartbeat(instanceId: string): Heartbeat | null {
|
|
|
369
385
|
if (!isSafeInstanceId(instanceId)) return null;
|
|
370
386
|
const root = monorepoRoot();
|
|
371
387
|
if (!root) return null;
|
|
372
|
-
|
|
388
|
+
const path = resolveContainedFile(resolve(root, ".harnery", "active"), `${instanceId}.json`);
|
|
389
|
+
return readJsonHeartbeatFile(path);
|
|
373
390
|
}
|
|
374
391
|
|
|
375
392
|
function readJsonHeartbeatFile(path: string): Heartbeat | null {
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
assertSafeInstanceId,
|
|
15
15
|
monorepoRoot,
|
|
16
16
|
readHeartbeat,
|
|
17
|
+
resolveContainedFile,
|
|
17
18
|
resolveOwner,
|
|
18
19
|
} from "../agents/index.ts";
|
|
19
20
|
|
|
@@ -94,7 +95,7 @@ export function archiveDir(): string {
|
|
|
94
95
|
|
|
95
96
|
export function scratchPath(instanceId: string): string {
|
|
96
97
|
assertSafeInstanceId(instanceId);
|
|
97
|
-
return
|
|
98
|
+
return resolveContainedFile(scratchDir(), `${instanceId}.md`);
|
|
98
99
|
}
|
|
99
100
|
|
|
100
101
|
// ─── Chicago time formatting ──────────────────────────────────────────────
|
|
@@ -385,7 +386,7 @@ export function archiveScratch(instanceId: string): string | null {
|
|
|
385
386
|
const src = scratchPath(instanceId);
|
|
386
387
|
if (!existsSync(src)) return null;
|
|
387
388
|
const ts = new Date().toISOString().replace(/[:.]/g, "-");
|
|
388
|
-
const dest =
|
|
389
|
+
const dest = resolveContainedFile(archiveDir(), `${instanceId}-${ts}.md`);
|
|
389
390
|
renameSync(src, dest);
|
|
390
391
|
return dest;
|
|
391
392
|
}
|