oasis_test 0.1.5 → 0.1.6

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 +437 -208
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -2782,7 +2782,11 @@ var init_dispatcher = __esm({
2782
2782
  this.opts = opts;
2783
2783
  }
2784
2784
  inFlight = /* @__PURE__ */ new Map();
2785
+ /** v1 调度准入:在途会话的 actor(jobKey → actorId),用于每 agent 并发计数。与 inFlight 同生同灭。 */
2786
+ inFlightActor = /* @__PURE__ */ new Map();
2785
2787
  strikes = /* @__PURE__ */ new Map();
2788
+ /** v1 调度准入:上一轮 tick 中"够格但被并发上限挡下"的 produce jobKey(隐式队列;下个 tick 重评)。 */
2789
+ queued = /* @__PURE__ */ new Set();
2786
2790
  dispatchLog = [];
2787
2791
  emitJournal(entry) {
2788
2792
  try {
@@ -2801,6 +2805,10 @@ var init_dispatcher = __esm({
2801
2805
  fusedAt: st.fusedAt
2802
2806
  }));
2803
2807
  }
2808
+ /** v1 调度准入:当前被并发上限挡下、等待重评的 produce 会话数(/api/view dispatches 表面化)。 */
2809
+ queuedCount() {
2810
+ return this.queued.size;
2811
+ }
2804
2812
  /** 在途会话视图(liveness 的 hasInFlightWork 来源)。含 part 维度的生产会话。 */
2805
2813
  hasInFlight(artifactId) {
2806
2814
  return [...this.inFlight.keys()].some(
@@ -2814,6 +2822,7 @@ var init_dispatcher = __esm({
2814
2822
  async tick() {
2815
2823
  const { kernel } = this.opts;
2816
2824
  let started = 0;
2825
+ this.queued.clear();
2817
2826
  await this.autoConcludeReadyMergedArtifacts();
2818
2827
  for (const job of computeProduceJobs(kernel.model)) {
2819
2828
  if (kernel.isFrozen(job.artifactId)) continue;
@@ -2877,6 +2886,26 @@ var init_dispatcher = __esm({
2877
2886
  return 0;
2878
2887
  }
2879
2888
  }
2889
+ if (spec.action === "produce") {
2890
+ const o = this.opts;
2891
+ if (o.maxConcurrentProduce !== void 0) {
2892
+ const producing = [...this.inFlight.keys()].filter((k) => k.startsWith("produce::")).length;
2893
+ if (producing >= o.maxConcurrentProduce) {
2894
+ this.queued.add(jobKey);
2895
+ return 0;
2896
+ }
2897
+ }
2898
+ if (o.maxConcurrentProducePerActor !== void 0) {
2899
+ let perActor = 0;
2900
+ for (const [k, a] of this.inFlightActor) {
2901
+ if (a === spec.actor && k.startsWith("produce::")) perActor++;
2902
+ }
2903
+ if (perActor >= o.maxConcurrentProducePerActor) {
2904
+ this.queued.add(jobKey);
2905
+ return 0;
2906
+ }
2907
+ }
2908
+ }
2880
2909
  const retry = this.strikes.get(jobKey);
2881
2910
  const lastFailure = retry && retry.count > 0 && retry.lastReason ? { reason: retry.lastReason } : void 0;
2882
2911
  const ws = kernel.model.artifacts.get(spec.artifactId)?.workspace;
@@ -2909,6 +2938,7 @@ var init_dispatcher = __esm({
2909
2938
  };
2910
2939
  const session = await this.opts.adapter.spawn(job);
2911
2940
  this.inFlight.set(jobKey, session);
2941
+ this.inFlightActor.set(jobKey, spec.actor);
2912
2942
  const dispatchedAtMs = Date.now();
2913
2943
  const seqAtDispatch = seqNow;
2914
2944
  const sink = this.opts.trajectory;
@@ -2974,6 +3004,7 @@ var init_dispatcher = __esm({
2974
3004
  await this.maybeAutoConclude(spec);
2975
3005
  }
2976
3006
  this.inFlight.delete(jobKey);
3007
+ this.inFlightActor.delete(jobKey);
2977
3008
  const seqAtExit = kernel.model.lastSeq.get(spec.artifactId) ?? 0;
2978
3009
  if (sink) {
2979
3010
  void (async () => {
@@ -15823,12 +15854,12 @@ var require_dist = __commonJS({
15823
15854
  throw new Error(`Unknown format "${name}"`);
15824
15855
  return f;
15825
15856
  };
15826
- function addFormats(ajv, list, fs15, exportName) {
15857
+ function addFormats(ajv, list, fs16, exportName) {
15827
15858
  var _a;
15828
15859
  var _b;
15829
15860
  (_a = (_b = ajv.opts.code).formats) !== null && _a !== void 0 ? _a : _b.formats = (0, codegen_1._)`require("ajv-formats/dist/formats").${exportName}`;
15830
15861
  for (const f of list)
15831
- ajv.addFormat(f, fs15[f]);
15862
+ ajv.addFormat(f, fs16[f]);
15832
15863
  }
15833
15864
  module2.exports = exports2 = formatsPlugin;
15834
15865
  Object.defineProperty(exports2, "__esModule", { value: true });
@@ -17615,7 +17646,7 @@ async function startOasisServer(opts) {
17615
17646
  if (body.actorId && opts.resolveActorContext) {
17616
17647
  const actorCtx = await opts.resolveActorContext(body.actorId).catch(() => null);
17617
17648
  if (actorCtx) {
17618
- const { mkdtempSync: mkdtempSync6, mkdirSync: mkdirSync13, writeFileSync: writeFileSync13 } = await import("node:fs");
17649
+ const { mkdtempSync: mkdtempSync6, mkdirSync: mkdirSync13, writeFileSync: writeFileSync14 } = await import("node:fs");
17619
17650
  const { join: join16, dirname: dirname13 } = await import("node:path");
17620
17651
  const { tmpdir: tmpdir8 } = await import("node:os");
17621
17652
  const dir = mkdtempSync6(join16(tmpdir8(), "oasis-chat-"));
@@ -17623,7 +17654,7 @@ async function startOasisServer(opts) {
17623
17654
  for (const [rel, content] of Object.entries(splitIdentityFiles2(actorCtx.config.prompt))) {
17624
17655
  const file = join16(dir, rel);
17625
17656
  mkdirSync13(dirname13(file), { recursive: true });
17626
- writeFileSync13(file, content);
17657
+ writeFileSync14(file, content);
17627
17658
  }
17628
17659
  }
17629
17660
  const enabledSkillIds = new Set(actorCtx.config?.skills ?? []);
@@ -17633,7 +17664,7 @@ async function startOasisServer(opts) {
17633
17664
  const s = byId.get(id);
17634
17665
  return s ? `- **${s.name}** (\`${s.id}\`): ${s.description}` : `- \`${id}\`\uFF08\u672A\u5728\u6280\u80FD\u5E93\u4E2D\uFF0C\u53EF\u80FD\u5DF2\u5378\u8F7D\uFF09`;
17635
17666
  });
17636
- writeFileSync13(join16(dir, "SKILLS.md"), ["# \u53EF\u7528\u6280\u80FD", "", "\u4EE5\u4E0B\u6280\u80FD\u5DF2\u4E3A\u4F60\u542F\u7528\uFF0C\u53EF\u5728\u672C\u6B21\u4F1A\u8BDD\u4E2D\u76F4\u63A5\u4F7F\u7528\uFF1A", "", ...lines].join("\n"));
17667
+ writeFileSync14(join16(dir, "SKILLS.md"), ["# \u53EF\u7528\u6280\u80FD", "", "\u4EE5\u4E0B\u6280\u80FD\u5DF2\u4E3A\u4F60\u542F\u7528\uFF0C\u53EF\u5728\u672C\u6B21\u4F1A\u8BDD\u4E2D\u76F4\u63A5\u4F7F\u7528\uFF1A", "", ...lines].join("\n"));
17637
17668
  }
17638
17669
  const activeConnectors = actorCtx.connectors.filter(
17639
17670
  (c) => c.status === "connected" || c.status === "verifying"
@@ -17644,7 +17675,7 @@ async function startOasisServer(opts) {
17644
17675
  const modeNote = c.mode === "oauth" ? "OAuth \xB7 \u51ED\u8BC1\u7531\u5E73\u53F0\u7BA1\u7406\uFF0C\u901A\u8FC7\u5BF9\u5E94 CLI wrapper \u8C03\u7528" : "\u76F4\u63A5\u5199\u5165 \xB7 \u51ED\u8BC1\u5DF2\u6CE8\u5165\u73AF\u5883\u53D8\u91CF";
17645
17676
  return `- **${c.name}** (\`${c.id}\`): ${statusNote} \xB7 ${modeNote}`;
17646
17677
  });
17647
- writeFileSync13(join16(dir, "CONNECTORS.md"), ["# \u53EF\u7528\u8FDE\u63A5\u5668", "", "\u4EE5\u4E0B\u8FDE\u63A5\u5668\u5DF2\u4E3A\u672C\u6B21\u4F1A\u8BDD\u914D\u7F6E\uFF0C\u51ED\u8BC1\u5DF2\u901A\u8FC7\u73AF\u5883\u53D8\u91CF\u6216 CLI wrapper \u6CE8\u5165\uFF0C\u65E0\u9700\u624B\u52A8\u914D\u7F6E\uFF1A", "", ...lines].join("\n"));
17678
+ writeFileSync14(join16(dir, "CONNECTORS.md"), ["# \u53EF\u7528\u8FDE\u63A5\u5668", "", "\u4EE5\u4E0B\u8FDE\u63A5\u5668\u5DF2\u4E3A\u672C\u6B21\u4F1A\u8BDD\u914D\u7F6E\uFF0C\u51ED\u8BC1\u5DF2\u901A\u8FC7\u73AF\u5883\u53D8\u91CF\u6216 CLI wrapper \u6CE8\u5165\uFF0C\u65E0\u9700\u624B\u52A8\u914D\u7F6E\uFF1A", "", ...lines].join("\n"));
17648
17679
  }
17649
17680
  spawnCwd = dir;
17650
17681
  }
@@ -20237,7 +20268,7 @@ var init_service = __esm({
20237
20268
  });
20238
20269
 
20239
20270
  // ../server/src/dev-store.ts
20240
- var import_node_crypto4, fs2, path, NdjsonOplogStore, DirBlobStore, MUTATORS, FileRegistryStore, FileProjectStateStore, FileProjectDocumentStore, FileArtifactStateStore, FileTraceStore;
20271
+ var import_node_crypto4, fs2, path, NdjsonOplogStore, DirBlobStore, MUTATORS, FileRegistryStore, FileProjectStateStore, FileProjectDocumentStore, FileArtifactStateStore, FileTraceStore, MemoryChatSessionStore;
20241
20272
  var init_dev_store = __esm({
20242
20273
  "../server/src/dev-store.ts"() {
20243
20274
  "use strict";
@@ -20626,6 +20657,43 @@ var init_dev_store = __esm({
20626
20657
  this.appendLine(this.artifactsFile, artifact);
20627
20658
  }
20628
20659
  };
20660
+ MemoryChatSessionStore = class {
20661
+ sessions = /* @__PURE__ */ new Map();
20662
+ messages = /* @__PURE__ */ new Map();
20663
+ async createSession(s) {
20664
+ this.sessions.set(s.id, { ...s });
20665
+ this.messages.set(s.id, []);
20666
+ }
20667
+ async listSessions(humanActorId) {
20668
+ return [...this.sessions.values()].filter((s) => s.humanActorId === humanActorId).sort((a, b) => b.touchedAt.localeCompare(a.touchedAt));
20669
+ }
20670
+ async getSession(id) {
20671
+ return this.sessions.get(id) ?? null;
20672
+ }
20673
+ async updateSession(id, patch) {
20674
+ const s = this.sessions.get(id);
20675
+ if (!s) return;
20676
+ const updated = { ...s };
20677
+ if (patch.runtimeId !== void 0) updated.runtimeId = patch.runtimeId;
20678
+ if (Object.prototype.hasOwnProperty.call(patch, "runtimeSessionId")) updated.runtimeSessionId = patch.runtimeSessionId ?? null;
20679
+ if (patch.title !== void 0) updated.title = patch.title;
20680
+ if (patch.touchedAt !== void 0) updated.touchedAt = patch.touchedAt;
20681
+ this.sessions.set(id, updated);
20682
+ }
20683
+ async deleteSession(id) {
20684
+ this.sessions.delete(id);
20685
+ this.messages.delete(id);
20686
+ }
20687
+ async appendMessage(m) {
20688
+ const list = this.messages.get(m.sessionId) ?? [];
20689
+ list.push({ ...m });
20690
+ this.messages.set(m.sessionId, list);
20691
+ }
20692
+ async listMessages(sessionId, limit) {
20693
+ const list = this.messages.get(sessionId) ?? [];
20694
+ return limit !== void 0 ? list.slice(-limit) : [...list];
20695
+ }
20696
+ };
20629
20697
  }
20630
20698
  });
20631
20699
 
@@ -27380,72 +27448,110 @@ var init_connect_script = __esm({
27380
27448
  // ../server/src/domains/nodes/routes.ts
27381
27449
  function nodesDomain(deps) {
27382
27450
  const nodeNames = /* @__PURE__ */ new Map();
27451
+ let hubWired = false;
27452
+ function wireHub(hub) {
27453
+ if (hubWired) return;
27454
+ hubWired = true;
27455
+ const { nodeStore } = deps;
27456
+ const now = () => (/* @__PURE__ */ new Date()).toISOString();
27457
+ hub.addMessageListener(async (daemonId, msg) => {
27458
+ if (msg.type !== "hello") return;
27459
+ const d = hub.connectedDaemons().find((c) => c.daemonId === daemonId);
27460
+ if (!d) return;
27461
+ const existing = await nodeStore.getNode(daemonId);
27462
+ await nodeStore.upsertNode({
27463
+ id: daemonId,
27464
+ hostname: d.meta.hostname,
27465
+ nodeVersion: d.meta.nodeVersion ?? "unknown",
27466
+ status: "online",
27467
+ lastSeenAt: now(),
27468
+ enrolledAt: existing?.enrolledAt ?? now(),
27469
+ ...d.meta.nodeName ? { name: d.meta.nodeName } : existing?.name ? { name: existing.name } : {}
27470
+ });
27471
+ const capabilities = d.meta.runtimes?.length ? d.meta.runtimes : d.meta.adapters.map((kind) => ({ kind, binary: void 0, version: void 0 }));
27472
+ for (const cap of capabilities) {
27473
+ await nodeStore.upsertRuntime({
27474
+ id: `runtime:${daemonId}:${cap.kind}`,
27475
+ nodeId: daemonId,
27476
+ kind: cap.kind,
27477
+ hostname: d.meta.hostname,
27478
+ ...cap.binary ? { binary: cap.binary } : {},
27479
+ ...cap.version ? { version: cap.version } : {},
27480
+ status: "online",
27481
+ lastSeenAt: now()
27482
+ });
27483
+ }
27484
+ });
27485
+ hub.onDaemonDisconnected = async (daemonId) => {
27486
+ await nodeStore.setNodeOnline(daemonId, false);
27487
+ await nodeStore.setNodeRuntimesOnline(daemonId, false);
27488
+ };
27489
+ }
27383
27490
  return (router) => {
27384
27491
  router.get("/api/nodes", async () => {
27385
27492
  const hub = deps.getHub();
27386
- const onlineIds = /* @__PURE__ */ new Set();
27387
- const online = (hub?.connectedDaemons() ?? []).map((d) => {
27388
- onlineIds.add(d.daemonId);
27389
- if (d.meta.nodeName) nodeNames.set(d.daemonId, d.meta.nodeName);
27493
+ if (hub) wireHub(hub);
27494
+ const nodes = await deps.nodeStore.listNodes();
27495
+ const items = await Promise.all(nodes.map(async (n) => {
27496
+ const rts = await deps.nodeStore.listRuntimes(n.id);
27390
27497
  return {
27391
- id: d.daemonId,
27392
- hostname: d.meta.hostname,
27393
- adapters: d.meta.adapters,
27394
- runtimes: runtimeInfos(d.daemonId, d.meta, "online"),
27395
- nodeVersion: d.meta.nodeVersion,
27396
- name: nodeNames.get(d.daemonId)
27498
+ id: n.id,
27499
+ hostname: n.hostname,
27500
+ adapters: rts.map((r) => r.kind),
27501
+ runtimes: rts.map((r) => ({
27502
+ id: r.id,
27503
+ nodeId: r.nodeId,
27504
+ hostname: r.hostname,
27505
+ kind: r.kind,
27506
+ ...r.binary ? { binary: r.binary } : {},
27507
+ ...r.version ? { version: r.version } : {},
27508
+ status: r.status,
27509
+ busy: false,
27510
+ lastReportAt: r.lastSeenAt
27511
+ })),
27512
+ nodeVersion: n.nodeVersion,
27513
+ ...n.name ? { name: n.name } : {}
27397
27514
  };
27398
- });
27399
- const offline = (hub?.recentlyDisconnected() ?? []).filter((g) => !onlineIds.has(g.daemonId)).map((g) => ({
27400
- id: g.daemonId,
27401
- hostname: g.meta.hostname,
27402
- adapters: g.meta.adapters,
27403
- runtimes: runtimeInfos(g.daemonId, g.meta, "offline"),
27404
- nodeVersion: g.meta.nodeVersion,
27405
- name: nodeNames.get(g.daemonId)
27406
27515
  }));
27407
- return { status: 200, body: { items: [...online, ...offline] } };
27516
+ return { status: 200, body: { items } };
27408
27517
  });
27409
- const suppressedRuntimes = /* @__PURE__ */ new Set();
27410
- let hubListenerAdded = false;
27411
27518
  router.get("/api/runtimes", async () => {
27412
27519
  const hub = deps.getHub();
27413
- if (hub && !hubListenerAdded) {
27414
- hubListenerAdded = true;
27415
- hub.addMessageListener((daemonId, msg) => {
27416
- if (msg.type === "hello") {
27417
- for (const id of suppressedRuntimes) {
27418
- if (id.startsWith(`runtime:${daemonId}:`)) suppressedRuntimes.delete(id);
27419
- }
27420
- }
27421
- });
27422
- }
27423
- const online = (hub?.connectedDaemons() ?? []).flatMap((d) => {
27424
- if (d.meta.nodeName) nodeNames.set(d.daemonId, d.meta.nodeName);
27425
- return runtimeInfos(d.daemonId, d.meta, "online");
27426
- });
27427
- const onlineIds = new Set(online.map((r) => r.id));
27428
- const offline = (hub?.recentlyDisconnected() ?? []).flatMap(
27429
- (g) => runtimeInfos(g.daemonId, g.meta, "offline").filter((r) => !onlineIds.has(r.id))
27430
- );
27431
- return { status: 200, body: { items: [...online, ...offline].filter((r) => !suppressedRuntimes.has(r.id)) } };
27432
- });
27433
- router.delete("/api/runtimes/:runtimeId", async (req) => {
27434
- const runtimeId = req.params["runtimeId"];
27435
- if (!runtimeId) return { status: 400, body: { error: "missing runtimeId" } };
27436
- suppressedRuntimes.add(runtimeId);
27437
- return { status: 200, body: { ok: true } };
27520
+ if (hub) wireHub(hub);
27521
+ const rts = await deps.nodeStore.listRuntimes();
27522
+ const items = rts.map((r) => ({
27523
+ id: r.id,
27524
+ nodeId: r.nodeId,
27525
+ hostname: r.hostname,
27526
+ kind: r.kind,
27527
+ ...r.binary ? { binary: r.binary } : {},
27528
+ ...r.version ? { version: r.version } : {},
27529
+ status: r.status,
27530
+ busy: false,
27531
+ lastReportAt: r.lastSeenAt
27532
+ }));
27533
+ return { status: 200, body: { items } };
27438
27534
  });
27439
27535
  router.post("/api/nodes/enroll", async (req) => {
27440
27536
  const body = req.body ?? {};
27441
27537
  const requested = body.nodeId?.trim();
27442
27538
  const nodeId = requested || `node-${(0, import_node_crypto9.randomUUID)().slice(0, 8)}`;
27443
- const nodeName = body.nodeName?.trim() || nodeId;
27444
- for (const { token: token2, nodeId: existing } of deps.nodeTokens.list()) {
27445
- if (existing === nodeId) deps.nodeTokens.revoke(token2);
27539
+ const nodeName = body.nodeName?.trim() || void 0;
27540
+ for (const { token: token2, nodeId: existing2 } of deps.nodeTokens.list()) {
27541
+ if (existing2 === nodeId) deps.nodeTokens.revoke(token2);
27446
27542
  }
27447
27543
  const token = deps.nodeTokens.issue(nodeId);
27448
- nodeNames.set(nodeId, nodeName);
27544
+ if (nodeName) nodeNames.set(nodeId, nodeName);
27545
+ const existing = await deps.nodeStore.getNode(nodeId);
27546
+ await deps.nodeStore.upsertNode({
27547
+ id: nodeId,
27548
+ name: nodeName,
27549
+ hostname: existing?.hostname ?? "unknown",
27550
+ nodeVersion: existing?.nodeVersion ?? "unknown",
27551
+ status: "offline",
27552
+ lastSeenAt: existing?.lastSeenAt ?? (/* @__PURE__ */ new Date()).toISOString(),
27553
+ enrolledAt: existing?.enrolledAt ?? (/* @__PURE__ */ new Date()).toISOString()
27554
+ });
27449
27555
  const params = { gatewayUrl: deps.gatewayUrl, nodeId, token, repoRemote: deps.repoRemote };
27450
27556
  const script = body.platform === "windows" ? buildWindowsConnectScript(params) : buildConnectScript(params);
27451
27557
  const npxCmd = buildNpxCmd({ gatewayUrl: deps.gatewayUrl, nodeId, token, nodeName });
@@ -27455,7 +27561,7 @@ function nodesDomain(deps) {
27455
27561
  const nodeId = req.params["nodeId"];
27456
27562
  if (!nodeId) return { status: 400, body: { error: "missing nodeId" } };
27457
27563
  const body = req.body ?? {};
27458
- if (body.name) nodeNames.set(nodeId, body.name);
27564
+ if (body.name) await deps.nodeStore.updateNodeName(nodeId, body.name);
27459
27565
  return { status: 200, body: { ok: true } };
27460
27566
  });
27461
27567
  router.delete("/api/nodes/:nodeId", async (req) => {
@@ -27466,27 +27572,17 @@ function nodesDomain(deps) {
27466
27572
  }
27467
27573
  const hub = deps.getHub();
27468
27574
  hub?.connectedDaemons().find((d) => d.daemonId === nodeId)?.close();
27469
- hub?.evictGhost(nodeId);
27470
- nodeNames.delete(nodeId);
27575
+ await deps.nodeStore.deleteNode(nodeId);
27576
+ return { status: 200, body: { ok: true } };
27577
+ });
27578
+ router.delete("/api/runtimes/:runtimeId", async (req) => {
27579
+ const runtimeId = req.params["runtimeId"];
27580
+ if (!runtimeId) return { status: 400, body: { error: "missing runtimeId" } };
27581
+ await deps.nodeStore.deleteRuntime(runtimeId);
27471
27582
  return { status: 200, body: { ok: true } };
27472
27583
  });
27473
27584
  };
27474
27585
  }
27475
- function runtimeInfos(nodeId, meta, status) {
27476
- const lastReportAt = (/* @__PURE__ */ new Date()).toISOString();
27477
- const capabilities = meta.runtimes?.length ? meta.runtimes : meta.adapters.map((kind) => ({ kind }));
27478
- return capabilities.map((r) => ({
27479
- id: `runtime:${nodeId}:${r.kind}`,
27480
- nodeId,
27481
- hostname: meta.hostname,
27482
- kind: r.kind,
27483
- ...r.binary ? { binary: r.binary } : {},
27484
- ...r.version ? { version: r.version } : {},
27485
- status,
27486
- busy: false,
27487
- lastReportAt
27488
- }));
27489
- }
27490
27586
  var import_node_crypto9;
27491
27587
  var init_routes4 = __esm({
27492
27588
  "../server/src/domains/nodes/routes.ts"() {
@@ -27496,6 +27592,124 @@ var init_routes4 = __esm({
27496
27592
  }
27497
27593
  });
27498
27594
 
27595
+ // ../server/src/node-store.ts
27596
+ var fs5, MemoryNodeStore, FileNodeStore;
27597
+ var init_node_store = __esm({
27598
+ "../server/src/node-store.ts"() {
27599
+ "use strict";
27600
+ fs5 = __toESM(require("node:fs"), 1);
27601
+ MemoryNodeStore = class {
27602
+ nodes = /* @__PURE__ */ new Map();
27603
+ runtimes = /* @__PURE__ */ new Map();
27604
+ async upsertNode(node) {
27605
+ this.nodes.set(node.id, { ...this.nodes.get(node.id), ...node });
27606
+ }
27607
+ async upsertRuntime(rt) {
27608
+ this.runtimes.set(rt.id, { ...this.runtimes.get(rt.id), ...rt });
27609
+ }
27610
+ async setNodeOnline(id, online) {
27611
+ const n = this.nodes.get(id);
27612
+ if (n) {
27613
+ n.status = online ? "online" : "offline";
27614
+ n.lastSeenAt = (/* @__PURE__ */ new Date()).toISOString();
27615
+ }
27616
+ }
27617
+ async setNodeRuntimesOnline(nodeId, online) {
27618
+ const now = (/* @__PURE__ */ new Date()).toISOString();
27619
+ for (const rt of this.runtimes.values()) {
27620
+ if (rt.nodeId === nodeId) {
27621
+ rt.status = online ? "online" : "offline";
27622
+ rt.lastSeenAt = now;
27623
+ }
27624
+ }
27625
+ }
27626
+ async updateNodeName(id, name) {
27627
+ const n = this.nodes.get(id);
27628
+ if (n) n.name = name;
27629
+ }
27630
+ async getNode(id) {
27631
+ return this.nodes.get(id) ?? null;
27632
+ }
27633
+ async listNodes() {
27634
+ return [...this.nodes.values()];
27635
+ }
27636
+ async listRuntimes(nodeId) {
27637
+ const all = [...this.runtimes.values()];
27638
+ return nodeId ? all.filter((r) => r.nodeId === nodeId) : all;
27639
+ }
27640
+ async deleteNode(id) {
27641
+ this.nodes.delete(id);
27642
+ for (const [k, rt] of this.runtimes) {
27643
+ if (rt.nodeId === id) this.runtimes.delete(k);
27644
+ }
27645
+ }
27646
+ async deleteRuntime(id) {
27647
+ const rt = this.runtimes.get(id);
27648
+ this.runtimes.delete(id);
27649
+ if (!rt) return { nodeDeleted: false };
27650
+ const remaining = [...this.runtimes.values()].filter((r) => r.nodeId === rt.nodeId);
27651
+ if (remaining.length === 0) {
27652
+ this.nodes.delete(rt.nodeId);
27653
+ return { nodeDeleted: true };
27654
+ }
27655
+ return { nodeDeleted: false };
27656
+ }
27657
+ };
27658
+ FileNodeStore = class _FileNodeStore extends MemoryNodeStore {
27659
+ constructor(file) {
27660
+ super();
27661
+ this.file = file;
27662
+ }
27663
+ static async open(file) {
27664
+ const s = new _FileNodeStore(file);
27665
+ try {
27666
+ const snap = JSON.parse(fs5.readFileSync(file, "utf8"));
27667
+ for (const n of snap.nodes ?? []) s.nodes.set(n.id, n);
27668
+ for (const r of snap.runtimes ?? []) s.runtimes.set(r.id, r);
27669
+ } catch {
27670
+ fs5.writeFileSync(file, JSON.stringify({ nodes: [], runtimes: [] }, null, 2));
27671
+ }
27672
+ return s;
27673
+ }
27674
+ flush() {
27675
+ fs5.writeFileSync(this.file, JSON.stringify({
27676
+ nodes: [...this.nodes.values()],
27677
+ runtimes: [...this.runtimes.values()]
27678
+ }, null, 2));
27679
+ }
27680
+ async upsertNode(node) {
27681
+ await super.upsertNode(node);
27682
+ this.flush();
27683
+ }
27684
+ async upsertRuntime(rt) {
27685
+ await super.upsertRuntime(rt);
27686
+ this.flush();
27687
+ }
27688
+ async setNodeOnline(id, online) {
27689
+ await super.setNodeOnline(id, online);
27690
+ this.flush();
27691
+ }
27692
+ async setNodeRuntimesOnline(nodeId, online) {
27693
+ await super.setNodeRuntimesOnline(nodeId, online);
27694
+ this.flush();
27695
+ }
27696
+ async updateNodeName(id, name) {
27697
+ await super.updateNodeName(id, name);
27698
+ this.flush();
27699
+ }
27700
+ async deleteNode(id) {
27701
+ await super.deleteNode(id);
27702
+ this.flush();
27703
+ }
27704
+ async deleteRuntime(id) {
27705
+ const r = await super.deleteRuntime(id);
27706
+ this.flush();
27707
+ return r;
27708
+ }
27709
+ };
27710
+ }
27711
+ });
27712
+
27499
27713
  // ../server/src/domains/collab/workorder-detail.ts
27500
27714
  function buildWorkorderDetail(model, workspaceId, resolveActor, resolveProject, dispatchJournal = []) {
27501
27715
  const arts = [...model.artifacts.values()].filter((a) => a.workspace === workspaceId);
@@ -27515,6 +27729,7 @@ function buildWorkorderDetail(model, workspaceId, resolveActor, resolveProject,
27515
27729
  const nodes = arts.map((a) => {
27516
27730
  const info = nodeInfo(model, a);
27517
27731
  const runtime = runtimeByArtifact.get(a.id);
27732
+ const lifecycle = lifecycleOf(model, a.id);
27518
27733
  return {
27519
27734
  id: a.id,
27520
27735
  type: a.type,
@@ -27524,6 +27739,8 @@ function buildWorkorderDetail(model, workspaceId, resolveActor, resolveProject,
27524
27739
  queue: info.queueLen,
27525
27740
  blocked: blockedOf(model, a.id).blocked,
27526
27741
  owner: ref(a.owner),
27742
+ // 封存原因透出:stage 只到 "sealed" 粒度,原因(accepted/cancelled/frozen)另给,供前端区分展示。
27743
+ ...lifecycle !== "active" ? { sealedReason: lifecycle.slice("sealed:".length) } : {},
27527
27744
  ...runtime ? { runtime } : {}
27528
27745
  };
27529
27746
  });
@@ -29056,22 +29273,22 @@ var init_engine = __esm({
29056
29273
  });
29057
29274
 
29058
29275
  // ../server/src/mirror/fs-state.ts
29059
- var fs5, FsMirrorStateStore;
29276
+ var fs6, FsMirrorStateStore;
29060
29277
  var init_fs_state = __esm({
29061
29278
  "../server/src/mirror/fs-state.ts"() {
29062
29279
  "use strict";
29063
- fs5 = __toESM(require("node:fs"), 1);
29280
+ fs6 = __toESM(require("node:fs"), 1);
29064
29281
  FsMirrorStateStore = class {
29065
29282
  constructor(file) {
29066
29283
  this.file = file;
29067
- if (fs5.existsSync(file)) {
29068
- const obj = JSON.parse(fs5.readFileSync(file, "utf8"));
29284
+ if (fs6.existsSync(file)) {
29285
+ const obj = JSON.parse(fs6.readFileSync(file, "utf8"));
29069
29286
  for (const [k, v] of Object.entries(obj)) this.map.set(k, v);
29070
29287
  }
29071
29288
  }
29072
29289
  map = /* @__PURE__ */ new Map();
29073
29290
  flush() {
29074
- fs5.writeFileSync(this.file, JSON.stringify(Object.fromEntries(this.map), null, 2));
29291
+ fs6.writeFileSync(this.file, JSON.stringify(Object.fromEntries(this.map), null, 2));
29075
29292
  }
29076
29293
  async get(artifactId) {
29077
29294
  return this.map.get(artifactId) ?? null;
@@ -29088,12 +29305,12 @@ var init_fs_state = __esm({
29088
29305
  });
29089
29306
 
29090
29307
  // ../server/src/git/integrate.ts
29091
- var import_node_child_process2, fs6, path4, GitRemoteIntegrator;
29308
+ var import_node_child_process2, fs7, path4, GitRemoteIntegrator;
29092
29309
  var init_integrate = __esm({
29093
29310
  "../server/src/git/integrate.ts"() {
29094
29311
  "use strict";
29095
29312
  import_node_child_process2 = require("node:child_process");
29096
- fs6 = __toESM(require("node:fs"), 1);
29313
+ fs7 = __toESM(require("node:fs"), 1);
29097
29314
  path4 = __toESM(require("node:path"), 1);
29098
29315
  GitRemoteIntegrator = class {
29099
29316
  constructor(remote, cloneDir, develop = "develop") {
@@ -29111,8 +29328,8 @@ var init_integrate = __esm({
29111
29328
  }
29112
29329
  /** 确保本地工作克隆存在并拉到最新远程态。幂等。 */
29113
29330
  sync() {
29114
- if (!fs6.existsSync(path4.join(this.cloneDir, ".git"))) {
29115
- fs6.mkdirSync(path4.dirname(this.cloneDir), { recursive: true });
29331
+ if (!fs7.existsSync(path4.join(this.cloneDir, ".git"))) {
29332
+ fs7.mkdirSync(path4.dirname(this.cloneDir), { recursive: true });
29116
29333
  (0, import_node_child_process2.execFileSync)("git", ["clone", "--quiet", this.remote, this.cloneDir], { env: this.env, maxBuffer: 256 * 1024 * 1024 });
29117
29334
  this.git(["config", "user.email", "oasis@local"]);
29118
29335
  this.git(["config", "user.name", "oasis"]);
@@ -29518,9 +29735,11 @@ var init_src4 = __esm({
29518
29735
  init_auth();
29519
29736
  init_sender();
29520
29737
  init_routes4();
29738
+ init_node_store();
29521
29739
  init_collab();
29522
29740
  init_trace2();
29523
29741
  init_chat_sessions();
29742
+ init_dev_store();
29524
29743
  init_daemon_adapter();
29525
29744
  init_engine();
29526
29745
  init_fs_state();
@@ -31120,15 +31339,15 @@ var require_pg_connection_string = __commonJS({
31120
31339
  if (config2.sslcert || config2.sslkey || config2.sslrootcert || config2.sslmode) {
31121
31340
  config2.ssl = {};
31122
31341
  }
31123
- const fs15 = config2.sslcert || config2.sslkey || config2.sslrootcert ? require("fs") : null;
31342
+ const fs16 = config2.sslcert || config2.sslkey || config2.sslrootcert ? require("fs") : null;
31124
31343
  if (config2.sslcert) {
31125
- config2.ssl.cert = fs15.readFileSync(config2.sslcert).toString();
31344
+ config2.ssl.cert = fs16.readFileSync(config2.sslcert).toString();
31126
31345
  }
31127
31346
  if (config2.sslkey) {
31128
- config2.ssl.key = fs15.readFileSync(config2.sslkey).toString();
31347
+ config2.ssl.key = fs16.readFileSync(config2.sslkey).toString();
31129
31348
  }
31130
31349
  if (config2.sslrootcert) {
31131
- config2.ssl.ca = fs15.readFileSync(config2.sslrootcert).toString();
31350
+ config2.ssl.ca = fs16.readFileSync(config2.sslrootcert).toString();
31132
31351
  }
31133
31352
  if (options.useLibpqCompat && config2.uselibpqcompat) {
31134
31353
  throw new Error("Both useLibpqCompat and uselibpqcompat are set. Please use only one of them.");
@@ -33065,15 +33284,15 @@ var require_lib = __commonJS({
33065
33284
  "../../node_modules/.pnpm/pgpass@1.0.5/node_modules/pgpass/lib/index.js"(exports2, module2) {
33066
33285
  "use strict";
33067
33286
  var path14 = require("path");
33068
- var fs15 = require("fs");
33287
+ var fs16 = require("fs");
33069
33288
  var helper = require_helper();
33070
33289
  module2.exports = function(connInfo, cb) {
33071
33290
  var file = helper.getFileName();
33072
- fs15.stat(file, function(err, stat) {
33291
+ fs16.stat(file, function(err, stat) {
33073
33292
  if (err || !helper.usePgPass(stat, file)) {
33074
33293
  return cb(void 0);
33075
33294
  }
33076
- var st = fs15.createReadStream(file);
33295
+ var st = fs16.createReadStream(file);
33077
33296
  helper.getPassword(connInfo, st, cb);
33078
33297
  });
33079
33298
  };
@@ -34627,37 +34846,37 @@ __export(trajectory_exports, {
34627
34846
  });
34628
34847
  async function exportTrajectories(dataDir) {
34629
34848
  const root = path10.join(dataDir, "trajectories");
34630
- if (!fs11.existsSync(root)) return [];
34849
+ if (!fs12.existsSync(root)) return [];
34631
34850
  const model = emptyReadModel();
34632
34851
  const oplogFile = path10.join(dataDir, "oplog.ndjson");
34633
- if (fs11.existsSync(oplogFile)) {
34852
+ if (fs12.existsSync(oplogFile)) {
34634
34853
  const oplog = await NdjsonOplogStore.open(oplogFile);
34635
34854
  for await (const entry of oplog.readAll()) fold(model, entry.op);
34636
34855
  }
34637
34856
  const samples = [];
34638
- for (const sessionId of fs11.readdirSync(root).sort()) {
34857
+ for (const sessionId of fs12.readdirSync(root).sort()) {
34639
34858
  const dir = path10.join(root, sessionId);
34640
34859
  const sessionFile = path10.join(dir, "session.json");
34641
- if (!fs11.existsSync(sessionFile)) continue;
34642
- const session = JSON.parse(fs11.readFileSync(sessionFile, "utf8"));
34860
+ if (!fs12.existsSync(sessionFile)) continue;
34861
+ const session = JSON.parse(fs12.readFileSync(sessionFile, "utf8"));
34643
34862
  const events = [];
34644
34863
  const eventsFile = path10.join(dir, "events.ndjson");
34645
- if (fs11.existsSync(eventsFile)) {
34646
- for (const line of fs11.readFileSync(eventsFile, "utf8").split("\n")) {
34864
+ if (fs12.existsSync(eventsFile)) {
34865
+ for (const line of fs12.readFileSync(eventsFile, "utf8").split("\n")) {
34647
34866
  if (line.trim()) events.push(JSON.parse(line));
34648
34867
  }
34649
34868
  }
34650
34869
  const bundle = {};
34651
34870
  const bundleDir = path10.join(dir, "bundle");
34652
34871
  const walk = (sub) => {
34653
- for (const name of fs11.readdirSync(path10.join(bundleDir, sub))) {
34872
+ for (const name of fs12.readdirSync(path10.join(bundleDir, sub))) {
34654
34873
  const rel = sub ? `${sub}/${name}` : name;
34655
34874
  const full = path10.join(bundleDir, rel);
34656
- if (fs11.statSync(full).isDirectory()) walk(rel);
34657
- else bundle[rel] = fs11.readFileSync(full, "utf8");
34875
+ if (fs12.statSync(full).isDirectory()) walk(rel);
34876
+ else bundle[rel] = fs12.readFileSync(full, "utf8");
34658
34877
  }
34659
34878
  };
34660
- if (fs11.existsSync(bundleDir)) walk("");
34879
+ if (fs12.existsSync(bundleDir)) walk("");
34661
34880
  const proposed = (session.opRefs ?? []).filter((r) => r.kind === "propose_revision").map((r) => r.target);
34662
34881
  const verdicts = proposed.flatMap(
34663
34882
  (rev) => (model.reviews.get(rev) ?? []).map((v) => ({ author: v.author, verdict: v.verdict, ...v.note !== void 0 ? { note: v.note } : {} }))
@@ -34669,11 +34888,11 @@ async function exportTrajectories(dataDir) {
34669
34888
  }
34670
34889
  return samples;
34671
34890
  }
34672
- var fs11, path10, FsTrajectorySink;
34891
+ var fs12, path10, FsTrajectorySink;
34673
34892
  var init_trajectory = __esm({
34674
34893
  "../cli/src/trajectory.ts"() {
34675
34894
  "use strict";
34676
- fs11 = __toESM(require("node:fs"), 1);
34895
+ fs12 = __toESM(require("node:fs"), 1);
34677
34896
  path10 = __toESM(require("node:path"), 1);
34678
34897
  init_src2();
34679
34898
  init_src4();
@@ -34688,25 +34907,25 @@ var init_trajectory = __esm({
34688
34907
  }
34689
34908
  begin(session, bundleFiles) {
34690
34909
  const dir = this.dir(session.sessionId);
34691
- fs11.mkdirSync(path10.join(dir, "bundle"), { recursive: true });
34692
- fs11.writeFileSync(path10.join(dir, "session.json"), JSON.stringify(session, null, 2));
34910
+ fs12.mkdirSync(path10.join(dir, "bundle"), { recursive: true });
34911
+ fs12.writeFileSync(path10.join(dir, "session.json"), JSON.stringify(session, null, 2));
34693
34912
  for (const [rel, content] of Object.entries(bundleFiles)) {
34694
34913
  const file = path10.join(dir, "bundle", rel);
34695
- fs11.mkdirSync(path10.dirname(file), { recursive: true });
34696
- fs11.writeFileSync(file, content);
34914
+ fs12.mkdirSync(path10.dirname(file), { recursive: true });
34915
+ fs12.writeFileSync(file, content);
34697
34916
  }
34698
34917
  this.broadcast?.({ type: "begin", sessionId: session.sessionId, data: session });
34699
34918
  }
34700
34919
  event(sessionId, event) {
34701
- fs11.appendFileSync(path10.join(this.dir(sessionId), "events.ndjson"), JSON.stringify(event) + "\n");
34920
+ fs12.appendFileSync(path10.join(this.dir(sessionId), "events.ndjson"), JSON.stringify(event) + "\n");
34702
34921
  this.broadcast?.({ type: "event", sessionId, data: event });
34703
34922
  }
34704
34923
  end(sessionId, update) {
34705
34924
  const file = path10.join(this.dir(sessionId), "session.json");
34706
- const session = JSON.parse(fs11.readFileSync(file, "utf8"));
34925
+ const session = JSON.parse(fs12.readFileSync(file, "utf8"));
34707
34926
  session.exit = update.exit;
34708
34927
  session.opRefs = update.opRefs;
34709
- fs11.writeFileSync(file, JSON.stringify(session, null, 2));
34928
+ fs12.writeFileSync(file, JSON.stringify(session, null, 2));
34710
34929
  this.broadcast?.({ type: "end", sessionId, data: session });
34711
34930
  }
34712
34931
  };
@@ -34715,19 +34934,19 @@ var init_trajectory = __esm({
34715
34934
 
34716
34935
  // src/index.ts
34717
34936
  var import_node_crypto21 = require("node:crypto");
34718
- var fs14 = __toESM(require("node:fs"));
34937
+ var fs15 = __toESM(require("node:fs"));
34719
34938
  var os8 = __toESM(require("node:os"));
34720
34939
  var path13 = __toESM(require("node:path"));
34721
34940
  var import_node_child_process10 = require("node:child_process");
34722
34941
 
34723
34942
  // ../cli/src/cli.ts
34724
- var fs13 = __toESM(require("node:fs"), 1);
34943
+ var fs14 = __toESM(require("node:fs"), 1);
34725
34944
  var os7 = __toESM(require("node:os"), 1);
34726
34945
  var path12 = __toESM(require("node:path"), 1);
34727
34946
  var import_node_crypto20 = require("node:crypto");
34728
34947
 
34729
34948
  // ../cli/src/serve.ts
34730
- var fs12 = __toESM(require("node:fs"), 1);
34949
+ var fs13 = __toESM(require("node:fs"), 1);
34731
34950
  var os6 = __toESM(require("node:os"), 1);
34732
34951
  var path11 = __toESM(require("node:path"), 1);
34733
34952
  var import_node_crypto19 = require("node:crypto");
@@ -34936,7 +35155,7 @@ var import_node_path3 = require("node:path");
34936
35155
  // ../adapters/src/claude-code/index.ts
34937
35156
  var import_node_child_process5 = require("node:child_process");
34938
35157
  var import_node_crypto14 = require("node:crypto");
34939
- var fs7 = __toESM(require("node:fs"), 1);
35158
+ var fs8 = __toESM(require("node:fs"), 1);
34940
35159
  var os2 = __toESM(require("node:os"), 1);
34941
35160
  var path6 = __toESM(require("node:path"), 1);
34942
35161
  var readline = __toESM(require("node:readline"), 1);
@@ -35055,13 +35274,13 @@ var ClaudeCodeAdapter = class {
35055
35274
  async spawn(job) {
35056
35275
  const slug4 = job.artifactId.replace(/[^a-zA-Z0-9_-]+/g, "_").slice(0, 32);
35057
35276
  const id = `claude-${slug4}-${(0, import_node_crypto14.randomUUID)().slice(0, 8)}`;
35058
- const dir = fs7.mkdtempSync(
35277
+ const dir = fs8.mkdtempSync(
35059
35278
  path6.join(this.opts.workRoot ?? os2.tmpdir(), "oasis-session-")
35060
35279
  );
35061
35280
  for (const [rel, content] of Object.entries(job.bundle.files)) {
35062
35281
  const file = path6.join(dir, rel);
35063
- fs7.mkdirSync(path6.dirname(file), { recursive: true });
35064
- fs7.writeFileSync(file, content);
35282
+ fs8.mkdirSync(path6.dirname(file), { recursive: true });
35283
+ fs8.writeFileSync(file, content);
35065
35284
  }
35066
35285
  const prompt = job.bundle.files["TASK.md"];
35067
35286
  if (!prompt) throw new Error("bundle \u7F3A TASK.md\uFF08\u88C5\u914D\u5668\u5951\u7EA6\uFF09");
@@ -35098,7 +35317,7 @@ var ClaudeCodeAdapter = class {
35098
35317
  stdio: ["ignore", "pipe", "pipe"]
35099
35318
  });
35100
35319
  let transcriptRef = path6.join(dir, "transcript.txt");
35101
- const out = fs7.createWriteStream(transcriptRef);
35320
+ const out = fs8.createWriteStream(transcriptRef);
35102
35321
  const telemetryCbs = [];
35103
35322
  let eventSeq = 0;
35104
35323
  let lastResult;
@@ -35161,13 +35380,13 @@ var ClaudeCodeAdapter = class {
35161
35380
  if (this.opts.cleanupWorkdir) {
35162
35381
  try {
35163
35382
  const keepDir = path6.join(this.opts.workRoot ?? os2.tmpdir(), "oasis-transcripts");
35164
- fs7.mkdirSync(keepDir, { recursive: true });
35383
+ fs8.mkdirSync(keepDir, { recursive: true });
35165
35384
  const kept = path6.join(keepDir, `${id}.txt`);
35166
35385
  const src = transcriptRef;
35167
35386
  out.end(() => {
35168
35387
  try {
35169
- fs7.renameSync(src, kept);
35170
- fs7.rmSync(dir, { recursive: true, force: true });
35388
+ fs8.renameSync(src, kept);
35389
+ fs8.rmSync(dir, { recursive: true, force: true });
35171
35390
  } catch {
35172
35391
  }
35173
35392
  });
@@ -35202,7 +35421,7 @@ var ClaudeCodeAdapter = class {
35202
35421
  // ../adapters/src/codex/index.ts
35203
35422
  var import_node_child_process6 = require("node:child_process");
35204
35423
  var import_node_crypto15 = require("node:crypto");
35205
- var fs8 = __toESM(require("node:fs"), 1);
35424
+ var fs9 = __toESM(require("node:fs"), 1);
35206
35425
  var os3 = __toESM(require("node:os"), 1);
35207
35426
  var path7 = __toESM(require("node:path"), 1);
35208
35427
  var readline2 = __toESM(require("node:readline"), 1);
@@ -35247,7 +35466,7 @@ function codexSessionsRoot() {
35247
35466
  if (home) candidates.push(path7.join(home, ".codex", "sessions"));
35248
35467
  for (const c of candidates) {
35249
35468
  try {
35250
- if (fs8.statSync(c).isDirectory()) return c;
35469
+ if (fs9.statSync(c).isDirectory()) return c;
35251
35470
  } catch {
35252
35471
  }
35253
35472
  }
@@ -35259,7 +35478,7 @@ function listRecentRollouts(root, sinceMs) {
35259
35478
  const walk = (d) => {
35260
35479
  let ents;
35261
35480
  try {
35262
- ents = fs8.readdirSync(d, { withFileTypes: true });
35481
+ ents = fs9.readdirSync(d, { withFileTypes: true });
35263
35482
  } catch {
35264
35483
  return;
35265
35484
  }
@@ -35269,7 +35488,7 @@ function listRecentRollouts(root, sinceMs) {
35269
35488
  else if (ent.isFile() && ent.name.startsWith("rollout-") && ent.name.endsWith(".jsonl")) {
35270
35489
  let m = 0;
35271
35490
  try {
35272
- m = fs8.statSync(p).mtimeMs;
35491
+ m = fs9.statSync(p).mtimeMs;
35273
35492
  } catch {
35274
35493
  }
35275
35494
  if (m >= margin) found.push({ f: p, m });
@@ -35284,7 +35503,7 @@ function scanCodexUsage(workdir, sinceMs, sessionsRoot = codexSessionsRoot()) {
35284
35503
  for (const f of listRecentRollouts(sessionsRoot, sinceMs)) {
35285
35504
  let lines;
35286
35505
  try {
35287
- lines = fs8.readFileSync(f, "utf8").split("\n");
35506
+ lines = fs9.readFileSync(f, "utf8").split("\n");
35288
35507
  } catch {
35289
35508
  continue;
35290
35509
  }
@@ -35476,11 +35695,11 @@ var CodexAdapter = class {
35476
35695
  const slug4 = job.artifactId.replace(/[^a-zA-Z0-9_-]+/g, "_").slice(0, 32);
35477
35696
  const id = `codex-${slug4}-${(0, import_node_crypto15.randomUUID)().slice(0, 8)}`;
35478
35697
  const startedAtMs = Date.now();
35479
- const dir = fs8.mkdtempSync(path7.join(this.opts.workRoot ?? os3.tmpdir(), "oasis-session-"));
35698
+ const dir = fs9.mkdtempSync(path7.join(this.opts.workRoot ?? os3.tmpdir(), "oasis-session-"));
35480
35699
  for (const [rel, content] of Object.entries(job.bundle.files)) {
35481
35700
  const file = path7.join(dir, rel);
35482
- fs8.mkdirSync(path7.dirname(file), { recursive: true });
35483
- fs8.writeFileSync(file, content);
35701
+ fs9.mkdirSync(path7.dirname(file), { recursive: true });
35702
+ fs9.writeFileSync(file, content);
35484
35703
  }
35485
35704
  const task = job.bundle.files["TASK.md"];
35486
35705
  if (!task) throw new Error("bundle \u7F3A TASK.md\uFF08\u88C5\u914D\u5668\u5951\u7EA6\uFF09");
@@ -35509,7 +35728,7 @@ var CodexAdapter = class {
35509
35728
  stdio: ["ignore", "pipe", "pipe"]
35510
35729
  });
35511
35730
  let transcriptRef = path7.join(dir, "transcript.txt");
35512
- const out = fs8.createWriteStream(transcriptRef);
35731
+ const out = fs9.createWriteStream(transcriptRef);
35513
35732
  const outputCbs = [];
35514
35733
  const telemetryCbs = [];
35515
35734
  const normalizeState = createCodexNormalizeState();
@@ -35540,13 +35759,13 @@ var CodexAdapter = class {
35540
35759
  if (this.opts.cleanupWorkdir) {
35541
35760
  try {
35542
35761
  const keepDir = path7.join(this.opts.workRoot ?? os3.tmpdir(), "oasis-transcripts");
35543
- fs8.mkdirSync(keepDir, { recursive: true });
35762
+ fs9.mkdirSync(keepDir, { recursive: true });
35544
35763
  const kept = path7.join(keepDir, `${id}.txt`);
35545
35764
  const src = transcriptRef;
35546
35765
  out.end(() => {
35547
35766
  try {
35548
- fs8.renameSync(src, kept);
35549
- fs8.rmSync(dir, { recursive: true, force: true });
35767
+ fs9.renameSync(src, kept);
35768
+ fs9.rmSync(dir, { recursive: true, force: true });
35550
35769
  } catch {
35551
35770
  }
35552
35771
  });
@@ -35599,7 +35818,7 @@ var CodexAdapter = class {
35599
35818
  // ../adapters/src/_core/acp.ts
35600
35819
  var import_node_child_process7 = require("node:child_process");
35601
35820
  var import_node_crypto16 = require("node:crypto");
35602
- var fs9 = __toESM(require("node:fs"), 1);
35821
+ var fs10 = __toESM(require("node:fs"), 1);
35603
35822
  var os4 = __toESM(require("node:os"), 1);
35604
35823
  var path8 = __toESM(require("node:path"), 1);
35605
35824
  var readline3 = __toESM(require("node:readline"), 1);
@@ -35805,11 +36024,11 @@ async function runACPSession(job, cfg) {
35805
36024
  const slug4 = job.artifactId.replace(/[^a-zA-Z0-9_-]+/g, "_").slice(0, 32);
35806
36025
  const binTag = path8.basename(cfg.bin).replace(/[^a-zA-Z0-9]/g, "").slice(0, 12) || "acp";
35807
36026
  const id = `${binTag}-${slug4}-${(0, import_node_crypto16.randomUUID)().slice(0, 8)}`;
35808
- const dir = fs9.mkdtempSync(path8.join(cfg.workRoot ?? os4.tmpdir(), "oasis-session-"));
36027
+ const dir = fs10.mkdtempSync(path8.join(cfg.workRoot ?? os4.tmpdir(), "oasis-session-"));
35809
36028
  for (const [rel, content] of Object.entries(job.bundle.files)) {
35810
36029
  const file = path8.join(dir, rel);
35811
- fs9.mkdirSync(path8.dirname(file), { recursive: true });
35812
- fs9.writeFileSync(file, content);
36030
+ fs10.mkdirSync(path8.dirname(file), { recursive: true });
36031
+ fs10.writeFileSync(file, content);
35813
36032
  }
35814
36033
  const task = job.bundle.files["TASK.md"];
35815
36034
  if (!task) throw new Error("bundle \u7F3A TASK.md\uFF08\u88C5\u914D\u5668\u5951\u7EA6\uFF09");
@@ -35827,7 +36046,7 @@ async function runACPSession(job, cfg) {
35827
36046
  stdio: ["pipe", "pipe", "pipe"]
35828
36047
  });
35829
36048
  let transcriptRef = path8.join(dir, "transcript.txt");
35830
- const transcriptOut = fs9.createWriteStream(transcriptRef);
36049
+ const transcriptOut = fs10.createWriteStream(transcriptRef);
35831
36050
  const outputCbs = [];
35832
36051
  const telemetryCbs = [];
35833
36052
  const exitCbs = [];
@@ -35838,13 +36057,13 @@ async function runACPSession(job, cfg) {
35838
36057
  if (cfg.cleanupWorkdir) {
35839
36058
  try {
35840
36059
  const keepDir = path8.join(cfg.workRoot ?? os4.tmpdir(), "oasis-transcripts");
35841
- fs9.mkdirSync(keepDir, { recursive: true });
36060
+ fs10.mkdirSync(keepDir, { recursive: true });
35842
36061
  const kept = path8.join(keepDir, `${id}.txt`);
35843
36062
  const src = transcriptRef;
35844
36063
  transcriptOut.end(() => {
35845
36064
  try {
35846
- fs9.renameSync(src, kept);
35847
- fs9.rmSync(dir, { recursive: true, force: true });
36065
+ fs10.renameSync(src, kept);
36066
+ fs10.rmSync(dir, { recursive: true, force: true });
35848
36067
  } catch {
35849
36068
  }
35850
36069
  });
@@ -36021,18 +36240,18 @@ var KiroAdapter = class {
36021
36240
  // ../adapters/src/_core/subprocess.ts
36022
36241
  var import_node_child_process8 = require("node:child_process");
36023
36242
  var import_node_crypto17 = require("node:crypto");
36024
- var fs10 = __toESM(require("node:fs"), 1);
36243
+ var fs11 = __toESM(require("node:fs"), 1);
36025
36244
  var os5 = __toESM(require("node:os"), 1);
36026
36245
  var path9 = __toESM(require("node:path"), 1);
36027
36246
  var readline4 = __toESM(require("node:readline"), 1);
36028
36247
  function materialize(job, workRoot) {
36029
36248
  const slug4 = job.artifactId.replace(/[^a-zA-Z0-9_-]+/g, "_").slice(0, 32);
36030
36249
  const id = `${slug4}-${(0, import_node_crypto17.randomUUID)().slice(0, 8)}`;
36031
- const dir = fs10.mkdtempSync(path9.join(workRoot ?? os5.tmpdir(), "oasis-session-"));
36250
+ const dir = fs11.mkdtempSync(path9.join(workRoot ?? os5.tmpdir(), "oasis-session-"));
36032
36251
  for (const [rel, content] of Object.entries(job.bundle.files)) {
36033
36252
  const file = path9.join(dir, rel);
36034
- fs10.mkdirSync(path9.dirname(file), { recursive: true });
36035
- fs10.writeFileSync(file, content);
36253
+ fs11.mkdirSync(path9.dirname(file), { recursive: true });
36254
+ fs11.writeFileSync(file, content);
36036
36255
  }
36037
36256
  const task = job.bundle.files["TASK.md"];
36038
36257
  if (!task) throw new Error("bundle \u7F3A TASK.md\uFF08\u88C5\u914D\u5668\u5951\u7EA6\uFF09");
@@ -36055,13 +36274,13 @@ function makeFinish(id, cfg, transcriptRefPtr, transcriptOut, dir, exitCbs) {
36055
36274
  if (cfg.cleanupWorkdir) {
36056
36275
  try {
36057
36276
  const keepDir = path9.join(cfg.workRoot ?? os5.tmpdir(), "oasis-transcripts");
36058
- fs10.mkdirSync(keepDir, { recursive: true });
36277
+ fs11.mkdirSync(keepDir, { recursive: true });
36059
36278
  const kept = path9.join(keepDir, `${id}.txt`);
36060
36279
  const src = transcriptRefPtr.value;
36061
36280
  transcriptOut.end(() => {
36062
36281
  try {
36063
- fs10.renameSync(src, kept);
36064
- fs10.rmSync(dir, { recursive: true, force: true });
36282
+ fs11.renameSync(src, kept);
36283
+ fs11.rmSync(dir, { recursive: true, force: true });
36065
36284
  } catch {
36066
36285
  }
36067
36286
  });
@@ -36084,7 +36303,7 @@ function runStreamJson(job, cfg) {
36084
36303
  stdio: ["ignore", "pipe", "pipe"]
36085
36304
  });
36086
36305
  const transcriptRefPtr = { value: path9.join(dir, "transcript.txt") };
36087
- const transcriptOut = fs10.createWriteStream(transcriptRefPtr.value);
36306
+ const transcriptOut = fs11.createWriteStream(transcriptRefPtr.value);
36088
36307
  const outputCbs = [];
36089
36308
  const telemetryCbs = [];
36090
36309
  const exitCbs = [];
@@ -36142,7 +36361,7 @@ function runOneShotText(job, cfg) {
36142
36361
  stdio: ["ignore", "pipe", "pipe"]
36143
36362
  });
36144
36363
  const transcriptRefPtr = { value: path9.join(dir, "transcript.txt") };
36145
- const transcriptOut = fs10.createWriteStream(transcriptRefPtr.value);
36364
+ const transcriptOut = fs11.createWriteStream(transcriptRefPtr.value);
36146
36365
  const outputCbs = [];
36147
36366
  const exitCbs = [];
36148
36367
  const { finish, exited } = makeFinish(id, cfg, transcriptRefPtr, transcriptOut, dir, exitCbs);
@@ -38647,16 +38866,16 @@ var rowToMessage = (row) => ({
38647
38866
  init_trajectory();
38648
38867
  var oasisWrapperCache;
38649
38868
  function oasisWrapperScript() {
38650
- if (oasisWrapperCache && fs12.existsSync(oasisWrapperCache)) return oasisWrapperCache;
38869
+ if (oasisWrapperCache && fs13.existsSync(oasisWrapperCache)) return oasisWrapperCache;
38651
38870
  const repoRoot = (0, import_node_url3.fileURLToPath)(new URL("../../../", __esm_import_meta_url));
38652
38871
  const tsx = path11.join(repoRoot, "node_modules/.bin/tsx");
38653
38872
  const main = path11.join(repoRoot, "packages/cli/src/main.ts");
38654
- const dir = fs12.mkdtempSync(path11.join(os6.tmpdir(), "oasis-cli-"));
38873
+ const dir = fs13.mkdtempSync(path11.join(os6.tmpdir(), "oasis-cli-"));
38655
38874
  const wrapper = path11.join(dir, "oasis");
38656
- fs12.writeFileSync(wrapper, `#!/usr/bin/env bash
38875
+ fs13.writeFileSync(wrapper, `#!/usr/bin/env bash
38657
38876
  exec ${JSON.stringify(tsx)} ${JSON.stringify(main)} "$@"
38658
38877
  `);
38659
- fs12.chmodSync(wrapper, 493);
38878
+ fs13.chmodSync(wrapper, 493);
38660
38879
  oasisWrapperCache = wrapper;
38661
38880
  return wrapper;
38662
38881
  }
@@ -38717,10 +38936,10 @@ function traceRuntimeKind(runtimeKind) {
38717
38936
  return "custom";
38718
38937
  }
38719
38938
  async function startServe(opts) {
38720
- fs12.mkdirSync(opts.dir, { recursive: true });
38939
+ fs13.mkdirSync(opts.dir, { recursive: true });
38721
38940
  const lock = path11.join(opts.dir, "serve.lock");
38722
- if (fs12.existsSync(lock)) {
38723
- const pid = Number(fs12.readFileSync(lock, "utf8"));
38941
+ if (fs13.existsSync(lock)) {
38942
+ const pid = Number(fs13.readFileSync(lock, "utf8"));
38724
38943
  let alive = false;
38725
38944
  try {
38726
38945
  process.kill(pid, 0);
@@ -38728,12 +38947,12 @@ async function startServe(opts) {
38728
38947
  } catch {
38729
38948
  }
38730
38949
  if (alive) throw new Error(`\u8BE5\u6570\u636E\u76EE\u5F55\u5DF2\u6709 serve \u5728\u8DD1\uFF08pid=${pid}\uFF09\u2014\u2014\u5355\u5199\u8005\u8FDB\u7A0B\uFF0C\u4E0D\u80FD\u53CC\u5F00`);
38731
- fs12.unlinkSync(lock);
38950
+ fs13.unlinkSync(lock);
38732
38951
  }
38733
- fs12.writeFileSync(lock, String(process.pid));
38952
+ fs13.writeFileSync(lock, String(process.pid));
38734
38953
  const loadJson = (name) => {
38735
38954
  const file = path11.join(opts.dir, name);
38736
- return fs12.existsSync(file) ? JSON.parse(fs12.readFileSync(file, "utf8")) : void 0;
38955
+ return fs13.existsSync(file) ? JSON.parse(fs13.readFileSync(file, "utf8")) : void 0;
38737
38956
  };
38738
38957
  const oplog = await NdjsonOplogStore.open(path11.join(opts.dir, "oplog.ndjson"));
38739
38958
  const blobs = new DirBlobStore(path11.join(opts.dir, "blobs"));
@@ -38770,12 +38989,13 @@ async function startServe(opts) {
38770
38989
  const issuer = createTokenIssuer();
38771
38990
  const operatorActor = `actor:human:${os6.userInfo().username}`;
38772
38991
  const operatorToken = issuer.issue(operatorActor);
38773
- fs12.writeFileSync(path11.join(opts.dir, "operator-token"), operatorToken, { mode: 384 });
38992
+ fs13.writeFileSync(path11.join(opts.dir, "operator-token"), operatorToken, { mode: 384 });
38774
38993
  const nodeTokens = createNodeTokenStore(path11.join(opts.dir, "node-tokens.json"));
38994
+ const nodeStore = await FileNodeStore.open(path11.join(opts.dir, "nodes.json"));
38775
38995
  const registryAuditFile = path11.join(opts.dir, "registry-audit.ndjson");
38776
38996
  const registryListeners = /* @__PURE__ */ new Set();
38777
38997
  const registryAudit = (record3) => {
38778
- fs12.appendFile(registryAuditFile, JSON.stringify(record3) + "\n", () => {
38998
+ fs13.appendFile(registryAuditFile, JSON.stringify(record3) + "\n", () => {
38779
38999
  });
38780
39000
  for (const l of registryListeners) l({ kind: record3.kind, actor: record3.actor, target: record3.target, timestamp: record3.timestamp });
38781
39001
  };
@@ -38807,6 +39027,7 @@ async function startServe(opts) {
38807
39027
  } else {
38808
39028
  projectStateStore = await FileProjectStateStore.open(path11.join(opts.dir, "artifact-document-projects.json"));
38809
39029
  artifactStateStore = await FileArtifactStateStore.open(path11.join(opts.dir, "artifact-document-state.json"));
39030
+ chatSessionStore = new MemoryChatSessionStore();
38810
39031
  console.log("[serve] \u4EA4\u4ED8\u7269\u72B6\u6001\u4F53\u7CFB\uFF1A\u672C\u5730 JSON dev store");
38811
39032
  }
38812
39033
  const outlineUrl = process.env["OUTLINE_URL"];
@@ -38910,9 +39131,9 @@ async function startServe(opts) {
38910
39131
  const interventionDrafts = new InterventionDraftStore();
38911
39132
  const journalFile = path11.join(opts.dir, "dispatch-journal.ndjson");
38912
39133
  const journalRing = [];
38913
- if (fs12.existsSync(journalFile)) {
39134
+ if (fs13.existsSync(journalFile)) {
38914
39135
  try {
38915
- const lines = fs12.readFileSync(journalFile, "utf8").split(/\r?\n/).filter(Boolean);
39136
+ const lines = fs13.readFileSync(journalFile, "utf8").split(/\r?\n/).filter(Boolean);
38916
39137
  for (const line of lines.slice(-500)) {
38917
39138
  try {
38918
39139
  journalRing.push(JSON.parse(line));
@@ -38925,7 +39146,7 @@ async function startServe(opts) {
38925
39146
  const journal = (entry) => {
38926
39147
  journalRing.push(entry);
38927
39148
  if (journalRing.length > 500) journalRing.shift();
38928
- fs12.appendFile(journalFile, JSON.stringify(entry) + "\n", () => {
39149
+ fs13.appendFile(journalFile, JSON.stringify(entry) + "\n", () => {
38929
39150
  });
38930
39151
  };
38931
39152
  let dispatcher;
@@ -39017,6 +39238,7 @@ async function startServe(opts) {
39017
39238
  nodesDomain({
39018
39239
  getHub: () => hub,
39019
39240
  nodeTokens,
39241
+ nodeStore,
39020
39242
  gatewayUrl: opts.gatewayUrl ?? `ws://127.0.0.1:${opts.port ?? 7320}/node-gateway`,
39021
39243
  repoRemote: opts.nodeRepoRemote ?? "git@github.com:open-friday/oasis-core.git"
39022
39244
  }),
@@ -39030,7 +39252,7 @@ async function startServe(opts) {
39030
39252
  resolveEngine: (cid) => engineRouter.getEngine(cid ?? DEFAULT_COMPANY_ID)
39031
39253
  }),
39032
39254
  trace.register,
39033
- ...chatSessionStore ? [createChatSessionsDomain({ store: chatSessionStore, registry: registryStore })] : []
39255
+ createChatSessionsDomain({ store: chatSessionStore, registry: registryStore })
39034
39256
  ],
39035
39257
  drafts: interventionDrafts,
39036
39258
  // 暴露注册的 artifact 类型,给前端「发起工单」动态拉取根产物可选类型——schema.json 增改即自动扩展,
@@ -39039,7 +39261,8 @@ async function startServe(opts) {
39039
39261
  "artifact-types": () => schema.map((d) => ({ name: d.name, ownerRole: d.ownerRole, contentType: d.contentType })),
39040
39262
  dispatches: () => ({
39041
39263
  recent: journalRing.slice(-100),
39042
- fused: dispatcher?.fusedJobs() ?? []
39264
+ fused: dispatcher?.fusedJobs() ?? [],
39265
+ queued: dispatcher?.queuedCount() ?? 0
39043
39266
  })
39044
39267
  },
39045
39268
  resolveActorEnv: async (actorId) => (await buildActorProvision(actors.service, actorId)).env,
@@ -39282,7 +39505,10 @@ async function startServe(opts) {
39282
39505
  journal,
39283
39506
  // TrajectoryEvent 是唯一事件模型;trace 旁账与 M7 蒸馏库并行消费同一流(§16.D 收口)。
39284
39507
  trajectory: combineTrajectorySinks([trajectorySink, traceStoreSink]),
39285
- ...opts.backoff !== void 0 ? { backoff: opts.backoff } : {}
39508
+ ...opts.backoff !== void 0 ? { backoff: opts.backoff } : {},
39509
+ ...opts.maxConcurrentProduce !== void 0 ? { maxConcurrentProduce: opts.maxConcurrentProduce } : {},
39510
+ // 每 agent 并发产出上限默认 5(工单数 / 全局总量默认不限);--max-produce-per-agent N 覆盖。
39511
+ maxConcurrentProducePerActor: opts.maxConcurrentProducePerActor ?? 5
39286
39512
  });
39287
39513
  const d = dispatcher;
39288
39514
  let logged = 0;
@@ -39444,7 +39670,7 @@ async function startServe(opts) {
39444
39670
  await server.close();
39445
39671
  if (pgPool) await pgPool.end().catch(() => {
39446
39672
  });
39447
- fs12.rmSync(lock, { force: true });
39673
+ fs13.rmSync(lock, { force: true });
39448
39674
  }
39449
39675
  };
39450
39676
  }
@@ -39708,7 +39934,7 @@ async function startNode(opts) {
39708
39934
  init_src4();
39709
39935
  var USAGE = `oasis \u2014\u2014 artifact-centric \u534F\u4F5C\u5185\u6838 CLI\uFF08\u670D\u52A1\u7AEF\u7626\u5BA2\u6237\u7AEF\uFF09
39710
39936
 
39711
- oasis serve [--dir .oasis] [--port 7320] [--dispatch true [--model haiku]] [--allow-simple-tokens true] [--gateway-url wss://host/oasis-api/node-gateway]
39937
+ oasis serve [--dir .oasis] [--port 7320] [--dispatch true [--model haiku] [--max-produce-per-agent 5] [--max-concurrent-produce N]] [--allow-simple-tokens true] [--gateway-url wss://host/oasis-api/node-gateway]
39712
39938
  # --gateway-url\uFF08\u6216 $OASIS_GATEWAY_URL\uFF09\uFF1A\u8282\u70B9\u5165\u7F51\u811A\u672C\u91CC\u5B88\u62A4\u8FDB\u7A0B\u8981\u8FDE\u7684\u7F51\u5173 URL\uFF1B\u516C\u7F51\u7ECF nginx \u5FC5\u8BBE\uFF0C\u5426\u5219\u9ED8\u8BA4\u672C\u673A ws://127.0.0.1:<port>\u3002
39713
39939
  oasis export-trajectories [--dir d] [--out samples.jsonl] \u5BFC\u51FA\u84B8\u998F\u6837\u672C\uFF08\u8F93\u5165\u5305+\u4E8B\u4EF6\u6D41+\u7ED3\u679C\u5F15\u7528+gate \u6807\u7B7E\uFF09
39714
39940
  oasis serve install [--dir d] [--port p] [--dispatch true --model m] [--bin ~/.local/bin/oasis] [--unit-dir ~/.config/systemd/user] \u751F\u6210 systemd \u7528\u6237\u5355\u5143\uFF08D2 \u5B88\u62A4\u5316\uFF09
@@ -39867,19 +40093,19 @@ async function runCli(argv, println = console.log) {
39867
40093
  const serverUrl = flags.get("server-ws") ?? process.env["OASIS_SERVER_WS"] ?? "ws://127.0.0.1:7320/node-gateway";
39868
40094
  const nodeName = flags.get("name") ?? process.env["OASIS_NODE_NAME"];
39869
40095
  const nodeIdFile = path12.join(dir, "node-id");
39870
- const storedNodeId = fs13.existsSync(nodeIdFile) ? fs13.readFileSync(nodeIdFile, "utf8").trim() : null;
40096
+ const storedNodeId = fs14.existsSync(nodeIdFile) ? fs14.readFileSync(nodeIdFile, "utf8").trim() : null;
39871
40097
  const nodeId = storedNodeId ?? flags.get("id") ?? process.env["OASIS_NODE_ID"] ?? `node-${(0, import_node_crypto20.randomUUID)()}`;
39872
40098
  if (!storedNodeId) {
39873
- fs13.mkdirSync(dir, { recursive: true });
39874
- fs13.writeFileSync(nodeIdFile, nodeId);
40099
+ fs14.mkdirSync(dir, { recursive: true });
40100
+ fs14.writeFileSync(nodeIdFile, nodeId);
39875
40101
  }
39876
40102
  const nodeTokenFile = path12.join(dir, "node-token");
39877
- const token2 = flags.get("token") ?? process.env["OASIS_NODE_TOKEN"] ?? (fs13.existsSync(nodeTokenFile) ? fs13.readFileSync(nodeTokenFile, "utf8").trim() : void 0);
40103
+ const token2 = flags.get("token") ?? process.env["OASIS_NODE_TOKEN"] ?? (fs14.existsSync(nodeTokenFile) ? fs14.readFileSync(nodeTokenFile, "utf8").trim() : void 0);
39878
40104
  if (flags.has("service")) {
39879
40105
  const { spawnSync, execSync: execSync2 } = await import("node:child_process");
39880
40106
  if (token2) {
39881
- fs13.mkdirSync(dir, { recursive: true });
39882
- fs13.writeFileSync(nodeTokenFile, token2, { mode: 384 });
40107
+ fs14.mkdirSync(dir, { recursive: true });
40108
+ fs14.writeFileSync(nodeTokenFile, token2, { mode: 384 });
39883
40109
  }
39884
40110
  println("\u2192 Installing oasis_test globally...");
39885
40111
  spawnSync("npm", ["install", "-g", "oasis_test@latest"], { stdio: "inherit" });
@@ -39888,10 +40114,10 @@ async function runCli(argv, println = console.log) {
39888
40114
  const svcArgs = `node --server-ws ${serverUrl} --id ${nodeId} --dir ${dir}${nodeName ? ` --name ${nodeName}` : ""}`;
39889
40115
  if (process.platform === "linux") {
39890
40116
  const svcFile = path12.join(os7.homedir(), ".config/systemd/user/oasis-node.service");
39891
- fs13.mkdirSync(path12.dirname(svcFile), { recursive: true });
40117
+ fs14.mkdirSync(path12.dirname(svcFile), { recursive: true });
39892
40118
  if (spawnSync("systemctl", ["--user", "is-active", "--quiet", "oasis-node"], {}).status === 0)
39893
40119
  spawnSync("systemctl", ["--user", "stop", "oasis-node"], { stdio: "inherit" });
39894
- fs13.writeFileSync(svcFile, [
40120
+ fs14.writeFileSync(svcFile, [
39895
40121
  "[Unit]",
39896
40122
  "Description=Oasis Node Daemon",
39897
40123
  "After=network.target",
@@ -39916,10 +40142,10 @@ async function runCli(argv, println = console.log) {
39916
40142
  println(" Stop : systemctl --user stop oasis-node");
39917
40143
  } else if (process.platform === "darwin") {
39918
40144
  const plist = path12.join(os7.homedir(), "Library/LaunchAgents/com.oasis.node.plist");
39919
- fs13.mkdirSync(path12.dirname(plist), { recursive: true });
40145
+ fs14.mkdirSync(path12.dirname(plist), { recursive: true });
39920
40146
  spawnSync("launchctl", ["unload", plist], { stdio: "ignore" });
39921
40147
  const argXml = [binPath, "node", "--server-ws", serverUrl, "--id", nodeId, "--dir", dir, ...nodeName ? ["--name", nodeName] : []].map((a) => `<string>${a}</string>`).join("");
39922
- fs13.writeFileSync(plist, `<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"><plist version="1.0"><dict><key>Label</key><string>com.oasis.node</string><key>ProgramArguments</key><array>${argXml}</array><key>RunAtLoad</key><true/><key>KeepAlive</key><true/><key>StandardOutPath</key><string>${os7.homedir()}/.oasis-node.log</string><key>StandardErrorPath</key><string>${os7.homedir()}/.oasis-node.err</string></dict></plist>`);
40148
+ fs14.writeFileSync(plist, `<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"><plist version="1.0"><dict><key>Label</key><string>com.oasis.node</string><key>ProgramArguments</key><array>${argXml}</array><key>RunAtLoad</key><true/><key>KeepAlive</key><true/><key>StandardOutPath</key><string>${os7.homedir()}/.oasis-node.log</string><key>StandardErrorPath</key><string>${os7.homedir()}/.oasis-node.err</string></dict></plist>`);
39923
40149
  spawnSync("launchctl", ["load", plist], { stdio: "inherit" });
39924
40150
  println("\u2713 Oasis node service started (macOS LaunchAgent com.oasis.node)");
39925
40151
  println(` Logs : tail -f ${os7.homedir()}/.oasis-node.log`);
@@ -39979,7 +40205,7 @@ async function runCli(argv, println = console.log) {
39979
40205
  const outFile = flags.get("out");
39980
40206
  const lines = samples.map((sample) => JSON.stringify(sample)).join("\n");
39981
40207
  if (outFile !== void 0) {
39982
- fs13.writeFileSync(outFile, lines + (lines ? "\n" : ""));
40208
+ fs14.writeFileSync(outFile, lines + (lines ? "\n" : ""));
39983
40209
  println(`\u5DF2\u5BFC\u51FA ${samples.length} \u6761\u8F68\u8FF9\u6837\u672C \u2192 ${outFile}\uFF08approved/rejected \u6210\u5BF9\u5373\u504F\u597D\u5BF9\uFF09`);
39984
40210
  } else {
39985
40211
  println(lines);
@@ -40007,9 +40233,9 @@ async function runCli(argv, println = console.log) {
40007
40233
  `WantedBy=default.target`,
40008
40234
  ``
40009
40235
  ].join("\n");
40010
- fs13.mkdirSync(unitDir, { recursive: true });
40236
+ fs14.mkdirSync(unitDir, { recursive: true });
40011
40237
  const unitPath = path12.join(unitDir, `${name}.service`);
40012
- fs13.writeFileSync(unitPath, unit);
40238
+ fs14.writeFileSync(unitPath, unit);
40013
40239
  println(`\u5DF2\u5199\u5165 ${unitPath}`);
40014
40240
  println(`\u542F\u7528\uFF1Asystemctl --user daemon-reload && systemctl --user enable --now ${name}`);
40015
40241
  println(`\u5F00\u673A\u81EA\u542F\uFF08\u542B\u672A\u767B\u5F55\uFF09\uFF1Aloginctl enable-linger $USER`);
@@ -40022,6 +40248,8 @@ async function runCli(argv, println = console.log) {
40022
40248
  dir,
40023
40249
  ...port !== void 0 ? { port } : {},
40024
40250
  dispatch: flags.get("dispatch") === "true",
40251
+ ...flags.has("max-concurrent-produce") ? { maxConcurrentProduce: Number(flags.get("max-concurrent-produce")) } : {},
40252
+ ...flags.has("max-produce-per-agent") ? { maxConcurrentProducePerActor: Number(flags.get("max-produce-per-agent")) } : {},
40025
40253
  ...flags.has("model") ? { model: flags.get("model") } : {},
40026
40254
  allowSimpleTokens: flags.get("allow-simple-tokens") === "true",
40027
40255
  ...flags.get("gateway-url") ?? process.env["OASIS_GATEWAY_URL"] ? { gatewayUrl: flags.get("gateway-url") ?? process.env["OASIS_GATEWAY_URL"] } : {}
@@ -40036,12 +40264,12 @@ async function runCli(argv, println = console.log) {
40036
40264
  }
40037
40265
  const base = flags.get("server") ?? process.env["OASIS_SERVER"] ?? "http://127.0.0.1:7320";
40038
40266
  const tokenFile = path12.join(dir, "operator-token");
40039
- const token = flags.get("token") ?? process.env["OASIS_TOKEN"] ?? (fs13.existsSync(tokenFile) ? fs13.readFileSync(tokenFile, "utf8").trim() : void 0);
40267
+ const token = flags.get("token") ?? process.env["OASIS_TOKEN"] ?? (fs14.existsSync(tokenFile) ? fs14.readFileSync(tokenFile, "utf8").trim() : void 0);
40040
40268
  if (!token) {
40041
40269
  throw new Error(`\u65E0 token\uFF1A\u5148 oasis serve\uFF08\u4F1A\u751F\u6210 ${tokenFile}\uFF09\uFF0C\u6216\u663E\u5F0F --token / $OASIS_TOKEN`);
40042
40270
  }
40043
40271
  const api = makeClient(base, token);
40044
- const readFileArg = (file) => fs13.readFileSync(file, "utf8");
40272
+ const readFileArg = (file) => fs14.readFileSync(file, "utf8");
40045
40273
  const parseJsonFlag = (value, name) => {
40046
40274
  const parsed = JSON.parse(value);
40047
40275
  if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
@@ -40059,11 +40287,11 @@ async function runCli(argv, println = console.log) {
40059
40287
  const readDirFiles = (root) => {
40060
40288
  const out = [];
40061
40289
  const walk = (abs, rel) => {
40062
- for (const name of fs13.readdirSync(abs)) {
40290
+ for (const name of fs14.readdirSync(abs)) {
40063
40291
  const childAbs = path12.join(abs, name);
40064
40292
  const childRel = rel ? `${rel}/${name}` : name;
40065
- if (fs13.statSync(childAbs).isDirectory()) walk(childAbs, childRel);
40066
- else out.push({ path: childRel, contentBase64: fs13.readFileSync(childAbs).toString("base64") });
40293
+ if (fs14.statSync(childAbs).isDirectory()) walk(childAbs, childRel);
40294
+ else out.push({ path: childRel, contentBase64: fs14.readFileSync(childAbs).toString("base64") });
40067
40295
  }
40068
40296
  };
40069
40297
  walk(root, "");
@@ -40747,18 +40975,18 @@ var parseFlags = (argv) => {
40747
40975
  };
40748
40976
  var readConfig = () => {
40749
40977
  try {
40750
- return JSON.parse(fs14.readFileSync(CONFIG_FILE, "utf8"));
40978
+ return JSON.parse(fs15.readFileSync(CONFIG_FILE, "utf8"));
40751
40979
  } catch {
40752
40980
  return null;
40753
40981
  }
40754
40982
  };
40755
40983
  var saveConfig = (c) => {
40756
- fs14.mkdirSync(OASIS_DIR, { recursive: true });
40757
- fs14.writeFileSync(CONFIG_FILE, JSON.stringify(c), { mode: 384 });
40984
+ fs15.mkdirSync(OASIS_DIR, { recursive: true });
40985
+ fs15.writeFileSync(CONFIG_FILE, JSON.stringify(c), { mode: 384 });
40758
40986
  };
40759
40987
  var readPid = () => {
40760
40988
  try {
40761
- const p = parseInt(fs14.readFileSync(PID_FILE, "utf8").trim(), 10);
40989
+ const p = parseInt(fs15.readFileSync(PID_FILE, "utf8").trim(), 10);
40762
40990
  return isNaN(p) ? null : p;
40763
40991
  } catch {
40764
40992
  return null;
@@ -40774,10 +41002,10 @@ var isRunning = (pid) => {
40774
41002
  };
40775
41003
  var httpBase = (wsUrl) => wsUrl.replace(/^wss?:\/\//, "http://").replace(/\/node-gateway.*$/, "");
40776
41004
  function installBinary() {
40777
- fs14.mkdirSync(path13.dirname(BIN_FILE), { recursive: true });
40778
- fs14.copyFileSync(process.argv[1], BIN_FILE);
40779
- fs14.mkdirSync(path13.dirname(LOCAL_BIN), { recursive: true });
40780
- fs14.writeFileSync(LOCAL_BIN, `#!/bin/sh
41005
+ fs15.mkdirSync(path13.dirname(BIN_FILE), { recursive: true });
41006
+ fs15.copyFileSync(process.argv[1], BIN_FILE);
41007
+ fs15.mkdirSync(path13.dirname(LOCAL_BIN), { recursive: true });
41008
+ fs15.writeFileSync(LOCAL_BIN, `#!/bin/sh
40781
41009
  exec node "${BIN_FILE}" "$@"
40782
41010
  `, { mode: 493 });
40783
41011
  }
@@ -40785,8 +41013,8 @@ function setupAutostart() {
40785
41013
  const cmd = `${process.execPath} "${BIN_FILE}" --_daemon`;
40786
41014
  if (process.platform === "linux") {
40787
41015
  const unitDir = path13.join(os8.homedir(), ".config", "systemd", "user");
40788
- fs14.mkdirSync(unitDir, { recursive: true });
40789
- fs14.writeFileSync(path13.join(unitDir, "oasis-node.service"), [
41016
+ fs15.mkdirSync(unitDir, { recursive: true });
41017
+ fs15.writeFileSync(path13.join(unitDir, "oasis-node.service"), [
40790
41018
  "[Unit]",
40791
41019
  "Description=Oasis Node Daemon",
40792
41020
  "After=network.target",
@@ -40808,8 +41036,8 @@ function setupAutostart() {
40808
41036
  }
40809
41037
  } else if (process.platform === "darwin") {
40810
41038
  const plist = path13.join(os8.homedir(), "Library", "LaunchAgents", "com.oasis.node.plist");
40811
- fs14.mkdirSync(path13.dirname(plist), { recursive: true });
40812
- fs14.writeFileSync(plist, `<?xml version="1.0" encoding="UTF-8"?>
41039
+ fs15.mkdirSync(path13.dirname(plist), { recursive: true });
41040
+ fs15.writeFileSync(plist, `<?xml version="1.0" encoding="UTF-8"?>
40813
41041
  <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
40814
41042
  <plist version="1.0"><dict>
40815
41043
  <key>Label</key><string>com.oasis.node</string>
@@ -40825,14 +41053,14 @@ function setupAutostart() {
40825
41053
  }
40826
41054
  }
40827
41055
  function spawnDaemon() {
40828
- const log3 = fs14.openSync(LOG_FILE, "a");
41056
+ const log3 = fs15.openSync(LOG_FILE, "a");
40829
41057
  const child = (0, import_node_child_process10.spawn)(process.execPath, [BIN_FILE, "--_daemon"], {
40830
41058
  detached: true,
40831
41059
  stdio: ["ignore", log3, log3]
40832
41060
  });
40833
41061
  child.unref();
40834
41062
  const pid = child.pid ?? 0;
40835
- fs14.writeFileSync(PID_FILE, String(pid));
41063
+ fs15.writeFileSync(PID_FILE, String(pid));
40836
41064
  return pid;
40837
41065
  }
40838
41066
  function stopDaemon() {
@@ -40844,7 +41072,7 @@ function stopDaemon() {
40844
41072
  }
40845
41073
  }
40846
41074
  try {
40847
- fs14.unlinkSync(PID_FILE);
41075
+ fs15.unlinkSync(PID_FILE);
40848
41076
  } catch {
40849
41077
  }
40850
41078
  try {
@@ -40865,10 +41093,10 @@ void (async () => {
40865
41093
  console.error("[oasis] no config \u2014 run `oasis start` first");
40866
41094
  process.exit(1);
40867
41095
  }
40868
- fs14.writeFileSync(PID_FILE, String(process.pid));
41096
+ fs15.writeFileSync(PID_FILE, String(process.pid));
40869
41097
  const cleanup = () => {
40870
41098
  try {
40871
- fs14.unlinkSync(PID_FILE);
41099
+ fs15.unlinkSync(PID_FILE);
40872
41100
  } catch {
40873
41101
  }
40874
41102
  };
@@ -40894,7 +41122,7 @@ void (async () => {
40894
41122
  serverUrl: cfg.serverUrl,
40895
41123
  token: cfg.token,
40896
41124
  nodeId: cfg.nodeId ?? `node-${(0, import_node_crypto21.randomUUID)()}`,
40897
- ...cfg.name ? { nodeName: cfg.name } : {}
41125
+ nodeName: cfg.name ?? cfg.nodeId ?? `node-${(0, import_node_crypto21.randomUUID)()}`
40898
41126
  });
40899
41127
  return;
40900
41128
  }
@@ -40906,7 +41134,8 @@ void (async () => {
40906
41134
  console.error("Usage: oasis start --server-ws <wss://...> --token <token> [--name <name>] [--id <nodeId>]");
40907
41135
  process.exit(1);
40908
41136
  }
40909
- const nodeId = prev?.nodeId ?? flags.get("id") ?? `node-${(0, import_node_crypto21.randomUUID)()}`;
41137
+ const isReEnrollment = flags.get("token") && flags.get("token") !== prev?.token;
41138
+ const nodeId = isReEnrollment ? flags.get("id") ?? prev?.nodeId ?? `node-${(0, import_node_crypto21.randomUUID)()}` : prev?.nodeId ?? flags.get("id") ?? `node-${(0, import_node_crypto21.randomUUID)()}`;
40910
41139
  const name = flags.get("name") ?? prev?.name;
40911
41140
  const nameChanged = prev?.name !== void 0 && name !== prev.name;
40912
41141
  saveConfig({ serverUrl, token, nodeId, ...name ? { name } : {} });