@achieveai/hitl-mcp-server 2.9.6 → 2.11.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/cli.d.ts +31 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +133 -5
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +2 -0
- package/dist/config.js.map +1 -1
- package/dist/git-context.d.ts +53 -2
- package/dist/git-context.d.ts.map +1 -1
- package/dist/git-context.js +118 -18
- package/dist/git-context.js.map +1 -1
- package/dist/host-settings.d.ts +68 -0
- package/dist/host-settings.d.ts.map +1 -0
- package/dist/host-settings.js +136 -0
- package/dist/host-settings.js.map +1 -0
- package/dist/identity.d.ts +20 -0
- package/dist/identity.d.ts.map +1 -0
- package/dist/identity.js +35 -0
- package/dist/identity.js.map +1 -0
- package/dist/mcp-server.d.ts +65 -1
- package/dist/mcp-server.d.ts.map +1 -1
- package/dist/mcp-server.js +446 -65
- package/dist/mcp-server.js.map +1 -1
- package/dist/ntfy-transport.d.ts +293 -13
- package/dist/ntfy-transport.d.ts.map +1 -1
- package/dist/ntfy-transport.js +765 -88
- package/dist/ntfy-transport.js.map +1 -1
- package/dist/payload.d.ts +80 -0
- package/dist/payload.d.ts.map +1 -0
- package/dist/payload.js +135 -0
- package/dist/payload.js.map +1 -0
- package/dist/plan-diff.d.ts +29 -0
- package/dist/plan-diff.d.ts.map +1 -0
- package/dist/plan-diff.js +138 -0
- package/dist/plan-diff.js.map +1 -0
- package/dist/plan-file.d.ts +24 -0
- package/dist/plan-file.d.ts.map +1 -0
- package/dist/plan-file.js +98 -0
- package/dist/plan-file.js.map +1 -0
- package/dist/plan-review.d.ts +54 -0
- package/dist/plan-review.d.ts.map +1 -0
- package/dist/plan-review.js +110 -0
- package/dist/plan-review.js.map +1 -0
- package/dist/setup.d.ts +22 -5
- package/dist/setup.d.ts.map +1 -1
- package/dist/setup.js +52 -15
- package/dist/setup.js.map +1 -1
- package/dist/snapshot-store.d.ts +104 -0
- package/dist/snapshot-store.d.ts.map +1 -0
- package/dist/snapshot-store.js +209 -0
- package/dist/snapshot-store.js.map +1 -0
- package/dist/types.d.ts +144 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -1
- package/dist/version.d.ts +13 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +15 -0
- package/dist/version.js.map +1 -0
- package/package.json +64 -61
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { InlineComment, PlanVerdict, PlanReviewResponseBody } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Pure shaping and validation of a plan-review response.
|
|
4
|
+
*
|
|
5
|
+
* Kept out of mcp-server.ts so it can be tested without booting an MCP server,
|
|
6
|
+
* and because the rules here are the authoritative ones — the review window
|
|
7
|
+
* mirrors them for fast feedback, but the server decides.
|
|
8
|
+
*/
|
|
9
|
+
/** Every verdict the wire may legitimately carry. */
|
|
10
|
+
export declare const VALID_VERDICTS: readonly PlanVerdict[];
|
|
11
|
+
/** Raised when a response cannot be accepted as submitted. */
|
|
12
|
+
export declare class ReviewResponseError extends Error {
|
|
13
|
+
constructor(message: string);
|
|
14
|
+
}
|
|
15
|
+
/** What ReviewPlan returns to the agent. */
|
|
16
|
+
export interface ReviewPlanResult {
|
|
17
|
+
success: true;
|
|
18
|
+
timestamp: number;
|
|
19
|
+
respondedFrom: string;
|
|
20
|
+
verdict: PlanVerdict;
|
|
21
|
+
overallFeedback: string;
|
|
22
|
+
inlineComments: InlineComment[];
|
|
23
|
+
revision: number;
|
|
24
|
+
isNewPlan: boolean;
|
|
25
|
+
/** 'sha256:<hex>' of the content the human actually reviewed (A-8). */
|
|
26
|
+
snapshotHash: string;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Coerce an unknown verdict string.
|
|
30
|
+
*
|
|
31
|
+
* An unrecognized value is a client that is broken or newer than this server —
|
|
32
|
+
* never a human decision. Reporting it as `'skipped'` would tell the agent the
|
|
33
|
+
* human deliberately declined, and would route around the A-5 gate that makes
|
|
34
|
+
* `changes_requested` say what to change. Refusing is the only honest answer.
|
|
35
|
+
*/
|
|
36
|
+
export declare function parseVerdict(raw: unknown): PlanVerdict;
|
|
37
|
+
/**
|
|
38
|
+
* Trim, drop empties, and sort inline comments into a canonical order.
|
|
39
|
+
*
|
|
40
|
+
* The comparator is total — it falls through to the comment text — so the same
|
|
41
|
+
* set of comments serializes byte-identically no matter which order the human
|
|
42
|
+
* clicked them in (A-4). A merely stable sort would not achieve that: two
|
|
43
|
+
* comments on one anchor would keep their insertion order.
|
|
44
|
+
*/
|
|
45
|
+
export declare function normalizeInlineComments(raw: unknown, defaultPath: string): InlineComment[];
|
|
46
|
+
/**
|
|
47
|
+
* Normalize a decoded response body and enforce the server-side rules.
|
|
48
|
+
*
|
|
49
|
+
* `changes_requested` and `rejected` need either overall feedback or at least
|
|
50
|
+
* one surviving inline comment; anything else leaves the agent told to change
|
|
51
|
+
* something with no indication of what (A-5).
|
|
52
|
+
*/
|
|
53
|
+
export declare function normalizeResponseBody(verdict: PlanVerdict, raw: unknown, defaultPath: string): PlanReviewResponseBody;
|
|
54
|
+
//# sourceMappingURL=plan-review.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plan-review.d.ts","sourceRoot":"","sources":["../src/plan-review.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAErF;;;;;;GAMG;AAEH,qDAAqD;AACrD,eAAO,MAAM,cAAc,EAAE,SAAS,WAAW,EAMhD,CAAC;AAKF,8DAA8D;AAC9D,qBAAa,mBAAoB,SAAQ,KAAK;gBAChC,OAAO,EAAE,MAAM;CAI5B;AAED,4CAA4C;AAC5C,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,IAAI,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,WAAW,CAAC;IACrB,eAAe,EAAE,MAAM,CAAC;IACxB,cAAc,EAAE,aAAa,EAAE,CAAC;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,OAAO,CAAC;IACnB,uEAAuE;IACvE,YAAY,EAAE,MAAM,CAAC;CACtB;AAED;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,OAAO,GAAG,WAAW,CAKtD;AAgBD;;;;;;;GAOG;AACH,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,GAAG,aAAa,EAAE,CA+B1F;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,WAAW,EACpB,GAAG,EAAE,OAAO,EACZ,WAAW,EAAE,MAAM,GAClB,sBAAsB,CAkBxB"}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure shaping and validation of a plan-review response.
|
|
3
|
+
*
|
|
4
|
+
* Kept out of mcp-server.ts so it can be tested without booting an MCP server,
|
|
5
|
+
* and because the rules here are the authoritative ones — the review window
|
|
6
|
+
* mirrors them for fast feedback, but the server decides.
|
|
7
|
+
*/
|
|
8
|
+
/** Every verdict the wire may legitimately carry. */
|
|
9
|
+
export const VALID_VERDICTS = [
|
|
10
|
+
'approved',
|
|
11
|
+
'changes_requested',
|
|
12
|
+
'rejected',
|
|
13
|
+
'skipped',
|
|
14
|
+
'cancelled',
|
|
15
|
+
];
|
|
16
|
+
/** Verdicts that say "change something" and therefore must say what (A-5). */
|
|
17
|
+
const VERDICTS_REQUIRING_FEEDBACK = ['changes_requested', 'rejected'];
|
|
18
|
+
/** Raised when a response cannot be accepted as submitted. */
|
|
19
|
+
export class ReviewResponseError extends Error {
|
|
20
|
+
constructor(message) {
|
|
21
|
+
super(message);
|
|
22
|
+
this.name = 'ReviewResponseError';
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Coerce an unknown verdict string.
|
|
27
|
+
*
|
|
28
|
+
* An unrecognized value is a client that is broken or newer than this server —
|
|
29
|
+
* never a human decision. Reporting it as `'skipped'` would tell the agent the
|
|
30
|
+
* human deliberately declined, and would route around the A-5 gate that makes
|
|
31
|
+
* `changes_requested` say what to change. Refusing is the only honest answer.
|
|
32
|
+
*/
|
|
33
|
+
export function parseVerdict(raw) {
|
|
34
|
+
if (VALID_VERDICTS.includes(raw))
|
|
35
|
+
return raw;
|
|
36
|
+
throw new ReviewResponseError(`Unrecognized verdict ${JSON.stringify(raw)}; expected one of ${VALID_VERDICTS.join(', ')}.`);
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Order two strings by Unicode code point.
|
|
40
|
+
*
|
|
41
|
+
* `localeCompare` is wrong here twice over: it returns 0 for strings that are
|
|
42
|
+
* canonically equivalent but not byte-equal (NFC vs NFD, which is exactly what
|
|
43
|
+
* a macOS and a Windows client produce for the same accented word), and its
|
|
44
|
+
* ordering depends on the host locale. A-4 requires the same comments to
|
|
45
|
+
* serialize to the same bytes on every device, so the comparator has to be
|
|
46
|
+
* decided by the bytes (M3).
|
|
47
|
+
*/
|
|
48
|
+
function compareCodePoints(a, b) {
|
|
49
|
+
return a < b ? -1 : a > b ? 1 : 0;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Trim, drop empties, and sort inline comments into a canonical order.
|
|
53
|
+
*
|
|
54
|
+
* The comparator is total — it falls through to the comment text — so the same
|
|
55
|
+
* set of comments serializes byte-identically no matter which order the human
|
|
56
|
+
* clicked them in (A-4). A merely stable sort would not achieve that: two
|
|
57
|
+
* comments on one anchor would keep their insertion order.
|
|
58
|
+
*/
|
|
59
|
+
export function normalizeInlineComments(raw, defaultPath) {
|
|
60
|
+
if (!Array.isArray(raw))
|
|
61
|
+
return [];
|
|
62
|
+
const cleaned = [];
|
|
63
|
+
for (const entry of raw) {
|
|
64
|
+
if (typeof entry !== 'object' || entry === null)
|
|
65
|
+
continue;
|
|
66
|
+
const c = entry;
|
|
67
|
+
const comment = typeof c.comment === 'string' ? c.comment.trim() : '';
|
|
68
|
+
if (comment === '')
|
|
69
|
+
continue; // An empty comment anchors nothing.
|
|
70
|
+
const startLine = toLineNumber(c.startLine, 1);
|
|
71
|
+
const endLine = Math.max(startLine, toLineNumber(c.endLine, startLine));
|
|
72
|
+
cleaned.push({
|
|
73
|
+
path: typeof c.path === 'string' && c.path !== '' ? c.path : defaultPath,
|
|
74
|
+
startLine,
|
|
75
|
+
endLine,
|
|
76
|
+
side: c.side === 'old' ? 'old' : 'new',
|
|
77
|
+
comment,
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
return cleaned.sort((a, b) => compareCodePoints(a.path, b.path) ||
|
|
81
|
+
a.startLine - b.startLine ||
|
|
82
|
+
a.endLine - b.endLine ||
|
|
83
|
+
compareCodePoints(a.side, b.side) ||
|
|
84
|
+
compareCodePoints(a.comment, b.comment));
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Normalize a decoded response body and enforce the server-side rules.
|
|
88
|
+
*
|
|
89
|
+
* `changes_requested` and `rejected` need either overall feedback or at least
|
|
90
|
+
* one surviving inline comment; anything else leaves the agent told to change
|
|
91
|
+
* something with no indication of what (A-5).
|
|
92
|
+
*/
|
|
93
|
+
export function normalizeResponseBody(verdict, raw, defaultPath) {
|
|
94
|
+
const body = (typeof raw === 'object' && raw !== null ? raw : {});
|
|
95
|
+
const overallFeedback = typeof body.overallFeedback === 'string' ? body.overallFeedback.trim() : '';
|
|
96
|
+
const inlineComments = normalizeInlineComments(body.inlineComments, defaultPath);
|
|
97
|
+
if (VERDICTS_REQUIRING_FEEDBACK.includes(verdict) &&
|
|
98
|
+
overallFeedback === '' &&
|
|
99
|
+
inlineComments.length === 0) {
|
|
100
|
+
throw new ReviewResponseError(`Verdict '${verdict}' requires either overall feedback or at least one inline comment, ` +
|
|
101
|
+
`but the response carried neither.`);
|
|
102
|
+
}
|
|
103
|
+
return { overallFeedback, inlineComments };
|
|
104
|
+
}
|
|
105
|
+
function toLineNumber(value, fallback) {
|
|
106
|
+
return typeof value === 'number' && Number.isFinite(value) && value >= 1
|
|
107
|
+
? Math.floor(value)
|
|
108
|
+
: fallback;
|
|
109
|
+
}
|
|
110
|
+
//# sourceMappingURL=plan-review.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plan-review.js","sourceRoot":"","sources":["../src/plan-review.ts"],"names":[],"mappings":"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"}
|
package/dist/setup.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/** Result of a single setup step. */
|
|
2
2
|
export interface SetupStepResult {
|
|
3
3
|
step: string;
|
|
4
|
-
status: 'ok' | 'created' | 'launched' | 'already_running' | 'not_found' | 'error';
|
|
4
|
+
status: 'ok' | 'created' | 'launched' | 'already_running' | 'not_found' | 'error' | 'warning';
|
|
5
5
|
message: string;
|
|
6
6
|
}
|
|
7
7
|
/** Aggregated result of the full setup process. */
|
|
@@ -24,18 +24,35 @@ export declare function isProcessRunning(processName: string): boolean;
|
|
|
24
24
|
export declare function findClientBinary(serverDir: string): string | null;
|
|
25
25
|
/**
|
|
26
26
|
* Launch the client binary as a fully detached background process.
|
|
27
|
+
*
|
|
28
|
+
* The `'error'` listener is not optional. Node reports a failed exec (ENOENT,
|
|
29
|
+
* EACCES) by emitting `'error'` asynchronously, and an unhandled `'error'` on
|
|
30
|
+
* an EventEmitter is rethrown as an uncaught exception — which, from inside a
|
|
31
|
+
* tool call, kills the whole MCP server instead of returning a message the
|
|
32
|
+
* agent can act on (H9).
|
|
27
33
|
*/
|
|
28
34
|
export declare function launchClient(binaryPath: string): void;
|
|
35
|
+
/** Outcome of ensureClientRunning — the caller must not publish when `ok` is false. */
|
|
36
|
+
export interface ClientRunningResult {
|
|
37
|
+
ok: boolean;
|
|
38
|
+
/** Why the client is unavailable. Present only when `ok` is false. */
|
|
39
|
+
reason?: string;
|
|
40
|
+
}
|
|
29
41
|
/**
|
|
30
42
|
* Ensure the HITL client is running. Finds and launches it if needed.
|
|
31
|
-
*
|
|
43
|
+
*
|
|
44
|
+
* Returns the outcome rather than only logging it: with timeouts removed, a
|
|
45
|
+
* caller that publishes to a topic nobody is subscribed to blocks forever and
|
|
46
|
+
* the human never sees anything (A-10). Both "binary not found" and "launch
|
|
47
|
+
* failed" must reach the agent as an error before anything is published.
|
|
32
48
|
*/
|
|
33
|
-
export declare function ensureClientRunning(serverDir: string):
|
|
49
|
+
export declare function ensureClientRunning(serverDir: string): ClientRunningResult;
|
|
34
50
|
/**
|
|
35
51
|
* Perform the full HITL client setup:
|
|
36
52
|
* 1. Ensure ~/.hitl/config.json exists
|
|
37
|
-
* 2.
|
|
38
|
-
* 3.
|
|
53
|
+
* 2. Non-fatal diagnostic: warn if the auto-background env guard isn't active
|
|
54
|
+
* 3. Check if the client process is running
|
|
55
|
+
* 4. Find and launch the client binary if needed
|
|
39
56
|
*
|
|
40
57
|
* @param serverDir - The directory of the running server JS (used to resolve relative binary paths)
|
|
41
58
|
*/
|
package/dist/setup.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../src/setup.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../src/setup.ts"],"names":[],"mappings":"AAOA,qCAAqC;AACrC,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,IAAI,GAAG,SAAS,GAAG,UAAU,GAAG,iBAAiB,GAAG,WAAW,GAAG,OAAO,GAAG,SAAS,CAAC;IAC9F,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,mDAAmD;AACnD,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,eAAe,EAAE,CAAC;IACzB,OAAO,EAAE,MAAM,CAAC;CACjB;AAkBD;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAe7D;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CA0BjE;AAED;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CASrD;AAED,uFAAuF;AACvF,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,OAAO,CAAC;IACZ,sEAAsE;IACtE,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,mBAAmB,CA0B1E;AAqBD;;;;;;;;GAQG;AACH,wBAAsB,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAmE1E"}
|
package/dist/setup.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { existsSync, chmodSync } from 'fs';
|
|
1
|
+
import { existsSync, chmodSync, accessSync, constants } from 'fs';
|
|
2
2
|
import { execSync, spawn } from 'child_process';
|
|
3
3
|
import { homedir, arch } from 'os';
|
|
4
4
|
import path from 'path';
|
|
5
5
|
import { saveConfig, generateDefaultConfig, getConfigPath } from './config.js';
|
|
6
|
+
import { detectAutoBackgroundStatus, buildAutoBackgroundRemediationText } from './host-settings.js';
|
|
6
7
|
/**
|
|
7
8
|
* Get the platform-specific subdirectory name for bundled binaries.
|
|
8
9
|
* Maps Node.js platform/arch to the artifact names used in CI.
|
|
@@ -73,32 +74,54 @@ export function findClientBinary(serverDir) {
|
|
|
73
74
|
}
|
|
74
75
|
/**
|
|
75
76
|
* Launch the client binary as a fully detached background process.
|
|
77
|
+
*
|
|
78
|
+
* The `'error'` listener is not optional. Node reports a failed exec (ENOENT,
|
|
79
|
+
* EACCES) by emitting `'error'` asynchronously, and an unhandled `'error'` on
|
|
80
|
+
* an EventEmitter is rethrown as an uncaught exception — which, from inside a
|
|
81
|
+
* tool call, kills the whole MCP server instead of returning a message the
|
|
82
|
+
* agent can act on (H9).
|
|
76
83
|
*/
|
|
77
84
|
export function launchClient(binaryPath) {
|
|
78
85
|
const child = spawn(binaryPath, [], {
|
|
79
86
|
detached: true,
|
|
80
87
|
stdio: 'ignore',
|
|
81
88
|
});
|
|
89
|
+
child.on('error', (err) => {
|
|
90
|
+
console.error(`HITL client at ${binaryPath} failed to start: ${err.message}`);
|
|
91
|
+
});
|
|
82
92
|
child.unref();
|
|
83
93
|
}
|
|
84
94
|
/**
|
|
85
95
|
* Ensure the HITL client is running. Finds and launches it if needed.
|
|
86
|
-
*
|
|
96
|
+
*
|
|
97
|
+
* Returns the outcome rather than only logging it: with timeouts removed, a
|
|
98
|
+
* caller that publishes to a topic nobody is subscribed to blocks forever and
|
|
99
|
+
* the human never sees anything (A-10). Both "binary not found" and "launch
|
|
100
|
+
* failed" must reach the agent as an error before anything is published.
|
|
87
101
|
*/
|
|
88
102
|
export function ensureClientRunning(serverDir) {
|
|
89
103
|
const binaryName = process.platform === 'win32' ? 'hitl-client.exe' : 'hitl-client';
|
|
90
104
|
if (isProcessRunning(binaryName)) {
|
|
91
|
-
return;
|
|
105
|
+
return { ok: true };
|
|
92
106
|
}
|
|
93
107
|
const binaryPath = findClientBinary(serverDir);
|
|
94
|
-
if (binaryPath) {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
108
|
+
if (!binaryPath) {
|
|
109
|
+
return { ok: false, reason: buildNotFoundMessage(serverDir) };
|
|
110
|
+
}
|
|
111
|
+
try {
|
|
112
|
+
// Checked synchronously so an unusable binary becomes a returned reason
|
|
113
|
+
// rather than an async 'error' event that arrives after this function has
|
|
114
|
+
// already told the caller everything was fine.
|
|
115
|
+
accessSync(binaryPath, constants.X_OK);
|
|
116
|
+
launchClient(binaryPath);
|
|
117
|
+
console.error(`Auto-launched HITL client from ${binaryPath}`);
|
|
118
|
+
return { ok: true };
|
|
119
|
+
}
|
|
120
|
+
catch (err) {
|
|
121
|
+
return {
|
|
122
|
+
ok: false,
|
|
123
|
+
reason: `Failed to launch the HITL client at ${binaryPath}: ${err instanceof Error ? err.message : String(err)}`,
|
|
124
|
+
};
|
|
102
125
|
}
|
|
103
126
|
}
|
|
104
127
|
/**
|
|
@@ -122,8 +145,9 @@ function buildNotFoundMessage(serverDir) {
|
|
|
122
145
|
/**
|
|
123
146
|
* Perform the full HITL client setup:
|
|
124
147
|
* 1. Ensure ~/.hitl/config.json exists
|
|
125
|
-
* 2.
|
|
126
|
-
* 3.
|
|
148
|
+
* 2. Non-fatal diagnostic: warn if the auto-background env guard isn't active
|
|
149
|
+
* 3. Check if the client process is running
|
|
150
|
+
* 4. Find and launch the client binary if needed
|
|
127
151
|
*
|
|
128
152
|
* @param serverDir - The directory of the running server JS (used to resolve relative binary paths)
|
|
129
153
|
*/
|
|
@@ -150,13 +174,26 @@ export async function performSetup(serverDir) {
|
|
|
150
174
|
});
|
|
151
175
|
}
|
|
152
176
|
}
|
|
153
|
-
// Step 2:
|
|
177
|
+
// Step 2 (non-fatal diagnostic): warn if the auto-background env guard
|
|
178
|
+
// isn't active, so a blocking HITL call can't get yanked into the
|
|
179
|
+
// background before the human answers. This never flips overallSuccess —
|
|
180
|
+
// it's advisory only, and setup never writes to the host settings file
|
|
181
|
+
// itself; the user (or a future explicit command) applies the fix.
|
|
182
|
+
const autoBackgroundStatus = detectAutoBackgroundStatus();
|
|
183
|
+
steps.push({
|
|
184
|
+
step: 'auto-background-env',
|
|
185
|
+
status: autoBackgroundStatus.active ? 'ok' : 'warning',
|
|
186
|
+
message: autoBackgroundStatus.active
|
|
187
|
+
? `Auto-background guard is active (${autoBackgroundStatus.activeInEnv ? 'current session' : 'configured for next restart'})`
|
|
188
|
+
: buildAutoBackgroundRemediationText(),
|
|
189
|
+
});
|
|
190
|
+
// Step 3: Check if client is already running
|
|
154
191
|
const binaryName = process.platform === 'win32' ? 'hitl-client.exe' : 'hitl-client';
|
|
155
192
|
if (isProcessRunning(binaryName)) {
|
|
156
193
|
steps.push({ step: 'client', status: 'already_running', message: 'HITL client is already running' });
|
|
157
194
|
return { success: overallSuccess, steps, summary: formatSummary(steps) };
|
|
158
195
|
}
|
|
159
|
-
// Step
|
|
196
|
+
// Step 4: Find and launch binary
|
|
160
197
|
const binaryPath = findClientBinary(serverDir);
|
|
161
198
|
if (!binaryPath) {
|
|
162
199
|
overallSuccess = false;
|
package/dist/setup.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"setup.js","sourceRoot":"","sources":["../src/setup.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"setup.js","sourceRoot":"","sources":["../src/setup.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* On-disk revision history for reviewed plans.
|
|
3
|
+
*
|
|
4
|
+
* ~/.hitl/plans/<sha256(identityKey)>/<sha256(normalizedPath)>/
|
|
5
|
+
* latest.json {displayPath, digest, revision, createdAt}
|
|
6
|
+
* objects/<sha256> immutable content, one file per unique revision
|
|
7
|
+
* drafts/<planId>.json in-flight comments — written by the client, not here
|
|
8
|
+
*
|
|
9
|
+
* Deliberately lock-free. Two MCP server processes reviewing the same plan can
|
|
10
|
+
* race `latest.json`, but `objects/<sha256>` is content-addressed and immutable,
|
|
11
|
+
* so a lost race costs cosmetic staleness (whose revision is called "latest"),
|
|
12
|
+
* never corruption. Last writer wins.
|
|
13
|
+
*
|
|
14
|
+
* Crash safety is the atomic temp-file + rename on `latest.json` (B-9): the
|
|
15
|
+
* object lands first, then the pointer flips in one operation, so an
|
|
16
|
+
* interrupted write leaves the previous `latest.json` byte-identical.
|
|
17
|
+
*/
|
|
18
|
+
/** The trusted contents of `latest.json`. */
|
|
19
|
+
export interface SnapshotMeta {
|
|
20
|
+
/** Repo-relative display path — never absolute (F-9). */
|
|
21
|
+
displayPath: string;
|
|
22
|
+
/** Bare lowercase sha256 hex of the content. `snapshotHash` prefixes it with 'sha256:'. */
|
|
23
|
+
digest: string;
|
|
24
|
+
revision: number;
|
|
25
|
+
/** Unix millis. */
|
|
26
|
+
createdAt: number;
|
|
27
|
+
}
|
|
28
|
+
/** Where a given plan file's history lives, and how it is named on the wire. */
|
|
29
|
+
export interface PlanIdentity {
|
|
30
|
+
/** Stable across revisions; keys drafts. */
|
|
31
|
+
planId: string;
|
|
32
|
+
/** Absolute directory holding latest.json / objects / drafts. */
|
|
33
|
+
dir: string;
|
|
34
|
+
/** Repo-relative when the plan is inside a repo, else just the basename (F-9). */
|
|
35
|
+
displayPath: string;
|
|
36
|
+
/** Absolute repo root, or null when the plan is not in a work tree. */
|
|
37
|
+
repoRoot: string | null;
|
|
38
|
+
/** The resolved, symlink-free plan path this identity describes. */
|
|
39
|
+
resolvedPath: string;
|
|
40
|
+
}
|
|
41
|
+
/** Outcome of recording a revision. */
|
|
42
|
+
export interface RecordedRevision {
|
|
43
|
+
revision: number;
|
|
44
|
+
/** Bare sha256 hex of the content just recorded. */
|
|
45
|
+
digest: string;
|
|
46
|
+
isNewPlan: boolean;
|
|
47
|
+
/** The revision this one supersedes, or null on a first review. */
|
|
48
|
+
previous: SnapshotMeta | null;
|
|
49
|
+
/** Content of `previous`, or null when there is none / the object is missing. */
|
|
50
|
+
previousContent: string | null;
|
|
51
|
+
}
|
|
52
|
+
/** A revision whose content is stored but which is not yet the plan's latest. */
|
|
53
|
+
export interface PreparedRevision extends RecordedRevision {
|
|
54
|
+
/**
|
|
55
|
+
* Flip `latest.json` to this revision, making it the baseline the next review
|
|
56
|
+
* diffs against. Until this is called the object sits in `objects/` and the
|
|
57
|
+
* plan's history is unchanged.
|
|
58
|
+
*/
|
|
59
|
+
commit(): RecordedRevision;
|
|
60
|
+
}
|
|
61
|
+
/** Root of the snapshot store. Overridable so tests never touch a real home dir. */
|
|
62
|
+
export declare function getPlansRoot(): string;
|
|
63
|
+
/**
|
|
64
|
+
* Locate a plan file's history directory and its display name.
|
|
65
|
+
*
|
|
66
|
+
* Identity is keyed on the repo root when there is one, else on the containing
|
|
67
|
+
* directory — so the same relative path in two different repos gets two
|
|
68
|
+
* identities (B-7), while two spellings of one Windows path collapse to one
|
|
69
|
+
* (B-10). Git is resolved from the plan's own directory, never `process.cwd()`,
|
|
70
|
+
* because the plan usually lives in a sibling repo (B-6).
|
|
71
|
+
*/
|
|
72
|
+
export declare function resolvePlanIdentity(resolvedPath: string): PlanIdentity;
|
|
73
|
+
/**
|
|
74
|
+
* Validate a raw `latest.json` body before anything trusts it.
|
|
75
|
+
*
|
|
76
|
+
* Returns null — never a partially-trusted object — when the digest is not 64
|
|
77
|
+
* lowercase hex, the revision is not a positive integer, `createdAt` is zero,
|
|
78
|
+
* or the recorded `displayPath` names a different file than the one being
|
|
79
|
+
* reviewed. A rejected pointer degrades to "no history", which restarts the
|
|
80
|
+
* revision count rather than diffing against something unverified.
|
|
81
|
+
*/
|
|
82
|
+
export declare function parseLatest(raw: string, expectedDisplayPath: string): SnapshotMeta | null;
|
|
83
|
+
/** Read and validate `latest.json`, or null when absent or untrustworthy. */
|
|
84
|
+
export declare function readLatest(identity: PlanIdentity): SnapshotMeta | null;
|
|
85
|
+
/** Read a stored revision by digest, or null when the object is missing. */
|
|
86
|
+
export declare function readObject(identity: PlanIdentity, digest: string): string | null;
|
|
87
|
+
/**
|
|
88
|
+
* Stage `content` as the next revision of this plan.
|
|
89
|
+
*
|
|
90
|
+
* Byte-identical content still advances the revision: a resubmit is a distinct
|
|
91
|
+
* review event, and the human must still be able to select every line of an
|
|
92
|
+
* unchanged plan (B-3).
|
|
93
|
+
*
|
|
94
|
+
* The pointer flip is deliberately a separate step. `latest.json` is what the
|
|
95
|
+
* next review diffs against, so writing it before the plan has actually reached
|
|
96
|
+
* the human means a failed publish silently rebases the next review onto a
|
|
97
|
+
* revision nobody ever saw — the change the human was meant to review vanishes
|
|
98
|
+
* from the diff (M6). Callers commit once the revision is out the door.
|
|
99
|
+
*
|
|
100
|
+
* The object is written either way; it is content-addressed and immutable, so
|
|
101
|
+
* an uncommitted one costs a few KB of orphaned disk and nothing else.
|
|
102
|
+
*/
|
|
103
|
+
export declare function prepareRevision(identity: PlanIdentity, content: string): PreparedRevision;
|
|
104
|
+
//# sourceMappingURL=snapshot-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"snapshot-store.d.ts","sourceRoot":"","sources":["../src/snapshot-store.ts"],"names":[],"mappings":"AAMA;;;;;;;;;;;;;;;;GAgBG;AAEH,6CAA6C;AAC7C,MAAM,WAAW,YAAY;IAC3B,yDAAyD;IACzD,WAAW,EAAE,MAAM,CAAC;IACpB,2FAA2F;IAC3F,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,mBAAmB;IACnB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,gFAAgF;AAChF,MAAM,WAAW,YAAY;IAC3B,4CAA4C;IAC5C,MAAM,EAAE,MAAM,CAAC;IACf,iEAAiE;IACjE,GAAG,EAAE,MAAM,CAAC;IACZ,kFAAkF;IAClF,WAAW,EAAE,MAAM,CAAC;IACpB,uEAAuE;IACvE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,oEAAoE;IACpE,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,uCAAuC;AACvC,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,oDAAoD;IACpD,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,OAAO,CAAC;IACnB,mEAAmE;IACnE,QAAQ,EAAE,YAAY,GAAG,IAAI,CAAC;IAC9B,iFAAiF;IACjF,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;CAChC;AAED,iFAAiF;AACjF,MAAM,WAAW,gBAAiB,SAAQ,gBAAgB;IACxD;;;;OAIG;IACH,MAAM,IAAI,gBAAgB,CAAC;CAC5B;AAED,oFAAoF;AACpF,wBAAgB,YAAY,IAAI,MAAM,CAErC;AASD;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CAAC,YAAY,EAAE,MAAM,GAAG,YAAY,CAgBtE;AAED;;;;;;;;GAQG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,mBAAmB,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI,CAiBzF;AAED,6EAA6E;AAC7E,wBAAgB,UAAU,CAAC,QAAQ,EAAE,YAAY,GAAG,YAAY,GAAG,IAAI,CAStE;AAED,4EAA4E;AAC5E,wBAAgB,UAAU,CAAC,QAAQ,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAShF;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,GAAG,gBAAgB,CAmCzF"}
|