jaitus 0.3.12 → 0.3.14

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/bin/jaitus.js CHANGED
@@ -1,73 +1,86 @@
1
- #!/usr/bin/env node
2
- // The app is imported only after an unfinished update has completed: importing it
3
- // statically would load a half-installed copy and crash before any check could run.
4
- // Node 20 is the floor — checked FIRST: loading the app on an older Node crashed in its
5
- // dependencies before this friendly message could print.
6
- if (Number(process.versions.node.split('.')[0]) < 20) {
7
- process.stderr.write(`jaitus needs Node 20 or newer (you have ${process.versions.node}).
8
- `);
9
- process.exit(1);
10
- }
11
-
12
- import { waitForPendingUpdate } from '../src/pendingUpdate.js';
13
-
14
- await waitForPendingUpdate().catch(() => false);
15
- const { default: main } = await import('../src/index.js');
16
-
17
- // Node 20 is the floor: the CLI uses global fetch, and on 18 it exists but streaming
18
- // bodies behave differently enough to matter. Better a clear message than a confusing
19
- // failure halfway through a task.
20
- const major = Number(process.versions.node.split('.')[0]);
21
- if (major < 20) {
22
- process.stderr.write(`jaitus needs Node 20 or newer (you have ${process.versions.node}).\n`);
23
- process.exit(1);
24
- }
25
-
26
- // Ctrl-C should leave the terminal usable rather than mid-escape-sequence. Mouse
27
- // reporting has to be turned off here too: the terminal keeps sending reports after the
28
- // process is gone, and every click in the shell afterwards prints escape codes.
29
- const restoreTerminal = () => {
30
- // Only to a real terminal. Written unconditionally, these codes end up in the output
31
- // of every piped invocation — `jaitus models --json | jq` would be handed a JSON
32
- // document with escape sequences glued to the end of it.
33
- if (!process.stdout.isTTY) return;
34
- try { process.stdout.write('\x1b[?1006l\x1b[?1002l\x1b[?1000l\x1b[?25h'); } catch { /* going away anyway */ }
35
- };
36
- process.on('SIGINT', () => {
37
- restoreTerminal();
38
- process.stdout.write('\n');
39
- process.exit(130);
40
- });
41
- process.on('exit', restoreTerminal);
42
-
43
- /**
44
- * Finish WITHOUT process.exit().
45
- *
46
- * Calling process.exit() after two fetches have shared a keep-alive connection crashes
47
- * Node on Windows with `Assertion failed: !(handle->flags & UV_HANDLE_CLOSING)` — a
48
- * libuv teardown race, not something this code does wrong, but it would print a crash
49
- * after every successful run. Closing undici's dispatcher first does not help; only not
50
- * calling exit() does.
51
- *
52
- * Setting exitCode and letting the loop drain is also measurably faster (295ms vs 406ms
53
- * on the same work), because exit() was racing teardown rather than waiting for it. The
54
- * dispatcher is still closed first so idle sockets are released immediately instead of
55
- * holding the process open for their keep-alive timeout.
56
- */
57
- async function finish(code) {
58
- process.exitCode = code ?? 0;
59
- try {
60
- const dispatcher = globalThis[Symbol.for('undici.globalDispatcher.1')];
61
- if (dispatcher?.close) await dispatcher.close();
62
- } catch {
63
- // Internal and version-dependent; if it is gone, natural drain still gets there.
64
- }
65
- }
66
-
67
- main(process.argv.slice(2))
68
- .then(finish)
69
- .catch(async (err) => {
70
- process.stderr.write(`\x1b[31m✗\x1b[39m ${err?.message || err}\n`);
71
- if (process.env.JAITUS_DEBUG) process.stderr.write(`${err?.stack}\n`);
72
- await finish(1);
73
- });
1
+ #!/usr/bin/env node
2
+ // The app is imported only after an unfinished update has completed: importing it
3
+ // statically would load a half-installed copy and crash before any check could run.
4
+ // Node 20 is the floor — checked FIRST: loading the app on an older Node crashed in its
5
+ // dependencies before this friendly message could print.
6
+ if (Number(process.versions.node.split('.')[0]) < 20) {
7
+ process.stderr.write(`jaitus needs Node 20 or newer (you have ${process.versions.node}).
8
+ `);
9
+ process.exit(1);
10
+ }
11
+
12
+ import { waitForPendingUpdate } from '../src/pendingUpdate.js';
13
+
14
+ await waitForPendingUpdate().catch(() => false);
15
+ // React picks its build from NODE_ENV the moment it is first loaded, and the app is
16
+ // loaded right here. Unset (or a developer's own NODE_ENV=development), it picked the
17
+ // DEVELOPMENT build, which records a performance.measure() for every component of
18
+ // every frame — and Node keeps every one of them. At the spinner's frame rate that is
19
+ // thousands a minute: a long session grew past 4 GB and died in "JavaScript heap out
20
+ // of memory", getting slower the whole way (each step cost ~5× the CPU, then GC
21
+ // thrash). So the app always loads production React, and the user's own NODE_ENV is
22
+ // put back straight after: every command the agent runs inherits this environment,
23
+ // and NODE_ENV=production would make `npm install` skip devDependencies.
24
+ const userNodeEnv = process.env.NODE_ENV;
25
+ process.env.NODE_ENV = 'production';
26
+ const { default: main } = await import('../src/index.js');
27
+ if (userNodeEnv === undefined) delete process.env.NODE_ENV;
28
+ else process.env.NODE_ENV = userNodeEnv;
29
+
30
+ // Node 20 is the floor: the CLI uses global fetch, and on 18 it exists but streaming
31
+ // bodies behave differently enough to matter. Better a clear message than a confusing
32
+ // failure halfway through a task.
33
+ const major = Number(process.versions.node.split('.')[0]);
34
+ if (major < 20) {
35
+ process.stderr.write(`jaitus needs Node 20 or newer (you have ${process.versions.node}).\n`);
36
+ process.exit(1);
37
+ }
38
+
39
+ // Ctrl-C should leave the terminal usable rather than mid-escape-sequence. Mouse
40
+ // reporting has to be turned off here too: the terminal keeps sending reports after the
41
+ // process is gone, and every click in the shell afterwards prints escape codes.
42
+ const restoreTerminal = () => {
43
+ // Only to a real terminal. Written unconditionally, these codes end up in the output
44
+ // of every piped invocation — `jaitus models --json | jq` would be handed a JSON
45
+ // document with escape sequences glued to the end of it.
46
+ if (!process.stdout.isTTY) return;
47
+ try { process.stdout.write('\x1b[?1006l\x1b[?1002l\x1b[?1000l\x1b[?25h'); } catch { /* going away anyway */ }
48
+ };
49
+ process.on('SIGINT', () => {
50
+ restoreTerminal();
51
+ process.stdout.write('\n');
52
+ process.exit(130);
53
+ });
54
+ process.on('exit', restoreTerminal);
55
+
56
+ /**
57
+ * Finish WITHOUT process.exit().
58
+ *
59
+ * Calling process.exit() after two fetches have shared a keep-alive connection crashes
60
+ * Node on Windows with `Assertion failed: !(handle->flags & UV_HANDLE_CLOSING)` — a
61
+ * libuv teardown race, not something this code does wrong, but it would print a crash
62
+ * after every successful run. Closing undici's dispatcher first does not help; only not
63
+ * calling exit() does.
64
+ *
65
+ * Setting exitCode and letting the loop drain is also measurably faster (295ms vs 406ms
66
+ * on the same work), because exit() was racing teardown rather than waiting for it. The
67
+ * dispatcher is still closed first so idle sockets are released immediately instead of
68
+ * holding the process open for their keep-alive timeout.
69
+ */
70
+ async function finish(code) {
71
+ process.exitCode = code ?? 0;
72
+ try {
73
+ const dispatcher = globalThis[Symbol.for('undici.globalDispatcher.1')];
74
+ if (dispatcher?.close) await dispatcher.close();
75
+ } catch {
76
+ // Internal and version-dependent; if it is gone, natural drain still gets there.
77
+ }
78
+ }
79
+
80
+ main(process.argv.slice(2))
81
+ .then(finish)
82
+ .catch(async (err) => {
83
+ process.stderr.write(`\x1b[31m✗\x1b[39m ${err?.message || err}\n`);
84
+ if (process.env.JAITUS_DEBUG) process.stderr.write(`${err?.stack}\n`);
85
+ await finish(1);
86
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jaitus",
3
- "version": "0.3.12",
3
+ "version": "0.3.14",
4
4
  "description": "Jaitus — a coding agent in your terminal.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -16,7 +16,7 @@
16
16
  "node": ">=20"
17
17
  },
18
18
  "scripts": {
19
- "test": "node tests/source-sanity.js && node tests/rules.js && node tests/run.js && node tests/commands.js && node tests/login.js && node tests/session.js && node tests/openUrl.js && node tests/ink-tui.js && node tests/tui-render.js && node tests/mouse-e2e.js && node tests/environment.js && node tests/update.js && node tests/continuation.js && node tests/steps.js && node tests/files.js && node tests/processes.js && node tests/security.js && node tests/context-budget.js && node tests/context-trim.js",
19
+ "test": "node tests/source-sanity.js && node tests/react-build.js && node tests/stalls.js && node tests/clipboard-write.js && node tests/rewind-images.js && node tests/rules.js && node tests/run.js && node tests/commands.js && node tests/login.js && node tests/session.js && node tests/openUrl.js && node tests/ink-tui.js && node tests/tui-render.js && node tests/mouse-e2e.js && node tests/environment.js && node tests/update.js && node tests/service-notices.js && node tests/continuation.js && node tests/steps.js && node tests/files.js && node tests/processes.js && node tests/security.js && node tests/context-budget.js && node tests/context-trim.js",
20
20
  "preview": "node scripts/tui-preview.js",
21
21
  "prepublishOnly": "npm test"
22
22
  },