@kernelonpanic/kitcode 1.0.0-beta → 1.1.1
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 +2 -1
- package/README.ru.md +2 -1
- package/dist/index.js +591 -124
- package/dist/index.js.map +1 -1
- package/package.json +9 -1
package/dist/index.js
CHANGED
|
@@ -62,7 +62,7 @@ var diagnosticsSchema = z.object({
|
|
|
62
62
|
commands: z.array(z.string().trim().min(1).max(2e4)).max(8).default([])
|
|
63
63
|
});
|
|
64
64
|
var updatesSchema = z.object({
|
|
65
|
-
checkOnStart: z.boolean().default(
|
|
65
|
+
checkOnStart: z.boolean().default(true)
|
|
66
66
|
});
|
|
67
67
|
var configSchema = z.object({
|
|
68
68
|
version: z.literal(1).default(1),
|
|
@@ -658,6 +658,7 @@ import path3 from "path";
|
|
|
658
658
|
import { randomUUID } from "crypto";
|
|
659
659
|
import { readFile, realpath, rename, rm, writeFile } from "fs/promises";
|
|
660
660
|
import path2 from "path";
|
|
661
|
+
var trustLock = Promise.resolve();
|
|
661
662
|
var emptyTrust = () => ({ version: 1, workspaces: [] });
|
|
662
663
|
async function canonicalWorkspace(cwd) {
|
|
663
664
|
const resolved = await workspaceRootFor(cwd);
|
|
@@ -686,6 +687,7 @@ async function revokeWorkspaceTrust(cwd) {
|
|
|
686
687
|
return workspace;
|
|
687
688
|
}
|
|
688
689
|
async function loadTrust() {
|
|
690
|
+
await trustLock;
|
|
689
691
|
let parsed;
|
|
690
692
|
try {
|
|
691
693
|
parsed = JSON.parse(await readFile(trustPath, "utf8"));
|
|
@@ -702,15 +704,25 @@ async function loadTrust() {
|
|
|
702
704
|
};
|
|
703
705
|
}
|
|
704
706
|
async function saveTrust(value) {
|
|
705
|
-
|
|
706
|
-
|
|
707
|
+
const release = trustLock;
|
|
708
|
+
let resolveLock;
|
|
709
|
+
trustLock = new Promise((resolve3) => {
|
|
710
|
+
resolveLock = resolve3;
|
|
711
|
+
});
|
|
712
|
+
await release;
|
|
707
713
|
try {
|
|
708
|
-
await
|
|
714
|
+
await ensureDir(path2.dirname(trustPath));
|
|
715
|
+
const temp = `${trustPath}.${randomUUID()}.tmp`;
|
|
716
|
+
try {
|
|
717
|
+
await writeFile(temp, `${JSON.stringify(value, null, 2)}
|
|
709
718
|
`, { encoding: "utf8", mode: 384 });
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
719
|
+
await rename(temp, trustPath);
|
|
720
|
+
} catch (error) {
|
|
721
|
+
await rm(temp, { force: true });
|
|
722
|
+
throw error;
|
|
723
|
+
}
|
|
724
|
+
} finally {
|
|
725
|
+
resolveLock();
|
|
714
726
|
}
|
|
715
727
|
}
|
|
716
728
|
|
|
@@ -1631,11 +1643,12 @@ function createTurnBudget(limits, resolvePricing = pricingFor) {
|
|
|
1631
1643
|
};
|
|
1632
1644
|
}
|
|
1633
1645
|
const pricing = resolvePricing(request.modelRef);
|
|
1634
|
-
if (
|
|
1646
|
+
if (current.costUsd !== null) {
|
|
1635
1647
|
const remainingUsd = limits.maxCostUsdPerTurn - current.costUsd;
|
|
1636
|
-
const
|
|
1648
|
+
const effectivePricing = pricing ?? { input: 3e-3, output: 0.015 };
|
|
1649
|
+
const inputUsd = estimatedInput * effectivePricing.input / 1e6;
|
|
1637
1650
|
const affordableOutput = Math.floor(
|
|
1638
|
-
(remainingUsd - inputUsd) * 1e6 /
|
|
1651
|
+
(remainingUsd - inputUsd) * 1e6 / effectivePricing.output
|
|
1639
1652
|
);
|
|
1640
1653
|
outputLimit = Math.min(outputLimit, affordableOutput);
|
|
1641
1654
|
if (outputLimit < 1) {
|
|
@@ -1813,7 +1826,7 @@ function beginCheckpoint(options) {
|
|
|
1813
1826
|
lastCheckpointTimestamp = Math.max(Date.now(), lastCheckpointTimestamp + 1);
|
|
1814
1827
|
const id = `${String(lastCheckpointTimestamp).padStart(13, "0")}-${randomBytes2(4).toString("hex")}`;
|
|
1815
1828
|
await writeCheckpoint(path5.join(sessionDir, `${id}.json`), stored);
|
|
1816
|
-
|
|
1829
|
+
pruneCheckpoints(sessionDir).catch(() => void 0);
|
|
1817
1830
|
return { id, paths: entries.map((entry) => entry.path) };
|
|
1818
1831
|
}
|
|
1819
1832
|
};
|
|
@@ -2315,7 +2328,7 @@ async function isFile2(file) {
|
|
|
2315
2328
|
|
|
2316
2329
|
// src/core/attachments.ts
|
|
2317
2330
|
import { execFile } from "child_process";
|
|
2318
|
-
import { readFile as readFile6, stat as stat5 } from "fs/promises";
|
|
2331
|
+
import { lstat as lstat2, readFile as readFile6, stat as stat5 } from "fs/promises";
|
|
2319
2332
|
import os2 from "os";
|
|
2320
2333
|
import path7 from "path";
|
|
2321
2334
|
import { fileURLToPath } from "url";
|
|
@@ -2328,6 +2341,8 @@ var SENSITIVE_AUTO_NAMES = /* @__PURE__ */ new Set([
|
|
|
2328
2341
|
".netrc",
|
|
2329
2342
|
".npmrc",
|
|
2330
2343
|
".pypirc",
|
|
2344
|
+
".pgpass",
|
|
2345
|
+
".my.cnf",
|
|
2331
2346
|
"auth.json",
|
|
2332
2347
|
"credentials.json",
|
|
2333
2348
|
"id_dsa",
|
|
@@ -2337,11 +2352,17 @@ var SENSITIVE_AUTO_NAMES = /* @__PURE__ */ new Set([
|
|
|
2337
2352
|
"kitcode.json",
|
|
2338
2353
|
"secret.json",
|
|
2339
2354
|
"secrets.json",
|
|
2340
|
-
"tokens.json"
|
|
2355
|
+
"tokens.json",
|
|
2356
|
+
"kubeconfig",
|
|
2357
|
+
"service-account.json"
|
|
2341
2358
|
]);
|
|
2342
|
-
var SENSITIVE_AUTO_EXTENSIONS = /* @__PURE__ */ new Set([".jks", ".key", ".keystore", ".p12", ".pem", ".pfx"]);
|
|
2359
|
+
var SENSITIVE_AUTO_EXTENSIONS = /* @__PURE__ */ new Set([".jks", ".key", ".keystore", ".p12", ".pem", ".pfx", ".p8", ".der"]);
|
|
2343
2360
|
async function loadAttachment(cwd, requestedPath) {
|
|
2344
2361
|
const resolved = resolveAttachmentPath(cwd, requestedPath);
|
|
2362
|
+
const linkInfo = await lstat2(resolved).catch(() => null);
|
|
2363
|
+
if (linkInfo?.isSymbolicLink()) {
|
|
2364
|
+
throw new Error(`Cannot attach symbolic links: ${path7.basename(resolved)}`);
|
|
2365
|
+
}
|
|
2345
2366
|
const info = await stat5(resolved).catch((error) => {
|
|
2346
2367
|
if (error.code === "ENOENT") throw new Error(`Attachment not found: ${resolved}`);
|
|
2347
2368
|
throw error;
|
|
@@ -2352,16 +2373,23 @@ async function loadAttachment(cwd, requestedPath) {
|
|
|
2352
2373
|
async function loadAutomaticAttachment(cwd, requestedPath) {
|
|
2353
2374
|
if (!looksLikeAttachmentPath(requestedPath)) return null;
|
|
2354
2375
|
const resolved = resolveAttachmentPath(cwd, requestedPath);
|
|
2355
|
-
const info = await stat5(resolved).catch((error) => {
|
|
2356
|
-
if (error.code === "ENOENT" || error.code === "ENOTDIR") return null;
|
|
2357
|
-
throw error;
|
|
2358
|
-
});
|
|
2359
|
-
if (!info?.isFile()) return null;
|
|
2360
2376
|
if (isSensitiveAutomaticPath(resolved)) {
|
|
2361
2377
|
throw new Error(
|
|
2362
2378
|
`For safety, sensitive-looking files must be attached explicitly with /attach: ${path7.basename(resolved)}`
|
|
2363
2379
|
);
|
|
2364
2380
|
}
|
|
2381
|
+
if (path7.isAbsolute(resolved)) {
|
|
2382
|
+
const normalized = resolved.toLowerCase();
|
|
2383
|
+
const isProjectFile = normalized.includes(path7.sep + "src" + path7.sep) || normalized.includes(path7.sep + "lib" + path7.sep) || normalized.includes(path7.sep + "app" + path7.sep) || normalized.includes(path7.sep + "test" + path7.sep) || normalized.includes(path7.sep + "tests" + path7.sep) || normalized.includes(path7.sep + "public" + path7.sep) || normalized.includes(path7.sep + "assets" + path7.sep) || normalized.includes(path7.sep + "static" + path7.sep) || normalized.endsWith(".ts") || normalized.endsWith(".js") || normalized.endsWith(".tsx") || normalized.endsWith(".jsx") || normalized.endsWith(".py") || normalized.endsWith(".go") || normalized.endsWith(".rs") || normalized.endsWith(".java") || normalized.endsWith(".c") || normalized.endsWith(".cpp") || normalized.endsWith(".h") || normalized.endsWith(".hpp") || normalized.endsWith(".css") || normalized.endsWith(".html") || normalized.endsWith(".json") || normalized.endsWith(".yaml") || normalized.endsWith(".yml") || normalized.endsWith(".md") || normalized.endsWith(".txt") || normalized.endsWith(".toml") || normalized.endsWith(".ini") || normalized.endsWith(".cfg") || normalized.endsWith(".sh") || normalized.endsWith(".bat") || normalized.endsWith(".cmd") || normalized.endsWith(".ps1") || normalized.endsWith(".dockerfile") || normalized.endsWith(".makefile");
|
|
2384
|
+
if (!isProjectFile) return null;
|
|
2385
|
+
}
|
|
2386
|
+
const linkInfo = await lstat2(resolved).catch(() => null);
|
|
2387
|
+
if (linkInfo?.isSymbolicLink()) return null;
|
|
2388
|
+
const info = await stat5(resolved).catch((error) => {
|
|
2389
|
+
if (error.code === "ENOENT" || error.code === "ENOTDIR") return null;
|
|
2390
|
+
throw error;
|
|
2391
|
+
});
|
|
2392
|
+
if (!info?.isFile()) return null;
|
|
2365
2393
|
return loadResolvedAttachment(resolved, info.size);
|
|
2366
2394
|
}
|
|
2367
2395
|
function looksLikeAttachmentPath(value) {
|
|
@@ -2622,7 +2650,7 @@ function normalizeInputPath(value) {
|
|
|
2622
2650
|
return value.slice(1, -1);
|
|
2623
2651
|
}
|
|
2624
2652
|
}
|
|
2625
|
-
return value.replace(/\\([\\ "'()\[\]
|
|
2653
|
+
return value.replace(/\\([\\ "'()\[\]&;#$!])/g, (_match, character) => character);
|
|
2626
2654
|
}
|
|
2627
2655
|
function safeName(value) {
|
|
2628
2656
|
return value.replace(/[\r\n\0-\x1f\x7f]/g, "").slice(0, 200) || "attachment";
|
|
@@ -2708,7 +2736,8 @@ function compactCutIndex(history) {
|
|
|
2708
2736
|
function estimateCompactTokens(history) {
|
|
2709
2737
|
const cut = compactCutIndex(history);
|
|
2710
2738
|
if (cut <= 0) return 1;
|
|
2711
|
-
|
|
2739
|
+
const rendered = renderHistory(history.slice(0, cut));
|
|
2740
|
+
return Math.max(1, Math.min(rendered.length, MAX_SOURCE_CHARS) + 1e3);
|
|
2712
2741
|
}
|
|
2713
2742
|
function isConversationUser(message) {
|
|
2714
2743
|
if (message.role !== "user") return false;
|
|
@@ -2797,6 +2826,8 @@ var STEP_LIMIT_NOTE = `The subagent was stopped after ${MAX_SUBAGENT_STEPS} mode
|
|
|
2797
2826
|
var SUBAGENT_SYSTEM = [
|
|
2798
2827
|
"You are a subagent launched by the main kitcode agent to carry out one task on your own.",
|
|
2799
2828
|
"",
|
|
2829
|
+
"SECURITY CRITICAL: You must NEVER execute commands that read, modify, or exfiltrate sensitive files including but not limited to: /etc/passwd, /etc/shadow, ~/.ssh/*, ~/.aws/*, ~/.env*, .git/config, credentials files, or any files outside the workspace. Never run `curl ... | bash`, `wget ... | bash`, or similar patterns. Never send data to external servers.",
|
|
2830
|
+
"",
|
|
2800
2831
|
"Nobody is reading along and nobody can answer you. Never ask a question, never stop to propose a plan, never wait for approval. If the request is ambiguous, take the most reasonable reading, do the work, and state the assumption in your answer.",
|
|
2801
2832
|
"",
|
|
2802
2833
|
'Your final message is the return value \u2014 it is the only thing the caller ever sees. End with the answer itself: the findings, the file paths, the conclusion. Never end with "I will now..." or a recap of the steps you took. Carry over every concrete detail the caller needs (paths, names, line numbers) and leave out the narration of how you found them.'
|
|
@@ -2970,10 +3001,14 @@ function printableInput(input) {
|
|
|
2970
3001
|
}
|
|
2971
3002
|
async function invoke(call, tool, input, signal) {
|
|
2972
3003
|
try {
|
|
2973
|
-
|
|
3004
|
+
if (input !== null && typeof input !== "object" || Array.isArray(input)) {
|
|
3005
|
+
return { content: `MCP tool "${tool}" received invalid input: arguments must be a JSON object.`, isError: true };
|
|
3006
|
+
}
|
|
3007
|
+
const result = await call(tool, input ?? {}, signal);
|
|
2974
3008
|
return { content: flattenResult(result), isError: result.isError };
|
|
2975
3009
|
} catch (error) {
|
|
2976
|
-
|
|
3010
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
3011
|
+
return { content: redactSecrets(message), isError: true };
|
|
2977
3012
|
}
|
|
2978
3013
|
}
|
|
2979
3014
|
function flattenResult(result) {
|
|
@@ -3015,10 +3050,18 @@ function clip(value) {
|
|
|
3015
3050
|
// package.json
|
|
3016
3051
|
var package_default = {
|
|
3017
3052
|
name: "@kernelonpanic/kitcode",
|
|
3018
|
-
version: "1.
|
|
3053
|
+
version: "1.1.1",
|
|
3019
3054
|
description: "Terminal coding agent with a config you never have to write by hand",
|
|
3020
3055
|
type: "module",
|
|
3021
3056
|
license: "MIT",
|
|
3057
|
+
homepage: "https://github.com/KernelEditor/KitCode#readme",
|
|
3058
|
+
bugs: {
|
|
3059
|
+
url: "https://github.com/KernelEditor/KitCode/issues"
|
|
3060
|
+
},
|
|
3061
|
+
repository: {
|
|
3062
|
+
type: "git",
|
|
3063
|
+
url: "git+https://github.com/KernelEditor/KitCode.git"
|
|
3064
|
+
},
|
|
3022
3065
|
bin: {
|
|
3023
3066
|
kitcode: "dist/index.js"
|
|
3024
3067
|
},
|
|
@@ -3074,8 +3117,8 @@ var package_default = {
|
|
|
3074
3117
|
|
|
3075
3118
|
// src/version.ts
|
|
3076
3119
|
var KITCODE_VERSION = package_default.version;
|
|
3077
|
-
var KITCODE_REPOSITORY = "
|
|
3078
|
-
var KITCODE_COMMIT = true ? "
|
|
3120
|
+
var KITCODE_REPOSITORY = "KernelEditor/KitCode";
|
|
3121
|
+
var KITCODE_COMMIT = true ? "a57e54102a5e1ca1e60cec4ee9c629c890d27057" : "development";
|
|
3079
3122
|
|
|
3080
3123
|
// src/mcp/client.ts
|
|
3081
3124
|
var clientInfo = { name: "kitcode", version: KITCODE_VERSION };
|
|
@@ -3099,6 +3142,36 @@ var inheritedEnvKeys = /* @__PURE__ */ new Set([
|
|
|
3099
3142
|
"APPDATA",
|
|
3100
3143
|
"LOCALAPPDATA"
|
|
3101
3144
|
]);
|
|
3145
|
+
var blockedEnvKeys = /* @__PURE__ */ new Set([
|
|
3146
|
+
"ANTHROPIC_API_KEY",
|
|
3147
|
+
"OPENAI_API_KEY",
|
|
3148
|
+
"AWS_ACCESS_KEY_ID",
|
|
3149
|
+
"AWS_SECRET_ACCESS_KEY",
|
|
3150
|
+
"AWS_SESSION_TOKEN",
|
|
3151
|
+
"GITHUB_TOKEN",
|
|
3152
|
+
"GH_TOKEN",
|
|
3153
|
+
"GIT_TOKEN",
|
|
3154
|
+
"NPM_TOKEN",
|
|
3155
|
+
"PYPI_TOKEN",
|
|
3156
|
+
"TWINE_PASSWORD",
|
|
3157
|
+
"DOCKER_PASSWORD",
|
|
3158
|
+
"DATABASE_URL",
|
|
3159
|
+
"REDIS_URL",
|
|
3160
|
+
"MONGODB_URI",
|
|
3161
|
+
"POSTGRES_PASSWORD",
|
|
3162
|
+
"MYSQL_PASSWORD",
|
|
3163
|
+
"PRIVATE_KEY",
|
|
3164
|
+
"SSH_KEY",
|
|
3165
|
+
"GPG_KEY",
|
|
3166
|
+
"SIGNING_KEY",
|
|
3167
|
+
"COOKIE",
|
|
3168
|
+
"SESSION_SECRET",
|
|
3169
|
+
"JWT_SECRET",
|
|
3170
|
+
"ENCRYPTION_KEY",
|
|
3171
|
+
"MASTER_KEY",
|
|
3172
|
+
"API_KEY",
|
|
3173
|
+
"API_SECRET"
|
|
3174
|
+
]);
|
|
3102
3175
|
function createMcpManager(servers) {
|
|
3103
3176
|
const serverStates = /* @__PURE__ */ new Map();
|
|
3104
3177
|
const sessions = /* @__PURE__ */ new Map();
|
|
@@ -3245,7 +3318,9 @@ function configuredSecrets(config) {
|
|
|
3245
3318
|
function inheritedEnv(source = process.env) {
|
|
3246
3319
|
const env = {};
|
|
3247
3320
|
for (const [key, value] of Object.entries(source)) {
|
|
3248
|
-
if (value !== void 0 && inheritedEnvKeys.has(key)
|
|
3321
|
+
if (value !== void 0 && inheritedEnvKeys.has(key) && !blockedEnvKeys.has(key)) {
|
|
3322
|
+
env[key] = value;
|
|
3323
|
+
}
|
|
3249
3324
|
}
|
|
3250
3325
|
return env;
|
|
3251
3326
|
}
|
|
@@ -4070,7 +4145,7 @@ function isFileEdit(tool) {
|
|
|
4070
4145
|
}
|
|
4071
4146
|
|
|
4072
4147
|
// src/tools/edit.ts
|
|
4073
|
-
import { readFile as readFile8, stat as stat6, writeFile as writeFile6 } from "fs/promises";
|
|
4148
|
+
import { lstat as lstat3, readFile as readFile8, stat as stat6, writeFile as writeFile6 } from "fs/promises";
|
|
4074
4149
|
var MAX_FILE_BYTES2 = 5e6;
|
|
4075
4150
|
function countOccurrences(haystack, needle) {
|
|
4076
4151
|
if (needle === "") return 0;
|
|
@@ -4126,6 +4201,10 @@ var editTool = {
|
|
|
4126
4201
|
const { path: path14, oldString, newString, replaceAll = false } = input;
|
|
4127
4202
|
const safe = resolveInside(ctx.cwd, path14);
|
|
4128
4203
|
if (!safe.ok) return { content: safe.reason, isError: true };
|
|
4204
|
+
const linkInfo = await lstat3(safe.path).catch(() => null);
|
|
4205
|
+
if (linkInfo?.isSymbolicLink()) {
|
|
4206
|
+
return { content: `Cannot edit ${path14}: the path is a symbolic link. Remove the symlink first.`, isError: true };
|
|
4207
|
+
}
|
|
4129
4208
|
const info = await stat6(safe.path).catch(() => null);
|
|
4130
4209
|
if (info && info.size > MAX_FILE_BYTES2) {
|
|
4131
4210
|
return {
|
|
@@ -4243,13 +4322,25 @@ var sensitiveNames = /* @__PURE__ */ new Set([
|
|
|
4243
4322
|
".npmrc",
|
|
4244
4323
|
".pypirc",
|
|
4245
4324
|
".git-credentials",
|
|
4325
|
+
".netrc",
|
|
4326
|
+
".pgpass",
|
|
4327
|
+
".my.cnf",
|
|
4328
|
+
".docker/config.json",
|
|
4246
4329
|
"auth.json",
|
|
4247
4330
|
"credentials",
|
|
4248
4331
|
"credentials.json",
|
|
4249
4332
|
"id_rsa",
|
|
4250
|
-
"id_ed25519"
|
|
4333
|
+
"id_ed25519",
|
|
4334
|
+
"id_dsa",
|
|
4335
|
+
"id_ecdsa",
|
|
4336
|
+
"kubeconfig",
|
|
4337
|
+
"known_hosts",
|
|
4338
|
+
"secret.json",
|
|
4339
|
+
"secrets.json",
|
|
4340
|
+
"tokens.json",
|
|
4341
|
+
"service-account.json"
|
|
4251
4342
|
]);
|
|
4252
|
-
var sensitiveExtensions = /* @__PURE__ */ new Set([".pem", ".key", ".p12", ".pfx"]);
|
|
4343
|
+
var sensitiveExtensions = /* @__PURE__ */ new Set([".pem", ".key", ".p12", ".pfx", ".jks", ".keystore", ".p8", ".der"]);
|
|
4253
4344
|
function isSensitivePath(value) {
|
|
4254
4345
|
if (typeof value !== "string") return false;
|
|
4255
4346
|
const normalized = value.replaceAll("\\", "/").toLowerCase();
|
|
@@ -4261,7 +4352,7 @@ function isSensitivePath(value) {
|
|
|
4261
4352
|
function mentionsSensitivePattern(value) {
|
|
4262
4353
|
if (typeof value !== "string") return false;
|
|
4263
4354
|
const lower = value.toLowerCase();
|
|
4264
|
-
return isSensitivePath(lower) || lower.includes(".env") || lower.includes(".npmrc") || lower.includes("auth.json") || /\.(?:pem|key|p12|pfx)(?:$|[^a-z0-9])/.test(lower);
|
|
4355
|
+
return isSensitivePath(lower) || lower.includes(".env") || lower.includes(".npmrc") || lower.includes(".netrc") || lower.includes(".pgpass") || lower.includes("auth.json") || lower.includes("credentials") || lower.includes("kubeconfig") || lower.includes("secret") || lower.includes("tokens.json") || /\.(?:pem|key|p12|pfx|jks|keystore|p8|der)(?:$|[^a-z0-9])/.test(lower);
|
|
4265
4356
|
}
|
|
4266
4357
|
|
|
4267
4358
|
// src/tools/regex.ts
|
|
@@ -4538,7 +4629,7 @@ function toToolSchema(tool) {
|
|
|
4538
4629
|
}
|
|
4539
4630
|
|
|
4540
4631
|
// src/tools/write.ts
|
|
4541
|
-
import { mkdir as mkdir3, readFile as readFile11, stat as stat9, writeFile as writeFile7 } from "fs/promises";
|
|
4632
|
+
import { lstat as lstat4, mkdir as mkdir3, readFile as readFile11, stat as stat9, writeFile as writeFile7 } from "fs/promises";
|
|
4542
4633
|
import { dirname as dirname2 } from "path";
|
|
4543
4634
|
var MAX_FILE_BYTES5 = 5e6;
|
|
4544
4635
|
var writeTool = {
|
|
@@ -4589,6 +4680,10 @@ var writeTool = {
|
|
|
4589
4680
|
}
|
|
4590
4681
|
const safe = resolveInside(ctx.cwd, path14);
|
|
4591
4682
|
if (!safe.ok) return { content: safe.reason, isError: true };
|
|
4683
|
+
const linkInfo = await lstat4(safe.path).catch(() => null);
|
|
4684
|
+
if (linkInfo?.isSymbolicLink()) {
|
|
4685
|
+
return { content: `Cannot write ${path14}: the path is a symbolic link. Remove the symlink first.`, isError: true };
|
|
4686
|
+
}
|
|
4592
4687
|
const beforeInfo = await stat9(safe.path).catch(() => null);
|
|
4593
4688
|
if (beforeInfo && beforeInfo.size > MAX_FILE_BYTES5) {
|
|
4594
4689
|
return {
|
|
@@ -5438,6 +5533,28 @@ async function boot(options) {
|
|
|
5438
5533
|
}
|
|
5439
5534
|
return { removed: providerId, wasActive, ...nextModel ? { nextModel } : {} };
|
|
5440
5535
|
},
|
|
5536
|
+
async changeProviderKey(providerId, newKey) {
|
|
5537
|
+
const provider = config.providers[providerId];
|
|
5538
|
+
if (!provider) throw new Error(`Provider "${providerId}" is not configured.`);
|
|
5539
|
+
const previousKey = auth[providerId];
|
|
5540
|
+
auth[providerId] = newKey;
|
|
5541
|
+
try {
|
|
5542
|
+
const testRegistry = createRegistry(config, auth);
|
|
5543
|
+
await loadModels(testRegistry.get(providerId));
|
|
5544
|
+
} catch (error) {
|
|
5545
|
+
auth[providerId] = previousKey;
|
|
5546
|
+
throw new Error(
|
|
5547
|
+
`Key verification failed for "${providerId}": ${error instanceof Error ? error.message : String(error)}`
|
|
5548
|
+
);
|
|
5549
|
+
}
|
|
5550
|
+
await saveAuth(auth);
|
|
5551
|
+
registry = createRegistry(config, auth);
|
|
5552
|
+
if (parseModelRef(modelRef)?.provider === providerId) {
|
|
5553
|
+
const models = await loadModels(registry.get(providerId));
|
|
5554
|
+
rememberModels(providerId, models);
|
|
5555
|
+
void refreshModelContextWindow();
|
|
5556
|
+
}
|
|
5557
|
+
},
|
|
5441
5558
|
sessionId: () => session.id,
|
|
5442
5559
|
async newSession() {
|
|
5443
5560
|
await persistQueue.catch(() => void 0);
|
|
@@ -5744,6 +5861,17 @@ ${lines.join("\n")}` : null;
|
|
|
5744
5861
|
startupUpdateCheck: () => startupUpdate,
|
|
5745
5862
|
async run(history, hooks, signal) {
|
|
5746
5863
|
syncMcpTools();
|
|
5864
|
+
const runProviderId = parseModelRef(modelRef)?.provider;
|
|
5865
|
+
if (runProviderId) {
|
|
5866
|
+
try {
|
|
5867
|
+
const models = await loadModels(registry.get(runProviderId));
|
|
5868
|
+
rememberModels(runProviderId, models);
|
|
5869
|
+
} catch (error) {
|
|
5870
|
+
warnings.push(
|
|
5871
|
+
`Could not refresh models for "${runProviderId}": ${error instanceof Error ? error.message : String(error)}`
|
|
5872
|
+
);
|
|
5873
|
+
}
|
|
5874
|
+
}
|
|
5747
5875
|
let requestHistory = history;
|
|
5748
5876
|
const budget = createTurnBudget(config.budget, resolvePricing);
|
|
5749
5877
|
const previousContext = runtime.modelContext();
|
|
@@ -6037,7 +6165,7 @@ function validateKey(value) {
|
|
|
6037
6165
|
import { render } from "ink";
|
|
6038
6166
|
|
|
6039
6167
|
// src/ui/App.tsx
|
|
6040
|
-
import { Box as Box12, useApp } from "ink";
|
|
6168
|
+
import { Box as Box12, Text as Text12, useApp, useInput as useInput2 } from "ink";
|
|
6041
6169
|
import { execFile as execFile2, execFileSync as execFileSync2 } from "child_process";
|
|
6042
6170
|
import { useCallback, useEffect as useEffect2, useMemo as useMemo4, useRef as useRef4, useState as useState5 } from "react";
|
|
6043
6171
|
|
|
@@ -6079,6 +6207,7 @@ var COMMANDS = [
|
|
|
6079
6207
|
{ name: "login" },
|
|
6080
6208
|
{ name: "provider" },
|
|
6081
6209
|
{ name: "logout", args: "[provider]" },
|
|
6210
|
+
{ name: "key", args: "[provider]" },
|
|
6082
6211
|
{ name: "effort" },
|
|
6083
6212
|
{ name: "thinking" },
|
|
6084
6213
|
{ name: "budget", args: "[tokens]" },
|
|
@@ -6166,6 +6295,43 @@ function editDistance(a, b) {
|
|
|
6166
6295
|
return previous[b.length] ?? 0;
|
|
6167
6296
|
}
|
|
6168
6297
|
|
|
6298
|
+
// src/ui/App.tsx
|
|
6299
|
+
import TextInput2 from "ink-text-input";
|
|
6300
|
+
|
|
6301
|
+
// src/ui/theme.ts
|
|
6302
|
+
import { createContext, useContext } from "react";
|
|
6303
|
+
var PRESETS = {
|
|
6304
|
+
purple: "#a78bfa",
|
|
6305
|
+
violet: "#8b5cf6",
|
|
6306
|
+
blue: "#60a5fa",
|
|
6307
|
+
cyan: "#22d3ee",
|
|
6308
|
+
green: "#4ade80",
|
|
6309
|
+
orange: "#fb923c",
|
|
6310
|
+
pink: "#f472b6",
|
|
6311
|
+
red: "#f87171",
|
|
6312
|
+
yellow: "#fbbf24",
|
|
6313
|
+
white: "#e5e7eb"
|
|
6314
|
+
};
|
|
6315
|
+
var DEFAULT_ACCENT = PRESETS.purple;
|
|
6316
|
+
function resolveAccent(value) {
|
|
6317
|
+
if (!value) return DEFAULT_ACCENT;
|
|
6318
|
+
const preset = PRESETS[value.toLowerCase()];
|
|
6319
|
+
if (preset) return preset;
|
|
6320
|
+
return /^#[0-9a-f]{6}$/i.test(value) ? value : DEFAULT_ACCENT;
|
|
6321
|
+
}
|
|
6322
|
+
function makeTheme(accent) {
|
|
6323
|
+
return {
|
|
6324
|
+
accent: resolveAccent(accent),
|
|
6325
|
+
ok: PRESETS.green,
|
|
6326
|
+
warn: PRESETS.yellow,
|
|
6327
|
+
error: PRESETS.red
|
|
6328
|
+
};
|
|
6329
|
+
}
|
|
6330
|
+
var ThemeContext = createContext(makeTheme(void 0));
|
|
6331
|
+
function useTheme() {
|
|
6332
|
+
return useContext(ThemeContext);
|
|
6333
|
+
}
|
|
6334
|
+
|
|
6169
6335
|
// src/ui/components/Confirm.tsx
|
|
6170
6336
|
import { Box, Text } from "ink";
|
|
6171
6337
|
|
|
@@ -6199,7 +6365,7 @@ function normalizeHotkey(char) {
|
|
|
6199
6365
|
}
|
|
6200
6366
|
|
|
6201
6367
|
// src/ui/i18n.ts
|
|
6202
|
-
import { createContext, useContext } from "react";
|
|
6368
|
+
import { createContext as createContext2, useContext as useContext2 } from "react";
|
|
6203
6369
|
var LANGS = [
|
|
6204
6370
|
{ key: "en", label: "English" },
|
|
6205
6371
|
{ key: "ru", label: "\u0420\u0443\u0441\u0441\u043A\u0438\u0439" }
|
|
@@ -6229,6 +6395,7 @@ var en = {
|
|
|
6229
6395
|
workingFor: (elapsed) => `working ${elapsed}`,
|
|
6230
6396
|
titleProvider: "Provider",
|
|
6231
6397
|
titleLogout: "Sign out of a provider",
|
|
6398
|
+
titleKeyChange: "Change API key for a provider",
|
|
6232
6399
|
titleSessions: "Resume a session",
|
|
6233
6400
|
titleSessionAction: "Session action",
|
|
6234
6401
|
resumed: (id, count) => `Resumed ${id} \u2014 ${count} messages restored`,
|
|
@@ -6316,6 +6483,9 @@ var en = {
|
|
|
6316
6483
|
logoutAskBody: "Its API key is removed from auth.json and the provider is dropped from the config.",
|
|
6317
6484
|
loggedOut: (provider) => `Signed out of "${provider}".`,
|
|
6318
6485
|
logoutNothing: "No provider is signed in.",
|
|
6486
|
+
keyChangeAsk: (provider) => `Change API key for "${provider}"?`,
|
|
6487
|
+
keyChangeAskBody: "The new key replaces the old one. Models are re-fetched automatically.",
|
|
6488
|
+
keyChanged: (provider) => `API key updated for "${provider}".`,
|
|
6319
6489
|
promptUsage: "Usage: /prompt save <name>",
|
|
6320
6490
|
promptNothing: "Nothing to save yet \u2014 send a message first.",
|
|
6321
6491
|
promptSaved: (name) => `Saved prompt "${name}"`,
|
|
@@ -6347,6 +6517,7 @@ var en = {
|
|
|
6347
6517
|
login: "add a provider (url + key)",
|
|
6348
6518
|
provider: "switch between configured providers",
|
|
6349
6519
|
logout: "choose a provider to sign out from",
|
|
6520
|
+
key: "change API key for a provider",
|
|
6350
6521
|
effort: "set reasoning depth",
|
|
6351
6522
|
thinking: "toggle reasoning",
|
|
6352
6523
|
budget: "set token budget per turn",
|
|
@@ -6407,6 +6578,7 @@ var ru = {
|
|
|
6407
6578
|
workingFor: (elapsed) => `\u0434\u0443\u043C\u0430\u0435\u0442 ${elapsed}`,
|
|
6408
6579
|
titleProvider: "\u041F\u0440\u043E\u0432\u0430\u0439\u0434\u0435\u0440",
|
|
6409
6580
|
titleLogout: "\u0412\u044B\u0431\u0440\u0430\u0442\u044C API \u0434\u043B\u044F \u0432\u044B\u0445\u043E\u0434\u0430",
|
|
6581
|
+
titleKeyChange: "\u0421\u043C\u0435\u043D\u0438\u0442\u044C \u043A\u043B\u044E\u0447 \u043F\u0440\u043E\u0432\u0430\u0439\u0434\u0435\u0440\u0430",
|
|
6410
6582
|
titleSessions: "\u0412\u043E\u0441\u0441\u0442\u0430\u043D\u043E\u0432\u0438\u0442\u044C \u0441\u0435\u0441\u0441\u0438\u044E",
|
|
6411
6583
|
titleSessionAction: "\u0414\u0435\u0439\u0441\u0442\u0432\u0438\u0435 \u0441 \u0441\u0435\u0441\u0441\u0438\u0435\u0439",
|
|
6412
6584
|
resumed: (id, count) => `\u0421\u0435\u0441\u0441\u0438\u044F ${id} \u0432\u043E\u0441\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D\u0430 \u2014 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0439: ${count}`,
|
|
@@ -6494,6 +6666,9 @@ var ru = {
|
|
|
6494
6666
|
logoutAskBody: "\u041A\u043B\u044E\u0447 \u0431\u0443\u0434\u0435\u0442 \u0443\u0434\u0430\u043B\u0451\u043D \u0438\u0437 auth.json, \u0430 \u043F\u0440\u043E\u0432\u0430\u0439\u0434\u0435\u0440 \u2014 \u0438\u0437 \u043A\u043E\u043D\u0444\u0438\u0433\u0430.",
|
|
6495
6667
|
loggedOut: (provider) => `\u0412\u044B\u043F\u043E\u043B\u043D\u0435\u043D \u0432\u044B\u0445\u043E\u0434 \u0438\u0437 \xAB${provider}\xBB.`,
|
|
6496
6668
|
logoutNothing: "\u041D\u0438 \u043E\u0434\u0438\u043D \u043F\u0440\u043E\u0432\u0430\u0439\u0434\u0435\u0440 \u043D\u0435 \u043F\u043E\u0434\u043A\u043B\u044E\u0447\u0451\u043D.",
|
|
6669
|
+
keyChangeAsk: (provider) => `\u0421\u043C\u0435\u043D\u0438\u0442\u044C \u043A\u043B\u044E\u0447 \u0434\u043B\u044F \xAB${provider}\xBB?`,
|
|
6670
|
+
keyChangeAskBody: "\u041D\u043E\u0432\u044B\u0439 \u043A\u043B\u044E\u0447 \u0437\u0430\u043C\u0435\u043D\u0438\u0442 \u0441\u0442\u0430\u0440\u044B\u0439. \u041C\u043E\u0434\u0435\u043B\u0438 \u043E\u0431\u043D\u043E\u0432\u044F\u0442\u0441\u044F \u0430\u0432\u0442\u043E\u043C\u0430\u0442\u0438\u0447\u0435\u0441\u043A\u0438.",
|
|
6671
|
+
keyChanged: (provider) => `\u041A\u043B\u044E\u0447 \u043E\u0431\u043D\u043E\u0432\u043B\u0451\u043D \u0434\u043B\u044F \xAB${provider}\xBB.`,
|
|
6497
6672
|
promptUsage: "\u0418\u0441\u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u043D\u0438\u0435: /prompt save <\u0438\u043C\u044F>",
|
|
6498
6673
|
promptNothing: "\u041F\u043E\u043A\u0430 \u043D\u0435\u0447\u0435\u0433\u043E \u0441\u043E\u0445\u0440\u0430\u043D\u044F\u0442\u044C \u2014 \u0441\u043D\u0430\u0447\u0430\u043B\u0430 \u043E\u0442\u043F\u0440\u0430\u0432\u044C \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0435.",
|
|
6499
6674
|
promptSaved: (name) => `\u041F\u0440\u043E\u043C\u0442 \xAB${name}\xBB \u0441\u043E\u0445\u0440\u0430\u043D\u0451\u043D`,
|
|
@@ -6525,6 +6700,7 @@ var ru = {
|
|
|
6525
6700
|
login: "\u0434\u043E\u0431\u0430\u0432\u0438\u0442\u044C \u043F\u0440\u043E\u0432\u0430\u0439\u0434\u0435\u0440\u0430 (url + \u043A\u043B\u044E\u0447)",
|
|
6526
6701
|
provider: "\u043F\u0435\u0440\u0435\u043A\u043B\u044E\u0447\u0438\u0442\u044C\u0441\u044F \u043C\u0435\u0436\u0434\u0443 \u043F\u0440\u043E\u0432\u0430\u0439\u0434\u0435\u0440\u0430\u043C\u0438",
|
|
6527
6702
|
logout: "\u0432\u044B\u0431\u0440\u0430\u0442\u044C \u043F\u0440\u043E\u0432\u0430\u0439\u0434\u0435\u0440\u0430 \u0438 \u0443\u0434\u0430\u043B\u0438\u0442\u044C \u0435\u0433\u043E \u043A\u043B\u044E\u0447",
|
|
6703
|
+
key: "\u0441\u043C\u0435\u043D\u0438\u0442\u044C \u043A\u043B\u044E\u0447 \u043F\u0440\u043E\u0432\u0430\u0439\u0434\u0435\u0440\u0430",
|
|
6528
6704
|
effort: "\u0433\u043B\u0443\u0431\u0438\u043D\u0430 \u0440\u0430\u0437\u043C\u044B\u0448\u043B\u0435\u043D\u0438\u0439",
|
|
6529
6705
|
thinking: "\u0432\u043A\u043B\u044E\u0447\u0438\u0442\u044C/\u0432\u044B\u043A\u043B\u044E\u0447\u0438\u0442\u044C \u0440\u0430\u0437\u043C\u044B\u0448\u043B\u0435\u043D\u0438\u044F",
|
|
6530
6706
|
budget: "\u043B\u0438\u043C\u0438\u0442 \u0442\u043E\u043A\u0435\u043D\u043E\u0432 \u0437\u0430 \u0445\u043E\u0434 (0 = \u0431\u0435\u0437\u043B\u0438\u043C\u0438\u0442)",
|
|
@@ -6562,43 +6738,9 @@ var DICTIONARIES = { en, ru };
|
|
|
6562
6738
|
function stringsFor(lang) {
|
|
6563
6739
|
return DICTIONARIES[lang ?? "en"];
|
|
6564
6740
|
}
|
|
6565
|
-
var StringsContext =
|
|
6741
|
+
var StringsContext = createContext2(en);
|
|
6566
6742
|
function useStrings() {
|
|
6567
|
-
return
|
|
6568
|
-
}
|
|
6569
|
-
|
|
6570
|
-
// src/ui/theme.ts
|
|
6571
|
-
import { createContext as createContext2, useContext as useContext2 } from "react";
|
|
6572
|
-
var PRESETS = {
|
|
6573
|
-
purple: "#a78bfa",
|
|
6574
|
-
violet: "#8b5cf6",
|
|
6575
|
-
blue: "#60a5fa",
|
|
6576
|
-
cyan: "#22d3ee",
|
|
6577
|
-
green: "#4ade80",
|
|
6578
|
-
orange: "#fb923c",
|
|
6579
|
-
pink: "#f472b6",
|
|
6580
|
-
red: "#f87171",
|
|
6581
|
-
yellow: "#fbbf24",
|
|
6582
|
-
white: "#e5e7eb"
|
|
6583
|
-
};
|
|
6584
|
-
var DEFAULT_ACCENT = PRESETS.purple;
|
|
6585
|
-
function resolveAccent(value) {
|
|
6586
|
-
if (!value) return DEFAULT_ACCENT;
|
|
6587
|
-
const preset = PRESETS[value.toLowerCase()];
|
|
6588
|
-
if (preset) return preset;
|
|
6589
|
-
return /^#[0-9a-f]{6}$/i.test(value) ? value : DEFAULT_ACCENT;
|
|
6590
|
-
}
|
|
6591
|
-
function makeTheme(accent) {
|
|
6592
|
-
return {
|
|
6593
|
-
accent: resolveAccent(accent),
|
|
6594
|
-
ok: PRESETS.green,
|
|
6595
|
-
warn: PRESETS.yellow,
|
|
6596
|
-
error: PRESETS.red
|
|
6597
|
-
};
|
|
6598
|
-
}
|
|
6599
|
-
var ThemeContext = createContext2(makeTheme(void 0));
|
|
6600
|
-
function useTheme() {
|
|
6601
|
-
return useContext2(ThemeContext);
|
|
6743
|
+
return useContext2(StringsContext);
|
|
6602
6744
|
}
|
|
6603
6745
|
|
|
6604
6746
|
// src/ui/components/Confirm.tsx
|
|
@@ -7375,7 +7517,10 @@ function StatusBar({ status }) {
|
|
|
7375
7517
|
}
|
|
7376
7518
|
)
|
|
7377
7519
|
] }),
|
|
7378
|
-
details.length > 0 && /* @__PURE__ */ jsx9(Box9, { flexWrap: "wrap", children: /* @__PURE__ */
|
|
7520
|
+
details.length > 0 && /* @__PURE__ */ jsx9(Box9, { flexWrap: "wrap", children: details.map((item, index) => /* @__PURE__ */ jsxs9(Fragment2, { children: [
|
|
7521
|
+
index > 0 && /* @__PURE__ */ jsx9(Text9, { dimColor: true, children: " \xB7 " }),
|
|
7522
|
+
item
|
|
7523
|
+
] }, index)) })
|
|
7379
7524
|
]
|
|
7380
7525
|
}
|
|
7381
7526
|
);
|
|
@@ -7385,11 +7530,7 @@ function Segments({ items }) {
|
|
|
7385
7530
|
const elements = [];
|
|
7386
7531
|
if (index > 0) {
|
|
7387
7532
|
elements.push(
|
|
7388
|
-
/* @__PURE__ */
|
|
7389
|
-
" ",
|
|
7390
|
-
"\xB7",
|
|
7391
|
-
" "
|
|
7392
|
-
] }, `sep-${index}`)
|
|
7533
|
+
/* @__PURE__ */ jsx9(Box9, { marginTop: 1, children: /* @__PURE__ */ jsx9(Text9, { dimColor: true, children: " \xB7 " }) }, `sep-${index}`)
|
|
7393
7534
|
);
|
|
7394
7535
|
}
|
|
7395
7536
|
elements.push(/* @__PURE__ */ jsx9(Fragment2, { children: item }, `item-${index}`));
|
|
@@ -7475,7 +7616,7 @@ import Spinner2 from "ink-spinner";
|
|
|
7475
7616
|
// src/ui/markdown.tsx
|
|
7476
7617
|
import { Box as Box10, Text as Text10 } from "ink";
|
|
7477
7618
|
import { useMemo as useMemo2 } from "react";
|
|
7478
|
-
import { jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
7619
|
+
import { Fragment as Fragment4, jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
7479
7620
|
function Markdown({ children }) {
|
|
7480
7621
|
const blocks = useMemo2(() => extractBlocks(children), [children]);
|
|
7481
7622
|
return /* @__PURE__ */ jsx10(Box10, { flexDirection: "column", children: blocks.map((block, i) => /* @__PURE__ */ jsx10(BlockView, { block }, i)) });
|
|
@@ -7488,38 +7629,108 @@ function BlockView({ block }) {
|
|
|
7488
7629
|
return /* @__PURE__ */ jsx10(Box10, { marginTop: block.level === 1 ? 1 : 0, children: /* @__PURE__ */ jsx10(Text10, { bold: true, children: truncateBySize(block.text ?? "", size) }) });
|
|
7489
7630
|
}
|
|
7490
7631
|
case "blockquote":
|
|
7491
|
-
return /* @__PURE__ */ jsx10(Box10, { marginLeft: 2, children: /* @__PURE__ */ jsxs10(
|
|
7492
|
-
"\u2502 ",
|
|
7493
|
-
|
|
7494
|
-
] }) });
|
|
7632
|
+
return /* @__PURE__ */ jsx10(Box10, { flexDirection: "column", marginLeft: 2, children: (block.text ?? "").split("\n").map((line, i) => /* @__PURE__ */ jsxs10(Box10, { children: [
|
|
7633
|
+
/* @__PURE__ */ jsx10(Text10, { dimColor: true, children: "\u2502 " }),
|
|
7634
|
+
/* @__PURE__ */ jsx10(Text10, { dimColor: true, children: line })
|
|
7635
|
+
] }, i)) });
|
|
7495
7636
|
case "ul":
|
|
7496
7637
|
return /* @__PURE__ */ jsx10(Box10, { flexDirection: "column", children: (block.items ?? []).map((item, i) => /* @__PURE__ */ jsxs10(Box10, { children: [
|
|
7497
7638
|
/* @__PURE__ */ jsx10(Text10, { dimColor: true, children: "\u2022 " }),
|
|
7498
|
-
/* @__PURE__ */ jsx10(
|
|
7639
|
+
/* @__PURE__ */ jsx10(Box10, { marginLeft: 2, children: /* @__PURE__ */ jsx10(BlockView, { block: item }) })
|
|
7499
7640
|
] }, i)) });
|
|
7500
7641
|
case "ol":
|
|
7501
7642
|
return /* @__PURE__ */ jsx10(Box10, { flexDirection: "column", children: (block.items ?? []).map((item, i) => /* @__PURE__ */ jsxs10(Box10, { children: [
|
|
7502
7643
|
/* @__PURE__ */ jsx10(Text10, { dimColor: true, children: `${i + 1}. ` }),
|
|
7503
|
-
/* @__PURE__ */ jsx10(
|
|
7644
|
+
/* @__PURE__ */ jsx10(Box10, { marginLeft: 2, children: /* @__PURE__ */ jsx10(BlockView, { block: item }) })
|
|
7504
7645
|
] }, i)) });
|
|
7505
7646
|
case "paragraph":
|
|
7506
7647
|
default:
|
|
7507
7648
|
return /* @__PURE__ */ jsx10(Text10, { children: inline(block.text ?? "") });
|
|
7649
|
+
case "table":
|
|
7650
|
+
return /* @__PURE__ */ jsx10(TableView, { block });
|
|
7651
|
+
case "code":
|
|
7652
|
+
return /* @__PURE__ */ jsx10(CodeBlock, { lang: block.lang, code: block.text ?? "" });
|
|
7653
|
+
case "hr":
|
|
7654
|
+
return /* @__PURE__ */ jsx10(Box10, { marginTop: 1, marginBottom: 1, children: /* @__PURE__ */ jsx10(Text10, { dimColor: true, children: "\u2500".repeat(60) }) });
|
|
7655
|
+
}
|
|
7656
|
+
}
|
|
7657
|
+
function CodeBlock({ lang, code }) {
|
|
7658
|
+
const theme = useTheme();
|
|
7659
|
+
const lines = code.split("\n");
|
|
7660
|
+
return /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", marginTop: 1, children: [
|
|
7661
|
+
lang && /* @__PURE__ */ jsx10(Text10, { dimColor: true, color: theme.accent, children: lang }),
|
|
7662
|
+
/* @__PURE__ */ jsx10(Box10, { borderColor: "gray", borderStyle: "round", paddingX: 1, children: /* @__PURE__ */ jsx10(Box10, { flexDirection: "column", children: lines.map((line, i) => /* @__PURE__ */ jsx10(Text10, { color: "cyan", children: line }, i)) }) })
|
|
7663
|
+
] });
|
|
7664
|
+
}
|
|
7665
|
+
function TableView({ block }) {
|
|
7666
|
+
const headers = block.headers ?? [];
|
|
7667
|
+
const rows = block.rows ?? [];
|
|
7668
|
+
if (headers.length === 0 && rows.length === 0) {
|
|
7669
|
+
return /* @__PURE__ */ jsx10(Text10, { dimColor: true, children: "(empty table)" });
|
|
7508
7670
|
}
|
|
7671
|
+
const colCount = Math.max(headers.length, ...rows.map((r) => r?.length ?? 0));
|
|
7672
|
+
if (colCount === 0) {
|
|
7673
|
+
return /* @__PURE__ */ jsx10(Text10, { dimColor: true, children: "(empty table)" });
|
|
7674
|
+
}
|
|
7675
|
+
const colWidths = new Array(colCount).fill(0);
|
|
7676
|
+
const allRows = [headers, ...rows];
|
|
7677
|
+
for (const row of allRows) {
|
|
7678
|
+
for (let i = 0; i < colCount; i++) {
|
|
7679
|
+
const cell = row[i] ?? "";
|
|
7680
|
+
colWidths[i] = Math.max(colWidths[i], [...cell].length);
|
|
7681
|
+
}
|
|
7682
|
+
}
|
|
7683
|
+
const separator = colWidths.map((w) => "\u2500".repeat(w)).join("\u253C");
|
|
7684
|
+
const lines = [];
|
|
7685
|
+
allRows.forEach((row, rowIdx) => {
|
|
7686
|
+
const cells = [];
|
|
7687
|
+
for (let i = 0; i < colCount; i++) {
|
|
7688
|
+
const cell = row[i] ?? "";
|
|
7689
|
+
const align = block.colAligns?.[i] ?? "left";
|
|
7690
|
+
const visualWidth = [...cell].length;
|
|
7691
|
+
const padWidth = colWidths[i] + (cell.length - visualWidth);
|
|
7692
|
+
let padded;
|
|
7693
|
+
if (align === "right") {
|
|
7694
|
+
padded = cell.padStart(padWidth);
|
|
7695
|
+
} else if (align === "center") {
|
|
7696
|
+
const totalPad = padWidth - visualWidth;
|
|
7697
|
+
const left = Math.floor(totalPad / 2);
|
|
7698
|
+
padded = " ".repeat(left) + cell + " ".repeat(totalPad - left);
|
|
7699
|
+
} else {
|
|
7700
|
+
padded = cell.padEnd(padWidth);
|
|
7701
|
+
}
|
|
7702
|
+
cells.push(padded);
|
|
7703
|
+
}
|
|
7704
|
+
lines.push(
|
|
7705
|
+
/* @__PURE__ */ jsx10(Text10, { bold: rowIdx === 0, children: cells.join(" \u2502 ") }, `row-${rowIdx}`)
|
|
7706
|
+
);
|
|
7707
|
+
if (rowIdx === 0) {
|
|
7708
|
+
lines.push(/* @__PURE__ */ jsx10(Text10, { dimColor: true, children: separator }, `sep-${rowIdx}`));
|
|
7709
|
+
}
|
|
7710
|
+
});
|
|
7711
|
+
return /* @__PURE__ */ jsx10(Box10, { flexDirection: "column", children: lines });
|
|
7509
7712
|
}
|
|
7713
|
+
var CHARS_PER_FONT_LEVEL = 3;
|
|
7510
7714
|
function truncateBySize(text, size) {
|
|
7511
|
-
|
|
7715
|
+
const maxLen = size * CHARS_PER_FONT_LEVEL;
|
|
7716
|
+
return text.length > maxLen ? text.slice(0, maxLen) + "\u2026" : text;
|
|
7512
7717
|
}
|
|
7513
7718
|
var HEADING_RE = /^(#{1,6})\s+(.*)$/;
|
|
7514
7719
|
var UL_RE = /^([-*+])\s+(.*)$/;
|
|
7515
7720
|
var OL_RE = /^(\d+)\.\s+(.*)$/;
|
|
7516
7721
|
var QUOTE_RE = /^>\s?(.*)$/;
|
|
7722
|
+
var TASK_RE = /^[-*+]\s+\[([ xX])\]\s+(.*)$/;
|
|
7723
|
+
var INDENT_RE = /^(\s*)(.*)$/;
|
|
7724
|
+
var HR_RE = /^([-*_])\1{2,}$/;
|
|
7725
|
+
var FENCE_RE = /^(`{3,}|~{3,})(\w*)\s*$/;
|
|
7517
7726
|
function extractBlocks(src) {
|
|
7518
7727
|
const lines = src.replace(/\r\n/g, "\n").split("\n");
|
|
7519
7728
|
const blocks = [];
|
|
7520
7729
|
let paragraph = [];
|
|
7521
7730
|
let list = null;
|
|
7522
7731
|
let quote = [];
|
|
7732
|
+
let code = null;
|
|
7733
|
+
let table = null;
|
|
7523
7734
|
const flushParagraph = () => {
|
|
7524
7735
|
if (paragraph.length) {
|
|
7525
7736
|
blocks.push({ type: "paragraph", text: paragraph.join(" ").trim() });
|
|
@@ -7527,7 +7738,7 @@ function extractBlocks(src) {
|
|
|
7527
7738
|
}
|
|
7528
7739
|
};
|
|
7529
7740
|
const flushList = () => {
|
|
7530
|
-
if (list) {
|
|
7741
|
+
if (list && list.items.length) {
|
|
7531
7742
|
blocks.push({ type: list.ordered ? "ol" : "ul", items: list.items });
|
|
7532
7743
|
list = null;
|
|
7533
7744
|
}
|
|
@@ -7538,100 +7749,271 @@ function extractBlocks(src) {
|
|
|
7538
7749
|
quote = [];
|
|
7539
7750
|
}
|
|
7540
7751
|
};
|
|
7752
|
+
const flushCode = () => {
|
|
7753
|
+
if (code) {
|
|
7754
|
+
blocks.push({ type: "code", lang: code.lang, text: code.lines.join("\n") });
|
|
7755
|
+
code = null;
|
|
7756
|
+
}
|
|
7757
|
+
};
|
|
7541
7758
|
const flushAll = () => {
|
|
7542
7759
|
flushParagraph();
|
|
7543
7760
|
flushList();
|
|
7544
7761
|
flushQuote();
|
|
7762
|
+
flushCode();
|
|
7763
|
+
};
|
|
7764
|
+
const flushTable = () => {
|
|
7765
|
+
if (table && table.headers.length > 0) {
|
|
7766
|
+
blocks.push({
|
|
7767
|
+
type: "table",
|
|
7768
|
+
headers: table.headers,
|
|
7769
|
+
rows: table.rows,
|
|
7770
|
+
colAligns: table.colAligns
|
|
7771
|
+
});
|
|
7772
|
+
}
|
|
7773
|
+
table = null;
|
|
7774
|
+
};
|
|
7775
|
+
const parseListItem = (text, indent) => {
|
|
7776
|
+
const task = text.match(TASK_RE);
|
|
7777
|
+
if (task) {
|
|
7778
|
+
const done = task[1].toLowerCase() === "x";
|
|
7779
|
+
return {
|
|
7780
|
+
type: "paragraph",
|
|
7781
|
+
text: `${done ? "\u2611" : "\u2610"} ${task[2]}`
|
|
7782
|
+
};
|
|
7783
|
+
}
|
|
7784
|
+
return { type: "paragraph", text };
|
|
7545
7785
|
};
|
|
7546
|
-
for (
|
|
7786
|
+
for (let i = 0; i < lines.length; i++) {
|
|
7787
|
+
const line = lines[i];
|
|
7547
7788
|
const trimmed = line.trim();
|
|
7789
|
+
if (code) {
|
|
7790
|
+
const endMatch = trimmed.match(FENCE_RE);
|
|
7791
|
+
if (endMatch && endMatch[1][0] === code.fence[0] && endMatch[1].length >= code.fence.length) {
|
|
7792
|
+
flushCode();
|
|
7793
|
+
continue;
|
|
7794
|
+
}
|
|
7795
|
+
code.lines.push(line);
|
|
7796
|
+
continue;
|
|
7797
|
+
}
|
|
7798
|
+
const fenceMatch = trimmed.match(FENCE_RE);
|
|
7799
|
+
if (fenceMatch) {
|
|
7800
|
+
flushAll();
|
|
7801
|
+
flushTable();
|
|
7802
|
+
code = { fence: fenceMatch[1], lang: fenceMatch[2], lines: [] };
|
|
7803
|
+
continue;
|
|
7804
|
+
}
|
|
7548
7805
|
if (trimmed === "") {
|
|
7806
|
+
flushTable();
|
|
7807
|
+
flushAll();
|
|
7808
|
+
continue;
|
|
7809
|
+
}
|
|
7810
|
+
const hrMatch = trimmed.match(HR_RE);
|
|
7811
|
+
if (hrMatch) {
|
|
7812
|
+
flushTable();
|
|
7549
7813
|
flushAll();
|
|
7814
|
+
blocks.push({ type: "hr" });
|
|
7550
7815
|
continue;
|
|
7551
7816
|
}
|
|
7552
7817
|
const heading = trimmed.match(HEADING_RE);
|
|
7553
7818
|
if (heading) {
|
|
7819
|
+
flushTable();
|
|
7554
7820
|
flushAll();
|
|
7555
7821
|
blocks.push({ type: "heading", level: heading[1].length, text: heading[2].trim() });
|
|
7556
7822
|
continue;
|
|
7557
7823
|
}
|
|
7558
7824
|
const quoteMatch = trimmed.match(QUOTE_RE);
|
|
7559
7825
|
if (quoteMatch) {
|
|
7826
|
+
flushTable();
|
|
7560
7827
|
flushParagraph();
|
|
7561
7828
|
flushList();
|
|
7829
|
+
flushCode();
|
|
7562
7830
|
quote.push(quoteMatch[1]);
|
|
7563
7831
|
continue;
|
|
7564
7832
|
}
|
|
7833
|
+
const indentMatch = line.match(INDENT_RE);
|
|
7834
|
+
const indent = indentMatch?.[1].length ?? 0;
|
|
7835
|
+
const taskMatch = trimmed.match(TASK_RE);
|
|
7836
|
+
if (taskMatch) {
|
|
7837
|
+
flushTable();
|
|
7838
|
+
flushParagraph();
|
|
7839
|
+
flushQuote();
|
|
7840
|
+
flushCode();
|
|
7841
|
+
if (!list || list.ordered || indent !== list.indent) {
|
|
7842
|
+
flushList();
|
|
7843
|
+
list = { ordered: false, items: [], indent };
|
|
7844
|
+
}
|
|
7845
|
+
list.items.push(parseListItem(trimmed, indent));
|
|
7846
|
+
continue;
|
|
7847
|
+
}
|
|
7565
7848
|
const ulMatch = trimmed.match(UL_RE);
|
|
7566
7849
|
if (ulMatch) {
|
|
7850
|
+
flushTable();
|
|
7567
7851
|
flushParagraph();
|
|
7568
7852
|
flushQuote();
|
|
7569
|
-
|
|
7853
|
+
flushCode();
|
|
7854
|
+
if (!list || list.ordered || indent !== list.indent) {
|
|
7570
7855
|
flushList();
|
|
7571
|
-
list = { ordered: false, items: [] };
|
|
7856
|
+
list = { ordered: false, items: [], indent };
|
|
7572
7857
|
}
|
|
7573
|
-
list.items.push(ulMatch[2]
|
|
7858
|
+
list.items.push(parseListItem(ulMatch[2], indent));
|
|
7574
7859
|
continue;
|
|
7575
7860
|
}
|
|
7576
7861
|
const olMatch = trimmed.match(OL_RE);
|
|
7577
7862
|
if (olMatch) {
|
|
7863
|
+
flushTable();
|
|
7578
7864
|
flushParagraph();
|
|
7579
7865
|
flushQuote();
|
|
7580
|
-
|
|
7866
|
+
flushCode();
|
|
7867
|
+
if (!list || !list.ordered || indent !== list.indent) {
|
|
7581
7868
|
flushList();
|
|
7582
|
-
list = { ordered: true, items: [] };
|
|
7869
|
+
list = { ordered: true, items: [], indent };
|
|
7583
7870
|
}
|
|
7584
|
-
list.items.push(olMatch[2]
|
|
7871
|
+
list.items.push(parseListItem(olMatch[2], indent));
|
|
7585
7872
|
continue;
|
|
7586
7873
|
}
|
|
7874
|
+
const sepMatch = trimmed.match(/^\|?\s*(\s*:?-+:?\s*\|)+\s*:?-+:?\s*\|?$/);
|
|
7875
|
+
if (sepMatch) {
|
|
7876
|
+
const cells = trimmed.split("|").map((c) => c.trim()).filter((c) => c.length > 0);
|
|
7877
|
+
if (cells.length >= 2 && cells.every((c) => /^:?-+:?$/.test(c))) {
|
|
7878
|
+
if (table) {
|
|
7879
|
+
table.colAligns = cells.map((c) => {
|
|
7880
|
+
if (c.startsWith(":") && c.endsWith(":")) return "center";
|
|
7881
|
+
if (c.endsWith(":")) return "right";
|
|
7882
|
+
return "left";
|
|
7883
|
+
});
|
|
7884
|
+
continue;
|
|
7885
|
+
}
|
|
7886
|
+
}
|
|
7887
|
+
}
|
|
7888
|
+
const rowMatch = trimmed.match(/^\|?.+\|.+\|?$/);
|
|
7889
|
+
if (rowMatch) {
|
|
7890
|
+
const cells = trimmed.split("|").map((c) => c.trim()).filter((c, idx, arr) => {
|
|
7891
|
+
if (idx === 0 && c === "") return false;
|
|
7892
|
+
if (idx === arr.length - 1 && c === "") return false;
|
|
7893
|
+
return true;
|
|
7894
|
+
});
|
|
7895
|
+
const isWindowsPath = cells.length === 2 && (/^[A-Za-z]:\\/.test(cells[0]) || /^\\\\/.test(cells[0]));
|
|
7896
|
+
if (cells.length >= 2 && cells.every((c) => c.length > 0) && !isWindowsPath) {
|
|
7897
|
+
if (table) {
|
|
7898
|
+
table.rows.push(cells);
|
|
7899
|
+
} else {
|
|
7900
|
+
flushAll();
|
|
7901
|
+
table = { headers: cells, rows: [], colAligns: [] };
|
|
7902
|
+
}
|
|
7903
|
+
continue;
|
|
7904
|
+
}
|
|
7905
|
+
}
|
|
7906
|
+
const asciiSepMatch = trimmed.match(/^[\s\-─┄┈━┅]{3,}$/);
|
|
7907
|
+
if (asciiSepMatch) {
|
|
7908
|
+
if (table && table.headers.length > 0 && table.colAligns.length === 0) {
|
|
7909
|
+
table.colAligns = table.headers.map(() => "left");
|
|
7910
|
+
continue;
|
|
7911
|
+
}
|
|
7912
|
+
if (paragraph.length === 1) {
|
|
7913
|
+
const prevLine = paragraph[0].trim();
|
|
7914
|
+
const tokens3 = prevLine.split(/\s{2,}/).map((c) => c.trim()).filter((c) => c.length > 0);
|
|
7915
|
+
if (tokens3.length >= 2 && tokens3.length <= 10) {
|
|
7916
|
+
paragraph = [];
|
|
7917
|
+
flushList();
|
|
7918
|
+
flushQuote();
|
|
7919
|
+
flushCode();
|
|
7920
|
+
table = { headers: tokens3, rows: [], colAligns: tokens3.map(() => "left") };
|
|
7921
|
+
continue;
|
|
7922
|
+
}
|
|
7923
|
+
}
|
|
7924
|
+
}
|
|
7925
|
+
if (table && table.headers.length > 0 && !trimmed.includes("|")) {
|
|
7926
|
+
const tokens3 = trimmed.split(/\s{2,}/).map((c) => c.trim()).filter((c) => c.length > 0);
|
|
7927
|
+
if (tokens3.length >= 2 && tokens3.length <= table.headers.length + 2) {
|
|
7928
|
+
const looksLikeCode = tokens3.some((t) => t.startsWith("//") || t.startsWith("#") || t.startsWith("/*"));
|
|
7929
|
+
const looksLikePath = tokens3.length === 2 && (/^[A-Za-z]:\\/.test(tokens3[0]) || /^\\\\/.test(tokens3[0]));
|
|
7930
|
+
if (!looksLikeCode && !looksLikePath) {
|
|
7931
|
+
table.rows.push(tokens3);
|
|
7932
|
+
continue;
|
|
7933
|
+
}
|
|
7934
|
+
}
|
|
7935
|
+
}
|
|
7936
|
+
flushTable();
|
|
7587
7937
|
flushList();
|
|
7588
7938
|
flushQuote();
|
|
7939
|
+
flushCode();
|
|
7589
7940
|
paragraph.push(trimmed);
|
|
7590
7941
|
}
|
|
7942
|
+
flushTable();
|
|
7591
7943
|
flushAll();
|
|
7592
7944
|
return blocks;
|
|
7593
7945
|
}
|
|
7594
|
-
|
|
7946
|
+
var MAX_INLINE_DEPTH = 10;
|
|
7947
|
+
function inline(text, depth = 0) {
|
|
7948
|
+
if (depth > MAX_INLINE_DEPTH) {
|
|
7949
|
+
return text;
|
|
7950
|
+
}
|
|
7595
7951
|
const nodes = [];
|
|
7596
7952
|
let rest = text;
|
|
7597
7953
|
let key = 0;
|
|
7598
|
-
const
|
|
7954
|
+
const matchers = [
|
|
7955
|
+
{
|
|
7956
|
+
re: /(!\[)([^\]]*)\]\(([^)]+)\)/,
|
|
7957
|
+
handler: (m) => /* @__PURE__ */ jsxs10(Text10, { color: "cyan", bold: true, children: [
|
|
7958
|
+
"[img: ",
|
|
7959
|
+
m[2],
|
|
7960
|
+
"]"
|
|
7961
|
+
] }, key++)
|
|
7962
|
+
},
|
|
7963
|
+
{
|
|
7964
|
+
re: /(\[)([^\]]*)\]\(([^)]+)\)/,
|
|
7965
|
+
handler: (m) => /* @__PURE__ */ jsxs10(Fragment4, { children: [
|
|
7966
|
+
/* @__PURE__ */ jsx10(Text10, { color: "cyan", underline: true, children: inline(m[2], depth + 1) }, key++),
|
|
7967
|
+
/* @__PURE__ */ jsxs10(Text10, { dimColor: true, color: "gray", children: [
|
|
7968
|
+
"(",
|
|
7969
|
+
m[3],
|
|
7970
|
+
")"
|
|
7971
|
+
] }, key++)
|
|
7972
|
+
] })
|
|
7973
|
+
},
|
|
7974
|
+
{
|
|
7975
|
+
re: /(`+)([^`]+?)\1/,
|
|
7976
|
+
handler: (m) => /* @__PURE__ */ jsx10(Text10, { bold: true, color: "cyan", children: m[2] }, key++)
|
|
7977
|
+
},
|
|
7978
|
+
{
|
|
7979
|
+
re: /(\*\*)(.+?)\1/,
|
|
7980
|
+
handler: (m) => /* @__PURE__ */ jsx10(Text10, { bold: true, children: inline(m[2], depth + 1) }, key++)
|
|
7981
|
+
},
|
|
7982
|
+
{
|
|
7983
|
+
re: /(__)(.+?)\1/,
|
|
7984
|
+
handler: (m) => /* @__PURE__ */ jsx10(Text10, { bold: true, children: inline(m[2], depth + 1) }, key++)
|
|
7985
|
+
},
|
|
7986
|
+
{
|
|
7987
|
+
re: /(~~)(.+?)\1/,
|
|
7988
|
+
handler: (m) => /* @__PURE__ */ jsx10(Text10, { strikethrough: true, children: m[2] }, key++)
|
|
7989
|
+
},
|
|
7990
|
+
{
|
|
7991
|
+
re: /(\*)(.+?)\1/,
|
|
7992
|
+
handler: (m) => /* @__PURE__ */ jsx10(Text10, { italic: true, children: inline(m[2], depth + 1) }, key++)
|
|
7993
|
+
}
|
|
7994
|
+
];
|
|
7599
7995
|
while (rest.length > 0) {
|
|
7600
|
-
|
|
7601
|
-
|
|
7996
|
+
let best = null;
|
|
7997
|
+
for (const { re, handler } of matchers) {
|
|
7998
|
+
const m = rest.match(re);
|
|
7999
|
+
if (m && (best === null || (m.index ?? 0) < (best.match.index ?? 0))) {
|
|
8000
|
+
best = { match: m, handler };
|
|
8001
|
+
}
|
|
8002
|
+
}
|
|
8003
|
+
if (!best) {
|
|
7602
8004
|
nodes.push(rest);
|
|
7603
8005
|
break;
|
|
7604
8006
|
}
|
|
7605
|
-
const idx =
|
|
8007
|
+
const idx = best.match.index ?? 0;
|
|
7606
8008
|
if (idx > 0) nodes.push(rest.slice(0, idx));
|
|
7607
|
-
|
|
7608
|
-
|
|
7609
|
-
/* @__PURE__ */ jsx10(Text10, { bold: true, color: "cyan", children: m[2] }, key++)
|
|
7610
|
-
);
|
|
7611
|
-
} else if (m[3]) {
|
|
7612
|
-
nodes.push(
|
|
7613
|
-
/* @__PURE__ */ jsx10(Text10, { bold: true, children: inline(m[4]) }, key++)
|
|
7614
|
-
);
|
|
7615
|
-
} else if (m[5]) {
|
|
7616
|
-
nodes.push(
|
|
7617
|
-
/* @__PURE__ */ jsx10(Text10, { strikethrough: true, children: m[6] }, key++)
|
|
7618
|
-
);
|
|
7619
|
-
} else if (m[7]) {
|
|
7620
|
-
nodes.push(
|
|
7621
|
-
/* @__PURE__ */ jsx10(Text10, { italic: true, children: inline(m[8]) }, key++)
|
|
7622
|
-
);
|
|
7623
|
-
} else if (m[9]) {
|
|
7624
|
-
nodes.push(
|
|
7625
|
-
/* @__PURE__ */ jsx10(Text10, { bold: true, children: inline(m[10]) }, key++)
|
|
7626
|
-
);
|
|
7627
|
-
}
|
|
7628
|
-
rest = rest.slice(idx + m[0].length);
|
|
8009
|
+
nodes.push(best.handler(best.match));
|
|
8010
|
+
rest = rest.slice(idx + best.match[0].length);
|
|
7629
8011
|
}
|
|
7630
8012
|
return nodes.length === 1 ? nodes[0] : nodes;
|
|
7631
8013
|
}
|
|
7632
8014
|
|
|
7633
8015
|
// src/ui/components/Transcript.tsx
|
|
7634
|
-
import { Fragment as
|
|
8016
|
+
import { Fragment as Fragment5, jsx as jsx11, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
7635
8017
|
var HEADER = { kind: "header" };
|
|
7636
8018
|
var Transcript = memo2(function Transcript2({ bubbles, workspace }) {
|
|
7637
8019
|
const liveAt = bubbles.findLastIndex(
|
|
@@ -7698,7 +8080,7 @@ function ToolView({ bubble }) {
|
|
|
7698
8080
|
mark,
|
|
7699
8081
|
" ",
|
|
7700
8082
|
bubble.summary,
|
|
7701
|
-
hasChanges && /* @__PURE__ */ jsxs11(
|
|
8083
|
+
hasChanges && /* @__PURE__ */ jsxs11(Fragment5, { children: [
|
|
7702
8084
|
diff.added > 0 && /* @__PURE__ */ jsx11(Text11, { color: theme.ok, bold: true, children: ` +${diff.added}` }),
|
|
7703
8085
|
diff.removed > 0 && /* @__PURE__ */ jsx11(Text11, { color: theme.error, bold: true, children: ` -${diff.removed}` })
|
|
7704
8086
|
] })
|
|
@@ -7823,7 +8205,7 @@ function applyEvent(state, event) {
|
|
|
7823
8205
|
};
|
|
7824
8206
|
case "subagent_event": {
|
|
7825
8207
|
const idx = state.bubbles.findLastIndex(
|
|
7826
|
-
(b) => b.kind === "subagent" && b.id === event.id
|
|
8208
|
+
(b) => b.kind === "subagent" && b.id === event.id
|
|
7827
8209
|
);
|
|
7828
8210
|
if (idx === -1) return state;
|
|
7829
8211
|
const sub = state.bubbles[idx];
|
|
@@ -8077,7 +8459,7 @@ function App({
|
|
|
8077
8459
|
return () => clearInterval(timer);
|
|
8078
8460
|
}, [turnStart]);
|
|
8079
8461
|
useEffect2(() => {
|
|
8080
|
-
if (process.platform === "darwin") return;
|
|
8462
|
+
if (process.platform === "darwin" || process.platform === "win32") return;
|
|
8081
8463
|
let last = "";
|
|
8082
8464
|
let cancelling = false;
|
|
8083
8465
|
let giveUp = false;
|
|
@@ -8151,6 +8533,12 @@ function App({
|
|
|
8151
8533
|
}),
|
|
8152
8534
|
[]
|
|
8153
8535
|
);
|
|
8536
|
+
const promptInput = useCallback(
|
|
8537
|
+
(title, mask) => new Promise((resolve3) => {
|
|
8538
|
+
setOverlay({ kind: "textinput", title, mask, resolve: resolve3, cancel: () => resolve3(null) });
|
|
8539
|
+
}),
|
|
8540
|
+
[]
|
|
8541
|
+
);
|
|
8154
8542
|
const runAgent = useCallback(async () => {
|
|
8155
8543
|
const controller = new AbortController();
|
|
8156
8544
|
abort.current = controller;
|
|
@@ -8468,6 +8856,35 @@ ${strings.modelSet(result.nextModel)}` : ""}`;
|
|
|
8468
8856
|
forceRender((n) => n + 1);
|
|
8469
8857
|
return;
|
|
8470
8858
|
}
|
|
8859
|
+
case "key": {
|
|
8860
|
+
const providers = runtime.listProviderItems();
|
|
8861
|
+
if (providers.length === 0) {
|
|
8862
|
+
notice("warn", strings.logoutNothing);
|
|
8863
|
+
return;
|
|
8864
|
+
}
|
|
8865
|
+
let providerId = rest[0];
|
|
8866
|
+
if (!providerId) {
|
|
8867
|
+
providerId = providers.length === 1 ? providers[0]?.key : await pick(strings.titleKeyChange, providers) ?? void 0;
|
|
8868
|
+
}
|
|
8869
|
+
if (!providerId) return;
|
|
8870
|
+
if (!providers.some((provider) => provider.key === providerId)) {
|
|
8871
|
+
notice("warn", `Provider "${providerId}" is not configured.`);
|
|
8872
|
+
return;
|
|
8873
|
+
}
|
|
8874
|
+
if (!await ask2(strings.keyChangeAsk(providerId), strings.keyChangeAskBody, true)) return;
|
|
8875
|
+
const newKey = await promptInput(strings.apiKey, true);
|
|
8876
|
+
if (!newKey || newKey.trim() === "") {
|
|
8877
|
+
notice("warn", "Empty key \u2014 nothing changed.");
|
|
8878
|
+
return;
|
|
8879
|
+
}
|
|
8880
|
+
try {
|
|
8881
|
+
await runtime.changeProviderKey(providerId, newKey.trim());
|
|
8882
|
+
notice("info", strings.keyChanged(providerId));
|
|
8883
|
+
} catch (error) {
|
|
8884
|
+
notice("error", error instanceof Error ? error.message : String(error));
|
|
8885
|
+
}
|
|
8886
|
+
return;
|
|
8887
|
+
}
|
|
8471
8888
|
case "lang": {
|
|
8472
8889
|
const choice = await pick(
|
|
8473
8890
|
strings.titleLang,
|
|
@@ -9201,6 +9618,21 @@ ${strings.configAt(runtime.configPath())}`);
|
|
|
9201
9618
|
}
|
|
9202
9619
|
}
|
|
9203
9620
|
),
|
|
9621
|
+
overlay.kind === "textinput" && /* @__PURE__ */ jsx12(
|
|
9622
|
+
TextInputOverlay,
|
|
9623
|
+
{
|
|
9624
|
+
title: overlay.title,
|
|
9625
|
+
mask: overlay.mask,
|
|
9626
|
+
onAnswer: (value) => {
|
|
9627
|
+
overlay.resolve(value);
|
|
9628
|
+
setOverlay({ kind: "none" });
|
|
9629
|
+
},
|
|
9630
|
+
onCancel: () => {
|
|
9631
|
+
overlay.cancel?.();
|
|
9632
|
+
setOverlay({ kind: "none" });
|
|
9633
|
+
}
|
|
9634
|
+
}
|
|
9635
|
+
),
|
|
9204
9636
|
overlay.kind === "none" && /* @__PURE__ */ jsx12(
|
|
9205
9637
|
PromptInput,
|
|
9206
9638
|
{
|
|
@@ -9241,6 +9673,34 @@ ${strings.configAt(runtime.configPath())}`);
|
|
|
9241
9673
|
] })
|
|
9242
9674
|
);
|
|
9243
9675
|
}
|
|
9676
|
+
function TextInputOverlay({
|
|
9677
|
+
title,
|
|
9678
|
+
mask,
|
|
9679
|
+
onAnswer,
|
|
9680
|
+
onCancel
|
|
9681
|
+
}) {
|
|
9682
|
+
const [value, setValue] = useState5("");
|
|
9683
|
+
const theme = useTheme();
|
|
9684
|
+
const submit = useCallback(() => {
|
|
9685
|
+
onAnswer(value || null);
|
|
9686
|
+
}, [value, onAnswer]);
|
|
9687
|
+
useInput2((_input, key) => {
|
|
9688
|
+
if (key.escape) onCancel();
|
|
9689
|
+
});
|
|
9690
|
+
return /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", children: [
|
|
9691
|
+
/* @__PURE__ */ jsx12(Text12, { dimColor: true, children: title }),
|
|
9692
|
+
/* @__PURE__ */ jsx12(
|
|
9693
|
+
TextInput2,
|
|
9694
|
+
{
|
|
9695
|
+
value,
|
|
9696
|
+
onChange: setValue,
|
|
9697
|
+
onSubmit: submit,
|
|
9698
|
+
mask: mask ? "\u2022" : void 0
|
|
9699
|
+
}
|
|
9700
|
+
),
|
|
9701
|
+
/* @__PURE__ */ jsx12(Box12, { marginTop: 1, children: /* @__PURE__ */ jsx12(Text12, { dimColor: true, color: theme.accent, children: "enter submit \xB7 esc cancel" }) })
|
|
9702
|
+
] });
|
|
9703
|
+
}
|
|
9244
9704
|
function slashNeedsIdle(line) {
|
|
9245
9705
|
const [command = "", subcommand = ""] = line.slice(1).trim().toLowerCase().split(/\s+/);
|
|
9246
9706
|
return NEEDS_IDLE.has(command) || command === "mcp" && (subcommand === "add" || subcommand === "delete" || subcommand === "enable" || subcommand === "disable");
|
|
@@ -9322,6 +9782,13 @@ function copyToClipboard(text) {
|
|
|
9322
9782
|
}
|
|
9323
9783
|
return;
|
|
9324
9784
|
}
|
|
9785
|
+
if (process.platform === "win32") {
|
|
9786
|
+
try {
|
|
9787
|
+
run("clip", []);
|
|
9788
|
+
} catch {
|
|
9789
|
+
}
|
|
9790
|
+
return;
|
|
9791
|
+
}
|
|
9325
9792
|
const candidates = [
|
|
9326
9793
|
["wl-copy", []],
|
|
9327
9794
|
["xclip", ["-selection", "clipboard"]],
|