create-walle 0.9.27 → 0.9.29
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 +3 -2
- package/bin/create-walle.js +166 -6
- package/package.json +1 -1
- package/template/bin/ctm-launch.sh +7 -0
- package/template/bin/dev.sh +5 -0
- package/template/claude-task-manager/api-prompts.js +32 -15
- package/template/claude-task-manager/db.js +717 -38
- 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-status-redesign.html +554 -0
- package/template/claude-task-manager/docs/terminal-rendering-redesign.html +529 -0
- package/template/claude-task-manager/lib/auth-rules.js +13 -0
- package/template/claude-task-manager/lib/codex-rollout-snapshot.js +53 -0
- package/template/claude-task-manager/lib/flush-redraw-markers.js +72 -0
- package/template/claude-task-manager/lib/headless-term-service.js +246 -4
- package/template/claude-task-manager/lib/macos-capabilities.js +190 -0
- 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/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 +226 -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/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 +5 -1
- 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-projection.js +231 -4
- package/template/claude-task-manager/lib/session-stream.js +19 -1
- package/template/claude-task-manager/lib/state-sync/cell-diff.js +41 -0
- package/template/claude-task-manager/lib/state-sync/frame-emitter.js +173 -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-store.js +87 -6
- package/template/claude-task-manager/lib/ttl-memo.js +61 -0
- package/template/claude-task-manager/lib/wal-checkpoint-policy.js +40 -0
- 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/index.html +1778 -181
- package/template/claude-task-manager/public/js/activation-render-check.js +40 -2
- 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 +374 -0
- package/template/claude-task-manager/public/js/session-status-precedence.js +7 -173
- package/template/claude-task-manager/public/js/setup.js +120 -4
- package/template/claude-task-manager/public/js/state-sync-client.js +218 -0
- package/template/claude-task-manager/public/js/state-sync-predictor.js +41 -0
- package/template/claude-task-manager/public/js/stream-view.js +169 -11
- package/template/claude-task-manager/public/js/terminal-reconciler.js +24 -4
- package/template/claude-task-manager/public/js/walle-session.js +28 -0
- package/template/claude-task-manager/public/js/walle.js +26 -9
- package/template/claude-task-manager/queue-engine.js +140 -0
- package/template/claude-task-manager/server.js +2777 -414
- package/template/claude-task-manager/session-integrity.js +16 -1
- package/template/claude-task-manager/workers/db-owner-worker.js +8 -0
- package/template/claude-task-manager/workers/read-pool-worker.js +18 -1
- package/template/claude-task-manager/workers/session-host-pool-process.js +188 -0
- package/template/claude-task-manager/workers/session-host-process.js +42 -11
- package/template/package.json +1 -1
- package/template/wall-e/agent.js +167 -16
- package/template/wall-e/api-walle.js +85 -10
- package/template/wall-e/auth/flow-manager.js +78 -1
- package/template/wall-e/auth/provider-flows.js +56 -2
- package/template/wall-e/brain.js +85 -15
- package/template/wall-e/embeddings.js +262 -17
- 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 +109 -0
- package/template/wall-e/lib/brain-owner-worker-client.js +16 -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/runtime-process-inventory.js +114 -0
- package/template/wall-e/lib/runtime-worker-pool.js +200 -23
- package/template/wall-e/lib/scheduler-worker-jobs.js +172 -121
- package/template/wall-e/lib/scheduler.js +321 -38
- package/template/wall-e/lib/worker-thread-pool.js +55 -7
- package/template/wall-e/llm/claude-cli.js +21 -3
- package/template/wall-e/llm/cli-binary.js +77 -0
- package/template/wall-e/llm/codex-cli.js +22 -3
- package/template/wall-e/llm/default-fallback.js +10 -4
- package/template/wall-e/llm/mlx.js +46 -8
- package/template/wall-e/llm/ollama-library.js +126 -0
- package/template/wall-e/llm/ollama.js +13 -0
- package/template/wall-e/llm/provider-backpressure.js +134 -0
- package/template/wall-e/llm/provider-detector.js +112 -22
- package/template/wall-e/llm/provider-health-state.js +24 -0
- package/template/wall-e/loops/backfill.js +43 -16
- package/template/wall-e/loops/initiative.js +1 -0
- package/template/wall-e/loops/tasks.js +521 -25
- package/template/wall-e/loops/think.js +12 -2
- 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 +180 -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 +267 -41
- 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-fallback.js +34 -1
- package/template/wall-e/skills/skill-planner.js +60 -2
- package/template/wall-e/telemetry.js +42 -7
- package/template/wall-e/training/real-trajectory-miner.js +24 -114
- package/template/wall-e/workers/brain-owner-worker.js +8 -0
- package/template/wall-e/workers/runtime-worker.js +9 -1
- package/template/website/index.html +8 -0
package/README.md
CHANGED
|
@@ -16,6 +16,7 @@ A web dashboard for running and managing AI coding sessions across multiple prov
|
|
|
16
16
|
- **Code & Doc Review** — Review git diffs and Markdown docs side by side, add anchored comments, and send feedback into an agent session or queue
|
|
17
17
|
- **Model Registry** — Manage providers (Anthropic, OpenAI, Google, DeepSeek, Ollama, LM Studio, MLX, and CLI subscription providers), compare pricing, switch models per session
|
|
18
18
|
- **Session Insights** — Analyze patterns across sessions to optimize prompts and workflows
|
|
19
|
+
- **Reliable Setup & Updates** — Install and update checks keep native dependencies aligned with your active Node.js runtime, with optional Node pinning for multi-version machines
|
|
19
20
|
|
|
20
21
|
### Wall-E (Personal Digital Twin)
|
|
21
22
|
|
|
@@ -39,9 +40,9 @@ This copies the project, installs dependencies, auto-detects your name and timez
|
|
|
39
40
|
|
|
40
41
|
### Download for Mac (no terminal)
|
|
41
42
|
|
|
42
|
-
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):
|
|
43
44
|
|
|
44
|
-
**⬇ [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)
|
|
45
46
|
|
|
46
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.
|
|
47
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.29",
|
|
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"
|
|
@@ -47,6 +47,13 @@ CTM_BUNDLE="$HOME/.walle/bundles/Coding Task Manager.app/Contents/MacOS/Coding T
|
|
|
47
47
|
if _ver_match "$CTM_BUNDLE" && codesign -dv "$CTM_BUNDLE" 2>&1 | grep -q '^TeamIdentifier=[A-Z0-9]'; then
|
|
48
48
|
exec "$CTM_BUNDLE" "$SERVER" "$@"
|
|
49
49
|
fi
|
|
50
|
+
# 2.5) The adopted Developer-ID-notarized Wall-E.app's vendored node (create-walle
|
|
51
|
+
# ensureNotarizedBrandedApp) — branded "Wall-E" AND grant-persisting, for no-Dev-ID machines.
|
|
52
|
+
# Gated on the version match + a stable Team Identifier (skips a self-signed/wrong bundle).
|
|
53
|
+
BRANDED_APP_NODE="$HOME/.walle/notarized-app/Wall-E.app/Contents/Resources/node"
|
|
54
|
+
if _ver_match "$BRANDED_APP_NODE" && codesign -dv "$BRANDED_APP_NODE" 2>&1 | grep -q '^TeamIdentifier=[A-Z0-9]'; then
|
|
55
|
+
exec "$BRANDED_APP_NODE" "$SERVER" "$@"
|
|
56
|
+
fi
|
|
50
57
|
# 3) Notarized node we provisioned ourselves (~/.walle/notarized-node) — stable but anonymous
|
|
51
58
|
# ("node"); the fallback for machines without a Developer ID.
|
|
52
59
|
NOTARIZED_NODE="$HOME/.walle/notarized-node/bin/node"
|
package/template/bin/dev.sh
CHANGED
|
@@ -221,6 +221,11 @@ elif [[ "$MODE" == "refresh" ]]; then
|
|
|
221
221
|
elif [[ "$COPY_IMAGES" != "1" ]]; then
|
|
222
222
|
echo " Images: skipped (--no-images)"
|
|
223
223
|
fi
|
|
224
|
+
rm -rf "$DEV_DIR/scrollback"
|
|
225
|
+
if [[ -d "$PROD_CTM_DIR/scrollback" ]]; then
|
|
226
|
+
echo " Scrollback: syncing $PROD_CTM_DIR/scrollback -> $DEV_DIR/scrollback"
|
|
227
|
+
"$NODE_BIN" "$ROOT/bin/sync-images.js" "$PROD_CTM_DIR/scrollback" "$DEV_DIR/scrollback"
|
|
228
|
+
fi
|
|
224
229
|
# Ensure the dev instance owns its WAL files from first open.
|
|
225
230
|
rm -f "$DEV_DIR"/*.db-wal "$DEV_DIR"/*.db-shm
|
|
226
231
|
elif [[ "$MODE" == "reuse" ]]; then
|
|
@@ -1053,7 +1053,6 @@ const {
|
|
|
1053
1053
|
createCodexUserDeduper,
|
|
1054
1054
|
parseCodexJsonlFileIntoMessagesAsync,
|
|
1055
1055
|
parseCodexJsonlFileIntoMessages,
|
|
1056
|
-
parseCodexJsonlIntoMessages,
|
|
1057
1056
|
readCodexRolloutMetadata,
|
|
1058
1057
|
} = require('./lib/session-history');
|
|
1059
1058
|
const fsp = require('fs').promises;
|
|
@@ -1592,17 +1591,6 @@ function _codexUserDeduperFromMessages(messages) {
|
|
|
1592
1591
|
return createCodexUserDeduper((Array.isArray(messages) ? messages : []).filter(msg => msg && msg.role === 'user'));
|
|
1593
1592
|
}
|
|
1594
1593
|
|
|
1595
|
-
async function _readFileRange(filePath, start, length) {
|
|
1596
|
-
const fh = await fsp.open(filePath, 'r');
|
|
1597
|
-
try {
|
|
1598
|
-
const buf = Buffer.alloc(length);
|
|
1599
|
-
const { bytesRead } = await fh.read(buf, 0, length, start);
|
|
1600
|
-
return buf.subarray(0, bytesRead).toString('utf8');
|
|
1601
|
-
} finally {
|
|
1602
|
-
await fh.close();
|
|
1603
|
-
}
|
|
1604
|
-
}
|
|
1605
|
-
|
|
1606
1594
|
function _conversationImportIndexRows() {
|
|
1607
1595
|
// Attribution: two full-table index scans built on every conversation-import
|
|
1608
1596
|
// tick. They run as the sync prefix of _conversationImportCandidates, which is
|
|
@@ -1794,8 +1782,15 @@ async function _importCodexSessionFile(parsed, filePath, options = {}) {
|
|
|
1794
1782
|
const newMessages = [];
|
|
1795
1783
|
let parsedTail;
|
|
1796
1784
|
if (prevFileSize > 0 && parsed.fileSize > prevFileSize) {
|
|
1797
|
-
|
|
1798
|
-
|
|
1785
|
+
// Stream the new tail [prevFileSize, EOF) in bounded 1 MiB chunks, line-by-line. Reading the
|
|
1786
|
+
// whole delta in one fs.read (the old _readFileRange path) passed a >2 GiB `length` for a
|
|
1787
|
+
// session that grew >2 GiB since the last import, tripping V8's Int32 assertion in node::fs::Read
|
|
1788
|
+
// and ABORTING the process (uncatchable) → launchd respawn loop → all Codex sessions unusable.
|
|
1789
|
+
parsedTail = await parseCodexJsonlFileIntoMessagesAsync(filePath, newMessages, {
|
|
1790
|
+
codexUserDeduper,
|
|
1791
|
+
startOffset: prevFileSize,
|
|
1792
|
+
yieldAfterMs: options.yieldAfterMs || 25,
|
|
1793
|
+
});
|
|
1799
1794
|
} else if (options.cooperative) {
|
|
1800
1795
|
parsedTail = await parseCodexJsonlFileIntoMessagesAsync(filePath, newMessages, {
|
|
1801
1796
|
codexUserDeduper,
|
|
@@ -2407,6 +2402,16 @@ function handleGetConversation(req, res, url) {
|
|
|
2407
2402
|
const sessionId = url.pathname.split('/').pop();
|
|
2408
2403
|
const conv = db.getSessionConversation(sessionId);
|
|
2409
2404
|
if (!conv) return jsonResponse(res, 404, { error: 'Not found' });
|
|
2405
|
+
// The stored `messages` blob is retired ('[]') by default — the conversation lives in the
|
|
2406
|
+
// faithful session_message_rows. Hydrate the blob from rows for this cold API read so callers
|
|
2407
|
+
// still receive the turns. O(N) reconstruction, paid only on this rare endpoint (the hot UI
|
|
2408
|
+
// paths page rows directly and never hit this).
|
|
2409
|
+
if ((!conv.messages || conv.messages === '[]') && typeof db.getSessionMessagesArray === 'function') {
|
|
2410
|
+
try {
|
|
2411
|
+
const rows = db.getSessionMessagesArray(sessionId, { fallbackToBlob: false });
|
|
2412
|
+
if (Array.isArray(rows) && rows.length) conv.messages = JSON.stringify(rows);
|
|
2413
|
+
} catch { /* fall through with the empty blob */ }
|
|
2414
|
+
}
|
|
2410
2415
|
jsonResponse(res, 200, conv);
|
|
2411
2416
|
}
|
|
2412
2417
|
|
|
@@ -2926,10 +2931,22 @@ function screenshotResponsibleContext() {
|
|
|
2926
2931
|
try { nodeIsBundle = require('./lib/real-node').isBundleExec(node); } catch {}
|
|
2927
2932
|
const useBridge = disclaimOk && !!node && !nodeIsBundle;
|
|
2928
2933
|
const responsiblePath = useBridge ? node : process.execPath;
|
|
2929
|
-
const responsibleName =
|
|
2934
|
+
const responsibleName = screenshotResponsibleDisplayName(responsiblePath);
|
|
2930
2935
|
return { realNode: node, disclaim, useBridge, responsiblePath, responsibleName, granted };
|
|
2931
2936
|
}
|
|
2932
2937
|
|
|
2938
|
+
// macOS attributes the Screen Recording grant to the responsible binary's code SIGNATURE, and shows
|
|
2939
|
+
// the enclosing .app's CFBundleName. The adopted Developer-ID-notarized app's vendored node
|
|
2940
|
+
// (~/.walle/notarized-app/Wall-E.app/Contents/Resources/node, create-walle ensureNotarizedBrandedApp)
|
|
2941
|
+
// and the branded daemon bundles list as "Wall-E"/"Coding Task Manager", not the basename "node" —
|
|
2942
|
+
// so the guidance must name the entry the user will actually see, else they grant the wrong thing.
|
|
2943
|
+
function screenshotResponsibleDisplayName(responsiblePath) {
|
|
2944
|
+
if (!responsiblePath) return 'CTM';
|
|
2945
|
+
if (/(?:^|\/)Wall-E\.app\//.test(responsiblePath) || responsiblePath.includes('/.walle/notarized-app/')) return 'Wall-E';
|
|
2946
|
+
if (/(?:^|\/)Coding Task Manager\.app\//.test(responsiblePath)) return 'Coding Task Manager';
|
|
2947
|
+
return path.basename(responsiblePath);
|
|
2948
|
+
}
|
|
2949
|
+
|
|
2933
2950
|
function screenshotCaptureCommand(tmpFile, context = {}) {
|
|
2934
2951
|
if (context.useBridge) {
|
|
2935
2952
|
return {
|