oasis_test_v2 2.2.36 → 2.2.38

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 +362 -130
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -5645,7 +5645,6 @@ CREATE TABLE IF NOT EXISTS ${q2}.workorders (
5645
5645
  paused_reason text,
5646
5646
  done_at timestamptz,
5647
5647
  structural_version integer NOT NULL DEFAULT 0,
5648
- row_version integer NOT NULL DEFAULT 0,
5649
5648
  created_at timestamptz NOT NULL,
5650
5649
  created_by text NOT NULL,
5651
5650
  updated_at timestamptz NOT NULL
@@ -5660,7 +5659,6 @@ CREATE TABLE IF NOT EXISTS ${q2}.workflow_nodes (
5660
5659
  spec text,
5661
5660
  version integer NOT NULL DEFAULT 0,
5662
5661
  assignee_actor_id text,
5663
- assignee_role text,
5664
5662
  latest_work_id text,
5665
5663
  latest_proposed_work_id text,
5666
5664
  latest_accept_id text,
@@ -5670,7 +5668,6 @@ CREATE TABLE IF NOT EXISTS ${q2}.workflow_nodes (
5670
5668
  cancelled_at timestamptz,
5671
5669
  -- \u5C01\u5B58\u539F\u56E0\uFF08accepted/cancelled/frozen\uFF09\u3002NULL = \u5B58\u91CF\u884C\uFF0C\u8BFB\u6A21\u578B\u6309 cancelled \u515C\u5E95\u3002
5672
5670
  seal_reason text,
5673
- row_version integer NOT NULL DEFAULT 0,
5674
5671
  created_at timestamptz NOT NULL,
5675
5672
  updated_at timestamptz NOT NULL
5676
5673
  );
@@ -5684,7 +5681,6 @@ CREATE TABLE IF NOT EXISTS ${q2}.workflow_edges (
5684
5681
  kind text NOT NULL CHECK (kind IN ('data','order')),
5685
5682
  required boolean NOT NULL DEFAULT true,
5686
5683
  pinned_work_id text,
5687
- consumed_work_id text,
5688
5684
  PRIMARY KEY (from_node_id, to_node_id)
5689
5685
  );
5690
5686
  CREATE INDEX IF NOT EXISTS edges_to_idx ON ${q2}.workflow_edges (to_node_id);
@@ -5886,7 +5882,6 @@ CREATE TABLE IF NOT EXISTS ${q2}.events (
5886
5882
  origin text NOT NULL CHECK (origin IN ('command','apply','effect')),
5887
5883
  dedup_key text,
5888
5884
  status text NOT NULL CHECK (status IN ('pending','applied','done','failed')),
5889
- attempts integer NOT NULL DEFAULT 0,
5890
5885
  available_at timestamptz,
5891
5886
  failed_reason text,
5892
5887
  created_at timestamptz NOT NULL,
@@ -6077,6 +6072,31 @@ BEGIN
6077
6072
  END;
6078
6073
  END $$;
6079
6074
 
6075
+ -- ADR 0564\uFF1A\u9000\u5F79\u672A\u6D88\u8D39\u7684\u8BA1\u6570\u5668\u548C\u5355\u5411\u526F\u672C\uFF1B\u5386\u53F2 events.payload \u539F\u6587\u4E0D\u6539\u3002
6076
+ -- \u90E8\u7F72\u524D\u5907\u4EFD\u5B58\u91CF\u5E93\uFF08\u542B\u975E\u7A7A consumed_work_id / assignee_role\uFF09\u3002\u6309\u5217\u9694\u79BB\u6E05\u7406\uFF0C
6077
+ -- \u5916\u90E8\u4F9D\u8D56\u53EA\u63A8\u8FDF\u8BE5\u5217\uFF0C\u4E0D CASCADE\u3001\u4E0D\u963B\u65AD\u542F\u52A8\uFF08\u6CBF\u7528 ADR 0512 \u7684\u53EF\u7528\u6027\u7EA6\u675F\uFF09\u3002
6078
+ -- \u81EA\u6709 node_view \u5DF2\u5728\u4E0A\u65B9\u5220\u9664\uFF0C\u540E\u7EED ENGINE_VIEWS \u6309\u65B0\u5217\u96C6\u91CD\u5EFA\uFF1B\u4ECD\u88AB\u4FDD\u7559\u7684\u65E7
6079
+ -- workorder_view \u6309\u5916\u90E8\u4F9D\u8D56\u5904\u7406\uFF0C\u4E0D\u80FD\u4E3A\u672C\u6B21\u9000\u5F79\u7834\u574F ADR 0512 \u7684\u5386\u53F2\u503C\u4FDD\u7559\u3002
6080
+ DO $$
6081
+ DECLARE obsolete record;
6082
+ BEGIN
6083
+ FOR obsolete IN SELECT * FROM (VALUES
6084
+ ('workorders', 'row_version'),
6085
+ ('workflow_nodes', 'row_version'),
6086
+ ('workflow_nodes', 'assignee_role'),
6087
+ ('workflow_edges', 'consumed_work_id'),
6088
+ ('events', 'attempts')
6089
+ ) AS retired(table_name, column_name) LOOP
6090
+ BEGIN
6091
+ EXECUTE format('ALTER TABLE %I.%I DROP COLUMN IF EXISTS %I',
6092
+ ${lit}, obsolete.table_name, obsolete.column_name);
6093
+ EXCEPTION WHEN dependent_objects_still_exist THEN
6094
+ RAISE WARNING 'Retaining retired engine field %.%.%: external dependencies require reconciliation (ADR 0564)',
6095
+ ${lit}, obsolete.table_name, obsolete.column_name;
6096
+ END;
6097
+ END LOOP;
6098
+ END $$;
6099
+
6080
6100
  -- \u8C03\u5EA6\u5668\u5B8C\u7ED3\u6807\u8BB0\uFF08mark_done/unmark_done \u843D\u70B9\uFF0C2026-08-12\uFF09\u3002
6081
6101
  ALTER TABLE ${q2}.workorders ADD COLUMN IF NOT EXISTS done_at timestamptz;
6082
6102
  -- \u2605 \u5DE5\u5355\u7EA7\u5C01\u5B58\u6574\u4F53\u79FB\u9664\uFF08ADR 0137\uFF09\uFF1Alifecycle \u53EA\u5269 draft/active\uFF0Cseal_reason \u5217\u5220\u9664\u3002
@@ -11329,7 +11349,6 @@ function toRecord(r) {
11329
11349
  origin: r.origin,
11330
11350
  dedupKey: r.dedup_key ?? null,
11331
11351
  status: r.status,
11332
- attempts: Number(r.attempts ?? 0),
11333
11352
  availableAt: iso(r.available_at),
11334
11353
  failedReason: r.failed_reason ?? null,
11335
11354
  createdAt: iso(r.created_at),
@@ -11558,7 +11577,6 @@ var init_store_postgres = __esm({
11558
11577
  pausedReason: r.paused_reason,
11559
11578
  doneAt: iso(r.done_at),
11560
11579
  structuralVersion: r.structural_version,
11561
- rowVersion: r.row_version,
11562
11580
  createdAt: iso(r.created_at),
11563
11581
  createdBy: r.created_by,
11564
11582
  updatedAt: iso(r.updated_at)
@@ -11571,7 +11589,6 @@ var init_store_postgres = __esm({
11571
11589
  spec: n.spec,
11572
11590
  version: n.version ?? 0,
11573
11591
  assigneeActorId: n.assignee_actor_id,
11574
- assigneeRole: n.assignee_role,
11575
11592
  latestWorkId: n.latest_work_id,
11576
11593
  latestProposedWorkId: n.latest_proposed_work_id ?? null,
11577
11594
  latestAcceptId: n.latest_accept_id,
@@ -11581,7 +11598,6 @@ var init_store_postgres = __esm({
11581
11598
  sealReason: n.seal_reason ?? null,
11582
11599
  fields: n.fields ?? void 0,
11583
11600
  continuationBudget: n.continuation_budget ?? null,
11584
- rowVersion: n.row_version,
11585
11601
  createdAt: iso(n.created_at),
11586
11602
  updatedAt: iso(n.updated_at)
11587
11603
  })),
@@ -11591,8 +11607,7 @@ var init_store_postgres = __esm({
11591
11607
  toNodeId: e.to_node_id,
11592
11608
  kind: e.kind,
11593
11609
  required: e.required,
11594
- pinnedWorkId: e.pinned_work_id,
11595
- consumedWorkId: e.consumed_work_id
11610
+ pinnedWorkId: e.pinned_work_id
11596
11611
  })),
11597
11612
  requirements: reqs.rows.map((q2) => ({
11598
11613
  nodeId: q2.node_id,
@@ -11731,8 +11746,8 @@ var init_store_postgres = __esm({
11731
11746
  await this.c.query(
11732
11747
  `INSERT INTO ${this.s}.workorders
11733
11748
  (id,company_id,title,owner_actor_id,lifecycle,dispatch_paused,paused_by,paused_reason,
11734
- done_at,structural_version,row_version,created_at,created_by,updated_at)
11735
- VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14)
11749
+ done_at,structural_version,created_at,created_by,updated_at)
11750
+ VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13)
11736
11751
  ON CONFLICT (id) DO NOTHING`,
11737
11752
  [
11738
11753
  r.id,
@@ -11745,7 +11760,6 @@ var init_store_postgres = __esm({
11745
11760
  r.pausedReason,
11746
11761
  r.doneAt,
11747
11762
  r.structuralVersion,
11748
- r.rowVersion,
11749
11763
  r.createdAt,
11750
11764
  r.createdBy,
11751
11765
  r.updatedAt
@@ -11760,10 +11774,10 @@ var init_store_postgres = __esm({
11760
11774
  const n = m2.row;
11761
11775
  await this.c.query(
11762
11776
  `INSERT INTO ${this.s}.workflow_nodes
11763
- (id,workorder_id,type,title,spec,version,assignee_actor_id,assignee_role,
11777
+ (id,workorder_id,type,title,spec,version,assignee_actor_id,
11764
11778
  latest_work_id,latest_proposed_work_id,latest_accept_id,is_final_output,
11765
- fields,cancelled_at,seal_reason,row_version,created_at,updated_at,continuation_budget)
11766
- VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19)
11779
+ fields,cancelled_at,seal_reason,created_at,updated_at,continuation_budget)
11780
+ VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17)
11767
11781
  ON CONFLICT (id) DO NOTHING`,
11768
11782
  [
11769
11783
  n.id,
@@ -11773,7 +11787,6 @@ var init_store_postgres = __esm({
11773
11787
  n.spec,
11774
11788
  n.version,
11775
11789
  n.assigneeActorId,
11776
- n.assigneeRole,
11777
11790
  n.latestWorkId,
11778
11791
  n.latestProposedWorkId,
11779
11792
  n.latestAcceptId,
@@ -11781,7 +11794,6 @@ var init_store_postgres = __esm({
11781
11794
  n.fields ? JSON.stringify(n.fields) : null,
11782
11795
  n.cancelledAt,
11783
11796
  n.sealReason ?? null,
11784
- n.rowVersion,
11785
11797
  n.createdAt,
11786
11798
  n.updatedAt,
11787
11799
  n.continuationBudget ? JSON.stringify(n.continuationBudget) : null
@@ -11796,9 +11808,9 @@ var init_store_postgres = __esm({
11796
11808
  const e = m2.row;
11797
11809
  await this.c.query(
11798
11810
  `INSERT INTO ${this.s}.workflow_edges
11799
- (workorder_id,from_node_id,to_node_id,kind,required,pinned_work_id,consumed_work_id)
11800
- VALUES ($1,$2,$3,$4,$5,$6,$7) ON CONFLICT (from_node_id,to_node_id) DO NOTHING`,
11801
- [e.workorderId, e.fromNodeId, e.toNodeId, e.kind, e.required, e.pinnedWorkId, e.consumedWorkId]
11811
+ (workorder_id,from_node_id,to_node_id,kind,required,pinned_work_id)
11812
+ VALUES ($1,$2,$3,$4,$5,$6) ON CONFLICT (from_node_id,to_node_id) DO NOTHING`,
11813
+ [e.workorderId, e.fromNodeId, e.toNodeId, e.kind, e.required, e.pinnedWorkId]
11802
11814
  );
11803
11815
  break;
11804
11816
  }
@@ -12017,8 +12029,8 @@ var init_store_postgres = __esm({
12017
12029
  const res = await this.c.query(
12018
12030
  `INSERT INTO ${this.s}.events
12019
12031
  (company_id,workorder_id,node_id,work_id,actor_id,hand_actor_id,kind,payload,caused_by_seq,
12020
- origin,dedup_key,status,attempts,created_at)
12021
- VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,'pending',0,$12)
12032
+ origin,dedup_key,status,created_at)
12033
+ VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,'pending',$12)
12022
12034
  ON CONFLICT (workorder_id, dedup_key) WHERE dedup_key IS NOT NULL DO NOTHING
12023
12035
  RETURNING seq`,
12024
12036
  [
@@ -12203,13 +12215,11 @@ function newNode(id, workorderId, at, version2) {
12203
12215
  spec: null,
12204
12216
  version: version2,
12205
12217
  assigneeActorId: null,
12206
- assigneeRole: null,
12207
12218
  latestWorkId: null,
12208
12219
  latestProposedWorkId: null,
12209
12220
  latestAcceptId: null,
12210
12221
  isFinalOutput: false,
12211
12222
  cancelledAt: null,
12212
- rowVersion: 0,
12213
12223
  createdAt: at,
12214
12224
  updatedAt: at
12215
12225
  };
@@ -12280,7 +12290,6 @@ var init_plan = __esm({
12280
12290
  title: e.title,
12281
12291
  spec: e.spec,
12282
12292
  assigneeActorId: e.assigneeActorId,
12283
- assigneeRole: e.assigneeRole,
12284
12293
  isFinalOutput: e.isFinalOutput,
12285
12294
  ...e.fields ? { fields: e.fields } : {}
12286
12295
  });
@@ -12332,8 +12341,7 @@ var init_plan = __esm({
12332
12341
  toNodeId: e.toNodeId,
12333
12342
  kind: e.edgeKind,
12334
12343
  required: e.required,
12335
- pinnedWorkId: upstream?.latestAcceptId ?? null,
12336
- consumedWorkId: null
12344
+ pinnedWorkId: upstream?.latestAcceptId ?? null
12337
12345
  });
12338
12346
  const dn = state.node(e.toNodeId);
12339
12347
  if (dn) state.updateNode(e.toNodeId, { version: dn.version + 1 });
@@ -12362,7 +12370,6 @@ var init_plan = __esm({
12362
12370
  }
12363
12371
  state.updateNode(e.nodeId, {
12364
12372
  assigneeActorId: e.assigneeActorId,
12365
- assigneeRole: e.assigneeRole,
12366
12373
  ...changed && !settled ? { version: node2.version + 1 } : {},
12367
12374
  updatedAt: ctx.at
12368
12375
  });
@@ -12458,7 +12465,6 @@ var init_plan = __esm({
12458
12465
  title: spec.title,
12459
12466
  spec: spec.spec,
12460
12467
  assigneeActorId: spec.assigneeActorId,
12461
- assigneeRole: spec.assigneeRole,
12462
12468
  isFinalOutput: spec.isFinalOutput,
12463
12469
  reviewers: spec.reviewers,
12464
12470
  ...spec.fields ? { fields: spec.fields } : {}
@@ -12528,7 +12534,6 @@ var init_workorder = __esm({
12528
12534
  pausedReason: null,
12529
12535
  doneAt: null,
12530
12536
  structuralVersion: 0,
12531
- rowVersion: 0,
12532
12537
  createdAt: ctx.at,
12533
12538
  createdBy: ctx.actorId,
12534
12539
  updatedAt: ctx.at
@@ -12543,7 +12548,7 @@ var init_workorder = __esm({
12543
12548
  ([key]) => Object.hasOwn(state.workorder, key)
12544
12549
  ));
12545
12550
  if (Object.keys(patch).length === 0) return;
12546
- state.updateWorkorder({ ...patch, updatedAt: ctx.at, rowVersion: state.workorder.rowVersion + 1 });
12551
+ state.updateWorkorder({ ...patch, updatedAt: ctx.at });
12547
12552
  }
12548
12553
  };
12549
12554
  workorderPause = {
@@ -13347,11 +13352,6 @@ var init_review = __esm({
13347
13352
  for (const r of state.openReviewsOf(e.workId)) {
13348
13353
  state.updateReview(r.id, { cancelledAt: ctx.at, status: "dead" });
13349
13354
  }
13350
- const inputs = state.workInputsOf(e.workId);
13351
- for (const edge of state.edgesInto(w2.nodeId)) {
13352
- const real = inputs.find((i) => i.upstreamNodeId === edge.fromNodeId);
13353
- state.updateEdge(edge.fromNodeId, edge.toNodeId, { consumedWorkId: real?.upstreamWorkId ?? edge.pinnedWorkId });
13354
- }
13355
13355
  for (const edge of state.edgesFrom(w2.nodeId)) {
13356
13356
  state.updateEdge(edge.fromNodeId, edge.toNodeId, { pinnedWorkId: e.workId });
13357
13357
  }
@@ -13614,7 +13614,6 @@ function emptySnapshotPlaceholder(record8) {
13614
13614
  pausedReason: null,
13615
13615
  doneAt: null,
13616
13616
  structuralVersion: 0,
13617
- rowVersion: 0,
13618
13617
  createdAt: "",
13619
13618
  createdBy: "",
13620
13619
  updatedAt: ""
@@ -19657,7 +19656,7 @@ function groupRequirementsForPreview(rows) {
19657
19656
  const out = [];
19658
19657
  for (const [key, b2] of groups) {
19659
19658
  if (b2.source === "type") {
19660
- out.push({ source: "type", eligible: [], quorum: 1, roles: [key] });
19659
+ out.push({ source: "type", eligible: b2.eligible, quorum: 1, roles: [key] });
19661
19660
  } else {
19662
19661
  out.push({ source: b2.source, eligible: b2.eligible, quorum: 1 });
19663
19662
  }
@@ -20140,9 +20139,14 @@ var init_kernel_bridge = __esm({
20140
20139
  const nodeReqs = a.reviewers ?? [];
20141
20140
  if (nodeReqs.length > 0) {
20142
20141
  const requirements2 = groupRequirementsForPreview(nodeReqs);
20143
- const namedActors2 = nodeReqs.filter((r) => r.source !== "type").map((r) => ({ actor: r.actor, source: r.source }));
20142
+ const listed = nodeReqs.filter((r) => r.source !== "type").map((r) => ({ actor: r.actor, source: r.source }));
20143
+ for (const r of nodeReqs) {
20144
+ if (r.source === "type" && !listed.some((n) => n.actor === r.actor)) {
20145
+ listed.push({ actor: r.actor, source: "type" });
20146
+ }
20147
+ }
20144
20148
  return {
20145
- reviewers: namedActors2,
20149
+ reviewers: listed,
20146
20150
  quorum: requirements2.length,
20147
20151
  requirements: requirements2,
20148
20152
  typeName: a.type
@@ -20236,7 +20240,6 @@ var init_kernel_bridge = __esm({
20236
20240
  title: args.title ?? args.description ?? args.type,
20237
20241
  spec: args.description ?? null,
20238
20242
  assigneeActorId: owner,
20239
- assigneeRole: null,
20240
20243
  isFinalOutput: args.isFinalOutput ?? false,
20241
20244
  // ★ docType(projects 域的具体文档类型)随节点 fields 落库(同 reviewRoles 的做法,jsonb 免加列);
20242
20245
  // bridge 投影时从 fields 剥出成 artifact.docType、不混进用户自定义字段。
@@ -21101,8 +21104,7 @@ var init_kernel_bridge = __esm({
21101
21104
  kind: "plan.assign_actor",
21102
21105
  workorderId: wid,
21103
21106
  nodeId: args.artifactId,
21104
- assigneeActorId: args.owner,
21105
- assigneeRole: null
21107
+ assigneeActorId: args.owner
21106
21108
  }
21107
21109
  });
21108
21110
  }
@@ -21628,7 +21630,6 @@ var init_kernel_bridge = __esm({
21628
21630
  title: op.title ?? op.type,
21629
21631
  spec: op.description ?? null,
21630
21632
  assigneeActorId: spawnOwner,
21631
- assigneeRole: null,
21632
21633
  isFinalOutput: op.isFinalOutput ?? false,
21633
21634
  ...allReviewers.length > 0 ? { reviewers: allReviewers } : {},
21634
21635
  ...mergedFields ? { fields: mergedFields } : {}
@@ -21675,7 +21676,7 @@ var init_kernel_bridge = __esm({
21675
21676
  break;
21676
21677
  }
21677
21678
  case "assign":
21678
- await enqueue({ kind: "plan.assign_actor", workorderId: woFor(op.artifactId), nodeId: op.artifactId, assigneeActorId: op.owner, assigneeRole: null });
21679
+ await enqueue({ kind: "plan.assign_actor", workorderId: woFor(op.artifactId), nodeId: op.artifactId, assigneeActorId: op.owner });
21679
21680
  applied++;
21680
21681
  break;
21681
21682
  case "seal":
@@ -31180,6 +31181,9 @@ var init_chat_turn_gate = __esm({
31180
31181
  function isDeletedActor(actor) {
31181
31182
  return actor?.status === "disabled";
31182
31183
  }
31184
+ function hideDeleted(rows) {
31185
+ return rows.filter((row) => row.status !== "disabled");
31186
+ }
31183
31187
  var ACTOR_DELETED_CHAT_MESSAGE, ACTOR_DELETED_ERROR_CODE, ActorAlreadyDeletedError;
31184
31188
  var init_deletion = __esm({
31185
31189
  "../server/src/domains/actors/deletion.ts"() {
@@ -203936,6 +203940,19 @@ function resolveCallbackBase(supplied, requestHost) {
203936
203940
  }
203937
203941
  return `${parsed.origin}${parsed.pathname.replace(/\/+$/, "")}`;
203938
203942
  }
203943
+ function companyAmbiguousError(actor, companies) {
203944
+ return new ApiError(
203945
+ 400,
203946
+ "COMPANY_AMBIGUOUS",
203947
+ `\u65E0\u6CD5\u786E\u5B9A\u5F53\u524D\u516C\u53F8\uFF1A${actor} \u5C5E\u4E8E\u591A\u5BB6\u516C\u53F8\uFF08${companies.join("\u3001")}\uFF09\uFF0C\u672C\u6B21\u8BF7\u6C42\u6CA1\u6709\u6307\u5B9A\u3002\u8BF7\u7528 x-oasis-company \u5934\u6307\u5B9A\u516C\u53F8 id \u6216 slug\uFF08CLI\uFF1A--company <\u516C\u53F8> \u6216 $OASIS_COMPANY\uFF09\u3002`,
203948
+ { companies: [...companies] }
203949
+ );
203950
+ }
203951
+ function writeApiError(res, err) {
203952
+ res.writeHead(err.status, { "content-type": "application/json" }).end(JSON.stringify({
203953
+ error: { code: err.code, message: err.message, ...err.details ?? {} }
203954
+ }));
203955
+ }
203939
203956
  function toApiError(err) {
203940
203957
  if (err instanceof ApiError) return err;
203941
203958
  const message = err instanceof Error ? err.message : String(err);
@@ -206281,6 +206298,10 @@ async function startOasisServer(opts) {
206281
206298
  }
206282
206299
  if (url.pathname.startsWith("/api/files/")) {
206283
206300
  const company = opts.resolveCompanyContext ? await opts.resolveCompanyContext(actor, req.headers, url.pathname) : void 0;
206301
+ if (company?.kind === "ambiguous") {
206302
+ writeApiError(res, companyAmbiguousError(actor, company.companies));
206303
+ return;
206304
+ }
206284
206305
  if (company && company.kind !== "ok") {
206285
206306
  res.writeHead(403, { "content-type": "application/json" }).end(JSON.stringify({
206286
206307
  error: { code: "FORBIDDEN", message: "\u65E0\u6743\u8BFB\u53D6\u8BE5\u516C\u53F8\u7684\u6587\u4EF6" }
@@ -206321,6 +206342,10 @@ async function startOasisServer(opts) {
206321
206342
  const rawBody = Buffer.concat(chunks).toString("utf8");
206322
206343
  const companyCtx = opts.resolveCompanyContext ? await opts.resolveCompanyContext(actor, req.headers, url.pathname) : void 0;
206323
206344
  const currentCompanyId = companyCtx?.kind === "ok" ? companyCtx.companyId : void 0;
206345
+ if (companyCtx?.kind === "ambiguous") {
206346
+ writeApiError(res, companyAmbiguousError(actor, companyCtx.companies));
206347
+ return;
206348
+ }
206324
206349
  const connectorActorsService = async (companyId) => opts.actors ? (await opts.actors.resolveCtx(companyId ?? currentCompanyId)).service : void 0;
206325
206350
  const wodrafts = opts.resolveWorkorderDrafts ? await opts.resolveWorkorderDrafts(currentCompanyId) : opts.workorderDrafts;
206326
206351
  const wodraftPlannerIssues = (opts.resolveWorkorderDraftPlannerIssues && await opts.resolveWorkorderDraftPlannerIssues(currentCompanyId)) ?? opts.workorderDraftPlannerIssues ?? defaultDraftPlannerIssuesStore;
@@ -210253,6 +210278,11 @@ var init_memory_registry_store = __esm({
210253
210278
  ...v2.updatedBy ? { updatedBy: v2.updatedBy } : {}
210254
210279
  });
210255
210280
  }
210281
+ async insertVariable(v2) {
210282
+ if (this.variables.has(this._varKey(v2.key, v2.actorId, v2.projectId))) return false;
210283
+ await this.putVariable(v2);
210284
+ return true;
210285
+ }
210256
210286
  async touchVariableUsage(key, actorId, projectId2, when) {
210257
210287
  const mk = this._varKey(key, actorId, projectId2);
210258
210288
  const ex = this.variables.get(mk);
@@ -216681,6 +216711,9 @@ var init_company_scoped_registry = __esm({
216681
216711
  async putVariable(...args) {
216682
216712
  return this.base.putVariable(...args);
216683
216713
  }
216714
+ async insertVariable(...args) {
216715
+ return this.base.insertVariable(...args);
216716
+ }
216684
216717
  async patchVariableValue(...args) {
216685
216718
  return this.base.patchVariableValue(...args);
216686
216719
  }
@@ -221846,6 +221879,9 @@ function computeEffectiveEntries(config2, connections, connectors) {
221846
221879
  }
221847
221880
  return out;
221848
221881
  }
221882
+ function personalConnectionPairs(vars) {
221883
+ return vars.flatMap((v2) => v2.scope === "personal" && v2.actorId && v2.connectorId ? [{ actorId: v2.actorId, connectorId: v2.connectorId }] : []);
221884
+ }
221849
221885
  function variableKeyFromEnv(env = process.env) {
221850
221886
  const hex = env.OASIS_VAR_KEY;
221851
221887
  if (hex) {
@@ -222587,35 +222623,43 @@ ${input.description}
222587
222623
  }
222588
222624
  }
222589
222625
  /**
222590
- * 连接器 + 分配派生:按每个 AI 员工的**有效启用**口径聚合「此连接器对谁生效」(决策 0080:
222591
- * 连接记录 > 旧 connectorIds > 组织默认,与运行时注入同一判定)。此前只扫旧 connectorIds,
222592
- * 组织页显示的员工集合与真正生效的注入口径脱节。
222626
+ * 管理页连接器列表的「N 个 Agent 已连接」:每个连接器上**建立过 Agent 层连接**的 Agent
222627
+ * (判据见 {@link personalConnectionPairs},与类型详情页的行、组织页 tab 同一份)。
222628
+ *
222629
+ * 口径与原型帧 `1496:5390` / `1551:767` 对齐:卡片上的数 = 点进去那一页的行数——
222630
+ * 开关关着的、凭据不全或解不开的都是一行,也都算一个;从没连过的不算。
222631
+ *
222632
+ * 两处刻意的范围:
222633
+ * · 人群 = 花名册(`GET /api/actors?kind=agent`)那一群:**已删除的 Agent 不算**——详情页压根列不出他们,
222634
+ * 算进来就是「卡片 5、点进去 4」。
222635
+ * · 不以连接器表为界:2026-09-09 起连接器类型对所有组织默认可用、个人授权又不碰组织行,
222636
+ * 连接器表**天然是空的**(二期生产 09-15 实测 `items: []`)。此前只对表里有行的连接器聚合,
222637
+ * 卡片恒为「暂无连接的 Agent」、点进去却有好几位。没有组织行的连接器单列在
222638
+ * `connectedActorIdsByConnector` 里,**不**编一条 Connector 记录塞进 `items`(没有的 name/mode 不造)。
222639
+ *
222640
+ * 这里**不再**按 `effective` 数(此前的「对谁生效」口径):那是注入闸,含组织默认与旧 connectorIds 两档,
222641
+ * 数出来的人与详情行、组织页 tab 都对不上。注入闸本身(`computeEffectiveEntries`)不动。
222593
222642
  */
222594
222643
  async listConnectorsWithAssignments() {
222595
- const [connectors, agents] = await Promise.all([
222644
+ const [connectors, agents, vars] = await Promise.all([
222596
222645
  this.opts.store.listConnectors(),
222597
- this.opts.store.listActors({ kind: "agent" })
222646
+ this.opts.store.listActors({ kind: "agent" }),
222647
+ this.opts.store.listVariables()
222598
222648
  ]);
222599
- const agentIds = agents.map((a) => a.id);
222600
- const configs = this.opts.store.latestConfigs ? await this.opts.store.latestConfigs(agentIds) : new Map(await Promise.all(agentIds.map(async (id) => [id, await this.opts.store.latestConfig(id)])));
222601
- const allConns = this.opts.store.listAllActorConnectorConnections ? await this.opts.store.listAllActorConnectorConnections() : (await Promise.all(agentIds.map((id) => this.opts.store.listActorConnectorConnections(id)))).flat();
222602
- const connsByActor = /* @__PURE__ */ new Map();
222603
- for (const c of allConns) {
222604
- const arr = connsByActor.get(c.actorId) ?? [];
222605
- arr.push(c);
222606
- connsByActor.set(c.actorId, arr);
222607
- }
222649
+ const roster = new Set(hideDeleted(agents).map((a) => a.id));
222608
222650
  const byConn = /* @__PURE__ */ new Map();
222609
- for (const a of agents) {
222610
- const entries = computeEffectiveEntries(configs.get(a.id) ?? null, connsByActor.get(a.id) ?? [], connectors);
222611
- for (const e of entries) {
222612
- if (!e.enabled) continue;
222613
- const list2 = byConn.get(e.connectorId) ?? [];
222614
- list2.push(a.id);
222615
- byConn.set(e.connectorId, list2);
222616
- }
222617
- }
222618
- return connectors.map((c) => ({ ...c, connectedActorIds: [...new Set(byConn.get(c.id) ?? [])] }));
222651
+ for (const { actorId, connectorId } of personalConnectionPairs(vars)) {
222652
+ if (!roster.has(actorId)) continue;
222653
+ const set = byConn.get(connectorId) ?? /* @__PURE__ */ new Set();
222654
+ set.add(actorId);
222655
+ byConn.set(connectorId, set);
222656
+ }
222657
+ const connectedActorIdsByConnector = {};
222658
+ for (const [connectorId, set] of byConn) connectedActorIdsByConnector[connectorId] = [...set];
222659
+ return {
222660
+ items: connectors.map((c) => ({ ...c, connectedActorIds: connectedActorIdsByConnector[c.id] ?? [] })),
222661
+ connectedActorIdsByConnector
222662
+ };
222619
222663
  }
222620
222664
  /** 该员工最新配置里启用的 connector id 集合——与 resolveActorEnv 同一闸门(connectorIds 成员资格)。 */
222621
222665
  async activeConnectorIdsForActor(actorId, cfg) {
@@ -222712,17 +222756,94 @@ ${input.description}
222712
222756
  ]);
222713
222757
  const globalConnected = connectors.filter((c) => c.status === "connected").map((c) => c.id);
222714
222758
  const actorConnected = [...new Set(
222715
- vars.filter((v2) => v2.scope === "personal" && v2.actorId === actorId && v2.connectorId).map((v2) => v2.connectorId)
222759
+ personalConnectionPairs(vars).filter((p2) => p2.actorId === actorId).map((p2) => p2.connectorId)
222716
222760
  )];
222717
222761
  const effective = await this.effectiveConnectorEntries(actorId, config2, connectors);
222762
+ const { ready, assessed, undecryptable } = this.connectorCredentialReadiness(actorId, vars);
222763
+ const credentialReady = [...ready];
222764
+ const credentialUndecryptable = [...undecryptable];
222765
+ const credentialMissing = actorConnected.filter(
222766
+ (id) => undecryptable.has(id) || assessed.has(id) && !ready.has(id)
222767
+ );
222768
+ return { connections, globalConnected, actorConnected, effective, credentialReady, credentialMissing, credentialUndecryptable };
222769
+ }
222770
+ /**
222771
+ * 这名员工每个连接器的凭据齐不齐——**派单那条路的等价判定**:
222772
+ * ① `collectAllConnectorCredentials`(派单与会话中途续期共用的那个函数)现在就收得齐;或
222773
+ * ② 连接器声明了续期能力,且续期材料里 required 的几项都在(`orgDefault` 的可回落组织级,
222774
+ * 与 `readRefreshMaterials` 同口径)——`resolveActorEnv` 注入前会先跑
222775
+ * `refreshEnabledConnectorCredentials`,把短命凭据铸出来再收。
222776
+ * GitHub App 就是这一类:组织级连接只存 App 材料、token 每次派单现铸(server.ts 的 manifest
222777
+ * 回调刻意不存组织级 token)。只看 ① 会把它一刀切报成「凭据不全」。
222778
+ *
222779
+ * 续期**会不会成功**(私钥被吊销、App 被卸载、飞书 refresh_token 过期、平台不可达 / 限流)这里判不了——
222780
+ * 要真打一次平台才知道,PRD「不能获取时不伪造」,所以不算异常。
222781
+ * `assessed` = 本服务判得了的连接器(注册表里有的)。
222782
+ *
222783
+ * **解不开的密文**(变量密钥换过、坏行)不能当「没有」处理,要记成那个连接器的异常证据(`undecryptable`)。
222784
+ * 派单那条路(serve.ts `buildActorProvision` → `resolveActorProvisionEntries`;`resolveActorEnv` 同规则)
222785
+ * 对「无 connectorId 的行 + 启用中连接器的行」**逐行**解密,任何一行解不开就抛,dispatcher 记 spawn-failed
222786
+ * ——整单派不出去,不是只少一个连接器:
222787
+ * · 不回落同 key 的组织值(个人行存在就解个人行,解不开就抛;续期读材料的 `revealResolvedVariable` 同理);
222788
+ * · 不跳过可选项(用户 token、只在服务端用的 refresh_token 也逐行解);
222789
+ * · 续期读材料撞上坏行 → 该连接器本轮不续(best-effort catch),坏行原样留着,下一步解密照样抛。
222790
+ * 所以这里若把坏行当「没有」再回落组织值、或因为它可选就放过,界面会说凭据齐全而派单整单失败
222791
+ * (2026-09-15 验收时补的回归:connector-connection-consistency.test.ts「解不开的密文」一组)。
222792
+ * 反过来**不改运行时**:派单遇坏行就失败是既有的身份策略,不为了显示去动它。
222793
+ *
222794
+ * 归属:行上有 `connectorId` 就归它;没有(密钥页手录的那种)就归「声明了这个变量名的连接器」——
222795
+ * 与这里收凭据时不看 `connectorId`、只按变量名取值是同一口径。归属不到任何连接器的坏行(如组织级
222796
+ * OPENAI_API_KEY)一样会让派单失败,但它不是哪条连接的事,不拿它把无关连接器标坏。
222797
+ * 此前(更早)这里直接抛,整个 `GET /api/actors/:id/connectors` 500;现在接口照样可读。
222798
+ */
222799
+ connectorCredentialReadiness(actorId, vars) {
222800
+ const connectors = createAllConnectors();
222801
+ const declaredBy = /* @__PURE__ */ new Map();
222802
+ for (const connector of connectors) {
222803
+ for (const s2 of [...connector.credentialSources, ...connector.refreshCredentialSources ?? []]) {
222804
+ for (const key of [s2.from, ...s2.fromAny ?? []]) {
222805
+ const slugs = declaredBy.get(key) ?? [];
222806
+ if (!slugs.includes(connector.config.slug)) declaredBy.set(key, [...slugs, connector.config.slug]);
222807
+ }
222808
+ }
222809
+ }
222810
+ const undecryptable = /* @__PURE__ */ new Set();
222811
+ const decode2 = (v2) => {
222812
+ try {
222813
+ return this.decodeCiphertext(v2.valueEncrypted);
222814
+ } catch {
222815
+ for (const slug6 of v2.connectorId ? [v2.connectorId] : declaredBy.get(v2.key) ?? []) undecryptable.add(slug6);
222816
+ return void 0;
222817
+ }
222818
+ };
222718
222819
  const flat = {};
222719
- for (const v2 of vars) if (v2.scope === "global" && !v2.actorId) flat[v2.key] = this.decodeCiphertext(v2.valueEncrypted);
222720
- for (const v2 of vars) if (v2.scope === "personal" && (!v2.actorId || v2.actorId === actorId)) flat[v2.key] = this.decodeCiphertext(v2.valueEncrypted);
222721
- const credentialReady = collectAllConnectorCredentials({
222820
+ const own3 = {};
222821
+ for (const v2 of vars) {
222822
+ if (v2.scope !== "global" || v2.actorId) continue;
222823
+ const value2 = decode2(v2);
222824
+ if (value2 !== void 0) flat[v2.key] = value2;
222825
+ }
222826
+ for (const v2 of vars) {
222827
+ if (v2.scope !== "personal" || v2.actorId && v2.actorId !== actorId) continue;
222828
+ const value2 = decode2(v2);
222829
+ if (value2 === void 0) continue;
222830
+ flat[v2.key] = value2;
222831
+ if (v2.actorId === actorId) own3[v2.key] = value2;
222832
+ }
222833
+ const ready = new Set(collectAllConnectorCredentials({
222722
222834
  vars: flat,
222723
222835
  actorName: flat["GIT_AUTHOR_NAME"] ?? actorId.split(":").pop() ?? "oasis-agent"
222724
- }).map((c) => c.slug);
222725
- return { connections, globalConnected, actorConnected, effective, credentialReady };
222836
+ }).map((c) => c.slug));
222837
+ const assessed = /* @__PURE__ */ new Set();
222838
+ for (const connector of connectors) {
222839
+ const slug6 = connector.config.slug;
222840
+ assessed.add(slug6);
222841
+ if (ready.has(slug6) || !connector.refreshCredentials) continue;
222842
+ const materials = (connector.refreshCredentialSources ?? []).filter((s2) => s2.required);
222843
+ if (materials.length > 0 && materials.every((s2) => Boolean((s2.orgDefault ? flat : own3)[s2.from]))) ready.add(slug6);
222844
+ }
222845
+ for (const slug6 of undecryptable) ready.delete(slug6);
222846
+ return { ready, assessed, undecryptable };
222726
222847
  }
222727
222848
  /**
222728
222849
  * 该员工每个连接器的「有效启用」集合(变量/技能注入闸门):
@@ -222815,6 +222936,23 @@ ${input.description}
222815
222936
  if (v2.value === void 0 || v2.value === "") {
222816
222937
  return;
222817
222938
  }
222939
+ await this.opts.store.putVariable(this.toStoredVariableRow(v2, v2.value));
222940
+ }
222941
+ /**
222942
+ * **新建**一条变量(决策 0565):这一行(组织变量按 key、个人变量按 key + actorId、项目变量按 key + projectId)
222943
+ * 已经存在就返回 `"exists"`、什么都不写——不像 `putVariable` 那样悄悄覆盖。
222944
+ *
222945
+ * 「管理页新建组织变量」「Agent 详情新建个人变量」都走这里:同一组织第二条同名组织变量、同一 Agent
222946
+ * 第二条同名个人变量在存储层当场被挡下;不同 Agent 的同名个人变量、以及个人变量与同名组织变量
222947
+ * 是不同的唯一键,照常共存。值必填(新建出一条没有值的空壳没有意义)。
222948
+ */
222949
+ async createVariable(v2) {
222950
+ if (!v2.value) throw new ApiError(400, "BAD_REQUEST", "\u65B0\u5EFA\u53D8\u91CF\u5FC5\u987B\u63D0\u4F9B\u53D8\u91CF\u503C");
222951
+ const inserted = await this.opts.store.insertVariable(this.toStoredVariableRow(v2, v2.value));
222952
+ return inserted ? "created" : "exists";
222953
+ }
222954
+ /** `putVariable` / `createVariable` 共用:校验 + 加密 + 摊成存储行。两条写路径的落行口径必须逐字一致。 */
222955
+ toStoredVariableRow(v2, value2) {
222818
222956
  if (v2.deliveryMode === void 0 && v2.connectorId === void 0) {
222819
222957
  throw new ApiError(
222820
222958
  400,
@@ -222822,7 +222960,7 @@ ${input.description}
222822
222960
  "\u5199\u53D8\u91CF\u5FC5\u987B\u663E\u5F0F\u63D0\u4F9B deliveryMode\uFF08env | ref | broker\uFF09\u2014\u2014\u6295\u9012\u6863\u51B3\u5B9A\u660E\u6587\u662F\u5426\u8FDB agent env\uFF0C\u4E0D\u8BBE\u7F3A\u7701"
222823
222961
  );
222824
222962
  }
222825
- await this.opts.store.putVariable({
222963
+ return {
222826
222964
  key: v2.key,
222827
222965
  // 展示名(子 PRD《密钥管理》§2)。没传就不传下去——存储层按「不动」处理,不抹既有名字。
222828
222966
  ...v2.name !== void 0 && v2.name !== "" ? { name: v2.name } : {},
@@ -222833,7 +222971,7 @@ ${input.description}
222833
222971
  ...v2.overrides !== void 0 ? { overrides: v2.overrides } : {},
222834
222972
  // 非连接器写入到此 deliveryMode 必非空(上面已拦);连接器写入沿用 env(其值经 sensitiveVars 剔除,不进 env)。
222835
222973
  deliveryMode: v2.deliveryMode ?? "env",
222836
- valueEncrypted: v2.encrypted === false ? `plain:${v2.value}` : this.encrypt(v2.value),
222974
+ valueEncrypted: v2.encrypted === false ? `plain:${value2}` : this.encrypt(value2),
222837
222975
  updatedAt: this.now(),
222838
222976
  /* 创建者 / 创建时间 / 更新者:**由服务端落,不接受调用方自报**。
222839
222977
  createdBy/createdAt 两格存储层按「第一次落行才写」处理(COALESCE 旧值),所以这里每次都传,
@@ -222846,7 +222984,7 @@ ${input.description}
222846
222984
  // 决策 0556:发给谁只对全局行有意义;个人档 / 项目档一律不带(存储层 COALESCE:不带 = 不动)。
222847
222985
  ...v2.scope === "global" && v2.audience !== void 0 ? { audience: v2.audience } : {},
222848
222986
  ...v2.scope === "global" && v2.enabledActorIds !== void 0 ? { enabledActorIds: dedupe(v2.enabledActorIds) } : {}
222849
- });
222987
+ };
222850
222988
  }
222851
222989
  /**
222852
222990
  * 只改一行变量的元数据、不碰值(决策 0556)。
@@ -222906,15 +223044,18 @@ ${input.description}
222906
223044
  /** 前端/接口可见的列表。
222907
223045
  * - actorId 指定时:返回全局 + 该员工个人变量,按 key 去重(个人覆盖全局);
222908
223046
  * 个人变量如覆盖同名全局则 overrides 字段填 key 名,告知前端。
223047
+ * `opts.includeShadowed`(决策 0565):**不去重**——被同名个人变量覆盖的那条组织变量也带上,
223048
+ * Agent 详情要把「组织变量」与「个人变量」分开摆,覆盖关系由调用方按同名个人行判。
223049
+ * 发给谁(audience)的过滤仍由存储层那一条查询做,这里不另判。
222909
223050
  * - 不指定时:返回全部(管理员视图,不去重)。
222910
223051
  */
222911
- async listVariables(actorId) {
223052
+ async listVariables(actorId, opts) {
222912
223053
  const rows = await this.opts.store.listVariables(actorId);
222913
223054
  let source = rows;
222914
223055
  if (actorId) {
222915
223056
  const personalKeys = new Set(rows.filter((r) => r.actorId).map((r) => r.key));
222916
223057
  const globalKeys = new Set(rows.filter((r) => !r.actorId).map((r) => r.key));
222917
- source = rows.filter((r) => r.actorId !== void 0 || !personalKeys.has(r.key));
223058
+ if (!opts?.includeShadowed) source = rows.filter((r) => r.actorId !== void 0 || !personalKeys.has(r.key));
222918
223059
  source = source.map(
222919
223060
  (r) => r.actorId && globalKeys.has(r.key) && !r.overrides ? { ...r, overrides: r.key } : r
222920
223061
  );
@@ -222956,11 +223097,19 @@ ${input.description}
222956
223097
  * 或行是 2026-09-10 之前落的(那时还没有 created_by 这一格)。
222957
223098
  */
222958
223099
  async variableCreatedBy(key, selector = {}) {
223100
+ return (await this.variableRowMeta(key, selector))?.createdBy;
223101
+ }
223102
+ /** 按存储唯一键 (key, actorId?, projectId?) 找那一行的元数据(**不解密**)。没有这一行返回 undefined。 */
223103
+ async variableRowMeta(key, selector = {}) {
222959
223104
  const rows = await this.opts.store.listVariables();
222960
223105
  const hit = rows.find(
222961
223106
  (r) => r.key === key && (r.actorId ?? "") === (selector.actorId ?? "") && (r.projectId ?? "") === (selector.projectId ?? "")
222962
223107
  );
222963
- return hit?.createdBy;
223108
+ if (!hit) return void 0;
223109
+ return {
223110
+ ...hit.createdBy !== void 0 ? { createdBy: hit.createdBy } : {},
223111
+ ...hit.connectorId !== void 0 ? { connectorId: hit.connectorId } : {}
223112
+ };
222964
223113
  }
222965
223114
  async revealVariable(key, actorId) {
222966
223115
  const ct = await this.opts.store.getVariableCiphertext(key, actorId);
@@ -224316,7 +224465,8 @@ function actorsDomain(opts) {
224316
224465
  });
224317
224466
  router.get("/api/connectors", async (req) => {
224318
224467
  const { service } = await resolveCtx(req.auth.companyId);
224319
- return { status: 200, body: { items: await service.listConnectorsWithAssignments() } };
224468
+ const { items, connectedActorIdsByConnector } = await service.listConnectorsWithAssignments();
224469
+ return { status: 200, body: { items, connectedActorIdsByConnector } };
224320
224470
  });
224321
224471
  router.post("/api/connectors", async (req) => {
224322
224472
  const { service } = await resolveCtx(req.auth.companyId);
@@ -224367,9 +224517,39 @@ function actorsDomain(opts) {
224367
224517
  throw new ApiError(
224368
224518
  403,
224369
224519
  "SECRET_NOT_OWNED",
224370
- "\u8FD9\u4EFD\u5BC6\u94A5\u7531\u522B\u4EBA\u521B\u5EFA\uFF0C\u53EA\u6709\u521B\u5EFA\u8005\u672C\u4EBA\u6216\u7EC4\u7EC7\u7BA1\u7406\u8005\u53EF\u4EE5\u4FEE\u6539 / \u5220\u9664"
224520
+ "\u8FD9\u6761\u53D8\u91CF\u7531\u522B\u4EBA\u521B\u5EFA\uFF0C\u53EA\u6709\u521B\u5EFA\u8005\u672C\u4EBA\u6216\u7EC4\u7EC7\u7BA1\u7406\u8005\u53EF\u4EE5\u4FEE\u6539 / \u5220\u9664"
224521
+ );
224522
+ };
224523
+ const variableExistsError = async (service, key, selector) => {
224524
+ const meta = await service.variableRowMeta(key, selector).catch(() => void 0);
224525
+ if (meta?.connectorId) {
224526
+ return new ApiError(
224527
+ 409,
224528
+ "VARIABLE_ALREADY_EXISTS",
224529
+ `\u53D8\u91CF\u540D ${key} \u5DF2\u88AB\u8FDE\u63A5\u5668\u300C${meta.connectorId}\u300D\u7684\u51ED\u636E\u5360\u7528\uFF1B\u8FDE\u63A5\u5668\u51ED\u636E\u8BF7\u901A\u8FC7\u8FDE\u63A5\u5668\u6388\u6743\u6D41\u7A0B\u7EF4\u62A4`
224530
+ );
224531
+ }
224532
+ if (selector.actorId) {
224533
+ return new ApiError(
224534
+ 409,
224535
+ "VARIABLE_ALREADY_EXISTS",
224536
+ `\u8FD9\u4F4D Agent \u5DF2\u7ECF\u6709\u53EB ${key} \u7684\u4E2A\u4EBA\u53D8\u91CF\u4E86\u2014\u2014\u540C\u4E00 Agent \u540C\u4E00\u4E2A\u53D8\u91CF\u540D\u53EA\u80FD\u6709\u4E00\u6761\u4E2A\u4EBA\u53D8\u91CF\uFF0C\u8981\u6539\u8BF7\u7F16\u8F91\u90A3\u4E00\u6761`
224537
+ );
224538
+ }
224539
+ if (selector.projectId) {
224540
+ return new ApiError(409, "VARIABLE_ALREADY_EXISTS", `\u9879\u76EE ${selector.projectId} \u5DF2\u7ECF\u6709\u53EB ${key} \u7684\u9879\u76EE\u53D8\u91CF\u4E86`);
224541
+ }
224542
+ return new ApiError(
224543
+ 409,
224544
+ "VARIABLE_ALREADY_EXISTS",
224545
+ `\u7EC4\u7EC7\u91CC\u5DF2\u7ECF\u6709\u53EB ${key} \u7684\u7EC4\u7EC7\u53D8\u91CF\u4E86\u2014\u2014\u540C\u4E00\u4E2A\u53D8\u91CF\u540D\u5728\u7EC4\u7EC7\u91CC\u53EA\u80FD\u6709\u4E00\u6761\u7EC4\u7EC7\u53D8\u91CF\uFF0C\u8981\u6539\u8BF7\u7F16\u8F91\u90A3\u4E00\u6761`
224371
224546
  );
224372
224547
  };
224548
+ const parseCreateFlag = (raw) => {
224549
+ if (raw === void 0 || raw === false) return false;
224550
+ if (raw === true) return true;
224551
+ throw new ApiError(400, "BAD_REQUEST", "create \u53EA\u80FD\u662F\u5E03\u5C14\u503C true / false");
224552
+ };
224373
224553
  const auditVariableChange = async (req, kind, variableKey, targetScope) => {
224374
224554
  await opts.credentialAudit?.({
224375
224555
  kind,
@@ -224391,6 +224571,7 @@ function actorsDomain(opts) {
224391
224571
  const { service } = await resolveCtx(req.auth.companyId);
224392
224572
  const b2 = req.body;
224393
224573
  if (!b2?.key) throw new ApiError(400, "BAD_REQUEST", "\u7F3A\u5C11 key");
224574
+ const create = parseCreateFlag(b2.create);
224394
224575
  const expiresAt2 = normalizeExpiresAt(b2.expiresAt);
224395
224576
  const audienceMeta = parseAudienceMeta(b2);
224396
224577
  if ((audienceMeta.audience !== void 0 || audienceMeta.enabledActorIds !== void 0) && b2.scope !== "global") {
@@ -224407,7 +224588,7 @@ function actorsDomain(opts) {
224407
224588
  if (scope === "project" && !b2.projectId) {
224408
224589
  throw new ApiError(400, "BAD_REQUEST", "scope=project \u5FC5\u987B\u63D0\u4F9B projectId");
224409
224590
  }
224410
- if (scope === "project" && opts.projectExists && !await opts.projectExists(b2.projectId)) {
224591
+ if (scope === "project" && opts.projectExists && !await opts.projectExists(b2.projectId, req.auth.companyId)) {
224411
224592
  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`);
224412
224593
  }
224413
224594
  if (b2.deliveryMode !== void 0 && b2.deliveryMode !== "env" && b2.deliveryMode !== "ref" && b2.deliveryMode !== "broker") {
@@ -224427,11 +224608,9 @@ function actorsDomain(opts) {
224427
224608
  "personal \u53D8\u91CF\u8BF7\u8D70 POST /api/actors/:id/variables\uFF08\u672C\u7AEF\u70B9\u65E0 actorId\uFF0C\u5199\u5165\u4F1A\u843D\u4E0B\u65E0\u5F52\u5C5E\u7684\u7578\u5F62\u884C\uFF09"
224428
224609
  );
224429
224610
  }
224430
- if (b2.value === void 0 || b2.value === "") return { status: 200, body: { ok: true } };
224431
- await requireSecretOwner(req, service, b2.key, scope === "project" ? { projectId: b2.projectId } : {});
224432
- await service.putVariable({
224611
+ const selector = scope === "project" ? { projectId: b2.projectId } : {};
224612
+ const input = {
224433
224613
  key: b2.key,
224434
- value: b2.value,
224435
224614
  scope,
224436
224615
  // 展示名(子 PRD《密钥管理》§2「名称」);不传 = 不改既有名字。
224437
224616
  ...typeof b2.name === "string" && b2.name.trim() ? { name: b2.name.trim() } : {},
@@ -224444,7 +224623,16 @@ function actorsDomain(opts) {
224444
224623
  // 创建者/更新者取**当前登录人**,不接受 body 传入——自报的「谁建的」等于没有这一格。
224445
224624
  by: req.auth.actor,
224446
224625
  expiresAt: expiresAt2
224447
- });
224626
+ };
224627
+ if (create) {
224628
+ if (b2.value === void 0 || b2.value === "") throw new ApiError(400, "BAD_REQUEST", "\u65B0\u5EFA\u53D8\u91CF\u5FC5\u987B\u63D0\u4F9B\u53D8\u91CF\u503C");
224629
+ const outcome = await service.createVariable({ ...input, value: b2.value });
224630
+ if (outcome === "exists") throw await variableExistsError(service, b2.key, selector);
224631
+ } else {
224632
+ if (b2.value === void 0 || b2.value === "") return { status: 200, body: { ok: true } };
224633
+ await requireSecretOwner(req, service, b2.key, selector);
224634
+ await service.putVariable({ ...input, value: b2.value });
224635
+ }
224448
224636
  await auditVariableChange(
224449
224637
  req,
224450
224638
  "credential_write",
@@ -224486,7 +224674,8 @@ function actorsDomain(opts) {
224486
224674
  router.get("/api/actors/:id/variables", async (req) => {
224487
224675
  requireVariableManager(req);
224488
224676
  const { service } = await resolveCtx(req.auth.companyId);
224489
- return { status: 200, body: { items: await service.listVariables(req.params.id) } };
224677
+ const includeShadowed = ["1", "true"].includes(req.query.get("includeShadowed") ?? "");
224678
+ return { status: 200, body: { items: await service.listVariables(req.params.id, includeShadowed ? { includeShadowed } : void 0) } };
224490
224679
  });
224491
224680
  router.post("/api/actors/:id/variables", async (req) => {
224492
224681
  requireVariableManager(req);
@@ -224494,6 +224683,12 @@ function actorsDomain(opts) {
224494
224683
  const actorId = req.params.id;
224495
224684
  const b2 = req.body;
224496
224685
  if (!b2?.key) throw new ApiError(400, "BAD_REQUEST", "\u7F3A\u5C11 key");
224686
+ const create = parseCreateFlag(b2.create);
224687
+ if (create) {
224688
+ const owner = await service.getActor(actorId);
224689
+ if (!owner) throw new ApiError(404, "NOT_FOUND", `\u6CA1\u6709\u8FD9\u4E2A Agent\uFF1A${actorId}`);
224690
+ if (owner.kind !== "agent") throw new ApiError(400, "BAD_REQUEST", "\u4E2A\u4EBA\u53D8\u91CF\u53EA\u80FD\u5EFA\u5728 Agent \u4E0A");
224691
+ }
224497
224692
  const expiresAt2 = normalizeExpiresAt(b2.expiresAt);
224498
224693
  if (b2.deliveryMode !== void 0 && b2.deliveryMode !== "env" && b2.deliveryMode !== "ref" && b2.deliveryMode !== "broker") {
224499
224694
  throw new ApiError(400, "BAD_REQUEST", "deliveryMode \u53EA\u80FD\u662F env\u3001ref \u6216 broker");
@@ -224505,6 +224700,7 @@ function actorsDomain(opts) {
224505
224700
  "\u5FC5\u987B\u663E\u5F0F\u63D0\u4F9B deliveryMode\uFF08env | ref | broker\uFF09\u2014\u2014\u6295\u9012\u6863\u51B3\u5B9A\u660E\u6587\u662F\u5426\u8FDB agent env\uFF0C\u4E0D\u8BBE\u7F3A\u7701"
224506
224701
  );
224507
224702
  }
224703
+ if (create && (b2.value === void 0 || b2.value === "")) throw new ApiError(400, "BAD_REQUEST", "\u65B0\u5EFA\u53D8\u91CF\u5FC5\u987B\u63D0\u4F9B\u53D8\u91CF\u503C");
224508
224704
  if (b2.value === void 0 || b2.value === "") return { status: 200, body: { ok: true } };
224509
224705
  if (b2.connectorId === void 0) {
224510
224706
  const existing = await service.listVariables(actorId);
@@ -224517,13 +224713,12 @@ function actorsDomain(opts) {
224517
224713
  );
224518
224714
  }
224519
224715
  }
224520
- await requireSecretOwner(req, service, b2.key, { actorId });
224521
- await service.putVariable({
224716
+ const input = {
224522
224717
  key: b2.key,
224523
224718
  value: b2.value,
224524
224719
  actorId,
224525
224720
  scope: "personal",
224526
- // 展示名(同上)。组织页「添加密钥 → 新建」走的就是这条端点。
224721
+ // 展示名(同上)。组织页「新建变量」走的就是这条端点。
224527
224722
  ...typeof b2.name === "string" && b2.name.trim() ? { name: b2.name.trim() } : {},
224528
224723
  ...b2.deliveryMode !== void 0 ? { deliveryMode: b2.deliveryMode } : {},
224529
224724
  ...b2.connectorId !== void 0 ? { connectorId: b2.connectorId } : {},
@@ -224531,7 +224726,14 @@ function actorsDomain(opts) {
224531
224726
  ...b2.encrypted !== void 0 ? { encrypted: b2.encrypted } : {},
224532
224727
  by: req.auth.actor,
224533
224728
  expiresAt: expiresAt2
224534
- });
224729
+ };
224730
+ if (create) {
224731
+ const outcome = await service.createVariable(input);
224732
+ if (outcome === "exists") throw await variableExistsError(service, b2.key, { actorId });
224733
+ } else {
224734
+ await requireSecretOwner(req, service, b2.key, { actorId });
224735
+ await service.putVariable(input);
224736
+ }
224535
224737
  await auditVariableChange(req, "credential_write", b2.key, `personal:${actorId}`);
224536
224738
  return { status: 201, body: { ok: true } };
224537
224739
  });
@@ -244949,6 +245151,26 @@ var init_postgres = __esm({
244949
245151
  });
244950
245152
 
244951
245153
  // ../storage/src/postgres-registry.ts
245154
+ function variableInsertParams(v2) {
245155
+ return [
245156
+ v2.key,
245157
+ v2.scope,
245158
+ v2.actorId ?? null,
245159
+ v2.projectId ?? null,
245160
+ v2.connectorId ?? null,
245161
+ v2.overrides ?? null,
245162
+ v2.deliveryMode ?? null,
245163
+ v2.valueEncrypted,
245164
+ v2.updatedAt,
245165
+ v2.expiresAt ?? null,
245166
+ v2.name ?? null,
245167
+ v2.createdBy ?? null,
245168
+ v2.createdAt ?? null,
245169
+ v2.updatedBy ?? null,
245170
+ v2.audience ?? null,
245171
+ v2.enabledActorIds ?? null
245172
+ ];
245173
+ }
244952
245174
  function rowToVariable(row) {
244953
245175
  return {
244954
245176
  key: row.key,
@@ -244976,7 +245198,7 @@ function rowToVariable(row) {
244976
245198
  ...Array.isArray(row.enabled_actor_ids) ? { enabledActorIds: row.enabled_actor_ids } : {}
244977
245199
  };
244978
245200
  }
244979
- var ident5, PostgresRegistryStore, rowToActor, rowToConfig, rowToAgentPrefs;
245201
+ var ident5, PostgresRegistryStore, VARIABLE_INSERT_COLUMNS, rowToActor, rowToConfig, rowToAgentPrefs;
244980
245202
  var init_postgres_registry = __esm({
244981
245203
  "../storage/src/postgres-registry.ts"() {
244982
245204
  "use strict";
@@ -245657,7 +245879,7 @@ var init_postgres_registry = __esm({
245657
245879
  updated_by 反过来:每次都覆盖。三者与 name 的 COALESCE 方向相反,别照抄。 */
245658
245880
  /* audience / enabled_actor_ids(决策 0556)走 COALESCE(**新值**, 旧值):不传 = 不动——
245659
245881
  改默认值时手上不带「发给谁」,普通赋值会把「仅指定 Agent」悄悄打回全员生效。 */
245660
- `INSERT INTO ${this.s}.variables (key, scope, actor_id, project_id, connector_id, overrides, delivery_mode, value_encrypted, updated_at, expires_at, name, created_by, created_at, updated_by, audience, enabled_actor_ids)
245882
+ `INSERT INTO ${this.s}.variables (${VARIABLE_INSERT_COLUMNS})
245661
245883
  VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16)
245662
245884
  ON CONFLICT (key, COALESCE(actor_id, ''), COALESCE(project_id, '')) DO UPDATE
245663
245885
  SET scope=$2, actor_id=$3, project_id=$4, connector_id=$5, overrides=$6, delivery_mode=$7, value_encrypted=$8, updated_at=$9, expires_at=$10,
@@ -245667,25 +245889,17 @@ var init_postgres_registry = __esm({
245667
245889
  updated_by=$14,
245668
245890
  audience=COALESCE($15, ${this.s}.variables.audience),
245669
245891
  enabled_actor_ids=COALESCE($16, ${this.s}.variables.enabled_actor_ids)`,
245670
- [
245671
- v2.key,
245672
- v2.scope,
245673
- v2.actorId ?? null,
245674
- v2.projectId ?? null,
245675
- v2.connectorId ?? null,
245676
- v2.overrides ?? null,
245677
- v2.deliveryMode ?? null,
245678
- v2.valueEncrypted,
245679
- v2.updatedAt,
245680
- v2.expiresAt ?? null,
245681
- v2.name ?? null,
245682
- v2.createdBy ?? null,
245683
- v2.createdAt ?? null,
245684
- v2.updatedBy ?? null,
245685
- v2.audience ?? null,
245686
- v2.enabledActorIds ?? null
245687
- ]
245892
+ variableInsertParams(v2)
245893
+ );
245894
+ }
245895
+ async insertVariable(v2) {
245896
+ const r = await this.pool.query(
245897
+ `INSERT INTO ${this.s}.variables (${VARIABLE_INSERT_COLUMNS})
245898
+ VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16)
245899
+ ON CONFLICT (key, COALESCE(actor_id, ''), COALESCE(project_id, '')) DO NOTHING`,
245900
+ variableInsertParams(v2)
245688
245901
  );
245902
+ return (r.rowCount ?? 0) > 0;
245689
245903
  }
245690
245904
  async touchVariableUsage(key, actorId, projectId2, when) {
245691
245905
  await this.pool.query(
@@ -245773,6 +245987,7 @@ var init_postgres_registry = __esm({
245773
245987
  );
245774
245988
  }
245775
245989
  };
245990
+ VARIABLE_INSERT_COLUMNS = "key, scope, actor_id, project_id, connector_id, overrides, delivery_mode, value_encrypted, updated_at, expires_at, name, created_by, created_at, updated_by, audience, enabled_actor_ids";
245776
245991
  rowToActor = (row) => ({
245777
245992
  id: row.id,
245778
245993
  kind: row.kind,
@@ -257049,6 +257264,9 @@ var init_cutover_aware_registry = __esm({
257049
257264
  async putVariable(...args) {
257050
257265
  return (await this.orgStoreForWrite()).putVariable(...args);
257051
257266
  }
257267
+ async insertVariable(...args) {
257268
+ return (await this.orgStoreForWrite()).insertVariable(...args);
257269
+ }
257052
257270
  async patchVariableValue(...args) {
257053
257271
  return (await this.orgStoreForWrite()).patchVariableValue(...args);
257054
257272
  }
@@ -263398,8 +263616,10 @@ async function startServe(opts) {
263398
263616
  credentialAudit,
263399
263617
  readCredentialAudit,
263400
263618
  // ADR 0125:写 scope=project 变量时校验 projectId 真实存在(getProject 直查,含临时项目)。
263401
- // projectStateStore 在下方 ~行 1023 才赋值——本箭头只在请求时跑,那时已就绪(同 resolveDispatchScope 惰性模式)。
263402
- projectExists: async (id) => await projectStateStore.getProject(id) !== null,
263619
+ // 按请求所属公司查(同 PR #870 的剧本/自动化):此前固定查 projectStateStore(默认公司那一份),
263620
+ // 非默认公司的项目一律报「项目不存在」。未解析出公司 = 默认公司,与 experiments 同一口径。
263621
+ // projectExistsIn 在下方才声明——本箭头只在请求时跑,那时已就绪(同 resolveDispatchScope 惰性模式)。
263622
+ projectExists: async (id, companyId) => projectExistsIn(id, companyId ?? defaultCompanyId),
263403
263623
  /* 子 PRD《密钥管理》§1 的权限判据:密钥的写 / 删要认「创建者本人或组织管理者」。
263404
263624
  owner 与 admin 都算组织管理者(与组织页 `orgIdentityOf` 同一口径)。
263405
263625
  `controlPlaneStore` 在下方才赋值——本箭头只在请求时跑,那时已就绪(同 projectExists 的惰性模式)。
@@ -264308,6 +264528,10 @@ async function startServe(opts) {
264308
264528
  const raw = headers["x-oasis-company"];
264309
264529
  const selector = typeof raw === "string" && raw ? raw : void 0;
264310
264530
  const me = await companies.currentUser({ actor }, headers);
264531
+ if (!selector && authz.startsWith("Bearer ")) {
264532
+ const active = (await companies.service.listForUser(me.accountId)).filter((c) => c.status === "active");
264533
+ if (active.length > 1) return { kind: "ambiguous", companies: active.map((c) => c.id) };
264534
+ }
264311
264535
  const ctx = await companies.service.resolveCurrentCompany(me.accountId, selector);
264312
264536
  if (!ctx) return { kind: "not-found", ...selector !== void 0 ? { selector } : {} };
264313
264537
  return ctx.isMember ? { kind: "ok", companyId: ctx.company.id } : { kind: "forbidden" };
@@ -270950,7 +271174,7 @@ function declaredBooleanFlags() {
270950
271174
  }
270951
271175
  return out;
270952
271176
  }
270953
- var GLOBAL_FLAGS = Object.freeze(/* @__PURE__ */ new Set(["server", "token", "dir", "workspace"]));
271177
+ var GLOBAL_FLAGS = Object.freeze(/* @__PURE__ */ new Set(["server", "token", "dir", "workspace", "company"]));
270954
271178
  var BEHAVIORAL_FLAGS = Object.freeze(/* @__PURE__ */ new Set(["set-json", "clear"]));
270955
271179
  var COMMAND_DECLS = {
270956
271180
  // —— 基础设施 ——
@@ -272724,8 +272948,9 @@ async function runConnectorEffectCli(argv) {
272724
272948
  var USAGE_HEADER = `oasis \u2014\u2014 artifact-centric \u534F\u4F5C\u5185\u6838 CLI\uFF08\u670D\u52A1\u7AEF\u7626\u5BA2\u6237\u7AEF\uFF09
272725
272949
 
272726
272950
  \u547D\u4EE4\u603B\u89C8\uFF08\u6309\u5206\u7EC4\u5217\u51FA\uFF1B\u5355\u6761\u8BE6\u60C5\u4E0E\u53C2\u6570\u7528 \`oasis help <\u547D\u4EE4>\` \u6216 \`oasis <\u547D\u4EE4> --help\`\uFF09\uFF1A`;
272727
- var USAGE_FOOTER = `\u901A\u7528\u53C2\u6570\uFF08\u6240\u6709\u547D\u4EE4\u901A\u7528\uFF09: [--server http://127.0.0.1:7320] [--token <t>] [--dir .oasis]
272728
- token \u89E3\u6790\u987A\u5E8F: --token > $OASIS_TOKEN > <dir>/operator-token\uFF08serve \u542F\u52A8\u65F6\u5199\u5230 <dir>/operator-token\uFF0C\u672C\u673A CLI \u81EA\u52A8\u8BFB\u53D6\uFF09`;
272951
+ var USAGE_FOOTER = `\u901A\u7528\u53C2\u6570\uFF08\u6240\u6709\u547D\u4EE4\u901A\u7528\uFF09: [--server http://127.0.0.1:7320] [--token <t>] [--dir .oasis] [--company <\u516C\u53F8 id \u6216 slug>]
272952
+ token \u89E3\u6790\u987A\u5E8F: --token > $OASIS_TOKEN > <dir>/operator-token\uFF08serve \u542F\u52A8\u65F6\u5199\u5230 <dir>/operator-token\uFF0C\u672C\u673A CLI \u81EA\u52A8\u8BFB\u53D6\uFF09
272953
+ \u516C\u53F8\u89E3\u6790\u987A\u5E8F: --company > $OASIS_COMPANY > \u670D\u52A1\u7AEF\u63A8\u65AD\uFF08token \u81EA\u5E26\u516C\u53F8\u7684\u6309 token\uFF1B\u8FD0\u7EF4/\u4EBA\u7C7B token \u6309\u8D26\u53F7\u6240\u5C5E\u516C\u53F8\uFF09`;
272729
272954
  var USAGE = `${USAGE_HEADER}
272730
272955
 
272731
272956
  ${buildCommandIndex()}
@@ -272966,8 +273191,13 @@ async function waitForCommandAuthorization(base, headers, draftId, fallback) {
272966
273191
  }
272967
273192
  }
272968
273193
  }
272969
- function makeClient(base, token) {
272970
- const baseHeaders = { authorization: `Bearer ${token}`, "content-type": "application/json" };
273194
+ function apiHeaders(token, company) {
273195
+ const headers = { authorization: `Bearer ${token}`, "content-type": "application/json" };
273196
+ if (company) headers["x-oasis-company"] = company;
273197
+ return headers;
273198
+ }
273199
+ function makeClient(base, token, opts = {}) {
273200
+ const baseHeaders = apiHeaders(token, opts.company);
272971
273201
  const chatSessionId = process.env["OASIS_CHAT_SESSION_ID"];
272972
273202
  if (chatSessionId) baseHeaders["x-chat-session-id"] = chatSessionId;
272973
273203
  const headers = baseHeaders;
@@ -273428,7 +273658,8 @@ async function runCli(argv, println = console.log, progressln = console.error) {
273428
273658
  if (!token) {
273429
273659
  throw new Error(`\u65E0 token\uFF1A\u5148 oasis serve\uFF08\u4F1A\u751F\u6210 ${tokenFile}\uFF09\uFF0C\u6216\u663E\u5F0F --token / $OASIS_TOKEN`);
273430
273660
  }
273431
- const api = makeClient(base, token);
273661
+ const companySelector = (flags.get("company") ?? process.env["OASIS_COMPANY"])?.trim() || void 0;
273662
+ const api = makeClient(base, token, companySelector ? { company: companySelector } : {});
273432
273663
  if (command === "reconcile-terminal-runs") {
273433
273664
  const runIds = flags.get("run-ids")?.split(",").map((x2) => x2.trim()).filter(Boolean);
273434
273665
  const rows = await api.post("/api/admin/reconcile-terminal-runs", { execute: flags.get("execute") === "true", ...runIds ? { runIds } : {} });
@@ -273550,8 +273781,9 @@ async function runCli(argv, println = console.log, progressln = console.error) {
273550
273781
  switch (command) {
273551
273782
  // 委派一位专家(提案 §7)。四个子形态共用一条命令,靠给了哪个 flag 分流。
273552
273783
  //
273553
- // ⚠️ **没有 `--company`**:公司与父会话身份一律从签名 claims 取(不变量 3 / §6.1.2)。
273554
- // 加一个 `--company` 就是把这份保证亲手拆掉——那等于允许 agent 写到别人名下。
273784
+ // ⚠️ **没有自己的 `--company`**:公司与父会话身份一律从签名 claims 取(不变量 3 / §6.1.2)。
273785
+ // 往请求体里加一个公司参数就是把这份保证亲手拆掉——那等于允许 agent 写到别人名下。
273786
+ // 全局 `--company` 只发 `x-oasis-company` 头,服务端对带公司 claims 的 token 不看这个头(见 apiHeaders)。
273555
273787
  // ⚠️ **没有 `--wait-ms`**:一期只做异步。理由不是墙钟不够(chat 墙钟 120 分钟),是三条:
273556
273788
  // 阻塞期间主会话那一轮一直挂着(用户看着「正在输入」十几分钟、他这期间说的话只能排队)、
273557
273789
  // 一个进程挂着空等不产出任何东西、这十几分钟里 serve 重启或节点掉线那一轮就白丢了
@@ -275005,7 +275237,7 @@ ${res.warning}`);
275005
275237
  throw forbidden(e) ?? e;
275006
275238
  }
275007
275239
  const where = scope === "project" ? `\u9879\u76EE ${projectId2}` : scope === "personal" ? `\u5458\u5DE5 ${actorId}` : "\u5168\u7EC4\u7EC7";
275008
- println(`\u5DF2\u5199\u5165 ${key}\uFF08${where}\uFF0C\u6295\u9012=${delivery}\uFF09`);
275240
+ println(`\u5DF2\u5199\u5165 ${key}\uFF08${companySelector ? `\u516C\u53F8 ${companySelector} \xB7 ` : ""}${where}\uFF0C\u6295\u9012=${delivery}\uFF09`);
275009
275241
  break;
275010
275242
  }
275011
275243
  if (sub === "rm") {
@@ -275021,7 +275253,7 @@ ${res.warning}`);
275021
275253
  } catch (e) {
275022
275254
  throw forbidden(e) ?? e;
275023
275255
  }
275024
- println(`\u5DF2\u5220\u9664 ${key}\uFF08${scope === "project" ? `\u9879\u76EE ${projectId2}` : scope === "personal" ? `\u5458\u5DE5 ${actorId}` : "\u5168\u7EC4\u7EC7"}\uFF09`);
275256
+ println(`\u5DF2\u5220\u9664 ${key}\uFF08${companySelector ? `\u516C\u53F8 ${companySelector} \xB7 ` : ""}${scope === "project" ? `\u9879\u76EE ${projectId2}` : scope === "personal" ? `\u5458\u5DE5 ${actorId}` : "\u5168\u7EC4\u7EC7"}\uFF09`);
275025
275257
  break;
275026
275258
  }
275027
275259
  throw new Error(`\u672A\u77E5\u5B50\u547D\u4EE4\uFF1Aoasis var ${sub ?? ""}
@@ -275409,7 +275641,7 @@ ${res.warning}`);
275409
275641
  // A3:成图事务(与 intervene 同管线、分语义命名——铺图不是手术)
275410
275642
  case "intervene": {
275411
275643
  const plan = JSON.parse(readFileArg(need(flags, "file")));
275412
- const headers = { authorization: `Bearer ${token}`, "content-type": "application/json" };
275644
+ const headers = apiHeaders(token, companySelector);
275413
275645
  const previewRes = await fetch(`${base}/api/intervention/preview`, {
275414
275646
  method: "POST",
275415
275647
  headers,
@@ -276452,7 +276684,7 @@ function shimScript() {
276452
276684
  }
276453
276685
 
276454
276686
  // src/index.ts
276455
- var PKG_VERSION = true ? "2.2.36" : "dev";
276687
+ var PKG_VERSION = true ? "2.2.38" : "dev";
276456
276688
  var LOCAL_BIN = localBin();
276457
276689
  var NPM_PREFIX = npmPrefix();
276458
276690
  var INSTANCE = DEFAULT_INSTANCE;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oasis_test_v2",
3
- "version": "2.2.36",
3
+ "version": "2.2.38",
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": "696858b581661883b2eb278fb40c793f3ecf70da"
29
+ "sourceHead": "55454aeb4d2b4d5b0b530b8777cd1b62d86803c5"
30
30
  }
31
31
  }