@addai/node 0.27.0 → 0.28.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/assets/vault-extension/assets/inter.woff2 +0 -0
- package/assets/vault-extension/background.js +45 -0
- package/assets/vault-extension/content.js +62 -0
- package/assets/vault-extension/icons/icon-128.png +0 -0
- package/assets/vault-extension/icons/icon-16.png +0 -0
- package/assets/vault-extension/icons/icon-32.png +0 -0
- package/assets/vault-extension/icons/icon-48.png +0 -0
- package/assets/vault-extension/manifest.json +39 -0
- package/dist/capabilities.js +2 -1
- package/dist/command-runner.js +109 -2
- package/dist/desktop/docker.d.ts +4 -1
- package/dist/desktop/docker.js +7 -3
- package/dist/desktop/engine.d.ts +13 -0
- package/dist/desktop/engine.js +35 -4
- package/dist/desktop/install-engine.d.ts +33 -2
- package/dist/desktop/install-engine.js +138 -12
- package/dist/desktop/learn/browser.d.ts +41 -0
- package/dist/desktop/learn/browser.js +136 -0
- package/dist/desktop/learn/injected.mjs +173 -0
- package/dist/desktop/learn/recorder.mjs +151 -0
- package/dist/desktop/learn/session.d.ts +51 -0
- package/dist/desktop/learn/session.js +224 -0
- package/dist/desktop/provider.d.ts +4 -0
- package/dist/desktop/start-engine.d.ts +7 -4
- package/dist/desktop/start-engine.js +50 -6
- package/dist/desktop/vault-extension.d.ts +21 -0
- package/dist/desktop/vault-extension.js +112 -0
- package/dist/desktop/vault-seed.mjs +112 -0
- package/dist/desktop/vault-session.d.ts +41 -0
- package/dist/desktop/vault-session.js +164 -0
- package/dist/kimi-spawn.js +29 -10
- package/package.json +4 -3
- package/scripts/copy-assets.js +18 -0
- package/dist/tui.d.ts +0 -1
- package/dist/tui.js +0 -314
|
@@ -0,0 +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
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/** A GoTrue session, reduced to the two fields the cookie carries. */
|
|
2
|
+
export interface VaultSession {
|
|
3
|
+
access_token: string;
|
|
4
|
+
refresh_token: string;
|
|
5
|
+
/** Epoch ms. Used to decide whether to refresh before handing it over. */
|
|
6
|
+
expires_at: number;
|
|
7
|
+
email?: string;
|
|
8
|
+
entity_name?: string;
|
|
9
|
+
}
|
|
10
|
+
export declare function sessionPath(confDir: string): string;
|
|
11
|
+
export declare function readSession(confDir: string): VaultSession | null;
|
|
12
|
+
export declare function writeSession(confDir: string, s: VaultSession): void;
|
|
13
|
+
export declare function isFresh(s: VaultSession | null, now: number): boolean;
|
|
14
|
+
/** Exactly what the extension expects to find: the add.ai `auth` cookie is a
|
|
15
|
+
* URI-encoded JSON object with the two tokens in it. */
|
|
16
|
+
export declare function cookieValue(s: VaultSession): string;
|
|
17
|
+
type Fetcher = typeof fetch;
|
|
18
|
+
/** Trade a refresh token for a live one. Returns null when it has expired or
|
|
19
|
+
* been revoked — which is the normal way a rotation elsewhere reaches us. */
|
|
20
|
+
export declare function refreshSession(refreshToken: string, doFetch?: Fetcher): Promise<VaultSession | null>;
|
|
21
|
+
/** Sign in with a freshly rotated password. */
|
|
22
|
+
export declare function passwordSession(email: string, password: string, doFetch?: Fetcher): Promise<VaultSession | null>;
|
|
23
|
+
export interface LoginRpc {
|
|
24
|
+
(desktopId: string, rotate: boolean): Promise<any>;
|
|
25
|
+
}
|
|
26
|
+
export interface EnsureResult {
|
|
27
|
+
session: VaultSession | null;
|
|
28
|
+
/** Why there is no session, when there is none. */
|
|
29
|
+
reason?: string;
|
|
30
|
+
/** True when a rotation was performed — worth logging, it is destructive. */
|
|
31
|
+
rotated: boolean;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Get a live session for this desktop, minting one only when necessary.
|
|
35
|
+
*
|
|
36
|
+
* Order matters: cached-and-fresh, then refresh, then rotate. Skipping to
|
|
37
|
+
* rotation because it is simpler would log the entity out of every other
|
|
38
|
+
* browser it is signed into, every time a desktop restarts.
|
|
39
|
+
*/
|
|
40
|
+
export declare function ensureVaultSession(confDir: string, login: LoginRpc, desktopId: string, doFetch?: Fetcher, now?: number): Promise<EnsureResult>;
|
|
41
|
+
export {};
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.sessionPath = sessionPath;
|
|
37
|
+
exports.readSession = readSession;
|
|
38
|
+
exports.writeSession = writeSession;
|
|
39
|
+
exports.isFresh = isFresh;
|
|
40
|
+
exports.cookieValue = cookieValue;
|
|
41
|
+
exports.refreshSession = refreshSession;
|
|
42
|
+
exports.passwordSession = passwordSession;
|
|
43
|
+
exports.ensureVaultSession = ensureVaultSession;
|
|
44
|
+
// Signing a desktop's browser in, so the vault has something in it.
|
|
45
|
+
//
|
|
46
|
+
// The +Ai Vault extension shows whatever the browser's add.ai session can see,
|
|
47
|
+
// so an installed extension and no session is an empty vault. This mints the
|
|
48
|
+
// session on the host and leaves it where the desktop can pick it up.
|
|
49
|
+
//
|
|
50
|
+
// Rotation is destructive — asking for a new password drops every other
|
|
51
|
+
// session that identity holds, which would sign an entity out of every browser
|
|
52
|
+
// it uses. So the order is always: use the cached refresh token, and only ask
|
|
53
|
+
// the server for a password when there is nothing to refresh.
|
|
54
|
+
const fs = __importStar(require("fs"));
|
|
55
|
+
const path = __importStar(require("path"));
|
|
56
|
+
const config_1 = require("../config");
|
|
57
|
+
/** Refresh this long before expiry rather than at it: a browser that starts
|
|
58
|
+
* with a token about to die shows an empty vault for its first minute. */
|
|
59
|
+
const EARLY_REFRESH_MS = 5 * 60_000;
|
|
60
|
+
function sessionPath(confDir) {
|
|
61
|
+
return path.join(confDir, 'vault', 'session.json');
|
|
62
|
+
}
|
|
63
|
+
function readSession(confDir) {
|
|
64
|
+
try {
|
|
65
|
+
const raw = fs.readFileSync(sessionPath(confDir), 'utf8');
|
|
66
|
+
const s = JSON.parse(raw);
|
|
67
|
+
if (!s?.access_token || !s?.refresh_token)
|
|
68
|
+
return null;
|
|
69
|
+
return s;
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
function writeSession(confDir, s) {
|
|
76
|
+
const p = sessionPath(confDir);
|
|
77
|
+
fs.mkdirSync(path.dirname(p), { recursive: true });
|
|
78
|
+
// 0600 on the host side. Inside the container the entity has root and can
|
|
79
|
+
// read it, which is fine: these are its OWN credentials, and it can already
|
|
80
|
+
// rotate them through its browser MCP. What matters is that the host does
|
|
81
|
+
// not leave them world-readable in a shared runtime directory.
|
|
82
|
+
fs.writeFileSync(p, JSON.stringify(s), { mode: 0o600 });
|
|
83
|
+
}
|
|
84
|
+
function isFresh(s, now) {
|
|
85
|
+
return !!s && s.expires_at - EARLY_REFRESH_MS > now;
|
|
86
|
+
}
|
|
87
|
+
/** Exactly what the extension expects to find: the add.ai `auth` cookie is a
|
|
88
|
+
* URI-encoded JSON object with the two tokens in it. */
|
|
89
|
+
function cookieValue(s) {
|
|
90
|
+
return encodeURIComponent(JSON.stringify({
|
|
91
|
+
access_token: s.access_token,
|
|
92
|
+
refresh_token: s.refresh_token,
|
|
93
|
+
}));
|
|
94
|
+
}
|
|
95
|
+
function toSession(body, extra = {}) {
|
|
96
|
+
if (!body?.access_token || !body?.refresh_token)
|
|
97
|
+
return null;
|
|
98
|
+
const ttl = Number(body.expires_in ?? 3600) * 1000;
|
|
99
|
+
return {
|
|
100
|
+
access_token: body.access_token,
|
|
101
|
+
refresh_token: body.refresh_token,
|
|
102
|
+
expires_at: Date.now() + ttl,
|
|
103
|
+
...extra,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
/** Trade a refresh token for a live one. Returns null when it has expired or
|
|
107
|
+
* been revoked — which is the normal way a rotation elsewhere reaches us. */
|
|
108
|
+
async function refreshSession(refreshToken, doFetch = fetch) {
|
|
109
|
+
const res = await doFetch(`${config_1.SUPABASE_URL}/auth/v1/token?grant_type=refresh_token`, {
|
|
110
|
+
method: 'POST',
|
|
111
|
+
headers: { 'Content-Type': 'application/json', apikey: config_1.SUPABASE_ANON_KEY },
|
|
112
|
+
body: JSON.stringify({ refresh_token: refreshToken }),
|
|
113
|
+
});
|
|
114
|
+
if (!res.ok)
|
|
115
|
+
return null;
|
|
116
|
+
return toSession(await res.json());
|
|
117
|
+
}
|
|
118
|
+
/** Sign in with a freshly rotated password. */
|
|
119
|
+
async function passwordSession(email, password, doFetch = fetch) {
|
|
120
|
+
const res = await doFetch(`${config_1.SUPABASE_URL}/auth/v1/token?grant_type=password`, {
|
|
121
|
+
method: 'POST',
|
|
122
|
+
headers: { 'Content-Type': 'application/json', apikey: config_1.SUPABASE_ANON_KEY },
|
|
123
|
+
body: JSON.stringify({ email, password }),
|
|
124
|
+
});
|
|
125
|
+
if (!res.ok)
|
|
126
|
+
return null;
|
|
127
|
+
return toSession(await res.json(), { email });
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Get a live session for this desktop, minting one only when necessary.
|
|
131
|
+
*
|
|
132
|
+
* Order matters: cached-and-fresh, then refresh, then rotate. Skipping to
|
|
133
|
+
* rotation because it is simpler would log the entity out of every other
|
|
134
|
+
* browser it is signed into, every time a desktop restarts.
|
|
135
|
+
*/
|
|
136
|
+
async function ensureVaultSession(confDir, login, desktopId, doFetch = fetch, now = Date.now()) {
|
|
137
|
+
const cached = readSession(confDir);
|
|
138
|
+
if (isFresh(cached, now))
|
|
139
|
+
return { session: cached, rotated: false };
|
|
140
|
+
if (cached?.refresh_token) {
|
|
141
|
+
const refreshed = await refreshSession(cached.refresh_token, doFetch);
|
|
142
|
+
if (refreshed) {
|
|
143
|
+
const merged = { ...refreshed, email: cached.email, entity_name: cached.entity_name };
|
|
144
|
+
writeSession(confDir, merged);
|
|
145
|
+
return { session: merged, rotated: false };
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
// Ask the server who this desktop belongs to AND for a password in one call.
|
|
149
|
+
// It refuses to guess on a shared desktop with no default entity, which is
|
|
150
|
+
// the case where planting one entity's session would hand its vault to
|
|
151
|
+
// another.
|
|
152
|
+
const info = await login(desktopId, true);
|
|
153
|
+
if (!info?.ok)
|
|
154
|
+
return { session: null, reason: info?.reason ?? 'login_refused', rotated: false };
|
|
155
|
+
if (!info.password || !info.email) {
|
|
156
|
+
return { session: null, reason: 'no_credentials_returned', rotated: false };
|
|
157
|
+
}
|
|
158
|
+
const fresh = await passwordSession(info.email, info.password, doFetch);
|
|
159
|
+
if (!fresh)
|
|
160
|
+
return { session: null, reason: 'sign_in_failed', rotated: true };
|
|
161
|
+
const out = { ...fresh, entity_name: info.entity_name };
|
|
162
|
+
writeSession(confDir, out);
|
|
163
|
+
return { session: out, rotated: true };
|
|
164
|
+
}
|
package/dist/kimi-spawn.js
CHANGED
|
@@ -68,17 +68,37 @@ const child_process_1 = require("child_process");
|
|
|
68
68
|
const fs = __importStar(require("fs"));
|
|
69
69
|
const path = __importStar(require("path"));
|
|
70
70
|
const kimi_binary_1 = require("./kimi-binary");
|
|
71
|
+
const harness_registry_1 = require("./harness-registry");
|
|
71
72
|
const win_1 = require("./win");
|
|
72
73
|
const events_1 = require("./events");
|
|
73
74
|
const think_split_1 = require("./think-split");
|
|
74
75
|
const mcp_headers_1 = require("./mcp-headers");
|
|
75
|
-
/**
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
*
|
|
79
|
-
*
|
|
76
|
+
/**
|
|
77
|
+
* The effort level to send kimi, or null to send nothing.
|
|
78
|
+
*
|
|
79
|
+
* KIMI_MODEL_THINKING_EFFORT is a real kimi env var, but it exists precisely to
|
|
80
|
+
* BYPASS the model's declared support_efforts — kimi forwards the string to the
|
|
81
|
+
* provider verbatim and lets the API judge it. Setting it unconditionally is
|
|
82
|
+
* therefore not a safe default: an entity on medium effort got
|
|
83
|
+
*
|
|
84
|
+
* provider.api_error: 400 Invalid request Error
|
|
85
|
+
*
|
|
86
|
+
* four seconds into the run, every run, because the model behind kimi takes no
|
|
87
|
+
* thinking effort at all.
|
|
88
|
+
*
|
|
89
|
+
* The harness registry already records which levels a harness supports, and
|
|
90
|
+
* kimi's list is empty — so the registry decides, and an empty list means the
|
|
91
|
+
* variable is never set. If kimi gains effort support, updating the registry is
|
|
92
|
+
* enough; nothing here needs to change.
|
|
93
|
+
*/
|
|
80
94
|
function kimiEffort(level) {
|
|
81
|
-
|
|
95
|
+
const supported = (0, harness_registry_1.harness)('kimi')?.efforts ?? [];
|
|
96
|
+
if (supported.length === 0)
|
|
97
|
+
return null;
|
|
98
|
+
// 'max' was retired by kimi (it migrates a stored 'max' to 'high') and it
|
|
99
|
+
// never had our 'xhigh', so both collapse to the highest level it declares.
|
|
100
|
+
const wanted = level === 'max' || level === 'xhigh' ? 'high' : level;
|
|
101
|
+
return supported.includes(wanted) ? wanted : null;
|
|
82
102
|
}
|
|
83
103
|
/**
|
|
84
104
|
* Write the entity's MCP servers where kimi will actually read them:
|
|
@@ -339,13 +359,12 @@ function spawnKimi(input) {
|
|
|
339
359
|
// No HOME override: the real $HOME flows through so kimi's own auth +
|
|
340
360
|
// shelled-out tools (git/pip/node) can find their config files. The
|
|
341
361
|
// explicit --mcp-config-file already prevents host mcp.json leakage.
|
|
362
|
+
const effortValue = input.effortLevel ? kimiEffort(input.effortLevel) : null;
|
|
342
363
|
const childEnv = {
|
|
343
364
|
...process.env,
|
|
344
365
|
TERM: 'dumb',
|
|
345
|
-
//
|
|
346
|
-
|
|
347
|
-
// rather than send a level the provider may reject.
|
|
348
|
-
...(input.effortLevel ? { KIMI_MODEL_THINKING_EFFORT: kimiEffort(input.effortLevel) } : {}),
|
|
366
|
+
// Effort, only when the registry says kimi can take one — see kimiEffort.
|
|
367
|
+
...(effortValue ? { KIMI_MODEL_THINKING_EFFORT: effortValue } : {}),
|
|
349
368
|
};
|
|
350
369
|
const listeners = [];
|
|
351
370
|
const emit = (e) => { for (const l of listeners)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@addai/node",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.28.0",
|
|
4
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
5
|
"license": "MIT",
|
|
6
6
|
"keywords": [
|
|
@@ -34,10 +34,11 @@
|
|
|
34
34
|
"files": [
|
|
35
35
|
"dist",
|
|
36
36
|
"scripts",
|
|
37
|
-
"README.md"
|
|
37
|
+
"README.md",
|
|
38
|
+
"assets"
|
|
38
39
|
],
|
|
39
40
|
"scripts": {
|
|
40
|
-
"build": "tsc -p tsconfig.json",
|
|
41
|
+
"build": "tsc -p tsconfig.json && node scripts/copy-assets.js",
|
|
41
42
|
"start": "node dist/cli.js",
|
|
42
43
|
"dev": "tsc -p tsconfig.json && node dist/cli.js",
|
|
43
44
|
"test": "npm run build && node --test test/*.test.mjs",
|
|
@@ -0,0 +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/`);
|
package/dist/tui.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare function runHarnessesTui(): Promise<void>;
|