@zigrivers/mmr 1.3.0 → 1.4.1
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 +444 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +4 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/ack.d.ts +11 -0
- package/dist/commands/ack.d.ts.map +1 -0
- package/dist/commands/ack.js +123 -0
- package/dist/commands/ack.js.map +1 -0
- package/dist/commands/config.d.ts +5 -0
- package/dist/commands/config.d.ts.map +1 -1
- package/dist/commands/config.js +248 -14
- package/dist/commands/config.js.map +1 -1
- package/dist/commands/jobs.d.ts.map +1 -1
- package/dist/commands/jobs.js +3 -4
- package/dist/commands/jobs.js.map +1 -1
- package/dist/commands/reconcile.d.ts.map +1 -1
- package/dist/commands/reconcile.js +12 -5
- package/dist/commands/reconcile.js.map +1 -1
- package/dist/commands/results.d.ts.map +1 -1
- package/dist/commands/results.js +13 -5
- package/dist/commands/results.js.map +1 -1
- package/dist/commands/review.d.ts +25 -0
- package/dist/commands/review.d.ts.map +1 -1
- package/dist/commands/review.js +459 -44
- package/dist/commands/review.js.map +1 -1
- package/dist/commands/sessions.d.ts +58 -0
- package/dist/commands/sessions.d.ts.map +1 -0
- package/dist/commands/sessions.js +266 -0
- package/dist/commands/sessions.js.map +1 -0
- package/dist/commands/status.d.ts.map +1 -1
- package/dist/commands/status.js +2 -3
- package/dist/commands/status.js.map +1 -1
- package/dist/config/defaults.d.ts +2 -2
- package/dist/config/defaults.d.ts.map +1 -1
- package/dist/config/defaults.js +76 -0
- package/dist/config/defaults.js.map +1 -1
- package/dist/config/loader.d.ts +22 -0
- package/dist/config/loader.d.ts.map +1 -1
- package/dist/config/loader.js +279 -36
- package/dist/config/loader.js.map +1 -1
- package/dist/config/schema.d.ts +897 -53
- package/dist/config/schema.d.ts.map +1 -1
- package/dist/config/schema.js +155 -4
- package/dist/config/schema.js.map +1 -1
- package/dist/core/ack-store.d.ts +109 -0
- package/dist/core/ack-store.d.ts.map +1 -0
- package/dist/core/ack-store.js +363 -0
- package/dist/core/ack-store.js.map +1 -0
- package/dist/core/auth.d.ts +10 -1
- package/dist/core/auth.d.ts.map +1 -1
- package/dist/core/auth.js +106 -35
- package/dist/core/auth.js.map +1 -1
- package/dist/core/compensator.d.ts +33 -4
- package/dist/core/compensator.d.ts.map +1 -1
- package/dist/core/compensator.js +120 -15
- package/dist/core/compensator.js.map +1 -1
- package/dist/core/diff-introspect.d.ts +21 -0
- package/dist/core/diff-introspect.d.ts.map +1 -0
- package/dist/core/diff-introspect.js +42 -0
- package/dist/core/diff-introspect.js.map +1 -0
- package/dist/core/dispatcher.d.ts +10 -0
- package/dist/core/dispatcher.d.ts.map +1 -1
- package/dist/core/dispatcher.js +91 -20
- package/dist/core/dispatcher.js.map +1 -1
- package/dist/core/git-show.d.ts +31 -0
- package/dist/core/git-show.d.ts.map +1 -0
- package/dist/core/git-show.js +72 -0
- package/dist/core/git-show.js.map +1 -0
- package/dist/core/host-isolation.d.ts +24 -0
- package/dist/core/host-isolation.d.ts.map +1 -0
- package/dist/core/host-isolation.js +107 -0
- package/dist/core/host-isolation.js.map +1 -0
- package/dist/core/http-dispatcher.d.ts +20 -0
- package/dist/core/http-dispatcher.d.ts.map +1 -0
- package/dist/core/http-dispatcher.js +125 -0
- package/dist/core/http-dispatcher.js.map +1 -0
- package/dist/core/job-store.d.ts +7 -1
- package/dist/core/job-store.d.ts.map +1 -1
- package/dist/core/job-store.js +21 -1
- package/dist/core/job-store.js.map +1 -1
- package/dist/core/jsonpath.d.ts +15 -0
- package/dist/core/jsonpath.d.ts.map +1 -0
- package/dist/core/jsonpath.js +63 -0
- package/dist/core/jsonpath.js.map +1 -0
- package/dist/core/oss-examples.d.ts +18 -0
- package/dist/core/oss-examples.d.ts.map +1 -0
- package/dist/core/oss-examples.js +66 -0
- package/dist/core/oss-examples.js.map +1 -0
- package/dist/core/parser.d.ts +8 -3
- package/dist/core/parser.d.ts.map +1 -1
- package/dist/core/parser.js +157 -6
- package/dist/core/parser.js.map +1 -1
- package/dist/core/project-root.d.ts +10 -0
- package/dist/core/project-root.d.ts.map +1 -0
- package/dist/core/project-root.js +23 -0
- package/dist/core/project-root.js.map +1 -0
- package/dist/core/reconciler.d.ts +1 -1
- package/dist/core/reconciler.d.ts.map +1 -1
- package/dist/core/reconciler.js +100 -18
- package/dist/core/reconciler.js.map +1 -1
- package/dist/core/redact.d.ts +17 -0
- package/dist/core/redact.d.ts.map +1 -0
- package/dist/core/redact.js +140 -0
- package/dist/core/redact.js.map +1 -0
- package/dist/core/results-pipeline.d.ts +8 -2
- package/dist/core/results-pipeline.d.ts.map +1 -1
- package/dist/core/results-pipeline.js +50 -3
- package/dist/core/results-pipeline.js.map +1 -1
- package/dist/core/runtime-probe.d.ts +14 -0
- package/dist/core/runtime-probe.d.ts.map +1 -0
- package/dist/core/runtime-probe.js +57 -0
- package/dist/core/runtime-probe.js.map +1 -0
- package/dist/core/stable-id.d.ts +19 -0
- package/dist/core/stable-id.d.ts.map +1 -0
- package/dist/core/stable-id.js +148 -0
- package/dist/core/stable-id.js.map +1 -0
- package/dist/core/trust-mode.d.ts +29 -0
- package/dist/core/trust-mode.d.ts.map +1 -0
- package/dist/core/trust-mode.js +103 -0
- package/dist/core/trust-mode.js.map +1 -0
- package/dist/formatters/markdown.d.ts.map +1 -1
- package/dist/formatters/markdown.js +9 -0
- package/dist/formatters/markdown.js.map +1 -1
- package/dist/formatters/text.d.ts.map +1 -1
- package/dist/formatters/text.js +9 -0
- package/dist/formatters/text.js.map +1 -1
- package/dist/types.d.ts +44 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +2 -2
package/dist/core/compensator.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { dispatchChannel } from './dispatcher.js';
|
|
2
|
+
import { dispatchHttpChannel } from './http-dispatcher.js';
|
|
2
3
|
/** Focus areas for compensating passes, keyed by the channel being compensated */
|
|
3
4
|
const COMPENSATING_FOCUS = {
|
|
4
5
|
codex: 'Focus your review on: implementation correctness, security vulnerabilities,'
|
|
@@ -7,30 +8,115 @@ const COMPENSATING_FOCUS = {
|
|
|
7
8
|
gemini: 'Focus your review on: architectural patterns, design consistency,'
|
|
8
9
|
+ ' broad-context reasoning, separation of concerns, and dependency analysis.'
|
|
9
10
|
+ ' You are compensating for a missing Gemini review.',
|
|
11
|
+
grok: 'Focus your review on: an independent second-opinion pass over correctness'
|
|
12
|
+
+ ' and code quality — edge cases, logic errors, and risky assumptions other'
|
|
13
|
+
+ ' reviewers may have anchored past. You are compensating for a missing Grok review.',
|
|
10
14
|
};
|
|
11
|
-
|
|
12
|
-
|
|
15
|
+
function defaultCompensatorDispatch(config) {
|
|
16
|
+
return {
|
|
17
|
+
command: 'claude',
|
|
18
|
+
flags: ['-p', '--output-format', 'json'],
|
|
19
|
+
env: {},
|
|
20
|
+
timeout: config.defaults.timeout,
|
|
21
|
+
prompt_wrapper: '{{prompt}}',
|
|
22
|
+
stderr: 'capture',
|
|
23
|
+
output_parser: 'default',
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
export function resolveCompensatorDispatch(config) {
|
|
27
|
+
const { defaults } = config;
|
|
28
|
+
const channelName = defaults.compensator?.channel;
|
|
29
|
+
if (!channelName) {
|
|
30
|
+
return defaultCompensatorDispatch(config);
|
|
31
|
+
}
|
|
32
|
+
const channelConfig = getDispatchableCompensatorChannel(config, channelName);
|
|
33
|
+
return {
|
|
34
|
+
command: channelConfig.command,
|
|
35
|
+
flags: channelConfig.flags,
|
|
36
|
+
env: channelConfig.env,
|
|
37
|
+
timeout: channelConfig.timeout ?? config.defaults.timeout,
|
|
38
|
+
prompt_wrapper: channelConfig.prompt_wrapper ?? '{{prompt}}',
|
|
39
|
+
stderr: channelConfig.stderr,
|
|
40
|
+
output_parser: channelConfig.output_parser,
|
|
41
|
+
prompt_delivery: channelConfig.prompt_delivery,
|
|
42
|
+
cwd: channelConfig.cwd,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
export function resolveCompensatorChannelName(config) {
|
|
46
|
+
return config.defaults.compensator?.channel ?? 'claude';
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Resolve the configured compensator channel config (any kind), or undefined
|
|
50
|
+
* when none is configured (the default `claude -p` subprocess fallback applies).
|
|
51
|
+
* Throws only for a missing / abstract reference. Unlike
|
|
52
|
+
* getDispatchableCompensatorChannel this does NOT require a `command`, so it is
|
|
53
|
+
* safe for http compensators.
|
|
54
|
+
*/
|
|
55
|
+
export function getCompensatorChannel(config) {
|
|
56
|
+
const channelName = config.defaults.compensator?.channel;
|
|
57
|
+
if (!channelName)
|
|
58
|
+
return undefined;
|
|
59
|
+
const channelConfig = config.channels[channelName];
|
|
60
|
+
if (!channelConfig) {
|
|
61
|
+
throw new Error(`Compensator channel "${channelName}" not found in config`);
|
|
62
|
+
}
|
|
63
|
+
if (channelConfig.abstract) {
|
|
64
|
+
throw new Error(`Compensator channel "${channelName}" is abstract and cannot be dispatched`);
|
|
65
|
+
}
|
|
66
|
+
return channelConfig;
|
|
67
|
+
}
|
|
68
|
+
/** Output parser for the configured compensator channel (any kind); 'default' when none. */
|
|
69
|
+
export function resolveCompensatorOutputParser(config) {
|
|
70
|
+
return getCompensatorChannel(config)?.output_parser ?? 'default';
|
|
71
|
+
}
|
|
72
|
+
export function getDispatchableCompensatorChannel(config, channelName) {
|
|
73
|
+
const channelConfig = config.channels[channelName];
|
|
74
|
+
if (!channelConfig) {
|
|
75
|
+
throw new Error(`Compensator channel "${channelName}" not found in config`);
|
|
76
|
+
}
|
|
77
|
+
if (channelConfig.abstract) {
|
|
78
|
+
throw new Error(`Compensator channel "${channelName}" is abstract and cannot be dispatched`);
|
|
79
|
+
}
|
|
80
|
+
if (!channelConfig.command) {
|
|
81
|
+
throw new Error(`Compensator channel "${channelName}" has no command`);
|
|
82
|
+
}
|
|
83
|
+
return channelConfig;
|
|
84
|
+
}
|
|
85
|
+
function applyPromptWrapper(wrapper, prompt) {
|
|
86
|
+
return wrapper === '{{prompt}}'
|
|
87
|
+
? prompt
|
|
88
|
+
: wrapper.replaceAll('{{prompt}}', () => prompt);
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Resolve the focus-area prompt prefix for a compensating pass.
|
|
92
|
+
*/
|
|
93
|
+
export function resolveCompensatorFocus(config, originalChannel) {
|
|
94
|
+
const override = config.defaults.compensator?.channel_focus_map?.[originalChannel];
|
|
95
|
+
if (typeof override === 'string' && override.trim().length > 0)
|
|
96
|
+
return override;
|
|
97
|
+
const builtin = COMPENSATING_FOCUS[originalChannel];
|
|
98
|
+
if (builtin)
|
|
99
|
+
return builtin;
|
|
100
|
+
return `Focus your review on areas typically covered by ${originalChannel}.`
|
|
101
|
+
+ ` You are compensating for a missing ${originalChannel} review.`;
|
|
102
|
+
}
|
|
13
103
|
/**
|
|
14
104
|
* Determine which channels need compensating passes.
|
|
15
105
|
* Returns a list of compensating channel descriptors.
|
|
16
106
|
*/
|
|
17
|
-
export function getCompensatingChannels(channelStatuses) {
|
|
107
|
+
export function getCompensatingChannels(channelStatuses, compensatorChannel) {
|
|
18
108
|
const compensating = [];
|
|
19
109
|
for (const [name, status] of Object.entries(channelStatuses)) {
|
|
20
|
-
if (
|
|
110
|
+
if (name === compensatorChannel)
|
|
21
111
|
continue;
|
|
22
112
|
if (status === 'not_installed'
|
|
23
113
|
|| status === 'auth_failed'
|
|
24
114
|
|| status === 'timeout'
|
|
25
115
|
|| status === 'skipped'
|
|
26
116
|
|| status === 'failed') {
|
|
27
|
-
const focus = COMPENSATING_FOCUS[name]
|
|
28
|
-
?? `Focus your review on areas typically covered by ${name}.`
|
|
29
|
-
+ ` You are compensating for a missing ${name} review.`;
|
|
30
117
|
compensating.push({
|
|
31
118
|
originalChannel: name,
|
|
32
119
|
compensatingName: `compensating-${name}`,
|
|
33
|
-
focusPrompt: focus,
|
|
34
120
|
});
|
|
35
121
|
}
|
|
36
122
|
}
|
|
@@ -40,16 +126,35 @@ export function getCompensatingChannels(channelStatuses) {
|
|
|
40
126
|
* Dispatch compensating passes via claude CLI for unavailable channels.
|
|
41
127
|
* Each compensating pass uses the same prompt but with a focused preamble.
|
|
42
128
|
*/
|
|
43
|
-
export async function dispatchCompensatingPasses(store, jobId, prompt, compensatingChannels,
|
|
129
|
+
export async function dispatchCompensatingPasses(store, jobId, prompt, compensatingChannels, config) {
|
|
130
|
+
const compChannel = getCompensatorChannel(config);
|
|
131
|
+
// HTTP compensator: route each pass through the HTTP dispatcher.
|
|
132
|
+
if (compChannel && compChannel.kind === 'http') {
|
|
133
|
+
await Promise.all(compensatingChannels.map((comp) => {
|
|
134
|
+
const focus = resolveCompensatorFocus(config, comp.originalChannel);
|
|
135
|
+
const compensatingPrompt = applyPromptWrapper(compChannel.prompt_wrapper ?? '{{prompt}}', `${focus}\n\n${prompt}`);
|
|
136
|
+
return dispatchHttpChannel(store, jobId, comp.compensatingName, {
|
|
137
|
+
channel: compChannel,
|
|
138
|
+
prompt: compensatingPrompt,
|
|
139
|
+
timeout: compChannel.timeout ?? config.defaults.timeout,
|
|
140
|
+
});
|
|
141
|
+
}));
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
// Subprocess compensator (default `claude -p` or a configured command).
|
|
145
|
+
const dispatch = resolveCompensatorDispatch(config);
|
|
44
146
|
await Promise.all(compensatingChannels.map((comp) => {
|
|
45
|
-
const
|
|
147
|
+
const focus = resolveCompensatorFocus(config, comp.originalChannel);
|
|
148
|
+
const compensatingPrompt = applyPromptWrapper(dispatch.prompt_wrapper, `${focus}\n\n${prompt}`);
|
|
46
149
|
return dispatchChannel(store, jobId, comp.compensatingName, {
|
|
47
|
-
command:
|
|
150
|
+
command: dispatch.command,
|
|
48
151
|
prompt: compensatingPrompt,
|
|
49
|
-
flags:
|
|
50
|
-
env:
|
|
51
|
-
timeout,
|
|
52
|
-
stderr:
|
|
152
|
+
flags: dispatch.flags,
|
|
153
|
+
env: dispatch.env,
|
|
154
|
+
timeout: dispatch.timeout,
|
|
155
|
+
stderr: dispatch.stderr,
|
|
156
|
+
promptDelivery: dispatch.prompt_delivery,
|
|
157
|
+
cwd: dispatch.cwd,
|
|
53
158
|
});
|
|
54
159
|
}));
|
|
55
160
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"compensator.js","sourceRoot":"","sources":["../../src/core/compensator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;
|
|
1
|
+
{"version":3,"file":"compensator.js","sourceRoot":"","sources":["../../src/core/compensator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AACjD,OAAO,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAA;AAK1D,kFAAkF;AAClF,MAAM,kBAAkB,GAA2B;IACjD,KAAK,EACH,6EAA6E;UAC3E,iEAAiE;UACjE,mDAAmD;IACvD,MAAM,EACJ,mEAAmE;UACjE,4EAA4E;UAC5E,oDAAoD;IACxD,IAAI,EACF,2EAA2E;UACzE,2EAA2E;UAC3E,oFAAoF;CACzF,CAAA;AAqBD,SAAS,0BAA0B,CAAC,MAAuB;IACzD,OAAO;QACL,OAAO,EAAE,QAAQ;QACjB,KAAK,EAAE,CAAC,IAAI,EAAE,iBAAiB,EAAE,MAAM,CAAC;QACxC,GAAG,EAAE,EAAE;QACP,OAAO,EAAE,MAAM,CAAC,QAAQ,CAAC,OAAO;QAChC,cAAc,EAAE,YAAY;QAC5B,MAAM,EAAE,SAAS;QACjB,aAAa,EAAE,SAAS;KACzB,CAAA;AACH,CAAC;AAED,MAAM,UAAU,0BAA0B,CAAC,MAAuB;IAChE,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,CAAA;IAC3B,MAAM,WAAW,GAAG,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAA;IACjD,IAAI,CAAC,WAAW,EAAE,CAAC;QACjB,OAAO,0BAA0B,CAAC,MAAM,CAAC,CAAA;IAC3C,CAAC;IAED,MAAM,aAAa,GAAG,iCAAiC,CAAC,MAAM,EAAE,WAAW,CAAC,CAAA;IAE5E,OAAO;QACL,OAAO,EAAE,aAAa,CAAC,OAAO;QAC9B,KAAK,EAAE,aAAa,CAAC,KAAK;QAC1B,GAAG,EAAE,aAAa,CAAC,GAAG;QACtB,OAAO,EAAE,aAAa,CAAC,OAAO,IAAI,MAAM,CAAC,QAAQ,CAAC,OAAO;QACzD,cAAc,EAAE,aAAa,CAAC,cAAc,IAAI,YAAY;QAC5D,MAAM,EAAE,aAAa,CAAC,MAAM;QAC5B,aAAa,EAAE,aAAa,CAAC,aAAa;QAC1C,eAAe,EAAE,aAAa,CAAC,eAAe;QAC9C,GAAG,EAAE,aAAa,CAAC,GAAG;KACvB,CAAA;AACH,CAAC;AAED,MAAM,UAAU,6BAA6B,CAAC,MAAuB;IACnE,OAAO,MAAM,CAAC,QAAQ,CAAC,WAAW,EAAE,OAAO,IAAI,QAAQ,CAAA;AACzD,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,qBAAqB,CAAC,MAAuB;IAC3D,MAAM,WAAW,GAAG,MAAM,CAAC,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAA;IACxD,IAAI,CAAC,WAAW;QAAE,OAAO,SAAS,CAAA;IAClC,MAAM,aAAa,GAAG,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAA;IAClD,IAAI,CAAC,aAAa,EAAE,CAAC;QACnB,MAAM,IAAI,KAAK,CAAC,wBAAwB,WAAW,uBAAuB,CAAC,CAAA;IAC7E,CAAC;IACD,IAAI,aAAa,CAAC,QAAQ,EAAE,CAAC;QAC3B,MAAM,IAAI,KAAK,CAAC,wBAAwB,WAAW,wCAAwC,CAAC,CAAA;IAC9F,CAAC;IACD,OAAO,aAAa,CAAA;AACtB,CAAC;AAED,4FAA4F;AAC5F,MAAM,UAAU,8BAA8B,CAAC,MAAuB;IACpE,OAAO,qBAAqB,CAAC,MAAM,CAAC,EAAE,aAAa,IAAI,SAAS,CAAA;AAClE,CAAC;AAED,MAAM,UAAU,iCAAiC,CAC/C,MAAuB,EACvB,WAAmB;IAEnB,MAAM,aAAa,GAAG,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAA;IAClD,IAAI,CAAC,aAAa,EAAE,CAAC;QACnB,MAAM,IAAI,KAAK,CAAC,wBAAwB,WAAW,uBAAuB,CAAC,CAAA;IAC7E,CAAC;IACD,IAAI,aAAa,CAAC,QAAQ,EAAE,CAAC;QAC3B,MAAM,IAAI,KAAK,CAAC,wBAAwB,WAAW,wCAAwC,CAAC,CAAA;IAC9F,CAAC;IACD,IAAI,CAAC,aAAa,CAAC,OAAO,EAAE,CAAC;QAC3B,MAAM,IAAI,KAAK,CAAC,wBAAwB,WAAW,kBAAkB,CAAC,CAAA;IACxE,CAAC;IACD,OAAO,aAA0D,CAAA;AACnE,CAAC;AAED,SAAS,kBAAkB,CAAC,OAAe,EAAE,MAAc;IACzD,OAAO,OAAO,KAAK,YAAY;QAC7B,CAAC,CAAC,MAAM;QACR,CAAC,CAAC,OAAO,CAAC,UAAU,CAAC,YAAY,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,CAAA;AACpD,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,uBAAuB,CACrC,MAAuB,EACvB,eAAuB;IAEvB,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC,WAAW,EAAE,iBAAiB,EAAE,CAAC,eAAe,CAAC,CAAA;IAClF,IAAI,OAAO,QAAQ,KAAK,QAAQ,IAAI,QAAQ,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,QAAQ,CAAA;IAC/E,MAAM,OAAO,GAAG,kBAAkB,CAAC,eAAe,CAAC,CAAA;IACnD,IAAI,OAAO;QAAE,OAAO,OAAO,CAAA;IAC3B,OAAO,mDAAmD,eAAe,GAAG;UACxE,uCAAuC,eAAe,UAAU,CAAA;AACtE,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,uBAAuB,CACrC,eAA8C,EAC9C,kBAA0B;IAE1B,MAAM,YAAY,GAA0B,EAAE,CAAA;IAE9C,KAAK,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,eAAe,CAAC,EAAE,CAAC;QAC7D,IAAI,IAAI,KAAK,kBAAkB;YAAE,SAAQ;QACzC,IACE,MAAM,KAAK,eAAe;eACvB,MAAM,KAAK,aAAa;eACxB,MAAM,KAAK,SAAS;eACpB,MAAM,KAAK,SAAS;eACpB,MAAM,KAAK,QAAQ,EACtB,CAAC;YACD,YAAY,CAAC,IAAI,CAAC;gBAChB,eAAe,EAAE,IAAI;gBACrB,gBAAgB,EAAE,gBAAgB,IAAI,EAAE;aACzC,CAAC,CAAA;QACJ,CAAC;IACH,CAAC;IAED,OAAO,YAAY,CAAA;AACrB,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,0BAA0B,CAC9C,KAAe,EACf,KAAa,EACb,MAAc,EACd,oBAA2C,EAC3C,MAAuB;IAEvB,MAAM,WAAW,GAAG,qBAAqB,CAAC,MAAM,CAAC,CAAA;IAEjD,iEAAiE;IACjE,IAAI,WAAW,IAAI,WAAW,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;QAC/C,MAAM,OAAO,CAAC,GAAG,CACf,oBAAoB,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;YAChC,MAAM,KAAK,GAAG,uBAAuB,CAAC,MAAM,EAAE,IAAI,CAAC,eAAe,CAAC,CAAA;YACnE,MAAM,kBAAkB,GAAG,kBAAkB,CAC3C,WAAW,CAAC,cAAc,IAAI,YAAY,EAC1C,GAAG,KAAK,OAAO,MAAM,EAAE,CACxB,CAAA;YACD,OAAO,mBAAmB,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,CAAC,gBAAgB,EAAE;gBAC9D,OAAO,EAAE,WAAW;gBACpB,MAAM,EAAE,kBAAkB;gBAC1B,OAAO,EAAE,WAAW,CAAC,OAAO,IAAI,MAAM,CAAC,QAAQ,CAAC,OAAO;aACxD,CAAC,CAAA;QACJ,CAAC,CAAC,CACH,CAAA;QACD,OAAM;IACR,CAAC;IAED,wEAAwE;IACxE,MAAM,QAAQ,GAAG,0BAA0B,CAAC,MAAM,CAAC,CAAA;IACnD,MAAM,OAAO,CAAC,GAAG,CACf,oBAAoB,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;QAChC,MAAM,KAAK,GAAG,uBAAuB,CAAC,MAAM,EAAE,IAAI,CAAC,eAAe,CAAC,CAAA;QACnE,MAAM,kBAAkB,GAAG,kBAAkB,CAAC,QAAQ,CAAC,cAAc,EAAE,GAAG,KAAK,OAAO,MAAM,EAAE,CAAC,CAAA;QAC/F,OAAO,eAAe,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,CAAC,gBAAgB,EAAE;YAC1D,OAAO,EAAE,QAAQ,CAAC,OAAO;YACzB,MAAM,EAAE,kBAAkB;YAC1B,KAAK,EAAE,QAAQ,CAAC,KAAK;YACrB,GAAG,EAAE,QAAQ,CAAC,GAAG;YACjB,OAAO,EAAE,QAAQ,CAAC,OAAO;YACzB,MAAM,EAAE,QAAQ,CAAC,MAAM;YACvB,cAAc,EAAE,QAAQ,CAAC,eAAe;YACxC,GAAG,EAAE,QAAQ,CAAC,GAAG;SAClB,CAAC,CAAA;IACJ,CAAC,CAAC,CACH,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export interface ConfigChangeReport {
|
|
2
|
+
/** Whether the diff touches `./.mmr.yaml` at all. */
|
|
3
|
+
config_file_changed: boolean;
|
|
4
|
+
/** Paths under `./.mmr/acks/` touched by the diff (added/modified/removed). */
|
|
5
|
+
ack_files_changed: string[];
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Inspect a unified diff for changes to MMR trust-relevant files: the project
|
|
9
|
+
* config (`.mmr.yaml`) and any project acks (`.mmr/acks/*`). Callers use this
|
|
10
|
+
* to force `needs-user-decision` when a diff under review proposes new
|
|
11
|
+
* config/acks, so an untrusted PR can't silently change channels or suppress
|
|
12
|
+
* its own findings.
|
|
13
|
+
*
|
|
14
|
+
* The diff is attacker-controllable (`--diff`/stdin), so detection is
|
|
15
|
+
* deliberately liberal: it matches across diff producers (git a/b, --no-prefix,
|
|
16
|
+
* raw diff -u, merge `--cc`, rename/copy) and is case-insensitive. Spurious
|
|
17
|
+
* over-detection only forces a (overridable) user decision — the safe
|
|
18
|
+
* direction — while a miss would silently trust attacker config/acks.
|
|
19
|
+
*/
|
|
20
|
+
export declare function detectConfigChanges(diff: string): ConfigChangeReport;
|
|
21
|
+
//# sourceMappingURL=diff-introspect.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diff-introspect.d.ts","sourceRoot":"","sources":["../../src/core/diff-introspect.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,kBAAkB;IACjC,qDAAqD;IACrD,mBAAmB,EAAE,OAAO,CAAA;IAC5B,+EAA+E;IAC/E,iBAAiB,EAAE,MAAM,EAAE,CAAA;CAC5B;AAgBD;;;;;;;;;;;;GAYG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,kBAAkB,CAWpE"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// Lines that name files in a unified/combined diff, across producers:
|
|
2
|
+
// `git diff` (a/ b/), `git diff --no-prefix`, raw `diff -u` (---/+++ only),
|
|
3
|
+
// merge diffs (`diff --cc`), and rename/copy metadata. We inspect only these
|
|
4
|
+
// header-ish lines (not hunk bodies) for the sentinel paths.
|
|
5
|
+
const HEADER_RE = /^(?:diff --git |diff --cc |diff --combined |--- |\+\+\+ |rename (?:from|to) |copy (?:from|to) )/;
|
|
6
|
+
// `.mmr.yaml` as a path component (start, separator, or quote on each side),
|
|
7
|
+
// case-insensitive so a casing variant on case-insensitive filesystems still
|
|
8
|
+
// trips the gate (over-detection is the safe failure mode here).
|
|
9
|
+
const CONFIG_RE = /(?:^|[\s/"'])\.mmr\.yaml(?:["'\s]|$)/i;
|
|
10
|
+
// Any `.mmr/acks/<file>` occurrence; the match starts at `.mmr` so a leading
|
|
11
|
+
// a//b/ prefix is naturally excluded.
|
|
12
|
+
const ACK_RE = /\.mmr\/acks\/[^\s"']+/gi;
|
|
13
|
+
/**
|
|
14
|
+
* Inspect a unified diff for changes to MMR trust-relevant files: the project
|
|
15
|
+
* config (`.mmr.yaml`) and any project acks (`.mmr/acks/*`). Callers use this
|
|
16
|
+
* to force `needs-user-decision` when a diff under review proposes new
|
|
17
|
+
* config/acks, so an untrusted PR can't silently change channels or suppress
|
|
18
|
+
* its own findings.
|
|
19
|
+
*
|
|
20
|
+
* The diff is attacker-controllable (`--diff`/stdin), so detection is
|
|
21
|
+
* deliberately liberal: it matches across diff producers (git a/b, --no-prefix,
|
|
22
|
+
* raw diff -u, merge `--cc`, rename/copy) and is case-insensitive. Spurious
|
|
23
|
+
* over-detection only forces a (overridable) user decision — the safe
|
|
24
|
+
* direction — while a miss would silently trust attacker config/acks.
|
|
25
|
+
*/
|
|
26
|
+
export function detectConfigChanges(diff) {
|
|
27
|
+
const report = { config_file_changed: false, ack_files_changed: [] };
|
|
28
|
+
if (!diff)
|
|
29
|
+
return report;
|
|
30
|
+
const acks = new Set();
|
|
31
|
+
for (const line of diff.split('\n')) {
|
|
32
|
+
if (!HEADER_RE.test(line))
|
|
33
|
+
continue;
|
|
34
|
+
if (CONFIG_RE.test(line))
|
|
35
|
+
report.config_file_changed = true;
|
|
36
|
+
for (const m of line.matchAll(ACK_RE))
|
|
37
|
+
acks.add(m[0]);
|
|
38
|
+
}
|
|
39
|
+
report.ack_files_changed = [...acks];
|
|
40
|
+
return report;
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=diff-introspect.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diff-introspect.js","sourceRoot":"","sources":["../../src/core/diff-introspect.ts"],"names":[],"mappings":"AAOA,sEAAsE;AACtE,4EAA4E;AAC5E,6EAA6E;AAC7E,6DAA6D;AAC7D,MAAM,SAAS,GACb,iGAAiG,CAAA;AACnG,6EAA6E;AAC7E,6EAA6E;AAC7E,iEAAiE;AACjE,MAAM,SAAS,GAAG,uCAAuC,CAAA;AACzD,6EAA6E;AAC7E,sCAAsC;AACtC,MAAM,MAAM,GAAG,yBAAyB,CAAA;AAExC;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,mBAAmB,CAAC,IAAY;IAC9C,MAAM,MAAM,GAAuB,EAAE,mBAAmB,EAAE,KAAK,EAAE,iBAAiB,EAAE,EAAE,EAAE,CAAA;IACxF,IAAI,CAAC,IAAI;QAAE,OAAO,MAAM,CAAA;IACxB,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAA;IAC9B,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QACpC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC;YAAE,SAAQ;QACnC,IAAI,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC;YAAE,MAAM,CAAC,mBAAmB,GAAG,IAAI,CAAA;QAC3D,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC;YAAE,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;IACvD,CAAC;IACD,MAAM,CAAC,iBAAiB,GAAG,CAAC,GAAG,IAAI,CAAC,CAAA;IACpC,OAAO,MAAM,CAAA;AACf,CAAC"}
|
|
@@ -7,6 +7,16 @@ export interface DispatchOptions {
|
|
|
7
7
|
env: Record<string, string>;
|
|
8
8
|
timeout: number;
|
|
9
9
|
stderr: 'capture' | 'suppress' | 'passthrough';
|
|
10
|
+
/**
|
|
11
|
+
* How to hand the prompt to the process. 'stdin' (default) pipes it to
|
|
12
|
+
* stdin. 'prompt-file' writes it to a file in the channel dir and passes
|
|
13
|
+
* the path via a {{prompt_file}} placeholder in flags (or appended when no
|
|
14
|
+
* placeholder is present), for CLIs that require the prompt as an arg.
|
|
15
|
+
*/
|
|
16
|
+
promptDelivery?: 'stdin' | 'prompt-file';
|
|
17
|
+
/** Working directory for the spawned process. {{neutral_cwd}} is expanded
|
|
18
|
+
* (with {{neutral_home}} in env) into a per-run isolated dir before spawn. */
|
|
19
|
+
cwd?: string;
|
|
10
20
|
}
|
|
11
21
|
/** Check whether a channel status represents a terminal (done) state */
|
|
12
22
|
export declare function isChannelComplete(status: ChannelStatus): boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dispatcher.d.ts","sourceRoot":"","sources":["../../src/core/dispatcher.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAChD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;
|
|
1
|
+
{"version":3,"file":"dispatcher.d.ts","sourceRoot":"","sources":["../../src/core/dispatcher.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAChD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AAG9C,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,EAAE,MAAM,EAAE,CAAA;IACf,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC3B,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,SAAS,GAAG,UAAU,GAAG,aAAa,CAAA;IAC9C;;;;;OAKG;IACH,cAAc,CAAC,EAAE,OAAO,GAAG,aAAa,CAAA;IACxC;mFAC+E;IAC/E,GAAG,CAAC,EAAE,MAAM,CAAA;CACb;AAiDD,wEAAwE;AACxE,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAEhE;AAED,qEAAqE;AACrE,wBAAsB,eAAe,CACnC,KAAK,EAAE,QAAQ,EACf,KAAK,EAAE,MAAM,EACb,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE,eAAe,GACpB,OAAO,CAAC,IAAI,CAAC,CAgLf"}
|
package/dist/core/dispatcher.js
CHANGED
|
@@ -2,8 +2,17 @@ import { spawn } from 'node:child_process';
|
|
|
2
2
|
import fs from 'node:fs';
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import { TERMINAL_STATUSES } from '../types.js';
|
|
5
|
+
import { withNeutralPosture, sweepStaleNeutralDirs } from './host-isolation.js';
|
|
6
|
+
/** Placeholder token replaced with the prompt-file path in prompt-file mode. */
|
|
7
|
+
const PROMPT_FILE_PLACEHOLDER = '{{prompt_file}}';
|
|
5
8
|
/** Track active child PIDs for cleanup on parent exit */
|
|
6
9
|
const activeChildren = new Set();
|
|
10
|
+
/**
|
|
11
|
+
* Track per-dispatch neutral-posture cleanups so a SIGINT/SIGTERM that
|
|
12
|
+
* terminates the run also removes the isolated HOME/cwd temp dirs (which hold a
|
|
13
|
+
* grok auth symlink). Each entry is removed once its own dispatch settles.
|
|
14
|
+
*/
|
|
15
|
+
const activePostureCleanups = new Set();
|
|
7
16
|
function cleanupChildren() {
|
|
8
17
|
for (const pid of activeChildren) {
|
|
9
18
|
try {
|
|
@@ -14,6 +23,15 @@ function cleanupChildren() {
|
|
|
14
23
|
}
|
|
15
24
|
}
|
|
16
25
|
activeChildren.clear();
|
|
26
|
+
// Remove any neutral-posture temp dirs from interrupted dispatches. Snapshot
|
|
27
|
+
// first: a cleanup callback (or a settling dispatch) could mutate the live Set.
|
|
28
|
+
for (const cleanup of [...activePostureCleanups]) {
|
|
29
|
+
try {
|
|
30
|
+
cleanup();
|
|
31
|
+
}
|
|
32
|
+
catch { /* best effort */ }
|
|
33
|
+
}
|
|
34
|
+
activePostureCleanups.clear();
|
|
17
35
|
}
|
|
18
36
|
// Register cleanup once
|
|
19
37
|
let cleanupRegistered = false;
|
|
@@ -24,6 +42,17 @@ function ensureCleanupRegistered() {
|
|
|
24
42
|
process.on('SIGINT', () => { cleanupChildren(); process.exit(130); });
|
|
25
43
|
process.on('SIGTERM', () => { cleanupChildren(); process.exit(143); });
|
|
26
44
|
}
|
|
45
|
+
// Sweep stale neutral dirs once at process start
|
|
46
|
+
let sweepDone = false;
|
|
47
|
+
function ensureSweepOnce() {
|
|
48
|
+
if (sweepDone)
|
|
49
|
+
return;
|
|
50
|
+
sweepDone = true;
|
|
51
|
+
try {
|
|
52
|
+
sweepStaleNeutralDirs();
|
|
53
|
+
}
|
|
54
|
+
catch { /* best effort — never block dispatch */ }
|
|
55
|
+
}
|
|
27
56
|
/** Check whether a channel status represents a terminal (done) state */
|
|
28
57
|
export function isChannelComplete(status) {
|
|
29
58
|
return TERMINAL_STATUSES.has(status);
|
|
@@ -31,6 +60,7 @@ export function isChannelComplete(status) {
|
|
|
31
60
|
/** Spawn a background process for a review channel and monitor it */
|
|
32
61
|
export async function dispatchChannel(store, jobId, channelName, opts) {
|
|
33
62
|
ensureCleanupRegistered();
|
|
63
|
+
ensureSweepOnce();
|
|
34
64
|
if (!/^[a-zA-Z0-9._-]+$/.test(channelName)) {
|
|
35
65
|
throw new Error(`Unsafe channel name: ${channelName}`);
|
|
36
66
|
}
|
|
@@ -38,7 +68,20 @@ export async function dispatchChannel(store, jobId, channelName, opts) {
|
|
|
38
68
|
const channelsDir = path.join(jobDir, 'channels');
|
|
39
69
|
// Split multi-word commands (e.g. "claude -p" → ["claude", "-p"])
|
|
40
70
|
const [cmd, ...cmdArgs] = opts.command.split(/\s+/);
|
|
41
|
-
|
|
71
|
+
let args = [...cmdArgs, ...opts.flags];
|
|
72
|
+
// In prompt-file mode, write the prompt to a file in the channel dir and
|
|
73
|
+
// substitute its path for the {{prompt_file}} placeholder (or append it).
|
|
74
|
+
// The prompt is NOT piped to stdin in this mode.
|
|
75
|
+
const promptDelivery = opts.promptDelivery ?? 'stdin';
|
|
76
|
+
if (promptDelivery === 'prompt-file') {
|
|
77
|
+
const promptFile = path.join(channelsDir, `${channelName}.prompt.txt`);
|
|
78
|
+
// Async write: prompts can carry large diffs and channels dispatch in
|
|
79
|
+
// parallel, so avoid blocking the event loop.
|
|
80
|
+
await fs.promises.writeFile(promptFile, opts.prompt);
|
|
81
|
+
args = args.some((a) => a.includes(PROMPT_FILE_PLACEHOLDER))
|
|
82
|
+
? args.map((a) => a.split(PROMPT_FILE_PLACEHOLDER).join(promptFile))
|
|
83
|
+
: [...args, promptFile];
|
|
84
|
+
}
|
|
42
85
|
// Update channel to running
|
|
43
86
|
store.updateChannel(jobId, channelName, {
|
|
44
87
|
status: 'running',
|
|
@@ -48,25 +91,50 @@ export async function dispatchChannel(store, jobId, channelName, opts) {
|
|
|
48
91
|
const stderrStdio = opts.stderr === 'passthrough' ? 'inherit'
|
|
49
92
|
: opts.stderr === 'capture' ? 'pipe'
|
|
50
93
|
: 'ignore'; // suppress
|
|
51
|
-
//
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
//
|
|
65
|
-
proc
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
94
|
+
// Expand neutral posture placeholders ({{neutral_home}}, {{neutral_cwd}});
|
|
95
|
+
// for channels without placeholders, withNeutralPosture is a passthrough.
|
|
96
|
+
const posture = withNeutralPosture(opts.env, opts.cwd);
|
|
97
|
+
// Register for SIGINT/SIGTERM cleanup, and remove + run once this dispatch
|
|
98
|
+
// settles. cleanup is idempotent, so a signal firing alongside a terminal
|
|
99
|
+
// handler is safe.
|
|
100
|
+
activePostureCleanups.add(posture.cleanup);
|
|
101
|
+
const runPostureCleanup = () => {
|
|
102
|
+
activePostureCleanups.delete(posture.cleanup);
|
|
103
|
+
posture.cleanup();
|
|
104
|
+
};
|
|
105
|
+
// Spawn + all synchronous setup (stdin, PID file) in one guarded block: any
|
|
106
|
+
// throw between dir creation and the async close/error handlers being armed
|
|
107
|
+
// would otherwise leak the per-run temp dir.
|
|
108
|
+
let proc;
|
|
109
|
+
try {
|
|
110
|
+
// Pipe prompt via stdin to avoid E2BIG on large diffs
|
|
111
|
+
proc = spawn(cmd, args, {
|
|
112
|
+
detached: true,
|
|
113
|
+
stdio: ['pipe', 'pipe', stderrStdio],
|
|
114
|
+
env: { ...process.env, ...posture.env },
|
|
115
|
+
cwd: posture.cwd, // undefined ⇒ inherit parent cwd (unchanged for non-isolated channels)
|
|
116
|
+
});
|
|
117
|
+
if (proc.pid)
|
|
118
|
+
activeChildren.add(proc.pid);
|
|
119
|
+
// Handle stdin pipe errors (child may close stdin early)
|
|
120
|
+
// stdin is always 'pipe' so proc.stdin is guaranteed non-null
|
|
121
|
+
proc.stdin.on('error', () => {
|
|
122
|
+
// Swallow EPIPE — the close handler will deal with the process exit
|
|
123
|
+
});
|
|
124
|
+
// Write prompt to stdin (stdin delivery only; prompt-file mode passes the
|
|
125
|
+
// prompt as an arg). Always end stdin so processes that read it don't hang.
|
|
126
|
+
if (promptDelivery === 'stdin') {
|
|
127
|
+
proc.stdin.write(opts.prompt);
|
|
128
|
+
}
|
|
129
|
+
proc.stdin.end();
|
|
130
|
+
// Write PID file
|
|
131
|
+
const pidFile = path.join(channelsDir, `${channelName}.pid`);
|
|
132
|
+
fs.writeFileSync(pidFile, String(proc.pid));
|
|
133
|
+
}
|
|
134
|
+
catch (err) {
|
|
135
|
+
runPostureCleanup();
|
|
136
|
+
throw err;
|
|
137
|
+
}
|
|
70
138
|
// Collect stdout and stderr
|
|
71
139
|
let stdout = '';
|
|
72
140
|
let stderr = '';
|
|
@@ -107,6 +175,7 @@ export async function dispatchChannel(store, jobId, channelName, opts) {
|
|
|
107
175
|
if (stderr) {
|
|
108
176
|
store.saveChannelLog(jobId, channelName, stderr);
|
|
109
177
|
}
|
|
178
|
+
runPostureCleanup();
|
|
110
179
|
resolve();
|
|
111
180
|
}, timeoutMs);
|
|
112
181
|
// Handle process close
|
|
@@ -134,6 +203,7 @@ export async function dispatchChannel(store, jobId, channelName, opts) {
|
|
|
134
203
|
completed_at: completedAt,
|
|
135
204
|
});
|
|
136
205
|
}
|
|
206
|
+
runPostureCleanup();
|
|
137
207
|
resolve();
|
|
138
208
|
});
|
|
139
209
|
// Handle spawn errors
|
|
@@ -149,6 +219,7 @@ export async function dispatchChannel(store, jobId, channelName, opts) {
|
|
|
149
219
|
completed_at: new Date().toISOString(),
|
|
150
220
|
});
|
|
151
221
|
store.saveChannelLog(jobId, channelName, err.message);
|
|
222
|
+
runPostureCleanup();
|
|
152
223
|
resolve();
|
|
153
224
|
});
|
|
154
225
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dispatcher.js","sourceRoot":"","sources":["../../src/core/dispatcher.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAA;AAC1C,OAAO,EAAE,MAAM,SAAS,CAAA;AACxB,OAAO,IAAI,MAAM,WAAW,CAAA;AAC5B,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"dispatcher.js","sourceRoot":"","sources":["../../src/core/dispatcher.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/** Whether a git ref is safe to embed in `git show <ref>:<path>`. */
|
|
2
|
+
export declare function isSafeRef(ref: string): boolean;
|
|
3
|
+
export interface ReadFileAtRefOptions {
|
|
4
|
+
cwd: string;
|
|
5
|
+
ref: string;
|
|
6
|
+
/** Path relative to the repo root, with leading `./` allowed. */
|
|
7
|
+
filePath: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Read file contents at a specific Git ref via `git show <ref>:<path>`.
|
|
11
|
+
* Returns `undefined` when the ref or the path does not exist at the ref.
|
|
12
|
+
* Never throws; callers must handle the undefined-fallback case.
|
|
13
|
+
*
|
|
14
|
+
* Fails closed (returns undefined) on an unsafe ref or path rather than
|
|
15
|
+
* trusting callers to pre-validate — this is the §5-decision-1 trust boundary,
|
|
16
|
+
* so the guard lives at the boundary itself.
|
|
17
|
+
*/
|
|
18
|
+
export declare function readFileAtRef(opts: ReadFileAtRefOptions): string | undefined;
|
|
19
|
+
export interface ListFilesAtRefOptions {
|
|
20
|
+
cwd: string;
|
|
21
|
+
ref: string;
|
|
22
|
+
/** Repo-relative directory to list, with leading `./` allowed. */
|
|
23
|
+
dirPath: string;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* List the file paths (repo-root-relative) under a directory at a Git ref via
|
|
27
|
+
* `git ls-tree`. Returns `[]` on an unsafe ref/path, a missing ref/dir, or any
|
|
28
|
+
* git error. Never throws.
|
|
29
|
+
*/
|
|
30
|
+
export declare function listFilesAtRef(opts: ListFilesAtRefOptions): string[];
|
|
31
|
+
//# sourceMappingURL=git-show.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git-show.d.ts","sourceRoot":"","sources":["../../src/core/git-show.ts"],"names":[],"mappings":"AAQA,qEAAqE;AACrE,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAS9C;AAQD,MAAM,WAAW,oBAAoB;IACnC,GAAG,EAAE,MAAM,CAAA;IACX,GAAG,EAAE,MAAM,CAAA;IACX,iEAAiE;IACjE,QAAQ,EAAE,MAAM,CAAA;CACjB;AAED;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,oBAAoB,GAAG,MAAM,GAAG,SAAS,CAa5E;AAED,MAAM,WAAW,qBAAqB;IACpC,GAAG,EAAE,MAAM,CAAA;IACX,GAAG,EAAE,MAAM,CAAA;IACX,kEAAkE;IAClE,OAAO,EAAE,MAAM,CAAA;CAChB;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,qBAAqB,GAAG,MAAM,EAAE,CAiBpE"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { execFileSync } from 'node:child_process';
|
|
2
|
+
// Conservative git refname allow-list: letters, digits, . _ - / and the safe
|
|
3
|
+
// relative-rev modifiers ~ ^. Rejects the constructs that could smuggle
|
|
4
|
+
// surprising rev/path syntax into `git show <ref>:<path>` — ':' (the rev:path
|
|
5
|
+
// separator), '..' (ranges), '@{' (reflog), and leading '-'/'/'.
|
|
6
|
+
const SAFE_REF_RE = /^[A-Za-z0-9._/~^-]+$/;
|
|
7
|
+
/** Whether a git ref is safe to embed in `git show <ref>:<path>`. */
|
|
8
|
+
export function isSafeRef(ref) {
|
|
9
|
+
return (SAFE_REF_RE.test(ref) &&
|
|
10
|
+
!ref.includes('..') &&
|
|
11
|
+
!ref.includes('@{') &&
|
|
12
|
+
!ref.startsWith('-') &&
|
|
13
|
+
!ref.startsWith('/') &&
|
|
14
|
+
!ref.endsWith('/'));
|
|
15
|
+
}
|
|
16
|
+
/** Whether a repo-relative path is safe (no rev separator, no traversal). */
|
|
17
|
+
function isSafePath(p) {
|
|
18
|
+
if (p.length === 0 || p.includes(':'))
|
|
19
|
+
return false;
|
|
20
|
+
return !p.split('/').includes('..');
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Read file contents at a specific Git ref via `git show <ref>:<path>`.
|
|
24
|
+
* Returns `undefined` when the ref or the path does not exist at the ref.
|
|
25
|
+
* Never throws; callers must handle the undefined-fallback case.
|
|
26
|
+
*
|
|
27
|
+
* Fails closed (returns undefined) on an unsafe ref or path rather than
|
|
28
|
+
* trusting callers to pre-validate — this is the §5-decision-1 trust boundary,
|
|
29
|
+
* so the guard lives at the boundary itself.
|
|
30
|
+
*/
|
|
31
|
+
export function readFileAtRef(opts) {
|
|
32
|
+
const cleanedPath = opts.filePath.replace(/^\.\//, '');
|
|
33
|
+
if (!isSafeRef(opts.ref) || !isSafePath(cleanedPath))
|
|
34
|
+
return undefined;
|
|
35
|
+
try {
|
|
36
|
+
return execFileSync('git', ['-C', opts.cwd, 'show', `${opts.ref}:${cleanedPath}`], {
|
|
37
|
+
encoding: 'utf-8',
|
|
38
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
39
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
40
|
+
timeout: 10000,
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
return undefined;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* List the file paths (repo-root-relative) under a directory at a Git ref via
|
|
49
|
+
* `git ls-tree`. Returns `[]` on an unsafe ref/path, a missing ref/dir, or any
|
|
50
|
+
* git error. Never throws.
|
|
51
|
+
*/
|
|
52
|
+
export function listFilesAtRef(opts) {
|
|
53
|
+
const cleaned = opts.dirPath.replace(/^\.\//, '').replace(/\/$/, '');
|
|
54
|
+
if (!isSafeRef(opts.ref) || !isSafePath(cleaned))
|
|
55
|
+
return [];
|
|
56
|
+
try {
|
|
57
|
+
const out = execFileSync('git', ['-C', opts.cwd, 'ls-tree', '--name-only', opts.ref, '--', `${cleaned}/`], {
|
|
58
|
+
encoding: 'utf-8',
|
|
59
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
60
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
61
|
+
timeout: 10000,
|
|
62
|
+
});
|
|
63
|
+
return out
|
|
64
|
+
.split('\n')
|
|
65
|
+
.map((line) => line.trim())
|
|
66
|
+
.filter((line) => line.length > 0);
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
return [];
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=git-show.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git-show.js","sourceRoot":"","sources":["../../src/core/git-show.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export declare const NEUTRAL_HOME_PLACEHOLDER = "{{neutral_home}}";
|
|
2
|
+
export declare const NEUTRAL_CWD_PLACEHOLDER = "{{neutral_cwd}}";
|
|
3
|
+
export interface NeutralPosture {
|
|
4
|
+
env: Record<string, string>;
|
|
5
|
+
cwd?: string;
|
|
6
|
+
/** Synchronous, idempotent removal of any dir this call created. */
|
|
7
|
+
cleanup: () => void;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Expand {{neutral_home}}/{{neutral_cwd}} placeholders into a single fresh
|
|
11
|
+
* per-call temp directory (unique → safe for parallel channel runs; each call
|
|
12
|
+
* owns its dir lifetime). Returns the concrete env/cwd plus a synchronous
|
|
13
|
+
* cleanup fn. When no placeholder is present, returns the inputs unchanged with
|
|
14
|
+
* a no-op cleanup.
|
|
15
|
+
*/
|
|
16
|
+
export declare function withNeutralPosture(env: Record<string, string>, cwd?: string): NeutralPosture;
|
|
17
|
+
/**
|
|
18
|
+
* Backstop for dirs orphaned by SIGKILL/crashes: remove stale mmr-grok-* temp
|
|
19
|
+
* dirs older than `maxAgeMs`. Call once at process start. Best-effort/sync.
|
|
20
|
+
* Default is 24h — comfortably longer than any plausible review timeout, so the
|
|
21
|
+
* sweep never reaps the HOME/cwd of a still-running long review.
|
|
22
|
+
*/
|
|
23
|
+
export declare function sweepStaleNeutralDirs(maxAgeMs?: number): void;
|
|
24
|
+
//# sourceMappingURL=host-isolation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"host-isolation.d.ts","sourceRoot":"","sources":["../../src/core/host-isolation.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,wBAAwB,qBAAqB,CAAA;AAC1D,eAAO,MAAM,uBAAuB,oBAAoB,CAAA;AAQxD,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC3B,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,oEAAoE;IACpE,OAAO,EAAE,MAAM,IAAI,CAAA;CACpB;AAOD;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,cAAc,CAgD5F;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,SAAsB,GAAG,IAAI,CAa1E"}
|