oasis_test 0.1.87 → 0.1.90

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 +983 -253
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -2488,6 +2488,24 @@ var init_registry = __esm({
2488
2488
  }
2489
2489
  });
2490
2490
 
2491
+ // ../contract/src/credential-ref.ts
2492
+ function makeVarRef(key) {
2493
+ return `${VAR_REF_PREFIX}${key}`;
2494
+ }
2495
+ function parseVarRef(s2) {
2496
+ if (!s2.startsWith(VAR_REF_PREFIX)) return null;
2497
+ const key = s2.slice(VAR_REF_PREFIX.length);
2498
+ if (key.length === 0 || /[\s/]/.test(key)) return null;
2499
+ return key;
2500
+ }
2501
+ var VAR_REF_PREFIX;
2502
+ var init_credential_ref = __esm({
2503
+ "../contract/src/credential-ref.ts"() {
2504
+ "use strict";
2505
+ VAR_REF_PREFIX = "oasis://var/";
2506
+ }
2507
+ });
2508
+
2491
2509
  // ../contract/src/views.ts
2492
2510
  var init_views = __esm({
2493
2511
  "../contract/src/views.ts"() {
@@ -2565,6 +2583,7 @@ var init_src = __esm({
2565
2583
  init_runtime();
2566
2584
  init_daemon_protocol();
2567
2585
  init_registry();
2586
+ init_credential_ref();
2568
2587
  init_views();
2569
2588
  init_telemetry();
2570
2589
  init_trace();
@@ -5982,6 +6001,13 @@ var init_dispatcher = __esm({
5982
6001
  * 是否相对本会话推进过**(上游产新版才动 pin,天然排除驳回/人类改单)。运行时态、不进 oplog/重放,
5983
6002
  * 与 inFlight 同生同灭。 */
5984
6003
  inFlightPins = /* @__PURE__ */ new Map();
6004
+ /**
6005
+ * 本次会话的凭据清理器,与 inFlight 同生同灭。
6006
+ * **必须在「只有槽位主人才能解锁」那个守卫里调**(见 handleSessionExit)——
6007
+ * 无差别调用 = 拿僵尸会话的死讯删掉**活会话正在用的** creds 文件,
6008
+ * 那比不清理更糟:活着的 agent 下一次 `gh` 就炸,且现场看起来像凭据凭空消失。
6009
+ */
6010
+ inFlightCleanup = /* @__PURE__ */ new Map();
5985
6011
  /** ADR-0094:本会话已确认不支持原生 append(codex/subprocess 远端报 unsupported,或会话在收尾)——
5986
6012
  * 停投,回落"等会话退出 + fresh 重派"。与 inFlight 同生同灭。 */
5987
6013
  appendSkip = /* @__PURE__ */ new Set();
@@ -6887,7 +6913,7 @@ var init_dispatcher = __esm({
6887
6913
  this.assertSpawnAttemptCurrent(jobKey, attempt);
6888
6914
  const tokenFor = this.opts.tokenFor ?? ((a, _ctx) => `token:${a}`);
6889
6915
  const actorToken = tokenFor(dispatchSpec.actor, credentialCtx);
6890
- const provisioned = this.opts.provision ? await this.opts.provision(dispatchSpec.actor) : void 0;
6916
+ const provisioned = this.opts.provision ? await this.opts.provision(dispatchSpec.actor, spec.artifactId) : void 0;
6891
6917
  this.assertSpawnAttemptCurrent(jobKey, attempt);
6892
6918
  const jobEnv = {
6893
6919
  ...provisioned?.env ?? {},
@@ -6915,6 +6941,8 @@ var init_dispatcher = __esm({
6915
6941
  ...Object.keys(jobEnv).length > 0 ? { env: jobEnv } : {},
6916
6942
  ...provisioned?.wrapperPaths && provisioned.wrapperPaths.length > 0 ? { wrapperPaths: provisioned.wrapperPaths } : {},
6917
6943
  ...provisioned?.requiredTools && provisioned.requiredTools.length > 0 ? { requiredTools: provisioned.requiredTools } : {},
6944
+ // 凭据本体随 job 下发,供**节点侧本机注入**(见上方 provision 注释)。
6945
+ ...provisioned?.connectorCreds && provisioned.connectorCreds.length > 0 ? { connectorCreds: provisioned.connectorCreds } : {},
6918
6946
  // ADR-0078 D1/D3:把本次派发的 id 交给 adapter 当 handle.id——于是
6919
6947
  // `handle.id` = `agent_runs.id` = token 的 dispatchId claim,三处同值。
6920
6948
  dispatchId: attempt
@@ -6946,6 +6974,7 @@ var init_dispatcher = __esm({
6946
6974
  }
6947
6975
  this.inFlight.set(jobKey, session);
6948
6976
  this.inFlightActor.set(jobKey, dispatchSpec.actor);
6977
+ if (provisioned?.cleanup) this.inFlightCleanup.set(jobKey, provisioned.cleanup);
6949
6978
  const dispatchedAtMs = Date.now();
6950
6979
  this.inFlightStartedAt.set(jobKey, dispatchedAtMs);
6951
6980
  const seqAtDispatch = seqNow;
@@ -7040,6 +7069,12 @@ var init_dispatcher = __esm({
7040
7069
  this.inFlightStartedAt.delete(jobKey);
7041
7070
  this.inFlightPins.delete(jobKey);
7042
7071
  this.appendSkip.delete(jobKey);
7072
+ const cleanup = this.inFlightCleanup.get(jobKey);
7073
+ if (cleanup) {
7074
+ this.inFlightCleanup.delete(jobKey);
7075
+ void cleanup().catch(() => {
7076
+ });
7077
+ }
7043
7078
  }
7044
7079
  const seqAtExit = kernel.model.lastSeq.get(spec.artifactId) ?? 0;
7045
7080
  const exitSessionKey = this.produceSessionKeyFor(spec);
@@ -23533,8 +23568,8 @@ var require_axios = __commonJS({
23533
23568
  axios.toFormData = toFormData;
23534
23569
  axios.AxiosError = AxiosError$1;
23535
23570
  axios.Cancel = axios.CanceledError;
23536
- axios.all = function all(promises) {
23537
- return Promise.all(promises);
23571
+ axios.all = function all(promises2) {
23572
+ return Promise.all(promises2);
23538
23573
  };
23539
23574
  axios.spread = spread;
23540
23575
  axios.isAxiosError = isAxiosError;
@@ -124780,8 +124815,8 @@ function getElementAtPath(obj, path30) {
124780
124815
  }
124781
124816
  function promiseAllObject(promisesObj) {
124782
124817
  const keys = Object.keys(promisesObj);
124783
- const promises = keys.map((key) => promisesObj[key]);
124784
- return Promise.all(promises).then((results) => {
124818
+ const promises2 = keys.map((key) => promisesObj[key]);
124819
+ return Promise.all(promises2).then((results) => {
124785
124820
  const resolvedObj = {};
124786
124821
  for (let i = 0; i < keys.length; i++) {
124787
124822
  resolvedObj[keys[i]] = results[i];
@@ -140760,8 +140795,8 @@ var init_install = __esm({
140760
140795
  function shSingleQuote(v2) {
140761
140796
  return `'${v2.replace(/'/g, `'\\''`)}'`;
140762
140797
  }
140763
- async function writeSessionCredsFile(slug6, exports2) {
140764
- const dir = await (0, import_promises.mkdtemp)((0, import_node_path5.join)((0, import_node_os3.tmpdir)(), `oasis-${slug6}-`));
140798
+ async function writeSessionCredsFile(slug6, exports2, reuseDir) {
140799
+ const dir = reuseDir ?? await (0, import_promises.mkdtemp)((0, import_node_path5.join)((0, import_node_os3.tmpdir)(), `oasis-${slug6}-`));
140765
140800
  const file = (0, import_node_path5.join)(dir, "creds.env");
140766
140801
  const body = Object.entries(exports2).map(([k2, v2]) => `export ${k2}=${shSingleQuote(v2)}`).join("\n");
140767
140802
  await (0, import_promises.writeFile)(file, body + "\n", { mode: 384 });
@@ -140787,7 +140822,7 @@ async function refreshCredentialsIfNeeded(connector, credentials, opts = {}) {
140787
140822
  return { status: "unsupported", detail: `${connector.config.slug}: \u51ED\u636E\u4E0D\u8FC7\u671F\uFF0C\u65E0\u9700\u7EED\u671F` };
140788
140823
  }
140789
140824
  const nowMs = opts.nowMs ?? Date.now();
140790
- const skewMs = opts.skewMs ?? CREDENTIAL_REFRESH_SKEW_MS;
140825
+ const skewMs = opts.skewMs ?? connector.minRemainingLifetimeMs ?? CREDENTIAL_REFRESH_SKEW_MS;
140791
140826
  const expiresAt = readEpochMs(credentials, connector.expiresAtCredentialKey);
140792
140827
  if (!isExpiringSoon(expiresAt, nowMs, skewMs)) {
140793
140828
  return { status: "fresh" };
@@ -141072,6 +141107,8 @@ var init_feishu = __esm({
141072
141107
  "FEISHU_REFRESH_TOKEN_EXPIRES_AT",
141073
141108
  "FEISHU_USER_OPEN_ID"
141074
141109
  ];
141110
+ /** 宿主机登录态隔离,理由同 github(见接口注释):由通用装配层无条件设。 */
141111
+ hostCredentialIsolationEnvKeys = ["LARKSUITE_CLI_CONFIG_DIR"];
141075
141112
  /** inject() 写进 sessionEnv 的全部键——通用 cleanup() 按它逐个删。 */
141076
141113
  injectedEnvKeys = [
141077
141114
  "LARK_BIN",
@@ -141140,7 +141177,7 @@ var init_feishu = __esm({
141140
141177
  LARKSUITE_CLI_APP_ID: appId,
141141
141178
  LARKSUITE_CLI_APP_SECRET: appSecret,
141142
141179
  ...userAccessToken ? { LARKSUITE_CLI_USER_ACCESS_TOKEN: userAccessToken } : {}
141143
- });
141180
+ }, sessionEnv.get("FEISHU_RUN_TMPDIR"));
141144
141181
  const configDir = (0, import_node_path6.join)(creds.dir, "lark-config");
141145
141182
  await (0, import_promises3.mkdir)(configDir, { recursive: true, mode: 448 });
141146
141183
  sessionEnv.set("LARK_BIN", await this.resolveAbsoluteLarkBin());
@@ -141633,10 +141670,17 @@ async function exchangeManifestCode(code, deps = {}) {
141633
141670
  const appId = body["id"] === void 0 ? "" : String(body["id"]);
141634
141671
  const privateKeyPem = typeof body["pem"] === "string" ? body["pem"] : "";
141635
141672
  if (!appId || !privateKeyPem) throw new Error("github app: id/pem missing in conversion response");
141673
+ const owner = body["owner"];
141674
+ const rawPerms = body["permissions"];
141675
+ const permissions = {};
141676
+ for (const [k2, v2] of Object.entries(rawPerms ?? {})) if (typeof v2 === "string") permissions[k2] = v2;
141636
141677
  return {
141637
141678
  appId,
141638
141679
  privateKeyPem,
141639
141680
  slug: typeof body["slug"] === "string" ? body["slug"] : "",
141681
+ ownerLogin: typeof owner?.["login"] === "string" ? owner["login"] : "",
141682
+ ownerType: typeof owner?.["type"] === "string" ? owner["type"] : "",
141683
+ permissions,
141640
141684
  ...typeof body["webhook_secret"] === "string" ? { webhookSecret: body["webhook_secret"] } : {}
141641
141685
  };
141642
141686
  }
@@ -141720,6 +141764,18 @@ var init_github = __esm({
141720
141764
  { key: "access_token", from: "GITHUB_APP_TOKEN", required: false },
141721
141765
  { key: "app_token_expires_at", from: "GITHUB_APP_TOKEN_EXPIRES_AT", required: false }
141722
141766
  ];
141767
+ /**
141768
+ * installation token 满寿命就是 1 小时;声明成全寿命 ⇒ **每次注入都铸新的**。
141769
+ *
141770
+ * 代价可接受:铸 token 是纯换取(私钥 + installation_id 推出来的),
141771
+ * **旧 token 不会被新的作废**(GitHub 要显式 `DELETE /installation/token` 才失效),
141772
+ * 所以多铸一次既不打断在跑的会话、也不消耗任何一次性材料。
141773
+ *
141774
+ * 换来的是:一场会话开头拿到的 token 一定是满 60 分钟,而不是「上一场剩下的 6 分钟」。
141775
+ * ⚠ 仍挡不住跑超过 60 分钟的会话——会话硬顶是 120 分钟(`SESSION_WALL_CLOCK_MS`),
141776
+ * 那半截要靠会话内续期,本字段管不到。
141777
+ */
141778
+ minRemainingLifetimeMs = 60 * 6e4;
141723
141779
  /** 过期判定只认这个键——必须与注入时消费的 token 同源(契约测试盯着)。 */
141724
141780
  expiresAtCredentialKey = "app_token_expires_at";
141725
141781
  // 私钥不会过期,故不声明 refreshMaterialExpiresAtCredentialKey,也无需 clearOnExpired。
@@ -141729,6 +141785,12 @@ var init_github = __esm({
141729
141785
  "GITHUB_APP_TOKEN",
141730
141786
  "GITHUB_APP_PRIVATE_KEY"
141731
141787
  ];
141788
+ /**
141789
+ * 宿主机登录态隔离:`gh` 只认 env 里的 GH_TOKEN 或 `~/.config/gh/hosts.yml`。
141790
+ * 这个键**由通用装配层无条件设**(哪怕本连接器没接通),否则关掉连接器时
141791
+ * agent 会静默用成宿主机那个真人账号——见接口里的详细说明。
141792
+ */
141793
+ hostCredentialIsolationEnvKeys = ["GH_CONFIG_DIR"];
141732
141794
  /** inject() 写进 sessionEnv 的全部键——通用 cleanup() 按它逐个删。 */
141733
141795
  injectedEnvKeys = [
141734
141796
  "GH_BIN",
@@ -141819,7 +141881,7 @@ var init_github = __esm({
141819
141881
  const creds = await writeSessionCredsFile(this.config.slug, {
141820
141882
  GH_TOKEN: token,
141821
141883
  GITHUB_TOKEN: token
141822
- });
141884
+ }, sessionEnv.get("GH_RUN_TMPDIR"));
141823
141885
  const configDir = (0, import_node_path7.join)(creds.dir, "gh-config");
141824
141886
  await (0, import_promises4.mkdir)(configDir, { recursive: true, mode: 448 });
141825
141887
  sessionEnv.set("GH_BIN", await this.resolveRealBin("gh"));
@@ -141872,7 +141934,27 @@ function callbackHtml(ok, message) {
141872
141934
  setTimeout(function(){window.close()},${ok ? 800 : 4e3})</script>
141873
141935
  </body>`;
141874
141936
  }
141875
- var GITHUB_APP_DEFAULT_PERMISSIONS, GITHUB_APP_PERMISSION_LABELS, PENDING_TTL_MS, PendingAppCreations;
141937
+ function checkAppOwnership(conv, want) {
141938
+ if (want.ownerKind === "org" && want.ownerLogin) {
141939
+ const got = conv.ownerLogin;
141940
+ if (!got) {
141941
+ return { message: "GitHub \u6CA1\u8FD4\u56DE App \u7684\u5F52\u5C5E\u8D26\u53F7\uFF0C\u65E0\u6CD5\u786E\u8BA4\u5EFA\u5BF9\u4E86\u5730\u65B9\u2014\u2014\u8BF7\u5220\u6389\u8FD9\u4E2A App \u91CD\u8BD5\u3002" };
141942
+ }
141943
+ if (got.toLowerCase() !== want.ownerLogin.toLowerCase()) {
141944
+ return {
141945
+ message: `\u4F60\u9009\u7684\u662F\u7EC4\u7EC7 ${want.ownerLogin}\uFF0C\u4F46\u8FD9\u4E2A App \u5EFA\u5728\u4E86 ${got}\uFF08${conv.ownerType || "\u672A\u77E5\u7C7B\u578B"}\uFF09\u540D\u4E0B\u3002\u79C1\u6709 App \u53EA\u80FD\u88C5\u5728\u62E5\u6709\u5B83\u7684\u8D26\u53F7\u4E0A\uFF0C\u6240\u4EE5\u5B83\u6C38\u8FDC\u8BBF\u95EE\u4E0D\u5230 ${want.ownerLogin} \u7684\u4ED3\u5E93\u2014\u2014\u8865\u88C5\u4E5F\u6CA1\u7528\u3002\u8BF7\u53BB GitHub \u5220\u6389 ${got} \u540D\u4E0B\u8FD9\u4E2A App\uFF0C\u7136\u540E\u91CD\u65B0\u8FDE\u63A5\uFF0C\u5728\u521B\u5EFA\u9875\u786E\u8BA4\u5730\u5740\u662F github.com/organizations/${want.ownerLogin}/settings/apps/new\u3002`
141946
+ };
141947
+ }
141948
+ }
141949
+ const missing = REQUIRED_WRITE_PERMISSIONS.filter((k2) => conv.permissions[k2] !== "write");
141950
+ if (missing.length > 0) {
141951
+ return {
141952
+ message: `\u8FD9\u4E2A App \u7F3A\u5C11\u5FC5\u8981\u7684\u5199\u6743\u9650\uFF1A${missing.join(" / ")}\uFF08\u9700\u8981 write\uFF09\u3002agent \u5C06\u65E0\u6CD5\u63A8\u9001\u5206\u652F\u6216\u5F00 PR\u3002\u8BF7\u5728 App \u8BBE\u7F6E\u9875\u8865\u4E0A\u6743\u9650\u540E\u91CD\u65B0\u8FDE\u63A5\u3002`
141953
+ };
141954
+ }
141955
+ return null;
141956
+ }
141957
+ var GITHUB_APP_DEFAULT_PERMISSIONS, GITHUB_APP_PERMISSION_LABELS, PENDING_TTL_MS, PendingAppCreations, REQUIRED_WRITE_PERMISSIONS;
141876
141958
  var init_app_manifest = __esm({
141877
141959
  "../connectors/src/github/app-manifest.ts"() {
141878
141960
  "use strict";
@@ -141915,6 +141997,7 @@ var init_app_manifest = __esm({
141915
141997
  return this.map.size;
141916
141998
  }
141917
141999
  };
142000
+ REQUIRED_WRITE_PERMISSIONS = ["contents", "pull_requests"];
141918
142001
  }
141919
142002
  });
141920
142003
 
@@ -141971,19 +142054,27 @@ function collectCredentials(connector, ctx) {
141971
142054
  }
141972
142055
  return creds;
141973
142056
  }
142057
+ function collectAllConnectorCredentials(ctx) {
142058
+ return createAllConnectors().flatMap((connector) => {
142059
+ const credentials = collectCredentials(connector, ctx);
142060
+ return credentials ? [{ slug: connector.config.slug, credentials }] : [];
142061
+ });
142062
+ }
141974
142063
  async function buildConnectorProvision(ctx) {
141975
142064
  const envOverrides = {};
141976
142065
  const wrapperPaths = [];
141977
142066
  const requiredTools = /* @__PURE__ */ new Set();
141978
- const connectorCreds = [];
141979
142067
  const cleanups = [];
141980
142068
  const sensitiveVars = /* @__PURE__ */ new Set();
142069
+ const isolationKeys = /* @__PURE__ */ new Set();
142070
+ const connectorCreds = collectAllConnectorCredentials(ctx);
142071
+ const credentialsBySlug = new Map(connectorCreds.map((c) => [c.slug, c.credentials]));
141981
142072
  for (const connector of createAllConnectors()) {
141982
142073
  for (const v2 of connector.sensitiveVars) sensitiveVars.add(v2);
141983
- const credentials = collectCredentials(connector, ctx);
141984
- if (credentials === null) continue;
142074
+ for (const k2 of connector.hostCredentialIsolationEnvKeys ?? []) isolationKeys.add(k2);
142075
+ const credentials = credentialsBySlug.get(connector.config.slug);
142076
+ if (credentials === void 0) continue;
141985
142077
  try {
141986
- connectorCreds.push({ slug: connector.config.slug, credentials });
141987
142078
  const sessionEnv = /* @__PURE__ */ new Map();
141988
142079
  await connector.inject(credentials, sessionEnv);
141989
142080
  for (const [k2, v2] of sessionEnv) envOverrides[k2] = v2;
@@ -141999,6 +142090,19 @@ async function buildConnectorProvision(ctx) {
141999
142090
  log("[provision]", `${connector.config.slug}: \u88C5\u914D\u5931\u8D25\uFF08\u8BE5\u8FDE\u63A5\u5668\u672C\u8F6E\u4E0D\u53EF\u7528\uFF09: ${String(err)}`);
142000
142091
  }
142001
142092
  }
142093
+ const isolationRoot = await (0, import_promises5.mkdtemp)((0, import_node_path8.join)((0, import_node_os4.tmpdir)(), "oasis-isolate-"));
142094
+ let isolationUsed = false;
142095
+ for (const key of isolationKeys) {
142096
+ if (envOverrides[key]) continue;
142097
+ const dir = (0, import_node_path8.join)(isolationRoot, key.toLowerCase());
142098
+ await (0, import_promises5.mkdir)(dir, { recursive: true, mode: 448 });
142099
+ envOverrides[key] = dir;
142100
+ isolationUsed = true;
142101
+ }
142102
+ if (!isolationUsed) await (0, import_promises5.rm)(isolationRoot, { recursive: true, force: true });
142103
+ else cleanups.push(async () => {
142104
+ await (0, import_promises5.rm)(isolationRoot, { recursive: true, force: true });
142105
+ });
142002
142106
  const cleanup = async () => {
142003
142107
  for (const fn of cleanups) await fn().catch(() => {
142004
142108
  });
@@ -142012,23 +142116,28 @@ async function buildConnectorProvision(ctx) {
142012
142116
  cleanup
142013
142117
  };
142014
142118
  }
142119
+ var import_promises5, import_node_os4, import_node_path8;
142015
142120
  var init_provision = __esm({
142016
142121
  "../connectors/src/provision.ts"() {
142017
142122
  "use strict";
142018
142123
  init_base();
142019
142124
  init_registry2();
142020
142125
  init_logger();
142126
+ import_promises5 = require("node:fs/promises");
142127
+ import_node_os4 = require("node:os");
142128
+ import_node_path8 = require("node:path");
142021
142129
  }
142022
142130
  });
142023
142131
 
142024
142132
  // ../connectors/src/prepare-job.ts
142025
- async function prepareConnectorsForJob(job) {
142133
+ async function prepareConnectorsForJob(job, deps) {
142026
142134
  const creds = job.connectorCreds ?? [];
142027
142135
  const remoteWrappers = job.wrapperPaths ?? [];
142028
142136
  const localWrappers = [];
142029
142137
  const cleanups = [];
142030
142138
  const envOverrides = {};
142031
142139
  const statuses = [];
142140
+ const injected = /* @__PURE__ */ new Map();
142032
142141
  for (const { slug: slug6, credentials } of creds) {
142033
142142
  const connector = createConnectorBySlug(slug6);
142034
142143
  if (!connector) {
@@ -142043,6 +142152,7 @@ async function prepareConnectorsForJob(job) {
142043
142152
  await connector.inject(credentials, sessionEnv);
142044
142153
  for (const [k2, v2] of sessionEnv) envOverrides[k2] = v2;
142045
142154
  cleanups.push(() => connector.cleanup(sessionEnv));
142155
+ injected.set(slug6, { connector, sessionEnv });
142046
142156
  status.credsReady = true;
142047
142157
  } catch (err) {
142048
142158
  status.error = String(err);
@@ -142066,30 +142176,117 @@ async function prepareConnectorsForJob(job) {
142066
142176
  }
142067
142177
  const keptLocal = localWrappers.filter((p2) => isStagedWrapperUsable(p2));
142068
142178
  const keptRemote = remoteWrappers.filter((p2) => (0, import_node_fs5.existsSync)(p2));
142069
- const dropped = remoteWrappers.length - keptRemote.length + (localWrappers.length - keptLocal.length);
142070
- if (dropped > 0) log("[node-connectors]", `\u4E22\u5F03 ${dropped} \u6761\u672C\u673A\u4E0D\u53EF\u7528\u7684 wrapper \u8DEF\u5F84`);
142179
+ const droppedWrappers = remoteWrappers.length - keptRemote.length + (localWrappers.length - keptLocal.length);
142180
+ if (droppedWrappers > 0) log("[node-connectors]", `\u4E22\u5F03 ${droppedWrappers} \u6761\u672C\u673A\u4E0D\u53EF\u7528\u7684 wrapper \u8DEF\u5F84`);
142181
+ const staleKeys = /* @__PURE__ */ new Set();
142182
+ const isolationKeys = /* @__PURE__ */ new Set();
142183
+ for (const connector of createAllConnectors()) {
142184
+ if (injected.has(connector.config.slug)) continue;
142185
+ const isolation = new Set(connector.hostCredentialIsolationEnvKeys ?? []);
142186
+ for (const key of connector.injectedEnvKeys) {
142187
+ if (isolation.has(key)) isolationKeys.add(key);
142188
+ else staleKeys.add(key);
142189
+ }
142190
+ }
142071
142191
  const basePath = job.env?.["PATH"] ?? process.env["PATH"] ?? "";
142072
142192
  const toolsBin = connectorToolsBinDir();
142073
142193
  const pathWithTools = basePath.split(":").includes(toolsBin) ? basePath : [toolsBin, basePath].filter(Boolean).join(":");
142194
+ const jobEnv = { ...job.env ?? {}, ...envOverrides, PATH: pathWithTools };
142195
+ let dropped = 0;
142196
+ for (const key of staleKeys) {
142197
+ if (jobEnv[key] === void 0) continue;
142198
+ delete jobEnv[key];
142199
+ dropped++;
142200
+ }
142201
+ if (isolationKeys.size > 0) {
142202
+ const isolationRoot = await (0, import_promises6.mkdtemp)((0, import_node_path9.join)((0, import_node_os5.tmpdir)(), "oasis-node-isolate-"));
142203
+ for (const key of isolationKeys) {
142204
+ const dir = (0, import_node_path9.join)(isolationRoot, key.toLowerCase());
142205
+ await (0, import_promises6.mkdir)(dir, { recursive: true, mode: 448 });
142206
+ jobEnv[key] = dir;
142207
+ }
142208
+ cleanups.push(async () => {
142209
+ await (0, import_promises6.rm)(isolationRoot, { recursive: true, force: true });
142210
+ });
142211
+ }
142212
+ if (dropped > 0) {
142213
+ log(
142214
+ "[node-connectors]",
142215
+ `\u26A0 \u6E05\u6389 ${dropped} \u4E2A\u6307\u5411\u522B\u7684\u673A\u5668\u7684\u8FDE\u63A5\u5668 env \u952E\uFF08server \u53D1\u4E86\u6307\u9488\u4F46\u6CA1\u53D1\u51ED\u636E\uFF09\u2014\u2014\u672C\u8F6E\u8FD9\u4E9B\u8FDE\u63A5\u5668**\u4E0D\u53EF\u7528**\uFF0Cagent \u5E94\u636E\u6B64\u5224\u5B9A\u300C\u6CA1\u6709\u8BE5\u80FD\u529B\u300D\uFF0C\u800C\u4E0D\u662F\u4EE5\u4E3A\u914D\u597D\u4E86\u3002`
142216
+ );
142217
+ }
142074
142218
  const preparedJob = {
142075
142219
  ...job,
142076
- env: { ...job.env ?? {}, ...envOverrides, PATH: pathWithTools },
142220
+ env: jobEnv,
142077
142221
  wrapperPaths: [...keptLocal, ...keptRemote]
142078
142222
  };
142223
+ if (injected.size > 0 && (job.limits?.wallClockMs ?? 0) > SESSION_CREDENTIAL_RENEW_INTERVAL_MS) {
142224
+ const fetchCredentials = deps?.fetchCredentials ?? fetchCredentialsFromServer(job);
142225
+ const setTimer = deps?.setTimer ?? defaultSetTimer;
142226
+ const timer = setTimer(() => {
142227
+ void renewInjectedCredentials(injected, fetchCredentials);
142228
+ }, SESSION_CREDENTIAL_RENEW_INTERVAL_MS);
142229
+ cleanups.push(async () => {
142230
+ timer.stop();
142231
+ });
142232
+ log("[node-connectors]", `\u4F1A\u8BDD\u4E2D\u9014\u7EED\u671F\u5DF2\u542F\u52A8\uFF08\u6BCF ${SESSION_CREDENTIAL_RENEW_INTERVAL_MS / 6e4} \u5206\u949F\uFF0C${injected.size} \u4E2A\u8FDE\u63A5\u5668\uFF09`);
142233
+ }
142079
142234
  const cleanup = async () => {
142080
142235
  for (const fn of cleanups) await fn().catch(() => {
142081
142236
  });
142082
142237
  };
142083
142238
  return { job: preparedJob, cleanup, statuses };
142084
142239
  }
142085
- var import_node_fs5;
142240
+ async function renewInjectedCredentials(injected, fetchCredentials) {
142241
+ const outcome = { renewed: [], failed: [] };
142242
+ let fresh;
142243
+ try {
142244
+ fresh = await fetchCredentials();
142245
+ } catch (err) {
142246
+ for (const slug6 of injected.keys()) outcome.failed.push({ slug: slug6, error: String(err) });
142247
+ log("[node-connectors]", `\u7EED\u671F\uFF1A\u5411 server \u53D6\u51ED\u636E\u5931\u8D25\uFF0C\u672C\u8F6E\u6CBF\u7528\u65E7\u51ED\u636E: ${String(err)}`);
142248
+ return outcome;
142249
+ }
142250
+ for (const { slug: slug6, credentials } of fresh) {
142251
+ const target = injected.get(slug6);
142252
+ if (!target) continue;
142253
+ try {
142254
+ await target.connector.inject(credentials, target.sessionEnv);
142255
+ outcome.renewed.push(slug6);
142256
+ } catch (err) {
142257
+ outcome.failed.push({ slug: slug6, error: String(err) });
142258
+ log("[node-connectors]", `\u26A0 \u7EED\u671F\uFF1A${slug6} \u5C31\u5730\u91CD\u6CE8\u5931\u8D25\uFF0C\u6CBF\u7528\u65E7\u51ED\u636E: ${String(err)}`);
142259
+ }
142260
+ }
142261
+ if (outcome.renewed.length > 0) log("[node-connectors]", `\u7EED\u671F\uFF1A\u5DF2\u5C31\u5730\u6362\u65B0 ${outcome.renewed.join(", ")}`);
142262
+ return outcome;
142263
+ }
142264
+ function defaultSetTimer(fn, ms) {
142265
+ const timer = setInterval(fn, ms);
142266
+ timer.unref?.();
142267
+ return { stop: () => clearInterval(timer) };
142268
+ }
142269
+ function fetchCredentialsFromServer(job) {
142270
+ return async () => {
142271
+ const url = new URL("/api/connector-credentials", job.server?.url ?? "").toString();
142272
+ const res = await fetch(url, { headers: { authorization: `Bearer ${job.actorToken}` } });
142273
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
142274
+ const body = await res.json();
142275
+ return body.connectorCreds ?? [];
142276
+ };
142277
+ }
142278
+ var import_node_fs5, import_promises6, import_node_os5, import_node_path9, SESSION_CREDENTIAL_RENEW_INTERVAL_MS;
142086
142279
  var init_prepare_job = __esm({
142087
142280
  "../connectors/src/prepare-job.ts"() {
142088
142281
  "use strict";
142089
142282
  import_node_fs5 = require("node:fs");
142283
+ import_promises6 = require("node:fs/promises");
142284
+ import_node_os5 = require("node:os");
142285
+ import_node_path9 = require("node:path");
142090
142286
  init_base();
142091
142287
  init_registry2();
142092
142288
  init_logger();
142289
+ SESSION_CREDENTIAL_RENEW_INTERVAL_MS = 30 * 6e4;
142093
142290
  }
142094
142291
  });
142095
142292
 
@@ -142428,8 +142625,8 @@ function materializeFiles(dir, files) {
142428
142625
  if (!files) return;
142429
142626
  for (const [rel, content] of Object.entries(files)) {
142430
142627
  try {
142431
- const file = import_node_path8.default.join(dir, rel);
142432
- import_node_fs6.default.mkdirSync(import_node_path8.default.dirname(file), { recursive: true });
142628
+ const file = import_node_path10.default.join(dir, rel);
142629
+ import_node_fs6.default.mkdirSync(import_node_path10.default.dirname(file), { recursive: true });
142433
142630
  import_node_fs6.default.writeFileSync(file, content);
142434
142631
  } catch {
142435
142632
  }
@@ -142445,7 +142642,7 @@ function resolveLegacyWorkRoot(workRoot) {
142445
142642
  if (workRoot) return workRoot;
142446
142643
  const env = process.env["OASIS_WORK_ROOT"];
142447
142644
  if (env) return env;
142448
- return import_node_os4.default.tmpdir();
142645
+ return import_node_os6.default.tmpdir();
142449
142646
  }
142450
142647
  function resolveWorkRoots(workRoot) {
142451
142648
  return { workRoot: resolveWorkRoot(workRoot), legacyRoot: resolveLegacyWorkRoot(workRoot) };
@@ -142456,7 +142653,7 @@ function slug5(workdirKey) {
142456
142653
  return `${safe}-${h}`;
142457
142654
  }
142458
142655
  function sessionDirFor(workRoot, runtimeKind, workdirKey) {
142459
- return import_node_path8.default.join(resolveWorkRoot(workRoot), "sessions", runtimeKind, slug5(workdirKey));
142656
+ return import_node_path10.default.join(resolveWorkRoot(workRoot), "sessions", runtimeKind, slug5(workdirKey));
142460
142657
  }
142461
142658
  function sessionDirKind(runtimeKind) {
142462
142659
  return runtimeKind === "claude-code" ? "claude" : runtimeKind;
@@ -142469,20 +142666,20 @@ function resolveSessionDirWithLegacy(args) {
142469
142666
  return import_node_fs6.default.existsSync(legacy) ? legacy : fresh;
142470
142667
  }
142471
142668
  function oneShotDirFor(workRoot, runtimeKind) {
142472
- const base = import_node_path8.default.join(resolveWorkRoot(workRoot), "tmp");
142669
+ const base = import_node_path10.default.join(resolveWorkRoot(workRoot), "tmp");
142473
142670
  import_node_fs6.default.mkdirSync(base, { recursive: true });
142474
- return import_node_fs6.default.mkdtempSync(import_node_path8.default.join(base, `${runtimeKind}-`));
142671
+ return import_node_fs6.default.mkdtempSync(import_node_path10.default.join(base, `${runtimeKind}-`));
142475
142672
  }
142476
142673
  function writeMeta(dir, meta) {
142477
142674
  try {
142478
- import_node_fs6.default.writeFileSync(import_node_path8.default.join(dir, META_FILE), `${JSON.stringify(meta, null, 2)}
142675
+ import_node_fs6.default.writeFileSync(import_node_path10.default.join(dir, META_FILE), `${JSON.stringify(meta, null, 2)}
142479
142676
  `);
142480
142677
  } catch {
142481
142678
  }
142482
142679
  }
142483
142680
  function readMeta(dir) {
142484
142681
  try {
142485
- const raw = JSON.parse(import_node_fs6.default.readFileSync(import_node_path8.default.join(dir, META_FILE), "utf8"));
142682
+ const raw = JSON.parse(import_node_fs6.default.readFileSync(import_node_path10.default.join(dir, META_FILE), "utf8"));
142486
142683
  if (!raw || typeof raw !== "object") return null;
142487
142684
  const m2 = raw;
142488
142685
  return typeof m2.kind === "string" && typeof m2.createdAt === "string" ? m2 : null;
@@ -142511,14 +142708,14 @@ function lockWorkdir(dir, pid, holder, workdirKey) {
142511
142708
  ...workdirKey !== void 0 ? { workdirKey } : {}
142512
142709
  };
142513
142710
  try {
142514
- import_node_fs6.default.writeFileSync(import_node_path8.default.join(dir, LOCK_FILE), `${JSON.stringify(lock)}
142711
+ import_node_fs6.default.writeFileSync(import_node_path10.default.join(dir, LOCK_FILE), `${JSON.stringify(lock)}
142515
142712
  `);
142516
142713
  } catch {
142517
142714
  }
142518
142715
  }
142519
142716
  function readLock(dir) {
142520
142717
  try {
142521
- const raw = JSON.parse(import_node_fs6.default.readFileSync(import_node_path8.default.join(dir, LOCK_FILE), "utf8"));
142718
+ const raw = JSON.parse(import_node_fs6.default.readFileSync(import_node_path10.default.join(dir, LOCK_FILE), "utf8"));
142522
142719
  if (!raw || typeof raw !== "object") return null;
142523
142720
  const l = raw;
142524
142721
  return Number.isInteger(l.pid) && l.pid > 0 ? l : null;
@@ -142528,14 +142725,14 @@ function readLock(dir) {
142528
142725
  }
142529
142726
  function unlockWorkdir(dir) {
142530
142727
  try {
142531
- import_node_fs6.default.unlinkSync(import_node_path8.default.join(dir, LOCK_FILE));
142728
+ import_node_fs6.default.unlinkSync(import_node_path10.default.join(dir, LOCK_FILE));
142532
142729
  } catch {
142533
142730
  }
142534
142731
  }
142535
142732
  function isWorkdirLive(dir) {
142536
142733
  let raw;
142537
142734
  try {
142538
- raw = import_node_fs6.default.readFileSync(import_node_path8.default.join(dir, LOCK_FILE), "utf8");
142735
+ raw = import_node_fs6.default.readFileSync(import_node_path10.default.join(dir, LOCK_FILE), "utf8");
142539
142736
  } catch {
142540
142737
  return false;
142541
142738
  }
@@ -142552,7 +142749,7 @@ function clearWorkdir(dir) {
142552
142749
  );
142553
142750
  }
142554
142751
  for (const entry of import_node_fs6.default.readdirSync(dir)) {
142555
- import_node_fs6.default.rmSync(import_node_path8.default.join(dir, entry), { recursive: true, force: true });
142752
+ import_node_fs6.default.rmSync(import_node_path10.default.join(dir, entry), { recursive: true, force: true });
142556
142753
  }
142557
142754
  }
142558
142755
  function prepareWorkdir(args) {
@@ -142582,19 +142779,19 @@ function prepareWorkdir(args) {
142582
142779
  return dir;
142583
142780
  }
142584
142781
  function legacyChatSessionDir(workRoot, rtId) {
142585
- return import_node_path8.default.join(resolveLegacyWorkRoot(workRoot), "oasis-chat-sessions", rtId.replace(/[^a-zA-Z0-9_-]+/g, "_"));
142782
+ return import_node_path10.default.join(resolveLegacyWorkRoot(workRoot), "oasis-chat-sessions", rtId.replace(/[^a-zA-Z0-9_-]+/g, "_"));
142586
142783
  }
142587
- var import_node_crypto9, import_node_fs6, import_node_os4, import_node_path8, META_FILE, LOCK_FILE, NEW_WORK_ROOT, LEGACY_ROOTS, LEGACY_ONESHOT_PREFIX;
142784
+ var import_node_crypto9, import_node_fs6, import_node_os6, import_node_path10, META_FILE, LOCK_FILE, NEW_WORK_ROOT, LEGACY_ROOTS, LEGACY_ONESHOT_PREFIX;
142588
142785
  var init_session_paths = __esm({
142589
142786
  "../adapters/src/_core/session-paths.ts"() {
142590
142787
  "use strict";
142591
142788
  import_node_crypto9 = require("node:crypto");
142592
142789
  import_node_fs6 = __toESM(require("node:fs"), 1);
142593
- import_node_os4 = __toESM(require("node:os"), 1);
142594
- import_node_path8 = __toESM(require("node:path"), 1);
142790
+ import_node_os6 = __toESM(require("node:os"), 1);
142791
+ import_node_path10 = __toESM(require("node:path"), 1);
142595
142792
  META_FILE = ".oasis-meta.json";
142596
142793
  LOCK_FILE = ".oasis-lock";
142597
- NEW_WORK_ROOT = () => import_node_path8.default.join(import_node_os4.default.homedir(), ".oasis", "work");
142794
+ NEW_WORK_ROOT = () => import_node_path10.default.join(import_node_os6.default.homedir(), ".oasis", "work");
142598
142795
  LEGACY_ROOTS = ["oasis-chat-sessions", "oasis-transcripts"];
142599
142796
  LEGACY_ONESHOT_PREFIX = "oasis-session-";
142600
142797
  }
@@ -143816,7 +144013,7 @@ async function discoverACPModels(bin, provider, args = ["acp"]) {
143816
144013
  clientCapabilities: {}
143817
144014
  });
143818
144015
  try {
143819
- tmpDir = (0, import_node_fs7.mkdtempSync)((0, import_node_path9.join)((0, import_node_os5.tmpdir)(), `oasis-acp-${provider}-`));
144016
+ tmpDir = (0, import_node_fs7.mkdtempSync)((0, import_node_path11.join)((0, import_node_os7.tmpdir)(), `oasis-acp-${provider}-`));
143820
144017
  } catch {
143821
144018
  return fail();
143822
144019
  }
@@ -143872,14 +144069,14 @@ function parseACPSessionNewModels(result) {
143872
144069
  async function discoverOpenclawModels(bin) {
143873
144070
  throw new Error("discoverOpenclawModels: TODO");
143874
144071
  }
143875
- var import_node_child_process9, import_node_fs7, import_node_os5, import_node_path9, modelCache, CACHE_TTL_MS;
144072
+ var import_node_child_process9, import_node_fs7, import_node_os7, import_node_path11, modelCache, CACHE_TTL_MS;
143876
144073
  var init_models = __esm({
143877
144074
  "../adapters/src/_core/models.ts"() {
143878
144075
  "use strict";
143879
144076
  import_node_child_process9 = require("node:child_process");
143880
144077
  import_node_fs7 = require("node:fs");
143881
- import_node_os5 = require("node:os");
143882
- import_node_path9 = require("node:path");
144078
+ import_node_os7 = require("node:os");
144079
+ import_node_path11 = require("node:path");
143883
144080
  modelCache = /* @__PURE__ */ new Map();
143884
144081
  CACHE_TTL_MS = 6e4;
143885
144082
  }
@@ -146446,7 +146643,7 @@ function dirSizeBytes(dir) {
146446
146643
  return;
146447
146644
  }
146448
146645
  for (const e of entries) {
146449
- const p2 = import_node_path10.default.join(d, e.name);
146646
+ const p2 = import_node_path12.default.join(d, e.name);
146450
146647
  if (e.isSymbolicLink()) continue;
146451
146648
  if (e.isDirectory()) {
146452
146649
  walk(p2);
@@ -146463,40 +146660,40 @@ function dirSizeBytes(dir) {
146463
146660
  }
146464
146661
  function listWorkdirs(workRoot, legacyRoot) {
146465
146662
  const out = [];
146466
- const sessions = import_node_path10.default.join(workRoot, "sessions");
146663
+ const sessions = import_node_path12.default.join(workRoot, "sessions");
146467
146664
  try {
146468
146665
  for (const kind of import_node_fs8.default.readdirSync(sessions)) {
146469
- const kindDir = import_node_path10.default.join(sessions, kind);
146666
+ const kindDir = import_node_path12.default.join(sessions, kind);
146470
146667
  try {
146471
- for (const slug6 of import_node_fs8.default.readdirSync(kindDir)) out.push(import_node_path10.default.join(kindDir, slug6));
146668
+ for (const slug6 of import_node_fs8.default.readdirSync(kindDir)) out.push(import_node_path12.default.join(kindDir, slug6));
146472
146669
  } catch {
146473
146670
  }
146474
146671
  }
146475
146672
  } catch {
146476
146673
  }
146477
- const tmp = import_node_path10.default.join(workRoot, "tmp");
146674
+ const tmp = import_node_path12.default.join(workRoot, "tmp");
146478
146675
  try {
146479
- for (const d of import_node_fs8.default.readdirSync(tmp)) out.push(import_node_path10.default.join(tmp, d));
146676
+ for (const d of import_node_fs8.default.readdirSync(tmp)) out.push(import_node_path12.default.join(tmp, d));
146480
146677
  } catch {
146481
146678
  }
146482
146679
  for (const legacy of LEGACY_ROOTS) {
146483
- const root = import_node_path10.default.join(legacyRoot, legacy);
146680
+ const root = import_node_path12.default.join(legacyRoot, legacy);
146484
146681
  try {
146485
- for (const d of import_node_fs8.default.readdirSync(root)) out.push(import_node_path10.default.join(root, d));
146682
+ for (const d of import_node_fs8.default.readdirSync(root)) out.push(import_node_path12.default.join(root, d));
146486
146683
  } catch {
146487
146684
  }
146488
146685
  }
146489
146686
  try {
146490
146687
  for (const d of import_node_fs8.default.readdirSync(legacyRoot)) {
146491
- if (d.startsWith(LEGACY_ONESHOT_PREFIX)) out.push(import_node_path10.default.join(legacyRoot, d));
146688
+ if (d.startsWith(LEGACY_ONESHOT_PREFIX)) out.push(import_node_path12.default.join(legacyRoot, d));
146492
146689
  }
146493
146690
  } catch {
146494
146691
  }
146495
146692
  return [...new Set(out)];
146496
146693
  }
146497
146694
  function kindFromPath(workRoot, dir) {
146498
- const rel = import_node_path10.default.relative(workRoot, dir);
146499
- const parts = rel.split(import_node_path10.default.sep);
146695
+ const rel = import_node_path12.default.relative(workRoot, dir);
146696
+ const parts = rel.split(import_node_path12.default.sep);
146500
146697
  if (parts[0] === "sessions" && parts[1]) return parts[1];
146501
146698
  if (parts[0] === "tmp" && parts[1]) return parts[1].replace(/-[^-]*$/, "");
146502
146699
  return void 0;
@@ -146538,7 +146735,7 @@ async function runGcSweep(deps) {
146538
146735
  let deleted = 0;
146539
146736
  for (const d of decisions) {
146540
146737
  if (d.action !== "delete") continue;
146541
- if (import_node_fs8.default.existsSync(import_node_path10.default.join(d.dir, LOCK_FILE)) && isWorkdirLive(d.dir)) {
146738
+ if (import_node_fs8.default.existsSync(import_node_path12.default.join(d.dir, LOCK_FILE)) && isWorkdirLive(d.dir)) {
146542
146739
  log3(`[gc] \u8DF3\u8FC7 ${d.dir}\uFF1A\u6267\u884C\u524D\u590D\u67E5\u53D1\u73B0\u5DF2\u88AB\u5360\u7528`);
146543
146740
  continue;
146544
146741
  }
@@ -146589,12 +146786,12 @@ function startGcLoop(deps) {
146589
146786
  clearInterval(timer);
146590
146787
  };
146591
146788
  }
146592
- var import_node_fs8, import_node_path10;
146789
+ var import_node_fs8, import_node_path12;
146593
146790
  var init_gc_loop = __esm({
146594
146791
  "../adapters/src/_core/gc-loop.ts"() {
146595
146792
  "use strict";
146596
146793
  import_node_fs8 = __toESM(require("node:fs"), 1);
146597
- import_node_path10 = __toESM(require("node:path"), 1);
146794
+ import_node_path12 = __toESM(require("node:path"), 1);
146598
146795
  init_session_paths();
146599
146796
  init_workdir_gc();
146600
146797
  }
@@ -146630,14 +146827,14 @@ var init_src4 = __esm({
146630
146827
  });
146631
146828
 
146632
146829
  // ../connectors/src/connector-adapter.ts
146633
- var import_promises5, import_node_fs9, import_node_os6, import_node_path11, ConnectorAwareAdapter;
146830
+ var import_promises7, import_node_fs9, import_node_os8, import_node_path13, ConnectorAwareAdapter;
146634
146831
  var init_connector_adapter = __esm({
146635
146832
  "../connectors/src/connector-adapter.ts"() {
146636
146833
  "use strict";
146637
- import_promises5 = require("node:fs/promises");
146834
+ import_promises7 = require("node:fs/promises");
146638
146835
  import_node_fs9 = require("node:fs");
146639
- import_node_os6 = require("node:os");
146640
- import_node_path11 = require("node:path");
146836
+ import_node_os8 = require("node:os");
146837
+ import_node_path13 = require("node:path");
146641
146838
  init_src4();
146642
146839
  init_base();
146643
146840
  init_prepare_job();
@@ -146660,11 +146857,11 @@ var init_connector_adapter = __esm({
146660
146857
  const wrapperPaths = [...prepared.job.wrapperPaths ?? [], ...sentinelWrappers];
146661
146858
  let extraEnv = { ...strippedEnv };
146662
146859
  if (wrapperPaths.length > 0) {
146663
- const wrapperBinDir = await (0, import_promises5.mkdtemp)((0, import_node_path11.join)((0, import_node_os6.tmpdir)(), "oasis-wrappers-"));
146664
- await (0, import_promises5.mkdir)(wrapperBinDir, { recursive: true });
146860
+ const wrapperBinDir = await (0, import_promises7.mkdtemp)((0, import_node_path13.join)((0, import_node_os8.tmpdir)(), "oasis-wrappers-"));
146861
+ await (0, import_promises7.mkdir)(wrapperBinDir, { recursive: true });
146665
146862
  for (const wp of wrapperPaths) {
146666
- const linkName = (0, import_node_path11.basename)(wp).replace(/\.sh$/, "");
146667
- await (0, import_promises5.symlink)(wp, (0, import_node_path11.join)(wrapperBinDir, linkName));
146863
+ const linkName = (0, import_node_path13.basename)(wp).replace(/\.sh$/, "");
146864
+ await (0, import_promises7.symlink)(wp, (0, import_node_path13.join)(wrapperBinDir, linkName));
146668
146865
  }
146669
146866
  extraEnv = { ...extraEnv, PATH: [wrapperBinDir, extraEnv["PATH"] ?? ""].filter(Boolean).join(":") };
146670
146867
  log("[adapter]", ` connectors injected +${Date.now() - t0}ms wrappers=${wrapperPaths.length}`);
@@ -149451,12 +149648,12 @@ async function startOasisServer(opts) {
149451
149648
  const actorCtx = await opts.resolveActorContext(body.actorId).catch(() => null);
149452
149649
  if (actorCtx) {
149453
149650
  const { mkdtempSync: mkdtempSync5, mkdirSync: mkdirSync21, writeFileSync: writeFileSync13 } = await import("node:fs");
149454
- const { join: join30, dirname: dirname24 } = await import("node:path");
149455
- const { tmpdir: tmpdir8 } = await import("node:os");
149456
- const dir = mkdtempSync5(join30(tmpdir8(), "oasis-chat-"));
149651
+ const { join: join32, dirname: dirname24 } = await import("node:path");
149652
+ const { tmpdir: tmpdir10 } = await import("node:os");
149653
+ const dir = mkdtempSync5(join32(tmpdir10(), "oasis-chat-"));
149457
149654
  if (actorCtx.config?.prompt) {
149458
149655
  for (const [rel, content] of Object.entries(splitIdentityFiles2(actorCtx.config.prompt))) {
149459
- const file = join30(dir, rel);
149656
+ const file = join32(dir, rel);
149460
149657
  mkdirSync21(dirname24(file), { recursive: true });
149461
149658
  writeFileSync13(file, content);
149462
149659
  }
@@ -149468,12 +149665,12 @@ async function startOasisServer(opts) {
149468
149665
  const s2 = byId.get(id);
149469
149666
  return s2 ? `- **${s2.name}** (\`${s2.id}\`): ${s2.description}` : `- \`${id}\`\uFF08\u672A\u5728\u6280\u80FD\u5E93\u4E2D\uFF0C\u53EF\u80FD\u5DF2\u5378\u8F7D\uFF09`;
149470
149667
  });
149471
- writeFileSync13(join30(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"));
149668
+ writeFileSync13(join32(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"));
149472
149669
  }
149473
149670
  if (opts.materializeSkills) {
149474
149671
  const skillFiles = await opts.materializeSkills(body.actorId, "claude").catch(() => ({}));
149475
149672
  for (const [rel, content] of Object.entries(skillFiles)) {
149476
- const file = join30(dir, rel);
149673
+ const file = join32(dir, rel);
149477
149674
  mkdirSync21(dirname24(file), { recursive: true });
149478
149675
  writeFileSync13(file, content);
149479
149676
  }
@@ -149487,7 +149684,7 @@ async function startOasisServer(opts) {
149487
149684
  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";
149488
149685
  return `- **${c.name}** (\`${c.id}\`): ${statusNote} \xB7 ${modeNote}`;
149489
149686
  });
149490
- writeFileSync13(join30(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"));
149687
+ writeFileSync13(join32(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"));
149491
149688
  }
149492
149689
  spawnCwd = dir;
149493
149690
  }
@@ -149728,7 +149925,14 @@ async function startOasisServer(opts) {
149728
149925
  baseUrl: base,
149729
149926
  redirectPath: "/api/connectors/github/app/manifest-callback"
149730
149927
  });
149731
- githubAppPending.put(state, { ...actorId ? { actorId } : {}, employeeSlug: nameSlug, createdAtMs: Date.now() });
149928
+ githubAppPending.put(state, {
149929
+ ...actorId ? { actorId } : {},
149930
+ employeeSlug: nameSlug,
149931
+ createdAtMs: Date.now(),
149932
+ // 存下这次选的归属——回调时要拿它跟 GitHub 返回的 owner 比对。
149933
+ wantOwnerKind: kind,
149934
+ ...kind === "org" && owner?.login ? { wantOwnerLogin: owner.login } : {}
149935
+ });
149732
149936
  res.writeHead(200, { "content-type": "application/json" }).end(JSON.stringify({
149733
149937
  state,
149734
149938
  formAction: manifestFormAction(kind === "org" ? { kind: "org", login: owner.login } : { kind: "user" }, state),
@@ -149791,6 +149995,14 @@ async function startOasisServer(opts) {
149791
149995
  }
149792
149996
  try {
149793
149997
  const conv = await exchangeManifestCode(code);
149998
+ const problem = checkAppOwnership(conv, {
149999
+ ...pending.wantOwnerLogin ? { ownerLogin: pending.wantOwnerLogin } : {},
150000
+ ...pending.wantOwnerKind ? { ownerKind: pending.wantOwnerKind } : {}
150001
+ });
150002
+ if (problem) {
150003
+ fail(problem.message);
150004
+ return;
150005
+ }
149794
150006
  const svc = opts.actors?.service;
149795
150007
  if (!svc) throw new Error("actors service unavailable");
149796
150008
  const scope = pending.actorId ? { scope: "personal", actorId: pending.actorId } : { scope: "global" };
@@ -150446,9 +150658,11 @@ var init_memory_registry_store = __esm({
150446
150658
  // key: ${runtimeId}::${modelId}
150447
150659
  connectors = /* @__PURE__ */ new Map();
150448
150660
  variables = /* @__PURE__ */ new Map();
150449
- // key = key::actorId
150450
- _varKey(key, actorId) {
150451
- return `${key}::${actorId ?? ""}`;
150661
+ // key = key::actorId::projectId
150662
+ // 唯一键覆盖三种作用域(ADR 0125:无 environment 维):personal=(key,actorId)、project=(key,projectId)、global=(key)。
150663
+ // 三段拼接后天然互不碰撞(personal 有 actorId projectId,project 反之,global 全空)。
150664
+ _varKey(key, actorId, projectId) {
150665
+ return `${key}::${actorId ?? ""}::${projectId ?? ""}`;
150452
150666
  }
150453
150667
  skillCatalog = /* @__PURE__ */ new Map();
150454
150668
  installedSkills = /* @__PURE__ */ new Map();
@@ -150581,7 +150795,7 @@ var init_memory_registry_store = __esm({
150581
150795
  this.skillFiles.delete(skillId);
150582
150796
  }
150583
150797
  async putVariable(v2) {
150584
- this.variables.set(this._varKey(v2.key, v2.actorId), { ...v2 });
150798
+ this.variables.set(this._varKey(v2.key, v2.actorId, v2.projectId), { ...v2 });
150585
150799
  }
150586
150800
  async patchVariableValue(key, actorId, valueEncrypted, updatedAt) {
150587
150801
  const mk = this._varKey(key, actorId);
@@ -150596,8 +150810,11 @@ var init_memory_registry_store = __esm({
150596
150810
  if (actorId !== void 0) return all.filter((v2) => v2.scope === "global" || v2.actorId === actorId).map((v2) => ({ ...v2 }));
150597
150811
  return all.map((v2) => ({ ...v2 }));
150598
150812
  }
150599
- async deleteVariable(key, actorId) {
150600
- this.variables.delete(this._varKey(key, actorId));
150813
+ async resolveProjectVariables(projectId) {
150814
+ return [...this.variables.values()].filter((v2) => v2.scope === "project" && v2.projectId === projectId).map((v2) => ({ ...v2 }));
150815
+ }
150816
+ async deleteVariable(key, actorId, scoping) {
150817
+ this.variables.delete(this._varKey(key, actorId, scoping?.projectId));
150601
150818
  }
150602
150819
  };
150603
150820
  }
@@ -154721,6 +154938,10 @@ async function sweepSilentSessions(deps) {
154721
154938
  continue;
154722
154939
  }
154723
154940
  if (!live.lastEventAt) continue;
154941
+ if (deps.isTraceHealthy && !deps.isTraceHealthy(s2.sessionId)) {
154942
+ deps.log?.(`[liveness] \u8DF3\u8FC7 ${s2.artifactId}\uFF1Atrace \u5199\u5165\u4E0D\u5065\u5EB7\uFF08\u89C2\u6D4B\u964D\u7EA7\uFF09\uFF0C\u4FE1\u606F\u4E0D\u8DB3\u4E0D\u5224\u9759\u9ED8 \u2192 \u4EA4 wallClock \u515C\u5E95`);
154943
+ continue;
154944
+ }
154724
154945
  const runtime = { hasInFlight: true, lastSessionEventAt: live.lastEventAt, openToolCall: live.openToolCall };
154725
154946
  if (!isRuntimeSilent(runtime, deps.now, deps.silentThresholdMs)) continue;
154726
154947
  const ok = await deps.kill(s2.jobKey).catch(() => false);
@@ -155420,6 +155641,73 @@ var init_recovery_plan = __esm({
155420
155641
  }
155421
155642
  });
155422
155643
 
155644
+ // ../server/src/domains/trace/resilient-appender.ts
155645
+ var ResilientTraceAppender;
155646
+ var init_resilient_appender = __esm({
155647
+ "../server/src/domains/trace/resilient-appender.ts"() {
155648
+ "use strict";
155649
+ ResilientTraceAppender = class {
155650
+ constructor(opts) {
155651
+ this.opts = opts;
155652
+ this.seq = opts.startSeq ?? 0;
155653
+ }
155654
+ seq;
155655
+ failing = false;
155656
+ /** 当前已成功落库的最大 seq。 */
155657
+ get currentSeq() {
155658
+ return this.seq;
155659
+ }
155660
+ /** 恢复路径:把 seq 对齐到库里已有的最大值(不改健康态)。 */
155661
+ resetSeq(seq) {
155662
+ this.seq = seq;
155663
+ }
155664
+ onSuccess() {
155665
+ this.opts.health?.markHealthy(this.opts.runId);
155666
+ if (this.failing) {
155667
+ this.failing = false;
155668
+ this.opts.onRecover?.();
155669
+ }
155670
+ }
155671
+ onFailure(err) {
155672
+ this.opts.health?.markUnhealthy(this.opts.runId);
155673
+ if (!this.failing) {
155674
+ this.failing = true;
155675
+ this.opts.onError?.(err);
155676
+ }
155677
+ }
155678
+ /**
155679
+ * 追加一批事件(seq 自动连续分配);**成功才推进 seq**。返回是否落库。
155680
+ * 幂等吸收(storage 层)保证:若失败其实是"提交成功但连接抖断",下次同 seq 重投会被吸收、不重复。
155681
+ */
155682
+ async append(events) {
155683
+ if (events.length === 0) return true;
155684
+ const base = this.seq;
155685
+ const withSeq = events.map((e, i) => ({ ...e, seq: base + i + 1 }));
155686
+ try {
155687
+ await this.opts.store.appendEvents(this.opts.runId, withSeq);
155688
+ this.seq = base + events.length;
155689
+ this.onSuccess();
155690
+ return true;
155691
+ } catch (err) {
155692
+ this.onFailure(err);
155693
+ return false;
155694
+ }
155695
+ }
155696
+ /** 更新 run(心跳 lastProgressAt / 终态 / metadata 等);失败不熄灭事件通道。 */
155697
+ async update(patch) {
155698
+ try {
155699
+ await this.opts.store.updateRun(this.opts.runId, patch);
155700
+ this.onSuccess();
155701
+ return true;
155702
+ } catch (err) {
155703
+ this.onFailure(err);
155704
+ return false;
155705
+ }
155706
+ }
155707
+ };
155708
+ }
155709
+ });
155710
+
155423
155711
  // ../server/src/domains/trace/sink.ts
155424
155712
  function deriveMessage(payload) {
155425
155713
  if (payload && typeof payload === "object") {
@@ -155429,11 +155717,10 @@ function deriveMessage(payload) {
155429
155717
  }
155430
155718
  return void 0;
155431
155719
  }
155432
- function trajectoryEventToRunEvent(e, seq) {
155720
+ function trajectoryEventToRunEvent(e) {
155433
155721
  const map = KIND_MAP[e.kind] ?? { eventType: "progress", stream: "runtime" };
155434
155722
  const message = deriveMessage(e.payload);
155435
155723
  return {
155436
- seq,
155437
155724
  eventType: map.eventType,
155438
155725
  stream: map.stream,
155439
155726
  ...message !== void 0 ? { message } : {},
@@ -155512,6 +155799,7 @@ var KIND_MAP, MODEL_TEXT_LIMIT, TOOL_PREVIEW_LIMIT, PROGRESS_TOUCH_THROTTLE_MS,
155512
155799
  var init_sink = __esm({
155513
155800
  "../server/src/domains/trace/sink.ts"() {
155514
155801
  "use strict";
155802
+ init_resilient_appender();
155515
155803
  KIND_MAP = {
155516
155804
  message: { eventType: "model.output.completed", stream: "model" },
155517
155805
  thought: { eventType: "model.output.delta", stream: "model" },
@@ -155527,12 +155815,23 @@ var init_sink = __esm({
155527
155815
  store;
155528
155816
  runtimeKind;
155529
155817
  onError;
155818
+ health;
155530
155819
  sessions = /* @__PURE__ */ new Map();
155531
155820
  constructor(opts) {
155532
155821
  this.store = opts.store;
155533
155822
  this.runtimeKind = opts.runtimeKind ?? "custom";
155534
155823
  this.onError = opts.onError ?? (() => {
155535
155824
  });
155825
+ this.health = opts.health;
155826
+ }
155827
+ /** 为一个 run 造事件通道 appender(seq 两道防线 + 健康度上报都收在这里)。 */
155828
+ makeAppender(runId) {
155829
+ return new ResilientTraceAppender({
155830
+ runId,
155831
+ store: this.store,
155832
+ ...this.health ? { health: this.health } : {},
155833
+ onError: (err) => this.onError(err)
155834
+ });
155536
155835
  }
155537
155836
  runtimeKindFor(session) {
155538
155837
  if (this.runtimeKind !== "auto") return this.runtimeKind;
@@ -155554,7 +155853,7 @@ var init_sink = __esm({
155554
155853
  begin(session, _bundleFiles) {
155555
155854
  const st = {
155556
155855
  runId: session.runId,
155557
- seq: 0,
155856
+ appender: this.makeAppender(session.runId),
155558
155857
  chain: Promise.resolve(),
155559
155858
  openTools: /* @__PURE__ */ new Map(),
155560
155859
  // runtimeSessionId:运行时自己的会话号(= 会话工作目录名,也是 --resume 的键)。落账后
@@ -155588,8 +155887,7 @@ var init_sink = __esm({
155588
155887
  relation: "triggered_by",
155589
155888
  createdAt: session.startedAt
155590
155889
  });
155591
- await this.store.appendEvents(st.runId, [{
155592
- seq: ++st.seq,
155890
+ await st.appender.append([{
155593
155891
  eventType: "run.started",
155594
155892
  stream: "runtime",
155595
155893
  message: `${session.action} ${session.artifactId}`
@@ -155599,7 +155897,7 @@ var init_sink = __esm({
155599
155897
  recover(session) {
155600
155898
  const st = {
155601
155899
  runId: session.runId,
155602
- seq: 0,
155900
+ appender: this.makeAppender(session.runId),
155603
155901
  chain: Promise.resolve(),
155604
155902
  openTools: /* @__PURE__ */ new Map(),
155605
155903
  baseMetadata: { bundleManifest: session.bundleManifest, recovered: true },
@@ -155626,11 +155924,10 @@ var init_sink = __esm({
155626
155924
  st.baseMetadata = { ...asMetadata(existing.metadata) ?? st.baseMetadata, recovered: true };
155627
155925
  }
155628
155926
  const events = await this.store.listEvents(session.runId, {});
155629
- st.seq = events.reduce((max, event) => Math.max(max, event.seq), 0);
155927
+ st.appender.resetSeq(events.reduce((max, event) => Math.max(max, event.seq), 0));
155630
155928
  const recoveredAt = session.recoveredAt ?? (/* @__PURE__ */ new Date()).toISOString();
155631
155929
  const interruptedAt = session.interruptedAt ?? recoveredAt;
155632
- await this.store.appendEvents(st.runId, [{
155633
- seq: ++st.seq,
155930
+ await st.appender.append([{
155634
155931
  eventType: "progress",
155635
155932
  stream: "system",
155636
155933
  level: "warn",
@@ -155655,11 +155952,11 @@ var init_sink = __esm({
155655
155952
  const ms = Date.parse(event.ts);
155656
155953
  if (!Number.isNaN(ms) && ms - (st.lastProgressTouchMs ?? 0) >= PROGRESS_TOUCH_THROTTLE_MS) {
155657
155954
  st.lastProgressTouchMs = ms;
155658
- await this.store.updateRun(st.runId, { lastProgressAt: event.ts });
155955
+ await st.appender.update({ lastProgressAt: event.ts });
155659
155956
  }
155660
155957
  return;
155661
155958
  }
155662
- await this.store.appendEvents(st.runId, [trajectoryEventToRunEvent(event, ++st.seq)]);
155959
+ await st.appender.append([trajectoryEventToRunEvent(event)]);
155663
155960
  await this.materializeToolCall(st, event);
155664
155961
  });
155665
155962
  }
@@ -155710,8 +156007,7 @@ var init_sink = __esm({
155710
156007
  const captured = this.sessions.get(sessionId);
155711
156008
  this.enqueue(sessionId, async (st) => {
155712
156009
  for (const ref2 of update.opRefs) {
155713
- await this.store.appendEvents(st.runId, [{
155714
- seq: ++st.seq,
156010
+ await st.appender.append([{
155715
156011
  eventType: "oplog.appended",
155716
156012
  stream: "oasis",
155717
156013
  message: ref2.kind,
@@ -155725,8 +156021,7 @@ var init_sink = __esm({
155725
156021
  createdAt: update.exit.at
155726
156022
  });
155727
156023
  }
155728
- await this.store.appendEvents(st.runId, [{
155729
- seq: ++st.seq,
156024
+ await st.appender.append([{
155730
156025
  eventType: "run.finished",
155731
156026
  stream: "runtime",
155732
156027
  message: `exit code=${update.exit.code ?? "killed"} ops=${update.opRefs.length}`
@@ -155760,6 +156055,7 @@ var init_sink = __esm({
155760
156055
  });
155761
156056
  if (captured) captured.chain = captured.chain.finally(() => {
155762
156057
  this.sessions.delete(sessionId);
156058
+ this.health?.forget(captured.runId);
155763
156059
  });
155764
156060
  }
155765
156061
  };
@@ -155821,27 +156117,26 @@ function wireRecoveredChatTurn(deps) {
155821
156117
  live.emit(rest);
155822
156118
  }
155823
156119
  deps.register();
155824
- let traceSeq = 0;
155825
- let traceEnabled = true;
156120
+ const appender = new ResilientTraceAppender({
156121
+ runId: plan.runId,
156122
+ store: trace,
156123
+ ...deps.traceHealth ? { health: deps.traceHealth } : {},
156124
+ onError: (err) => log3(`[chat-recovery] run ${plan.runId} trace \u5199\u5931\u8D25\uFF08\u7EE7\u7EED\u3001\u4E0D\u7184\u706D\uFF09: ${String(err)}`)
156125
+ });
155826
156126
  let traceChain = trace.listEvents(plan.runId, {}).then((events) => {
155827
- for (const e of events) if (e.seq > traceSeq) traceSeq = e.seq;
156127
+ appender.resetSeq(events.reduce((n, e) => Math.max(n, e.seq), 0));
155828
156128
  }).catch((err) => {
155829
- traceEnabled = false;
155830
- log3(`[chat-recovery] run ${plan.runId} \u8BFB\u4E8B\u4EF6\u5931\u8D25\uFF0C\u672C\u8F6E trace \u7EED\u8D26\u5173\u95ED: ${String(err)}`);
156129
+ deps.traceHealth?.markUnhealthy(plan.runId);
156130
+ log3(`[chat-recovery] run ${plan.runId} \u8BFB\u4E8B\u4EF6\u5931\u8D25: ${String(err)}`);
155831
156131
  });
155832
156132
  const enqueueTrace = (step) => {
155833
- if (!traceEnabled) return;
155834
- traceChain = traceChain.then(async () => {
155835
- if (traceEnabled) await step();
155836
- }).catch((err) => {
155837
- traceEnabled = false;
155838
- log3(`[chat-recovery] run ${plan.runId} trace \u7EED\u8D26\u5931\u8D25: ${String(err)}`);
156133
+ traceChain = traceChain.then(step).catch((err) => {
156134
+ log3(`[chat-recovery] run ${plan.runId} trace \u7EED\u8D26\u5F02\u5E38: ${String(err)}`);
155839
156135
  });
155840
156136
  };
155841
156137
  const recoveredAt = (/* @__PURE__ */ new Date()).toISOString();
155842
156138
  enqueueTrace(async () => {
155843
- await trace.appendEvents(plan.runId, [{
155844
- seq: ++traceSeq,
156139
+ await appender.append([{
155845
156140
  eventType: "progress",
155846
156141
  stream: "system",
155847
156142
  level: "warn",
@@ -155865,11 +156160,11 @@ function wireRecoveredChatTurn(deps) {
155865
156160
  const ms = Date.parse(event.ts);
155866
156161
  if (!Number.isNaN(ms) && ms - lastProgressTouchMs >= PROGRESS_TOUCH_THROTTLE_MS2) {
155867
156162
  lastProgressTouchMs = ms;
155868
- enqueueTrace(() => trace.updateRun(plan.runId, { lastProgressAt: event.ts }).then(() => void 0));
156163
+ enqueueTrace(() => appender.update({ lastProgressAt: event.ts }).then(() => void 0));
155869
156164
  }
155870
156165
  return;
155871
156166
  }
155872
- enqueueTrace(() => trace.appendEvents(plan.runId, [trajectoryEventToRunEvent(event, ++traceSeq)]).then(() => void 0));
156167
+ enqueueTrace(() => appender.append([trajectoryEventToRunEvent(event)]).then(() => void 0));
155873
156168
  if (event.kind === "message" && sawTextOutput) return;
155874
156169
  const part = chatPartFromTrajectoryEvent(event);
155875
156170
  if (part) {
@@ -155950,14 +156245,13 @@ function wireRecoveredChatTurn(deps) {
155950
156245
  live.finish(failed ? "error" : "done");
155951
156246
  deps.unregister();
155952
156247
  enqueueTrace(async () => {
155953
- await trace.appendEvents(plan.runId, [{
155954
- seq: ++traceSeq,
156248
+ await appender.append([{
155955
156249
  eventType: "run.finished",
155956
156250
  stream: "runtime",
155957
156251
  message: `exit code=${info.code ?? "killed"}`,
155958
156252
  startedAt: finishedAt
155959
156253
  }]);
155960
- await trace.updateRun(plan.runId, runTerminalPatch(info, plan.startedAt, finishedAt));
156254
+ await appender.update(runTerminalPatch(info, plan.startedAt, finishedAt));
155961
156255
  if (info.usage && trace.updateUsageAggregates) {
155962
156256
  const run = await trace.getRun(plan.runId);
155963
156257
  if (run) void trace.updateUsageAggregates(run).catch(() => void 0);
@@ -156054,17 +156348,17 @@ async function reconcileChatExitFrame(deps) {
156054
156348
  }).catch(() => void 0);
156055
156349
  try {
156056
156350
  const events = await deps.trace.listEvents(row.runId, {});
156057
- let traceSeq = events.reduce((n, e) => Math.max(n, e.seq), 0);
156058
- await deps.trace.appendEvents(row.runId, [
156059
- ...stash.frames.filter((frame) => frame.type === "event").map((frame) => frame.event).filter((event) => event.kind !== "system").map((event) => trajectoryEventToRunEvent(event, ++traceSeq)),
156351
+ const baseSeq = events.reduce((n, e) => Math.max(n, e.seq), 0);
156352
+ const seqless = [
156353
+ ...stash.frames.filter((frame) => frame.type === "event").map((frame) => frame.event).filter((event) => event.kind !== "system").map((event) => trajectoryEventToRunEvent(event)),
156060
156354
  {
156061
- seq: ++traceSeq,
156062
156355
  eventType: "run.finished",
156063
156356
  stream: "runtime",
156064
156357
  message: `exit code=${info.code ?? "killed"}\uFF08serve \u505C\u673A\u7A97\u53E3\u5185\u7ED3\u675F\uFF0C\u8282\u70B9 outbox \u91CD\u53D1\u5BF9\u8D26\uFF09`,
156065
156358
  startedAt: finishedAt
156066
156359
  }
156067
- ]);
156360
+ ];
156361
+ await deps.trace.appendEvents(row.runId, seqless.map((e, i) => ({ ...e, seq: baseSeq + i + 1 })));
156068
156362
  await deps.trace.updateRun(row.runId, runTerminalPatch(info, run.startedAt, finishedAt));
156069
156363
  if (info.usage && deps.trace.updateUsageAggregates) {
156070
156364
  const fresh = await deps.trace.getRun(row.runId);
@@ -156085,6 +156379,7 @@ var init_chat_recovery = __esm({
156085
156379
  import_node_crypto20 = require("node:crypto");
156086
156380
  init_chat_parts();
156087
156381
  init_sink();
156382
+ init_resilient_appender();
156088
156383
  asObj3 = (v2) => v2 && typeof v2 === "object" && !Array.isArray(v2) ? v2 : void 0;
156089
156384
  partsOf = (parts) => Array.isArray(parts) ? parts.filter((p2) => p2 && typeof p2 === "object" && typeof p2.type === "string") : [];
156090
156385
  maxPartSeq = (parts) => parts.reduce((n, p2) => Math.max(n, typeof p2.seq === "number" ? p2.seq : 0), 0);
@@ -156678,6 +156973,24 @@ var init_skill_fetcher = __esm({
156678
156973
  });
156679
156974
 
156680
156975
  // ../server/src/domains/actors/service.ts
156976
+ function applyRefDelivery(env, entries, warn = (m2) => console.warn(m2)) {
156977
+ for (const e of entries) {
156978
+ if (!(e.key in env)) continue;
156979
+ const mode = e.deliveryMode;
156980
+ switch (mode) {
156981
+ case "env":
156982
+ break;
156983
+ // 非敏感:明文保持不动
156984
+ case "ref":
156985
+ env[e.key] = makeVarRef(e.key);
156986
+ break;
156987
+ default:
156988
+ delete env[e.key];
156989
+ warn(`[credential] \u672A\u77E5 deliveryMode "${mode}"\uFF08key=${e.key}\uFF09\u2014\u2014\u5DF2\u4ECE env \u5254\u9664\uFF0C\u4FDD\u5B88\u5904\u7406\u4E0D\u6CE8\u5165\u660E\u6587`);
156990
+ break;
156991
+ }
156992
+ }
156993
+ }
156681
156994
  function computeEffectiveEntries(config2, connections, connectors) {
156682
156995
  const globalConnected = new Set(connectors.filter((c) => c.status === "connected").map((c) => c.id));
156683
156996
  const record6 = new Map(connections.map((c) => [c.connectorId, c.enabled]));
@@ -156709,6 +157022,7 @@ var init_service3 = __esm({
156709
157022
  init_skill_fetcher();
156710
157023
  init_identity();
156711
157024
  init_src5();
157025
+ init_src();
156712
157026
  REDACTED_MARKER = "[REDACTED]";
156713
157027
  ActorsService = class {
156714
157028
  constructor(opts) {
@@ -157362,8 +157676,11 @@ ${input.description}
157362
157676
  key: v2.key,
157363
157677
  scope: v2.scope,
157364
157678
  ...v2.actorId !== void 0 ? { actorId: v2.actorId } : {},
157679
+ ...v2.projectId !== void 0 ? { projectId: v2.projectId } : {},
157365
157680
  ...v2.connectorId !== void 0 ? { connectorId: v2.connectorId } : {},
157366
157681
  ...v2.overrides !== void 0 ? { overrides: v2.overrides } : {},
157682
+ // 缺省 "env"(存量行为不变,敏感项需显式选 "ref",ADR §4.4 / 待确认 3)
157683
+ deliveryMode: v2.deliveryMode ?? "env",
157367
157684
  valueEncrypted: v2.encrypted === false ? `plain:${v2.value}` : this.encrypt(v2.value),
157368
157685
  updatedAt: this.now()
157369
157686
  });
@@ -157399,8 +157716,11 @@ ${input.description}
157399
157716
  key: r.key,
157400
157717
  scope: r.scope,
157401
157718
  ...r.actorId !== void 0 ? { actorId: r.actorId } : {},
157719
+ ...r.projectId !== void 0 ? { projectId: r.projectId } : {},
157402
157720
  ...r.connectorId !== void 0 ? { connectorId: r.connectorId } : {},
157403
157721
  ...r.overrides !== void 0 ? { overrides: r.overrides } : {},
157722
+ deliveryMode: r.deliveryMode ?? "env",
157723
+ // 缺省视为 env(ADR §4.4;存量行读侧统一回填)
157404
157724
  encrypted: !isPlain,
157405
157725
  maskedValue: isPlain ? r.valueEncrypted.slice(6) : mask(this.decrypt(r.valueEncrypted)),
157406
157726
  updatedAt: r.updatedAt
@@ -157424,8 +157744,13 @@ ${input.description}
157424
157744
  }
157425
157745
  return this.revealVariable(key, void 0);
157426
157746
  }
157427
- deleteVariable(key, actorId) {
157428
- return this.opts.store.deleteVariable(key, actorId);
157747
+ deleteVariable(key, actorId, scoping) {
157748
+ return this.opts.store.deleteVariable(key, actorId, scoping);
157749
+ }
157750
+ /** 该 key 是否在**任一**作用域存在(reveal 判权失败时区分 not_found / no_permission,仅 server 侧审计用;§4.5.2)。 */
157751
+ async hasVariableKey(key) {
157752
+ const rows = await this.opts.store.listVariables();
157753
+ return rows.some((r) => r.key === key);
157429
157754
  }
157430
157755
  async resolveActorEnv(actorId) {
157431
157756
  const cfg = await this.opts.store.latestConfig(actorId);
@@ -157455,6 +157780,64 @@ ${input.description}
157455
157780
  }
157456
157781
  return env;
157457
157782
  }
157783
+ /** 密文 → 明文(与 resolveActorEnv 内联的 decode 同规则:plain: 前缀直读,否则 AES 解密)。 */
157784
+ decodeCiphertext(ct) {
157785
+ return ct.startsWith("plain:") ? ct.slice(6) : this.decrypt(ct);
157786
+ }
157787
+ /**
157788
+ * ADR 凭据保险库 §4.3 / ADR 0125:按「项目」解析 project 变量为明文条目(含 deliveryMode)。
157789
+ * store 未实现 resolveProjectVariables(旧实现)时返回空——调用方跳过 project 层。
157790
+ */
157791
+ async resolveProjectVariables(projectId) {
157792
+ if (!this.opts.store.resolveProjectVariables) return [];
157793
+ const rows = await this.opts.store.resolveProjectVariables(projectId);
157794
+ return rows.map((r) => ({ key: r.key, value: this.decodeCiphertext(r.valueEncrypted), deliveryMode: r.deliveryMode ?? "env" }));
157795
+ }
157796
+ /**
157797
+ * ADR 凭据保险库 §4.3 / ADR 0125:派单/reveal 用的**合并后**变量条目(明文 + deliveryMode)。
157798
+ * 优先级低→高:global < project < personal(同 key 时高层覆盖低层,personal 最优先)。
157799
+ * 连接器门禁与 resolveActorEnv 同源(未启用的连接器变量不注入)。
157800
+ * - `opts.projectId` 给出时才叠加 project 层;缺省 = 只有 personal+global(存量行为)。
157801
+ */
157802
+ async resolveActorProvisionEntries(actorId, opts) {
157803
+ const cfg = await this.opts.store.latestConfig(actorId);
157804
+ const enabledConnectors = await this.effectiveEnabledConnectors(actorId, cfg);
157805
+ if (opts?.refreshConnectors !== false) {
157806
+ await refreshEnabledConnectorCredentials(
157807
+ this,
157808
+ createAllConnectors(),
157809
+ actorId,
157810
+ (slug6) => enabledConnectors.has(slug6)
157811
+ );
157812
+ }
157813
+ const rows = await this.opts.store.listVariables(actorId);
157814
+ const projectRows = opts?.projectId && this.opts.store.resolveProjectVariables ? await this.opts.store.resolveProjectVariables(opts.projectId) : [];
157815
+ const merged = /* @__PURE__ */ new Map();
157816
+ const gated = (row) => !row.connectorId || enabledConnectors.has(row.connectorId);
157817
+ const put = (row) => {
157818
+ if (!gated(row)) return;
157819
+ merged.set(row.key, { key: row.key, value: this.decodeCiphertext(row.valueEncrypted), deliveryMode: row.deliveryMode ?? "env" });
157820
+ };
157821
+ for (const row of rows) {
157822
+ if (row.scope === "global" && !row.actorId) put(row);
157823
+ }
157824
+ for (const row of projectRows) {
157825
+ if (row.scope === "project") put(row);
157826
+ }
157827
+ for (const row of rows) {
157828
+ if (row.scope === "personal" && (!row.actorId || row.actorId === actorId)) put(row);
157829
+ }
157830
+ return [...merged.values()];
157831
+ }
157832
+ /**
157833
+ * ADR 凭据保险库 §4.5.1:reveal 的判权即「能否被解析」——该 key 通过 §4.3 合并顺序对本 caller
157834
+ * 能得出值即可读,返回明文;否则返回 null(缺 key / 无权,统一由路由映射成 403,不区分)。
157835
+ */
157836
+ async resolveRevealValue(actorId, key, opts) {
157837
+ const entries = await this.resolveActorProvisionEntries(actorId, { ...opts, refreshConnectors: false });
157838
+ const hit = entries.find((e) => e.key === key);
157839
+ return hit ? { value: hit.value } : null;
157840
+ }
157458
157841
  encrypt(plain) {
157459
157842
  const iv = (0, import_node_crypto22.randomBytes)(12);
157460
157843
  const cipher = (0, import_node_crypto22.createCipheriv)("aes-256-gcm", this.opts.variableKey, iv);
@@ -158463,7 +158846,7 @@ var init_skill_materializer = __esm({
158463
158846
 
158464
158847
  // ../server/src/governance/connector-skills.ts
158465
158848
  function connectorSkillsDir(dataDir, connectorId) {
158466
- return (0, import_node_path12.join)(dataDir, "connector-skills", connectorId);
158849
+ return (0, import_node_path14.join)(dataDir, "connector-skills", connectorId);
158467
158850
  }
158468
158851
  function parseConnectorSkillId(id) {
158469
158852
  if (!id.startsWith("connector:")) return null;
@@ -158473,17 +158856,17 @@ function parseConnectorSkillId(id) {
158473
158856
  return { connectorId: rest.slice(0, i), slug: rest.slice(i + 1) };
158474
158857
  }
158475
158858
  async function collect(root, dir, out) {
158476
- for (const e of await (0, import_promises6.readdir)(dir, { withFileTypes: true })) {
158477
- const abs = (0, import_node_path12.join)(dir, e.name);
158859
+ for (const e of await (0, import_promises8.readdir)(dir, { withFileTypes: true })) {
158860
+ const abs = (0, import_node_path14.join)(dir, e.name);
158478
158861
  if (e.isDirectory()) await collect(root, abs, out);
158479
- else if (e.isFile()) out[(0, import_node_path12.relative)(root, abs).split(/[\\/]/).join("/")] = await (0, import_promises6.readFile)(abs, "utf8");
158862
+ else if (e.isFile()) out[(0, import_node_path14.relative)(root, abs).split(/[\\/]/).join("/")] = await (0, import_promises8.readFile)(abs, "utf8");
158480
158863
  }
158481
158864
  }
158482
158865
  async function readLocalPack(dataDir, connectorId) {
158483
158866
  const root = connectorSkillsDir(dataDir, connectorId);
158484
158867
  let slugs;
158485
158868
  try {
158486
- slugs = (await (0, import_promises6.readdir)(root, { withFileTypes: true })).filter((d) => d.isDirectory()).map((d) => d.name);
158869
+ slugs = (await (0, import_promises8.readdir)(root, { withFileTypes: true })).filter((d) => d.isDirectory()).map((d) => d.name);
158487
158870
  } catch {
158488
158871
  return [];
158489
158872
  }
@@ -158491,7 +158874,7 @@ async function readLocalPack(dataDir, connectorId) {
158491
158874
  for (const slug6 of slugs) {
158492
158875
  const files = {};
158493
158876
  try {
158494
- await collect((0, import_node_path12.join)(root, slug6), (0, import_node_path12.join)(root, slug6), files);
158877
+ await collect((0, import_node_path14.join)(root, slug6), (0, import_node_path14.join)(root, slug6), files);
158495
158878
  } catch {
158496
158879
  continue;
158497
158880
  }
@@ -158507,17 +158890,17 @@ async function readLocalPack(dataDir, connectorId) {
158507
158890
  async function writePack(dataDir, connectorId, pack) {
158508
158891
  const root = connectorSkillsDir(dataDir, connectorId);
158509
158892
  const tmp = `${root}.tmp`;
158510
- await (0, import_promises6.rm)(tmp, { recursive: true, force: true });
158893
+ await (0, import_promises8.rm)(tmp, { recursive: true, force: true });
158511
158894
  for (const s2 of pack) {
158512
158895
  for (const [rel, content] of Object.entries(s2.files)) {
158513
- const abs = (0, import_node_path12.join)(tmp, s2.slug, rel);
158514
- await (0, import_promises6.mkdir)((0, import_node_path12.dirname)(abs), { recursive: true });
158515
- await (0, import_promises6.writeFile)(abs, content, "utf8");
158896
+ const abs = (0, import_node_path14.join)(tmp, s2.slug, rel);
158897
+ await (0, import_promises8.mkdir)((0, import_node_path14.dirname)(abs), { recursive: true });
158898
+ await (0, import_promises8.writeFile)(abs, content, "utf8");
158516
158899
  }
158517
158900
  }
158518
- await (0, import_promises6.rm)(root, { recursive: true, force: true });
158519
- await (0, import_promises6.mkdir)((0, import_node_path12.dirname)(root), { recursive: true });
158520
- await (0, import_promises6.rename)(tmp, root);
158901
+ await (0, import_promises8.rm)(root, { recursive: true, force: true });
158902
+ await (0, import_promises8.mkdir)((0, import_node_path14.dirname)(root), { recursive: true });
158903
+ await (0, import_promises8.rename)(tmp, root);
158521
158904
  }
158522
158905
  function packMatches(local, remote) {
158523
158906
  if (local.length !== remote.length) return false;
@@ -158544,18 +158927,21 @@ async function syncConnectorSkills(args) {
158544
158927
  log3(`[connector-skills] ${args.connectorId}\uFF1A\u5DF2\u4ECE\u5382\u5546\u540C\u6B65 ${fresh.length} \u4E2A\u6280\u80FD`);
158545
158928
  return { skills: fresh, outcome: "fetched" };
158546
158929
  }
158547
- var import_promises6, import_node_path12, toId;
158930
+ var import_promises8, import_node_path14, toId;
158548
158931
  var init_connector_skills = __esm({
158549
158932
  "../server/src/governance/connector-skills.ts"() {
158550
158933
  "use strict";
158551
- import_promises6 = require("node:fs/promises");
158552
- import_node_path12 = require("node:path");
158934
+ import_promises8 = require("node:fs/promises");
158935
+ import_node_path14 = require("node:path");
158553
158936
  init_src5();
158554
158937
  toId = (connectorId, slug6) => `connector:${connectorId}/${slug6}`;
158555
158938
  }
158556
158939
  });
158557
158940
 
158558
158941
  // ../server/src/domains/actors/routes.ts
158942
+ function isKnownRevealSource(source) {
158943
+ return CREDENTIAL_REVEAL_SOURCES.has(source) || source.startsWith("agent-skill:");
158944
+ }
158559
158945
  function positionsFromPayload(body) {
158560
158946
  if (!body) return void 0;
158561
158947
  if (Object.prototype.hasOwnProperty.call(body, "roles")) {
@@ -158588,6 +158974,8 @@ function actorApiRecord(actor) {
158588
158974
  }
158589
158975
  function actorsDomain(opts) {
158590
158976
  const { resolveCtx } = opts;
158977
+ const revealLimits = opts.credentialRevealLimits ?? { perSession: 200, perSessionPerKey: 20 };
158978
+ const revealLimiter = new RevealRateLimiter(revealLimits.perSession, revealLimits.perSessionPerKey);
158591
158979
  return (router) => {
158592
158980
  router.get("/api/actors", async (req) => {
158593
158981
  const { service } = await resolveCtx(req.auth.companyId);
@@ -158827,11 +159215,23 @@ function actorsDomain(opts) {
158827
159215
  const { service } = await resolveCtx(req.auth.companyId);
158828
159216
  const b2 = req.body;
158829
159217
  if (!b2?.key) throw new ApiError(400, "BAD_REQUEST", "\u7F3A\u5C11 key");
159218
+ const scope = b2.scope ?? "global";
159219
+ if (scope === "project" && !b2.projectId) {
159220
+ throw new ApiError(400, "BAD_REQUEST", "scope=project \u5FC5\u987B\u63D0\u4F9B projectId");
159221
+ }
159222
+ if (scope === "project" && opts.projectExists && !await opts.projectExists(b2.projectId)) {
159223
+ throw new ApiError(400, "BAD_REQUEST", `\u9879\u76EE\u4E0D\u5B58\u5728\uFF1A${b2.projectId}\uFF08scope=project \u53D8\u91CF\u7684 projectId \u5FC5\u987B\u662F\u771F\u5B9E\u9879\u76EE\uFF09`);
159224
+ }
159225
+ if (b2.deliveryMode !== void 0 && b2.deliveryMode !== "env" && b2.deliveryMode !== "ref") {
159226
+ throw new ApiError(400, "BAD_REQUEST", "deliveryMode \u53EA\u80FD\u662F env \u6216 ref");
159227
+ }
158830
159228
  if (b2.value === void 0 || b2.value === "") return { status: 200, body: { ok: true } };
158831
159229
  await service.putVariable({
158832
159230
  key: b2.key,
158833
159231
  value: b2.value,
158834
- scope: b2.scope ?? "global",
159232
+ scope,
159233
+ ...scope === "project" ? { projectId: b2.projectId } : {},
159234
+ ...b2.deliveryMode !== void 0 ? { deliveryMode: b2.deliveryMode } : {},
158835
159235
  ...b2.connectorId !== void 0 ? { connectorId: b2.connectorId } : {},
158836
159236
  ...b2.overrides !== void 0 ? { overrides: b2.overrides } : {},
158837
159237
  ...b2.encrypted !== void 0 ? { encrypted: b2.encrypted } : {}
@@ -158840,7 +159240,12 @@ function actorsDomain(opts) {
158840
159240
  });
158841
159241
  router.delete("/api/variables/:key", async (req) => {
158842
159242
  const { service } = await resolveCtx(req.auth.companyId);
158843
- await service.deleteVariable(req.params.key);
159243
+ const projectId = req.query.get("projectId") ?? void 0;
159244
+ await service.deleteVariable(
159245
+ req.params.key,
159246
+ void 0,
159247
+ projectId ? { projectId } : void 0
159248
+ );
158844
159249
  return { status: 200, body: { ok: true } };
158845
159250
  });
158846
159251
  router.get("/api/actors/:id/variables", async (req) => {
@@ -158852,6 +159257,9 @@ function actorsDomain(opts) {
158852
159257
  const actorId = req.params.id;
158853
159258
  const b2 = req.body;
158854
159259
  if (!b2?.key) throw new ApiError(400, "BAD_REQUEST", "\u7F3A\u5C11 key");
159260
+ if (b2.deliveryMode !== void 0 && b2.deliveryMode !== "env" && b2.deliveryMode !== "ref") {
159261
+ throw new ApiError(400, "BAD_REQUEST", "deliveryMode \u53EA\u80FD\u662F env \u6216 ref");
159262
+ }
158855
159263
  if (b2.value === void 0 || b2.value === "") return { status: 200, body: { ok: true } };
158856
159264
  if (b2.connectorId === void 0) {
158857
159265
  const existing = await service.listVariables(actorId);
@@ -158869,6 +159277,7 @@ function actorsDomain(opts) {
158869
159277
  value: b2.value,
158870
159278
  actorId,
158871
159279
  scope: "personal",
159280
+ ...b2.deliveryMode !== void 0 ? { deliveryMode: b2.deliveryMode } : {},
158872
159281
  ...b2.connectorId !== void 0 ? { connectorId: b2.connectorId } : {},
158873
159282
  ...b2.overrides !== void 0 ? { overrides: b2.overrides } : {},
158874
159283
  ...b2.encrypted !== void 0 ? { encrypted: b2.encrypted } : {}
@@ -158880,6 +159289,51 @@ function actorsDomain(opts) {
158880
159289
  await service.deleteVariable(req.params.key, req.params.id);
158881
159290
  return { status: 200, body: { ok: true } };
158882
159291
  });
159292
+ router.post("/api/variables/reveal", async (req) => {
159293
+ const { service } = await resolveCtx(req.auth.companyId);
159294
+ const b2 = req.body;
159295
+ const rawKey = typeof b2?.key === "string" ? b2.key.trim() : "";
159296
+ if (!rawKey) throw new ApiError(400, "BAD_REQUEST", "\u7F3A\u5C11 key");
159297
+ const key = parseVarRef(rawKey) ?? rawKey;
159298
+ const source = typeof b2?.source === "string" && b2.source.trim() || "agent-cli";
159299
+ if (!isKnownRevealSource(source)) throw new ApiError(400, "BAD_REQUEST", `\u672A\u8BC6\u522B\u7684 source\u300C${source}\u300D\uFF08\xA74.5.4\uFF1A\u53EA\u63A5\u53D7\u8BC6\u522B\u503C\uFF09`);
159300
+ const actor = req.auth.actor;
159301
+ const artifactId = req.auth.dispatch?.artifactId;
159302
+ const sessionId = req.auth.dispatch?.sessionId;
159303
+ const now = Date.now();
159304
+ const timestamp = new Date(now).toISOString();
159305
+ const sessionKey = sessionId ?? `${actor}::${artifactId ?? "no-artifact"}`;
159306
+ const dispatchFields = { ...sessionId ? { sessionId } : {}, ...artifactId ? { artifactId } : {} };
159307
+ const throttled = revealLimiter.check(sessionKey, key, now);
159308
+ if (throttled) {
159309
+ await opts.credentialAudit?.({ kind: "credential_reveal_throttled", actor, variableKey: key, source, timestamp, scope: throttled, ...dispatchFields });
159310
+ return { status: 429, body: { code: "throttled", key, scope: throttled } };
159311
+ }
159312
+ const scope = artifactId && opts.resolveDispatchScope ? await opts.resolveDispatchScope(artifactId) : {};
159313
+ const resolved = await service.resolveRevealValue(actor, key, scope);
159314
+ if (!resolved) {
159315
+ const reason = await service.hasVariableKey(key) ? "no_permission" : "not_found";
159316
+ await opts.credentialAudit?.({ kind: "credential_reveal_denied", actor, variableKey: key, source, timestamp, reason, ...dispatchFields });
159317
+ return { status: 403, body: { code: "denied", key } };
159318
+ }
159319
+ await opts.credentialAudit?.({ kind: "credential_reveal", actor, variableKey: key, source, timestamp, ...dispatchFields });
159320
+ return { status: 200, body: { value: resolved.value } };
159321
+ });
159322
+ router.get("/api/variables/reveal-audit", async (req) => {
159323
+ if (!opts.readCredentialAudit) return { status: 501, body: { error: "credential audit reader \u672A\u914D\u7F6E" } };
159324
+ const actor = req.query.get("actor") ?? void 0;
159325
+ const key = req.query.get("key") ?? void 0;
159326
+ const kind = req.query.get("kind") ?? void 0;
159327
+ const limitRaw = req.query.get("limit");
159328
+ const limit = limitRaw ? Math.min(Math.max(parseInt(limitRaw, 10) || 50, 1), 500) : 50;
159329
+ const items = await opts.readCredentialAudit({
159330
+ ...actor ? { actor } : {},
159331
+ ...key ? { key } : {},
159332
+ ...kind ? { kind } : {},
159333
+ limit
159334
+ });
159335
+ return { status: 200, body: { items } };
159336
+ });
158883
159337
  router.get("/api/skills/market", async (req) => {
158884
159338
  const { service } = await resolveCtx(req.auth.companyId);
158885
159339
  const [catalog, installed] = await Promise.all([
@@ -159038,6 +159492,18 @@ function actorsDomain(opts) {
159038
159492
  const results = await opts.refreshConnectorSkills();
159039
159493
  return { status: 200, body: { results } };
159040
159494
  });
159495
+ router.get("/api/connector-credentials", async (req) => {
159496
+ const { service } = await resolveCtx(req.auth.companyId);
159497
+ const actorId = req.auth.actor;
159498
+ const entries = await service.resolveActorProvisionEntries(actorId);
159499
+ const vars = {};
159500
+ for (const e of entries) vars[e.key] = e.value;
159501
+ const connectorCreds = collectAllConnectorCredentials({
159502
+ vars,
159503
+ actorName: vars["GIT_AUTHOR_NAME"] ?? actorId.split(":").pop() ?? "oasis-agent"
159504
+ });
159505
+ return { status: 200, body: { connectorCreds } };
159506
+ });
159041
159507
  router.get("/api/skill-cache/manifest", async (req) => {
159042
159508
  const { service } = await resolveCtx(req.auth.companyId);
159043
159509
  const builtins = opts.getBuiltinSkills?.() ?? [];
@@ -159316,12 +159782,15 @@ function actorsDomain(opts) {
159316
159782
  });
159317
159783
  };
159318
159784
  }
159785
+ var CREDENTIAL_REVEAL_SOURCES, RevealRateLimiter;
159319
159786
  var init_routes = __esm({
159320
159787
  "../server/src/domains/actors/routes.ts"() {
159321
159788
  "use strict";
159322
159789
  init_src();
159323
159790
  init_src2();
159324
159791
  init_router();
159792
+ init_src();
159793
+ init_src5();
159325
159794
  init_memory();
159326
159795
  init_skill_fetcher();
159327
159796
  init_stats();
@@ -159330,6 +159799,44 @@ var init_routes = __esm({
159330
159799
  init_image_upload();
159331
159800
  init_skill_materializer();
159332
159801
  init_connector_skills();
159802
+ CREDENTIAL_REVEAL_SOURCES = /* @__PURE__ */ new Set(["agent-cli", "smoke-test", "oasis-internal"]);
159803
+ RevealRateLimiter = class {
159804
+ constructor(perSession, perSessionPerKey, ttlMs = 2 * 60 * 6e4) {
159805
+ this.perSession = perSession;
159806
+ this.perSessionPerKey = perSessionPerKey;
159807
+ this.ttlMs = ttlMs;
159808
+ }
159809
+ sessionCounts = /* @__PURE__ */ new Map();
159810
+ sessionKeyCounts = /* @__PURE__ */ new Map();
159811
+ /** 记一次尝试;返回命中的限流档(若超限)或 null(放行)。 */
159812
+ check(sessionKey, varKey, now) {
159813
+ this.prune(now);
159814
+ const sk = `${sessionKey}::${varKey}`;
159815
+ const s2 = this.sessionCounts.get(sessionKey) ?? { count: 0, last: now };
159816
+ const k2 = this.sessionKeyCounts.get(sk) ?? { count: 0, last: now };
159817
+ if (k2.count >= this.perSessionPerKey) {
159818
+ k2.last = now;
159819
+ this.sessionKeyCounts.set(sk, k2);
159820
+ return "session_key";
159821
+ }
159822
+ if (s2.count >= this.perSession) {
159823
+ s2.last = now;
159824
+ this.sessionCounts.set(sessionKey, s2);
159825
+ return "session";
159826
+ }
159827
+ s2.count += 1;
159828
+ s2.last = now;
159829
+ this.sessionCounts.set(sessionKey, s2);
159830
+ k2.count += 1;
159831
+ k2.last = now;
159832
+ this.sessionKeyCounts.set(sk, k2);
159833
+ return null;
159834
+ }
159835
+ prune(now) {
159836
+ for (const [key, v2] of this.sessionCounts) if (now - v2.last > this.ttlMs) this.sessionCounts.delete(key);
159837
+ for (const [key, v2] of this.sessionKeyCounts) if (now - v2.last > this.ttlMs) this.sessionKeyCounts.delete(key);
159838
+ }
159839
+ };
159333
159840
  }
159334
159841
  });
159335
159842
 
@@ -159428,7 +159935,7 @@ function createActorsDomain(opts) {
159428
159935
  return {
159429
159936
  service: defaultCtx.service,
159430
159937
  resolveCtx,
159431
- register: actorsDomain({ resolveCtx, ...opts.trace ? { trace: opts.trace } : {}, ...opts.listBuiltinSkills ? { listBuiltinSkills: opts.listBuiltinSkills } : {}, ...opts.getBuiltinSkills ? { getBuiltinSkills: opts.getBuiltinSkills } : {}, ...opts.getConnectorSkills ? { getConnectorSkills: opts.getConnectorSkills } : {}, ...opts.refreshConnectorSkills ? { refreshConnectorSkills: opts.refreshConnectorSkills } : {}, ...opts.memory ? { memory: opts.memory } : {} })
159938
+ register: actorsDomain({ resolveCtx, ...opts.trace ? { trace: opts.trace } : {}, ...opts.listBuiltinSkills ? { listBuiltinSkills: opts.listBuiltinSkills } : {}, ...opts.getBuiltinSkills ? { getBuiltinSkills: opts.getBuiltinSkills } : {}, ...opts.getConnectorSkills ? { getConnectorSkills: opts.getConnectorSkills } : {}, ...opts.refreshConnectorSkills ? { refreshConnectorSkills: opts.refreshConnectorSkills } : {}, ...opts.memory ? { memory: opts.memory } : {}, ...opts.resolveDispatchScope ? { resolveDispatchScope: opts.resolveDispatchScope } : {}, ...opts.projectExists ? { projectExists: opts.projectExists } : {}, ...opts.credentialAudit ? { credentialAudit: opts.credentialAudit } : {}, ...opts.readCredentialAudit ? { readCredentialAudit: opts.readCredentialAudit } : {}, ...opts.credentialRevealLimits ? { credentialRevealLimits: opts.credentialRevealLimits } : {} })
159432
159939
  };
159433
159940
  }
159434
159941
  var import_node_crypto24;
@@ -161857,10 +162364,10 @@ function nodesDomain(deps) {
161857
162364
  }
161858
162365
  }
161859
162366
  try {
161860
- const here = (0, import_node_path13.dirname)((0, import_node_url4.fileURLToPath)(__esm_import_meta_url));
162367
+ const here = (0, import_node_path15.dirname)((0, import_node_url4.fileURLToPath)(__esm_import_meta_url));
161861
162368
  for (const rel of ["../../../../node-daemon/package.json", "../../../../../node-daemon/package.json"]) {
161862
162369
  try {
161863
- const raw = (0, import_node_fs11.readFileSync)((0, import_node_path13.resolve)(here, rel), "utf8");
162370
+ const raw = (0, import_node_fs11.readFileSync)((0, import_node_path15.resolve)(here, rel), "utf8");
161864
162371
  const pkg = JSON.parse(raw);
161865
162372
  if (pkg.version) return { version: pkg.version, source: "monorepo" };
161866
162373
  } catch {
@@ -162172,14 +162679,14 @@ function nodesDomain(deps) {
162172
162679
  });
162173
162680
  };
162174
162681
  }
162175
- var import_node_crypto26, import_node_fs11, import_node_url4, import_node_path13;
162682
+ var import_node_crypto26, import_node_fs11, import_node_url4, import_node_path15;
162176
162683
  var init_routes5 = __esm({
162177
162684
  "../server/src/domains/nodes/routes.ts"() {
162178
162685
  "use strict";
162179
162686
  import_node_crypto26 = require("node:crypto");
162180
162687
  import_node_fs11 = require("node:fs");
162181
162688
  import_node_url4 = require("node:url");
162182
- import_node_path13 = require("node:path");
162689
+ import_node_path15 = require("node:path");
162183
162690
  init_src4();
162184
162691
  init_connect_script();
162185
162692
  init_node_health();
@@ -163336,6 +163843,7 @@ function buildOrganizationUsageSummary(input) {
163336
163843
  const runtimeEmployees = /* @__PURE__ */ new Map();
163337
163844
  const runtimeInstanceKind = /* @__PURE__ */ new Map();
163338
163845
  const runtimeHostnames = input.runtimeHostnames ?? /* @__PURE__ */ new Map();
163846
+ const runtimeNodeNames = input.runtimeNodeNames ?? /* @__PURE__ */ new Map();
163339
163847
  for (const run of input.runs) {
163340
163848
  const usage = usageOf(run);
163341
163849
  if (!usage) {
@@ -163466,11 +163974,27 @@ function buildOrganizationUsageSummary(input) {
163466
163974
  (e) => e.share,
163467
163975
  (e, s2) => ({ ...e, share: s2 })
163468
163976
  );
163977
+ function parseNodeId(rtKey) {
163978
+ const parts = rtKey.split(":");
163979
+ if (parts.length >= 3 && parts[0] === "runtime") return parts[1];
163980
+ return "";
163981
+ }
163982
+ function resolveDisplayName(rtKey, kind) {
163983
+ const nodeId = parseNodeId(rtKey);
163984
+ if (nodeId) {
163985
+ const name = runtimeNodeNames.get(nodeId);
163986
+ if (name) return name;
163987
+ }
163988
+ const h = runtimeHostnames.get(rtKey);
163989
+ if (h) return h;
163990
+ if (nodeId) return nodeId;
163991
+ return kind;
163992
+ }
163469
163993
  function resolveHostname(rtKey, kind) {
163470
163994
  const h = runtimeHostnames.get(rtKey);
163471
163995
  if (h) return h;
163472
- const parts = rtKey.split(":");
163473
- if (parts.length >= 3 && parts[0] === "runtime") return parts[1];
163996
+ const nodeId = parseNodeId(rtKey);
163997
+ if (nodeId) return nodeId;
163474
163998
  return kind;
163475
163999
  }
163476
164000
  const runtimeRows = normalizeShares(
@@ -163498,6 +164022,8 @@ function buildOrganizationUsageSummary(input) {
163498
164022
  );
163499
164023
  return {
163500
164024
  runtimeInstanceId: rtKey,
164025
+ nodeId: parseNodeId(rtKey),
164026
+ displayName: resolveDisplayName(rtKey, kind),
163501
164027
  hostname: resolveHostname(rtKey, kind),
163502
164028
  runtimeKind: kind,
163503
164029
  usage,
@@ -163535,12 +164061,12 @@ function nameKey(name) {
163535
164061
  function cleanName(name) {
163536
164062
  return name.trim().replace(/\s+/g, " ");
163537
164063
  }
163538
- var import_node_fs12, import_node_path14, import_node_crypto27, SEP, keyOf, prefixOf, MemoryWorkorderTagStore, FileWorkorderTagStore;
164064
+ var import_node_fs12, import_node_path16, import_node_crypto27, SEP, keyOf, prefixOf, MemoryWorkorderTagStore, FileWorkorderTagStore;
163539
164065
  var init_tags = __esm({
163540
164066
  "../server/src/domains/collab/tags.ts"() {
163541
164067
  "use strict";
163542
164068
  import_node_fs12 = __toESM(require("node:fs"), 1);
163543
- import_node_path14 = __toESM(require("node:path"), 1);
164069
+ import_node_path16 = __toESM(require("node:path"), 1);
163544
164070
  import_node_crypto27 = __toESM(require("node:crypto"), 1);
163545
164071
  SEP = "::";
163546
164072
  keyOf = (companyId, id) => `${companyId}${SEP}${id}`;
@@ -163626,7 +164152,7 @@ var init_tags = __esm({
163626
164152
  }
163627
164153
  persist() {
163628
164154
  try {
163629
- import_node_fs12.default.mkdirSync(import_node_path14.default.dirname(this.file), { recursive: true });
164155
+ import_node_fs12.default.mkdirSync(import_node_path16.default.dirname(this.file), { recursive: true });
163630
164156
  const tmp = `${this.file}.tmp`;
163631
164157
  import_node_fs12.default.writeFileSync(tmp, JSON.stringify(Object.fromEntries(this.map), null, 2));
163632
164158
  import_node_fs12.default.renameSync(tmp, this.file);
@@ -163767,17 +164293,22 @@ function collabDomain(opts) {
163767
164293
  return { status: 400, body: { error: { code: "bad_request", message: "timeZone \u65E0\u6548" } } };
163768
164294
  }
163769
164295
  const { kernel } = await resolveCtx(req.auth.companyId);
163770
- const [runs, runtimeRecords] = await Promise.all([
164296
+ const [runs, runtimeRecords, nodeRecords] = await Promise.all([
163771
164297
  opts.trace.listUsageRuns({ from, to }),
163772
- opts.listRuntimes ? opts.listRuntimes().catch(() => []) : Promise.resolve([])
164298
+ opts.listRuntimes ? opts.listRuntimes().catch(() => []) : Promise.resolve([]),
164299
+ opts.listNodes ? opts.listNodes().catch(() => []) : Promise.resolve([])
163773
164300
  ]);
163774
164301
  const runtimeHostnames = /* @__PURE__ */ new Map();
163775
164302
  for (const rt of runtimeRecords) {
163776
164303
  if (rt.id && rt.hostname) runtimeHostnames.set(rt.id, rt.hostname);
163777
164304
  }
164305
+ const runtimeNodeNames = /* @__PURE__ */ new Map();
164306
+ for (const n of nodeRecords) {
164307
+ if (n.id && n.name) runtimeNodeNames.set(n.id, n.name);
164308
+ }
163778
164309
  return {
163779
164310
  status: 200,
163780
- body: buildOrganizationUsageSummary({ model: kernel.model, runs, bucketKind: bucket, timeZone, runtimeHostnames })
164311
+ body: buildOrganizationUsageSummary({ model: kernel.model, runs, bucketKind: bucket, timeZone, runtimeHostnames, runtimeNodeNames })
163781
164312
  };
163782
164313
  });
163783
164314
  router.get("/api/workorders", async (req) => {
@@ -164577,10 +165108,10 @@ function tokenType(p2) {
164577
165108
  function parseOtlpMetricsUsage(body) {
164578
165109
  const req = body;
164579
165110
  const out = [];
164580
- for (const rm4 of req.resourceMetrics ?? []) {
164581
- const runId = attrString(rm4.resource?.attributes, "oasis.run_id");
165111
+ for (const rm6 of req.resourceMetrics ?? []) {
165112
+ const runId = attrString(rm6.resource?.attributes, "oasis.run_id");
164582
165113
  if (!runId) continue;
164583
- const actorId = attrString(rm4.resource?.attributes, "oasis.actor_id");
165114
+ const actorId = attrString(rm6.resource?.attributes, "oasis.actor_id");
164584
165115
  let input = 0;
164585
165116
  let output = 0;
164586
165117
  let cacheRead = 0;
@@ -164588,7 +165119,7 @@ function parseOtlpMetricsUsage(body) {
164588
165119
  let costUsd = 0;
164589
165120
  let sawToken = false;
164590
165121
  let sawCost = false;
164591
- for (const sm of rm4.scopeMetrics ?? []) {
165122
+ for (const sm of rm6.scopeMetrics ?? []) {
164592
165123
  for (const m2 of sm.metrics ?? []) {
164593
165124
  const name = (m2.name ?? "").toLowerCase();
164594
165125
  const points = m2.sum?.dataPoints ?? m2.gauge?.dataPoints ?? [];
@@ -164964,6 +165495,30 @@ var init_routes6 = __esm({
164964
165495
  }
164965
165496
  });
164966
165497
 
165498
+ // ../server/src/domains/trace/trace-health.ts
165499
+ function createTraceHealth() {
165500
+ const unhealthy = /* @__PURE__ */ new Set();
165501
+ return {
165502
+ markUnhealthy(runId) {
165503
+ unhealthy.add(runId);
165504
+ },
165505
+ markHealthy(runId) {
165506
+ unhealthy.delete(runId);
165507
+ },
165508
+ isHealthy(runId) {
165509
+ return !unhealthy.has(runId);
165510
+ },
165511
+ forget(runId) {
165512
+ unhealthy.delete(runId);
165513
+ }
165514
+ };
165515
+ }
165516
+ var init_trace_health = __esm({
165517
+ "../server/src/domains/trace/trace-health.ts"() {
165518
+ "use strict";
165519
+ }
165520
+ });
165521
+
164967
165522
  // ../server/src/domains/trace/index.ts
164968
165523
  function createTraceDomain(opts) {
164969
165524
  const service = new TraceService({ store: opts.store });
@@ -164978,6 +165533,8 @@ var init_trace2 = __esm({
164978
165533
  init_routes6();
164979
165534
  init_sink();
164980
165535
  init_chat_parts();
165536
+ init_trace_health();
165537
+ init_resilient_appender();
164981
165538
  }
164982
165539
  });
164983
165540
 
@@ -167049,12 +167606,12 @@ function normalize(patch) {
167049
167606
  ...reviewRoles !== void 0 ? { reviewRoles: [...new Set(reviewRoles.map((r) => r.trim()).filter(Boolean))] } : {}
167050
167607
  };
167051
167608
  }
167052
- var import_node_fs13, import_node_path15, SEP2, keyOf2, prefixOf2, MemoryPlaybookOverrideStore, FilePlaybookOverrideStore;
167609
+ var import_node_fs13, import_node_path17, SEP2, keyOf2, prefixOf2, MemoryPlaybookOverrideStore, FilePlaybookOverrideStore;
167053
167610
  var init_overrides = __esm({
167054
167611
  "../server/src/domains/playbooks/overrides.ts"() {
167055
167612
  "use strict";
167056
167613
  import_node_fs13 = __toESM(require("node:fs"), 1);
167057
- import_node_path15 = __toESM(require("node:path"), 1);
167614
+ import_node_path17 = __toESM(require("node:path"), 1);
167058
167615
  SEP2 = "::";
167059
167616
  keyOf2 = (companyId, ref2, nodeKey) => `${companyId}${SEP2}${ref2}${SEP2}${nodeKey}`;
167060
167617
  prefixOf2 = (companyId, ref2) => `${companyId}${SEP2}${ref2}${SEP2}`;
@@ -167093,7 +167650,7 @@ var init_overrides = __esm({
167093
167650
  }
167094
167651
  persist() {
167095
167652
  try {
167096
- import_node_fs13.default.mkdirSync(import_node_path15.default.dirname(this.file), { recursive: true });
167653
+ import_node_fs13.default.mkdirSync(import_node_path17.default.dirname(this.file), { recursive: true });
167097
167654
  const tmp = `${this.file}.tmp`;
167098
167655
  import_node_fs13.default.writeFileSync(tmp, JSON.stringify(Object.fromEntries(this.map), null, 2));
167099
167656
  import_node_fs13.default.renameSync(tmp, this.file);
@@ -167532,12 +168089,12 @@ var init_daemon_adapter = __esm({
167532
168089
  });
167533
168090
 
167534
168091
  // ../server/src/side-map.ts
167535
- var import_node_fs14, import_node_path16, FileSideMap;
168092
+ var import_node_fs14, import_node_path18, FileSideMap;
167536
168093
  var init_side_map = __esm({
167537
168094
  "../server/src/side-map.ts"() {
167538
168095
  "use strict";
167539
168096
  import_node_fs14 = __toESM(require("node:fs"), 1);
167540
- import_node_path16 = __toESM(require("node:path"), 1);
168097
+ import_node_path18 = __toESM(require("node:path"), 1);
167541
168098
  FileSideMap = class {
167542
168099
  constructor(file) {
167543
168100
  this.file = file;
@@ -167564,7 +168121,7 @@ var init_side_map = __esm({
167564
168121
  }
167565
168122
  persist() {
167566
168123
  try {
167567
- import_node_fs14.default.mkdirSync(import_node_path16.default.dirname(this.file), { recursive: true });
168124
+ import_node_fs14.default.mkdirSync(import_node_path18.default.dirname(this.file), { recursive: true });
167568
168125
  const tmp = `${this.file}.tmp`;
167569
168126
  import_node_fs14.default.writeFileSync(tmp, JSON.stringify(Object.fromEntries(this.map), null, 2));
167570
168127
  import_node_fs14.default.renameSync(tmp, this.file);
@@ -169020,26 +169577,26 @@ ${ctx.nodeFault}
169020
169577
 
169021
169578
  // ../server/src/governance/builtin-skills.ts
169022
169579
  async function collectDir(root, dir, out) {
169023
- const entries = await (0, import_promises7.readdir)(dir, { withFileTypes: true });
169580
+ const entries = await (0, import_promises9.readdir)(dir, { withFileTypes: true });
169024
169581
  for (const e of entries) {
169025
- const abs = (0, import_node_path17.join)(dir, e.name);
169582
+ const abs = (0, import_node_path19.join)(dir, e.name);
169026
169583
  if (e.isDirectory()) {
169027
169584
  await collectDir(root, abs, out);
169028
169585
  } else if (e.isFile()) {
169029
- const info = await (0, import_promises7.stat)(abs);
169586
+ const info = await (0, import_promises9.stat)(abs);
169030
169587
  if (info.size > MAX_FILE_BYTES3) {
169031
169588
  console.warn(`[builtin-skills] skip oversized file (${info.size}B): ${abs}`);
169032
169589
  continue;
169033
169590
  }
169034
- const rel = (0, import_node_path17.relative)(root, abs).split(/[\\/]/).join("/");
169035
- out[rel] = await (0, import_promises7.readFile)(abs, "utf8");
169591
+ const rel = (0, import_node_path19.relative)(root, abs).split(/[\\/]/).join("/");
169592
+ out[rel] = await (0, import_promises9.readFile)(abs, "utf8");
169036
169593
  }
169037
169594
  }
169038
169595
  }
169039
169596
  async function loadBuiltinSkills(skillsDir) {
169040
169597
  let dirents;
169041
169598
  try {
169042
- dirents = await (0, import_promises7.readdir)(skillsDir, { withFileTypes: true });
169599
+ dirents = await (0, import_promises9.readdir)(skillsDir, { withFileTypes: true });
169043
169600
  } catch {
169044
169601
  return [];
169045
169602
  }
@@ -169047,7 +169604,7 @@ async function loadBuiltinSkills(skillsDir) {
169047
169604
  for (const d of dirents) {
169048
169605
  if (!d.isDirectory()) continue;
169049
169606
  const slug6 = d.name;
169050
- const skillDir = (0, import_node_path17.join)(skillsDir, slug6);
169607
+ const skillDir = (0, import_node_path19.join)(skillsDir, slug6);
169051
169608
  const files = {};
169052
169609
  try {
169053
169610
  await collectDir(skillDir, skillDir, files);
@@ -169075,12 +169632,12 @@ function materializeBuiltinSkills(builtins, skillsDirPrefix) {
169075
169632
  }
169076
169633
  return out;
169077
169634
  }
169078
- var import_promises7, import_node_path17, MAX_FILE_BYTES3;
169635
+ var import_promises9, import_node_path19, MAX_FILE_BYTES3;
169079
169636
  var init_builtin_skills = __esm({
169080
169637
  "../server/src/governance/builtin-skills.ts"() {
169081
169638
  "use strict";
169082
- import_promises7 = require("node:fs/promises");
169083
- import_node_path17 = require("node:path");
169639
+ import_promises9 = require("node:fs/promises");
169640
+ import_node_path19 = require("node:path");
169084
169641
  init_skill_fetcher();
169085
169642
  init_skill_materializer();
169086
169643
  MAX_FILE_BYTES3 = 1 << 20;
@@ -174572,6 +175129,21 @@ var init_postgres = __esm({
174572
175129
  });
174573
175130
 
174574
175131
  // ../storage/src/postgres-registry.ts
175132
+ function rowToVariable(row) {
175133
+ return {
175134
+ key: row.key,
175135
+ scope: row.scope === "connector" ? "global" : row.scope,
175136
+ ...row.actor_id !== null && row.actor_id !== void 0 ? { actorId: row.actor_id } : {},
175137
+ ...row.project_id !== null && row.project_id !== void 0 ? { projectId: row.project_id } : {},
175138
+ ...row.connector_id !== null && row.connector_id !== void 0 ? { connectorId: row.connector_id } : {},
175139
+ ...row.overrides !== null && row.overrides !== void 0 ? { overrides: row.overrides } : {},
175140
+ // 保留库里存的原始 delivery_mode(含未来 broker/wrapper 档,annotation d7456d5e Req3)——
175141
+ // **不塌成 env**,否则未知档位读回来变明文;仅 null/空回填 "env"(存量行)。派单侧按未知档保守处理。
175142
+ deliveryMode: typeof row.delivery_mode === "string" && row.delivery_mode ? row.delivery_mode : "env",
175143
+ valueEncrypted: row.value_encrypted,
175144
+ updatedAt: new Date(row.updated_at).toISOString()
175145
+ };
175146
+ }
174575
175147
  var ident4, PostgresRegistryStore, rowToActor, rowToConfig;
174576
175148
  var init_postgres_registry = __esm({
174577
175149
  "../storage/src/postgres-registry.ts"() {
@@ -174703,9 +175275,21 @@ var init_postgres_registry = __esm({
174703
175275
  value_encrypted text NOT NULL,
174704
175276
  updated_at timestamptz NOT NULL
174705
175277
  )`);
174706
- await pool.query(`CREATE UNIQUE INDEX IF NOT EXISTS "${s2}_variables_key_actor_uq" ON "${s2}".variables (key, COALESCE(actor_id, ''))`);
174707
175278
  await pool.query(`ALTER TABLE IF EXISTS "${s2}".variables ADD COLUMN IF NOT EXISTS overrides text`);
174708
175279
  await pool.query(`ALTER TABLE IF EXISTS "${s2}".variables ADD COLUMN IF NOT EXISTS actor_id text`);
175280
+ await pool.query(`ALTER TABLE IF EXISTS "${s2}".variables ADD COLUMN IF NOT EXISTS project_id text`);
175281
+ await pool.query(`ALTER TABLE IF EXISTS "${s2}".variables ADD COLUMN IF NOT EXISTS environment text`);
175282
+ await pool.query(`ALTER TABLE IF EXISTS "${s2}".variables ADD COLUMN IF NOT EXISTS delivery_mode text`);
175283
+ await pool.query(`DROP INDEX IF EXISTS "${s2}"."${s2}_variables_key_actor_uq"`);
175284
+ await pool.query(`DROP INDEX IF EXISTS "${s2}"."${s2}_variables_scope_uq"`);
175285
+ await pool.query(`
175286
+ DELETE FROM "${s2}".variables v
175287
+ WHERE v.ctid NOT IN (
175288
+ SELECT DISTINCT ON (key, COALESCE(actor_id, ''), COALESCE(project_id, '')) ctid
175289
+ FROM "${s2}".variables
175290
+ ORDER BY key, COALESCE(actor_id, ''), COALESCE(project_id, ''), updated_at DESC, ctid DESC
175291
+ )`);
175292
+ await pool.query(`CREATE UNIQUE INDEX IF NOT EXISTS "${s2}_variables_scope_uq_v2" ON "${s2}".variables (key, COALESCE(actor_id, ''), COALESCE(project_id, ''))`);
174709
175293
  await pool.query(`
174710
175294
  CREATE TABLE IF NOT EXISTS "${s2}".runtime_configs (
174711
175295
  runtime_id text PRIMARY KEY,
@@ -175149,27 +175733,36 @@ var init_postgres_registry = __esm({
175149
175733
  }
175150
175734
  /* ---------- 变量 ---------- */
175151
175735
  async putVariable(v2) {
175152
- const aid = v2.actorId ?? null;
175153
175736
  await this.pool.query(
175154
- `INSERT INTO ${this.s}.variables (key, scope, actor_id, connector_id, overrides, value_encrypted, updated_at)
175155
- VALUES ($1,$2,$3,$4,$5,$6,$7)
175156
- ON CONFLICT (key, COALESCE(actor_id, '')) DO UPDATE
175157
- SET scope=$2, actor_id=$3, connector_id=$4, overrides=$5, value_encrypted=$6, updated_at=$7`,
175158
- [v2.key, v2.scope, aid, v2.connectorId ?? null, v2.overrides ?? null, v2.valueEncrypted, v2.updatedAt]
175737
+ `INSERT INTO ${this.s}.variables (key, scope, actor_id, project_id, connector_id, overrides, delivery_mode, value_encrypted, updated_at)
175738
+ VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9)
175739
+ ON CONFLICT (key, COALESCE(actor_id, ''), COALESCE(project_id, '')) DO UPDATE
175740
+ SET scope=$2, actor_id=$3, project_id=$4, connector_id=$5, overrides=$6, delivery_mode=$7, value_encrypted=$8, updated_at=$9`,
175741
+ [
175742
+ v2.key,
175743
+ v2.scope,
175744
+ v2.actorId ?? null,
175745
+ v2.projectId ?? null,
175746
+ v2.connectorId ?? null,
175747
+ v2.overrides ?? null,
175748
+ v2.deliveryMode ?? null,
175749
+ v2.valueEncrypted,
175750
+ v2.updatedAt
175751
+ ]
175159
175752
  );
175160
175753
  }
175161
175754
  async patchVariableValue(key, actorId, valueEncrypted, updatedAt) {
175162
175755
  const aid = actorId ?? null;
175163
175756
  await this.pool.query(
175164
175757
  `UPDATE ${this.s}.variables SET value_encrypted=$3, updated_at=$4
175165
- WHERE key=$1 AND COALESCE(actor_id,'')=COALESCE($2::text,'')`,
175758
+ WHERE key=$1 AND COALESCE(actor_id,'')=COALESCE($2::text,'') AND COALESCE(project_id,'')=''`,
175166
175759
  [key, aid, valueEncrypted, updatedAt]
175167
175760
  );
175168
175761
  }
175169
175762
  async getVariableCiphertext(key, actorId) {
175170
175763
  const aid = actorId ?? null;
175171
175764
  const r = await this.pool.query(
175172
- `SELECT value_encrypted FROM ${this.s}.variables WHERE key=$1 AND COALESCE(actor_id,'')=COALESCE($2::text,'')`,
175765
+ `SELECT value_encrypted FROM ${this.s}.variables WHERE key=$1 AND COALESCE(actor_id,'')=COALESCE($2::text,'') AND COALESCE(project_id,'')=''`,
175173
175766
  [key, aid]
175174
175767
  );
175175
175768
  return r.rows[0]?.value_encrypted ?? null;
@@ -175184,21 +175777,21 @@ var init_postgres_registry = __esm({
175184
175777
  } else {
175185
175778
  r = await this.pool.query(`SELECT * FROM ${this.s}.variables ORDER BY key`);
175186
175779
  }
175187
- return r.rows.map((row) => ({
175188
- key: row.key,
175189
- scope: row.scope === "connector" ? "global" : row.scope,
175190
- ...row.actor_id !== null && row.actor_id !== void 0 ? { actorId: row.actor_id } : {},
175191
- ...row.connector_id !== null ? { connectorId: row.connector_id } : {},
175192
- ...row.overrides !== null ? { overrides: row.overrides } : {},
175193
- valueEncrypted: row.value_encrypted,
175194
- updatedAt: new Date(row.updated_at).toISOString()
175195
- }));
175780
+ return r.rows.map((row) => rowToVariable(row));
175196
175781
  }
175197
- async deleteVariable(key, actorId) {
175198
- const aid = actorId ?? null;
175782
+ async resolveProjectVariables(projectId) {
175783
+ const r = await this.pool.query(
175784
+ `SELECT * FROM ${this.s}.variables WHERE scope='project' AND project_id=$1 ORDER BY key`,
175785
+ [projectId]
175786
+ );
175787
+ return r.rows.map((row) => rowToVariable(row));
175788
+ }
175789
+ async deleteVariable(key, actorId, scoping) {
175199
175790
  await this.pool.query(
175200
- `DELETE FROM ${this.s}.variables WHERE key=$1 AND COALESCE(actor_id,'')=COALESCE($2::text,'')`,
175201
- [key, aid]
175791
+ `DELETE FROM ${this.s}.variables
175792
+ WHERE key=$1 AND COALESCE(actor_id,'')=COALESCE($2::text,'')
175793
+ AND COALESCE(project_id,'')=COALESCE($3::text,'')`,
175794
+ [key, actorId ?? null, scoping?.projectId ?? null]
175202
175795
  );
175203
175796
  }
175204
175797
  };
@@ -176017,6 +176610,7 @@ var init_postgres_projects = __esm({
176017
176610
  work_order_id text PRIMARY KEY,
176018
176611
  project_id text NOT NULL
176019
176612
  )`);
176613
+ await pool.query(`ALTER TABLE IF EXISTS "${s2}".workspace_bindings ADD COLUMN IF NOT EXISTS environment text`);
176020
176614
  await pool.query(`
176021
176615
  CREATE TABLE IF NOT EXISTS "${s2}".workspace_dispatch_hold (
176022
176616
  work_order_id text PRIMARY KEY
@@ -176886,6 +177480,38 @@ var init_postgres_control_plane = __esm({
176886
177480
  }
176887
177481
  });
176888
177482
 
177483
+ // ../storage/src/pg-sanitize.ts
177484
+ function hasPgUnstorable(s2) {
177485
+ return new RegExp(PG_UNSTORABLE_SOURCE).test(s2);
177486
+ }
177487
+ function scrubPgString(v2) {
177488
+ return typeof v2 === "string" ? v2.replace(new RegExp(PG_UNSTORABLE_SOURCE, "g"), REPLACEMENT) : v2;
177489
+ }
177490
+ function scrubPgJson(v2) {
177491
+ const re = new RegExp(PG_UNSTORABLE_SOURCE, "g");
177492
+ const walk = (x2) => {
177493
+ if (typeof x2 === "string") return x2.replace(re, REPLACEMENT);
177494
+ if (Array.isArray(x2)) return x2.map(walk);
177495
+ if (x2 && typeof x2 === "object") {
177496
+ const out = {};
177497
+ for (const [k2, val] of Object.entries(x2)) {
177498
+ out[k2.replace(re, REPLACEMENT)] = walk(val);
177499
+ }
177500
+ return out;
177501
+ }
177502
+ return x2;
177503
+ };
177504
+ return walk(v2);
177505
+ }
177506
+ var REPLACEMENT, PG_UNSTORABLE_SOURCE;
177507
+ var init_pg_sanitize = __esm({
177508
+ "../storage/src/pg-sanitize.ts"() {
177509
+ "use strict";
177510
+ REPLACEMENT = "\uFFFD";
177511
+ PG_UNSTORABLE_SOURCE = "\\u0000|[\\uD800-\\uDBFF](?![\\uDC00-\\uDFFF])|(?<![\\uD800-\\uDBFF])[\\uDC00-\\uDFFF]";
177512
+ }
177513
+ });
177514
+
176889
177515
  // ../storage/src/postgres-trace.ts
176890
177516
  var ident9, PostgresTraceStore, rowToRun, rowToEvent2, rowToToolCall, rowToArtifact2;
176891
177517
  var init_postgres_trace = __esm({
@@ -176893,6 +177519,7 @@ var init_postgres_trace = __esm({
176893
177519
  "use strict";
176894
177520
  init_esm2();
176895
177521
  init_src();
177522
+ init_pg_sanitize();
176896
177523
  ident9 = (s2) => {
176897
177524
  if (!/^[a-z_][a-z0-9_]*$/.test(s2)) throw new Error(`invalid schema name: ${s2}`);
176898
177525
  return s2;
@@ -177153,12 +177780,12 @@ var init_postgres_trace = __esm({
177153
177780
  run.exitCode,
177154
177781
  run.exitReason,
177155
177782
  run.errorCode,
177156
- run.errorMessage,
177783
+ scrubPgString(run.errorMessage),
177157
177784
  run.usage === null ? null : JSON.stringify(run.usage),
177158
177785
  run.effectiveModel,
177159
177786
  run.modelSource,
177160
177787
  run.transcriptRef,
177161
- JSON.stringify(run.metadata),
177788
+ JSON.stringify(scrubPgJson(run.metadata)),
177162
177789
  run.createdAt,
177163
177790
  run.updatedAt
177164
177791
  ]
@@ -177201,7 +177828,7 @@ var init_postgres_trace = __esm({
177201
177828
  for (const [key, spec] of Object.entries(_PostgresTraceStore.RUN_COLS)) {
177202
177829
  if (!(key in patch)) continue;
177203
177830
  const v2 = patch[key];
177204
- args.push(spec.json ? v2 == null ? null : JSON.stringify(v2) : v2 ?? null);
177831
+ args.push(spec.json ? v2 == null ? null : JSON.stringify(scrubPgJson(v2)) : scrubPgString(v2 ?? null));
177205
177832
  sets.push(spec.json ? `${spec.col}=$${args.length}::jsonb` : `${spec.col}=$${args.length}`);
177206
177833
  }
177207
177834
  args.push(this.now());
@@ -177340,8 +177967,8 @@ var init_postgres_trace = __esm({
177340
177967
  stream,
177341
177968
  level,
177342
177969
  inp.color ?? null,
177343
- inp.message ?? null,
177344
- JSON.stringify(payload),
177970
+ scrubPgString(inp.message ?? null),
177971
+ JSON.stringify(scrubPgJson(payload)),
177345
177972
  inp.blobRef ?? null,
177346
177973
  inp.spanId ?? null,
177347
177974
  inp.parentSpanId ?? null,
@@ -177416,11 +178043,11 @@ var init_postgres_trace = __esm({
177416
178043
  c.durationMs ?? null,
177417
178044
  c.inputRef ?? null,
177418
178045
  c.outputRef ?? null,
177419
- c.inputPreview ?? null,
177420
- c.outputPreview ?? null,
178046
+ scrubPgString(c.inputPreview ?? null),
178047
+ scrubPgString(c.outputPreview ?? null),
177421
178048
  c.errorCode ?? null,
177422
- c.errorMessage ?? null,
177423
- JSON.stringify(c.metadata ?? {})
178049
+ scrubPgString(c.errorMessage ?? null),
178050
+ JSON.stringify(scrubPgJson(c.metadata ?? {}))
177424
178051
  ]
177425
178052
  );
177426
178053
  }
@@ -177465,12 +178092,12 @@ var init_postgres_trace = __esm({
177465
178092
  a.id,
177466
178093
  a.runId,
177467
178094
  a.kind,
177468
- a.name,
178095
+ scrubPgString(a.name),
177469
178096
  a.contentType ?? null,
177470
178097
  a.size ?? null,
177471
178098
  a.blobRef,
177472
178099
  a.createdAt,
177473
- JSON.stringify(a.metadata ?? {})
178100
+ JSON.stringify(scrubPgJson(a.metadata ?? {}))
177474
178101
  ]
177475
178102
  );
177476
178103
  }
@@ -179182,7 +179809,10 @@ __export(src_exports, {
179182
179809
  PostgresTraceStore: () => PostgresTraceStore,
179183
179810
  PostgresTypeRegistryStore: () => PostgresTypeRegistryStore,
179184
179811
  backfillTypeRegistryFromFile: () => backfillTypeRegistryFromFile,
179185
- createPgPool: () => createPgPool
179812
+ createPgPool: () => createPgPool,
179813
+ hasPgUnstorable: () => hasPgUnstorable,
179814
+ scrubPgJson: () => scrubPgJson,
179815
+ scrubPgString: () => scrubPgString
179186
179816
  });
179187
179817
  var init_src8 = __esm({
179188
179818
  "../storage/src/index.ts"() {
@@ -179195,6 +179825,7 @@ var init_src8 = __esm({
179195
179825
  init_pool();
179196
179826
  init_postgres_control_plane();
179197
179827
  init_postgres_trace();
179828
+ init_pg_sanitize();
179198
179829
  init_postgres_automations();
179199
179830
  init_postgres_chat_sessions();
179200
179831
  init_postgres_nodes();
@@ -179594,15 +180225,18 @@ function splitIdentityFiles3(prompt) {
179594
180225
  }
179595
180226
  return out;
179596
180227
  }
179597
- async function buildActorProvision(service, actorId) {
179598
- const raw = await service.resolveActorEnv(actorId);
179599
- const env = { ...raw };
180228
+ async function buildActorProvision(service, actorId, scope) {
180229
+ const entries = await service.resolveActorProvisionEntries(actorId, scope);
180230
+ const raw = {};
180231
+ for (const e of entries) raw[e.key] = e.value;
179600
180232
  const provision = await buildConnectorProvision({
179601
180233
  vars: raw,
179602
180234
  actorName: raw["GIT_AUTHOR_NAME"] ?? actorId.split(":").pop() ?? "oasis-agent"
179603
180235
  });
180236
+ const env = { ...raw };
179604
180237
  for (const k2 of provision.sensitiveVars) delete env[k2];
179605
180238
  for (const [k2, v2] of Object.entries(provision.envOverrides)) env[k2] = v2;
180239
+ applyRefDelivery(env, entries);
179606
180240
  return {
179607
180241
  env,
179608
180242
  wrapperPaths: [oasisWrapperScript(), ...provision.wrapperPaths],
@@ -179828,6 +180462,45 @@ async function startServe(opts) {
179828
180462
  });
179829
180463
  for (const l of registryListeners) l({ kind: record6.kind, actor: record6.actor, target: record6.target, timestamp: record6.timestamp });
179830
180464
  };
180465
+ const credentialAudit = (record6) => {
180466
+ fs29.appendFile(registryAuditFile, JSON.stringify(record6) + "\n", () => {
180467
+ });
180468
+ for (const l of registryListeners) l({ kind: record6.kind, actor: record6.actor, target: record6.variableKey, timestamp: record6.timestamp });
180469
+ };
180470
+ const readCredentialAudit = async (filter) => {
180471
+ let raw;
180472
+ try {
180473
+ raw = await fs29.promises.readFile(registryAuditFile, "utf8");
180474
+ } catch {
180475
+ return [];
180476
+ }
180477
+ const out = [];
180478
+ for (const line of raw.split("\n")) {
180479
+ if (!line.trim()) continue;
180480
+ let rec;
180481
+ try {
180482
+ rec = JSON.parse(line);
180483
+ } catch {
180484
+ continue;
180485
+ }
180486
+ if (typeof rec.kind !== "string" || !rec.kind.startsWith("credential_reveal")) continue;
180487
+ if (filter.actor && rec.actor !== filter.actor) continue;
180488
+ if (filter.key && rec.variableKey !== filter.key) continue;
180489
+ if (filter.kind && rec.kind !== filter.kind) continue;
180490
+ out.push(rec);
180491
+ }
180492
+ out.reverse();
180493
+ return typeof filter.limit === "number" ? out.slice(0, filter.limit) : out;
180494
+ };
180495
+ const resolveDispatchScope = async (artifactId) => {
180496
+ const ws = kernel.model.artifacts.get(artifactId)?.workspace;
180497
+ if (!ws) return {};
180498
+ const binding = await artifactStateStore.getWorkspaceBinding(ws);
180499
+ if (!binding) return {};
180500
+ return {
180501
+ ...binding.projectId ? { projectId: binding.projectId } : {}
180502
+ };
180503
+ };
179831
180504
  await syncRegistryRolesToKernel(registryStore, kernel);
179832
180505
  let builtinSkills = [];
179833
180506
  let connectorSkills = [];
@@ -179844,6 +180517,13 @@ async function startServe(opts) {
179844
180517
  blobs: assets,
179845
180518
  trace: () => traceStoreForActors,
179846
180519
  audit: registryAudit,
180520
+ // ADR 凭据保险库 §4.5/§4.7 / ADR 0125:reveal 判权的项目解析 + 凭据读取审计读写。
180521
+ resolveDispatchScope,
180522
+ credentialAudit,
180523
+ readCredentialAudit,
180524
+ // ADR 0125:写 scope=project 变量时校验 projectId 真实存在(getProject 直查,含临时项目)。
180525
+ // projectStateStore 在下方 ~行 1023 才赋值——本箭头只在请求时跑,那时已就绪(同 resolveDispatchScope 惰性模式)。
180526
+ projectExists: async (id) => await projectStateStore.getProject(id) !== null,
179847
180527
  // CO-302 数据面隔离(actors 域试点):按当前公司取其引擎,用各自 registry 服务该域请求。
179848
180528
  // engineRouter 在下方(行 ~411)以 const 声明;此箭头只在请求时调用,那时已初始化,闭包引用合法
179849
180529
  // (TDZ 只在构造时访问才报错,这里不访问)。默认公司命中现有单实例(registry===registryStore),
@@ -179974,7 +180654,13 @@ async function startServe(opts) {
179974
180654
  traceStoreForActors = traceStore;
179975
180655
  const trajReader = new FsTrajectorySink(opts.dir);
179976
180656
  const trace = createTraceDomain({ store: traceStore, readBundleFile: (id, rel) => trajReader.readBundleFile(id, rel) });
179977
- const traceStoreSink = new TraceStoreSink({ store: traceStore, runtimeKind: "auto" });
180657
+ const traceHealth = createTraceHealth();
180658
+ const traceStoreSink = new TraceStoreSink({
180659
+ store: traceStore,
180660
+ runtimeKind: "auto",
180661
+ health: traceHealth,
180662
+ onError: (err) => console.warn(`[trace-sink] \u5199\u89C2\u6D4B\u6D41\u5931\u8D25\uFF08\u7EE7\u7EED\u3001\u4E0D\u5F71\u54CD\u6D3E\u53D1\uFF09: ${String(err)}`)
180663
+ });
179978
180664
  const automationStore = pgPool ? await PostgresAutomationStore.open(pgPool, pgSchema) : new MemoryAutomationStore();
179979
180665
  console.log(`[serve] \u81EA\u52A8\u5316\u4F53\u7CFB\uFF1A${pgPool ? `Postgres schema=${pgSchema}` : "\u5185\u5B58 dev store"}`);
179980
180666
  const automations = createAutomationsDomain({ store: automationStore, kernel });
@@ -180438,6 +181124,8 @@ async function startServe(opts) {
180438
181124
  // 组织汇总用——从 node_runtimes 取 hostname,解析运行时实例标签(kind @ hostname)
180439
181125
  listRuntimes: (nodeId) => nodeStore.listRuntimes(nodeId),
180440
181126
  dispatchJournal: () => journalRing.slice(-200),
181127
+ // 组织汇总用——从 nodes 取 name(用户友好名),优先于 hostname 作为展示标签
181128
+ listNodes: () => nodeStore.listNodes(),
180441
181129
  // 诊断 92ac8f18-83e5fde6 §④ D-2:按**请求公司**取其 dispatcher 的 queued/backoff 信号。
180442
181130
  // CO-302 每家公司各有一套 dispatcher(dispatchers 以 companyId 为键)——固定读默认公司会让
180443
181131
  // 非默认公司请求拿到错的/空的信号(code-review 打回)。该公司未起 dispatcher(老部署无 --dispatch、
@@ -180577,10 +181265,14 @@ async function startServe(opts) {
180577
181265
  const traceRunId = `chat-run:${(0, import_node_crypto37.randomUUID)()}`;
180578
181266
  const artifactId = `artifact:chat:${(0, import_node_crypto37.randomUUID)()}`;
180579
181267
  const traceStartedAt = (/* @__PURE__ */ new Date()).toISOString();
180580
- let traceSeq = 0;
180581
181268
  let lastProgressTouchMs = 0;
180582
181269
  const PROGRESS_TOUCH_THROTTLE_MS2 = 2e4;
180583
- let traceEnabled = true;
181270
+ const traceAppender = new ResilientTraceAppender({
181271
+ runId: traceRunId,
181272
+ store: traceStore,
181273
+ health: traceHealth,
181274
+ onError: (err) => console.warn(`[trace] chat run ${traceRunId} \u5199\u5931\u8D25\uFF08\u7EE7\u7EED\u3001\u4E0D\u7184\u706D\uFF09: ${String(err)}`)
181275
+ });
180584
181276
  const analyzedRunId = chatSessionId ? (await chatSessionStore.getSession(chatSessionId).catch(() => null))?.analyzedRunId ?? void 0 : void 0;
180585
181277
  let traceChain = traceStore.createRun({
180586
181278
  id: traceRunId,
@@ -180601,16 +181293,14 @@ async function startServe(opts) {
180601
181293
  ...analyzedRunId ? { analyzedRunId } : {}
180602
181294
  }
180603
181295
  }).then(async () => {
180604
- await traceStore.appendEvents(traceRunId, [
181296
+ await traceAppender.append([
180605
181297
  {
180606
- seq: ++traceSeq,
180607
181298
  eventType: "run.started",
180608
181299
  stream: "runtime",
180609
181300
  message: `chat ${actorId}`,
180610
181301
  startedAt: traceStartedAt
180611
181302
  },
180612
181303
  {
180613
- seq: ++traceSeq,
180614
181304
  eventType: "input.message",
180615
181305
  stream: "model",
180616
181306
  actorId,
@@ -180620,16 +181310,12 @@ async function startServe(opts) {
180620
181310
  }
180621
181311
  ]);
180622
181312
  }).catch((err) => {
180623
- traceEnabled = false;
180624
- console.warn(`[trace] chat run create failed: ${String(err)}`);
181313
+ traceHealth.markUnhealthy(traceRunId);
181314
+ console.warn(`[trace] chat run create failed (continuing): ${String(err)}`);
180625
181315
  });
180626
181316
  const enqueueTrace = (step) => {
180627
- if (!traceEnabled) return;
180628
- traceChain = traceChain.then(async () => {
180629
- if (traceEnabled) await step();
180630
- }).catch((err) => {
180631
- traceEnabled = false;
180632
- console.warn(`[trace] chat run ${traceRunId} failed: ${String(err)}`);
181317
+ traceChain = traceChain.then(step).catch((err) => {
181318
+ console.warn(`[trace] chat run ${traceRunId} \u7EED\u8D26\u5F02\u5E38: ${String(err)}`);
180633
181319
  });
180634
181320
  };
180635
181321
  const provision = await buildActorProvision(actorService, actorId);
@@ -180776,7 +181462,7 @@ async function startServe(opts) {
180776
181462
  const run = await traceStore.getRun(traceRunId);
180777
181463
  if (!run) return;
180778
181464
  const base = run.metadata && typeof run.metadata === "object" && !Array.isArray(run.metadata) ? run.metadata : {};
180779
- await traceStore.updateRun(traceRunId, {
181465
+ await traceAppender.update({
180780
181466
  metadata: { ...base, dispatchId: handle.id },
180781
181467
  // 恢复路径拿不到 dispatchChat 闭包里的 fallback 解析结果,开跑时先写进账本;退出帧若上报
180782
181468
  // 更精确 model 仍会覆盖为 runtime 来源。
@@ -180798,23 +181484,23 @@ async function startServe(opts) {
180798
181484
  const ms = Date.parse(event.ts);
180799
181485
  if (!Number.isNaN(ms) && ms - lastProgressTouchMs >= PROGRESS_TOUCH_THROTTLE_MS2) {
180800
181486
  lastProgressTouchMs = ms;
180801
- enqueueTrace(() => traceStore.updateRun(traceRunId, { lastProgressAt: event.ts }).then(() => void 0));
181487
+ enqueueTrace(() => traceAppender.update({ lastProgressAt: event.ts }).then(() => void 0));
180802
181488
  }
180803
181489
  return;
180804
181490
  }
180805
- enqueueTrace(() => traceStore.appendEvents(traceRunId, [trajectoryEventToRunEvent(event, ++traceSeq)]).then(() => void 0));
181491
+ enqueueTrace(() => traceAppender.append([trajectoryEventToRunEvent(event)]).then(() => void 0));
180806
181492
  });
180807
181493
  let capturedNativeSessionId;
180808
181494
  const done = new Promise((resolve8, reject) => {
180809
181495
  handle.onExit((info) => {
180810
181496
  chatLiveSessions.delete(chatJobKey);
181497
+ traceHealth.forget(traceRunId);
180811
181498
  void provision.cleanup();
180812
181499
  const endedAt = (/* @__PURE__ */ new Date()).toISOString();
180813
181500
  const durationMs = Date.parse(endedAt) - Date.parse(traceStartedAt);
180814
181501
  if (info.runtimeSessionId) capturedNativeSessionId = info.runtimeSessionId;
180815
181502
  enqueueTrace(async () => {
180816
- await traceStore.appendEvents(traceRunId, [{
180817
- seq: ++traceSeq,
181503
+ await traceAppender.append([{
180818
181504
  eventType: "run.finished",
180819
181505
  stream: "runtime",
180820
181506
  message: `exit code=${info.code ?? "killed"}`,
@@ -180823,7 +181509,7 @@ async function startServe(opts) {
180823
181509
  const usage = info.usage ?? null;
180824
181510
  const effectiveModel = info.model ?? chatModel ?? null;
180825
181511
  const modelSource = info.model ? "runtime" : chatModelSource;
180826
- await traceStore.updateRun(traceRunId, {
181512
+ await traceAppender.update({
180827
181513
  status: info.reason === "timeout" ? "timeout" : info.reason === "cancelled" ? "cancelled" : info.code === 0 ? "succeeded" : "failed",
180828
181514
  endedAt,
180829
181515
  durationMs,
@@ -180928,6 +181614,7 @@ async function startServe(opts) {
180928
181614
  unregister: () => {
180929
181615
  chatLiveSessions.delete(jobKey);
180930
181616
  },
181617
+ traceHealth,
180931
181618
  log: (m2) => console.log(m2)
180932
181619
  });
180933
181620
  console.log(`[chat-recovery] \u91CD\u6302 chat \u8F6E\uFF1Asession=${plan.chatSessionId} run=${plan.runId} node=${daemonId}`);
@@ -181148,7 +181835,7 @@ async function startServe(opts) {
181148
181835
  console.warn(`[dispatch] artifact ${artifactId} owner ${actor} \u5DF2\u505C\u7528\uFF0C\u6309 ownerRole=${ownerRole} \u6539\u6D3E\u7ED9 ${replacement.id}${rejected.length ? `\uFF08\u8DF3\u8FC7\u4E0D\u5065\u5EB7\u7684\uFF1A${rejected.join("\u3001")}\uFF09` : ""}`);
181149
181836
  return { actor: replacement.id, originalActor: actor, routeReason: "disabled-owner-fallback" };
181150
181837
  },
181151
- provision: async (actorId) => buildActorProvision((await runActors()).service, actorId),
181838
+ provision: async (actorId, artifactId) => buildActorProvision((await runActors()).service, actorId, artifactId ? await resolveDispatchScope(artifactId) : void 0),
181152
181839
  resolveActorContext: async (actorId) => buildActorContext((await runActors()).service, actorId),
181153
181840
  // 收尾评审的角色分工段(reviewer 此前彼此不知道对方存在 → 越界 / 重复劳动)。
181154
181841
  // 职责取自**岗位目录**(配置面),不在代码里硬编码任何岗位——加角色自动就在。
@@ -181771,7 +182458,7 @@ ${nodeFault}` : "");
181771
182458
  resolveManager: (id) => resolveManagerBinding(registryStore, id),
181772
182459
  schemaTypes: schema.map((d) => d.name),
181773
182460
  schemaTypeDescriptions: Object.fromEntries(schema.filter((d) => d.description).map((d) => [d.name, d.description])),
181774
- provision: (actorId) => buildActorProvision(actors.service, actorId),
182461
+ provision: async (actorId, artifactId) => buildActorProvision(actors.service, actorId, artifactId ? await resolveDispatchScope(artifactId) : void 0),
181775
182462
  // 阻塞诊断(proposal D1/D3):派发器运行时信号(queued/fused/backoff/hasInFlight)注入诊断,
181776
182463
  // 让协调者把"资源 / 运行时"病因和"图病"分开——资源排队的节点不会被误唤去砍流程。
181777
182464
  runtimeSignals: (id) => {
@@ -181811,6 +182498,9 @@ ${nodeFault}` : "");
181811
182498
  if (c) return Promise.resolve(c.kill()).then(() => true).catch(() => false);
181812
182499
  return dispatcherByJob.get(jobKey)?.killSession(jobKey) ?? Promise.resolve(false);
181813
182500
  },
182501
+ // trace 写不进去的 run:冻结的事件钟是"观测降级"不是"真静默",按"信息不足→不判"跳过、交 wallClock
182502
+ // 兜底(brief §限制1 / 诊断 §6)。真卡死的 run trace 健康、isTraceHealthy=true,照常被杀。
182503
+ isTraceHealthy: (sessionId) => traceHealth.isHealthy(sessionId),
181814
182504
  now: Date.now(),
181815
182505
  silentThresholdMs,
181816
182506
  log: (m2) => console.log(m2)
@@ -181973,7 +182663,7 @@ var SessionManager = class {
181973
182663
 
181974
182664
  // ../cli/src/daemon/ws-client.ts
181975
182665
  init_wrapper();
181976
- var import_node_os7 = require("node:os");
182666
+ var import_node_os9 = require("node:os");
181977
182667
  var import_node_crypto38 = require("node:crypto");
181978
182668
  init_src5();
181979
182669
 
@@ -182029,7 +182719,7 @@ function detectRuntimes() {
182029
182719
 
182030
182720
  // ../cli/src/daemon/workdir-handler.ts
182031
182721
  var import_node_fs16 = __toESM(require("node:fs"), 1);
182032
- var import_node_path18 = __toESM(require("node:path"), 1);
182722
+ var import_node_path20 = __toESM(require("node:path"), 1);
182033
182723
  init_src4();
182034
182724
  init_src();
182035
182725
  var WORKDIR_READ_MAX_BYTES2 = 2 * 1024 * 1024;
@@ -182048,10 +182738,10 @@ function isSensitiveSegment(segment) {
182048
182738
  }
182049
182739
  function relPathIsSensitive(rel) {
182050
182740
  if (!rel) return false;
182051
- return rel.split(import_node_path18.default.sep).some((seg) => seg.length > 0 && isSensitiveSegment(seg));
182741
+ return rel.split(import_node_path20.default.sep).some((seg) => seg.length > 0 && isSensitiveSegment(seg));
182052
182742
  }
182053
182743
  function withinBase(p2, base) {
182054
- return p2 === base || p2.startsWith(base + import_node_path18.default.sep);
182744
+ return p2 === base || p2.startsWith(base + import_node_path20.default.sep);
182055
182745
  }
182056
182746
  function normalizeRel(raw) {
182057
182747
  const trimmed = (raw ?? "").trim();
@@ -182068,7 +182758,7 @@ async function trustedCanonicalContainer(req, logicalBase, dirKind) {
182068
182758
  } catch {
182069
182759
  return null;
182070
182760
  }
182071
- return isLegacy ? import_node_path18.default.join(trustedRootReal, "oasis-chat-sessions") : import_node_path18.default.join(trustedRootReal, "sessions", dirKind);
182761
+ return isLegacy ? import_node_path20.default.join(trustedRootReal, "oasis-chat-sessions") : import_node_path20.default.join(trustedRootReal, "sessions", dirKind);
182072
182762
  }
182073
182763
  async function resolveWithinWorkdir(req) {
182074
182764
  const dirKind = sessionDirKind(req.runtimeKind);
@@ -182086,11 +182776,11 @@ async function resolveWithinWorkdir(req) {
182086
182776
  } catch {
182087
182777
  return { ok: false, code: "NOT_FOUND" };
182088
182778
  }
182089
- if (import_node_path18.default.dirname(base) !== canonicalContainer) return { ok: false, code: "PATH_ESCAPE" };
182779
+ if (import_node_path20.default.dirname(base) !== canonicalContainer) return { ok: false, code: "PATH_ESCAPE" };
182090
182780
  const rel = normalizeRel(req.path);
182091
- const requested = import_node_path18.default.resolve(base, rel);
182781
+ const requested = import_node_path20.default.resolve(base, rel);
182092
182782
  if (!withinBase(requested, base)) return { ok: false, code: "PATH_ESCAPE" };
182093
- const cleanRel = base === requested ? "" : import_node_path18.default.relative(base, requested);
182783
+ const cleanRel = base === requested ? "" : import_node_path20.default.relative(base, requested);
182094
182784
  if (relPathIsSensitive(cleanRel)) return { ok: false, code: "SENSITIVE" };
182095
182785
  let real;
182096
182786
  try {
@@ -182100,7 +182790,7 @@ async function resolveWithinWorkdir(req) {
182100
182790
  return { ok: false, code: "PATH_ESCAPE" };
182101
182791
  }
182102
182792
  if (!withinBase(real, base)) return { ok: false, code: "PATH_ESCAPE" };
182103
- const realRel = base === real ? "" : import_node_path18.default.relative(base, real);
182793
+ const realRel = base === real ? "" : import_node_path20.default.relative(base, real);
182104
182794
  if (relPathIsSensitive(realRel)) return { ok: false, code: "SENSITIVE" };
182105
182795
  return { ok: true, base, real };
182106
182796
  }
@@ -182136,7 +182826,7 @@ async function handleWorkdirList(req) {
182136
182826
  const slice = truncated ? visible.slice(0, WORKDIR_LIST_MAX_ENTRIES) : visible;
182137
182827
  const entries = [];
182138
182828
  for (const d of slice) {
182139
- const abs = import_node_path18.default.join(anchor, d.name);
182829
+ const abs = import_node_path20.default.join(anchor, d.name);
182140
182830
  try {
182141
182831
  const st = await import_node_fs16.default.promises.lstat(abs);
182142
182832
  if (st.isSymbolicLink()) continue;
@@ -182168,7 +182858,7 @@ async function verifyOpenedFd(fh, base, fallback) {
182168
182858
  const fdReal = await fdCanonicalPath(fh);
182169
182859
  if (fdReal === null) return { anchor: fallback };
182170
182860
  if (!withinBase(fdReal, base)) return { error: { ok: false, code: "PATH_ESCAPE" } };
182171
- const fdRel = base === fdReal ? "" : import_node_path18.default.relative(base, fdReal);
182861
+ const fdRel = base === fdReal ? "" : import_node_path20.default.relative(base, fdReal);
182172
182862
  if (relPathIsSensitive(fdRel)) return { error: { ok: false, code: "SENSITIVE" } };
182173
182863
  return { anchor: `/proc/self/fd/${fh.fd}` };
182174
182864
  }
@@ -182257,7 +182947,7 @@ function looksBinary(bytes) {
182257
182947
  function contentTypeFor(absPath, bytes) {
182258
182948
  const sniffed = sniffContentType(bytes);
182259
182949
  if (sniffed) return sniffed;
182260
- const ext = import_node_path18.default.extname(absPath).toLowerCase();
182950
+ const ext = import_node_path20.default.extname(absPath).toLowerCase();
182261
182951
  if (EXT_CONTENT_TYPE[ext]) return EXT_CONTENT_TYPE[ext];
182262
182952
  return looksBinary(bytes) ? "application/octet-stream" : "text/plain; charset=utf-8";
182263
182953
  }
@@ -182600,7 +183290,7 @@ var DaemonWsClient = class {
182600
183290
  buildMeta() {
182601
183291
  const activeSessions = this.sessions.activeSessions();
182602
183292
  return {
182603
- hostname: (0, import_node_os7.hostname)(),
183293
+ hostname: (0, import_node_os9.hostname)(),
182604
183294
  adapters: this.adapters,
182605
183295
  ...this.reportRuntimes ? { runtimes: this.runtimes } : {},
182606
183296
  nodeVersion: process.version,
@@ -182630,18 +183320,18 @@ var RuntimeRouterAdapter = class {
182630
183320
 
182631
183321
  // ../cli/src/daemon/reap-claude-projects.ts
182632
183322
  var import_node_fs17 = __toESM(require("node:fs"), 1);
182633
- var import_node_os8 = __toESM(require("node:os"), 1);
182634
- var import_node_path19 = __toESM(require("node:path"), 1);
183323
+ var import_node_os10 = __toESM(require("node:os"), 1);
183324
+ var import_node_path21 = __toESM(require("node:path"), 1);
182635
183325
  function claudeProjectSlug(cwd) {
182636
183326
  return cwd.replace(/[^a-zA-Z0-9]/g, "-");
182637
183327
  }
182638
183328
  function claudeProjectsRoot() {
182639
- const configDir = process.env["CLAUDE_CONFIG_DIR"] || import_node_path19.default.join(import_node_os8.default.homedir(), ".claude");
182640
- return import_node_path19.default.join(configDir, "projects");
183329
+ const configDir = process.env["CLAUDE_CONFIG_DIR"] || import_node_path21.default.join(import_node_os10.default.homedir(), ".claude");
183330
+ return import_node_path21.default.join(configDir, "projects");
182641
183331
  }
182642
183332
  function reapClaudeProjects(workdir, runtimeKind) {
182643
183333
  if (runtimeKind !== "claude" && runtimeKind !== "claude-code") return;
182644
- const dir = import_node_path19.default.join(claudeProjectsRoot(), claudeProjectSlug(workdir));
183334
+ const dir = import_node_path21.default.join(claudeProjectsRoot(), claudeProjectSlug(workdir));
182645
183335
  if (!import_node_fs17.default.existsSync(dir)) return;
182646
183336
  try {
182647
183337
  import_node_fs17.default.rmSync(dir, { recursive: true, force: true });
@@ -182651,7 +183341,7 @@ function reapClaudeProjects(workdir, runtimeKind) {
182651
183341
 
182652
183342
  // ../cli/src/node.ts
182653
183343
  init_src4();
182654
- var import_node_os9 = __toESM(require("node:os"), 1);
183344
+ var import_node_os11 = __toESM(require("node:os"), 1);
182655
183345
  var RESILIENCE = {
182656
183346
  codex: { idleTimeoutMs: 6e5 }
182657
183347
  };
@@ -182715,7 +183405,7 @@ async function startNode(opts) {
182715
183405
  console.log(`[oasis node] ${opts.nodeId} \u2192 ${opts.serverUrl}`);
182716
183406
  client.start();
182717
183407
  const { workRoot, legacyRoot } = resolveWorkRoots(opts.workRoot);
182718
- const workRootIsEphemeral = workRoot.startsWith(import_node_os9.default.tmpdir());
183408
+ const workRootIsEphemeral = workRoot.startsWith(import_node_os11.default.tmpdir());
182719
183409
  if (opts.gcEnabled === false && !workRootIsEphemeral) {
182720
183410
  console.warn(`[oasis node] \u26A0 \u56DE\u6536\u5668\u5DF2\u5173\u95ED\uFF0C\u800C\u5DE5\u4F5C\u533A\u5728\u6301\u4E45\u76D8\uFF08${workRoot}\uFF09\u2014\u2014\u6CA1\u6709\u4EFB\u4F55\u5783\u573E\u56DE\u6536\uFF0C\u5B9E\u6D4B\u4E24\u5468\u4F1A\u6DA8 12.4GB\u3002\u4EC5\u4F9B\u6392\u969C\uFF0C\u522B\u957F\u671F\u8FD9\u4E48\u8DD1\u3002`);
182721
183411
  }
@@ -182750,7 +183440,7 @@ async function startNode(opts) {
182750
183440
  var import_node_child_process16 = require("node:child_process");
182751
183441
  var import_node_fs18 = require("node:fs");
182752
183442
  var import_node_crypto39 = require("node:crypto");
182753
- var import_node_os10 = require("node:os");
183443
+ var import_node_os12 = require("node:os");
182754
183444
  function linuxMachineId() {
182755
183445
  for (const p2 of ["/etc/machine-id", "/var/lib/dbus/machine-id"]) {
182756
183446
  try {
@@ -182790,13 +183480,13 @@ function windowsMachineId() {
182790
183480
  }
182791
183481
  function fallbackFingerprint() {
182792
183482
  const macs = [];
182793
- const ifaces = (0, import_node_os10.networkInterfaces)();
183483
+ const ifaces = (0, import_node_os12.networkInterfaces)();
182794
183484
  for (const name of Object.keys(ifaces).sort()) {
182795
183485
  for (const ni of ifaces[name] ?? []) {
182796
183486
  if (!ni.internal && ni.mac && ni.mac !== "00:00:00:00:00:00") macs.push(ni.mac);
182797
183487
  }
182798
183488
  }
182799
- return `fallback:${(0, import_node_os10.hostname)()}:${macs.sort()[0] ?? "no-mac"}`;
183489
+ return `fallback:${(0, import_node_os12.hostname)()}:${macs.sort()[0] ?? "no-mac"}`;
182800
183490
  }
182801
183491
  function machineFingerprint() {
182802
183492
  const byOs = process.platform === "darwin" ? macMachineId() : process.platform === "win32" ? windowsMachineId() : linuxMachineId();
@@ -182806,7 +183496,7 @@ var defaultSources = {
182806
183496
  machineFingerprint,
182807
183497
  osUser: () => {
182808
183498
  try {
182809
- return (0, import_node_os10.userInfo)().username;
183499
+ return (0, import_node_os12.userInfo)().username;
182810
183500
  } catch {
182811
183501
  return process.env["USER"] ?? process.env["USERNAME"] ?? "unknown";
182812
183502
  }
@@ -183810,6 +184500,19 @@ var COMMAND_DECLS = {
183810
184500
  description: "\u4EA4\u4ED8\u7269\u6587\u6863\u4F53\u7CFB\u64CD\u4F5C\uFF08artifact revision \u5B50\u547D\u4EE4\u96C6\uFF09\u3002",
183811
184501
  subCommands: ["artifact revision register", "artifact revision approve", "artifact revision reject"],
183812
184502
  examples: ['oasis artifact revision register --project oasis --artifact-id artifact:dev:abc --type dev --title "v1.0" --version 1 --content-ref ref-001']
184503
+ },
184504
+ var: {
184505
+ usage: "oasis var <reveal> ...",
184506
+ description: "\u51ED\u636E\u4FDD\u9669\u5E93\uFF1A\u8BFB\u53D6\u654F\u611F\u53D8\u91CF\u660E\u6587\uFF08ADR \u51ED\u636E\u4FDD\u9669\u5E93 \xA74.5\uFF09\u3002",
184507
+ subCommands: ["var reveal"],
184508
+ examples: ["oasis var reveal DEV_SPA_LOGIN_PASSWORD"]
184509
+ },
184510
+ "var reveal": {
184511
+ usage: "oasis var reveal <key> [--source <tag>]",
184512
+ description: "\u8BFB\u53D6\u654F\u611F\u53D8\u91CF\u7684\u660E\u6587\u3002env \u91CC\u82E5\u89C1 oasis://var/X\uFF08\u5F15\u7528\u975E\u660E\u6587\uFF09\u2192 \u7528\u672C\u547D\u4EE4\u53D6\u660E\u6587\u3002\u7F3A key/\u65E0\u6743\u8FD4\u56DE denied\uFF1B\u77ED\u65F6\u8BFB\u53D6\u8FC7\u591A\u4F1A\u9650\u6D41\u3002\u660E\u6587\u53EA\u8F93\u51FA\u5230 stdout\u3002",
184513
+ positional: [{ name: "key", required: true, desc: "\u53D8\u91CF key\uFF08\u53EF\u4F20\u88F8 key \u6216 oasis://var/<key> \u5F15\u7528\uFF0C\u4E8C\u8005\u7B49\u4EF7\uFF09" }],
184514
+ flags: [{ name: "source", desc: "\u8C03\u7528\u6765\u6E90\u6807\u8BB0\uFF08\u5BA1\u8BA1\u7528\uFF0C\u5982 smoke-test\uFF1B\u7F3A\u7701 agent-cli\uFF09", own: true }],
184515
+ examples: ["oasis var reveal DEV_SPA_LOGIN_PASSWORD", "oasis var reveal oasis://var/GLM_API_KEY --source smoke-test"]
183813
184516
  }
183814
184517
  };
183815
184518
  function formatCommandHelp(decl) {
@@ -185475,6 +186178,33 @@ ${res.warning}`);
185475
186178
  println(`\u5DF2\u5220\u9664 ${memId}`);
185476
186179
  break;
185477
186180
  }
186181
+ case "var": {
186182
+ const sub = positional[0];
186183
+ if (sub === "reveal") {
186184
+ const rawKey = needPos(positional, 1, "oasis var reveal <key> [--source <tag>]");
186185
+ const key = parseVarRef(rawKey) ?? rawKey;
186186
+ const source = flags.get("source");
186187
+ try {
186188
+ const out = await api.request("POST", "/api/variables/reveal", {
186189
+ key,
186190
+ ...source ? { source } : {}
186191
+ });
186192
+ println(out.value);
186193
+ } catch (e) {
186194
+ if (e instanceof ApiRequestError && e.status === 403) {
186195
+ throw new Error(`\u51ED\u636E\u4E0D\u53EF\u8BFB\uFF1A${key}\uFF08403 denied\uFF09\u2014\u2014\u8BE5 key \u4E0D\u5B58\u5728\uFF0C\u6216\u672C\u4F1A\u8BDD\u65E0\u6743\u8BFB\u53D6\uFF08ADR \xA74.5\uFF09\u3002\u522B\u5FAA\u73AF\u91CD\u8BD5\uFF1B\u7F3A\u51ED\u636E\u8BF7\u4E0A\u62A5\u8FD0\u7EF4\u3002`);
186196
+ }
186197
+ if (e instanceof ApiRequestError && e.status === 429) {
186198
+ const scope = e.body?.scope ?? "session";
186199
+ throw new Error(`reveal \u89E6\u53D1\u9650\u6D41\uFF1A${key}\uFF08429 throttled\uFF0C\u6863=${scope}\uFF09\u2014\u2014\u77ED\u65F6\u5185\u8BFB\u53D6\u8FC7\u591A\u3002\u522B\u518D\u5FAA\u73AF reveal \u540C\u4E00 key\u3002`);
186200
+ }
186201
+ throw e;
186202
+ }
186203
+ break;
186204
+ }
186205
+ throw new Error(`\u672A\u77E5\u5B50\u547D\u4EE4\uFF1Aoasis var ${sub ?? ""}
186206
+ \u53EF\u7528\uFF1Aoasis var reveal <key> [--source <tag>]`);
186207
+ }
185478
186208
  case "pin": {
185479
186209
  const { message } = await api.cmd("pin", {
185480
186210
  artifactId: needPos(positional, 0, "oasis pin <artifactId> --to u --rev r"),
@@ -186174,7 +186904,7 @@ function syncRuntimeAssets(candidateRoots, binDir) {
186174
186904
  }
186175
186905
 
186176
186906
  // src/index.ts
186177
- var PKG_VERSION = true ? "0.1.87" : "dev";
186907
+ var PKG_VERSION = true ? "0.1.90" : "dev";
186178
186908
  var OASIS_DIR = path29.join(os9.homedir(), ".oasis");
186179
186909
  var CONFIG_FILE = path29.join(OASIS_DIR, "node-config.json");
186180
186910
  var PID_FILE = path29.join(OASIS_DIR, "node.pid");