@topolo/cli 0.13.17 → 0.13.21
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 +18 -4
- package/agent-packs/README.md +469 -1
- package/agent-packs/coverage_changed.py +1 -1
- package/agent-packs/docs/selected-artifacts.md +47 -0
- package/agent-packs/documentation/cloud-task-state.md +56 -0
- package/agent-packs/domain-contexts/README.md +25 -0
- package/agent-packs/domain-contexts/cicd.json +8 -0
- package/agent-packs/gui/feed-adapter.js +4 -0
- package/agent-packs/gui/index.html +9 -0
- package/agent-packs/gui/studio.css +14 -0
- package/agent-packs/gui/studio.js +121 -0
- package/agent-packs/gui/studio_server.py +355 -0
- package/agent-packs/roles/cicd/CLAUDE.md +24 -0
- package/agent-packs/specializations/cicd--cicd.json +5 -0
- package/agent-packs/specializations/cicd--pipeline-engineering.json +5 -0
- package/agent-packs/specializations/cicd--promotion-deployment.json +5 -0
- package/agent-packs/specializations/cicd--queue-recovery.json +5 -0
- package/agent-packs/specializations/cicd--release-verification.json +5 -0
- package/agent-packs/specializations/cicd--runner-capacity.json +5 -0
- package/agent-packs/specializations/intakes--engineering-02.json +5 -0
- package/agent-packs/specializations/intakes--engineering-03.json +5 -0
- package/agent-packs/specializations/intakes--engineering.json +5 -0
- package/agent-packs/specializations/intakes--operator-01.json +5 -0
- package/agent-packs/specializations/intakes--operator-02.json +5 -0
- package/agent-packs/specializations/operator-skills--audio-and-narration-specialist.json +5 -0
- package/agent-packs/specializations/operator-skills--brand-and-copywriting-specialist.json +5 -0
- package/agent-packs/specializations/operator-skills--capability-research-specialist.json +5 -0
- package/agent-packs/specializations/operator-skills--data-analysis-and-visualization.json +5 -0
- package/agent-packs/specializations/operator-skills--financial-evidence-specialist.json +5 -0
- package/agent-packs/specializations/operator-skills--operating-template-specialist.json +5 -0
- package/agent-packs/specializations/operator-skills--operator-skill-library.json +5 -0
- package/agent-packs/specializations/operator-skills--operator-skills.json +5 -0
- package/agent-packs/specializations/operator-skills--operator-workflow-evaluation.json +5 -0
- package/agent-packs/specializations/operator-skills--privacy-and-agreement-review.json +5 -0
- package/agent-packs/specializations/operator-skills--storytelling-specialist.json +5 -0
- package/agent-packs/specializations/operator-skills--ux-and-accessibility-specialist.json +5 -0
- package/agent-packs/specializations/operator-skills--video-production-specialist.json +5 -0
- package/agent-packs/specializations/operator-skills--visual-design-specialist.json +5 -0
- package/agent-packs/specializations/operator-skills--workflow-integration-specialist.json +5 -0
- package/agent-packs/specializations/operators--access-and-privacy.json +5 -0
- package/agent-packs/specializations/operators--accounting-evidence-review.json +5 -0
- package/agent-packs/specializations/operators--agreement-operations.json +5 -0
- package/agent-packs/specializations/operators--books-operator.json +5 -0
- package/agent-packs/specializations/operators--brand-operator.json +5 -0
- package/agent-packs/specializations/operators--commerce-operations.json +5 -0
- package/agent-packs/specializations/operators--communications-operator.json +5 -0
- package/agent-packs/specializations/operators--content-and-assets-operator.json +5 -0
- package/agent-packs/specializations/operators--customer-operations.json +5 -0
- package/agent-packs/specializations/operators--design-operator.json +5 -0
- package/agent-packs/specializations/operators--design-production-02.json +5 -0
- package/agent-packs/specializations/operators--design-production-03.json +5 -0
- package/agent-packs/specializations/operators--design-production-04.json +5 -0
- package/agent-packs/specializations/operators--device-and-feed-operator.json +5 -0
- package/agent-packs/specializations/operators--director-operator.json +5 -0
- package/agent-packs/specializations/operators--experience-review.json +5 -0
- package/agent-packs/specializations/operators--growth-operations.json +5 -0
- package/agent-packs/specializations/operators--learn-operator.json +5 -0
- package/agent-packs/specializations/operators--localization-operator.json +5 -0
- package/agent-packs/specializations/operators--operators.json +5 -0
- package/agent-packs/specializations/operators--pay-operator.json +5 -0
- package/agent-packs/specializations/operators--research-and-intelligence.json +5 -0
- package/agent-packs/specializations/operators--roadmap-operator.json +5 -0
- package/agent-packs/specializations/operators--social-content-operator.json +5 -0
- package/agent-packs/specializations/operators--talent-operations.json +5 -0
- package/agent-packs/specializations/operators--web-operator.json +5 -0
- package/agent-packs/specializations/operators--workflow-operator.json +5 -0
- package/agent-packs/specializations/specialists--adversarial-qa.json +5 -0
- package/agent-packs/specializations/specialists--backlog-dispatch.json +5 -0
- package/agent-packs/specializations/specialists--build-01.json +5 -0
- package/agent-packs/specializations/specialists--build-02.json +5 -0
- package/agent-packs/specializations/specialists--build-03.json +5 -0
- package/agent-packs/specializations/specialists--developer-environment-steward.json +5 -0
- package/agent-packs/specializations/specialists--documentation-knowledge-steward.json +5 -0
- package/agent-packs/specializations/specialists--execution-readiness.json +5 -0
- package/agent-packs/specializations/specialists--incident-triage.json +5 -0
- package/agent-packs/specializations/specialists--platform-package-steward.json +5 -0
- package/agent-packs/specializations/specialists--product-direction-01.json +5 -0
- package/agent-packs/specializations/specialists--product-direction-02.json +5 -0
- package/agent-packs/specializations/specialists--release-recovery.json +5 -0
- package/agent-packs/specializations/specialists--release-watch.json +5 -0
- package/agent-packs/specializations/specialists--roadmap-planning-01.json +5 -0
- package/agent-packs/specializations/specialists--roadmap-planning-02.json +5 -0
- package/agent-packs/specializations/specialists--security-permissions-review.json +5 -0
- package/agent-packs/specializations/specialists--system-architecture-01.json +5 -0
- package/agent-packs/specializations/specialists--system-architecture-02.json +5 -0
- package/agent-packs/specializations/specialists--translation.json +5 -0
- package/agent-packs/specializations/specialists--ux-consistency-review.json +5 -0
- package/agent-packs/specializations/specialists--workflow-improvement.json +5 -0
- package/agent-packs/specializations/specialists--worktree-retirement.json +5 -0
- package/agent-packs/src/topolo_agent_pack/cicd.py +101 -0
- package/agent-packs/src/topolo_agent_pack/claude.py +22 -7
- package/agent-packs/src/topolo_agent_pack/claude_activation.py +157 -0
- package/agent-packs/src/topolo_agent_pack/claude_completion_recovery.py +216 -0
- package/agent-packs/src/topolo_agent_pack/claude_desktop.py +181 -0
- package/agent-packs/src/topolo_agent_pack/claude_dispatch.py +240 -0
- package/agent-packs/src/topolo_agent_pack/claude_prompt.py +74 -0
- package/agent-packs/src/topolo_agent_pack/claude_runner.py +207 -0
- package/agent-packs/src/topolo_agent_pack/claude_service.py +397 -0
- package/agent-packs/src/topolo_agent_pack/cli.py +188 -18
- package/agent-packs/src/topolo_agent_pack/closed_ledger_fence.py +118 -0
- package/agent-packs/src/topolo_agent_pack/cloud_attach.py +128 -0
- package/agent-packs/src/topolo_agent_pack/cloud_cutover.py +134 -0
- package/agent-packs/src/topolo_agent_pack/cloud_delta.py +162 -0
- package/agent-packs/src/topolo_agent_pack/cloud_migrate.py +142 -0
- package/agent-packs/src/topolo_agent_pack/cloud_recovery.py +42 -0
- package/agent-packs/src/topolo_agent_pack/cloud_work.py +126 -0
- package/agent-packs/src/topolo_agent_pack/coordinator_activity.py +27 -0
- package/agent-packs/src/topolo_agent_pack/coordinator_context.py +91 -0
- package/agent-packs/src/topolo_agent_pack/coordinator_refresh.py +116 -0
- package/agent-packs/src/topolo_agent_pack/desktop_acceptance.py +45 -0
- package/agent-packs/src/topolo_agent_pack/desktop_launcher.py +114 -0
- package/agent-packs/src/topolo_agent_pack/domain_context.py +38 -0
- package/agent-packs/src/topolo_agent_pack/execution.py +104 -7
- package/agent-packs/src/topolo_agent_pack/fresh.py +1 -1
- package/agent-packs/src/topolo_agent_pack/frontdoor.py +23 -0
- package/agent-packs/src/topolo_agent_pack/linked_task_context.py +80 -0
- package/agent-packs/src/topolo_agent_pack/live.py +285 -18
- package/agent-packs/src/topolo_agent_pack/native.py +160 -4
- package/agent-packs/src/topolo_agent_pack/policy.py +8 -0
- package/agent-packs/src/topolo_agent_pack/review_context.py +33 -0
- package/agent-packs/src/topolo_agent_pack/role_catalog.py +137 -0
- package/agent-packs/src/topolo_agent_pack/role_context.py +63 -0
- package/agent-packs/src/topolo_agent_pack/role_refresh.py +50 -0
- package/agent-packs/src/topolo_agent_pack/task_models.py +11 -7
- package/agent-packs/src/topolo_agent_pack/throughput.py +149 -0
- package/agent-packs/src/topolo_agent_pack/topolo.py +7 -1
- package/agent-packs/src/topolo_agent_pack/work.py +322 -0
- package/agent-packs/src/topolo_agent_pack/work_adoption.py +122 -0
- package/agent-packs/src/topolo_agent_pack/work_artifacts.py +84 -0
- package/agent-packs/src/topolo_agent_pack/work_context.py +77 -0
- package/agent-packs/src/topolo_agent_pack/work_gui.py +51 -0
- package/agent-packs/src/topolo_agent_pack/work_native.py +414 -0
- package/agent-packs/src/topolo_agent_pack/work_wait.py +151 -0
- package/agent-packs/templates/topolo-coordinators/AGENTS.md +7 -0
- package/agent-packs/templates/topolo-coordinators/agents/intakes/cicd/AGENTS.md +6 -0
- package/agent-packs/templates/topolo-coordinators/agents/intakes/cicd/config.json +4 -0
- package/agent-packs/templates/topolo-coordinators/agents/intakes/engineering/AGENTS.md +3 -0
- package/agent-packs/templates/topolo-coordinators/agents/intakes/engineering/config.json +4 -0
- package/agent-packs/templates/topolo-coordinators/agents/intakes/operator-01/AGENTS.md +3 -0
- package/agent-packs/templates/topolo-coordinators/agents/intakes/operator-01/config.json +4 -0
- package/agent-packs/templates/topolo-coordinators/pack.json +56 -0
- package/dist/chunk-NYDIAYVY.js +144 -0
- package/dist/{claude-pack-backend-YEVPAOID.js → chunk-UY5LEU5C.js} +1659 -21
- package/dist/claude-desktop-profile-JCQKSY3K.js +13 -0
- package/dist/claude-pack-activation-Q25MCEPU.js +506 -0
- package/dist/claude-pack-backend-HUTKGIDS.js +90 -0
- package/dist/index.js +408 -164
- package/package.json +8 -6
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
acknowledgeClaudeDesktopProfile,
|
|
4
|
+
launchClaudeDesktopProfile,
|
|
5
|
+
readClaudeDesktopProfile,
|
|
6
|
+
verifyClaudeDesktopProfileNative
|
|
7
|
+
} from "./chunk-NYDIAYVY.js";
|
|
8
|
+
export {
|
|
9
|
+
acknowledgeClaudeDesktopProfile,
|
|
10
|
+
launchClaudeDesktopProfile,
|
|
11
|
+
readClaudeDesktopProfile,
|
|
12
|
+
verifyClaudeDesktopProfileNative
|
|
13
|
+
};
|
|
@@ -0,0 +1,506 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
bindClaudeFrontdoorHooks,
|
|
4
|
+
discoverClaudeNativeCapabilities,
|
|
5
|
+
evaluateClaudeMain,
|
|
6
|
+
lockClaudeLibrary,
|
|
7
|
+
materializeClaudeHookAdapter,
|
|
8
|
+
nativeClaudeDesktopControlExpression,
|
|
9
|
+
observeClaudeFrontdoors,
|
|
10
|
+
qualifyClaudePython,
|
|
11
|
+
readClaudeTaskConnection,
|
|
12
|
+
validateInspectorEndpoint
|
|
13
|
+
} from "./chunk-UY5LEU5C.js";
|
|
14
|
+
import {
|
|
15
|
+
acknowledgeClaudeDesktopProfile,
|
|
16
|
+
launchClaudeDesktopProfile,
|
|
17
|
+
readClaudeDesktopProfile,
|
|
18
|
+
verifyClaudeDesktopProfileNative
|
|
19
|
+
} from "./chunk-NYDIAYVY.js";
|
|
20
|
+
|
|
21
|
+
// src/lib/claude-desktop-menu.ts
|
|
22
|
+
import { spawnSync } from "child_process";
|
|
23
|
+
var claudeInspectorMenuScript = `on run argv
|
|
24
|
+
set targetPID to item 1 of argv as integer
|
|
25
|
+
set desiredEnabled to item 2 of argv is "enable"
|
|
26
|
+
tell application "System Events"
|
|
27
|
+
repeat 100 times
|
|
28
|
+
set targets to application processes whose unix id is targetPID
|
|
29
|
+
if (count of targets) > 1 then error "Native process identity is ambiguous"
|
|
30
|
+
if (count of targets) is 1 then exit repeat
|
|
31
|
+
delay 0.1
|
|
32
|
+
end repeat
|
|
33
|
+
if (count of targets) is not 1 then error "Exact native process is unavailable"
|
|
34
|
+
set targetProcess to item 1 of targets
|
|
35
|
+
if name of targetProcess is not "Claude" then error "Native application identity differs"
|
|
36
|
+
tell targetProcess
|
|
37
|
+
set ready to false
|
|
38
|
+
repeat 100 times
|
|
39
|
+
set matches to {}
|
|
40
|
+
set parentNames to {}
|
|
41
|
+
try
|
|
42
|
+
repeat with topItem in menu bar items of menu bar 1
|
|
43
|
+
try
|
|
44
|
+
set candidates to menu items of menu 1 of topItem whose name is "Enable Main Process Debugger"
|
|
45
|
+
repeat with candidate in candidates
|
|
46
|
+
set end of matches to contents of candidate
|
|
47
|
+
set end of parentNames to name of topItem
|
|
48
|
+
end repeat
|
|
49
|
+
end try
|
|
50
|
+
end repeat
|
|
51
|
+
end try
|
|
52
|
+
if (count of matches) > 1 then error "Unique native main-debugger capability is unavailable"
|
|
53
|
+
if (count of matches) is 1 then
|
|
54
|
+
if enabled of item 1 of matches is true then
|
|
55
|
+
set ready to true
|
|
56
|
+
exit repeat
|
|
57
|
+
end if
|
|
58
|
+
end if
|
|
59
|
+
delay 0.1
|
|
60
|
+
end repeat
|
|
61
|
+
if ready is not true then error "Unique enabled native main-debugger capability did not become ready"
|
|
62
|
+
set exactItem to item 1 of matches
|
|
63
|
+
if enabled of exactItem is not true then error "Native main-debugger capability is disabled"
|
|
64
|
+
set mark to value of attribute "AXMenuItemMarkChar" of exactItem
|
|
65
|
+
set isChecked to mark is not missing value and mark is not ""
|
|
66
|
+
if isChecked is desiredEnabled then error "Native inspector checkbox is already in the requested state; ownership must be reconciled"
|
|
67
|
+
set parentName to item 1 of parentNames
|
|
68
|
+
set frontmost to true
|
|
69
|
+
set parents to menu bar items of menu bar 1 whose name is parentName
|
|
70
|
+
if (count of parents) is not 1 then error "Native debugger parent changed"
|
|
71
|
+
if (count of (value of attribute "AXVisibleChildren" of menu 1 of item 1 of parents)) is 0 then perform action "AXPress" of item 1 of parents
|
|
72
|
+
set opened to false
|
|
73
|
+
repeat 40 times
|
|
74
|
+
set parents to menu bar items of menu bar 1 whose name is parentName
|
|
75
|
+
if (count of parents) is not 1 then error "Native debugger parent changed"
|
|
76
|
+
if (count of (value of attribute "AXVisibleChildren" of menu 1 of item 1 of parents)) > 0 then
|
|
77
|
+
set opened to true
|
|
78
|
+
exit repeat
|
|
79
|
+
end if
|
|
80
|
+
delay 0.05
|
|
81
|
+
end repeat
|
|
82
|
+
if opened is not true then error "Native debugger menu did not open"
|
|
83
|
+
set freshMatches to menu items of menu 1 of item 1 of parents whose name is "Enable Main Process Debugger"
|
|
84
|
+
if (count of freshMatches) is not 1 then error "Unique open debugger capability changed"
|
|
85
|
+
set exactItem to item 1 of freshMatches
|
|
86
|
+
if enabled of exactItem is not true then error "Native main-debugger capability is disabled"
|
|
87
|
+
set mark to value of attribute "AXMenuItemMarkChar" of exactItem
|
|
88
|
+
set isChecked to mark is not missing value and mark is not ""
|
|
89
|
+
if isChecked is desiredEnabled then error "Inspector ownership changed"
|
|
90
|
+
if "AXPress" is not in (name of actions of exactItem) then error "Native debugger press capability unavailable"
|
|
91
|
+
perform action "AXPress" of exactItem
|
|
92
|
+
repeat 40 times
|
|
93
|
+
set parents to menu bar items of menu bar 1 whose name is parentName
|
|
94
|
+
if (count of parents) is not 1 then error "Native debugger parent changed"
|
|
95
|
+
set freshMatches to menu items of menu 1 of item 1 of parents whose name is "Enable Main Process Debugger"
|
|
96
|
+
if (count of freshMatches) is not 1 then error "Native debugger checkbox changed"
|
|
97
|
+
set mark to value of attribute "AXMenuItemMarkChar" of item 1 of freshMatches
|
|
98
|
+
set isChecked to mark is not missing value and mark is not ""
|
|
99
|
+
if isChecked is desiredEnabled then
|
|
100
|
+
if desiredEnabled then return "owned-main-debugger-click-acknowledged"
|
|
101
|
+
return "owned-main-debugger-closed-acknowledged"
|
|
102
|
+
end if
|
|
103
|
+
delay 0.05
|
|
104
|
+
end repeat
|
|
105
|
+
error "Native debugger checkbox did not acknowledge requested state"
|
|
106
|
+
end tell
|
|
107
|
+
end tell
|
|
108
|
+
end run`;
|
|
109
|
+
function setInspectorMenu(pid, enabled, run) {
|
|
110
|
+
if (!Number.isSafeInteger(pid) || pid <= 1) throw Error("Exact owned Desktop PID required.");
|
|
111
|
+
const result = run("/usr/bin/osascript", ["-e", claudeInspectorMenuScript, String(pid), enabled ? "enable" : "disable"], { encoding: "utf8", timeout: 3e4, maxBuffer: 16384 });
|
|
112
|
+
if (result.error || result.status !== 0 || result.stdout.trim() !== (enabled ? "owned-main-debugger-click-acknowledged" : "owned-main-debugger-closed-acknowledged")) throw Error("Approved native main-debugger menu activation unavailable; no signal or fallback used.");
|
|
113
|
+
return { state: enabled ? "owned-native-debugger-menu-acknowledged" : "owned-native-debugger-menu-disabled", pid };
|
|
114
|
+
}
|
|
115
|
+
function enableClaudeInspectorMenu(pid, run = spawnSync) {
|
|
116
|
+
return setInspectorMenu(pid, true, run);
|
|
117
|
+
}
|
|
118
|
+
function disableClaudeInspectorMenu(pid, run = spawnSync) {
|
|
119
|
+
return setInspectorMenu(pid, false, run);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// src/lib/claude-pack-activation.ts
|
|
123
|
+
import * as fs from "fs";
|
|
124
|
+
import * as path from "path";
|
|
125
|
+
import { createHash, randomBytes, randomUUID } from "crypto";
|
|
126
|
+
import { spawnSync as spawnSync2 } from "child_process";
|
|
127
|
+
import { fileURLToPath } from "url";
|
|
128
|
+
import { createServer } from "net";
|
|
129
|
+
var digest = (data) => createHash("sha256").update(data).digest("hex");
|
|
130
|
+
function owned(file) {
|
|
131
|
+
const s = fs.lstatSync(file);
|
|
132
|
+
if (!s.isFile() || s.isSymbolicLink() || s.nlink !== 1 || s.uid !== process.getuid?.() || s.mode & 18 || s.size > 1048576 || fs.realpathSync(file) !== file) throw Error("Exact owned bounded activation record required.");
|
|
133
|
+
return fs.readFileSync(file, "utf8");
|
|
134
|
+
}
|
|
135
|
+
function put(file, text, exclusive = false) {
|
|
136
|
+
if (exclusive) {
|
|
137
|
+
const fd = fs.openSync(file, "wx", 384);
|
|
138
|
+
try {
|
|
139
|
+
fs.writeFileSync(fd, text);
|
|
140
|
+
fs.fsyncSync(fd);
|
|
141
|
+
} finally {
|
|
142
|
+
fs.closeSync(fd);
|
|
143
|
+
}
|
|
144
|
+
} else {
|
|
145
|
+
const temporary = file + "." + randomUUID() + ".tmp";
|
|
146
|
+
put(temporary, text, true);
|
|
147
|
+
fs.renameSync(temporary, file);
|
|
148
|
+
}
|
|
149
|
+
const d = fs.openSync(path.dirname(file), "r");
|
|
150
|
+
try {
|
|
151
|
+
fs.fsyncSync(d);
|
|
152
|
+
} finally {
|
|
153
|
+
fs.closeSync(d);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
function json(file) {
|
|
157
|
+
return JSON.parse(owned(file));
|
|
158
|
+
}
|
|
159
|
+
function append(file, row) {
|
|
160
|
+
const fd = fs.openSync(file, "a", 384);
|
|
161
|
+
try {
|
|
162
|
+
fs.writeFileSync(fd, JSON.stringify(row) + "\n");
|
|
163
|
+
fs.fsyncSync(fd);
|
|
164
|
+
} finally {
|
|
165
|
+
fs.closeSync(fd);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
function cloud(root, params) {
|
|
169
|
+
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
170
|
+
const adapter = [path.resolve(here, "../agent-packs/topolo-agent-pack"), path.resolve(here, "../../agent-packs/topolo-agent-pack")].find(fs.existsSync);
|
|
171
|
+
if (!adapter) throw Error("Bundled activation helper unavailable.");
|
|
172
|
+
const frozen = materializeClaudeHookAdapter(root, adapter), python = qualifyClaudePython();
|
|
173
|
+
const script = `import sys,json,pathlib,hashlib
|
|
174
|
+
p=json.loads(sys.stdin.read(65537))
|
|
175
|
+
assert hashlib.sha256(pathlib.Path(sys.executable).read_bytes()).hexdigest()==p['python']['sha256']
|
|
176
|
+
for name,digest in p['files'].items():
|
|
177
|
+
assert hashlib.sha256(pathlib.Path(name).read_bytes()).hexdigest()==digest
|
|
178
|
+
sys.path.insert(0,p['source'])
|
|
179
|
+
from topolo_agent_pack.claude_activation import observe
|
|
180
|
+
r=observe(p['root'],**p['params'])
|
|
181
|
+
print(json.dumps(r))`;
|
|
182
|
+
const result = spawnSync2(python.executable, ["-I", "-B", "-c", script], { encoding: "utf8", input: JSON.stringify({ root, source: frozen.source, files: frozen.files, python, params }), timeout: 18e4, maxBuffer: 1048576 });
|
|
183
|
+
if (result.error || result.status !== 0) throw Error("Cloud activation/closure proof failed; preserve native ownership.");
|
|
184
|
+
return JSON.parse(result.stdout);
|
|
185
|
+
}
|
|
186
|
+
function observer(pid, profile, members, journal, warm = false) {
|
|
187
|
+
return `(async()=>{if(process.pid!==${pid})throw Error('Desktop PID changed');const load=process.getBuiltinModule('module').createRequire(process.resourcesPath+'/app.asar/package.json'),caps=(${discoverClaudeNativeCapabilities.toString()})(load,process.resourcesPath);const check=()=>{caps.assertCurrent();(${verifyClaudeDesktopProfileNative.toString()})(${JSON.stringify(profile)},process.pid,load('electron').app,caps.manager,process.env);};check();const fs=load('node:fs');const checkpoint=row=>{const fd=fs.openSync(${JSON.stringify(journal)},'a',384);try{fs.writeSync(fd,JSON.stringify(row)+'\\n');fs.fsyncSync(fd);}finally{fs.closeSync(fd);}};return (${observeClaudeFrontdoors.toString()})(caps.manager,${JSON.stringify(members)},check,${warm},checkpoint);})()`;
|
|
188
|
+
}
|
|
189
|
+
function checkResult(result, members) {
|
|
190
|
+
if (result?.state !== "installed-frontdoors-verified" || result.nativeInputsSent !== false || result.members?.length !== 3 || result.members.some((r, i) => ["key", "sessionId", "cwd", "title", "model", "effort", "permissionMode"].some((k) => r[k] !== members[i][k]) || !r.pinned || !/^[-a-f0-9]{36}$/.test(r.cliSessionId))) throw Error("Exact installed native front-door acknowledgement differs.");
|
|
191
|
+
}
|
|
192
|
+
function sameNativeConnection(left, right) {
|
|
193
|
+
const keys = ["identity", "pid", "runtime", "secretPath", "socket"];
|
|
194
|
+
return left && Object.keys(left).sort().join() === keys.join() && keys.every((k) => left[k] === right[k]);
|
|
195
|
+
}
|
|
196
|
+
function installedBindingMatches(binding, connection, pid, members) {
|
|
197
|
+
const channels = { engineering: "engineering", "operator-01": "operations", cicd: "cicd" };
|
|
198
|
+
return sameNativeConnection(binding.nativeConnection, connection) && binding.desktopPid === pid && binding.roles?.length === 3 && binding.roles.every((r, i) => Object.keys(r).sort().join() === "channel,cwd,sessionId" && r.channel === channels[members[i].key.split("/").at(-1)] && r.cwd === members[i].cwd);
|
|
199
|
+
}
|
|
200
|
+
function immutable(root, pack, policy) {
|
|
201
|
+
if (["startup-operation.json", "native-operation.json"].some((n) => fs.existsSync(path.join(root, "claude", n)))) throw Error("Unresolved native startup retains custody.");
|
|
202
|
+
const profile = readClaudeDesktopProfile(root);
|
|
203
|
+
if (!profile || profile.profile.pack !== pack) throw Error("Owned signed-in Desktop profile required.");
|
|
204
|
+
const p = json(policy);
|
|
205
|
+
if (p.pack !== pack || p.installationConsent?.profile !== "full-access" || profile.profile.consentDigest !== digest(JSON.stringify(p.installationConsent))) throw Error("Current activation consent differs.");
|
|
206
|
+
return { profile, policy: p };
|
|
207
|
+
}
|
|
208
|
+
async function finalizeClaudePack(options, evaluate = evaluateClaudeMain, prove = cloud, finishInspector = closeClaudeInspector, bind = bindClaudeFrontdoorHooks) {
|
|
209
|
+
const root = fs.realpathSync(options.store), release = await lockClaudeLibrary(root);
|
|
210
|
+
try {
|
|
211
|
+
const { profile } = immutable(root, options.pack, options.policy);
|
|
212
|
+
if (profile.pid !== options.pid) throw Error("Current native profile PID differs.");
|
|
213
|
+
if (!/^[a-f0-9-]{36}$/.test(options.startupOperation) || !/^[a-f0-9-]{36}$/.test(options.qualificationTask)) throw Error("Exact startup and qualification identities required.");
|
|
214
|
+
const file = path.join(root, "claude/activation.json"), pending = path.join(root, "claude/activation-operation.json");
|
|
215
|
+
const prior = fs.existsSync(pending) ? json(pending) : null;
|
|
216
|
+
if (fs.existsSync(file) && !prior) {
|
|
217
|
+
const { receipt: receipt2 } = validateActivation(root, options.pack, options.policy);
|
|
218
|
+
if (receipt2.startupOperation !== options.startupOperation || receipt2.qualificationTask !== options.qualificationTask || receipt2.pid !== options.pid) throw Error("Existing activation belongs to another operation.");
|
|
219
|
+
const proof2 = prove(root, { qualification_task: options.qualificationTask, start_service: true, native_proof: true });
|
|
220
|
+
checkResult(proof2.native, receipt2.members);
|
|
221
|
+
return { state: "claude-pack-activated", alreadyActivated: true, cloudReady: true, nativeInputsSent: false, coordinators: 3 };
|
|
222
|
+
}
|
|
223
|
+
if (prior && (prior.startupOperation !== options.startupOperation || prior.qualificationTask !== options.qualificationTask)) throw Error("Another activation operation retains custody.");
|
|
224
|
+
const startup = path.join(root, "claude/journals", options.startupOperation + ".jsonl"), text = owned(startup), rows = text.trimEnd().split("\n").map((r) => JSON.parse(r)), plan = rows[0];
|
|
225
|
+
if (!text.endsWith("\n") || plan.kind !== "startup-plan" || plan.operationId !== options.startupOperation || plan.libraryRoot !== root || plan.members?.length !== 3 || !rows.some((r) => r.kind === "frontdoor-hooks-prepared") || plan.previousCoordinators?.length && !rows.some((r) => r.kind === "previous-coordinators-retired" && JSON.stringify(r.retired) === JSON.stringify(plan.previousCoordinators.map((r2) => r2.sessionId)))) throw Error("Completed original startup acknowledgement required.");
|
|
226
|
+
const statePath = path.join(root, "claude/state.json"), state = json(statePath), members = plan.members;
|
|
227
|
+
const selected = state.agents.filter((r) => r.pack === options.pack);
|
|
228
|
+
if (selected.length !== 3 || selected.some((r, i) => r.key !== members[i].key || r.desktopSessionId !== members[i].sessionId || r.sourceRevision !== plan.sourceRevision || r.workspace !== members[i].cwd)) throw Error("Original prepared coordinator identity differs.");
|
|
229
|
+
for (const [f, hash] of Object.entries(plan.context)) {
|
|
230
|
+
if (f === statePath && prior) continue;
|
|
231
|
+
if (digest(owned(f)) !== hash) throw Error("Original startup context changed.");
|
|
232
|
+
}
|
|
233
|
+
const connection = readClaudeTaskConnection(path.join(root, "claude/desktop-task-connection.json"), options.pid), binding = json(path.join(root, "work/claude-frontdoors.json"));
|
|
234
|
+
if (!installedBindingMatches(binding, connection, options.pid, members)) throw Error("Current installed front-door binding differs.");
|
|
235
|
+
const controlJournal = path.join(root, "claude/journals", options.startupOperation + ".activation.jsonl");
|
|
236
|
+
if (!fs.existsSync(controlJournal)) put(controlJournal, JSON.stringify({ kind: "activation-native-observation", startupOperation: options.startupOperation }) + "\n", true);
|
|
237
|
+
const boundMembers = members.map((m, i) => ({ ...m, cliSessionId: binding.roles[i].sessionId }));
|
|
238
|
+
const observed = await evaluate(options.endpoint, observer(options.pid, profile.profile, boundMembers, controlJournal, true));
|
|
239
|
+
checkResult(observed, members);
|
|
240
|
+
if (observed.members.some((m, i) => m.cliSessionId !== binding.roles[i].sessionId)) throw Error("Native SDK and installed hook identity differ.");
|
|
241
|
+
const proof = prove(root, { qualification_task: options.qualificationTask, start_service: true });
|
|
242
|
+
if (proof.activated !== true || proof.service?.desktopPid !== options.pid || proof.qualificationTaskId !== options.qualificationTask) throw Error("Live cloud qualification differs.");
|
|
243
|
+
const next = structuredClone(state), changes = [];
|
|
244
|
+
for (let i = 0; i < selected.length; i++) {
|
|
245
|
+
const before = selected[i], native = observed.members[i], record = next.agents.find((r) => r.key === before.key);
|
|
246
|
+
const key = members[i].cwd.replace(/[^a-zA-Z0-9]/g, "-");
|
|
247
|
+
if (key.length > 200) throw Error("Native SDK path codec exceeds qualified bounds.");
|
|
248
|
+
const transcript = path.join(profile.profile.codeConfig, "projects", key, native.cliSessionId + ".jsonl");
|
|
249
|
+
owned(transcript);
|
|
250
|
+
const identity = path.join(root, "claude/identities", digest(before.key) + ".json"), oldIdentity = owned(identity);
|
|
251
|
+
if (!prior && JSON.stringify(JSON.parse(oldIdentity)) !== JSON.stringify(before)) throw Error("Prepared identity and state differ.");
|
|
252
|
+
Object.assign(record, { importState: "desktop-adopted", cliSessionId: native.cliSessionId, transcript });
|
|
253
|
+
changes.push({ file: identity, before: oldIdentity, after: JSON.stringify(record) + "\n" });
|
|
254
|
+
}
|
|
255
|
+
changes.push({ file: statePath, before: owned(statePath), after: JSON.stringify(next) + "\n" });
|
|
256
|
+
const active = { operationId: options.startupOperation, pack: options.pack, keys: members.map((m) => m.key), sourceRevision: plan.sourceRevision };
|
|
257
|
+
const activePath = path.join(root, "claude/active.json");
|
|
258
|
+
changes.push({ file: activePath, before: fs.existsSync(activePath) ? owned(activePath) : null, after: JSON.stringify(active) + "\n" });
|
|
259
|
+
const receipt = { format: "topolo-claude-pack-activation-v1", root, pack: options.pack, startupOperation: options.startupOperation, startupDigest: digest(text), sourceRevision: plan.sourceRevision, policyDigest: digest(owned(options.policy)), members: observed.members, qualificationTask: options.qualificationTask, cloud: proof.binding, profile: profile.profile, pid: options.pid, connection, cloudReady: true, nativeInputsSent: false, menuAutomationConsent: { approved: options.allowNativeDebuggerMenu === true, principalId: JSON.parse(owned(options.policy)).installationConsent.principalId, scope: "owned-claude-main-debugger-checkbox-only" } };
|
|
260
|
+
const operation = prior ?? { format: "topolo-claude-activation-operation-v1", startupOperation: options.startupOperation, qualificationTask: options.qualificationTask, state: "prepared", changes, receipt };
|
|
261
|
+
if (prior && JSON.stringify(prior.receipt) !== JSON.stringify(receipt)) throw Error("Pending activation proof differs; reconcile without adoption.");
|
|
262
|
+
if (!prior) put(pending, JSON.stringify(operation) + "\n", true);
|
|
263
|
+
for (const change of operation.changes) {
|
|
264
|
+
const current = fs.existsSync(change.file) ? owned(change.file) : null;
|
|
265
|
+
if (current !== change.before && current !== change.after) throw Error("Activation metadata changed outside its journal.");
|
|
266
|
+
if (current !== change.after) put(change.file, change.after);
|
|
267
|
+
}
|
|
268
|
+
const control = await evaluate(options.endpoint, nativeClaudeDesktopControlExpression(options.pid, root, connection, connection.secretPath, controlJournal, { existing: connection, coordinators: members, frontdoors: observed.members }));
|
|
269
|
+
if (!["native-desktop-task-control-installed", "native-desktop-task-control-reused"].includes(control.state)) throw Error("Qualified closed native control was not acknowledged.");
|
|
270
|
+
const hookInput = path.join(root, "claude/journals", options.startupOperation + ".activation-frontdoors.json");
|
|
271
|
+
bind(root, options.pid, observed.members.map((m) => ({ key: m.key, cwd: m.cwd, sessionId: m.cliSessionId })), hookInput, void 0, connection, fs.existsSync(hookInput));
|
|
272
|
+
for (const [file2, hash] of Object.entries(plan.context)) if (file2 !== statePath && digest(owned(file2)) !== hash) throw Error("Selected role context changed during hook migration.");
|
|
273
|
+
put(file, JSON.stringify(receipt) + "\n");
|
|
274
|
+
fs.unlinkSync(pending);
|
|
275
|
+
return { state: "claude-pack-activated", pack: options.pack, coordinators: 3, cloudReady: true, nativeInputsSent: false, qualificationTask: options.qualificationTask };
|
|
276
|
+
} finally {
|
|
277
|
+
try {
|
|
278
|
+
const p = readClaudeDesktopProfile(root);
|
|
279
|
+
if (p?.pid === options.pid) await finishInspector(options.endpoint, options.pid, p.profile, evaluate);
|
|
280
|
+
} finally {
|
|
281
|
+
await release();
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
async function upgradeClaudeControl(options, evaluate = evaluateClaudeMain, finishInspector = closeClaudeInspector) {
|
|
286
|
+
const root = fs.realpathSync(options.store);
|
|
287
|
+
let release;
|
|
288
|
+
try {
|
|
289
|
+
release = await lockClaudeLibrary(root, true);
|
|
290
|
+
const { profile } = immutable(root, options.pack, options.policy);
|
|
291
|
+
if (profile.pid !== options.pid || !/^[a-f0-9-]{36}$/.test(options.startupOperation)) throw Error("Exact current profile and original startup required.");
|
|
292
|
+
if (["activation-operation.json", "launch-operation.json"].some((n) => fs.existsSync(path.join(root, "claude", n)))) throw Error("Unresolved activation or launch retains custody.");
|
|
293
|
+
const startup = path.join(root, "claude/journals", options.startupOperation + ".jsonl"), text = owned(startup), rows = text.trimEnd().split("\n").map((r) => JSON.parse(r)), plan = rows[0];
|
|
294
|
+
if (!text.endsWith("\n") || plan.kind !== "startup-plan" || plan.operationId !== options.startupOperation || plan.libraryRoot !== root || plan.installationConsent?.pack !== options.pack || plan.members?.length !== 3 || !rows.some((r) => r.kind === "frontdoor-hooks-prepared") || plan.previousCoordinators?.length && !rows.some((r) => r.kind === "previous-coordinators-retired" && JSON.stringify(r.retired) === JSON.stringify(plan.previousCoordinators.map((m) => m.sessionId)))) throw Error("Completed exact original startup required.");
|
|
295
|
+
const statePath = path.join(root, "claude/state.json"), state = json(statePath), selected = state.agents.filter((m) => m.pack === options.pack), members = plan.members;
|
|
296
|
+
if (selected.length !== 3 || selected.some((m, i) => m.key !== members[i].key || m.desktopSessionId !== members[i].sessionId || m.workspace !== members[i].cwd || m.sourceRevision !== plan.sourceRevision)) throw Error("Original coordinator roster changed.");
|
|
297
|
+
const adopted = fs.existsSync(path.join(root, "claude/activation.json")) ? validateActivation(root, options.pack, options.policy).receipt : null;
|
|
298
|
+
if (adopted && (adopted.startupOperation !== options.startupOperation || adopted.startupDigest !== digest(text))) throw Error("Activated startup ownership differs.");
|
|
299
|
+
for (const [file, hash] of Object.entries(plan.context)) if (!(adopted && file === statePath) && digest(owned(file)) !== hash) throw Error("Original startup context changed.");
|
|
300
|
+
const connection = readClaudeTaskConnection(path.join(root, "claude/desktop-task-connection.json"), options.pid), binding = json(path.join(root, "work/claude-frontdoors.json"));
|
|
301
|
+
if (!installedBindingMatches(binding, connection, options.pid, members)) throw Error("Installed native front-door binding differs.");
|
|
302
|
+
const operationId = randomUUID(), journal = path.join(root, "claude/journals", operationId + ".control-upgrade.jsonl");
|
|
303
|
+
put(journal, JSON.stringify({ kind: "owned-control-upgrade", operationId, startupOperation: options.startupOperation, startupDigest: digest(text), policyDigest: digest(owned(options.policy)), connection, members, nativeInputsSent: false }) + "\n", true);
|
|
304
|
+
const observed = await evaluate(options.endpoint, observer(options.pid, profile.profile, members.map((m, i) => ({ ...m, cliSessionId: binding.roles[i].sessionId })), journal));
|
|
305
|
+
checkResult(observed, members);
|
|
306
|
+
if (observed.members.some((m, i) => m.cliSessionId !== binding.roles[i].sessionId)) throw Error("Native SDK and installed binding differ.");
|
|
307
|
+
const result = await evaluate(options.endpoint, nativeClaudeDesktopControlExpression(options.pid, root, connection, connection.secretPath, journal, { existing: connection, coordinators: members, frontdoors: observed.members }));
|
|
308
|
+
if (!["native-desktop-task-control-installed", "native-desktop-task-control-reused"].includes(result?.state)) throw Error("Owned native control upgrade was not acknowledged.");
|
|
309
|
+
append(journal, { kind: "owned-control-upgrade-verified", operationId, nativeInputsSent: false });
|
|
310
|
+
return { state: "claude-control-upgraded", startupOperation: options.startupOperation, pid: options.pid, coordinators: 3, nativeInputsSent: false };
|
|
311
|
+
} finally {
|
|
312
|
+
try {
|
|
313
|
+
const profile = readClaudeDesktopProfile(root);
|
|
314
|
+
if (profile?.pid === options.pid) await finishInspector(options.endpoint, options.pid, profile.profile, evaluate);
|
|
315
|
+
} finally {
|
|
316
|
+
if (release) await release();
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
function validateActivation(root, pack, policy, continueLaunch = false) {
|
|
321
|
+
const { profile } = immutable(root, pack, policy), receipt = json(path.join(root, "claude/activation.json"));
|
|
322
|
+
if (fs.existsSync(path.join(root, "claude/activation-operation.json")) || !continueLaunch && fs.existsSync(path.join(root, "claude/launch-operation.json"))) throw Error("Unresolved activation or launch retains custody.");
|
|
323
|
+
if (receipt.format !== "topolo-claude-pack-activation-v1" || receipt.root !== root || receipt.pack !== pack || receipt.policyDigest !== digest(owned(policy)) || JSON.stringify(receipt.profile) !== JSON.stringify(profile.profile)) throw Error("Activated pack/profile/consent changed.");
|
|
324
|
+
const state = json(path.join(root, "claude/state.json")), active = json(path.join(root, "claude/active.json"));
|
|
325
|
+
if (active.pack !== pack || active.operationId !== receipt.startupOperation || JSON.stringify(active.keys) !== JSON.stringify(receipt.members.map((m) => m.key))) throw Error("Verified active coordinator roster changed.");
|
|
326
|
+
for (const m of receipt.members) {
|
|
327
|
+
const r = state.agents.filter((r2) => r2.key === m.key);
|
|
328
|
+
if (r.length !== 1 || r[0].importState !== "desktop-adopted" || r[0].desktopSessionId !== m.sessionId || r[0].cliSessionId !== m.cliSessionId || r[0].workspace !== m.cwd || r[0].sourceRevision !== receipt.sourceRevision) throw Error("Activated coordinator identity changed.");
|
|
329
|
+
}
|
|
330
|
+
return { profile, receipt };
|
|
331
|
+
}
|
|
332
|
+
async function port(preferred = 0) {
|
|
333
|
+
const server = createServer();
|
|
334
|
+
await new Promise((done, fail) => {
|
|
335
|
+
server.once("error", fail);
|
|
336
|
+
server.listen(preferred, "127.0.0.1", done);
|
|
337
|
+
});
|
|
338
|
+
const selected = server.address().port;
|
|
339
|
+
await new Promise((done) => server.close(() => done()));
|
|
340
|
+
return selected;
|
|
341
|
+
}
|
|
342
|
+
async function resolveClaudeInspectorEndpoint(port2) {
|
|
343
|
+
let last;
|
|
344
|
+
for (let n = 0; n < 100; n++) {
|
|
345
|
+
let response;
|
|
346
|
+
try {
|
|
347
|
+
response = await fetch("http://127.0.0.1:" + port2 + "/json/list", { signal: AbortSignal.timeout(500) });
|
|
348
|
+
} catch (error) {
|
|
349
|
+
last = error;
|
|
350
|
+
await new Promise((r) => setTimeout(r, 100));
|
|
351
|
+
continue;
|
|
352
|
+
}
|
|
353
|
+
if (!response.ok) throw Error("Native inspector discovery was refused.");
|
|
354
|
+
const text = await response.text();
|
|
355
|
+
if (Buffer.byteLength(text) > 16384) throw Error("Native inspector discovery exceeded bounds.");
|
|
356
|
+
const rows = JSON.parse(text);
|
|
357
|
+
if (!Array.isArray(rows) || rows.length !== 1 || typeof rows[0].webSocketDebuggerUrl !== "string") throw Error("Ambiguous native inspector endpoint.");
|
|
358
|
+
const u = new URL(rows[0].webSocketDebuggerUrl);
|
|
359
|
+
if (u.hostname !== "127.0.0.1" || Number(u.port) !== port2 || u.protocol !== "ws:") throw Error("Native inspector left loopback boundary.");
|
|
360
|
+
return u.href;
|
|
361
|
+
}
|
|
362
|
+
throw Error("Owned temporary native inspector unavailable: " + String(last));
|
|
363
|
+
}
|
|
364
|
+
async function closeClaudeInspector(url, pid, profile, evaluate, disable = disableClaudeInspectorMenu) {
|
|
365
|
+
const ack = await evaluate(url, `(()=>{if(process.pid!==${pid})throw Error('Inspector owner changed');const load=process.getBuiltinModule('module').createRequire(process.resourcesPath+'/app.asar/package.json'),app=load('electron').app;if(app.getPath('userData')!==${JSON.stringify(profile.userData)})throw Error('Inspector profile differs');return{closing:true};})()`);
|
|
366
|
+
if (ack?.closing !== true) throw Error("Native inspector closure acknowledgement differs.");
|
|
367
|
+
const menu = disable(pid);
|
|
368
|
+
if (menu.state !== "owned-native-debugger-menu-disabled" || menu.pid !== pid) throw Error("Owned native debugger checkbox closure was not acknowledged.");
|
|
369
|
+
await new Promise((r) => setTimeout(r, 100));
|
|
370
|
+
try {
|
|
371
|
+
await fetch(new URL("/json/list", url.replace(/^ws:/, "http:")), { signal: AbortSignal.timeout(500) });
|
|
372
|
+
} catch {
|
|
373
|
+
return;
|
|
374
|
+
}
|
|
375
|
+
throw Error("Temporary inspector closure was not verified.");
|
|
376
|
+
}
|
|
377
|
+
async function launchActivatedClaudePack(options, deps = {}) {
|
|
378
|
+
const root = fs.realpathSync(options.store), release = await lockClaudeLibrary(root), prove = deps.prove ?? cloud, launch = deps.launch ?? launchClaudeDesktopProfile, evaluate = deps.evaluate ?? evaluateClaudeMain;
|
|
379
|
+
let inspection;
|
|
380
|
+
let pendingInspection;
|
|
381
|
+
try {
|
|
382
|
+
const continuing = options.continueLaunch !== void 0;
|
|
383
|
+
const { profile, receipt } = validateActivation(root, options.pack, options.policy, continuing);
|
|
384
|
+
const args = { root, pack: options.pack, policy: options.policy, userData: profile.profile.userData, codeConfig: profile.profile.codeConfig, app: profile.profile.app };
|
|
385
|
+
const before = launch({ ...args, apply: false });
|
|
386
|
+
if (continuing && before.pid === receipt.pid) throw Error("Pending cold launch cannot adopt the previous owner.");
|
|
387
|
+
if (before.pid === receipt.pid) {
|
|
388
|
+
const binding = json(path.join(root, "work/claude-frontdoors.json"));
|
|
389
|
+
if (!installedBindingMatches(binding, receipt.connection, receipt.pid, receipt.members) || binding.roles.some((m, i) => m.sessionId !== receipt.members[i].cliSessionId)) throw Error("Warm coordinator binding differs.");
|
|
390
|
+
const proof2 = prove(root, { warm_connection: receipt.connection });
|
|
391
|
+
if (proof2.warmOwner !== true || proof2.nativeInputsSent !== false || proof2.workerCustodyPreserved !== true) throw Error("Warm ownership proof differs.");
|
|
392
|
+
if (proof2.service?.desktopPid !== receipt.pid || JSON.stringify(proof2.binding) !== JSON.stringify(receipt.cloud)) throw Error("Current owning dispatcher differs.");
|
|
393
|
+
return { state: "claude-pack-ready", nativeReady: true, cloudReady: true, coordinators: 3, nativeInputsSent: false, pid: receipt.pid };
|
|
394
|
+
}
|
|
395
|
+
if (receipt.menuAutomationConsent?.approved !== true || receipt.menuAutomationConsent.scope !== "owned-claude-main-debugger-checkbox-only" || receipt.menuAutomationConsent.principalId !== json(options.policy).installationConsent.principalId) throw Error("Explicit installation-owner main-debugger menu consent required for cold restore.");
|
|
396
|
+
try {
|
|
397
|
+
process.kill(receipt.pid, 0);
|
|
398
|
+
throw Error("Previous native owner remains alive.");
|
|
399
|
+
} catch (error) {
|
|
400
|
+
if (error.code !== "ESRCH") throw error;
|
|
401
|
+
}
|
|
402
|
+
if (continuing && !/^[a-f0-9-]{36}$/.test(options.continueLaunch)) throw Error("Exact pending cold launch identity required.");
|
|
403
|
+
const operationId = options.continueLaunch ?? randomUUID(), journal = path.join(root, "claude/journals", operationId + ".launch.jsonl"), secretPath = path.join(root, "claude/journals", operationId + ".desktop-secret.json");
|
|
404
|
+
const marker = path.join(root, "claude/launch-operation.json"), previous = json(path.join(root, "work/claude-frontdoors.json"));
|
|
405
|
+
if (continuing) {
|
|
406
|
+
if (!options.endpoint || !Number.isSafeInteger(options.pid) || options.pid !== before.pid) throw Error("Exact already launched profile PID and approved endpoint required.");
|
|
407
|
+
validateInspectorEndpoint(options.endpoint);
|
|
408
|
+
inspection = { url: options.endpoint, pid: options.pid, profile: profile.profile };
|
|
409
|
+
}
|
|
410
|
+
if (continuing) {
|
|
411
|
+
const pending = json(marker), text = owned(journal), rows = text.trimEnd().split("\n").map((row) => JSON.parse(row)), first = rows[0], installs = rows.filter((r) => r.kind === "native-desktop-task-control-installed"), installed2 = installs[0];
|
|
412
|
+
if (installed2) {
|
|
413
|
+
const setup = rows.slice(0, rows.indexOf(installed2) + 1), activity = rows.slice(setup.length);
|
|
414
|
+
const workerKinds = ["worker-workspace-trust-attempt", "worker-workspace-trust-verified", "worker-create-attempt", "worker-create-verified", "worker-task-send-attempt", "worker-task-send-acknowledged", "worker-archive-attempt", "worker-archive-verified"];
|
|
415
|
+
const pid2 = options.pid, url2 = options.endpoint, descriptor2 = { ...receipt.connection, pid: pid2, identity: digest(JSON.stringify({ root, pack: options.pack, operationId })), socket: path.join("/tmp", "topolo-claude-task-" + operationId + ".sock"), secretPath };
|
|
416
|
+
const allowed = ["closed-native-rebind", "closed-native-rebind-continuation", "frontdoor-connect-only-attempt", "native-python-capability-qualified", "native-desktop-task-control-installed"];
|
|
417
|
+
const created = setup.filter((r) => r.kind === "native-desktop-task-control-installed"), connects = setup.filter((r) => r.kind === "frontdoor-connect-only-attempt"), continuations = setup.filter((r) => r.kind === "closed-native-rebind-continuation");
|
|
418
|
+
const input2 = path.join(root, "claude/journals", operationId + ".frontdoors.json"), bound = json(input2);
|
|
419
|
+
if (Object.keys(pending).sort().join() !== "journal,operationId,previousPid,state" || pending.operationId !== operationId || pending.state !== "may-have-effects" || pending.previousPid !== receipt.pid || pending.journal !== journal || !text.endsWith("\n") || first.kind !== "closed-native-rebind" || first.operationId !== operationId || first.previousPid !== receipt.pid || JSON.stringify(first.members) !== JSON.stringify(receipt.members) || setup.some((r) => !allowed.includes(r.kind)) || activity.some((r) => !workerKinds.includes(r.kind)) || installs.length !== 1 || created.length !== 1 || setup.filter((r) => r.kind === "native-python-capability-qualified").length !== 1 || continuations.length > 1 || continuations.some((r) => r.operationId !== operationId || r.pid !== pid2 || r.nativeInputsSent !== false || r.originalJournalSHA256 !== digest(JSON.stringify(first) + "\n")) || new Set(connects.map((r) => r.sessionId)).size !== connects.length || connects.some((r) => !receipt.members.some((m) => m.sessionId === r.sessionId && m.cliSessionId === r.cliSessionId)) || ["identity", "pid", "runtime", "secretPath", "socket"].some((k) => installed2[k] !== descriptor2[k]) || !sameNativeConnection(readClaudeTaskConnection(path.join(root, "claude/desktop-task-connection.json"), receipt.pid), receipt.connection) || !installedBindingMatches(previous, descriptor2, pid2, receipt.members) || previous.roles.some((r, i) => r.sessionId !== receipt.members[i].cliSessionId) || bound.desktopPid !== pid2 || !sameNativeConnection(bound.nativeConnection, descriptor2) || JSON.stringify(bound.roles) !== JSON.stringify(receipt.members.map((m) => ({ key: m.key, cwd: m.cwd, sessionId: m.cliSessionId }))) || Object.keys(json(secretPath)).join() !== "token" || !/^[a-f0-9]{64}$/.test(json(secretPath).token)) throw Error("Pending post-control launch custody differs; do not replay.");
|
|
420
|
+
let proof2 = prove(root, { successor_connection: descriptor2 });
|
|
421
|
+
if (proof2.existingSuccessor !== true) {
|
|
422
|
+
if (activity.length) throw Error("Worker audit activity requires the exact authenticated successor; preserve its custody.");
|
|
423
|
+
prove(root, { previous: { ...previous, desktopPid: receipt.pid, nativeConnection: receipt.connection }, connection: descriptor2, wait_retired_service: true });
|
|
424
|
+
proof2 = prove(root, { start_service: true, native_proof: true, require_native_idle: true });
|
|
425
|
+
} else if (proof2.serviceStarted !== false || proof2.workerCustodyPreserved !== true || proof2.nativeInputsSent !== false) throw Error("Successor reconciliation must preserve live worker custody.");
|
|
426
|
+
checkResult(proof2.native, receipt.members);
|
|
427
|
+
if (proof2.service?.desktopPid !== pid2 || JSON.stringify(proof2.binding) !== JSON.stringify(receipt.cloud) || proof2.native.members.some((m, i) => m.cliSessionId !== receipt.members[i].cliSessionId)) throw Error("Successor dispatcher differs.");
|
|
428
|
+
const actual2 = await evaluate(url2, `(()=>{if(process.pid!==${pid2})throw Error('Native PID changed');const load=process.getBuiltinModule('module').createRequire(process.resourcesPath+'/app.asar/package.json');return{state:'native-profile-verified',pid:process.pid,userData:load('electron').app.getPath('userData'),codeConfig:process.env.CLAUDE_CONFIG_DIR};})()`);
|
|
429
|
+
acknowledgeClaudeDesktopProfile(root, readClaudeDesktopProfile(root), actual2);
|
|
430
|
+
put(path.join(root, "claude/desktop-task-connection.json"), JSON.stringify(descriptor2) + "\n");
|
|
431
|
+
await (deps.close ?? closeClaudeInspector)(url2, pid2, profile.profile, evaluate);
|
|
432
|
+
inspection = void 0;
|
|
433
|
+
append(journal, { kind: "closed-native-rebind-verified", pid: pid2, nativeInputsSent: false, completedExistingControl: true, originalJournalSHA256: digest(text) });
|
|
434
|
+
put(path.join(root, "claude/activation.json"), JSON.stringify({ ...receipt, pid: pid2, connection: descriptor2 }) + "\n");
|
|
435
|
+
fs.unlinkSync(marker);
|
|
436
|
+
return { state: "claude-pack-ready", nativeReady: true, cloudReady: true, coordinators: 3, nativeInputsSent: false, pid: pid2 };
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
if (!sameNativeConnection(previous.nativeConnection, receipt.connection)) throw Error("Previous native connection ownership differs.");
|
|
440
|
+
prove(root, { previous, wait_retired_service: true });
|
|
441
|
+
let pid, url;
|
|
442
|
+
if (continuing) {
|
|
443
|
+
pid = options.pid;
|
|
444
|
+
url = options.endpoint;
|
|
445
|
+
const pending = json(marker), text = owned(journal), rows = text.trimEnd().split("\n").map((row) => JSON.parse(row)), first = rows[0];
|
|
446
|
+
if (Object.keys(pending).sort().join() !== "journal,operationId,previousPid,state" || pending.operationId !== operationId || pending.state !== "may-have-effects" || pending.previousPid !== receipt.pid || pending.journal !== journal || !text.endsWith("\n") || rows.length !== 1 || first.kind !== "closed-native-rebind" || first.operationId !== operationId || first.previousPid !== receipt.pid || JSON.stringify(first.members) !== JSON.stringify(receipt.members) || fs.existsSync(secretPath) || fs.existsSync(path.join(root, "claude/journals", operationId + ".frontdoors.json")) || !sameNativeConnection(readClaudeTaskConnection(path.join(root, "claude/desktop-task-connection.json"), receipt.pid), receipt.connection)) throw Error("Pending launch progressed beyond the exact pre-control phase; preserve its custody.");
|
|
447
|
+
append(journal, { kind: "closed-native-rebind-continuation", operationId, pid, originalJournalSHA256: digest(text), nativeInputsSent: false });
|
|
448
|
+
} else {
|
|
449
|
+
const selected = await (deps.port ?? port)(9229);
|
|
450
|
+
put(marker, JSON.stringify({ operationId, state: "may-have-effects", previousPid: receipt.pid, journal }) + "\n", true);
|
|
451
|
+
put(journal, JSON.stringify({ kind: "closed-native-rebind", operationId, members: receipt.members, previousPid: receipt.pid }) + "\n", true);
|
|
452
|
+
if (!before.pid) launch({ ...args, apply: true });
|
|
453
|
+
let current = launch({ ...args, apply: false });
|
|
454
|
+
for (let n = 0; !current.pid && n < 100; n++) {
|
|
455
|
+
await new Promise((r) => setTimeout(r, 100));
|
|
456
|
+
current = launch({ ...args, apply: false });
|
|
457
|
+
}
|
|
458
|
+
if (!Number.isSafeInteger(current.pid) || current.pid === receipt.pid) throw Error("Exact successor profile process missing.");
|
|
459
|
+
pid = current.pid;
|
|
460
|
+
pendingInspection = { port: selected, pid, profile: profile.profile };
|
|
461
|
+
(deps.activateInspector ?? enableClaudeInspectorMenu)(pid);
|
|
462
|
+
url = await (deps.endpoint ?? resolveClaudeInspectorEndpoint)(selected);
|
|
463
|
+
inspection = { url, pid, profile: profile.profile };
|
|
464
|
+
pendingInspection = void 0;
|
|
465
|
+
}
|
|
466
|
+
const native = await evaluate(url, observer(pid, profile.profile, receipt.members, journal, true));
|
|
467
|
+
checkResult(native, receipt.members);
|
|
468
|
+
const descriptor = { ...receipt.connection, pid, identity: digest(JSON.stringify({ root, pack: options.pack, operationId })), socket: path.join("/tmp", "topolo-claude-task-" + operationId + ".sock"), secretPath };
|
|
469
|
+
prove(root, { previous, connection: descriptor });
|
|
470
|
+
put(secretPath, JSON.stringify({ token: randomBytes(32).toString("hex") }) + "\n", true);
|
|
471
|
+
const installed = await evaluate(url, nativeClaudeDesktopControlExpression(pid, root, descriptor, secretPath, journal, { coordinators: receipt.members, frontdoors: receipt.members }));
|
|
472
|
+
if (installed.state !== "native-desktop-task-control-installed") throw Error("Successor closed control is unacknowledged.");
|
|
473
|
+
const input = path.join(root, "claude/journals", operationId + ".frontdoors.json");
|
|
474
|
+
(deps.bind ?? bindClaudeFrontdoorHooks)(root, pid, receipt.members.map((m) => ({ key: m.key, cwd: m.cwd, sessionId: m.cliSessionId })), input, void 0, descriptor);
|
|
475
|
+
const proof = prove(root, { start_service: true, native_proof: true });
|
|
476
|
+
checkResult(proof.native, receipt.members);
|
|
477
|
+
if (proof.service?.desktopPid !== pid || JSON.stringify(proof.binding) !== JSON.stringify(receipt.cloud) || proof.native.members.some((m, i) => m.cliSessionId !== receipt.members[i].cliSessionId)) throw Error("Successor dispatcher differs.");
|
|
478
|
+
const next = { ...receipt, pid, connection: descriptor };
|
|
479
|
+
put(path.join(root, "claude/desktop-task-connection.json"), JSON.stringify(descriptor) + "\n");
|
|
480
|
+
const actual = await evaluate(url, `(()=>{if(process.pid!==${pid})throw Error('Native PID changed');const load=process.getBuiltinModule('module').createRequire(process.resourcesPath+'/app.asar/package.json');return{state:'native-profile-verified',pid:process.pid,userData:load('electron').app.getPath('userData'),codeConfig:process.env.CLAUDE_CONFIG_DIR};})()`);
|
|
481
|
+
acknowledgeClaudeDesktopProfile(root, readClaudeDesktopProfile(root), actual);
|
|
482
|
+
await (deps.close ?? closeClaudeInspector)(url, pid, profile.profile, evaluate);
|
|
483
|
+
inspection = void 0;
|
|
484
|
+
append(journal, { kind: "closed-native-rebind-verified", pid, nativeInputsSent: false });
|
|
485
|
+
put(path.join(root, "claude/activation.json"), JSON.stringify(next) + "\n");
|
|
486
|
+
fs.unlinkSync(marker);
|
|
487
|
+
return { state: "claude-pack-ready", nativeReady: true, cloudReady: true, coordinators: 3, nativeInputsSent: false, pid };
|
|
488
|
+
} finally {
|
|
489
|
+
try {
|
|
490
|
+
if (pendingInspection) {
|
|
491
|
+
const url = await (deps.endpoint ?? resolveClaudeInspectorEndpoint)(pendingInspection.port);
|
|
492
|
+
inspection = { url, pid: pendingInspection.pid, profile: pendingInspection.profile };
|
|
493
|
+
}
|
|
494
|
+
if (inspection) await (deps.close ?? closeClaudeInspector)(inspection.url, inspection.pid, inspection.profile, evaluate);
|
|
495
|
+
} finally {
|
|
496
|
+
await release();
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
export {
|
|
501
|
+
closeClaudeInspector,
|
|
502
|
+
finalizeClaudePack,
|
|
503
|
+
launchActivatedClaudePack,
|
|
504
|
+
resolveClaudeInspectorEndpoint,
|
|
505
|
+
upgradeClaudeControl
|
|
506
|
+
};
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
CLAUDE_NATIVE_QUALIFIER,
|
|
4
|
+
bindClaudeFrontdoorHooks,
|
|
5
|
+
claudeLaneExpression,
|
|
6
|
+
claudeTaskConnectionCoordinators,
|
|
7
|
+
completeClaudeStartup,
|
|
8
|
+
configureClaudePack,
|
|
9
|
+
configureClaudeStartup,
|
|
10
|
+
ensureClaudeCoordinators,
|
|
11
|
+
evaluateClaudeMain,
|
|
12
|
+
lockClaudeLibrary,
|
|
13
|
+
materializeClaudeHookAdapter,
|
|
14
|
+
materializeClaudeWorkerGuard,
|
|
15
|
+
nativeClaudeDesktopControlExpression,
|
|
16
|
+
nativeClaudeSettingsExpression,
|
|
17
|
+
nativePackExpression,
|
|
18
|
+
operateClaudeApprovals,
|
|
19
|
+
operateClaudeLane,
|
|
20
|
+
operateClaudeLaneControl,
|
|
21
|
+
operateClaudeSmoke,
|
|
22
|
+
prepareClaudeDynamicLane,
|
|
23
|
+
prepareClaudeNativePlan,
|
|
24
|
+
prepareClaudePrivateHelperConnection,
|
|
25
|
+
prepareClaudeStartup,
|
|
26
|
+
privateClaudeHelperEnvironment,
|
|
27
|
+
qualifyClaudePython,
|
|
28
|
+
readClaudePreviousCoordinatorLibrary,
|
|
29
|
+
readClaudeReadinessContinuation,
|
|
30
|
+
readClaudeTaskConnection,
|
|
31
|
+
reconcileClaudeStartupAbsence,
|
|
32
|
+
recoverClaudeStartup,
|
|
33
|
+
requestClaudeApprovalControl,
|
|
34
|
+
requireClaudePrivateQueryEffectQualified,
|
|
35
|
+
retireClaudeCoordinators,
|
|
36
|
+
runClaudeLaneHelper,
|
|
37
|
+
runClaudeSmokeHelper,
|
|
38
|
+
snapshotClaudeMarkdown,
|
|
39
|
+
snapshotClaudeWorkerSource,
|
|
40
|
+
validateInspectorEndpoint,
|
|
41
|
+
verifyClaudeOrganizationPolicy,
|
|
42
|
+
verifyClaudePlanContext,
|
|
43
|
+
verifyClaudePreviousCoordinatorCustody,
|
|
44
|
+
verifyClaudeReadinessContinuation
|
|
45
|
+
} from "./chunk-UY5LEU5C.js";
|
|
46
|
+
import "./chunk-NYDIAYVY.js";
|
|
47
|
+
export {
|
|
48
|
+
CLAUDE_NATIVE_QUALIFIER,
|
|
49
|
+
bindClaudeFrontdoorHooks,
|
|
50
|
+
claudeLaneExpression,
|
|
51
|
+
claudeTaskConnectionCoordinators,
|
|
52
|
+
completeClaudeStartup,
|
|
53
|
+
configureClaudePack,
|
|
54
|
+
configureClaudeStartup,
|
|
55
|
+
ensureClaudeCoordinators,
|
|
56
|
+
evaluateClaudeMain,
|
|
57
|
+
lockClaudeLibrary,
|
|
58
|
+
materializeClaudeHookAdapter,
|
|
59
|
+
materializeClaudeWorkerGuard,
|
|
60
|
+
nativeClaudeDesktopControlExpression,
|
|
61
|
+
nativeClaudeSettingsExpression,
|
|
62
|
+
nativePackExpression,
|
|
63
|
+
operateClaudeApprovals,
|
|
64
|
+
operateClaudeLane,
|
|
65
|
+
operateClaudeLaneControl,
|
|
66
|
+
operateClaudeSmoke,
|
|
67
|
+
prepareClaudeDynamicLane,
|
|
68
|
+
prepareClaudeNativePlan,
|
|
69
|
+
prepareClaudePrivateHelperConnection,
|
|
70
|
+
prepareClaudeStartup,
|
|
71
|
+
privateClaudeHelperEnvironment,
|
|
72
|
+
qualifyClaudePython,
|
|
73
|
+
readClaudePreviousCoordinatorLibrary,
|
|
74
|
+
readClaudeReadinessContinuation,
|
|
75
|
+
readClaudeTaskConnection,
|
|
76
|
+
reconcileClaudeStartupAbsence,
|
|
77
|
+
recoverClaudeStartup,
|
|
78
|
+
requestClaudeApprovalControl,
|
|
79
|
+
requireClaudePrivateQueryEffectQualified,
|
|
80
|
+
retireClaudeCoordinators,
|
|
81
|
+
runClaudeLaneHelper,
|
|
82
|
+
runClaudeSmokeHelper,
|
|
83
|
+
snapshotClaudeMarkdown,
|
|
84
|
+
snapshotClaudeWorkerSource,
|
|
85
|
+
validateInspectorEndpoint,
|
|
86
|
+
verifyClaudeOrganizationPolicy,
|
|
87
|
+
verifyClaudePlanContext,
|
|
88
|
+
verifyClaudePreviousCoordinatorCustody,
|
|
89
|
+
verifyClaudeReadinessContinuation
|
|
90
|
+
};
|