oasis_test 0.1.14 → 0.1.15

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 +1671 -519
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -2806,13 +2806,10 @@ async function assembleContext(args) {
2806
2806
  for (const [p2, c] of Object.entries(identityFiles)) files[p2] = c;
2807
2807
  identityNote = `\`identity/\` \u2014\u2014 \u4F60\u7684\u4EBA\u683C\u4E0E\u884C\u4E3A\u51C6\u5219\uFF08${Object.keys(identityFiles).join(", ")}\uFF09\uFF0C\u5F00\u5DE5\u524D\u52A1\u5FC5\u8BFB`;
2808
2808
  }
2809
- const enabledSkillIds = new Set(actorCtx.config?.skills ?? []);
2810
- if (enabledSkillIds.size > 0) {
2811
- const byId = new Map(actorCtx.installedSkills.map((s2) => [s2.id, s2]));
2812
- skillsLines = [...enabledSkillIds].map((id) => {
2813
- const s2 = byId.get(id);
2814
- return s2 ? `- **${s2.name}** (\`${s2.id}\`)\uFF1A${s2.description}` : `- \`${id}\`\uFF08\u672A\u5728\u6280\u80FD\u5E93\u4E2D\uFF0C\u53EF\u80FD\u5DF2\u5378\u8F7D\uFF09`;
2815
- });
2809
+ if (actorCtx.installedSkills.length > 0) {
2810
+ skillsLines = actorCtx.installedSkills.map(
2811
+ (s2) => `- **${s2.name}** (\`${s2.id}\`)\uFF1A${s2.description}`
2812
+ );
2816
2813
  }
2817
2814
  const activeConnectors = actorCtx.connectors.filter((c) => c.status === "connected" || c.status === "verifying");
2818
2815
  if (activeConnectors.length > 0) {
@@ -3330,7 +3327,8 @@ var init_dispatcher = __esm({
3330
3327
  limits,
3331
3328
  ...binding !== void 0 ? { binding } : {},
3332
3329
  ...provisioned?.env && Object.keys(provisioned.env).length > 0 ? { env: provisioned.env } : {},
3333
- ...provisioned?.wrapperPaths && provisioned.wrapperPaths.length > 0 ? { wrapperPaths: provisioned.wrapperPaths } : {}
3330
+ ...provisioned?.wrapperPaths && provisioned.wrapperPaths.length > 0 ? { wrapperPaths: provisioned.wrapperPaths } : {},
3331
+ ...provisioned?.requiredTools && provisioned.requiredTools.length > 0 ? { requiredTools: provisioned.requiredTools } : {}
3334
3332
  };
3335
3333
  const session = await this.opts.adapter.spawn(job);
3336
3334
  this.inFlight.set(jobKey, session);
@@ -17124,49 +17122,49 @@ var require_fast_uri = __commonJS({
17124
17122
  schemelessOptions.skipEscape = true;
17125
17123
  return serialize(resolved, schemelessOptions);
17126
17124
  }
17127
- function resolveComponent(base, relative, options, skipNormalization) {
17125
+ function resolveComponent(base, relative2, options, skipNormalization) {
17128
17126
  const target = {};
17129
17127
  if (!skipNormalization) {
17130
17128
  base = parse2(serialize(base, options), options);
17131
- relative = parse2(serialize(relative, options), options);
17129
+ relative2 = parse2(serialize(relative2, options), options);
17132
17130
  }
17133
17131
  options = options || {};
17134
- if (!options.tolerant && relative.scheme) {
17135
- target.scheme = relative.scheme;
17136
- target.userinfo = relative.userinfo;
17137
- target.host = relative.host;
17138
- target.port = relative.port;
17139
- target.path = removeDotSegments(relative.path || "");
17140
- target.query = relative.query;
17132
+ if (!options.tolerant && relative2.scheme) {
17133
+ target.scheme = relative2.scheme;
17134
+ target.userinfo = relative2.userinfo;
17135
+ target.host = relative2.host;
17136
+ target.port = relative2.port;
17137
+ target.path = removeDotSegments(relative2.path || "");
17138
+ target.query = relative2.query;
17141
17139
  } else {
17142
- if (relative.userinfo !== void 0 || relative.host !== void 0 || relative.port !== void 0) {
17143
- target.userinfo = relative.userinfo;
17144
- target.host = relative.host;
17145
- target.port = relative.port;
17146
- target.path = removeDotSegments(relative.path || "");
17147
- target.query = relative.query;
17140
+ if (relative2.userinfo !== void 0 || relative2.host !== void 0 || relative2.port !== void 0) {
17141
+ target.userinfo = relative2.userinfo;
17142
+ target.host = relative2.host;
17143
+ target.port = relative2.port;
17144
+ target.path = removeDotSegments(relative2.path || "");
17145
+ target.query = relative2.query;
17148
17146
  } else {
17149
- if (!relative.path) {
17147
+ if (!relative2.path) {
17150
17148
  target.path = base.path;
17151
- if (relative.query !== void 0) {
17152
- target.query = relative.query;
17149
+ if (relative2.query !== void 0) {
17150
+ target.query = relative2.query;
17153
17151
  } else {
17154
17152
  target.query = base.query;
17155
17153
  }
17156
17154
  } else {
17157
- if (relative.path[0] === "/") {
17158
- target.path = removeDotSegments(relative.path);
17155
+ if (relative2.path[0] === "/") {
17156
+ target.path = removeDotSegments(relative2.path);
17159
17157
  } else {
17160
17158
  if ((base.userinfo !== void 0 || base.host !== void 0 || base.port !== void 0) && !base.path) {
17161
- target.path = "/" + relative.path;
17159
+ target.path = "/" + relative2.path;
17162
17160
  } else if (!base.path) {
17163
- target.path = relative.path;
17161
+ target.path = relative2.path;
17164
17162
  } else {
17165
- target.path = base.path.slice(0, base.path.lastIndexOf("/") + 1) + relative.path;
17163
+ target.path = base.path.slice(0, base.path.lastIndexOf("/") + 1) + relative2.path;
17166
17164
  }
17167
17165
  target.path = removeDotSegments(target.path);
17168
17166
  }
17169
- target.query = relative.query;
17167
+ target.query = relative2.query;
17170
17168
  }
17171
17169
  target.userinfo = base.userinfo;
17172
17170
  target.host = base.host;
@@ -17174,7 +17172,7 @@ var require_fast_uri = __commonJS({
17174
17172
  }
17175
17173
  target.scheme = base.scheme;
17176
17174
  }
17177
- target.fragment = relative.fragment;
17175
+ target.fragment = relative2.fragment;
17178
17176
  return target;
17179
17177
  }
17180
17178
  function equal(uriA, uriB, options) {
@@ -21222,12 +21220,83 @@ var init_planner = __esm({
21222
21220
  }
21223
21221
  });
21224
21222
 
21223
+ // ../server/src/domains/trace/chat-parts.ts
21224
+ function asObj(v2) {
21225
+ return v2 && typeof v2 === "object" && !Array.isArray(v2) ? v2 : void 0;
21226
+ }
21227
+ function textOf(v2) {
21228
+ if (typeof v2 === "string") return v2;
21229
+ if (Array.isArray(v2)) return v2.map(textOf).filter(Boolean).join("\n") || void 0;
21230
+ const o = asObj(v2);
21231
+ if (o) {
21232
+ if (typeof o.text === "string") return o.text;
21233
+ if (typeof o.content === "string") return o.content;
21234
+ if (typeof o.thinking === "string") return o.thinking;
21235
+ }
21236
+ return void 0;
21237
+ }
21238
+ function truncate(s2, limit = OUTPUT_PREVIEW_LIMIT) {
21239
+ return s2.length > limit ? `${s2.slice(0, limit)}\u2026` : s2;
21240
+ }
21241
+ function isSkillBootstrapText(text) {
21242
+ return typeof text === "string" && text.trimStart().startsWith("Base directory for this skill:");
21243
+ }
21244
+ function deriveChatPartsFromEvents(events) {
21245
+ const parts = [];
21246
+ for (const ev of [...events].sort((a, b2) => a.seq - b2.seq)) {
21247
+ const p2 = asObj(ev.payload);
21248
+ const kind = p2 && typeof p2.kind === "string" ? p2.kind : void 0;
21249
+ const raw = p2?.raw;
21250
+ if (kind === "thought") {
21251
+ const text = textOf(raw);
21252
+ if (text) parts.push({ type: "thinking", seq: ev.seq, text });
21253
+ continue;
21254
+ }
21255
+ if (kind === "tool_use") {
21256
+ const ro = asObj(raw);
21257
+ parts.push({
21258
+ type: "tool_use",
21259
+ seq: ev.seq,
21260
+ toolName: ro && typeof ro.name === "string" ? ro.name : "tool",
21261
+ ...ro && ro.input !== void 0 ? { input: ro.input } : {},
21262
+ ...ro && typeof ro.id === "string" ? { toolCallId: ro.id } : {}
21263
+ });
21264
+ continue;
21265
+ }
21266
+ if (kind === "tool_result") {
21267
+ const ro = asObj(raw);
21268
+ const output = textOf(raw) ?? "";
21269
+ parts.push({
21270
+ type: "tool_result",
21271
+ seq: ev.seq,
21272
+ output: truncate(output),
21273
+ isError: ro?.is_error === true || ro?.isError === true,
21274
+ ...ro && typeof ro.tool_use_id === "string" ? { toolCallId: ro.tool_use_id } : {}
21275
+ });
21276
+ continue;
21277
+ }
21278
+ if (kind === "message") {
21279
+ const text = textOf(raw);
21280
+ if (text && !isSkillBootstrapText(text)) parts.push({ type: "text", seq: ev.seq, text });
21281
+ continue;
21282
+ }
21283
+ }
21284
+ return parts;
21285
+ }
21286
+ var OUTPUT_PREVIEW_LIMIT;
21287
+ var init_chat_parts = __esm({
21288
+ "../server/src/domains/trace/chat-parts.ts"() {
21289
+ "use strict";
21290
+ OUTPUT_PREVIEW_LIMIT = 4e3;
21291
+ }
21292
+ });
21293
+
21225
21294
  // ../server/src/server.ts
21226
21295
  function wantsChatParts(req) {
21227
21296
  const accept = req.headers.accept;
21228
21297
  return typeof accept === "string" && accept.includes("application/x-ndjson");
21229
21298
  }
21230
- function asObj(value) {
21299
+ function asObj2(value) {
21231
21300
  return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
21232
21301
  }
21233
21302
  function asJsonValue(value) {
@@ -21240,9 +21309,9 @@ function asJsonValue(value) {
21240
21309
  }
21241
21310
  return null;
21242
21311
  }
21243
- function textOf(value) {
21312
+ function textOf2(value) {
21244
21313
  if (typeof value === "string") return value;
21245
- if (Array.isArray(value)) return value.map(textOf).filter(Boolean).join("\n");
21314
+ if (Array.isArray(value)) return value.map(textOf2).filter(Boolean).join("\n");
21246
21315
  if (value && typeof value === "object") {
21247
21316
  const obj = value;
21248
21317
  if (typeof obj.text === "string") return obj.text;
@@ -21255,13 +21324,14 @@ function truncateOutput(text) {
21255
21324
  ...[truncated]` : text;
21256
21325
  }
21257
21326
  function chatPartFromTrajectoryEvent(event) {
21258
- const payload = asObj(event.payload);
21327
+ const payload = asObj2(event.payload);
21259
21328
  if (event.kind === "message") {
21260
- const text = textOf(payload?.text ?? payload?.content ?? event.payload);
21261
- return text ? { type: "text", text } : null;
21329
+ const text = textOf2(payload?.text ?? payload?.content ?? event.payload);
21330
+ if (!text || isSkillBootstrapText(text)) return null;
21331
+ return { type: "text", text };
21262
21332
  }
21263
21333
  if (event.kind === "thought") {
21264
- const text = textOf(payload?.text ?? payload?.thinking ?? event.payload);
21334
+ const text = textOf2(payload?.text ?? payload?.thinking ?? event.payload);
21265
21335
  return text ? { type: "thinking", text } : null;
21266
21336
  }
21267
21337
  if (event.kind === "tool_use") {
@@ -21273,7 +21343,7 @@ function chatPartFromTrajectoryEvent(event) {
21273
21343
  };
21274
21344
  }
21275
21345
  if (event.kind === "tool_result") {
21276
- const output = textOf(payload?.content ?? payload?.output ?? event.payload) ?? "";
21346
+ const output = textOf2(payload?.content ?? payload?.output ?? event.payload) ?? "";
21277
21347
  return {
21278
21348
  type: "tool_result",
21279
21349
  toolCallId: typeof payload?.tool_use_id === "string" ? payload.tool_use_id : typeof payload?.id === "string" ? payload.id : void 0,
@@ -21292,9 +21362,9 @@ function chatPartsFromClaudeStreamLine(line, state) {
21292
21362
  return line ? [{ type: "text", text: line }] : [];
21293
21363
  }
21294
21364
  if (parsed.type === "stream_event") {
21295
- const event = asObj(parsed.event);
21365
+ const event = asObj2(parsed.event);
21296
21366
  if (event?.type === "content_block_delta") {
21297
- const delta = asObj(event.delta);
21367
+ const delta = asObj2(event.delta);
21298
21368
  if (delta?.type === "text_delta" && typeof delta.text === "string") {
21299
21369
  state.sawTextDelta = true;
21300
21370
  return [{ type: "text", text: delta.text }];
@@ -21304,7 +21374,7 @@ function chatPartsFromClaudeStreamLine(line, state) {
21304
21374
  }
21305
21375
  }
21306
21376
  if (event?.type === "content_block_start") {
21307
- const block = asObj(event.content_block);
21377
+ const block = asObj2(event.content_block);
21308
21378
  if (block?.type === "tool_use") {
21309
21379
  return [{
21310
21380
  type: "tool_use",
@@ -21317,12 +21387,12 @@ function chatPartsFromClaudeStreamLine(line, state) {
21317
21387
  return [];
21318
21388
  }
21319
21389
  if (parsed.type === "assistant" || parsed.type === "user") {
21320
- const message = asObj(parsed.message);
21390
+ const message = asObj2(parsed.message);
21321
21391
  const content = Array.isArray(message?.content) ? message.content : [];
21322
21392
  const parts = [];
21323
21393
  for (const block of content) {
21324
21394
  if (block.type === "text" && !state.sawTextDelta && typeof block.text === "string") {
21325
- parts.push({ type: "text", text: block.text });
21395
+ if (!isSkillBootstrapText(block.text)) parts.push({ type: "text", text: block.text });
21326
21396
  } else if (block.type === "thinking" && typeof block.thinking === "string") {
21327
21397
  parts.push({ type: "thinking", text: block.thinking });
21328
21398
  } else if (block.type === "tool_use") {
@@ -21336,7 +21406,7 @@ function chatPartsFromClaudeStreamLine(line, state) {
21336
21406
  parts.push({
21337
21407
  type: "tool_result",
21338
21408
  toolCallId: typeof block.tool_use_id === "string" ? block.tool_use_id : void 0,
21339
- output: truncateOutput(textOf(block.content) ?? ""),
21409
+ output: truncateOutput(textOf2(block.content) ?? ""),
21340
21410
  isError: block.is_error === true
21341
21411
  });
21342
21412
  }
@@ -22589,15 +22659,55 @@ async function startOasisServer(opts) {
22589
22659
  ...body.chatSessionId ? { chatSessionId: body.chatSessionId } : {},
22590
22660
  ...attachments.length ? { attachments } : {}
22591
22661
  });
22662
+ const chatStore = opts.chatSession;
22663
+ const persistSession = chatStore && body.chatSessionId ? await chatStore.getSession(body.chatSessionId).catch(() => null) : null;
22664
+ const persistTarget = persistSession && persistSession.humanActorId === actor ? persistSession : null;
22665
+ if (chatStore && persistTarget) {
22666
+ const { randomUUID: randomUUID15 } = await import("node:crypto");
22667
+ await chatStore.appendMessage({
22668
+ id: randomUUID15(),
22669
+ sessionId: persistTarget.id,
22670
+ role: "user",
22671
+ content: body.message,
22672
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
22673
+ }).catch(() => void 0);
22674
+ }
22675
+ let assistantText = "";
22676
+ let persisted = false;
22677
+ const streamedParts = [];
22678
+ const finalizeAssistant = async () => {
22679
+ if (persisted || !chatStore || !persistTarget) return;
22680
+ persisted = true;
22681
+ if (!assistantText && !session.runId && streamedParts.length === 0) return;
22682
+ const { randomUUID: randomUUID15 } = await import("node:crypto");
22683
+ await chatStore.appendMessage({
22684
+ id: randomUUID15(),
22685
+ sessionId: persistTarget.id,
22686
+ role: "assistant",
22687
+ content: assistantText,
22688
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
22689
+ ...session.runId ? { runId: session.runId } : {},
22690
+ ...streamedParts.length ? { parts: streamedParts } : {}
22691
+ }).catch(() => void 0);
22692
+ await chatStore.updateSession(persistTarget.id, {
22693
+ runtimeSessionId: session.id,
22694
+ touchedAt: (/* @__PURE__ */ new Date()).toISOString()
22695
+ }).catch(() => void 0);
22696
+ };
22592
22697
  let finished = false;
22593
22698
  res.on("close", () => {
22594
- if (!finished) void session.kill?.();
22699
+ if (!finished) {
22700
+ void session.kill?.();
22701
+ void finalizeAssistant();
22702
+ }
22595
22703
  });
22596
22704
  if (typedParts) {
22597
22705
  let seq = 0;
22598
22706
  const writePart = (part) => {
22707
+ const withSeq = { ...part, seq: ++seq };
22708
+ if (part.type !== "done") streamedParts.push(withSeq);
22599
22709
  if (res.destroyed) return;
22600
- res.write(`${JSON.stringify({ ...part, seq: ++seq })}
22710
+ res.write(`${JSON.stringify(withSeq)}
22601
22711
  `);
22602
22712
  };
22603
22713
  res.writeHead(200, {
@@ -22610,6 +22720,7 @@ async function startOasisServer(opts) {
22610
22720
  let sawTextOutput = false;
22611
22721
  session.onOutput((chunk) => {
22612
22722
  sawTextOutput = true;
22723
+ assistantText += chunk;
22613
22724
  writePart({ type: "text", text: chunk });
22614
22725
  });
22615
22726
  session.onTelemetry?.((event) => {
@@ -22623,6 +22734,7 @@ async function startOasisServer(opts) {
22623
22734
  writePart({ type: "error", text: err instanceof Error ? err.message : String(err) });
22624
22735
  }
22625
22736
  finished = true;
22737
+ await finalizeAssistant();
22626
22738
  writePart({ type: "done", sessionId: session.id });
22627
22739
  res.end();
22628
22740
  return;
@@ -22633,7 +22745,10 @@ async function startOasisServer(opts) {
22633
22745
  "access-control-expose-headers": "X-Session-Id",
22634
22746
  "x-session-id": session.id
22635
22747
  });
22636
- session.onOutput((chunk) => res.write(chunk));
22748
+ session.onOutput((chunk) => {
22749
+ assistantText += chunk;
22750
+ res.write(chunk);
22751
+ });
22637
22752
  try {
22638
22753
  await session.done;
22639
22754
  } catch (err) {
@@ -22643,6 +22758,7 @@ async function startOasisServer(opts) {
22643
22758
  `);
22644
22759
  }
22645
22760
  finished = true;
22761
+ await finalizeAssistant();
22646
22762
  res.end();
22647
22763
  return;
22648
22764
  }
@@ -22654,13 +22770,13 @@ async function startOasisServer(opts) {
22654
22770
  if (body.actorId && opts.resolveActorContext) {
22655
22771
  const actorCtx = await opts.resolveActorContext(body.actorId).catch(() => null);
22656
22772
  if (actorCtx) {
22657
- const { mkdtempSync: mkdtempSync6, mkdirSync: mkdirSync13, writeFileSync: writeFileSync14 } = await import("node:fs");
22658
- const { join: join16, dirname: dirname13 } = await import("node:path");
22659
- const { tmpdir: tmpdir8 } = await import("node:os");
22660
- const dir = mkdtempSync6(join16(tmpdir8(), "oasis-chat-"));
22773
+ const { mkdtempSync: mkdtempSync7, mkdirSync: mkdirSync13, writeFileSync: writeFileSync14 } = await import("node:fs");
22774
+ const { join: join18, dirname: dirname13 } = await import("node:path");
22775
+ const { tmpdir: tmpdir9 } = await import("node:os");
22776
+ const dir = mkdtempSync7(join18(tmpdir9(), "oasis-chat-"));
22661
22777
  if (actorCtx.config?.prompt) {
22662
22778
  for (const [rel, content] of Object.entries(splitIdentityFiles2(actorCtx.config.prompt))) {
22663
- const file = join16(dir, rel);
22779
+ const file = join18(dir, rel);
22664
22780
  mkdirSync13(dirname13(file), { recursive: true });
22665
22781
  writeFileSync14(file, content);
22666
22782
  }
@@ -22672,12 +22788,12 @@ async function startOasisServer(opts) {
22672
22788
  const s2 = byId.get(id);
22673
22789
  return s2 ? `- **${s2.name}** (\`${s2.id}\`): ${s2.description}` : `- \`${id}\`\uFF08\u672A\u5728\u6280\u80FD\u5E93\u4E2D\uFF0C\u53EF\u80FD\u5DF2\u5378\u8F7D\uFF09`;
22674
22790
  });
22675
- 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"));
22791
+ writeFileSync14(join18(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"));
22676
22792
  }
22677
22793
  if (opts.materializeSkills) {
22678
22794
  const skillFiles = await opts.materializeSkills(body.actorId, "claude").catch(() => ({}));
22679
22795
  for (const [rel, content] of Object.entries(skillFiles)) {
22680
- const file = join16(dir, rel);
22796
+ const file = join18(dir, rel);
22681
22797
  mkdirSync13(dirname13(file), { recursive: true });
22682
22798
  writeFileSync14(file, content);
22683
22799
  }
@@ -22691,7 +22807,7 @@ async function startOasisServer(opts) {
22691
22807
  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";
22692
22808
  return `- **${c.name}** (\`${c.id}\`): ${statusNote} \xB7 ${modeNote}`;
22693
22809
  });
22694
- 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"));
22810
+ writeFileSync14(join18(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"));
22695
22811
  }
22696
22812
  spawnCwd = dir;
22697
22813
  }
@@ -22808,9 +22924,9 @@ async function startOasisServer(opts) {
22808
22924
  return;
22809
22925
  }
22810
22926
  try {
22811
- const { execFile: execFile3 } = await import("node:child_process");
22812
- const { promisify: promisify3 } = await import("node:util");
22813
- const { stdout } = await promisify3(execFile3)(larkBin, ["config", "show", "--json"]);
22927
+ const { execFile: execFile4 } = await import("node:child_process");
22928
+ const { promisify: promisify4 } = await import("node:util");
22929
+ const { stdout } = await promisify4(execFile4)(larkBin, ["config", "show", "--json"]);
22814
22930
  const cfg = JSON.parse(stdout);
22815
22931
  send(cfg.appId ? { type: "done", appId: cfg.appId } : { type: "error", message: "could not determine appId" });
22816
22932
  } catch (e) {
@@ -22833,9 +22949,9 @@ async function startOasisServer(opts) {
22833
22949
  }
22834
22950
  const larkBin = process.env["LARK_CLI_BIN"] ?? "lark-cli";
22835
22951
  try {
22836
- const { execFile: execFile3 } = await import("node:child_process");
22837
- const { promisify: promisify3 } = await import("node:util");
22838
- const { stdout } = await promisify3(execFile3)(larkBin, ["--profile", appId, "auth", "login", "--no-wait", "--json", "--recommend"]);
22952
+ const { execFile: execFile4 } = await import("node:child_process");
22953
+ const { promisify: promisify4 } = await import("node:util");
22954
+ const { stdout } = await promisify4(execFile4)(larkBin, ["--profile", appId, "auth", "login", "--no-wait", "--json", "--recommend"]);
22839
22955
  const data = JSON.parse(stdout.trim());
22840
22956
  if (!data.verification_url) throw new Error("no verification_url in response");
22841
22957
  res.writeHead(200, { "content-type": "application/json" }).end(JSON.stringify({ verificationUrl: data.verification_url, deviceCode: data.device_code }));
@@ -22852,14 +22968,14 @@ async function startOasisServer(opts) {
22852
22968
  }
22853
22969
  const larkBin = process.env["LARK_CLI_BIN"] ?? "lark-cli";
22854
22970
  try {
22855
- const { execFile: execFile3 } = await import("node:child_process");
22856
- const { promisify: promisify3 } = await import("node:util");
22857
- const { stdout } = await promisify3(execFile3)(larkBin, ["--profile", appId, "auth", "login", "--device-code", deviceCode, "--json"]);
22971
+ const { execFile: execFile4 } = await import("node:child_process");
22972
+ const { promisify: promisify4 } = await import("node:util");
22973
+ const { stdout } = await promisify4(execFile4)(larkBin, ["--profile", appId, "auth", "login", "--device-code", deviceCode, "--json"]);
22858
22974
  const data = JSON.parse(stdout.trim());
22859
22975
  if (data.ok === false) throw new Error(data.error?.message ?? "login failed");
22860
22976
  let appSecret;
22861
22977
  try {
22862
- const { stdout: cfgOut } = await promisify3(execFile3)(larkBin, ["--profile", appId, "config", "show", "--json"]);
22978
+ const { stdout: cfgOut } = await promisify4(execFile4)(larkBin, ["--profile", appId, "config", "show", "--json"]);
22863
22979
  const cfg = JSON.parse(cfgOut.trim());
22864
22980
  appSecret = cfg.appSecret ?? cfg.app_secret;
22865
22981
  } catch {
@@ -23005,6 +23121,7 @@ var init_server3 = __esm({
23005
23121
  init_draft_edit();
23006
23122
  init_planner();
23007
23123
  init_identity();
23124
+ init_chat_parts();
23008
23125
  CoordinatorRequiredError = class extends Error {
23009
23126
  constructor(requirement) {
23010
23127
  super(requirement.message ?? "\u7CFB\u7EDF\u7F3A\u5C11\u534F\u8C03\u8005\u667A\u80FD\u4F53\u3002");
@@ -25575,7 +25692,7 @@ var init_service = __esm({
25575
25692
  });
25576
25693
 
25577
25694
  // ../server/src/dev-store.ts
25578
- var import_node_crypto4, fs3, path, NdjsonOplogStore, DirBlobStore, MUTATORS, FileTypeRegistryStore, FileRegistryStore, FileProjectStateStore, FileProjectDocumentStore, FileArtifactStateStore, FileTraceStore, MemoryChatSessionStore;
25695
+ var import_node_crypto4, fs3, path, NdjsonOplogStore, DirBlobStore, MUTATORS, FileTypeRegistryStore, FileRegistryStore, FileProjectStateStore, FileProjectDocumentStore, FileArtifactStateStore, FileTraceStore, MemoryChatSessionStore, FileChatSessionStore;
25579
25696
  var init_dev_store = __esm({
25580
25697
  "../server/src/dev-store.ts"() {
25581
25698
  "use strict";
@@ -26016,8 +26133,11 @@ var init_dev_store = __esm({
26016
26133
  }
26017
26134
  async appendMessage(m2) {
26018
26135
  const list = this.messages.get(m2.sessionId) ?? [];
26019
- list.push(m2);
26136
+ const seq = (list.length ? Math.max(...list.map((x2) => x2.seq)) : 0) + 1;
26137
+ const record4 = { ...m2, seq };
26138
+ list.push(record4);
26020
26139
  this.messages.set(m2.sessionId, list);
26140
+ return record4;
26021
26141
  }
26022
26142
  async listMessages(sessionId, limit) {
26023
26143
  const list = (this.messages.get(sessionId) ?? []).sort((a, b2) => a.seq - b2.seq);
@@ -26032,6 +26152,54 @@ var init_dev_store = __esm({
26032
26152
  return [...this.sessionWorkOrders.get(sessionId) ?? []];
26033
26153
  }
26034
26154
  };
26155
+ FileChatSessionStore = class _FileChatSessionStore extends MemoryChatSessionStore {
26156
+ constructor(file) {
26157
+ super();
26158
+ this.file = file;
26159
+ }
26160
+ static async open(file) {
26161
+ const store = new _FileChatSessionStore(file);
26162
+ if (fs3.existsSync(file)) {
26163
+ const snap = JSON.parse(fs3.readFileSync(file, "utf8"));
26164
+ for (const s2 of snap.sessions ?? []) store.sessions.set(s2.id, s2);
26165
+ for (const [sid, list] of Object.entries(snap.messages ?? {})) store.messages.set(sid, list);
26166
+ for (const [sid, ids] of Object.entries(snap.sessionWorkOrders ?? {})) store.sessionWorkOrders.set(sid, new Set(ids));
26167
+ } else {
26168
+ fs3.mkdirSync(path.dirname(file), { recursive: true });
26169
+ fs3.writeFileSync(file, JSON.stringify({ sessions: [], messages: {}, sessionWorkOrders: {} }, null, 2));
26170
+ }
26171
+ return store;
26172
+ }
26173
+ async createSession(s2) {
26174
+ await super.createSession(s2);
26175
+ this.save();
26176
+ }
26177
+ async updateSession(id, patch) {
26178
+ await super.updateSession(id, patch);
26179
+ this.save();
26180
+ }
26181
+ async deleteSession(id) {
26182
+ await super.deleteSession(id);
26183
+ this.save();
26184
+ }
26185
+ async appendMessage(m2) {
26186
+ const record4 = await super.appendMessage(m2);
26187
+ this.save();
26188
+ return record4;
26189
+ }
26190
+ async linkWorkOrder(sessionId, workOrderId) {
26191
+ await super.linkWorkOrder(sessionId, workOrderId);
26192
+ this.save();
26193
+ }
26194
+ save() {
26195
+ const snap = {
26196
+ sessions: [...this.sessions.values()],
26197
+ messages: Object.fromEntries(this.messages),
26198
+ sessionWorkOrders: Object.fromEntries([...this.sessionWorkOrders].map(([k2, v2]) => [k2, [...v2]]))
26199
+ };
26200
+ fs3.writeFileSync(this.file, JSON.stringify(snap, null, 2));
26201
+ }
26202
+ };
26035
26203
  }
26036
26204
  });
26037
26205
 
@@ -28652,7 +28820,7 @@ var require_websocket = __commonJS({
28652
28820
  var http2 = require("http");
28653
28821
  var net = require("net");
28654
28822
  var tls = require("tls");
28655
- var { randomBytes: randomBytes5, createHash: createHash6 } = require("crypto");
28823
+ var { randomBytes: randomBytes5, createHash: createHash8 } = require("crypto");
28656
28824
  var { Duplex, Readable } = require("stream");
28657
28825
  var { URL: URL2 } = require("url");
28658
28826
  var PerMessageDeflate2 = require_permessage_deflate();
@@ -29320,7 +29488,7 @@ var require_websocket = __commonJS({
29320
29488
  abortHandshake(websocket, socket, "Invalid Upgrade header");
29321
29489
  return;
29322
29490
  }
29323
- const digest = createHash6("sha1").update(key + GUID).digest("base64");
29491
+ const digest = createHash8("sha1").update(key + GUID).digest("base64");
29324
29492
  if (res.headers["sec-websocket-accept"] !== digest) {
29325
29493
  abortHandshake(websocket, socket, "Invalid Sec-WebSocket-Accept header");
29326
29494
  return;
@@ -29689,7 +29857,7 @@ var require_websocket_server = __commonJS({
29689
29857
  var EventEmitter = require("events");
29690
29858
  var http2 = require("http");
29691
29859
  var { Duplex } = require("stream");
29692
- var { createHash: createHash6 } = require("crypto");
29860
+ var { createHash: createHash8 } = require("crypto");
29693
29861
  var extension2 = require_extension();
29694
29862
  var PerMessageDeflate2 = require_permessage_deflate();
29695
29863
  var subprotocol2 = require_subprotocol();
@@ -29996,7 +30164,7 @@ var require_websocket_server = __commonJS({
29996
30164
  );
29997
30165
  }
29998
30166
  if (this._state > RUNNING) return abortHandshake(socket, 503);
29999
- const digest = createHash6("sha1").update(key + GUID).digest("base64");
30167
+ const digest = createHash8("sha1").update(key + GUID).digest("base64");
30000
30168
  const headers = [
30001
30169
  "HTTP/1.1 101 Switching Protocols",
30002
30170
  "Upgrade: websocket",
@@ -30748,6 +30916,17 @@ ${input.description}
30748
30916
  await this.opts.store.putInstalledSkill(updated);
30749
30917
  return updated;
30750
30918
  }
30919
+ /**
30920
+ * 设置组织技能的「全员生效」标记:true = 挂给本组织所有 agent(无需各 agent 启用);
30921
+ * false = 回落到「技能库」,由各 agent 在 config.skills 里按需挑选。
30922
+ */
30923
+ async setSkillAppliedToAll(skillId, appliedToAll) {
30924
+ const cur = (await this.opts.store.listInstalledSkills()).find((x2) => x2.id === skillId);
30925
+ if (!cur) throw new Error(`\u672A\u5B89\u88C5\u8BE5\u6280\u80FD\uFF1A${skillId}`);
30926
+ const updated = { ...cur, appliedToAll };
30927
+ await this.opts.store.putInstalledSkill(updated);
30928
+ return updated;
30929
+ }
30751
30930
  /**
30752
30931
  * 已安装技能 + 分配派生:员工←→技能的挂载事实在 actor_configs.skills 版本链里
30753
30932
  * (决策:不另建 assignment 表),这里按各 AI 员工最新配置聚合出"谁挂了它"。
@@ -30779,9 +30958,10 @@ ${input.description}
30779
30958
  return this.opts.store.listConnectors();
30780
30959
  }
30781
30960
  /**
30782
- * 为指定 AI 员工筛出已启用的技能:取该员工最新 config.skills 列表,
30783
- * 与公司已安装技能库做交叉,返回有元数据的完整 InstalledSkill 记录。
30784
- * 供派单装配器在每次会话启动时注入 SKILLS.md。
30961
+ * 为指定 AI 员工筛出**生效**的组织技能 = 该员工 config.skills 显式启用集 ∪ 全组织 appliedToAll 技能,
30962
+ * 与公司已安装技能库做交叉、去重,返回带元数据的完整 InstalledSkill 记录。
30963
+ * (内置技能不在 DB、不走这里,由 filesystem 加载后无条件挂给所有 agent。)
30964
+ * 供派单装配器在每次会话启动时注入技能正文与 TASK.md 索引。
30785
30965
  */
30786
30966
  async listInstalledSkillsForActor(actorId) {
30787
30967
  const [config2, all] = await Promise.all([
@@ -30789,8 +30969,9 @@ ${input.description}
30789
30969
  this.opts.store.listInstalledSkills()
30790
30970
  ]);
30791
30971
  const enabledKeys = new Set(config2?.skills ?? []);
30792
- if (enabledKeys.size === 0) return [];
30793
- return all.filter((s2) => enabledKeys.has(s2.id) || enabledKeys.has(s2.name));
30972
+ return all.filter(
30973
+ (s2) => s2.appliedToAll === true || enabledKeys.has(s2.id) || enabledKeys.has(s2.name)
30974
+ );
30794
30975
  }
30795
30976
  /** 全局已装技能平铺列表(「默认挂」materialize 用;installedSkillsWithAssignments 带分配关系太重)。 */
30796
30977
  async listInstalledSkills() {
@@ -31235,6 +31416,16 @@ function actorsDomain(opts) {
31235
31416
  const { service } = await resolveCtx(req.auth.companyId);
31236
31417
  return { status: 200, body: { items: await service.installedSkillsWithAssignments() } };
31237
31418
  });
31419
+ router.get("/api/skills/builtin", async () => {
31420
+ const items = (opts.listBuiltinSkills?.() ?? []).map((s2) => ({
31421
+ ...s2,
31422
+ source: "builtin",
31423
+ enabled: true,
31424
+ appliedToAll: true,
31425
+ builtin: true
31426
+ }));
31427
+ return { status: 200, body: { items } };
31428
+ });
31238
31429
  router.post("/api/skills/install", async (req) => {
31239
31430
  const { service } = await resolveCtx(req.auth.companyId);
31240
31431
  const b2 = req.body;
@@ -31259,6 +31450,10 @@ function actorsDomain(opts) {
31259
31450
  router.patch("/api/skills/:id", async (req) => {
31260
31451
  const { service } = await resolveCtx(req.auth.companyId);
31261
31452
  const b2 = req.body;
31453
+ if (typeof b2?.appliedToAll === "boolean") {
31454
+ const updated = await service.setSkillAppliedToAll(req.params.id, b2.appliedToAll);
31455
+ return { status: 200, body: updated };
31456
+ }
31262
31457
  if (typeof b2?.enabled === "boolean") {
31263
31458
  await service.setSkillEnabled(req.params.id, b2.enabled);
31264
31459
  return { status: 200, body: { ok: true } };
@@ -31270,7 +31465,7 @@ function actorsDomain(opts) {
31270
31465
  });
31271
31466
  return { status: 200, body: updated };
31272
31467
  }
31273
- throw new ApiError(400, "BAD_REQUEST", "\u4F20 enabled\uFF08\u5F00\u5173\uFF09\u6216 name/description\uFF08\u66F4\u65B0\u5143\u6570\u636E\uFF09");
31468
+ throw new ApiError(400, "BAD_REQUEST", "\u4F20 enabled\uFF08\u5F00\u5173\uFF09/ appliedToAll\uFF08\u5168\u5458\u751F\u6548\uFF09/ name\xB7description\uFF08\u66F4\u65B0\u5143\u6570\u636E\uFF09");
31274
31469
  });
31275
31470
  router.get("/api/actors/:id/stats", async (req) => {
31276
31471
  const { oplog } = await resolveCtx(req.auth.companyId);
@@ -31329,14 +31524,7 @@ function sanitizeSkillDir(name) {
31329
31524
  return s2 || "skill";
31330
31525
  }
31331
31526
  async function materializeSkillFiles(args) {
31332
- const own = await args.service.listInstalledSkillsForActor(args.actorId);
31333
- let extras = [];
31334
- if (args.extraSkillIds && args.extraSkillIds.length > 0) {
31335
- const ownIds = new Set(own.map((s2) => s2.id));
31336
- const wanted = new Set(args.extraSkillIds);
31337
- extras = (await args.service.listInstalledSkills()).filter((s2) => wanted.has(s2.id) && !ownIds.has(s2.id));
31338
- }
31339
- const skills = [...own, ...extras];
31527
+ const skills = await args.service.listInstalledSkillsForActor(args.actorId);
31340
31528
  if (skills.length === 0) return {};
31341
31529
  const prefix = skillsDirForRuntime(args.runtimeKind);
31342
31530
  const out = {};
@@ -31414,7 +31602,7 @@ function createActorsDomain(opts) {
31414
31602
  cache.set(companyId, ctx);
31415
31603
  return ctx;
31416
31604
  }
31417
- return { service: defaultCtx.service, register: actorsDomain({ resolveCtx }) };
31605
+ return { service: defaultCtx.service, register: actorsDomain({ resolveCtx, ...opts.listBuiltinSkills ? { listBuiltinSkills: opts.listBuiltinSkills } : {} }) };
31418
31606
  }
31419
31607
  var import_node_crypto6;
31420
31608
  var init_actors = __esm({
@@ -32206,84 +32394,12 @@ var init_projects = __esm({
32206
32394
  }
32207
32395
  });
32208
32396
 
32209
- // ../server/src/domains/companies/migrate.ts
32210
- async function migrateDefaultCompany(store, actors, opts = {}) {
32211
- const now = opts.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
32212
- let company = await store.getCompany(DEFAULT_COMPANY_ID);
32213
- let createdCompany = false;
32214
- if (!company) {
32215
- company = {
32216
- id: DEFAULT_COMPANY_ID,
32217
- slug: DEFAULT_COMPANY_SLUG,
32218
- name: DEFAULT_COMPANY_NAME,
32219
- status: "active",
32220
- createdAt: now()
32221
- };
32222
- await store.createCompany(company);
32223
- createdCompany = true;
32224
- }
32225
- let addedMembers = 0;
32226
- for (const a of actors) {
32227
- if (!await store.getMember(DEFAULT_COMPANY_ID, a.id)) {
32228
- await store.upsertMember({ companyId: DEFAULT_COMPANY_ID, accountId: a.id, actorId: a.id, role: "member" });
32229
- addedMembers++;
32230
- }
32231
- }
32232
- let designatedOwner;
32233
- const members = await store.listMembers(DEFAULT_COMPANY_ID);
32234
- if (members.length > 0 && !members.some((m2) => m2.role === "owner")) {
32235
- const sorted = [...actors].sort((x2, y) => x2.id.localeCompare(y.id));
32236
- const pick2 = sorted.find((a) => a.kind === "human") ?? sorted[0];
32237
- if (pick2) {
32238
- await store.upsertMember({ companyId: DEFAULT_COMPANY_ID, accountId: pick2.id, actorId: pick2.id, role: "owner" });
32239
- designatedOwner = pick2.id;
32240
- }
32241
- }
32242
- return {
32243
- company,
32244
- createdCompany,
32245
- addedMembers,
32246
- ...designatedOwner !== void 0 ? { designatedOwner } : {}
32247
- };
32248
- }
32249
- async function ensureCompanyActors(store, companyId, registry2, opts = {}) {
32250
- const now = opts.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
32251
- const members = await store.listMembers(companyId);
32252
- let created = 0;
32253
- for (const m2 of members) {
32254
- if (await registry2.getActor(m2.actorId)) continue;
32255
- const account = await store.getAccount(m2.accountId);
32256
- const rec = {
32257
- id: m2.actorId,
32258
- kind: "human",
32259
- name: account?.name ?? m2.accountId,
32260
- ...account?.email ? { email: account.email } : {},
32261
- roles: [],
32262
- status: "active",
32263
- createdAt: now()
32264
- };
32265
- await registry2.upsertActor(rec);
32266
- created++;
32267
- }
32268
- return created;
32269
- }
32270
- var DEFAULT_COMPANY_ID, DEFAULT_COMPANY_SLUG, DEFAULT_COMPANY_NAME;
32271
- var init_migrate = __esm({
32272
- "../server/src/domains/companies/migrate.ts"() {
32273
- "use strict";
32274
- DEFAULT_COMPANY_ID = "company:oasis";
32275
- DEFAULT_COMPANY_SLUG = "oasis";
32276
- DEFAULT_COMPANY_NAME = "Oasis \u603B\u90E8";
32277
- }
32278
- });
32279
-
32280
32397
  // ../server/src/domains/companies/service.ts
32281
32398
  var import_node_crypto7, ROLES, INVITABLE_ROLES, INVITATION_TTL_MS, SLUG_RE, CompanyError, CompaniesService;
32282
32399
  var init_service3 = __esm({
32283
32400
  "../server/src/domains/companies/service.ts"() {
32284
32401
  "use strict";
32285
32402
  import_node_crypto7 = require("node:crypto");
32286
- init_migrate();
32287
32403
  ROLES = ["owner", "admin", "member"];
32288
32404
  INVITABLE_ROLES = ["admin", "member"];
32289
32405
  INVITATION_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
@@ -32354,16 +32470,25 @@ var init_service3 = __esm({
32354
32470
  return membership;
32355
32471
  }
32356
32472
  /**
32357
- * CO-101 当前公司解析:默认归到「Oasis 总部」(company:hq),可由 selector(公司 id 或 slug)改选——
32358
- * 这是「先恒等到默认公司、留接缝」的接缝(CO-301 路由层据此取/懒建对应引擎)。
32359
- * 返回 company + 该用户是否其成员;公司不存在返回 null。
32473
+ * 当前公司解析(动态、随登录用户走):
32474
+ * - 带 selector(公司 id 或 slug)→ 按它取公司(CO-301 路由层据此取/懒建对应引擎)。
32475
+ * - 不带 selector 回退到**该用户自己的**第一个 active 公司(按 membership),而非全局硬编默认公司。
32476
+ * 前端 CompanyProvider 也是这个语义(存的当前公司不在列表时回退列表首个)。
32477
+ * 返回 company + 该用户是否其成员;找不到(selector 不存在,或用户无任何 active 公司)返回 null。
32360
32478
  */
32361
32479
  async resolveCurrentCompany(accountId, selector) {
32362
32480
  let company;
32363
32481
  if (selector) {
32364
32482
  company = await this.store.getCompany(selector) ?? await this.store.getCompanyBySlug(selector);
32365
32483
  } else {
32366
- company = await this.store.getCompany(DEFAULT_COMPANY_ID);
32484
+ company = null;
32485
+ for (const m2 of await this.store.listMembershipsForAccount(accountId)) {
32486
+ const c = await this.store.getCompany(m2.companyId);
32487
+ if (c?.status === "active") {
32488
+ company = c;
32489
+ break;
32490
+ }
32491
+ }
32367
32492
  }
32368
32493
  if (!company) return null;
32369
32494
  const member = await this.store.getMember(company.id, accountId);
@@ -32606,6 +32731,84 @@ var init_routes3 = __esm({
32606
32731
  }
32607
32732
  });
32608
32733
 
32734
+ // ../server/src/domains/companies/migrate.ts
32735
+ function resolveDefaultCompanyConfig(env = process.env) {
32736
+ const slug4 = env["OASIS_DEFAULT_COMPANY_SLUG"]?.trim() || "oasis";
32737
+ const name = env["OASIS_DEFAULT_COMPANY_NAME"]?.trim() || "Oasis \u603B\u90E8";
32738
+ return { id: `company:${slug4}`, slug: slug4, name };
32739
+ }
32740
+ async function migrateDefaultCompany(store, actors, opts = {}) {
32741
+ const now = opts.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
32742
+ const cfg = resolveDefaultCompanyConfig(opts.env);
32743
+ let company = await store.getCompany(cfg.id);
32744
+ let createdCompany = false;
32745
+ if (!company) {
32746
+ company = {
32747
+ id: cfg.id,
32748
+ slug: cfg.slug,
32749
+ name: cfg.name,
32750
+ status: "active",
32751
+ createdAt: now()
32752
+ };
32753
+ await store.createCompany(company);
32754
+ createdCompany = true;
32755
+ }
32756
+ let addedMembers = 0;
32757
+ for (const a of actors) {
32758
+ if (!await store.getMember(cfg.id, a.id)) {
32759
+ await store.upsertMember({ companyId: cfg.id, accountId: a.id, actorId: a.id, role: "member" });
32760
+ addedMembers++;
32761
+ }
32762
+ }
32763
+ let designatedOwner;
32764
+ const members = await store.listMembers(cfg.id);
32765
+ if (members.length > 0 && !members.some((m2) => m2.role === "owner")) {
32766
+ const sorted = [...actors].sort((x2, y) => x2.id.localeCompare(y.id));
32767
+ const pick2 = sorted.find((a) => a.kind === "human") ?? sorted[0];
32768
+ if (pick2) {
32769
+ await store.upsertMember({ companyId: cfg.id, accountId: pick2.id, actorId: pick2.id, role: "owner" });
32770
+ designatedOwner = pick2.id;
32771
+ }
32772
+ }
32773
+ return {
32774
+ company,
32775
+ createdCompany,
32776
+ addedMembers,
32777
+ ...designatedOwner !== void 0 ? { designatedOwner } : {}
32778
+ };
32779
+ }
32780
+ async function ensureCompanyActors(store, companyId, registry2, opts = {}) {
32781
+ const now = opts.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
32782
+ const members = await store.listMembers(companyId);
32783
+ let created = 0;
32784
+ for (const m2 of members) {
32785
+ if (await registry2.getActor(m2.actorId)) continue;
32786
+ const account = await store.getAccount(m2.accountId);
32787
+ const rec = {
32788
+ id: m2.actorId,
32789
+ kind: "human",
32790
+ name: account?.name ?? m2.accountId,
32791
+ ...account?.email ? { email: account.email } : {},
32792
+ roles: [],
32793
+ status: "active",
32794
+ createdAt: now()
32795
+ };
32796
+ await registry2.upsertActor(rec);
32797
+ created++;
32798
+ }
32799
+ return created;
32800
+ }
32801
+ var _defaultCompany, DEFAULT_COMPANY_ID, DEFAULT_COMPANY_SLUG, DEFAULT_COMPANY_NAME;
32802
+ var init_migrate = __esm({
32803
+ "../server/src/domains/companies/migrate.ts"() {
32804
+ "use strict";
32805
+ _defaultCompany = resolveDefaultCompanyConfig();
32806
+ DEFAULT_COMPANY_ID = _defaultCompany.id;
32807
+ DEFAULT_COMPANY_SLUG = _defaultCompany.slug;
32808
+ DEFAULT_COMPANY_NAME = _defaultCompany.name;
32809
+ }
32810
+ });
32811
+
32609
32812
  // ../server/src/domains/companies/index.ts
32610
32813
  function createCompaniesDomain(opts) {
32611
32814
  const service = new CompaniesService({
@@ -33282,19 +33485,29 @@ function nodesDomain(deps) {
33282
33485
  const entry = deps.enrollTokens.peek(enrollToken);
33283
33486
  if (!entry) return { status: 401, body: { error: "invalid or expired enroll token" } };
33284
33487
  const incomingNodeId = body.nodeId?.trim();
33285
- const ownsIncoming = !!incomingNodeId && (deps.nodeTokens.list().some((e) => e.nodeId === incomingNodeId) || await deps.nodeStore.getNode(incomingNodeId) !== null);
33286
33488
  let nodeId;
33287
33489
  if (entry.issuedNodeId) {
33288
- if (incomingNodeId && incomingNodeId !== entry.issuedNodeId && !ownsIncoming)
33289
- return { status: 409, body: { error: "nodeId mismatch" } };
33290
33490
  nodeId = entry.issuedNodeId;
33291
- } else if (ownsIncoming) {
33491
+ } else if (incomingNodeId) {
33292
33492
  nodeId = incomingNodeId;
33293
33493
  deps.enrollTokens.setIssuedNodeId(enrollToken, nodeId);
33294
33494
  } else {
33295
33495
  nodeId = `node-${(0, import_node_crypto9.randomUUID)().slice(0, 8)}`;
33296
33496
  deps.enrollTokens.setIssuedNodeId(enrollToken, nodeId);
33297
33497
  }
33498
+ const existingNode = await deps.nodeStore.getNode(nodeId);
33499
+ if (!existingNode || existingNode.status === "deleted") {
33500
+ const nowIso = (/* @__PURE__ */ new Date()).toISOString();
33501
+ await deps.nodeStore.upsertNode({
33502
+ id: nodeId,
33503
+ hostname: body.hostname ?? existingNode?.hostname ?? "unknown",
33504
+ nodeVersion: existingNode?.nodeVersion ?? "unknown",
33505
+ status: "online",
33506
+ lastSeenAt: nowIso,
33507
+ enrolledAt: existingNode?.enrolledAt ?? nowIso,
33508
+ ...entry.name ? { name: entry.name } : existingNode?.name ? { name: existingNode.name } : {}
33509
+ });
33510
+ }
33298
33511
  deps.enrollTokens.consume(enrollToken);
33299
33512
  if (entry.name) await deps.nodeStore.updateNodeName(nodeId, entry.name);
33300
33513
  const existing = deps.nodeTokens.list().find((e) => e.nodeId === nodeId);
@@ -35068,69 +35281,6 @@ var init_sink = __esm({
35068
35281
  }
35069
35282
  });
35070
35283
 
35071
- // ../server/src/domains/trace/chat-parts.ts
35072
- function asObj2(v2) {
35073
- return v2 && typeof v2 === "object" && !Array.isArray(v2) ? v2 : void 0;
35074
- }
35075
- function textOf2(v2) {
35076
- if (typeof v2 === "string") return v2;
35077
- if (Array.isArray(v2)) return v2.map(textOf2).filter(Boolean).join("\n") || void 0;
35078
- const o = asObj2(v2);
35079
- if (o) {
35080
- if (typeof o.text === "string") return o.text;
35081
- if (typeof o.content === "string") return o.content;
35082
- if (typeof o.thinking === "string") return o.thinking;
35083
- }
35084
- return void 0;
35085
- }
35086
- function truncate(s2, limit = OUTPUT_PREVIEW_LIMIT) {
35087
- return s2.length > limit ? `${s2.slice(0, limit)}\u2026` : s2;
35088
- }
35089
- function deriveChatPartsFromEvents(events) {
35090
- const parts = [];
35091
- for (const ev of [...events].sort((a, b2) => a.seq - b2.seq)) {
35092
- const p2 = asObj2(ev.payload);
35093
- const kind = p2 && typeof p2.kind === "string" ? p2.kind : void 0;
35094
- const raw = p2?.raw;
35095
- if (kind === "thought") {
35096
- const text = textOf2(raw);
35097
- if (text) parts.push({ type: "thinking", seq: ev.seq, text });
35098
- continue;
35099
- }
35100
- if (kind === "tool_use") {
35101
- const ro = asObj2(raw);
35102
- parts.push({
35103
- type: "tool_use",
35104
- seq: ev.seq,
35105
- toolName: ro && typeof ro.name === "string" ? ro.name : "tool",
35106
- ...ro && ro.input !== void 0 ? { input: ro.input } : {},
35107
- ...ro && typeof ro.id === "string" ? { toolCallId: ro.id } : {}
35108
- });
35109
- continue;
35110
- }
35111
- if (kind === "tool_result") {
35112
- const ro = asObj2(raw);
35113
- const output = textOf2(raw) ?? "";
35114
- parts.push({
35115
- type: "tool_result",
35116
- seq: ev.seq,
35117
- output: truncate(output),
35118
- isError: ro?.is_error === true || ro?.isError === true,
35119
- ...ro && typeof ro.tool_use_id === "string" ? { toolCallId: ro.tool_use_id } : {}
35120
- });
35121
- continue;
35122
- }
35123
- }
35124
- return parts;
35125
- }
35126
- var OUTPUT_PREVIEW_LIMIT;
35127
- var init_chat_parts = __esm({
35128
- "../server/src/domains/trace/chat-parts.ts"() {
35129
- "use strict";
35130
- OUTPUT_PREVIEW_LIMIT = 4e3;
35131
- }
35132
- });
35133
-
35134
35284
  // ../server/src/domains/trace/index.ts
35135
35285
  function createTraceDomain(opts) {
35136
35286
  const service = new TraceService({ store: opts.store });
@@ -35156,9 +35306,9 @@ function createChatSessionsDomain(opts) {
35156
35306
  return binding?.nodeId ?? null;
35157
35307
  }
35158
35308
  async function enrichMessages(messages) {
35159
- if (!opts.trace) return messages;
35160
35309
  return Promise.all(messages.map(async (m2) => {
35161
- if (!m2.runId) return m2;
35310
+ if (Array.isArray(m2.parts) && m2.parts.length) return m2;
35311
+ if (!opts.trace || !m2.runId) return m2;
35162
35312
  const events = await opts.trace.listEvents(m2.runId, {}).catch(() => []);
35163
35313
  const parts = deriveChatPartsFromEvents(events);
35164
35314
  return parts.length ? { ...m2, parts } : m2;
@@ -35222,17 +35372,14 @@ function createChatSessionsDomain(opts) {
35222
35372
  const body = req.body;
35223
35373
  if (!body?.role || !body?.content) throw new ApiError(400, "BAD_REQUEST", "role and content required");
35224
35374
  if (body.role !== "user" && body.role !== "assistant") throw new ApiError(400, "BAD_REQUEST", "role must be user or assistant");
35225
- const existing = await store.listMessages(req.params.id);
35226
- const msg = {
35375
+ const msg = await store.appendMessage({
35227
35376
  id: (0, import_node_crypto10.randomUUID)(),
35228
35377
  sessionId: req.params.id,
35229
35378
  role: body.role,
35230
35379
  content: body.content,
35231
- seq: existing.length + 1,
35232
35380
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
35233
35381
  ...body.runId ? { runId: body.runId } : {}
35234
- };
35235
- await store.appendMessage(msg);
35382
+ });
35236
35383
  await store.updateSession(req.params.id, { touchedAt: (/* @__PURE__ */ new Date()).toISOString() });
35237
35384
  return { status: 201, body: msg };
35238
35385
  });
@@ -37108,7 +37255,8 @@ var init_worker = __esm({
37108
37255
  limits,
37109
37256
  // OASIS_STAGE=1:本会话里 link/spawn/unlink/seal/cancel-part 进编辑缓冲、apply 才提交(react 攒事务)。
37110
37257
  env: { ...prov?.env ?? {}, OASIS_STAGE: "1", ...workspace ? { OASIS_WORKSPACE: workspace } : {} },
37111
- ...prov?.wrapperPaths && prov.wrapperPaths.length > 0 ? { wrapperPaths: prov.wrapperPaths } : {}
37258
+ ...prov?.wrapperPaths && prov.wrapperPaths.length > 0 ? { wrapperPaths: prov.wrapperPaths } : {},
37259
+ ...prov?.requiredTools && prov.requiredTools.length > 0 ? { requiredTools: prov.requiredTools } : {}
37112
37260
  };
37113
37261
  this.deps.log?.(`[coordinator] ${logMsg}`);
37114
37262
  const handle = await this.deps.adapter.spawn(job);
@@ -37135,112 +37283,75 @@ var init_worker = __esm({
37135
37283
  }
37136
37284
  });
37137
37285
 
37138
- // ../server/src/governance/build-workorder-skill.ts
37139
- async function bootstrapBuildWorkorderSkill(service) {
37140
- const SYSTEM = "actor:system:bootstrap";
37141
- const installed = await service.listInstalledSkills();
37142
- if (!installed.some((s2) => s2.id === BUILD_WORKORDER_SKILL_ID)) {
37143
- await service.installCustomSkill(
37144
- { id: BUILD_WORKORDER_SKILL_ID, name: BUILD_WORKORDER_SKILL_ID, description: DESCRIPTION, source: "custom" },
37145
- SYSTEM
37146
- );
37286
+ // ../server/src/governance/builtin-skills.ts
37287
+ async function collectDir(root, dir, out) {
37288
+ const entries = await (0, import_promises.readdir)(dir, { withFileTypes: true });
37289
+ for (const e of entries) {
37290
+ const abs = (0, import_node_path.join)(dir, e.name);
37291
+ if (e.isDirectory()) {
37292
+ await collectDir(root, abs, out);
37293
+ } else if (e.isFile()) {
37294
+ const info = await (0, import_promises.stat)(abs);
37295
+ if (info.size > MAX_FILE_BYTES2) {
37296
+ console.warn(`[builtin-skills] skip oversized file (${info.size}B): ${abs}`);
37297
+ continue;
37298
+ }
37299
+ const rel = (0, import_node_path.relative)(root, abs).split(/[\\/]/).join("/");
37300
+ out[rel] = await (0, import_promises.readFile)(abs, "utf8");
37301
+ }
37302
+ }
37303
+ }
37304
+ async function loadBuiltinSkills(skillsDir) {
37305
+ let dirents;
37306
+ try {
37307
+ dirents = await (0, import_promises.readdir)(skillsDir, { withFileTypes: true });
37308
+ } catch {
37309
+ return [];
37310
+ }
37311
+ const skills = [];
37312
+ for (const d of dirents) {
37313
+ if (!d.isDirectory()) continue;
37314
+ const slug4 = d.name;
37315
+ const skillDir = (0, import_node_path.join)(skillsDir, slug4);
37316
+ const files = {};
37317
+ try {
37318
+ await collectDir(skillDir, skillDir, files);
37319
+ } catch (err) {
37320
+ console.warn(`[builtin-skills] failed reading ${slug4}: ${String(err)}`);
37321
+ continue;
37322
+ }
37323
+ const skillMd = files["SKILL.md"];
37324
+ if (!skillMd) {
37325
+ console.warn(`[builtin-skills] ${slug4} has no SKILL.md, skipping`);
37326
+ continue;
37327
+ }
37328
+ const { name, description } = parseSkillFrontmatter(skillMd);
37329
+ skills.push({ id: slug4, name: name || slug4, description, files });
37330
+ }
37331
+ return skills;
37332
+ }
37333
+ function materializeBuiltinSkills(builtins, skillsDirPrefix) {
37334
+ const out = {};
37335
+ for (const skill of builtins) {
37336
+ const dir = sanitize(skill.name || skill.id);
37337
+ for (const [rel, content] of Object.entries(skill.files)) {
37338
+ out[`${skillsDirPrefix}/${dir}/${rel}`] = content;
37339
+ }
37147
37340
  }
37148
- await service.putSkillFile(BUILD_WORKORDER_SKILL_ID, "SKILL.md", BUILD_WORKORDER_SKILL_MD);
37341
+ return out;
37149
37342
  }
37150
- var BUILD_WORKORDER_SKILL_ID, DESCRIPTION, BUILD_WORKORDER_SKILL_MD;
37151
- var init_build_workorder_skill = __esm({
37152
- "../server/src/governance/build-workorder-skill.ts"() {
37343
+ function sanitize(name) {
37344
+ const s2 = name.toLowerCase().trim().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
37345
+ return s2 || "skill";
37346
+ }
37347
+ var import_promises, import_node_path, MAX_FILE_BYTES2;
37348
+ var init_builtin_skills = __esm({
37349
+ "../server/src/governance/builtin-skills.ts"() {
37153
37350
  "use strict";
37154
- BUILD_WORKORDER_SKILL_ID = "build-workorder";
37155
- DESCRIPTION = "\u628A\u804A\u6E05\u695A\u7684\u9700\u6C42\u843D\u6210\u4E00\u5F20\u5DE5\u5355\uFF08\u534F\u4F5C\u56FE\uFF09\uFF0C\u4EE5\u53CA\u5728\u4EBA\u786E\u8BA4\u524D\u7EE7\u7EED\u6539\u8FD9\u4EFD\u8349\u6848\u3002\u5F53\u7528\u6237\u8981\u5EFA\u5355/\u7ACB\u9879\uFF0C\u6216\u8981\u8C03\u6574\u521A\u751F\u6210\u7684\u5DE5\u5355\u8349\u6848\u65F6\u7528\u672C\u6280\u80FD\uFF1A\u67E5\u91CD oasis workorders \u2192 \u5EFA\u5355 oasis create-workorder \u2192 \u6539\u8349\u6848 oasis spawn/edit/link/cancel\u3002";
37156
- BUILD_WORKORDER_SKILL_MD = String.raw`---
37157
- name: build-workorder
37158
- description: ${DESCRIPTION}
37159
- ---
37160
-
37161
- # 建单:把对话落成一张工单
37162
-
37163
- 工单 = 一张以产物为中心的协作图:一个根 ´brief´(工单总规格)+ 若干分解节点(prd/research/design/dev/qa…),节点间连依赖边。你把对话敲定的内容**结构化暂存**成一份草案,人在编辑器里看、确认才真创建。
37164
-
37165
- ## 流程
37166
- 1. **查重** ´oasis workorders --search <词>´:有没有类似工单在做/做过,别重复立项。
37167
- 2. **建单** ´oasis create-workorder …´:暂存**草案**(stage=draft,人确认才真创建),命令**直接打印** workspace(工单 id)+ 各节点 id。
37168
- 3. **改草案** ´oasis spawn/edit/link/cancel …´:人确认前,随时调这份草案(加/删/改节点、连边)。
37169
- 4. 人在编辑器确认 → 工单创建。**你不用 apply**,确认是人的事。
37170
-
37171
- ## 一、查重(建单前必做)
37172
- - ´oasis workorders --search <关键词>´:列/搜工单。命中类似的 → ´oasis workorder <工单id>´ 看详情,已覆盖就别再建、跟用户说明。
37173
-
37174
- ## 二、建单
37175
- 信息不足、关键决策没定**别硬建**,用文字问用户;一个工单一个目标,多件独立的事拆成多张。
37176
-
37177
- ´´´bash
37178
- oasis create-workorder \
37179
- --type <工单主产物类型,如 prd;根 brief 自动建> \
37180
- --title "工单名,名词短语,≤20字" \
37181
- --goal "一句话目标,≤50字" \
37182
- --brief "完整规格(markdown):背景、关键决策、约束、范围、不做什么" \
37183
- --acceptance '["逐条可判定的验收条件","没聊到给 []"]' \
37184
- --plan '<节点分解 JSON,见下>'
37185
- ´´´
37186
- **不用填 workspace——系统自动生成工单 id。** 命令**直接打印 workspace(工单 id)和每个节点的 id**,记住它们、改草案要用——**不必再去 query**。它产出的是**草案**(stage=draft):人在编辑器确认才真创建,你别拿它当活工单使。
37187
-
37188
- ### --plan 结构
37189
- ´´´json
37190
- {
37191
- "nodes": [
37192
- { "type": "节点类型", "title": "≤20字,同 type 内唯一",
37193
- "dependsOn": ["上游节点写 type:title,如 prd:需求;无上游写 []"],
37194
- "brief": "这个节点具体要产出什么的规格,别套模板、别只写类型名" }
37195
- ],
37196
- "notes": ["可选:分解理由/缺口;没有给 []"]
37197
- }
37198
- ´´´
37199
-
37200
- ## 三、改草案(人确认前随时改)
37201
- 建单后草案在编辑器里等人确认。用户要调整就用下面的命令改这份草案(**改完即时生效、人在编辑器里能看到**,你不用 apply):
37202
-
37203
- **节点引用 = id 或 type:title,分清:**
37204
- - **填完整 id**(´oasis workorder´ / create 返回的,如 ´artifact:prd:x7k2-需求´)→ **不用带 workspace**。推荐。
37205
- - **填 type:title**(简写,如 ´prd:需求´)→ **必须加 ´--workspace <工单id>´**(create 返回的那个)。
37206
- - ´spawn´ 是新建节点、没有现成 id → **一律带 ´--workspace´**。
37207
-
37208
- ´´´bash
37209
- # 加节点(带 workspace;--input 上游可用 type:title)
37210
- oasis spawn --type qa --title 验收用例 --brief "覆盖登录成功/失败" --input dev:实现 --workspace ws:wo-x7k2
37211
- # 改节点标题/规格(用 id,免 workspace)
37212
- oasis edit artifact:prd:x7k2-需求 --title "需求与范围"
37213
- # 连边:消费者依赖上游
37214
- oasis link artifact:qa:x7k2-验收 --to artifact:dev:x7k2-实现
37215
- # 删节点(从草案删掉,连它的边一起摘)
37216
- oasis cancel prd:需求 --workspace ws:wo-x7k2
37217
- ´´´
37218
-
37219
- # 命令参考
37220
-
37221
- ## 查询
37222
- - ´oasis workorders [--search <词>]´ —— 列/搜工单(id/名/阶段/进度)。无参列全部;´--search´ 端侧过滤。
37223
- - ´oasis workorder <工单id>´ —— 工单详情:**stage**(draft 草案 / executing 进行中 / review 评审 / blocked 卡住 / concluded 已完成 / sealed 已封存)+ 目标/验收 + 节点(id/type)+ 依赖边。**刚建的草案这里 ´stage=draft´、也查得到(含各节点 id)**;真查不到才报不存在。
37224
- - ´oasis artifact-types list | search <词> | show <type>´ —— 看可用节点类型(´show´ 看该类型可填的自定义字段)。
37225
- - ´oasis projects [--search <词>]´ —— 列/搜项目(建单 ´--project´ 用)。
37226
-
37227
- ## 建单
37228
- - ´oasis create-workorder --type <t> --title <s> [--goal <s>] [--brief <s>] [--acceptance '<json string[]>'] [--plan '<json>'] [--project <id>]´
37229
- 暂存建单草案。´--type´=主产物类型(必填,根 brief 自动建);´--title´ 必填。**不填 workspace(系统生成)**。返回 ´workspace´ + 各节点 ´id´。
37230
-
37231
- ## 改草案(引用 id 不带 workspace;引用 type:title 或 spawn 带 ´--workspace <工单id>´)
37232
- - ´oasis spawn --type <t> --title <s> [--brief <s>] [--input <ref>,<ref>…] --workspace <工单id>´ —— 加节点,id 据 (type,title) 自动派生。´--type´/´--title´ 必填,title 同 type 唯一;´--input´=上游依赖(逗号分隔)。
37233
- - ´oasis edit <ref> [--title <s>] [--brief <s>]´ —— 改节点标题/规格。
37234
- - ´oasis link <consumer-ref> --to <upstream-ref> [--required true]´ —— 加依赖边。´--required true´=阻塞性(上游没完成下游动不了),省=软依赖。
37235
- - ´oasis unlink <consumer-ref> --to <upstream-ref>´ —— 删依赖边。
37236
- - ´oasis cancel <ref> [--note <s>]´ —— 从草案删节点(连它的边一起摘)。
37237
-
37238
- ## 规则
37239
- - **节点类型**:先 ´oasis artifact-types list/search/show´ 挑最贴合的;都不贴合用 ´oasis artifact-types add <name> --content-type <text|code|…> --owner-role <角色>´ 新建再用。常见:prd、research、design、adr、dev、qa。
37240
- - **title**:≤20字、同 type 内唯一、别用半角冒号 ´:´(与 type:title 分隔冲突,要冒号用全角 ´:´)。一个工单可多个同 type 节点,靠 title 区分。
37241
- - **项目**:归某项目就 ´oasis projects --search <词>´ 找 id 再加 ´--project´。
37242
- - **边界**:你只管图的结构与规格,不替用户拍产品决策——拿不准就问。改**已建好的活工单**是协调者的活,不在本技能范围。
37243
- `.replace(/´/g, "`");
37351
+ import_promises = require("node:fs/promises");
37352
+ import_node_path = require("node:path");
37353
+ init_skill_fetcher();
37354
+ MAX_FILE_BYTES2 = 1 << 20;
37244
37355
  }
37245
37356
  });
37246
37357
 
@@ -37283,7 +37394,7 @@ var init_src4 = __esm({
37283
37394
  init_worker();
37284
37395
  init_drafts();
37285
37396
  init_workorder_drafts();
37286
- init_build_workorder_skill();
37397
+ init_builtin_skills();
37287
37398
  }
37288
37399
  });
37289
37400
 
@@ -38855,11 +38966,11 @@ var require_pg_connection_string = __commonJS({
38855
38966
  config2.client_encoding = result.searchParams.get("encoding");
38856
38967
  return config2;
38857
38968
  }
38858
- const hostname5 = dummyHost ? "" : result.hostname;
38969
+ const hostname6 = dummyHost ? "" : result.hostname;
38859
38970
  if (!config2.host) {
38860
- config2.host = decodeURIComponent(hostname5);
38861
- } else if (hostname5 && /^%2f/i.test(hostname5)) {
38862
- result.pathname = hostname5 + result.pathname;
38971
+ config2.host = decodeURIComponent(hostname6);
38972
+ } else if (hostname6 && /^%2f/i.test(hostname6)) {
38973
+ result.pathname = hostname6 + result.pathname;
38863
38974
  }
38864
38975
  if (!config2.port) {
38865
38976
  config2.port = result.port;
@@ -40824,8 +40935,8 @@ var require_lib = __commonJS({
40824
40935
  var helper = require_helper();
40825
40936
  module2.exports = function(connInfo, cb) {
40826
40937
  var file = helper.getFileName();
40827
- fs17.stat(file, function(err, stat) {
40828
- if (err || !helper.usePgPass(stat, file)) {
40938
+ fs17.stat(file, function(err, stat2) {
40939
+ if (err || !helper.usePgPass(stat2, file)) {
40829
40940
  return cb(void 0);
40830
40941
  }
40831
40942
  var st = fs17.createReadStream(file);
@@ -41500,7 +41611,7 @@ var require_pg_pool = __commonJS({
41500
41611
  function throwOnDoubleRelease() {
41501
41612
  throw new Error("Release called on client which has already been released to the pool.");
41502
41613
  }
41503
- function promisify3(Promise2, callback) {
41614
+ function promisify4(Promise2, callback) {
41504
41615
  if (callback) {
41505
41616
  return { callback, result: void 0 };
41506
41617
  }
@@ -41638,7 +41749,7 @@ var require_pg_pool = __commonJS({
41638
41749
  const err = new Error("Cannot use a pool after calling end on the pool");
41639
41750
  return cb ? cb(err) : this.Promise.reject(err);
41640
41751
  }
41641
- const response = promisify3(this.Promise, cb);
41752
+ const response = promisify4(this.Promise, cb);
41642
41753
  const result = response.result;
41643
41754
  if (this._isFull() || this._idle.length) {
41644
41755
  if (this._idle.length) {
@@ -41823,7 +41934,7 @@ var require_pg_pool = __commonJS({
41823
41934
  }
41824
41935
  query(text, values, cb) {
41825
41936
  if (typeof text === "function") {
41826
- const response2 = promisify3(this.Promise, text);
41937
+ const response2 = promisify4(this.Promise, text);
41827
41938
  setImmediate(function() {
41828
41939
  return response2.callback(new Error("Passing a function as the first parameter to pool.query is not supported"));
41829
41940
  });
@@ -41833,7 +41944,7 @@ var require_pg_pool = __commonJS({
41833
41944
  cb = values;
41834
41945
  values = void 0;
41835
41946
  }
41836
- const response = promisify3(this.Promise, cb);
41947
+ const response = promisify4(this.Promise, cb);
41837
41948
  cb = response.callback;
41838
41949
  this.connect((err, client) => {
41839
41950
  if (err) {
@@ -41878,7 +41989,7 @@ var require_pg_pool = __commonJS({
41878
41989
  return cb ? cb(err) : this.Promise.reject(err);
41879
41990
  }
41880
41991
  this.ending = true;
41881
- const promised = promisify3(this.Promise, cb);
41992
+ const promised = promisify4(this.Promise, cb);
41882
41993
  this._endCallback = promised.callback;
41883
41994
  this._pulseQueue();
41884
41995
  return promised.result;
@@ -42472,30 +42583,138 @@ var init_trajectory = __esm({
42472
42583
  var fs16 = __toESM(require("node:fs"));
42473
42584
  var os8 = __toESM(require("node:os"));
42474
42585
  var path13 = __toESM(require("node:path"));
42475
- var import_node_child_process10 = require("node:child_process");
42586
+ var import_node_child_process12 = require("node:child_process");
42476
42587
 
42477
42588
  // ../cli/src/cli.ts
42478
42589
  var fs15 = __toESM(require("node:fs"), 1);
42479
42590
  var os7 = __toESM(require("node:os"), 1);
42480
42591
  var path12 = __toESM(require("node:path"), 1);
42481
- var import_node_crypto21 = require("node:crypto");
42592
+ var import_node_crypto23 = require("node:crypto");
42482
42593
 
42483
42594
  // ../cli/src/serve.ts
42484
42595
  var fs14 = __toESM(require("node:fs"), 1);
42485
42596
  var os6 = __toESM(require("node:os"), 1);
42486
42597
  var path11 = __toESM(require("node:path"), 1);
42487
- var import_node_crypto20 = require("node:crypto");
42598
+ var import_node_crypto21 = require("node:crypto");
42488
42599
  var import_node_url3 = require("node:url");
42489
42600
  init_src2();
42490
42601
  init_src4();
42491
42602
 
42492
- // ../connectors/src/feishu/index.ts
42603
+ // ../connectors/src/_base/stage.ts
42604
+ var import_node_fs2 = require("node:fs");
42605
+ var import_node_os = require("node:os");
42606
+ var import_node_path2 = require("node:path");
42607
+ var stagedBinDirs = /* @__PURE__ */ new Map();
42608
+ function stageConnectorWrapper(connector) {
42609
+ const scriptPath = connector.wrapperScriptPath();
42610
+ if (scriptPath === null) return null;
42611
+ const commandName = connector.commandName;
42612
+ if (!commandName) {
42613
+ throw new Error(`connector ${connector.config.slug}: commandName \u4E3A\u7A7A\uFF0CCLI \u7C7B\u5FC5\u987B\u58F0\u660E\u8981\u906E\u853D\u7684\u547D\u4EE4\u540D`);
42614
+ }
42615
+ const cacheKey = `${commandName}\0${scriptPath}`;
42616
+ const cached2 = stagedBinDirs.get(cacheKey);
42617
+ if (cached2 && (0, import_node_fs2.existsSync)((0, import_node_path2.join)(cached2, commandName))) return (0, import_node_path2.join)(cached2, commandName);
42618
+ const binDir = (0, import_node_fs2.mkdtempSync)((0, import_node_path2.join)((0, import_node_os.tmpdir)(), `oasis-conn-${connector.config.slug}-`));
42619
+ const link = (0, import_node_path2.join)(binDir, commandName);
42620
+ (0, import_node_fs2.symlinkSync)(scriptPath, link);
42621
+ stagedBinDirs.set(cacheKey, binDir);
42622
+ return link;
42623
+ }
42624
+
42625
+ // ../connectors/src/_base/install.ts
42493
42626
  var import_node_child_process3 = require("node:child_process");
42494
42627
  var import_node_util = require("node:util");
42495
- var import_node_path = require("node:path");
42496
- var import_node_url = require("node:url");
42497
42628
  var execFileAsync = (0, import_node_util.promisify)(import_node_child_process3.execFile);
42498
- var __dirname = (0, import_node_path.dirname)((0, import_node_url.fileURLToPath)(__esm_import_meta_url));
42629
+ function installPlan(cmd, platform) {
42630
+ if (cmd === "lark-cli") {
42631
+ const npm = platform === "win32" ? "npm.cmd" : "npm";
42632
+ return [{ file: npm, args: ["install", "-g", "@larksuite/cli"] }];
42633
+ }
42634
+ const pkg = cmd === "gh" ? { brew: "gh", apt: "gh", dnf: "gh", pacman: "github-cli", apk: "github-cli", winget: "GitHub.cli", choco: "gh", scoop: "gh" } : { brew: "git", apt: "git", dnf: "git", pacman: "git", apk: "git", winget: "Git.Git", choco: "git", scoop: "git" };
42635
+ if (platform === "darwin") {
42636
+ return [{ file: "brew", args: ["install", pkg.brew], requires: "brew" }];
42637
+ }
42638
+ if (platform === "win32") {
42639
+ return [
42640
+ { file: "winget", args: ["install", "--silent", "--accept-package-agreements", "--accept-source-agreements", "-e", "--id", pkg.winget], requires: "winget" },
42641
+ { file: "choco", args: ["install", "-y", pkg.choco], requires: "choco" },
42642
+ { file: "scoop", args: ["install", pkg.scoop], requires: "scoop" }
42643
+ ];
42644
+ }
42645
+ return [
42646
+ { file: "sudo", args: ["apt-get", "install", "-y", "--no-install-recommends", pkg.apt], requires: "apt-get" },
42647
+ { file: "sudo", args: ["dnf", "install", "-y", pkg.dnf], requires: "dnf" },
42648
+ { file: "sudo", args: ["pacman", "-S", "--noconfirm", pkg.pacman], requires: "pacman" },
42649
+ { file: "sudo", args: ["apk", "add", pkg.apk], requires: "apk" }
42650
+ ];
42651
+ }
42652
+ var confirmed = /* @__PURE__ */ new Set();
42653
+ function defaultDeps() {
42654
+ const whichCmd = process.platform === "win32" ? "where" : "which";
42655
+ const probe = async (cmd) => {
42656
+ try {
42657
+ await execFileAsync(whichCmd, [cmd]);
42658
+ return true;
42659
+ } catch {
42660
+ return false;
42661
+ }
42662
+ };
42663
+ return {
42664
+ isPresent: probe,
42665
+ hasCommand: probe,
42666
+ run: async (file, args) => {
42667
+ await execFileAsync(file, args, { timeout: 18e4 });
42668
+ },
42669
+ platform: process.platform,
42670
+ log: (msg) => console.warn(msg)
42671
+ };
42672
+ }
42673
+ async function ensureToolInstalled(cmd, deps = {}) {
42674
+ const d = { ...defaultDeps(), ...deps };
42675
+ if (confirmed.has(cmd)) return true;
42676
+ if (await d.isPresent(cmd)) {
42677
+ confirmed.add(cmd);
42678
+ return true;
42679
+ }
42680
+ const steps = installPlan(cmd, d.platform);
42681
+ if (steps.length === 0) {
42682
+ d.log(`[connector-install] ${cmd} \u7F3A\u5931\uFF0C\u4E14 ${d.platform} \u4E0B\u65E0\u5DF2\u77E5\u81EA\u52A8\u5B89\u88C5\u6CD5\u2014\u2014\u8BF7\u624B\u52A8\u5B89\u88C5`);
42683
+ return false;
42684
+ }
42685
+ d.log(`[connector-install] ${cmd} \u7F3A\u5931\uFF0C\u5C1D\u8BD5\u81EA\u52A8\u5B89\u88C5\u2026`);
42686
+ for (const step of steps) {
42687
+ if (step.requires && !await d.hasCommand(step.requires)) continue;
42688
+ try {
42689
+ await d.run(step.file, step.args);
42690
+ if (await d.isPresent(cmd)) {
42691
+ confirmed.add(cmd);
42692
+ d.log(`[connector-install] ${cmd} \u5B89\u88C5\u6210\u529F\uFF08${step.file} ${step.args.join(" ")}\uFF09`);
42693
+ return true;
42694
+ }
42695
+ } catch (err) {
42696
+ d.log(`[connector-install] ${cmd} \u5B89\u88C5\u5C1D\u8BD5\u5931\u8D25\uFF08${step.file}\uFF09\uFF1A${String(err)}`);
42697
+ }
42698
+ }
42699
+ d.log(`[connector-install] ${cmd} \u81EA\u52A8\u5B89\u88C5\u672A\u6210\u529F\u2014\u2014\u5C06\u56DE\u9000\u5230 wrapper/\u547D\u4EE4\u81EA\u8EAB\u62A5\u9519`);
42700
+ return false;
42701
+ }
42702
+ async function ensureConnectorTools(tools, deps = {}) {
42703
+ if (!tools || tools.length === 0) return [];
42704
+ const missing = [];
42705
+ for (const cmd of tools) {
42706
+ if (!await ensureToolInstalled(cmd, deps)) missing.push(cmd);
42707
+ }
42708
+ return missing;
42709
+ }
42710
+
42711
+ // ../connectors/src/feishu/index.ts
42712
+ var import_node_child_process4 = require("node:child_process");
42713
+ var import_node_util2 = require("node:util");
42714
+ var import_node_path3 = require("node:path");
42715
+ var import_node_url = require("node:url");
42716
+ var execFileAsync2 = (0, import_node_util2.promisify)(import_node_child_process4.execFile);
42717
+ var __dirname = (0, import_node_path3.dirname)((0, import_node_url.fileURLToPath)(__esm_import_meta_url));
42499
42718
  var FeishuConnector = class {
42500
42719
  config = {
42501
42720
  slug: "feishu",
@@ -42504,6 +42723,10 @@ var FeishuConnector = class {
42504
42723
  { key: "app_id", description: "\u98DE\u4E66 App ID\uFF08\u7559\u7A7A\u5219\u81EA\u52A8\u521B\u5EFA\u65B0\u5E94\u7528\uFF09", secret: false, required: false }
42505
42724
  ]
42506
42725
  };
42726
+ /** wrapper 在 agent PATH 上冒名的命令——必须与真实 CLI 同名,否则遮蔽不生效。 */
42727
+ commandName = "lark-cli";
42728
+ /** node 侧需存在的真实 CLI(缺失则自动装:npm i -g @larksuite/cli,见 _base/install.ts)。 */
42729
+ requiredCommands = ["lark-cli"];
42507
42730
  larkBin;
42508
42731
  constructor(larkBin) {
42509
42732
  this.larkBin = larkBin ?? this.resolveLarkBin();
@@ -42520,17 +42743,17 @@ var FeishuConnector = class {
42520
42743
  const profileName = credentials["app_id"];
42521
42744
  if (!profileName) throw new Error("feishu inject: missing app_id in credentials");
42522
42745
  sessionEnv.set("LARK_PROFILE", profileName);
42523
- sessionEnv.set("LARK_BIN", this.larkBin);
42746
+ sessionEnv.set("LARK_BIN", await this.resolveRealBin());
42524
42747
  }
42525
42748
  wrapperScriptPath() {
42526
- return (0, import_node_path.join)(__dirname, "wrapper.sh");
42749
+ return (0, import_node_path3.join)(__dirname, "wrapper.sh");
42527
42750
  }
42528
42751
  async cleanup(sessionEnv) {
42529
42752
  const profile = sessionEnv.get("LARK_PROFILE");
42530
42753
  if (!profile) return;
42531
42754
  if (!process.env["FEISHU_SIMULATE"]) {
42532
42755
  try {
42533
- await execFileAsync(this.larkBin, ["profile", "remove", profile, "--confirm"]);
42756
+ await execFileAsync2(this.larkBin, ["profile", "remove", profile, "--confirm"]);
42534
42757
  } catch {
42535
42758
  }
42536
42759
  }
@@ -42539,18 +42762,18 @@ var FeishuConnector = class {
42539
42762
  }
42540
42763
  async initNewApp() {
42541
42764
  await new Promise((resolve3, reject) => {
42542
- const proc = (0, import_node_child_process3.spawn)(this.larkBin, ["config", "init", "--new"], { stdio: "inherit" });
42765
+ const proc = (0, import_node_child_process4.spawn)(this.larkBin, ["config", "init", "--new"], { stdio: "inherit" });
42543
42766
  proc.on("close", (code) => code === 0 ? resolve3() : reject(new Error(`lark-cli config init exited ${code}`)));
42544
42767
  proc.on("error", reject);
42545
42768
  });
42546
- const { stdout } = await execFileAsync(this.larkBin, ["config", "show", "--json"]);
42769
+ const { stdout } = await execFileAsync2(this.larkBin, ["config", "show", "--json"]);
42547
42770
  const cfg = JSON.parse(stdout);
42548
42771
  if (!cfg.appId) throw new Error("feishu connector: could not determine app_id after config init");
42549
42772
  return cfg.appId;
42550
42773
  }
42551
42774
  async runAuthLogin(app_id) {
42552
42775
  await new Promise((resolve3, reject) => {
42553
- const proc = (0, import_node_child_process3.spawn)(this.larkBin, ["--profile", app_id, "auth", "login"], { stdio: "inherit" });
42776
+ const proc = (0, import_node_child_process4.spawn)(this.larkBin, ["--profile", app_id, "auth", "login"], { stdio: "inherit" });
42554
42777
  proc.on("close", (code) => code === 0 ? resolve3() : reject(new Error(`lark-cli auth login exited ${code}`)));
42555
42778
  proc.on("error", reject);
42556
42779
  });
@@ -42558,15 +42781,31 @@ var FeishuConnector = class {
42558
42781
  resolveLarkBin() {
42559
42782
  return process.env["LARK_CLI_BIN"] ?? "lark-cli";
42560
42783
  }
42784
+ /**
42785
+ * 把 larkBin 解析成真实二进制的绝对路径,供 wrapper 的 LARK_BIN 使用。
42786
+ * 已是绝对路径直接返回;裸名("lark-cli")走 `which` 定位——绝不能把裸名交给 wrapper,
42787
+ * 否则 wrapper 的 exec 会命中 PATH 上的同名 wrapper 软链造成无限递归。which 失败时兜底原值,
42788
+ * 交由 wrapper 内 PATH 扫描逻辑(跳过自身)处理。
42789
+ */
42790
+ async resolveRealBin() {
42791
+ if (this.larkBin.includes("/")) return this.larkBin;
42792
+ try {
42793
+ const { stdout } = await execFileAsync2("which", [this.larkBin]);
42794
+ const resolved = stdout.trim();
42795
+ if (resolved) return resolved;
42796
+ } catch {
42797
+ }
42798
+ return this.larkBin;
42799
+ }
42561
42800
  };
42562
42801
 
42563
42802
  // ../connectors/src/github/index.ts
42564
- var import_node_child_process4 = require("node:child_process");
42565
- var import_node_util2 = require("node:util");
42566
- var import_node_path2 = require("node:path");
42803
+ var import_node_child_process5 = require("node:child_process");
42804
+ var import_node_util3 = require("node:util");
42805
+ var import_node_path4 = require("node:path");
42567
42806
  var import_node_url2 = require("node:url");
42568
- var execFile2 = (0, import_node_util2.promisify)(import_node_child_process4.execFile);
42569
- var __dirname2 = (0, import_node_path2.dirname)((0, import_node_url2.fileURLToPath)(__esm_import_meta_url));
42807
+ var execFile3 = (0, import_node_util3.promisify)(import_node_child_process5.execFile);
42808
+ var __dirname2 = (0, import_node_path4.dirname)((0, import_node_url2.fileURLToPath)(__esm_import_meta_url));
42570
42809
  var GithubConnector = class {
42571
42810
  config = {
42572
42811
  slug: "github",
@@ -42576,6 +42815,10 @@ var GithubConnector = class {
42576
42815
  { key: "actor_name", description: "AI \u5458\u5DE5\u540D\u79F0\uFF08\u7528\u4E8E git \u63D0\u4EA4\u7F72\u540D\uFF09", secret: false, required: false }
42577
42816
  ]
42578
42817
  };
42818
+ /** wrapper 在 agent PATH 上冒名的命令——必须与真实 CLI 同名,否则遮蔽不生效。 */
42819
+ commandName = "gh";
42820
+ /** node 侧需存在的真实 CLI(缺失则按 OS 自动装,见 _base/install.ts)。gh + git 都要。 */
42821
+ requiredCommands = ["gh", "git"];
42579
42822
  async connect(options = {}) {
42580
42823
  if (process.env["GITHUB_SIMULATE"] && options["access_token"]) {
42581
42824
  return { credentials: { access_token: options["access_token"] ?? "ghp_simulated", actor_name: options["actor_name"] ?? "oasis-agent" } };
@@ -42605,7 +42848,7 @@ var GithubConnector = class {
42605
42848
  sessionEnv.set("EMAIL", "");
42606
42849
  }
42607
42850
  wrapperScriptPath() {
42608
- return (0, import_node_path2.join)(__dirname2, "wrapper.sh");
42851
+ return (0, import_node_path4.join)(__dirname2, "wrapper.sh");
42609
42852
  }
42610
42853
  async cleanup(sessionEnv) {
42611
42854
  for (const k2 of [
@@ -42622,59 +42865,19 @@ var GithubConnector = class {
42622
42865
  }
42623
42866
  }
42624
42867
  // ── tool availability ────────────────────────────────────────────────────────
42868
+ /**
42869
+ * 确保 gh + git 存在(缺失则按 OS 装)。委托给共享 installer(_base/install.ts)——
42870
+ * 与 node 侧 pre-spawn 钩子同一套跨平台安装逻辑,不再各写一份。装不上则抛(connect() 语义要求可用)。
42871
+ */
42625
42872
  async ensureTools() {
42626
- const missing = [];
42627
- for (const tool of ["git", "gh"]) {
42628
- if (!await this.isAvailable(tool)) missing.push(tool);
42629
- }
42630
- if (missing.length === 0) return;
42631
- console.warn(`[github connector] missing tools: ${missing.join(", ")} \u2014 installing`);
42632
- await this.installTools(missing);
42633
- for (const tool of missing) {
42634
- if (!await this.isAvailable(tool))
42635
- throw new Error(`github connector: ${tool} could not be installed \u2014 install it manually`);
42636
- }
42637
- }
42638
- async isAvailable(tool) {
42639
- try {
42640
- await execFile2("which", [tool]);
42641
- return true;
42642
- } catch {
42643
- return false;
42644
- }
42645
- }
42646
- async installTools(tools) {
42647
- for (const tool of tools) {
42648
- try {
42649
- if (process.platform === "darwin") {
42650
- await execFile2("brew", ["install", tool === "gh" ? "gh" : "git"]);
42651
- } else {
42652
- if (tool === "git") {
42653
- await execFile2("sudo", ["apt-get", "install", "-y", "--no-install-recommends", "git"]);
42654
- } else {
42655
- try {
42656
- await execFile2("snap", ["install", "gh"]);
42657
- } catch {
42658
- await execFile2("bash", ["-c", [
42659
- "curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg",
42660
- "| sudo dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg",
42661
- "&& sudo chmod go+r /usr/share/keyrings/githubcli-archive-keyring.gpg",
42662
- '&& echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main"',
42663
- "| sudo tee /etc/apt/sources.list.d/github-cli.list > /dev/null",
42664
- "&& sudo apt-get update && sudo apt-get install -y gh"
42665
- ].join(" ")]);
42666
- }
42667
- }
42668
- }
42669
- console.warn(`[github connector] installed ${tool}`);
42670
- } catch (err) {
42671
- console.error(`[github connector] failed to install ${tool}:`, String(err));
42672
- }
42873
+ const missing = await ensureConnectorTools(this.requiredCommands);
42874
+ if (missing.length > 0) {
42875
+ throw new Error(`github connector: ${missing.join(", ")} could not be installed \u2014 install it manually`);
42673
42876
  }
42674
42877
  }
42675
42878
  async resolveRealBin(name) {
42676
42879
  try {
42677
- const { stdout } = await execFile2("which", [name]);
42880
+ const { stdout } = await execFile3("which", [name]);
42678
42881
  return stdout.trim();
42679
42882
  } catch {
42680
42883
  return name;
@@ -42683,12 +42886,12 @@ var GithubConnector = class {
42683
42886
  };
42684
42887
 
42685
42888
  // ../connectors/src/connector-adapter.ts
42686
- var import_promises = require("node:fs/promises");
42687
- var import_node_os = require("node:os");
42688
- var import_node_path3 = require("node:path");
42889
+ var import_promises2 = require("node:fs/promises");
42890
+ var import_node_os2 = require("node:os");
42891
+ var import_node_path5 = require("node:path");
42689
42892
 
42690
42893
  // ../adapters/src/claude-code/index.ts
42691
- var import_node_child_process5 = require("node:child_process");
42894
+ var import_node_child_process6 = require("node:child_process");
42692
42895
  var import_node_crypto14 = require("node:crypto");
42693
42896
  var fs9 = __toESM(require("node:fs"), 1);
42694
42897
  var os2 = __toESM(require("node:os"), 1);
@@ -42840,7 +43043,7 @@ var ClaudeCodeAdapter = class {
42840
43043
  ...this.opts.extraArgs ?? []
42841
43044
  ];
42842
43045
  const otelEnv = this.resolveOtelEnv(job, id);
42843
- const child = (0, import_node_child_process5.spawn)(this.opts.claudeBin ?? "claude", args, {
43046
+ const child = (0, import_node_child_process6.spawn)(this.opts.claudeBin ?? "claude", args, {
42844
43047
  cwd: dir,
42845
43048
  env: {
42846
43049
  // 凭证泄漏防护:剔除 pnpm/npm 脚本注入的变量——npm_lifecycle_script 等会把 serve 的启动命令
@@ -42962,7 +43165,7 @@ var ClaudeCodeAdapter = class {
42962
43165
  };
42963
43166
 
42964
43167
  // ../adapters/src/codex/index.ts
42965
- var import_node_child_process6 = require("node:child_process");
43168
+ var import_node_child_process7 = require("node:child_process");
42966
43169
  var import_node_crypto15 = require("node:crypto");
42967
43170
  var fs10 = __toESM(require("node:fs"), 1);
42968
43171
  var os3 = __toESM(require("node:os"), 1);
@@ -43261,7 +43464,7 @@ var CodexAdapter = class {
43261
43464
  ...this.opts.extraArgs ?? [],
43262
43465
  prompt
43263
43466
  ];
43264
- const child = (0, import_node_child_process6.spawn)(this.opts.codexBin ?? "codex", args, {
43467
+ const child = (0, import_node_child_process7.spawn)(this.opts.codexBin ?? "codex", args, {
43265
43468
  cwd: dir,
43266
43469
  env: {
43267
43470
  ...scrubLeakyEnv(process.env),
@@ -43362,7 +43565,7 @@ var CodexAdapter = class {
43362
43565
  };
43363
43566
 
43364
43567
  // ../adapters/src/_core/acp.ts
43365
- var import_node_child_process7 = require("node:child_process");
43568
+ var import_node_child_process8 = require("node:child_process");
43366
43569
  var import_node_crypto16 = require("node:crypto");
43367
43570
  var fs11 = __toESM(require("node:fs"), 1);
43368
43571
  var os4 = __toESM(require("node:os"), 1);
@@ -43785,7 +43988,7 @@ async function runACPSession(job, cfg) {
43785
43988
  }
43786
43989
  const task = job.bundle.files["TASK.md"];
43787
43990
  if (!task) throw new Error("bundle \u7F3A TASK.md\uFF08\u88C5\u914D\u5668\u5951\u7EA6\uFF09");
43788
- const child = (0, import_node_child_process7.spawn)(cfg.bin, [...cfg.args ?? [], ...cfg.extraArgs ?? []], {
43991
+ const child = (0, import_node_child_process8.spawn)(cfg.bin, [...cfg.args ?? [], ...cfg.extraArgs ?? []], {
43789
43992
  cwd: dir,
43790
43993
  env: {
43791
43994
  ...scrubLeakyEnv(process.env),
@@ -43991,7 +44194,7 @@ var KiroAdapter = class {
43991
44194
  };
43992
44195
 
43993
44196
  // ../adapters/src/_core/subprocess.ts
43994
- var import_node_child_process8 = require("node:child_process");
44197
+ var import_node_child_process9 = require("node:child_process");
43995
44198
  var import_node_crypto17 = require("node:crypto");
43996
44199
  var fs12 = __toESM(require("node:fs"), 1);
43997
44200
  var os5 = __toESM(require("node:os"), 1);
@@ -44050,7 +44253,7 @@ function makeFinish(id, cfg, transcriptRefPtr, transcriptOut, dir, exitCbs) {
44050
44253
  function runStreamJson(job, cfg) {
44051
44254
  const { id, dir, task: _task } = materialize(job, cfg.workRoot);
44052
44255
  const args = [...cfg.buildArgs(job, dir), ...cfg.extraArgs ?? []];
44053
- const child = (0, import_node_child_process8.spawn)(cfg.bin, args, {
44256
+ const child = (0, import_node_child_process9.spawn)(cfg.bin, args, {
44054
44257
  cwd: dir,
44055
44258
  env: buildEnv(job, cfg.env),
44056
44259
  stdio: ["ignore", "pipe", "pipe"]
@@ -44108,7 +44311,7 @@ function runStreamJson(job, cfg) {
44108
44311
  function runOneShotText(job, cfg) {
44109
44312
  const { id, dir } = materialize(job, cfg.workRoot);
44110
44313
  const args = [...cfg.buildArgs(job, dir), ...cfg.extraArgs ?? []];
44111
- const child = (0, import_node_child_process8.spawn)(cfg.bin, args, {
44314
+ const child = (0, import_node_child_process9.spawn)(cfg.bin, args, {
44112
44315
  cwd: dir,
44113
44316
  env: buildEnv(job, cfg.env),
44114
44317
  stdio: ["ignore", "pipe", "pipe"]
@@ -44616,6 +44819,10 @@ var ConnectorAwareAdapter = class {
44616
44819
  }
44617
44820
  async spawn(job) {
44618
44821
  const t0 = Date.now();
44822
+ if (job.requiredTools?.length) {
44823
+ const missing = await ensureConnectorTools(job.requiredTools);
44824
+ if (missing.length) log("[adapter]", ` connector tools still missing (agent may fail): ${missing.join(", ")}`);
44825
+ }
44619
44826
  const sentinelWrappers = Object.entries(job.env ?? {}).filter(([k2]) => k2.endsWith("_WRAPPER")).map(([, v2]) => v2);
44620
44827
  const wrapperPaths = [...job.wrapperPaths ?? [], ...sentinelWrappers];
44621
44828
  const strippedEnv = Object.fromEntries(
@@ -44623,11 +44830,11 @@ var ConnectorAwareAdapter = class {
44623
44830
  );
44624
44831
  let extraEnv = { ...strippedEnv };
44625
44832
  if (wrapperPaths.length > 0) {
44626
- const wrapperBinDir = await (0, import_promises.mkdtemp)((0, import_node_path3.join)((0, import_node_os.tmpdir)(), "oasis-wrappers-"));
44627
- await (0, import_promises.mkdir)(wrapperBinDir, { recursive: true });
44833
+ const wrapperBinDir = await (0, import_promises2.mkdtemp)((0, import_node_path5.join)((0, import_node_os2.tmpdir)(), "oasis-wrappers-"));
44834
+ await (0, import_promises2.mkdir)(wrapperBinDir, { recursive: true });
44628
44835
  for (const wp of wrapperPaths) {
44629
- const linkName = (0, import_node_path3.basename)(wp).replace(/\.sh$/, "");
44630
- await (0, import_promises.symlink)(wp, (0, import_node_path3.join)(wrapperBinDir, linkName));
44836
+ const linkName = (0, import_node_path5.basename)(wp).replace(/\.sh$/, "");
44837
+ await (0, import_promises2.symlink)(wp, (0, import_node_path5.join)(wrapperBinDir, linkName));
44631
44838
  }
44632
44839
  extraEnv = { ...extraEnv, PATH: `${wrapperBinDir}:${process.env["PATH"] ?? ""}` };
44633
44840
  log("[adapter]", ` connectors injected +${Date.now() - t0}ms wrappers=${wrapperPaths.length} env_keys=${Object.keys(job.env ?? {}).join(",")}`);
@@ -45156,6 +45363,9 @@ function defaultDocumentPath2(input) {
45156
45363
  return `${domain}/${workOrder}/${input.artifactId}.md`;
45157
45364
  }
45158
45365
 
45366
+ // ../storage/src/postgres.ts
45367
+ var import_node_crypto19 = require("node:crypto");
45368
+
45159
45369
  // ../../node_modules/.pnpm/pg@8.21.0/node_modules/pg/esm/index.mjs
45160
45370
  var import_lib = __toESM(require_lib2(), 1);
45161
45371
  var Client = import_lib.default.Client;
@@ -45173,9 +45383,551 @@ var esm_default = import_lib.default;
45173
45383
 
45174
45384
  // ../storage/src/postgres.ts
45175
45385
  init_src();
45386
+ var ident = (s2) => {
45387
+ if (!/^[a-z_][a-z0-9_]*$/.test(s2)) throw new Error(`invalid schema identifier: ${s2}`);
45388
+ return s2;
45389
+ };
45390
+ var isUniqueViolation = (err) => typeof err === "object" && err !== null && err.code === "23505";
45391
+ var PostgresOplogStore = class _PostgresOplogStore {
45392
+ constructor(pool, schema) {
45393
+ this.pool = pool;
45394
+ this.t = `"${ident(schema)}".oplog`;
45395
+ }
45396
+ t;
45397
+ static async open(pool, schema = "public") {
45398
+ const s2 = ident(schema);
45399
+ await pool.query(`CREATE SCHEMA IF NOT EXISTS "${s2}"`);
45400
+ await pool.query(`
45401
+ CREATE TABLE IF NOT EXISTS "${s2}".oplog (
45402
+ artifact_id text NOT NULL,
45403
+ seq integer NOT NULL,
45404
+ op jsonb NOT NULL,
45405
+ global_seq bigserial,
45406
+ appended_at timestamptz NOT NULL DEFAULT now(),
45407
+ PRIMARY KEY (artifact_id, seq)
45408
+ )`);
45409
+ await pool.query(`CREATE INDEX IF NOT EXISTS oplog_global_idx ON "${s2}".oplog (global_seq)`);
45410
+ return new _PostgresOplogStore(pool, schema);
45411
+ }
45412
+ async append(artifactId, op, expectedSeq) {
45413
+ for (let attempt = 0; ; attempt++) {
45414
+ const target = expectedSeq ?? await this.nextSeq(artifactId);
45415
+ try {
45416
+ const res = await this.pool.query(
45417
+ `INSERT INTO ${this.t} (artifact_id, seq, op)
45418
+ SELECT $1, $2::int, $3::jsonb
45419
+ WHERE (SELECT COALESCE(MAX(seq), 0) + 1 FROM ${this.t} WHERE artifact_id = $1) = $2::int`,
45420
+ [artifactId, target, JSON.stringify(op)]
45421
+ );
45422
+ if (res.rowCount === 1) return target;
45423
+ } catch (err) {
45424
+ if (!isUniqueViolation(err)) throw err;
45425
+ }
45426
+ if (expectedSeq !== void 0) {
45427
+ throw new SeqConflictError(artifactId, expectedSeq, await this.nextSeq(artifactId));
45428
+ }
45429
+ if (attempt >= 5) throw new Error(`oplog append contention on ${artifactId}`);
45430
+ }
45431
+ }
45432
+ async nextSeq(artifactId) {
45433
+ const res = await this.pool.query(
45434
+ `SELECT COALESCE(MAX(seq), 0) + 1 AS next FROM ${this.t} WHERE artifact_id = $1`,
45435
+ [artifactId]
45436
+ );
45437
+ return Number(res.rows[0].next);
45438
+ }
45439
+ async read(artifactId, fromSeq = 1) {
45440
+ const res = await this.pool.query(
45441
+ `SELECT seq, op FROM ${this.t} WHERE artifact_id = $1 AND seq >= $2 ORDER BY seq`,
45442
+ [artifactId, fromSeq]
45443
+ );
45444
+ return res.rows.map((row) => ({ ...row.op, artifactId, seq: Number(row.seq) }));
45445
+ }
45446
+ async *readAll(from) {
45447
+ yield* this.scan(from, false);
45448
+ }
45449
+ async *subscribe(from) {
45450
+ yield* this.scan(from, true);
45451
+ }
45452
+ async *scan(from, follow) {
45453
+ let cursor = from ?? "0";
45454
+ try {
45455
+ for (; ; ) {
45456
+ const res = await this.pool.query(
45457
+ `SELECT artifact_id, seq, op, global_seq FROM ${this.t}
45458
+ WHERE global_seq > $1::bigint ORDER BY global_seq LIMIT 500`,
45459
+ [cursor]
45460
+ );
45461
+ if (res.rowCount === 0) {
45462
+ if (!follow) return;
45463
+ await new Promise((resolve3) => setTimeout(resolve3, 120));
45464
+ continue;
45465
+ }
45466
+ for (const row of res.rows) {
45467
+ cursor = String(row.global_seq);
45468
+ yield {
45469
+ op: { ...row.op, artifactId: row.artifact_id, seq: Number(row.seq) },
45470
+ cursor
45471
+ };
45472
+ }
45473
+ }
45474
+ } catch (err) {
45475
+ if (follow) return;
45476
+ throw err;
45477
+ }
45478
+ }
45479
+ };
45480
+ var PostgresBlobStore = class _PostgresBlobStore {
45481
+ constructor(pool, schema) {
45482
+ this.pool = pool;
45483
+ this.t = `"${ident(schema)}".blobs`;
45484
+ }
45485
+ t;
45486
+ static async open(pool, schema = "public") {
45487
+ const s2 = ident(schema);
45488
+ await pool.query(`CREATE SCHEMA IF NOT EXISTS "${s2}"`);
45489
+ await pool.query(`
45490
+ CREATE TABLE IF NOT EXISTS "${s2}".blobs (
45491
+ hash text PRIMARY KEY,
45492
+ bytes bytea NOT NULL,
45493
+ size bigint NOT NULL,
45494
+ content_type text, -- \u5199\u5165\u65F6\u6309\u5B57\u8282\u55C5\u63A2\uFF1B\u55C5\u4E0D\u51FA\u4E3A NULL
45495
+ created_at timestamptz NOT NULL DEFAULT now()
45496
+ )`);
45497
+ await pool.query(`ALTER TABLE "${s2}".blobs ADD COLUMN IF NOT EXISTS content_type text`);
45498
+ await pool.query(`ALTER TABLE "${s2}".blobs ADD COLUMN IF NOT EXISTS created_at timestamptz NOT NULL DEFAULT now()`);
45499
+ return new _PostgresBlobStore(pool, schema);
45500
+ }
45501
+ async put(bytes) {
45502
+ const hash = (0, import_node_crypto19.createHash)("sha256").update(bytes).digest("hex");
45503
+ await this.pool.query(
45504
+ `INSERT INTO ${this.t} (hash, bytes, size, content_type) VALUES ($1, $2, $3, $4) ON CONFLICT (hash) DO NOTHING`,
45505
+ [hash, Buffer.from(bytes), bytes.byteLength, sniffContentType(bytes) ?? null]
45506
+ );
45507
+ return hash;
45508
+ }
45509
+ async meta(hash) {
45510
+ const res = await this.pool.query(`SELECT size, content_type FROM ${this.t} WHERE hash = $1`, [hash]);
45511
+ const row = res.rows[0];
45512
+ if (!row) return null;
45513
+ return { size: Number(row.size), ...row.content_type ? { contentType: row.content_type } : {} };
45514
+ }
45515
+ async get(hash) {
45516
+ const res = await this.pool.query(`SELECT bytes FROM ${this.t} WHERE hash = $1`, [hash]);
45517
+ if (res.rowCount === 0) throw new BlobNotFoundError(hash);
45518
+ return new Uint8Array(res.rows[0].bytes);
45519
+ }
45520
+ async has(hash) {
45521
+ const res = await this.pool.query(`SELECT 1 FROM ${this.t} WHERE hash = $1`, [hash]);
45522
+ return res.rowCount === 1;
45523
+ }
45524
+ async sweep(reachable) {
45525
+ await this.pool.query(`DELETE FROM ${this.t} WHERE NOT (hash = ANY($1::text[]))`, [
45526
+ [...reachable]
45527
+ ]);
45528
+ }
45529
+ };
45530
+
45531
+ // ../storage/src/postgres-registry.ts
45532
+ var ident2 = (s2) => {
45533
+ if (!/^[a-z_][a-z0-9_]*$/.test(s2)) throw new Error(`invalid schema name: ${s2}`);
45534
+ return s2;
45535
+ };
45536
+ var PostgresRegistryStore = class _PostgresRegistryStore {
45537
+ constructor(pool, schema) {
45538
+ this.pool = pool;
45539
+ this.s = `"${ident2(schema)}"`;
45540
+ }
45541
+ s;
45542
+ static async open(pool, schema = "public") {
45543
+ const s2 = ident2(schema);
45544
+ await pool.query(`CREATE SCHEMA IF NOT EXISTS "${s2}"`);
45545
+ await pool.query(`
45546
+ CREATE TABLE IF NOT EXISTS "${s2}".actors (
45547
+ id text PRIMARY KEY, -- "actor:human:<slug>" / "actor:agent:<slug>"
45548
+ kind text NOT NULL, -- human | agent
45549
+ name text NOT NULL,
45550
+ email text,
45551
+ team_id text,
45552
+ status text NOT NULL, -- active | disabled\uFF08\u5220\u9664\u5373\u505C\u7528\uFF09
45553
+ roles jsonb NOT NULL DEFAULT '[]'::jsonb, -- spec \xA76.2 \u540D\u518C\u89D2\u8272\uFF08\u4EFB\u52A1 2.0\uFF09
45554
+ avatar text, -- "blob:<hash>"\uFF0C\u7A7A=\u9ED8\u8BA4\u751F\u6210
45555
+ created_at timestamptz NOT NULL
45556
+ )`);
45557
+ await pool.query(`
45558
+ CREATE TABLE IF NOT EXISTS "${s2}".actor_configs (
45559
+ actor_id text NOT NULL,
45560
+ version integer NOT NULL, -- 1 \u8D77\u9012\u589E\uFF1B\u53EA\u589E\u4E0D\u6539\uFF0C\u56DE\u6EDA=\u590D\u5236\u65E7\u7248\u4E3A\u65B0\u7248
45561
+ prompt text NOT NULL,
45562
+ model text NOT NULL,
45563
+ skills jsonb NOT NULL,
45564
+ connector_ids jsonb NOT NULL,
45565
+ reviewer_ids jsonb NOT NULL DEFAULT '[]'::jsonb,
45566
+ max_concurrent integer NOT NULL DEFAULT 4,
45567
+ created_by text NOT NULL,
45568
+ created_at timestamptz NOT NULL,
45569
+ source text NOT NULL, -- patch | rollback
45570
+ rolled_back_from integer,
45571
+ PRIMARY KEY (actor_id, version)
45572
+ )`);
45573
+ await pool.query(`ALTER TABLE "${s2}".actor_configs ADD COLUMN IF NOT EXISTS max_concurrent integer NOT NULL DEFAULT 4`);
45574
+ await pool.query(`ALTER TABLE "${s2}".actor_configs ADD COLUMN IF NOT EXISTS reviewer_ids jsonb NOT NULL DEFAULT '[]'::jsonb`);
45575
+ await pool.query(`
45576
+ CREATE TABLE IF NOT EXISTS "${s2}".teams (
45577
+ id text PRIMARY KEY,
45578
+ name text NOT NULL,
45579
+ parent_id text, -- \u7EC4\u7EC7\u6811
45580
+ lead_actor_id text,
45581
+ icon text -- "blob:<hash>"\uFF0C\u7A7A=\u9ED8\u8BA4\u751F\u6210\uFF08\u4E0D\u7528 emoji\uFF09
45582
+ )`);
45583
+ await pool.query(`
45584
+ CREATE TABLE IF NOT EXISTS "${s2}".actor_bindings (
45585
+ actor_id text PRIMARY KEY, -- \u4E00\u5458\u5DE5\u4E00\u7ED1\u5B9A\uFF08\u51B3\u7B56 0001\uFF09
45586
+ node_id text NOT NULL,
45587
+ runtime_kind text NOT NULL,
45588
+ status text NOT NULL -- active | paused
45589
+ )`);
45590
+ await pool.query(`
45591
+ CREATE TABLE IF NOT EXISTS "${s2}".connectors (
45592
+ id text PRIMARY KEY,
45593
+ name text NOT NULL,
45594
+ mode text NOT NULL, -- oauth | direct
45595
+ status text NOT NULL,
45596
+ account text,
45597
+ verified_at timestamptz
45598
+ )`);
45599
+ await pool.query(`
45600
+ CREATE TABLE IF NOT EXISTS "${s2}".skill_catalog (
45601
+ id text PRIMARY KEY, -- slug
45602
+ name text NOT NULL,
45603
+ description text NOT NULL,
45604
+ category text NOT NULL,
45605
+ maker text NOT NULL,
45606
+ tags jsonb NOT NULL DEFAULT '[]'::jsonb,
45607
+ installs integer NOT NULL DEFAULT 0, -- \u5E02\u573A\u70ED\u5EA6(\u5C55\u793A)
45608
+ created_at timestamptz NOT NULL
45609
+ )`);
45610
+ await pool.query(`
45611
+ CREATE TABLE IF NOT EXISTS "${s2}".installed_skills (
45612
+ id text PRIMARY KEY, -- \u4E0E\u76EE\u5F55\u540C slug;\u81EA\u5B9A\u4E49\u4E3A\u81EA\u8D77 slug
45613
+ name text NOT NULL,
45614
+ description text NOT NULL,
45615
+ source text NOT NULL, -- market | custom | github
45616
+ enabled boolean NOT NULL DEFAULT true,
45617
+ installed_by text NOT NULL,
45618
+ installed_at timestamptz NOT NULL,
45619
+ source_url text -- \u5916\u90E8\u6765\u6E90\u539F\u59CB URL\uFF08\u5BFC\u5165\u65F6\u8BBE\u7F6E\uFF09
45620
+ )`);
45621
+ await pool.query(`ALTER TABLE "${s2}".installed_skills ADD COLUMN IF NOT EXISTS applied_to_all boolean NOT NULL DEFAULT false`);
45622
+ await pool.query(`
45623
+ CREATE TABLE IF NOT EXISTS "${s2}".skill_files (
45624
+ skill_id text NOT NULL,
45625
+ path text NOT NULL, -- \u76F8\u5BF9\u8DEF\u5F84\uFF0C\u5982 "SKILL.md"
45626
+ blob_hash text NOT NULL, -- BlobStore \u54C8\u5E0C\uFF08\u4E0D\u542B "blob:" \u524D\u7F00\uFF09
45627
+ size integer NOT NULL,
45628
+ updated_at timestamptz NOT NULL,
45629
+ PRIMARY KEY (skill_id, path)
45630
+ )`);
45631
+ await pool.query(`
45632
+ CREATE TABLE IF NOT EXISTS "${s2}".variables (
45633
+ key text NOT NULL,
45634
+ scope text NOT NULL,
45635
+ actor_id text,
45636
+ connector_id text,
45637
+ overrides text,
45638
+ value_encrypted text NOT NULL,
45639
+ updated_at timestamptz NOT NULL
45640
+ )`);
45641
+ await pool.query(`CREATE UNIQUE INDEX IF NOT EXISTS "${s2}_variables_key_actor_uq" ON "${s2}".variables (key, COALESCE(actor_id, ''))`);
45642
+ await pool.query(`ALTER TABLE IF EXISTS "${s2}".variables ADD COLUMN IF NOT EXISTS overrides text`);
45643
+ await pool.query(`ALTER TABLE IF EXISTS "${s2}".variables ADD COLUMN IF NOT EXISTS actor_id text`);
45644
+ return new _PostgresRegistryStore(pool, schema);
45645
+ }
45646
+ /* ---------- 员工 ---------- */
45647
+ async upsertActor(a) {
45648
+ await this.pool.query(
45649
+ `INSERT INTO ${this.s}.actors (id, kind, name, email, team_id, status, roles, avatar, created_at)
45650
+ VALUES ($1,$2,$3,$4,$5,$6,$7::jsonb,$8,$9)
45651
+ ON CONFLICT (id) DO UPDATE SET kind=$2, name=$3, email=$4, team_id=$5, status=$6, roles=$7::jsonb, avatar=$8`,
45652
+ [a.id, a.kind, a.name, a.email ?? null, a.teamId ?? null, a.status, JSON.stringify(a.roles), a.avatar ?? null, a.createdAt]
45653
+ );
45654
+ }
45655
+ async getActor(id) {
45656
+ const r = await this.pool.query(`SELECT * FROM ${this.s}.actors WHERE id = $1`, [id]);
45657
+ return r.rows[0] ? rowToActor(r.rows[0]) : null;
45658
+ }
45659
+ async listActors(filter) {
45660
+ const conds = [];
45661
+ const args = [];
45662
+ if (filter?.kind) {
45663
+ args.push(filter.kind);
45664
+ conds.push(`kind = $${args.length}`);
45665
+ }
45666
+ if (filter?.teamId) {
45667
+ args.push(filter.teamId);
45668
+ conds.push(`team_id = $${args.length}`);
45669
+ }
45670
+ if (filter?.status) {
45671
+ args.push(filter.status);
45672
+ conds.push(`status = $${args.length}`);
45673
+ }
45674
+ const where = conds.length ? `WHERE ${conds.join(" AND ")}` : "";
45675
+ const r = await this.pool.query(`SELECT * FROM ${this.s}.actors ${where} ORDER BY created_at`, args);
45676
+ return r.rows.map(rowToActor);
45677
+ }
45678
+ /* ---------- 配置版本 ---------- */
45679
+ async appendConfig(c) {
45680
+ const r = await this.pool.query(
45681
+ `INSERT INTO ${this.s}.actor_configs
45682
+ (actor_id, version, prompt, model, skills, connector_ids, reviewer_ids, max_concurrent, created_by, created_at, source, rolled_back_from)
45683
+ SELECT $1,$2::int,$3,$4,$5::jsonb,$6::jsonb,$7::jsonb,$8::int,$9,$10,$11,$12
45684
+ WHERE (SELECT COALESCE(MAX(version), 0) + 1 FROM ${this.s}.actor_configs WHERE actor_id = $1) = $2::int`,
45685
+ [
45686
+ c.actorId,
45687
+ c.version,
45688
+ c.prompt,
45689
+ c.model,
45690
+ JSON.stringify(c.skills),
45691
+ JSON.stringify(c.connectorIds),
45692
+ JSON.stringify(c.reviewerIds ?? []),
45693
+ c.maxConcurrent,
45694
+ c.createdBy,
45695
+ c.createdAt,
45696
+ c.source,
45697
+ c.rolledBackFrom ?? null
45698
+ ]
45699
+ );
45700
+ if (r.rowCount !== 1) throw new Error(`config version conflict for ${c.actorId}: got ${c.version}`);
45701
+ }
45702
+ async latestConfig(actorId) {
45703
+ const r = await this.pool.query(
45704
+ `SELECT * FROM ${this.s}.actor_configs WHERE actor_id = $1 ORDER BY version DESC LIMIT 1`,
45705
+ [actorId]
45706
+ );
45707
+ return r.rows[0] ? rowToConfig(r.rows[0]) : null;
45708
+ }
45709
+ async listConfigVersions(actorId) {
45710
+ const r = await this.pool.query(
45711
+ `SELECT * FROM ${this.s}.actor_configs WHERE actor_id = $1 ORDER BY version`,
45712
+ [actorId]
45713
+ );
45714
+ return r.rows.map(rowToConfig);
45715
+ }
45716
+ /* ---------- 团队 ---------- */
45717
+ async upsertTeam(t) {
45718
+ await this.pool.query(
45719
+ `INSERT INTO ${this.s}.teams (id, name, parent_id, lead_actor_id, icon) VALUES ($1,$2,$3,$4,$5)
45720
+ ON CONFLICT (id) DO UPDATE SET name=$2, parent_id=$3, lead_actor_id=$4, icon=$5`,
45721
+ [t.id, t.name, t.parentId ?? null, t.leadActorId ?? null, t.icon ?? null]
45722
+ );
45723
+ }
45724
+ async listTeams() {
45725
+ const r = await this.pool.query(`SELECT * FROM ${this.s}.teams ORDER BY id`);
45726
+ return r.rows.map((row) => ({
45727
+ id: row.id,
45728
+ name: row.name,
45729
+ ...row.parent_id !== null ? { parentId: row.parent_id } : {},
45730
+ ...row.lead_actor_id !== null ? { leadActorId: row.lead_actor_id } : {},
45731
+ ...row.icon !== null ? { icon: row.icon } : {}
45732
+ }));
45733
+ }
45734
+ /* ---------- 绑定 ---------- */
45735
+ async upsertBinding(b2) {
45736
+ await this.pool.query(
45737
+ `INSERT INTO ${this.s}.actor_bindings (actor_id, node_id, runtime_kind, status) VALUES ($1,$2,$3,$4)
45738
+ ON CONFLICT (actor_id) DO UPDATE SET node_id=$2, runtime_kind=$3, status=$4`,
45739
+ [b2.actorId, b2.nodeId, b2.runtimeKind, b2.status]
45740
+ );
45741
+ }
45742
+ async removeBinding(actorId) {
45743
+ await this.pool.query(`DELETE FROM ${this.s}.actor_bindings WHERE actor_id = $1`, [actorId]);
45744
+ }
45745
+ async getBinding(actorId) {
45746
+ const r = await this.pool.query(`SELECT * FROM ${this.s}.actor_bindings WHERE actor_id = $1`, [actorId]);
45747
+ const row = r.rows[0];
45748
+ return row ? { actorId: row.actor_id, nodeId: row.node_id, runtimeKind: row.runtime_kind, status: row.status } : null;
45749
+ }
45750
+ async listBindings() {
45751
+ const r = await this.pool.query(`SELECT * FROM ${this.s}.actor_bindings ORDER BY actor_id`);
45752
+ return r.rows.map((row) => ({ actorId: row.actor_id, nodeId: row.node_id, runtimeKind: row.runtime_kind, status: row.status }));
45753
+ }
45754
+ /* ---------- 连接器 ---------- */
45755
+ async upsertConnector(c) {
45756
+ await this.pool.query(
45757
+ `INSERT INTO ${this.s}.connectors (id, name, mode, status, account, verified_at) VALUES ($1,$2,$3,$4,$5,$6)
45758
+ ON CONFLICT (id) DO UPDATE SET name=$2, mode=$3, status=$4, account=$5, verified_at=$6`,
45759
+ [c.id, c.name, c.mode, c.status, c.account ?? null, c.verifiedAt ?? null]
45760
+ );
45761
+ }
45762
+ async listConnectors() {
45763
+ const r = await this.pool.query(`SELECT * FROM ${this.s}.connectors ORDER BY id`);
45764
+ return r.rows.map((row) => ({
45765
+ id: row.id,
45766
+ name: row.name,
45767
+ mode: row.mode,
45768
+ status: row.status,
45769
+ ...row.account !== null ? { account: row.account } : {},
45770
+ ...row.verified_at !== null ? { verifiedAt: new Date(row.verified_at).toISOString() } : {}
45771
+ }));
45772
+ }
45773
+ /* ---------- 技能 ---------- */
45774
+ async upsertSkillCatalogItem(item) {
45775
+ await this.pool.query(
45776
+ `INSERT INTO ${this.s}.skill_catalog (id, name, description, category, maker, tags, installs, created_at)
45777
+ VALUES ($1,$2,$3,$4,$5,$6::jsonb,$7,$8)
45778
+ ON CONFLICT (id) DO UPDATE SET name=$2, description=$3, category=$4, maker=$5, tags=$6::jsonb, installs=$7`,
45779
+ [item.id, item.name, item.description, item.category, item.maker, JSON.stringify(item.tags), item.installs, item.createdAt]
45780
+ );
45781
+ }
45782
+ async listSkillCatalog() {
45783
+ const r = await this.pool.query(`SELECT * FROM ${this.s}.skill_catalog ORDER BY installs DESC`);
45784
+ return r.rows.map((row) => ({
45785
+ id: row.id,
45786
+ name: row.name,
45787
+ description: row.description,
45788
+ category: row.category,
45789
+ maker: row.maker,
45790
+ tags: row.tags,
45791
+ installs: row.installs,
45792
+ createdAt: new Date(row.created_at).toISOString()
45793
+ }));
45794
+ }
45795
+ async putInstalledSkill(x2) {
45796
+ await this.pool.query(
45797
+ `INSERT INTO ${this.s}.installed_skills (id, name, description, source, enabled, installed_by, installed_at, source_url, applied_to_all)
45798
+ VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9)
45799
+ ON CONFLICT (id) DO UPDATE SET name=$2, description=$3, source=$4, enabled=$5, source_url=$8, applied_to_all=$9`,
45800
+ [x2.id, x2.name, x2.description, x2.source, x2.enabled, x2.installedBy, x2.installedAt, x2.sourceUrl ?? null, x2.appliedToAll ?? false]
45801
+ );
45802
+ }
45803
+ async removeInstalledSkill(id) {
45804
+ await this.pool.query(`DELETE FROM ${this.s}.installed_skills WHERE id = $1`, [id]);
45805
+ await this.pool.query(`DELETE FROM ${this.s}.skill_files WHERE skill_id = $1`, [id]);
45806
+ }
45807
+ async listInstalledSkills() {
45808
+ const r = await this.pool.query(`SELECT * FROM ${this.s}.installed_skills ORDER BY installed_at`);
45809
+ return r.rows.map((row) => ({
45810
+ id: row.id,
45811
+ name: row.name,
45812
+ description: row.description,
45813
+ source: row.source,
45814
+ enabled: row.enabled,
45815
+ installedBy: row.installed_by,
45816
+ installedAt: new Date(row.installed_at).toISOString(),
45817
+ ...row.source_url !== null ? { sourceUrl: row.source_url } : {},
45818
+ appliedToAll: !!row.applied_to_all
45819
+ }));
45820
+ }
45821
+ async putSkillFiles(skillId, files) {
45822
+ await this.pool.query(`DELETE FROM ${this.s}.skill_files WHERE skill_id = $1`, [skillId]);
45823
+ for (const f2 of files) {
45824
+ await this.pool.query(
45825
+ `INSERT INTO ${this.s}.skill_files (skill_id, path, blob_hash, size, updated_at)
45826
+ VALUES ($1,$2,$3,$4,$5)
45827
+ ON CONFLICT (skill_id, path) DO UPDATE SET blob_hash=$3, size=$4, updated_at=$5`,
45828
+ [skillId, f2.path, f2.blobHash, f2.size, f2.updatedAt]
45829
+ );
45830
+ }
45831
+ }
45832
+ async listSkillFiles(skillId) {
45833
+ const r = await this.pool.query(
45834
+ `SELECT * FROM ${this.s}.skill_files WHERE skill_id = $1 ORDER BY path`,
45835
+ [skillId]
45836
+ );
45837
+ return r.rows.map((row) => ({
45838
+ skillId: row.skill_id,
45839
+ path: row.path,
45840
+ blobHash: row.blob_hash,
45841
+ size: row.size,
45842
+ updatedAt: new Date(row.updated_at).toISOString()
45843
+ }));
45844
+ }
45845
+ async deleteSkillFiles(skillId) {
45846
+ await this.pool.query(`DELETE FROM ${this.s}.skill_files WHERE skill_id = $1`, [skillId]);
45847
+ }
45848
+ /* ---------- 变量 ---------- */
45849
+ async putVariable(v2) {
45850
+ const aid = v2.actorId ?? null;
45851
+ await this.pool.query(
45852
+ `INSERT INTO ${this.s}.variables (key, scope, actor_id, connector_id, overrides, value_encrypted, updated_at)
45853
+ VALUES ($1,$2,$3,$4,$5,$6,$7)
45854
+ ON CONFLICT (key, COALESCE(actor_id, '')) DO UPDATE
45855
+ SET scope=$2, actor_id=$3, connector_id=$4, overrides=$5, value_encrypted=$6, updated_at=$7`,
45856
+ [v2.key, v2.scope, aid, v2.connectorId ?? null, v2.overrides ?? null, v2.valueEncrypted, v2.updatedAt]
45857
+ );
45858
+ }
45859
+ async patchVariableValue(key, actorId, valueEncrypted, updatedAt) {
45860
+ const aid = actorId ?? null;
45861
+ await this.pool.query(
45862
+ `UPDATE ${this.s}.variables SET value_encrypted=$3, updated_at=$4
45863
+ WHERE key=$1 AND COALESCE(actor_id,'')=COALESCE($2::text,'')`,
45864
+ [key, aid, valueEncrypted, updatedAt]
45865
+ );
45866
+ }
45867
+ async getVariableCiphertext(key, actorId) {
45868
+ const aid = actorId ?? null;
45869
+ const r = await this.pool.query(
45870
+ `SELECT value_encrypted FROM ${this.s}.variables WHERE key=$1 AND COALESCE(actor_id,'')=COALESCE($2::text,'')`,
45871
+ [key, aid]
45872
+ );
45873
+ return r.rows[0]?.value_encrypted ?? null;
45874
+ }
45875
+ async listVariables(actorId) {
45876
+ let r;
45877
+ if (actorId !== void 0) {
45878
+ r = await this.pool.query(
45879
+ `SELECT * FROM ${this.s}.variables WHERE scope='global' OR actor_id=$1 ORDER BY key`,
45880
+ [actorId]
45881
+ );
45882
+ } else {
45883
+ r = await this.pool.query(`SELECT * FROM ${this.s}.variables ORDER BY key`);
45884
+ }
45885
+ return r.rows.map((row) => ({
45886
+ key: row.key,
45887
+ scope: row.scope === "connector" ? "global" : row.scope,
45888
+ ...row.actor_id !== null && row.actor_id !== void 0 ? { actorId: row.actor_id } : {},
45889
+ ...row.connector_id !== null ? { connectorId: row.connector_id } : {},
45890
+ ...row.overrides !== null ? { overrides: row.overrides } : {},
45891
+ valueEncrypted: row.value_encrypted,
45892
+ updatedAt: new Date(row.updated_at).toISOString()
45893
+ }));
45894
+ }
45895
+ async deleteVariable(key, actorId) {
45896
+ const aid = actorId ?? null;
45897
+ await this.pool.query(
45898
+ `DELETE FROM ${this.s}.variables WHERE key=$1 AND COALESCE(actor_id,'')=COALESCE($2::text,'')`,
45899
+ [key, aid]
45900
+ );
45901
+ }
45902
+ };
45903
+ var rowToActor = (row) => ({
45904
+ id: row.id,
45905
+ kind: row.kind,
45906
+ name: row.name,
45907
+ ...row.email !== null ? { email: row.email } : {},
45908
+ ...row.team_id !== null ? { teamId: row.team_id } : {},
45909
+ status: row.status,
45910
+ roles: row.roles ?? [],
45911
+ ...row.avatar !== null && row.avatar !== void 0 ? { avatar: row.avatar } : {},
45912
+ createdAt: new Date(row.created_at).toISOString()
45913
+ });
45914
+ var rowToConfig = (row) => ({
45915
+ actorId: row.actor_id,
45916
+ version: row.version,
45917
+ prompt: row.prompt,
45918
+ model: row.model,
45919
+ skills: row.skills,
45920
+ connectorIds: row.connector_ids,
45921
+ reviewerIds: row.reviewer_ids ?? [],
45922
+ maxConcurrent: row.max_concurrent ?? 4,
45923
+ createdBy: row.created_by,
45924
+ createdAt: new Date(row.created_at).toISOString(),
45925
+ source: row.source,
45926
+ ...row.rolled_back_from !== null ? { rolledBackFrom: row.rolled_back_from } : {}
45927
+ });
45176
45928
 
45177
45929
  // ../storage/src/postgres-projects.ts
45178
- var ident = (s2) => {
45930
+ var ident3 = (s2) => {
45179
45931
  if (!/^[a-z_][a-z0-9_]*$/.test(s2)) throw new Error(`invalid schema name: ${s2}`);
45180
45932
  return s2;
45181
45933
  };
@@ -45183,11 +45935,11 @@ var iso = (v2) => new Date(v2).toISOString();
45183
45935
  var PostgresProjectStateStore = class _PostgresProjectStateStore {
45184
45936
  constructor(pool, schema) {
45185
45937
  this.pool = pool;
45186
- this.s = `"${ident(schema)}"`;
45938
+ this.s = `"${ident3(schema)}"`;
45187
45939
  }
45188
45940
  s;
45189
45941
  static async open(pool, schema = "public") {
45190
- const s2 = ident(schema);
45942
+ const s2 = ident3(schema);
45191
45943
  await pool.query(`CREATE SCHEMA IF NOT EXISTS "${s2}"`);
45192
45944
  await pool.query(`
45193
45945
  CREATE TABLE IF NOT EXISTS "${s2}".projects (
@@ -45373,11 +46125,11 @@ var rowToProjectMember = (row) => ({
45373
46125
  var PostgresArtifactStateStore = class _PostgresArtifactStateStore {
45374
46126
  constructor(pool, schema) {
45375
46127
  this.pool = pool;
45376
- this.s = `"${ident(schema)}"`;
46128
+ this.s = `"${ident3(schema)}"`;
45377
46129
  }
45378
46130
  s;
45379
46131
  static async open(pool, schema = "public") {
45380
- const s2 = ident(schema);
46132
+ const s2 = ident3(schema);
45381
46133
  await pool.query(`CREATE SCHEMA IF NOT EXISTS "${s2}"`);
45382
46134
  await pool.query(`
45383
46135
  CREATE TABLE IF NOT EXISTS "${s2}".artifacts (
@@ -45953,20 +46705,285 @@ function createPgPool(dsn, opts) {
45953
46705
  });
45954
46706
  }
45955
46707
 
46708
+ // ../storage/src/postgres-control-plane.ts
46709
+ var ident4 = (s2) => {
46710
+ if (!/^[a-z_][a-z0-9_]*$/.test(s2)) throw new Error(`invalid schema name: ${s2}`);
46711
+ return s2;
46712
+ };
46713
+ var PostgresControlPlaneStore = class _PostgresControlPlaneStore {
46714
+ constructor(pool, schema) {
46715
+ this.pool = pool;
46716
+ this.s = `"${ident4(schema)}"`;
46717
+ }
46718
+ s;
46719
+ static async open(pool, schema = "public") {
46720
+ const s2 = ident4(schema);
46721
+ await pool.query(`CREATE SCHEMA IF NOT EXISTS "${s2}"`);
46722
+ await pool.query(`
46723
+ CREATE TABLE IF NOT EXISTS "${s2}".companies (
46724
+ id text PRIMARY KEY,
46725
+ slug text NOT NULL UNIQUE, -- \u4E0D\u53EF\u6539\uFF0C\u8DEF\u7531\u952E
46726
+ name text NOT NULL,
46727
+ avatar text, -- "blob:<hash>"\uFF0C\u7A7A=\u9ED8\u8BA4
46728
+ status text NOT NULL, -- active | suspended
46729
+ created_at timestamptz NOT NULL
46730
+ )`);
46731
+ await pool.query(`
46732
+ CREATE TABLE IF NOT EXISTS "${s2}".company_members (
46733
+ company_id text NOT NULL,
46734
+ account_id text NOT NULL,
46735
+ actor_id text NOT NULL, -- \u8BE5\u8D26\u53F7\u5728\u672C\u516C\u53F8\u7684\u5458\u5DE5\uFF08\u8FDB oplog \u7F72\u540D\uFF09
46736
+ role text NOT NULL, -- owner | admin | member
46737
+ PRIMARY KEY (company_id, account_id)
46738
+ )`);
46739
+ await pool.query(`CREATE INDEX IF NOT EXISTS company_members_account_idx ON "${s2}".company_members (account_id)`);
46740
+ await pool.query(`
46741
+ CREATE TABLE IF NOT EXISTS "${s2}".company_invitations (
46742
+ id text PRIMARY KEY,
46743
+ company_id text NOT NULL,
46744
+ email text NOT NULL,
46745
+ role text NOT NULL, -- admin | member\uFF08\u4E0D\u542B owner\uFF09
46746
+ status text NOT NULL, -- pending | accepted | declined | expired
46747
+ expires_at timestamptz NOT NULL
46748
+ )`);
46749
+ await pool.query(`CREATE INDEX IF NOT EXISTS company_invitations_email_idx ON "${s2}".company_invitations (email)`);
46750
+ await pool.query(`CREATE INDEX IF NOT EXISTS company_invitations_company_idx ON "${s2}".company_invitations (company_id)`);
46751
+ await pool.query(`
46752
+ CREATE TABLE IF NOT EXISTS "${s2}".accounts (
46753
+ id text PRIMARY KEY,
46754
+ email text NOT NULL,
46755
+ name text NOT NULL,
46756
+ avatar text,
46757
+ status text NOT NULL, -- active | disabled
46758
+ password_hash text, -- scrypt \u4E32\uFF1BNULL=\u672A\u8BBE\u5BC6\u7801\uFF08\u4EC5\u9A8C\u8BC1\u7801\u767B\u5F55\uFF09\u3002\u7EDD\u4E0D\u7ECF HTTP \u8FD4\u56DE
46759
+ token_version int NOT NULL DEFAULT 0 -- \u4F1A\u8BDD\u603B\u95F8\uFF1Abump \u5373\u4F5C\u5E9F\u8BE5\u8D26\u53F7\u6240\u6709 oasis_auth
46760
+ )`);
46761
+ await pool.query(`CREATE INDEX IF NOT EXISTS accounts_email_idx ON "${s2}".accounts (email)`);
46762
+ await pool.query(`
46763
+ CREATE TABLE IF NOT EXISTS "${s2}".verification_codes (
46764
+ email text PRIMARY KEY, -- \u5355\u90AE\u7BB1\u4E00\u6761 active
46765
+ code_hash text NOT NULL,
46766
+ created_at timestamptz NOT NULL,
46767
+ expires_at timestamptz NOT NULL,
46768
+ attempts int NOT NULL DEFAULT 0
46769
+ )`);
46770
+ return new _PostgresControlPlaneStore(pool, schema);
46771
+ }
46772
+ // —— 公司 ——
46773
+ async createCompany(c) {
46774
+ await this.pool.query(
46775
+ `INSERT INTO ${this.s}.companies (id, slug, name, avatar, status, created_at) VALUES ($1,$2,$3,$4,$5,$6)`,
46776
+ [c.id, c.slug, c.name, c.avatar ?? null, c.status, c.createdAt]
46777
+ );
46778
+ }
46779
+ async getCompany(id) {
46780
+ const r = await this.pool.query(`SELECT * FROM ${this.s}.companies WHERE id = $1`, [id]);
46781
+ return r.rows[0] ? rowToCompany(r.rows[0]) : null;
46782
+ }
46783
+ async getCompanyBySlug(slug4) {
46784
+ const r = await this.pool.query(`SELECT * FROM ${this.s}.companies WHERE slug = $1`, [slug4]);
46785
+ return r.rows[0] ? rowToCompany(r.rows[0]) : null;
46786
+ }
46787
+ async listCompanies() {
46788
+ const r = await this.pool.query(`SELECT * FROM ${this.s}.companies ORDER BY created_at`);
46789
+ return r.rows.map(rowToCompany);
46790
+ }
46791
+ async updateCompany(id, patch) {
46792
+ const sets = [];
46793
+ const args = [];
46794
+ if (patch.name !== void 0) {
46795
+ args.push(patch.name);
46796
+ sets.push(`name = $${args.length}`);
46797
+ }
46798
+ if (patch.avatar !== void 0) {
46799
+ args.push(patch.avatar);
46800
+ sets.push(`avatar = $${args.length}`);
46801
+ }
46802
+ if (patch.status !== void 0) {
46803
+ args.push(patch.status);
46804
+ sets.push(`status = $${args.length}`);
46805
+ }
46806
+ args.push(id);
46807
+ const sql = sets.length ? `UPDATE ${this.s}.companies SET ${sets.join(", ")} WHERE id = $${args.length}` : `SELECT 1 FROM ${this.s}.companies WHERE id = $${args.length}`;
46808
+ const r = await this.pool.query(sql, args);
46809
+ if (r.rowCount === 0) throw new Error(`company not found: ${id}`);
46810
+ }
46811
+ // —— 成员 ——
46812
+ async upsertMember(m2) {
46813
+ await this.pool.query(
46814
+ `INSERT INTO ${this.s}.company_members (company_id, account_id, actor_id, role) VALUES ($1,$2,$3,$4)
46815
+ ON CONFLICT (company_id, account_id) DO UPDATE SET actor_id = $3, role = $4`,
46816
+ [m2.companyId, m2.accountId, m2.actorId, m2.role]
46817
+ );
46818
+ }
46819
+ async getMember(companyId, accountId) {
46820
+ const r = await this.pool.query(
46821
+ `SELECT * FROM ${this.s}.company_members WHERE company_id = $1 AND account_id = $2`,
46822
+ [companyId, accountId]
46823
+ );
46824
+ return r.rows[0] ? rowToMember(r.rows[0]) : null;
46825
+ }
46826
+ async listMembers(companyId) {
46827
+ const r = await this.pool.query(`SELECT * FROM ${this.s}.company_members WHERE company_id = $1`, [companyId]);
46828
+ return r.rows.map(rowToMember);
46829
+ }
46830
+ async listMembershipsForAccount(accountId) {
46831
+ const r = await this.pool.query(`SELECT * FROM ${this.s}.company_members WHERE account_id = $1`, [accountId]);
46832
+ return r.rows.map(rowToMember);
46833
+ }
46834
+ async removeMember(companyId, accountId) {
46835
+ await this.pool.query(`DELETE FROM ${this.s}.company_members WHERE company_id = $1 AND account_id = $2`, [companyId, accountId]);
46836
+ }
46837
+ // —— 邀请 ——
46838
+ async createInvitation(inv) {
46839
+ await this.pool.query(
46840
+ `INSERT INTO ${this.s}.company_invitations (id, company_id, email, role, status, expires_at) VALUES ($1,$2,$3,$4,$5,$6)`,
46841
+ [inv.id, inv.companyId, inv.email, inv.role, inv.status, inv.expiresAt]
46842
+ );
46843
+ }
46844
+ async getInvitation(id) {
46845
+ const r = await this.pool.query(`SELECT * FROM ${this.s}.company_invitations WHERE id = $1`, [id]);
46846
+ return r.rows[0] ? rowToInvitation(r.rows[0]) : null;
46847
+ }
46848
+ async listInvitations(filter) {
46849
+ const conds = [];
46850
+ const args = [];
46851
+ if (filter?.companyId !== void 0) {
46852
+ args.push(filter.companyId);
46853
+ conds.push(`company_id = $${args.length}`);
46854
+ }
46855
+ if (filter?.email !== void 0) {
46856
+ args.push(filter.email);
46857
+ conds.push(`email = $${args.length}`);
46858
+ }
46859
+ if (filter?.status !== void 0) {
46860
+ args.push(filter.status);
46861
+ conds.push(`status = $${args.length}`);
46862
+ }
46863
+ const where = conds.length ? `WHERE ${conds.join(" AND ")}` : "";
46864
+ const r = await this.pool.query(`SELECT * FROM ${this.s}.company_invitations ${where} ORDER BY id`, args);
46865
+ return r.rows.map(rowToInvitation);
46866
+ }
46867
+ async updateInvitationStatus(id, status) {
46868
+ const r = await this.pool.query(`UPDATE ${this.s}.company_invitations SET status = $2 WHERE id = $1`, [id, status]);
46869
+ if (r.rowCount === 0) throw new Error(`invitation not found: ${id}`);
46870
+ }
46871
+ // —— 账号 ——
46872
+ async upsertAccount(a) {
46873
+ await this.pool.query(
46874
+ `INSERT INTO ${this.s}.accounts (id, email, name, avatar, status) VALUES ($1,$2,$3,$4,$5)
46875
+ ON CONFLICT (id) DO UPDATE SET email = $2, name = $3, avatar = $4, status = $5`,
46876
+ [a.id, a.email, a.name, a.avatar ?? null, a.status]
46877
+ );
46878
+ }
46879
+ async getAccount(id) {
46880
+ const r = await this.pool.query(`SELECT * FROM ${this.s}.accounts WHERE id = $1`, [id]);
46881
+ return r.rows[0] ? rowToAccount(r.rows[0]) : null;
46882
+ }
46883
+ async getAccountByEmail(email2) {
46884
+ const r = await this.pool.query(`SELECT * FROM ${this.s}.accounts WHERE email = $1 LIMIT 1`, [email2]);
46885
+ return r.rows[0] ? rowToAccount(r.rows[0]) : null;
46886
+ }
46887
+ async listAccounts() {
46888
+ const r = await this.pool.query(`SELECT * FROM ${this.s}.accounts ORDER BY id`);
46889
+ return r.rows.map(rowToAccount);
46890
+ }
46891
+ // —— 账号密钥(绝不经 HTTP 返回)——
46892
+ async setAccountPassword(accountId, passwordHash) {
46893
+ const r = await this.pool.query(`UPDATE ${this.s}.accounts SET password_hash = $2 WHERE id = $1`, [accountId, passwordHash]);
46894
+ if (r.rowCount === 0) throw new Error(`account not found: ${accountId}`);
46895
+ }
46896
+ async getAccountSecret(accountId) {
46897
+ const r = await this.pool.query(`SELECT password_hash, token_version FROM ${this.s}.accounts WHERE id = $1`, [accountId]);
46898
+ const row = r.rows[0];
46899
+ if (!row) return null;
46900
+ return {
46901
+ tokenVersion: Number(row.token_version),
46902
+ ...row.password_hash !== null && row.password_hash !== void 0 ? { passwordHash: row.password_hash } : {}
46903
+ };
46904
+ }
46905
+ async bumpAccountTokenVersion(accountId) {
46906
+ const r = await this.pool.query(
46907
+ `UPDATE ${this.s}.accounts SET token_version = token_version + 1 WHERE id = $1 RETURNING token_version`,
46908
+ [accountId]
46909
+ );
46910
+ if (r.rowCount === 0) throw new Error(`account not found: ${accountId}`);
46911
+ return Number(r.rows[0].token_version);
46912
+ }
46913
+ // —— 邮箱验证码(单邮箱一条 active)——
46914
+ async putVerificationCode(rec) {
46915
+ await this.pool.query(
46916
+ `INSERT INTO ${this.s}.verification_codes (email, code_hash, created_at, expires_at, attempts) VALUES ($1,$2,$3,$4,$5)
46917
+ ON CONFLICT (email) DO UPDATE SET code_hash = $2, created_at = $3, expires_at = $4, attempts = $5`,
46918
+ [rec.email, rec.codeHash, rec.createdAt, rec.expiresAt, rec.attempts]
46919
+ );
46920
+ }
46921
+ async getVerificationCode(email2) {
46922
+ const r = await this.pool.query(`SELECT * FROM ${this.s}.verification_codes WHERE email = $1`, [email2]);
46923
+ return r.rows[0] ? rowToVerificationCode(r.rows[0]) : null;
46924
+ }
46925
+ async incrementCodeAttempts(email2) {
46926
+ const r = await this.pool.query(
46927
+ `UPDATE ${this.s}.verification_codes SET attempts = attempts + 1 WHERE email = $1 RETURNING attempts`,
46928
+ [email2]
46929
+ );
46930
+ return r.rows[0] ? Number(r.rows[0].attempts) : 0;
46931
+ }
46932
+ async deleteVerificationCode(email2) {
46933
+ await this.pool.query(`DELETE FROM ${this.s}.verification_codes WHERE email = $1`, [email2]);
46934
+ }
46935
+ };
46936
+ var rowToCompany = (row) => ({
46937
+ id: row.id,
46938
+ slug: row.slug,
46939
+ name: row.name,
46940
+ ...row.avatar !== null && row.avatar !== void 0 ? { avatar: row.avatar } : {},
46941
+ status: row.status,
46942
+ createdAt: new Date(row.created_at).toISOString()
46943
+ });
46944
+ var rowToMember = (row) => ({
46945
+ companyId: row.company_id,
46946
+ accountId: row.account_id,
46947
+ actorId: row.actor_id,
46948
+ role: row.role
46949
+ });
46950
+ var rowToInvitation = (row) => ({
46951
+ id: row.id,
46952
+ companyId: row.company_id,
46953
+ email: row.email,
46954
+ role: row.role,
46955
+ status: row.status,
46956
+ expiresAt: new Date(row.expires_at).toISOString()
46957
+ });
46958
+ var rowToAccount = (row) => ({
46959
+ id: row.id,
46960
+ email: row.email,
46961
+ name: row.name,
46962
+ ...row.avatar !== null && row.avatar !== void 0 ? { avatar: row.avatar } : {},
46963
+ status: row.status
46964
+ });
46965
+ var rowToVerificationCode = (row) => ({
46966
+ email: row.email,
46967
+ codeHash: row.code_hash,
46968
+ createdAt: new Date(row.created_at).toISOString(),
46969
+ expiresAt: new Date(row.expires_at).toISOString(),
46970
+ attempts: Number(row.attempts)
46971
+ });
46972
+
45956
46973
  // ../storage/src/postgres-trace.ts
45957
46974
  init_src();
45958
- var ident2 = (s2) => {
46975
+ var ident5 = (s2) => {
45959
46976
  if (!/^[a-z_][a-z0-9_]*$/.test(s2)) throw new Error(`invalid schema name: ${s2}`);
45960
46977
  return s2;
45961
46978
  };
45962
46979
  var PostgresTraceStore = class _PostgresTraceStore {
45963
46980
  constructor(pool, schema) {
45964
46981
  this.pool = pool;
45965
- this.s = `"${ident2(schema)}"`;
46982
+ this.s = `"${ident5(schema)}"`;
45966
46983
  }
45967
46984
  s;
45968
46985
  static async open(pool, schema = "public") {
45969
- const s2 = ident2(schema);
46986
+ const s2 = ident5(schema);
45970
46987
  await pool.query(`CREATE SCHEMA IF NOT EXISTS "${s2}"`);
45971
46988
  await pool.query(`
45972
46989
  CREATE TABLE IF NOT EXISTS "${s2}".agent_runs (
@@ -46518,7 +47535,7 @@ var rowToArtifact2 = (row) => ({
46518
47535
 
46519
47536
  // ../storage/src/postgres-automations.ts
46520
47537
  init_src();
46521
- var ident3 = (s2) => {
47538
+ var ident6 = (s2) => {
46522
47539
  if (!/^[a-z_][a-z0-9_]*$/.test(s2)) throw new Error(`invalid schema name: ${s2}`);
46523
47540
  return s2;
46524
47541
  };
@@ -46526,11 +47543,11 @@ var genId = (prefix) => `${prefix}_${Math.random().toString(36).slice(2, 12)}`;
46526
47543
  var PostgresAutomationStore = class _PostgresAutomationStore {
46527
47544
  constructor(pool, schema) {
46528
47545
  this.pool = pool;
46529
- this.s = `"${ident3(schema)}"`;
47546
+ this.s = `"${ident6(schema)}"`;
46530
47547
  }
46531
47548
  s;
46532
47549
  static async open(pool, schema = "public") {
46533
- const s2 = ident3(schema);
47550
+ const s2 = ident6(schema);
46534
47551
  await pool.query(`CREATE SCHEMA IF NOT EXISTS "${s2}"`);
46535
47552
  await pool.query(`
46536
47553
  CREATE TABLE IF NOT EXISTS "${s2}".automations (
@@ -46825,18 +47842,18 @@ var rowToRun2 = (row) => ({
46825
47842
  });
46826
47843
 
46827
47844
  // ../storage/src/postgres-chat-sessions.ts
46828
- var ident4 = (s2) => {
47845
+ var ident7 = (s2) => {
46829
47846
  if (!/^[a-z_][a-z0-9_]*$/.test(s2)) throw new Error(`invalid schema name: ${s2}`);
46830
47847
  return s2;
46831
47848
  };
46832
47849
  var PostgresChatSessionStore = class _PostgresChatSessionStore {
46833
47850
  constructor(pool, schema) {
46834
47851
  this.pool = pool;
46835
- this.s = `"${ident4(schema)}"`;
47852
+ this.s = `"${ident7(schema)}"`;
46836
47853
  }
46837
47854
  s;
46838
47855
  static async open(pool, schema = "public") {
46839
- const s2 = ident4(schema);
47856
+ const s2 = ident7(schema);
46840
47857
  await pool.query(`CREATE SCHEMA IF NOT EXISTS "${s2}"`);
46841
47858
  await pool.query(`
46842
47859
  CREATE TABLE IF NOT EXISTS "${s2}".chat_sessions (
@@ -46862,6 +47879,14 @@ var PostgresChatSessionStore = class _PostgresChatSessionStore {
46862
47879
  )`);
46863
47880
  await pool.query(`CREATE INDEX IF NOT EXISTS chat_messages_session_idx ON "${s2}".chat_messages (session_id, seq)`);
46864
47881
  await pool.query(`ALTER TABLE "${s2}".chat_messages ADD COLUMN IF NOT EXISTS run_id text`);
47882
+ await pool.query(`ALTER TABLE "${s2}".chat_messages ADD COLUMN IF NOT EXISTS parts jsonb`);
47883
+ await pool.query(`
47884
+ WITH ranked AS (
47885
+ SELECT id, row_number() OVER (PARTITION BY session_id ORDER BY seq, created_at, id) AS rn
47886
+ FROM "${s2}".chat_messages
47887
+ )
47888
+ UPDATE "${s2}".chat_messages m SET seq = ranked.rn FROM ranked WHERE m.id = ranked.id AND m.seq <> ranked.rn`);
47889
+ await pool.query(`CREATE UNIQUE INDEX IF NOT EXISTS chat_messages_session_seq_uq ON "${s2}".chat_messages (session_id, seq)`);
46865
47890
  await pool.query(`
46866
47891
  CREATE TABLE IF NOT EXISTS "${s2}".chat_session_work_orders (
46867
47892
  session_id text NOT NULL,
@@ -46917,11 +47942,25 @@ var PostgresChatSessionStore = class _PostgresChatSessionStore {
46917
47942
  await this.pool.query(`DELETE FROM ${this.s}.chat_sessions WHERE id = $1`, [id]);
46918
47943
  }
46919
47944
  async appendMessage(m2) {
46920
- await this.pool.query(
46921
- `INSERT INTO ${this.s}.chat_messages (id, session_id, role, content, seq, created_at, run_id)
46922
- VALUES ($1,$2,$3,$4,$5,$6,$7)`,
46923
- [m2.id, m2.sessionId, m2.role, m2.content, m2.seq, m2.createdAt, m2.runId ?? null]
46924
- );
47945
+ const insert = async () => {
47946
+ const r = await this.pool.query(
47947
+ `INSERT INTO ${this.s}.chat_messages (id, session_id, role, content, seq, created_at, run_id, parts)
47948
+ SELECT $1,$2,$3,$4, COALESCE(MAX(seq),0)+1, $5,$6,$7
47949
+ FROM ${this.s}.chat_messages WHERE session_id = $2
47950
+ RETURNING seq`,
47951
+ [m2.id, m2.sessionId, m2.role, m2.content, m2.createdAt, m2.runId ?? null, m2.parts !== void 0 ? JSON.stringify(m2.parts) : null]
47952
+ );
47953
+ return Number(r.rows[0].seq);
47954
+ };
47955
+ for (let attempt = 0; ; attempt++) {
47956
+ try {
47957
+ return { ...m2, seq: await insert() };
47958
+ } catch (err) {
47959
+ const isUniqueViolation2 = err && typeof err === "object" && err.code === "23505";
47960
+ if (isUniqueViolation2 && attempt < 10) continue;
47961
+ throw err;
47962
+ }
47963
+ }
46925
47964
  }
46926
47965
  async listMessages(sessionId, limit) {
46927
47966
  const limitClause = limit ? `LIMIT ${limit}` : "";
@@ -46963,23 +48002,24 @@ var rowToMessage = (row) => ({
46963
48002
  content: row.content,
46964
48003
  seq: Number(row.seq),
46965
48004
  createdAt: new Date(row.created_at).toISOString(),
46966
- ...row.run_id != null ? { runId: row.run_id } : {}
48005
+ ...row.run_id != null ? { runId: row.run_id } : {},
48006
+ ...row.parts != null ? { parts: row.parts } : {}
46967
48007
  });
46968
48008
 
46969
48009
  // ../storage/src/postgres-nodes.ts
46970
- var import_node_crypto19 = require("node:crypto");
46971
- var ident5 = (s2) => {
48010
+ var import_node_crypto20 = require("node:crypto");
48011
+ var ident8 = (s2) => {
46972
48012
  if (!/^[a-z_][a-z0-9_]*$/.test(s2)) throw new Error(`invalid schema name: ${s2}`);
46973
48013
  return s2;
46974
48014
  };
46975
48015
  var PostgresNodeStore = class _PostgresNodeStore {
46976
48016
  constructor(pool, schema) {
46977
48017
  this.pool = pool;
46978
- this.s = `"${ident5(schema)}"`;
48018
+ this.s = `"${ident8(schema)}"`;
46979
48019
  }
46980
48020
  s;
46981
48021
  static async open(pool, schema = "public") {
46982
- const s2 = ident5(schema);
48022
+ const s2 = ident8(schema);
46983
48023
  await pool.query(`CREATE SCHEMA IF NOT EXISTS "${s2}"`);
46984
48024
  await pool.query(`
46985
48025
  CREATE TABLE IF NOT EXISTS "${s2}".nodes (
@@ -47086,12 +48126,12 @@ var PostgresNodeStore = class _PostgresNodeStore {
47086
48126
  var PostgresNodeTokenStore = class _PostgresNodeTokenStore {
47087
48127
  constructor(pool, schema) {
47088
48128
  this.pool = pool;
47089
- this.s = `"${ident5(schema)}"`;
48129
+ this.s = `"${ident8(schema)}"`;
47090
48130
  }
47091
48131
  s;
47092
48132
  cache = /* @__PURE__ */ new Map();
47093
48133
  static async open(pool, schema = "public") {
47094
- const s2 = ident5(schema);
48134
+ const s2 = ident8(schema);
47095
48135
  await pool.query(`CREATE SCHEMA IF NOT EXISTS "${s2}"`);
47096
48136
  await pool.query(`CREATE TABLE IF NOT EXISTS "${s2}".node_tokens (token text PRIMARY KEY, node_id text NOT NULL)`);
47097
48137
  const store = new _PostgresNodeTokenStore(pool, schema);
@@ -47100,7 +48140,7 @@ var PostgresNodeTokenStore = class _PostgresNodeTokenStore {
47100
48140
  return store;
47101
48141
  }
47102
48142
  issue(nodeId) {
47103
- const token = `ont_${(0, import_node_crypto19.randomBytes)(24).toString("base64url")}`;
48143
+ const token = `ont_${(0, import_node_crypto20.randomBytes)(24).toString("base64url")}`;
47104
48144
  this.cache.set(token, nodeId);
47105
48145
  void this.pool.query(`INSERT INTO ${this.s}.node_tokens (token,node_id) VALUES ($1,$2)`, [token, nodeId]);
47106
48146
  return token;
@@ -47139,7 +48179,16 @@ var rowToRuntime = (row) => ({
47139
48179
  // ../cli/src/serve.ts
47140
48180
  init_src3();
47141
48181
  init_trajectory();
47142
- var DEFAULT_CHAT_SKILLS = ["build-workorder"];
48182
+ function builtinSkillsDir() {
48183
+ const override = process.env["OASIS_BUILTIN_SKILLS_DIR"]?.trim();
48184
+ if (override) return override;
48185
+ const repoRoot = (0, import_node_url3.fileURLToPath)(new URL("../../../", __esm_import_meta_url));
48186
+ return path11.join(repoRoot, "skills");
48187
+ }
48188
+ function materializeBuiltins(builtins, runtimeKind) {
48189
+ if (builtins.length === 0) return {};
48190
+ return materializeBuiltinSkills(builtins, skillsDirForRuntime(runtimeKind));
48191
+ }
47143
48192
  var SESSION_TOKEN_GRACE_MS = 15 * 6e4;
47144
48193
  function normalizeHttpBaseUrl(raw) {
47145
48194
  const url = new URL(raw);
@@ -47195,13 +48244,16 @@ async function buildActorProvision(service, actorId) {
47195
48244
  const raw = await service.resolveActorEnv(actorId);
47196
48245
  const env = { ...raw };
47197
48246
  const wrapperPaths = [oasisWrapperScript()];
48247
+ const requiredTools = /* @__PURE__ */ new Set();
47198
48248
  if (raw["FEISHU_APP_ID"] && raw["FEISHU_APP_SECRET"]) {
47199
48249
  try {
47200
48250
  const feishu = new FeishuConnector();
47201
48251
  const sessionEnv = /* @__PURE__ */ new Map();
47202
48252
  await feishu.inject({ app_id: raw["FEISHU_APP_ID"], app_secret: raw["FEISHU_APP_SECRET"] }, sessionEnv);
47203
48253
  for (const [k2, v2] of sessionEnv) env[k2] = v2;
47204
- wrapperPaths.push(feishu.wrapperScriptPath());
48254
+ const staged = stageConnectorWrapper(feishu);
48255
+ if (staged) wrapperPaths.push(staged);
48256
+ for (const c of feishu.requiredCommands) requiredTools.add(c);
47205
48257
  } catch (err) {
47206
48258
  console.warn(`[serve] feishu connector setup failed for ${actorId}:`, String(err));
47207
48259
  }
@@ -47213,13 +48265,14 @@ async function buildActorProvision(service, actorId) {
47213
48265
  await github.inject({ access_token: raw["GITHUB_TOKEN"], actor_name: raw["GIT_AUTHOR_NAME"] ?? actorId.split(":").pop() ?? "oasis-agent" }, sessionEnv);
47214
48266
  for (const [k2, v2] of sessionEnv) env[k2] = v2;
47215
48267
  delete env["GITHUB_TOKEN"];
47216
- const wp = github.wrapperScriptPath();
47217
- if (wp) wrapperPaths.push(wp);
48268
+ const staged = stageConnectorWrapper(github);
48269
+ if (staged) wrapperPaths.push(staged);
48270
+ for (const c of github.requiredCommands) requiredTools.add(c);
47218
48271
  } catch (err) {
47219
48272
  console.warn(`[serve] github connector setup failed for ${actorId}:`, String(err));
47220
48273
  }
47221
48274
  }
47222
- return { env, wrapperPaths };
48275
+ return { env, wrapperPaths, requiredTools: [...requiredTools] };
47223
48276
  }
47224
48277
  async function buildActorContext(service, actorId) {
47225
48278
  const [config2, connectors, installedSkills] = await Promise.all([
@@ -47254,8 +48307,11 @@ async function startServe(opts) {
47254
48307
  const file = path11.join(opts.dir, name);
47255
48308
  return fs14.existsSync(file) ? JSON.parse(fs14.readFileSync(file, "utf8")) : void 0;
47256
48309
  };
47257
- const oplog = await NdjsonOplogStore.open(path11.join(opts.dir, "oplog.ndjson"));
47258
- const blobs = new DirBlobStore(path11.join(opts.dir, "blobs"));
48310
+ const pgDsn = process.env["OASIS_PG_DSN"] ?? process.env["DATABASE_URL"];
48311
+ const pgSchema = process.env["OASIS_PG_SCHEMA"] ?? "public";
48312
+ const pgPool = pgDsn ? createPgPool(pgDsn) : void 0;
48313
+ const oplog = pgDsn && pgPool ? await PostgresOplogStore.open(pgPool, pgSchema) : await NdjsonOplogStore.open(path11.join(opts.dir, "oplog.ndjson"));
48314
+ const blobs = pgDsn && pgPool ? await PostgresBlobStore.open(pgPool, pgSchema) : new DirBlobStore(path11.join(opts.dir, "blobs"));
47259
48315
  const assets = new DirBlobStore(path11.join(opts.dir, "assets"));
47260
48316
  const defaultRootSchema = [
47261
48317
  { name: "brief", description: "\u5DE5\u5355\u603B\u7EB2\uFF1A\u53D1\u8D77\u65B9\u5BF9\u9F50\u7684\u603B\u76EE\u6807\u4E0E\u8303\u56F4\uFF0C\u6574\u5F20\u534F\u4F5C\u56FE\u7684\u6839\u3002", contentType: "text", ownerRole: "founder" },
@@ -47282,7 +48338,7 @@ async function startServe(opts) {
47282
48338
  schema.length = 0;
47283
48339
  schema.push(...live);
47284
48340
  }
47285
- const registryStore = await FileRegistryStore.open(path11.join(opts.dir, "registry-store.json"));
48341
+ const registryStore = pgDsn && pgPool ? await PostgresRegistryStore.open(pgPool, pgSchema) : await FileRegistryStore.open(path11.join(opts.dir, "registry-store.json"));
47286
48342
  const kernel = await Kernel.open(oplog, {
47287
48343
  schema,
47288
48344
  registry: loadJson("registry.json") ?? [],
@@ -47309,6 +48365,8 @@ async function startServe(opts) {
47309
48365
  for (const l of registryListeners) l({ kind: record4.kind, actor: record4.actor, target: record4.target, timestamp: record4.timestamp });
47310
48366
  };
47311
48367
  await syncRegistryRolesToKernel(registryStore, kernel);
48368
+ let builtinSkills = [];
48369
+ let defaultCompanyId = resolveDefaultCompanyConfig().id;
47312
48370
  const actors = createActorsDomain({
47313
48371
  store: registryStore,
47314
48372
  oplog,
@@ -47319,27 +48377,24 @@ async function startServe(opts) {
47319
48377
  // engineRouter 在下方(行 ~411)以 const 声明;此箭头只在请求时调用,那时已初始化,闭包引用合法
47320
48378
  // (TDZ 只在构造时访问才报错,这里不访问)。默认公司命中现有单实例(registry===registryStore),
47321
48379
  // resolveCtx 会复用默认 ctx,行为不变。collab 域仍共用同一 registry(行 ~443),本试点只做 actors。
47322
- resolveEngine: (cid) => engineRouter.getEngine(cid ?? DEFAULT_COMPANY_ID)
48380
+ resolveEngine: (cid) => engineRouter.getEngine(cid ?? defaultCompanyId),
48381
+ // 内置技能只读列表:/api/skills/builtin 展示用(getter 闭包,加载完成后即返回实际集)。
48382
+ listBuiltinSkills: () => builtinSkills.map((s2) => ({ id: s2.id, name: s2.name, description: s2.description }))
47323
48383
  });
47324
- const pgDsn = process.env["OASIS_PG_DSN"] ?? process.env["DATABASE_URL"];
47325
- const pgSchema = process.env["OASIS_PG_SCHEMA"] ?? "public";
47326
- let pgPool;
47327
48384
  let projectStateStore;
47328
48385
  let artifactStateStore;
47329
48386
  let chatSessionStore;
47330
- if (pgDsn) {
47331
- pgPool = createPgPool(pgDsn);
48387
+ if (pgDsn && pgPool) {
47332
48388
  projectStateStore = await PostgresProjectStateStore.open(pgPool, pgSchema);
47333
48389
  artifactStateStore = await PostgresArtifactStateStore.open(pgPool, pgSchema);
47334
48390
  chatSessionStore = await PostgresChatSessionStore.open(pgPool, pgSchema);
47335
48391
  nodeStore = await PostgresNodeStore.open(pgPool, pgSchema);
47336
48392
  nodeTokens = await PostgresNodeTokenStore.open(pgPool, pgSchema);
47337
- console.log(`[serve] \u4EA4\u4ED8\u7269\u72B6\u6001\u4F53\u7CFB\uFF1APostgres schema=${pgSchema}`);
47338
- console.log(`[serve] \u8282\u70B9/\u8FD0\u884C\u65F6\u4F53\u7CFB\uFF1APostgres schema=${pgSchema}`);
48393
+ console.log(`[serve] \u4E8B\u5B9E/\u72B6\u6001\u4F53\u7CFB\uFF1APostgres schema=${pgSchema}\uFF08oplog / registry / blobs / artifacts / chat\uFF09`);
47339
48394
  } else {
47340
48395
  projectStateStore = await FileProjectStateStore.open(path11.join(opts.dir, "artifact-document-projects.json"));
47341
48396
  artifactStateStore = await FileArtifactStateStore.open(path11.join(opts.dir, "artifact-document-state.json"));
47342
- chatSessionStore = new MemoryChatSessionStore();
48397
+ chatSessionStore = await FileChatSessionStore.open(path11.join(opts.dir, "chat-sessions.json"));
47343
48398
  console.log("[serve] \u4EA4\u4ED8\u7269\u72B6\u6001\u4F53\u7CFB\uFF1A\u672C\u5730 JSON dev store");
47344
48399
  console.log("[serve] \u8282\u70B9/\u8FD0\u884C\u65F6\u4F53\u7CFB\uFF1A\u672C\u5730 JSON dev store");
47345
48400
  }
@@ -47392,7 +48447,7 @@ async function startServe(opts) {
47392
48447
  const automationStore = pgPool ? await PostgresAutomationStore.open(pgPool, pgSchema) : new MemoryAutomationStore();
47393
48448
  console.log(`[serve] \u81EA\u52A8\u5316\u4F53\u7CFB\uFF1A${pgPool ? `Postgres schema=${pgSchema}` : "\u5185\u5B58 dev store"}`);
47394
48449
  const automations = createAutomationsDomain({ store: automationStore, kernel });
47395
- const controlPlaneStore = await FileControlPlaneStore.open(path11.join(opts.dir, "control-plane.json"));
48450
+ const controlPlaneStore = pgDsn && pgPool ? await PostgresControlPlaneStore.open(pgPool, pgSchema) : await FileControlPlaneStore.open(path11.join(opts.dir, "control-plane.json"));
47396
48451
  const resendKey = process.env["RESEND_API_KEY"];
47397
48452
  const mailFrom = process.env["RESEND_FROM_EMAIL"] ?? process.env["OASIS_MAIL_FROM"] ?? "noreply@open-friday.com";
47398
48453
  if (!resendKey) console.warn("[serve] RESEND_API_KEY \u672A\u8BBE\u2014\u2014\u9A8C\u8BC1\u7801/\u9080\u8BF7\u6253\u5370\u5230\u65E5\u5FD7\uFF08dev\uFF09\uFF1B\u751F\u4EA7\u8BF7\u914D RESEND_API_KEY + RESEND_FROM_EMAIL\uFF08Resend \u5DF2\u9A8C\u8BC1\u57DF\u540D\uFF09");
@@ -47410,17 +48465,27 @@ async function startServe(opts) {
47410
48465
  // 邀请上手(切片5):被邀请但无账号的邮箱,验证码登录成功时建账号。
47411
48466
  provisionAccount: (email2) => companies.service.ensureAccount(email2)
47412
48467
  });
47413
- await migrateDefaultCompany(controlPlaneStore, await actors.service.listActors());
47414
- await bootstrapBuildWorkorderSkill(actors.service).catch((err) => console.warn(`[serve] build-workorder skill bootstrap failed: ${String(err)}`));
47415
- if (!await controlPlaneStore.getMember(DEFAULT_COMPANY_ID, operatorActor)) {
47416
- await controlPlaneStore.upsertMember({ companyId: DEFAULT_COMPANY_ID, accountId: operatorActor, actorId: operatorActor, role: "owner" });
48468
+ const defaultCompanyMigration = await migrateDefaultCompany(controlPlaneStore, await actors.service.listActors());
48469
+ defaultCompanyId = defaultCompanyMigration.company.id;
48470
+ builtinSkills = await loadBuiltinSkills(builtinSkillsDir()).catch((err) => {
48471
+ console.warn(`[serve] builtin skills load failed: ${String(err)}`);
48472
+ return [];
48473
+ });
48474
+ console.log(`[serve] loaded ${builtinSkills.length} builtin skill(s): ${builtinSkills.map((s2) => s2.id).join(", ") || "(none)"}`);
48475
+ for (const devActor of ["actor:human:yx"]) {
48476
+ if (!await controlPlaneStore.getMember(defaultCompanyId, devActor)) {
48477
+ await controlPlaneStore.upsertMember({ companyId: defaultCompanyId, accountId: devActor, actorId: devActor, role: "member" });
48478
+ }
48479
+ }
48480
+ if (!await controlPlaneStore.getMember(defaultCompanyId, operatorActor)) {
48481
+ await controlPlaneStore.upsertMember({ companyId: defaultCompanyId, accountId: operatorActor, actorId: operatorActor, role: "owner" });
47417
48482
  }
47418
48483
  const bootstrapEmail = process.env["OASIS_BOOTSTRAP_OWNER_EMAIL"];
47419
48484
  if (bootstrapEmail) {
47420
- const m2 = await companies.service.grantOwner(DEFAULT_COMPANY_ID, bootstrapEmail);
48485
+ const m2 = await companies.service.grantOwner(defaultCompanyId, bootstrapEmail);
47421
48486
  console.log(`[serve] bootstrap owner\uFF1A${bootstrapEmail} \u2192 ${m2.accountId}\uFF08\u9ED8\u8BA4\u516C\u53F8 owner\uFF09`);
47422
48487
  }
47423
- await ensureCompanyActors(controlPlaneStore, DEFAULT_COMPANY_ID, registryStore);
48488
+ await ensureCompanyActors(controlPlaneStore, defaultCompanyId, registryStore);
47424
48489
  const CONTROL_PLANE_PREFIXES = ["/api/companies", "/api/invitations"];
47425
48490
  const resolveCompanyContext = async (actor, headers, pathname) => {
47426
48491
  if (CONTROL_PLANE_PREFIXES.some((p2) => pathname === p2 || pathname.startsWith(`${p2}/`))) {
@@ -47507,7 +48572,7 @@ async function startServe(opts) {
47507
48572
  });
47508
48573
  const engineRouter = new CompanyEngineRouter({
47509
48574
  defaultEngine: { kernel, oplog, blobs, registry: registryStore, assets },
47510
- defaultCompanyId: DEFAULT_COMPANY_ID,
48575
+ defaultCompanyId,
47511
48576
  buildEngine: async (companyId) => {
47512
48577
  const engine2 = await openFileEngine(path11.join(opts.dir, "companies", companyEngineDirName(companyId)), { schema });
47513
48578
  await ensureCompanyActors(controlPlaneStore, companyId, engine2.registry);
@@ -47516,7 +48581,7 @@ async function startServe(opts) {
47516
48581
  }
47517
48582
  });
47518
48583
  const typesAdmin = new TypesAdminService(typeStore, () => engineRouter.liveEngines().map((e) => e.kernel));
47519
- const engine = await engineRouter.getEngine(DEFAULT_COMPANY_ID);
48584
+ const engine = await engineRouter.getEngine(defaultCompanyId);
47520
48585
  let hub = null;
47521
48586
  let chatRemoteAdapter = null;
47522
48587
  let localUrl = "";
@@ -47552,7 +48617,7 @@ async function startServe(opts) {
47552
48617
  resolveCompanyContext,
47553
48618
  auth,
47554
48619
  // CO-301 阶段1:请求热路径按当前公司经路由层取引擎(恒等到默认公司;per-company 懒建是后续阶段)。
47555
- resolveEngine: (companyId) => engineRouter.getEngine(companyId ?? DEFAULT_COMPANY_ID),
48620
+ resolveEngine: (companyId) => engineRouter.getEngine(companyId ?? defaultCompanyId),
47556
48621
  actors,
47557
48622
  port: opts.port ?? 7320,
47558
48623
  projectState: projectStateStore,
@@ -47589,7 +48654,7 @@ async function startServe(opts) {
47589
48654
  return {};
47590
48655
  }));
47591
48656
  const limits = { wallClockMs: 5 * 6e4 };
47592
- const artifactId = `artifact:planner:${(0, import_node_crypto20.randomUUID)()}`;
48657
+ const artifactId = `artifact:planner:${(0, import_node_crypto21.randomUUID)()}`;
47593
48658
  const handle = await chatRemoteAdapter.spawn({
47594
48659
  actor: planner.id,
47595
48660
  actorToken: issueSessionToken(planner.id, { artifactId, action: "plan-workorder", limits, binding }),
@@ -47600,7 +48665,8 @@ async function startServe(opts) {
47600
48665
  binding,
47601
48666
  limits,
47602
48667
  env: provision.env,
47603
- wrapperPaths: provision.wrapperPaths
48668
+ wrapperPaths: provision.wrapperPaths,
48669
+ ...provision.requiredTools.length > 0 ? { requiredTools: provision.requiredTools } : {}
47604
48670
  });
47605
48671
  const chunks = [];
47606
48672
  handle.onOutput?.((chunk) => chunks.push(chunk));
@@ -47635,7 +48701,7 @@ async function startServe(opts) {
47635
48701
  workorderDrafts,
47636
48702
  // 活工单查不到时回退查建单草案(status="draft")
47637
48703
  ...opts.sla ? { sla: opts.sla } : {},
47638
- resolveEngine: (cid) => engineRouter.getEngine(cid ?? DEFAULT_COMPANY_ID)
48704
+ resolveEngine: (cid) => engineRouter.getEngine(cid ?? defaultCompanyId)
47639
48705
  }),
47640
48706
  trace.register,
47641
48707
  automations.register,
@@ -47662,7 +48728,10 @@ async function startServe(opts) {
47662
48728
  },
47663
48729
  resolveActorEnv: async (actorId) => (await buildActorProvision(actors.service, actorId)).env,
47664
48730
  resolveActorContext: (actorId) => buildActorContext(actors.service, actorId),
47665
- materializeSkills: (actorId, runtimeKind) => materializeSkillFiles({ service: actors.service, blobs: assets, actorId, runtimeKind }),
48731
+ materializeSkills: async (actorId, runtimeKind) => ({
48732
+ ...materializeBuiltins(builtinSkills, runtimeKind),
48733
+ ...await materializeSkillFiles({ service: actors.service, blobs: assets, actorId, runtimeKind })
48734
+ }),
47666
48735
  uploadChatAttachment: async ({ contentBase64 }) => ({ blobRef: await assets.put(new Uint8Array(Buffer.from(contentBase64, "base64"))) }),
47667
48736
  dispatchProduce: async ({ artifactId, actorId, part }) => {
47668
48737
  if (!dispatcher) {
@@ -47692,9 +48761,9 @@ async function startServe(opts) {
47692
48761
  if (!localUrl) {
47693
48762
  throw new ApiError(503, "SERVER_URL_NOT_READY", "server URL \u5C1A\u672A\u5C31\u7EEA");
47694
48763
  }
47695
- const runtimeSessionId = sessionId ?? (0, import_node_crypto20.randomUUID)();
48764
+ const runtimeSessionId = sessionId ?? (0, import_node_crypto21.randomUUID)();
47696
48765
  const resumeRuntimeSession = Boolean(sessionId);
47697
- const traceRunId = `chat-run:${(0, import_node_crypto20.randomUUID)()}`;
48766
+ const traceRunId = `chat-run:${(0, import_node_crypto21.randomUUID)()}`;
47698
48767
  const traceStartedAt = (/* @__PURE__ */ new Date()).toISOString();
47699
48768
  let traceSeq = 0;
47700
48769
  let traceEnabled = true;
@@ -47789,18 +48858,18 @@ async function startServe(opts) {
47789
48858
  if (actorCtx?.config?.prompt) {
47790
48859
  Object.assign(files, splitIdentityFiles3(actorCtx.config.prompt));
47791
48860
  }
48861
+ Object.assign(files, materializeBuiltins(builtinSkills, binding.runtimeKind));
47792
48862
  Object.assign(files, await materializeSkillFiles({
47793
48863
  service: actors.service,
47794
48864
  blobs: assets,
47795
48865
  actorId,
47796
- runtimeKind: binding.runtimeKind,
47797
- extraSkillIds: DEFAULT_CHAT_SKILLS
48866
+ runtimeKind: binding.runtimeKind
47798
48867
  }).catch((err) => {
47799
48868
  console.warn(`[chat] skill materialize failed for ${actorId}: ${String(err)}`);
47800
48869
  return {};
47801
48870
  }));
47802
48871
  const limits = { wallClockMs: 10 * 6e4 };
47803
- const artifactId = `artifact:chat:${(0, import_node_crypto20.randomUUID)()}`;
48872
+ const artifactId = `artifact:chat:${(0, import_node_crypto21.randomUUID)()}`;
47804
48873
  const handle = await chatRemoteAdapter.spawn({
47805
48874
  actor: actorId,
47806
48875
  actorToken: issueSessionToken(actorId, { artifactId, action: "chat", limits, binding, sessionId: runtimeSessionId }),
@@ -47816,7 +48885,8 @@ async function startServe(opts) {
47816
48885
  // (建单/改图暂存统一,见 docs/proposals/draft-edit-unification.md)。不设 OASIS_WORKSPACE——
47817
48886
  // chat agent 不绑单一工单,按命令 --workspace / id 定位是哪份草案。
47818
48887
  env: { ...provision.env, OASIS_STAGE: "1", ...chatSessionId ? { OASIS_CHAT_SESSION_ID: chatSessionId } : {} },
47819
- wrapperPaths: provision.wrapperPaths
48888
+ wrapperPaths: provision.wrapperPaths,
48889
+ ...provision.requiredTools.length > 0 ? { requiredTools: provision.requiredTools } : {}
47820
48890
  });
47821
48891
  const buffered = [];
47822
48892
  const bufferedTelemetry = [];
@@ -47941,7 +49011,10 @@ async function startServe(opts) {
47941
49011
  resolveBinding: actors.service.resolveBinding(),
47942
49012
  provision: (actorId) => buildActorProvision(actors.service, actorId),
47943
49013
  resolveActorContext: (actorId) => buildActorContext(actors.service, actorId),
47944
- materializeSkills: (actorId, runtimeKind) => materializeSkillFiles({ service: actors.service, blobs: assets, actorId, runtimeKind }),
49014
+ materializeSkills: async (actorId, runtimeKind) => ({
49015
+ ...materializeBuiltins(builtinSkills, runtimeKind),
49016
+ ...await materializeSkillFiles({ service: actors.service, blobs: assets, actorId, runtimeKind })
49017
+ }),
47945
49018
  schema: new Map(schema.map((d2) => [d2.name, d2])),
47946
49019
  resolveCodeRepo,
47947
49020
  // §4/C:异步从权威源(store)解析;dispatcher 装配前 await
@@ -48201,7 +49274,7 @@ var SessionManager = class {
48201
49274
 
48202
49275
  // ../cli/src/daemon/ws-client.ts
48203
49276
  init_wrapper();
48204
- var import_node_os2 = require("node:os");
49277
+ var import_node_os3 = require("node:os");
48205
49278
 
48206
49279
  // ../cli/src/daemon/logger.ts
48207
49280
  function ts2() {
@@ -48304,6 +49377,10 @@ var DaemonWsClient = class {
48304
49377
  return;
48305
49378
  }
48306
49379
  }
49380
+ if (job.requiredTools?.length) {
49381
+ const missing = await ensureConnectorTools(job.requiredTools);
49382
+ if (missing.length) log2("[node-cli]", ` connector tools still missing (agent may fail): ${missing.join(", ")}`);
49383
+ }
48307
49384
  const handle = await this.sessions.getAdapter().spawn(job);
48308
49385
  this.sessions.register(dispatchId, handle);
48309
49386
  log2("[node-cli]", ` \u2192 session_started id=${handle.id} setup=${Date.now() - t0}ms`);
@@ -48338,12 +49415,12 @@ var DaemonWsClient = class {
48338
49415
  if (this.ws?.readyState === import_websocket.default.OPEN) this.ws.send(JSON.stringify(msg));
48339
49416
  }
48340
49417
  buildMeta() {
48341
- return { hostname: (0, import_node_os2.hostname)(), adapters: this.adapters, ...this.reportRuntimes ? { runtimes: this.runtimes } : {}, nodeVersion: process.version, ...this.nodeName ? { nodeName: this.nodeName } : {} };
49418
+ return { hostname: (0, import_node_os3.hostname)(), adapters: this.adapters, ...this.reportRuntimes ? { runtimes: this.runtimes } : {}, nodeVersion: process.version, ...this.nodeName ? { nodeName: this.nodeName } : {} };
48342
49419
  }
48343
49420
  };
48344
49421
 
48345
49422
  // ../cli/src/daemon/detect-adapters.ts
48346
- var import_node_child_process9 = require("node:child_process");
49423
+ var import_node_child_process10 = require("node:child_process");
48347
49424
  var KNOWN = [
48348
49425
  { adapter: "claude-code", binary: "claude" },
48349
49426
  { adapter: "codex", binary: "codex" },
@@ -48362,7 +49439,7 @@ var KNOWN = [
48362
49439
  function onPath(binary) {
48363
49440
  try {
48364
49441
  const cmd = process.platform === "win32" ? "where" : "which";
48365
- (0, import_node_child_process9.execFileSync)(cmd, [binary], { stdio: "ignore" });
49442
+ (0, import_node_child_process10.execFileSync)(cmd, [binary], { stdio: "ignore" });
48366
49443
  return true;
48367
49444
  } catch {
48368
49445
  return false;
@@ -48370,7 +49447,7 @@ function onPath(binary) {
48370
49447
  }
48371
49448
  function versionOf(binary) {
48372
49449
  try {
48373
- return (0, import_node_child_process9.execFileSync)(binary, ["--version"], {
49450
+ return (0, import_node_child_process10.execFileSync)(binary, ["--version"], {
48374
49451
  encoding: "utf8",
48375
49452
  stdio: ["ignore", "pipe", "ignore"],
48376
49453
  timeout: 1500
@@ -48455,6 +49532,79 @@ async function startNode(opts) {
48455
49532
  });
48456
49533
  }
48457
49534
 
49535
+ // ../cli/src/daemon/machine-id.ts
49536
+ var import_node_child_process11 = require("node:child_process");
49537
+ var import_node_fs3 = require("node:fs");
49538
+ var import_node_crypto22 = require("node:crypto");
49539
+ var import_node_os4 = require("node:os");
49540
+ function linuxMachineId() {
49541
+ for (const p2 of ["/etc/machine-id", "/var/lib/dbus/machine-id"]) {
49542
+ try {
49543
+ const v2 = (0, import_node_fs3.readFileSync)(p2, "utf8").trim();
49544
+ if (v2) return v2;
49545
+ } catch {
49546
+ }
49547
+ }
49548
+ return null;
49549
+ }
49550
+ function macMachineId() {
49551
+ try {
49552
+ const out = (0, import_node_child_process11.execFileSync)("ioreg", ["-rd1", "-c", "IOPlatformExpertDevice"], {
49553
+ encoding: "utf8",
49554
+ timeout: 2e3,
49555
+ stdio: ["ignore", "pipe", "ignore"]
49556
+ });
49557
+ const m2 = out.match(/"IOPlatformUUID"\s*=\s*"([^"]+)"/);
49558
+ return m2?.[1] ?? null;
49559
+ } catch {
49560
+ return null;
49561
+ }
49562
+ }
49563
+ function windowsMachineId() {
49564
+ try {
49565
+ const out = (0, import_node_child_process11.execFileSync)("reg", [
49566
+ "query",
49567
+ "HKLM\\SOFTWARE\\Microsoft\\Cryptography",
49568
+ "/v",
49569
+ "MachineGuid"
49570
+ ], { encoding: "utf8", timeout: 2e3, stdio: ["ignore", "pipe", "ignore"] });
49571
+ const m2 = out.match(/MachineGuid\s+REG_SZ\s+([A-Za-z0-9-]+)/);
49572
+ return m2?.[1] ?? null;
49573
+ } catch {
49574
+ return null;
49575
+ }
49576
+ }
49577
+ function fallbackFingerprint() {
49578
+ const macs = [];
49579
+ const ifaces = (0, import_node_os4.networkInterfaces)();
49580
+ for (const name of Object.keys(ifaces).sort()) {
49581
+ for (const ni of ifaces[name] ?? []) {
49582
+ if (!ni.internal && ni.mac && ni.mac !== "00:00:00:00:00:00") macs.push(ni.mac);
49583
+ }
49584
+ }
49585
+ return `fallback:${(0, import_node_os4.hostname)()}:${macs.sort()[0] ?? "no-mac"}`;
49586
+ }
49587
+ function machineFingerprint() {
49588
+ const byOs = process.platform === "darwin" ? macMachineId() : process.platform === "win32" ? windowsMachineId() : linuxMachineId();
49589
+ return byOs ?? fallbackFingerprint();
49590
+ }
49591
+ var defaultSources = {
49592
+ machineFingerprint,
49593
+ osUser: () => {
49594
+ try {
49595
+ return (0, import_node_os4.userInfo)().username;
49596
+ } catch {
49597
+ return process.env["USER"] ?? process.env["USERNAME"] ?? "unknown";
49598
+ }
49599
+ }
49600
+ };
49601
+ function resolveNodeId(sources = {}) {
49602
+ const s2 = { ...defaultSources, ...sources };
49603
+ const material = `${s2.machineFingerprint()}:${s2.osUser()}`;
49604
+ const digest = (0, import_node_crypto22.createHash)("sha256").update(material).digest("hex").slice(0, 12);
49605
+ return `node-${digest}`;
49606
+ }
49607
+
48458
49608
  // ../cli/src/cli.ts
48459
49609
  init_src4();
48460
49610
  var USAGE = `oasis \u2014\u2014 artifact-centric \u534F\u4F5C\u5185\u6838 CLI\uFF08\u670D\u52A1\u7AEF\u7626\u5BA2\u6237\u7AEF\uFF09
@@ -48636,16 +49786,17 @@ async function runCli(argv, println = console.log) {
48636
49786
  const nodeTokenFile = path12.join(dir, "node-token");
48637
49787
  const enrollTokenFile = path12.join(dir, "enroll-token");
48638
49788
  const reportMarkerFile = path12.join(dir, "report-runtimes");
48639
- let nodeId = fs15.existsSync(nodeIdFile) ? fs15.readFileSync(nodeIdFile, "utf8").trim() : null;
49789
+ const persistedNodeId = fs15.existsSync(nodeIdFile) ? fs15.readFileSync(nodeIdFile, "utf8").trim() : null;
49790
+ let nodeId = persistedNodeId || resolveNodeId();
48640
49791
  let token2 = flags.get("token") ?? process.env["OASIS_NODE_TOKEN"] ?? (fs15.existsSync(nodeTokenFile) ? fs15.readFileSync(nodeTokenFile, "utf8").trim() : void 0);
48641
49792
  const enrollToken = flags.get("enroll-token") ?? process.env["OASIS_ENROLL_TOKEN"] ?? (fs15.existsSync(enrollTokenFile) ? fs15.readFileSync(enrollTokenFile, "utf8").trim() : void 0);
48642
49793
  let freshEnrollment = false;
48643
- if ((!nodeId || !token2) && enrollToken) {
49794
+ if (!token2 && enrollToken) {
48644
49795
  const httpBase2 = serverUrl.replace(/^ws(s?):\/\//, "http$1://").replace(/\/node-gateway.*$/, "");
48645
49796
  const res = await fetch(`${httpBase2}/api/nodes/exchange`, {
48646
49797
  method: "POST",
48647
49798
  headers: { "content-type": "application/json" },
48648
- body: JSON.stringify({ enrollToken, ...nodeId ? { nodeId } : {}, hostname: os7.hostname() })
49799
+ body: JSON.stringify({ enrollToken, nodeId, hostname: os7.hostname() })
48649
49800
  });
48650
49801
  if (!res.ok) throw new Error(`enroll token exchange failed: HTTP ${res.status}`);
48651
49802
  const body = await res.json();
@@ -48738,7 +49889,7 @@ async function runCli(argv, println = console.log) {
48738
49889
  const store = createNodeTokenStore(path12.join(dir, "node-tokens.json"));
48739
49890
  const sub = positional[0];
48740
49891
  if (sub === "issue") {
48741
- const id = flags.get("id") ?? `node-${(0, import_node_crypto21.randomUUID)()}`;
49892
+ const id = flags.get("id") ?? `node-${(0, import_node_crypto23.randomUUID)()}`;
48742
49893
  const token2 = store.issue(id);
48743
49894
  println(token2);
48744
49895
  process.stderr.write(
@@ -48763,9 +49914,10 @@ async function runCli(argv, println = console.log) {
48763
49914
  }
48764
49915
  if (command === "admin" && positional[0] === "grant-owner") {
48765
49916
  const email2 = need(flags, "email");
48766
- const companyId = flags.get("company") ?? DEFAULT_COMPANY_ID;
48767
- const name = flags.get("name");
48768
49917
  const store = await FileControlPlaneStore.open(path12.join(dir, "control-plane.json"));
49918
+ const activeCompanies = (await store.listCompanies()).filter((c) => c.status === "active");
49919
+ const companyId = flags.get("company") ?? activeCompanies[0]?.id ?? resolveDefaultCompanyConfig().id;
49920
+ const name = flags.get("name");
48769
49921
  if (!await store.getCompany(companyId)) throw new Error(`\u516C\u53F8\u4E0D\u5B58\u5728\uFF1A${companyId}\uFF08\u5148\u8BA9 serve \u8DD1\u4E00\u6B21\u4EE5\u5EFA\u9ED8\u8BA4\u516C\u53F8\uFF0C\u6216\u7528 --company \u6307\u5B9A\u5DF2\u5B58\u5728\u516C\u53F8\uFF09`);
48770
49922
  const svc = new CompaniesService({ store });
48771
49923
  const m2 = await svc.grantOwner(companyId, email2, name);
@@ -49896,7 +51048,7 @@ function setupAutostart() {
49896
51048
  ""
49897
51049
  ].join("\n"));
49898
51050
  try {
49899
- (0, import_node_child_process10.execSync)("systemctl --user daemon-reload && systemctl --user enable --now oasis-node", { stdio: "ignore" });
51051
+ (0, import_node_child_process12.execSync)("systemctl --user daemon-reload && systemctl --user enable --now oasis-node", { stdio: "ignore" });
49900
51052
  } catch {
49901
51053
  }
49902
51054
  } else if (process.platform === "darwin") {
@@ -49913,14 +51065,14 @@ function setupAutostart() {
49913
51065
  <key>StandardErrorPath</key><string>${LOG_FILE}</string>
49914
51066
  </dict></plist>`);
49915
51067
  try {
49916
- (0, import_node_child_process10.execSync)(`launchctl load "${plist}"`, { stdio: "ignore" });
51068
+ (0, import_node_child_process12.execSync)(`launchctl load "${plist}"`, { stdio: "ignore" });
49917
51069
  } catch {
49918
51070
  }
49919
51071
  }
49920
51072
  }
49921
51073
  function spawnDaemon() {
49922
51074
  const log3 = fs16.openSync(LOG_FILE, "a");
49923
- const child = (0, import_node_child_process10.spawn)(process.execPath, [BIN_FILE, "--_daemon"], {
51075
+ const child = (0, import_node_child_process12.spawn)(process.execPath, [BIN_FILE, "--_daemon"], {
49924
51076
  detached: true,
49925
51077
  stdio: ["ignore", log3, log3]
49926
51078
  });
@@ -49942,11 +51094,11 @@ function stopDaemon() {
49942
51094
  } catch {
49943
51095
  }
49944
51096
  try {
49945
- (0, import_node_child_process10.execSync)('pkill -f "oasis.js --_daemon"', { stdio: "ignore" });
51097
+ (0, import_node_child_process12.execSync)('pkill -f "oasis.js --_daemon"', { stdio: "ignore" });
49946
51098
  } catch {
49947
51099
  }
49948
51100
  try {
49949
- (0, import_node_child_process10.execSync)("systemctl --user stop oasis-node 2>/dev/null", { stdio: "ignore" });
51101
+ (0, import_node_child_process12.execSync)("systemctl --user stop oasis-node 2>/dev/null", { stdio: "ignore" });
49950
51102
  } catch {
49951
51103
  }
49952
51104
  }