@addai/node 0.29.0 → 0.30.0
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 +96 -96
- package/assets/vault-extension/background.js +45 -45
- package/assets/vault-extension/content.js +62 -62
- package/assets/vault-extension/manifest.json +39 -39
- package/dist/autostart-linux.js +20 -20
- package/dist/autostart-mac.js +30 -30
- package/dist/autostart-win.js +50 -50
- package/dist/cli.js +19 -19
- package/dist/desktop/install-engine.js +12 -12
- package/dist/desktop/learn/browser.js +4 -4
- package/dist/desktop/learn/injected.mjs +173 -173
- package/dist/desktop/learn/recorder.mjs +151 -151
- package/dist/desktop/manager.d.ts +15 -2
- package/dist/desktop/manager.js +38 -6
- package/dist/desktop/provider.js +9 -0
- package/dist/desktop/relay-client.js +29 -5
- package/dist/desktop/spec.d.ts +23 -0
- package/dist/desktop/spec.js +24 -1
- package/dist/desktop/vault-seed.mjs +112 -112
- package/dist/tui/run.js +21 -0
- package/package.json +62 -62
- package/scripts/copy-assets.js +18 -18
- package/scripts/fix-pty-helper.js +28 -28
- package/scripts/precompact-capture.js +292 -292
- package/scripts/probe-tui.mjs +122 -122
- package/scripts/smoke-test.sh +74 -74
|
@@ -1,112 +1,112 @@
|
|
|
1
|
-
// Plants the add.ai session cookie in a desktop's browser, from inside it.
|
|
2
|
-
//
|
|
3
|
-
// Runs in the container, not the daemon: the debugging port is bound to
|
|
4
|
-
// loopback there on purpose (CDP is total control of a browser, and the host
|
|
5
|
-
// must not be able to reach it), so the only place that can talk to it is the
|
|
6
|
-
// container itself. Node 22 is in the image already — this is the same
|
|
7
|
-
// arrangement +Ai Learn's recorder uses.
|
|
8
|
-
//
|
|
9
|
-
// Launched by the browser wrapper, in the background, immediately before Chrome
|
|
10
|
-
// starts. It waits for the port rather than assuming, sets the cookie, and
|
|
11
|
-
// exits. Nothing depends on its exit code: a browser that comes up without a
|
|
12
|
-
// vault session is worse than one with, but it is still a working browser.
|
|
13
|
-
import * as fs from 'node:fs';
|
|
14
|
-
|
|
15
|
-
const PORT = Number(process.env.ADDAI_CDP_PORT || 9222);
|
|
16
|
-
const SESSION_FILE = process.env.ADDAI_VAULT_SESSION || '/conf/vault/session.json';
|
|
17
|
-
const DEADLINE_MS = 60_000;
|
|
18
|
-
const POLL_MS = 250;
|
|
19
|
-
|
|
20
|
-
const log = (m) => process.stdout.write(`[vault-seed] ${m}\n`);
|
|
21
|
-
|
|
22
|
-
/** The cookie the extension reads: URI-encoded JSON with the two tokens. */
|
|
23
|
-
export function cookieValue(session) {
|
|
24
|
-
return encodeURIComponent(JSON.stringify({
|
|
25
|
-
access_token: session.access_token,
|
|
26
|
-
refresh_token: session.refresh_token,
|
|
27
|
-
}));
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
/** Both add.ai itself and every *.add.ai app, which is what the session is
|
|
31
|
-
* for — signing in at auth.add.ai signs you in across all of them. */
|
|
32
|
-
export function cookieParams(session) {
|
|
33
|
-
return {
|
|
34
|
-
name: 'auth',
|
|
35
|
-
value: cookieValue(session),
|
|
36
|
-
domain: '.add.ai',
|
|
37
|
-
path: '/',
|
|
38
|
-
secure: true,
|
|
39
|
-
httpOnly: false,
|
|
40
|
-
sameSite: 'Lax',
|
|
41
|
-
};
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
async function waitForBrowser(deadline) {
|
|
45
|
-
while (Date.now() < deadline) {
|
|
46
|
-
try {
|
|
47
|
-
const res = await fetch(`http://127.0.0.1:${PORT}/json/version`);
|
|
48
|
-
if (res.ok) return await res.json();
|
|
49
|
-
} catch { /* not up yet */ }
|
|
50
|
-
await new Promise((r) => setTimeout(r, POLL_MS));
|
|
51
|
-
}
|
|
52
|
-
return null;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
async function setCookie(wsUrl, params) {
|
|
56
|
-
const ws = new WebSocket(wsUrl);
|
|
57
|
-
await new Promise((resolve, reject) => {
|
|
58
|
-
ws.addEventListener('open', resolve, { once: true });
|
|
59
|
-
ws.addEventListener('error', () => reject(new Error('could not open the debugger')), { once: true });
|
|
60
|
-
});
|
|
61
|
-
const done = new Promise((resolve, reject) => {
|
|
62
|
-
const timer = setTimeout(() => reject(new Error('the browser did not answer')), 10_000);
|
|
63
|
-
ws.addEventListener('message', (ev) => {
|
|
64
|
-
let msg;
|
|
65
|
-
try { msg = JSON.parse(ev.data); } catch { return; }
|
|
66
|
-
if (msg.id !== 1) return;
|
|
67
|
-
clearTimeout(timer);
|
|
68
|
-
if (msg.error) reject(new Error(msg.error.message || 'setCookie failed'));
|
|
69
|
-
else resolve(msg.result);
|
|
70
|
-
});
|
|
71
|
-
});
|
|
72
|
-
ws.send(JSON.stringify({ id: 1, method: 'Network.setCookie', params }));
|
|
73
|
-
// finally, not after the await: an error reply rejects, and a socket left
|
|
74
|
-
// open holds the event loop forever. The failure mode is a hung node process
|
|
75
|
-
// per browser launch, which is worse than the failure it is reporting.
|
|
76
|
-
try {
|
|
77
|
-
return await done;
|
|
78
|
-
} finally {
|
|
79
|
-
ws.close();
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
async function main() {
|
|
84
|
-
let session;
|
|
85
|
-
try {
|
|
86
|
-
session = JSON.parse(fs.readFileSync(SESSION_FILE, 'utf8'));
|
|
87
|
-
} catch {
|
|
88
|
-
log('no session staged for this desktop — the vault will be empty until somebody signs in');
|
|
89
|
-
return;
|
|
90
|
-
}
|
|
91
|
-
if (!session?.access_token || !session?.refresh_token) {
|
|
92
|
-
log('the staged session is not usable');
|
|
93
|
-
return;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
const version = await waitForBrowser(Date.now() + DEADLINE_MS);
|
|
97
|
-
if (!version?.webSocketDebuggerUrl) {
|
|
98
|
-
log('the browser never opened its debugging port');
|
|
99
|
-
return;
|
|
100
|
-
}
|
|
101
|
-
try {
|
|
102
|
-
const res = await setCookie(version.webSocketDebuggerUrl, cookieParams(session));
|
|
103
|
-
log(res?.success === false ? 'the browser refused the cookie' : 'signed in');
|
|
104
|
-
} catch (err) {
|
|
105
|
-
log(`could not sign in: ${err.message}`);
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
// Importable for tests; only runs when executed directly.
|
|
110
|
-
if (process.argv[1] && process.argv[1].endsWith('vault-seed.mjs')) {
|
|
111
|
-
main().catch((err) => log(`unexpected: ${err.message}`));
|
|
112
|
-
}
|
|
1
|
+
// Plants the add.ai session cookie in a desktop's browser, from inside it.
|
|
2
|
+
//
|
|
3
|
+
// Runs in the container, not the daemon: the debugging port is bound to
|
|
4
|
+
// loopback there on purpose (CDP is total control of a browser, and the host
|
|
5
|
+
// must not be able to reach it), so the only place that can talk to it is the
|
|
6
|
+
// container itself. Node 22 is in the image already — this is the same
|
|
7
|
+
// arrangement +Ai Learn's recorder uses.
|
|
8
|
+
//
|
|
9
|
+
// Launched by the browser wrapper, in the background, immediately before Chrome
|
|
10
|
+
// starts. It waits for the port rather than assuming, sets the cookie, and
|
|
11
|
+
// exits. Nothing depends on its exit code: a browser that comes up without a
|
|
12
|
+
// vault session is worse than one with, but it is still a working browser.
|
|
13
|
+
import * as fs from 'node:fs';
|
|
14
|
+
|
|
15
|
+
const PORT = Number(process.env.ADDAI_CDP_PORT || 9222);
|
|
16
|
+
const SESSION_FILE = process.env.ADDAI_VAULT_SESSION || '/conf/vault/session.json';
|
|
17
|
+
const DEADLINE_MS = 60_000;
|
|
18
|
+
const POLL_MS = 250;
|
|
19
|
+
|
|
20
|
+
const log = (m) => process.stdout.write(`[vault-seed] ${m}\n`);
|
|
21
|
+
|
|
22
|
+
/** The cookie the extension reads: URI-encoded JSON with the two tokens. */
|
|
23
|
+
export function cookieValue(session) {
|
|
24
|
+
return encodeURIComponent(JSON.stringify({
|
|
25
|
+
access_token: session.access_token,
|
|
26
|
+
refresh_token: session.refresh_token,
|
|
27
|
+
}));
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** Both add.ai itself and every *.add.ai app, which is what the session is
|
|
31
|
+
* for — signing in at auth.add.ai signs you in across all of them. */
|
|
32
|
+
export function cookieParams(session) {
|
|
33
|
+
return {
|
|
34
|
+
name: 'auth',
|
|
35
|
+
value: cookieValue(session),
|
|
36
|
+
domain: '.add.ai',
|
|
37
|
+
path: '/',
|
|
38
|
+
secure: true,
|
|
39
|
+
httpOnly: false,
|
|
40
|
+
sameSite: 'Lax',
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async function waitForBrowser(deadline) {
|
|
45
|
+
while (Date.now() < deadline) {
|
|
46
|
+
try {
|
|
47
|
+
const res = await fetch(`http://127.0.0.1:${PORT}/json/version`);
|
|
48
|
+
if (res.ok) return await res.json();
|
|
49
|
+
} catch { /* not up yet */ }
|
|
50
|
+
await new Promise((r) => setTimeout(r, POLL_MS));
|
|
51
|
+
}
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async function setCookie(wsUrl, params) {
|
|
56
|
+
const ws = new WebSocket(wsUrl);
|
|
57
|
+
await new Promise((resolve, reject) => {
|
|
58
|
+
ws.addEventListener('open', resolve, { once: true });
|
|
59
|
+
ws.addEventListener('error', () => reject(new Error('could not open the debugger')), { once: true });
|
|
60
|
+
});
|
|
61
|
+
const done = new Promise((resolve, reject) => {
|
|
62
|
+
const timer = setTimeout(() => reject(new Error('the browser did not answer')), 10_000);
|
|
63
|
+
ws.addEventListener('message', (ev) => {
|
|
64
|
+
let msg;
|
|
65
|
+
try { msg = JSON.parse(ev.data); } catch { return; }
|
|
66
|
+
if (msg.id !== 1) return;
|
|
67
|
+
clearTimeout(timer);
|
|
68
|
+
if (msg.error) reject(new Error(msg.error.message || 'setCookie failed'));
|
|
69
|
+
else resolve(msg.result);
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
ws.send(JSON.stringify({ id: 1, method: 'Network.setCookie', params }));
|
|
73
|
+
// finally, not after the await: an error reply rejects, and a socket left
|
|
74
|
+
// open holds the event loop forever. The failure mode is a hung node process
|
|
75
|
+
// per browser launch, which is worse than the failure it is reporting.
|
|
76
|
+
try {
|
|
77
|
+
return await done;
|
|
78
|
+
} finally {
|
|
79
|
+
ws.close();
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
async function main() {
|
|
84
|
+
let session;
|
|
85
|
+
try {
|
|
86
|
+
session = JSON.parse(fs.readFileSync(SESSION_FILE, 'utf8'));
|
|
87
|
+
} catch {
|
|
88
|
+
log('no session staged for this desktop — the vault will be empty until somebody signs in');
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
if (!session?.access_token || !session?.refresh_token) {
|
|
92
|
+
log('the staged session is not usable');
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const version = await waitForBrowser(Date.now() + DEADLINE_MS);
|
|
97
|
+
if (!version?.webSocketDebuggerUrl) {
|
|
98
|
+
log('the browser never opened its debugging port');
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
try {
|
|
102
|
+
const res = await setCookie(version.webSocketDebuggerUrl, cookieParams(session));
|
|
103
|
+
log(res?.success === false ? 'the browser refused the cookie' : 'signed in');
|
|
104
|
+
} catch (err) {
|
|
105
|
+
log(`could not sign in: ${err.message}`);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Importable for tests; only runs when executed directly.
|
|
110
|
+
if (process.argv[1] && process.argv[1].endsWith('vault-seed.mjs')) {
|
|
111
|
+
main().catch((err) => log(`unexpected: ${err.message}`));
|
|
112
|
+
}
|
package/dist/tui/run.js
CHANGED
|
@@ -91,6 +91,9 @@ function createConsole(build, hooks = {}) {
|
|
|
91
91
|
function onKeypress(_s, key) {
|
|
92
92
|
void app.handleKey(key);
|
|
93
93
|
}
|
|
94
|
+
function onStdinError() {
|
|
95
|
+
teardown();
|
|
96
|
+
}
|
|
94
97
|
function attachInput() {
|
|
95
98
|
if (inputAttached)
|
|
96
99
|
return;
|
|
@@ -99,12 +102,20 @@ function createConsole(build, hooks = {}) {
|
|
|
99
102
|
process.stdin.setRawMode(true);
|
|
100
103
|
process.stdin.resume();
|
|
101
104
|
process.stdin.on('keypress', onKeypress);
|
|
105
|
+
// A terminal can be taken away mid-read: an ssh session drops, a window
|
|
106
|
+
// closes. The read then fails with EIO, and an 'error' with no listener is
|
|
107
|
+
// a thrown exception in node — the process dies without ever running
|
|
108
|
+
// teardown, so raw mode is never turned off and the shell that inherits
|
|
109
|
+
// the terminal reports its own "error on TTY read" afterwards. Treat it as
|
|
110
|
+
// what it is: the terminal is gone, so put it back and leave quietly.
|
|
111
|
+
process.stdin.on('error', onStdinError);
|
|
102
112
|
inputAttached = true;
|
|
103
113
|
}
|
|
104
114
|
function detachInput() {
|
|
105
115
|
if (!inputAttached)
|
|
106
116
|
return;
|
|
107
117
|
process.stdin.off('keypress', onKeypress);
|
|
118
|
+
process.stdin.off('error', onStdinError);
|
|
108
119
|
if (process.stdin.isTTY)
|
|
109
120
|
process.stdin.setRawMode(false);
|
|
110
121
|
process.stdin.pause();
|
|
@@ -142,6 +153,16 @@ function createConsole(build, hooks = {}) {
|
|
|
142
153
|
draw();
|
|
143
154
|
}
|
|
144
155
|
};
|
|
156
|
+
// Backgrounding a TUI is not an error, but reading the terminal from a
|
|
157
|
+
// background process group earns SIGTTIN, and the default action is to STOP
|
|
158
|
+
// the process — which is what "zsh: suspended (tty input)" is. Stopped mid-
|
|
159
|
+
// draw it still holds the terminal in raw mode, so the session it was
|
|
160
|
+
// sharing degrades too. Stop reading and leave instead; the daemon is a
|
|
161
|
+
// separate process and keeps running either way.
|
|
162
|
+
process.on('SIGTTIN', teardown);
|
|
163
|
+
process.on('SIGTTOU', teardown);
|
|
164
|
+
// The terminal went away without the read failing first.
|
|
165
|
+
process.on('SIGHUP', teardown);
|
|
145
166
|
app.replace(build(app, suspend));
|
|
146
167
|
return {
|
|
147
168
|
host: app,
|
package/package.json
CHANGED
|
@@ -1,62 +1,62 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@addai/node",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Daemon that pairs a machine with your +Ai account and runs Claude / Codex / Kimi / Gemini agents on its behalf. Reachable via Supabase from Vault, Entity Studio, or any other +Ai surface.",
|
|
5
|
-
"license": "MIT",
|
|
6
|
-
"keywords": [
|
|
7
|
-
"addai",
|
|
8
|
-
"entity-studio",
|
|
9
|
-
"claude",
|
|
10
|
-
"codex",
|
|
11
|
-
"kimi",
|
|
12
|
-
"gemini",
|
|
13
|
-
"agent",
|
|
14
|
-
"daemon",
|
|
15
|
-
"supabase",
|
|
16
|
-
"mcp"
|
|
17
|
-
],
|
|
18
|
-
"repository": {
|
|
19
|
-
"type": "git",
|
|
20
|
-
"url": "git+https://github.com/just-AddAi/addai-entity-runtime.git"
|
|
21
|
-
},
|
|
22
|
-
"homepage": "https://github.com/just-AddAi/addai-entity-runtime#readme",
|
|
23
|
-
"bugs": {
|
|
24
|
-
"url": "https://github.com/just-AddAi/addai-entity-runtime/issues"
|
|
25
|
-
},
|
|
26
|
-
"type": "commonjs",
|
|
27
|
-
"main": "dist/index.js",
|
|
28
|
-
"bin": {
|
|
29
|
-
"ainode": "dist/cli.js"
|
|
30
|
-
},
|
|
31
|
-
"publishConfig": {
|
|
32
|
-
"access": "public"
|
|
33
|
-
},
|
|
34
|
-
"files": [
|
|
35
|
-
"dist",
|
|
36
|
-
"scripts",
|
|
37
|
-
"README.md",
|
|
38
|
-
"assets"
|
|
39
|
-
],
|
|
40
|
-
"scripts": {
|
|
41
|
-
"build": "tsc -p tsconfig.json && node scripts/copy-assets.js",
|
|
42
|
-
"start": "node dist/cli.js",
|
|
43
|
-
"dev": "tsc -p tsconfig.json && node dist/cli.js",
|
|
44
|
-
"test": "npm run build && node --test test/*.test.mjs",
|
|
45
|
-
"clean": "node -e \"fs.rmSync('dist',{recursive:true,force:true})\"",
|
|
46
|
-
"postinstall": "node scripts/fix-pty-helper.js",
|
|
47
|
-
"prepublishOnly": "npm run build"
|
|
48
|
-
},
|
|
49
|
-
"engines": {
|
|
50
|
-
"node": ">=18"
|
|
51
|
-
},
|
|
52
|
-
"dependencies": {
|
|
53
|
-
"@addai/node-flows": "^1.0.0",
|
|
54
|
-
"node-pty": "^1.1.0",
|
|
55
|
-
"ws": "^8.21.3"
|
|
56
|
-
},
|
|
57
|
-
"devDependencies": {
|
|
58
|
-
"@types/node": "^20.0.0",
|
|
59
|
-
"@types/ws": "^8.18.1",
|
|
60
|
-
"typescript": "^5.6.0"
|
|
61
|
-
}
|
|
62
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@addai/node",
|
|
3
|
+
"version": "0.30.0",
|
|
4
|
+
"description": "Daemon that pairs a machine with your +Ai account and runs Claude / Codex / Kimi / Gemini agents on its behalf. Reachable via Supabase from Vault, Entity Studio, or any other +Ai surface.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"addai",
|
|
8
|
+
"entity-studio",
|
|
9
|
+
"claude",
|
|
10
|
+
"codex",
|
|
11
|
+
"kimi",
|
|
12
|
+
"gemini",
|
|
13
|
+
"agent",
|
|
14
|
+
"daemon",
|
|
15
|
+
"supabase",
|
|
16
|
+
"mcp"
|
|
17
|
+
],
|
|
18
|
+
"repository": {
|
|
19
|
+
"type": "git",
|
|
20
|
+
"url": "git+https://github.com/just-AddAi/addai-entity-runtime.git"
|
|
21
|
+
},
|
|
22
|
+
"homepage": "https://github.com/just-AddAi/addai-entity-runtime#readme",
|
|
23
|
+
"bugs": {
|
|
24
|
+
"url": "https://github.com/just-AddAi/addai-entity-runtime/issues"
|
|
25
|
+
},
|
|
26
|
+
"type": "commonjs",
|
|
27
|
+
"main": "dist/index.js",
|
|
28
|
+
"bin": {
|
|
29
|
+
"ainode": "dist/cli.js"
|
|
30
|
+
},
|
|
31
|
+
"publishConfig": {
|
|
32
|
+
"access": "public"
|
|
33
|
+
},
|
|
34
|
+
"files": [
|
|
35
|
+
"dist",
|
|
36
|
+
"scripts",
|
|
37
|
+
"README.md",
|
|
38
|
+
"assets"
|
|
39
|
+
],
|
|
40
|
+
"scripts": {
|
|
41
|
+
"build": "tsc -p tsconfig.json && node scripts/copy-assets.js",
|
|
42
|
+
"start": "node dist/cli.js",
|
|
43
|
+
"dev": "tsc -p tsconfig.json && node dist/cli.js",
|
|
44
|
+
"test": "npm run build && node --test test/*.test.mjs",
|
|
45
|
+
"clean": "node -e \"fs.rmSync('dist',{recursive:true,force:true})\"",
|
|
46
|
+
"postinstall": "node scripts/fix-pty-helper.js",
|
|
47
|
+
"prepublishOnly": "npm run build"
|
|
48
|
+
},
|
|
49
|
+
"engines": {
|
|
50
|
+
"node": ">=18"
|
|
51
|
+
},
|
|
52
|
+
"dependencies": {
|
|
53
|
+
"@addai/node-flows": "^1.0.0",
|
|
54
|
+
"node-pty": "^1.1.0",
|
|
55
|
+
"ws": "^8.21.3"
|
|
56
|
+
},
|
|
57
|
+
"devDependencies": {
|
|
58
|
+
"@types/node": "^20.0.0",
|
|
59
|
+
"@types/ws": "^8.18.1",
|
|
60
|
+
"typescript": "^5.6.0"
|
|
61
|
+
}
|
|
62
|
+
}
|
package/scripts/copy-assets.js
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
// tsc compiles .ts and ignores everything else, but two files in this package
|
|
2
|
-
// are deliberately plain .mjs: they run inside a desktop container (node 22,
|
|
3
|
-
// no build step, no node_modules) rather than in the daemon. Ship them next to
|
|
4
|
-
// the compiled output so dist/ is self-contained.
|
|
5
|
-
const fs = require('fs');
|
|
6
|
-
const path = require('path');
|
|
7
|
-
|
|
8
|
-
const ASSETS = [
|
|
9
|
-
['src/desktop/learn/recorder.mjs', 'dist/desktop/learn/recorder.mjs'],
|
|
10
|
-
['src/desktop/learn/injected.mjs', 'dist/desktop/learn/injected.mjs'],
|
|
11
|
-
['src/desktop/vault-seed.mjs', 'dist/desktop/vault-seed.mjs'],
|
|
12
|
-
];
|
|
13
|
-
|
|
14
|
-
for (const [from, to] of ASSETS) {
|
|
15
|
-
fs.mkdirSync(path.dirname(to), { recursive: true });
|
|
16
|
-
fs.copyFileSync(from, to);
|
|
17
|
-
}
|
|
18
|
-
console.log(`copied ${ASSETS.length} runtime assets into dist/`);
|
|
1
|
+
// tsc compiles .ts and ignores everything else, but two files in this package
|
|
2
|
+
// are deliberately plain .mjs: they run inside a desktop container (node 22,
|
|
3
|
+
// no build step, no node_modules) rather than in the daemon. Ship them next to
|
|
4
|
+
// the compiled output so dist/ is self-contained.
|
|
5
|
+
const fs = require('fs');
|
|
6
|
+
const path = require('path');
|
|
7
|
+
|
|
8
|
+
const ASSETS = [
|
|
9
|
+
['src/desktop/learn/recorder.mjs', 'dist/desktop/learn/recorder.mjs'],
|
|
10
|
+
['src/desktop/learn/injected.mjs', 'dist/desktop/learn/injected.mjs'],
|
|
11
|
+
['src/desktop/vault-seed.mjs', 'dist/desktop/vault-seed.mjs'],
|
|
12
|
+
];
|
|
13
|
+
|
|
14
|
+
for (const [from, to] of ASSETS) {
|
|
15
|
+
fs.mkdirSync(path.dirname(to), { recursive: true });
|
|
16
|
+
fs.copyFileSync(from, to);
|
|
17
|
+
}
|
|
18
|
+
console.log(`copied ${ASSETS.length} runtime assets into dist/`);
|
|
@@ -1,28 +1,28 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// node-pty ships POSIX prebuilds whose `spawn-helper` binary can lose its
|
|
3
|
-
// executable bit when the npm tarball is extracted. macOS node-pty
|
|
4
|
-
// posix_spawn()s that helper, so without +x every PTY spawn dies with
|
|
5
|
-
// "posix_spawnp failed." — see src/pty-helper.ts for the full story.
|
|
6
|
-
//
|
|
7
|
-
// This used to hunt for <pkg>/node_modules/node-pty by hand, a path that
|
|
8
|
-
// only exists when node-pty is NESTED. npm hoists it to the tree root under
|
|
9
|
-
// npx, so the fix silently did nothing on exactly the installs that needed
|
|
10
|
-
// it. The real logic now lives in the daemon (dist/pty-helper.js) and
|
|
11
|
-
// resolves the package the way Node does. The daemon also repairs at boot,
|
|
12
|
-
// so if dist isn't built yet (a source checkout installing before its first
|
|
13
|
-
// build) we exit quietly instead of duplicating the logic here.
|
|
14
|
-
|
|
15
|
-
let mod;
|
|
16
|
-
try {
|
|
17
|
-
mod = require('../dist/pty-helper.js');
|
|
18
|
-
} catch {
|
|
19
|
-
process.exit(0);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
try {
|
|
23
|
-
const line = mod.describeRepair(mod.ensurePtyHelperExecutable());
|
|
24
|
-
if (line) process.stdout.write(`[entities-runtime] ${line}\n`);
|
|
25
|
-
} catch (err) {
|
|
26
|
-
process.stderr.write(`[entities-runtime] pty helper check failed: ${err.message}\n`);
|
|
27
|
-
}
|
|
28
|
-
process.exit(0);
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// node-pty ships POSIX prebuilds whose `spawn-helper` binary can lose its
|
|
3
|
+
// executable bit when the npm tarball is extracted. macOS node-pty
|
|
4
|
+
// posix_spawn()s that helper, so without +x every PTY spawn dies with
|
|
5
|
+
// "posix_spawnp failed." — see src/pty-helper.ts for the full story.
|
|
6
|
+
//
|
|
7
|
+
// This used to hunt for <pkg>/node_modules/node-pty by hand, a path that
|
|
8
|
+
// only exists when node-pty is NESTED. npm hoists it to the tree root under
|
|
9
|
+
// npx, so the fix silently did nothing on exactly the installs that needed
|
|
10
|
+
// it. The real logic now lives in the daemon (dist/pty-helper.js) and
|
|
11
|
+
// resolves the package the way Node does. The daemon also repairs at boot,
|
|
12
|
+
// so if dist isn't built yet (a source checkout installing before its first
|
|
13
|
+
// build) we exit quietly instead of duplicating the logic here.
|
|
14
|
+
|
|
15
|
+
let mod;
|
|
16
|
+
try {
|
|
17
|
+
mod = require('../dist/pty-helper.js');
|
|
18
|
+
} catch {
|
|
19
|
+
process.exit(0);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
try {
|
|
23
|
+
const line = mod.describeRepair(mod.ensurePtyHelperExecutable());
|
|
24
|
+
if (line) process.stdout.write(`[entities-runtime] ${line}\n`);
|
|
25
|
+
} catch (err) {
|
|
26
|
+
process.stderr.write(`[entities-runtime] pty helper check failed: ${err.message}\n`);
|
|
27
|
+
}
|
|
28
|
+
process.exit(0);
|