create-walle 0.9.28 → 0.9.30
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 +2 -2
- package/bin/create-walle.js +166 -6
- package/package.json +1 -1
- package/template/bin/ctm-launch.sh +70 -18
- package/template/bin/dev.sh +18 -0
- package/template/bin/ensure-stable-node.js +11 -0
- package/template/bin/node-bin.sh +9 -0
- package/template/claude-task-manager/api-prompts.js +214 -23
- package/template/claude-task-manager/db.js +884 -50
- package/template/claude-task-manager/docs/backfill-incremental-no-main-fallback.md +48 -0
- package/template/claude-task-manager/docs/conversation-import-freshness.md +21 -0
- package/template/claude-task-manager/docs/conversation-log-redesign.html +587 -0
- package/template/claude-task-manager/docs/session-title-authority.md +8 -3
- package/template/claude-task-manager/lib/auth-rules.js +13 -0
- package/template/claude-task-manager/lib/claude-desktop-sessions.js +63 -0
- package/template/claude-task-manager/lib/codex-config-guard.js +124 -0
- package/template/claude-task-manager/lib/codex-rollout-snapshot.js +93 -0
- package/template/claude-task-manager/lib/coding-agent-models.js +5 -4
- package/template/claude-task-manager/lib/db-owner-cooperative-scheduler.js +114 -0
- package/template/claude-task-manager/lib/db-owner-task-queue.js +67 -0
- package/template/claude-task-manager/lib/db-owner-worker-client.js +5 -1
- package/template/claude-task-manager/lib/desktop-fork.js +81 -0
- package/template/claude-task-manager/lib/headless-term-service.js +251 -4
- package/template/claude-task-manager/lib/message-identity.js +115 -0
- package/template/claude-task-manager/lib/mirror-feed-guards.js +25 -0
- package/template/claude-task-manager/lib/mirror-feed-sanitize.js +45 -0
- package/template/claude-task-manager/lib/path-suggest.js +77 -0
- package/template/claude-task-manager/lib/prompt-index-inputs.js +136 -0
- package/template/claude-task-manager/lib/real-node.js +36 -4
- package/template/claude-task-manager/lib/restore-auto-resume-policy.js +67 -0
- package/template/claude-task-manager/lib/restore-resume-batch.js +20 -0
- package/template/claude-task-manager/lib/restore-terminal-dims.js +109 -0
- package/template/claude-task-manager/lib/resume-cwd.js +124 -3
- package/template/claude-task-manager/lib/runtime-approval-recorder.js +152 -0
- package/template/claude-task-manager/lib/runtime-context-truth.js +236 -0
- package/template/claude-task-manager/lib/runtime-contract.js +195 -0
- package/template/claude-task-manager/lib/runtime-history-builder.js +205 -0
- package/template/claude-task-manager/lib/runtime-hook-bus.js +98 -0
- package/template/claude-task-manager/lib/runtime-input-queue.js +114 -0
- package/template/claude-task-manager/lib/runtime-input-recorder.js +156 -0
- package/template/claude-task-manager/lib/runtime-lineage.js +189 -0
- package/template/claude-task-manager/lib/runtime-registry.js +263 -0
- package/template/claude-task-manager/lib/runtime-session-history.js +41 -0
- package/template/claude-task-manager/lib/scrollback-snapshot-policy.js +37 -0
- package/template/claude-task-manager/lib/server-phase-conditions.js +103 -0
- package/template/claude-task-manager/lib/session-content-backfill.js +55 -8
- package/template/claude-task-manager/lib/session-db-read-contract.js +67 -0
- package/template/claude-task-manager/lib/session-history.js +93 -5
- package/template/claude-task-manager/lib/session-host-manager.js +154 -2
- package/template/claude-task-manager/lib/session-messages-defer.js +50 -0
- package/template/claude-task-manager/lib/session-messages-page.js +13 -0
- package/template/claude-task-manager/lib/session-messages-projection.js +48 -29
- package/template/claude-task-manager/lib/session-stream.js +80 -17
- package/template/claude-task-manager/lib/session-title-signals.js +54 -0
- package/template/claude-task-manager/lib/session-token-usage.js +13 -0
- package/template/claude-task-manager/lib/state-sync/cell-diff.js +41 -0
- package/template/claude-task-manager/lib/state-sync/frame-emitter.js +214 -0
- package/template/claude-task-manager/lib/state-sync/frame-rate.js +75 -0
- package/template/claude-task-manager/lib/state-sync/row-serializer.js +166 -0
- package/template/claude-task-manager/lib/terminal-fingerprint.js +19 -3
- package/template/claude-task-manager/lib/transcript-ingest-chunker.js +41 -0
- package/template/claude-task-manager/lib/transcript-store.js +99 -7
- package/template/claude-task-manager/lib/wal-checkpoint-policy.js +40 -0
- package/template/claude-task-manager/lib/walle-session-model-catalog.js +100 -9
- package/template/claude-task-manager/lib/worktree-output-binding.js +93 -0
- package/template/claude-task-manager/lib/write-coalescer.js +83 -0
- package/template/claude-task-manager/public/css/walle-session.css +4 -0
- package/template/claude-task-manager/public/css/walle.css +0 -66
- package/template/claude-task-manager/public/index.html +1707 -266
- package/template/claude-task-manager/public/js/feedback.js +8 -1
- package/template/claude-task-manager/public/js/message-renderer.js +72 -2
- package/template/claude-task-manager/public/js/session-phase.js +4 -0
- package/template/claude-task-manager/public/js/session-status-precedence.js +7 -173
- package/template/claude-task-manager/public/js/setup.js +46 -3
- package/template/claude-task-manager/public/js/state-sync-client.js +257 -0
- package/template/claude-task-manager/public/js/state-sync-predictor.js +41 -0
- package/template/claude-task-manager/public/js/stream-view.js +113 -9
- package/template/claude-task-manager/public/js/terminal-reconciler.js +24 -4
- package/template/claude-task-manager/public/js/walle-session.js +239 -19
- package/template/claude-task-manager/public/js/walle.js +32 -119
- package/template/claude-task-manager/queue-engine.js +140 -0
- package/template/claude-task-manager/server.js +2802 -416
- package/template/claude-task-manager/session-integrity.js +16 -1
- package/template/claude-task-manager/workers/db-owner-worker.js +23 -6
- package/template/claude-task-manager/workers/read-pool-worker.js +55 -1
- package/template/claude-task-manager/workers/session-host-pool-process.js +193 -0
- package/template/claude-task-manager/workers/session-host-process.js +47 -11
- package/template/claude-task-manager/workers/state-detectors/codex.js +33 -0
- package/template/package.json +1 -1
- package/template/wall-e/agent.js +191 -31
- package/template/wall-e/api-walle.js +97 -52
- package/template/wall-e/auth/flow-manager.js +78 -1
- package/template/wall-e/auth/provider-flows.js +56 -2
- package/template/wall-e/bin/walle-mcp-stdio.js +138 -5
- package/template/wall-e/brain.js +175 -13
- package/template/wall-e/chat.js +46 -1
- package/template/wall-e/embeddings.js +70 -0
- package/template/wall-e/events/event-bus.js +11 -1
- package/template/wall-e/http/auth.js +3 -1
- package/template/wall-e/http/model-admin.js +22 -0
- package/template/wall-e/lib/brain-owner-worker-client.js +36 -4
- package/template/wall-e/lib/diagnostics-flags.js +9 -0
- package/template/wall-e/lib/event-loop-monitor.js +84 -5
- package/template/wall-e/lib/mcp-scan-lifecycle.js +247 -0
- package/template/wall-e/lib/parent-brain-owner-client.js +109 -0
- package/template/wall-e/lib/runtime-process-inventory.js +114 -0
- package/template/wall-e/lib/runtime-worker-pool.js +214 -23
- package/template/wall-e/lib/scheduler-worker-jobs.js +49 -4
- package/template/wall-e/lib/scheduler.js +320 -35
- package/template/wall-e/lib/slack-identity.js +120 -0
- package/template/wall-e/lib/slack-permalink.js +107 -0
- package/template/wall-e/lib/slack-web.js +174 -0
- package/template/wall-e/lib/worker-thread-pool.js +55 -4
- package/template/wall-e/llm/claude-cli.js +21 -3
- package/template/wall-e/llm/cli-binary.js +90 -0
- package/template/wall-e/llm/codex-cli.js +113 -49
- package/template/wall-e/llm/default-fallback.js +10 -4
- package/template/wall-e/llm/mlx.js +46 -8
- package/template/wall-e/llm/model-catalog.js +129 -17
- package/template/wall-e/llm/provider-detector.js +112 -22
- package/template/wall-e/loops/backfill.js +32 -16
- package/template/wall-e/loops/ingest.js +50 -16
- package/template/wall-e/loops/tasks.js +521 -25
- package/template/wall-e/mcp-server.js +215 -6
- package/template/wall-e/memory/ctm-session-context.js +93 -0
- package/template/wall-e/skills/_bundled/google-calendar/run.js +15 -23
- package/template/wall-e/skills/_bundled/gws-workspace/gws-router +237 -0
- package/template/wall-e/skills/_bundled/gws-workspace/setup.js +112 -1
- package/template/wall-e/skills/_bundled/mcp-scan/run.js +265 -41
- package/template/wall-e/skills/_bundled/slack-mentions/run.js +434 -93
- package/template/wall-e/skills/internal-skill-registry.js +27 -5
- package/template/wall-e/skills/mcp-client.js +18 -3
- package/template/wall-e/skills/script-skill-runner.js +53 -5
- package/template/wall-e/skills/skill-planner.js +5 -26
- package/template/wall-e/training/real-trajectory-miner.js +24 -114
- package/template/wall-e/utils/dedup.js +165 -66
- package/template/wall-e/weather-runtime.js +12 -4
- package/template/wall-e/workers/brain-owner-worker.js +68 -0
- package/template/wall-e/workers/runtime-worker.js +4 -0
- package/template/website/index.html +3 -0
package/README.md
CHANGED
|
@@ -40,9 +40,9 @@ This copies the project, installs dependencies, auto-detects your name and timez
|
|
|
40
40
|
|
|
41
41
|
### Download for Mac (no terminal)
|
|
42
42
|
|
|
43
|
-
Prefer a click-to-install app? Download the notarized **Wall-E.app** (Apple Silicon):
|
|
43
|
+
Prefer a click-to-install app? Download the notarized **Wall-E.app** (Apple Silicon & Intel):
|
|
44
44
|
|
|
45
|
-
**⬇ [Download for Mac](https://github.com/ShanniLi/walle-dist/releases/latest)** → `Wall-E-…-arm64.dmg`
|
|
45
|
+
**⬇ [Download for Mac](https://github.com/ShanniLi/walle-dist/releases/latest)** → `Wall-E-…-arm64.dmg` (Apple Silicon) or `Wall-E-…-x64.dmg` (Intel)
|
|
46
46
|
|
|
47
47
|
Open the DMG, drag **Wall-E** to Applications, and launch it. First run installs everything into `~/.walle` and opens the dashboard — Developer-ID notarized + stapled, so there's no Gatekeeper warning. Under the hood it runs the same `create-walle` setup.
|
|
48
48
|
|
package/bin/create-walle.js
CHANGED
|
@@ -38,6 +38,21 @@ const NATIVE_DEPENDENCIES = new Set([
|
|
|
38
38
|
// create-walle/macos/build-macos-app.sh (NODE_VERSION). Set WALLE_NO_NOTARIZED_NODE=1 to opt out.
|
|
39
39
|
const NOTARIZED_NODE_VERSION = '25.2.1';
|
|
40
40
|
|
|
41
|
+
// ── Branded notarized macOS app (the real Screen Recording fix for no-Dev-ID users) ──
|
|
42
|
+
// A bare notarized node is stable but anonymous — macOS shows "node" in the Screen Recording
|
|
43
|
+
// prompt. The downloadable Developer-ID-notarized Wall-E.app runs the daemon under its OWN vendored
|
|
44
|
+
// node, whose signature is part of the branded bundle (Team HQSJ8SS8Q6), so the grant shows
|
|
45
|
+
// "Wall-E", auto-lists, and persists. `npx create-walle` users never get that app — so we fetch the
|
|
46
|
+
// SAME prebuilt notarized app once and adopt its vendored node as the daemon identity (parity with
|
|
47
|
+
// the downloadable app's `WALLE_NOTARIZED_NODE`). All best-effort: a download/verify failure leaves
|
|
48
|
+
// the daemon on the bare notarized node (no regression). Set WALLE_NO_BRANDED_APP=1 to opt out.
|
|
49
|
+
// The runtime-shell app is versioned independently of the npm package (it changes only when the
|
|
50
|
+
// vendored Node / entitlements / signing change) — keep in sync with build-macos-app.sh VERSION.
|
|
51
|
+
const NOTARIZED_APP_VERSION = '1.0.2';
|
|
52
|
+
const NOTARIZED_APP_TEAM_ID = 'HQSJ8SS8Q6';
|
|
53
|
+
const WALLE_DIST_RELEASE_BASE = process.env.WALLE_DIST_RELEASE_BASE
|
|
54
|
+
|| 'https://github.com/ShanniLi/walle-dist/releases/download';
|
|
55
|
+
|
|
41
56
|
// Files to preserve during update (user config, not code)
|
|
42
57
|
const PRESERVE_ON_UPDATE = ['.env', 'wall-e/wall-e-config.json'];
|
|
43
58
|
|
|
@@ -88,10 +103,11 @@ function execTeamIdentifier(binaryPath) {
|
|
|
88
103
|
// app's own branded launcher identity, so it shows "Wall-E").
|
|
89
104
|
// 2. The CTM .app bundle exec WHEN it carries a stable Team Identifier (Developer-ID-signed by
|
|
90
105
|
// bin/ensure-stable-node.js) — branded AND grant-persisting.
|
|
91
|
-
// 3. The
|
|
92
|
-
//
|
|
93
|
-
// 4. The
|
|
94
|
-
// 5. The
|
|
106
|
+
// 3. The adopted Developer-ID-notarized Wall-E.app's vendored node (ensureNotarizedBrandedApp) —
|
|
107
|
+
// branded ("Wall-E") AND grant-persisting, for no-Dev-ID machines (the npx common case).
|
|
108
|
+
// 4. The official notarized node we downloaded ourselves — stable but anonymous ("node").
|
|
109
|
+
// 5. The self-signed CTM bundle exec — branded but re-prompts (no stable Team ID).
|
|
110
|
+
// 6. The running node.
|
|
95
111
|
function daemonExec() {
|
|
96
112
|
const notarized = process.env.WALLE_NOTARIZED_NODE;
|
|
97
113
|
if (notarized && process.platform === 'darwin') {
|
|
@@ -104,6 +120,11 @@ function daemonExec() {
|
|
|
104
120
|
// Prefer the branded bundle only when it is Developer-ID-signed (has a Team ID): branded AND
|
|
105
121
|
// its grants persist. Otherwise prefer the notarized bare node (stable but "node").
|
|
106
122
|
if (bundleExists && execTeamIdentifier(bundle)) return bundle;
|
|
123
|
+
// Adopted Developer-ID-notarized Wall-E.app vendored node — branded ("Wall-E") AND
|
|
124
|
+
// grant-persisting, for no-Dev-ID npx users where the bundle above has no Team ID. Beats the
|
|
125
|
+
// bare notarized node (anonymous "node").
|
|
126
|
+
const brandedApp = validatedNotarizedApp();
|
|
127
|
+
if (brandedApp) return brandedApp;
|
|
107
128
|
const own = validatedNotarizedNode();
|
|
108
129
|
if (own) return own;
|
|
109
130
|
if (bundleExists) return bundle; // self-signed branded bundle — branded name, but re-prompts
|
|
@@ -140,10 +161,13 @@ function validatedNotarizedNode() {
|
|
|
140
161
|
return null;
|
|
141
162
|
}
|
|
142
163
|
|
|
143
|
-
// The node whose ABI native modules must target = whatever the daemon will run under.
|
|
164
|
+
// The node whose ABI native modules must target = whatever the daemon will run under. The bare
|
|
165
|
+
// notarized node and the adopted branded app's vendored node are BOTH pinned to NOTARIZED_NODE_VERSION
|
|
166
|
+
// (same ABI), so order is belt-and-suspenders; the app fallback only matters in the edge where the
|
|
167
|
+
// node fetch failed but the app fetch succeeded (the daemon then runs under the app's vendored node).
|
|
144
168
|
function daemonNodeForBuild() {
|
|
145
169
|
if (process.platform !== 'darwin') return process.execPath;
|
|
146
|
-
return validatedNotarizedNode() || process.execPath;
|
|
170
|
+
return validatedNotarizedNode() || validatedNotarizedApp() || process.execPath;
|
|
147
171
|
}
|
|
148
172
|
|
|
149
173
|
function nodeReportsVersion(bin, version) {
|
|
@@ -238,6 +262,124 @@ function disableNotarizedNode() {
|
|
|
238
262
|
try { fs.rmSync(notarizedNodePath(), { force: true }); } catch {}
|
|
239
263
|
}
|
|
240
264
|
|
|
265
|
+
// ── Branded notarized app adoption (mirrors the notarized-node fetch above) ──
|
|
266
|
+
|
|
267
|
+
function notarizedAppDir() {
|
|
268
|
+
return process.env.WALLE_NOTARIZED_APP_DIR || path.join(process.env.HOME, '.walle', 'notarized-app');
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// The vendored node inside the adopted Wall-E.app — the exact path the downloadable app uses as
|
|
272
|
+
// WALLE_NOTARIZED_NODE. It lives in Contents/Resources (NOT Contents/MacOS), so isBundleExec() is
|
|
273
|
+
// false for it and the screenshot bridge routes capture through it under the branded identity.
|
|
274
|
+
// A SEPARATE dir from ~/.walle/bundles (which buildAppBundles re-clones every update and would
|
|
275
|
+
// clobber the notarized app).
|
|
276
|
+
function notarizedAppVendoredNode() {
|
|
277
|
+
return path.join(notarizedAppDir(), 'Wall-E.app', 'Contents', 'Resources', 'node');
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// Returns the adopted app's vendored node ONLY when the marker matches the pinned app version (the
|
|
281
|
+
// marker is written last, so a partial/failed download is never trusted). Cheap — no codesign here;
|
|
282
|
+
// signature verification happens once at adopt time in ensureNotarizedBrandedApp.
|
|
283
|
+
function validatedNotarizedApp() {
|
|
284
|
+
if (process.platform !== 'darwin') return null;
|
|
285
|
+
if (process.env.WALLE_NO_BRANDED_APP === '1') return null;
|
|
286
|
+
const node = notarizedAppVendoredNode();
|
|
287
|
+
const marker = path.join(notarizedAppDir(), 'version');
|
|
288
|
+
try {
|
|
289
|
+
if (fs.existsSync(node) && fs.existsSync(marker) &&
|
|
290
|
+
fs.readFileSync(marker, 'utf8').trim() === NOTARIZED_APP_VERSION) {
|
|
291
|
+
return node;
|
|
292
|
+
}
|
|
293
|
+
} catch {}
|
|
294
|
+
return null;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// codesign --verify --strict gates adoption (catches a tampered/altered bundle); the vendored node
|
|
298
|
+
// must carry the expected Developer-ID Team Identifier (else it's not the branded notarized app and
|
|
299
|
+
// would prompt as "node" anyway). spctl is informational only.
|
|
300
|
+
function verifyNotarizedApp(appDir, { log } = {}) {
|
|
301
|
+
try {
|
|
302
|
+
execFileSync('codesign', ['--verify', '--strict', appDir], { stdio: 'pipe', timeout: 60000 });
|
|
303
|
+
} catch {
|
|
304
|
+
return false;
|
|
305
|
+
}
|
|
306
|
+
const vendored = path.join(appDir, 'Contents', 'Resources', 'node');
|
|
307
|
+
if (execTeamIdentifier(vendored) !== NOTARIZED_APP_TEAM_ID) return false;
|
|
308
|
+
if (log) {
|
|
309
|
+
let assessment;
|
|
310
|
+
try {
|
|
311
|
+
execFileSync('spctl', ['-a', '-vv', '--type', 'exec', vendored], { stdio: 'pipe', timeout: 30000 });
|
|
312
|
+
assessment = 'accepted (notarized)';
|
|
313
|
+
} catch { assessment = 'signature valid (spctl unconfirmed)'; }
|
|
314
|
+
log(` ${DIM}app signature: ${assessment}${RESET}`);
|
|
315
|
+
}
|
|
316
|
+
return true;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// Download (once) the prebuilt Developer-ID-notarized Wall-E.app and adopt its vendored node as the
|
|
320
|
+
// daemon identity. Returns the vendored-node path on success, else null (daemon keeps its current
|
|
321
|
+
// node). Best-effort: any failure is non-blocking. Mirrors ensureNotarizedDaemonNode's
|
|
322
|
+
// download→verify→marker-last discipline.
|
|
323
|
+
function ensureNotarizedBrandedApp({ log = console.log } = {}) {
|
|
324
|
+
if (process.platform !== 'darwin') return null;
|
|
325
|
+
if (process.env.WALLE_NO_BRANDED_APP === '1') return null;
|
|
326
|
+
const arch = process.arch === 'arm64' ? 'arm64' : process.arch === 'x64' ? 'x64' : null;
|
|
327
|
+
if (!arch) return null;
|
|
328
|
+
|
|
329
|
+
const dir = notarizedAppDir();
|
|
330
|
+
const appDir = path.join(dir, 'Wall-E.app');
|
|
331
|
+
const vendored = notarizedAppVendoredNode();
|
|
332
|
+
const marker = path.join(dir, 'version');
|
|
333
|
+
|
|
334
|
+
// Cached and still valid (marker + signature) → reuse (the "download once").
|
|
335
|
+
if (validatedNotarizedApp() && verifyNotarizedApp(appDir)) {
|
|
336
|
+
return vendored;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
let tmp;
|
|
340
|
+
try {
|
|
341
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
342
|
+
const asset = `Wall-E-${NOTARIZED_APP_VERSION}-${arch}.zip`;
|
|
343
|
+
const url = `${WALLE_DIST_RELEASE_BASE}/v${NOTARIZED_APP_VERSION}/${asset}`;
|
|
344
|
+
tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'walle-napp-'));
|
|
345
|
+
const zip = path.join(tmp, asset);
|
|
346
|
+
log(` Downloading notarized Wall-E.app ${NOTARIZED_APP_VERSION} (${arch})…`);
|
|
347
|
+
execFileSync('curl', ['-fsSL', '-o', zip, url], { timeout: 300000, stdio: 'pipe' });
|
|
348
|
+
// ditto -x -k preserves the code signature + stapled notarization ticket through extraction.
|
|
349
|
+
const exDir = path.join(tmp, 'x');
|
|
350
|
+
fs.mkdirSync(exDir, { recursive: true });
|
|
351
|
+
execFileSync('ditto', ['-x', '-k', zip, exDir], { timeout: 120000, stdio: 'pipe' });
|
|
352
|
+
const extractedApp = path.join(exDir, 'Wall-E.app');
|
|
353
|
+
if (!fs.existsSync(extractedApp)) throw new Error('archive missing Wall-E.app');
|
|
354
|
+
// Verify BEFORE adopting — never install an unsigned/wrong-team bundle.
|
|
355
|
+
if (!verifyNotarizedApp(extractedApp)) throw new Error('signature/team verification failed');
|
|
356
|
+
const exVendored = path.join(extractedApp, 'Contents', 'Resources', 'node');
|
|
357
|
+
if (!nodeReportsVersion(exVendored, NOTARIZED_NODE_VERSION)) throw new Error('vendored node ABI mismatch');
|
|
358
|
+
|
|
359
|
+
fs.rmSync(appDir, { recursive: true, force: true });
|
|
360
|
+
fs.rmSync(marker, { force: true });
|
|
361
|
+
fs.cpSync(extractedApp, appDir, { recursive: true });
|
|
362
|
+
fs.chmodSync(vendored, 0o755);
|
|
363
|
+
|
|
364
|
+
if (!verifyNotarizedApp(appDir, { log })) throw new Error('post-install verification failed');
|
|
365
|
+
fs.writeFileSync(marker, `${NOTARIZED_APP_VERSION}\n`);
|
|
366
|
+
log(` ${GREEN}Notarized Wall-E.app ready${RESET} ${DIM}(${appDir})${RESET}`);
|
|
367
|
+
return vendored;
|
|
368
|
+
} catch (e) {
|
|
369
|
+
log(` ${DIM}Notarized branded app unavailable (${e && e.message ? e.message : e}) — daemon keeps its current node${RESET}`);
|
|
370
|
+
disableNotarizedBrandedApp();
|
|
371
|
+
return null;
|
|
372
|
+
} finally {
|
|
373
|
+
if (tmp) { try { fs.rmSync(tmp, { recursive: true, force: true }); } catch {} }
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
// Drop the marker (and app) so daemonExec stops trusting it. Used when the download/verify fails.
|
|
378
|
+
function disableNotarizedBrandedApp() {
|
|
379
|
+
try { fs.rmSync(path.join(notarizedAppDir(), 'version'), { force: true }); } catch {}
|
|
380
|
+
try { fs.rmSync(path.join(notarizedAppDir(), 'Wall-E.app'), { recursive: true, force: true }); } catch {}
|
|
381
|
+
}
|
|
382
|
+
|
|
241
383
|
function writeCliLifecycleEvent(event, meta = {}) {
|
|
242
384
|
if (process.env.WALLE_TELEMETRY === '0' || process.env.WALLE_TELEMETRY === 'false') return;
|
|
243
385
|
try {
|
|
@@ -389,6 +531,9 @@ function install(targetDir) {
|
|
|
389
531
|
// Fetch the notarized daemon node BEFORE installing deps, so native modules build against
|
|
390
532
|
// its ABI (the daemon and its forks will all run under it). Best-effort / macOS-only.
|
|
391
533
|
ensureNotarizedDaemonNode();
|
|
534
|
+
// Adopt the branded notarized Wall-E.app so the Screen Recording grant shows "Wall-E" (not the
|
|
535
|
+
// anonymous "node"). Best-effort; daemonExec prefers its vendored node when present.
|
|
536
|
+
ensureNotarizedBrandedApp();
|
|
392
537
|
|
|
393
538
|
console.log(` Installing dependencies...\n`);
|
|
394
539
|
npmInstall(targetDir);
|
|
@@ -412,6 +557,10 @@ function install(targetDir) {
|
|
|
412
557
|
`CTM_PORT=${port}`,
|
|
413
558
|
`WALL_E_PORT=${wallePort}`,
|
|
414
559
|
'',
|
|
560
|
+
'# Terminal rendering: Option B (mosh-style server-authoritative state-sync) is on by default.',
|
|
561
|
+
'# Uncomment to fall back to the raw byte-stream render path.',
|
|
562
|
+
'# CTM_STATE_SYNC=0',
|
|
563
|
+
'',
|
|
415
564
|
'# SLACK_TOKEN=',
|
|
416
565
|
'# SLACK_OWNER_USER_ID=',
|
|
417
566
|
'# SLACK_OWNER_HANDLE=',
|
|
@@ -509,6 +658,9 @@ function update() {
|
|
|
509
658
|
// 6. Reinstall deps (in case package.json changed). Refresh the notarized daemon node first
|
|
510
659
|
// so native modules rebuild against its ABI (idempotent: a valid cache is reused, not re-DLed).
|
|
511
660
|
ensureNotarizedDaemonNode();
|
|
661
|
+
// Refresh the branded notarized Wall-E.app (idempotent) so the in-web upgrade migrates a user
|
|
662
|
+
// off the anonymous "node" Screen Recording identity onto the branded "Wall-E" one.
|
|
663
|
+
ensureNotarizedBrandedApp();
|
|
512
664
|
console.log(` Installing dependencies...\n`);
|
|
513
665
|
npmInstall(dir);
|
|
514
666
|
|
|
@@ -1537,4 +1689,12 @@ module.exports = {
|
|
|
1537
1689
|
notarizedNodeDir,
|
|
1538
1690
|
notarizedNodePath,
|
|
1539
1691
|
NOTARIZED_NODE_VERSION,
|
|
1692
|
+
ensureNotarizedBrandedApp,
|
|
1693
|
+
validatedNotarizedApp,
|
|
1694
|
+
verifyNotarizedApp,
|
|
1695
|
+
disableNotarizedBrandedApp,
|
|
1696
|
+
notarizedAppDir,
|
|
1697
|
+
notarizedAppVendoredNode,
|
|
1698
|
+
NOTARIZED_APP_VERSION,
|
|
1699
|
+
NOTARIZED_APP_TEAM_ID,
|
|
1540
1700
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-walle",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.30",
|
|
4
4
|
"description": "CTM + Wall-E — AI coding dashboard and personal digital twin agent. Multi-agent terminal for Claude Code, Codex, Gemini, Aider, OpenCode, and more, plus prompt editor, task queue, remote phone and tablet access, code/doc review, and an agent that learns from Slack, email & calendar.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"create-walle": "bin/create-walle.js"
|
|
@@ -9,10 +9,35 @@
|
|
|
9
9
|
# LaunchAgent plist never needs to change.
|
|
10
10
|
set -euo pipefail
|
|
11
11
|
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
|
12
|
-
|
|
12
|
+
# CTM_LAUNCH_NODE_BIN lets a test inject the pinned node without running node-bin.sh.
|
|
13
|
+
NODE_BIN="${CTM_LAUNCH_NODE_BIN:-"$(bash "$ROOT/bin/node-bin.sh")"}"
|
|
13
14
|
SERVER="$ROOT/claude-task-manager/server.js"
|
|
14
15
|
PINNED_V="$("$NODE_BIN" -v 2>/dev/null)"
|
|
15
16
|
|
|
17
|
+
# Exec the chosen runtime — or, under CTM_LAUNCH_PRINT_CHOICE (tests), print the path it WOULD exec
|
|
18
|
+
# and exit 0 without launching the server. Keeps the candidate ladder below assertable end-to-end.
|
|
19
|
+
_exec_choice() {
|
|
20
|
+
local bin="$1"; shift
|
|
21
|
+
if [ -n "${CTM_LAUNCH_PRINT_CHOICE:-}" ]; then printf '%s\n' "$bin"; exit 0; fi
|
|
22
|
+
exec "$bin" "$SERVER" "$@"
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
# Self-heal the native SQLite driver BEFORE exec'ing the server. A hard power-off can leave
|
|
26
|
+
# better_sqlite3.node torn on disk, and a Homebrew node upgrade can leave it built for the wrong
|
|
27
|
+
# ABI. The server DETECTS this but cannot hot-reload a native module mid-process, so it wedges
|
|
28
|
+
# the boot: DB-owner worker dies -> "Database not initialized" everywhere -> no port bound ->
|
|
29
|
+
# launchd respawns into the SAME broken state every ~5s (a respawn spiral that never converges).
|
|
30
|
+
# Running the preflight here rebuilds the driver if needed, so the fresh `exec` below loads a good
|
|
31
|
+
# binary. It is cheap on the happy path (a require + smoke-test that early-returns when the ABI
|
|
32
|
+
# already matches) and only does the heavy `npm rebuild` when the driver is genuinely broken.
|
|
33
|
+
# Best-effort: a failure here never blocks the boot — the server's own in-process repair still runs.
|
|
34
|
+
# Skipped under CTM_LAUNCH_PRINT_CHOICE: it is irrelevant to runtime selection and needs a real DB.
|
|
35
|
+
CHECK_DRIVER="$ROOT/claude-task-manager/bin/check-sqlite-driver.js"
|
|
36
|
+
if [ -z "${CTM_LAUNCH_PRINT_CHOICE:-}" ] && [ -f "$CHECK_DRIVER" ]; then
|
|
37
|
+
( cd "$ROOT/claude-task-manager" && "$NODE_BIN" "$CHECK_DRIVER" ) >/tmp/ctm-boot-sqlite-check.log 2>&1 \
|
|
38
|
+
|| echo "[ctm-launch] sqlite driver preflight reported a problem; see /tmp/ctm-boot-sqlite-check.log (boot continues; server self-repair still applies)" >&2
|
|
39
|
+
fi
|
|
40
|
+
|
|
16
41
|
# Prefer a STABLE-IDENTITY node so macOS TCC grants (e.g. the "Coding Task Manager would like to
|
|
17
42
|
# access data from other apps" prompt) PERSIST across restarts. macOS keys a TCC grant to the
|
|
18
43
|
# binary's code-signing Designated Requirement: a notarized / Developer-ID-signed node keeps its
|
|
@@ -23,39 +48,66 @@ PINNED_V="$("$NODE_BIN" -v 2>/dev/null)"
|
|
|
23
48
|
# bump can never select a mismatched-ABI runtime (preserves the "upgrade = edit .node-version"
|
|
24
49
|
# guarantee, and the daemon's native modules stay ABI-correct).
|
|
25
50
|
_ver_match() { [ -x "$1" ] && [ "$("$1" -v 2>/dev/null)" = "$PINNED_V" ]; }
|
|
51
|
+
# Does this binary carry a stable code-signing Team Identifier (Developer-ID signed)? Signal that a
|
|
52
|
+
# TCC/Full-Disk-Access grant will PERSIST and that prompts show a branded name. A fast local read.
|
|
53
|
+
# NB this alone CANNOT distinguish "our branded CTM bundle" from the bare notarized node — the
|
|
54
|
+
# notarized node is also Developer-ID signed (Team HX7739G8FX, Node.js Foundation). The discriminator
|
|
55
|
+
# is the code-signing IDENTIFIER (see _is_ctm_bundle_identity).
|
|
56
|
+
# NB capture codesign's output FIRST, then match the string — do NOT pipe `codesign | grep -q`.
|
|
57
|
+
# `grep -q` exits on the first match and closes the pipe; codesign then dies with SIGPIPE (141), and
|
|
58
|
+
# under `set -o pipefail` (above) the pipeline is reported as FAILED even though the pattern matched.
|
|
59
|
+
# That false-negative is exactly what made the daemon keep running the wrong node. Capturing to a
|
|
60
|
+
# var runs codesign to completion (exit 0), then a here-string grep has no upstream to kill.
|
|
61
|
+
_has_team_id() { local o; o="$(codesign -dv "$1" 2>&1)" || true; grep -q '^TeamIdentifier=[A-Z0-9]' <<<"$o"; }
|
|
62
|
+
# Is this binary OUR branded CTM bundle (Identifier=com.walle.ctm)? That is the EXACT identity the
|
|
63
|
+
# Full Disk Access banner tells the user to grant ("Coding Task Manager.app"). The notarized node's
|
|
64
|
+
# Identifier is "node", so this cleanly separates the two.
|
|
65
|
+
_is_ctm_bundle_identity() { local o; o="$(codesign -dv "$1" 2>&1)" || true; grep -q '^Identifier=com\.walle\.ctm$' <<<"$o"; }
|
|
26
66
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
#
|
|
30
|
-
#
|
|
31
|
-
#
|
|
67
|
+
CTM_BUNDLE="$HOME/.walle/bundles/Coding Task Manager.app/Contents/MacOS/Coding Task Manager"
|
|
68
|
+
|
|
69
|
+
# 0) The branded CTM bundle (Identifier=com.walle.ctm) — the EXACT identity the FDA banner asks the
|
|
70
|
+
# user to grant Full Disk Access to. Prefer it ABOVE the stable-node marker. WHY first: the marker
|
|
71
|
+
# written by ensure-stable-node.js can point at the bare notarized node ("node", Team HX7739G8FX);
|
|
72
|
+
# because that node IS Developer-ID signed it would satisfy a naive "has a Team Identifier" check
|
|
73
|
+
# and win — the daemon then runs as "node", the user's grant to "Coding Task Manager.app" never
|
|
74
|
+
# applies, and the macOS "network volume"/FDA prompt recurs on EVERY restart. Checking THIS bundle
|
|
75
|
+
# path + its com.walle.ctm identity makes the grant-matching runtime authoritative. Gated on the
|
|
76
|
+
# version (ABI) match too. Costs one fast `codesign -dv` read per boot (boots are rare); on a
|
|
77
|
+
# no-Dev-ID machine the bundle is absent/self-signed so this falls through with no behavior change.
|
|
78
|
+
if _ver_match "$CTM_BUNDLE" && _is_ctm_bundle_identity "$CTM_BUNDLE" && _has_team_id "$CTM_BUNDLE"; then
|
|
79
|
+
_exec_choice "$CTM_BUNDLE" "$@"
|
|
80
|
+
fi
|
|
81
|
+
# 1) The stable daemon node chosen by bin/ensure-stable-node.js (run off the boot path from
|
|
82
|
+
# restart-ctm.sh). On a machine WITHOUT a Developer ID this is the bare notarized node — the best
|
|
83
|
+
# stable identity available there. Version-gated; the version check rejects a stale marker.
|
|
32
84
|
MARKER="$HOME/.walle/.stable-daemon-node"
|
|
33
85
|
if [ -f "$MARKER" ]; then
|
|
34
86
|
STABLE_NODE="$(head -n1 "$MARKER" 2>/dev/null)"
|
|
35
87
|
if [ -n "$STABLE_NODE" ] && _ver_match "$STABLE_NODE"; then
|
|
36
|
-
|
|
88
|
+
_exec_choice "$STABLE_NODE" "$@"
|
|
37
89
|
fi
|
|
38
90
|
fi
|
|
39
|
-
#
|
|
91
|
+
# 2) Notarized node handed in by the downloadable Developer-ID Wall-E.app.
|
|
40
92
|
if [ -n "${WALLE_NOTARIZED_NODE:-}" ] && _ver_match "$WALLE_NOTARIZED_NODE"; then
|
|
41
|
-
|
|
93
|
+
_exec_choice "$WALLE_NOTARIZED_NODE" "$@"
|
|
42
94
|
fi
|
|
43
|
-
# 2) The
|
|
44
|
-
#
|
|
45
|
-
#
|
|
46
|
-
|
|
47
|
-
if _ver_match "$
|
|
48
|
-
|
|
95
|
+
# 2.5) The adopted Developer-ID-notarized Wall-E.app's vendored node (create-walle
|
|
96
|
+
# ensureNotarizedBrandedApp) — branded "Wall-E" AND grant-persisting, for no-Dev-ID machines.
|
|
97
|
+
# Gated on the version match + a stable Team Identifier (skips a self-signed/wrong bundle).
|
|
98
|
+
BRANDED_APP_NODE="$HOME/.walle/notarized-app/Wall-E.app/Contents/Resources/node"
|
|
99
|
+
if _ver_match "$BRANDED_APP_NODE" && _has_team_id "$BRANDED_APP_NODE"; then
|
|
100
|
+
_exec_choice "$BRANDED_APP_NODE" "$@"
|
|
49
101
|
fi
|
|
50
102
|
# 3) Notarized node we provisioned ourselves (~/.walle/notarized-node) — stable but anonymous
|
|
51
103
|
# ("node"); the fallback for machines without a Developer ID.
|
|
52
104
|
NOTARIZED_NODE="$HOME/.walle/notarized-node/bin/node"
|
|
53
105
|
if _ver_match "$NOTARIZED_NODE"; then
|
|
54
|
-
|
|
106
|
+
_exec_choice "$NOTARIZED_NODE" "$@"
|
|
55
107
|
fi
|
|
56
108
|
# 4) The branded bundle node even if only self-signed (branded name, but may re-prompt).
|
|
57
109
|
if _ver_match "$CTM_BUNDLE"; then
|
|
58
|
-
|
|
110
|
+
_exec_choice "$CTM_BUNDLE" "$@"
|
|
59
111
|
fi
|
|
60
112
|
# 5) Last resort: the pinned node (ABI-correct, but no stable TCC identity → may re-prompt).
|
|
61
|
-
|
|
113
|
+
_exec_choice "$NODE_BIN" "$@"
|
package/template/bin/dev.sh
CHANGED
|
@@ -164,6 +164,19 @@ mkdir -p "$DEV_DIR"
|
|
|
164
164
|
# (corruption + contention). A throwaway CODEX_HOME keeps dev codex writes — and
|
|
165
165
|
# CTM's own rollout reads, which resolve via the same CODEX_HOME — inside DEV_DIR.
|
|
166
166
|
mkdir -p "$DEV_DIR/codex/sessions"
|
|
167
|
+
# Seed the throwaway CODEX_HOME with the user's existing Codex login. The isolation above
|
|
168
|
+
# relocates CODEX_HOME, which ALSO relocates ~/.codex/auth.json — so without this, dev codex
|
|
169
|
+
# sessions are logged out and prompt for `codex login` (the "codex login issue in staging").
|
|
170
|
+
# Copy only auth.json from the real ~/.codex so dev codex is authenticated while rollouts/sessions
|
|
171
|
+
# stay isolated in DEV_DIR. We deliberately do NOT copy config.toml — it can carry MCP entries that
|
|
172
|
+
# point at the user's primary (e.g. the node_repl missing-binary noise). Idempotent: never clobber
|
|
173
|
+
# an existing dev login.
|
|
174
|
+
_REAL_CODEX_HOME="$HOME/.codex"
|
|
175
|
+
if [[ -f "$_REAL_CODEX_HOME/auth.json" && ! -f "$DEV_DIR/codex/auth.json" ]]; then
|
|
176
|
+
cp "$_REAL_CODEX_HOME/auth.json" "$DEV_DIR/codex/auth.json" 2>/dev/null \
|
|
177
|
+
&& chmod 600 "$DEV_DIR/codex/auth.json" 2>/dev/null \
|
|
178
|
+
&& echo " seeded dev CODEX_HOME auth from $_REAL_CODEX_HOME/auth.json"
|
|
179
|
+
fi
|
|
167
180
|
|
|
168
181
|
cleanup_processes() {
|
|
169
182
|
local stale_args=()
|
|
@@ -221,6 +234,11 @@ elif [[ "$MODE" == "refresh" ]]; then
|
|
|
221
234
|
elif [[ "$COPY_IMAGES" != "1" ]]; then
|
|
222
235
|
echo " Images: skipped (--no-images)"
|
|
223
236
|
fi
|
|
237
|
+
rm -rf "$DEV_DIR/scrollback"
|
|
238
|
+
if [[ -d "$PROD_CTM_DIR/scrollback" ]]; then
|
|
239
|
+
echo " Scrollback: syncing $PROD_CTM_DIR/scrollback -> $DEV_DIR/scrollback"
|
|
240
|
+
"$NODE_BIN" "$ROOT/bin/sync-images.js" "$PROD_CTM_DIR/scrollback" "$DEV_DIR/scrollback"
|
|
241
|
+
fi
|
|
224
242
|
# Ensure the dev instance owns its WAL files from first open.
|
|
225
243
|
rm -f "$DEV_DIR"/*.db-wal "$DEV_DIR"/*.db-shm
|
|
226
244
|
elif [[ "$MODE" == "reuse" ]]; then
|
|
@@ -65,6 +65,17 @@ function resolveStableNode(deps) {
|
|
|
65
65
|
if (t.exec === ctmExec && team) ctmTeam = team;
|
|
66
66
|
}
|
|
67
67
|
if (ctmTeam) return ctmExec;
|
|
68
|
+
// Re-signing did not land a Team Identifier THIS run (e.g. a transient codesign failure), but
|
|
69
|
+
// the CTM bundle may ALREADY be Developer-ID-signed on disk from a prior run — that signature
|
|
70
|
+
// is self-contained and stays valid regardless. Prefer it over the anonymous notarized node so
|
|
71
|
+
// the marker records the BRANDED identity and the daemon keeps matching the user's Full Disk
|
|
72
|
+
// Access grant to "Coding Task Manager.app" (otherwise it runs as "node" → the FDA banner
|
|
73
|
+
// re-prompts every restart). Gated on existing + version-matching the pin (ABI safety).
|
|
74
|
+
if (ctmExec && existsSync(ctmExec) && (!pin || cw.nodeReportsVersion(ctmExec, pin))
|
|
75
|
+
&& codesign.localCodeSignTeamId && codesign.localCodeSignTeamId(ctmExec)) {
|
|
76
|
+
log('CTM bundle already Developer-ID-signed on disk → using it (skip notarized fallback)');
|
|
77
|
+
return ctmExec;
|
|
78
|
+
}
|
|
68
79
|
} catch (e) {
|
|
69
80
|
log(`bundle signing failed: ${e && e.message ? e.message : e}`);
|
|
70
81
|
}
|
package/template/bin/node-bin.sh
CHANGED
|
@@ -44,6 +44,15 @@ if [[ -z "$PIN" ]]; then
|
|
|
44
44
|
fi
|
|
45
45
|
|
|
46
46
|
candidates=(
|
|
47
|
+
# Immutable, self-managed runtimes FIRST. A `brew upgrade node` (often dragged in by an
|
|
48
|
+
# unrelated `brew install`) repoints or deletes the Cellar dir below, which would otherwise
|
|
49
|
+
# leave this resolver unable to find the pin -> daemon won't start. These vendored nodes are
|
|
50
|
+
# provisioned by bin/ensure-stable-node.js / create-walle and are not touched by Homebrew, so
|
|
51
|
+
# resolution survives that churn. Every candidate is STILL gated on an exact version match with
|
|
52
|
+
# the pin in the loop below, so a stale vendored node is skipped (it can never select a wrong ABI).
|
|
53
|
+
"$HOME/.walle/notarized-node/bin/node"
|
|
54
|
+
"$HOME/.walle/notarized-app/Wall-E.app/Contents/Resources/node"
|
|
55
|
+
"$HOME/.walle/bundles/Coding Task Manager.app/Contents/MacOS/Coding Task Manager"
|
|
47
56
|
"/opt/homebrew/Cellar/node/$PIN/bin/node"
|
|
48
57
|
"/usr/local/Cellar/node/$PIN/bin/node"
|
|
49
58
|
"$HOME/.fnm/node-versions/v$PIN/installation/bin/node"
|