harnery 0.41.0 → 0.42.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 +3 -0
- package/dist/commands/agents.d.ts.map +1 -1
- package/dist/commands/agents.js +9 -1
- package/dist/commands/artifacts.d.ts.map +1 -1
- package/dist/commands/artifacts.js +113 -12
- package/dist/commands/browse.d.ts +164 -0
- package/dist/commands/browse.d.ts.map +1 -1
- package/dist/commands/browse.js +263 -51
- package/dist/commands/claude-desktop.d.ts.map +1 -1
- package/dist/commands/claude-desktop.js +52 -0
- package/dist/commands/disk.d.ts +6 -0
- package/dist/commands/disk.d.ts.map +1 -0
- package/dist/commands/disk.js +80 -0
- package/dist/commands/doctor.d.ts +8 -0
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +48 -1
- package/dist/commands/files.d.ts +16 -0
- package/dist/commands/files.d.ts.map +1 -1
- package/dist/commands/files.js +54 -2
- package/dist/commands/init.d.ts +2 -2
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +43 -9
- package/dist/commands/rm.d.ts +4 -0
- package/dist/commands/rm.d.ts.map +1 -0
- package/dist/commands/rm.js +87 -0
- package/dist/commands/servers.d.ts +7 -0
- package/dist/commands/servers.d.ts.map +1 -0
- package/dist/commands/servers.js +394 -0
- package/dist/commands/tunnel.d.ts.map +1 -1
- package/dist/commands/tunnel.js +82 -2
- package/dist/core/adapters/profiles.d.ts +9 -0
- package/dist/core/adapters/profiles.d.ts.map +1 -1
- package/dist/core/adapters/profiles.js +22 -1
- package/dist/core/adapters/types.d.ts +9 -0
- package/dist/core/adapters/types.d.ts.map +1 -1
- package/dist/core/agents/cli.js +13 -6
- package/dist/core/agents/render/session-context.d.ts.map +1 -1
- package/dist/core/agents/render/session-context.js +2 -0
- package/dist/core/agents/state/live-coordination-view.d.ts +20 -0
- package/dist/core/agents/state/live-coordination-view.d.ts.map +1 -1
- package/dist/core/agents/state/live-coordination-view.js +60 -0
- package/dist/core/artifacts/delivery-card.js +9 -2
- package/dist/core/artifacts/index.d.ts +140 -4
- package/dist/core/artifacts/index.d.ts.map +1 -1
- package/dist/core/artifacts/index.js +522 -49
- package/dist/core/config.d.ts +68 -11
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +128 -3
- package/dist/core/diagnostics/bundle.d.ts.map +1 -1
- package/dist/core/diagnostics/bundle.js +2 -2
- package/dist/core/events/v3/producers/recorder.d.ts.map +1 -1
- package/dist/core/events/v3/producers/recorder.js +16 -0
- package/dist/core/events/v3/projection.js +9 -1
- package/dist/core/hooks/adapter/events.d.ts +17 -0
- package/dist/core/hooks/adapter/events.d.ts.map +1 -1
- package/dist/core/hooks/adapter/events.js +5 -0
- package/dist/core/hooks/adapter/runtime-telemetry.js +3 -1
- package/dist/core/hooks/adapter/wiring.d.ts +37 -7
- package/dist/core/hooks/adapter/wiring.d.ts.map +1 -1
- package/dist/core/hooks/adapter/wiring.js +62 -4
- package/dist/core/hooks/cli.js +45 -6
- package/dist/core/hooks/run-markers.d.ts +37 -0
- package/dist/core/hooks/run-markers.d.ts.map +1 -0
- package/dist/core/hooks/run-markers.js +109 -0
- package/dist/core/servers/index.d.ts +211 -0
- package/dist/core/servers/index.d.ts.map +1 -0
- package/dist/core/servers/index.js +560 -0
- package/dist/core/servers/net.d.ts +58 -0
- package/dist/core/servers/net.d.ts.map +1 -0
- package/dist/core/servers/net.js +270 -0
- package/dist/core/servers/tunnels.d.ts +14 -0
- package/dist/core/servers/tunnels.d.ts.map +1 -0
- package/dist/core/servers/tunnels.js +33 -0
- package/dist/core/storage/builtins.js +1 -0
- package/dist/core/workflow/engine.d.ts.map +1 -1
- package/dist/core/workflow/engine.js +30 -1
- package/dist/core/workflow/sandbox-projection.d.ts +13 -2
- package/dist/core/workflow/sandbox-projection.d.ts.map +1 -1
- package/dist/core/workflow/sandbox-projection.js +23 -0
- package/dist/core/workflow/spawn-claude.d.ts.map +1 -1
- package/dist/core/workflow/spawn-claude.js +6 -1
- package/dist/core/workflow/spawn-codex.d.ts +2 -0
- package/dist/core/workflow/spawn-codex.d.ts.map +1 -1
- package/dist/core/workflow/spawn-codex.js +20 -1
- package/dist/core/workflow/spawn-cursor.d.ts.map +1 -1
- package/dist/core/workflow/spawn-cursor.js +6 -1
- package/dist/core/workflow/spawn-opencode.d.ts.map +1 -1
- package/dist/core/workflow/spawn-opencode.js +4 -1
- package/dist/core/workflow/types.d.ts +47 -2
- package/dist/core/workflow/types.d.ts.map +1 -1
- package/dist/core/workflow/worker-access.d.ts +92 -0
- package/dist/core/workflow/worker-access.d.ts.map +1 -0
- package/dist/core/workflow/worker-access.js +450 -0
- package/dist/lib/browser/batch-steps.d.ts +18 -0
- package/dist/lib/browser/batch-steps.d.ts.map +1 -0
- package/dist/lib/browser/batch-steps.js +82 -0
- package/dist/lib/browser/collect/collector.d.ts +99 -0
- package/dist/lib/browser/collect/collector.d.ts.map +1 -0
- package/dist/lib/browser/collect/collector.js +202 -0
- package/dist/lib/browser/collect/fields.d.ts +31 -0
- package/dist/lib/browser/collect/fields.d.ts.map +1 -0
- package/dist/lib/browser/collect/fields.js +75 -0
- package/dist/lib/browser/collect/index.d.ts +6 -0
- package/dist/lib/browser/collect/index.d.ts.map +1 -0
- package/dist/lib/browser/collect/index.js +5 -0
- package/dist/lib/browser/collect/page-driver.d.ts +14 -0
- package/dist/lib/browser/collect/page-driver.d.ts.map +1 -0
- package/dist/lib/browser/collect/page-driver.js +306 -0
- package/dist/lib/browser/collect/presets.d.ts +52 -0
- package/dist/lib/browser/collect/presets.d.ts.map +1 -0
- package/dist/lib/browser/collect/presets.js +176 -0
- package/dist/lib/browser/collect/scroll-plan.d.ts +85 -0
- package/dist/lib/browser/collect/scroll-plan.d.ts.map +1 -0
- package/dist/lib/browser/collect/scroll-plan.js +118 -0
- package/dist/lib/browser/index.d.ts +1 -1
- package/dist/lib/browser/index.d.ts.map +1 -1
- package/dist/lib/browser/page-review-judge.d.ts +2 -0
- package/dist/lib/browser/page-review-judge.d.ts.map +1 -1
- package/dist/lib/browser/page-review-judge.js +13 -0
- package/dist/lib/browser/qa-run-contracts.d.ts +10 -0
- package/dist/lib/browser/qa-run-contracts.d.ts.map +1 -1
- package/dist/lib/browser/qa-run.d.ts.map +1 -1
- package/dist/lib/browser/qa-run.js +1 -0
- package/dist/lib/browser/windows-chrome.d.ts +59 -0
- package/dist/lib/browser/windows-chrome.d.ts.map +1 -0
- package/dist/lib/browser/windows-chrome.js +179 -0
- package/dist/lib/claude-desktop-share.d.ts +136 -0
- package/dist/lib/claude-desktop-share.d.ts.map +1 -0
- package/dist/lib/claude-desktop-share.js +394 -0
- package/dist/lib/disk-usage.d.ts +50 -0
- package/dist/lib/disk-usage.d.ts.map +1 -0
- package/dist/lib/disk-usage.js +303 -0
- package/dist/lib/docs-lint.d.ts +6 -0
- package/dist/lib/docs-lint.d.ts.map +1 -1
- package/dist/lib/docs-lint.js +8 -3
- package/dist/lib/guarded-remove.d.ts +34 -0
- package/dist/lib/guarded-remove.d.ts.map +1 -0
- package/dist/lib/guarded-remove.js +310 -0
- package/dist/lib/instructions/templates.js +8 -8
- package/dist/lib/tunnel/error-page.d.ts +1 -1
- package/dist/lib/tunnel/error-page.d.ts.map +1 -1
- package/dist/lib/tunnel/error-page.js +19 -8
- package/dist/lib/tunnel/gate.js +20 -1
- package/dist/lib/tunnel/path-scope.d.ts +22 -0
- package/dist/lib/tunnel/path-scope.d.ts.map +1 -0
- package/dist/lib/tunnel/path-scope.js +62 -0
- package/dist/lib/tunnel/state.d.ts +5 -0
- package/dist/lib/tunnel/state.d.ts.map +1 -1
- package/dist/lib/tunnel/state.js +17 -3
- package/package.json +6 -1
- package/schemas/config.schema.json +91 -0
- package/src/commander.ts +19 -0
- package/src/commands/agents.ts +14 -0
- package/src/commands/artifacts.ts +171 -19
- package/src/commands/browse.ts +400 -57
- package/src/commands/claude-desktop.ts +61 -0
- package/src/commands/disk.ts +114 -0
- package/src/commands/doctor.ts +52 -1
- package/src/commands/files.ts +93 -2
- package/src/commands/init.ts +55 -5
- package/src/commands/rm.ts +115 -0
- package/src/commands/servers.ts +506 -0
- package/src/commands/tunnel.ts +102 -2
- package/src/core/adapters/profiles.ts +22 -1
- package/src/core/adapters/types.ts +10 -0
- package/src/core/agents/cli.ts +14 -6
- package/src/core/agents/render/session-context.ts +1 -0
- package/src/core/agents/state/live-coordination-view.ts +73 -0
- package/src/core/artifacts/delivery-card.ts +10 -2
- package/src/core/artifacts/index.ts +697 -44
- package/src/core/config.ts +183 -4
- package/src/core/diagnostics/bundle.ts +2 -1
- package/src/core/events/v3/producers/recorder.ts +16 -0
- package/src/core/events/v3/projection.ts +11 -1
- package/src/core/hooks/adapter/events.ts +19 -0
- package/src/core/hooks/adapter/runtime-telemetry.ts +3 -1
- package/src/core/hooks/adapter/wiring.ts +116 -8
- package/src/core/hooks/cli.ts +47 -6
- package/src/core/hooks/run-markers.ts +132 -0
- package/src/core/servers/index.ts +712 -0
- package/src/core/servers/net.ts +283 -0
- package/src/core/servers/tunnels.ts +39 -0
- package/src/core/storage/builtins.ts +1 -0
- package/src/core/workflow/engine.ts +38 -1
- package/src/core/workflow/sandbox-projection.ts +38 -2
- package/src/core/workflow/spawn-claude.ts +5 -1
- package/src/core/workflow/spawn-codex.ts +18 -1
- package/src/core/workflow/spawn-cursor.ts +5 -1
- package/src/core/workflow/spawn-opencode.ts +4 -1
- package/src/core/workflow/types.ts +62 -2
- package/src/core/workflow/worker-access.ts +525 -0
- package/src/lib/browser/batch-steps.ts +79 -0
- package/src/lib/browser/collect/collector.ts +306 -0
- package/src/lib/browser/collect/fields.ts +87 -0
- package/src/lib/browser/collect/index.ts +47 -0
- package/src/lib/browser/collect/page-driver.ts +327 -0
- package/src/lib/browser/collect/presets.ts +237 -0
- package/src/lib/browser/collect/scroll-plan.ts +179 -0
- package/src/lib/browser/index.ts +1 -0
- package/src/lib/browser/page-review-judge.ts +16 -0
- package/src/lib/browser/qa-run-contracts.ts +11 -0
- package/src/lib/browser/qa-run.ts +1 -0
- package/src/lib/browser/windows-chrome.ts +227 -0
- package/src/lib/claude-desktop-share.ts +511 -0
- package/src/lib/disk-usage.ts +347 -0
- package/src/lib/docs-lint.ts +8 -3
- package/src/lib/guarded-remove.ts +376 -0
- package/src/lib/instructions/templates.ts +8 -8
- package/src/lib/tunnel/error-page.ts +25 -9
- package/src/lib/tunnel/gate.ts +22 -1
- package/src/lib/tunnel/path-scope.ts +73 -0
- package/src/lib/tunnel/state.ts +20 -3
package/dist/commands/doctor.js
CHANGED
|
@@ -15,12 +15,13 @@ import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
|
15
15
|
import os from "node:os";
|
|
16
16
|
import path from "node:path";
|
|
17
17
|
import { BUILTIN_ADAPTER_IDS } from "../core/adapters/index.js";
|
|
18
|
-
import { resolveBinName, ripgrepAutoInstall } from "../core/config.js";
|
|
18
|
+
import { codexWindowsBridgeConfig, resolveBinName, ripgrepAutoInstall } from "../core/config.js";
|
|
19
19
|
import { countSummarizedSinceV3, listDiagnosticSummariesV3, } from "../core/events/v3/producers/diagnostic-summaries.js";
|
|
20
20
|
import { ADAPTER_SPECS } from "../core/hooks/adapter/events.js";
|
|
21
21
|
import { loadAdapterWiring, summarizeAdapterWiring } from "../core/hooks/adapter/wiring.js";
|
|
22
22
|
import { probeCodexHookAuthorization, } from "../core/hooks/codex-authorization.js";
|
|
23
23
|
import { inspectCodexWslBridge, } from "../core/hooks/codex-wsl-bridge.js";
|
|
24
|
+
import { findUnfinishedHookRuns } from "../core/hooks/run-markers.js";
|
|
24
25
|
import { ADAPTER_BINARIES, ADAPTER_INSTALL_HINTS, ADAPTER_LOGIN_HINTS, } from "../core/workflow/adapters.js";
|
|
25
26
|
import { probeBilling } from "../core/workflow/billing.js";
|
|
26
27
|
import { checkGitHooks, gitHooksInstalled } from "../lib/instructions/git-hooks.js";
|
|
@@ -74,6 +75,8 @@ export async function runChecks() {
|
|
|
74
75
|
const codexWslStatus = inspectCodexWslBridge();
|
|
75
76
|
const recentCodexMidFlightCount = root ? countRecentCodexMidFlightOnboardings(root) : 0;
|
|
76
77
|
const codexWslBridge = codexWslBridgeCheck(codexWslStatus, recentCodexMidFlightCount);
|
|
78
|
+
const codexHookRuns = codexHookRunsCheck(root);
|
|
79
|
+
const codexWindowsRoute = codexWindowsBridgeCheck(root);
|
|
77
80
|
// Probe the adapter CLIs before the hook check runs, not in output order:
|
|
78
81
|
// an unwired adapter only matters if its CLI is actually installed, and
|
|
79
82
|
// probing once here keeps that join to one spawn per CLI.
|
|
@@ -90,6 +93,8 @@ export async function runChecks() {
|
|
|
90
93
|
...(codexAuthorization ? [codexAuthorization] : []),
|
|
91
94
|
checkGitHookRegions(),
|
|
92
95
|
...(codexWslBridge ? [codexWslBridge] : []),
|
|
96
|
+
...(codexWindowsRoute ? [codexWindowsRoute] : []),
|
|
97
|
+
...(codexHookRuns ? [codexHookRuns] : []),
|
|
93
98
|
...workflow.map((w) => w.check),
|
|
94
99
|
checkRestic(),
|
|
95
100
|
checkRclone(),
|
|
@@ -141,6 +146,48 @@ export function codexWslBridgeCheck(status, recentMidFlightCount = 0) {
|
|
|
141
146
|
: "forward CODEX_THREAD_ID through WSLENV in the Codex shell environment policy, then start a fresh task",
|
|
142
147
|
};
|
|
143
148
|
}
|
|
149
|
+
/**
|
|
150
|
+
* Hook runs that started but never finished (ADR 0197). Codex kills a hook at
|
|
151
|
+
* its timeout and the tool proceeds without that hook's checks; the start
|
|
152
|
+
* marker is the only trace such a run leaves.
|
|
153
|
+
*/
|
|
154
|
+
export function codexHookRunsCheck(coordRoot, nowMs = Date.now(), isAlive) {
|
|
155
|
+
if (!coordRoot)
|
|
156
|
+
return null;
|
|
157
|
+
const runs = findUnfinishedHookRuns(coordRoot, nowMs, undefined, isAlive);
|
|
158
|
+
if (runs.count === 0)
|
|
159
|
+
return null;
|
|
160
|
+
const events = runs.events.map((e) => `${e.event} ×${e.count}`).join(", ");
|
|
161
|
+
return {
|
|
162
|
+
name: "codex:hook runs",
|
|
163
|
+
severity: "warn",
|
|
164
|
+
detail: `${runs.count} hook run${runs.count === 1 ? "" : "s"} started but never finished in the last 24h ` +
|
|
165
|
+
`(${events}; latest ${runs.latest}); their tools proceeded without those hooks' checks`,
|
|
166
|
+
hint: "a hook that outlives its timeout is killed; look for a stalled WSL session or a slow hook",
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
/** Report the per-project Windows bridge opt-in, or an invalid value init ignored. */
|
|
170
|
+
export function codexWindowsBridgeCheck(coordRoot) {
|
|
171
|
+
if (!coordRoot)
|
|
172
|
+
return null;
|
|
173
|
+
const bridge = codexWindowsBridgeConfig(coordRoot);
|
|
174
|
+
if (bridge.error) {
|
|
175
|
+
return {
|
|
176
|
+
name: "codex:Windows hook route",
|
|
177
|
+
severity: "warn",
|
|
178
|
+
detail: `${bridge.error}; Codex hooks are wired without commandWindows`,
|
|
179
|
+
hint: "set hooks.codexWindowsBridge.entryPoint to a plain command name in .harnery/config.jsonc, then re-run init",
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
if (!bridge.entryPoint)
|
|
183
|
+
return null;
|
|
184
|
+
return {
|
|
185
|
+
name: "codex:Windows hook route",
|
|
186
|
+
severity: "ok",
|
|
187
|
+
detail: `opted in: Windows-native Codex hooks hand off to \`${bridge.entryPoint}\` from WSL paths ` +
|
|
188
|
+
`when it is on PATH, otherwise run the usual command; bridge health is reported by the bridge itself`,
|
|
189
|
+
};
|
|
190
|
+
}
|
|
144
191
|
const CODEX_MID_FLIGHT_HISTORY_WINDOW_MS = 48 * 60 * 60 * 1000;
|
|
145
192
|
/** Count bounded, privacy-safe recovery diagnostics so a healthy environment
|
|
146
193
|
* now does not hide an intermittent Codex-to-WSL identity drop. */
|
package/dist/commands/files.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Command } from "commander";
|
|
2
2
|
import type { EmitContext, HarneryProgramContext } from "../commander.js";
|
|
3
|
+
import { type ProcessAliveCheck, type TunnelState } from "../lib/tunnel/state.js";
|
|
3
4
|
export { FILES_ORIGIN_HOST } from "../lib/local-file-url.js";
|
|
4
5
|
export type LocalFileUrlMode = "auto" | "raw" | "viewer";
|
|
5
6
|
export interface LocalFileUrlOptions {
|
|
@@ -30,5 +31,20 @@ export declare function verifyDashboardRoot(coordRoot: string, port: number, fet
|
|
|
30
31
|
* work. Other files default to the dashboard viewer.
|
|
31
32
|
*/
|
|
32
33
|
export declare function mintLocalFileUrl(input: string, options: LocalFileUrlOptions): LocalFileUrl;
|
|
34
|
+
export declare class TunnelLinkError extends Error {
|
|
35
|
+
readonly hint: string;
|
|
36
|
+
constructor(message: string, hint: string);
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Turn a minted local URL into the same file's URL on a live tunnel.
|
|
40
|
+
*
|
|
41
|
+
* HTML needs a tunnel in front of the isolated files origin (its vhost is
|
|
42
|
+
* FILES_ORIGIN_HOST), because only that origin renders the page and serves its
|
|
43
|
+
* relative assets; a dashboard tunnel can only show the viewer, which displays
|
|
44
|
+
* HTML as source. Other files need a dashboard tunnel that serves the viewer.
|
|
45
|
+
* In both cases the tunnel's path scope must forward the request, or its gate
|
|
46
|
+
* would refuse the link, so a tunnel that does not cover the file is skipped.
|
|
47
|
+
*/
|
|
48
|
+
export declare function tunnelFileUrl(local: LocalFileUrl, port: number, states: TunnelState[], processAlive?: ProcessAliveCheck): string;
|
|
33
49
|
export declare function registerFilesCommand(program: Command, emit: EmitContext, context?: HarneryProgramContext): void;
|
|
34
50
|
//# sourceMappingURL=files.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"files.d.ts","sourceRoot":"","sources":["../../src/commands/files.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,KAAK,EAAE,WAAW,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"files.d.ts","sourceRoot":"","sources":["../../src/commands/files.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,KAAK,EAAE,WAAW,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAY1E,OAAO,EAGL,KAAK,iBAAiB,EACtB,KAAK,WAAW,EAEjB,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAE7D,MAAM,MAAM,gBAAgB,GAAG,MAAM,GAAG,KAAK,GAAG,QAAQ,CAAC;AAEzD,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,gBAAgB,CAAC;CACzB;AAED,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,OAAO,CAAC,gBAAgB,EAAE,MAAM,CAAC,CAAC;CACzC;AAED,MAAM,MAAM,sBAAsB,GAC9B,uBAAuB,GACvB,gCAAgC,GAChC,yBAAyB,CAAC;AAE9B,qBAAa,kBAAmB,SAAQ,KAAK;IAEzC,QAAQ,CAAC,IAAI,EAAE,sBAAsB;IAErC,QAAQ,CAAC,IAAI,EAAE,MAAM;gBAFZ,IAAI,EAAE,sBAAsB,EACrC,OAAO,EAAE,MAAM,EACN,IAAI,EAAE,MAAM;CAKxB;AAMD,MAAM,MAAM,kBAAkB,GAAG,CAC/B,KAAK,EAAE,MAAM,GAAG,GAAG,GAAG,OAAO,EAC7B,IAAI,CAAC,EAAE,WAAW,KACf,OAAO,CAAC,QAAQ,CAAC,CAAC;AAEvB;;;GAGG;AACH,wBAAsB,mBAAmB,CACvC,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,MAAM,EACZ,SAAS,GAAE,kBAA0B,GACpC,OAAO,CAAC,IAAI,CAAC,CAmDf;AAuCD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,mBAAmB,GAAG,YAAY,CAS1F;AAED,qBAAa,eAAgB,SAAQ,KAAK;IAGtC,QAAQ,CAAC,IAAI,EAAE,MAAM;gBADrB,OAAO,EAAE,MAAM,EACN,IAAI,EAAE,MAAM;CAKxB;AAUD;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAC3B,KAAK,EAAE,YAAY,EACnB,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,WAAW,EAAE,EACrB,YAAY,CAAC,EAAE,iBAAiB,GAC/B,MAAM,CA+BR;AAED,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,WAAW,EACjB,OAAO,CAAC,EAAE,qBAAqB,GAC9B,IAAI,CAqFN"}
|
package/dist/commands/files.js
CHANGED
|
@@ -3,7 +3,9 @@ import path from "node:path";
|
|
|
3
3
|
import { resolveCoordRoot } from "../core/agents/coord-client.js";
|
|
4
4
|
import { DEFAULT_WEB_PORT, resolveWebPort } from "../core/config.js";
|
|
5
5
|
import { coordRootId, localCoordRootIdUrl } from "../lib/coord-root-id.js";
|
|
6
|
-
import { localFilesOriginUrl, localFileViewerUrl } from "../lib/local-file-url.js";
|
|
6
|
+
import { encodedRepoPath, encodeLinkSafeComponent, FILES_ORIGIN_HOST, localFilesOriginUrl, localFileViewerUrl, } from "../lib/local-file-url.js";
|
|
7
|
+
import { isPathAllowed } from "../lib/tunnel/path-scope.js";
|
|
8
|
+
import { isTunnelStateLive, listStates, tunnelTargetPort, } from "../lib/tunnel/state.js";
|
|
7
9
|
export { FILES_ORIGIN_HOST } from "../lib/local-file-url.js";
|
|
8
10
|
export class DashboardRootError extends Error {
|
|
9
11
|
code;
|
|
@@ -99,6 +101,49 @@ export function mintLocalFileUrl(input, options) {
|
|
|
99
101
|
: localFileViewerUrl(relPath, options.port);
|
|
100
102
|
return { url, relPath, mode };
|
|
101
103
|
}
|
|
104
|
+
export class TunnelLinkError extends Error {
|
|
105
|
+
hint;
|
|
106
|
+
constructor(message, hint) {
|
|
107
|
+
super(message);
|
|
108
|
+
this.hint = hint;
|
|
109
|
+
this.name = "TunnelLinkError";
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
function vhostName(vhost) {
|
|
113
|
+
return vhost.replace(/:\d+$/, "").toLowerCase();
|
|
114
|
+
}
|
|
115
|
+
function newestFirst(states) {
|
|
116
|
+
return [...states].sort((a, b) => b.started_at.localeCompare(a.started_at));
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Turn a minted local URL into the same file's URL on a live tunnel.
|
|
120
|
+
*
|
|
121
|
+
* HTML needs a tunnel in front of the isolated files origin (its vhost is
|
|
122
|
+
* FILES_ORIGIN_HOST), because only that origin renders the page and serves its
|
|
123
|
+
* relative assets; a dashboard tunnel can only show the viewer, which displays
|
|
124
|
+
* HTML as source. Other files need a dashboard tunnel that serves the viewer.
|
|
125
|
+
* In both cases the tunnel's path scope must forward the request, or its gate
|
|
126
|
+
* would refuse the link, so a tunnel that does not cover the file is skipped.
|
|
127
|
+
*/
|
|
128
|
+
export function tunnelFileUrl(local, port, states, processAlive) {
|
|
129
|
+
const live = states.filter((state) => tunnelTargetPort(state) === port && isTunnelStateLive(state, processAlive));
|
|
130
|
+
if (local.mode === "raw") {
|
|
131
|
+
const path = `/${encodedRepoPath(local.relPath)}`;
|
|
132
|
+
const tunnel = newestFirst(live).find((state) => vhostName(state.vhost) === FILES_ORIGIN_HOST && isPathAllowed(path, state.allow_paths));
|
|
133
|
+
if (tunnel)
|
|
134
|
+
return `${tunnel.url.replace(/\/+$/, "")}${path}`;
|
|
135
|
+
const folder = local.relPath.includes("/")
|
|
136
|
+
? `/${local.relPath.slice(0, local.relPath.lastIndexOf("/"))}`
|
|
137
|
+
: "/";
|
|
138
|
+
throw new TunnelLinkError(`no live tunnel serves the files origin for /${local.relPath}`, `Start one scoped to the page's folder, for example: tunnel up --name pages --target 127.0.0.1:${port} --vhost ${FILES_ORIGIN_HOST} --allow-path ${folder}`);
|
|
139
|
+
}
|
|
140
|
+
const tunnel = newestFirst(live).find((state) => vhostName(state.vhost) !== FILES_ORIGIN_HOST &&
|
|
141
|
+
["/files", "/api/file", "/_next"].every((path) => isPathAllowed(path, state.allow_paths)));
|
|
142
|
+
if (tunnel) {
|
|
143
|
+
return `${tunnel.url.replace(/\/+$/, "")}/files?path=${encodeLinkSafeComponent(local.relPath)}`;
|
|
144
|
+
}
|
|
145
|
+
throw new TunnelLinkError(`no live tunnel serves the dashboard file viewer on port ${port}`, `Start one, for example: tunnel up --name files --target 127.0.0.1:${port} --allow-path /files --allow-path /api/file --allow-path /_next`);
|
|
146
|
+
}
|
|
102
147
|
export function registerFilesCommand(program, emit, context) {
|
|
103
148
|
const files = program
|
|
104
149
|
.command("files")
|
|
@@ -112,6 +157,7 @@ export function registerFilesCommand(program, emit, context) {
|
|
|
112
157
|
.option("--raw", "Use the isolated files origin, regardless of extension")
|
|
113
158
|
.option("--viewer", "Use the dashboard file viewer, regardless of extension")
|
|
114
159
|
.option("--no-verify", "Mint without checking the running dashboard's repository identity")
|
|
160
|
+
.option("--tunnel", "Print the file's URL on a live tunnel instead of localhost (HTML uses a files-origin tunnel)")
|
|
115
161
|
.action(async (filePath, opts) => {
|
|
116
162
|
if (opts.raw && opts.viewer) {
|
|
117
163
|
emit.error({
|
|
@@ -147,9 +193,15 @@ export function registerFilesCommand(program, emit, context) {
|
|
|
147
193
|
});
|
|
148
194
|
if (opts.verify !== false)
|
|
149
195
|
await verifyDashboardRoot(coordRoot, port);
|
|
150
|
-
|
|
196
|
+
const url = opts.tunnel ? tunnelFileUrl(result, port, listStates(coordRoot)) : result.url;
|
|
197
|
+
emit.text(`${url}\n`);
|
|
151
198
|
}
|
|
152
199
|
catch (error) {
|
|
200
|
+
if (error instanceof TunnelLinkError) {
|
|
201
|
+
emit.error({ code: "no_tunnel_for_file", message: error.message, hint: error.hint });
|
|
202
|
+
emit.setExitCode(1);
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
153
205
|
if (error instanceof DashboardRootError) {
|
|
154
206
|
emit.error({ code: error.code, message: error.message, hint: error.hint });
|
|
155
207
|
emit.setExitCode(1);
|
package/dist/commands/init.d.ts
CHANGED
|
@@ -23,7 +23,7 @@ import type { Command } from "commander";
|
|
|
23
23
|
import type { EmitContext } from "../commander.js";
|
|
24
24
|
import type { EventV3ControlState } from "../core/events/v3/control.js";
|
|
25
25
|
import { type AdapterId, type AdapterSpec } from "../core/hooks/adapter/events.js";
|
|
26
|
-
import { type SettingsFile } from "../core/hooks/adapter/wiring.js";
|
|
26
|
+
import { type SettingsFile, type WiringOptions } from "../core/hooks/adapter/wiring.js";
|
|
27
27
|
/**
|
|
28
28
|
* Which adapters `init` should wire. An explicit `--adapter` wins. Without one:
|
|
29
29
|
* every adapter whose CLI is installed on this machine, plus every adapter the
|
|
@@ -55,7 +55,7 @@ export declare function codexHookReviewAction(adapter: AdapterId): string | null
|
|
|
55
55
|
*
|
|
56
56
|
* Token-aware matching keeps `stop` from matching `stop-failure`.
|
|
57
57
|
*/
|
|
58
|
-
export declare function wireHooks(settings: SettingsFile, spec: AdapterSpec, agentHookPath: string, adapter: AdapterId): {
|
|
58
|
+
export declare function wireHooks(settings: SettingsFile, spec: AdapterSpec, agentHookPath: string, adapter: AdapterId, options?: WiringOptions): {
|
|
59
59
|
wired: number;
|
|
60
60
|
already: number;
|
|
61
61
|
removed: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAOH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAOH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AASnD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,8BAA8B,CAAC;AAOxE,OAAO,EAAiB,KAAK,SAAS,EAAE,KAAK,WAAW,EAAE,MAAM,iCAAiC,CAAC;AAKlG,OAAO,EAcL,KAAK,YAAY,EAEjB,KAAK,aAAa,EACnB,MAAM,iCAAiC,CAAC;AA0CzC;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CACjC,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,EAAE,EACnB,SAAS,CAAC,EAAE,SAAS,EAAE,GACtB;IAAE,QAAQ,EAAE,SAAS,EAAE,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAM3C;AA6FD,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAmU/F;AAiBD;;;;GAIG;AACH,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,mBAAmB,GAAG,MAAM,EAAE,CAiBjF;AAED,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,SAAS,GAAG,MAAM,GAAG,IAAI,CAOvE;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,SAAS,CACvB,QAAQ,EAAE,YAAY,EACtB,IAAI,EAAE,WAAW,EACjB,aAAa,EAAE,MAAM,EACrB,OAAO,EAAE,SAAS,EAClB,OAAO,CAAC,EAAE,aAAa,GACtB;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAmEvE;AA2GD;;;;;;;;;GASG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,YAAY,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAa1F;AAED;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAmDhG;AASD;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAkCxF"}
|
package/dist/commands/init.js
CHANGED
|
@@ -24,14 +24,14 @@ import { createHash } from "node:crypto";
|
|
|
24
24
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
25
25
|
import { dirname, relative, resolve } from "node:path";
|
|
26
26
|
import { fileURLToPath } from "node:url";
|
|
27
|
-
import { DEFAULT_BIN_NAME, pinnedBinName, stripJsonComments } from "../core/config.js";
|
|
27
|
+
import { codexWindowsBridgeConfig, DEFAULT_BIN_NAME, pinnedBinName, stripJsonComments, } from "../core/config.js";
|
|
28
28
|
import { canonicalJsonV3 } from "../core/events/v3/canonical.js";
|
|
29
29
|
import { ADAPTER_CAPABILITY_PROFILES_V3 } from "../core/events/v3/capabilities.js";
|
|
30
30
|
import { EVENT_V3_SCHEMA_DIGEST } from "../core/events/v3/generated.js";
|
|
31
31
|
import { initializeEventLedgerV3, readEventV3ControlState, sha256V3, } from "../core/events/v3/index.js";
|
|
32
32
|
import { ADAPTER_SPECS } from "../core/hooks/adapter/events.js";
|
|
33
33
|
import { checkOpenCodePlugin, installOpenCodePlugin, } from "../core/hooks/adapter/opencode-plugin.js";
|
|
34
|
-
import { agentHookPathForProject, applyCursorStopLoopLimit, CURSOR_STOP_FOLLOWUP_LOOP_LIMIT, commandWiresSubcommand, diffWiring, groupCommands, hookCommand, isAgentHookCommand, makeEntry, summarizeAdapterWiring, } from "../core/hooks/adapter/wiring.js";
|
|
34
|
+
import { agentHookPathForProject, applyCursorStopLoopLimit, CURSOR_STOP_FOLLOWUP_LOOP_LIMIT, commandWiresSubcommand, diffWiring, expectedCommandWindows, groupCommands, hookCommand, hookTimeoutFor, isAgentHookCommand, makeEntry, orderHandlerKeys, summarizeAdapterWiring, } from "../core/hooks/adapter/wiring.js";
|
|
35
35
|
import { detectInstalledAdapters } from "../core/workflow/adapters.js";
|
|
36
36
|
import { applyIndexerExclusions } from "../lib/indexer-exclusions.js";
|
|
37
37
|
import { applyInstructions, checkInstructions, } from "../lib/instructions/apply.js";
|
|
@@ -102,7 +102,11 @@ function checkAdapterHooks(projectRoot, adapter, bin) {
|
|
|
102
102
|
let drift = false;
|
|
103
103
|
try {
|
|
104
104
|
const settings = JSON.parse(readFileSync(settingsPath, "utf8"));
|
|
105
|
-
const hookDiff = diffWiring(settings, spec, {
|
|
105
|
+
const hookDiff = diffWiring(settings, spec, {
|
|
106
|
+
agentHookPath: agentHook,
|
|
107
|
+
adapter,
|
|
108
|
+
windowsBridgeEntryPoint: codexWindowsBridgeConfig(projectRoot).entryPoint,
|
|
109
|
+
});
|
|
106
110
|
drift =
|
|
107
111
|
hookDiff.missing.length > 0 ||
|
|
108
112
|
hookDiff.stale.length > 0 ||
|
|
@@ -379,7 +383,11 @@ export function registerInitCommand(program, emit, binName) {
|
|
|
379
383
|
else {
|
|
380
384
|
settings = {};
|
|
381
385
|
}
|
|
382
|
-
const
|
|
386
|
+
const bridge = codexWindowsBridgeConfig(projectRoot);
|
|
387
|
+
if (bridge.error && spec.windowsBridgeHooks) {
|
|
388
|
+
actions.push(`⚠ ${bridge.error}; writing ${adapter} hooks without commandWindows`);
|
|
389
|
+
}
|
|
390
|
+
const { wired, already, removed, upgraded } = wireHooks(settings, spec, agentHook, adapter, { windowsBridgeEntryPoint: bridge.entryPoint });
|
|
383
391
|
if (wired === 0 && removed === 0 && upgraded === 0) {
|
|
384
392
|
actions.push(`· all ${spec.events.length} ${adapter} hooks already wired in ${rel(projectRoot, settingsPath)}`);
|
|
385
393
|
}
|
|
@@ -481,7 +489,7 @@ export function codexHookReviewAction(adapter) {
|
|
|
481
489
|
*
|
|
482
490
|
* Token-aware matching keeps `stop` from matching `stop-failure`.
|
|
483
491
|
*/
|
|
484
|
-
export function wireHooks(settings, spec, agentHookPath, adapter) {
|
|
492
|
+
export function wireHooks(settings, spec, agentHookPath, adapter, options) {
|
|
485
493
|
if (spec.rootVersion !== undefined && settings.version === undefined) {
|
|
486
494
|
settings.version = spec.rootVersion;
|
|
487
495
|
}
|
|
@@ -498,6 +506,13 @@ export function wireHooks(settings, spec, agentHookPath, adapter) {
|
|
|
498
506
|
}
|
|
499
507
|
for (const { settingsKey, subcommand } of spec.events) {
|
|
500
508
|
const command = hookCommand(spec, agentHookPath, subcommand, adapter);
|
|
509
|
+
const timeout = hookTimeoutFor(spec, subcommand);
|
|
510
|
+
const commandWindows = expectedCommandWindows(spec, agentHookPath, subcommand, adapter, options);
|
|
511
|
+
const handlerFields = {
|
|
512
|
+
timeout,
|
|
513
|
+
commandWindows,
|
|
514
|
+
windowsBridge: spec.windowsBridgeHooks === true,
|
|
515
|
+
};
|
|
501
516
|
let present = false;
|
|
502
517
|
for (const key of Object.keys(settings.hooks)) {
|
|
503
518
|
if (key === settingsKey)
|
|
@@ -507,7 +522,7 @@ export function wireHooks(settings, spec, agentHookPath, adapter) {
|
|
|
507
522
|
const groups = settings.hooks[settingsKey] ?? [];
|
|
508
523
|
const nextGroups = [];
|
|
509
524
|
for (const group of groups) {
|
|
510
|
-
const normalized = normalizeEventGroup(group, subcommand, command, present);
|
|
525
|
+
const normalized = normalizeEventGroup(group, subcommand, command, handlerFields, present);
|
|
511
526
|
if (normalized.found && !present)
|
|
512
527
|
present = true;
|
|
513
528
|
upgraded += normalized.upgraded;
|
|
@@ -526,7 +541,7 @@ export function wireHooks(settings, spec, agentHookPath, adapter) {
|
|
|
526
541
|
const current = settings.hooks[settingsKey] ?? [];
|
|
527
542
|
current.push(makeEntry(spec.entryShape, command, spec.entryShape === "cursor" && subcommand === "stop"
|
|
528
543
|
? { loop_limit: CURSOR_STOP_FOLLOWUP_LOOP_LIMIT }
|
|
529
|
-
:
|
|
544
|
+
: { timeout, commandWindows }));
|
|
530
545
|
settings.hooks[settingsKey] = current;
|
|
531
546
|
wired++;
|
|
532
547
|
}
|
|
@@ -542,7 +557,7 @@ export function wireHooks(settings, spec, agentHookPath, adapter) {
|
|
|
542
557
|
* that don't set the var. Only the env expansion is quoted so the
|
|
543
558
|
* `agent-hook <subcommand> ` wiring match stays byte-identical.
|
|
544
559
|
*/
|
|
545
|
-
function normalizeEventGroup(group, subcommand, canonical, alreadyFound) {
|
|
560
|
+
function normalizeEventGroup(group, subcommand, canonical, fields, alreadyFound) {
|
|
546
561
|
let found = false;
|
|
547
562
|
let upgraded = 0;
|
|
548
563
|
let removed = 0;
|
|
@@ -561,7 +576,8 @@ function normalizeEventGroup(group, subcommand, canonical, alreadyFound) {
|
|
|
561
576
|
}
|
|
562
577
|
if ("hooks" in group && Array.isArray(group.hooks)) {
|
|
563
578
|
const kept = [];
|
|
564
|
-
for (const
|
|
579
|
+
for (const original of group.hooks) {
|
|
580
|
+
let hook = original;
|
|
565
581
|
if (typeof hook.command !== "string" || !commandWiresSubcommand(hook.command, subcommand)) {
|
|
566
582
|
kept.push(hook);
|
|
567
583
|
continue;
|
|
@@ -571,8 +587,26 @@ function normalizeEventGroup(group, subcommand, canonical, alreadyFound) {
|
|
|
571
587
|
continue;
|
|
572
588
|
}
|
|
573
589
|
found = true;
|
|
590
|
+
let changed = false;
|
|
574
591
|
if (hook.command !== canonical) {
|
|
575
592
|
hook.command = canonical;
|
|
593
|
+
changed = true;
|
|
594
|
+
}
|
|
595
|
+
if (fields.timeout !== undefined && hook.timeout !== fields.timeout) {
|
|
596
|
+
hook.timeout = fields.timeout;
|
|
597
|
+
changed = true;
|
|
598
|
+
}
|
|
599
|
+
// Harnery owns commandWindows on its own handlers for adapters that run
|
|
600
|
+
// it: write the opted-in command, or remove one a project opted out of.
|
|
601
|
+
if (fields.windowsBridge && hook.commandWindows !== fields.commandWindows) {
|
|
602
|
+
if (fields.commandWindows === undefined)
|
|
603
|
+
delete hook.commandWindows;
|
|
604
|
+
else
|
|
605
|
+
hook.commandWindows = fields.commandWindows;
|
|
606
|
+
changed = true;
|
|
607
|
+
}
|
|
608
|
+
if (changed) {
|
|
609
|
+
hook = orderHandlerKeys(hook);
|
|
576
610
|
upgraded++;
|
|
577
611
|
}
|
|
578
612
|
kept.push(hook);
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { Command } from "commander";
|
|
2
|
+
import type { EmitContext, HarneryProgramContext } from "../commander.js";
|
|
3
|
+
export declare function registerRmCommand(program: Command, emit: EmitContext, context?: HarneryProgramContext): void;
|
|
4
|
+
//# sourceMappingURL=rm.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rm.d.ts","sourceRoot":"","sources":["../../src/commands/rm.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,KAAK,EAAE,WAAW,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAU1E,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,WAAW,EACjB,OAAO,CAAC,EAAE,qBAAqB,GAC9B,IAAI,CAgFN"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { spawnSync } from "node:child_process";
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
3
|
+
import { isAbsolute, relative, resolve, sep } from "node:path";
|
|
4
|
+
import { resolveCoordRoot } from "../core/agents/coord-client.js";
|
|
5
|
+
import { readCoordinationViewV3, requireAuthoritySafeCoordinationViewV3, } from "../core/events/v3/coordination-view.js";
|
|
6
|
+
import { liveInstanceIdV3 } from "../core/events/v3/live-route-observer.js";
|
|
7
|
+
import { resolveOwner } from "../core/hooks/resolve/owner.js";
|
|
8
|
+
import { guardedRemove } from "../lib/guarded-remove.js";
|
|
9
|
+
export function registerRmCommand(program, emit, context) {
|
|
10
|
+
program
|
|
11
|
+
.command("rm <paths...>")
|
|
12
|
+
.description("Preview guarded removal of untracked files; --yes applies permanent deletion")
|
|
13
|
+
.requiredOption("--root <directory>", "Allowed directory; each target must be strictly inside it")
|
|
14
|
+
.option("-r, --recursive", "Permit directory removal after inspecting its complete contents")
|
|
15
|
+
.option("--allow-protected <path>", "Remove this one protected path (Git metadata, credentials, Harnery state, keys) inside a target; repeat per path", (value, previous = []) => [...previous, value])
|
|
16
|
+
.option("--yes", "Permanently remove validated targets")
|
|
17
|
+
.option("--dry-run", "Preview only, even with --yes")
|
|
18
|
+
.option("--json", "Emit the versioned removal report")
|
|
19
|
+
.action((paths, options) => {
|
|
20
|
+
try {
|
|
21
|
+
const coordRoot = context?.resolveCoordRoot?.() ?? context?.repoRoot ?? resolveCoordRoot();
|
|
22
|
+
const checkClaims = (targets) => {
|
|
23
|
+
if (!coordRoot || !existsSync(resolve(coordRoot, ".harnery")))
|
|
24
|
+
return;
|
|
25
|
+
const view = requireAuthoritySafeCoordinationViewV3(readCoordinationViewV3(coordRoot));
|
|
26
|
+
const owner = resolveOwner({ payload: null, coordRoot });
|
|
27
|
+
const ownId = owner ? liveInstanceIdV3(owner.instance_id) : undefined;
|
|
28
|
+
const ownSession = ownId ? view.instances[ownId]?.session_id : undefined;
|
|
29
|
+
for (const peer of Object.values(view.instances)) {
|
|
30
|
+
if (!peer.authority_eligible ||
|
|
31
|
+
peer.instance_id === ownId ||
|
|
32
|
+
(ownSession && peer.session_id === ownSession))
|
|
33
|
+
continue;
|
|
34
|
+
for (const claim of peer.files_touched) {
|
|
35
|
+
const claimed = resolve(coordRoot, claim);
|
|
36
|
+
for (const target of targets) {
|
|
37
|
+
if (overlaps(target, claimed))
|
|
38
|
+
throw new Error(`Target overlaps another agent's claim: ${claim} (${peer.instance_id})`);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
// The workspace this command coordinates, and the checkout it runs in:
|
|
44
|
+
// their own .git and .harnery can never be allowed.
|
|
45
|
+
const liveRoots = [coordRoot, gitTopLevel(process.cwd())].filter((dir) => Boolean(dir));
|
|
46
|
+
const report = guardedRemove(paths, { ...options, liveRoots }, checkClaims);
|
|
47
|
+
if (options.json) {
|
|
48
|
+
emit.config({ format: "json" });
|
|
49
|
+
emit.data(report);
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
const lines = [
|
|
53
|
+
`${report.applied ? "Removed" : "Would remove"} ${report.targets.length} target(s), ${report.entries} entries, ${report.bytes} bytes:`,
|
|
54
|
+
...report.targets,
|
|
55
|
+
];
|
|
56
|
+
if (report.protected.length)
|
|
57
|
+
lines.push("Protected paths named with --allow-protected:", ...report.protected.map((path) => ` ${path}`));
|
|
58
|
+
if (report.links.length)
|
|
59
|
+
lines.push("Links removed as links (their targets are untouched):", ...report.links.map((link) => ` ${link.path} -> ${link.target}`));
|
|
60
|
+
if (!report.applied)
|
|
61
|
+
lines.push("Preview only. Add --yes to permanently delete these targets.");
|
|
62
|
+
emit.text(lines.join("\n"));
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
emit.error({
|
|
67
|
+
code: "removal_refused",
|
|
68
|
+
message: error instanceof Error ? error.message : String(error),
|
|
69
|
+
});
|
|
70
|
+
emit.setExitCode(1);
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
function gitTopLevel(dir) {
|
|
75
|
+
const result = spawnSync("git", ["-C", dir, "rev-parse", "--show-toplevel"], {
|
|
76
|
+
encoding: "utf8",
|
|
77
|
+
timeout: 10_000,
|
|
78
|
+
});
|
|
79
|
+
return result.status === 0 ? result.stdout.trim() || undefined : undefined;
|
|
80
|
+
}
|
|
81
|
+
function overlaps(a, b) {
|
|
82
|
+
const inside = (parent, child) => {
|
|
83
|
+
const rel = relative(parent, child);
|
|
84
|
+
return rel === "" || (rel !== ".." && !rel.startsWith(`..${sep}`) && !isAbsolute(rel));
|
|
85
|
+
};
|
|
86
|
+
return inside(a, b) || inside(b, a);
|
|
87
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Command } from "commander";
|
|
2
|
+
import type { EmitContext, HarneryProgramContext } from "../commander.js";
|
|
3
|
+
import { type ServersReport } from "../core/servers/index.js";
|
|
4
|
+
export declare function registerServersCommand(program: Command, emit: EmitContext, context?: HarneryProgramContext): void;
|
|
5
|
+
/** Plain-text table grouped by kind; exported for tests. */
|
|
6
|
+
export declare function renderReport(report: ServersReport, bin: string): string;
|
|
7
|
+
//# sourceMappingURL=servers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"servers.d.ts","sourceRoot":"","sources":["../../src/commands/servers.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,KAAK,EAAE,WAAW,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAG1E,OAAO,EAWL,KAAK,aAAa,EAMnB,MAAM,0BAA0B,CAAC;AAElC,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,WAAW,EACjB,OAAO,CAAC,EAAE,qBAAqB,GAC9B,IAAI,CA0YN;AA8BD,4DAA4D;AAC5D,wBAAgB,YAAY,CAAC,MAAM,EAAE,aAAa,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAmDvE"}
|