@kernelonpanic/kitcode 1.0.1-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 +588 -122
- 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";
|
|
@@ -2798,6 +2826,8 @@ var STEP_LIMIT_NOTE = `The subagent was stopped after ${MAX_SUBAGENT_STEPS} mode
|
|
|
2798
2826
|
var SUBAGENT_SYSTEM = [
|
|
2799
2827
|
"You are a subagent launched by the main kitcode agent to carry out one task on your own.",
|
|
2800
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
|
+
"",
|
|
2801
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.",
|
|
2802
2832
|
"",
|
|
2803
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.'
|
|
@@ -2971,10 +3001,14 @@ function printableInput(input) {
|
|
|
2971
3001
|
}
|
|
2972
3002
|
async function invoke(call, tool, input, signal) {
|
|
2973
3003
|
try {
|
|
2974
|
-
|
|
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);
|
|
2975
3008
|
return { content: flattenResult(result), isError: result.isError };
|
|
2976
3009
|
} catch (error) {
|
|
2977
|
-
|
|
3010
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
3011
|
+
return { content: redactSecrets(message), isError: true };
|
|
2978
3012
|
}
|
|
2979
3013
|
}
|
|
2980
3014
|
function flattenResult(result) {
|
|
@@ -3016,10 +3050,18 @@ function clip(value) {
|
|
|
3016
3050
|
// package.json
|
|
3017
3051
|
var package_default = {
|
|
3018
3052
|
name: "@kernelonpanic/kitcode",
|
|
3019
|
-
version: "1.
|
|
3053
|
+
version: "1.1.1",
|
|
3020
3054
|
description: "Terminal coding agent with a config you never have to write by hand",
|
|
3021
3055
|
type: "module",
|
|
3022
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
|
+
},
|
|
3023
3065
|
bin: {
|
|
3024
3066
|
kitcode: "dist/index.js"
|
|
3025
3067
|
},
|
|
@@ -3075,8 +3117,8 @@ var package_default = {
|
|
|
3075
3117
|
|
|
3076
3118
|
// src/version.ts
|
|
3077
3119
|
var KITCODE_VERSION = package_default.version;
|
|
3078
|
-
var KITCODE_REPOSITORY = "
|
|
3079
|
-
var KITCODE_COMMIT = true ? "
|
|
3120
|
+
var KITCODE_REPOSITORY = "KernelEditor/KitCode";
|
|
3121
|
+
var KITCODE_COMMIT = true ? "a57e54102a5e1ca1e60cec4ee9c629c890d27057" : "development";
|
|
3080
3122
|
|
|
3081
3123
|
// src/mcp/client.ts
|
|
3082
3124
|
var clientInfo = { name: "kitcode", version: KITCODE_VERSION };
|
|
@@ -3100,6 +3142,36 @@ var inheritedEnvKeys = /* @__PURE__ */ new Set([
|
|
|
3100
3142
|
"APPDATA",
|
|
3101
3143
|
"LOCALAPPDATA"
|
|
3102
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
|
+
]);
|
|
3103
3175
|
function createMcpManager(servers) {
|
|
3104
3176
|
const serverStates = /* @__PURE__ */ new Map();
|
|
3105
3177
|
const sessions = /* @__PURE__ */ new Map();
|
|
@@ -3246,7 +3318,9 @@ function configuredSecrets(config) {
|
|
|
3246
3318
|
function inheritedEnv(source = process.env) {
|
|
3247
3319
|
const env = {};
|
|
3248
3320
|
for (const [key, value] of Object.entries(source)) {
|
|
3249
|
-
if (value !== void 0 && inheritedEnvKeys.has(key)
|
|
3321
|
+
if (value !== void 0 && inheritedEnvKeys.has(key) && !blockedEnvKeys.has(key)) {
|
|
3322
|
+
env[key] = value;
|
|
3323
|
+
}
|
|
3250
3324
|
}
|
|
3251
3325
|
return env;
|
|
3252
3326
|
}
|
|
@@ -4071,7 +4145,7 @@ function isFileEdit(tool) {
|
|
|
4071
4145
|
}
|
|
4072
4146
|
|
|
4073
4147
|
// src/tools/edit.ts
|
|
4074
|
-
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";
|
|
4075
4149
|
var MAX_FILE_BYTES2 = 5e6;
|
|
4076
4150
|
function countOccurrences(haystack, needle) {
|
|
4077
4151
|
if (needle === "") return 0;
|
|
@@ -4127,6 +4201,10 @@ var editTool = {
|
|
|
4127
4201
|
const { path: path14, oldString, newString, replaceAll = false } = input;
|
|
4128
4202
|
const safe = resolveInside(ctx.cwd, path14);
|
|
4129
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
|
+
}
|
|
4130
4208
|
const info = await stat6(safe.path).catch(() => null);
|
|
4131
4209
|
if (info && info.size > MAX_FILE_BYTES2) {
|
|
4132
4210
|
return {
|
|
@@ -4244,13 +4322,25 @@ var sensitiveNames = /* @__PURE__ */ new Set([
|
|
|
4244
4322
|
".npmrc",
|
|
4245
4323
|
".pypirc",
|
|
4246
4324
|
".git-credentials",
|
|
4325
|
+
".netrc",
|
|
4326
|
+
".pgpass",
|
|
4327
|
+
".my.cnf",
|
|
4328
|
+
".docker/config.json",
|
|
4247
4329
|
"auth.json",
|
|
4248
4330
|
"credentials",
|
|
4249
4331
|
"credentials.json",
|
|
4250
4332
|
"id_rsa",
|
|
4251
|
-
"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"
|
|
4252
4342
|
]);
|
|
4253
|
-
var sensitiveExtensions = /* @__PURE__ */ new Set([".pem", ".key", ".p12", ".pfx"]);
|
|
4343
|
+
var sensitiveExtensions = /* @__PURE__ */ new Set([".pem", ".key", ".p12", ".pfx", ".jks", ".keystore", ".p8", ".der"]);
|
|
4254
4344
|
function isSensitivePath(value) {
|
|
4255
4345
|
if (typeof value !== "string") return false;
|
|
4256
4346
|
const normalized = value.replaceAll("\\", "/").toLowerCase();
|
|
@@ -4262,7 +4352,7 @@ function isSensitivePath(value) {
|
|
|
4262
4352
|
function mentionsSensitivePattern(value) {
|
|
4263
4353
|
if (typeof value !== "string") return false;
|
|
4264
4354
|
const lower = value.toLowerCase();
|
|
4265
|
-
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);
|
|
4266
4356
|
}
|
|
4267
4357
|
|
|
4268
4358
|
// src/tools/regex.ts
|
|
@@ -4539,7 +4629,7 @@ function toToolSchema(tool) {
|
|
|
4539
4629
|
}
|
|
4540
4630
|
|
|
4541
4631
|
// src/tools/write.ts
|
|
4542
|
-
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";
|
|
4543
4633
|
import { dirname as dirname2 } from "path";
|
|
4544
4634
|
var MAX_FILE_BYTES5 = 5e6;
|
|
4545
4635
|
var writeTool = {
|
|
@@ -4590,6 +4680,10 @@ var writeTool = {
|
|
|
4590
4680
|
}
|
|
4591
4681
|
const safe = resolveInside(ctx.cwd, path14);
|
|
4592
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
|
+
}
|
|
4593
4687
|
const beforeInfo = await stat9(safe.path).catch(() => null);
|
|
4594
4688
|
if (beforeInfo && beforeInfo.size > MAX_FILE_BYTES5) {
|
|
4595
4689
|
return {
|
|
@@ -5439,6 +5533,28 @@ async function boot(options) {
|
|
|
5439
5533
|
}
|
|
5440
5534
|
return { removed: providerId, wasActive, ...nextModel ? { nextModel } : {} };
|
|
5441
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
|
+
},
|
|
5442
5558
|
sessionId: () => session.id,
|
|
5443
5559
|
async newSession() {
|
|
5444
5560
|
await persistQueue.catch(() => void 0);
|
|
@@ -5745,6 +5861,17 @@ ${lines.join("\n")}` : null;
|
|
|
5745
5861
|
startupUpdateCheck: () => startupUpdate,
|
|
5746
5862
|
async run(history, hooks, signal) {
|
|
5747
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
|
+
}
|
|
5748
5875
|
let requestHistory = history;
|
|
5749
5876
|
const budget = createTurnBudget(config.budget, resolvePricing);
|
|
5750
5877
|
const previousContext = runtime.modelContext();
|
|
@@ -6038,7 +6165,7 @@ function validateKey(value) {
|
|
|
6038
6165
|
import { render } from "ink";
|
|
6039
6166
|
|
|
6040
6167
|
// src/ui/App.tsx
|
|
6041
|
-
import { Box as Box12, useApp } from "ink";
|
|
6168
|
+
import { Box as Box12, Text as Text12, useApp, useInput as useInput2 } from "ink";
|
|
6042
6169
|
import { execFile as execFile2, execFileSync as execFileSync2 } from "child_process";
|
|
6043
6170
|
import { useCallback, useEffect as useEffect2, useMemo as useMemo4, useRef as useRef4, useState as useState5 } from "react";
|
|
6044
6171
|
|
|
@@ -6080,6 +6207,7 @@ var COMMANDS = [
|
|
|
6080
6207
|
{ name: "login" },
|
|
6081
6208
|
{ name: "provider" },
|
|
6082
6209
|
{ name: "logout", args: "[provider]" },
|
|
6210
|
+
{ name: "key", args: "[provider]" },
|
|
6083
6211
|
{ name: "effort" },
|
|
6084
6212
|
{ name: "thinking" },
|
|
6085
6213
|
{ name: "budget", args: "[tokens]" },
|
|
@@ -6167,6 +6295,43 @@ function editDistance(a, b) {
|
|
|
6167
6295
|
return previous[b.length] ?? 0;
|
|
6168
6296
|
}
|
|
6169
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
|
+
|
|
6170
6335
|
// src/ui/components/Confirm.tsx
|
|
6171
6336
|
import { Box, Text } from "ink";
|
|
6172
6337
|
|
|
@@ -6200,7 +6365,7 @@ function normalizeHotkey(char) {
|
|
|
6200
6365
|
}
|
|
6201
6366
|
|
|
6202
6367
|
// src/ui/i18n.ts
|
|
6203
|
-
import { createContext, useContext } from "react";
|
|
6368
|
+
import { createContext as createContext2, useContext as useContext2 } from "react";
|
|
6204
6369
|
var LANGS = [
|
|
6205
6370
|
{ key: "en", label: "English" },
|
|
6206
6371
|
{ key: "ru", label: "\u0420\u0443\u0441\u0441\u043A\u0438\u0439" }
|
|
@@ -6230,6 +6395,7 @@ var en = {
|
|
|
6230
6395
|
workingFor: (elapsed) => `working ${elapsed}`,
|
|
6231
6396
|
titleProvider: "Provider",
|
|
6232
6397
|
titleLogout: "Sign out of a provider",
|
|
6398
|
+
titleKeyChange: "Change API key for a provider",
|
|
6233
6399
|
titleSessions: "Resume a session",
|
|
6234
6400
|
titleSessionAction: "Session action",
|
|
6235
6401
|
resumed: (id, count) => `Resumed ${id} \u2014 ${count} messages restored`,
|
|
@@ -6317,6 +6483,9 @@ var en = {
|
|
|
6317
6483
|
logoutAskBody: "Its API key is removed from auth.json and the provider is dropped from the config.",
|
|
6318
6484
|
loggedOut: (provider) => `Signed out of "${provider}".`,
|
|
6319
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}".`,
|
|
6320
6489
|
promptUsage: "Usage: /prompt save <name>",
|
|
6321
6490
|
promptNothing: "Nothing to save yet \u2014 send a message first.",
|
|
6322
6491
|
promptSaved: (name) => `Saved prompt "${name}"`,
|
|
@@ -6348,6 +6517,7 @@ var en = {
|
|
|
6348
6517
|
login: "add a provider (url + key)",
|
|
6349
6518
|
provider: "switch between configured providers",
|
|
6350
6519
|
logout: "choose a provider to sign out from",
|
|
6520
|
+
key: "change API key for a provider",
|
|
6351
6521
|
effort: "set reasoning depth",
|
|
6352
6522
|
thinking: "toggle reasoning",
|
|
6353
6523
|
budget: "set token budget per turn",
|
|
@@ -6408,6 +6578,7 @@ var ru = {
|
|
|
6408
6578
|
workingFor: (elapsed) => `\u0434\u0443\u043C\u0430\u0435\u0442 ${elapsed}`,
|
|
6409
6579
|
titleProvider: "\u041F\u0440\u043E\u0432\u0430\u0439\u0434\u0435\u0440",
|
|
6410
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",
|
|
6411
6582
|
titleSessions: "\u0412\u043E\u0441\u0441\u0442\u0430\u043D\u043E\u0432\u0438\u0442\u044C \u0441\u0435\u0441\u0441\u0438\u044E",
|
|
6412
6583
|
titleSessionAction: "\u0414\u0435\u0439\u0441\u0442\u0432\u0438\u0435 \u0441 \u0441\u0435\u0441\u0441\u0438\u0435\u0439",
|
|
6413
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}`,
|
|
@@ -6495,6 +6666,9 @@ var ru = {
|
|
|
6495
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.",
|
|
6496
6667
|
loggedOut: (provider) => `\u0412\u044B\u043F\u043E\u043B\u043D\u0435\u043D \u0432\u044B\u0445\u043E\u0434 \u0438\u0437 \xAB${provider}\xBB.`,
|
|
6497
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.`,
|
|
6498
6672
|
promptUsage: "\u0418\u0441\u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u043D\u0438\u0435: /prompt save <\u0438\u043C\u044F>",
|
|
6499
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.",
|
|
6500
6674
|
promptSaved: (name) => `\u041F\u0440\u043E\u043C\u0442 \xAB${name}\xBB \u0441\u043E\u0445\u0440\u0430\u043D\u0451\u043D`,
|
|
@@ -6526,6 +6700,7 @@ var ru = {
|
|
|
6526
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)",
|
|
6527
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",
|
|
6528
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",
|
|
6529
6704
|
effort: "\u0433\u043B\u0443\u0431\u0438\u043D\u0430 \u0440\u0430\u0437\u043C\u044B\u0448\u043B\u0435\u043D\u0438\u0439",
|
|
6530
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",
|
|
6531
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)",
|
|
@@ -6563,43 +6738,9 @@ var DICTIONARIES = { en, ru };
|
|
|
6563
6738
|
function stringsFor(lang) {
|
|
6564
6739
|
return DICTIONARIES[lang ?? "en"];
|
|
6565
6740
|
}
|
|
6566
|
-
var StringsContext =
|
|
6741
|
+
var StringsContext = createContext2(en);
|
|
6567
6742
|
function useStrings() {
|
|
6568
|
-
return
|
|
6569
|
-
}
|
|
6570
|
-
|
|
6571
|
-
// src/ui/theme.ts
|
|
6572
|
-
import { createContext as createContext2, useContext as useContext2 } from "react";
|
|
6573
|
-
var PRESETS = {
|
|
6574
|
-
purple: "#a78bfa",
|
|
6575
|
-
violet: "#8b5cf6",
|
|
6576
|
-
blue: "#60a5fa",
|
|
6577
|
-
cyan: "#22d3ee",
|
|
6578
|
-
green: "#4ade80",
|
|
6579
|
-
orange: "#fb923c",
|
|
6580
|
-
pink: "#f472b6",
|
|
6581
|
-
red: "#f87171",
|
|
6582
|
-
yellow: "#fbbf24",
|
|
6583
|
-
white: "#e5e7eb"
|
|
6584
|
-
};
|
|
6585
|
-
var DEFAULT_ACCENT = PRESETS.purple;
|
|
6586
|
-
function resolveAccent(value) {
|
|
6587
|
-
if (!value) return DEFAULT_ACCENT;
|
|
6588
|
-
const preset = PRESETS[value.toLowerCase()];
|
|
6589
|
-
if (preset) return preset;
|
|
6590
|
-
return /^#[0-9a-f]{6}$/i.test(value) ? value : DEFAULT_ACCENT;
|
|
6591
|
-
}
|
|
6592
|
-
function makeTheme(accent) {
|
|
6593
|
-
return {
|
|
6594
|
-
accent: resolveAccent(accent),
|
|
6595
|
-
ok: PRESETS.green,
|
|
6596
|
-
warn: PRESETS.yellow,
|
|
6597
|
-
error: PRESETS.red
|
|
6598
|
-
};
|
|
6599
|
-
}
|
|
6600
|
-
var ThemeContext = createContext2(makeTheme(void 0));
|
|
6601
|
-
function useTheme() {
|
|
6602
|
-
return useContext2(ThemeContext);
|
|
6743
|
+
return useContext2(StringsContext);
|
|
6603
6744
|
}
|
|
6604
6745
|
|
|
6605
6746
|
// src/ui/components/Confirm.tsx
|
|
@@ -7376,7 +7517,10 @@ function StatusBar({ status }) {
|
|
|
7376
7517
|
}
|
|
7377
7518
|
)
|
|
7378
7519
|
] }),
|
|
7379
|
-
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)) })
|
|
7380
7524
|
]
|
|
7381
7525
|
}
|
|
7382
7526
|
);
|
|
@@ -7386,11 +7530,7 @@ function Segments({ items }) {
|
|
|
7386
7530
|
const elements = [];
|
|
7387
7531
|
if (index > 0) {
|
|
7388
7532
|
elements.push(
|
|
7389
|
-
/* @__PURE__ */
|
|
7390
|
-
" ",
|
|
7391
|
-
"\xB7",
|
|
7392
|
-
" "
|
|
7393
|
-
] }, `sep-${index}`)
|
|
7533
|
+
/* @__PURE__ */ jsx9(Box9, { marginTop: 1, children: /* @__PURE__ */ jsx9(Text9, { dimColor: true, children: " \xB7 " }) }, `sep-${index}`)
|
|
7394
7534
|
);
|
|
7395
7535
|
}
|
|
7396
7536
|
elements.push(/* @__PURE__ */ jsx9(Fragment2, { children: item }, `item-${index}`));
|
|
@@ -7476,7 +7616,7 @@ import Spinner2 from "ink-spinner";
|
|
|
7476
7616
|
// src/ui/markdown.tsx
|
|
7477
7617
|
import { Box as Box10, Text as Text10 } from "ink";
|
|
7478
7618
|
import { useMemo as useMemo2 } from "react";
|
|
7479
|
-
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";
|
|
7480
7620
|
function Markdown({ children }) {
|
|
7481
7621
|
const blocks = useMemo2(() => extractBlocks(children), [children]);
|
|
7482
7622
|
return /* @__PURE__ */ jsx10(Box10, { flexDirection: "column", children: blocks.map((block, i) => /* @__PURE__ */ jsx10(BlockView, { block }, i)) });
|
|
@@ -7489,38 +7629,108 @@ function BlockView({ block }) {
|
|
|
7489
7629
|
return /* @__PURE__ */ jsx10(Box10, { marginTop: block.level === 1 ? 1 : 0, children: /* @__PURE__ */ jsx10(Text10, { bold: true, children: truncateBySize(block.text ?? "", size) }) });
|
|
7490
7630
|
}
|
|
7491
7631
|
case "blockquote":
|
|
7492
|
-
return /* @__PURE__ */ jsx10(Box10, { marginLeft: 2, children: /* @__PURE__ */ jsxs10(
|
|
7493
|
-
"\u2502 ",
|
|
7494
|
-
|
|
7495
|
-
] }) });
|
|
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)) });
|
|
7496
7636
|
case "ul":
|
|
7497
7637
|
return /* @__PURE__ */ jsx10(Box10, { flexDirection: "column", children: (block.items ?? []).map((item, i) => /* @__PURE__ */ jsxs10(Box10, { children: [
|
|
7498
7638
|
/* @__PURE__ */ jsx10(Text10, { dimColor: true, children: "\u2022 " }),
|
|
7499
|
-
/* @__PURE__ */ jsx10(
|
|
7639
|
+
/* @__PURE__ */ jsx10(Box10, { marginLeft: 2, children: /* @__PURE__ */ jsx10(BlockView, { block: item }) })
|
|
7500
7640
|
] }, i)) });
|
|
7501
7641
|
case "ol":
|
|
7502
7642
|
return /* @__PURE__ */ jsx10(Box10, { flexDirection: "column", children: (block.items ?? []).map((item, i) => /* @__PURE__ */ jsxs10(Box10, { children: [
|
|
7503
7643
|
/* @__PURE__ */ jsx10(Text10, { dimColor: true, children: `${i + 1}. ` }),
|
|
7504
|
-
/* @__PURE__ */ jsx10(
|
|
7644
|
+
/* @__PURE__ */ jsx10(Box10, { marginLeft: 2, children: /* @__PURE__ */ jsx10(BlockView, { block: item }) })
|
|
7505
7645
|
] }, i)) });
|
|
7506
7646
|
case "paragraph":
|
|
7507
7647
|
default:
|
|
7508
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)" });
|
|
7509
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 });
|
|
7510
7712
|
}
|
|
7713
|
+
var CHARS_PER_FONT_LEVEL = 3;
|
|
7511
7714
|
function truncateBySize(text, size) {
|
|
7512
|
-
|
|
7715
|
+
const maxLen = size * CHARS_PER_FONT_LEVEL;
|
|
7716
|
+
return text.length > maxLen ? text.slice(0, maxLen) + "\u2026" : text;
|
|
7513
7717
|
}
|
|
7514
7718
|
var HEADING_RE = /^(#{1,6})\s+(.*)$/;
|
|
7515
7719
|
var UL_RE = /^([-*+])\s+(.*)$/;
|
|
7516
7720
|
var OL_RE = /^(\d+)\.\s+(.*)$/;
|
|
7517
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*$/;
|
|
7518
7726
|
function extractBlocks(src) {
|
|
7519
7727
|
const lines = src.replace(/\r\n/g, "\n").split("\n");
|
|
7520
7728
|
const blocks = [];
|
|
7521
7729
|
let paragraph = [];
|
|
7522
7730
|
let list = null;
|
|
7523
7731
|
let quote = [];
|
|
7732
|
+
let code = null;
|
|
7733
|
+
let table = null;
|
|
7524
7734
|
const flushParagraph = () => {
|
|
7525
7735
|
if (paragraph.length) {
|
|
7526
7736
|
blocks.push({ type: "paragraph", text: paragraph.join(" ").trim() });
|
|
@@ -7528,7 +7738,7 @@ function extractBlocks(src) {
|
|
|
7528
7738
|
}
|
|
7529
7739
|
};
|
|
7530
7740
|
const flushList = () => {
|
|
7531
|
-
if (list) {
|
|
7741
|
+
if (list && list.items.length) {
|
|
7532
7742
|
blocks.push({ type: list.ordered ? "ol" : "ul", items: list.items });
|
|
7533
7743
|
list = null;
|
|
7534
7744
|
}
|
|
@@ -7539,100 +7749,271 @@ function extractBlocks(src) {
|
|
|
7539
7749
|
quote = [];
|
|
7540
7750
|
}
|
|
7541
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
|
+
};
|
|
7542
7758
|
const flushAll = () => {
|
|
7543
7759
|
flushParagraph();
|
|
7544
7760
|
flushList();
|
|
7545
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 };
|
|
7546
7785
|
};
|
|
7547
|
-
for (
|
|
7786
|
+
for (let i = 0; i < lines.length; i++) {
|
|
7787
|
+
const line = lines[i];
|
|
7548
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
|
+
}
|
|
7549
7805
|
if (trimmed === "") {
|
|
7806
|
+
flushTable();
|
|
7807
|
+
flushAll();
|
|
7808
|
+
continue;
|
|
7809
|
+
}
|
|
7810
|
+
const hrMatch = trimmed.match(HR_RE);
|
|
7811
|
+
if (hrMatch) {
|
|
7812
|
+
flushTable();
|
|
7550
7813
|
flushAll();
|
|
7814
|
+
blocks.push({ type: "hr" });
|
|
7551
7815
|
continue;
|
|
7552
7816
|
}
|
|
7553
7817
|
const heading = trimmed.match(HEADING_RE);
|
|
7554
7818
|
if (heading) {
|
|
7819
|
+
flushTable();
|
|
7555
7820
|
flushAll();
|
|
7556
7821
|
blocks.push({ type: "heading", level: heading[1].length, text: heading[2].trim() });
|
|
7557
7822
|
continue;
|
|
7558
7823
|
}
|
|
7559
7824
|
const quoteMatch = trimmed.match(QUOTE_RE);
|
|
7560
7825
|
if (quoteMatch) {
|
|
7826
|
+
flushTable();
|
|
7561
7827
|
flushParagraph();
|
|
7562
7828
|
flushList();
|
|
7829
|
+
flushCode();
|
|
7563
7830
|
quote.push(quoteMatch[1]);
|
|
7564
7831
|
continue;
|
|
7565
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
|
+
}
|
|
7566
7848
|
const ulMatch = trimmed.match(UL_RE);
|
|
7567
7849
|
if (ulMatch) {
|
|
7850
|
+
flushTable();
|
|
7568
7851
|
flushParagraph();
|
|
7569
7852
|
flushQuote();
|
|
7570
|
-
|
|
7853
|
+
flushCode();
|
|
7854
|
+
if (!list || list.ordered || indent !== list.indent) {
|
|
7571
7855
|
flushList();
|
|
7572
|
-
list = { ordered: false, items: [] };
|
|
7856
|
+
list = { ordered: false, items: [], indent };
|
|
7573
7857
|
}
|
|
7574
|
-
list.items.push(ulMatch[2]
|
|
7858
|
+
list.items.push(parseListItem(ulMatch[2], indent));
|
|
7575
7859
|
continue;
|
|
7576
7860
|
}
|
|
7577
7861
|
const olMatch = trimmed.match(OL_RE);
|
|
7578
7862
|
if (olMatch) {
|
|
7863
|
+
flushTable();
|
|
7579
7864
|
flushParagraph();
|
|
7580
7865
|
flushQuote();
|
|
7581
|
-
|
|
7866
|
+
flushCode();
|
|
7867
|
+
if (!list || !list.ordered || indent !== list.indent) {
|
|
7582
7868
|
flushList();
|
|
7583
|
-
list = { ordered: true, items: [] };
|
|
7869
|
+
list = { ordered: true, items: [], indent };
|
|
7584
7870
|
}
|
|
7585
|
-
list.items.push(olMatch[2]
|
|
7871
|
+
list.items.push(parseListItem(olMatch[2], indent));
|
|
7586
7872
|
continue;
|
|
7587
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();
|
|
7588
7937
|
flushList();
|
|
7589
7938
|
flushQuote();
|
|
7939
|
+
flushCode();
|
|
7590
7940
|
paragraph.push(trimmed);
|
|
7591
7941
|
}
|
|
7942
|
+
flushTable();
|
|
7592
7943
|
flushAll();
|
|
7593
7944
|
return blocks;
|
|
7594
7945
|
}
|
|
7595
|
-
|
|
7946
|
+
var MAX_INLINE_DEPTH = 10;
|
|
7947
|
+
function inline(text, depth = 0) {
|
|
7948
|
+
if (depth > MAX_INLINE_DEPTH) {
|
|
7949
|
+
return text;
|
|
7950
|
+
}
|
|
7596
7951
|
const nodes = [];
|
|
7597
7952
|
let rest = text;
|
|
7598
7953
|
let key = 0;
|
|
7599
|
-
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
|
+
];
|
|
7600
7995
|
while (rest.length > 0) {
|
|
7601
|
-
|
|
7602
|
-
|
|
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) {
|
|
7603
8004
|
nodes.push(rest);
|
|
7604
8005
|
break;
|
|
7605
8006
|
}
|
|
7606
|
-
const idx =
|
|
8007
|
+
const idx = best.match.index ?? 0;
|
|
7607
8008
|
if (idx > 0) nodes.push(rest.slice(0, idx));
|
|
7608
|
-
|
|
7609
|
-
|
|
7610
|
-
/* @__PURE__ */ jsx10(Text10, { bold: true, color: "cyan", children: m[2] }, key++)
|
|
7611
|
-
);
|
|
7612
|
-
} else if (m[3]) {
|
|
7613
|
-
nodes.push(
|
|
7614
|
-
/* @__PURE__ */ jsx10(Text10, { bold: true, children: inline(m[4]) }, key++)
|
|
7615
|
-
);
|
|
7616
|
-
} else if (m[5]) {
|
|
7617
|
-
nodes.push(
|
|
7618
|
-
/* @__PURE__ */ jsx10(Text10, { strikethrough: true, children: m[6] }, key++)
|
|
7619
|
-
);
|
|
7620
|
-
} else if (m[7]) {
|
|
7621
|
-
nodes.push(
|
|
7622
|
-
/* @__PURE__ */ jsx10(Text10, { italic: true, children: inline(m[8]) }, key++)
|
|
7623
|
-
);
|
|
7624
|
-
} else if (m[9]) {
|
|
7625
|
-
nodes.push(
|
|
7626
|
-
/* @__PURE__ */ jsx10(Text10, { bold: true, children: inline(m[10]) }, key++)
|
|
7627
|
-
);
|
|
7628
|
-
}
|
|
7629
|
-
rest = rest.slice(idx + m[0].length);
|
|
8009
|
+
nodes.push(best.handler(best.match));
|
|
8010
|
+
rest = rest.slice(idx + best.match[0].length);
|
|
7630
8011
|
}
|
|
7631
8012
|
return nodes.length === 1 ? nodes[0] : nodes;
|
|
7632
8013
|
}
|
|
7633
8014
|
|
|
7634
8015
|
// src/ui/components/Transcript.tsx
|
|
7635
|
-
import { Fragment as
|
|
8016
|
+
import { Fragment as Fragment5, jsx as jsx11, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
7636
8017
|
var HEADER = { kind: "header" };
|
|
7637
8018
|
var Transcript = memo2(function Transcript2({ bubbles, workspace }) {
|
|
7638
8019
|
const liveAt = bubbles.findLastIndex(
|
|
@@ -7699,7 +8080,7 @@ function ToolView({ bubble }) {
|
|
|
7699
8080
|
mark,
|
|
7700
8081
|
" ",
|
|
7701
8082
|
bubble.summary,
|
|
7702
|
-
hasChanges && /* @__PURE__ */ jsxs11(
|
|
8083
|
+
hasChanges && /* @__PURE__ */ jsxs11(Fragment5, { children: [
|
|
7703
8084
|
diff.added > 0 && /* @__PURE__ */ jsx11(Text11, { color: theme.ok, bold: true, children: ` +${diff.added}` }),
|
|
7704
8085
|
diff.removed > 0 && /* @__PURE__ */ jsx11(Text11, { color: theme.error, bold: true, children: ` -${diff.removed}` })
|
|
7705
8086
|
] })
|
|
@@ -8078,7 +8459,7 @@ function App({
|
|
|
8078
8459
|
return () => clearInterval(timer);
|
|
8079
8460
|
}, [turnStart]);
|
|
8080
8461
|
useEffect2(() => {
|
|
8081
|
-
if (process.platform === "darwin") return;
|
|
8462
|
+
if (process.platform === "darwin" || process.platform === "win32") return;
|
|
8082
8463
|
let last = "";
|
|
8083
8464
|
let cancelling = false;
|
|
8084
8465
|
let giveUp = false;
|
|
@@ -8152,6 +8533,12 @@ function App({
|
|
|
8152
8533
|
}),
|
|
8153
8534
|
[]
|
|
8154
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
|
+
);
|
|
8155
8542
|
const runAgent = useCallback(async () => {
|
|
8156
8543
|
const controller = new AbortController();
|
|
8157
8544
|
abort.current = controller;
|
|
@@ -8469,6 +8856,35 @@ ${strings.modelSet(result.nextModel)}` : ""}`;
|
|
|
8469
8856
|
forceRender((n) => n + 1);
|
|
8470
8857
|
return;
|
|
8471
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
|
+
}
|
|
8472
8888
|
case "lang": {
|
|
8473
8889
|
const choice = await pick(
|
|
8474
8890
|
strings.titleLang,
|
|
@@ -9202,6 +9618,21 @@ ${strings.configAt(runtime.configPath())}`);
|
|
|
9202
9618
|
}
|
|
9203
9619
|
}
|
|
9204
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
|
+
),
|
|
9205
9636
|
overlay.kind === "none" && /* @__PURE__ */ jsx12(
|
|
9206
9637
|
PromptInput,
|
|
9207
9638
|
{
|
|
@@ -9242,6 +9673,34 @@ ${strings.configAt(runtime.configPath())}`);
|
|
|
9242
9673
|
] })
|
|
9243
9674
|
);
|
|
9244
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
|
+
}
|
|
9245
9704
|
function slashNeedsIdle(line) {
|
|
9246
9705
|
const [command = "", subcommand = ""] = line.slice(1).trim().toLowerCase().split(/\s+/);
|
|
9247
9706
|
return NEEDS_IDLE.has(command) || command === "mcp" && (subcommand === "add" || subcommand === "delete" || subcommand === "enable" || subcommand === "disable");
|
|
@@ -9323,6 +9782,13 @@ function copyToClipboard(text) {
|
|
|
9323
9782
|
}
|
|
9324
9783
|
return;
|
|
9325
9784
|
}
|
|
9785
|
+
if (process.platform === "win32") {
|
|
9786
|
+
try {
|
|
9787
|
+
run("clip", []);
|
|
9788
|
+
} catch {
|
|
9789
|
+
}
|
|
9790
|
+
return;
|
|
9791
|
+
}
|
|
9326
9792
|
const candidates = [
|
|
9327
9793
|
["wl-copy", []],
|
|
9328
9794
|
["xclip", ["-selection", "clipboard"]],
|