oasis_test 0.1.9 → 0.1.10

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 (2) hide show
  1. package/dist/index.js +180 -66
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1275,6 +1275,19 @@ var init_kernel = __esm({
1275
1275
  const payload = { owner: args.owner };
1276
1276
  await this.commit(args.artifactId, args.actor, "assign_owner", args.artifactId, payload);
1277
1277
  }
1278
+ // ── artifactType 目录(决策 0021):运行时可变的命令期策略,**不进 oplog、不参与 fold/重放** ──
1279
+ /** 当前注册的全部类型(视图/CLI 读活目录)。 */
1280
+ listTypes() {
1281
+ return [...this.schema.values()];
1282
+ }
1283
+ /** 增/改一个类型(按 name upsert)——只改命令期策略,重放不受影响(schema-free fold)。持久化由管理层调 store。 */
1284
+ setType(def) {
1285
+ this.schema.set(def.name, def);
1286
+ }
1287
+ /** 删一个类型(管理层先以 typeUsage.total===0 把关,避免孤儿存量节点命令期落空)。 */
1288
+ removeType(name) {
1289
+ this.schema.delete(name);
1290
+ }
1278
1291
  // ── 版本机(§5)────────────────────────────────────────────────────────
1279
1292
  /**
1280
1293
  * propose:进入队列(review 机制随 M4,proposed 即 approved 入队)。
@@ -16130,7 +16143,10 @@ async function buildDynamicWorkorderPlan(input) {
16130
16143
  ops.push({ action: "annotate", artifactId: briefId, body: workorderSpecBody(spec), blocks: false });
16131
16144
  }
16132
16145
  const spawned = [{ id: briefId, type: "brief", role: "pm", owner: briefOwner }];
16133
- const agentGraph = input.agentPlan ? normalizeAgentPlan(input.agentPlan, primaryType) : null;
16146
+ const agentGraph = input.agentPlan ? normalizeAgentPlan(input.agentPlan, schema.map((d) => d.name)) : null;
16147
+ const briefByType = new Map(
16148
+ (input.agentPlan?.nodes ?? []).filter((n) => typeof n.brief === "string" && n.brief.trim().length > 0).map((n) => [n.type.trim().toLowerCase(), n.brief.trim()])
16149
+ );
16134
16150
  if (input.agentPlan && !agentGraph) {
16135
16151
  issues.push({
16136
16152
  code: "planner_invalid",
@@ -16152,13 +16168,10 @@ async function buildDynamicWorkorderPlan(input) {
16152
16168
  continue;
16153
16169
  }
16154
16170
  const owner = await resolveRole(def.ownerRole, type);
16155
- if (!owner.actor) continue;
16171
+ const ownerActor = owner.actor ?? (type === primaryType ? input.actor : void 0);
16172
+ if (!ownerActor) continue;
16156
16173
  const id = type === primaryType ? primaryId : `artifact:${type}:${slug4}`;
16157
- availableNodes.push({ type, id, role: def.ownerRole, owner: owner.actor });
16158
- }
16159
- const hasPrimary = availableNodes.some((node) => node.id === primaryId);
16160
- if (!hasPrimary && schemaByName.has("prd")) {
16161
- availableNodes.unshift({ type: "prd", id: primaryId, role: "pm", owner: planner.actor ?? input.actor });
16174
+ availableNodes.push({ type, id, role: def.ownerRole, owner: ownerActor });
16162
16175
  }
16163
16176
  for (const node of availableNodes) {
16164
16177
  const inputs = inputEdgesFor(node.type, availableNodes, briefId, agentGraph?.dependsOn);
@@ -16174,7 +16187,7 @@ async function buildDynamicWorkorderPlan(input) {
16174
16187
  ops.push({
16175
16188
  action: "annotate",
16176
16189
  artifactId: node.id,
16177
- body: taskBookFor(node.type, input.description, node.role, issues, agentGraph?.notes),
16190
+ body: briefByType.get(node.type) ?? taskBookFor(node.type, input.description, node.role, issues, agentGraph?.notes),
16178
16191
  blocks: false
16179
16192
  });
16180
16193
  spawned.push(node);
@@ -16193,7 +16206,7 @@ async function buildDynamicWorkorderPlan(input) {
16193
16206
  ops
16194
16207
  },
16195
16208
  rootArtifactId: briefId,
16196
- primaryArtifactId: primaryId,
16209
+ primaryArtifactId: availableNodes[0]?.id ?? briefId,
16197
16210
  issues: dedupeIssues(issues),
16198
16211
  spawned,
16199
16212
  fallback: issues.length > 0
@@ -16231,23 +16244,17 @@ function buildPmAgentPlannerPrompt(input) {
16231
16244
  "",
16232
16245
  JSON.stringify({
16233
16246
  nodes: [
16234
- { type: "prd", dependsOn: ["brief"], title: "\u9700\u6C42\u4E0E\u8303\u56F4\u8BF4\u660E" },
16235
- { type: "adr", dependsOn: ["prd"], title: "\u6280\u672F\u65B9\u6848" },
16236
- { type: "dev", dependsOn: ["adr"], title: "\u5B9E\u73B0" },
16237
- { type: "qa", dependsOn: ["dev"], title: "\u9A8C\u6536" }
16247
+ { type: "prd", dependsOn: ["brief"], title: "\u9700\u6C42\u4E0E\u8303\u56F4\u8BF4\u660E", brief: "\u8FD9\u4E2A\u8282\u70B9\u5177\u4F53\u8981\u505A\u4EC0\u4E48\u7684\u89C4\u683C\uFF1A\u4EA7\u51FA\u4EC0\u4E48\u3001\u8981\u70B9\u3001\u7EA6\u675F" },
16248
+ { type: "dev", dependsOn: ["prd"], title: "\u5B9E\u73B0", brief: "\u2026" }
16238
16249
  ],
16239
16250
  notes: ["\u53EF\u9009\uFF1A\u89C4\u5212\u7406\u7531\u6216\u98CE\u9669\u63D0\u793A"]
16240
16251
  }, null, 2),
16241
16252
  "",
16242
16253
  "\u89C4\u5219\uFF1A",
16243
16254
  "- brief \u6839\u8282\u70B9\u7531\u7CFB\u7EDF\u56FA\u5B9A\u521B\u5EFA\uFF0C\u4F60\u4E0D\u8981\u628A brief \u5199\u8FDB nodes\u3002",
16244
- "- type \u53EA\u80FD\u4ECE ArtifactType \u76EE\u5F55\u91CC\u9009\uFF1Aprd\u3001research\u3001design\u3001adr\u3001dev\u3001qa\u3002",
16245
- "- \u7B80\u5355\u6587\u6848/\u65B9\u6848\u7C7B\u9700\u6C42\u53EF\u4EE5\u53EA\u8981 prd\u3002",
16246
- "- \u6D89\u53CA\u8C03\u7814/\u7ADE\u54C1/\u5E02\u573A/\u7528\u6237\u8BBF\u8C08\u65F6\u52A0 research\u3002",
16247
- "- \u6D89\u53CA UI/UX/Figma/\u9875\u9762/\u4EA4\u4E92\u65F6\u52A0 design\u3002",
16248
- "- \u6D89\u53CA\u67B6\u6784/API/\u6570\u636E\u6A21\u578B/\u5B89\u5168/\u6280\u672F\u9009\u578B\u65F6\u52A0 adr\u3002",
16249
- "- \u6D89\u53CA\u5F00\u53D1/\u4EE3\u7801/\u524D\u7AEF/\u540E\u7AEF/\u6570\u636E\u5E93/bug \u4FEE\u590D/\u96C6\u6210\u65F6\u52A0 dev\u3002",
16250
- "- \u6D89\u53CA\u6D4B\u8BD5/\u9A8C\u6536/\u56DE\u5F52\uFF0C\u6216\u521B\u5EFA\u4E86 dev \u65F6\uFF0C\u52A0 qa\u3002",
16255
+ "- type \u53EA\u80FD\u4ECE\u4E0A\u9762\u3010\u53EF\u7528 ArtifactType\u3011\u76EE\u5F55\u91CC\u9009\uFF08\u4EE5\u6CE8\u5165\u76EE\u5F55\u4E3A\u51C6\uFF0C\u4E0D\u9650\u4E8E\u67D0\u51E0\u4E2A\u56FA\u5B9A\u7C7B\u578B\uFF09\u3002",
16256
+ "- \u6BCF\u4E2A\u8282\u70B9\u5199 brief\uFF1A\u8FD9\u4E2A\u8282\u70B9\u5177\u4F53\u8981\u505A\u4EC0\u4E48\u7684\u89C4\u683C\uFF08\u4EA7\u51FA\u4EC0\u4E48\u3001\u8981\u70B9\u3001\u7EA6\u675F\uFF09\uFF0C\u522B\u5957\u6A21\u677F\u3001\u522B\u53EA\u5199\u7C7B\u578B\u540D\u3002",
16257
+ "- \u7B80\u5355\u9700\u6C42\u53EF\u4EE5\u53EA\u8981 prd\uFF1B\u6709\u8C03\u7814/\u8BBE\u8BA1/\u5F00\u53D1/\u6D4B\u8BD5/\u67B6\u6784\u9700\u8981\u65F6\u518D\u52A0\u5BF9\u5E94\u7C7B\u578B\u7684\u8282\u70B9\u3002",
16251
16258
  "- dependsOn \u4F7F\u7528 type \u540D\uFF0C\u5141\u8BB8\u5F15\u7528 brief\u3002"
16252
16259
  ].join("\n");
16253
16260
  }
@@ -16268,7 +16275,8 @@ function parseAgentWorkorderPlan(text) {
16268
16275
  nodes.push({
16269
16276
  type: n.type.trim().toLowerCase(),
16270
16277
  ...Array.isArray(n.dependsOn) ? { dependsOn: n.dependsOn.filter((x2) => typeof x2 === "string").map((x2) => x2.trim().toLowerCase()).filter(Boolean) } : {},
16271
- ...typeof n.title === "string" && n.title.trim() ? { title: n.title.trim() } : {}
16278
+ ...typeof n.title === "string" && n.title.trim() ? { title: n.title.trim() } : {},
16279
+ ...typeof n.brief === "string" && n.brief.trim() ? { brief: n.brief.trim() } : {}
16272
16280
  });
16273
16281
  }
16274
16282
  if (nodes.length === 0) return null;
@@ -16343,8 +16351,8 @@ function desiredArtifacts(primaryType, description) {
16343
16351
  if (needsQa) wanted.add("qa");
16344
16352
  return ["prd", "research", "design", "adr", "dev", "qa"].filter((type) => wanted.has(type));
16345
16353
  }
16346
- function normalizeAgentPlan(plan, primaryType) {
16347
- const allowed = ["prd", "research", "design", "adr", "dev", "qa"];
16354
+ function normalizeAgentPlan(plan, validTypes) {
16355
+ const allowed = validTypes.map((t) => t.trim().toLowerCase()).filter((t) => t !== "brief");
16348
16356
  const types2 = [];
16349
16357
  const dependsOn = /* @__PURE__ */ new Map();
16350
16358
  for (const node of plan.nodes) {
@@ -16353,8 +16361,6 @@ function normalizeAgentPlan(plan, primaryType) {
16353
16361
  if (!types2.includes(type)) types2.push(type);
16354
16362
  if (node.dependsOn?.length) dependsOn.set(type, [...new Set(node.dependsOn.map((dep) => dep.trim().toLowerCase()).filter(Boolean))]);
16355
16363
  }
16356
- const normalizedPrimary = primaryType === "brief" ? "prd" : primaryType;
16357
- if (!types2.includes(normalizedPrimary)) types2.unshift(normalizedPrimary);
16358
16364
  const filtered = allowed.filter((type) => types2.includes(type));
16359
16365
  return filtered.length > 0 ? { types: filtered, dependsOn, ...plan.notes?.length ? { notes: plan.notes } : {} } : null;
16360
16366
  }
@@ -16765,7 +16771,14 @@ async function runCommand(kernel, blobs, oplog, actor, command, args, ctx = {})
16765
16771
  ${acceptanceCriteria.map((c) => `- ${c}`).join("\n")}` : brief;
16766
16772
  const agentIssues = [];
16767
16773
  let agentPlan = null;
16768
- if (ctx.planWorkorderWithAgent) {
16774
+ const inlinePlan = args["plan"];
16775
+ if (inlinePlan !== void 0 && inlinePlan !== null) {
16776
+ agentPlan = parseAgentWorkorderPlan(typeof inlinePlan === "string" ? inlinePlan : JSON.stringify(inlinePlan));
16777
+ if (!agentPlan) {
16778
+ agentIssues.push({ code: "planner_invalid", severity: "warning", role: "pm", message: "\u524D\u7AEF\u63D0\u4F9B\u7684 plan \u7ED3\u6784\u65E0\u6548\uFF0C\u5DF2\u56DE\u9000\u5230\u89C4\u5219 planner\u3002" });
16779
+ }
16780
+ }
16781
+ if (agentPlan === null && ctx.planWorkorderWithAgent) {
16769
16782
  try {
16770
16783
  const availableActors = ctx.registry ? (await ctx.registry.listActors({ status: "active" })).map((a) => ({
16771
16784
  id: a.id,
@@ -17324,6 +17337,25 @@ async function startOasisServer(opts) {
17324
17337
  res.writeHead(result.status, headers).end(JSON.stringify(result.body ?? null));
17325
17338
  return;
17326
17339
  }
17340
+ if (url.pathname === "/api/nodes/exchange" && req.method === "POST") {
17341
+ const matched2 = domainRouter.match(req.method, url.pathname);
17342
+ if (!matched2) {
17343
+ res.writeHead(404).end();
17344
+ return;
17345
+ }
17346
+ const chunks2 = [];
17347
+ for await (const chunk of req) chunks2.push(chunk);
17348
+ const rawBody2 = Buffer.concat(chunks2).toString("utf8");
17349
+ const out = await matched2.handler({
17350
+ params: matched2.params,
17351
+ query: url.searchParams,
17352
+ body: rawBody2 ? JSON.parse(rawBody2) : void 0,
17353
+ auth: { actor: "actor:system:enroll" },
17354
+ raw: req
17355
+ });
17356
+ res.writeHead(out.status, { "content-type": "application/json" }).end(JSON.stringify(out.body ?? null));
17357
+ return;
17358
+ }
17327
17359
  let actor;
17328
17360
  if (opts.resolvePrincipal) {
17329
17361
  actor = await opts.resolvePrincipal(req.headers);
@@ -18101,31 +18133,48 @@ function createNodeTokenStore(file) {
18101
18133
  }
18102
18134
  };
18103
18135
  }
18104
- function createEnrollTokenStore(defaultTtlMs = 30 * 60 * 1e3) {
18136
+ function createEnrollTokenStore(defaultTtlMs = 30 * 60 * 1e3, file) {
18137
+ const read = () => {
18138
+ if (!file) return table;
18139
+ try {
18140
+ return new Map(Object.entries(JSON.parse(fs.readFileSync(file, "utf8"))));
18141
+ } catch {
18142
+ return /* @__PURE__ */ new Map();
18143
+ }
18144
+ };
18145
+ const write = (m2) => {
18146
+ if (!file) return;
18147
+ fs.writeFileSync(file, JSON.stringify(Object.fromEntries(m2), null, 2), { mode: 384 });
18148
+ };
18105
18149
  const table = /* @__PURE__ */ new Map();
18106
18150
  const prune = () => {
18151
+ const m2 = read();
18107
18152
  const now = Date.now();
18108
- for (const [token, entry] of table) if (entry.expiresAt <= now) table.delete(token);
18153
+ for (const [token, entry] of m2) if (entry.expiresAt <= now) m2.delete(token);
18154
+ write(m2);
18155
+ return m2;
18109
18156
  };
18110
18157
  return {
18111
18158
  issue(nodeId, ttlMs = defaultTtlMs) {
18112
- prune();
18159
+ const m2 = prune();
18113
18160
  const token = `ent_${(0, import_node_crypto3.randomBytes)(24).toString("base64url")}`;
18114
18161
  const expiresAt = Date.now() + ttlMs;
18115
- table.set(token, { nodeId, expiresAt });
18162
+ m2.set(token, { nodeId, expiresAt });
18163
+ write(m2);
18116
18164
  return { token, expiresAt: new Date(expiresAt).toISOString() };
18117
18165
  },
18118
18166
  peek(token) {
18119
- prune();
18120
- const entry = table.get(token);
18167
+ const m2 = prune();
18168
+ const entry = m2.get(token);
18121
18169
  if (!entry) return null;
18122
18170
  return { nodeId: entry.nodeId };
18123
18171
  },
18124
18172
  consume(token) {
18125
- prune();
18126
- const entry = table.get(token);
18173
+ const m2 = prune();
18174
+ const entry = m2.get(token);
18127
18175
  if (!entry) return null;
18128
- table.delete(token);
18176
+ m2.delete(token);
18177
+ write(m2);
18129
18178
  return { nodeId: entry.nodeId };
18130
18179
  }
18131
18180
  };
@@ -18328,6 +18377,13 @@ var init_memory_registry_store = __esm({
18328
18377
  }
18329
18378
  });
18330
18379
 
18380
+ // ../testkit/src/memory-type-registry-store.ts
18381
+ var init_memory_type_registry_store = __esm({
18382
+ "../testkit/src/memory-type-registry-store.ts"() {
18383
+ "use strict";
18384
+ }
18385
+ });
18386
+
18331
18387
  // ../testkit/src/memory-trace-store.ts
18332
18388
  var MemoryTraceStore;
18333
18389
  var init_memory_trace_store = __esm({
@@ -18846,6 +18902,7 @@ var init_src3 = __esm({
18846
18902
  init_memory_oplog_store();
18847
18903
  init_fake_adapter();
18848
18904
  init_memory_registry_store();
18905
+ init_memory_type_registry_store();
18849
18906
  init_memory_trace_store();
18850
18907
  init_memory_automation_store();
18851
18908
  init_memory_control_plane_store();
@@ -21007,10 +21064,22 @@ var init_company_engine_router = __esm({
21007
21064
  has(companyId) {
21008
21065
  return this.cache.has(companyId);
21009
21066
  }
21067
+ /** 当前已持有(缓存)的全部活引擎——不触发懒建。给"全局类型变更传播到每个活内核"用(决策 0021)。 */
21068
+ liveEngines() {
21069
+ return [...this.cache.values()];
21070
+ }
21010
21071
  };
21011
21072
  }
21012
21073
  });
21013
21074
 
21075
+ // ../server/src/types-admin.ts
21076
+ var init_types_admin = __esm({
21077
+ "../server/src/types-admin.ts"() {
21078
+ "use strict";
21079
+ init_src2();
21080
+ }
21081
+ });
21082
+
21014
21083
  // ../server/src/file-engine.ts
21015
21084
  async function openFileEngine(dir, seed = {}) {
21016
21085
  fs4.mkdirSync(dir, { recursive: true });
@@ -27685,18 +27754,21 @@ function nodesDomain(deps) {
27685
27754
  enrolledAt: existing?.enrolledAt ?? now(),
27686
27755
  ...d.meta.nodeName ? { name: d.meta.nodeName } : existing?.name ? { name: existing.name } : {}
27687
27756
  });
27688
- const capabilities = d.meta.runtimes?.length ? d.meta.runtimes : d.meta.adapters.map((kind) => ({ kind, binary: void 0, version: void 0 }));
27689
- for (const cap of capabilities) {
27690
- await nodeStore.upsertRuntime({
27691
- id: `runtime:${daemonId}:${cap.kind}`,
27692
- nodeId: daemonId,
27693
- kind: cap.kind,
27694
- hostname: d.meta.hostname,
27695
- ...cap.binary ? { binary: cap.binary } : {},
27696
- ...cap.version ? { version: cap.version } : {},
27697
- status: "online",
27698
- lastSeenAt: now()
27699
- });
27757
+ if (d.meta.runtimes) {
27758
+ for (const cap of d.meta.runtimes) {
27759
+ await nodeStore.upsertRuntime({
27760
+ id: `runtime:${daemonId}:${cap.kind}`,
27761
+ nodeId: daemonId,
27762
+ kind: cap.kind,
27763
+ hostname: d.meta.hostname,
27764
+ ...cap.binary ? { binary: cap.binary } : {},
27765
+ ...cap.version ? { version: cap.version } : {},
27766
+ status: "online",
27767
+ lastSeenAt: now()
27768
+ });
27769
+ }
27770
+ } else {
27771
+ await nodeStore.setNodeRuntimesOnline(daemonId, true);
27700
27772
  }
27701
27773
  });
27702
27774
  hub.onDaemonDisconnected = async (daemonId) => {
@@ -27718,18 +27790,21 @@ function nodesDomain(deps) {
27718
27790
  enrolledAt: existing?.enrolledAt ?? (/* @__PURE__ */ new Date()).toISOString(),
27719
27791
  ...d.meta.nodeName ? { name: d.meta.nodeName } : existing?.name ? { name: existing.name } : {}
27720
27792
  });
27721
- const capabilities = d.meta.runtimes?.length ? d.meta.runtimes : d.meta.adapters.map((kind) => ({ kind, binary: void 0, version: void 0 }));
27722
- for (const cap of capabilities) {
27723
- await deps.nodeStore.upsertRuntime({
27724
- id: `runtime:${daemonId}:${cap.kind}`,
27725
- nodeId: daemonId,
27726
- kind: cap.kind,
27727
- hostname: d.meta.hostname,
27728
- ...cap.binary ? { binary: cap.binary } : {},
27729
- ...cap.version ? { version: cap.version } : {},
27730
- status: "online",
27731
- lastSeenAt: (/* @__PURE__ */ new Date()).toISOString()
27732
- });
27793
+ if (d.meta.runtimes) {
27794
+ for (const cap of d.meta.runtimes) {
27795
+ await deps.nodeStore.upsertRuntime({
27796
+ id: `runtime:${daemonId}:${cap.kind}`,
27797
+ nodeId: daemonId,
27798
+ kind: cap.kind,
27799
+ hostname: d.meta.hostname,
27800
+ ...cap.binary ? { binary: cap.binary } : {},
27801
+ ...cap.version ? { version: cap.version } : {},
27802
+ status: "online",
27803
+ lastSeenAt: (/* @__PURE__ */ new Date()).toISOString()
27804
+ });
27805
+ }
27806
+ } else {
27807
+ await deps.nodeStore.setNodeRuntimesOnline(daemonId, true);
27733
27808
  }
27734
27809
  }
27735
27810
  router.get("/api/nodes", async () => {
@@ -27810,6 +27885,7 @@ function nodesDomain(deps) {
27810
27885
  const consumed = deps.enrollTokens.consume(enrollToken);
27811
27886
  if (!consumed) return { status: 401, body: { error: "invalid or expired enroll token" } };
27812
27887
  const nodeId = requestedNodeId;
27888
+ await deps.nodeStore.clearDeletedRuntimesForNode(nodeId);
27813
27889
  const existing = deps.nodeTokens.list().find((e) => e.nodeId === nodeId);
27814
27890
  const token = existing?.token ?? deps.nodeTokens.issue(nodeId);
27815
27891
  return { status: 200, body: { nodeId, token } };
@@ -27905,6 +27981,11 @@ var init_node_store = __esm({
27905
27981
  if (k2.startsWith(`runtime:${id}:`)) this.deletedRuntimeIds.delete(k2);
27906
27982
  }
27907
27983
  }
27984
+ async clearDeletedRuntimesForNode(nodeId) {
27985
+ for (const id of [...this.deletedRuntimeIds]) {
27986
+ if (id.startsWith(`runtime:${nodeId}:`)) this.deletedRuntimeIds.delete(id);
27987
+ }
27988
+ }
27908
27989
  async deleteRuntime(id) {
27909
27990
  this.deletedRuntimeIds.add(id);
27910
27991
  const rt = this.runtimes.get(id);
@@ -27966,6 +28047,10 @@ var init_node_store = __esm({
27966
28047
  await super.deleteNode(id);
27967
28048
  this.flush();
27968
28049
  }
28050
+ async clearDeletedRuntimesForNode(nodeId) {
28051
+ await super.clearDeletedRuntimesForNode(nodeId);
28052
+ this.flush();
28053
+ }
27969
28054
  async deleteRuntime(id) {
27970
28055
  const r = await super.deleteRuntime(id);
27971
28056
  this.flush();
@@ -31122,6 +31207,7 @@ var init_src4 = __esm({
31122
31207
  init_dev_store();
31123
31208
  init_control_plane_file_store();
31124
31209
  init_company_engine_router();
31210
+ init_types_admin();
31125
31211
  init_file_engine();
31126
31212
  init_daemon_hub();
31127
31213
  init_router();
@@ -40698,7 +40784,7 @@ async function startServe(opts) {
40698
40784
  const operatorToken = issuer.issue(operatorActor);
40699
40785
  fs13.writeFileSync(path11.join(opts.dir, "operator-token"), operatorToken, { mode: 384 });
40700
40786
  const nodeTokens = createNodeTokenStore(path11.join(opts.dir, "node-tokens.json"));
40701
- const enrollTokens = createEnrollTokenStore();
40787
+ const enrollTokens = createEnrollTokenStore(30 * 60 * 1e3, path11.join(opts.dir, "enroll-tokens.json"));
40702
40788
  const nodeStore = await FileNodeStore.open(path11.join(opts.dir, "nodes.json"));
40703
40789
  const registryAuditFile = path11.join(opts.dir, "registry-audit.ndjson");
40704
40790
  const registryListeners = /* @__PURE__ */ new Set();
@@ -40971,7 +41057,13 @@ async function startServe(opts) {
40971
41057
  // 暴露注册的 artifact 类型,给前端「发起工单」动态拉取根产物可选类型——schema.json 增改即自动扩展,
40972
41058
  // 前端不再写死枚举。走通用 /api/view(generic data),非域路由契约,不进 gen:api。
40973
41059
  views: {
40974
- "artifact-types": () => schema.map((d) => ({ name: d.name, ownerRole: d.ownerRole, contentType: d.contentType })),
41060
+ "artifact-types": () => schema.map((d) => ({
41061
+ name: d.name,
41062
+ ...d.description ? { description: d.description } : {},
41063
+ contentType: d.contentType,
41064
+ ownerRole: d.ownerRole,
41065
+ status: d.status ?? "active"
41066
+ })),
40975
41067
  dispatches: () => ({
40976
41068
  recent: journalRing.slice(-100),
40977
41069
  fused: dispatcher?.fusedJobs() ?? [],
@@ -41441,7 +41533,7 @@ function log2(tag, msg, extra) {
41441
41533
  var RECONNECT_DELAY_MS = 3e3;
41442
41534
  var PING_INTERVAL_MS = 2e4;
41443
41535
  var DaemonWsClient = class {
41444
- constructor(serverUrl, daemonId, sessions, adapters = ["claude-code"], token, runtimes = adapters.map((kind) => ({ kind })), nodeName) {
41536
+ constructor(serverUrl, daemonId, sessions, adapters = ["claude-code"], token, runtimes = adapters.map((kind) => ({ kind })), nodeName, reportRuntimes = false) {
41445
41537
  this.serverUrl = serverUrl;
41446
41538
  this.daemonId = daemonId;
41447
41539
  this.sessions = sessions;
@@ -41449,6 +41541,7 @@ var DaemonWsClient = class {
41449
41541
  this.token = token;
41450
41542
  this.runtimes = runtimes;
41451
41543
  this.nodeName = nodeName;
41544
+ this.reportRuntimes = reportRuntimes;
41452
41545
  }
41453
41546
  ws = null;
41454
41547
  pingTimer = null;
@@ -41535,7 +41628,7 @@ var DaemonWsClient = class {
41535
41628
  if (this.ws?.readyState === import_websocket.default.OPEN) this.ws.send(JSON.stringify(msg));
41536
41629
  }
41537
41630
  buildMeta() {
41538
- return { hostname: (0, import_node_os2.hostname)(), adapters: this.adapters, runtimes: this.runtimes, nodeVersion: process.version, ...this.nodeName ? { nodeName: this.nodeName } : {} };
41631
+ return { hostname: (0, import_node_os2.hostname)(), adapters: this.adapters, ...this.reportRuntimes ? { runtimes: this.runtimes } : {}, nodeVersion: process.version, ...this.nodeName ? { nodeName: this.nodeName } : {} };
41539
41632
  }
41540
41633
  };
41541
41634
 
@@ -41637,7 +41730,7 @@ async function startNode(opts) {
41637
41730
  claude
41638
41731
  )
41639
41732
  );
41640
- const client = new DaemonWsClient(opts.serverUrl, opts.nodeId, sessions, adapters, opts.token, runtimes, opts.nodeName);
41733
+ const client = new DaemonWsClient(opts.serverUrl, opts.nodeId, sessions, adapters, opts.token, runtimes, opts.nodeName, opts.reportRuntimes ?? false);
41641
41734
  console.log(`[oasis node] ${opts.nodeId} \u2192 ${opts.serverUrl}`);
41642
41735
  client.start();
41643
41736
  await new Promise((resolve3) => {
@@ -41703,6 +41796,7 @@ var USAGE = `oasis \u2014\u2014 artifact-centric \u534F\u4F5C\u5185\u6838 CLI\uF
41703
41796
  gc # \u5B64\u513F blob \u56DE\u6536\uFF08mark-and-sweep\uFF1B\u64CD\u4F5C\u8005\u547D\u4EE4\uFF0CF1\uFF09
41704
41797
  \u8BFB\u4EA7\u7269: status <id>\uFF08\u72B6\u6001\u7D22\u5F15\uFF0C\u4E0D\u542B\u6B63\u6587\uFF09/ content <id> [path]\uFF08\u5B9E\u9645\u5185\u5BB9\uFF0C\u591A\u6587\u4EF6\u7ED9 path \u53D6\u5355\u4E2A\uFF09/ projects [--search q]\uFF08\u5217/\u641C\u9879\u76EE\uFF09/ project <id>\uFF08\u9879\u76EE\u76EE\u5F55\uFF09 # \u7ED9 agent\uFF0C\u6E10\u8FDB\u62AB\u9732
41705
41798
  graph / show / log / ls # \u5168\u5C40/\u8BE6\u5C3D\u89C6\u56FE\uFF08\u534F\u8C03\u8005\u3001\u4EBA\uFF1Bls \u4EC5\u534F\u8C03\u8005\uFF09
41799
+ artifact-types [list | search <q>] # \u770B/\u68C0\u7D22\u5DF2\u6CE8\u518C\u7684 artifact \u7C7B\u578B\uFF08\u7528\u9014/owner/\u72B6\u6001\uFF09\u2014\u2014\u7ED9\u4EBA\u548C agent \u9009 type
41706
41800
 
41707
41801
  \u6CBB\u7406\uFF08\xA711\uFF09
41708
41802
  reopen <artifactId> [--note t] # \u89E3\u5C01\uFF08lifecycle \u2192 active\uFF09
@@ -42656,6 +42750,23 @@ async function runCli(argv, println = console.log) {
42656
42750
  }
42657
42751
  break;
42658
42752
  }
42753
+ case "artifact-types": {
42754
+ const sub = positional[0] ?? "list";
42755
+ const all = await api.view(
42756
+ "artifact-types"
42757
+ );
42758
+ const q = sub === "search" ? positional[1]?.toLowerCase() : void 0;
42759
+ const rows = q ? all.filter((t) => t.name.toLowerCase().includes(q) || (t.description ?? "").toLowerCase().includes(q)) : all;
42760
+ if (rows.length === 0) {
42761
+ println("\uFF08\u65E0\u5339\u914D\u7684 artifact \u7C7B\u578B\uFF09");
42762
+ break;
42763
+ }
42764
+ for (const t of rows) {
42765
+ const dep = t.status === "deprecated" ? " [\u5DF2\u5F03\u7528]" : "";
42766
+ println(`${t.name}${dep} (${t.contentType}, owner=${t.ownerRole})${t.description ? ` \u2014\u2014 ${t.description}` : ""}`);
42767
+ }
42768
+ break;
42769
+ }
42659
42770
  case "staged": {
42660
42771
  const r = await api.request("GET", "/api/stage");
42661
42772
  if (r.ops.length === 0) {
@@ -42841,6 +42952,8 @@ void (async () => {
42841
42952
  cleanup();
42842
42953
  process.exit(0);
42843
42954
  });
42955
+ const reportRuntimes = cfg.reportRuntimesOnNextStart === true;
42956
+ if (reportRuntimes) saveConfig({ ...cfg, reportRuntimesOnNextStart: false });
42844
42957
  if (cfg.nodeId && cfg.name) {
42845
42958
  const base = httpBase(cfg.serverUrl);
42846
42959
  fetch(`${base}/api/nodes/${cfg.nodeId}`, {
@@ -42854,7 +42967,8 @@ void (async () => {
42854
42967
  serverUrl: cfg.serverUrl,
42855
42968
  token: cfg.token,
42856
42969
  nodeId: cfg.nodeId ?? `node-${(0, import_node_crypto21.randomUUID)()}`,
42857
- nodeName: cfg.name ?? cfg.nodeId ?? `node-${(0, import_node_crypto21.randomUUID)()}`
42970
+ nodeName: cfg.name ?? cfg.nodeId ?? `node-${(0, import_node_crypto21.randomUUID)()}`,
42971
+ reportRuntimes
42858
42972
  });
42859
42973
  return;
42860
42974
  }
@@ -42892,7 +43006,7 @@ void (async () => {
42892
43006
  }
42893
43007
  const name = flags.get("name") ?? prev?.name;
42894
43008
  const nameChanged = prev?.name !== void 0 && name !== prev.name;
42895
- saveConfig({ serverUrl, token, nodeId, ...name ? { name } : {} });
43009
+ saveConfig({ serverUrl, token, nodeId, ...name ? { name } : {}, reportRuntimesOnNextStart: true });
42896
43010
  installBinary();
42897
43011
  stopDaemon();
42898
43012
  setupAutostart();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oasis_test",
3
- "version": "0.1.9",
3
+ "version": "0.1.10",
4
4
  "description": "Oasis node daemon + CLI — background daemon, auto-start, full server CLI",
5
5
  "bin": { "oasis": "./dist/index.js" },
6
6
  "files": ["dist"],