@kili-ai/dev-install 0.2.64

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Kili
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,36 @@
1
+ # @kili-ai/install
2
+
3
+ One-command setup for **Kili for Claude Code**, built entirely on Claude
4
+ Code's own supported settings (`spinnerVerbs`, `statusLine`,
5
+ `claudeCode.spinnerVerbs`). Nothing here patches VS Code, Cursor, or Claude
6
+ Code's own files.
7
+
8
+ ```sh
9
+ npx -y @kili-ai/install
10
+ ```
11
+
12
+ This will:
13
+
14
+ 1. Detect which of VS Code / Cursor / VSCodium you have installed.
15
+ 2. Install the `@kili-ai/ide` extension into each.
16
+ 3. Open your browser to sign in (or create a Kili publisher account).
17
+ 4. Write the resulting API key into each editor's settings automatically.
18
+
19
+ Reload your editor and Claude Code's "Discombobulating…" spinner starts
20
+ showing sponsored copy instead.
21
+
22
+ ## Re-authenticating
23
+
24
+ ```sh
25
+ npx @kili-ai/install login
26
+ ```
27
+
28
+ ## How sign-in works
29
+
30
+ A loopback HTTP server on `127.0.0.1:<random port>` opens
31
+ `https://app.trykili.ai/cli-auth?port=…&state=…` in your default browser. That
32
+ page reuses the normal dashboard login (magic link or Google) and, once
33
+ you're signed in, mints an API key through the existing `POST /api-keys`
34
+ endpoint and posts it back to the loopback server. No password or key is ever
35
+ typed into the terminal — you approve everything in the browser tab you
36
+ already trust. See `pkg.vscode.kili` for the full architecture.
package/dist/index.js ADDED
@@ -0,0 +1,464 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __create = Object.create;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") {
11
+ for (let key of __getOwnPropNames(from))
12
+ if (!__hasOwnProp.call(to, key) && key !== except)
13
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
14
+ }
15
+ return to;
16
+ };
17
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
18
+ // If the importer is in node compatibility mode or this is not an ESM
19
+ // file that has been converted to a CommonJS file using a Babel-
20
+ // compatible transform (i.e. "__esModule" has not been set), then set
21
+ // "default" to the CommonJS "module.exports" for node compatibility.
22
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
23
+ mod
24
+ ));
25
+
26
+ // src/index.ts
27
+ var import_node_fs3 = require("fs");
28
+ var import_node_path4 = require("path");
29
+
30
+ // src/auth.ts
31
+ var import_node_crypto = require("crypto");
32
+ var import_node_http = require("http");
33
+ var import_node_readline = require("readline");
34
+
35
+ // src/constants.ts
36
+ var PACKAGE_VERSION = true ? "0.2.64" : "0.1.0";
37
+ var NPM_ORG = "kili-ai";
38
+ var PACKAGE_NAME = `@${NPM_ORG}/ide.install`;
39
+ var DEFAULT_WEB_URL = true ? "https://kili-ui.vercel.app/" : "https://app.trykili.ai";
40
+ var DEFAULT_API_URL = true ? "https://app-dev.trykili.ai" : "https://api.trykili.ai";
41
+ var CLI_AUTH_PATH = "/cli-auth";
42
+ var AUTH_TIMEOUT_MS = 5 * 60 * 1e3;
43
+ var EXTENSION_ID = true ? "kili-ai.kili-ide-dev" : "kili-ai.kili-ide";
44
+
45
+ // src/auth.ts
46
+ async function signIn(webUrl = DEFAULT_WEB_URL) {
47
+ const state = (0, import_node_crypto.randomBytes)(16).toString("hex");
48
+ const { server, port, result } = await _listen(state);
49
+ const authUrl = new URL(CLI_AUTH_PATH, webUrl);
50
+ authUrl.searchParams.set("port", String(port));
51
+ authUrl.searchParams.set("state", state);
52
+ const open = (await import("open")).default;
53
+ console.log(`Authenticate your account at:
54
+ ${authUrl.toString()}`);
55
+ console.log("Press ENTER to open in the browser...");
56
+ await _waitForEnter();
57
+ await open(authUrl.toString());
58
+ try {
59
+ return await _withTimeout(result, AUTH_TIMEOUT_MS);
60
+ } finally {
61
+ server.close();
62
+ }
63
+ }
64
+ function _waitForEnter() {
65
+ return new Promise((resolve) => {
66
+ const rl = (0, import_node_readline.createInterface)({
67
+ input: process.stdin,
68
+ output: process.stdout
69
+ });
70
+ rl.question("", () => {
71
+ rl.close();
72
+ resolve();
73
+ });
74
+ });
75
+ }
76
+ function _listen(expectedState) {
77
+ return new Promise((resolveListen) => {
78
+ let settle;
79
+ let fail;
80
+ const result = new Promise((res, rej) => {
81
+ settle = res;
82
+ fail = rej;
83
+ });
84
+ const server = (0, import_node_http.createServer)((req, res) => {
85
+ const url = new URL(req.url ?? "/", "http://127.0.0.1");
86
+ if (url.pathname !== "/callback") {
87
+ res.writeHead(404).end();
88
+ return;
89
+ }
90
+ res.setHeader("Access-Control-Allow-Origin", "*");
91
+ if (req.method === "OPTIONS") {
92
+ res.writeHead(204).end();
93
+ return;
94
+ }
95
+ const state = url.searchParams.get("state");
96
+ const apiKey = url.searchParams.get("key");
97
+ if (state !== expectedState || !apiKey) {
98
+ res.writeHead(400).end("Invalid or mismatched sign-in state.");
99
+ fail(new Error("Sign-in state mismatch."));
100
+ return;
101
+ }
102
+ res.writeHead(200, { "Content-Type": "text/html" }).end(
103
+ '<html><body style="font-family:sans-serif;padding:2rem"><h2>Signed in to Kili</h2><p>You can close this tab and go back to your editor.</p></body></html>'
104
+ );
105
+ settle({ apiKey });
106
+ });
107
+ server.listen(0, "127.0.0.1", () => {
108
+ const address = server.address();
109
+ const port = typeof address === "object" && address ? address.port : 0;
110
+ resolveListen({ server, port, result });
111
+ });
112
+ });
113
+ }
114
+ function _withTimeout(promise, ms) {
115
+ return new Promise((resolve, reject) => {
116
+ const timer = setTimeout(
117
+ () => reject(new Error("Timed out waiting for sign-in in the browser.")),
118
+ ms
119
+ );
120
+ promise.then(
121
+ (value) => {
122
+ clearTimeout(timer);
123
+ resolve(value);
124
+ },
125
+ (error) => {
126
+ clearTimeout(timer);
127
+ reject(error);
128
+ }
129
+ );
130
+ });
131
+ }
132
+
133
+ // src/editors.ts
134
+ var import_node_child_process = require("child_process");
135
+ var import_node_os = require("os");
136
+ var import_node_path = require("path");
137
+ var EDITORS = [
138
+ { id: "code", label: "VS Code", bin: "code", userDataFolder: "Code" },
139
+ { id: "cursor", label: "Cursor", bin: "cursor", userDataFolder: "Cursor" },
140
+ {
141
+ id: "codium",
142
+ label: "VSCodium",
143
+ bin: "codium",
144
+ userDataFolder: "VSCodium"
145
+ }
146
+ ];
147
+ function detectEditors() {
148
+ return EDITORS.filter((editor) => _isOnPath(editor.bin));
149
+ }
150
+ function _isOnPath(bin) {
151
+ try {
152
+ const checkCmd = (0, import_node_os.platform)() === "win32" ? "where" : "which";
153
+ (0, import_node_child_process.execFileSync)(checkCmd, [bin], { stdio: "ignore" });
154
+ return true;
155
+ } catch {
156
+ return false;
157
+ }
158
+ }
159
+ function installExtension(editor, vsixPathOrId) {
160
+ (0, import_node_child_process.execFileSync)(editor.bin, ["--install-extension", vsixPathOrId, "--force"], {
161
+ stdio: "inherit",
162
+ shell: (0, import_node_os.platform)() === "win32"
163
+ });
164
+ }
165
+ function userSettingsPath(editor) {
166
+ const home = (0, import_node_os.homedir)();
167
+ switch ((0, import_node_os.platform)()) {
168
+ case "win32": {
169
+ const appData = process.env.APPDATA ?? (0, import_node_path.join)(home, "AppData", "Roaming");
170
+ return (0, import_node_path.join)(appData, editor.userDataFolder, "User", "settings.json");
171
+ }
172
+ case "darwin":
173
+ return (0, import_node_path.join)(
174
+ home,
175
+ "Library",
176
+ "Application Support",
177
+ editor.userDataFolder,
178
+ "User",
179
+ "settings.json"
180
+ );
181
+ default:
182
+ return (0, import_node_path.join)(
183
+ process.env.XDG_CONFIG_HOME ?? (0, import_node_path.join)(home, ".config"),
184
+ editor.userDataFolder,
185
+ "User",
186
+ "settings.json"
187
+ );
188
+ }
189
+ }
190
+
191
+ // src/settings.ts
192
+ var import_node_fs = require("fs");
193
+ var import_node_path2 = require("path");
194
+ var import_jsonc_parser = require("jsonc-parser");
195
+ var SettingsParseError = class extends Error {
196
+ };
197
+ function mergeSettings(path, values) {
198
+ const current = _readJsonObject(path);
199
+ const next = { ...current, ...values };
200
+ _writeAtomic(path, next);
201
+ }
202
+ function _readJsonObject(path) {
203
+ if (!(0, import_node_fs.existsSync)(path)) return {};
204
+ const text = (0, import_node_fs.readFileSync)(path, "utf8");
205
+ const errors = [];
206
+ const parsed = (0, import_jsonc_parser.parse)(text, errors, { allowTrailingComma: true });
207
+ if (errors.length > 0) {
208
+ const first = errors[0];
209
+ throw new SettingsParseError(
210
+ `${path} exists but could not be parsed as JSON, even allowing for JSONC comments/trailing commas -- refusing to overwrite it. (${(0, import_jsonc_parser.printParseErrorCode)(first.error)} at offset ${first.offset})`
211
+ );
212
+ }
213
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
214
+ return parsed;
215
+ }
216
+ return {};
217
+ }
218
+ function _writeAtomic(path, value) {
219
+ (0, import_node_fs.mkdirSync)((0, import_node_path2.dirname)(path), { recursive: true });
220
+ const tmp = `${path}.kili-tmp-${process.pid}`;
221
+ (0, import_node_fs.writeFileSync)(tmp, `${JSON.stringify(value, null, 2)}
222
+ `, "utf8");
223
+ (0, import_node_fs.writeFileSync)(path, (0, import_node_fs.readFileSync)(tmp, "utf8"), "utf8");
224
+ try {
225
+ require("fs").unlinkSync(tmp);
226
+ } catch {
227
+ }
228
+ }
229
+
230
+ // src/terminal.ts
231
+ var import_node_crypto2 = require("crypto");
232
+ var import_node_fs2 = require("fs");
233
+ var import_node_os2 = require("os");
234
+ var import_node_path3 = require("path");
235
+ var import_jsonc_parser2 = require("jsonc-parser");
236
+ function _kiliHome() {
237
+ return (0, import_node_path3.join)((0, import_node_os2.homedir)(), ".kili");
238
+ }
239
+ function _runtimeConfigPath() {
240
+ return (0, import_node_path3.join)(_kiliHome(), "config.json");
241
+ }
242
+ function _claudeSettingsPath() {
243
+ return (0, import_node_path3.join)((0, import_node_os2.homedir)(), ".claude", "settings.json");
244
+ }
245
+ function _settingsBackupPath() {
246
+ return (0, import_node_path3.join)(_kiliHome(), "settings.backup.json");
247
+ }
248
+ function _terminalBinDir() {
249
+ return (0, import_node_path3.join)(_kiliHome(), "bin");
250
+ }
251
+ function _readJson(path, fallback) {
252
+ if (!(0, import_node_fs2.existsSync)(path)) return fallback;
253
+ try {
254
+ return JSON.parse((0, import_node_fs2.readFileSync)(path, "utf8"));
255
+ } catch {
256
+ return fallback;
257
+ }
258
+ }
259
+ function _readJsonOwnedByUser(path, fallback) {
260
+ if (!(0, import_node_fs2.existsSync)(path)) return fallback;
261
+ const text = (0, import_node_fs2.readFileSync)(path, "utf8");
262
+ const errors = [];
263
+ const parsed = (0, import_jsonc_parser2.parse)(text, errors, { allowTrailingComma: true });
264
+ if (errors.length > 0) {
265
+ const first = errors[0];
266
+ throw new SettingsParseError(
267
+ `${path} exists but could not be parsed as JSON, even allowing for JSONC comments/trailing commas -- refusing to overwrite it. (${(0, import_jsonc_parser2.printParseErrorCode)(first.error)} at offset ${first.offset})`
268
+ );
269
+ }
270
+ return parsed;
271
+ }
272
+ function _writeJsonAtomic(path, value) {
273
+ (0, import_node_fs2.mkdirSync)((0, import_node_path3.dirname)(path), { recursive: true });
274
+ const tmp = `${path}.kili-tmp-${process.pid}`;
275
+ (0, import_node_fs2.writeFileSync)(tmp, `${JSON.stringify(value, null, 2)}
276
+ `, "utf8");
277
+ (0, import_node_fs2.writeFileSync)(path, (0, import_node_fs2.readFileSync)(tmp, "utf8"), "utf8");
278
+ try {
279
+ require("fs").unlinkSync(tmp);
280
+ } catch {
281
+ }
282
+ }
283
+ function installTerminalBin(packageDistDir) {
284
+ const sourceDir = (0, import_node_path3.join)(packageDistDir, "kili-bin");
285
+ const hookSource = (0, import_node_path3.join)(sourceDir, "hook.js");
286
+ const statuslineSource = (0, import_node_path3.join)(sourceDir, "statusline.js");
287
+ if (!(0, import_node_fs2.existsSync)(hookSource) || !(0, import_node_fs2.existsSync)(statuslineSource)) return null;
288
+ const destDir = _terminalBinDir();
289
+ (0, import_node_fs2.mkdirSync)(destDir, { recursive: true });
290
+ const hookPath = (0, import_node_path3.join)(destDir, "hook.js");
291
+ const statuslinePath = (0, import_node_path3.join)(destDir, "statusline.js");
292
+ (0, import_node_fs2.copyFileSync)(hookSource, hookPath);
293
+ (0, import_node_fs2.copyFileSync)(statuslineSource, statuslinePath);
294
+ for (const name of ["hook.js.map", "statusline.js.map"]) {
295
+ const src = (0, import_node_path3.join)(sourceDir, name);
296
+ if ((0, import_node_fs2.existsSync)(src)) (0, import_node_fs2.copyFileSync)(src, (0, import_node_path3.join)(destDir, name));
297
+ }
298
+ return { hookPath, statuslinePath };
299
+ }
300
+ function readCachedApiKey() {
301
+ const existing = _readJson(_runtimeConfigPath(), {});
302
+ return existing.apiKey?.trim() || null;
303
+ }
304
+ function writeTerminalRuntimeConfig(input) {
305
+ const existing = _readJson(_runtimeConfigPath(), {});
306
+ const installId = existing.installId ?? (0, import_node_crypto2.randomUUID)();
307
+ _writeJsonAtomic(_runtimeConfigPath(), {
308
+ ...existing,
309
+ apiKey: input.apiKey,
310
+ apiUrl: input.apiUrl,
311
+ webUrl: input.webUrl,
312
+ enabled: true,
313
+ installId,
314
+ webviewPatchedEditors: existing.webviewPatchedEditors ?? []
315
+ });
316
+ }
317
+ var _HOOK_MARKER = "__kili_hook__";
318
+ var _OWNED_HOOK_EVENTS = ["UserPromptSubmit", "Stop"];
319
+ function _backupOnce() {
320
+ const backupPath = _settingsBackupPath();
321
+ if ((0, import_node_fs2.existsSync)(backupPath)) return;
322
+ const settingsPath = _claudeSettingsPath();
323
+ if (!(0, import_node_fs2.existsSync)(settingsPath)) return;
324
+ (0, import_node_fs2.mkdirSync)((0, import_node_path3.dirname)(backupPath), { recursive: true });
325
+ (0, import_node_fs2.writeFileSync)(backupPath, (0, import_node_fs2.readFileSync)(settingsPath, "utf8"), "utf8");
326
+ }
327
+ function _isOwnedHookEntry(entry) {
328
+ return JSON.stringify(entry).includes(_HOOK_MARKER);
329
+ }
330
+ function _mergeHooks(hooks, hookPath) {
331
+ const next = { ...hooks };
332
+ for (const event of _OWNED_HOOK_EVENTS) {
333
+ const existing = (next[event] ?? []).filter(
334
+ (entry) => !_isOwnedHookEntry(entry)
335
+ );
336
+ next[event] = [
337
+ ...existing,
338
+ {
339
+ matcher: "*",
340
+ hooks: [
341
+ {
342
+ type: "command",
343
+ command: `node "${hookPath}" ${event} ${_HOOK_MARKER}`
344
+ }
345
+ ]
346
+ }
347
+ ];
348
+ }
349
+ return next;
350
+ }
351
+ function enableTerminalHooks(hookPath, statuslinePath) {
352
+ _backupOnce();
353
+ const settings = _readJsonOwnedByUser(
354
+ _claudeSettingsPath(),
355
+ {}
356
+ );
357
+ settings.spinnerVerbs = { mode: "replace", verbs: ["Sponsored"] };
358
+ settings.statusLine = {
359
+ type: "command",
360
+ command: `node "${statuslinePath}"`
361
+ };
362
+ settings.hooks = _mergeHooks(settings.hooks ?? {}, hookPath);
363
+ _writeJsonAtomic(_claudeSettingsPath(), settings);
364
+ }
365
+
366
+ // src/index.ts
367
+ async function main() {
368
+ const args = process.argv.slice(2);
369
+ const command = args[0] ?? "install";
370
+ const webUrl = _flag(args, "--web-url") ?? DEFAULT_WEB_URL;
371
+ if (command === "--version" || command === "-v") {
372
+ console.log(PACKAGE_VERSION);
373
+ return;
374
+ }
375
+ if (command === "login") {
376
+ await _login(webUrl);
377
+ return;
378
+ }
379
+ if (command === "install") {
380
+ await _install(webUrl);
381
+ return;
382
+ }
383
+ console.log(`Unknown command "${command}". Try "install" or "login".`);
384
+ process.exitCode = 1;
385
+ }
386
+ async function _install(webUrl) {
387
+ const editors = detectEditors();
388
+ if (editors.length === 0) {
389
+ console.log(
390
+ "No supported editor CLI (code / cursor / codium) found on PATH -- setting up the terminal only. Install VS Code or Cursor and re-run this installer if you also want the chat-panel spinner ad."
391
+ );
392
+ } else {
393
+ console.log(`Found: ${editors.map((e) => e.label).join(", ")}`);
394
+ const vsix = _localVsix();
395
+ for (const editor of editors) {
396
+ console.log(`Installing Kili into ${editor.label}...`);
397
+ try {
398
+ installExtension(editor, vsix ?? EXTENSION_ID);
399
+ } catch (error) {
400
+ console.log(
401
+ `Could not install into ${editor.label} automatically (${error.message}). Install "${EXTENSION_ID}" manually from its Extensions view.`
402
+ );
403
+ }
404
+ }
405
+ }
406
+ const cachedApiKey = readCachedApiKey();
407
+ const apiKey = cachedApiKey ?? (await signIn(webUrl)).apiKey;
408
+ for (const editor of editors) {
409
+ _mergeEditorSettingsSafely(editor, apiKey, webUrl);
410
+ }
411
+ _setUpTerminal(apiKey, webUrl);
412
+ console.log(
413
+ editors.length > 0 ? '\nSigned in. Reload your editor(s) -- Claude Code\'s spinner will start showing sponsored copy in both your editor and any terminal.\nRun `npx @kili-ai/install login` any time to re-sign-in, or "Kili: Disable" from the command palette to turn it off.' : "\nSigned in. Open a new terminal and run `claude` -- the spinner will start showing sponsored copy.\nRun `npx @kili-ai/install login` any time to re-sign-in."
414
+ );
415
+ }
416
+ function _mergeEditorSettingsSafely(editor, apiKey, webUrl) {
417
+ try {
418
+ mergeSettings(userSettingsPath(editor), {
419
+ "kili.apiKey": apiKey,
420
+ "kili.webUrl": webUrl
421
+ });
422
+ } catch (error) {
423
+ console.log(
424
+ `Could not update ${editor.label}'s settings.json (${error.message}). Set "kili.apiKey" there by hand, or fix the file's JSON and re-run.`
425
+ );
426
+ }
427
+ }
428
+ function _setUpTerminal(apiKey, webUrl) {
429
+ try {
430
+ const bin = installTerminalBin(__dirname);
431
+ if (!bin) {
432
+ console.log(
433
+ "(terminal spinner/status line not set up -- this build has no bundled hook.js/statusline.js)"
434
+ );
435
+ return;
436
+ }
437
+ writeTerminalRuntimeConfig({ apiKey, apiUrl: DEFAULT_API_URL, webUrl });
438
+ enableTerminalHooks(bin.hookPath, bin.statuslinePath);
439
+ } catch (error) {
440
+ console.log(
441
+ `(terminal spinner/status line setup failed: ${error.message})`
442
+ );
443
+ }
444
+ }
445
+ async function _login(webUrl) {
446
+ const editors = detectEditors();
447
+ const { apiKey } = await signIn(webUrl);
448
+ for (const editor of editors) {
449
+ _mergeEditorSettingsSafely(editor, apiKey, webUrl);
450
+ }
451
+ _setUpTerminal(apiKey, webUrl);
452
+ console.log("Signed in.");
453
+ }
454
+ function _localVsix() {
455
+ const candidate = (0, import_node_path4.join)(__dirname, "kili-ide.vsix");
456
+ return (0, import_node_fs3.existsSync)(candidate) ? candidate : null;
457
+ }
458
+ function _flag(args, name) {
459
+ const index = args.indexOf(name);
460
+ if (index === -1 || index === args.length - 1) return void 0;
461
+ return args[index + 1];
462
+ }
463
+ void main();
464
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/auth.ts","../src/constants.ts","../src/editors.ts","../src/settings.ts","../src/terminal.ts"],"sourcesContent":["import { existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { signIn } from \"./auth\";\nimport {\n\tDEFAULT_API_URL,\n\tDEFAULT_WEB_URL,\n\tEXTENSION_ID,\n\tPACKAGE_VERSION,\n} from \"./constants\";\nimport { detectEditors, installExtension, userSettingsPath } from \"./editors\";\nimport { mergeSettings } from \"./settings\";\nimport {\n\tenableTerminalHooks,\n\tinstallTerminalBin,\n\treadCachedApiKey,\n\twriteTerminalRuntimeConfig,\n} from \"./terminal\";\n\nasync function main() {\n\tconst args = process.argv.slice(2);\n\tconst command = args[0] ?? \"install\";\n\tconst webUrl = _flag(args, \"--web-url\") ?? DEFAULT_WEB_URL;\n\n\tif (command === \"--version\" || command === \"-v\") {\n\t\tconsole.log(PACKAGE_VERSION);\n\t\treturn;\n\t}\n\n\tif (command === \"login\") {\n\t\tawait _login(webUrl);\n\t\treturn;\n\t}\n\n\tif (command === \"install\") {\n\t\tawait _install(webUrl);\n\t\treturn;\n\t}\n\n\tconsole.log(`Unknown command \"${command}\". Try \"install\" or \"login\".`);\n\tprocess.exitCode = 1;\n}\n\n/**\n * Full flow: detect editors (if\n * any), install the extension into each, sign in once, and configure both\n * every detected editor AND the terminal directly -- so someone with no\n * VS Code-family editor installed at all still gets working terminal ads,\n * not just an error. See `terminal.ts`'s top doc comment for why the\n * terminal setup can't just wait for the extension to do it.\n */\nasync function _install(webUrl: string) {\n\tconst editors = detectEditors();\n\tif (editors.length === 0) {\n\t\tconsole.log(\n\t\t\t\"No supported editor CLI (code / cursor / codium) found on PATH -- \" +\n\t\t\t\t\"setting up the terminal only. Install VS Code or Cursor and \" +\n\t\t\t\t\"re-run this installer if you also want the chat-panel spinner ad.\",\n\t\t);\n\t} else {\n\t\tconsole.log(`Found: ${editors.map((e) => e.label).join(\", \")}`);\n\t\tconst vsix = _localVsix();\n\t\tfor (const editor of editors) {\n\t\t\tconsole.log(`Installing Kili into ${editor.label}...`);\n\t\t\ttry {\n\t\t\t\tinstallExtension(editor, vsix ?? EXTENSION_ID);\n\t\t\t} catch (error) {\n\t\t\t\tconsole.log(\n\t\t\t\t\t`Could not install into ${editor.label} automatically (${(error as Error).message}). ` +\n\t\t\t\t\t\t`Install \"${EXTENSION_ID}\" manually from its Extensions view.`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\t// Reuse this device's existing key rather than minting a new one on every\n\t// run -- see `readCachedApiKey`'s doc comment for what unconditionally\n\t// signing in every time actually did (37 duplicate surfaces for one\n\t// real account, confirmed live). `login` still always re-signs in, for\n\t// someone who genuinely wants a fresh key.\n\tconst cachedApiKey = readCachedApiKey();\n\tconst apiKey = cachedApiKey ?? (await signIn(webUrl)).apiKey;\n\n\tfor (const editor of editors) {\n\t\t_mergeEditorSettingsSafely(editor, apiKey, webUrl);\n\t}\n\n\t_setUpTerminal(apiKey, webUrl);\n\n\tconsole.log(\n\t\teditors.length > 0\n\t\t\t? \"\\nSigned in. Reload your editor(s) -- Claude Code's spinner will \" +\n\t\t\t\t\t\"start showing sponsored copy in both your editor and any terminal.\\n\" +\n\t\t\t\t\t\"Run `npx @kili-ai/install login` any time to re-sign-in, or \" +\n\t\t\t\t\t'\"Kili: Disable\" from the command palette to turn it off.'\n\t\t\t: \"\\nSigned in. Open a new terminal and run `claude` -- the spinner \" +\n\t\t\t\t\t\"will start showing sponsored copy.\\n\" +\n\t\t\t\t\t\"Run `npx @kili-ai/install login` any time to re-sign-in.\",\n\t);\n}\n\n/**\n * One editor's unparseable `settings.json` (JSONC comments, trailing commas\n * -- `mergeSettings` throws `SettingsParseError` rather than silently\n * treating that as \"empty and safe to overwrite\", see its doc comment) must\n * not crash the whole install or block updating a *different* editor's\n * settings. Warn and move on.\n */\nfunction _mergeEditorSettingsSafely(\n\teditor: ReturnType<typeof detectEditors>[number],\n\tapiKey: string,\n\twebUrl: string,\n): void {\n\ttry {\n\t\tmergeSettings(userSettingsPath(editor), {\n\t\t\t\"kili.apiKey\": apiKey,\n\t\t\t\"kili.webUrl\": webUrl,\n\t\t});\n\t} catch (error) {\n\t\tconsole.log(\n\t\t\t`Could not update ${editor.label}'s settings.json (${(error as Error).message}). ` +\n\t\t\t\t`Set \"kili.apiKey\" there by hand, or fix the file's JSON and re-run.`,\n\t\t);\n\t}\n}\n\n/**\n * Configures `~/.claude/settings.json` directly, independent of whether any\n * editor was found -- see `terminal.ts`. Non-fatal on failure: a broken\n * terminal bin copy must not take down an otherwise-successful editor\n * install (or vice versa).\n */\nfunction _setUpTerminal(apiKey: string, webUrl: string): void {\n\ttry {\n\t\tconst bin = installTerminalBin(__dirname);\n\t\tif (!bin) {\n\t\t\tconsole.log(\n\t\t\t\t\"(terminal spinner/status line not set up -- this build has no bundled hook.js/statusline.js)\",\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\t\twriteTerminalRuntimeConfig({ apiKey, apiUrl: DEFAULT_API_URL, webUrl });\n\t\tenableTerminalHooks(bin.hookPath, bin.statuslinePath);\n\t} catch (error) {\n\t\tconsole.log(\n\t\t\t`(terminal spinner/status line setup failed: ${(error as Error).message})`,\n\t\t);\n\t}\n}\n\n/** Sign-in only -- for re-authenticating without reinstalling anything. */\nasync function _login(webUrl: string) {\n\tconst editors = detectEditors();\n\tconst { apiKey } = await signIn(webUrl);\n\tfor (const editor of editors) {\n\t\t_mergeEditorSettingsSafely(editor, apiKey, webUrl);\n\t}\n\t_setUpTerminal(apiKey, webUrl);\n\tconsole.log(\"Signed in.\");\n}\n\n/**\n * Before this package is published with a Marketplace listing, a `.vsix`\n * shipped alongside `dist/` is how the installer gets real bits onto disk --\n * `installExtension` falls back to installing by Marketplace id once one\n * exists.\n */\nfunction _localVsix(): string | null {\n\tconst candidate = join(__dirname, \"kili-ide.vsix\");\n\treturn existsSync(candidate) ? candidate : null;\n}\n\nfunction _flag(args: string[], name: string): string | undefined {\n\tconst index = args.indexOf(name);\n\tif (index === -1 || index === args.length - 1) return undefined;\n\treturn args[index + 1];\n}\n\nvoid main();\n","import { randomBytes } from \"node:crypto\";\nimport { type Server, createServer } from \"node:http\";\nimport { createInterface } from \"node:readline\";\nimport { AUTH_TIMEOUT_MS, CLI_AUTH_PATH, DEFAULT_WEB_URL } from \"./constants\";\n\nexport type TSignInResult = {\n\tapiKey: string;\n};\n\n/**\n * Loopback device-auth flow -- the same shape `gh auth login` / `vercel login`\n * use. Our callback page signs in through the dashboard's existing login\n * (magic link / Google) and mints a key through the existing\n * `POST /api-keys` endpoint. No new backend, no password ever touches this\n * CLI.\n */\nexport async function signIn(\n\twebUrl: string = DEFAULT_WEB_URL,\n): Promise<TSignInResult> {\n\tconst state = randomBytes(16).toString(\"hex\");\n\tconst { server, port, result } = await _listen(state);\n\n\tconst authUrl = new URL(CLI_AUTH_PATH, webUrl);\n\tauthUrl.searchParams.set(\"port\", String(port));\n\tauthUrl.searchParams.set(\"state\", state);\n\n\t// Deferred require: `open` is ESM-flavored and this file is CJS output --\n\t// keep the import lazy so a missing/broken `open` never breaks `--help`.\n\tconst open = (await import(\"open\")).default;\n\t// Same UX as `npm login`/`npm publish`'s own device-auth prompt: print the\n\t// URL and wait for ENTER rather than launching a browser unannounced --\n\t// the user picked this moment to run the command, not us to pick a\n\t// moment to hijack their browser.\n\tconsole.log(`Authenticate your account at:\\n${authUrl.toString()}`);\n\tconsole.log(\"Press ENTER to open in the browser...\");\n\tawait _waitForEnter();\n\tawait open(authUrl.toString());\n\n\ttry {\n\t\treturn await _withTimeout(result, AUTH_TIMEOUT_MS);\n\t} finally {\n\t\tserver.close();\n\t}\n}\n\n/** Resolves on the next Enter keypress (or any line + Enter). */\nfunction _waitForEnter(): Promise<void> {\n\treturn new Promise((resolve) => {\n\t\tconst rl = createInterface({\n\t\t\tinput: process.stdin,\n\t\t\toutput: process.stdout,\n\t\t});\n\t\trl.question(\"\", () => {\n\t\t\trl.close();\n\t\t\tresolve();\n\t\t});\n\t});\n}\n\nfunction _listen(\n\texpectedState: string,\n): Promise<{ server: Server; port: number; result: Promise<TSignInResult> }> {\n\treturn new Promise((resolveListen) => {\n\t\tlet settle: (result: TSignInResult) => void;\n\t\tlet fail: (error: Error) => void;\n\t\tconst result = new Promise<TSignInResult>((res, rej) => {\n\t\t\tsettle = res;\n\t\t\tfail = rej;\n\t\t});\n\n\t\tconst server = createServer((req, res) => {\n\t\t\tconst url = new URL(req.url ?? \"/\", \"http://127.0.0.1\");\n\t\t\tif (url.pathname !== \"/callback\") {\n\t\t\t\tres.writeHead(404).end();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t// CORS: the callback page runs on the dashboard's own origin and\n\t\t\t// calls this loopback port directly from the browser.\n\t\t\tres.setHeader(\"Access-Control-Allow-Origin\", \"*\");\n\t\t\tif (req.method === \"OPTIONS\") {\n\t\t\t\tres.writeHead(204).end();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst state = url.searchParams.get(\"state\");\n\t\t\tconst apiKey = url.searchParams.get(\"key\");\n\t\t\tif (state !== expectedState || !apiKey) {\n\t\t\t\tres.writeHead(400).end(\"Invalid or mismatched sign-in state.\");\n\t\t\t\tfail(new Error(\"Sign-in state mismatch.\"));\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tres\n\t\t\t\t.writeHead(200, { \"Content-Type\": \"text/html\" })\n\t\t\t\t.end(\n\t\t\t\t\t'<html><body style=\"font-family:sans-serif;padding:2rem\">' +\n\t\t\t\t\t\t\"<h2>Signed in to Kili</h2>\" +\n\t\t\t\t\t\t\"<p>You can close this tab and go back to your editor.</p>\" +\n\t\t\t\t\t\t\"</body></html>\",\n\t\t\t\t);\n\t\t\tsettle({ apiKey });\n\t\t});\n\n\t\tserver.listen(0, \"127.0.0.1\", () => {\n\t\t\tconst address = server.address();\n\t\t\tconst port = typeof address === \"object\" && address ? address.port : 0;\n\t\t\tresolveListen({ server, port, result });\n\t\t});\n\t});\n}\n\nfunction _withTimeout<T>(promise: Promise<T>, ms: number): Promise<T> {\n\treturn new Promise((resolve, reject) => {\n\t\tconst timer = setTimeout(\n\t\t\t() => reject(new Error(\"Timed out waiting for sign-in in the browser.\")),\n\t\t\tms,\n\t\t);\n\t\tpromise.then(\n\t\t\t(value) => {\n\t\t\t\tclearTimeout(timer);\n\t\t\t\tresolve(value);\n\t\t\t},\n\t\t\t(error) => {\n\t\t\t\tclearTimeout(timer);\n\t\t\t\treject(error);\n\t\t\t},\n\t\t);\n\t});\n}\n","declare const __PACKAGE_VERSION__: string;\ndeclare const __DEFAULT_API_URL__: string;\ndeclare const __DEFAULT_WEB_URL__: string;\ndeclare const __EXTENSION_ID__: string;\n\nexport const PACKAGE_VERSION =\n\ttypeof __PACKAGE_VERSION__ !== \"undefined\" ? __PACKAGE_VERSION__ : \"0.1.0\";\n\nexport const NPM_ORG = \"kili-ai\";\nexport const PACKAGE_NAME = `@${NPM_ORG}/ide.install`;\n\n/**\n * Always the PROD url as a literal fallback, on every branch -- never\n * hand-edited per branch. See `pkg.vscode.kili`'s `core/constants.ts` doc\n * comment: dev/prod identity is a publish-time-only concern\n * (`scripts/publish-channel.cjs`), never a committed diff between `dev` and\n * `master`, so the two branches merge cleanly into each other either way.\n *\n * Same default as the VS Code extension's `kili.webUrl` setting.\n */\nexport const DEFAULT_WEB_URL =\n\ttypeof __DEFAULT_WEB_URL__ !== \"undefined\"\n\t\t? __DEFAULT_WEB_URL__\n\t\t: \"https://app.trykili.ai\";\n\n/** Same default as `pkg.vscode.kili`'s `core/constants.ts` `DEFAULT_API_URL`\n * -- used only for the terminal-only setup path (`terminal.ts`), since that\n * doesn't go through the editor settings the extension itself would resolve\n * this from. */\nexport const DEFAULT_API_URL =\n\ttypeof __DEFAULT_API_URL__ !== \"undefined\"\n\t\t? __DEFAULT_API_URL__\n\t\t: \"https://api.trykili.ai\";\n\n/** The dashboard page that completes the loopback auth handshake. */\nexport const CLI_AUTH_PATH = \"/cli-auth\";\n\n/** How long we wait on the loopback server before giving up. */\nexport const AUTH_TIMEOUT_MS = 5 * 60 * 1000;\n\n/** Marketplace id `@kili-ai/ide` publishes under (see pkg.vscode.kili) --\n * `kili-ai.kili-ide` for prod; a dev publish overrides this to\n * `kili-ai.kili-ide-dev` via `KILI_EXTENSION_ID`\n * (`scripts/publish-channel.cjs` sets it to match whatever name it\n * temporarily renamed the sibling `pkg.vscode.kili` checkout to for that\n * one build). Never a committed value -- see `DEFAULT_API_URL`'s doc\n * comment for why. */\nexport const EXTENSION_ID =\n\ttypeof __EXTENSION_ID__ !== \"undefined\" ? __EXTENSION_ID__ : \"kili-ai.kili-ide\";\n","import { execFileSync } from \"node:child_process\";\nimport { homedir, platform } from \"node:os\";\nimport { join } from \"node:path\";\n\n/**\n * The three editors Claude Code's VS Code extension runs inside. Detection is\n * \"is the editor's CLI on PATH\", the same signal `code --install-extension`\n * itself needs to work at all.\n */\nexport type TEditor = {\n\tid: \"code\" | \"cursor\" | \"codium\";\n\tlabel: string;\n\t/** CLI binary used for `--install-extension` and detection. */\n\tbin: string;\n\t/** Folder name under the OS's per-user application-support directory. */\n\tuserDataFolder: string;\n};\n\nexport const EDITORS: TEditor[] = [\n\t{ id: \"code\", label: \"VS Code\", bin: \"code\", userDataFolder: \"Code\" },\n\t{ id: \"cursor\", label: \"Cursor\", bin: \"cursor\", userDataFolder: \"Cursor\" },\n\t{\n\t\tid: \"codium\",\n\t\tlabel: \"VSCodium\",\n\t\tbin: \"codium\",\n\t\tuserDataFolder: \"VSCodium\",\n\t},\n];\n\nexport function detectEditors(): TEditor[] {\n\treturn EDITORS.filter((editor) => _isOnPath(editor.bin));\n}\n\nfunction _isOnPath(bin: string): boolean {\n\ttry {\n\t\tconst checkCmd = platform() === \"win32\" ? \"where\" : \"which\";\n\t\texecFileSync(checkCmd, [bin], { stdio: \"ignore\" });\n\t\treturn true;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\n/**\n * `code --install-extension <path-or-id> --force`. Passing a local `.vsix`\n * path installs from disk (how this installer ships the extension before it\n * has a Marketplace listing); passing `EXTENSION_ID` installs from the\n * Marketplace once published. Never touches any file the editor didn't\n * already own the concept of — this is the CLI's own supported install path,\n * not a patch.\n *\n * `shell: true` on Windows: `code`/`cursor`/`codium` resolve to `.cmd` shim\n * files there, and `execFileSync` doesn't apply Windows' PATHEXT-based\n * resolution the way a real shell does -- without this it fails with\n * `ENOENT` even though `where code` finds it fine (that lookup runs through\n * `where.exe`, a real binary, not a `.cmd`). Confirmed live: this was\n * silently breaking every install on Windows until now.\n */\nexport function installExtension(editor: TEditor, vsixPathOrId: string) {\n\texecFileSync(editor.bin, [\"--install-extension\", vsixPathOrId, \"--force\"], {\n\t\tstdio: \"inherit\",\n\t\tshell: platform() === \"win32\",\n\t});\n}\n\n/** Where each editor keeps its User `settings.json`, per OS. */\nexport function userSettingsPath(editor: TEditor): string {\n\tconst home = homedir();\n\tswitch (platform()) {\n\t\tcase \"win32\": {\n\t\t\tconst appData = process.env.APPDATA ?? join(home, \"AppData\", \"Roaming\");\n\t\t\treturn join(appData, editor.userDataFolder, \"User\", \"settings.json\");\n\t\t}\n\t\tcase \"darwin\":\n\t\t\treturn join(\n\t\t\t\thome,\n\t\t\t\t\"Library\",\n\t\t\t\t\"Application Support\",\n\t\t\t\teditor.userDataFolder,\n\t\t\t\t\"User\",\n\t\t\t\t\"settings.json\",\n\t\t\t);\n\t\tdefault:\n\t\t\treturn join(\n\t\t\t\tprocess.env.XDG_CONFIG_HOME ?? join(home, \".config\"),\n\t\t\t\teditor.userDataFolder,\n\t\t\t\t\"User\",\n\t\t\t\t\"settings.json\",\n\t\t\t);\n\t}\n}\n","import { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { dirname } from \"node:path\";\nimport {\n\ttype ParseError,\n\tparse as parseJsonc,\n\tprintParseErrorCode,\n} from \"jsonc-parser\";\n\n/**\n * The keys this installer ever writes into an editor's `settings.json`. Kept\n * to exactly this list so `merge`/`remove` never touch anything the user or\n * another extension put there — the same discipline `pkg.vscode.kili`'s\n * `claude/settings.ts` uses for `~/.claude/settings.json`.\n */\nconst _OWNED_KEYS = [\"kili.apiKey\", \"kili.webUrl\", \"kili.apiUrl\"] as const;\n\ntype TOwnedValues = Partial<Record<(typeof _OWNED_KEYS)[number], string>>;\n\n/**\n * Thrown by `mergeSettings` when the target file exists but can't be parsed\n * -- callers must catch this and skip/warn, never let it fall through\n * silently (that would mean the write below ran anyway).\n */\nexport class SettingsParseError extends Error {}\n\n/**\n * Merge-only write: read the existing file (or start from `{}` if it doesn't\n * exist yet), overwrite exactly our own keys, leave every other key —\n * `mcpServers`, user themes, unrelated extension settings — byte-for-byte\n * untouched, and write back atomically (temp file + rename) since the running\n * editor may read this file concurrently.\n *\n * Throws `SettingsParseError` rather than treating a parse failure as \"empty\"\n * -- a real user's file being unparseable to us is not the same thing as it\n * being empty. Confirmed the hard way: treating those the same once silently\n * wiped a user's theme, activity bar layout, and everything else in the file\n * down to just our own keys.\n *\n * Parsed with `jsonc-parser` (the same library VS Code's own settings UI\n * uses), NOT `JSON.parse` -- a bare `JSON.parse` rejects comments and\n * trailing commas outright, which are completely normal in a hand-edited\n * settings.json. That used to mean an ordinary, valid settings.json threw\n * here every time, permanently blocking `kili.apiKey` from ever being\n * written on sign-in -- confirmed live, not a hypothetical: this happened on\n * a real user's settings.json whose only \"problem\" was a comment and a\n * trailing comma, both of which VS Code itself accepts fine.\n */\nexport function mergeSettings(path: string, values: TOwnedValues): void {\n\tconst current = _readJsonObject(path);\n\tconst next = { ...current, ...values };\n\t_writeAtomic(path, next);\n}\n\nfunction _readJsonObject(path: string): Record<string, unknown> {\n\tif (!existsSync(path)) return {};\n\tconst text = readFileSync(path, \"utf8\");\n\tconst errors: ParseError[] = [];\n\tconst parsed = parseJsonc(text, errors, { allowTrailingComma: true });\n\tif (errors.length > 0) {\n\t\tconst first = errors[0];\n\t\tthrow new SettingsParseError(\n\t\t\t`${path} exists but could not be parsed as JSON, even allowing for ` +\n\t\t\t\t`JSONC comments/trailing commas -- refusing to overwrite it. ` +\n\t\t\t\t`(${printParseErrorCode(first.error)} at offset ${first.offset})`,\n\t\t);\n\t}\n\tif (parsed && typeof parsed === \"object\" && !Array.isArray(parsed)) {\n\t\treturn parsed as Record<string, unknown>;\n\t}\n\treturn {};\n}\n\nfunction _writeAtomic(path: string, value: Record<string, unknown>): void {\n\tmkdirSync(dirname(path), { recursive: true });\n\tconst tmp = `${path}.kili-tmp-${process.pid}`;\n\twriteFileSync(tmp, `${JSON.stringify(value, null, 2)}\\n`, \"utf8\");\n\twriteFileSync(path, readFileSync(tmp, \"utf8\"), \"utf8\");\n\ttry {\n\t\trequire(\"node:fs\").unlinkSync(tmp);\n\t} catch {\n\t\t/* best effort cleanup */\n\t}\n}\n","import { randomUUID } from \"node:crypto\";\nimport {\n\tcopyFileSync,\n\texistsSync,\n\tmkdirSync,\n\treadFileSync,\n\twriteFileSync,\n} from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport {\n\ttype ParseError,\n\tparse as parseJsonc,\n\tprintParseErrorCode,\n} from \"jsonc-parser\";\nimport { SettingsParseError } from \"./settings\";\n\n/**\n * Sets up the CLI/terminal surfaces (`~/.claude/settings.json`'s\n * `spinnerVerbs` / `statusLine` / hooks) directly -- without needing the VS\n * Code extension to ever activate.\n *\n * Before this existed, that setup only ever happened inside\n * `pkg.vscode.kili`'s `extension.ts` (`onStartupFinished` -> `claudeEnable`),\n * which meant someone with no VS Code-family editor installed at all got\n * nothing from this installer: `detectEditors()` would find nothing, and the\n * whole flow stopped there -- no sign-in, no key, no terminal ads either,\n * even though the terminal surfaces don't actually need an editor.\n *\n * This is a small, self-contained copy of `pkg.vscode.kili/src/claude/settings.ts`'s\n * `enable()` and `core/config.ts`'s `resolveRuntimeConfig`/`writeRuntimeConfig`,\n * not a shared dependency -- same reasoning as `signin.ts`'s copy of the\n * loopback flow: these two packages publish and version independently, and\n * `pkg.vscode.kili` isn't published anywhere on its own to depend on. Uses\n * the exact same file paths, JSON shapes, and hook marker\n * (`__kili_hook__`) as the original, so whichever of the two -- this\n * installer or the extension, whichever runs first or last -- writes to\n * `~/.kili/config.json` / `~/.claude/settings.json`, the other reads and\n * merges into it correctly. `hook.js`/`statusline.js` themselves don't care\n * which one wrote the config; they just read it.\n */\n\nfunction _kiliHome(): string {\n\treturn join(homedir(), \".kili\");\n}\n\nfunction _runtimeConfigPath(): string {\n\treturn join(_kiliHome(), \"config.json\");\n}\n\nfunction _claudeSettingsPath(): string {\n\treturn join(homedir(), \".claude\", \"settings.json\");\n}\n\nfunction _settingsBackupPath(): string {\n\treturn join(_kiliHome(), \"settings.backup.json\");\n}\n\n/** Where the bundled `hook.js`/`statusline.js` (copied in at build time by\n * `scripts/copy-vsix.cjs`) end up once installed on this machine -- a stable\n * location, unlike wherever `npx` happened to cache this package's own\n * files, which isn't guaranteed to still exist later. */\nfunction _terminalBinDir(): string {\n\treturn join(_kiliHome(), \"bin\");\n}\n\nfunction _readJson<T>(path: string, fallback: T): T {\n\tif (!existsSync(path)) return fallback;\n\ttry {\n\t\treturn JSON.parse(readFileSync(path, \"utf8\")) as T;\n\t} catch {\n\t\treturn fallback;\n\t}\n}\n\n/**\n * For `~/.claude/settings.json` specifically -- the user's own file.\n * `_readJson`'s silent fallback-to-`{}` is fine for `~/.kili/config.json`\n * (nobody hand-edits that), but doing the same here means a merge-write\n * silently wipes the file down to just our own keys -- confirmed: this\n * happened for real. Throws instead, so `enableTerminalHooks` never falls\n * through to a write it shouldn't make.\n *\n * Parsed with `jsonc-parser`, NOT `JSON.parse` -- comments and trailing\n * commas are completely normal in a hand-edited settings.json (VS Code's own\n * editor accepts them fine), and a bare `JSON.parse` used to reject an\n * otherwise-valid file outright, permanently blocking every write here.\n * Confirmed live on a real, ordinary settings.json.\n */\nfunction _readJsonOwnedByUser<T>(path: string, fallback: T): T {\n\tif (!existsSync(path)) return fallback;\n\tconst text = readFileSync(path, \"utf8\");\n\tconst errors: ParseError[] = [];\n\tconst parsed = parseJsonc(text, errors, { allowTrailingComma: true });\n\tif (errors.length > 0) {\n\t\tconst first = errors[0];\n\t\tthrow new SettingsParseError(\n\t\t\t`${path} exists but could not be parsed as JSON, even allowing for ` +\n\t\t\t\t`JSONC comments/trailing commas -- refusing to overwrite it. ` +\n\t\t\t\t`(${printParseErrorCode(first.error)} at offset ${first.offset})`,\n\t\t);\n\t}\n\treturn parsed as T;\n}\n\nfunction _writeJsonAtomic(path: string, value: unknown): void {\n\tmkdirSync(dirname(path), { recursive: true });\n\tconst tmp = `${path}.kili-tmp-${process.pid}`;\n\twriteFileSync(tmp, `${JSON.stringify(value, null, 2)}\\n`, \"utf8\");\n\twriteFileSync(path, readFileSync(tmp, \"utf8\"), \"utf8\");\n\ttry {\n\t\trequire(\"node:fs\").unlinkSync(tmp);\n\t} catch {\n\t\t/* best effort cleanup */\n\t}\n}\n\n/**\n * Copies the compiled `hook.js`/`statusline.js` (and their sourcemaps, if\n * present) from this package's own bundled `dist/kili-bin/` into\n * `~/.kili/bin/` -- run on every install so a later `@kili-ai/install`\n * version's fixes always land, same as re-running `npx` always gets the\n * newest bundled `.vsix` for the editor path.\n */\nexport function installTerminalBin(\n\tpackageDistDir: string,\n): { hookPath: string; statuslinePath: string } | null {\n\tconst sourceDir = join(packageDistDir, \"kili-bin\");\n\tconst hookSource = join(sourceDir, \"hook.js\");\n\tconst statuslineSource = join(sourceDir, \"statusline.js\");\n\tif (!existsSync(hookSource) || !existsSync(statuslineSource)) return null;\n\n\tconst destDir = _terminalBinDir();\n\tmkdirSync(destDir, { recursive: true });\n\tconst hookPath = join(destDir, \"hook.js\");\n\tconst statuslinePath = join(destDir, \"statusline.js\");\n\tcopyFileSync(hookSource, hookPath);\n\tcopyFileSync(statuslineSource, statuslinePath);\n\tfor (const name of [\"hook.js.map\", \"statusline.js.map\"]) {\n\t\tconst src = join(sourceDir, name);\n\t\tif (existsSync(src)) copyFileSync(src, join(destDir, name));\n\t}\n\n\treturn { hookPath, statuslinePath };\n}\n\ntype TStoredConfig = {\n\tapiKey?: string;\n\tapiUrl?: string;\n\twebUrl?: string;\n\tenabled?: boolean;\n\tinstallId?: string;\n\twebviewPatchedEditors?: string[];\n};\n\n/**\n * The API key this device already signed in with, if any -- read from the\n * same `~/.kili/config.json` `writeTerminalRuntimeConfig` writes to, shared\n * with `pkg.vscode.kili`. `_install()` checks this before minting a new key:\n * without it, every `npx -y @kili-ai/install` run unconditionally called\n * `signIn()`, minting a brand-new API key (and, server-side, a brand-new\n * `publisher_surfaces` row) even on a device that already had one --\n * confirmed live: 37 near-identical \"VS Code — Kili for Claude Code\"\n * surfaces for one account, most created minutes or seconds apart, each\n * accumulating only a handful of events instead of one surface aggregating\n * all of them. The dashboard's per-surface page reads one specific surface\n * ID, so it kept landing on whichever was newest (and emptiest) while\n * \"Overview\" summed correctly across all the fragments.\n */\nexport function readCachedApiKey(): string | null {\n\tconst existing = _readJson<TStoredConfig>(_runtimeConfigPath(), {});\n\treturn existing.apiKey?.trim() || null;\n}\n\n/** Same shape `pkg.vscode.kili`'s `resolveRuntimeConfig`/`writeRuntimeConfig`\n * produce -- `hook.js`/`statusline.js` read this file directly and expect\n * exactly these fields. `installId` is merge-preserved, never regenerated,\n * so re-running this doesn't reset dwell/frequency bookkeeping identity. */\nexport function writeTerminalRuntimeConfig(input: {\n\tapiKey: string;\n\tapiUrl: string;\n\twebUrl: string;\n}): void {\n\tconst existing = _readJson<TStoredConfig>(_runtimeConfigPath(), {});\n\tconst installId = existing.installId ?? randomUUID();\n\t_writeJsonAtomic(_runtimeConfigPath(), {\n\t\t...existing,\n\t\tapiKey: input.apiKey,\n\t\tapiUrl: input.apiUrl,\n\t\twebUrl: input.webUrl,\n\t\tenabled: true,\n\t\tinstallId,\n\t\twebviewPatchedEditors: existing.webviewPatchedEditors ?? [],\n\t});\n}\n\ntype TClaudeSettings = Record<string, unknown> & {\n\thooks?: Record<string, unknown[]>;\n};\n\nconst _HOOK_MARKER = \"__kili_hook__\";\nconst _OWNED_HOOK_EVENTS = [\"UserPromptSubmit\", \"Stop\"] as const;\n\n/** Copies the file byte-for-byte, not parse-then-restringify -- works even\n * when the file has JSONC comments `JSON.parse` can't handle, and doesn't\n * silently drop them from the backup the way round-tripping through\n * `JSON.stringify` would. No-ops if the file doesn't exist yet. */\nfunction _backupOnce(): void {\n\tconst backupPath = _settingsBackupPath();\n\tif (existsSync(backupPath)) return;\n\tconst settingsPath = _claudeSettingsPath();\n\tif (!existsSync(settingsPath)) return;\n\tmkdirSync(dirname(backupPath), { recursive: true });\n\twriteFileSync(backupPath, readFileSync(settingsPath, \"utf8\"), \"utf8\");\n}\n\nfunction _isOwnedHookEntry(entry: unknown): boolean {\n\treturn JSON.stringify(entry).includes(_HOOK_MARKER);\n}\n\nfunction _mergeHooks(\n\thooks: Record<string, unknown[]>,\n\thookPath: string,\n): Record<string, unknown[]> {\n\tconst next = { ...hooks };\n\tfor (const event of _OWNED_HOOK_EVENTS) {\n\t\tconst existing = (next[event] ?? []).filter(\n\t\t\t(entry) => !_isOwnedHookEntry(entry),\n\t\t);\n\t\tnext[event] = [\n\t\t\t...existing,\n\t\t\t{\n\t\t\t\tmatcher: \"*\",\n\t\t\t\thooks: [\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: \"command\",\n\t\t\t\t\t\tcommand: `node \"${hookPath}\" ${event} ${_HOOK_MARKER}`,\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t},\n\t\t];\n\t}\n\treturn next;\n}\n\n/** Merge-writes `spinnerVerbs`/`statusLine`/hooks into `~/.claude/settings.json`\n * directly -- see this file's top doc comment for why the installer needs\n * its own copy of this instead of relying on the extension to do it.\n *\n * `statusLine` is still configured even though it no longer renders an ad\n * (see `pkg.vscode.kili/src/statusline.ts`) -- it running at all is\n * `isTerminalActive`'s only signal that a real terminal, not the IDE chat\n * panel, is in front of the user right now; removing it would silently\n * misattribute every terminal turn's impression to the extension surface. */\nexport function enableTerminalHooks(\n\thookPath: string,\n\tstatuslinePath: string,\n): void {\n\t_backupOnce();\n\tconst settings = _readJsonOwnedByUser<TClaudeSettings>(\n\t\t_claudeSettingsPath(),\n\t\t{},\n\t);\n\n\tsettings.spinnerVerbs = { mode: \"replace\", verbs: [\"Sponsored\"] };\n\t// Must actually invoke node -- see pkg.vscode.kili/src/claude/settings.ts's\n\t// enable() for why a bare .js path here silently never runs at all.\n\tsettings.statusLine = {\n\t\ttype: \"command\",\n\t\tcommand: `node \"${statuslinePath}\"`,\n\t};\n\tsettings.hooks = _mergeHooks(settings.hooks ?? {}, hookPath);\n\n\t_writeJsonAtomic(_claudeSettingsPath(), settings);\n}\n"],"mappings":"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","names":["import_node_fs","import_node_path","import_node_path","parseJsonc","import_node_crypto","import_node_fs","import_node_os","import_node_path","import_jsonc_parser","parseJsonc"]}