@dotobokuri/fleet-console 1.70.0 → 1.71.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.
Files changed (68) hide show
  1. package/dist/cli.mjs +78 -14
  2. package/dist/client/assets/{_baseUniq-81Y5kFtp.js → _baseUniq-Bhd1wyKE.js} +1 -1
  3. package/dist/client/assets/{arc-d96Fgk-5.js → arc-Cu9FOmUr.js} +1 -1
  4. package/dist/client/assets/{architectureDiagram-Q4EWVU46-BBgIklLp.js → architectureDiagram-Q4EWVU46-Dq9FA5I3.js} +1 -1
  5. package/dist/client/assets/{blockDiagram-DXYQGD6D-BCjY9dJk.js → blockDiagram-DXYQGD6D-BDZkCLI1.js} +1 -1
  6. package/dist/client/assets/{c4Diagram-AHTNJAMY-BVJRdf8q.js → c4Diagram-AHTNJAMY-XBPYwanu.js} +1 -1
  7. package/dist/client/assets/channel-BTX-SvBb.js +1 -0
  8. package/dist/client/assets/{chunk-4BX2VUAB-CT7BCzBg.js → chunk-4BX2VUAB-BbTQSLs0.js} +1 -1
  9. package/dist/client/assets/{chunk-4TB4RGXK-DUbDaHiY.js → chunk-4TB4RGXK-DukpmkUz.js} +1 -1
  10. package/dist/client/assets/{chunk-55IACEB6-C7HDwq0R.js → chunk-55IACEB6-BFyIa1Ge.js} +1 -1
  11. package/dist/client/assets/{chunk-EDXVE4YY-Mh16Bf7t.js → chunk-EDXVE4YY-D7RHsfQm.js} +1 -1
  12. package/dist/client/assets/{chunk-FMBD7UC4-D3BjqAun.js → chunk-FMBD7UC4-Co64dt-k.js} +1 -1
  13. package/dist/client/assets/{chunk-OYMX7WX6-DmzxPT1k.js → chunk-OYMX7WX6-CdycD6Yh.js} +1 -1
  14. package/dist/client/assets/{chunk-QZHKN3VN-CaZdQK6Z.js → chunk-QZHKN3VN-CNvTB1xu.js} +1 -1
  15. package/dist/client/assets/{chunk-YZCP3GAM-Q2hrlgLY.js → chunk-YZCP3GAM-CY1_pTO2.js} +1 -1
  16. package/dist/client/assets/classDiagram-6PBFFD2Q-CixB9uB6.js +1 -0
  17. package/dist/client/assets/classDiagram-v2-HSJHXN6E-CixB9uB6.js +1 -0
  18. package/dist/client/assets/clone-DuMuKBBB.js +1 -0
  19. package/dist/client/assets/{cose-bilkent-S5V4N54A-CPdIsNiQ.js → cose-bilkent-S5V4N54A-PLhfHDPQ.js} +1 -1
  20. package/dist/client/assets/{dagre-KV5264BT-BYWdUXO0.js → dagre-KV5264BT-gJnGByq0.js} +1 -1
  21. package/dist/client/assets/{diagram-5BDNPKRD-BC8rqmOK.js → diagram-5BDNPKRD-y48OdlFp.js} +1 -1
  22. package/dist/client/assets/{diagram-G4DWMVQ6-CES6iBQQ.js → diagram-G4DWMVQ6-DSLYc2V1.js} +1 -1
  23. package/dist/client/assets/{diagram-MMDJMWI5-BfhQrSaN.js → diagram-MMDJMWI5-DWgtcwcn.js} +1 -1
  24. package/dist/client/assets/{diagram-TYMM5635-C2FgHNxP.js → diagram-TYMM5635-CLd83m-G.js} +1 -1
  25. package/dist/client/assets/{erDiagram-SMLLAGMA-KQgKZTcf.js → erDiagram-SMLLAGMA-CGP2g7Ug.js} +1 -1
  26. package/dist/client/assets/{flowDiagram-DWJPFMVM-7Ytk1sX_.js → flowDiagram-DWJPFMVM-BhKzXbma.js} +1 -1
  27. package/dist/client/assets/{ganttDiagram-T4ZO3ILL-C4yl6vGV.js → ganttDiagram-T4ZO3ILL-BdpBmhZF.js} +1 -1
  28. package/dist/client/assets/{gitGraphDiagram-UUTBAWPF-DyN7iLk4.js → gitGraphDiagram-UUTBAWPF-IylczS5r.js} +1 -1
  29. package/dist/client/assets/{graph-BsOHL3Ty.js → graph-xlKIIKKy.js} +1 -1
  30. package/dist/client/assets/index-DH7xHFza.js +467 -0
  31. package/dist/client/assets/index-DKMT7UVt.css +1 -0
  32. package/dist/client/assets/{infoDiagram-42DDH7IO-BIOEjyDP.js → infoDiagram-42DDH7IO-Dm9-Iqxb.js} +1 -1
  33. package/dist/client/assets/{ishikawaDiagram-UXIWVN3A-C18t-DEH.js → ishikawaDiagram-UXIWVN3A-0tPrKKU3.js} +1 -1
  34. package/dist/client/assets/{journeyDiagram-VCZTEJTY-BysZXCkq.js → journeyDiagram-VCZTEJTY-3wTioYxO.js} +1 -1
  35. package/dist/client/assets/{kanban-definition-6JOO6SKY-Zdulrt1W.js → kanban-definition-6JOO6SKY-DjVcpuse.js} +1 -1
  36. package/dist/client/assets/{layout-DhT9CiMw.js → layout-BZ0nnXEn.js} +1 -1
  37. package/dist/client/assets/{linear-DQna1rOZ.js → linear-DNdW4gLV.js} +1 -1
  38. package/dist/client/assets/{mermaid.core-C0sjt4NJ.js → mermaid.core-DpO6yFcA.js} +4 -4
  39. package/dist/client/assets/{min-Dyx0ykEh.js → min-xMtTJxkM.js} +1 -1
  40. package/dist/client/assets/{mindmap-definition-QFDTVHPH-BzEvAUNd.js → mindmap-definition-QFDTVHPH-8tcRIKM5.js} +1 -1
  41. package/dist/client/assets/{pieDiagram-DEJITSTG-Cd4ORVnw.js → pieDiagram-DEJITSTG-DOuWZGrv.js} +1 -1
  42. package/dist/client/assets/{quadrantDiagram-34T5L4WZ-CvNxhBIm.js → quadrantDiagram-34T5L4WZ-BWPtgVdA.js} +1 -1
  43. package/dist/client/assets/{requirementDiagram-MS252O5E-CuGuie-o.js → requirementDiagram-MS252O5E-CkNpSYqR.js} +1 -1
  44. package/dist/client/assets/{sankeyDiagram-XADWPNL6-zn7buHsU.js → sankeyDiagram-XADWPNL6-DUPS39lN.js} +1 -1
  45. package/dist/client/assets/{sequenceDiagram-FGHM5R23-BxDReh8o.js → sequenceDiagram-FGHM5R23-B4n7l5jw.js} +1 -1
  46. package/dist/client/assets/{stateDiagram-FHFEXIEX-DtZ0UE1G.js → stateDiagram-FHFEXIEX-DBHsG7yj.js} +1 -1
  47. package/dist/client/assets/stateDiagram-v2-QKLJ7IA2--18aeGq2.js +1 -0
  48. package/dist/client/assets/{timeline-definition-GMOUNBTQ-DfKveVha.js → timeline-definition-GMOUNBTQ-BLxUyg3R.js} +1 -1
  49. package/dist/client/assets/{vennDiagram-DHZGUBPP-CZe6StT_.js → vennDiagram-DHZGUBPP-27NkxCen.js} +1 -1
  50. package/dist/client/assets/{wardley-RL74JXVD-B10WfMsc.js → wardley-RL74JXVD-VNgEaZlH.js} +1 -1
  51. package/dist/client/assets/{wardleyDiagram-NUSXRM2D-DzN04fy9.js → wardleyDiagram-NUSXRM2D-byIhDvPD.js} +1 -1
  52. package/dist/client/assets/{xychartDiagram-5P7HB3ND-CIgNRRNp.js → xychartDiagram-5P7HB3ND-D0amCilL.js} +1 -1
  53. package/dist/client/index.html +2 -2
  54. package/dist/fleet-plugins/file-explorer/routes.mjs +138 -96
  55. package/dist/fleet-plugins/ledger/routes.mjs +73 -12
  56. package/dist/fleet-plugins/quota/routes.mjs +73 -12
  57. package/dist/fleet-plugins/scuttlebutt/routes.mjs +74 -12
  58. package/dist/fleet-plugins/skills/routes.mjs +126 -60
  59. package/dist/fleet-plugins/terminal/routes.mjs +482 -69
  60. package/dist/fleet.mjs +479 -67
  61. package/package.json +1 -1
  62. package/dist/client/assets/channel-CiSQi5S5.js +0 -1
  63. package/dist/client/assets/classDiagram-6PBFFD2Q-Cy-ZC6xF.js +0 -1
  64. package/dist/client/assets/classDiagram-v2-HSJHXN6E-Cy-ZC6xF.js +0 -1
  65. package/dist/client/assets/clone-BAhE6Fq8.js +0 -1
  66. package/dist/client/assets/index-2049WCXu.css +0 -1
  67. package/dist/client/assets/index-DJcpFTNB.js +0 -466
  68. package/dist/client/assets/stateDiagram-v2-QKLJ7IA2-aYct9Owm.js +0 -1
@@ -17709,7 +17709,7 @@ var benchmarks_default = {
17709
17709
  };
17710
17710
  var models_default = {
17711
17711
  version: 1,
17712
- updatedAt: "2026-08-20T00:00:00Z",
17712
+ updatedAt: "2026-08-22T00:00:00Z",
17713
17713
  providers: {
17714
17714
  codex: {
17715
17715
  name: "Codex",
@@ -18120,7 +18120,7 @@ var models_default = {
18120
18120
  {
18121
18121
  modelId: "composer-2.5",
18122
18122
  name: "Composer-2.5",
18123
- capabilityClass: "flagship",
18123
+ capabilityClass: "standard",
18124
18124
  quotaScope: "auto",
18125
18125
  contextWindow: 2e5,
18126
18126
  benchmarkKey: "composer-2.5"
@@ -18128,9 +18128,11 @@ var models_default = {
18128
18128
  {
18129
18129
  modelId: "composer-2.5-fast",
18130
18130
  name: "Composer-2.5-Fast",
18131
- capabilityClass: "light",
18131
+ capabilityClass: "standard",
18132
+ variantOf: "composer-2.5",
18132
18133
  quotaScope: "auto",
18133
- contextWindow: 2e5
18134
+ contextWindow: 2e5,
18135
+ benchmarkKey: "composer-2.5"
18134
18136
  },
18135
18137
  {
18136
18138
  modelId: "grok-4.5",
@@ -18152,7 +18154,8 @@ var models_default = {
18152
18154
  {
18153
18155
  modelId: "grok-4.5-fast",
18154
18156
  name: "Grok-4.5-Fast",
18155
- capabilityClass: "light",
18157
+ capabilityClass: "flagship",
18158
+ variantOf: "grok-4.5",
18156
18159
  quotaScope: "auto",
18157
18160
  contextWindow: 256e3,
18158
18161
  effort: {
@@ -18163,7 +18166,8 @@ var models_default = {
18163
18166
  "high"
18164
18167
  ],
18165
18168
  upstreamModelIdTemplate: "cursor-grok-4.5-{effort}-fast"
18166
- }
18169
+ },
18170
+ benchmarkKey: "grok-4.5"
18167
18171
  },
18168
18172
  {
18169
18173
  modelId: "grok-4.6",
@@ -18186,7 +18190,8 @@ var models_default = {
18186
18190
  {
18187
18191
  modelId: "grok-4.6-fast",
18188
18192
  name: "Grok-4.6-Fast",
18189
- capabilityClass: "light",
18193
+ capabilityClass: "flagship",
18194
+ variantOf: "grok-4.6",
18190
18195
  quotaScope: "auto",
18191
18196
  contextWindow: 256e3,
18192
18197
  effort: {
@@ -18198,7 +18203,8 @@ var models_default = {
18198
18203
  "xhigh"
18199
18204
  ],
18200
18205
  upstreamModelIdTemplate: "cursor-grok-4.6-{effort}-fast"
18201
- }
18206
+ },
18207
+ benchmarkKey: "grok-4.6"
18202
18208
  },
18203
18209
  {
18204
18210
  modelId: "claude-opus-5",
@@ -18426,7 +18432,7 @@ var models_default = {
18426
18432
  opencode: {
18427
18433
  name: "OpenCode",
18428
18434
  defaultModel: "minimax-m3",
18429
- source: "live GET https://opencode.ai/zen/go/v1/models + per-model wire probes on /messages, /responses, /chat/completions (2026-08-03); contextWindow cross-checked against models.dev api.json opencode-go entries and oh-my-pi packages/catalog/src/models.json (2026-08-03, both agree). Excluded: mimo-v2-pro/mimo-v2-omni (upstream server_error), hy3-preview (listed but Router.ModelNotFound); superseded generations removed 2026-08-08 \u2014 the catalog keeps each lineup's current generation only",
18435
+ source: "live GET https://opencode.ai/zen/go/v1/models + per-model wire probes on /messages, /responses, /chat/completions (2026-08-03); contextWindow cross-checked against models.dev api.json opencode-go entries and oh-my-pi packages/catalog/src/models.json (2026-08-03, both agree). Excluded: mimo-v2-pro/mimo-v2-omni (upstream server_error), hy3-preview (listed but Router.ModelNotFound); superseded generations removed 2026-08-08 \u2014 the catalog keeps each lineup's current generation only; ox-alpha-free and muse-spark-1.2-contributor added 2026-08-21 from the same live list, each wire chosen by a streaming probe on that date (ox-alpha-free frames natively on /chat/completions and returns reasoning_content deltas; muse-spark-1.2-contributor frames natively on /responses and echoes its own reasoning block) and each effort ladder taken from the upstream's own rejection message (ox-alpha-free: low/high/max; muse-spark-1.2-contributor: none/minimal/low/medium/high/xhigh, so max is refused); their contextWindow comes from models.dev api.json opencode-go entries (2026-08-21). muse-spark-1.2-contributor's Responses backend accepts only tool_choice auto: named function choices, required, and none are refused with a 400 (2026-08-21), so a caller that forces a tool cannot use it",
18430
18436
  models: [
18431
18437
  {
18432
18438
  modelId: "minimax-m3",
@@ -18474,6 +18480,22 @@ var models_default = {
18474
18480
  },
18475
18481
  benchmarkKey: "grok-4.5"
18476
18482
  },
18483
+ {
18484
+ modelId: "muse-spark-1.2-contributor",
18485
+ name: "Muse-Spark-1.2-Contributor",
18486
+ capabilityClass: "flagship",
18487
+ wire: "responses",
18488
+ contextWindow: 1048576,
18489
+ effort: {
18490
+ supported: true,
18491
+ levels: [
18492
+ "low",
18493
+ "medium",
18494
+ "high",
18495
+ "xhigh"
18496
+ ]
18497
+ }
18498
+ },
18477
18499
  {
18478
18500
  modelId: "deepseek-v4-flash",
18479
18501
  name: "DeepSeek-V4-Flash",
@@ -18524,6 +18546,21 @@ var models_default = {
18524
18546
  capabilityClass: "flagship",
18525
18547
  wire: "chat-completions",
18526
18548
  contextWindow: 256e3
18549
+ },
18550
+ {
18551
+ modelId: "ox-alpha-free",
18552
+ name: "Ox-Alpha-Free",
18553
+ capabilityClass: "flagship",
18554
+ wire: "chat-completions",
18555
+ contextWindow: 1e6,
18556
+ effort: {
18557
+ supported: true,
18558
+ levels: [
18559
+ "low",
18560
+ "high",
18561
+ "max"
18562
+ ]
18563
+ }
18527
18564
  }
18528
18565
  ]
18529
18566
  },
@@ -18552,7 +18589,7 @@ var models_default = {
18552
18589
  {
18553
18590
  modelId: "grok-composer-2.5-fast",
18554
18591
  name: "Grok-Composer-2.5-Fast",
18555
- capabilityClass: "light",
18592
+ capabilityClass: "standard",
18556
18593
  wire: "responses",
18557
18594
  contextWindow: 2e5
18558
18595
  }
@@ -18747,6 +18784,14 @@ var GatewayModelEntrySchema = external_exports.object({
18747
18784
  benchmarkKey: external_exports.string().min(1).optional(),
18748
18785
  description: external_exports.string().min(1).optional(),
18749
18786
  providerModelId: external_exports.string().min(1).optional(),
18787
+ /**
18788
+ * The catalog entry this one is a serving variant of, when the provider gives
18789
+ * the variant its own wire id and `providerModelId` is therefore unavailable
18790
+ * as the lineage link. Pure provenance: it names a sibling `modelId` in the
18791
+ * same provider and never reaches a request, so the variant keeps sending its
18792
+ * own upstream id while inheriting the base's class and benchmark evidence.
18793
+ */
18794
+ variantOf: external_exports.string().min(1).optional(),
18750
18795
  serviceTier: external_exports.literal("priority").optional(),
18751
18796
  cursorMaxMode: external_exports.literal(true).optional(),
18752
18797
  quotaScope: external_exports.enum(GATEWAY_QUOTA_SCOPES).optional(),
@@ -18984,8 +19029,24 @@ function validateRegistry(value) {
18984
19029
  if (!isRoutingAlias && !model.capabilityClass) {
18985
19030
  throw new Error(`Gateway model is missing a capability class: ${provider}/${model.modelId}`);
18986
19031
  }
18987
- if (!isRoutingAlias && model.providerModelId) {
18988
- const base = definition.models.find((candidate) => candidate.modelId === model.providerModelId);
19032
+ const catalogEntry = (modelId) => modelId === void 0 ? void 0 : definition.models.find((candidate) => candidate.modelId === modelId);
19033
+ const providerLinkedBase = isRoutingAlias ? void 0 : catalogEntry(model.providerModelId);
19034
+ if (model.variantOf && providerLinkedBase && model.providerModelId !== model.variantOf) {
19035
+ throw new Error(`Gateway service-tier sibling names two different bases: ${provider}/${model.modelId}`);
19036
+ }
19037
+ if (model.variantOf === model.modelId) {
19038
+ throw new Error(`Gateway service-tier sibling names itself as its base: ${provider}/${model.modelId}`);
19039
+ }
19040
+ const baseModelId = isRoutingAlias ? void 0 : model.variantOf ?? model.providerModelId;
19041
+ if (baseModelId) {
19042
+ const base = catalogEntry(baseModelId);
19043
+ if (model.variantOf && !base) {
19044
+ throw new Error(`Gateway service-tier sibling names an unknown base: ${provider}/${model.modelId} -> ${model.variantOf}`);
19045
+ }
19046
+ const baseLink = base && base.modelId !== base.providerModelId ? base.variantOf ?? base.providerModelId : base?.variantOf;
19047
+ if (base && catalogEntry(baseLink)) {
19048
+ throw new Error(`Gateway service-tier sibling names another sibling as its base: ${provider}/${model.modelId}`);
19049
+ }
18989
19050
  if (base && base.capabilityClass !== model.capabilityClass) {
18990
19051
  throw new Error(`Gateway service-tier sibling class differs from its base: ${provider}/${model.modelId}`);
18991
19052
  }
@@ -20028,6 +20089,7 @@ function claudeGatewayLaunchEnv(inherited, options) {
20028
20089
  env.CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY = "1";
20029
20090
  env.ENABLE_TOOL_SEARCH = "true";
20030
20091
  env.CLAUDE_CODE_AUTO_COMPACT_WINDOW ??= "1000000";
20092
+ env.CLAUDE_CODE_MAX_SUBAGENT_SPAWN_DEPTH ??= "1";
20031
20093
  delete env.ANTHROPIC_AUTH_TOKEN;
20032
20094
  delete env.ANTHROPIC_API_KEY;
20033
20095
  delete env.ANTHROPIC_MODEL;
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import path from 'node:path';
3
3
  import { execFile } from 'node:child_process';
4
- import fs3 from 'node:fs/promises';
4
+ import fs4 from 'node:fs/promises';
5
5
  import os2 from 'node:os';
6
6
  import { existsSync, accessSync, constants } from 'node:fs';
7
7
  import crypto from 'node:crypto';
@@ -10,16 +10,16 @@ import crypto from 'node:crypto';
10
10
  function definePlugin(definition) {
11
11
  return definition;
12
12
  }
13
- function registerRouter(ctx, path6, handler, catalog) {
13
+ function registerRouter(ctx, path7, handler, catalog) {
14
14
  if (catalog) {
15
- ctx.registerRouter(normalizePath(path6), handler, catalog);
15
+ ctx.registerRouter(normalizePath(path7), handler, catalog);
16
16
  return;
17
17
  }
18
- ctx.registerRouter(normalizePath(path6), handler);
18
+ ctx.registerRouter(normalizePath(path7), handler);
19
19
  }
20
- function normalizePath(path6) {
21
- if (path6.length === 0 || path6 === "/") return "/";
22
- return path6;
20
+ function normalizePath(path7) {
21
+ if (path7.length === 0 || path7 === "/") return "/";
22
+ return path7;
23
23
  }
24
24
  var DEFAULT_WINDOWS_PATHEXT = ".COM;.EXE;.BAT;.CMD";
25
25
  var WINDOWS_PATH_SEPARATOR = ";";
@@ -161,14 +161,14 @@ async function ensureCliMjs(cliHome, onBootstrap) {
161
161
  const pkgPath = path.join(cliHome, "node_modules", SKILLS_PACKAGE, "package.json");
162
162
  let needsInstall = true;
163
163
  try {
164
- const raw = await fs3.readFile(pkgPath, "utf-8");
164
+ const raw = await fs4.readFile(pkgPath, "utf-8");
165
165
  const pkg = JSON.parse(raw);
166
166
  if (pkg.version === SKILLS_VERSION) needsInstall = false;
167
167
  } catch {
168
168
  }
169
169
  if (needsInstall) {
170
170
  onBootstrap?.(WARMUP_LINE);
171
- await fs3.mkdir(cliHome, { recursive: true });
171
+ await fs4.mkdir(cliHome, { recursive: true });
172
172
  await runNpmInstall(cliHome);
173
173
  }
174
174
  _cliMjsPath = mjsPath;
@@ -293,6 +293,55 @@ function getJobResult(id, cursor) {
293
293
  exitCode: job.exitCode
294
294
  };
295
295
  }
296
+ var HEAD_BYTES = 8192;
297
+ var MAX_DESCRIPTION = 500;
298
+ function parseSkillDescription(head) {
299
+ const text = head.charCodeAt(0) === 65279 ? head.slice(1) : head;
300
+ if (!/^---\r?\n/.test(text)) return void 0;
301
+ const body = text.slice(text.indexOf("\n") + 1);
302
+ const endIndex = body.search(/^---\s*$/m);
303
+ if (endIndex === -1) return void 0;
304
+ const lines = body.slice(0, endIndex).split(/\r?\n/);
305
+ const startLine = lines.findIndex((line) => /^description\s*:/.test(line));
306
+ if (startLine === -1) return void 0;
307
+ const first = (lines[startLine] ?? "").replace(/^description\s*:\s*/, "");
308
+ if (/^[>|]/.test(first.trim())) return void 0;
309
+ const parts = [first.trim()];
310
+ for (let i = startLine + 1; i < lines.length; i += 1) {
311
+ const line = lines[i] ?? "";
312
+ if (!/^\s+\S/.test(line)) break;
313
+ parts.push(line.trim());
314
+ }
315
+ let value = parts.filter(Boolean).join(" ").trim();
316
+ if (value.startsWith('"') && value.endsWith('"') && value.length > 1 || value.startsWith("'") && value.endsWith("'") && value.length > 1) {
317
+ value = value.slice(1, -1).trim();
318
+ }
319
+ if (!value) return void 0;
320
+ return value.length > MAX_DESCRIPTION ? value.slice(0, MAX_DESCRIPTION) : value;
321
+ }
322
+ async function readSkillDescription(skillRoot, allowedRoot) {
323
+ let handle;
324
+ try {
325
+ const [realAllowedRoot, realRoot] = await Promise.all([
326
+ fs4.realpath(allowedRoot),
327
+ fs4.realpath(skillRoot)
328
+ ]);
329
+ if (realRoot !== realAllowedRoot && !realRoot.startsWith(realAllowedRoot + path.sep)) {
330
+ return void 0;
331
+ }
332
+ const realMd = await fs4.realpath(path.join(realRoot, "SKILL.md"));
333
+ if (!realMd.startsWith(realRoot + path.sep)) return void 0;
334
+ handle = await fs4.open(realMd, "r");
335
+ const buffer = Buffer.alloc(HEAD_BYTES);
336
+ const { bytesRead } = await handle.read(buffer, 0, HEAD_BYTES, 0);
337
+ return parseSkillDescription(buffer.subarray(0, bytesRead).toString("utf-8"));
338
+ } catch {
339
+ return void 0;
340
+ } finally {
341
+ await handle?.close().catch(() => {
342
+ });
343
+ }
344
+ }
296
345
  var ProjectPathError = class extends Error {
297
346
  code;
298
347
  constructor(code) {
@@ -305,12 +354,12 @@ async function resolveProjectCwd(theaterRoot) {
305
354
  const lexicalRoot = path.resolve(theaterRoot);
306
355
  let realRoot;
307
356
  try {
308
- realRoot = await fs3.realpath(lexicalRoot);
357
+ realRoot = await fs4.realpath(lexicalRoot);
309
358
  } catch (error) {
310
359
  throw mapFilesystemError(error);
311
360
  }
312
361
  try {
313
- if (!(await fs3.stat(realRoot)).isDirectory()) {
362
+ if (!(await fs4.stat(realRoot)).isDirectory()) {
314
363
  throw new ProjectPathError("invalid_path");
315
364
  }
316
365
  } catch (error) {
@@ -402,30 +451,47 @@ var CLI_TIMEOUT_MS = 12e4;
402
451
  var USERINFO_URL_RE = /\b[a-z][a-z0-9+.-]*:\/\/[^\s/@]+@[^\s]+/gi;
403
452
  var TOKEN_URL_PARAM_RE = /([?&](?:access_?token|token|api_?key|apikey|auth(?:orization)?|password|secret|credential)=)[^&#\s]*/gi;
404
453
  var installedSkillsByTheater = /* @__PURE__ */ new Map();
454
+ function isRecord(value) {
455
+ return typeof value === "object" && value !== null && !Array.isArray(value);
456
+ }
457
+ function hasSourceEntry(table) {
458
+ return Object.values(table).some((entry) => isRecord(entry) && typeof entry["source"] === "string");
459
+ }
460
+ function isLockShape(parsed) {
461
+ if (!isRecord(parsed)) return false;
462
+ if ("skills" in parsed) return isRecord(parsed["skills"]);
463
+ return hasSourceEntry(parsed);
464
+ }
465
+ function collectLockSources(lock, sources) {
466
+ const table = isRecord(lock.skills) ? lock.skills : lock;
467
+ for (const [name, entry] of Object.entries(table)) {
468
+ if (isRecord(entry) && typeof entry["source"] === "string") {
469
+ sources.set(name, entry["source"]);
470
+ }
471
+ }
472
+ }
405
473
  async function readSkillSources(cwd) {
406
474
  const sources = /* @__PURE__ */ new Map();
407
- try {
408
- const raw = await fs3.readFile(path.join(cwd, "skills-lock.json"), "utf-8");
409
- const parsed = JSON.parse(raw);
410
- if (typeof parsed === "object" && parsed !== null) {
411
- for (const [name, entry] of Object.entries(parsed)) {
412
- if (entry && typeof entry.source === "string") sources.set(name, entry.source);
413
- }
414
- return sources;
475
+ for (const candidate of [
476
+ path.join(cwd, "skills-lock.json"),
477
+ path.join(cwd, ".agents", ".skill-lock.json")
478
+ ]) {
479
+ let raw;
480
+ try {
481
+ raw = await fs4.readFile(candidate, "utf-8");
482
+ } catch {
483
+ continue;
415
484
  }
416
- } catch {
417
- }
418
- try {
419
- const raw = await fs3.readFile(path.join(cwd, ".agents", ".skill-lock.json"), "utf-8");
420
- const parsed = JSON.parse(raw);
421
- if (typeof parsed === "object" && parsed !== null) {
422
- for (const [name, entry] of Object.entries(parsed)) {
423
- if (entry && typeof entry.source === "string") sources.set(name, entry.source);
485
+ try {
486
+ const parsed = JSON.parse(raw);
487
+ if (isLockShape(parsed)) {
488
+ collectLockSources(parsed, sources);
489
+ return { sources, lockRead: true };
424
490
  }
491
+ } catch {
425
492
  }
426
- } catch {
427
493
  }
428
- return sources;
494
+ return { sources, lockRead: false };
429
495
  }
430
496
  async function runListCommand(args, cwd, executor) {
431
497
  const result = await executor(args, { cwd, timeout: CLI_TIMEOUT_MS });
@@ -488,42 +554,42 @@ function spawnJobAsync(jobId, args, cwd, redactionPaths, executor) {
488
554
  }).then((result) => finishJob(jobId, result.exitCode)).catch(() => finishJob(jobId, 1));
489
555
  });
490
556
  }
557
+ async function toListItems(raw, scope, lock, allowedRoot) {
558
+ return Promise.all(raw.map(async (entry) => {
559
+ const description = entry.path ? await readSkillDescription(entry.path, allowedRoot) : void 0;
560
+ const source = lock.sources.get(entry.name);
561
+ const unmanaged = !source && lock.lockRead;
562
+ return {
563
+ name: entry.name,
564
+ scope,
565
+ agents: entry.agents,
566
+ ...source ? { source } : {},
567
+ ...unmanaged ? { unmanaged: true } : {},
568
+ ...description ? { description } : {},
569
+ displayPath: buildDisplayPath(scope, entry.name)
570
+ };
571
+ }));
572
+ }
491
573
  async function listInstalledSkills(projectCwd, executor) {
574
+ const homeDir = os2.homedir();
492
575
  if (projectCwd) {
493
576
  const [projectRaw, globalRaw2, projectSources, globalSources2] = await Promise.all([
494
577
  runListCommand(["list", "--json"], projectCwd, executor),
495
- runListCommand(["list", "-g", "--json"], os2.homedir(), executor),
578
+ runListCommand(["list", "-g", "--json"], homeDir, executor),
496
579
  readSkillSources(projectCwd),
497
- readSkillSources(os2.homedir())
580
+ readSkillSources(homeDir)
581
+ ]);
582
+ const [projectItems, globalItems] = await Promise.all([
583
+ toListItems(projectRaw, "project", projectSources, projectCwd),
584
+ toListItems(globalRaw2, "global", globalSources2, homeDir)
498
585
  ]);
499
- return [
500
- ...projectRaw.map((entry) => ({
501
- name: entry.name,
502
- scope: "project",
503
- agents: entry.agents,
504
- source: projectSources.get(entry.name),
505
- displayPath: buildDisplayPath("project", entry.name)
506
- })),
507
- ...globalRaw2.map((entry) => ({
508
- name: entry.name,
509
- scope: "global",
510
- agents: entry.agents,
511
- source: globalSources2.get(entry.name),
512
- displayPath: buildDisplayPath("global", entry.name)
513
- }))
514
- ];
586
+ return [...projectItems, ...globalItems];
515
587
  }
516
588
  const [globalRaw, globalSources] = await Promise.all([
517
- runListCommand(["list", "-g", "--json"], os2.homedir(), executor),
518
- readSkillSources(os2.homedir())
589
+ runListCommand(["list", "-g", "--json"], homeDir, executor),
590
+ readSkillSources(homeDir)
519
591
  ]);
520
- return globalRaw.map((entry) => ({
521
- name: entry.name,
522
- scope: "global",
523
- agents: entry.agents,
524
- source: globalSources.get(entry.name),
525
- displayPath: buildDisplayPath("global", entry.name)
526
- }));
592
+ return toListItems(globalRaw, "global", globalSources, homeDir);
527
593
  }
528
594
  async function handleList(req, res, ctx, executor) {
529
595
  if (req.method !== "GET") {
@@ -900,9 +966,9 @@ async function handleInstalledFile(req, res, ctx, executor) {
900
966
  // CLI가 보고한 skillRoot 자체도 신뢰하지 않는다 — scope의 정당한 상위 경계
901
967
  // (project=theater 루트, global=홈)를 벗어나면 읽지 않는다. `.agents/skills`로
902
968
  // 고정하지 않는 이유: claude 단독 설치는 `.claude/skills` 아래에 놓인다.
903
- fs3.realpath(cwd),
904
- fs3.realpath(skillRoot),
905
- fs3.realpath(skillMdPath).catch(() => null)
969
+ fs4.realpath(cwd),
970
+ fs4.realpath(skillRoot),
971
+ fs4.realpath(skillMdPath).catch(() => null)
906
972
  ]);
907
973
  if (!realMd) {
908
974
  ctx.host.http.writeJson(res, 404, { error: "skill_not_found" });
@@ -916,7 +982,7 @@ async function handleInstalledFile(req, res, ctx, executor) {
916
982
  ctx.host.http.writeJson(res, 403, { error: "path_outside_theater" });
917
983
  return;
918
984
  }
919
- const markdown = await fs3.readFile(realMd, "utf-8");
985
+ const markdown = await fs4.readFile(realMd, "utf-8");
920
986
  ctx.host.http.writeJson(res, 200, { markdown });
921
987
  } catch {
922
988
  ctx.host.http.writeJson(res, 502, { error: "read_failed" });