oasis_test_v2 2.2.12 → 2.2.13

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 +121 -9
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -201815,6 +201815,15 @@ var init_provision = __esm({
201815
201815
  });
201816
201816
 
201817
201817
  // ../connectors/src/prepare-job.ts
201818
+ function loginShellShadow(commandName) {
201819
+ const home2 = process.env["HOME"];
201820
+ if (!home2) return "";
201821
+ for (const dir of [(0, import_node_path17.join)(home2, ".local", "bin"), (0, import_node_path17.join)(home2, "bin")]) {
201822
+ const candidate = (0, import_node_path17.join)(dir, commandName);
201823
+ if ((0, import_node_fs11.existsSync)(candidate)) return candidate;
201824
+ }
201825
+ return "";
201826
+ }
201818
201827
  async function prepareConnectorsForJob(job, deps) {
201819
201828
  const creds = job.connectorCreds ?? [];
201820
201829
  const remoteWrappers = job.wrapperPaths ?? [];
@@ -201850,7 +201859,16 @@ async function prepareConnectorsForJob(job, deps) {
201850
201859
  localWrappers.push(staged);
201851
201860
  status.wrapperReady = true;
201852
201861
  }
201853
- log("[node-connectors]", `${slug6}: creds + wrapper \u5DF2\u843D\u5728\u672C\u673A`);
201862
+ const shadow = loginShellShadow(connector.commandName);
201863
+ if (shadow) {
201864
+ status.shadowedBy = shadow;
201865
+ log(
201866
+ "[node-connectors]",
201867
+ `\u26A0 ${slug6}: creds + wrapper \u5DF2\u843D\u5728\u672C\u673A\uFF0C\u4F46 **${shadow} \u4F1A\u5728\u767B\u5F55 shell \u91CC\u6321\u4F4F wrapper**\u2014\u2014Ubuntu \u7684 ~/.profile \u628A $HOME/.local/bin \u524D\u7F6E\u5230 PATH \u6700\u524D\u3002agent \u4E00\u65E6\u7528 \`bash -lc\` \u8C03 ${connector.commandName}\uFF0C\u8D70\u5230\u7684\u662F\u90A3\u4E2A\u5BBF\u4E3B\u526F\u672C\u3001\u914D\u7F6E\u76EE\u5F55\u53C8\u88AB\u9694\u79BB\u6210\u7A7A\u7684\uFF0C\u7ED3\u679C\u5C31\u662F\u300C\u672A\u914D\u7F6E\u300D\u3002\u5904\u7406\u529E\u6CD5\uFF1A\u5220\u6389 ${shadow}\uFF08\u8FDE\u63A5\u5668\u81EA\u5E26\u7684\u771F CLI \u5728 ~/.oasis/connector-tools/bin \u4E0B\uFF0C\u4E0D\u53D7\u5F71\u54CD\uFF09\u3002`
201868
+ );
201869
+ } else {
201870
+ log("[node-connectors]", `${slug6}: creds + wrapper \u5DF2\u843D\u5728\u672C\u673A`);
201871
+ }
201854
201872
  } catch (err) {
201855
201873
  status.error = String(err);
201856
201874
  log(
@@ -206756,6 +206774,52 @@ ${composed}`;
206756
206774
  }
206757
206775
  return;
206758
206776
  }
206777
+ if (url.pathname.startsWith("/api/connectors/") && /\/auth\/(begin|rollback|commit)$/.test(url.pathname) && req.method === "POST") {
206778
+ const m2 = url.pathname.match(/^\/api\/connectors\/([^/]+)\/auth\/(begin|rollback|commit)$/);
206779
+ try {
206780
+ if (!m2) throw new Error("bad path");
206781
+ const connectorId = decodeURIComponent(m2[1]);
206782
+ const op = m2[2];
206783
+ const { actorId } = JSON.parse(rawBody || "{}");
206784
+ if (!actorId) {
206785
+ res.writeHead(400).end(JSON.stringify({ error: "actorId required" }));
206786
+ return;
206787
+ }
206788
+ const svc = await connectorActorsService();
206789
+ if (!svc) throw new Error("actors service unavailable");
206790
+ const cacheKey = `${currentCompanyId ?? ""}::${actorId}::${connectorId}`;
206791
+ const now = Date.now();
206792
+ for (const [k2, v2] of connectorAuthSnapshots) {
206793
+ if (now - v2.at > CONNECTOR_AUTH_SNAPSHOT_TTL_MS) connectorAuthSnapshots.delete(k2);
206794
+ }
206795
+ if (op === "begin") {
206796
+ const rows = await svc.snapshotActorConnectorVars(actorId, connectorId);
206797
+ connectorAuthSnapshots.set(cacheKey, { rows, at: now });
206798
+ res.writeHead(200, { "content-type": "application/json" }).end(JSON.stringify({ ok: true, snapshot: rows.length }));
206799
+ return;
206800
+ }
206801
+ if (op === "commit") {
206802
+ connectorAuthSnapshots.delete(cacheKey);
206803
+ res.writeHead(200, { "content-type": "application/json" }).end(JSON.stringify({ ok: true }));
206804
+ return;
206805
+ }
206806
+ const snap = connectorAuthSnapshots.get(cacheKey);
206807
+ if (!snap) {
206808
+ res.writeHead(200, { "content-type": "application/json" }).end(JSON.stringify({ ok: false, reason: "no_snapshot" }));
206809
+ return;
206810
+ }
206811
+ const out = await svc.restoreActorConnectorVars(
206812
+ actorId,
206813
+ connectorId,
206814
+ snap.rows
206815
+ );
206816
+ connectorAuthSnapshots.delete(cacheKey);
206817
+ res.writeHead(200, { "content-type": "application/json" }).end(JSON.stringify({ ok: true, ...out }));
206818
+ } catch (e) {
206819
+ res.writeHead(500).end(JSON.stringify({ error: String(e) }));
206820
+ }
206821
+ return;
206822
+ }
206759
206823
  if (req.method === "POST" && /^\/api\/connectors\/[^/]+\/disconnect$/.test(url.pathname)) {
206760
206824
  const connId = decodeURIComponent(url.pathname.split("/")[3] ?? "");
206761
206825
  const svc = await connectorActorsService();
@@ -207193,7 +207257,7 @@ ${composed}`;
207193
207257
  grantedFrom = "installation";
207194
207258
  }
207195
207259
  }
207196
- const missing = missingAppPermissions(effective);
207260
+ const missing = missingAppPermissions({ ...meta.permissions, ...effective });
207197
207261
  const requestedAll = Object.entries(meta.permissions).filter(([, level]) => Boolean(level)).map(([key, level]) => ({ key, level: String(level) })).sort((a, b2) => a.key.localeCompare(b2.key));
207198
207262
  const grantedKeys = new Set(
207199
207263
  Object.entries(effective).filter(([, level]) => Boolean(level)).map(([k2]) => k2)
@@ -207218,13 +207282,19 @@ ${composed}`;
207218
207282
  */
207219
207283
  otherInstallations: installs.filter((i) => i.installationId !== installationId).map((i) => ({ account: i.account, count: Object.keys(i.permissions).length })),
207220
207284
  /**
207221
- * App 申请了、但**这一处安装铸出来的 token 里没有**。
207222
- * **在全集上做差**,不再只看本仓必需的那八项(否则人另外勾的永远报不出来)。
207223
- *
207224
- * ⚠ 差集只说明「这一处安装现在给不了」,不等于「人没批」——同一个 App 装多处时
207225
- * 批准是逐处的(见 `otherInstallations`)。界面别把这层推断说死。
207285
+ * 保留只为**兼容老前端**(新卡片不再读它):新口径不分「已授权 / 申请中」,
207286
+ * 见 `detected`。这一格哪天确认没有旧客户端在读了就可以删。
207226
207287
  */
207227
207288
  awaitingApproval: requestedAll.filter((r) => !grantedKeys.has(r.key)).map((r) => r.key),
207289
+ /**
207290
+ * **检测到的全部权限**(App 申请的 ∪ 真拿得到的),英文原名 + 级别,按名排序。
207291
+ * 界面就照这一份平铺成一排 chip,不分组、不计数、不解释审批状态。
207292
+ */
207293
+ detected: [.../* @__PURE__ */ new Set([...requestedAll.map((r) => r.key), ...grantedKeys])].sort((a, b2) => a.localeCompare(b2)).map((key) => ({
207294
+ key,
207295
+ // 真给的级别优先;只在 App 侧见过的才回落成它申请的级别。
207296
+ level: String(effective[key] ?? meta.permissions[key] ?? "")
207297
+ })),
207228
207298
  /**
207229
207299
  * agent **真正拿得到**的全部权限(优先读 token 自己的权限位),一律用 GitHub 的英文原名 + 级别
207230
207300
  * (2026-09-10 发起人:「所有权限都用英文原文,不区分是不是八项权限」)。
@@ -207661,7 +207731,7 @@ ${composed}`;
207661
207731
  }
207662
207732
  };
207663
207733
  }
207664
- var http, import_node_crypto37, githubAppPending, defaultResolveActor, enc, CHAT_OUTPUT_LIMIT, STREAMING_PATHS, PLANNER_BLOCKING_CODES, WILL_VERBS, GOVERNANCE_COMMANDS, READ_ONLY_COMMANDS, isAgent, isHuman;
207734
+ var http, import_node_crypto37, githubAppPending, connectorAuthSnapshots, CONNECTOR_AUTH_SNAPSHOT_TTL_MS, defaultResolveActor, enc, CHAT_OUTPUT_LIMIT, STREAMING_PATHS, PLANNER_BLOCKING_CODES, WILL_VERBS, GOVERNANCE_COMMANDS, READ_ONLY_COMMANDS, isAgent, isHuman;
207665
207735
  var init_server3 = __esm({
207666
207736
  "../server/src/server.ts"() {
207667
207737
  "use strict";
@@ -207719,6 +207789,8 @@ var init_server3 = __esm({
207719
207789
  init_src8();
207720
207790
  init_continuation();
207721
207791
  githubAppPending = new PendingAppCreations();
207792
+ connectorAuthSnapshots = /* @__PURE__ */ new Map();
207793
+ CONNECTOR_AUTH_SNAPSHOT_TTL_MS = 30 * 60 * 1e3;
207722
207794
  defaultResolveActor = (token) => token.startsWith("token:") ? token.slice("token:".length) : null;
207723
207795
  enc = (s2) => new TextEncoder().encode(s2);
207724
207796
  CHAT_OUTPUT_LIMIT = 16e3;
@@ -219564,6 +219636,46 @@ ${input.description}
219564
219636
  listActorConnectorConnections(actorId) {
219565
219637
  return this.opts.store.listActorConnectorConnections(actorId);
219566
219638
  }
219639
+ /**
219640
+ * 授权流程**开始前**给这名员工在这个连接器下的全部个人变量拍一张快照(原样密文,不解密)。
219641
+ *
219642
+ * 为什么需要它(2026-09-10 发起人:「如果本身已经有连接器身份,如果重新连接没走完,
219643
+ * 则保留原身份不覆盖,之前重连到一半把原来的身份覆盖了」):
219644
+ * 「重新连接」是**边走边落库**的——飞书在「创建应用」那一步就把 `FEISHU_APP_ID` /
219645
+ * `FEISHU_APP_SECRET` 覆盖掉了,GitHub 在 manifest 回调那一步就把 `GITHUB_APP_*` 覆盖掉了,
219646
+ * 而这两步都在「用户完成授权」**之前**。人中途关掉弹窗,旧身份就已经没了。
219647
+ *
219648
+ * 前端此前的回滚只删「这次新增的键」(基线差集),**对已存在的键无能为力**——它拿不到旧值
219649
+ * (读侧永远只给掩码)。所以快照必须在服务端拍、也在服务端写回。
219650
+ */
219651
+ async snapshotActorConnectorVars(actorId, connectorId) {
219652
+ const rows = await this.opts.store.listVariables(actorId);
219653
+ return rows.filter((v2) => v2.scope === "personal" && v2.actorId === actorId && v2.connectorId === connectorId);
219654
+ }
219655
+ /**
219656
+ * 把快照原样写回:**快照里有的恢复旧值,快照之后新冒出来的键删掉**。
219657
+ *
219658
+ * 密文原样搬运,不经解密再加密——少一次明文落到内存里,也不受密钥轮换影响。
219659
+ * 返回改了几行,调用方好把「回滚做了什么」如实说出来(回滚静默是上一版的毛病)。
219660
+ */
219661
+ async restoreActorConnectorVars(actorId, connectorId, snapshot) {
219662
+ const keep = new Map(snapshot.map((r) => [r.key, r]));
219663
+ const now = await this.snapshotActorConnectorVars(actorId, connectorId);
219664
+ let removed = 0;
219665
+ for (const row of now) {
219666
+ if (keep.has(row.key)) continue;
219667
+ await this.deleteVariable(row.key, actorId);
219668
+ removed += 1;
219669
+ }
219670
+ let restored = 0;
219671
+ for (const row of keep.values()) {
219672
+ const current = now.find((r) => r.key === row.key);
219673
+ if (current && current.valueEncrypted === row.valueEncrypted) continue;
219674
+ await this.opts.store.putVariable({ ...row, updatedAt: this.now() });
219675
+ restored += 1;
219676
+ }
219677
+ return { restored, removed };
219678
+ }
219567
219679
  /**
219568
219680
  * 员工连接器面板所需状态:连接记录 + 组织级已连接集合 + 该员工已授权(有个人 connector 变量)集合。
219569
219681
  * 前端据此算每个 connector 的 hasRecord / 有效 enabled / connected(actor 或 global)。
@@ -271244,7 +271356,7 @@ function shimScript() {
271244
271356
  }
271245
271357
 
271246
271358
  // src/index.ts
271247
- var PKG_VERSION = true ? "2.2.12" : "dev";
271359
+ var PKG_VERSION = true ? "2.2.13" : "dev";
271248
271360
  var LOCAL_BIN = localBin();
271249
271361
  var NPM_PREFIX = npmPrefix();
271250
271362
  var INSTANCE = DEFAULT_INSTANCE;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oasis_test_v2",
3
- "version": "2.2.12",
3
+ "version": "2.2.13",
4
4
  "description": "Oasis node daemon + CLI — background daemon, auto-start, full server CLI",
5
5
  "bin": {
6
6
  "oasis": "./dist/index.js"
@@ -26,6 +26,6 @@
26
26
  "node": ">=20"
27
27
  },
28
28
  "oasisRelease": {
29
- "sourceHead": "ae18ecf5995f37b7a3f14b601e3a87c21d91ab03"
29
+ "sourceHead": "b907ec1d1a0ae4a57e7eb938c477f05323b84590"
30
30
  }
31
31
  }