jinzd-ai-cli 0.4.151 → 0.4.154

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.
Files changed (32) hide show
  1. package/dist/{batch-IPWV3DNK.js → batch-W57MV5OT.js} +2 -2
  2. package/dist/{chunk-WCBVNY6D.js → chunk-2IODI5TI.js} +1 -1
  3. package/dist/{chunk-YCF6S3P3.js → chunk-HVNEBTSF.js} +1 -1
  4. package/dist/{chunk-K76YV4EL.js → chunk-NP7WOVIH.js} +1 -1
  5. package/dist/{chunk-YJ2CUK5O.js → chunk-O6MLS5QO.js} +65 -24
  6. package/dist/{chunk-MEM43AE7.js → chunk-OSTMMSOV.js} +1 -1
  7. package/dist/{chunk-75YT3SZI.js → chunk-SH7NTECG.js} +1 -1
  8. package/dist/{chunk-ZQX2NCQK.js → chunk-UE26B3RO.js} +1 -1
  9. package/dist/{chunk-WXVG56NI.js → chunk-XWYWASPT.js} +3 -3
  10. package/dist/{chunk-MPMEYE43.js → chunk-ZAYDVWY4.js} +2 -2
  11. package/dist/{ci-YDMKYUQC.js → ci-JYZGZSMP.js} +2 -2
  12. package/dist/{constants-GRKJCSZF.js → constants-S4Y6A25E.js} +1 -1
  13. package/dist/{doctor-cli-R6ODVYFR.js → doctor-cli-FMTMDO2Z.js} +4 -4
  14. package/dist/electron-server.js +9 -5
  15. package/dist/{hub-3MKH3BMA.js → hub-OP7EWTQQ.js} +121 -5
  16. package/dist/{hub-server-AUMVPNU6.js → hub-server-OH7AYQIW.js} +1 -1
  17. package/dist/index.js +23 -17
  18. package/dist/{run-tests-HYELAYSL.js → run-tests-3QAZGHP2.js} +2 -2
  19. package/dist/{run-tests-XWEHN7DF.js → run-tests-4XNY7QB4.js} +1 -1
  20. package/dist/{server-32HM2KMX.js → server-UL42EXOA.js} +14 -10
  21. package/dist/{server-P7WY7VEX.js → server-W4TBZN6I.js} +4 -4
  22. package/dist/{task-orchestrator-EVJVR3HL.js → task-orchestrator-RLAZK5EB.js} +4 -4
  23. package/dist/web/client/app.js +2 -1
  24. package/dist/web/client/index.html +10 -6
  25. package/dist/web/client/sw.js +15 -26
  26. package/dist/web/client/vendor/daisyui-full.min.css +20 -0
  27. package/dist/web/client/vendor/github-dark.min.css +10 -0
  28. package/dist/web/client/vendor/github.min.css +10 -0
  29. package/dist/web/client/vendor/highlight.min.js +1213 -0
  30. package/dist/web/client/vendor/marked.min.js +69 -0
  31. package/dist/web/client/vendor/tailwind.js +83 -0
  32. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -20,12 +20,12 @@ import {
20
20
  saveDevState,
21
21
  sessionHasMeaningfulContent,
22
22
  setupProxy
23
- } from "./chunk-MPMEYE43.js";
23
+ } from "./chunk-ZAYDVWY4.js";
24
24
  import {
25
25
  getConfigDirUsage,
26
26
  listRecentCrashes,
27
27
  writeCrashLog
28
- } from "./chunk-75YT3SZI.js";
28
+ } from "./chunk-SH7NTECG.js";
29
29
  import {
30
30
  CONTENT_ONLY_STREAM_REMINDER,
31
31
  HALLUCINATION_CORRECTION_MESSAGE,
@@ -46,7 +46,7 @@ import {
46
46
  } from "./chunk-AIZOARZY.js";
47
47
  import {
48
48
  ConfigManager
49
- } from "./chunk-MEM43AE7.js";
49
+ } from "./chunk-OSTMMSOV.js";
50
50
  import {
51
51
  ToolExecutor,
52
52
  ToolRegistry,
@@ -65,16 +65,16 @@ import {
65
65
  spawnAgentContext,
66
66
  theme,
67
67
  undoStack
68
- } from "./chunk-WXVG56NI.js";
68
+ } from "./chunk-XWYWASPT.js";
69
69
  import "./chunk-HDSKW7Q3.js";
70
70
  import "./chunk-ZWVIDFGY.js";
71
- import "./chunk-K76YV4EL.js";
71
+ import "./chunk-NP7WOVIH.js";
72
72
  import {
73
73
  getStatsSnapshot,
74
74
  getTopFailingTools,
75
75
  getTopUsedTools,
76
76
  installFlushOnExit
77
- } from "./chunk-YCF6S3P3.js";
77
+ } from "./chunk-HVNEBTSF.js";
78
78
  import "./chunk-NXXNLLSG.js";
79
79
  import {
80
80
  AuthError,
@@ -102,7 +102,7 @@ import {
102
102
  SKILLS_DIR_NAME,
103
103
  VERSION,
104
104
  buildUserIdentityPrompt
105
- } from "./chunk-ZQX2NCQK.js";
105
+ } from "./chunk-UE26B3RO.js";
106
106
  import {
107
107
  formatGitContextForPrompt,
108
108
  getGitContext,
@@ -1769,7 +1769,7 @@ No tools match "${filter}".
1769
1769
  const { join: join6 } = await import("path");
1770
1770
  const { existsSync: existsSync6 } = await import("fs");
1771
1771
  const { getGitRoot: getGitRoot2 } = await import("./git-context-7KIP4X2V.js");
1772
- const { MCP_PROJECT_CONFIG_NAME: MCP_PROJECT_CONFIG_NAME2 } = await import("./constants-GRKJCSZF.js");
1772
+ const { MCP_PROJECT_CONFIG_NAME: MCP_PROJECT_CONFIG_NAME2 } = await import("./constants-S4Y6A25E.js");
1773
1773
  const { approveProject, hashMcpFile } = await import("./project-trust-IFM7FXEV.js");
1774
1774
  const cwd = process.cwd();
1775
1775
  const projectRoot = getGitRoot2(cwd) ?? cwd;
@@ -2830,7 +2830,7 @@ ${hint}` : "")
2830
2830
  usage: "/test [command|filter]",
2831
2831
  async execute(args, ctx) {
2832
2832
  try {
2833
- const { executeTests } = await import("./run-tests-HYELAYSL.js");
2833
+ const { executeTests } = await import("./run-tests-3QAZGHP2.js");
2834
2834
  const argStr = args.join(" ").trim();
2835
2835
  let testArgs = {};
2836
2836
  if (argStr) {
@@ -7530,7 +7530,7 @@ program.command("web").description("Start Web UI server with browser-based chat
7530
7530
  console.error("Error: Invalid port number. Must be between 1 and 65535.");
7531
7531
  process.exit(1);
7532
7532
  }
7533
- const { startWebServer } = await import("./server-32HM2KMX.js");
7533
+ const { startWebServer } = await import("./server-UL42EXOA.js");
7534
7534
  await startWebServer({ port, host: options.host });
7535
7535
  });
7536
7536
  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) => {
@@ -7697,12 +7697,12 @@ program.command("sessions").description("List recent conversation sessions").opt
7697
7697
  console.log(footer + "\n");
7698
7698
  });
7699
7699
  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) => {
7700
- const { runDoctorCli } = await import("./doctor-cli-R6ODVYFR.js");
7700
+ const { runDoctorCli } = await import("./doctor-cli-FMTMDO2Z.js");
7701
7701
  await runDoctorCli({ json: !!options.json, resetStats: !!options.resetStats });
7702
7702
  });
7703
7703
  program.command("batch <action> [arg] [arg2]").description("Anthropic Message Batches: submit | list | status <id> | results <id> [out] | cancel <id>").option("--dry-run", "Parse and validate input without submitting (submit only)").action(async (action, arg, arg2, options) => {
7704
7704
  try {
7705
- const batch = await import("./batch-IPWV3DNK.js");
7705
+ const batch = await import("./batch-W57MV5OT.js");
7706
7706
  switch (action) {
7707
7707
  case "submit":
7708
7708
  if (!arg) {
@@ -7745,7 +7745,7 @@ program.command("batch <action> [arg] [arg2]").description("Anthropic Message Ba
7745
7745
  }
7746
7746
  });
7747
7747
  program.command("mcp-serve").description("Start an MCP server over STDIO, exposing aicli's built-in tools to Claude Desktop / Cursor / other MCP clients").option("--allow-destructive", "Allow bash / run_interactive / task_create (always destructive in MCP mode)").option("--allow-outside-cwd", "Allow tool path arguments to escape the sandbox root \u2014 disabled by default").option("--tools <list>", "Comma-separated whitelist of tools to expose (default: all eligible tools)").option("--cwd <path>", "Working directory AND sandbox root (default: current directory)").action(async (options) => {
7748
- const { startMcpServer } = await import("./server-P7WY7VEX.js");
7748
+ const { startMcpServer } = await import("./server-W4TBZN6I.js");
7749
7749
  await startMcpServer({
7750
7750
  allowDestructive: !!options.allowDestructive,
7751
7751
  allowOutsideCwd: !!options.allowOutsideCwd,
@@ -7754,7 +7754,7 @@ program.command("mcp-serve").description("Start an MCP server over STDIO, exposi
7754
7754
  });
7755
7755
  });
7756
7756
  program.command("ci").description("Headless PR review (code + security) \u2014 reads git/gh diff, optionally posts to PR. Designed for GitHub Actions.").option("--pr <num>", "PR number; diff fetched via `gh pr diff <num>`", (v) => parseInt(v, 10)).option("--base <ref>", "Base ref for `git diff <ref>...HEAD` (ignored when --pr set)").option("--post", "Post review as a PR comment (requires gh CLI + GH_TOKEN, needs --pr)").option("--no-update", "Always create a new comment instead of updating the previous aicli review").option("--skip-code", "Skip the code review section").option("--skip-security", "Skip the security review section").option("--detailed", "Use the detailed code-review prompt").option("--max-diff <n>", "Max diff chars sent to the model (default 30000)", (v) => parseInt(v, 10)).option("--provider <id>", "Override provider (default: config.defaultProvider)").option("--model <id>", "Override model").option("--dry-run", "Print result to stdout instead of posting (overrides --post)").action(async (options) => {
7757
- const { runCi } = await import("./ci-YDMKYUQC.js");
7757
+ const { runCi } = await import("./ci-JYZGZSMP.js");
7758
7758
  const result = await runCi({
7759
7759
  pr: options.pr,
7760
7760
  base: options.base,
@@ -7827,6 +7827,9 @@ program.command("help").description("Show a comprehensive guide to all aicli fea
7827
7827
  `${B}${C} \u25A0 MULTI-AGENT HUB${R}`,
7828
7828
  ` ${Y}--preset <name>${R} Role preset (tech-review/brainstorm/code-review/debate)`,
7829
7829
  ` ${Y}--roles <file>${R} Custom roles JSON file`,
7830
+ ` ${Y}--mix [providers]${R} Mixed multi-model: spread providers across roles (e.g. claude,openai,deepseek)`,
7831
+ ` ${Y}--steer${R} Human-in-the-loop: steer/stop between rounds`,
7832
+ ` ${Y}--vote${R} Convergence voting: 2/3 [CONVERGED] majority ends early`,
7830
7833
  ` ${Y}-c, --context <file>${R} Inject context doc (repeatable: -c a.md -c b.md)`,
7831
7834
  ` ${Y}--task${R} Task mode: agents plan & write code with tools`,
7832
7835
  ` ${Y}--distributed${R} Distributed mode (WebSocket, multi-process)`,
@@ -7872,7 +7875,7 @@ program.command("help").description("Show a comprehensive guide to all aicli fea
7872
7875
  ` ${G}aicli --provider claude -m claude-sonnet-4-20250514${R} ${D}# specific model${R}`,
7873
7876
  ` ${G}aicli -p "explain this" < main.py${R} ${D}# headless + file${R}`,
7874
7877
  ` ${G}aicli web --host 0.0.0.0${R} ${D}# Web UI on LAN${R}`,
7875
- ` ${G}aicli hub --preset brainstorm "idea"${R} ${D}# multi-agent brainstorm${R}`,
7878
+ ` ${G}aicli hub --preset brainstorm --mix "idea"${R} ${D}# brainstorm across Claude/GPT/DeepSeek${R}`,
7876
7879
  ` ${G}aicli hub --task --roles team.json "build app"${R} ${D}# task mode: agents write code${R}`,
7877
7880
  ` ${G}aicli hub -c spec.md -c api.md "build it"${R} ${D}# hub + docs context${R}`,
7878
7881
  ` ${G}aicli batch submit prompts.jsonl${R} ${D}# Anthropic batch: 50% off, 24h window${R}`,
@@ -7884,7 +7887,7 @@ program.command("help").description("Show a comprehensive guide to all aicli fea
7884
7887
  ];
7885
7888
  console.log(lines.join("\n"));
7886
7889
  });
7887
- program.command("hub [topic]").description("Start multi-agent hub (discuss / brainstorm with multiple AI roles)").option("--preset <name>", "Use a built-in role preset (default: tech-review)").option("--roles <file>", "Load roles from a JSON file").option("--provider <name>", "Override default AI provider").option("-m, --model <name>", "Override default model").option("--max-rounds <n>", "Max discussion rounds (default: 10)").option("--presets", "List available role presets").option("--task", "Task mode: plan \u2192 approve \uFFFD\uFFFD execute with tools (agents write code)").option("--task-rounds <n>", "Max tool-call rounds per task (default: 30)").option("-c, --context <file>", "Inject context document (repeatable: -c a.md -c b.md)", (val, prev) => prev.concat(val), []).option("--distributed", "Start WebSocket server so remote aicli instances can join as agents").option("--port <n>", "WebSocket port for distributed mode (default: 9527)", "9527").action(async (topic, options) => {
7890
+ program.command("hub [topic]").description("Start multi-agent hub (discuss / brainstorm with multiple AI roles)").option("--preset <name>", "Use a built-in role preset (default: tech-review)").option("--roles <file>", "Load roles from a JSON file").option("--provider <name>", "Override default AI provider").option("-m, --model <name>", "Override default model").option("--mix [providers]", "Mixed multi-model: spread configured providers across roles (or --mix claude,openai,deepseek)").option("--steer", "Human-in-the-loop: pause between rounds to inject guidance or stop (discuss mode)").option("--vote", "Convergence voting: agents may [CONVERGED]; 2/3 majority ends early (discuss mode)").option("--max-rounds <n>", "Max discussion rounds (default: 10)").option("--presets", "List available role presets").option("--task", "Task mode: plan \u2192 approve \uFFFD\uFFFD execute with tools (agents write code)").option("--task-rounds <n>", "Max tool-call rounds per task (default: 30)").option("-c, --context <file>", "Inject context document (repeatable: -c a.md -c b.md)", (val, prev) => prev.concat(val), []).option("--distributed", "Start WebSocket server so remote aicli instances can join as agents").option("--port <n>", "WebSocket port for distributed mode (default: 9527)", "9527").action(async (topic, options) => {
7888
7891
  const config = new ConfigManager();
7889
7892
  const registry = new ProviderRegistry();
7890
7893
  await registry.initialize(
@@ -7895,7 +7898,7 @@ program.command("hub [topic]").description("Start multi-agent hub (discuss / bra
7895
7898
  }),
7896
7899
  config.get("customProviders")
7897
7900
  );
7898
- const { startHub } = await import("./hub-3MKH3BMA.js");
7901
+ const { startHub } = await import("./hub-OP7EWTQQ.js");
7899
7902
  await startHub(
7900
7903
  {
7901
7904
  topic: topic ?? "",
@@ -7903,6 +7906,9 @@ program.command("hub [topic]").description("Start multi-agent hub (discuss / bra
7903
7906
  rolesFile: options.roles,
7904
7907
  provider: options.provider,
7905
7908
  model: options.model,
7909
+ mix: options.mix,
7910
+ steer: options.steer === true,
7911
+ vote: options.vote === true,
7906
7912
  mode: options.task === true ? "task" : void 0,
7907
7913
  maxRounds: options.maxRounds ? parseInt(options.maxRounds, 10) : void 0,
7908
7914
  listPresets: options.presets === true,
@@ -2,8 +2,8 @@
2
2
  import {
3
3
  executeTests,
4
4
  runTestsTool
5
- } from "./chunk-K76YV4EL.js";
6
- import "./chunk-ZQX2NCQK.js";
5
+ } from "./chunk-NP7WOVIH.js";
6
+ import "./chunk-UE26B3RO.js";
7
7
  import "./chunk-PDX44BCA.js";
8
8
  export {
9
9
  executeTests,
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  executeTests,
3
3
  runTestsTool
4
- } from "./chunk-WCBVNY6D.js";
4
+ } from "./chunk-2IODI5TI.js";
5
5
  import "./chunk-3RG5ZIWI.js";
6
6
  export {
7
7
  executeTests,
@@ -14,7 +14,7 @@ import {
14
14
  loadDevState,
15
15
  persistToolRound,
16
16
  setupProxy
17
- } from "./chunk-MPMEYE43.js";
17
+ } from "./chunk-ZAYDVWY4.js";
18
18
  import {
19
19
  CONTENT_ONLY_STREAM_REMINDER,
20
20
  HALLUCINATION_CORRECTION_MESSAGE,
@@ -31,7 +31,7 @@ import {
31
31
  } from "./chunk-AIZOARZY.js";
32
32
  import {
33
33
  ConfigManager
34
- } from "./chunk-MEM43AE7.js";
34
+ } from "./chunk-OSTMMSOV.js";
35
35
  import {
36
36
  ToolExecutor,
37
37
  ToolRegistry,
@@ -49,13 +49,13 @@ import {
49
49
  spawnAgentContext,
50
50
  truncateOutput,
51
51
  undoStack
52
- } from "./chunk-WXVG56NI.js";
52
+ } from "./chunk-XWYWASPT.js";
53
53
  import "./chunk-HDSKW7Q3.js";
54
54
  import "./chunk-ZWVIDFGY.js";
55
- import "./chunk-K76YV4EL.js";
55
+ import "./chunk-NP7WOVIH.js";
56
56
  import {
57
57
  runTool
58
- } from "./chunk-YCF6S3P3.js";
58
+ } from "./chunk-HVNEBTSF.js";
59
59
  import {
60
60
  getDangerLevel
61
61
  } from "./chunk-NXXNLLSG.js";
@@ -78,7 +78,7 @@ import {
78
78
  SKILLS_DIR_NAME,
79
79
  VERSION,
80
80
  buildUserIdentityPrompt
81
- } from "./chunk-ZQX2NCQK.js";
81
+ } from "./chunk-UE26B3RO.js";
82
82
  import {
83
83
  formatGitContextForPrompt,
84
84
  getGitContext,
@@ -2462,7 +2462,7 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
2462
2462
  case "test": {
2463
2463
  this.send({ type: "info", message: "\u{1F9EA} Running tests..." });
2464
2464
  try {
2465
- const { executeTests } = await import("./run-tests-HYELAYSL.js");
2465
+ const { executeTests } = await import("./run-tests-3QAZGHP2.js");
2466
2466
  const argStr = args.join(" ").trim();
2467
2467
  let testArgs = {};
2468
2468
  if (argStr) {
@@ -3477,8 +3477,9 @@ async function startWebServer(options = {}) {
3477
3477
  }
3478
3478
  console.log(` Providers: ${availableProviders.map((p) => p.info.id).join(", ")}`);
3479
3479
  let mcpManager = null;
3480
- const globalMcpServers = config.get("mcpServers") ?? {};
3481
- const projectMcpResolved = resolveProjectMcpPath();
3480
+ const mcpEnabled = config.get("mcpEnabled") !== false;
3481
+ const globalMcpServers = mcpEnabled ? config.get("mcpServers") ?? {} : {};
3482
+ const projectMcpResolved = mcpEnabled ? resolveProjectMcpPath() : null;
3482
3483
  let projectMcpServers = {};
3483
3484
  if (projectMcpResolved) {
3484
3485
  const { checkTrust } = await import("./project-trust-IFM7FXEV.js");
@@ -3491,7 +3492,10 @@ async function startWebServer(options = {}) {
3491
3492
  }
3492
3493
  }
3493
3494
  const mergedMcpServers = { ...globalMcpServers, ...projectMcpServers };
3494
- if (Object.keys(mergedMcpServers).length > 0) {
3495
+ if (!mcpEnabled) {
3496
+ console.log(" \u{1F50C} MCP: disabled (config.mcpEnabled=false)");
3497
+ }
3498
+ if (mcpEnabled && Object.keys(mergedMcpServers).length > 0) {
3495
3499
  mcpManager = new McpManager();
3496
3500
  await mcpManager.connectAll(mergedMcpServers);
3497
3501
  const mcpTools = mcpManager.getAllTools();
@@ -1,13 +1,13 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  ToolRegistry
4
- } from "./chunk-WXVG56NI.js";
4
+ } from "./chunk-XWYWASPT.js";
5
5
  import "./chunk-HDSKW7Q3.js";
6
6
  import "./chunk-ZWVIDFGY.js";
7
- import "./chunk-K76YV4EL.js";
7
+ import "./chunk-NP7WOVIH.js";
8
8
  import {
9
9
  runTool
10
- } from "./chunk-YCF6S3P3.js";
10
+ } from "./chunk-HVNEBTSF.js";
11
11
  import {
12
12
  getDangerLevel,
13
13
  schemaToJsonSchema
@@ -15,7 +15,7 @@ import {
15
15
  import "./chunk-2ZD3YTVM.js";
16
16
  import {
17
17
  VERSION
18
- } from "./chunk-ZQX2NCQK.js";
18
+ } from "./chunk-UE26B3RO.js";
19
19
  import "./chunk-4BKXL7SM.js";
20
20
  import "./chunk-RXM76HB7.js";
21
21
  import "./chunk-KHYD3WXE.js";
@@ -3,20 +3,20 @@ import {
3
3
  ToolRegistry,
4
4
  googleSearchContext,
5
5
  truncateOutput
6
- } from "./chunk-WXVG56NI.js";
6
+ } from "./chunk-XWYWASPT.js";
7
7
  import "./chunk-HDSKW7Q3.js";
8
8
  import "./chunk-ZWVIDFGY.js";
9
- import "./chunk-K76YV4EL.js";
9
+ import "./chunk-NP7WOVIH.js";
10
10
  import {
11
11
  runTool
12
- } from "./chunk-YCF6S3P3.js";
12
+ } from "./chunk-HVNEBTSF.js";
13
13
  import {
14
14
  getDangerLevel
15
15
  } from "./chunk-NXXNLLSG.js";
16
16
  import "./chunk-2ZD3YTVM.js";
17
17
  import {
18
18
  SUBAGENT_ALLOWED_TOOLS
19
- } from "./chunk-ZQX2NCQK.js";
19
+ } from "./chunk-UE26B3RO.js";
20
20
  import "./chunk-4BKXL7SM.js";
21
21
  import "./chunk-RXM76HB7.js";
22
22
  import "./chunk-KHYD3WXE.js";
@@ -644,7 +644,8 @@ window.respondAutoPause = function(requestId, action) {
644
644
 
645
645
  // DaisyUI light themes → highlight.js light stylesheet; others → dark
646
646
  const LIGHT_DAISYUI_THEMES = new Set(['light', 'cupcake', 'bumblebee', 'emerald', 'corporate', 'garden', 'lofi', 'pastel', 'fantasy', 'wireframe', 'cmyk', 'autumn', 'acid', 'lemonade', 'winter', 'nord']);
647
- const HLJS_CDN = 'https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.9.0/build/styles';
647
+ // v0.4.153: vendored locally (was cdn.jsdelivr.net) — see index.html.
648
+ const HLJS_CDN = 'vendor';
648
649
 
649
650
  function updateCodeTheme(daisyTheme) {
650
651
  const isLight = LIGHT_DAISYUI_THEMES.has(daisyTheme);
@@ -12,13 +12,17 @@
12
12
  <link rel="apple-touch-icon" href="icon-192.png">
13
13
  <link rel="icon" type="image/svg+xml" href="icon.svg">
14
14
  <link rel="icon" type="image/png" sizes="192x192" href="icon-192.png">
15
- <!-- Tailwind CSS + DaisyUI (CDN) -->
16
- <link href="https://cdn.jsdelivr.net/npm/daisyui@4.12.23/dist/full.min.css" rel="stylesheet">
17
- <script src="https://cdn.tailwindcss.com"></script>
15
+ <!-- Tailwind CSS + DaisyUI (vendored locally — v0.4.153). These were on
16
+ cdn.tailwindcss.com / cdn.jsdelivr.net, which GFW blocks in CN; when the
17
+ CDN failed the whole UI rendered unstyled AND the `.hidden` login overlay
18
+ became visible (no users → "can't log in"). Serving locally makes the UI
19
+ work offline / behind GFW with zero behavior change. -->
20
+ <link href="vendor/daisyui-full.min.css" rel="stylesheet">
21
+ <script src="vendor/tailwind.js"></script>
18
22
  <!-- Markdown + Code highlighting -->
19
- <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
20
- <link id="hljs-theme" rel="stylesheet" href="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.9.0/build/styles/github-dark.min.css">
21
- <script src="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.9.0/build/highlight.min.js"></script>
23
+ <script src="vendor/marked.min.js"></script>
24
+ <link id="hljs-theme" rel="stylesheet" href="vendor/github-dark.min.css">
25
+ <script src="vendor/highlight.min.js"></script>
22
26
  <link rel="stylesheet" href="style.css">
23
27
  </head>
24
28
  <body class="bg-base-300 h-screen overflow-hidden">
@@ -4,9 +4,14 @@
4
4
  * API calls and WebSocket are always network-first.
5
5
  */
6
6
 
7
- const CACHE_NAME = 'aicli-v1';
7
+ // v0.4.153: bumped v1 → v2 so the `activate` handler evicts the old cache.
8
+ // Without this, clients that cached the pre-fix index.html (which referenced
9
+ // GFW-blocked CDNs) would keep serving the broken page forever.
10
+ const CACHE_NAME = 'aicli-v2';
8
11
 
9
- // App shell — files needed for offline-capable startup
12
+ // App shell — files needed for offline-capable startup. Now includes the
13
+ // vendored Tailwind/DaisyUI/marked/highlight.js assets (previously CDN-hosted),
14
+ // so the UI is fully self-contained and works behind a firewall / offline.
10
15
  const APP_SHELL = [
11
16
  '/',
12
17
  '/index.html',
@@ -16,12 +21,12 @@ const APP_SHELL = [
16
21
  '/icon.svg',
17
22
  '/icon-192.png',
18
23
  '/icon-512.png',
19
- ];
20
-
21
- // CDN resources to cache on first use
22
- const CDN_CACHE = [
23
- 'cdn.jsdelivr.net',
24
- 'cdn.tailwindcss.com',
24
+ '/vendor/daisyui-full.min.css',
25
+ '/vendor/tailwind.js',
26
+ '/vendor/marked.min.js',
27
+ '/vendor/highlight.min.js',
28
+ '/vendor/github-dark.min.css',
29
+ '/vendor/github.min.css',
25
30
  ];
26
31
 
27
32
  self.addEventListener('install', (event) => {
@@ -51,24 +56,8 @@ self.addEventListener('fetch', (event) => {
51
56
  return;
52
57
  }
53
58
 
54
- // CDN resources: cache-first (they're versioned by URL)
55
- if (CDN_CACHE.some(host => url.hostname.includes(host))) {
56
- event.respondWith(
57
- caches.match(event.request).then(cached => {
58
- if (cached) return cached;
59
- return fetch(event.request).then(response => {
60
- if (response.ok) {
61
- const clone = response.clone();
62
- caches.open(CACHE_NAME).then(cache => cache.put(event.request, clone));
63
- }
64
- return response;
65
- });
66
- })
67
- );
68
- return;
69
- }
70
-
71
- // App shell: network-first with cache fallback (always get latest from server)
59
+ // App shell (now incl. same-origin /vendor/*): network-first with cache
60
+ // fallback (always get latest from server)
72
61
  event.respondWith(
73
62
  fetch(event.request)
74
63
  .then(response => {