oasis_test_v2 2.2.13 → 2.2.14

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 +93 -17
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -203682,6 +203682,14 @@ async function runView(kernel, oplog, engineStore, blobs, name, artifactId, para
203682
203682
  throw new Error(`\u672A\u77E5\u89C6\u56FE: ${name}`);
203683
203683
  }
203684
203684
  }
203685
+ function resolveListenTarget(env, pid, port) {
203686
+ const count2 = Number(env["LISTEN_FDS"] ?? "0");
203687
+ const listenPid = env["LISTEN_PID"];
203688
+ if (Number.isFinite(count2) && count2 >= 1 && (!listenPid || listenPid === String(pid))) {
203689
+ return { fd: 3 };
203690
+ }
203691
+ return { port: port ?? 0, host: "0.0.0.0" };
203692
+ }
203685
203693
  async function startOasisServer(opts) {
203686
203694
  const resolveActor = opts.resolveActor ?? defaultResolveActor;
203687
203695
  const defaultDraftPlannerIssuesStore = new MemoryWorkorderDraftPlannerIssuesStore();
@@ -207700,7 +207708,7 @@ ${composed}`;
207700
207708
  });
207701
207709
  httpServer.keepAliveTimeout = 65e3;
207702
207710
  httpServer.headersTimeout = 66e3;
207703
- await new Promise((resolve10) => httpServer.listen(opts.port ?? 0, "0.0.0.0", resolve10));
207711
+ await new Promise((resolve10) => httpServer.listen(resolveListenTarget(process.env, process.pid, opts.port), resolve10));
207704
207712
  const address = httpServer.address();
207705
207713
  if (address === null || typeof address === "string") throw new Error("listen failed");
207706
207714
  const baseUrl = `http://127.0.0.1:${address.port}`;
@@ -207719,14 +207727,41 @@ ${composed}`;
207719
207727
  * 委派域没装配时返回 null,调用方照常。
207720
207728
  */
207721
207729
  settleDelegationFromSweep: (childSessionId, reason, companyId, opts2) => delegationService?.settleFromSweep(childSessionId, reason, companyId, opts2) ?? Promise.resolve(null),
207722
- close: async () => {
207730
+ /**
207731
+ * @param opts.drainMs **优雅停机预算**(毫秒)。缺省 0 = 老行为:立刻掐断所有连接后关闭。
207732
+ *
207733
+ * 给了预算就改成「排空」:先 `close()` 停收新连接、`closeIdleConnections()` 放掉空闲的
207734
+ * keep-alive,**在途请求让它跑完**;预算到点还没走完的(SSE 这类长连接本来就不会自己结束)
207735
+ * 再 `closeAllConnections()` 强断。前端的流有重连(`hydrateAndAttach`),被强断能自愈;
207736
+ * 普通请求跑到一半被掐则是实打实的失败,所以要给它们这几百毫秒。
207737
+ *
207738
+ * 生产上这条由 `oasis serve` 的 SIGTERM 处理器传(见 cli.ts)——现在 systemd 是硬杀
207739
+ * (journal: `code=exited, status=143`),在途请求当场断。
207740
+ */
207741
+ close: async (opts2) => {
207723
207742
  if (broadcastTimer) clearInterval(broadcastTimer);
207724
207743
  if (pendingSystemNoticesRecoveryTimer) clearInterval(pendingSystemNoticesRecoveryTimer);
207725
207744
  await knowledgeDomain?.close();
207726
207745
  channelWsSupervisor?.stop();
207746
+ const drainMs = opts2?.drainMs ?? 0;
207727
207747
  await new Promise((resolve10, reject) => {
207728
- httpServer.closeAllConnections();
207729
- httpServer.close((err) => err ? reject(err) : resolve10());
207748
+ let sweep;
207749
+ let force;
207750
+ httpServer.close((err) => {
207751
+ if (sweep) clearInterval(sweep);
207752
+ if (force) clearTimeout(force);
207753
+ if (err) reject(err);
207754
+ else resolve10();
207755
+ });
207756
+ if (drainMs > 0) {
207757
+ httpServer.closeIdleConnections();
207758
+ sweep = setInterval(() => httpServer.closeIdleConnections(), 100);
207759
+ sweep.unref?.();
207760
+ force = setTimeout(() => httpServer.closeAllConnections(), drainMs);
207761
+ force.unref?.();
207762
+ } else {
207763
+ httpServer.closeAllConnections();
207764
+ }
207730
207765
  });
207731
207766
  }
207732
207767
  };
@@ -210837,6 +210872,18 @@ var init_projection = __esm({
210837
210872
  function isUncategorizedProjectId(projectId2) {
210838
210873
  return projectId2 === UNCATEGORIZED_PROJECT_ID;
210839
210874
  }
210875
+ function latestUncategorizedActivityAt(candidates) {
210876
+ let bestIso = UNCATEGORIZED_UNKNOWN_TIME;
210877
+ let bestMs = Date.parse(UNCATEGORIZED_UNKNOWN_TIME);
210878
+ for (const iso6 of candidates) {
210879
+ if (!iso6) continue;
210880
+ const ms = Date.parse(iso6);
210881
+ if (Number.isNaN(ms) || ms <= bestMs) continue;
210882
+ bestMs = ms;
210883
+ bestIso = iso6;
210884
+ }
210885
+ return bestIso;
210886
+ }
210840
210887
  async function bindWorkorderProject(deps) {
210841
210888
  const { workOrderId, projectId: projectId2 } = deps;
210842
210889
  if (!projectId2) return null;
@@ -211472,6 +211519,10 @@ var init_service4 = __esm({
211472
211519
  async buildUncategorizedProject() {
211473
211520
  const workorders = await this.listProjectWorkorders(UNCATEGORIZED_PROJECT_ID);
211474
211521
  const artifactList = await this.aggregateUncategorizedArtifacts();
211522
+ const updatedAt = latestUncategorizedActivityAt([
211523
+ ...workorders.map((workorder) => workorder.updatedAt),
211524
+ ...artifactList.artifacts.map((artifact) => artifact.updatedAt)
211525
+ ]);
211475
211526
  return {
211476
211527
  id: UNCATEGORIZED_PROJECT_ID,
211477
211528
  name: UNCATEGORIZED_PROJECT_NAME,
@@ -211484,7 +211535,7 @@ var init_service4 = __esm({
211484
211535
  // 出 null 保持「取不到就 null」的域内红线,也不把一个不存在的 actor id 塞进契约。
211485
211536
  createdBy: null,
211486
211537
  createdAt: UNCATEGORIZED_UNKNOWN_TIME,
211487
- updatedAt: UNCATEGORIZED_UNKNOWN_TIME
211538
+ updatedAt
211488
211539
  };
211489
211540
  }
211490
211541
  async aggregateUncategorizedArtifacts() {
@@ -223362,8 +223413,12 @@ function toApiProject(project) {
223362
223413
  // - 取不到创建人 → `null`(老的手建项目 / 无分类虚拟卡),前端显示「—」;
223363
223414
  // - 有 id 但名册解析不到 → 对象在、`name`/`avatar` 为 `null`(**不回落 id 尾段**)。
223364
223415
  created_by: project.createdBy ? toApiProjectActorRef(project.createdBy) : null,
223365
- // 空桶哨兵(`UNCATEGORIZED_UNKNOWN_TIME`)→ `null`:无分类项目桶为空时**无可派生时间**,
223366
- // 前端见 null 显示「—」而不是一串远古时间;真项目/非空桶不受影响。
223416
+ // 「时间不可知」哨兵(`UNCATEGORIZED_UNKNOWN_TIME`)→ `null`,前端见 null 显示「—」而不是
223417
+ // 一串远古时间。**只有无分类虚拟卡会命中**,真项目不受影响(建项目时两个字段都写真时间戳)。
223418
+ // 命中的两种情形(2026-09-10 起):
223419
+ // · `created_at`:恒命中——桶不存在「被创建」这回事;
223420
+ // · `updated_at`:只在桶里空无一物时命中;桶里有工单/产物时是真时间(见 service.ts
223421
+ // `buildUncategorizedProject` 与 `latestUncategorizedActivityAt`)。
223367
223422
  created_at: project.createdAt === UNCATEGORIZED_UNKNOWN_TIME ? null : project.createdAt,
223368
223423
  updated_at: project.updatedAt === UNCATEGORIZED_UNKNOWN_TIME ? null : project.updatedAt
223369
223424
  };
@@ -234856,6 +234911,12 @@ function payloadStr(payload, key) {
234856
234911
  }
234857
234912
  return "";
234858
234913
  }
234914
+ function workorderGoal(title, prefix, suffix) {
234915
+ const subject = Array.from(title.replace(/\s+/g, " ").trim());
234916
+ const budget = 50 - Array.from(prefix + suffix).length;
234917
+ const shortTitle = subject.length > budget ? `${subject.slice(0, budget - 1).join("")}\u2026` : subject.join("");
234918
+ return `${prefix}${shortTitle}${suffix}`;
234919
+ }
234859
234920
  function buildPlan(structure, title, ctx, opts = {}) {
234860
234921
  const nodeOwners = payloadNodeOwners(ctx.payload);
234861
234922
  const nodeReviewers = payloadNodeReviewers(ctx.payload);
@@ -234915,9 +234976,7 @@ function bugFixResolve(ctx) {
234915
234976
  artifactType: "diagnosis",
234916
234977
  title,
234917
234978
  brief,
234918
- // goal 用标题兜底:弹窗不再单列「工单目标」(实测无任何消费方读它、让用户填两遍纯属白填),
234919
- // 但字段本身在内核里有设计意图(workorderSpec 读它),故填上而非留空。
234920
- goal: title,
234979
+ goal: workorderGoal(title, "\u4FEE\u590D\u300C", "\u300D\uFF0C\u9A8C\u8BC1\u6240\u62A5\u73B0\u8C61\u4E0D\u518D\u51FA\u73B0\u3002"),
234921
234980
  ...manager ? { manager } : {},
234922
234981
  acceptanceCriteria: ["\u6309\u63CF\u8FF0\u91CC\u7684\u590D\u73B0\u65B9\u5F0F\u9A8C\u8BC1\uFF1A\u6240\u62A5\u73B0\u8C61\u4E0D\u518D\u51FA\u73B0", "\u6539\u52A8\u6CA1\u6709\u660E\u663E\u6253\u574F\u76F4\u63A5\u76F8\u90BB\u7684\u8DEF\u5F84"],
234923
234982
  plan
@@ -234948,7 +235007,7 @@ function adoptExternalResolve(ctx) {
234948
235007
  artifactType: "research",
234949
235008
  title,
234950
235009
  brief,
234951
- goal: title,
235010
+ goal: workorderGoal(title, "\u56F4\u7ED5\u300C", "\u300D\u501F\u9274\u5916\u90E8\u65B9\u6848\uFF0C\u5B8C\u6210\u843D\u5730\u5E76\u9A8C\u8BC1\u6548\u679C\u3002"),
234952
235011
  ...manager ? { manager } : {},
234953
235012
  acceptanceCriteria: [
234954
235013
  "\u843D\u5730\u65B9\u6848\u9010\u6761\u8BF4\u660E\u4E86\u4E3A\u4EC0\u4E48\u4E0D\u76F4\u63A5\u91C7\u7528\u5DF2\u77E5\u89E3\u6CD5\uFF0C\u5E76\u5199\u660E\u6211\u4EEC\u65B9\u6848\u7684\u4EE3\u4EF7",
@@ -234979,7 +235038,7 @@ function simpleTaskResolve(ctx) {
234979
235038
  artifactType: "research",
234980
235039
  title,
234981
235040
  brief,
234982
- goal: title,
235041
+ goal: workorderGoal(title, "\u5B8C\u6210\u300C", "\u300D\uFF0C\u4EA4\u4ED8\u53D1\u8D77\u4EBA\u53EF\u76F4\u63A5\u4F7F\u7528\u7684\u7ED3\u679C\u3002"),
234983
235042
  ...manager ? { manager } : {},
234984
235043
  plan
234985
235044
  };
@@ -264088,7 +264147,8 @@ ${nodeFault}` : "");
264088
264147
  baseUrl: server.baseUrl,
264089
264148
  operatorToken,
264090
264149
  __coordinatorSpawnProbe: coordinatorSpawnProbe,
264091
- close: async () => {
264150
+ /** @param opts.drainMs 优雅停机排空预算,原样透传给 HTTP 层(见 server.ts 的 close)。缺省 = 老行为。 */
264151
+ close: async (opts2) => {
264092
264152
  for (const timer of dispatchTimers) clearInterval(timer);
264093
264153
  clearInterval(slaTimer);
264094
264154
  escalationDeliveryWorker.stop();
@@ -264107,7 +264167,7 @@ ${nodeFault}` : "");
264107
264167
  if (boardWatchTimer) clearInterval(boardWatchTimer);
264108
264168
  clearInterval(serverHeartbeatTimer);
264109
264169
  hub?.close();
264110
- await server.close();
264170
+ await server.close(opts2 ?? {});
264111
264171
  if (pgPool) await pgPool.end().catch(() => {
264112
264172
  });
264113
264173
  releaseServeLock(lock);
@@ -268326,9 +268386,25 @@ async function runCli(argv, println = console.log, progressln = console.error) {
268326
268386
  println(`oasis serve \u5C31\u7EEA\uFF1Ahttp://0.0.0.0:${port2}\uFF08\u672C\u673A\u8BBF\u95EE\u7528 http://127.0.0.1:${port2}\uFF09`);
268327
268387
  println(`operator token \u5DF2\u5199\u5165 ${path35.join(dir, "operator-token")}\uFF08\u672C\u673A CLI \u81EA\u52A8\u8BFB\u53D6\uFF09`);
268328
268388
  if (flags.get("dispatch") === "true") println(`\u8C03\u5EA6\u5FAA\u73AF\u5DF2\u542F\u52A8\uFF08claude-code adapter\uFF09`);
268329
- await new Promise(() => {
268389
+ const drainMs = Number(process.env["OASIS_SHUTDOWN_DRAIN_MS"] ?? "3000");
268390
+ const shutdownBudgetMs = drainMs + 5e3;
268391
+ await new Promise((resolve10) => {
268392
+ let stopping = false;
268393
+ const stop = (signal) => {
268394
+ if (stopping) return;
268395
+ stopping = true;
268396
+ println(`[serve] \u6536\u5230 ${signal}\uFF0C\u5F00\u59CB\u4F18\u96C5\u505C\u673A\uFF08\u6392\u7A7A\u9884\u7B97 ${drainMs}ms\uFF09\u2026`);
268397
+ const hardExit = setTimeout(() => {
268398
+ println(`[serve] \u505C\u673A\u603B\u9884\u7B97 ${shutdownBudgetMs}ms \u7528\u5C3D\uFF0C\u5F3A\u5236\u9000\u51FA`);
268399
+ process.exit(0);
268400
+ }, shutdownBudgetMs);
268401
+ hardExit.unref?.();
268402
+ void handle.close({ drainMs }).then(() => println("[serve] \u5DF2\u4F18\u96C5\u505C\u673A")).catch((err) => println(`[serve] \u505C\u673A\u8FC7\u7A0B\u51FA\u9519\uFF08\u7167\u5E38\u9000\u51FA\uFF09\uFF1A${String(err)}`)).finally(() => resolve10());
268403
+ };
268404
+ process.once("SIGTERM", () => stop("SIGTERM"));
268405
+ process.once("SIGINT", () => stop("SIGINT"));
268330
268406
  });
268331
- return;
268407
+ process.exit(0);
268332
268408
  }
268333
268409
  const base = flags.get("server") ?? process.env["OASIS_SERVER"] ?? "http://127.0.0.1:7320";
268334
268410
  const tokenFile = path35.join(dir, "operator-token");
@@ -271356,7 +271432,7 @@ function shimScript() {
271356
271432
  }
271357
271433
 
271358
271434
  // src/index.ts
271359
- var PKG_VERSION = true ? "2.2.13" : "dev";
271435
+ var PKG_VERSION = true ? "2.2.14" : "dev";
271360
271436
  var LOCAL_BIN = localBin();
271361
271437
  var NPM_PREFIX = npmPrefix();
271362
271438
  var INSTANCE = DEFAULT_INSTANCE;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oasis_test_v2",
3
- "version": "2.2.13",
3
+ "version": "2.2.14",
4
4
  "description": "Oasis node daemon + CLI — background daemon, auto-start, full server CLI",
5
5
  "bin": {
6
6
  "oasis": "./dist/index.js"
@@ -26,6 +26,6 @@
26
26
  "node": ">=20"
27
27
  },
28
28
  "oasisRelease": {
29
- "sourceHead": "b907ec1d1a0ae4a57e7eb938c477f05323b84590"
29
+ "sourceHead": "c6b9e871786ca57b2d1b01293c368ed043db700f"
30
30
  }
31
31
  }