jinzd-ai-cli 0.4.238 → 0.4.239
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/dist/{chunk-NAPAMAFY.js → chunk-UEFVKTAF.js} +56 -1
- package/dist/{doctor-cli-E5MBY7CZ.js → doctor-cli-Q3XR5DQT.js} +1 -1
- package/dist/electron-server.js +72 -1
- package/dist/index.js +74 -18
- package/dist/{server-7OUD4SE4.js → server-DWQTKBCQ.js} +19 -2
- package/dist/web/client/app.js +3338 -3334
- package/package.json +1 -1
|
@@ -509,6 +509,48 @@ function formatBytes(n) {
|
|
|
509
509
|
if (n < 1024 * 1024 * 1024) return `${(n / 1024 / 1024).toFixed(1)} MB`;
|
|
510
510
|
return `${(n / 1024 / 1024 / 1024).toFixed(2)} GB`;
|
|
511
511
|
}
|
|
512
|
+
function buildActionSummary(report) {
|
|
513
|
+
const problems = [];
|
|
514
|
+
const steps = [];
|
|
515
|
+
const configuredProviders = report.providers.filter((p) => p.configured).length;
|
|
516
|
+
if (configuredProviders === 0) {
|
|
517
|
+
problems.push("No API keys are configured.");
|
|
518
|
+
steps.push("Run aicli config and add a provider key, or configure Ollama for local use.");
|
|
519
|
+
}
|
|
520
|
+
if (report.npmCheck?.status === "outdated" && report.npmCheck.latest) {
|
|
521
|
+
problems.push(`Installed version ${report.version} is behind npm ${report.npmCheck.latest}.`);
|
|
522
|
+
steps.push("Upgrade with npm i -g jinzd-ai-cli@latest, then run aicli doctor again.");
|
|
523
|
+
}
|
|
524
|
+
if (report.context.skipped.length > 0) {
|
|
525
|
+
const first = report.context.skipped[0];
|
|
526
|
+
problems.push(`Context file skipped: ${first.displayPath} (${first.reason}).`);
|
|
527
|
+
steps.push("Fix or shrink the skipped context file, or adjust the context settings in config.");
|
|
528
|
+
}
|
|
529
|
+
if (report.hooks.pendingTrust > 0) {
|
|
530
|
+
problems.push(`${report.hooks.pendingTrust} project hook(s) are pending trust.`);
|
|
531
|
+
steps.push("Review hooks with /hooks list, then trust only the expected project hooks.");
|
|
532
|
+
}
|
|
533
|
+
if (report.plugins.invalid > 0 || report.plugins.untrusted > 0) {
|
|
534
|
+
problems.push(`Plugin attention needed: ${report.plugins.invalid} invalid, ${report.plugins.untrusted} untrusted.`);
|
|
535
|
+
steps.push("Inspect plugins with /plugin list and /plugin inspect <name>.");
|
|
536
|
+
}
|
|
537
|
+
const disconnected = report.mcp.statuses.filter((s) => !s.connected);
|
|
538
|
+
if (disconnected.length > 0) {
|
|
539
|
+
problems.push(`${disconnected.length} MCP server(s) are disconnected.`);
|
|
540
|
+
steps.push("Run /mcp reconnect; inspect server command/env if it stays disconnected.");
|
|
541
|
+
}
|
|
542
|
+
if (report.recentCrashes.length > 0) {
|
|
543
|
+
problems.push(`${report.recentCrashes.length} recent crash log(s) found.`);
|
|
544
|
+
steps.push("Open the newest crash log path shown below and include it in a bug report if reproducible.");
|
|
545
|
+
}
|
|
546
|
+
const total = report.toolStats.totalCalls;
|
|
547
|
+
const failures = report.toolStats.totalFailures;
|
|
548
|
+
if (total >= 10 && failures / total >= 0.2) {
|
|
549
|
+
problems.push(`Tool failure rate is ${(failures * 100 / total).toFixed(1)}% (${failures}/${total}).`);
|
|
550
|
+
steps.push("Check the Top failing tools section and narrow permissions or command scope.");
|
|
551
|
+
}
|
|
552
|
+
return { problems: problems.slice(0, 3), steps: steps.slice(0, 3) };
|
|
553
|
+
}
|
|
512
554
|
function formatDoctorReport(report, ansi = true) {
|
|
513
555
|
const wrap = (code, s) => ansi ? `${code}${s}\x1B[0m` : s;
|
|
514
556
|
const B = (s) => wrap("\x1B[1m", s);
|
|
@@ -523,7 +565,7 @@ function formatDoctorReport(report, ansi = true) {
|
|
|
523
565
|
const nc = report.npmCheck;
|
|
524
566
|
let versionLine = ` version: ${report.version}`;
|
|
525
567
|
if (nc?.status === "up-to-date") versionLine += ` ${D("(latest on npm)")}`;
|
|
526
|
-
else if (nc?.status === "outdated" && nc.latest) versionLine += ` ${Y(
|
|
568
|
+
else if (nc?.status === "outdated" && nc.latest) versionLine += ` ${Y(`update available: ${nc.latest}`)} ${D("(npm i -g jinzd-ai-cli@latest)")}`;
|
|
527
569
|
else if (nc?.status === "skipped") versionLine += ` ${D(`(npm check skipped: ${nc.reason ?? "unknown"})`)}`;
|
|
528
570
|
out.push(versionLine);
|
|
529
571
|
out.push(` node: ${report.node}`);
|
|
@@ -532,6 +574,19 @@ function formatDoctorReport(report, ansi = true) {
|
|
|
532
574
|
out.push(` project: ${report.projectRoot}`);
|
|
533
575
|
out.push(` config: ${report.configDir}`);
|
|
534
576
|
out.push("");
|
|
577
|
+
const actionSummary = buildActionSummary(report);
|
|
578
|
+
if (actionSummary.problems.length > 0) {
|
|
579
|
+
out.push(B("Problems:"));
|
|
580
|
+
for (const p of actionSummary.problems) out.push(` ${Y("!")} ${p}`);
|
|
581
|
+
out.push("");
|
|
582
|
+
out.push(B("Suggested next steps:"));
|
|
583
|
+
actionSummary.steps.forEach((step, i) => out.push(` ${i + 1}. ${step}`));
|
|
584
|
+
out.push("");
|
|
585
|
+
} else {
|
|
586
|
+
out.push(B("Problems:"));
|
|
587
|
+
out.push(` ${G("OK")} No high-priority issues detected.`);
|
|
588
|
+
out.push("");
|
|
589
|
+
}
|
|
535
590
|
out.push(B("API Keys:"));
|
|
536
591
|
for (const p of report.providers) {
|
|
537
592
|
out.push(` ${p.configured ? G("\u2713") : D("\u25CB")} ${p.id.padEnd(14)} ${p.configured ? G("configured") : D("not configured")}`);
|
package/dist/electron-server.js
CHANGED
|
@@ -15087,6 +15087,48 @@ function formatBytes(n) {
|
|
|
15087
15087
|
if (n < 1024 * 1024 * 1024) return `${(n / 1024 / 1024).toFixed(1)} MB`;
|
|
15088
15088
|
return `${(n / 1024 / 1024 / 1024).toFixed(2)} GB`;
|
|
15089
15089
|
}
|
|
15090
|
+
function buildActionSummary(report) {
|
|
15091
|
+
const problems = [];
|
|
15092
|
+
const steps = [];
|
|
15093
|
+
const configuredProviders = report.providers.filter((p) => p.configured).length;
|
|
15094
|
+
if (configuredProviders === 0) {
|
|
15095
|
+
problems.push("No API keys are configured.");
|
|
15096
|
+
steps.push("Run aicli config and add a provider key, or configure Ollama for local use.");
|
|
15097
|
+
}
|
|
15098
|
+
if (report.npmCheck?.status === "outdated" && report.npmCheck.latest) {
|
|
15099
|
+
problems.push(`Installed version ${report.version} is behind npm ${report.npmCheck.latest}.`);
|
|
15100
|
+
steps.push("Upgrade with npm i -g jinzd-ai-cli@latest, then run aicli doctor again.");
|
|
15101
|
+
}
|
|
15102
|
+
if (report.context.skipped.length > 0) {
|
|
15103
|
+
const first = report.context.skipped[0];
|
|
15104
|
+
problems.push(`Context file skipped: ${first.displayPath} (${first.reason}).`);
|
|
15105
|
+
steps.push("Fix or shrink the skipped context file, or adjust the context settings in config.");
|
|
15106
|
+
}
|
|
15107
|
+
if (report.hooks.pendingTrust > 0) {
|
|
15108
|
+
problems.push(`${report.hooks.pendingTrust} project hook(s) are pending trust.`);
|
|
15109
|
+
steps.push("Review hooks with /hooks list, then trust only the expected project hooks.");
|
|
15110
|
+
}
|
|
15111
|
+
if (report.plugins.invalid > 0 || report.plugins.untrusted > 0) {
|
|
15112
|
+
problems.push(`Plugin attention needed: ${report.plugins.invalid} invalid, ${report.plugins.untrusted} untrusted.`);
|
|
15113
|
+
steps.push("Inspect plugins with /plugin list and /plugin inspect <name>.");
|
|
15114
|
+
}
|
|
15115
|
+
const disconnected = report.mcp.statuses.filter((s) => !s.connected);
|
|
15116
|
+
if (disconnected.length > 0) {
|
|
15117
|
+
problems.push(`${disconnected.length} MCP server(s) are disconnected.`);
|
|
15118
|
+
steps.push("Run /mcp reconnect; inspect server command/env if it stays disconnected.");
|
|
15119
|
+
}
|
|
15120
|
+
if (report.recentCrashes.length > 0) {
|
|
15121
|
+
problems.push(`${report.recentCrashes.length} recent crash log(s) found.`);
|
|
15122
|
+
steps.push("Open the newest crash log path shown below and include it in a bug report if reproducible.");
|
|
15123
|
+
}
|
|
15124
|
+
const total = report.toolStats.totalCalls;
|
|
15125
|
+
const failures = report.toolStats.totalFailures;
|
|
15126
|
+
if (total >= 10 && failures / total >= 0.2) {
|
|
15127
|
+
problems.push(`Tool failure rate is ${(failures * 100 / total).toFixed(1)}% (${failures}/${total}).`);
|
|
15128
|
+
steps.push("Check the Top failing tools section and narrow permissions or command scope.");
|
|
15129
|
+
}
|
|
15130
|
+
return { problems: problems.slice(0, 3), steps: steps.slice(0, 3) };
|
|
15131
|
+
}
|
|
15090
15132
|
function formatDoctorReport(report, ansi = true) {
|
|
15091
15133
|
const wrap = (code, s) => ansi ? `${code}${s}\x1B[0m` : s;
|
|
15092
15134
|
const B = (s) => wrap("\x1B[1m", s);
|
|
@@ -15101,7 +15143,7 @@ function formatDoctorReport(report, ansi = true) {
|
|
|
15101
15143
|
const nc = report.npmCheck;
|
|
15102
15144
|
let versionLine = ` version: ${report.version}`;
|
|
15103
15145
|
if (nc?.status === "up-to-date") versionLine += ` ${D("(latest on npm)")}`;
|
|
15104
|
-
else if (nc?.status === "outdated" && nc.latest) versionLine += ` ${Y(
|
|
15146
|
+
else if (nc?.status === "outdated" && nc.latest) versionLine += ` ${Y(`update available: ${nc.latest}`)} ${D("(npm i -g jinzd-ai-cli@latest)")}`;
|
|
15105
15147
|
else if (nc?.status === "skipped") versionLine += ` ${D(`(npm check skipped: ${nc.reason ?? "unknown"})`)}`;
|
|
15106
15148
|
out.push(versionLine);
|
|
15107
15149
|
out.push(` node: ${report.node}`);
|
|
@@ -15110,6 +15152,19 @@ function formatDoctorReport(report, ansi = true) {
|
|
|
15110
15152
|
out.push(` project: ${report.projectRoot}`);
|
|
15111
15153
|
out.push(` config: ${report.configDir}`);
|
|
15112
15154
|
out.push("");
|
|
15155
|
+
const actionSummary = buildActionSummary(report);
|
|
15156
|
+
if (actionSummary.problems.length > 0) {
|
|
15157
|
+
out.push(B("Problems:"));
|
|
15158
|
+
for (const p of actionSummary.problems) out.push(` ${Y("!")} ${p}`);
|
|
15159
|
+
out.push("");
|
|
15160
|
+
out.push(B("Suggested next steps:"));
|
|
15161
|
+
actionSummary.steps.forEach((step, i) => out.push(` ${i + 1}. ${step}`));
|
|
15162
|
+
out.push("");
|
|
15163
|
+
} else {
|
|
15164
|
+
out.push(B("Problems:"));
|
|
15165
|
+
out.push(` ${G("OK")} No high-priority issues detected.`);
|
|
15166
|
+
out.push("");
|
|
15167
|
+
}
|
|
15113
15168
|
out.push(B("API Keys:"));
|
|
15114
15169
|
for (const p of report.providers) {
|
|
15115
15170
|
out.push(` ${p.configured ? G("\u2713") : D("\u25CB")} ${p.id.padEnd(14)} ${p.configured ? G("configured") : D("not configured")}`);
|
|
@@ -15873,6 +15928,10 @@ async function handleMcp(_args, ctx) {
|
|
|
15873
15928
|
ctx.sendToolsList();
|
|
15874
15929
|
return;
|
|
15875
15930
|
}
|
|
15931
|
+
if (sub) {
|
|
15932
|
+
ctx.send({ type: "error", message: `The Web UI supports /mcp reconnect and status display only. Run this command in the terminal instead: aicli, then /mcp ${[sub, ..._args.slice(1)].join(" ")}` });
|
|
15933
|
+
return;
|
|
15934
|
+
}
|
|
15876
15935
|
const statuses = ctx.mcpManager.getStatus();
|
|
15877
15936
|
if (statuses.length === 0) {
|
|
15878
15937
|
ctx.send({ type: "info", message: "No MCP servers configured." });
|
|
@@ -16674,12 +16733,21 @@ var SessionHandler = class {
|
|
|
16674
16733
|
planMode = false;
|
|
16675
16734
|
runtimeThinking = null;
|
|
16676
16735
|
sessionTokenUsage = { inputTokens: 0, outputTokens: 0, cacheCreationTokens: 0, cacheReadTokens: 0 };
|
|
16736
|
+
unknownPricingWarnings = /* @__PURE__ */ new Set();
|
|
16677
16737
|
/** Accumulate a TokenUsage (with optional cache fields) into sessionTokenUsage. */
|
|
16678
16738
|
addWebSessionUsage(u) {
|
|
16679
16739
|
this.sessionTokenUsage.inputTokens += u.inputTokens;
|
|
16680
16740
|
this.sessionTokenUsage.outputTokens += u.outputTokens;
|
|
16681
16741
|
this.sessionTokenUsage.cacheCreationTokens += u.cacheCreationTokens ?? 0;
|
|
16682
16742
|
this.sessionTokenUsage.cacheReadTokens += u.cacheReadTokens ?? 0;
|
|
16743
|
+
const totalTokens = u.inputTokens + u.outputTokens + (u.cacheCreationTokens ?? 0) + (u.cacheReadTokens ?? 0);
|
|
16744
|
+
if (totalTokens > 0 && !getPricing(this.currentProvider, this.currentModel)) {
|
|
16745
|
+
const key = `${this.currentProvider}/${this.currentModel}`;
|
|
16746
|
+
if (!this.unknownPricingWarnings.has(key)) {
|
|
16747
|
+
this.unknownPricingWarnings.add(key);
|
|
16748
|
+
this.send({ type: "info", message: `Cost notice: pricing is unknown for ${key}. Tokens are tracked, but cost is excluded from totals. Run /cost or aicli usage to review.` });
|
|
16749
|
+
}
|
|
16750
|
+
}
|
|
16683
16751
|
}
|
|
16684
16752
|
resetWebSessionUsage() {
|
|
16685
16753
|
this.sessionTokenUsage = { inputTokens: 0, outputTokens: 0, cacheCreationTokens: 0, cacheReadTokens: 0 };
|
|
@@ -16783,6 +16851,7 @@ var SessionHandler = class {
|
|
|
16783
16851
|
models: p.info.models.map((m) => ({ id: m.id, name: m.displayName ?? m.id }))
|
|
16784
16852
|
}));
|
|
16785
16853
|
const costUsd = computeCost(this.currentProvider, this.currentModel, this.sessionTokenUsage);
|
|
16854
|
+
const costPricingUnknown = costUsd == null && this.sessionTokenUsage.inputTokens + this.sessionTokenUsage.outputTokens + this.sessionTokenUsage.cacheCreationTokens + this.sessionTokenUsage.cacheReadTokens > 0;
|
|
16786
16855
|
const sess = this.sessions.current;
|
|
16787
16856
|
const branches = sess ? sess.listBranches().map((b) => ({
|
|
16788
16857
|
id: b.id,
|
|
@@ -16798,12 +16867,14 @@ var SessionHandler = class {
|
|
|
16798
16867
|
model: this.currentModel,
|
|
16799
16868
|
sessionId: this.sessions.current?.id ?? "",
|
|
16800
16869
|
sessionTitle: this.sessions.current?.title ?? void 0,
|
|
16870
|
+
cwd: process.cwd(),
|
|
16801
16871
|
messageCount: this.sessions.current?.messages.length ?? 0,
|
|
16802
16872
|
planMode: this.planMode,
|
|
16803
16873
|
thinkingMode: this.runtimeThinking ?? false,
|
|
16804
16874
|
permissionProfile: this.config.get("defaultPermissionProfile") ?? "legacy",
|
|
16805
16875
|
tokenUsage: { ...this.sessionTokenUsage },
|
|
16806
16876
|
costUsd,
|
|
16877
|
+
costPricingUnknown,
|
|
16807
16878
|
providers: providerList,
|
|
16808
16879
|
branches,
|
|
16809
16880
|
activeBranchId: sess?.activeBranchId ?? "main",
|
package/dist/index.js
CHANGED
|
@@ -74,7 +74,7 @@ import {
|
|
|
74
74
|
formatDoctorReport,
|
|
75
75
|
loadContextFiles,
|
|
76
76
|
writeCrashLog
|
|
77
|
-
} from "./chunk-
|
|
77
|
+
} from "./chunk-UEFVKTAF.js";
|
|
78
78
|
import {
|
|
79
79
|
ProviderRegistry
|
|
80
80
|
} from "./chunk-QYQI7ZWK.js";
|
|
@@ -4289,6 +4289,10 @@ function maskKey(key) {
|
|
|
4289
4289
|
if (key.length <= 10) return "****";
|
|
4290
4290
|
return key.slice(0, 6) + "****" + key.slice(-4);
|
|
4291
4291
|
}
|
|
4292
|
+
function errorMessage(err) {
|
|
4293
|
+
if (err instanceof Error && err.message) return err.message;
|
|
4294
|
+
return String(err ?? "unknown error");
|
|
4295
|
+
}
|
|
4292
4296
|
var SetupWizard = class {
|
|
4293
4297
|
constructor(config) {
|
|
4294
4298
|
this.config = config;
|
|
@@ -4312,7 +4316,17 @@ var SetupWizard = class {
|
|
|
4312
4316
|
${greeting} Starting ai-cli...
|
|
4313
4317
|
`));
|
|
4314
4318
|
return true;
|
|
4315
|
-
} catch {
|
|
4319
|
+
} catch (err) {
|
|
4320
|
+
const message = errorMessage(err);
|
|
4321
|
+
const canceled = /user force closed|interrupted|cancel|aborted/i.test(message);
|
|
4322
|
+
if (canceled) {
|
|
4323
|
+
console.log(theme.warning("\nSetup canceled. Run aicli config to try again.\n"));
|
|
4324
|
+
} else {
|
|
4325
|
+
console.log(theme.error(`
|
|
4326
|
+
First-run setup failed: ${message}`));
|
|
4327
|
+
console.log(theme.dim(`Config directory: ${this.config.getConfigDir()}`));
|
|
4328
|
+
console.log(theme.dim("Run aicli config to retry, or aicli doctor to inspect provider/config health.\n"));
|
|
4329
|
+
}
|
|
4316
4330
|
return false;
|
|
4317
4331
|
}
|
|
4318
4332
|
}
|
|
@@ -4340,6 +4354,7 @@ ${greeting} Starting ai-cli...
|
|
|
4340
4354
|
{ value: "proxy", name: `Configure proxy (HTTP/HTTPS) ${proxyStatus}` },
|
|
4341
4355
|
{ value: "google", name: `Configure Google Search (API Key + CX) ${googleKeyStatus}` },
|
|
4342
4356
|
{ value: "behavior", name: `Behavior toggles (YOLO, MCP, ...) ${behaviorStatus}` },
|
|
4357
|
+
{ value: "preset", name: "Apply task preset (safe review, workspace coding, local-only, team web)" },
|
|
4343
4358
|
{ value: "done", name: "Done" }
|
|
4344
4359
|
]
|
|
4345
4360
|
});
|
|
@@ -4351,6 +4366,8 @@ ${greeting} Starting ai-cli...
|
|
|
4351
4366
|
await this.setupGoogleSearch();
|
|
4352
4367
|
} else if (action === "behavior") {
|
|
4353
4368
|
await this.setupBehavior();
|
|
4369
|
+
} else if (action === "preset") {
|
|
4370
|
+
await this.setupPreset();
|
|
4354
4371
|
} else if (action === "apikey") {
|
|
4355
4372
|
const choicesWithStatus = PROVIDERS.map((p) => {
|
|
4356
4373
|
if (NO_KEY_PROVIDERS.has(p.value)) {
|
|
@@ -4379,6 +4396,42 @@ ${greeting} Starting ai-cli...
|
|
|
4379
4396
|
}
|
|
4380
4397
|
}
|
|
4381
4398
|
}
|
|
4399
|
+
async setupPreset() {
|
|
4400
|
+
const preset = await select({
|
|
4401
|
+
message: "Choose a task-oriented preset:",
|
|
4402
|
+
choices: [
|
|
4403
|
+
{ value: "safe-review", name: "Safe review only - read-only tools, network confirm" },
|
|
4404
|
+
{ value: "workspace-coding", name: "Workspace coding - write in workspace with confirmations" },
|
|
4405
|
+
{ value: "local-ollama", name: "Local-only Ollama - local provider, external network denied" },
|
|
4406
|
+
{ value: "team-web", name: "Team Web UI - workspace coding plus auth reminder" }
|
|
4407
|
+
]
|
|
4408
|
+
});
|
|
4409
|
+
const currentNetwork = this.config.get("networkPolicy");
|
|
4410
|
+
if (preset === "safe-review") {
|
|
4411
|
+
this.config.set("defaultPermissionProfile", "read-only");
|
|
4412
|
+
this.config.set("alwaysYolo", false);
|
|
4413
|
+
this.config.set("networkPolicy", { ...currentNetwork, enabled: true, defaultAction: "confirm", tools: { ...currentNetwork.tools, shell: "confirm", web_fetch: "confirm", web_search: "confirm", google_search: "confirm", mcp: "confirm" } });
|
|
4414
|
+
console.log(theme.success("Preset applied: Safe review only. Writes require denial/confirmation according to the read-only profile."));
|
|
4415
|
+
} else if (preset === "workspace-coding") {
|
|
4416
|
+
this.config.set("defaultPermissionProfile", "workspace-write");
|
|
4417
|
+
this.config.set("alwaysYolo", false);
|
|
4418
|
+
this.config.set("networkPolicy", { ...currentNetwork, enabled: true, defaultAction: "confirm", tools: { ...currentNetwork.tools, shell: "confirm", web_fetch: "confirm", web_search: "confirm", google_search: "confirm", mcp: "confirm" } });
|
|
4419
|
+
console.log(theme.success("Preset applied: Workspace coding. File writes stay inside the workspace and sensitive actions still ask."));
|
|
4420
|
+
} else if (preset === "local-ollama") {
|
|
4421
|
+
this.config.set("defaultProvider", "ollama");
|
|
4422
|
+
this.config.set("defaultPermissionProfile", "workspace-write");
|
|
4423
|
+
this.config.set("alwaysYolo", false);
|
|
4424
|
+
this.config.set("networkPolicy", { ...currentNetwork, enabled: true, defaultAction: "deny", allowPrivateNetwork: true, tools: { ...currentNetwork.tools, shell: "confirm", web_fetch: "deny", web_search: "deny", google_search: "deny", mcp: "confirm" } });
|
|
4425
|
+
console.log(theme.success("Preset applied: Local-only Ollama. External web/search is denied; private network is allowed for local services."));
|
|
4426
|
+
} else {
|
|
4427
|
+
this.config.set("defaultPermissionProfile", "workspace-write");
|
|
4428
|
+
this.config.set("alwaysYolo", false);
|
|
4429
|
+
this.config.set("networkPolicy", { ...currentNetwork, enabled: true, defaultAction: "confirm", tools: { ...currentNetwork.tools, shell: "confirm", web_fetch: "confirm", web_search: "confirm", google_search: "confirm", mcp: "confirm" } });
|
|
4430
|
+
console.log(theme.warning("Preset applied: Team Web UI. Start Web with auth enabled and put it behind your normal TLS/reverse proxy boundary."));
|
|
4431
|
+
}
|
|
4432
|
+
this.config.save();
|
|
4433
|
+
console.log(theme.dim("Review changes with aicli doctor or aicli config.\n"));
|
|
4434
|
+
}
|
|
4382
4435
|
async setupProvider(providerId) {
|
|
4383
4436
|
if (NO_KEY_PROVIDERS.has(providerId)) {
|
|
4384
4437
|
await this.setupOllama();
|
|
@@ -4396,25 +4449,18 @@ Managing ${displayName} API Key`);
|
|
|
4396
4449
|
}
|
|
4397
4450
|
const choices = existingKey ? [
|
|
4398
4451
|
{ value: "keep", name: "Keep current key" },
|
|
4399
|
-
{ value: "
|
|
4452
|
+
{ value: "fingerprint", name: `Copy masked fingerprint (${maskKey(existingKey)})` },
|
|
4400
4453
|
{ value: "change", name: "Update key" }
|
|
4401
4454
|
] : [{ value: "change", name: "Enter key" }, { value: "skip", name: "Skip" }];
|
|
4402
4455
|
const action = await select({
|
|
4403
4456
|
message: "Action:",
|
|
4404
4457
|
choices
|
|
4405
4458
|
});
|
|
4406
|
-
if (action === "
|
|
4407
|
-
console.log(theme.
|
|
4408
|
-
|
|
4409
|
-
|
|
4410
|
-
|
|
4411
|
-
message: "Would you like to update this key?",
|
|
4412
|
-
choices: [
|
|
4413
|
-
{ value: "keep", name: "Keep current" },
|
|
4414
|
-
{ value: "change", name: "Update" }
|
|
4415
|
-
]
|
|
4416
|
-
});
|
|
4417
|
-
if (updateAfterShow !== "change") return;
|
|
4459
|
+
if (action === "fingerprint" && existingKey) {
|
|
4460
|
+
console.log(theme.dim(`
|
|
4461
|
+
Fingerprint: ${maskKey(existingKey)}`));
|
|
4462
|
+
console.log(theme.dim(" Full secrets are not printed. Use Update key to replace it.\n"));
|
|
4463
|
+
return;
|
|
4418
4464
|
} else if (action === "keep" || action === "skip") {
|
|
4419
4465
|
return;
|
|
4420
4466
|
}
|
|
@@ -5366,6 +5412,7 @@ var Repl = class {
|
|
|
5366
5412
|
contextLoadResult = null;
|
|
5367
5413
|
/** 本次会话累计 token 用量 */
|
|
5368
5414
|
sessionTokenUsage = { inputTokens: 0, outputTokens: 0, cacheCreationTokens: 0, cacheReadTokens: 0 };
|
|
5415
|
+
unknownPricingWarnings = /* @__PURE__ */ new Set();
|
|
5369
5416
|
/** Fold a single-request TokenUsage (with optional cache fields) into sessionTokenUsage + cost tracker.
|
|
5370
5417
|
* modelOverride lets the smart router attribute cost to the actually-used model
|
|
5371
5418
|
* when it differs from the UI-selected currentModel. */
|
|
@@ -5374,7 +5421,16 @@ var Repl = class {
|
|
|
5374
5421
|
this.sessionTokenUsage.outputTokens += u.outputTokens;
|
|
5375
5422
|
this.sessionTokenUsage.cacheCreationTokens += u.cacheCreationTokens ?? 0;
|
|
5376
5423
|
this.sessionTokenUsage.cacheReadTokens += u.cacheReadTokens ?? 0;
|
|
5377
|
-
|
|
5424
|
+
const effectiveModel = modelOverride ?? this.currentModel;
|
|
5425
|
+
const totalTokens = u.inputTokens + u.outputTokens + (u.cacheCreationTokens ?? 0) + (u.cacheReadTokens ?? 0);
|
|
5426
|
+
if (totalTokens > 0 && !getPricing(this.currentProvider, effectiveModel)) {
|
|
5427
|
+
const key = `${this.currentProvider}/${effectiveModel}`;
|
|
5428
|
+
if (!this.unknownPricingWarnings.has(key)) {
|
|
5429
|
+
this.unknownPricingWarnings.add(key);
|
|
5430
|
+
this.renderer.printInfo(`Cost notice: pricing is unknown for ${key}. Tokens are tracked, but cost is excluded from totals. Run aicli usage to review unknown-priced models.`);
|
|
5431
|
+
}
|
|
5432
|
+
}
|
|
5433
|
+
this.costTracker.addCost(this.currentProvider, effectiveModel, u);
|
|
5378
5434
|
}
|
|
5379
5435
|
/** 启动时检测到的 Git 分支(无 git 仓库时为 null) */
|
|
5380
5436
|
gitBranch = null;
|
|
@@ -7747,7 +7803,7 @@ program.command("web").description("Start Web UI server with browser-based chat
|
|
|
7747
7803
|
console.error("Error: Invalid port number. Must be between 1 and 65535.");
|
|
7748
7804
|
process.exit(1);
|
|
7749
7805
|
}
|
|
7750
|
-
const { startWebServer } = await import("./server-
|
|
7806
|
+
const { startWebServer } = await import("./server-DWQTKBCQ.js");
|
|
7751
7807
|
await startWebServer({ port, host: options.host });
|
|
7752
7808
|
});
|
|
7753
7809
|
program.command("user [action] [username]").description("Manage Web UI users (list | create <name> | delete <name> | reset-password <name> | logout-all <name> | migrate <name>)").action(async (action, username) => {
|
|
@@ -7918,7 +7974,7 @@ program.command("usage").description("Show token + cost usage grouped by provide
|
|
|
7918
7974
|
await runUsageCli(options);
|
|
7919
7975
|
});
|
|
7920
7976
|
program.command("doctor").description("Health check: API keys, config, MCP, recent crashes, tool usage, disk usage").option("--json", "Output as JSON (for scripting)").option("--reset-stats", "Reset accumulated tool usage statistics").action(async (options) => {
|
|
7921
|
-
const { runDoctorCli } = await import("./doctor-cli-
|
|
7977
|
+
const { runDoctorCli } = await import("./doctor-cli-Q3XR5DQT.js");
|
|
7922
7978
|
const argv = process.argv.slice(2);
|
|
7923
7979
|
await runDoctorCli({
|
|
7924
7980
|
json: !!options.json || argv.includes("--json"),
|
|
@@ -78,13 +78,14 @@ import {
|
|
|
78
78
|
} from "./chunk-JMP3LJC2.js";
|
|
79
79
|
import {
|
|
80
80
|
computeCost,
|
|
81
|
-
formatCost
|
|
81
|
+
formatCost,
|
|
82
|
+
getPricing
|
|
82
83
|
} from "./chunk-E44DTERW.js";
|
|
83
84
|
import {
|
|
84
85
|
buildDoctorReport,
|
|
85
86
|
formatDoctorReport,
|
|
86
87
|
loadContextFiles
|
|
87
|
-
} from "./chunk-
|
|
88
|
+
} from "./chunk-UEFVKTAF.js";
|
|
88
89
|
import {
|
|
89
90
|
ProviderRegistry
|
|
90
91
|
} from "./chunk-QYQI7ZWK.js";
|
|
@@ -1431,6 +1432,10 @@ async function handleMcp(_args, ctx) {
|
|
|
1431
1432
|
ctx.sendToolsList();
|
|
1432
1433
|
return;
|
|
1433
1434
|
}
|
|
1435
|
+
if (sub) {
|
|
1436
|
+
ctx.send({ type: "error", message: `The Web UI supports /mcp reconnect and status display only. Run this command in the terminal instead: aicli, then /mcp ${[sub, ..._args.slice(1)].join(" ")}` });
|
|
1437
|
+
return;
|
|
1438
|
+
}
|
|
1434
1439
|
const statuses = ctx.mcpManager.getStatus();
|
|
1435
1440
|
if (statuses.length === 0) {
|
|
1436
1441
|
ctx.send({ type: "info", message: "No MCP servers configured." });
|
|
@@ -2232,12 +2237,21 @@ var SessionHandler = class {
|
|
|
2232
2237
|
planMode = false;
|
|
2233
2238
|
runtimeThinking = null;
|
|
2234
2239
|
sessionTokenUsage = { inputTokens: 0, outputTokens: 0, cacheCreationTokens: 0, cacheReadTokens: 0 };
|
|
2240
|
+
unknownPricingWarnings = /* @__PURE__ */ new Set();
|
|
2235
2241
|
/** Accumulate a TokenUsage (with optional cache fields) into sessionTokenUsage. */
|
|
2236
2242
|
addWebSessionUsage(u) {
|
|
2237
2243
|
this.sessionTokenUsage.inputTokens += u.inputTokens;
|
|
2238
2244
|
this.sessionTokenUsage.outputTokens += u.outputTokens;
|
|
2239
2245
|
this.sessionTokenUsage.cacheCreationTokens += u.cacheCreationTokens ?? 0;
|
|
2240
2246
|
this.sessionTokenUsage.cacheReadTokens += u.cacheReadTokens ?? 0;
|
|
2247
|
+
const totalTokens = u.inputTokens + u.outputTokens + (u.cacheCreationTokens ?? 0) + (u.cacheReadTokens ?? 0);
|
|
2248
|
+
if (totalTokens > 0 && !getPricing(this.currentProvider, this.currentModel)) {
|
|
2249
|
+
const key = `${this.currentProvider}/${this.currentModel}`;
|
|
2250
|
+
if (!this.unknownPricingWarnings.has(key)) {
|
|
2251
|
+
this.unknownPricingWarnings.add(key);
|
|
2252
|
+
this.send({ type: "info", message: `Cost notice: pricing is unknown for ${key}. Tokens are tracked, but cost is excluded from totals. Run /cost or aicli usage to review.` });
|
|
2253
|
+
}
|
|
2254
|
+
}
|
|
2241
2255
|
}
|
|
2242
2256
|
resetWebSessionUsage() {
|
|
2243
2257
|
this.sessionTokenUsage = { inputTokens: 0, outputTokens: 0, cacheCreationTokens: 0, cacheReadTokens: 0 };
|
|
@@ -2341,6 +2355,7 @@ var SessionHandler = class {
|
|
|
2341
2355
|
models: p.info.models.map((m) => ({ id: m.id, name: m.displayName ?? m.id }))
|
|
2342
2356
|
}));
|
|
2343
2357
|
const costUsd = computeCost(this.currentProvider, this.currentModel, this.sessionTokenUsage);
|
|
2358
|
+
const costPricingUnknown = costUsd == null && this.sessionTokenUsage.inputTokens + this.sessionTokenUsage.outputTokens + this.sessionTokenUsage.cacheCreationTokens + this.sessionTokenUsage.cacheReadTokens > 0;
|
|
2344
2359
|
const sess = this.sessions.current;
|
|
2345
2360
|
const branches = sess ? sess.listBranches().map((b) => ({
|
|
2346
2361
|
id: b.id,
|
|
@@ -2356,12 +2371,14 @@ var SessionHandler = class {
|
|
|
2356
2371
|
model: this.currentModel,
|
|
2357
2372
|
sessionId: this.sessions.current?.id ?? "",
|
|
2358
2373
|
sessionTitle: this.sessions.current?.title ?? void 0,
|
|
2374
|
+
cwd: process.cwd(),
|
|
2359
2375
|
messageCount: this.sessions.current?.messages.length ?? 0,
|
|
2360
2376
|
planMode: this.planMode,
|
|
2361
2377
|
thinkingMode: this.runtimeThinking ?? false,
|
|
2362
2378
|
permissionProfile: this.config.get("defaultPermissionProfile") ?? "legacy",
|
|
2363
2379
|
tokenUsage: { ...this.sessionTokenUsage },
|
|
2364
2380
|
costUsd,
|
|
2381
|
+
costPricingUnknown,
|
|
2365
2382
|
providers: providerList,
|
|
2366
2383
|
branches,
|
|
2367
2384
|
activeBranchId: sess?.activeBranchId ?? "main",
|