oasis_test 0.1.4 → 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 +477 -220
  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,77 +27448,122 @@ 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 });
27452
27558
  return { status: 200, body: { nodeId, nodeName, token, script, gatewayUrl: deps.gatewayUrl, npxCmd } };
27453
27559
  });
27560
+ router.patch("/api/nodes/:nodeId", async (req) => {
27561
+ const nodeId = req.params["nodeId"];
27562
+ if (!nodeId) return { status: 400, body: { error: "missing nodeId" } };
27563
+ const body = req.body ?? {};
27564
+ if (body.name) await deps.nodeStore.updateNodeName(nodeId, body.name);
27565
+ return { status: 200, body: { ok: true } };
27566
+ });
27454
27567
  router.delete("/api/nodes/:nodeId", async (req) => {
27455
27568
  const nodeId = req.params["nodeId"];
27456
27569
  if (!nodeId) return { status: 400, body: { error: "missing nodeId" } };
@@ -27459,27 +27572,17 @@ function nodesDomain(deps) {
27459
27572
  }
27460
27573
  const hub = deps.getHub();
27461
27574
  hub?.connectedDaemons().find((d) => d.daemonId === nodeId)?.close();
27462
- hub?.evictGhost(nodeId);
27463
- 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);
27464
27582
  return { status: 200, body: { ok: true } };
27465
27583
  });
27466
27584
  };
27467
27585
  }
27468
- function runtimeInfos(nodeId, meta, status) {
27469
- const lastReportAt = (/* @__PURE__ */ new Date()).toISOString();
27470
- const capabilities = meta.runtimes?.length ? meta.runtimes : meta.adapters.map((kind) => ({ kind }));
27471
- return capabilities.map((r) => ({
27472
- id: `runtime:${nodeId}:${r.kind}`,
27473
- nodeId,
27474
- hostname: meta.hostname,
27475
- kind: r.kind,
27476
- ...r.binary ? { binary: r.binary } : {},
27477
- ...r.version ? { version: r.version } : {},
27478
- status,
27479
- busy: false,
27480
- lastReportAt
27481
- }));
27482
- }
27483
27586
  var import_node_crypto9;
27484
27587
  var init_routes4 = __esm({
27485
27588
  "../server/src/domains/nodes/routes.ts"() {
@@ -27489,6 +27592,124 @@ var init_routes4 = __esm({
27489
27592
  }
27490
27593
  });
27491
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
+
27492
27713
  // ../server/src/domains/collab/workorder-detail.ts
27493
27714
  function buildWorkorderDetail(model, workspaceId, resolveActor, resolveProject, dispatchJournal = []) {
27494
27715
  const arts = [...model.artifacts.values()].filter((a) => a.workspace === workspaceId);
@@ -27508,6 +27729,7 @@ function buildWorkorderDetail(model, workspaceId, resolveActor, resolveProject,
27508
27729
  const nodes = arts.map((a) => {
27509
27730
  const info = nodeInfo(model, a);
27510
27731
  const runtime = runtimeByArtifact.get(a.id);
27732
+ const lifecycle = lifecycleOf(model, a.id);
27511
27733
  return {
27512
27734
  id: a.id,
27513
27735
  type: a.type,
@@ -27517,6 +27739,8 @@ function buildWorkorderDetail(model, workspaceId, resolveActor, resolveProject,
27517
27739
  queue: info.queueLen,
27518
27740
  blocked: blockedOf(model, a.id).blocked,
27519
27741
  owner: ref(a.owner),
27742
+ // 封存原因透出:stage 只到 "sealed" 粒度,原因(accepted/cancelled/frozen)另给,供前端区分展示。
27743
+ ...lifecycle !== "active" ? { sealedReason: lifecycle.slice("sealed:".length) } : {},
27520
27744
  ...runtime ? { runtime } : {}
27521
27745
  };
27522
27746
  });
@@ -29049,22 +29273,22 @@ var init_engine = __esm({
29049
29273
  });
29050
29274
 
29051
29275
  // ../server/src/mirror/fs-state.ts
29052
- var fs5, FsMirrorStateStore;
29276
+ var fs6, FsMirrorStateStore;
29053
29277
  var init_fs_state = __esm({
29054
29278
  "../server/src/mirror/fs-state.ts"() {
29055
29279
  "use strict";
29056
- fs5 = __toESM(require("node:fs"), 1);
29280
+ fs6 = __toESM(require("node:fs"), 1);
29057
29281
  FsMirrorStateStore = class {
29058
29282
  constructor(file) {
29059
29283
  this.file = file;
29060
- if (fs5.existsSync(file)) {
29061
- const obj = JSON.parse(fs5.readFileSync(file, "utf8"));
29284
+ if (fs6.existsSync(file)) {
29285
+ const obj = JSON.parse(fs6.readFileSync(file, "utf8"));
29062
29286
  for (const [k, v] of Object.entries(obj)) this.map.set(k, v);
29063
29287
  }
29064
29288
  }
29065
29289
  map = /* @__PURE__ */ new Map();
29066
29290
  flush() {
29067
- 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));
29068
29292
  }
29069
29293
  async get(artifactId) {
29070
29294
  return this.map.get(artifactId) ?? null;
@@ -29081,12 +29305,12 @@ var init_fs_state = __esm({
29081
29305
  });
29082
29306
 
29083
29307
  // ../server/src/git/integrate.ts
29084
- var import_node_child_process2, fs6, path4, GitRemoteIntegrator;
29308
+ var import_node_child_process2, fs7, path4, GitRemoteIntegrator;
29085
29309
  var init_integrate = __esm({
29086
29310
  "../server/src/git/integrate.ts"() {
29087
29311
  "use strict";
29088
29312
  import_node_child_process2 = require("node:child_process");
29089
- fs6 = __toESM(require("node:fs"), 1);
29313
+ fs7 = __toESM(require("node:fs"), 1);
29090
29314
  path4 = __toESM(require("node:path"), 1);
29091
29315
  GitRemoteIntegrator = class {
29092
29316
  constructor(remote, cloneDir, develop = "develop") {
@@ -29104,8 +29328,8 @@ var init_integrate = __esm({
29104
29328
  }
29105
29329
  /** 确保本地工作克隆存在并拉到最新远程态。幂等。 */
29106
29330
  sync() {
29107
- if (!fs6.existsSync(path4.join(this.cloneDir, ".git"))) {
29108
- 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 });
29109
29333
  (0, import_node_child_process2.execFileSync)("git", ["clone", "--quiet", this.remote, this.cloneDir], { env: this.env, maxBuffer: 256 * 1024 * 1024 });
29110
29334
  this.git(["config", "user.email", "oasis@local"]);
29111
29335
  this.git(["config", "user.name", "oasis"]);
@@ -29511,9 +29735,11 @@ var init_src4 = __esm({
29511
29735
  init_auth();
29512
29736
  init_sender();
29513
29737
  init_routes4();
29738
+ init_node_store();
29514
29739
  init_collab();
29515
29740
  init_trace2();
29516
29741
  init_chat_sessions();
29742
+ init_dev_store();
29517
29743
  init_daemon_adapter();
29518
29744
  init_engine();
29519
29745
  init_fs_state();
@@ -31113,15 +31339,15 @@ var require_pg_connection_string = __commonJS({
31113
31339
  if (config2.sslcert || config2.sslkey || config2.sslrootcert || config2.sslmode) {
31114
31340
  config2.ssl = {};
31115
31341
  }
31116
- const fs15 = config2.sslcert || config2.sslkey || config2.sslrootcert ? require("fs") : null;
31342
+ const fs16 = config2.sslcert || config2.sslkey || config2.sslrootcert ? require("fs") : null;
31117
31343
  if (config2.sslcert) {
31118
- config2.ssl.cert = fs15.readFileSync(config2.sslcert).toString();
31344
+ config2.ssl.cert = fs16.readFileSync(config2.sslcert).toString();
31119
31345
  }
31120
31346
  if (config2.sslkey) {
31121
- config2.ssl.key = fs15.readFileSync(config2.sslkey).toString();
31347
+ config2.ssl.key = fs16.readFileSync(config2.sslkey).toString();
31122
31348
  }
31123
31349
  if (config2.sslrootcert) {
31124
- config2.ssl.ca = fs15.readFileSync(config2.sslrootcert).toString();
31350
+ config2.ssl.ca = fs16.readFileSync(config2.sslrootcert).toString();
31125
31351
  }
31126
31352
  if (options.useLibpqCompat && config2.uselibpqcompat) {
31127
31353
  throw new Error("Both useLibpqCompat and uselibpqcompat are set. Please use only one of them.");
@@ -33058,15 +33284,15 @@ var require_lib = __commonJS({
33058
33284
  "../../node_modules/.pnpm/pgpass@1.0.5/node_modules/pgpass/lib/index.js"(exports2, module2) {
33059
33285
  "use strict";
33060
33286
  var path14 = require("path");
33061
- var fs15 = require("fs");
33287
+ var fs16 = require("fs");
33062
33288
  var helper = require_helper();
33063
33289
  module2.exports = function(connInfo, cb) {
33064
33290
  var file = helper.getFileName();
33065
- fs15.stat(file, function(err, stat) {
33291
+ fs16.stat(file, function(err, stat) {
33066
33292
  if (err || !helper.usePgPass(stat, file)) {
33067
33293
  return cb(void 0);
33068
33294
  }
33069
- var st = fs15.createReadStream(file);
33295
+ var st = fs16.createReadStream(file);
33070
33296
  helper.getPassword(connInfo, st, cb);
33071
33297
  });
33072
33298
  };
@@ -34620,37 +34846,37 @@ __export(trajectory_exports, {
34620
34846
  });
34621
34847
  async function exportTrajectories(dataDir) {
34622
34848
  const root = path10.join(dataDir, "trajectories");
34623
- if (!fs11.existsSync(root)) return [];
34849
+ if (!fs12.existsSync(root)) return [];
34624
34850
  const model = emptyReadModel();
34625
34851
  const oplogFile = path10.join(dataDir, "oplog.ndjson");
34626
- if (fs11.existsSync(oplogFile)) {
34852
+ if (fs12.existsSync(oplogFile)) {
34627
34853
  const oplog = await NdjsonOplogStore.open(oplogFile);
34628
34854
  for await (const entry of oplog.readAll()) fold(model, entry.op);
34629
34855
  }
34630
34856
  const samples = [];
34631
- for (const sessionId of fs11.readdirSync(root).sort()) {
34857
+ for (const sessionId of fs12.readdirSync(root).sort()) {
34632
34858
  const dir = path10.join(root, sessionId);
34633
34859
  const sessionFile = path10.join(dir, "session.json");
34634
- if (!fs11.existsSync(sessionFile)) continue;
34635
- const session = JSON.parse(fs11.readFileSync(sessionFile, "utf8"));
34860
+ if (!fs12.existsSync(sessionFile)) continue;
34861
+ const session = JSON.parse(fs12.readFileSync(sessionFile, "utf8"));
34636
34862
  const events = [];
34637
34863
  const eventsFile = path10.join(dir, "events.ndjson");
34638
- if (fs11.existsSync(eventsFile)) {
34639
- 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")) {
34640
34866
  if (line.trim()) events.push(JSON.parse(line));
34641
34867
  }
34642
34868
  }
34643
34869
  const bundle = {};
34644
34870
  const bundleDir = path10.join(dir, "bundle");
34645
34871
  const walk = (sub) => {
34646
- for (const name of fs11.readdirSync(path10.join(bundleDir, sub))) {
34872
+ for (const name of fs12.readdirSync(path10.join(bundleDir, sub))) {
34647
34873
  const rel = sub ? `${sub}/${name}` : name;
34648
34874
  const full = path10.join(bundleDir, rel);
34649
- if (fs11.statSync(full).isDirectory()) walk(rel);
34650
- else bundle[rel] = fs11.readFileSync(full, "utf8");
34875
+ if (fs12.statSync(full).isDirectory()) walk(rel);
34876
+ else bundle[rel] = fs12.readFileSync(full, "utf8");
34651
34877
  }
34652
34878
  };
34653
- if (fs11.existsSync(bundleDir)) walk("");
34879
+ if (fs12.existsSync(bundleDir)) walk("");
34654
34880
  const proposed = (session.opRefs ?? []).filter((r) => r.kind === "propose_revision").map((r) => r.target);
34655
34881
  const verdicts = proposed.flatMap(
34656
34882
  (rev) => (model.reviews.get(rev) ?? []).map((v) => ({ author: v.author, verdict: v.verdict, ...v.note !== void 0 ? { note: v.note } : {} }))
@@ -34662,11 +34888,11 @@ async function exportTrajectories(dataDir) {
34662
34888
  }
34663
34889
  return samples;
34664
34890
  }
34665
- var fs11, path10, FsTrajectorySink;
34891
+ var fs12, path10, FsTrajectorySink;
34666
34892
  var init_trajectory = __esm({
34667
34893
  "../cli/src/trajectory.ts"() {
34668
34894
  "use strict";
34669
- fs11 = __toESM(require("node:fs"), 1);
34895
+ fs12 = __toESM(require("node:fs"), 1);
34670
34896
  path10 = __toESM(require("node:path"), 1);
34671
34897
  init_src2();
34672
34898
  init_src4();
@@ -34681,25 +34907,25 @@ var init_trajectory = __esm({
34681
34907
  }
34682
34908
  begin(session, bundleFiles) {
34683
34909
  const dir = this.dir(session.sessionId);
34684
- fs11.mkdirSync(path10.join(dir, "bundle"), { recursive: true });
34685
- 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));
34686
34912
  for (const [rel, content] of Object.entries(bundleFiles)) {
34687
34913
  const file = path10.join(dir, "bundle", rel);
34688
- fs11.mkdirSync(path10.dirname(file), { recursive: true });
34689
- fs11.writeFileSync(file, content);
34914
+ fs12.mkdirSync(path10.dirname(file), { recursive: true });
34915
+ fs12.writeFileSync(file, content);
34690
34916
  }
34691
34917
  this.broadcast?.({ type: "begin", sessionId: session.sessionId, data: session });
34692
34918
  }
34693
34919
  event(sessionId, event) {
34694
- 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");
34695
34921
  this.broadcast?.({ type: "event", sessionId, data: event });
34696
34922
  }
34697
34923
  end(sessionId, update) {
34698
34924
  const file = path10.join(this.dir(sessionId), "session.json");
34699
- const session = JSON.parse(fs11.readFileSync(file, "utf8"));
34925
+ const session = JSON.parse(fs12.readFileSync(file, "utf8"));
34700
34926
  session.exit = update.exit;
34701
34927
  session.opRefs = update.opRefs;
34702
- fs11.writeFileSync(file, JSON.stringify(session, null, 2));
34928
+ fs12.writeFileSync(file, JSON.stringify(session, null, 2));
34703
34929
  this.broadcast?.({ type: "end", sessionId, data: session });
34704
34930
  }
34705
34931
  };
@@ -34708,19 +34934,19 @@ var init_trajectory = __esm({
34708
34934
 
34709
34935
  // src/index.ts
34710
34936
  var import_node_crypto21 = require("node:crypto");
34711
- var fs14 = __toESM(require("node:fs"));
34937
+ var fs15 = __toESM(require("node:fs"));
34712
34938
  var os8 = __toESM(require("node:os"));
34713
34939
  var path13 = __toESM(require("node:path"));
34714
34940
  var import_node_child_process10 = require("node:child_process");
34715
34941
 
34716
34942
  // ../cli/src/cli.ts
34717
- var fs13 = __toESM(require("node:fs"), 1);
34943
+ var fs14 = __toESM(require("node:fs"), 1);
34718
34944
  var os7 = __toESM(require("node:os"), 1);
34719
34945
  var path12 = __toESM(require("node:path"), 1);
34720
34946
  var import_node_crypto20 = require("node:crypto");
34721
34947
 
34722
34948
  // ../cli/src/serve.ts
34723
- var fs12 = __toESM(require("node:fs"), 1);
34949
+ var fs13 = __toESM(require("node:fs"), 1);
34724
34950
  var os6 = __toESM(require("node:os"), 1);
34725
34951
  var path11 = __toESM(require("node:path"), 1);
34726
34952
  var import_node_crypto19 = require("node:crypto");
@@ -34929,7 +35155,7 @@ var import_node_path3 = require("node:path");
34929
35155
  // ../adapters/src/claude-code/index.ts
34930
35156
  var import_node_child_process5 = require("node:child_process");
34931
35157
  var import_node_crypto14 = require("node:crypto");
34932
- var fs7 = __toESM(require("node:fs"), 1);
35158
+ var fs8 = __toESM(require("node:fs"), 1);
34933
35159
  var os2 = __toESM(require("node:os"), 1);
34934
35160
  var path6 = __toESM(require("node:path"), 1);
34935
35161
  var readline = __toESM(require("node:readline"), 1);
@@ -35048,13 +35274,13 @@ var ClaudeCodeAdapter = class {
35048
35274
  async spawn(job) {
35049
35275
  const slug4 = job.artifactId.replace(/[^a-zA-Z0-9_-]+/g, "_").slice(0, 32);
35050
35276
  const id = `claude-${slug4}-${(0, import_node_crypto14.randomUUID)().slice(0, 8)}`;
35051
- const dir = fs7.mkdtempSync(
35277
+ const dir = fs8.mkdtempSync(
35052
35278
  path6.join(this.opts.workRoot ?? os2.tmpdir(), "oasis-session-")
35053
35279
  );
35054
35280
  for (const [rel, content] of Object.entries(job.bundle.files)) {
35055
35281
  const file = path6.join(dir, rel);
35056
- fs7.mkdirSync(path6.dirname(file), { recursive: true });
35057
- fs7.writeFileSync(file, content);
35282
+ fs8.mkdirSync(path6.dirname(file), { recursive: true });
35283
+ fs8.writeFileSync(file, content);
35058
35284
  }
35059
35285
  const prompt = job.bundle.files["TASK.md"];
35060
35286
  if (!prompt) throw new Error("bundle \u7F3A TASK.md\uFF08\u88C5\u914D\u5668\u5951\u7EA6\uFF09");
@@ -35091,7 +35317,7 @@ var ClaudeCodeAdapter = class {
35091
35317
  stdio: ["ignore", "pipe", "pipe"]
35092
35318
  });
35093
35319
  let transcriptRef = path6.join(dir, "transcript.txt");
35094
- const out = fs7.createWriteStream(transcriptRef);
35320
+ const out = fs8.createWriteStream(transcriptRef);
35095
35321
  const telemetryCbs = [];
35096
35322
  let eventSeq = 0;
35097
35323
  let lastResult;
@@ -35154,13 +35380,13 @@ var ClaudeCodeAdapter = class {
35154
35380
  if (this.opts.cleanupWorkdir) {
35155
35381
  try {
35156
35382
  const keepDir = path6.join(this.opts.workRoot ?? os2.tmpdir(), "oasis-transcripts");
35157
- fs7.mkdirSync(keepDir, { recursive: true });
35383
+ fs8.mkdirSync(keepDir, { recursive: true });
35158
35384
  const kept = path6.join(keepDir, `${id}.txt`);
35159
35385
  const src = transcriptRef;
35160
35386
  out.end(() => {
35161
35387
  try {
35162
- fs7.renameSync(src, kept);
35163
- fs7.rmSync(dir, { recursive: true, force: true });
35388
+ fs8.renameSync(src, kept);
35389
+ fs8.rmSync(dir, { recursive: true, force: true });
35164
35390
  } catch {
35165
35391
  }
35166
35392
  });
@@ -35195,7 +35421,7 @@ var ClaudeCodeAdapter = class {
35195
35421
  // ../adapters/src/codex/index.ts
35196
35422
  var import_node_child_process6 = require("node:child_process");
35197
35423
  var import_node_crypto15 = require("node:crypto");
35198
- var fs8 = __toESM(require("node:fs"), 1);
35424
+ var fs9 = __toESM(require("node:fs"), 1);
35199
35425
  var os3 = __toESM(require("node:os"), 1);
35200
35426
  var path7 = __toESM(require("node:path"), 1);
35201
35427
  var readline2 = __toESM(require("node:readline"), 1);
@@ -35240,7 +35466,7 @@ function codexSessionsRoot() {
35240
35466
  if (home) candidates.push(path7.join(home, ".codex", "sessions"));
35241
35467
  for (const c of candidates) {
35242
35468
  try {
35243
- if (fs8.statSync(c).isDirectory()) return c;
35469
+ if (fs9.statSync(c).isDirectory()) return c;
35244
35470
  } catch {
35245
35471
  }
35246
35472
  }
@@ -35252,7 +35478,7 @@ function listRecentRollouts(root, sinceMs) {
35252
35478
  const walk = (d) => {
35253
35479
  let ents;
35254
35480
  try {
35255
- ents = fs8.readdirSync(d, { withFileTypes: true });
35481
+ ents = fs9.readdirSync(d, { withFileTypes: true });
35256
35482
  } catch {
35257
35483
  return;
35258
35484
  }
@@ -35262,7 +35488,7 @@ function listRecentRollouts(root, sinceMs) {
35262
35488
  else if (ent.isFile() && ent.name.startsWith("rollout-") && ent.name.endsWith(".jsonl")) {
35263
35489
  let m = 0;
35264
35490
  try {
35265
- m = fs8.statSync(p).mtimeMs;
35491
+ m = fs9.statSync(p).mtimeMs;
35266
35492
  } catch {
35267
35493
  }
35268
35494
  if (m >= margin) found.push({ f: p, m });
@@ -35277,7 +35503,7 @@ function scanCodexUsage(workdir, sinceMs, sessionsRoot = codexSessionsRoot()) {
35277
35503
  for (const f of listRecentRollouts(sessionsRoot, sinceMs)) {
35278
35504
  let lines;
35279
35505
  try {
35280
- lines = fs8.readFileSync(f, "utf8").split("\n");
35506
+ lines = fs9.readFileSync(f, "utf8").split("\n");
35281
35507
  } catch {
35282
35508
  continue;
35283
35509
  }
@@ -35469,11 +35695,11 @@ var CodexAdapter = class {
35469
35695
  const slug4 = job.artifactId.replace(/[^a-zA-Z0-9_-]+/g, "_").slice(0, 32);
35470
35696
  const id = `codex-${slug4}-${(0, import_node_crypto15.randomUUID)().slice(0, 8)}`;
35471
35697
  const startedAtMs = Date.now();
35472
- 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-"));
35473
35699
  for (const [rel, content] of Object.entries(job.bundle.files)) {
35474
35700
  const file = path7.join(dir, rel);
35475
- fs8.mkdirSync(path7.dirname(file), { recursive: true });
35476
- fs8.writeFileSync(file, content);
35701
+ fs9.mkdirSync(path7.dirname(file), { recursive: true });
35702
+ fs9.writeFileSync(file, content);
35477
35703
  }
35478
35704
  const task = job.bundle.files["TASK.md"];
35479
35705
  if (!task) throw new Error("bundle \u7F3A TASK.md\uFF08\u88C5\u914D\u5668\u5951\u7EA6\uFF09");
@@ -35502,7 +35728,7 @@ var CodexAdapter = class {
35502
35728
  stdio: ["ignore", "pipe", "pipe"]
35503
35729
  });
35504
35730
  let transcriptRef = path7.join(dir, "transcript.txt");
35505
- const out = fs8.createWriteStream(transcriptRef);
35731
+ const out = fs9.createWriteStream(transcriptRef);
35506
35732
  const outputCbs = [];
35507
35733
  const telemetryCbs = [];
35508
35734
  const normalizeState = createCodexNormalizeState();
@@ -35533,13 +35759,13 @@ var CodexAdapter = class {
35533
35759
  if (this.opts.cleanupWorkdir) {
35534
35760
  try {
35535
35761
  const keepDir = path7.join(this.opts.workRoot ?? os3.tmpdir(), "oasis-transcripts");
35536
- fs8.mkdirSync(keepDir, { recursive: true });
35762
+ fs9.mkdirSync(keepDir, { recursive: true });
35537
35763
  const kept = path7.join(keepDir, `${id}.txt`);
35538
35764
  const src = transcriptRef;
35539
35765
  out.end(() => {
35540
35766
  try {
35541
- fs8.renameSync(src, kept);
35542
- fs8.rmSync(dir, { recursive: true, force: true });
35767
+ fs9.renameSync(src, kept);
35768
+ fs9.rmSync(dir, { recursive: true, force: true });
35543
35769
  } catch {
35544
35770
  }
35545
35771
  });
@@ -35592,7 +35818,7 @@ var CodexAdapter = class {
35592
35818
  // ../adapters/src/_core/acp.ts
35593
35819
  var import_node_child_process7 = require("node:child_process");
35594
35820
  var import_node_crypto16 = require("node:crypto");
35595
- var fs9 = __toESM(require("node:fs"), 1);
35821
+ var fs10 = __toESM(require("node:fs"), 1);
35596
35822
  var os4 = __toESM(require("node:os"), 1);
35597
35823
  var path8 = __toESM(require("node:path"), 1);
35598
35824
  var readline3 = __toESM(require("node:readline"), 1);
@@ -35798,11 +36024,11 @@ async function runACPSession(job, cfg) {
35798
36024
  const slug4 = job.artifactId.replace(/[^a-zA-Z0-9_-]+/g, "_").slice(0, 32);
35799
36025
  const binTag = path8.basename(cfg.bin).replace(/[^a-zA-Z0-9]/g, "").slice(0, 12) || "acp";
35800
36026
  const id = `${binTag}-${slug4}-${(0, import_node_crypto16.randomUUID)().slice(0, 8)}`;
35801
- 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-"));
35802
36028
  for (const [rel, content] of Object.entries(job.bundle.files)) {
35803
36029
  const file = path8.join(dir, rel);
35804
- fs9.mkdirSync(path8.dirname(file), { recursive: true });
35805
- fs9.writeFileSync(file, content);
36030
+ fs10.mkdirSync(path8.dirname(file), { recursive: true });
36031
+ fs10.writeFileSync(file, content);
35806
36032
  }
35807
36033
  const task = job.bundle.files["TASK.md"];
35808
36034
  if (!task) throw new Error("bundle \u7F3A TASK.md\uFF08\u88C5\u914D\u5668\u5951\u7EA6\uFF09");
@@ -35820,7 +36046,7 @@ async function runACPSession(job, cfg) {
35820
36046
  stdio: ["pipe", "pipe", "pipe"]
35821
36047
  });
35822
36048
  let transcriptRef = path8.join(dir, "transcript.txt");
35823
- const transcriptOut = fs9.createWriteStream(transcriptRef);
36049
+ const transcriptOut = fs10.createWriteStream(transcriptRef);
35824
36050
  const outputCbs = [];
35825
36051
  const telemetryCbs = [];
35826
36052
  const exitCbs = [];
@@ -35831,13 +36057,13 @@ async function runACPSession(job, cfg) {
35831
36057
  if (cfg.cleanupWorkdir) {
35832
36058
  try {
35833
36059
  const keepDir = path8.join(cfg.workRoot ?? os4.tmpdir(), "oasis-transcripts");
35834
- fs9.mkdirSync(keepDir, { recursive: true });
36060
+ fs10.mkdirSync(keepDir, { recursive: true });
35835
36061
  const kept = path8.join(keepDir, `${id}.txt`);
35836
36062
  const src = transcriptRef;
35837
36063
  transcriptOut.end(() => {
35838
36064
  try {
35839
- fs9.renameSync(src, kept);
35840
- fs9.rmSync(dir, { recursive: true, force: true });
36065
+ fs10.renameSync(src, kept);
36066
+ fs10.rmSync(dir, { recursive: true, force: true });
35841
36067
  } catch {
35842
36068
  }
35843
36069
  });
@@ -36014,18 +36240,18 @@ var KiroAdapter = class {
36014
36240
  // ../adapters/src/_core/subprocess.ts
36015
36241
  var import_node_child_process8 = require("node:child_process");
36016
36242
  var import_node_crypto17 = require("node:crypto");
36017
- var fs10 = __toESM(require("node:fs"), 1);
36243
+ var fs11 = __toESM(require("node:fs"), 1);
36018
36244
  var os5 = __toESM(require("node:os"), 1);
36019
36245
  var path9 = __toESM(require("node:path"), 1);
36020
36246
  var readline4 = __toESM(require("node:readline"), 1);
36021
36247
  function materialize(job, workRoot) {
36022
36248
  const slug4 = job.artifactId.replace(/[^a-zA-Z0-9_-]+/g, "_").slice(0, 32);
36023
36249
  const id = `${slug4}-${(0, import_node_crypto17.randomUUID)().slice(0, 8)}`;
36024
- const dir = fs10.mkdtempSync(path9.join(workRoot ?? os5.tmpdir(), "oasis-session-"));
36250
+ const dir = fs11.mkdtempSync(path9.join(workRoot ?? os5.tmpdir(), "oasis-session-"));
36025
36251
  for (const [rel, content] of Object.entries(job.bundle.files)) {
36026
36252
  const file = path9.join(dir, rel);
36027
- fs10.mkdirSync(path9.dirname(file), { recursive: true });
36028
- fs10.writeFileSync(file, content);
36253
+ fs11.mkdirSync(path9.dirname(file), { recursive: true });
36254
+ fs11.writeFileSync(file, content);
36029
36255
  }
36030
36256
  const task = job.bundle.files["TASK.md"];
36031
36257
  if (!task) throw new Error("bundle \u7F3A TASK.md\uFF08\u88C5\u914D\u5668\u5951\u7EA6\uFF09");
@@ -36048,13 +36274,13 @@ function makeFinish(id, cfg, transcriptRefPtr, transcriptOut, dir, exitCbs) {
36048
36274
  if (cfg.cleanupWorkdir) {
36049
36275
  try {
36050
36276
  const keepDir = path9.join(cfg.workRoot ?? os5.tmpdir(), "oasis-transcripts");
36051
- fs10.mkdirSync(keepDir, { recursive: true });
36277
+ fs11.mkdirSync(keepDir, { recursive: true });
36052
36278
  const kept = path9.join(keepDir, `${id}.txt`);
36053
36279
  const src = transcriptRefPtr.value;
36054
36280
  transcriptOut.end(() => {
36055
36281
  try {
36056
- fs10.renameSync(src, kept);
36057
- fs10.rmSync(dir, { recursive: true, force: true });
36282
+ fs11.renameSync(src, kept);
36283
+ fs11.rmSync(dir, { recursive: true, force: true });
36058
36284
  } catch {
36059
36285
  }
36060
36286
  });
@@ -36077,7 +36303,7 @@ function runStreamJson(job, cfg) {
36077
36303
  stdio: ["ignore", "pipe", "pipe"]
36078
36304
  });
36079
36305
  const transcriptRefPtr = { value: path9.join(dir, "transcript.txt") };
36080
- const transcriptOut = fs10.createWriteStream(transcriptRefPtr.value);
36306
+ const transcriptOut = fs11.createWriteStream(transcriptRefPtr.value);
36081
36307
  const outputCbs = [];
36082
36308
  const telemetryCbs = [];
36083
36309
  const exitCbs = [];
@@ -36135,7 +36361,7 @@ function runOneShotText(job, cfg) {
36135
36361
  stdio: ["ignore", "pipe", "pipe"]
36136
36362
  });
36137
36363
  const transcriptRefPtr = { value: path9.join(dir, "transcript.txt") };
36138
- const transcriptOut = fs10.createWriteStream(transcriptRefPtr.value);
36364
+ const transcriptOut = fs11.createWriteStream(transcriptRefPtr.value);
36139
36365
  const outputCbs = [];
36140
36366
  const exitCbs = [];
36141
36367
  const { finish, exited } = makeFinish(id, cfg, transcriptRefPtr, transcriptOut, dir, exitCbs);
@@ -38640,16 +38866,16 @@ var rowToMessage = (row) => ({
38640
38866
  init_trajectory();
38641
38867
  var oasisWrapperCache;
38642
38868
  function oasisWrapperScript() {
38643
- if (oasisWrapperCache && fs12.existsSync(oasisWrapperCache)) return oasisWrapperCache;
38869
+ if (oasisWrapperCache && fs13.existsSync(oasisWrapperCache)) return oasisWrapperCache;
38644
38870
  const repoRoot = (0, import_node_url3.fileURLToPath)(new URL("../../../", __esm_import_meta_url));
38645
38871
  const tsx = path11.join(repoRoot, "node_modules/.bin/tsx");
38646
38872
  const main = path11.join(repoRoot, "packages/cli/src/main.ts");
38647
- const dir = fs12.mkdtempSync(path11.join(os6.tmpdir(), "oasis-cli-"));
38873
+ const dir = fs13.mkdtempSync(path11.join(os6.tmpdir(), "oasis-cli-"));
38648
38874
  const wrapper = path11.join(dir, "oasis");
38649
- fs12.writeFileSync(wrapper, `#!/usr/bin/env bash
38875
+ fs13.writeFileSync(wrapper, `#!/usr/bin/env bash
38650
38876
  exec ${JSON.stringify(tsx)} ${JSON.stringify(main)} "$@"
38651
38877
  `);
38652
- fs12.chmodSync(wrapper, 493);
38878
+ fs13.chmodSync(wrapper, 493);
38653
38879
  oasisWrapperCache = wrapper;
38654
38880
  return wrapper;
38655
38881
  }
@@ -38710,10 +38936,10 @@ function traceRuntimeKind(runtimeKind) {
38710
38936
  return "custom";
38711
38937
  }
38712
38938
  async function startServe(opts) {
38713
- fs12.mkdirSync(opts.dir, { recursive: true });
38939
+ fs13.mkdirSync(opts.dir, { recursive: true });
38714
38940
  const lock = path11.join(opts.dir, "serve.lock");
38715
- if (fs12.existsSync(lock)) {
38716
- const pid = Number(fs12.readFileSync(lock, "utf8"));
38941
+ if (fs13.existsSync(lock)) {
38942
+ const pid = Number(fs13.readFileSync(lock, "utf8"));
38717
38943
  let alive = false;
38718
38944
  try {
38719
38945
  process.kill(pid, 0);
@@ -38721,12 +38947,12 @@ async function startServe(opts) {
38721
38947
  } catch {
38722
38948
  }
38723
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`);
38724
- fs12.unlinkSync(lock);
38950
+ fs13.unlinkSync(lock);
38725
38951
  }
38726
- fs12.writeFileSync(lock, String(process.pid));
38952
+ fs13.writeFileSync(lock, String(process.pid));
38727
38953
  const loadJson = (name) => {
38728
38954
  const file = path11.join(opts.dir, name);
38729
- 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;
38730
38956
  };
38731
38957
  const oplog = await NdjsonOplogStore.open(path11.join(opts.dir, "oplog.ndjson"));
38732
38958
  const blobs = new DirBlobStore(path11.join(opts.dir, "blobs"));
@@ -38763,12 +38989,13 @@ async function startServe(opts) {
38763
38989
  const issuer = createTokenIssuer();
38764
38990
  const operatorActor = `actor:human:${os6.userInfo().username}`;
38765
38991
  const operatorToken = issuer.issue(operatorActor);
38766
- fs12.writeFileSync(path11.join(opts.dir, "operator-token"), operatorToken, { mode: 384 });
38992
+ fs13.writeFileSync(path11.join(opts.dir, "operator-token"), operatorToken, { mode: 384 });
38767
38993
  const nodeTokens = createNodeTokenStore(path11.join(opts.dir, "node-tokens.json"));
38994
+ const nodeStore = await FileNodeStore.open(path11.join(opts.dir, "nodes.json"));
38768
38995
  const registryAuditFile = path11.join(opts.dir, "registry-audit.ndjson");
38769
38996
  const registryListeners = /* @__PURE__ */ new Set();
38770
38997
  const registryAudit = (record3) => {
38771
- fs12.appendFile(registryAuditFile, JSON.stringify(record3) + "\n", () => {
38998
+ fs13.appendFile(registryAuditFile, JSON.stringify(record3) + "\n", () => {
38772
38999
  });
38773
39000
  for (const l of registryListeners) l({ kind: record3.kind, actor: record3.actor, target: record3.target, timestamp: record3.timestamp });
38774
39001
  };
@@ -38800,6 +39027,7 @@ async function startServe(opts) {
38800
39027
  } else {
38801
39028
  projectStateStore = await FileProjectStateStore.open(path11.join(opts.dir, "artifact-document-projects.json"));
38802
39029
  artifactStateStore = await FileArtifactStateStore.open(path11.join(opts.dir, "artifact-document-state.json"));
39030
+ chatSessionStore = new MemoryChatSessionStore();
38803
39031
  console.log("[serve] \u4EA4\u4ED8\u7269\u72B6\u6001\u4F53\u7CFB\uFF1A\u672C\u5730 JSON dev store");
38804
39032
  }
38805
39033
  const outlineUrl = process.env["OUTLINE_URL"];
@@ -38903,9 +39131,9 @@ async function startServe(opts) {
38903
39131
  const interventionDrafts = new InterventionDraftStore();
38904
39132
  const journalFile = path11.join(opts.dir, "dispatch-journal.ndjson");
38905
39133
  const journalRing = [];
38906
- if (fs12.existsSync(journalFile)) {
39134
+ if (fs13.existsSync(journalFile)) {
38907
39135
  try {
38908
- const lines = fs12.readFileSync(journalFile, "utf8").split(/\r?\n/).filter(Boolean);
39136
+ const lines = fs13.readFileSync(journalFile, "utf8").split(/\r?\n/).filter(Boolean);
38909
39137
  for (const line of lines.slice(-500)) {
38910
39138
  try {
38911
39139
  journalRing.push(JSON.parse(line));
@@ -38918,7 +39146,7 @@ async function startServe(opts) {
38918
39146
  const journal = (entry) => {
38919
39147
  journalRing.push(entry);
38920
39148
  if (journalRing.length > 500) journalRing.shift();
38921
- fs12.appendFile(journalFile, JSON.stringify(entry) + "\n", () => {
39149
+ fs13.appendFile(journalFile, JSON.stringify(entry) + "\n", () => {
38922
39150
  });
38923
39151
  };
38924
39152
  let dispatcher;
@@ -39010,6 +39238,7 @@ async function startServe(opts) {
39010
39238
  nodesDomain({
39011
39239
  getHub: () => hub,
39012
39240
  nodeTokens,
39241
+ nodeStore,
39013
39242
  gatewayUrl: opts.gatewayUrl ?? `ws://127.0.0.1:${opts.port ?? 7320}/node-gateway`,
39014
39243
  repoRemote: opts.nodeRepoRemote ?? "git@github.com:open-friday/oasis-core.git"
39015
39244
  }),
@@ -39023,7 +39252,7 @@ async function startServe(opts) {
39023
39252
  resolveEngine: (cid) => engineRouter.getEngine(cid ?? DEFAULT_COMPANY_ID)
39024
39253
  }),
39025
39254
  trace.register,
39026
- ...chatSessionStore ? [createChatSessionsDomain({ store: chatSessionStore, registry: registryStore })] : []
39255
+ createChatSessionsDomain({ store: chatSessionStore, registry: registryStore })
39027
39256
  ],
39028
39257
  drafts: interventionDrafts,
39029
39258
  // 暴露注册的 artifact 类型,给前端「发起工单」动态拉取根产物可选类型——schema.json 增改即自动扩展,
@@ -39032,7 +39261,8 @@ async function startServe(opts) {
39032
39261
  "artifact-types": () => schema.map((d) => ({ name: d.name, ownerRole: d.ownerRole, contentType: d.contentType })),
39033
39262
  dispatches: () => ({
39034
39263
  recent: journalRing.slice(-100),
39035
- fused: dispatcher?.fusedJobs() ?? []
39264
+ fused: dispatcher?.fusedJobs() ?? [],
39265
+ queued: dispatcher?.queuedCount() ?? 0
39036
39266
  })
39037
39267
  },
39038
39268
  resolveActorEnv: async (actorId) => (await buildActorProvision(actors.service, actorId)).env,
@@ -39275,7 +39505,10 @@ async function startServe(opts) {
39275
39505
  journal,
39276
39506
  // TrajectoryEvent 是唯一事件模型;trace 旁账与 M7 蒸馏库并行消费同一流(§16.D 收口)。
39277
39507
  trajectory: combineTrajectorySinks([trajectorySink, traceStoreSink]),
39278
- ...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
39279
39512
  });
39280
39513
  const d = dispatcher;
39281
39514
  let logged = 0;
@@ -39437,7 +39670,7 @@ async function startServe(opts) {
39437
39670
  await server.close();
39438
39671
  if (pgPool) await pgPool.end().catch(() => {
39439
39672
  });
39440
- fs12.rmSync(lock, { force: true });
39673
+ fs13.rmSync(lock, { force: true });
39441
39674
  }
39442
39675
  };
39443
39676
  }
@@ -39701,7 +39934,7 @@ async function startNode(opts) {
39701
39934
  init_src4();
39702
39935
  var USAGE = `oasis \u2014\u2014 artifact-centric \u534F\u4F5C\u5185\u6838 CLI\uFF08\u670D\u52A1\u7AEF\u7626\u5BA2\u6237\u7AEF\uFF09
39703
39936
 
39704
- 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]
39705
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
39706
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
39707
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
@@ -39860,19 +40093,19 @@ async function runCli(argv, println = console.log) {
39860
40093
  const serverUrl = flags.get("server-ws") ?? process.env["OASIS_SERVER_WS"] ?? "ws://127.0.0.1:7320/node-gateway";
39861
40094
  const nodeName = flags.get("name") ?? process.env["OASIS_NODE_NAME"];
39862
40095
  const nodeIdFile = path12.join(dir, "node-id");
39863
- const storedNodeId = fs13.existsSync(nodeIdFile) ? fs13.readFileSync(nodeIdFile, "utf8").trim() : null;
40096
+ const storedNodeId = fs14.existsSync(nodeIdFile) ? fs14.readFileSync(nodeIdFile, "utf8").trim() : null;
39864
40097
  const nodeId = storedNodeId ?? flags.get("id") ?? process.env["OASIS_NODE_ID"] ?? `node-${(0, import_node_crypto20.randomUUID)()}`;
39865
40098
  if (!storedNodeId) {
39866
- fs13.mkdirSync(dir, { recursive: true });
39867
- fs13.writeFileSync(nodeIdFile, nodeId);
40099
+ fs14.mkdirSync(dir, { recursive: true });
40100
+ fs14.writeFileSync(nodeIdFile, nodeId);
39868
40101
  }
39869
40102
  const nodeTokenFile = path12.join(dir, "node-token");
39870
- 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);
39871
40104
  if (flags.has("service")) {
39872
40105
  const { spawnSync, execSync: execSync2 } = await import("node:child_process");
39873
40106
  if (token2) {
39874
- fs13.mkdirSync(dir, { recursive: true });
39875
- fs13.writeFileSync(nodeTokenFile, token2, { mode: 384 });
40107
+ fs14.mkdirSync(dir, { recursive: true });
40108
+ fs14.writeFileSync(nodeTokenFile, token2, { mode: 384 });
39876
40109
  }
39877
40110
  println("\u2192 Installing oasis_test globally...");
39878
40111
  spawnSync("npm", ["install", "-g", "oasis_test@latest"], { stdio: "inherit" });
@@ -39881,10 +40114,10 @@ async function runCli(argv, println = console.log) {
39881
40114
  const svcArgs = `node --server-ws ${serverUrl} --id ${nodeId} --dir ${dir}${nodeName ? ` --name ${nodeName}` : ""}`;
39882
40115
  if (process.platform === "linux") {
39883
40116
  const svcFile = path12.join(os7.homedir(), ".config/systemd/user/oasis-node.service");
39884
- fs13.mkdirSync(path12.dirname(svcFile), { recursive: true });
40117
+ fs14.mkdirSync(path12.dirname(svcFile), { recursive: true });
39885
40118
  if (spawnSync("systemctl", ["--user", "is-active", "--quiet", "oasis-node"], {}).status === 0)
39886
40119
  spawnSync("systemctl", ["--user", "stop", "oasis-node"], { stdio: "inherit" });
39887
- fs13.writeFileSync(svcFile, [
40120
+ fs14.writeFileSync(svcFile, [
39888
40121
  "[Unit]",
39889
40122
  "Description=Oasis Node Daemon",
39890
40123
  "After=network.target",
@@ -39909,10 +40142,10 @@ async function runCli(argv, println = console.log) {
39909
40142
  println(" Stop : systemctl --user stop oasis-node");
39910
40143
  } else if (process.platform === "darwin") {
39911
40144
  const plist = path12.join(os7.homedir(), "Library/LaunchAgents/com.oasis.node.plist");
39912
- fs13.mkdirSync(path12.dirname(plist), { recursive: true });
40145
+ fs14.mkdirSync(path12.dirname(plist), { recursive: true });
39913
40146
  spawnSync("launchctl", ["unload", plist], { stdio: "ignore" });
39914
40147
  const argXml = [binPath, "node", "--server-ws", serverUrl, "--id", nodeId, "--dir", dir, ...nodeName ? ["--name", nodeName] : []].map((a) => `<string>${a}</string>`).join("");
39915
- 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>`);
39916
40149
  spawnSync("launchctl", ["load", plist], { stdio: "inherit" });
39917
40150
  println("\u2713 Oasis node service started (macOS LaunchAgent com.oasis.node)");
39918
40151
  println(` Logs : tail -f ${os7.homedir()}/.oasis-node.log`);
@@ -39972,7 +40205,7 @@ async function runCli(argv, println = console.log) {
39972
40205
  const outFile = flags.get("out");
39973
40206
  const lines = samples.map((sample) => JSON.stringify(sample)).join("\n");
39974
40207
  if (outFile !== void 0) {
39975
- fs13.writeFileSync(outFile, lines + (lines ? "\n" : ""));
40208
+ fs14.writeFileSync(outFile, lines + (lines ? "\n" : ""));
39976
40209
  println(`\u5DF2\u5BFC\u51FA ${samples.length} \u6761\u8F68\u8FF9\u6837\u672C \u2192 ${outFile}\uFF08approved/rejected \u6210\u5BF9\u5373\u504F\u597D\u5BF9\uFF09`);
39977
40210
  } else {
39978
40211
  println(lines);
@@ -40000,9 +40233,9 @@ async function runCli(argv, println = console.log) {
40000
40233
  `WantedBy=default.target`,
40001
40234
  ``
40002
40235
  ].join("\n");
40003
- fs13.mkdirSync(unitDir, { recursive: true });
40236
+ fs14.mkdirSync(unitDir, { recursive: true });
40004
40237
  const unitPath = path12.join(unitDir, `${name}.service`);
40005
- fs13.writeFileSync(unitPath, unit);
40238
+ fs14.writeFileSync(unitPath, unit);
40006
40239
  println(`\u5DF2\u5199\u5165 ${unitPath}`);
40007
40240
  println(`\u542F\u7528\uFF1Asystemctl --user daemon-reload && systemctl --user enable --now ${name}`);
40008
40241
  println(`\u5F00\u673A\u81EA\u542F\uFF08\u542B\u672A\u767B\u5F55\uFF09\uFF1Aloginctl enable-linger $USER`);
@@ -40015,6 +40248,8 @@ async function runCli(argv, println = console.log) {
40015
40248
  dir,
40016
40249
  ...port !== void 0 ? { port } : {},
40017
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")) } : {},
40018
40253
  ...flags.has("model") ? { model: flags.get("model") } : {},
40019
40254
  allowSimpleTokens: flags.get("allow-simple-tokens") === "true",
40020
40255
  ...flags.get("gateway-url") ?? process.env["OASIS_GATEWAY_URL"] ? { gatewayUrl: flags.get("gateway-url") ?? process.env["OASIS_GATEWAY_URL"] } : {}
@@ -40029,12 +40264,12 @@ async function runCli(argv, println = console.log) {
40029
40264
  }
40030
40265
  const base = flags.get("server") ?? process.env["OASIS_SERVER"] ?? "http://127.0.0.1:7320";
40031
40266
  const tokenFile = path12.join(dir, "operator-token");
40032
- 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);
40033
40268
  if (!token) {
40034
40269
  throw new Error(`\u65E0 token\uFF1A\u5148 oasis serve\uFF08\u4F1A\u751F\u6210 ${tokenFile}\uFF09\uFF0C\u6216\u663E\u5F0F --token / $OASIS_TOKEN`);
40035
40270
  }
40036
40271
  const api = makeClient(base, token);
40037
- const readFileArg = (file) => fs13.readFileSync(file, "utf8");
40272
+ const readFileArg = (file) => fs14.readFileSync(file, "utf8");
40038
40273
  const parseJsonFlag = (value, name) => {
40039
40274
  const parsed = JSON.parse(value);
40040
40275
  if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
@@ -40052,11 +40287,11 @@ async function runCli(argv, println = console.log) {
40052
40287
  const readDirFiles = (root) => {
40053
40288
  const out = [];
40054
40289
  const walk = (abs, rel) => {
40055
- for (const name of fs13.readdirSync(abs)) {
40290
+ for (const name of fs14.readdirSync(abs)) {
40056
40291
  const childAbs = path12.join(abs, name);
40057
40292
  const childRel = rel ? `${rel}/${name}` : name;
40058
- if (fs13.statSync(childAbs).isDirectory()) walk(childAbs, childRel);
40059
- 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") });
40060
40295
  }
40061
40296
  };
40062
40297
  walk(root, "");
@@ -40740,18 +40975,18 @@ var parseFlags = (argv) => {
40740
40975
  };
40741
40976
  var readConfig = () => {
40742
40977
  try {
40743
- return JSON.parse(fs14.readFileSync(CONFIG_FILE, "utf8"));
40978
+ return JSON.parse(fs15.readFileSync(CONFIG_FILE, "utf8"));
40744
40979
  } catch {
40745
40980
  return null;
40746
40981
  }
40747
40982
  };
40748
40983
  var saveConfig = (c) => {
40749
- fs14.mkdirSync(OASIS_DIR, { recursive: true });
40750
- 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 });
40751
40986
  };
40752
40987
  var readPid = () => {
40753
40988
  try {
40754
- const p = parseInt(fs14.readFileSync(PID_FILE, "utf8").trim(), 10);
40989
+ const p = parseInt(fs15.readFileSync(PID_FILE, "utf8").trim(), 10);
40755
40990
  return isNaN(p) ? null : p;
40756
40991
  } catch {
40757
40992
  return null;
@@ -40765,11 +41000,12 @@ var isRunning = (pid) => {
40765
41000
  return false;
40766
41001
  }
40767
41002
  };
41003
+ var httpBase = (wsUrl) => wsUrl.replace(/^wss?:\/\//, "http://").replace(/\/node-gateway.*$/, "");
40768
41004
  function installBinary() {
40769
- fs14.mkdirSync(path13.dirname(BIN_FILE), { recursive: true });
40770
- fs14.copyFileSync(process.argv[1], BIN_FILE);
40771
- fs14.mkdirSync(path13.dirname(LOCAL_BIN), { recursive: true });
40772
- 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
40773
41009
  exec node "${BIN_FILE}" "$@"
40774
41010
  `, { mode: 493 });
40775
41011
  }
@@ -40777,8 +41013,8 @@ function setupAutostart() {
40777
41013
  const cmd = `${process.execPath} "${BIN_FILE}" --_daemon`;
40778
41014
  if (process.platform === "linux") {
40779
41015
  const unitDir = path13.join(os8.homedir(), ".config", "systemd", "user");
40780
- fs14.mkdirSync(unitDir, { recursive: true });
40781
- fs14.writeFileSync(path13.join(unitDir, "oasis-node.service"), [
41016
+ fs15.mkdirSync(unitDir, { recursive: true });
41017
+ fs15.writeFileSync(path13.join(unitDir, "oasis-node.service"), [
40782
41018
  "[Unit]",
40783
41019
  "Description=Oasis Node Daemon",
40784
41020
  "After=network.target",
@@ -40800,8 +41036,8 @@ function setupAutostart() {
40800
41036
  }
40801
41037
  } else if (process.platform === "darwin") {
40802
41038
  const plist = path13.join(os8.homedir(), "Library", "LaunchAgents", "com.oasis.node.plist");
40803
- fs14.mkdirSync(path13.dirname(plist), { recursive: true });
40804
- 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"?>
40805
41041
  <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
40806
41042
  <plist version="1.0"><dict>
40807
41043
  <key>Label</key><string>com.oasis.node</string>
@@ -40817,34 +41053,36 @@ function setupAutostart() {
40817
41053
  }
40818
41054
  }
40819
41055
  function spawnDaemon() {
40820
- const log3 = fs14.openSync(LOG_FILE, "a");
41056
+ const log3 = fs15.openSync(LOG_FILE, "a");
40821
41057
  const child = (0, import_node_child_process10.spawn)(process.execPath, [BIN_FILE, "--_daemon"], {
40822
41058
  detached: true,
40823
41059
  stdio: ["ignore", log3, log3]
40824
41060
  });
40825
41061
  child.unref();
40826
41062
  const pid = child.pid ?? 0;
40827
- fs14.writeFileSync(PID_FILE, String(pid));
41063
+ fs15.writeFileSync(PID_FILE, String(pid));
40828
41064
  return pid;
40829
41065
  }
40830
41066
  function stopDaemon() {
40831
41067
  const pid = readPid();
40832
- if (!pid || !isRunning(pid)) {
41068
+ if (pid && isRunning(pid)) {
40833
41069
  try {
40834
- fs14.unlinkSync(PID_FILE);
41070
+ process.kill(pid, "SIGTERM");
40835
41071
  } catch {
40836
41072
  }
40837
- return false;
40838
41073
  }
40839
41074
  try {
40840
- process.kill(pid, "SIGTERM");
41075
+ fs15.unlinkSync(PID_FILE);
40841
41076
  } catch {
40842
41077
  }
40843
41078
  try {
40844
- fs14.unlinkSync(PID_FILE);
41079
+ (0, import_node_child_process10.execSync)('pkill -f "oasis.js --_daemon"', { stdio: "ignore" });
41080
+ } catch {
41081
+ }
41082
+ try {
41083
+ (0, import_node_child_process10.execSync)("systemctl --user stop oasis-node 2>/dev/null", { stdio: "ignore" });
40845
41084
  } catch {
40846
41085
  }
40847
- return true;
40848
41086
  }
40849
41087
  void (async () => {
40850
41088
  const [, , cmd] = process.argv;
@@ -40855,10 +41093,10 @@ void (async () => {
40855
41093
  console.error("[oasis] no config \u2014 run `oasis start` first");
40856
41094
  process.exit(1);
40857
41095
  }
40858
- fs14.writeFileSync(PID_FILE, String(process.pid));
41096
+ fs15.writeFileSync(PID_FILE, String(process.pid));
40859
41097
  const cleanup = () => {
40860
41098
  try {
40861
- fs14.unlinkSync(PID_FILE);
41099
+ fs15.unlinkSync(PID_FILE);
40862
41100
  } catch {
40863
41101
  }
40864
41102
  };
@@ -40871,35 +41109,49 @@ void (async () => {
40871
41109
  cleanup();
40872
41110
  process.exit(0);
40873
41111
  });
41112
+ if (cfg.nodeId && cfg.name) {
41113
+ const base = httpBase(cfg.serverUrl);
41114
+ fetch(`${base}/api/nodes/${cfg.nodeId}`, {
41115
+ method: "PATCH",
41116
+ headers: { authorization: `Bearer ${cfg.token}`, "content-type": "application/json" },
41117
+ body: JSON.stringify({ name: cfg.name })
41118
+ }).catch(() => {
41119
+ });
41120
+ }
40874
41121
  await startNode({
40875
41122
  serverUrl: cfg.serverUrl,
40876
41123
  token: cfg.token,
40877
41124
  nodeId: cfg.nodeId ?? `node-${(0, import_node_crypto21.randomUUID)()}`,
40878
- ...cfg.name ? { nodeName: cfg.name } : {}
41125
+ nodeName: cfg.name ?? cfg.nodeId ?? `node-${(0, import_node_crypto21.randomUUID)()}`
40879
41126
  });
40880
41127
  return;
40881
41128
  }
40882
41129
  if (cmd === "start") {
40883
- const serverUrl = flags.get("server-ws") ?? process.env["OASIS_SERVER_WS"] ?? readConfig()?.serverUrl;
40884
- const token = flags.get("token") ?? process.env["OASIS_NODE_TOKEN"] ?? readConfig()?.token;
41130
+ const prev = readConfig();
41131
+ const serverUrl = flags.get("server-ws") ?? process.env["OASIS_SERVER_WS"] ?? prev?.serverUrl;
41132
+ const token = flags.get("token") ?? process.env["OASIS_NODE_TOKEN"] ?? prev?.token;
40885
41133
  if (!serverUrl || !token) {
40886
- console.error("Usage: oasis start --server-ws <wss://...> --token <token> [--name <name>]");
41134
+ console.error("Usage: oasis start --server-ws <wss://...> --token <token> [--name <name>] [--id <nodeId>]");
40887
41135
  process.exit(1);
40888
41136
  }
40889
- const nodeId = flags.get("id") ?? readConfig()?.nodeId ?? `node-${(0, import_node_crypto21.randomUUID)()}`;
40890
- saveConfig({ serverUrl, token, name: flags.get("name") ?? readConfig()?.name, nodeId });
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)()}`;
41139
+ const name = flags.get("name") ?? prev?.name;
41140
+ const nameChanged = prev?.name !== void 0 && name !== prev.name;
41141
+ saveConfig({ serverUrl, token, nodeId, ...name ? { name } : {} });
40891
41142
  installBinary();
40892
41143
  stopDaemon();
40893
41144
  setupAutostart();
40894
41145
  const pid = spawnDaemon();
40895
- console.log(`\u2713 daemon started (PID ${pid})
40896
- logs: tail -f ${LOG_FILE}`);
41146
+ console.log(`\u2713 daemon started (PID ${pid})${nameChanged ? ` [name updated: ${prev.name} \u2192 ${name}]` : ""}`);
41147
+ console.log(` logs: tail -f ${LOG_FILE}`);
40897
41148
  if (!process.env["PATH"]?.includes(path13.dirname(LOCAL_BIN)))
40898
41149
  console.log(` add to PATH: echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc && source ~/.bashrc`);
40899
41150
  return;
40900
41151
  }
40901
41152
  if (cmd === "stop") {
40902
- console.log(stopDaemon() ? "\u2713 stopped" : "not running");
41153
+ stopDaemon();
41154
+ console.log("\u2713 stopped");
40903
41155
  return;
40904
41156
  }
40905
41157
  if (cmd === "restart") {
@@ -40914,7 +41166,12 @@ void (async () => {
40914
41166
  }
40915
41167
  if (cmd === "status") {
40916
41168
  const pid = readPid();
40917
- console.log(pid && isRunning(pid) ? `running (PID ${pid})` : "not running");
41169
+ const cfg = readConfig();
41170
+ if (pid && isRunning(pid)) {
41171
+ console.log(`running PID=${pid} nodeId=${cfg?.nodeId ?? "?"} name=${cfg?.name ?? "(none)"}`);
41172
+ } else {
41173
+ console.log("not running");
41174
+ }
40918
41175
  return;
40919
41176
  }
40920
41177
  await runCli(process.argv.slice(2));