chainlesschain 0.156.6 → 0.156.7

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 (113) hide show
  1. package/README.md +24 -0
  2. package/package.json +2 -1
  3. package/src/assets/web-panel/assets/{ActionButton-Dme4LGax.js → ActionButton-Cs4QdjYb.js} +1 -1
  4. package/src/assets/web-panel/assets/{Analytics-B3-5BjRm.js → Analytics-Xot0e9TT.js} +1 -1
  5. package/src/assets/web-panel/assets/{AppLayout-DvVLRyPs.js → AppLayout-3qsE1-pz.js} +1 -1
  6. package/src/assets/web-panel/assets/{BaseInput-wLmjCc9u.js → BaseInput-Tg40P4JM.js} +1 -1
  7. package/src/assets/web-panel/assets/{Checkbox-BfbEUJDW.js → Checkbox-CheA2Ety.js} +1 -1
  8. package/src/assets/web-panel/assets/{Col-HJI40OzO.js → Col-Cdfsmnaq.js} +1 -1
  9. package/src/assets/web-panel/assets/{Compact-ADVAwcbQ.js → Compact-D3LSgEpW.js} +1 -1
  10. package/src/assets/web-panel/assets/{Cron-DcNB5TYu.js → Cron-9MV6k-MV.js} +1 -1
  11. package/src/assets/web-panel/assets/{Dashboard-p_Wuj0Un.js → Dashboard-A1TZC5_t.js} +1 -1
  12. package/src/assets/web-panel/assets/{Dropdown-CrXGzreQ.js → Dropdown-DZxUTZvw.js} +1 -1
  13. package/src/assets/web-panel/assets/{FormItemContext-B97Dibo2.js → FormItemContext-C3_-j_SR.js} +1 -1
  14. package/src/assets/web-panel/assets/{Git-90CPsOOr.js → Git-Cw-gW-kh.js} +1 -1
  15. package/src/assets/web-panel/assets/{Memory-B_3zNQNB.js → Memory-CMweTJyn.js} +1 -1
  16. package/src/assets/web-panel/assets/{Notes-DNQz9UXh.js → Notes-B_W3BfZF.js} +1 -1
  17. package/src/assets/web-panel/assets/{Organization-CgXUnp-W.js → Organization-Dz_jGbAM.js} +1 -1
  18. package/src/assets/web-panel/assets/{Overflow-BVsn6SM5.js → Overflow-Dka3nWV9.js} +1 -1
  19. package/src/assets/web-panel/assets/{Permissions-DIFqcnjU.js → Permissions-DvXVIlHX.js} +1 -1
  20. package/src/assets/web-panel/assets/{Providers-mscN7CK5.js → Providers-DUfX_ynl.js} +1 -1
  21. package/src/assets/web-panel/assets/{Row-BFUWxIkx.js → Row-DZhDSo2Q.js} +1 -1
  22. package/src/assets/web-panel/assets/{RssFeed-Dpa4h-q_.js → RssFeed-CHQpUl3h.js} +1 -1
  23. package/src/assets/web-panel/assets/{Security-DR6HKo_S.js → Security-FUSOn89T.js} +1 -1
  24. package/src/assets/web-panel/assets/{Skeleton-VNikEgM4.js → Skeleton-DxmZ7zRw.js} +1 -1
  25. package/src/assets/web-panel/assets/{Templates-Ny_4GO6a.js → Templates-BVbmyn38.js} +1 -1
  26. package/src/assets/web-panel/assets/{Trigger-C7MTh_xj.js → Trigger-xAvohiq9.js} +1 -1
  27. package/src/assets/web-panel/assets/{VideoEditing-BNRFHgJ9.js → VideoEditing-BUWYQv2y.js} +1 -1
  28. package/src/assets/web-panel/assets/{Wallet-BUfg4IAx.js → Wallet-BDYdEwFf.js} +1 -1
  29. package/src/assets/web-panel/assets/{WebAuthn-Cia89OyQ.js → WebAuthn-CvpuagtK.js} +1 -1
  30. package/src/assets/web-panel/assets/{WorkflowEditor-C1OsMtqv.js → WorkflowEditor-BR7W5cjw.js} +1 -1
  31. package/src/assets/web-panel/assets/{colors-C_wDMX2Q.js → colors-C5kDbQCi.js} +1 -1
  32. package/src/assets/web-panel/assets/{compact-item-C1ikzEN-.js → compact-item-Bo_1zDrX.js} +1 -1
  33. package/src/assets/web-panel/assets/{createContext-XExBTk9v.js → createContext-CniPpJsG.js} +1 -1
  34. package/src/assets/web-panel/assets/{hasIn-mXvd_Kdq.js → hasIn-ClDc6Sz8.js} +1 -1
  35. package/src/assets/web-panel/assets/{index-D6Hyy0Bc.js → index--lcO-bOn.js} +1 -1
  36. package/src/assets/web-panel/assets/{index-lPIeHtHE.js → index-6tQekF0Y.js} +1 -1
  37. package/src/assets/web-panel/assets/{index-BfncNR8d.js → index-8qWxPHSb.js} +1 -1
  38. package/src/assets/web-panel/assets/{index-C53dnYiq.js → index-B7nGNm_C.js} +1 -1
  39. package/src/assets/web-panel/assets/{index-CMYADk0v.js → index-B8y0NO-M.js} +1 -1
  40. package/src/assets/web-panel/assets/{index-DMcLOtIo.js → index-BAlSSCbs.js} +1 -1
  41. package/src/assets/web-panel/assets/{index-DJkIheU6.js → index-BCXFoTAw.js} +1 -1
  42. package/src/assets/web-panel/assets/{index-kLUQdSDJ.js → index-BHruTebo.js} +1 -1
  43. package/src/assets/web-panel/assets/{index-1ZqkTPt2.js → index-BJx6C3J8.js} +1 -1
  44. package/src/assets/web-panel/assets/{index-CbpKJ2W0.js → index-BUTCJTbj.js} +1 -1
  45. package/src/assets/web-panel/assets/{index-B6P9mWuk.js → index-BYShDlZ0.js} +1 -1
  46. package/src/assets/web-panel/assets/{index-D9tzxSFs.js → index-Bv2OmZAS.js} +1 -1
  47. package/src/assets/web-panel/assets/{index-BFFb9yPd.js → index-C92K4iDE.js} +1 -1
  48. package/src/assets/web-panel/assets/{index-v4Oi0d0l.js → index-CCdb36il.js} +1 -1
  49. package/src/assets/web-panel/assets/{index-B-TI0cZ2.js → index-CKR2ITFk.js} +1 -1
  50. package/src/assets/web-panel/assets/{index-fLUJs2Sr.js → index-CMcGcbea.js} +1 -1
  51. package/src/assets/web-panel/assets/{index-BirLVqrC.js → index-CTetsi8W.js} +1 -1
  52. package/src/assets/web-panel/assets/{index-LpE6Six-.js → index-CXxLp7Aw.js} +1 -1
  53. package/src/assets/web-panel/assets/{index-qtDQSqTG.js → index-CeSV8f3b.js} +1 -1
  54. package/src/assets/web-panel/assets/{index-DwMlStra.js → index-Ch5mAXeh.js} +1 -1
  55. package/src/assets/web-panel/assets/{index-BOqmUcij.js → index-CwhWEkmA.js} +1 -1
  56. package/src/assets/web-panel/assets/{index-D_oSE2Nk.js → index-D2fe9a6f.js} +1 -1
  57. package/src/assets/web-panel/assets/index-D3UDIt7h.js +1 -0
  58. package/src/assets/web-panel/assets/{index-CxwU-EjS.js → index-D90sLw5Q.js} +1 -1
  59. package/src/assets/web-panel/assets/{index-D1eekAaa.js → index-D9bolkbl.js} +1 -1
  60. package/src/assets/web-panel/assets/{index-BL27IhbN.js → index-DNY0K7iI.js} +1 -1
  61. package/src/assets/web-panel/assets/{index-Du7KGlCP.js → index-DSiHmo4b.js} +1 -1
  62. package/src/assets/web-panel/assets/{index-CttcpCq_.js → index-DTYnvYqB.js} +1 -1
  63. package/src/assets/web-panel/assets/{index-jg5cpQg9.js → index-DaLYbr0E.js} +1 -1
  64. package/src/assets/web-panel/assets/{index-DYLE4bnY.js → index-DkSNIJhM.js} +1 -1
  65. package/src/assets/web-panel/assets/{index-DZjQgmBq.js → index-DnQkqOZj.js} +1 -1
  66. package/src/assets/web-panel/assets/{index-DaMG8ksh.js → index-Dn_OQQaV.js} +3 -3
  67. package/src/assets/web-panel/assets/{index-Dz6RDRcu.js → index-Dtfrhky9.js} +1 -1
  68. package/src/assets/web-panel/assets/{index-CTle6zcb.js → index-JNbd08FN.js} +1 -1
  69. package/src/assets/web-panel/assets/index-PT376OZM.js +1 -0
  70. package/src/assets/web-panel/assets/{index-BBOVB9YK.js → index-cIgCeEqo.js} +1 -1
  71. package/src/assets/web-panel/assets/{index-a0qENb5U.js → index-vBi4x_6g.js} +1 -1
  72. package/src/assets/web-panel/assets/{index-C5Zv4fBx.js → index-xL8gcpmy.js} +1 -1
  73. package/src/assets/web-panel/assets/{initDefaultProps-DOj2K4bh.js → initDefaultProps-DMfJaUzk.js} +1 -1
  74. package/src/assets/web-panel/assets/{motion-joGf7r-l.js → motion-sEbWmOWo.js} +1 -1
  75. package/src/assets/web-panel/assets/{move-Cwb6tumJ.js → move-DIWXVs--.js} +1 -1
  76. package/src/assets/web-panel/assets/{omit-CPycjJ8C.js → omit-D7mkMPhu.js} +1 -1
  77. package/src/assets/web-panel/assets/{pickAttrs-CnibXC3T.js → pickAttrs-B25NUX4k.js} +1 -1
  78. package/src/assets/web-panel/assets/{placementArrow-DWcIO1y4.js → placementArrow-By1Bkq1d.js} +1 -1
  79. package/src/assets/web-panel/assets/{responsiveObserve-C5giLhLf.js → responsiveObserve-B3aCQz5r.js} +1 -1
  80. package/src/assets/web-panel/assets/{slide-zwgmm7vM.js → slide-eR-f56FQ.js} +1 -1
  81. package/src/assets/web-panel/assets/{statusUtils-CK8tJSHq.js → statusUtils-zcNWczhN.js} +1 -1
  82. package/src/assets/web-panel/assets/{styleChecker-BzLSEXyu.js → styleChecker-u9Z0IfRy.js} +1 -1
  83. package/src/assets/web-panel/assets/{transition-D4AbuDdO.js → transition-gRK4XSlW.js} +1 -1
  84. package/src/assets/web-panel/assets/{useConfigInject-ImjEZhXr.js → useConfigInject-ZEunuNHN.js} +1 -1
  85. package/src/assets/web-panel/assets/{useFlexGapSupport-Cd-PoTMl.js → useFlexGapSupport-BpbEJfeh.js} +1 -1
  86. package/src/assets/web-panel/assets/{vnode-DAWimP6X.js → vnode-DVHvXn9F.js} +1 -1
  87. package/src/assets/web-panel/assets/{zoom-u6SXbmzZ.js → zoom-ByzgJIn6.js} +1 -1
  88. package/src/assets/web-panel/index.html +1 -1
  89. package/src/commands/pack.js +463 -0
  90. package/src/commands/ui.js +6 -0
  91. package/src/gateways/ws/ws-server.js +29 -3
  92. package/src/index.js +34 -1
  93. package/src/lib/governance-v2-helpers.js +109 -0
  94. package/src/lib/packer/config-template-builder.js +151 -0
  95. package/src/lib/packer/errors.js +30 -0
  96. package/src/lib/packer/index.js +383 -0
  97. package/src/lib/packer/manifest-writer.js +93 -0
  98. package/src/lib/packer/native-prebuild-collector.js +305 -0
  99. package/src/lib/packer/pack-update-applier.js +203 -0
  100. package/src/lib/packer/pack-update-checker.js +185 -0
  101. package/src/lib/packer/pack-update-downloader.js +162 -0
  102. package/src/lib/packer/pkg-config-generator.js +325 -0
  103. package/src/lib/packer/pkg-runner.js +139 -0
  104. package/src/lib/packer/precheck.js +273 -0
  105. package/src/lib/packer/project-assets-collector.js +0 -0
  106. package/src/lib/packer/smoke-runner.js +339 -0
  107. package/src/lib/packer/web-panel-builder.js +157 -0
  108. package/src/lib/web-ui-server.js +95 -2
  109. package/src/lib/ws-server.js +1 -0
  110. package/src/runtime/agent-runtime.js +1 -0
  111. package/src/runtime/policies/agent-policy.js +1 -0
  112. package/src/assets/web-panel/assets/index-DQgS_8Fd.js +0 -1
  113. package/src/assets/web-panel/assets/index-f4W8Sok0.js +0 -1
@@ -0,0 +1,157 @@
1
+ /**
2
+ * Phase 2: web-panel-builder
3
+ *
4
+ * Ensures packages/cli/src/assets/web-panel/ (or packages/web-panel/dist)
5
+ * has a built Vue panel ready to embed. If missing or stale, runs
6
+ * `npm run build:web-panel` from the CLI package root.
7
+ *
8
+ * Stale = source files in packages/web-panel/src newer than dist/index.html.
9
+ */
10
+
11
+ import fs from "node:fs";
12
+ import path from "node:path";
13
+ import { spawnSync } from "node:child_process";
14
+ import { PackError, EXIT } from "./errors.js";
15
+
16
+ /**
17
+ * @param {object} ctx
18
+ * @param {string} ctx.cliRoot packages/cli absolute path
19
+ * @param {boolean} ctx.skipBuild reuse existing dist
20
+ * @param {object} [ctx.logger] optional logger.log(msg)
21
+ * @returns {{distDir:string, rebuilt:boolean, assetCount:number}}
22
+ */
23
+ export function ensureWebPanel(ctx) {
24
+ const { cliRoot, skipBuild, logger } = ctx;
25
+ const log = logger?.log || (() => {});
26
+
27
+ const candidates = [
28
+ path.join(cliRoot, "src", "assets", "web-panel"),
29
+ path.resolve(cliRoot, "..", "web-panel", "dist"),
30
+ ];
31
+
32
+ let distDir = candidates.find((d) =>
33
+ fs.existsSync(path.join(d, "index.html")),
34
+ );
35
+
36
+ // Decide if a rebuild is needed
37
+ let rebuilt = false;
38
+ let needsBuild = !distDir;
39
+ if (distDir && !skipBuild) {
40
+ needsBuild = isDistStale(distDir);
41
+ }
42
+
43
+ if (needsBuild && skipBuild) {
44
+ throw new PackError(
45
+ "web-panel/dist is missing or stale, but --skip-web-panel-build was passed.",
46
+ EXIT.WEB_PANEL,
47
+ );
48
+ }
49
+
50
+ if (needsBuild) {
51
+ log(" [web-panel] Building Vue panel via 'npm run build:web-panel'...");
52
+ const npmCmd = process.platform === "win32" ? "npm.cmd" : "npm";
53
+ const res = spawnSync(npmCmd, ["run", "build:web-panel"], {
54
+ cwd: cliRoot,
55
+ stdio: "inherit",
56
+ windowsHide: true,
57
+ });
58
+ if (res.status !== 0) {
59
+ throw new PackError(
60
+ `'npm run build:web-panel' exited with code ${res.status}`,
61
+ EXIT.WEB_PANEL,
62
+ );
63
+ }
64
+ rebuilt = true;
65
+ distDir = candidates.find((d) => fs.existsSync(path.join(d, "index.html")));
66
+ }
67
+
68
+ if (!distDir) {
69
+ throw new PackError(
70
+ "web-panel build did not produce dist/index.html in any expected location.",
71
+ EXIT.WEB_PANEL,
72
+ );
73
+ }
74
+
75
+ const assetCount = countAssets(distDir);
76
+ if (assetCount < 2) {
77
+ throw new PackError(
78
+ `web-panel dist looks empty (only ${assetCount} files). Build may be broken.`,
79
+ EXIT.WEB_PANEL,
80
+ );
81
+ }
82
+
83
+ return { distDir, rebuilt, assetCount };
84
+ }
85
+
86
+ /**
87
+ * dist is stale if any source file is newer than dist/index.html.
88
+ * Source roots tried in order; missing roots are skipped.
89
+ */
90
+ function isDistStale(distDir) {
91
+ const indexHtml = path.join(distDir, "index.html");
92
+ if (!fs.existsSync(indexHtml)) return true;
93
+ const builtAt = fs.statSync(indexHtml).mtimeMs;
94
+
95
+ const sourceRoots = [
96
+ path.resolve(distDir, "..", "..", "..", "web-panel", "src"),
97
+ path.resolve(distDir, "..", "src"),
98
+ ];
99
+ for (const root of sourceRoots) {
100
+ if (!fs.existsSync(root)) continue;
101
+ if (latestMTimeRecursive(root, builtAt) > builtAt) return true;
102
+ }
103
+ return false;
104
+ }
105
+
106
+ /**
107
+ * Walk dir; return earliest mtime that exceeds threshold.
108
+ * Short-circuits on first hit.
109
+ */
110
+ function latestMTimeRecursive(dir, threshold) {
111
+ let max = 0;
112
+ const stack = [dir];
113
+ while (stack.length) {
114
+ const cur = stack.pop();
115
+ let entries;
116
+ try {
117
+ entries = fs.readdirSync(cur, { withFileTypes: true });
118
+ } catch {
119
+ continue;
120
+ }
121
+ for (const e of entries) {
122
+ const full = path.join(cur, e.name);
123
+ try {
124
+ const st = fs.statSync(full);
125
+ if (e.isDirectory()) {
126
+ stack.push(full);
127
+ } else if (st.mtimeMs > max) {
128
+ max = st.mtimeMs;
129
+ if (max > threshold) return max;
130
+ }
131
+ } catch {
132
+ /* skip */
133
+ }
134
+ }
135
+ }
136
+ return max;
137
+ }
138
+
139
+ function countAssets(distDir) {
140
+ let count = 0;
141
+ const stack = [distDir];
142
+ while (stack.length) {
143
+ const cur = stack.pop();
144
+ let entries;
145
+ try {
146
+ entries = fs.readdirSync(cur, { withFileTypes: true });
147
+ } catch {
148
+ continue;
149
+ }
150
+ for (const e of entries) {
151
+ const full = path.join(cur, e.name);
152
+ if (e.isDirectory()) stack.push(full);
153
+ else count++;
154
+ }
155
+ }
156
+ return count;
157
+ }
@@ -4,8 +4,16 @@
4
4
  * The UI is a self-contained single-page app with an embedded WebSocket client.
5
5
  *
6
6
  * Usage:
7
- * const server = createWebUIServer({ wsPort, wsToken, wsHost, projectRoot, projectName, mode });
7
+ * const server = createWebUIServer({ wsPort, wsToken, wsHost, projectRoot, projectName, mode, uiMode });
8
8
  * server.listen(18810, '127.0.0.1');
9
+ *
10
+ * uiMode controls which front-end is served:
11
+ * - "auto" (default): SPA if web-panel/dist exists, else embedded HTML
12
+ * - "full": SPA only — throws if web-panel/dist is missing
13
+ * - "minimal": embedded HTML only, even if SPA is available
14
+ *
15
+ * `cc pack` artifacts always set uiMode="full" so the bundled exe never
16
+ * silently degrades to the minimal HTML when assets failed to bundle.
9
17
  */
10
18
 
11
19
  import http from "http";
@@ -13,6 +21,7 @@ import fs from "fs";
13
21
  import path from "path";
14
22
  import { fileURLToPath } from "url";
15
23
  import { getInlineSource as getEnvelopeInlineSource } from "./web-ui-envelope.js";
24
+ import { CLISkillLoader } from "./skill-loader.js";
16
25
 
17
26
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
18
27
 
@@ -1209,6 +1218,67 @@ function findWebPanelDist(staticDir) {
1209
1218
  return null;
1210
1219
  }
1211
1220
 
1221
+ /**
1222
+ * Handle `/api/*` routes. Returns `true` if the request was handled (caller
1223
+ * must stop further processing) and `false` for everything else (caller
1224
+ * continues with the static/SPA handler).
1225
+ *
1226
+ * Currently exposes a single read-only endpoint:
1227
+ *
1228
+ * GET /api/skills → 200 { schema: 1, skills: [{name, source, category,
1229
+ * description, version}] }
1230
+ *
1231
+ * The smoke-runner's project-mode probe (packer/smoke-runner.js) hits this
1232
+ * to verify bundled skills were materialized and registered. The shape is
1233
+ * deliberately minimal — the Web UI's richer skill views use WS.
1234
+ *
1235
+ * @param {import("http").IncomingMessage} req
1236
+ * @param {import("http").ServerResponse} res
1237
+ * @returns {boolean} true if handled, false if the caller should continue
1238
+ */
1239
+ export function handleApiRequest(req, res) {
1240
+ const urlPath = req.url.split("?")[0];
1241
+ if (!urlPath.startsWith("/api/")) return false;
1242
+
1243
+ if (urlPath === "/api/skills") {
1244
+ if (req.method !== "GET") {
1245
+ res.writeHead(405, { "Content-Type": "application/json" });
1246
+ res.end(JSON.stringify({ error: "Method Not Allowed" }));
1247
+ return true;
1248
+ }
1249
+ try {
1250
+ const loader = new CLISkillLoader();
1251
+ const resolved = loader.loadAll();
1252
+ const skills = resolved.map((s) => ({
1253
+ name: s.id,
1254
+ displayName: s.displayName,
1255
+ source: s.source,
1256
+ category: s.category,
1257
+ description: s.description,
1258
+ version: s.version,
1259
+ }));
1260
+ res.writeHead(200, {
1261
+ "Content-Type": "application/json; charset=utf-8",
1262
+ "Cache-Control": "no-store",
1263
+ "X-Content-Type-Options": "nosniff",
1264
+ });
1265
+ res.end(JSON.stringify({ schema: 1, skills }));
1266
+ } catch (err) {
1267
+ res.writeHead(500, { "Content-Type": "application/json" });
1268
+ res.end(
1269
+ JSON.stringify({ error: "Skill load failed", detail: err.message }),
1270
+ );
1271
+ }
1272
+ return true;
1273
+ }
1274
+
1275
+ // Unknown /api/* path — explicit 404 so callers (smoke-runner) can tell
1276
+ // "endpoint not implemented" from "server broken".
1277
+ res.writeHead(404, { "Content-Type": "application/json" });
1278
+ res.end(JSON.stringify({ error: "Not Found", path: urlPath }));
1279
+ return true;
1280
+ }
1281
+
1212
1282
  /**
1213
1283
  * Create and return a Node.js HTTP server that serves the Web UI.
1214
1284
  *
@@ -1223,16 +1293,37 @@ function findWebPanelDist(staticDir) {
1223
1293
  * @param {string|null} opts.projectRoot
1224
1294
  * @param {string|null} opts.projectName
1225
1295
  * @param {"project"|"global"} opts.mode
1296
+ * @param {"auto"|"full"|"minimal"} [opts.uiMode="auto"]
1226
1297
  * @param {string|null} [opts.staticDir] - Optional override for dist directory
1227
1298
  * @returns {import("http").Server}
1228
1299
  */
1229
1300
  export function createWebUIServer(opts) {
1230
- const distDir = findWebPanelDist(opts.staticDir || null);
1301
+ const uiMode = opts.uiMode || "auto";
1302
+ if (uiMode !== "auto" && uiMode !== "full" && uiMode !== "minimal") {
1303
+ throw new Error(
1304
+ `Invalid uiMode "${uiMode}". Expected "auto", "full", or "minimal".`,
1305
+ );
1306
+ }
1307
+
1308
+ const distDir =
1309
+ uiMode === "minimal" ? null : findWebPanelDist(opts.staticDir || null);
1310
+
1311
+ if (uiMode === "full" && !distDir) {
1312
+ throw new Error(
1313
+ 'uiMode="full" requires a built web-panel dist directory, but none was found. ' +
1314
+ "Run `npm run build:web-panel` in packages/cli, or pass `staticDir` to override.",
1315
+ );
1316
+ }
1317
+
1231
1318
  const configJson = buildConfigJson(opts);
1232
1319
 
1233
1320
  if (distDir) {
1234
1321
  // ── Serve built Vue3 web panel ──────────────────────────────────────────
1235
1322
  return http.createServer((req, res) => {
1323
+ // /api/* routes are handled before the static file logic so GET /api/skills
1324
+ // doesn't accidentally resolve to index.html via the SPA fallback.
1325
+ if (handleApiRequest(req, res)) return;
1326
+
1236
1327
  if (req.method !== "GET") {
1237
1328
  res.writeHead(405, { "Content-Type": "text/plain" });
1238
1329
  res.end("Method Not Allowed");
@@ -1290,6 +1381,8 @@ export function createWebUIServer(opts) {
1290
1381
  // ── Fallback: embedded classic single-page HTML ─────────────────────────
1291
1382
  const html = buildHtml(opts);
1292
1383
  return http.createServer((req, res) => {
1384
+ if (handleApiRequest(req, res)) return;
1385
+
1293
1386
  const urlPath = req.url.split("?")[0];
1294
1387
  if (
1295
1388
  req.method !== "GET" ||
@@ -13,4 +13,5 @@
13
13
  export {
14
14
  ChainlessChainWSServer,
15
15
  tokenizeCommand,
16
+ isCommandBlocked,
16
17
  } from "../gateways/ws/ws-server.js";
@@ -495,6 +495,7 @@ export class AgentRuntime {
495
495
  projectName,
496
496
  mode,
497
497
  staticDir: this.policy.webPanelDir,
498
+ uiMode: this.policy.uiMode,
498
499
  });
499
500
 
500
501
  await new Promise((resolve, reject) => {
@@ -51,5 +51,6 @@ export function resolveUiPolicy(overrides = {}) {
51
51
  open: overrides.open !== false,
52
52
  token: overrides.token || null,
53
53
  webPanelDir: overrides.webPanelDir || null,
54
+ uiMode: overrides.uiMode || "auto",
54
55
  };
55
56
  }
@@ -1 +0,0 @@
1
- import{A as o}from"./Row-BFUWxIkx.js";import{w as t}from"./index-DaMG8ksh.js";import"./useConfigInject-ImjEZhXr.js";import"./vendor-C5RM7MZO.js";import"./icons-CpgFsfkd.js";import"./responsiveObserve-C5giLhLf.js";import"./useFlexGapSupport-Cd-PoTMl.js";import"./styleChecker-BzLSEXyu.js";import"./index-BFFb9yPd.js";const n=t(o);export{n as default};
@@ -1 +0,0 @@
1
- import{C as o}from"./Col-HJI40OzO.js";import{w as t}from"./index-DaMG8ksh.js";import"./useConfigInject-ImjEZhXr.js";import"./vendor-C5RM7MZO.js";import"./icons-CpgFsfkd.js";import"./index-BFFb9yPd.js";const l=t(o);export{l as default};