harnery 0.46.0 → 0.47.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/README.md +24 -0
- package/bin/README.md +10 -0
- package/bin/agent-coord.mjs +3 -0
- package/bin/agent-hook.mjs +3 -0
- package/bin/harn.mjs +3 -0
- package/bin/run.mjs +43 -0
- package/dist/commander.d.ts +18 -6
- package/dist/commander.d.ts.map +1 -1
- package/dist/commander.js +74 -2
- package/dist/commands/agents.d.ts +25 -0
- package/dist/commands/agents.d.ts.map +1 -1
- package/dist/commands/agents.js +70 -27
- package/dist/commands/autoupdate.d.ts +12 -0
- package/dist/commands/autoupdate.d.ts.map +1 -0
- package/dist/commands/autoupdate.js +190 -0
- package/dist/commands/computer.d.ts +45 -0
- package/dist/commands/computer.d.ts.map +1 -0
- package/dist/commands/computer.js +509 -0
- package/dist/commands/docs.js +3 -1
- package/dist/commands/fetch.js +11 -1
- package/dist/commands/grep.d.ts +72 -0
- package/dist/commands/grep.d.ts.map +1 -1
- package/dist/commands/grep.js +200 -47
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +15 -9
- package/dist/commands/rm.js +2 -2
- package/dist/commands/web.d.ts.map +1 -1
- package/dist/commands/web.js +2 -0
- package/dist/core/agents/canonical-emit.d.ts.map +1 -1
- package/dist/core/agents/canonical-emit.js +2 -3
- package/dist/core/agents/coord-bin.d.ts +3 -0
- package/dist/core/agents/coord-bin.d.ts.map +1 -1
- package/dist/core/agents/coord-bin.js +6 -0
- package/dist/core/agents/coord-client.d.ts.map +1 -1
- package/dist/core/agents/coord-client.js +6 -2
- package/dist/core/agents/render/prompt-context.d.ts +55 -7
- package/dist/core/agents/render/prompt-context.d.ts.map +1 -1
- package/dist/core/agents/render/prompt-context.js +122 -48
- package/dist/core/agents/rules/claim-conflict.d.ts.map +1 -1
- package/dist/core/agents/rules/claim-conflict.js +36 -16
- package/dist/core/agents/rules/stop-hook.d.ts +6 -0
- package/dist/core/agents/rules/stop-hook.d.ts.map +1 -1
- package/dist/core/agents/rules/stop-hook.js +20 -1
- package/dist/core/agents/state/proc-start.d.ts +3 -1
- package/dist/core/agents/state/proc-start.d.ts.map +1 -1
- package/dist/core/agents/state/proc-start.js +26 -4
- package/dist/core/autoupdate/engine.d.ts +97 -0
- package/dist/core/autoupdate/engine.d.ts.map +1 -0
- package/dist/core/autoupdate/engine.js +548 -0
- package/dist/core/autoupdate/fingerprint.d.ts +16 -0
- package/dist/core/autoupdate/fingerprint.d.ts.map +1 -0
- package/dist/core/autoupdate/fingerprint.js +73 -0
- package/dist/core/autoupdate/git.d.ts +30 -0
- package/dist/core/autoupdate/git.d.ts.map +1 -0
- package/dist/core/autoupdate/git.js +82 -0
- package/dist/core/autoupdate/index.d.ts +6 -0
- package/dist/core/autoupdate/index.d.ts.map +1 -0
- package/dist/core/autoupdate/index.js +4 -0
- package/dist/core/autoupdate/state.d.ts +59 -0
- package/dist/core/autoupdate/state.d.ts.map +1 -0
- package/dist/core/autoupdate/state.js +214 -0
- package/dist/core/autoupdate/types.d.ts +113 -0
- package/dist/core/autoupdate/types.d.ts.map +1 -0
- package/dist/core/autoupdate/types.js +35 -0
- package/dist/core/config.d.ts +53 -1
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +114 -1
- package/dist/core/events/v3/bootstrap.js +1 -0
- package/dist/core/events/v3/runtime-build.d.ts.map +1 -1
- package/dist/core/events/v3/runtime-build.js +1 -0
- package/dist/core/hooks/adapter/wiring.d.ts +12 -2
- package/dist/core/hooks/adapter/wiring.d.ts.map +1 -1
- package/dist/core/hooks/adapter/wiring.js +38 -8
- package/dist/core/hooks/autoupdate-cue.d.ts +29 -0
- package/dist/core/hooks/autoupdate-cue.d.ts.map +1 -0
- package/dist/core/hooks/autoupdate-cue.js +53 -0
- package/dist/core/hooks/cli.js +52 -4
- package/dist/core/hooks/effects/index.js +4 -4
- package/dist/core/hooks/resolve/coord-root.js +1 -0
- package/dist/core/hooks/resolve/transcript.d.ts +12 -0
- package/dist/core/hooks/resolve/transcript.d.ts.map +1 -1
- package/dist/core/hooks/resolve/transcript.js +54 -0
- package/dist/core/semantic/contract.d.ts +3 -3
- package/dist/core/semantic/service.d.ts.map +1 -1
- package/dist/core/semantic/service.js +4 -2
- package/dist/core/storage/modes.d.ts +1 -1
- package/dist/core/storage/modes.d.ts.map +1 -1
- package/dist/core/storage/modes.js +5 -1
- package/dist/core/workflow/durable-record.d.ts +2 -0
- package/dist/core/workflow/durable-record.d.ts.map +1 -1
- package/dist/core/workflow/durable-record.js +13 -1
- package/dist/core/workflow/engine.js +4 -0
- package/dist/core/workflow/spawn-claude.d.ts +7 -0
- package/dist/core/workflow/spawn-claude.d.ts.map +1 -1
- package/dist/core/workflow/spawn-claude.js +16 -4
- package/dist/core/workflow/specialists.d.ts.map +1 -1
- package/dist/core/workflow/specialists.js +1 -0
- package/dist/core/workflow/types.d.ts +10 -0
- package/dist/core/workflow/types.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/leases.d.ts.map +1 -1
- package/dist/core/workflow/workspaces/leases.js +1 -9
- package/dist/lib/computer/cua.d.ts +50 -0
- package/dist/lib/computer/cua.d.ts.map +1 -0
- package/dist/lib/computer/cua.js +474 -0
- package/dist/lib/computer/host.d.ts +19 -0
- package/dist/lib/computer/host.d.ts.map +1 -0
- package/dist/lib/computer/host.js +72 -0
- package/dist/lib/computer/lease.d.ts +13 -0
- package/dist/lib/computer/lease.d.ts.map +1 -0
- package/dist/lib/computer/lease.js +269 -0
- package/dist/lib/computer/types.d.ts +163 -0
- package/dist/lib/computer/types.d.ts.map +1 -0
- package/dist/lib/computer/types.js +10 -0
- package/dist/lib/docs-metadata-v2.d.ts.map +1 -1
- package/dist/lib/docs-metadata-v2.js +6 -5
- package/dist/lib/exec.d.ts.map +1 -1
- package/dist/lib/exec.js +1 -0
- package/dist/lib/guarded-remove.d.ts.map +1 -1
- package/dist/lib/guarded-remove.js +5 -4
- package/dist/lib/headless/index.d.ts +33 -0
- package/dist/lib/headless/index.d.ts.map +1 -1
- package/dist/lib/headless/index.js +38 -2
- package/dist/lib/http/client.d.ts +2 -9
- package/dist/lib/http/client.d.ts.map +1 -1
- package/dist/lib/http/client.js +30 -9
- package/dist/lib/instructions/apply.d.ts +9 -0
- package/dist/lib/instructions/apply.d.ts.map +1 -1
- package/dist/lib/instructions/apply.js +92 -14
- package/dist/lib/instructions/git-hooks.d.ts.map +1 -1
- package/dist/lib/instructions/git-hooks.js +8 -4
- package/dist/lib/instructions/templates.d.ts +11 -0
- package/dist/lib/instructions/templates.d.ts.map +1 -1
- package/dist/lib/instructions/templates.js +81 -1
- package/package.json +5 -5
- package/schemas/config.schema.json +89 -1
- package/src/commander.ts +98 -8
- package/src/commands/agents.ts +100 -32
- package/src/commands/autoupdate.ts +250 -0
- package/src/commands/computer.ts +682 -0
- package/src/commands/docs.ts +3 -1
- package/src/commands/fetch.ts +11 -1
- package/src/commands/grep.ts +274 -59
- package/src/commands/init.ts +17 -7
- package/src/commands/rm.ts +2 -2
- package/src/commands/web.ts +2 -0
- package/src/core/agents/canonical-emit.ts +2 -3
- package/src/core/agents/coord-bin.ts +30 -0
- package/src/core/agents/coord-client.ts +6 -2
- package/src/core/agents/render/prompt-context.ts +152 -53
- package/src/core/agents/rules/claim-conflict.ts +50 -18
- package/src/core/agents/rules/stop-hook.ts +28 -1
- package/src/core/agents/state/proc-start.ts +29 -5
- package/src/core/autoupdate/engine.ts +691 -0
- package/src/core/autoupdate/fingerprint.ts +89 -0
- package/src/core/autoupdate/git.ts +120 -0
- package/src/core/autoupdate/index.ts +29 -0
- package/src/core/autoupdate/state.ts +269 -0
- package/src/core/autoupdate/types.ts +135 -0
- package/src/core/config.ts +142 -1
- package/src/core/events/v3/bootstrap.ts +1 -0
- package/src/core/events/v3/runtime-build.ts +1 -0
- package/src/core/hooks/adapter/wiring.ts +51 -6
- package/src/core/hooks/autoupdate-cue.ts +72 -0
- package/src/core/hooks/cli.ts +54 -1
- package/src/core/hooks/effects/index.ts +4 -4
- package/src/core/hooks/resolve/coord-root.ts +1 -0
- package/src/core/hooks/resolve/transcript.ts +61 -0
- package/src/core/semantic/service.ts +6 -2
- package/src/core/storage/modes.ts +7 -1
- package/src/core/workflow/durable-record.ts +15 -1
- package/src/core/workflow/engine.ts +3 -0
- package/src/core/workflow/spawn-claude.ts +16 -4
- package/src/core/workflow/specialists.ts +1 -0
- package/src/core/workflow/types.ts +10 -0
- package/src/core/workflow/workspaces/leases.ts +1 -9
- package/src/lib/computer/cua.ts +572 -0
- package/src/lib/computer/host.ts +87 -0
- package/src/lib/computer/lease.ts +299 -0
- package/src/lib/computer/types.ts +177 -0
- package/src/lib/docs-metadata-v2.ts +24 -5
- package/src/lib/exec.ts +1 -0
- package/src/lib/guarded-remove.ts +5 -3
- package/src/lib/headless/index.ts +66 -2
- package/src/lib/http/client.ts +33 -19
- package/src/lib/instructions/apply.ts +100 -12
- package/src/lib/instructions/git-hooks.ts +8 -4
- package/src/lib/instructions/templates.ts +95 -1
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The few Git reads self-update needs. Every call is synchronous, bounded by a
|
|
3
|
+
* timeout, and goes through an injectable runner so tests never touch a
|
|
4
|
+
* network remote.
|
|
5
|
+
*/
|
|
6
|
+
import { spawnSync } from "node:child_process";
|
|
7
|
+
const LOCAL_TIMEOUT_MS = 10_000;
|
|
8
|
+
const REMOTE_TIMEOUT_MS = 15_000;
|
|
9
|
+
export const defaultGitRunner = (args, opts) => {
|
|
10
|
+
const result = spawnSync("git", args, {
|
|
11
|
+
cwd: opts.cwd,
|
|
12
|
+
encoding: "utf8",
|
|
13
|
+
timeout: opts.timeoutMs,
|
|
14
|
+
windowsHide: true,
|
|
15
|
+
// Never block a background run on a credential prompt.
|
|
16
|
+
env: { ...process.env, GIT_TERMINAL_PROMPT: "0" },
|
|
17
|
+
});
|
|
18
|
+
return {
|
|
19
|
+
status: result.error ? null : result.status,
|
|
20
|
+
stdout: typeof result.stdout === "string" ? result.stdout : "",
|
|
21
|
+
stderr: typeof result.stderr === "string" ? result.stderr : "",
|
|
22
|
+
};
|
|
23
|
+
};
|
|
24
|
+
function read(run, root, args, timeoutMs = LOCAL_TIMEOUT_MS) {
|
|
25
|
+
const result = run(args, { cwd: root, timeoutMs });
|
|
26
|
+
if (result.status !== 0)
|
|
27
|
+
return null;
|
|
28
|
+
const out = result.stdout.trim();
|
|
29
|
+
return out || null;
|
|
30
|
+
}
|
|
31
|
+
export function head(root, run = defaultGitRunner) {
|
|
32
|
+
return read(run, root, ["rev-parse", "HEAD"]);
|
|
33
|
+
}
|
|
34
|
+
/** The current branch's upstream, or null when detached or untracked. */
|
|
35
|
+
export function upstream(root, run = defaultGitRunner) {
|
|
36
|
+
const display = read(run, root, ["rev-parse", "--abbrev-ref", "--symbolic-full-name", "@{u}"]);
|
|
37
|
+
if (!display)
|
|
38
|
+
return null;
|
|
39
|
+
const current = read(run, root, ["symbolic-ref", "--quiet", "--short", "HEAD"]);
|
|
40
|
+
if (current) {
|
|
41
|
+
const remote = read(run, root, ["config", "--get", `branch.${current}.remote`]);
|
|
42
|
+
const merge = read(run, root, ["config", "--get", `branch.${current}.merge`]);
|
|
43
|
+
if (remote && merge) {
|
|
44
|
+
return { remote, branch: merge.replace(/^refs\/heads\//, ""), display };
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
const slash = display.indexOf("/");
|
|
48
|
+
if (slash <= 0)
|
|
49
|
+
return null;
|
|
50
|
+
return { remote: display.slice(0, slash), branch: display.slice(slash + 1), display };
|
|
51
|
+
}
|
|
52
|
+
/** The remote branch's sha, or null when the remote cannot be reached or lacks the branch. */
|
|
53
|
+
export function lsRemote(root, remote, branch, run = defaultGitRunner) {
|
|
54
|
+
const out = read(run, root, ["ls-remote", "--exit-code", remote, `refs/heads/${branch}`], REMOTE_TIMEOUT_MS);
|
|
55
|
+
if (!out)
|
|
56
|
+
return null;
|
|
57
|
+
const sha = out.split(/\s+/)[0] ?? "";
|
|
58
|
+
return /^[0-9a-f]{40,64}$/.test(sha) ? sha : null;
|
|
59
|
+
}
|
|
60
|
+
/** True when `sha` exists locally and is an ancestor of HEAD. */
|
|
61
|
+
export function contains(root, sha, run = defaultGitRunner) {
|
|
62
|
+
const exists = run(["cat-file", "-e", `${sha}^{commit}`], {
|
|
63
|
+
cwd: root,
|
|
64
|
+
timeoutMs: LOCAL_TIMEOUT_MS,
|
|
65
|
+
});
|
|
66
|
+
if (exists.status !== 0)
|
|
67
|
+
return false;
|
|
68
|
+
const ancestor = run(["merge-base", "--is-ancestor", sha, "HEAD"], {
|
|
69
|
+
cwd: root,
|
|
70
|
+
timeoutMs: LOCAL_TIMEOUT_MS,
|
|
71
|
+
});
|
|
72
|
+
return ancestor.status === 0;
|
|
73
|
+
}
|
|
74
|
+
export function changedFiles(root, from, to, run = defaultGitRunner) {
|
|
75
|
+
const out = read(run, root, ["diff", "--name-only", from, to]);
|
|
76
|
+
if (!out)
|
|
77
|
+
return [];
|
|
78
|
+
return out
|
|
79
|
+
.split(/\r?\n/)
|
|
80
|
+
.map((line) => line.trim())
|
|
81
|
+
.filter(Boolean);
|
|
82
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { AUTOUPDATE_CHILD_ENV, type AutoUpdateDeps, type AutoUpdateStatus, autoUpdateStatus, autoUpdateTick, checkForUpdate, harnCliEntry, isHookConfigPath, isInstructionPath, parseNoticeLines, type RunAutoUpdateOptions, type RunAutoUpdateResult, runAutoUpdate, type StepRunner, type StepRunResult, } from "./engine.js";
|
|
2
|
+
export { expandWatchPath, fingerprint, fingerprintLines } from "./fingerprint.js";
|
|
3
|
+
export type { GitResult, GitRunner, Upstream } from "./git.js";
|
|
4
|
+
export { addNotice, autoUpdateLogPath, autoUpdateStatePath, type LockInspection, type LockOwner, pendingNotices, readAutoUpdateState, } from "./state.js";
|
|
5
|
+
export * from "./types.js";
|
|
6
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/autoupdate/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,oBAAoB,EACpB,KAAK,cAAc,EACnB,KAAK,gBAAgB,EACrB,gBAAgB,EAChB,cAAc,EACd,cAAc,EACd,YAAY,EACZ,gBAAgB,EAChB,iBAAiB,EACjB,gBAAgB,EAChB,KAAK,oBAAoB,EACzB,KAAK,mBAAmB,EACxB,aAAa,EACb,KAAK,UAAU,EACf,KAAK,aAAa,GACnB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAClF,YAAY,EAAE,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AAC/D,OAAO,EACL,SAAS,EACT,iBAAiB,EACjB,mBAAmB,EACnB,KAAK,cAAc,EACnB,KAAK,SAAS,EACd,cAAc,EACd,mBAAmB,GACpB,MAAM,YAAY,CAAC;AACpB,cAAc,YAAY,CAAC"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { AUTOUPDATE_CHILD_ENV, autoUpdateStatus, autoUpdateTick, checkForUpdate, harnCliEntry, isHookConfigPath, isInstructionPath, parseNoticeLines, runAutoUpdate, } from "./engine.js";
|
|
2
|
+
export { expandWatchPath, fingerprint, fingerprintLines } from "./fingerprint.js";
|
|
3
|
+
export { addNotice, autoUpdateLogPath, autoUpdateStatePath, pendingNotices, readAutoUpdateState, } from "./state.js";
|
|
4
|
+
export * from "./types.js";
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Persistent state for project self-update: `state.json` (last run, converged
|
|
3
|
+
* point, notices, per-session delivery) and the `run.lock` directory that keeps
|
|
4
|
+
* one runner per checkout.
|
|
5
|
+
*
|
|
6
|
+
* Writers re-read the file immediately before writing (`mutateAutoUpdateState`)
|
|
7
|
+
* because a long run and the prompt hooks that deliver notices both update it.
|
|
8
|
+
*/
|
|
9
|
+
import { type AutoUpdateConfig, type AutoUpdateNotice, type AutoUpdateState, type NoticeKind } from "./types.js";
|
|
10
|
+
export declare function autoUpdateDir(coordRoot: string): string;
|
|
11
|
+
export declare function autoUpdateStatePath(coordRoot: string): string;
|
|
12
|
+
export declare function autoUpdateStampPath(coordRoot: string): string;
|
|
13
|
+
export declare function autoUpdateLockPath(coordRoot: string): string;
|
|
14
|
+
/** The run log lives with the other Harnery logs, not in private state. */
|
|
15
|
+
export declare function autoUpdateLogPath(coordRoot: string): string;
|
|
16
|
+
export declare function freshAutoUpdateState(): AutoUpdateState;
|
|
17
|
+
/** Read state; a missing or corrupt file yields fresh state. */
|
|
18
|
+
export declare function readAutoUpdateState(coordRoot: string): AutoUpdateState;
|
|
19
|
+
/** Write state atomically (temp file + rename). */
|
|
20
|
+
export declare function writeAutoUpdateState(coordRoot: string, state: AutoUpdateState): void;
|
|
21
|
+
/** Re-read, apply `mutate`, write. Returns the written state. */
|
|
22
|
+
export declare function mutateAutoUpdateState(coordRoot: string, mutate: (state: AutoUpdateState) => void): AutoUpdateState;
|
|
23
|
+
/**
|
|
24
|
+
* Append a notice to `state` in place. The same kind and text within 24 hours
|
|
25
|
+
* is not repeated; only the newest 20 notices are kept. Returns the notice now
|
|
26
|
+
* on record (the earlier one when deduplicated).
|
|
27
|
+
*/
|
|
28
|
+
export declare function addNotice(state: AutoUpdateState, kind: NoticeKind, text: string, now?: number): AutoUpdateNotice;
|
|
29
|
+
/**
|
|
30
|
+
* Notices this session has not seen yet, marked delivered. A session key with
|
|
31
|
+
* no record sees only the last 24 hours. Cheap when there is nothing to say:
|
|
32
|
+
* no write happens unless a notice is returned.
|
|
33
|
+
*/
|
|
34
|
+
export declare function pendingNotices(coordRoot: string, sessionKey: string, now?: number): AutoUpdateNotice[];
|
|
35
|
+
export interface LockOwner {
|
|
36
|
+
pid: number;
|
|
37
|
+
at: string;
|
|
38
|
+
host?: string;
|
|
39
|
+
}
|
|
40
|
+
/** Minutes after which a held lock counts as abandoned. */
|
|
41
|
+
export declare function lockStaleMinutes(cfg: Pick<AutoUpdateConfig, "timeoutMinutes" | "steps" | "dailySteps">): number;
|
|
42
|
+
export interface LockInspection {
|
|
43
|
+
held: boolean;
|
|
44
|
+
stale: boolean;
|
|
45
|
+
owner: LockOwner | null;
|
|
46
|
+
ageMs: number | null;
|
|
47
|
+
}
|
|
48
|
+
/** Inspect the lock without taking it. One stat when free. */
|
|
49
|
+
export declare function inspectLock(coordRoot: string, cfg: Pick<AutoUpdateConfig, "timeoutMinutes" | "steps" | "dailySteps">, now?: number): LockInspection;
|
|
50
|
+
export type LockAttempt = {
|
|
51
|
+
acquired: true;
|
|
52
|
+
release: () => void;
|
|
53
|
+
} | {
|
|
54
|
+
acquired: false;
|
|
55
|
+
owner: LockOwner | null;
|
|
56
|
+
};
|
|
57
|
+
/** Take the run lock, breaking it once when it is stale. */
|
|
58
|
+
export declare function acquireRunLock(coordRoot: string, cfg: Pick<AutoUpdateConfig, "timeoutMinutes" | "steps" | "dailySteps">, now?: number): LockAttempt;
|
|
59
|
+
//# sourceMappingURL=state.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../../../src/core/autoupdate/state.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAMH,OAAO,EAEL,KAAK,gBAAgB,EACrB,KAAK,gBAAgB,EACrB,KAAK,eAAe,EACpB,KAAK,UAAU,EAChB,MAAM,YAAY,CAAC;AAQpB,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAEvD;AAED,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAE7D;AAED,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAE7D;AAED,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAE5D;AAED,2EAA2E;AAC3E,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAE3D;AAED,wBAAgB,oBAAoB,IAAI,eAAe,CAEtD;AAgBD,gEAAgE;AAChE,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,eAAe,CAsBtE;AAED,mDAAmD;AACnD,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,eAAe,GAAG,IAAI,CAYpF;AAED,iEAAiE;AACjE,wBAAgB,qBAAqB,CACnC,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,CAAC,KAAK,EAAE,eAAe,KAAK,IAAI,GACvC,eAAe,CAKjB;AAMD;;;;GAIG;AACH,wBAAgB,SAAS,CACvB,KAAK,EAAE,eAAe,EACtB,IAAI,EAAE,UAAU,EAChB,IAAI,EAAE,MAAM,EACZ,GAAG,GAAE,MAAmB,GACvB,gBAAgB,CAYlB;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAC5B,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EAClB,GAAG,GAAE,MAAmB,GACvB,gBAAgB,EAAE,CAmBpB;AAID,MAAM,WAAW,SAAS;IACxB,GAAG,EAAE,MAAM,CAAC;IACZ,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,2DAA2D;AAC3D,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,IAAI,CAAC,gBAAgB,EAAE,gBAAgB,GAAG,OAAO,GAAG,YAAY,CAAC,GACrE,MAAM,CAER;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,OAAO,CAAC;IACd,KAAK,EAAE,OAAO,CAAC;IACf,KAAK,EAAE,SAAS,GAAG,IAAI,CAAC;IACxB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB;AAED,8DAA8D;AAC9D,wBAAgB,WAAW,CACzB,SAAS,EAAE,MAAM,EACjB,GAAG,EAAE,IAAI,CAAC,gBAAgB,EAAE,gBAAgB,GAAG,OAAO,GAAG,YAAY,CAAC,EACtE,GAAG,GAAE,MAAmB,GACvB,cAAc,CAyBhB;AAED,MAAM,MAAM,WAAW,GACnB;IAAE,QAAQ,EAAE,IAAI,CAAC;IAAC,OAAO,EAAE,MAAM,IAAI,CAAA;CAAE,GACvC;IAAE,QAAQ,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,SAAS,GAAG,IAAI,CAAA;CAAE,CAAC;AAEjD,4DAA4D;AAC5D,wBAAgB,cAAc,CAC5B,SAAS,EAAE,MAAM,EACjB,GAAG,EAAE,IAAI,CAAC,gBAAgB,EAAE,gBAAgB,GAAG,OAAO,GAAG,YAAY,CAAC,EACtE,GAAG,GAAE,MAAmB,GACvB,WAAW,CA8Bb"}
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Persistent state for project self-update: `state.json` (last run, converged
|
|
3
|
+
* point, notices, per-session delivery) and the `run.lock` directory that keeps
|
|
4
|
+
* one runner per checkout.
|
|
5
|
+
*
|
|
6
|
+
* Writers re-read the file immediately before writing (`mutateAutoUpdateState`)
|
|
7
|
+
* because a long run and the prompt hooks that deliver notices both update it.
|
|
8
|
+
*/
|
|
9
|
+
import { createHash } from "node:crypto";
|
|
10
|
+
import { mkdirSync, readFileSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
11
|
+
import { hostname } from "node:os";
|
|
12
|
+
import { join, resolve } from "node:path";
|
|
13
|
+
import { AUTOUPDATE_FILES, } from "./types.js";
|
|
14
|
+
const DAY_MS = 24 * 60 * 60 * 1000;
|
|
15
|
+
const NOTICE_DEDUPE_MS = DAY_MS;
|
|
16
|
+
const NOTICE_KEEP = 20;
|
|
17
|
+
const NEW_SESSION_WINDOW_MS = DAY_MS;
|
|
18
|
+
const DELIVERED_PRUNE_MS = 7 * DAY_MS;
|
|
19
|
+
export function autoUpdateDir(coordRoot) {
|
|
20
|
+
return join(resolve(coordRoot), AUTOUPDATE_FILES.dir);
|
|
21
|
+
}
|
|
22
|
+
export function autoUpdateStatePath(coordRoot) {
|
|
23
|
+
return join(autoUpdateDir(coordRoot), AUTOUPDATE_FILES.state);
|
|
24
|
+
}
|
|
25
|
+
export function autoUpdateStampPath(coordRoot) {
|
|
26
|
+
return join(autoUpdateDir(coordRoot), AUTOUPDATE_FILES.stamp);
|
|
27
|
+
}
|
|
28
|
+
export function autoUpdateLockPath(coordRoot) {
|
|
29
|
+
return join(autoUpdateDir(coordRoot), AUTOUPDATE_FILES.lock);
|
|
30
|
+
}
|
|
31
|
+
/** The run log lives with the other Harnery logs, not in private state. */
|
|
32
|
+
export function autoUpdateLogPath(coordRoot) {
|
|
33
|
+
return join(resolve(coordRoot), ".harnery", "logs", AUTOUPDATE_FILES.log);
|
|
34
|
+
}
|
|
35
|
+
export function freshAutoUpdateState() {
|
|
36
|
+
return { schema: 1, notices: [], delivered: {} };
|
|
37
|
+
}
|
|
38
|
+
function isRecord(value) {
|
|
39
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
40
|
+
}
|
|
41
|
+
function isNotice(value) {
|
|
42
|
+
return (isRecord(value) &&
|
|
43
|
+
typeof value.id === "string" &&
|
|
44
|
+
typeof value.at === "string" &&
|
|
45
|
+
typeof value.kind === "string" &&
|
|
46
|
+
typeof value.text === "string");
|
|
47
|
+
}
|
|
48
|
+
/** Read state; a missing or corrupt file yields fresh state. */
|
|
49
|
+
export function readAutoUpdateState(coordRoot) {
|
|
50
|
+
let raw;
|
|
51
|
+
try {
|
|
52
|
+
raw = readFileSync(autoUpdateStatePath(coordRoot), "utf8");
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
return freshAutoUpdateState();
|
|
56
|
+
}
|
|
57
|
+
try {
|
|
58
|
+
const parsed = JSON.parse(raw);
|
|
59
|
+
if (!isRecord(parsed) || parsed.schema !== 1)
|
|
60
|
+
return freshAutoUpdateState();
|
|
61
|
+
const state = parsed;
|
|
62
|
+
const notices = Array.isArray(parsed.notices) ? parsed.notices.filter(isNotice) : [];
|
|
63
|
+
const delivered = {};
|
|
64
|
+
if (isRecord(parsed.delivered)) {
|
|
65
|
+
for (const [key, at] of Object.entries(parsed.delivered)) {
|
|
66
|
+
if (typeof at === "string")
|
|
67
|
+
delivered[key] = at;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return { ...state, schema: 1, notices, delivered };
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
return freshAutoUpdateState();
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
/** Write state atomically (temp file + rename). */
|
|
77
|
+
export function writeAutoUpdateState(coordRoot, state) {
|
|
78
|
+
const dir = autoUpdateDir(coordRoot);
|
|
79
|
+
mkdirSync(dir, { recursive: true });
|
|
80
|
+
const target = autoUpdateStatePath(coordRoot);
|
|
81
|
+
const tmp = `${target}.${process.pid}.${Date.now()}.tmp`;
|
|
82
|
+
writeFileSync(tmp, `${JSON.stringify(state, null, 2)}\n`);
|
|
83
|
+
try {
|
|
84
|
+
renameSync(tmp, target);
|
|
85
|
+
}
|
|
86
|
+
catch (err) {
|
|
87
|
+
rmSync(tmp, { force: true });
|
|
88
|
+
throw err;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
/** Re-read, apply `mutate`, write. Returns the written state. */
|
|
92
|
+
export function mutateAutoUpdateState(coordRoot, mutate) {
|
|
93
|
+
const state = readAutoUpdateState(coordRoot);
|
|
94
|
+
mutate(state);
|
|
95
|
+
writeAutoUpdateState(coordRoot, state);
|
|
96
|
+
return state;
|
|
97
|
+
}
|
|
98
|
+
function noticeId(kind, text, at) {
|
|
99
|
+
return createHash("sha1").update(`${kind}\n${text}\n${at}`).digest("hex").slice(0, 12);
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Append a notice to `state` in place. The same kind and text within 24 hours
|
|
103
|
+
* is not repeated; only the newest 20 notices are kept. Returns the notice now
|
|
104
|
+
* on record (the earlier one when deduplicated).
|
|
105
|
+
*/
|
|
106
|
+
export function addNotice(state, kind, text, now = Date.now()) {
|
|
107
|
+
const existing = state.notices.find((n) => n.kind === kind && n.text === text && now - Date.parse(n.at) < NOTICE_DEDUPE_MS);
|
|
108
|
+
if (existing)
|
|
109
|
+
return existing;
|
|
110
|
+
const at = new Date(now).toISOString();
|
|
111
|
+
const notice = { id: noticeId(kind, text, at), at, kind, text };
|
|
112
|
+
state.notices.push(notice);
|
|
113
|
+
if (state.notices.length > NOTICE_KEEP) {
|
|
114
|
+
state.notices = state.notices.slice(state.notices.length - NOTICE_KEEP);
|
|
115
|
+
}
|
|
116
|
+
return notice;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Notices this session has not seen yet, marked delivered. A session key with
|
|
120
|
+
* no record sees only the last 24 hours. Cheap when there is nothing to say:
|
|
121
|
+
* no write happens unless a notice is returned.
|
|
122
|
+
*/
|
|
123
|
+
export function pendingNotices(coordRoot, sessionKey, now = Date.now()) {
|
|
124
|
+
const state = readAutoUpdateState(coordRoot);
|
|
125
|
+
if (state.notices.length === 0)
|
|
126
|
+
return [];
|
|
127
|
+
const since = state.delivered[sessionKey];
|
|
128
|
+
const cutoff = since ?? new Date(now - NEW_SESSION_WINDOW_MS).toISOString();
|
|
129
|
+
const fresh = state.notices.filter((n) => (since ? n.at > cutoff : n.at >= cutoff));
|
|
130
|
+
if (fresh.length === 0)
|
|
131
|
+
return [];
|
|
132
|
+
const newest = fresh.reduce((max, n) => (n.at > max ? n.at : max), fresh[0]?.at ?? "");
|
|
133
|
+
mutateAutoUpdateState(coordRoot, (s) => {
|
|
134
|
+
const prior = s.delivered[sessionKey];
|
|
135
|
+
if (!prior || newest > prior)
|
|
136
|
+
s.delivered[sessionKey] = newest;
|
|
137
|
+
for (const [key, at] of Object.entries(s.delivered)) {
|
|
138
|
+
const ms = Date.parse(at);
|
|
139
|
+
if (key !== sessionKey && (!Number.isFinite(ms) || now - ms > DELIVERED_PRUNE_MS)) {
|
|
140
|
+
delete s.delivered[key];
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
return fresh;
|
|
145
|
+
}
|
|
146
|
+
/** Minutes after which a held lock counts as abandoned. */
|
|
147
|
+
export function lockStaleMinutes(cfg) {
|
|
148
|
+
return cfg.timeoutMinutes * Math.max(1, cfg.steps.length + cfg.dailySteps.length) + 5;
|
|
149
|
+
}
|
|
150
|
+
/** Inspect the lock without taking it. One stat when free. */
|
|
151
|
+
export function inspectLock(coordRoot, cfg, now = Date.now()) {
|
|
152
|
+
const lockDir = autoUpdateLockPath(coordRoot);
|
|
153
|
+
let dirMtime;
|
|
154
|
+
try {
|
|
155
|
+
dirMtime = statSync(lockDir).mtimeMs;
|
|
156
|
+
}
|
|
157
|
+
catch {
|
|
158
|
+
return { held: false, stale: false, owner: null, ageMs: null };
|
|
159
|
+
}
|
|
160
|
+
let owner = null;
|
|
161
|
+
try {
|
|
162
|
+
const parsed = JSON.parse(readFileSync(join(lockDir, "owner.json"), "utf8"));
|
|
163
|
+
if (isRecord(parsed) && typeof parsed.pid === "number" && typeof parsed.at === "string") {
|
|
164
|
+
owner = {
|
|
165
|
+
pid: parsed.pid,
|
|
166
|
+
at: parsed.at,
|
|
167
|
+
...(typeof parsed.host === "string" ? { host: parsed.host } : {}),
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
catch {
|
|
172
|
+
owner = null;
|
|
173
|
+
}
|
|
174
|
+
const since = owner ? Date.parse(owner.at) : dirMtime;
|
|
175
|
+
const ageMs = now - (Number.isFinite(since) ? since : dirMtime);
|
|
176
|
+
const stale = ageMs > lockStaleMinutes(cfg) * 60_000;
|
|
177
|
+
return { held: true, stale, owner, ageMs };
|
|
178
|
+
}
|
|
179
|
+
/** Take the run lock, breaking it once when it is stale. */
|
|
180
|
+
export function acquireRunLock(coordRoot, cfg, now = Date.now()) {
|
|
181
|
+
mkdirSync(autoUpdateDir(coordRoot), { recursive: true });
|
|
182
|
+
const lockDir = autoUpdateLockPath(coordRoot);
|
|
183
|
+
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
184
|
+
try {
|
|
185
|
+
mkdirSync(lockDir);
|
|
186
|
+
}
|
|
187
|
+
catch (err) {
|
|
188
|
+
if (err.code !== "EEXIST")
|
|
189
|
+
throw err;
|
|
190
|
+
const inspection = inspectLock(coordRoot, cfg, now);
|
|
191
|
+
if (!inspection.stale || attempt > 0)
|
|
192
|
+
return { acquired: false, owner: inspection.owner };
|
|
193
|
+
rmSync(lockDir, { recursive: true, force: true });
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
const owner = {
|
|
197
|
+
pid: process.pid,
|
|
198
|
+
at: new Date(now).toISOString(),
|
|
199
|
+
host: hostname(),
|
|
200
|
+
};
|
|
201
|
+
writeFileSync(join(lockDir, "owner.json"), `${JSON.stringify(owner)}\n`);
|
|
202
|
+
let released = false;
|
|
203
|
+
return {
|
|
204
|
+
acquired: true,
|
|
205
|
+
release: () => {
|
|
206
|
+
if (released)
|
|
207
|
+
return;
|
|
208
|
+
released = true;
|
|
209
|
+
rmSync(lockDir, { recursive: true, force: true });
|
|
210
|
+
},
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
return { acquired: false, owner: inspectLock(coordRoot, cfg, now).owner };
|
|
214
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared contracts for project self-update (ADR 0229).
|
|
3
|
+
*
|
|
4
|
+
* Activity fires it: every prompt, session start, and host CLI command calls
|
|
5
|
+
* `autoUpdateTick`, whose hot path is one stat of a throttle stamp. When the
|
|
6
|
+
* stamp is stale, the tick launches a detached `<harn> autoupdate run
|
|
7
|
+
* --background`, which exits early unless the upstream branch moved, a
|
|
8
|
+
* watched path outside Git changed, the daily check is due, or the last run
|
|
9
|
+
* failed. Only then does it run the host's configured steps.
|
|
10
|
+
*/
|
|
11
|
+
/** `.harnery/config.jsonc` `autoUpdate` section, after lenient parsing. */
|
|
12
|
+
export interface AutoUpdateConfig {
|
|
13
|
+
enabled: boolean;
|
|
14
|
+
/** Minimum minutes between remote checks. Default 15, range 1..1440. */
|
|
15
|
+
checkMinutes: number;
|
|
16
|
+
/** Argv arrays run in order from the project root when an update is needed; first failure stops. */
|
|
17
|
+
steps: string[][];
|
|
18
|
+
/** Argv arrays run at most once per 24h even when current (e.g. a doctor). */
|
|
19
|
+
dailySteps: string[][];
|
|
20
|
+
/**
|
|
21
|
+
* Paths outside Git whose change forces a run (managed tools, generated
|
|
22
|
+
* hook configs). `~/` and `$VAR` expand; relative paths resolve from the
|
|
23
|
+
* project root. A directory is fingerprinted shallowly (its own mtime plus
|
|
24
|
+
* each direct child's mtime and size).
|
|
25
|
+
*/
|
|
26
|
+
watch: string[];
|
|
27
|
+
/** Per-step timeout in minutes. Default 20. */
|
|
28
|
+
timeoutMinutes: number;
|
|
29
|
+
/** Run `artifacts clean --yes` (expired only) at most once per day after a successful run. Default true. */
|
|
30
|
+
cleanup: boolean;
|
|
31
|
+
}
|
|
32
|
+
export declare const AUTOUPDATE_DEFAULTS: {
|
|
33
|
+
readonly checkMinutes: 15;
|
|
34
|
+
readonly timeoutMinutes: 20;
|
|
35
|
+
/** After a failed run, wait at least this long before the next attempt. */
|
|
36
|
+
readonly failureBackoffMinutes: 60;
|
|
37
|
+
readonly dailyHours: 24;
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* Step output protocol. A line `harnery-notice: <text>` (stdout or stderr)
|
|
41
|
+
* becomes a notice for the agents in this checkout. Exit code 75 means the
|
|
42
|
+
* step finished what it could but needs a person (outcome `blocked`), which
|
|
43
|
+
* is not retried as a failure.
|
|
44
|
+
*/
|
|
45
|
+
export declare const NOTICE_PREFIX = "harnery-notice:";
|
|
46
|
+
export declare const EXIT_NEEDS_PERSON = 75;
|
|
47
|
+
export type CheckReason = "disabled" | "behind" | "drift" | "daily" | "retry" | "forced" | "current" | "no-upstream" | "offline";
|
|
48
|
+
export interface CheckDecision {
|
|
49
|
+
needed: boolean;
|
|
50
|
+
reason: CheckReason;
|
|
51
|
+
head: string | null;
|
|
52
|
+
upstream: string | null;
|
|
53
|
+
remoteSha: string | null;
|
|
54
|
+
fingerprint: string;
|
|
55
|
+
/** True when only daily steps should run (repo already current). */
|
|
56
|
+
dailyOnly: boolean;
|
|
57
|
+
detail?: string;
|
|
58
|
+
}
|
|
59
|
+
export type NoticeKind = "instructions" | "hooks" | "person" | "failure" | "info";
|
|
60
|
+
export interface AutoUpdateNotice {
|
|
61
|
+
id: string;
|
|
62
|
+
at: string;
|
|
63
|
+
kind: NoticeKind;
|
|
64
|
+
text: string;
|
|
65
|
+
}
|
|
66
|
+
export interface StepRecord {
|
|
67
|
+
argv: string[];
|
|
68
|
+
exitCode: number | null;
|
|
69
|
+
ms: number;
|
|
70
|
+
timedOut?: boolean;
|
|
71
|
+
}
|
|
72
|
+
export type RunOutcome = "current" | "updated" | "blocked" | "failed" | "skipped-locked" | "offline";
|
|
73
|
+
export interface RunRecord {
|
|
74
|
+
startedAt: string;
|
|
75
|
+
finishedAt: string;
|
|
76
|
+
outcome: RunOutcome;
|
|
77
|
+
reason: CheckReason;
|
|
78
|
+
fromHead: string | null;
|
|
79
|
+
toHead: string | null;
|
|
80
|
+
steps: StepRecord[];
|
|
81
|
+
error?: string;
|
|
82
|
+
}
|
|
83
|
+
/** `.harnery/private/autoupdate/state.json`. */
|
|
84
|
+
export interface AutoUpdateState {
|
|
85
|
+
schema: 1;
|
|
86
|
+
converged?: {
|
|
87
|
+
head: string;
|
|
88
|
+
remoteSha: string | null;
|
|
89
|
+
fingerprint: string;
|
|
90
|
+
at: string;
|
|
91
|
+
};
|
|
92
|
+
lastRun?: RunRecord;
|
|
93
|
+
dailyAt?: string;
|
|
94
|
+
cleanupAt?: string;
|
|
95
|
+
notices: AutoUpdateNotice[];
|
|
96
|
+
/** Per session key (agent instance id), the `at` of the newest notice already delivered. */
|
|
97
|
+
delivered: Record<string, string>;
|
|
98
|
+
}
|
|
99
|
+
export interface TickResult {
|
|
100
|
+
action: "disabled" | "throttled" | "locked" | "launched" | "error";
|
|
101
|
+
detail?: string;
|
|
102
|
+
}
|
|
103
|
+
/** Files under `.harnery/private/autoupdate/` (except `log`, see below). */
|
|
104
|
+
export declare const AUTOUPDATE_FILES: {
|
|
105
|
+
readonly dir: ".harnery/private/autoupdate";
|
|
106
|
+
/** Throttle stamp; only its mtime matters. */
|
|
107
|
+
readonly stamp: "checked-at";
|
|
108
|
+
readonly state: "state.json";
|
|
109
|
+
readonly lock: "run.lock";
|
|
110
|
+
/** Run log lives at .harnery/logs/autoupdate.log (not in this dir). */
|
|
111
|
+
readonly log: "autoupdate.log";
|
|
112
|
+
};
|
|
113
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/core/autoupdate/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared contracts for project self-update (ADR 0229).
|
|
3
|
+
*
|
|
4
|
+
* Activity fires it: every prompt, session start, and host CLI command calls
|
|
5
|
+
* `autoUpdateTick`, whose hot path is one stat of a throttle stamp. When the
|
|
6
|
+
* stamp is stale, the tick launches a detached `<harn> autoupdate run
|
|
7
|
+
* --background`, which exits early unless the upstream branch moved, a
|
|
8
|
+
* watched path outside Git changed, the daily check is due, or the last run
|
|
9
|
+
* failed. Only then does it run the host's configured steps.
|
|
10
|
+
*/
|
|
11
|
+
export const AUTOUPDATE_DEFAULTS = {
|
|
12
|
+
checkMinutes: 15,
|
|
13
|
+
timeoutMinutes: 20,
|
|
14
|
+
/** After a failed run, wait at least this long before the next attempt. */
|
|
15
|
+
failureBackoffMinutes: 60,
|
|
16
|
+
dailyHours: 24,
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Step output protocol. A line `harnery-notice: <text>` (stdout or stderr)
|
|
20
|
+
* becomes a notice for the agents in this checkout. Exit code 75 means the
|
|
21
|
+
* step finished what it could but needs a person (outcome `blocked`), which
|
|
22
|
+
* is not retried as a failure.
|
|
23
|
+
*/
|
|
24
|
+
export const NOTICE_PREFIX = "harnery-notice:";
|
|
25
|
+
export const EXIT_NEEDS_PERSON = 75;
|
|
26
|
+
/** Files under `.harnery/private/autoupdate/` (except `log`, see below). */
|
|
27
|
+
export const AUTOUPDATE_FILES = {
|
|
28
|
+
dir: ".harnery/private/autoupdate",
|
|
29
|
+
/** Throttle stamp; only its mtime matters. */
|
|
30
|
+
stamp: "checked-at",
|
|
31
|
+
state: "state.json",
|
|
32
|
+
lock: "run.lock",
|
|
33
|
+
/** Run log lives at .harnery/logs/autoupdate.log (not in this dir). */
|
|
34
|
+
log: "autoupdate.log",
|
|
35
|
+
};
|
package/dist/core/config.d.ts
CHANGED
|
@@ -11,13 +11,15 @@
|
|
|
11
11
|
* `coord` (heartbeat freshness), `logs` (structured-log storage budgets),
|
|
12
12
|
* `artifacts` (working-file retention),
|
|
13
13
|
* `backup` (restic repo/password/prune policy), `sync` (rclone
|
|
14
|
-
* remote/prefix),
|
|
14
|
+
* remote/prefix), `computer` (opt-in desktop control), `autoUpdate` (activity-fired
|
|
15
|
+
* project self-update), and `web` (dashboard port) sections. The `files` deny/override section
|
|
15
16
|
* is parsed separately by `web/lib/files.ts`.
|
|
16
17
|
*
|
|
17
18
|
* Env vars and CLI flags override any config value per invocation (each accessor
|
|
18
19
|
* documents its own precedence). Dependency-free (no jsonc npm dep) so it runs on
|
|
19
20
|
* both Bun and Node, and so the ADR-009 vendored copies stay portable.
|
|
20
21
|
*/
|
|
22
|
+
import { type AutoUpdateConfig } from "./autoupdate/types.js";
|
|
21
23
|
import { type PromptContextConfig } from "./hooks/prompt-context/contract.js";
|
|
22
24
|
export { DEFAULT_WEB_PORT } from "../lib/local-file-url.js";
|
|
23
25
|
export { DEFAULT_EVENT_LEDGER_ROTATE_ACTIVE_BYTES } from "./events/v3/rotation-config.js";
|
|
@@ -112,6 +114,15 @@ export interface CodexWindowsBridgeConfig {
|
|
|
112
114
|
* ignored and reported rather than spliced into a shell command.
|
|
113
115
|
*/
|
|
114
116
|
export declare function codexWindowsBridgeConfig(projectRoot: string): CodexWindowsBridgeConfig;
|
|
117
|
+
/**
|
|
118
|
+
* Per-project opt-in that makes `init` write the same adapter hook commands on
|
|
119
|
+
* every OS: `agent-hook` from PATH, plus a native Codex `commandWindows`. A
|
|
120
|
+
* project that commits its settings files and is cloned on Windows and POSIX
|
|
121
|
+
* needs it, because the default form depends on the OS that ran `init`. Read
|
|
122
|
+
* from the project config only, for the same reason as the Windows bridge.
|
|
123
|
+
* Anything but `true` keeps the default.
|
|
124
|
+
*/
|
|
125
|
+
export declare function portableHookCommands(projectRoot: string): boolean;
|
|
115
126
|
export declare function hostPromptReminder(coordRoot?: string | null): string | null;
|
|
116
127
|
/**
|
|
117
128
|
* Project-owned prompt-context provider settings. The user-global config is
|
|
@@ -191,6 +202,47 @@ export interface AdmissionProjectConfig {
|
|
|
191
202
|
* project declares no admission settings.
|
|
192
203
|
*/
|
|
193
204
|
export declare function readAdmissionConfig(coordRoot?: string | null): AdmissionProjectConfig | undefined;
|
|
205
|
+
/** Effective `computer` settings; unset tunables fall back to the command's defaults. */
|
|
206
|
+
export interface ComputerProjectConfig {
|
|
207
|
+
enabled: boolean;
|
|
208
|
+
/** Desktop lease idle expiry in seconds. */
|
|
209
|
+
idle_seconds?: number;
|
|
210
|
+
/** How long `computer call` waits for another agent's lease, in seconds. */
|
|
211
|
+
wait_seconds?: number;
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Desktop-control settings from `.harnery/config.jsonc`:
|
|
215
|
+
*
|
|
216
|
+
* ```jsonc
|
|
217
|
+
* "computer": { "enabled": true, "idle_seconds": 120, "wait_seconds": 30 }
|
|
218
|
+
* ```
|
|
219
|
+
*
|
|
220
|
+
* Project config only: enabling desktop control is a per-project decision, so a
|
|
221
|
+
* user-global value never turns it on. Malformed values are dropped one by one;
|
|
222
|
+
* `enabled` is true only when the project sets it to exactly `true`.
|
|
223
|
+
*/
|
|
224
|
+
export declare function readComputerConfig(coordRoot?: string | null): ComputerProjectConfig;
|
|
225
|
+
/**
|
|
226
|
+
* Project self-update settings from `.harnery/config.jsonc` (ADR 0229):
|
|
227
|
+
*
|
|
228
|
+
* ```jsonc
|
|
229
|
+
* "autoUpdate": {
|
|
230
|
+
* "enabled": true,
|
|
231
|
+
* "steps": [["bin/pull-all"]],
|
|
232
|
+
* "dailySteps": [["bin/doctor", "--quiet"]],
|
|
233
|
+
* "watch": ["~/.local/share/example/hooks.json"],
|
|
234
|
+
* "checkMinutes": 15,
|
|
235
|
+
* "timeoutMinutes": 20,
|
|
236
|
+
* "cleanup": true
|
|
237
|
+
* }
|
|
238
|
+
* ```
|
|
239
|
+
*
|
|
240
|
+
* Project config only: running commands on activity is a per-project
|
|
241
|
+
* decision, so a user-global value never turns it on. Null when the project
|
|
242
|
+
* has no `autoUpdate` section. Malformed values are dropped one by one;
|
|
243
|
+
* `enabled` is true only when the project sets it to exactly `true`.
|
|
244
|
+
*/
|
|
245
|
+
export declare function readAutoUpdateConfig(coordRoot: string): AutoUpdateConfig | null;
|
|
194
246
|
/**
|
|
195
247
|
* Whether this repo pins workflow runs to subscription billing:
|
|
196
248
|
* `.harnery/config.jsonc` `{ "workflow": { "subscriptionOnly": true } }`.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/core/config.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/core/config.ts"],"names":[],"mappings":"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"}
|