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.
- package/dist/index.js +362 -130
- 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,
|
|
11735
|
-
VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13
|
|
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,
|
|
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,
|
|
11766
|
-
VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17
|
|
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
|
|
11800
|
-
VALUES ($1,$2,$3,$4,$5,$6
|
|
11801
|
-
[e.workorderId, e.fromNodeId, e.toNodeId, e.kind, e.required, e.pinnedWorkId
|
|
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,
|
|
12021
|
-
VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,'pending'
|
|
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
|
|
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:
|
|
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
|
|
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:
|
|
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
|
|
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
|
-
*
|
|
222591
|
-
*
|
|
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
|
|
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
|
|
222610
|
-
|
|
222611
|
-
|
|
222612
|
-
|
|
222613
|
-
|
|
222614
|
-
|
|
222615
|
-
|
|
222616
|
-
|
|
222617
|
-
|
|
222618
|
-
|
|
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((
|
|
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
|
-
|
|
222720
|
-
for (const v2 of vars)
|
|
222721
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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:${
|
|
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
|
|
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
|
-
|
|
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\
|
|
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
|
-
|
|
224431
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 (
|
|
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
|
-
|
|
245672
|
-
|
|
245673
|
-
|
|
245674
|
-
|
|
245675
|
-
|
|
245676
|
-
|
|
245677
|
-
|
|
245678
|
-
|
|
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
|
-
//
|
|
263402
|
-
|
|
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
|
|
272970
|
-
const
|
|
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
|
|
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
|
-
// ⚠️
|
|
273554
|
-
//
|
|
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 =
|
|
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.
|
|
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.
|
|
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": "
|
|
29
|
+
"sourceHead": "55454aeb4d2b4d5b0b530b8777cd1b62d86803c5"
|
|
30
30
|
}
|
|
31
31
|
}
|