quotacap 0.0.22 → 0.0.24
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 +30 -9
- package/dist/adapters/grok.js +2 -2
- package/dist/adapters/index.js +3 -0
- package/dist/adapters/muse.d.ts +8 -0
- package/dist/adapters/muse.js +96 -0
- package/dist/adapters/pty.d.ts +53 -0
- package/dist/adapters/pty.js +402 -74
- package/dist/advisory/provider-names.d.ts +14 -0
- package/dist/advisory/provider-names.js +98 -0
- package/dist/advisory/snapshot.d.ts +7 -0
- package/dist/advisory/snapshot.js +7 -0
- package/dist/advisory/types.d.ts +12 -0
- package/dist/advisory/validation.d.ts +9 -0
- package/dist/advisory/validation.js +39 -0
- package/dist/cli/advise.js +74 -7
- package/dist/cli/clients.d.ts +10 -0
- package/dist/cli/clients.js +2 -0
- package/dist/cli/index.js +13 -8
- package/dist/cli/init.d.ts +2 -0
- package/dist/cli/init.js +29 -0
- package/dist/cli/providers.d.ts +3 -0
- package/dist/cli/providers.js +174 -0
- package/dist/cli/runtime.d.ts +21 -0
- package/dist/cli/runtime.js +169 -42
- package/dist/cli/snapshot-source.d.ts +4 -1
- package/dist/cli/snapshot-source.js +52 -4
- package/dist/cli/status.js +78 -4
- package/dist/cli/takeover.d.ts +64 -0
- package/dist/cli/takeover.js +175 -0
- package/dist/cli/update.d.ts +61 -0
- package/dist/cli/update.js +392 -0
- package/dist/config.d.ts +18 -0
- package/dist/config.js +106 -1
- package/dist/diagnostics/failure.d.ts +26 -0
- package/dist/diagnostics/failure.js +417 -0
- package/dist/format/issues.d.ts +2 -0
- package/dist/format/issues.js +17 -0
- package/dist/format/markdown.js +1 -1
- package/dist/format/rows.js +2 -0
- package/dist/format/terminal.js +6 -6
- package/dist/http/server.d.ts +10 -0
- package/dist/http/server.js +115 -2
- package/dist/mcp/server.js +2 -0
- package/dist/runtime/client.d.ts +1 -0
- package/dist/runtime/client.js +21 -1
- package/dist/runtime/poll.d.ts +14 -4
- package/dist/runtime/poll.js +56 -39
- package/dist/runtime/service.js +26 -3
- package/dist/runtime/spawn.js +14 -4
- package/dist/runtime/updates.d.ts +74 -0
- package/dist/runtime/updates.js +445 -0
- package/dist/runtime/versions.d.ts +2 -0
- package/dist/runtime/versions.js +26 -0
- package/dist/service/index.d.ts +7 -2
- package/dist/service/index.js +67 -11
- package/dist/service/macos.d.ts +4 -0
- package/dist/service/macos.js +62 -11
- package/dist/service/systemd.d.ts +40 -0
- package/dist/service/systemd.js +387 -0
- package/dist/store/attempts.d.ts +6 -0
- package/dist/store/attempts.js +27 -20
- package/dist/store/db.js +6 -1
- package/dist/version.js +1 -1
- package/dist/web/src/App.js +1 -1
- package/dist/web/src/api.d.ts +2 -0
- package/dist/web/src/api.js +20 -0
- package/dist/web/src/components/AdviceDrawer.js +3 -4
- package/dist/web/src/components/FaultBanner.js +2 -3
- package/dist/web/src/components/Header.js +5 -1
- package/dist/web/src/components/ProviderCard.js +1 -2
- package/dist/web/src/components/ProviderDrawer.d.ts +8 -1
- package/dist/web/src/components/ProviderDrawer.js +103 -7
- package/dist/web/src/components/ProviderIcon.js +1 -0
- package/dist/web/src/components/ProviderRow.js +1 -2
- package/dist/web/src/components/Recommendation.js +2 -3
- package/dist/web/src/components/ResetRail.js +4 -5
- package/dist/web/src/components/SubscriptionList.js +1 -2
- package/dist/web/src/components/settings/ProvidersPanel.js +1 -2
- package/dist/web/src/pages/Onboarding.js +4 -5
- package/dist/web/src/state.d.ts +18 -0
- package/dist/web/src/state.js +18 -0
- package/dist/webAssets.js +1 -1
- package/dist/webHtml.js +1 -1
- package/package.json +4 -3
- package/web/dist/assets/index-CzObzbxb.js +12 -0
- package/web/dist/assets/index-VioQmN7J.css +1 -0
- package/web/dist/index.html +2 -2
- package/dist/src/adapters/agy.d.ts +0 -3
- package/dist/src/adapters/agy.js +0 -81
- package/dist/src/adapters/claude.d.ts +0 -9
- package/dist/src/adapters/claude.js +0 -30
- package/dist/src/adapters/codex.d.ts +0 -7
- package/dist/src/adapters/codex.js +0 -140
- package/dist/src/adapters/grok.d.ts +0 -7
- package/dist/src/adapters/grok.js +0 -126
- package/dist/src/adapters/index.d.ts +0 -21
- package/dist/src/adapters/index.js +0 -66
- package/dist/src/adapters/kimi.d.ts +0 -7
- package/dist/src/adapters/kimi.js +0 -76
- package/dist/src/adapters/manual.d.ts +0 -8
- package/dist/src/adapters/manual.js +0 -28
- package/dist/src/adapters/parse.d.ts +0 -1
- package/dist/src/adapters/parse.js +0 -81
- package/dist/src/adapters/pty.d.ts +0 -23
- package/dist/src/adapters/pty.js +0 -487
- package/dist/src/adapters/types.d.ts +0 -21
- package/dist/src/adapters/types.js +0 -1
- package/dist/src/advisory/engine.d.ts +0 -28
- package/dist/src/advisory/engine.js +0 -149
- package/dist/src/advisory/snapshot.d.ts +0 -16
- package/dist/src/advisory/snapshot.js +0 -213
- package/dist/src/advisory/types.d.ts +0 -56
- package/dist/src/advisory/types.js +0 -1
- package/dist/src/advisory/validate.d.ts +0 -5
- package/dist/src/advisory/validate.js +0 -18
- package/dist/src/cli/advise.d.ts +0 -3
- package/dist/src/cli/advise.js +0 -62
- package/dist/src/cli/clients.d.ts +0 -18
- package/dist/src/cli/clients.js +0 -11
- package/dist/src/cli/index.d.ts +0 -2
- package/dist/src/cli/index.js +0 -22
- package/dist/src/cli/ingest.d.ts +0 -3
- package/dist/src/cli/ingest.js +0 -31
- package/dist/src/cli/runtime.d.ts +0 -12
- package/dist/src/cli/runtime.js +0 -125
- package/dist/src/cli/snapshot-source.d.ts +0 -25
- package/dist/src/cli/snapshot-source.js +0 -223
- package/dist/src/cli/status.d.ts +0 -3
- package/dist/src/cli/status.js +0 -84
- package/dist/src/config.d.ts +0 -40
- package/dist/src/config.js +0 -157
- package/dist/src/daemon.d.ts +0 -4
- package/dist/src/daemon.js +0 -15
- package/dist/src/format/markdown.d.ts +0 -3
- package/dist/src/format/markdown.js +0 -24
- package/dist/src/format/rows.d.ts +0 -27
- package/dist/src/format/rows.js +0 -241
- package/dist/src/format/terminal.d.ts +0 -12
- package/dist/src/format/terminal.js +0 -144
- package/dist/src/http/server.d.ts +0 -25
- package/dist/src/http/server.js +0 -274
- package/dist/src/mcp/server.d.ts +0 -46
- package/dist/src/mcp/server.js +0 -163
- package/dist/src/runtime/client.d.ts +0 -24
- package/dist/src/runtime/client.js +0 -72
- package/dist/src/runtime/owner.d.ts +0 -35
- package/dist/src/runtime/owner.js +0 -393
- package/dist/src/runtime/poll.d.ts +0 -48
- package/dist/src/runtime/poll.js +0 -222
- package/dist/src/runtime/service.d.ts +0 -24
- package/dist/src/runtime/service.js +0 -251
- package/dist/src/runtime/spawn.d.ts +0 -17
- package/dist/src/runtime/spawn.js +0 -97
- package/dist/src/runtime/token.d.ts +0 -3
- package/dist/src/runtime/token.js +0 -64
- package/dist/src/service/index.d.ts +0 -4
- package/dist/src/service/index.js +0 -47
- package/dist/src/service/macos.d.ts +0 -96
- package/dist/src/service/macos.js +0 -551
- package/dist/src/store/attempts.d.ts +0 -12
- package/dist/src/store/attempts.js +0 -29
- package/dist/src/store/db.d.ts +0 -2
- package/dist/src/store/db.js +0 -74
- package/dist/src/store/quotas.d.ts +0 -6
- package/dist/src/store/quotas.js +0 -98
- package/dist/src/version.d.ts +0 -1
- package/dist/src/version.js +0 -2
- package/dist/src/webAssets.d.ts +0 -1
- package/dist/src/webAssets.js +0 -2
- package/dist/src/webHtml.d.ts +0 -1
- package/dist/src/webHtml.js +0 -2
- package/dist/tests/adapters/agy.test.d.ts +0 -1
- package/dist/tests/adapters/agy.test.js +0 -337
- package/dist/tests/adapters/claude.test.d.ts +0 -1
- package/dist/tests/adapters/claude.test.js +0 -83
- package/dist/tests/adapters/codex.test.d.ts +0 -1
- package/dist/tests/adapters/codex.test.js +0 -246
- package/dist/tests/adapters/credential-free.test.d.ts +0 -1
- package/dist/tests/adapters/credential-free.test.js +0 -211
- package/dist/tests/adapters/grok.test.d.ts +0 -1
- package/dist/tests/adapters/grok.test.js +0 -259
- package/dist/tests/adapters/kimi.test.d.ts +0 -1
- package/dist/tests/adapters/kimi.test.js +0 -235
- package/dist/tests/adapters/manual.test.d.ts +0 -1
- package/dist/tests/adapters/manual.test.js +0 -28
- package/dist/tests/adapters/pty.test.d.ts +0 -1
- package/dist/tests/adapters/pty.test.js +0 -240
- package/dist/tests/adapters/registry.test.d.ts +0 -1
- package/dist/tests/adapters/registry.test.js +0 -25
- package/dist/tests/advisory/engine.test.d.ts +0 -1
- package/dist/tests/advisory/engine.test.js +0 -222
- package/dist/tests/advisory/pace.test.d.ts +0 -1
- package/dist/tests/advisory/pace.test.js +0 -60
- package/dist/tests/advisory/snapshot.test.d.ts +0 -1
- package/dist/tests/advisory/snapshot.test.js +0 -65
- package/dist/tests/advisory/validate.test.d.ts +0 -1
- package/dist/tests/advisory/validate.test.js +0 -19
- package/dist/tests/bootstrap.test.d.ts +0 -1
- package/dist/tests/bootstrap.test.js +0 -14
- package/dist/tests/cli/advise.test.d.ts +0 -1
- package/dist/tests/cli/advise.test.js +0 -294
- package/dist/tests/cli/cli.test.d.ts +0 -1
- package/dist/tests/cli/cli.test.js +0 -46
- package/dist/tests/cli/helpers.d.ts +0 -9
- package/dist/tests/cli/helpers.js +0 -93
- package/dist/tests/cli/ingest.test.d.ts +0 -1
- package/dist/tests/cli/ingest.test.js +0 -188
- package/dist/tests/cli/parity.test.d.ts +0 -1
- package/dist/tests/cli/parity.test.js +0 -225
- package/dist/tests/cli/snapshot-source.test.d.ts +0 -1
- package/dist/tests/cli/snapshot-source.test.js +0 -305
- package/dist/tests/cli/status.test.d.ts +0 -1
- package/dist/tests/cli/status.test.js +0 -423
- package/dist/tests/daemon.test.d.ts +0 -1
- package/dist/tests/daemon.test.js +0 -164
- package/dist/tests/fixtures/stable-state.d.ts +0 -22
- package/dist/tests/fixtures/stable-state.js +0 -152
- package/dist/tests/fixtures/stable-state.test.d.ts +0 -1
- package/dist/tests/fixtures/stable-state.test.js +0 -17
- package/dist/tests/format/markdown.test.d.ts +0 -1
- package/dist/tests/format/markdown.test.js +0 -49
- package/dist/tests/format/terminal.test.d.ts +0 -1
- package/dist/tests/format/terminal.test.js +0 -476
- package/dist/tests/http/api.test.d.ts +0 -1
- package/dist/tests/http/api.test.js +0 -236
- package/dist/tests/integration.test.d.ts +0 -1
- package/dist/tests/integration.test.js +0 -61
- package/dist/tests/mcp/protocol.test.d.ts +0 -1
- package/dist/tests/mcp/protocol.test.js +0 -134
- package/dist/tests/mcp/quotas.test.d.ts +0 -1
- package/dist/tests/mcp/quotas.test.js +0 -56
- package/dist/tests/mcp/server.test.d.ts +0 -1
- package/dist/tests/mcp/server.test.js +0 -7
- package/dist/tests/mcp/tools.test.d.ts +0 -1
- package/dist/tests/mcp/tools.test.js +0 -291
- package/dist/tests/runtime/cancel.test.d.ts +0 -1
- package/dist/tests/runtime/cancel.test.js +0 -264
- package/dist/tests/runtime/cli.test.d.ts +0 -1
- package/dist/tests/runtime/cli.test.js +0 -226
- package/dist/tests/runtime/config.test.d.ts +0 -1
- package/dist/tests/runtime/config.test.js +0 -144
- package/dist/tests/runtime/http.test.d.ts +0 -1
- package/dist/tests/runtime/http.test.js +0 -267
- package/dist/tests/runtime/lifecycle.test.d.ts +0 -1
- package/dist/tests/runtime/lifecycle.test.js +0 -303
- package/dist/tests/runtime/ownership.test.d.ts +0 -1
- package/dist/tests/runtime/ownership.test.js +0 -355
- package/dist/tests/runtime/poll.test.d.ts +0 -1
- package/dist/tests/runtime/poll.test.js +0 -410
- package/dist/tests/runtime/status.test.d.ts +0 -1
- package/dist/tests/runtime/status.test.js +0 -232
- package/dist/tests/service/macos.test.d.ts +0 -1
- package/dist/tests/service/macos.test.js +0 -349
- package/dist/tests/store/attempts.test.d.ts +0 -1
- package/dist/tests/store/attempts.test.js +0 -29
- package/dist/tests/store/db.test.d.ts +0 -1
- package/dist/tests/store/db.test.js +0 -96
- package/dist/tests/store/migration.test.d.ts +0 -1
- package/dist/tests/store/migration.test.js +0 -41
- package/dist/tests/web/advice-drawer.test.d.ts +0 -1
- package/dist/tests/web/advice-drawer.test.js +0 -45
- package/dist/tests/web/build.test.d.ts +0 -1
- package/dist/tests/web/build.test.js +0 -8
- package/dist/tests/web/dashboard.e2e.d.ts +0 -1
- package/dist/tests/web/dashboard.e2e.js +0 -670
- package/dist/tests/web/header.test.d.ts +0 -1
- package/dist/tests/web/header.test.js +0 -56
- package/dist/tests/web/names.test.d.ts +0 -1
- package/dist/tests/web/names.test.js +0 -19
- package/dist/tests/web/onboarding.test.d.ts +0 -1
- package/dist/tests/web/onboarding.test.js +0 -21
- package/dist/tests/web/pacing.test.d.ts +0 -1
- package/dist/tests/web/pacing.test.js +0 -62
- package/dist/tests/web/playwright.config.d.ts +0 -2
- package/dist/tests/web/playwright.config.js +0 -10
- package/dist/tests/web/provider-drawer.test.d.ts +0 -1
- package/dist/tests/web/provider-drawer.test.js +0 -68
- package/dist/tests/web/recommendation.test.d.ts +0 -1
- package/dist/tests/web/recommendation.test.js +0 -38
- package/dist/tests/web/reset-rail.test.d.ts +0 -1
- package/dist/tests/web/reset-rail.test.js +0 -63
- package/dist/tests/web/responsive.test.d.ts +0 -1
- package/dist/tests/web/responsive.test.js +0 -21
- package/dist/tests/web/settings.test.d.ts +0 -1
- package/dist/tests/web/settings.test.js +0 -32
- package/dist/tests/web/state-catalog.test.d.ts +0 -1
- package/dist/tests/web/state-catalog.test.js +0 -206
- package/dist/tests/web/state.test.d.ts +0 -1
- package/dist/tests/web/state.test.js +0 -27
- package/dist/tests/web/stub-server.d.ts +0 -37
- package/dist/tests/web/stub-server.js +0 -132
- package/dist/tests/web/theme.test.d.ts +0 -1
- package/dist/tests/web/theme.test.js +0 -12
- package/dist/web/src/names.d.ts +0 -3
- package/dist/web/src/names.js +0 -19
- package/scripts/fix-pty-perms.mjs +0 -21
- package/web/dist/assets/index-CyUkWdwJ.css +0 -1
- package/web/dist/assets/index-PN4RpMKG.js +0 -12
package/dist/adapters/pty.js
CHANGED
|
@@ -3,6 +3,7 @@ import os from "node:os";
|
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { createRequire } from "node:module";
|
|
5
5
|
import { registerTrackedChild } from "../runtime/spawn.js";
|
|
6
|
+
import { diagnosticError, DiagnosticError } from "../diagnostics/failure.js";
|
|
6
7
|
export function stripAnsi(s) {
|
|
7
8
|
return s
|
|
8
9
|
.replace(/\x1b\[[^A-Za-z]*[A-Za-z]/g, "")
|
|
@@ -18,17 +19,116 @@ function regexTest(re, s) {
|
|
|
18
19
|
const fresh = new RegExp(re.source, re.flags.replace("g", ""));
|
|
19
20
|
return fresh.test(s);
|
|
20
21
|
}
|
|
22
|
+
/**
|
|
23
|
+
* Canned answers to terminal capability queries, table-driven so a future
|
|
24
|
+
* query type is a one-line row. Replies are static: colour answers never
|
|
25
|
+
* affect parsing because styling is stripped before matching.
|
|
26
|
+
*/
|
|
27
|
+
export const TERMINAL_QUERY_RESPONSES = [
|
|
28
|
+
// DSR (device status report): cursor position request. Unanswered, some
|
|
29
|
+
// TUIs exit outright ("cursor position could not be read").
|
|
30
|
+
{ match: /\x1b\[[0-9;]*6n/, reply: () => "\x1b[1;1R" },
|
|
31
|
+
// DA1 (primary device attributes): `CSI c` is exclusively a DA request.
|
|
32
|
+
{ match: /\x1b\[[0-9;]*c/, reply: () => "\x1b[?1;2c" },
|
|
33
|
+
// OSC 4/10/11 colour queries, BEL- or ST-terminated. The palette index is
|
|
34
|
+
// echoed back; 16 OSC 4 queries were measured in a single Muse startup.
|
|
35
|
+
{ match: /\x1b\]4;(\d+);\?(?:\x07|\x1b\\)/, reply: (m) => `\x1b]4;${m[1]};rgb:0000/0000/0000\x07` },
|
|
36
|
+
{ match: /\x1b\]10;\?(?:\x07|\x1b\\)/, reply: () => "\x1b]10;rgb:ffff/ffff/ffff\x07" },
|
|
37
|
+
{ match: /\x1b\]11;\?(?:\x07|\x1b\\)/, reply: () => "\x1b]11;rgb:0000/0000/0000\x07" },
|
|
38
|
+
];
|
|
39
|
+
/**
|
|
40
|
+
* Stateful feeder for `TERMINAL_QUERY_RESPONSES`. Consumes each matched query
|
|
41
|
+
* from an internal buffer, so a query split across chunks is answered once it
|
|
42
|
+
* completes and never answered twice. Plain output passes through silently.
|
|
43
|
+
*/
|
|
44
|
+
export function createTerminalQueryResponder(write) {
|
|
45
|
+
let buf = "";
|
|
46
|
+
return (chunk) => {
|
|
47
|
+
buf += chunk;
|
|
48
|
+
let progress = true;
|
|
49
|
+
while (progress) {
|
|
50
|
+
progress = false;
|
|
51
|
+
for (const rule of TERMINAL_QUERY_RESPONSES) {
|
|
52
|
+
const m = rule.match.exec(buf);
|
|
53
|
+
if (m?.index !== undefined) {
|
|
54
|
+
write(rule.reply(m));
|
|
55
|
+
buf = buf.slice(0, m.index) + buf.slice(m.index + m[0].length);
|
|
56
|
+
progress = true;
|
|
57
|
+
break;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
// Bounded tail: a partial query can only straddle the end of the stream,
|
|
62
|
+
// and the longest query is well under 64 bytes.
|
|
63
|
+
if (buf.length > 64)
|
|
64
|
+
buf = buf.slice(-64);
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Lazy replacement for the removed postinstall: best-effort `chmod 0o755`
|
|
69
|
+
* over every prebuilds entry `spawn-helper` under the given node-pty roots.
|
|
70
|
+
* Missing roots, missing prebuild dirs, and missing helpers are skipped
|
|
71
|
+
* silently; only attempted repairs are recorded.
|
|
72
|
+
*/
|
|
73
|
+
export function repairSpawnHelpers(roots, chmod = fs.chmodSync) {
|
|
74
|
+
const outcomes = [];
|
|
75
|
+
const seen = new Set();
|
|
76
|
+
for (const root of roots) {
|
|
77
|
+
let entries;
|
|
78
|
+
try {
|
|
79
|
+
entries = fs.readdirSync(path.join(root, "prebuilds"));
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
for (const entry of entries) {
|
|
85
|
+
const helper = path.join(root, "prebuilds", entry, "spawn-helper");
|
|
86
|
+
if (seen.has(helper))
|
|
87
|
+
continue;
|
|
88
|
+
seen.add(helper);
|
|
89
|
+
try {
|
|
90
|
+
if (!fs.existsSync(helper))
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
try {
|
|
97
|
+
chmod(helper, 0o755);
|
|
98
|
+
outcomes.push({ helper, ok: true });
|
|
99
|
+
}
|
|
100
|
+
catch (e) {
|
|
101
|
+
outcomes.push({ helper, ok: false, error: String(e?.message ?? e) });
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return outcomes;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Human diagnostic for failed repairs, or null when nothing failed.
|
|
109
|
+
* Load-bearing for root-owned trees (e.g. `sudo` global installs) where the
|
|
110
|
+
* invoking user cannot repair: without it the failure would go opaque again.
|
|
111
|
+
*/
|
|
112
|
+
export function describeSpawnHelperRepairs(outcomes) {
|
|
113
|
+
const failed = outcomes.filter((o) => !o.ok);
|
|
114
|
+
if (failed.length === 0)
|
|
115
|
+
return null;
|
|
116
|
+
const lines = failed.map((o) => `could not make ${o.helper} executable (${o.error ?? "unknown error"})`);
|
|
117
|
+
return (`${lines.join("; ")} — the install tree is likely root-owned (e.g. installed with sudo); ` +
|
|
118
|
+
`reinstall without sudo or fix the install's ownership so the invoking user can write it`);
|
|
119
|
+
}
|
|
120
|
+
function nodePtyPackageRoot(resolvedEntry) {
|
|
121
|
+
const dir = path.dirname(resolvedEntry);
|
|
122
|
+
// node-pty's main entry is lib/index.js; the package root is two levels up.
|
|
123
|
+
if (path.basename(dir) === "lib" && /^index\.[cm]?js$/.test(path.basename(resolvedEntry))) {
|
|
124
|
+
return path.dirname(dir);
|
|
125
|
+
}
|
|
126
|
+
return dir;
|
|
127
|
+
}
|
|
21
128
|
let ptyMod = null;
|
|
22
129
|
function getPty() {
|
|
23
130
|
if (ptyMod)
|
|
24
131
|
return ptyMod;
|
|
25
|
-
try {
|
|
26
|
-
const require = createRequire(import.meta.url);
|
|
27
|
-
ptyMod = require("node-pty");
|
|
28
|
-
if (ptyMod?.spawn)
|
|
29
|
-
return ptyMod;
|
|
30
|
-
}
|
|
31
|
-
catch { }
|
|
32
132
|
// Compiled binary (bun --compile) has no node_modules; try sidecar locations
|
|
33
133
|
const execDir = path.dirname(process.execPath);
|
|
34
134
|
const platformArch = `${process.platform}-${process.arch}`;
|
|
@@ -40,6 +140,29 @@ function getPty() {
|
|
|
40
140
|
path.join(os.homedir(), ".local", "share", "quotacap", "pty", platformArch, "node-pty"),
|
|
41
141
|
path.join(os.homedir(), ".quotacap", "pty", "node-pty"),
|
|
42
142
|
];
|
|
143
|
+
// Best-effort +x before requiring: the resolvable npm root (when present)
|
|
144
|
+
// plus every existing sidecar root. Successful repairs stay silent.
|
|
145
|
+
const repairRoots = [];
|
|
146
|
+
try {
|
|
147
|
+
const require0 = createRequire(import.meta.url);
|
|
148
|
+
repairRoots.push(nodePtyPackageRoot(require0.resolve("node-pty")));
|
|
149
|
+
}
|
|
150
|
+
catch { }
|
|
151
|
+
for (const c of candidates) {
|
|
152
|
+
try {
|
|
153
|
+
if (fs.existsSync(c))
|
|
154
|
+
repairRoots.push(c);
|
|
155
|
+
}
|
|
156
|
+
catch { }
|
|
157
|
+
}
|
|
158
|
+
const repairs = repairSpawnHelpers(repairRoots);
|
|
159
|
+
try {
|
|
160
|
+
const require = createRequire(import.meta.url);
|
|
161
|
+
ptyMod = require("node-pty");
|
|
162
|
+
if (ptyMod?.spawn)
|
|
163
|
+
return ptyMod;
|
|
164
|
+
}
|
|
165
|
+
catch { }
|
|
43
166
|
for (const c of candidates) {
|
|
44
167
|
try {
|
|
45
168
|
if (!fs.existsSync(c))
|
|
@@ -65,7 +188,9 @@ function getPty() {
|
|
|
65
188
|
}
|
|
66
189
|
catch { }
|
|
67
190
|
}
|
|
68
|
-
|
|
191
|
+
const repairNote = describeSpawnHelperRepairs(repairs);
|
|
192
|
+
throw diagnosticError(new Error("pty: node-pty not available — install with build tools (Xcode on macOS, build-essential + python3 on Linux) or use exec-based adapters only; for compiled binaries, ensure the pty sidecar is installed alongside the binary (see install.sh)" +
|
|
193
|
+
(repairNote ? `; ${repairNote}` : "")), { source: "pty" });
|
|
69
194
|
}
|
|
70
195
|
function isBunRuntime() {
|
|
71
196
|
return typeof globalThis.Bun !== "undefined" && typeof globalThis.Bun.spawn === "function";
|
|
@@ -76,9 +201,9 @@ async function runPtyBun(opts) {
|
|
|
76
201
|
const rows = opts.rows ?? 50;
|
|
77
202
|
const readyTimeoutMs = opts.readyTimeoutMs ?? 6000;
|
|
78
203
|
if (opts.timeoutMs <= 0)
|
|
79
|
-
throw new Error("pty: timeoutMs must be > 0");
|
|
204
|
+
throw diagnosticError(new Error("pty: timeoutMs must be > 0"), { source: "pty" });
|
|
80
205
|
if (opts.signal?.aborted)
|
|
81
|
-
throw new Error("pty aborted");
|
|
206
|
+
throw diagnosticError(new Error("pty aborted"), { source: "pty", checkpoint: "abort" });
|
|
82
207
|
let aborted = false;
|
|
83
208
|
const onAbort = () => {
|
|
84
209
|
aborted = true;
|
|
@@ -87,7 +212,7 @@ async function runPtyBun(opts) {
|
|
|
87
212
|
opts.signal.addEventListener("abort", onAbort, { once: true });
|
|
88
213
|
const checkAborted = () => {
|
|
89
214
|
if (aborted)
|
|
90
|
-
throw new Error("pty aborted");
|
|
215
|
+
throw diagnosticError(new Error("pty aborted"), { source: "pty", checkpoint: "abort", stdout: transcript });
|
|
91
216
|
};
|
|
92
217
|
const teardownAbort = () => {
|
|
93
218
|
if (opts.signal)
|
|
@@ -97,6 +222,12 @@ async function runPtyBun(opts) {
|
|
|
97
222
|
let transcript = "";
|
|
98
223
|
let exited = false;
|
|
99
224
|
let exitCode;
|
|
225
|
+
// Genuine PTY via the Bun.spawn `terminal` option (verified on Bun 1.3.11):
|
|
226
|
+
// the child's stdin, stdout, and stderr are all the PTY slave, input goes
|
|
227
|
+
// through proc.terminal.write, and merged output arrives in `data`.
|
|
228
|
+
// proc.stdin/stdout/stderr are null in this mode.
|
|
229
|
+
const decoder = new TextDecoder();
|
|
230
|
+
let feedQueries = null;
|
|
100
231
|
const proc = BunGlobal.spawn([opts.file, ...(opts.args ?? [])], {
|
|
101
232
|
cwd: opts.cwd ?? process.cwd(),
|
|
102
233
|
env: {
|
|
@@ -104,25 +235,36 @@ async function runPtyBun(opts) {
|
|
|
104
235
|
...(opts.env ?? {}),
|
|
105
236
|
TERM: "xterm-256color",
|
|
106
237
|
},
|
|
107
|
-
|
|
108
|
-
|
|
238
|
+
terminal: {
|
|
239
|
+
cols,
|
|
240
|
+
rows,
|
|
241
|
+
data(_term, chunk) {
|
|
242
|
+
try {
|
|
243
|
+
const text = typeof chunk === "string" ? chunk : decoder.decode(chunk, { stream: true });
|
|
244
|
+
transcript += text;
|
|
245
|
+
feedQueries?.(text);
|
|
246
|
+
}
|
|
247
|
+
catch { }
|
|
248
|
+
},
|
|
249
|
+
},
|
|
109
250
|
});
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
let reading = true;
|
|
114
|
-
const readLoop = (async () => {
|
|
251
|
+
if (!proc.terminal) {
|
|
252
|
+
// Old Bun runtimes silently ignore the unknown `terminal` option, so an
|
|
253
|
+
// absent proc.terminal is the fail-loud signal — never run half-attached.
|
|
115
254
|
try {
|
|
116
|
-
|
|
117
|
-
const { value, done } = await reader.read();
|
|
118
|
-
if (done)
|
|
119
|
-
break;
|
|
120
|
-
if (value)
|
|
121
|
-
transcript += decoder.decode(value);
|
|
122
|
-
}
|
|
255
|
+
proc.kill();
|
|
123
256
|
}
|
|
124
257
|
catch { }
|
|
125
|
-
|
|
258
|
+
throw diagnosticError(new Error("pty: Bun runtime lacks terminal (PTY) support (Bun.spawn terminal option unavailable) — rebuild the standalone binary with a newer Bun version"), { source: "pty" });
|
|
259
|
+
}
|
|
260
|
+
if (opts.respondToQueries) {
|
|
261
|
+
feedQueries = createTerminalQueryResponder((s) => {
|
|
262
|
+
try {
|
|
263
|
+
proc.terminal.write(s);
|
|
264
|
+
}
|
|
265
|
+
catch { }
|
|
266
|
+
});
|
|
267
|
+
}
|
|
126
268
|
// Poll for exit
|
|
127
269
|
const exitPoll = (async () => {
|
|
128
270
|
try {
|
|
@@ -132,14 +274,23 @@ async function runPtyBun(opts) {
|
|
|
132
274
|
}
|
|
133
275
|
catch { }
|
|
134
276
|
})();
|
|
277
|
+
void exitPoll;
|
|
135
278
|
const checkCap = () => {
|
|
136
279
|
if (Buffer.byteLength(transcript, "utf8") > maxBytes) {
|
|
137
|
-
throw new Error(`pty transcript exceeds ${maxBytes} bytes`)
|
|
280
|
+
throw diagnosticError(new Error(`pty transcript exceeds ${maxBytes} bytes`), {
|
|
281
|
+
source: "pty",
|
|
282
|
+
checkpoint: "transcript overflow",
|
|
283
|
+
stdout: transcript,
|
|
284
|
+
});
|
|
138
285
|
}
|
|
139
286
|
};
|
|
140
287
|
const checkAbort = (clean) => {
|
|
141
288
|
if (opts.abortOn && regexTest(opts.abortOn, clean)) {
|
|
142
|
-
throw new Error(`pty: untrusted workspace — trust prompt detected in ${opts.cwd ?? process.cwd()} — run \`${opts.file}\` there and select Trust this folder`)
|
|
289
|
+
throw diagnosticError(new Error(`pty: untrusted workspace — trust prompt detected in ${opts.cwd ?? process.cwd()} — run \`${opts.file}\` there and select Trust this folder`), {
|
|
290
|
+
source: "pty",
|
|
291
|
+
checkpoint: "trust prompt",
|
|
292
|
+
stdout: transcript,
|
|
293
|
+
});
|
|
143
294
|
}
|
|
144
295
|
};
|
|
145
296
|
const kill = async () => {
|
|
@@ -169,6 +320,10 @@ async function runPtyBun(opts) {
|
|
|
169
320
|
proc.kill();
|
|
170
321
|
}
|
|
171
322
|
catch { }
|
|
323
|
+
try {
|
|
324
|
+
proc.terminal?.close();
|
|
325
|
+
}
|
|
326
|
+
catch { }
|
|
172
327
|
}, opts.label ?? opts.file);
|
|
173
328
|
try {
|
|
174
329
|
if (opts.readyRegex) {
|
|
@@ -177,8 +332,14 @@ async function runPtyBun(opts) {
|
|
|
177
332
|
while (Date.now() < deadline) {
|
|
178
333
|
checkCap();
|
|
179
334
|
checkAborted();
|
|
180
|
-
if (exited)
|
|
181
|
-
throw new Error(`pty exited before ready (code ${exitCode})`)
|
|
335
|
+
if (exited) {
|
|
336
|
+
throw diagnosticError(new Error(`pty exited before ready (code ${exitCode})`), {
|
|
337
|
+
source: "pty",
|
|
338
|
+
checkpoint: "before ready",
|
|
339
|
+
exitCode,
|
|
340
|
+
stdout: transcript,
|
|
341
|
+
});
|
|
342
|
+
}
|
|
182
343
|
const clean = stripAnsi(transcript);
|
|
183
344
|
checkAbort(clean);
|
|
184
345
|
if (regexTest(opts.readyRegex, clean)) {
|
|
@@ -189,13 +350,24 @@ async function runPtyBun(opts) {
|
|
|
189
350
|
}
|
|
190
351
|
if (!matched) {
|
|
191
352
|
await kill();
|
|
192
|
-
throw new Error(`pty ready timeout after ${readyTimeoutMs}ms`)
|
|
353
|
+
throw diagnosticError(new Error(`pty ready timeout after ${readyTimeoutMs}ms`), {
|
|
354
|
+
source: "pty",
|
|
355
|
+
checkpoint: "ready timeout",
|
|
356
|
+
durationMs: readyTimeoutMs,
|
|
357
|
+
stdout: transcript,
|
|
358
|
+
});
|
|
193
359
|
}
|
|
194
|
-
await delay(200);
|
|
360
|
+
await delay(opts.settleDelayMs ?? 200);
|
|
195
361
|
checkCap();
|
|
196
362
|
checkAborted();
|
|
197
|
-
if (exited)
|
|
198
|
-
throw new Error(`pty exited before input (code ${exitCode})`)
|
|
363
|
+
if (exited) {
|
|
364
|
+
throw diagnosticError(new Error(`pty exited before input (code ${exitCode})`), {
|
|
365
|
+
source: "pty",
|
|
366
|
+
checkpoint: "before input",
|
|
367
|
+
exitCode,
|
|
368
|
+
stdout: transcript,
|
|
369
|
+
});
|
|
370
|
+
}
|
|
199
371
|
const postReadyClean = stripAnsi(transcript);
|
|
200
372
|
checkAbort(postReadyClean);
|
|
201
373
|
}
|
|
@@ -203,20 +375,57 @@ async function runPtyBun(opts) {
|
|
|
203
375
|
await delay(opts.settleDelayMs);
|
|
204
376
|
checkCap();
|
|
205
377
|
checkAborted();
|
|
206
|
-
if (exited)
|
|
207
|
-
throw new Error(`pty exited during settle (code ${exitCode})`)
|
|
378
|
+
if (exited) {
|
|
379
|
+
throw diagnosticError(new Error(`pty exited during settle (code ${exitCode})`), {
|
|
380
|
+
source: "pty",
|
|
381
|
+
checkpoint: "during settle",
|
|
382
|
+
exitCode,
|
|
383
|
+
stdout: transcript,
|
|
384
|
+
});
|
|
385
|
+
}
|
|
208
386
|
const c = stripAnsi(transcript);
|
|
209
387
|
checkAbort(c);
|
|
210
388
|
}
|
|
211
|
-
if (exited)
|
|
212
|
-
throw new Error(`pty exited before input (code ${exitCode})`)
|
|
389
|
+
if (exited) {
|
|
390
|
+
throw diagnosticError(new Error(`pty exited before input (code ${exitCode})`), {
|
|
391
|
+
source: "pty",
|
|
392
|
+
checkpoint: "before input",
|
|
393
|
+
exitCode,
|
|
394
|
+
stdout: transcript,
|
|
395
|
+
});
|
|
396
|
+
}
|
|
213
397
|
try {
|
|
214
|
-
proc.
|
|
215
|
-
if (typeof proc.stdin.flush === "function")
|
|
216
|
-
proc.stdin.flush();
|
|
398
|
+
proc.terminal.write(opts.input);
|
|
217
399
|
}
|
|
218
400
|
catch (e) {
|
|
219
|
-
throw new Error(`pty write failed: ${e.message}`)
|
|
401
|
+
throw diagnosticError(new Error(`pty write failed: ${e.message}`), {
|
|
402
|
+
source: "pty",
|
|
403
|
+
checkpoint: "write",
|
|
404
|
+
stdout: transcript,
|
|
405
|
+
});
|
|
406
|
+
}
|
|
407
|
+
if (opts.submitInput !== undefined && opts.submitAfterMs !== undefined) {
|
|
408
|
+
await delay(opts.submitAfterMs);
|
|
409
|
+
checkCap();
|
|
410
|
+
checkAborted();
|
|
411
|
+
if (exited) {
|
|
412
|
+
throw diagnosticError(new Error(`pty exited before input (code ${exitCode})`), {
|
|
413
|
+
source: "pty",
|
|
414
|
+
checkpoint: "before input",
|
|
415
|
+
exitCode,
|
|
416
|
+
stdout: transcript,
|
|
417
|
+
});
|
|
418
|
+
}
|
|
419
|
+
try {
|
|
420
|
+
proc.terminal.write(opts.submitInput);
|
|
421
|
+
}
|
|
422
|
+
catch (e) {
|
|
423
|
+
throw diagnosticError(new Error(`pty write failed: ${e.message}`), {
|
|
424
|
+
source: "pty",
|
|
425
|
+
checkpoint: "write",
|
|
426
|
+
stdout: transcript,
|
|
427
|
+
});
|
|
428
|
+
}
|
|
220
429
|
}
|
|
221
430
|
if (opts.completionRegex) {
|
|
222
431
|
const deadline = Date.now() + opts.timeoutMs;
|
|
@@ -232,7 +441,12 @@ async function runPtyBun(opts) {
|
|
|
232
441
|
}
|
|
233
442
|
if (exited) {
|
|
234
443
|
if (!regexTest(opts.completionRegex, clean)) {
|
|
235
|
-
throw new Error(`pty exited before completion (code ${exitCode})`)
|
|
444
|
+
throw diagnosticError(new Error(`pty exited before completion (code ${exitCode})`), {
|
|
445
|
+
source: "pty",
|
|
446
|
+
checkpoint: "before completion",
|
|
447
|
+
exitCode,
|
|
448
|
+
stdout: transcript,
|
|
449
|
+
});
|
|
236
450
|
}
|
|
237
451
|
done = true;
|
|
238
452
|
break;
|
|
@@ -241,7 +455,12 @@ async function runPtyBun(opts) {
|
|
|
241
455
|
}
|
|
242
456
|
if (!done) {
|
|
243
457
|
await kill();
|
|
244
|
-
throw new Error(`pty completion timeout after ${opts.timeoutMs}ms`)
|
|
458
|
+
throw diagnosticError(new Error(`pty completion timeout after ${opts.timeoutMs}ms`), {
|
|
459
|
+
source: "pty",
|
|
460
|
+
checkpoint: "completion timeout",
|
|
461
|
+
durationMs: opts.timeoutMs,
|
|
462
|
+
stdout: transcript,
|
|
463
|
+
});
|
|
245
464
|
}
|
|
246
465
|
}
|
|
247
466
|
else {
|
|
@@ -257,12 +476,17 @@ async function runPtyBun(opts) {
|
|
|
257
476
|
}
|
|
258
477
|
}
|
|
259
478
|
await kill();
|
|
260
|
-
|
|
479
|
+
// Drain late `data` callbacks, then close the terminal and flush the
|
|
480
|
+
// streamed decoder before the final checks.
|
|
481
|
+
await delay(200);
|
|
261
482
|
try {
|
|
262
|
-
|
|
483
|
+
proc.terminal.close();
|
|
484
|
+
}
|
|
485
|
+
catch { }
|
|
486
|
+
try {
|
|
487
|
+
transcript += decoder.decode();
|
|
263
488
|
}
|
|
264
489
|
catch { }
|
|
265
|
-
await Promise.race([readLoop, delay(200)]);
|
|
266
490
|
checkCap();
|
|
267
491
|
checkAborted();
|
|
268
492
|
const finalClean = stripAnsi(transcript);
|
|
@@ -272,15 +496,16 @@ async function runPtyBun(opts) {
|
|
|
272
496
|
return transcript;
|
|
273
497
|
}
|
|
274
498
|
catch (e) {
|
|
275
|
-
|
|
499
|
+
await kill();
|
|
276
500
|
try {
|
|
277
|
-
|
|
501
|
+
proc.terminal.close();
|
|
278
502
|
}
|
|
279
503
|
catch { }
|
|
280
|
-
await kill();
|
|
281
504
|
unregisterChild();
|
|
282
505
|
teardownAbort();
|
|
283
|
-
|
|
506
|
+
if (e instanceof DiagnosticError)
|
|
507
|
+
throw e;
|
|
508
|
+
throw diagnosticError(e, { source: "pty", stdout: transcript, exitCode });
|
|
284
509
|
}
|
|
285
510
|
}
|
|
286
511
|
async function runPtyNode(opts) {
|
|
@@ -289,9 +514,9 @@ async function runPtyNode(opts) {
|
|
|
289
514
|
const rows = opts.rows ?? 50;
|
|
290
515
|
const readyTimeoutMs = opts.readyTimeoutMs ?? 6000;
|
|
291
516
|
if (opts.timeoutMs <= 0)
|
|
292
|
-
throw new Error("pty: timeoutMs must be > 0");
|
|
517
|
+
throw diagnosticError(new Error("pty: timeoutMs must be > 0"), { source: "pty" });
|
|
293
518
|
if (opts.signal?.aborted)
|
|
294
|
-
throw new Error("pty aborted");
|
|
519
|
+
throw diagnosticError(new Error("pty aborted"), { source: "pty", checkpoint: "abort" });
|
|
295
520
|
let aborted = false;
|
|
296
521
|
const onAbort = () => {
|
|
297
522
|
aborted = true;
|
|
@@ -300,7 +525,7 @@ async function runPtyNode(opts) {
|
|
|
300
525
|
opts.signal.addEventListener("abort", onAbort, { once: true });
|
|
301
526
|
const checkAborted = () => {
|
|
302
527
|
if (aborted)
|
|
303
|
-
throw new Error("pty aborted");
|
|
528
|
+
throw diagnosticError(new Error("pty aborted"), { source: "pty", checkpoint: "abort", stdout: transcript });
|
|
304
529
|
};
|
|
305
530
|
const teardownAbort = () => {
|
|
306
531
|
if (opts.signal)
|
|
@@ -325,7 +550,10 @@ async function runPtyNode(opts) {
|
|
|
325
550
|
});
|
|
326
551
|
}
|
|
327
552
|
catch (e) {
|
|
328
|
-
throw new Error(`pty spawn failed for ${opts.file}: ${e.message}`)
|
|
553
|
+
throw diagnosticError(new Error(`pty spawn failed for ${opts.file}: ${e.message}`), {
|
|
554
|
+
source: "pty",
|
|
555
|
+
checkpoint: "spawn",
|
|
556
|
+
});
|
|
329
557
|
}
|
|
330
558
|
const unregisterChild = registerTrackedChild(() => {
|
|
331
559
|
try {
|
|
@@ -333,9 +561,21 @@ async function runPtyNode(opts) {
|
|
|
333
561
|
}
|
|
334
562
|
catch { }
|
|
335
563
|
}, opts.label ?? opts.file);
|
|
564
|
+
const feedQueries = opts.respondToQueries
|
|
565
|
+
? createTerminalQueryResponder((s) => {
|
|
566
|
+
try {
|
|
567
|
+
ptyProcess.write(s);
|
|
568
|
+
}
|
|
569
|
+
catch { }
|
|
570
|
+
})
|
|
571
|
+
: null;
|
|
336
572
|
const disposables = [];
|
|
337
573
|
disposables.push(ptyProcess.onData((data) => {
|
|
338
574
|
transcript += data;
|
|
575
|
+
try {
|
|
576
|
+
feedQueries?.(data);
|
|
577
|
+
}
|
|
578
|
+
catch { }
|
|
339
579
|
}));
|
|
340
580
|
disposables.push(ptyProcess.onExit(({ exitCode: c }) => {
|
|
341
581
|
exited = true;
|
|
@@ -373,12 +613,20 @@ async function runPtyNode(opts) {
|
|
|
373
613
|
};
|
|
374
614
|
const checkCap = () => {
|
|
375
615
|
if (Buffer.byteLength(transcript, "utf8") > maxBytes) {
|
|
376
|
-
throw new Error(`pty transcript exceeds ${maxBytes} bytes`)
|
|
616
|
+
throw diagnosticError(new Error(`pty transcript exceeds ${maxBytes} bytes`), {
|
|
617
|
+
source: "pty",
|
|
618
|
+
checkpoint: "transcript overflow",
|
|
619
|
+
stdout: transcript,
|
|
620
|
+
});
|
|
377
621
|
}
|
|
378
622
|
};
|
|
379
623
|
const checkAbort = (clean) => {
|
|
380
624
|
if (opts.abortOn && regexTest(opts.abortOn, clean)) {
|
|
381
|
-
throw new Error(`pty: untrusted workspace — trust prompt detected in ${opts.cwd ?? process.cwd()} — run \`${opts.file}\` there and select Trust this folder`)
|
|
625
|
+
throw diagnosticError(new Error(`pty: untrusted workspace — trust prompt detected in ${opts.cwd ?? process.cwd()} — run \`${opts.file}\` there and select Trust this folder`), {
|
|
626
|
+
source: "pty",
|
|
627
|
+
checkpoint: "trust prompt",
|
|
628
|
+
stdout: transcript,
|
|
629
|
+
});
|
|
382
630
|
}
|
|
383
631
|
};
|
|
384
632
|
try {
|
|
@@ -388,8 +636,14 @@ async function runPtyNode(opts) {
|
|
|
388
636
|
while (Date.now() < deadline) {
|
|
389
637
|
checkCap();
|
|
390
638
|
checkAborted();
|
|
391
|
-
if (exited)
|
|
392
|
-
throw new Error(`pty exited before ready (code ${exitCode})`)
|
|
639
|
+
if (exited) {
|
|
640
|
+
throw diagnosticError(new Error(`pty exited before ready (code ${exitCode})`), {
|
|
641
|
+
source: "pty",
|
|
642
|
+
checkpoint: "before ready",
|
|
643
|
+
exitCode,
|
|
644
|
+
stdout: transcript,
|
|
645
|
+
});
|
|
646
|
+
}
|
|
393
647
|
const clean = stripAnsi(transcript);
|
|
394
648
|
checkAbort(clean);
|
|
395
649
|
if (regexTest(opts.readyRegex, clean)) {
|
|
@@ -400,13 +654,24 @@ async function runPtyNode(opts) {
|
|
|
400
654
|
}
|
|
401
655
|
if (!matched) {
|
|
402
656
|
await kill();
|
|
403
|
-
throw new Error(`pty ready timeout after ${readyTimeoutMs}ms`)
|
|
657
|
+
throw diagnosticError(new Error(`pty ready timeout after ${readyTimeoutMs}ms`), {
|
|
658
|
+
source: "pty",
|
|
659
|
+
checkpoint: "ready timeout",
|
|
660
|
+
durationMs: readyTimeoutMs,
|
|
661
|
+
stdout: transcript,
|
|
662
|
+
});
|
|
404
663
|
}
|
|
405
|
-
await delay(200);
|
|
664
|
+
await delay(opts.settleDelayMs ?? 200);
|
|
406
665
|
checkCap();
|
|
407
666
|
checkAborted();
|
|
408
|
-
if (exited)
|
|
409
|
-
throw new Error(`pty exited before input (code ${exitCode})`)
|
|
667
|
+
if (exited) {
|
|
668
|
+
throw diagnosticError(new Error(`pty exited before input (code ${exitCode})`), {
|
|
669
|
+
source: "pty",
|
|
670
|
+
checkpoint: "before input",
|
|
671
|
+
exitCode,
|
|
672
|
+
stdout: transcript,
|
|
673
|
+
});
|
|
674
|
+
}
|
|
410
675
|
const postReadyClean = stripAnsi(transcript);
|
|
411
676
|
checkAbort(postReadyClean);
|
|
412
677
|
}
|
|
@@ -414,14 +679,58 @@ async function runPtyNode(opts) {
|
|
|
414
679
|
await delay(opts.settleDelayMs);
|
|
415
680
|
checkCap();
|
|
416
681
|
checkAborted();
|
|
417
|
-
if (exited)
|
|
418
|
-
throw new Error(`pty exited during settle (code ${exitCode})`)
|
|
682
|
+
if (exited) {
|
|
683
|
+
throw diagnosticError(new Error(`pty exited during settle (code ${exitCode})`), {
|
|
684
|
+
source: "pty",
|
|
685
|
+
checkpoint: "during settle",
|
|
686
|
+
exitCode,
|
|
687
|
+
stdout: transcript,
|
|
688
|
+
});
|
|
689
|
+
}
|
|
419
690
|
const c = stripAnsi(transcript);
|
|
420
691
|
checkAbort(c);
|
|
421
692
|
}
|
|
422
|
-
if (exited)
|
|
423
|
-
throw new Error(`pty exited before input (code ${exitCode})`)
|
|
424
|
-
|
|
693
|
+
if (exited) {
|
|
694
|
+
throw diagnosticError(new Error(`pty exited before input (code ${exitCode})`), {
|
|
695
|
+
source: "pty",
|
|
696
|
+
checkpoint: "before input",
|
|
697
|
+
exitCode,
|
|
698
|
+
stdout: transcript,
|
|
699
|
+
});
|
|
700
|
+
}
|
|
701
|
+
try {
|
|
702
|
+
ptyProcess.write(opts.input);
|
|
703
|
+
}
|
|
704
|
+
catch (e) {
|
|
705
|
+
throw diagnosticError(new Error(`pty write failed: ${e.message}`), {
|
|
706
|
+
source: "pty",
|
|
707
|
+
checkpoint: "write",
|
|
708
|
+
stdout: transcript,
|
|
709
|
+
});
|
|
710
|
+
}
|
|
711
|
+
if (opts.submitInput !== undefined && opts.submitAfterMs !== undefined) {
|
|
712
|
+
await delay(opts.submitAfterMs);
|
|
713
|
+
checkCap();
|
|
714
|
+
checkAborted();
|
|
715
|
+
if (exited) {
|
|
716
|
+
throw diagnosticError(new Error(`pty exited before input (code ${exitCode})`), {
|
|
717
|
+
source: "pty",
|
|
718
|
+
checkpoint: "before input",
|
|
719
|
+
exitCode,
|
|
720
|
+
stdout: transcript,
|
|
721
|
+
});
|
|
722
|
+
}
|
|
723
|
+
try {
|
|
724
|
+
ptyProcess.write(opts.submitInput);
|
|
725
|
+
}
|
|
726
|
+
catch (e) {
|
|
727
|
+
throw diagnosticError(new Error(`pty write failed: ${e.message}`), {
|
|
728
|
+
source: "pty",
|
|
729
|
+
checkpoint: "write",
|
|
730
|
+
stdout: transcript,
|
|
731
|
+
});
|
|
732
|
+
}
|
|
733
|
+
}
|
|
425
734
|
if (opts.completionRegex) {
|
|
426
735
|
const deadline = Date.now() + opts.timeoutMs;
|
|
427
736
|
let done = false;
|
|
@@ -436,7 +745,12 @@ async function runPtyNode(opts) {
|
|
|
436
745
|
}
|
|
437
746
|
if (exited) {
|
|
438
747
|
if (!regexTest(opts.completionRegex, clean)) {
|
|
439
|
-
throw new Error(`pty exited before completion (code ${exitCode})`)
|
|
748
|
+
throw diagnosticError(new Error(`pty exited before completion (code ${exitCode})`), {
|
|
749
|
+
source: "pty",
|
|
750
|
+
checkpoint: "before completion",
|
|
751
|
+
exitCode,
|
|
752
|
+
stdout: transcript,
|
|
753
|
+
});
|
|
440
754
|
}
|
|
441
755
|
done = true;
|
|
442
756
|
break;
|
|
@@ -445,7 +759,12 @@ async function runPtyNode(opts) {
|
|
|
445
759
|
}
|
|
446
760
|
if (!done) {
|
|
447
761
|
await kill();
|
|
448
|
-
throw new Error(`pty completion timeout after ${opts.timeoutMs}ms`)
|
|
762
|
+
throw diagnosticError(new Error(`pty completion timeout after ${opts.timeoutMs}ms`), {
|
|
763
|
+
source: "pty",
|
|
764
|
+
checkpoint: "completion timeout",
|
|
765
|
+
durationMs: opts.timeoutMs,
|
|
766
|
+
stdout: transcript,
|
|
767
|
+
});
|
|
449
768
|
}
|
|
450
769
|
}
|
|
451
770
|
else {
|
|
@@ -471,7 +790,9 @@ async function runPtyNode(opts) {
|
|
|
471
790
|
}
|
|
472
791
|
catch (e) {
|
|
473
792
|
await kill();
|
|
474
|
-
|
|
793
|
+
if (e instanceof DiagnosticError)
|
|
794
|
+
throw e;
|
|
795
|
+
throw diagnosticError(e, { source: "pty", stdout: transcript, exitCode });
|
|
475
796
|
}
|
|
476
797
|
finally {
|
|
477
798
|
cleanup();
|
|
@@ -480,8 +801,15 @@ async function runPtyNode(opts) {
|
|
|
480
801
|
}
|
|
481
802
|
}
|
|
482
803
|
export async function runPty(opts) {
|
|
483
|
-
|
|
484
|
-
|
|
804
|
+
try {
|
|
805
|
+
if (isBunRuntime()) {
|
|
806
|
+
return await runPtyBun(opts);
|
|
807
|
+
}
|
|
808
|
+
return await runPtyNode(opts);
|
|
809
|
+
}
|
|
810
|
+
catch (e) {
|
|
811
|
+
if (e instanceof DiagnosticError)
|
|
812
|
+
throw e;
|
|
813
|
+
throw diagnosticError(e, { source: "pty" });
|
|
485
814
|
}
|
|
486
|
-
return runPtyNode(opts);
|
|
487
815
|
}
|