agentbox-app 0.1.12 → 0.1.13
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/main/acp.mjs +296 -0
- package/main/agent-homes.mjs +158 -0
- package/main/agent-sessions.mjs +57 -20
- package/main/agent-updates.mjs +9 -22
- package/main/analytics.mjs +30 -0
- package/main/claude-bin.mjs +45 -311
- package/main/claude-input.mjs +9 -3
- package/main/claude-launch-env.mjs +54 -0
- package/main/claude-models.mjs +39 -7
- package/main/claude-plan.mjs +16 -16
- package/main/claude-usage.mjs +82 -25
- package/main/codex-bin.mjs +51 -249
- package/main/codex-session.mjs +38 -3
- package/main/codex-threads.mjs +41 -8
- package/main/codex-watch.mjs +67 -3
- package/main/config.mjs +26 -4
- package/main/corner-tag.mjs +489 -0
- package/main/crash-report.mjs +26 -3
- package/main/engine-bin.mjs +595 -0
- package/main/engine-setup.mjs +4 -15
- package/main/feedback.mjs +11 -6
- package/main/harnesses/claude-stream.mjs +208 -0
- package/main/harnesses/claude.mjs +84 -0
- package/main/harnesses/codex.mjs +65 -0
- package/main/harnesses/common.mjs +78 -0
- package/main/harnesses/instructions-file.mjs +116 -0
- package/main/harnesses/opencode.mjs +234 -0
- package/main/harnesses.mjs +65 -0
- package/main/importable-threads.mjs +38 -0
- package/main/ipc.mjs +165 -31
- package/main/local-app-frames.mjs +82 -0
- package/main/main.mjs +100 -4
- package/main/memory-gate-history.mjs +51 -12
- package/main/memory-gate-server.mjs +67 -11
- package/main/memory-gate.mjs +49 -8
- package/main/memory-log.mjs +124 -0
- package/main/notify.mjs +47 -4
- package/main/opencode-server.mjs +271 -0
- package/main/opencode.mjs +287 -0
- package/main/phone-link.mjs +551 -0
- package/main/relay-address.mjs +28 -0
- package/main/relay-client.mjs +246 -0
- package/main/row-label.mjs +6 -6
- package/main/runs-on.mjs +32 -35
- package/main/serve.mjs +113 -9
- package/main/settings.mjs +148 -10
- package/main/shell-path.mjs +86 -0
- package/main/ship-queue.mjs +24 -6
- package/main/sign-in-files.mjs +3 -7
- package/main/source-updater.mjs +27 -1
- package/main/start-hold.mjs +73 -0
- package/main/store.mjs +1 -1
- package/main/supervisor.mjs +832 -428
- package/main/task-commands.mjs +13 -17
- package/main/task-folders-offthread.mjs +6 -1
- package/main/task-folders.mjs +490 -98
- package/main/task-terminals.mjs +37 -7
- package/main/updater.mjs +71 -9
- package/main/worktree-include.mjs +494 -0
- package/package.json +10 -1
- package/renderer/dist/assets/abnfDiagram-YUKMZFIV-DPPuMWt-.js +1 -0
- package/renderer/dist/assets/arc-HmFfdseH.js +1 -0
- package/renderer/dist/assets/architectureDiagram-47P4ROYG-DxJ3zHrH.js +36 -0
- package/renderer/dist/assets/blockDiagram-E7TT5TSB-CbTXzOqQ.js +140 -0
- package/renderer/dist/assets/c4Diagram-CRA5TL53-X5ItFyLn.js +38 -0
- package/renderer/dist/assets/channel-BlT2g9Sz.js +1 -0
- package/renderer/dist/assets/chunk-2Q5K7J3B-DC12Ho_L.js +1 -0
- package/renderer/dist/assets/chunk-5VM5RSS4-ClVtfPd0.js +15 -0
- package/renderer/dist/assets/chunk-7TKQ45FW-DgcOm0HT.js +1 -0
- package/renderer/dist/assets/chunk-AW2ZBBNX-C01s4yoF.js +88 -0
- package/renderer/dist/assets/chunk-FVRAUYC3-Bj0zxhR4.js +2 -0
- package/renderer/dist/assets/chunk-HTAEGDNF-DWDeEhLU.js +1 -0
- package/renderer/dist/assets/chunk-JWPE2WC7-Dsqwv3eW.js +1 -0
- package/renderer/dist/assets/chunk-KQW6MTUR-CE86ANL2.js +1 -0
- package/renderer/dist/assets/chunk-XXDRQBXY-CoNTASfs.js +1 -0
- package/renderer/dist/assets/classDiagram-v2-K4WV4PDN-2wprLqtw.js +217 -0
- package/renderer/dist/assets/cornerTag-BLrxiDE5.css +1 -0
- package/renderer/dist/assets/cornerTag-BOT6u3Tn.js +1 -0
- package/renderer/dist/assets/cose-bilkent-JH36ORCC-enqMiYbn.js +1 -0
- package/renderer/dist/assets/cynefin-EF2NZ3EQ-DHngLxvQ.js +141 -0
- package/renderer/dist/assets/cynefinDiagram-3GCD6N5R-C0rZZIl3.js +62 -0
- package/renderer/dist/assets/cytoscape.esm-C5JX3QLV.js +331 -0
- package/renderer/dist/assets/dagre-W4DXFKR2-DQnaEWO7.js +4 -0
- package/renderer/dist/assets/defaultLocale-DX6XiGOO.js +1 -0
- package/renderer/dist/assets/diagram-2UJZ2QOL-CS8AlGPx.js +200 -0
- package/renderer/dist/assets/diagram-OVF4WLC6-yiDMHIkY.js +24 -0
- package/renderer/dist/assets/diagram-PFPMY2P6-XbQKzbBA.js +30 -0
- package/renderer/dist/assets/diagram-UMYRVEAY-dhVvlfl_.js +24 -0
- package/renderer/dist/assets/diagram-YEKJPTXX-DZHe8SCf.js +41 -0
- package/renderer/dist/assets/diagram-ZIFT7M5P-B5h6aXVR.js +3 -0
- package/renderer/dist/assets/ebnfDiagram-VR2GEFS7-PR9ScDco.js +1 -0
- package/renderer/dist/assets/elk-IJKZMXRS-Cg-yQXPd.js +27 -0
- package/renderer/dist/assets/erDiagram-O2IAPWRE-omOrJMxu.js +99 -0
- package/renderer/dist/assets/flowDiagram-OXPTDLAJ-aOX2hzvT.js +213 -0
- package/renderer/dist/assets/ganttDiagram-R7TSEDQI-B1H3pWyI.js +292 -0
- package/renderer/dist/assets/gitGraphDiagram-XJZIOB7I-Cvw304HC.js +106 -0
- package/renderer/dist/assets/graph-DGbsTEsI.js +1 -0
- package/renderer/dist/assets/infoDiagram-5W2HQ5XZ-_ZXcNViH.js +2 -0
- package/renderer/dist/assets/init-Gi6I4Gst.js +1 -0
- package/renderer/dist/assets/ishikawaDiagram-K3B6WC7H-C7Mx_qQk.js +70 -0
- package/renderer/dist/assets/journeyDiagram-COXZFDF6-fr62QoAt.js +139 -0
- package/renderer/dist/assets/kanban-definition-P3RFRI5V-J8zsabCe.js +89 -0
- package/renderer/dist/assets/katex-C5jXJg4s.js +257 -0
- package/renderer/dist/assets/linear-BmNdEtIi.js +1 -0
- package/renderer/dist/assets/main-ChNdnUvt.js +1132 -0
- package/renderer/dist/assets/main-nj0QR5kg.css +32 -0
- package/renderer/dist/assets/mermaid.core-CnYzvFkz.js +314 -0
- package/renderer/dist/assets/mindmap-definition-LZFPQGKD-Bfcxc7PM.js +96 -0
- package/renderer/dist/assets/ordinal-Cboi1Yqb.js +1 -0
- package/renderer/dist/assets/pegDiagram-BGZESJAR-CwUJOq6X.js +1 -0
- package/renderer/dist/assets/pieDiagram-CAPJLFHJ-BrwMmqfC.js +39 -0
- package/renderer/dist/assets/product-name-CTKLYNWY.js +40 -0
- package/renderer/dist/assets/quadrantDiagram-GDMTTRAM-BN4Stoj-.js +7 -0
- package/renderer/dist/assets/railroadDiagram-SM67HX2B-CPZ5bbeR.js +1 -0
- package/renderer/dist/assets/requirementDiagram-X7JNWC4A-CgRnimVe.js +84 -0
- package/renderer/dist/assets/sankeyDiagram-UM26HJRW-Bt2rej__.js +40 -0
- package/renderer/dist/assets/sequenceDiagram-ZO4K6R2Y-Dd5nKe9G.js +169 -0
- package/renderer/dist/assets/sizeCapture-INFHLROL-usyNyXyh.js +1 -0
- package/renderer/dist/assets/stateDiagram-v2-TBUQTH76-CRzUBb0u.js +291 -0
- package/renderer/dist/assets/swimlanes-N4OXWK64-C1GJvMS0.js +1 -0
- package/renderer/dist/assets/swimlanesDiagram-K3J5GTZL-Cy4Av2_p.js +8 -0
- package/renderer/dist/assets/timeline-definition-YOQAKGHF-caeyPlcc.js +120 -0
- package/renderer/dist/assets/usecaseDiagram-VIAY4XPW-BN_SV6PC.js +370 -0
- package/renderer/dist/assets/vennDiagram-BLWOH2XV-BLGuaUX-.js +34 -0
- package/renderer/dist/assets/wardleyDiagram-YMQ3BMBF-Da0cJeNc.js +78 -0
- package/renderer/dist/assets/xychartDiagram-TAQBALBS-cGfUffMk.js +7 -0
- package/renderer/dist/corner-tag.html +13 -0
- package/renderer/dist/emulator/audio-rules.mjs +74 -0
- package/renderer/dist/emulator/data/compression/README.md +11 -0
- package/renderer/dist/emulator/data/compression/extract7z.js +1 -0
- package/renderer/dist/emulator/data/compression/extractzip.js +1 -0
- package/renderer/dist/emulator/data/compression/libunrar.js +519 -0
- package/renderer/dist/emulator/data/compression/libunrar.wasm +0 -0
- package/renderer/dist/emulator/data/cores/README.md +22 -0
- package/renderer/dist/emulator/data/cores/mgba-legacy-wasm.data +0 -0
- package/renderer/dist/emulator/data/cores/mgba-thread-legacy-wasm.data +0 -0
- package/renderer/dist/emulator/data/cores/mgba-thread-wasm.data +0 -0
- package/renderer/dist/emulator/data/cores/mgba-wasm.data +0 -0
- package/renderer/dist/emulator/data/cores/package.json +65 -0
- package/renderer/dist/emulator/data/cores/reports/mgba.json +1 -0
- package/renderer/dist/emulator/data/emulator.css +1614 -0
- package/renderer/dist/emulator/data/loader.js +170 -0
- package/renderer/dist/emulator/data/localization/README.md +63 -0
- package/renderer/dist/emulator/data/localization/af-FR.json +339 -0
- package/renderer/dist/emulator/data/localization/ar-AR.json +301 -0
- package/renderer/dist/emulator/data/localization/ben-BEN.json +301 -0
- package/renderer/dist/emulator/data/localization/de-GER.json +301 -0
- package/renderer/dist/emulator/data/localization/el-GR.json +301 -0
- package/renderer/dist/emulator/data/localization/en-US.json +339 -0
- package/renderer/dist/emulator/data/localization/es-ES.json +310 -0
- package/renderer/dist/emulator/data/localization/fa-AF.json +310 -0
- package/renderer/dist/emulator/data/localization/hi-HI.json +301 -0
- package/renderer/dist/emulator/data/localization/it-IT.json +302 -0
- package/renderer/dist/emulator/data/localization/ja-JA.json +301 -0
- package/renderer/dist/emulator/data/localization/jv-JV.json +301 -0
- package/renderer/dist/emulator/data/localization/ko-KO.json +336 -0
- package/renderer/dist/emulator/data/localization/pt-BR.json +361 -0
- package/renderer/dist/emulator/data/localization/retroarch.json +617 -0
- package/renderer/dist/emulator/data/localization/ro-RO.json +310 -0
- package/renderer/dist/emulator/data/localization/ru-RU.json +301 -0
- package/renderer/dist/emulator/data/localization/tr-TR.json +310 -0
- package/renderer/dist/emulator/data/localization/vi-VN.json +302 -0
- package/renderer/dist/emulator/data/localization/zh-CN.json +351 -0
- package/renderer/dist/emulator/data/src/GameManager.js +464 -0
- package/renderer/dist/emulator/data/src/compression.js +142 -0
- package/renderer/dist/emulator/data/src/emulator.js +6158 -0
- package/renderer/dist/emulator/data/src/gamepad.js +167 -0
- package/renderer/dist/emulator/data/src/nipplejs.js +1 -0
- package/renderer/dist/emulator/data/src/shaders.js +236 -0
- package/renderer/dist/emulator/data/src/socket.io.min.js +7 -0
- package/renderer/dist/emulator/data/src/storage.js +125 -0
- package/renderer/dist/emulator/data/version.json +5 -0
- package/renderer/dist/emulator/host.js +297 -0
- package/renderer/dist/emulator/index.html +28 -0
- package/renderer/dist/emulator/provenance.json +6 -0
- package/renderer/dist/index.html +16 -3
- package/scripts/approval-server.sh +24 -5
- package/scripts/before-publish.mjs +12 -10
- package/shared/agent-import.mjs +2 -0
- package/shared/agents.d.mts +1 -0
- package/shared/bridge-map.mjs +18 -0
- package/shared/claude-commands.generated.mjs +6 -4
- package/shared/claude-models.generated.mjs +6 -4
- package/shared/corner-tag.d.mts +14 -0
- package/shared/corner-tag.mjs +141 -0
- package/shared/engines.mjs +20 -5
- package/shared/harness-definitions.mjs +103 -0
- package/shared/notify-rules.d.mts +25 -0
- package/shared/notify-rules.mjs +19 -0
- package/shared/provider-commands.mjs +10 -2
- package/shared/rank.d.mts +2 -0
- package/shared/rank.mjs +50 -0
- package/shared/relay-address.mjs +6 -0
- package/shared/relay-crypto.mjs +165 -0
- package/shared/runs-on.d.mts +0 -10
- package/shared/runs-on.mjs +25 -49
- package/shared/shortcuts.mjs +10 -4
- package/shared/spawn-trouble.mjs +57 -6
- package/shared/terminal-input.d.mts +1 -0
- package/shared/terminal-input.mjs +30 -0
- package/shared/terminal-keys.d.mts +1 -0
- package/shared/terminal-keys.mjs +16 -0
- package/shared/terminal-size.d.mts +4 -0
- package/shared/terminal-size.mjs +29 -0
- package/shared/usage.d.mts +3 -0
- package/shared/usage.mjs +7 -0
- package/shared/where-it-runs-from.d.mts +43 -0
- package/shared/where-it-runs-from.mjs +155 -0
- package/shared/work-items.mjs +25 -1
- package/shared/work-lines.mjs +1 -1
- package/shared/xterm-invalid-range.d.mts +2 -0
- package/shared/xterm-invalid-range.mjs +58 -0
- package/renderer/dist/assets/index-ChKsQkRw.js +0 -1169
- package/renderer/dist/assets/index-ClBXnq3B.css +0 -32
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// Types for shared/where-it-runs-from.mjs, which is plain ESM so that main
|
|
2
|
+
// (Node) and the screen (Vite) share one copy of the rule about whether this
|
|
3
|
+
// copy of the app can update itself from where it is, and one copy of the
|
|
4
|
+
// words that say so. The screen asks for the words and `sayWhere`; the places
|
|
5
|
+
// and the move are main's.
|
|
6
|
+
|
|
7
|
+
/** The place a copy of the app is in, as far as updating itself is concerned. */
|
|
8
|
+
export type AppPlace =
|
|
9
|
+
| 'disk-image' | 'translocated' | 'applications' | 'elsewhere' | 'source' | 'not-mac';
|
|
10
|
+
|
|
11
|
+
export const PLACE: {
|
|
12
|
+
diskImage: 'disk-image';
|
|
13
|
+
translocated: 'translocated';
|
|
14
|
+
applications: 'applications';
|
|
15
|
+
elsewhere: 'elsewhere';
|
|
16
|
+
source: 'source';
|
|
17
|
+
notMac: 'not-mac';
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export const MOVE_SAY: {
|
|
21
|
+
cannotUpdateHere: string;
|
|
22
|
+
button: string;
|
|
23
|
+
title: string;
|
|
24
|
+
where: Record<string, string>;
|
|
25
|
+
shortWhere: Record<string, string>;
|
|
26
|
+
fix: string;
|
|
27
|
+
retry: string;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
/** The sentence for a place, or null where there is nothing to say. */
|
|
31
|
+
export function sayWhere(place: string | null | undefined, how?: { short?: boolean }): string | null;
|
|
32
|
+
|
|
33
|
+
export function placeOfApp(facts?: {
|
|
34
|
+
platform?: string;
|
|
35
|
+
packaged?: boolean;
|
|
36
|
+
exePath?: string | null;
|
|
37
|
+
inApplications?: boolean;
|
|
38
|
+
}): { place: AppPlace; canUpdate: boolean; offerMove: boolean };
|
|
39
|
+
|
|
40
|
+
export function createPlace(host: { app: unknown; log?: unknown }): {
|
|
41
|
+
state(): { place: AppPlace; canUpdate: boolean; offerMove: boolean };
|
|
42
|
+
move(): { moved: boolean; error: string | null };
|
|
43
|
+
};
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
// AN APP IN THE WRONG PLACE CAN NEVER UPDATE ITSELF, AND NEVER SAID SO.
|
|
2
|
+
//
|
|
3
|
+
// macOS has two places it will happily run Agentbox from and never let it
|
|
4
|
+
// replace itself:
|
|
5
|
+
//
|
|
6
|
+
// THE DISK IMAGE. You double-click the .dmg, you double-click the app
|
|
7
|
+
// inside it, and it works. The volume is read-only, so Squirrel.Mac refuses
|
|
8
|
+
// the swap with "Cannot update while running on a read-only volume".
|
|
9
|
+
//
|
|
10
|
+
// A TRANSLOCATED COPY. You drag the app to Downloads and open it. Because
|
|
11
|
+
// it still carries the quarantine flag and is not in Applications, macOS
|
|
12
|
+
// runs it from a read-only copy it makes under
|
|
13
|
+
// `/private/var/folders/…/AppTranslocation/<uuid>/d/`. Same refusal, and
|
|
14
|
+
// this is why Squirrel's own sentence talks about the Downloads directory.
|
|
15
|
+
//
|
|
16
|
+
// Measured in PostHog on 2026-10-12: 23 crash reports over three weeks, two
|
|
17
|
+
// installs, both on 0.1.11, one report every six hours (`EVERY_MS` in
|
|
18
|
+
// main/updater.mjs) for as long as the app was open. The reports stopped being
|
|
19
|
+
// filed as crashes in 292d419. The two Macs are still on 0.1.11.
|
|
20
|
+
//
|
|
21
|
+
// SO THE APP READS WHERE IT IS AND OFFERS THE ONE PRESS THAT FIXES IT. Electron
|
|
22
|
+
// has had `moveToApplicationsFolder` since 4: it moves the bundle, replaces an
|
|
23
|
+
// older copy if you say so, and relaunches from the new place. After that the
|
|
24
|
+
// ordinary six-hourly check works and the person never thinks about it again.
|
|
25
|
+
//
|
|
26
|
+
// EVERYTHING HERE IS A FACT AND NOT A GUESS, which is why the decision is a
|
|
27
|
+
// pure function with the four facts passed in: a wrong "you are in the wrong
|
|
28
|
+
// place" is a nag on a healthy install, and two of those are worse than the
|
|
29
|
+
// silence this replaces.
|
|
30
|
+
|
|
31
|
+
import { Name } from './product-name.mjs';
|
|
32
|
+
|
|
33
|
+
// THE WORDS, IN ONE PLACE, and main and the renderer both read them from here.
|
|
34
|
+
// `cannotUpdateHere` is the sentence `shortError` in main/updater.mjs puts in
|
|
35
|
+
// place of Squirrel's own paragraph, which is 240 characters with a URL in the
|
|
36
|
+
// middle and was arriving on the Settings row cut off at 160.
|
|
37
|
+
//
|
|
38
|
+
// Every sentence says where the app is and what one press does about it, and
|
|
39
|
+
// none of them says "read-only volume": that is the mechanism, and nobody
|
|
40
|
+
// holding the Mac asked about the mechanism.
|
|
41
|
+
export const MOVE_SAY = {
|
|
42
|
+
cannotUpdateHere: `${Name} cannot update itself from where it is.`,
|
|
43
|
+
button: 'Move to Applications',
|
|
44
|
+
/** The sidebar card's heading. */
|
|
45
|
+
title: 'Updates are stuck',
|
|
46
|
+
/** What the place is, as a person would say it. */
|
|
47
|
+
where: {
|
|
48
|
+
'disk-image': `${Name} is running from the disk image it was downloaded in.`,
|
|
49
|
+
translocated: `${Name} is running from a read-only copy macOS made of it.`,
|
|
50
|
+
},
|
|
51
|
+
/** The same fact in half the words, for the sidebar card, which is one
|
|
52
|
+
* column wide and had the row's sentence running to six lines in it. */
|
|
53
|
+
shortWhere: {
|
|
54
|
+
'disk-image': `${Name} can’t update itself from the disk image.`,
|
|
55
|
+
translocated: `${Name} can’t update itself from the copy macOS made.`,
|
|
56
|
+
},
|
|
57
|
+
/** And the press, which is the same either way. */
|
|
58
|
+
fix: 'Moving it to your Applications folder takes a few seconds and gets updates working again.',
|
|
59
|
+
retry: 'Pressing it again tries again.',
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
/** The sentence for a place, or null where there is nothing to say. `short` is
|
|
63
|
+
* the sidebar card's half of it: the button beside it already says the fix. */
|
|
64
|
+
export function sayWhere(place, { short = false } = {}) {
|
|
65
|
+
const words = short ? MOVE_SAY.shortWhere : MOVE_SAY.where;
|
|
66
|
+
return words[place] ?? null;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** Where a copy of the app can be, as far as updating itself is concerned. */
|
|
70
|
+
export const PLACE = {
|
|
71
|
+
/** Run out of the mounted .dmg. Read-only; cannot update. */
|
|
72
|
+
diskImage: 'disk-image',
|
|
73
|
+
/** macOS is running a read-only copy of its own. Cannot update. */
|
|
74
|
+
translocated: 'translocated',
|
|
75
|
+
/** Where it belongs. */
|
|
76
|
+
applications: 'applications',
|
|
77
|
+
/** Somewhere writable that is not Applications: updates itself fine. */
|
|
78
|
+
elsewhere: 'elsewhere',
|
|
79
|
+
/** `npm start` from a checkout: main/source-updater.mjs, nothing to move. */
|
|
80
|
+
source: 'source',
|
|
81
|
+
/** No Applications folder and no `moveToApplicationsFolder`. */
|
|
82
|
+
notMac: 'not-mac',
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
// The folder macOS puts a translocated copy in. The path is always under
|
|
86
|
+
// `/private/var/folders`, but the one segment that names the mechanism is this
|
|
87
|
+
// one, and it is the only part Apple has kept stable. Written with "/" and
|
|
88
|
+
// not `path.sep`: these are only ever read on a Mac (`placeOfApp` returns
|
|
89
|
+
// before them anywhere else), and the screen imports this file, so it may not
|
|
90
|
+
// import anything from Node.
|
|
91
|
+
const TRANSLOCATED = '/AppTranslocation/';
|
|
92
|
+
// Every mounted volume lives here, including the disk image the download is.
|
|
93
|
+
const VOLUMES = '/Volumes/';
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* PURE. The whole decision, off four facts, so every case can be tested
|
|
97
|
+
* without a packaged app, a disk image, or a Mac.
|
|
98
|
+
*
|
|
99
|
+
* `exePath` is `app.getPath('exe')`, the binary inside the bundle;
|
|
100
|
+
* `inApplications` is Electron's own `app.isInApplicationsFolder()`, which
|
|
101
|
+
* knows about `~/Applications` as well as `/Applications` and is therefore
|
|
102
|
+
* never re-derived from the path here.
|
|
103
|
+
*/
|
|
104
|
+
export function placeOfApp({ platform, packaged, exePath, inApplications } = {}) {
|
|
105
|
+
const no = (place) => ({ place, canUpdate: true, offerMove: false });
|
|
106
|
+
if (platform !== 'darwin') return no(PLACE.notMac);
|
|
107
|
+
if (!packaged) return no(PLACE.source);
|
|
108
|
+
if (inApplications) return no(PLACE.applications);
|
|
109
|
+
if (typeof exePath !== 'string' || !exePath) return no(PLACE.elsewhere);
|
|
110
|
+
const where = exePath.includes(TRANSLOCATED) ? PLACE.translocated
|
|
111
|
+
: exePath.startsWith(VOLUMES) ? PLACE.diskImage
|
|
112
|
+
: null;
|
|
113
|
+
// THE PLACES THAT ARE NOT APPLICATIONS AND UPDATE THEMSELVES ANYWAY are left
|
|
114
|
+
// alone on purpose. A copy sitting in Downloads that macOS did not
|
|
115
|
+
// translocate is on a writable disk and Squirrel swaps it happily; offering
|
|
116
|
+
// to move it would be the app asking for something it does not need.
|
|
117
|
+
if (!where) return no(PLACE.elsewhere);
|
|
118
|
+
return { place: where, canUpdate: false, offerMove: true };
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* The live one, over Electron's `app`. Read ONCE, at construction: the bundle
|
|
123
|
+
* cannot move under a running process except by the move below, which
|
|
124
|
+
* relaunches.
|
|
125
|
+
*/
|
|
126
|
+
export function createPlace({ app, log = console }) {
|
|
127
|
+
const where = placeOfApp({
|
|
128
|
+
platform: app?.platform ?? process.platform,
|
|
129
|
+
packaged: !!app?.isPackaged,
|
|
130
|
+
exePath: (() => { try { return app?.getPath?.('exe') ?? null; } catch { return null; } })(),
|
|
131
|
+
inApplications: (() => { try { return !!app?.isInApplicationsFolder?.(); } catch { return false; } })(),
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
return {
|
|
135
|
+
state: () => ({ ...where }),
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* THE PRESS. Electron moves the bundle and relaunches, so nothing after a
|
|
139
|
+
* `true` here ever runs. It answers false when the person cancels its own
|
|
140
|
+
* "a copy is already there" box, which is an answer and not a failure.
|
|
141
|
+
*/
|
|
142
|
+
move() {
|
|
143
|
+
if (!where.offerMove) return { moved: false, error: null };
|
|
144
|
+
try {
|
|
145
|
+
const moved = !!app.moveToApplicationsFolder();
|
|
146
|
+
return { moved, error: null };
|
|
147
|
+
} catch (e) {
|
|
148
|
+
// One line, because Settings shows this string to a person.
|
|
149
|
+
const error = String(e?.message ?? e ?? 'The move did not work.').split('\n')[0].trim();
|
|
150
|
+
log.warn?.('[place] could not move the app into Applications:', error);
|
|
151
|
+
return { moved: false, error };
|
|
152
|
+
}
|
|
153
|
+
},
|
|
154
|
+
};
|
|
155
|
+
}
|
package/shared/work-items.mjs
CHANGED
|
@@ -366,6 +366,14 @@ function str(value, max) {
|
|
|
366
366
|
return trimmed.slice(0, lo) + mark(trimmed.length - lo);
|
|
367
367
|
}
|
|
368
368
|
|
|
369
|
+
// A text field whose empty string MEANS something: nothing chosen. Only a
|
|
370
|
+
// string may clear it, so a `null` that reached here without going through
|
|
371
|
+
// `answerItem` is still dropped rather than guessed at.
|
|
372
|
+
function clearable(value, max) {
|
|
373
|
+
if (typeof value !== 'string') return undefined;
|
|
374
|
+
return value.trim() ? str(value, max) : '';
|
|
375
|
+
}
|
|
376
|
+
|
|
369
377
|
function coerceField(field, value) {
|
|
370
378
|
switch (field) {
|
|
371
379
|
case 'title': return str(value, MAX_TITLE);
|
|
@@ -388,7 +396,23 @@ function coerceField(field, value) {
|
|
|
388
396
|
// shared/effort-levels.mjs, and the spawn gates on them, so a value this
|
|
389
397
|
// module does not recognise is a run at Claude Code's own choice rather
|
|
390
398
|
// than a row that never runs.
|
|
391
|
-
case 'kind': case 'product': case 'parent': case 'engine':
|
|
399
|
+
case 'kind': case 'product': case 'parent': case 'engine': return str(value, MAX_SHORT);
|
|
400
|
+
// THESE TWO ARE CLEARED BY WRITING '' AND SO '' IS A REAL VALUE, exactly
|
|
401
|
+
// as runAt's 0 and start's 'now' are, and for the same reason: a field that
|
|
402
|
+
// coerces to undefined is dropped by the fold, so without a falsy value
|
|
403
|
+
// that survives, a model could be chosen and never unchosen.
|
|
404
|
+
//
|
|
405
|
+
// They used to sit on the line above, which cost us the most common crash
|
|
406
|
+
// of launch week. The reply box's drawer hands a pick back as null for
|
|
407
|
+
// "nothing chosen" and sends `{ model, effort }` whenever it was touched at
|
|
408
|
+
// all; `answerItem` turns null into '' because `modelForEngine` reads the
|
|
409
|
+
// empty string as nothing chosen. `str()` then dropped it, `pickFields`
|
|
410
|
+
// answered null, and `buildLine` threw — before the answer was written,
|
|
411
|
+
// because the model is written first on purpose. So opening the drawer,
|
|
412
|
+
// picking a model and pressing send lost the message, with nothing on
|
|
413
|
+
// screen to say so. Five of the 14 crashes since the Oct 6 launch, on two
|
|
414
|
+
// installs (PostHog, 2026-10-08).
|
|
415
|
+
case 'model': case 'effort': return clearable(value, MAX_SHORT);
|
|
392
416
|
case 'status': return WORK_ITEM_STATUSES.includes(value) ? value : undefined;
|
|
393
417
|
case 'priority': return Number.isFinite(value) ? Math.trunc(value) : undefined;
|
|
394
418
|
// When this item may next be acted on, in epoch ms. 0 means unscheduled,
|
package/shared/work-lines.mjs
CHANGED
|
@@ -637,7 +637,7 @@ export function groupWork(events, { min = RUN_MIN } = {}) {
|
|
|
637
637
|
for (const e of events ?? []) {
|
|
638
638
|
// SOMETHING SHE DID IS NOT THE AGENT'S WORK and never folds into it: three
|
|
639
639
|
// snoozes are three lines, not one "You put it off" (w-49b4e45403).
|
|
640
|
-
if (e && e.kind === 'work' && !e.yours) { run.push(e); continue; }
|
|
640
|
+
if (e && e.kind === 'work' && !e.yours && !e.standalone) { run.push(e); continue; }
|
|
641
641
|
flush();
|
|
642
642
|
out.push(e);
|
|
643
643
|
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// XTERM'S OWN ASSERTION, CAUGHT WHERE WE CAN REACH IT.
|
|
2
|
+
//
|
|
3
|
+
// @xterm/xterm 6.0.0's AccessibilityManager listens on the document for
|
|
4
|
+
// 'selectionchange', maps the selection onto terminal rows and columns, and
|
|
5
|
+
// does `throw new Error('invalid range')` when the result covers no cells,
|
|
6
|
+
// under its own comment "This should not happen unless we have some bugs". It
|
|
7
|
+
// happens. Three ordinary selections reach it, measured against the published
|
|
8
|
+
// bundle; the test beside this file
|
|
9
|
+
// (tests/a-selection-that-covers-no-terminal-cells-is-not-a-crash.test.mjs)
|
|
10
|
+
// lists them and says how.
|
|
11
|
+
//
|
|
12
|
+
// Only a screen reader user gets that handler at all, because the pane asks
|
|
13
|
+
// `app.accessibilitySupportEnabled` before turning `screenReaderMode` on. For
|
|
14
|
+
// them the throw escapes as an unhandled renderer error, and the terminal keeps
|
|
15
|
+
// whatever was selected before it, so a copy afterwards takes stale text.
|
|
16
|
+
//
|
|
17
|
+
// WHY A WRAPPER AND NOT A PATCHED BUNDLE. What we build against is minified
|
|
18
|
+
// `lib/xterm.mjs`, and rewriting a file inside node_modules at build time is
|
|
19
|
+
// machinery that has to hold through `npm ci` on two CI runners and every
|
|
20
|
+
// release. xterm dispatches the handler as `() => this._handleSelectionChange()`,
|
|
21
|
+
// off the instance, so one own property on the manager shadows it and the
|
|
22
|
+
// wrapper dies with the terminal.
|
|
23
|
+
//
|
|
24
|
+
// IT SWALLOWS ONE MESSAGE AND NOTHING ELSE. The same handler warns about two
|
|
25
|
+
// genuine race conditions and returns; anything that is not this exact
|
|
26
|
+
// assertion is somebody's bug and goes on up.
|
|
27
|
+
//
|
|
28
|
+
// This is a workaround, and the upstream fix is the assertion becoming a
|
|
29
|
+
// clearSelection and a return. When an xterm release carries that, the test
|
|
30
|
+
// above turns red on the bundle it reads, and this file goes.
|
|
31
|
+
export const INVALID_RANGE = 'invalid range';
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Wrap the accessibility manager's selection handler on `terminal`, which must
|
|
35
|
+
* already be open. Answers whether there was one to wrap: false when
|
|
36
|
+
* screenReaderMode is off, which is every install not running a screen reader,
|
|
37
|
+
* and false when it is already wrapped.
|
|
38
|
+
*/
|
|
39
|
+
export function guardInvalidRange(terminal, { warn = console.warn } = {}) {
|
|
40
|
+
const manager = terminal?._core?._accessibilityManager?.value;
|
|
41
|
+
const handler = manager?._handleSelectionChange;
|
|
42
|
+
if (typeof handler !== 'function' || handler.guardsInvalidRange) return false;
|
|
43
|
+
const guarded = function (...args) {
|
|
44
|
+
try {
|
|
45
|
+
return handler.apply(this, args);
|
|
46
|
+
} catch (e) {
|
|
47
|
+
// Message, not instanceof: an Error from another realm is still this bug.
|
|
48
|
+
if (e?.message !== INVALID_RANGE) throw e;
|
|
49
|
+
warn(`xterm mapped the selection onto no cells and threw "${INVALID_RANGE}"; cleared the terminal's selection instead`);
|
|
50
|
+
// It threw before mirroring the selection, so the terminal is still
|
|
51
|
+
// holding the previous one. Nothing is selected; say that.
|
|
52
|
+
terminal.clearSelection();
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
guarded.guardsInvalidRange = true;
|
|
56
|
+
manager._handleSelectionChange = guarded;
|
|
57
|
+
return true;
|
|
58
|
+
}
|