@mmerterden/multi-agent-pipeline 20.29.0 → 20.33.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/CHANGELOG.md +86 -0
- package/README.md +50 -5
- package/README.tr.md +64 -5
- package/docs/architecture.md +3 -3
- package/docs/ecosystem.md +5 -5
- package/docs/facts.json +9 -9
- package/install/_unattended-profile.mjs +61 -0
- package/install/catalog-history.json +1 -1
- package/install/claude.mjs +19 -3
- package/install/codex.mjs +2 -0
- package/install/copilot.mjs +13 -0
- package/install/index.mjs +10 -1
- package/install/templates/claude-hooks.json +12 -1
- package/manifest.json +230 -88
- package/package.json +3 -1
- package/pipeline/commands/multi-agent/analysis/SKILL.md +4 -4
- package/pipeline/commands/multi-agent/autopilot-on/SKILL.md +13 -15
- package/pipeline/commands/multi-agent/office-off/SKILL.md +27 -0
- package/pipeline/commands/multi-agent/office-on/SKILL.md +51 -0
- package/pipeline/commands/multi-agent/office-status/SKILL.md +29 -0
- package/pipeline/commands/multi-agent/prune-logs/SKILL.md +4 -1
- package/pipeline/commands/multi-agent/review/SKILL.md +4 -3
- package/pipeline/commands/multi-agent/scenario-audit/SKILL.md +4 -2
- package/pipeline/commands/multi-agent/serve/SKILL.md +4 -2
- package/pipeline/commands/multi-agent/stack/SKILL.md +15 -8
- package/pipeline/commands/multi-agent/steer/SKILL.md +10 -9
- package/pipeline/commands/multi-agent/sync/SKILL.md +15 -3
- package/pipeline/commands/multi-agent/test-gen/SKILL.md +29 -3
- package/pipeline/contract/CHANGELOG.md +108 -0
- package/pipeline/contract/README.md +72 -12
- package/pipeline/contract/fixtures/error-invalid-request.json +1 -1
- package/pipeline/contract/fixtures/error-unauthorized.json +1 -1
- package/pipeline/contract/fixtures/error-unsigned.json +1 -1
- package/pipeline/contract/fixtures/launch-plan.json +1 -1
- package/pipeline/contract/fixtures/runs-awaiting-question.json +6 -1
- package/pipeline/contract/fixtures/runs-empty.json +1 -1
- package/pipeline/contract/fixtures/runs-failed.json +6 -1
- package/pipeline/contract/fixtures/runs-old-schema.json +6 -1
- package/pipeline/contract/fixtures/runs-pr-opened-redacted.json +5 -1
- package/pipeline/contract/fixtures/runs-pr-opened.json +6 -1
- package/pipeline/contract/fixtures/runs-running.json +6 -1
- package/pipeline/contract/manifest.json +506 -27
- package/pipeline/contract/types/index.d.ts +829 -4
- package/pipeline/lib/bg-launch.mjs +106 -0
- package/pipeline/lib/code-digest.mjs +49 -0
- package/pipeline/lib/credential-resolve.mjs +42 -2
- package/pipeline/multi-agent-refs/cross-cli-contract.md +5 -4
- package/pipeline/multi-agent-refs/features/office-threat-model.md +109 -0
- package/pipeline/multi-agent-refs/features/unattended-security.md +1 -1
- package/pipeline/multi-agent-refs/help/en.md +3 -0
- package/pipeline/multi-agent-refs/help/tr.md +3 -0
- package/pipeline/multi-agent-refs/phases/modes.md +7 -7
- package/pipeline/multi-agent-refs/phases/phase-5-report.md +16 -10
- package/pipeline/multi-agent-refs/review-standalone-tracker.md +37 -0
- package/pipeline/multi-agent-refs/tracker-contract.md +8 -3
- package/pipeline/office/NOTICE.md +45 -0
- package/pipeline/office/assets/ARCHIVE_BOXES.png +0 -0
- package/pipeline/office/assets/BIN.png +0 -0
- package/pipeline/office/assets/BOOKSHELF.png +0 -0
- package/pipeline/office/assets/CACTUS.png +0 -0
- package/pipeline/office/assets/CLOCK.png +0 -0
- package/pipeline/office/assets/COFFEE.png +0 -0
- package/pipeline/office/assets/COFFEE_MACHINE.png +0 -0
- package/pipeline/office/assets/COFFEE_TABLE.png +0 -0
- package/pipeline/office/assets/COUNTER_SINK.png +0 -0
- package/pipeline/office/assets/CUSHIONED_BENCH.png +0 -0
- package/pipeline/office/assets/CUSHIONED_CHAIR_BACK.png +0 -0
- package/pipeline/office/assets/CUSHIONED_CHAIR_FRONT.png +0 -0
- package/pipeline/office/assets/CUSHIONED_CHAIR_SIDE.png +0 -0
- package/pipeline/office/assets/DESK_FRONT.png +0 -0
- package/pipeline/office/assets/DESK_SIDE.png +0 -0
- package/pipeline/office/assets/DEVICE_STAND.png +0 -0
- package/pipeline/office/assets/DOUBLE_BOOKSHELF.png +0 -0
- package/pipeline/office/assets/FILING_CABINET.png +0 -0
- package/pipeline/office/assets/FRIDGE.png +0 -0
- package/pipeline/office/assets/HANGING_PLANT.png +0 -0
- package/pipeline/office/assets/LARGE_PAINTING.png +0 -0
- package/pipeline/office/assets/LARGE_PLANT.png +0 -0
- package/pipeline/office/assets/MAILBOX.png +0 -0
- package/pipeline/office/assets/MOP_BUCKET.png +0 -0
- package/pipeline/office/assets/PC_BACK.png +0 -0
- package/pipeline/office/assets/PC_FRONT_OFF.png +0 -0
- package/pipeline/office/assets/PC_FRONT_ON_1.png +0 -0
- package/pipeline/office/assets/PC_FRONT_ON_2.png +0 -0
- package/pipeline/office/assets/PC_FRONT_ON_3.png +0 -0
- package/pipeline/office/assets/PC_SIDE.png +0 -0
- package/pipeline/office/assets/PLANT.png +0 -0
- package/pipeline/office/assets/PLANT_2.png +0 -0
- package/pipeline/office/assets/POT.png +0 -0
- package/pipeline/office/assets/PRINTER.png +0 -0
- package/pipeline/office/assets/RECEPTION_DESK.png +0 -0
- package/pipeline/office/assets/SERVER_RACK.png +0 -0
- package/pipeline/office/assets/SERVER_RACK_2.png +0 -0
- package/pipeline/office/assets/SMALL_PAINTING.png +0 -0
- package/pipeline/office/assets/SMALL_PAINTING_2.png +0 -0
- package/pipeline/office/assets/SMALL_TABLE_FRONT.png +0 -0
- package/pipeline/office/assets/SMALL_TABLE_SIDE.png +0 -0
- package/pipeline/office/assets/SOFA_BACK.png +0 -0
- package/pipeline/office/assets/SOFA_FRONT.png +0 -0
- package/pipeline/office/assets/SOFA_SIDE.png +0 -0
- package/pipeline/office/assets/TABLE_FRONT.png +0 -0
- package/pipeline/office/assets/TOILET_STALL.png +0 -0
- package/pipeline/office/assets/VENDING.png +0 -0
- package/pipeline/office/assets/WASHBASIN.png +0 -0
- package/pipeline/office/assets/WATER_COOLER.png +0 -0
- package/pipeline/office/assets/WHITEBOARD.png +0 -0
- package/pipeline/office/assets/WOODEN_BENCH.png +0 -0
- package/pipeline/office/assets/WOODEN_CHAIR_BACK.png +0 -0
- package/pipeline/office/assets/WOODEN_CHAIR_FRONT.png +0 -0
- package/pipeline/office/assets/WOODEN_CHAIR_SIDE.png +0 -0
- package/pipeline/office/assets/char_0.png +0 -0
- package/pipeline/office/assets/char_1.png +0 -0
- package/pipeline/office/assets/char_2.png +0 -0
- package/pipeline/office/assets/char_3.png +0 -0
- package/pipeline/office/assets/char_4.png +0 -0
- package/pipeline/office/assets/char_5.png +0 -0
- package/pipeline/office/assets/floor_0.png +0 -0
- package/pipeline/office/assets/floor_1.png +0 -0
- package/pipeline/office/assets/floor_2.png +0 -0
- package/pipeline/office/assets/floor_3.png +0 -0
- package/pipeline/office/assets/floor_4.png +0 -0
- package/pipeline/office/assets/floor_5.png +0 -0
- package/pipeline/office/assets/floor_6.png +0 -0
- package/pipeline/office/assets/floor_7.png +0 -0
- package/pipeline/office/assets/floor_8.png +0 -0
- package/pipeline/office/assets/wall_0.png +0 -0
- package/pipeline/office/index.html +8431 -0
- package/pipeline/office/toolkit.json +3 -0
- package/pipeline/schemas/activity-chat.schema.json +78 -0
- package/pipeline/schemas/activity-roles.json +48 -0
- package/pipeline/schemas/activity-roles.schema.json +49 -0
- package/pipeline/schemas/activity.schema.json +613 -0
- package/pipeline/schemas/agent-state.schema.json +493 -115
- package/pipeline/schemas/autopilot-on-request.schema.json +33 -0
- package/pipeline/schemas/autopilot-on.schema.json +111 -0
- package/pipeline/schemas/contract-error.schema.json +45 -4
- package/pipeline/schemas/issue-maturity.schema.json +68 -0
- package/pipeline/schemas/job-change.schema.json +72 -0
- package/pipeline/schemas/job-request.schema.json +38 -0
- package/pipeline/schemas/launch.json +4 -4
- package/pipeline/schemas/ledger.schema.json +202 -0
- package/pipeline/schemas/office-credentials.schema.json +71 -0
- package/pipeline/schemas/office-jobs.json +253 -0
- package/pipeline/schemas/office-jobs.schema.json +62 -0
- package/pipeline/schemas/office-memory.schema.json +105 -0
- package/pipeline/schemas/office-settings-request.schema.json +30 -0
- package/pipeline/schemas/office-settings.schema.json +67 -0
- package/pipeline/schemas/permissions.schema.json +47 -0
- package/pipeline/schemas/phases.json +10 -2
- package/pipeline/schemas/phases.schema.json +86 -19
- package/pipeline/schemas/prefs.schema.json +19 -0
- package/pipeline/schemas/prs.schema.json +78 -0
- package/pipeline/schemas/prune-logs-request.schema.json +26 -0
- package/pipeline/schemas/prune-logs.schema.json +52 -0
- package/pipeline/schemas/repo-change.schema.json +54 -0
- package/pipeline/schemas/repo-request.schema.json +29 -0
- package/pipeline/schemas/repos.schema.json +49 -1
- package/pipeline/schemas/review-verdict-request.schema.json +20 -0
- package/pipeline/schemas/review-verdict.schema.json +62 -0
- package/pipeline/schemas/routine-delete.schema.json +59 -0
- package/pipeline/schemas/routines.schema.json +38 -0
- package/pipeline/schemas/run-evidence.schema.json +29 -0
- package/pipeline/schemas/run-results.schema.json +218 -0
- package/pipeline/schemas/runs-index.schema.json +257 -46
- package/pipeline/schemas/service-read.schema.json +58 -0
- package/pipeline/schemas/stack-change.schema.json +82 -0
- package/pipeline/schemas/stack-request.schema.json +33 -0
- package/pipeline/schemas/stack.schema.json +59 -0
- package/pipeline/schemas/steer-request.schema.json +25 -0
- package/pipeline/schemas/steer.schema.json +59 -0
- package/pipeline/schemas/system-status.schema.json +111 -0
- package/pipeline/schemas/tool-call-request.schema.json +19 -0
- package/pipeline/schemas/tool-call.schema.json +47 -0
- package/pipeline/schemas/tools.schema.json +65 -0
- package/pipeline/schemas/unattended-profile.json +7 -2
- package/pipeline/scripts/_active-run.mjs +2 -1
- package/pipeline/scripts/_cost.mjs +66 -1
- package/pipeline/scripts/_office-smoke-lib.sh +115 -0
- package/pipeline/scripts/_spawn-async.mjs +77 -0
- package/pipeline/scripts/_transcript-tail.mjs +274 -0
- package/pipeline/scripts/activity-snapshot.mjs +1223 -0
- package/pipeline/scripts/autopilot-control.mjs +214 -9
- package/pipeline/scripts/autopilot-runner.mjs +7 -5
- package/pipeline/scripts/contract-server.mjs +1853 -197
- package/pipeline/scripts/cost-analyze.mjs +2 -53
- package/pipeline/scripts/cost-table.json +1 -0
- package/pipeline/scripts/gen-mode-dispatch.mjs +3 -1
- package/pipeline/scripts/job-launch.mjs +477 -0
- package/pipeline/scripts/launch-request.mjs +94 -9
- package/pipeline/scripts/office-build.mjs +226 -0
- package/pipeline/scripts/office-control.mjs +410 -0
- package/pipeline/scripts/office-credentials.mjs +106 -0
- package/pipeline/scripts/office-ledger.mjs +409 -0
- package/pipeline/scripts/office-memory.mjs +200 -0
- package/pipeline/scripts/office-prune.mjs +125 -0
- package/pipeline/scripts/office-settings.mjs +145 -0
- package/pipeline/scripts/phase-tracker.sh +12 -5
- package/pipeline/scripts/prune-logs.sh +53 -2
- package/pipeline/scripts/routine-registry.mjs +42 -6
- package/pipeline/scripts/run-results.mjs +373 -0
- package/pipeline/scripts/runs-index.mjs +62 -2
- package/pipeline/scripts/service-read.mjs +244 -0
- package/pipeline/scripts/stack-toggle.mjs +285 -0
- package/pipeline/scripts/steer-run.mjs +97 -0
- package/pipeline/scripts/system-status.mjs +196 -0
- package/pipeline/scripts/task-mirror.mjs +150 -0
- package/pipeline/scripts/task-mirror.sh +20 -0
- package/pipeline/scripts/toolkit-tools.mjs +625 -0
- package/pipeline/scripts/transcript-index.mjs +4 -1
- package/pipeline/scripts/uninstall.mjs +3 -0
- package/pipeline/scripts/work-items.mjs +473 -0
- package/pipeline/skills/.skill-manifest.json +23 -11
- package/pipeline/skills/.skills-index.json +34 -1
- package/pipeline/skills/shared/README.md +6 -3
- package/pipeline/skills/shared/core/multi-agent-autopilot-on/SKILL.md +13 -15
- package/pipeline/skills/shared/core/multi-agent-office-off/SKILL.md +16 -0
- package/pipeline/skills/shared/core/multi-agent-office-on/SKILL.md +21 -0
- package/pipeline/skills/shared/core/multi-agent-office-status/SKILL.md +17 -0
- package/pipeline/skills/shared/core/multi-agent-prune-logs/SKILL.md +4 -1
- package/pipeline/skills/shared/core/multi-agent-review/SKILL.md +19 -0
- package/pipeline/skills/shared/core/multi-agent-scenario-audit/SKILL.md +4 -2
- package/pipeline/skills/shared/core/multi-agent-serve/SKILL.md +4 -2
- package/pipeline/skills/shared/core/multi-agent-stack/SKILL.md +14 -6
- package/pipeline/skills/shared/core/multi-agent-steer/SKILL.md +10 -9
- package/pipeline/skills/shared/core/multi-agent-sync/SKILL.md +3 -3
- package/pipeline/skills/shared/core/multi-agent-test-gen/SKILL.md +26 -3
- package/pipeline/skills/skills-index.md +4 -1
|
@@ -16,8 +16,17 @@
|
|
|
16
16
|
* under `autopilot/abandoned/<task-id>` and kept, a clean one is
|
|
17
17
|
* removed. Without --now nothing in flight is touched.
|
|
18
18
|
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
19
|
+
* on --repo <owner/name>... [--configure-only]
|
|
20
|
+
* the writing half of /multi-agent:autopilot-on, for a selection
|
|
21
|
+
* already made: config.json keeps exactly the selected repos
|
|
22
|
+
* (each must already be configured; adding a new one, with its
|
|
23
|
+
* sources and label, stays the terminal picker), every
|
|
24
|
+
* credential the result needs is probed (autopilot-arming.mjs
|
|
25
|
+
* probeCredentials), and unless --configure-only or a probe
|
|
26
|
+
* fails, the schedule plist is rendered and loaded and the awake
|
|
27
|
+
* agent installed. An empty selection is refused.
|
|
28
|
+
* on-preview --repo <owner/name>...
|
|
29
|
+
* what `on` would keep, drop and probe, writing nothing.
|
|
21
30
|
*
|
|
22
31
|
* Injectable for tests: MA_AUTOPILOT_ROOT, MA_AUTOPILOT_LABEL,
|
|
23
32
|
* MA_AUTOPILOT_PLIST, MA_AP_LAUNCH_AGENTS, MA_AP_LAUNCHCTL_BIN,
|
|
@@ -26,14 +35,28 @@
|
|
|
26
35
|
* Usage:
|
|
27
36
|
* node autopilot-control.mjs status
|
|
28
37
|
* node autopilot-control.mjs off [--now]
|
|
38
|
+
* node autopilot-control.mjs on --repo <owner/name>... [--configure-only]
|
|
39
|
+
* node autopilot-control.mjs on-preview --repo <owner/name>...
|
|
29
40
|
*
|
|
30
|
-
*
|
|
41
|
+
* With MA_CONTROL_JSON_ERRORS=1 a refusal is also printed on stdout as
|
|
42
|
+
* {error: {code, message}}, for contract-server.mjs, which runs this script
|
|
43
|
+
* as a child process.
|
|
44
|
+
*
|
|
45
|
+
* Exit: 0 done, 1 `on` refused, 2 usage, 3 status could not be built.
|
|
31
46
|
*
|
|
32
47
|
* @module pipeline/scripts/autopilot-control
|
|
33
48
|
*/
|
|
34
49
|
|
|
35
50
|
import { execFileSync, spawnSync } from "node:child_process";
|
|
36
|
-
import {
|
|
51
|
+
import {
|
|
52
|
+
chmodSync,
|
|
53
|
+
existsSync,
|
|
54
|
+
mkdirSync,
|
|
55
|
+
readFileSync,
|
|
56
|
+
renameSync,
|
|
57
|
+
rmSync,
|
|
58
|
+
writeFileSync,
|
|
59
|
+
} from "node:fs";
|
|
37
60
|
import { homedir } from "node:os";
|
|
38
61
|
import { dirname, join } from "node:path";
|
|
39
62
|
import { fileURLToPath } from "node:url";
|
|
@@ -45,7 +68,9 @@ import {
|
|
|
45
68
|
sessionIsLive,
|
|
46
69
|
stashWorktree,
|
|
47
70
|
} from "./autopilot-runner.mjs";
|
|
48
|
-
import { removeAwake } from "./autopilot-awake.mjs";
|
|
71
|
+
import { installAwake, removeAwake } from "./autopilot-awake.mjs";
|
|
72
|
+
import { probeCredentials } from "./autopilot-arming.mjs";
|
|
73
|
+
import { configRefusal } from "./_autopilot-config.mjs";
|
|
49
74
|
import { updateState } from "./gate-ledger.mjs";
|
|
50
75
|
import { autopilotRoot } from "../lib/pr-request-location.mjs";
|
|
51
76
|
import { runMain } from "../lib/fatal.mjs";
|
|
@@ -55,7 +80,12 @@ export const CONTRACT_VERSION = "1.0.0";
|
|
|
55
80
|
|
|
56
81
|
const HERE = dirname(fileURLToPath(import.meta.url));
|
|
57
82
|
|
|
58
|
-
export class ControlError extends Error {
|
|
83
|
+
export class ControlError extends Error {
|
|
84
|
+
constructor(message, code = "internal") {
|
|
85
|
+
super(message);
|
|
86
|
+
this.code = code;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
59
89
|
|
|
60
90
|
function readJson(path, fallback) {
|
|
61
91
|
try {
|
|
@@ -196,6 +226,155 @@ export async function autopilotOff({ now = false, env = process.env } = {}) {
|
|
|
196
226
|
return { enabled: false, stoppedInFlight };
|
|
197
227
|
}
|
|
198
228
|
|
|
229
|
+
const SCHEDULE_TEMPLATE = "multi-agent-autopilot.plist.template";
|
|
230
|
+
|
|
231
|
+
/** The installed tree keeps templates/ beside scripts/; the repo keeps it in install/. */
|
|
232
|
+
function scheduleTemplate(env) {
|
|
233
|
+
const home = env.HOME || homedir();
|
|
234
|
+
return (
|
|
235
|
+
[
|
|
236
|
+
env.MA_AP_SCHEDULE_TEMPLATE,
|
|
237
|
+
join(HERE, "..", "templates", SCHEDULE_TEMPLATE),
|
|
238
|
+
join(HERE, "..", "..", "install", "templates", SCHEDULE_TEMPLATE),
|
|
239
|
+
...[".claude", ".copilot", ".codex"].map((d) =>
|
|
240
|
+
join(home, d, "templates", SCHEDULE_TEMPLATE),
|
|
241
|
+
),
|
|
242
|
+
]
|
|
243
|
+
.filter(Boolean)
|
|
244
|
+
.find((p) => existsSync(p)) ?? null
|
|
245
|
+
);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
const xml = (v) => String(v).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
249
|
+
|
|
250
|
+
function which(bin, env) {
|
|
251
|
+
const r = spawnSync("/bin/sh", ["-c", `command -v ${bin}`], {
|
|
252
|
+
env,
|
|
253
|
+
encoding: "utf8",
|
|
254
|
+
timeout: 5000,
|
|
255
|
+
});
|
|
256
|
+
return r.status === 0 ? r.stdout.trim() : "";
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/** The schedule plist, rendered with the seven placeholders autopilot-on fills. */
|
|
260
|
+
export function renderSchedule(config, env = process.env) {
|
|
261
|
+
const tpl = scheduleTemplate(env);
|
|
262
|
+
if (!tpl)
|
|
263
|
+
throw new ControlError(
|
|
264
|
+
`${SCHEDULE_TEMPLATE} is not installed - re-run the installer`,
|
|
265
|
+
"not-installed",
|
|
266
|
+
);
|
|
267
|
+
const home = env.HOME || homedir();
|
|
268
|
+
const values = {
|
|
269
|
+
NODE: env.MA_AP_NODE_BIN || process.execPath,
|
|
270
|
+
TICK: join(HERE, "autopilot-runner.mjs"),
|
|
271
|
+
LABEL: env.MA_AUTOPILOT_LABEL || "com.multi-agent.autopilot",
|
|
272
|
+
ROOT: autopilotRoot(env),
|
|
273
|
+
INTERVAL: Number.isInteger(config?.scanIntervalSeconds) ? config.scanIntervalSeconds : 120,
|
|
274
|
+
HOME: home,
|
|
275
|
+
CLAUDE: env.MA_AP_CLAUDE_BIN || which("claude", env),
|
|
276
|
+
};
|
|
277
|
+
return readFileSync(tpl, "utf8").replace(/\{\{([A-Z]+)\}\}/g, (m, k) =>
|
|
278
|
+
k in values ? xml(values[k]) : m,
|
|
279
|
+
);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
function readPrefs(env) {
|
|
283
|
+
const path =
|
|
284
|
+
env.MULTI_AGENT_PREFS ||
|
|
285
|
+
env.MA_PREFS_FILE ||
|
|
286
|
+
join(env.HOME || homedir(), ".claude", "multi-agent-preferences.json");
|
|
287
|
+
return readJson(path, {});
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* What `on` would do for a selection: the config it writes and the probe of
|
|
292
|
+
* every credential that config needs. Writes nothing.
|
|
293
|
+
*
|
|
294
|
+
* @param {{repos: string[], env?: object}} opts nameWithOwner of each selected repo
|
|
295
|
+
*/
|
|
296
|
+
export function autopilotOnPlan({ repos, env = process.env }) {
|
|
297
|
+
const selected = [...new Set(Array.isArray(repos) ? repos : [])];
|
|
298
|
+
if (!selected.length) {
|
|
299
|
+
throw new ControlError(
|
|
300
|
+
"no repository selected; autopilot is never turned on without one",
|
|
301
|
+
"no-selection",
|
|
302
|
+
);
|
|
303
|
+
}
|
|
304
|
+
const root = autopilotRoot(env);
|
|
305
|
+
const config = readJson(join(root, "config.json"), null);
|
|
306
|
+
const configured = Array.isArray(config?.repos) ? config.repos : [];
|
|
307
|
+
const names = configured.map((r) => r?.nameWithOwner);
|
|
308
|
+
const missing = selected.filter((n) => !names.includes(n));
|
|
309
|
+
if (missing.length) {
|
|
310
|
+
throw new ControlError(
|
|
311
|
+
`not configured yet: ${missing.join(", ")} - add a repository with /multi-agent:autopilot-on in a terminal, which asks its sources and label`,
|
|
312
|
+
"repo-not-configured",
|
|
313
|
+
);
|
|
314
|
+
}
|
|
315
|
+
const next = { ...config, repos: configured.filter((r) => selected.includes(r?.nameWithOwner)) };
|
|
316
|
+
const refusal = configRefusal(next);
|
|
317
|
+
if (refusal) throw new ControlError(refusal, "invalid-config");
|
|
318
|
+
const probe = probeCredentials({ config: next, prefs: readPrefs(env), env });
|
|
319
|
+
return {
|
|
320
|
+
config: next,
|
|
321
|
+
keep: selected,
|
|
322
|
+
drop: names.filter((n) => !selected.includes(n)),
|
|
323
|
+
probe: {
|
|
324
|
+
ready: probe.ready,
|
|
325
|
+
problems: probe.problems.map((p) => p.message),
|
|
326
|
+
warnings: probe.warnings,
|
|
327
|
+
},
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* Turn continuous mode on for a selection of configured repositories.
|
|
333
|
+
*
|
|
334
|
+
* @param {{repos: string[], configureOnly?: boolean, env?: object}} opts
|
|
335
|
+
* @returns {{enabled: boolean, scheduled: boolean, repos: string[], dropped: string[],
|
|
336
|
+
* probe: {ready: boolean, problems: string[], warnings: string[]}, awake: boolean|null}}
|
|
337
|
+
*/
|
|
338
|
+
export function autopilotOn({ repos, configureOnly = false, env = process.env }) {
|
|
339
|
+
const plan = autopilotOnPlan({ repos, env });
|
|
340
|
+
const root = autopilotRoot(env);
|
|
341
|
+
mkdirSync(root, { recursive: true, mode: 0o700 });
|
|
342
|
+
const file = join(root, "config.json");
|
|
343
|
+
const tmp = join(root, `.config.json.${process.pid}`);
|
|
344
|
+
writeFileSync(tmp, JSON.stringify(plan.config, null, 2) + "\n", { mode: 0o600 });
|
|
345
|
+
chmodSync(tmp, 0o600);
|
|
346
|
+
renameSync(tmp, file);
|
|
347
|
+
const base = { repos: plan.keep, dropped: plan.drop, probe: plan.probe };
|
|
348
|
+
if (configureOnly || !plan.probe.ready) {
|
|
349
|
+
return { enabled: false, scheduled: false, awake: null, ...base };
|
|
350
|
+
}
|
|
351
|
+
const label = env.MA_AUTOPILOT_LABEL || "com.multi-agent.autopilot";
|
|
352
|
+
const agents = env.MA_AP_LAUNCH_AGENTS || join(env.HOME || homedir(), "Library", "LaunchAgents");
|
|
353
|
+
const plist = env.MA_AUTOPILOT_PLIST || join(agents, `${label}.plist`);
|
|
354
|
+
mkdirSync(agents, { recursive: true });
|
|
355
|
+
const ptmp = `${plist}.${process.pid}.tmp`;
|
|
356
|
+
writeFileSync(ptmp, renderSchedule(plan.config, env), { mode: 0o644 });
|
|
357
|
+
renameSync(ptmp, plist);
|
|
358
|
+
const loaded =
|
|
359
|
+
launchctl(["bootstrap", `gui/${process.getuid()}`, plist], env) ||
|
|
360
|
+
launchctl(["load", plist], env);
|
|
361
|
+
if (!loaded) {
|
|
362
|
+
throw new ControlError("launchd refused the schedule plist", "launchd-refused");
|
|
363
|
+
}
|
|
364
|
+
const awake = installAwake({ env });
|
|
365
|
+
return { enabled: true, scheduled: true, awake: awake.loaded, ...base };
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
function refuse(err, code) {
|
|
369
|
+
process.stderr.write(`autopilot-control: ${err.message}\n`);
|
|
370
|
+
if (process.env.MA_CONTROL_JSON_ERRORS === "1") {
|
|
371
|
+
process.stdout.write(
|
|
372
|
+
`${JSON.stringify({ error: { code: err.code, message: err.message } })}\n`,
|
|
373
|
+
);
|
|
374
|
+
}
|
|
375
|
+
return code;
|
|
376
|
+
}
|
|
377
|
+
|
|
199
378
|
async function main(argv) {
|
|
200
379
|
const [mode, ...rest] = argv;
|
|
201
380
|
if (mode === "status" && !rest.length) {
|
|
@@ -203,8 +382,8 @@ async function main(argv) {
|
|
|
203
382
|
process.stdout.write(`${JSON.stringify(autopilotStatus())}\n`);
|
|
204
383
|
return 0;
|
|
205
384
|
} catch (err) {
|
|
206
|
-
|
|
207
|
-
return 3;
|
|
385
|
+
if (!(err instanceof ControlError)) throw err;
|
|
386
|
+
return refuse(err, 3);
|
|
208
387
|
}
|
|
209
388
|
}
|
|
210
389
|
if (mode === "off" && rest.every((a) => a === "--now")) {
|
|
@@ -212,7 +391,33 @@ async function main(argv) {
|
|
|
212
391
|
process.stdout.write(`${JSON.stringify(out)}\n`);
|
|
213
392
|
return 0;
|
|
214
393
|
}
|
|
215
|
-
|
|
394
|
+
if (mode === "on" || mode === "on-preview") {
|
|
395
|
+
const repos = [];
|
|
396
|
+
let configureOnly = false;
|
|
397
|
+
for (let i = 0; i < rest.length; i++) {
|
|
398
|
+
if (rest[i] === "--repo" && rest[i + 1]) repos.push(rest[++i]);
|
|
399
|
+
else if (rest[i] === "--configure-only" && mode === "on") configureOnly = true;
|
|
400
|
+
else return usage();
|
|
401
|
+
}
|
|
402
|
+
try {
|
|
403
|
+
const out =
|
|
404
|
+
mode === "on"
|
|
405
|
+
? autopilotOn({ repos, configureOnly })
|
|
406
|
+
: (({ config: _c, ...plan }) => plan)(autopilotOnPlan({ repos }));
|
|
407
|
+
process.stdout.write(`${JSON.stringify(out)}\n`);
|
|
408
|
+
return 0;
|
|
409
|
+
} catch (err) {
|
|
410
|
+
if (!(err instanceof ControlError)) throw err;
|
|
411
|
+
return refuse(err, 1);
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
return usage();
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
function usage() {
|
|
418
|
+
process.stderr.write(
|
|
419
|
+
"usage: autopilot-control.mjs status | off [--now] | on --repo <owner/name>... [--configure-only] | on-preview --repo <owner/name>...\n",
|
|
420
|
+
);
|
|
216
421
|
return 2;
|
|
217
422
|
}
|
|
218
423
|
|
|
@@ -80,6 +80,7 @@ import { homedir } from "node:os";
|
|
|
80
80
|
import { randomUUID } from "node:crypto";
|
|
81
81
|
import { runMain } from "../lib/fatal.mjs";
|
|
82
82
|
import { LIVE_STATUSES, resolveClaudeBin } from "../lib/claude-sessions.mjs";
|
|
83
|
+
import { startBackground } from "../lib/bg-launch.mjs";
|
|
83
84
|
import { invokedDirectly } from "../lib/invoked-directly.mjs";
|
|
84
85
|
import { readRegularJson } from "../lib/regular-file.mjs";
|
|
85
86
|
import { completionGaps } from "../lib/run-completion.mjs";
|
|
@@ -219,7 +220,8 @@ const PROFILE_SCHEMA = (() => {
|
|
|
219
220
|
const PROFILE_ENV = Object.fromEntries((PROFILE_SCHEMA?.env || []).map(([k, v]) => [k, v]));
|
|
220
221
|
const TOOLKIT_URL_POLICY = PROFILE_ENV.MCP_TOOLKIT_URL_POLICY || "strict";
|
|
221
222
|
const TOOLKIT_INDEX_DENY =
|
|
222
|
-
PROFILE_ENV.MCP_TOOLKIT_INDEX_DENY ||
|
|
223
|
+
PROFILE_ENV.MCP_TOOLKIT_INDEX_DENY ||
|
|
224
|
+
"~/.ssh,~/.aws,~/.gnupg,~/Library/Keychains,~/.claude/logs/multi-agent/contract-server,~/.claude/logs/multi-agent/office";
|
|
223
225
|
// The three PreToolUse registrations install/claude.mjs writes for agent-guard.sh,
|
|
224
226
|
// and the tool names each one has to cover. The web entry is checked against
|
|
225
227
|
// WebFetch and the toolkit tools that reach a host: a web tool, agent_run_steps
|
|
@@ -1505,12 +1507,12 @@ function launch(argv, cwd, sessionId, baseSha) {
|
|
|
1505
1507
|
if (!sandbox.ok)
|
|
1506
1508
|
return { status: 1, stderr: `${OUTCOME.BLOCKED_SANDBOX_UNAVAILABLE}: ${sandbox.reason}` };
|
|
1507
1509
|
prepareLaunch(sessionId);
|
|
1508
|
-
return
|
|
1510
|
+
return startBackground({
|
|
1511
|
+
argv,
|
|
1509
1512
|
cwd,
|
|
1510
1513
|
env: launchEnv(sessionId, process.env, { baseSha }),
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
timeout: LAUNCH_TIMEOUT_MS,
|
|
1514
|
+
bin: CLAUDE_BIN,
|
|
1515
|
+
timeoutMs: LAUNCH_TIMEOUT_MS,
|
|
1514
1516
|
});
|
|
1515
1517
|
}
|
|
1516
1518
|
|