@opengeni/core 0.28.1 → 2.1.0-canary.0
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/access/index.d.ts +37 -0
- package/dist/application/new-session-drafts.d.ts +8 -1
- package/dist/application/session-commands.d.ts +18 -11
- package/dist/application/session-tenancy.d.ts +18 -0
- package/dist/application/user-resource-grants.d.ts +20 -0
- package/dist/{chunk-6WKPWE5S.js → chunk-H7X52RI2.js} +27 -18
- package/dist/chunk-H7X52RI2.js.map +1 -0
- package/dist/{chunk-JJU6I5XD.js → chunk-VCZFFEPB.js} +21 -9
- package/dist/chunk-VCZFFEPB.js.map +1 -0
- package/dist/{chunk-NYPMQ7NO.js → chunk-ZYCNLYGH.js} +29 -27
- package/dist/chunk-ZYCNLYGH.js.map +1 -0
- package/dist/dependencies.d.ts +12 -1
- package/dist/domain/automations.d.ts +51 -0
- package/dist/domain/company-brain-governed-writes.d.ts +70 -0
- package/dist/domain/company-profile-agent-admin.d.ts +24 -0
- package/dist/domain/connection-authority.d.ts +97 -0
- package/dist/domain/connection-authority.js +169 -0
- package/dist/domain/connection-authority.js.map +1 -0
- package/dist/domain/editable-artifacts/service.d.ts +1 -2
- package/dist/domain/environments.d.ts +1 -1
- package/dist/domain/github-repository-bindings.d.ts +117 -0
- package/dist/domain/governed-learning-activation.d.ts +16 -0
- package/dist/domain/governed-learning-evaluator.d.ts +6 -0
- package/dist/domain/governed-learning-slack-publication.d.ts +45 -0
- package/dist/domain/memory-slack-delivery.d.ts +1 -4
- package/dist/domain/organization-membership-lifecycle.d.ts +1 -0
- package/dist/domain/packs.d.ts +5 -1
- package/dist/domain/personal-connection-delegations.d.ts +44 -2
- package/dist/domain/pr-review.d.ts +149 -0
- package/dist/domain/remember.d.ts +69 -0
- package/dist/domain/resources.d.ts +23 -2
- package/dist/domain/scheduled-tasks.d.ts +43 -2
- package/dist/domain/session-tool-policy.d.ts +1 -1
- package/dist/domain/sessions.d.ts +43 -9
- package/dist/editable-artifact-live/application.d.ts +10 -2
- package/dist/editable-artifact-live/ports.d.ts +0 -1
- package/dist/editable-artifact-live/server.d.ts +0 -1
- package/dist/editable-artifact-live/ticket.d.ts +0 -1
- package/dist/editable-artifact-live/types.d.ts +5 -2
- package/dist/editable-artifact-live.js +2 -2
- package/dist/editable-artifacts.js +2 -4
- package/dist/index.d.ts +13 -1
- package/dist/index.js +4973 -1243
- package/dist/index.js.map +1 -1
- package/dist/rigs/index.d.ts +22 -5
- package/dist/sandbox/fleet.d.ts +34 -6
- package/dist/sandbox/routing.d.ts +15 -1
- package/dist/sandbox/runtime-settings.d.ts +39 -0
- package/package.json +16 -11
- package/src/access/index.ts +151 -31
- package/src/application/new-session-drafts.ts +32 -5
- package/src/application/session-commands.ts +305 -71
- package/src/application/session-tenancy.ts +321 -0
- package/src/application/user-resource-grants.ts +140 -0
- package/src/billing/limits.ts +25 -15
- package/src/dependencies.ts +12 -1
- package/src/domain/automations.ts +196 -0
- package/src/domain/capabilities.ts +7 -1
- package/src/domain/company-brain-governed-writes.ts +330 -0
- package/src/domain/company-profile-agent-admin.ts +55 -0
- package/src/domain/connection-authority.ts +329 -0
- package/src/domain/editable-artifacts/agent-application.ts +17 -9
- package/src/domain/editable-artifacts/service.ts +12 -1
- package/src/domain/editable-artifacts/snapshot-compaction.ts +9 -5
- package/src/domain/editable-artifacts/snapshot-verifier-genesis.ts +10 -8
- package/src/domain/editable-artifacts/snapshot-verifier.ts +10 -3
- package/src/domain/environments.ts +16 -3
- package/src/domain/github-repository-bindings.ts +295 -0
- package/src/domain/governed-learning-activation.ts +44 -0
- package/src/domain/governed-learning-evaluator.ts +49 -0
- package/src/domain/governed-learning-slack-publication.ts +157 -0
- package/src/domain/insights.ts +9 -0
- package/src/domain/memory-slack-delivery.ts +0 -30
- package/src/domain/organization-membership-lifecycle.ts +9 -0
- package/src/domain/packs.ts +106 -3
- package/src/domain/personal-connection-delegations.ts +597 -42
- package/src/domain/pr-review.ts +671 -0
- package/src/domain/remember.ts +549 -0
- package/src/domain/resources.ts +144 -6
- package/src/domain/scheduled-tasks.ts +504 -77
- package/src/domain/session-tool-policy.ts +12 -9
- package/src/domain/sessions.ts +629 -176
- package/src/editable-artifact-live/application.ts +21 -3
- package/src/editable-artifact-live/ports.ts +0 -1
- package/src/editable-artifact-live/server.ts +13 -7
- package/src/editable-artifact-live/ticket.ts +1 -5
- package/src/editable-artifact-live/types.ts +6 -2
- package/src/editable-artifact-live/wire.ts +0 -10
- package/src/index.ts +13 -1
- package/src/rigs/index.ts +165 -48
- package/src/sandbox/fleet.ts +376 -30
- package/src/sandbox/routing.ts +91 -29
- package/src/sandbox/runtime-settings.ts +274 -0
- package/src/session-authorization.ts +17 -49
- package/dist/chunk-6WKPWE5S.js.map +0 -1
- package/dist/chunk-JJU6I5XD.js.map +0 -1
- package/dist/chunk-NYPMQ7NO.js.map +0 -1
- package/dist/domain/durable-learning-slack-publication.d.ts +0 -72
- package/src/domain/durable-learning-slack-publication.ts +0 -202
|
@@ -23,9 +23,13 @@ import type { EditableArtifactLiveTicket } from "./types";
|
|
|
23
23
|
|
|
24
24
|
export type EditableArtifactLiveCompatibilityRequest = Readonly<{
|
|
25
25
|
artifact: EditableArtifact;
|
|
26
|
-
|
|
26
|
+
modality: EditableArtifactModality;
|
|
27
|
+
liveProtocolVersion: number;
|
|
27
28
|
kernelVersion: string;
|
|
28
29
|
modelSchemaVersion: number;
|
|
30
|
+
snapshotVersion: number;
|
|
31
|
+
commandProtocolVersion: number;
|
|
32
|
+
committedTransactionProtocolVersion: number;
|
|
29
33
|
}>;
|
|
30
34
|
|
|
31
35
|
/** A client/runtime protocol tuple cannot be served by the loaded kernel. */
|
|
@@ -71,9 +75,13 @@ export type ReadEditableArtifactApplicationInput = Readonly<{
|
|
|
71
75
|
|
|
72
76
|
export type MintEditableArtifactApplicationTicketInput = ReadEditableArtifactApplicationInput &
|
|
73
77
|
Readonly<{
|
|
74
|
-
|
|
78
|
+
modality: EditableArtifactModality;
|
|
79
|
+
liveProtocolVersion: number;
|
|
75
80
|
kernelVersion: string;
|
|
76
81
|
modelSchemaVersion: number;
|
|
82
|
+
snapshotVersion: number;
|
|
83
|
+
commandProtocolVersion: number;
|
|
84
|
+
committedTransactionProtocolVersion: number;
|
|
77
85
|
allowEdit: boolean;
|
|
78
86
|
}>;
|
|
79
87
|
|
|
@@ -148,9 +156,19 @@ export class EditableArtifactApplication implements EditableArtifactApplicationP
|
|
|
148
156
|
const artifact = await this.readArtifact(input);
|
|
149
157
|
await this.dependencies.compatibility.assertCompatible({
|
|
150
158
|
artifact,
|
|
151
|
-
|
|
159
|
+
modality: input.modality,
|
|
160
|
+
liveProtocolVersion: positiveInteger(input.liveProtocolVersion, "liveProtocolVersion"),
|
|
152
161
|
kernelVersion: boundedVersion(input.kernelVersion, "kernelVersion"),
|
|
153
162
|
modelSchemaVersion: positiveInteger(input.modelSchemaVersion, "modelSchemaVersion"),
|
|
163
|
+
snapshotVersion: positiveInteger(input.snapshotVersion, "snapshotVersion"),
|
|
164
|
+
commandProtocolVersion: positiveInteger(
|
|
165
|
+
input.commandProtocolVersion,
|
|
166
|
+
"commandProtocolVersion",
|
|
167
|
+
),
|
|
168
|
+
committedTransactionProtocolVersion: positiveInteger(
|
|
169
|
+
input.committedTransactionProtocolVersion,
|
|
170
|
+
"committedTransactionProtocolVersion",
|
|
171
|
+
),
|
|
154
172
|
});
|
|
155
173
|
return await this.dependencies.live.mintTicket({
|
|
156
174
|
scope: artifact.scope,
|
|
@@ -40,7 +40,6 @@ export type EditableArtifactLiveBootstrapRead = Readonly<{
|
|
|
40
40
|
scope: EditableArtifactScope;
|
|
41
41
|
artifactId: EditableArtifactId;
|
|
42
42
|
resume: EditableArtifactLiveResume;
|
|
43
|
-
protocolVersion: number;
|
|
44
43
|
}>;
|
|
45
44
|
|
|
46
45
|
export type EditableArtifactLiveTransactionPage = Readonly<{
|
|
@@ -81,7 +81,6 @@ const MAX_IDENTIFIER_BYTES = 512;
|
|
|
81
81
|
const MAX_CAUSAL_ACTORS = 1_024;
|
|
82
82
|
|
|
83
83
|
export type EditableArtifactLiveServerOptions = Readonly<{
|
|
84
|
-
protocolVersion?: number;
|
|
85
84
|
ticketTtlMs?: number;
|
|
86
85
|
maxClientFrameBytes?: number;
|
|
87
86
|
maxOutboundFrameBytes?: number;
|
|
@@ -101,7 +100,10 @@ export type EditableArtifactLiveServerOptions = Readonly<{
|
|
|
101
100
|
}>;
|
|
102
101
|
|
|
103
102
|
type NormalizedOptions = Required<Omit<EditableArtifactLiveServerOptions, "ticketTtlMs">> &
|
|
104
|
-
Readonly<{
|
|
103
|
+
Readonly<{
|
|
104
|
+
protocolVersion: typeof EDITABLE_ARTIFACT_LIVE_PROTOCOL_VERSION;
|
|
105
|
+
ticketTtlMs?: number;
|
|
106
|
+
}>;
|
|
105
107
|
|
|
106
108
|
export type OpenEditableArtifactLiveInput = Readonly<{
|
|
107
109
|
token: string;
|
|
@@ -147,7 +149,6 @@ export class EditableArtifactLiveServer {
|
|
|
147
149
|
tickets: dependencies.tickets,
|
|
148
150
|
tokens: dependencies.tokens,
|
|
149
151
|
clock: dependencies.clock,
|
|
150
|
-
protocolVersion: this.options.protocolVersion,
|
|
151
152
|
...(this.options.ticketTtlMs === undefined ? {} : { ttlMs: this.options.ticketTtlMs }),
|
|
152
153
|
});
|
|
153
154
|
}
|
|
@@ -312,7 +313,6 @@ class EditableArtifactLiveSessionImpl implements EditableArtifactLiveSession {
|
|
|
312
313
|
scope: this.ticket.scope,
|
|
313
314
|
artifactId: this.artifactId,
|
|
314
315
|
resume: this.resume,
|
|
315
|
-
protocolVersion: this.options.protocolVersion,
|
|
316
316
|
});
|
|
317
317
|
this.assertOpen();
|
|
318
318
|
validateBootstrap(bootstrap, this.artifactId, this.modality, this.resume, this.options);
|
|
@@ -706,7 +706,13 @@ class EditableArtifactLiveSessionImpl implements EditableArtifactLiveSession {
|
|
|
706
706
|
} as const;
|
|
707
707
|
await this.send(
|
|
708
708
|
snapshot.modality === "spreadsheet"
|
|
709
|
-
? {
|
|
709
|
+
? {
|
|
710
|
+
...common,
|
|
711
|
+
modality: snapshot.modality,
|
|
712
|
+
causalFrontier: causalFrontier!,
|
|
713
|
+
operationProtocolVersion: snapshot.operationProtocolVersion,
|
|
714
|
+
snapshotVersion: snapshot.snapshotVersion,
|
|
715
|
+
}
|
|
710
716
|
: {
|
|
711
717
|
...common,
|
|
712
718
|
modality: snapshot.modality,
|
|
@@ -1264,7 +1270,7 @@ function validateResume(
|
|
|
1264
1270
|
resume: EditableArtifactLiveResume,
|
|
1265
1271
|
durableModality: EditableArtifactModality,
|
|
1266
1272
|
): void {
|
|
1267
|
-
const resumeModality = resume.modality
|
|
1273
|
+
const resumeModality = resume.modality;
|
|
1268
1274
|
if (resumeModality !== durableModality) {
|
|
1269
1275
|
throw new EditableArtifactLiveError(
|
|
1270
1276
|
"invalid_frame",
|
|
@@ -1608,7 +1614,7 @@ function boundedString(value: string, label: string, maxBytes: number): void {
|
|
|
1608
1614
|
|
|
1609
1615
|
function normalizeOptions(options: EditableArtifactLiveServerOptions): NormalizedOptions {
|
|
1610
1616
|
const normalized = {
|
|
1611
|
-
protocolVersion:
|
|
1617
|
+
protocolVersion: EDITABLE_ARTIFACT_LIVE_PROTOCOL_VERSION,
|
|
1612
1618
|
maxClientFrameBytes: options.maxClientFrameBytes ?? DEFAULT_MAX_CLIENT_FRAME_BYTES,
|
|
1613
1619
|
maxOutboundFrameBytes: options.maxOutboundFrameBytes ?? DEFAULT_MAX_OUTBOUND_FRAME_BYTES,
|
|
1614
1620
|
maxSnapshotBytes: options.maxSnapshotBytes ?? DEFAULT_MAX_SNAPSHOT_BYTES,
|
|
@@ -31,7 +31,6 @@ export type EditableArtifactLiveTicketAuthorityDependencies = Readonly<{
|
|
|
31
31
|
tokens: EditableArtifactLiveTokenPort;
|
|
32
32
|
clock: EditableArtifactLiveClockPort;
|
|
33
33
|
ttlMs?: number;
|
|
34
|
-
protocolVersion?: number;
|
|
35
34
|
}>;
|
|
36
35
|
|
|
37
36
|
export class EditableArtifactLiveTicketAuthority {
|
|
@@ -43,10 +42,7 @@ export class EditableArtifactLiveTicketAuthority {
|
|
|
43
42
|
if (!Number.isSafeInteger(this.ttlMs) || this.ttlMs < 1_000 || this.ttlMs > MAX_TTL_MS) {
|
|
44
43
|
throw new TypeError(`ticket ttl must be 1000-${MAX_TTL_MS} milliseconds`);
|
|
45
44
|
}
|
|
46
|
-
this.protocolVersion =
|
|
47
|
-
if (!Number.isSafeInteger(this.protocolVersion) || this.protocolVersion < 1) {
|
|
48
|
-
throw new TypeError("protocol version must be a positive safe integer");
|
|
49
|
-
}
|
|
45
|
+
this.protocolVersion = EDITABLE_ARTIFACT_LIVE_PROTOCOL_VERSION;
|
|
50
46
|
}
|
|
51
47
|
|
|
52
48
|
async mint(
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { EDITABLE_ARTIFACT_LIVE_WIRE_VERSION } from "@opengeni/contracts/editable-artifact-live";
|
|
1
2
|
import type {
|
|
2
3
|
EditableArtifactActor,
|
|
3
4
|
EditableArtifactCausalFrontier,
|
|
@@ -11,7 +12,7 @@ import type {
|
|
|
11
12
|
EditableArtifactTransactionId,
|
|
12
13
|
} from "../domain/editable-artifacts/types";
|
|
13
14
|
|
|
14
|
-
export const EDITABLE_ARTIFACT_LIVE_PROTOCOL_VERSION =
|
|
15
|
+
export const EDITABLE_ARTIFACT_LIVE_PROTOCOL_VERSION = EDITABLE_ARTIFACT_LIVE_WIRE_VERSION;
|
|
15
16
|
|
|
16
17
|
export type EditableArtifactLiveTicket = Readonly<{
|
|
17
18
|
artifactId: EditableArtifactId;
|
|
@@ -52,6 +53,7 @@ export type EditableArtifactLiveSnapshot =
|
|
|
52
53
|
modality: "spreadsheet";
|
|
53
54
|
causalFrontier: EditableArtifactCausalFrontier;
|
|
54
55
|
operationProtocolVersion: number;
|
|
56
|
+
snapshotVersion: number;
|
|
55
57
|
}>)
|
|
56
58
|
| (EditableArtifactLiveSnapshotCommon &
|
|
57
59
|
Readonly<{
|
|
@@ -120,7 +122,7 @@ type EditableArtifactLiveResumeCommon = Readonly<{
|
|
|
120
122
|
export type EditableArtifactLiveResume =
|
|
121
123
|
| (EditableArtifactLiveResumeCommon &
|
|
122
124
|
Readonly<{
|
|
123
|
-
modality
|
|
125
|
+
modality: "spreadsheet";
|
|
124
126
|
localCausalFrontier: EditableArtifactCausalFrontier;
|
|
125
127
|
}>)
|
|
126
128
|
| (EditableArtifactLiveResumeCommon &
|
|
@@ -166,6 +168,8 @@ export type EditableArtifactLiveServerFrame =
|
|
|
166
168
|
| Readonly<{
|
|
167
169
|
modality: "spreadsheet";
|
|
168
170
|
causalFrontier: EditableArtifactCausalFrontier;
|
|
171
|
+
operationProtocolVersion: number;
|
|
172
|
+
snapshotVersion: number;
|
|
169
173
|
}>
|
|
170
174
|
| Readonly<{
|
|
171
175
|
modality: "document" | "presentation";
|
|
@@ -81,15 +81,5 @@ export function encodeEditableArtifactLiveMutationWireFrame(
|
|
|
81
81
|
export function encodeEditableArtifactLiveServerWireFrame(
|
|
82
82
|
frame: EditableArtifactLiveServerFrame,
|
|
83
83
|
): Uint8Array {
|
|
84
|
-
if (frame.type === "transaction" && frame.transaction.modality === "spreadsheet") {
|
|
85
|
-
const { operationProtocolVersion, modality: _modality, ...transaction } = frame.transaction;
|
|
86
|
-
return encodeContractServerFrame({
|
|
87
|
-
...frame,
|
|
88
|
-
transaction: {
|
|
89
|
-
...transaction,
|
|
90
|
-
protocolVersion: operationProtocolVersion,
|
|
91
|
-
},
|
|
92
|
-
} as unknown as ContractServerFrame);
|
|
93
|
-
}
|
|
94
84
|
return encodeContractServerFrame(frame as unknown as ContractServerFrame);
|
|
95
85
|
}
|
package/src/index.ts
CHANGED
|
@@ -43,6 +43,7 @@ export * from "./transcription";
|
|
|
43
43
|
// fleet tools, the machines REST route, and the rest of the sandbox layer.
|
|
44
44
|
export * from "./sandbox/fleet";
|
|
45
45
|
export * from "./sandbox/routing";
|
|
46
|
+
export * from "./sandbox/runtime-settings";
|
|
46
47
|
|
|
47
48
|
// Access layer (transport-neutral grant resolution + permission checks).
|
|
48
49
|
export * from "./access";
|
|
@@ -59,25 +60,36 @@ export * from "./domain/skill-imports";
|
|
|
59
60
|
export * from "./domain/environments";
|
|
60
61
|
export * from "./rigs";
|
|
61
62
|
export * from "./domain/packs";
|
|
63
|
+
export * from "./domain/automations";
|
|
64
|
+
export * from "./domain/pr-review";
|
|
62
65
|
export * from "./domain/personal-connection-delegations";
|
|
63
66
|
export * from "./domain/resources";
|
|
67
|
+
export * from "./domain/github-repository-bindings";
|
|
64
68
|
export * from "./domain/session-tool-policy";
|
|
65
69
|
export * from "./domain/scheduled-tasks";
|
|
66
70
|
export * from "./domain/sessions";
|
|
67
71
|
export * from "./domain/insights";
|
|
68
72
|
export * from "./domain/memory-slack-publication";
|
|
69
73
|
export * from "./domain/memory-slack-delivery";
|
|
70
|
-
export * from "./domain/
|
|
74
|
+
export * from "./domain/governed-learning-slack-publication";
|
|
71
75
|
export * from "./domain/slack-publication-secret-safety";
|
|
72
76
|
export * from "./domain/company-profile-durable-learning-adapter";
|
|
77
|
+
export * from "./domain/company-profile-agent-admin";
|
|
78
|
+
export * from "./domain/company-brain-governed-writes";
|
|
79
|
+
export * from "./domain/governed-learning-evaluator";
|
|
80
|
+
export * from "./domain/governed-learning-activation";
|
|
81
|
+
export * from "./domain/remember";
|
|
73
82
|
export * from "./domain/slack-bot";
|
|
74
83
|
export * from "./domain/conversation-integrations";
|
|
75
84
|
export * from "./domain/fiken";
|
|
76
85
|
export * from "./domain/workspace-members";
|
|
77
86
|
export * from "./domain/video-generation";
|
|
78
87
|
export * from "./domain/video-generation-capabilities";
|
|
88
|
+
export * from "./domain/organization-membership-lifecycle";
|
|
79
89
|
export * from "./application/new-session-drafts";
|
|
80
90
|
export * from "./application/session-commands";
|
|
91
|
+
export * from "./application/session-tenancy";
|
|
92
|
+
export * from "./application/user-resource-grants";
|
|
81
93
|
|
|
82
94
|
// Durable editable-artifact live broker, ticket, ports, and projection types.
|
|
83
95
|
export * from "./editable-artifact-live";
|
package/src/rigs/index.ts
CHANGED
|
@@ -11,6 +11,7 @@ import type {
|
|
|
11
11
|
ProposeRigChangeRequest,
|
|
12
12
|
Rig,
|
|
13
13
|
RigChange,
|
|
14
|
+
ResourceAuthorityScope,
|
|
14
15
|
RigVersion,
|
|
15
16
|
UpdateRigRequest,
|
|
16
17
|
} from "@opengeni/contracts";
|
|
@@ -21,7 +22,6 @@ import {
|
|
|
21
22
|
createRigChange,
|
|
22
23
|
createRigVersion,
|
|
23
24
|
createRigVersionForChangePromotion,
|
|
24
|
-
deleteRigIfNoActiveSessions,
|
|
25
25
|
getRig,
|
|
26
26
|
getRigByName,
|
|
27
27
|
getRigChange,
|
|
@@ -30,12 +30,15 @@ import {
|
|
|
30
30
|
listRigChanges,
|
|
31
31
|
listRigVersions,
|
|
32
32
|
recordAuditEvent,
|
|
33
|
+
revokeScopedRig,
|
|
33
34
|
RigActiveVersionChangedError,
|
|
34
35
|
RigChangeTransitionError,
|
|
35
|
-
|
|
36
|
+
updateScopedRig,
|
|
36
37
|
type Database,
|
|
37
38
|
} from "@opengeni/db";
|
|
38
39
|
import { HTTPException } from "hono/http-exception";
|
|
40
|
+
import { boundedParallelMap } from "@opengeni/runtime/mcp-network";
|
|
41
|
+
import { requirePermission } from "../access";
|
|
39
42
|
import { rigProviderImagesFromVerification } from "./provider-images";
|
|
40
43
|
|
|
41
44
|
export * from "./provider-images";
|
|
@@ -44,6 +47,34 @@ export const MAX_RIGS_PER_WORKSPACE = 50;
|
|
|
44
47
|
export const MAX_CHECKS_PER_RIG = 100;
|
|
45
48
|
export const MAX_CREDENTIAL_HOOKS_PER_RIG = 50;
|
|
46
49
|
export const MAX_DEFAULT_VARIABLE_SETS_PER_RIG = 25;
|
|
50
|
+
export const RIG_DEFAULT_VARIABLE_SET_LOAD_CONCURRENCY = 4;
|
|
51
|
+
|
|
52
|
+
type VariableSetEnvironment = { values: Record<string, string> } | null;
|
|
53
|
+
|
|
54
|
+
/** Load complete rig defaults concurrently while preserving listed precedence. */
|
|
55
|
+
export async function loadRigDefaultVariableSetEnvironment(
|
|
56
|
+
variableSetIds: readonly string[],
|
|
57
|
+
load: (variableSetId: string) => Promise<VariableSetEnvironment>,
|
|
58
|
+
): Promise<Record<string, string>> {
|
|
59
|
+
const variableSets = await boundedParallelMap(
|
|
60
|
+
variableSetIds,
|
|
61
|
+
RIG_DEFAULT_VARIABLE_SET_LOAD_CONCURRENCY,
|
|
62
|
+
load,
|
|
63
|
+
);
|
|
64
|
+
const values: Record<string, string> = {};
|
|
65
|
+
for (const variableSet of variableSets) {
|
|
66
|
+
Object.assign(values, variableSet?.values ?? {});
|
|
67
|
+
}
|
|
68
|
+
return values;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** Rig defaults layer below the session's explicitly selected variable set. */
|
|
72
|
+
export function mergeRigDefaultVariableSetEnvironment(
|
|
73
|
+
rigDefaultValues: Record<string, string>,
|
|
74
|
+
sessionValues: Record<string, string>,
|
|
75
|
+
): Record<string, string> {
|
|
76
|
+
return { ...rigDefaultValues, ...sessionValues };
|
|
77
|
+
}
|
|
47
78
|
|
|
48
79
|
export type RigServices = {
|
|
49
80
|
db: Database;
|
|
@@ -92,10 +123,10 @@ export function rigActorForGrant(grant: AccessGrant): string {
|
|
|
92
123
|
|
|
93
124
|
export async function requireRigForApi(
|
|
94
125
|
db: Database,
|
|
95
|
-
|
|
126
|
+
grant: AccessGrant,
|
|
96
127
|
rigId: string,
|
|
97
128
|
): Promise<Rig> {
|
|
98
|
-
const rig = await getRig(db,
|
|
129
|
+
const rig = await getRig(db, grant, rigId);
|
|
99
130
|
if (!rig) {
|
|
100
131
|
throw new HTTPException(404, { message: "rig not found" });
|
|
101
132
|
}
|
|
@@ -133,7 +164,9 @@ function assertUniqueCheckNames(checks: ReadonlyArray<{ name: string }> | undefi
|
|
|
133
164
|
const seen = new Set<string>();
|
|
134
165
|
for (const check of checks) {
|
|
135
166
|
if (seen.has(check.name)) {
|
|
136
|
-
throw new HTTPException(422, {
|
|
167
|
+
throw new HTTPException(422, {
|
|
168
|
+
message: `duplicate rig check name: ${check.name}`,
|
|
169
|
+
});
|
|
137
170
|
}
|
|
138
171
|
seen.add(check.name);
|
|
139
172
|
}
|
|
@@ -143,42 +176,78 @@ function assertUniqueCheckNames(checks: ReadonlyArray<{ name: string }> | undefi
|
|
|
143
176
|
// cross-workspace id indistinguishable from a missing one, so both map to 422.
|
|
144
177
|
async function assertVariableSetsExist(
|
|
145
178
|
db: Database,
|
|
146
|
-
|
|
179
|
+
access: AccessGrant,
|
|
180
|
+
rigScope: ResourceAuthorityScope,
|
|
147
181
|
ids: ReadonlyArray<string> | undefined,
|
|
148
182
|
): Promise<void> {
|
|
149
|
-
if (
|
|
183
|
+
if (ids === undefined) {
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
requirePermission(access, "variable-sets:attach");
|
|
187
|
+
if (ids.length === 0) {
|
|
150
188
|
return;
|
|
151
189
|
}
|
|
190
|
+
requirePermission(access, "variable-sets:use");
|
|
152
191
|
const unique = [...new Set(ids)];
|
|
153
192
|
for (const id of unique) {
|
|
154
|
-
const variableSet = await getVariableSet(db,
|
|
193
|
+
const variableSet = await getVariableSet(db, access, id);
|
|
155
194
|
if (!variableSet) {
|
|
156
|
-
throw new HTTPException(422, {
|
|
195
|
+
throw new HTTPException(422, {
|
|
196
|
+
message: `unknown defaultVariableSetId: ${id}`,
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
if (!variableSetScopeAllowedForRig(rigScope, variableSet.scope)) {
|
|
200
|
+
throw new HTTPException(422, {
|
|
201
|
+
message: `${rigScope}-scoped rigs cannot use ${variableSet.scope}-scoped variable sets`,
|
|
202
|
+
});
|
|
157
203
|
}
|
|
158
204
|
}
|
|
159
205
|
}
|
|
160
206
|
|
|
207
|
+
/** A resource may depend only on Variable Sets whose audience is no narrower
|
|
208
|
+
* than its own. User-owned rigs may use anything visible to their owner. */
|
|
209
|
+
export function variableSetScopeAllowedForRig(
|
|
210
|
+
rigScope: ResourceAuthorityScope,
|
|
211
|
+
variableSetScope: ResourceAuthorityScope,
|
|
212
|
+
): boolean {
|
|
213
|
+
return (
|
|
214
|
+
rigScope === "user" ||
|
|
215
|
+
variableSetScope === "organization" ||
|
|
216
|
+
(rigScope === "workspace" && variableSetScope === "workspace")
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
|
|
161
220
|
export async function createRigForApi(
|
|
162
221
|
deps: RigServices,
|
|
163
222
|
grant: AccessGrant,
|
|
164
223
|
payload: CreateRigRequest,
|
|
224
|
+
options: { allowOrganization?: boolean } = {},
|
|
165
225
|
): Promise<Rig> {
|
|
166
|
-
const workspaceId = grant.workspaceId;
|
|
167
226
|
const name = trimmedRigName(payload.name);
|
|
168
227
|
assertUniqueCheckNames(payload.checks);
|
|
169
|
-
await assertVariableSetsExist(
|
|
170
|
-
|
|
228
|
+
await assertVariableSetsExist(
|
|
229
|
+
deps.db,
|
|
230
|
+
grant,
|
|
231
|
+
payload.scope,
|
|
232
|
+
payload.defaultVariableSetIds.length > 0 ? payload.defaultVariableSetIds : undefined,
|
|
233
|
+
);
|
|
234
|
+
if ((await countRigs(deps.db, grant, payload.scope)) >= MAX_RIGS_PER_WORKSPACE) {
|
|
171
235
|
throw new HTTPException(422, {
|
|
172
236
|
message: `a workspace supports at most ${MAX_RIGS_PER_WORKSPACE} rigs`,
|
|
173
237
|
});
|
|
174
238
|
}
|
|
175
|
-
if (await getRigByName(deps.db,
|
|
176
|
-
throw new HTTPException(409, {
|
|
239
|
+
if (await getRigByName(deps.db, grant, name, payload.scope)) {
|
|
240
|
+
throw new HTTPException(409, {
|
|
241
|
+
message: `rig name is already in use: ${name}`,
|
|
242
|
+
});
|
|
177
243
|
}
|
|
178
244
|
const createdBy = rigActorForGrant(grant);
|
|
179
245
|
const rig = await createRig(deps.db, {
|
|
180
246
|
accountId: grant.accountId,
|
|
181
|
-
workspaceId,
|
|
247
|
+
workspaceId: grant.workspaceId,
|
|
248
|
+
scope: payload.scope,
|
|
249
|
+
subjectId: grant.subjectId,
|
|
250
|
+
allowOrganization: options.allowOrganization === true,
|
|
182
251
|
name,
|
|
183
252
|
description: payload.description ?? null,
|
|
184
253
|
createdBy,
|
|
@@ -192,7 +261,11 @@ export async function createRigForApi(
|
|
|
192
261
|
createdBy,
|
|
193
262
|
},
|
|
194
263
|
});
|
|
195
|
-
await recordRigAuditEvent(deps.db, {
|
|
264
|
+
await recordRigAuditEvent(deps.db, {
|
|
265
|
+
grant,
|
|
266
|
+
action: "rig.created",
|
|
267
|
+
rigId: rig.id,
|
|
268
|
+
});
|
|
196
269
|
return rig;
|
|
197
270
|
}
|
|
198
271
|
|
|
@@ -201,20 +274,27 @@ export async function updateRigForApi(
|
|
|
201
274
|
grant: AccessGrant,
|
|
202
275
|
rig: Rig,
|
|
203
276
|
payload: UpdateRigRequest,
|
|
277
|
+
options: { allowOrganization?: boolean } = {},
|
|
204
278
|
): Promise<Rig> {
|
|
205
|
-
const workspaceId = grant.workspaceId;
|
|
206
279
|
const name = payload.name !== undefined ? trimmedRigName(payload.name) : undefined;
|
|
207
280
|
if (name !== undefined && name !== rig.name) {
|
|
208
|
-
const existing = await getRigByName(deps.db,
|
|
281
|
+
const existing = await getRigByName(deps.db, grant, name, rig.scope);
|
|
209
282
|
if (existing && existing.id !== rig.id) {
|
|
210
|
-
throw new HTTPException(409, {
|
|
283
|
+
throw new HTTPException(409, {
|
|
284
|
+
message: `rig name is already in use: ${name}`,
|
|
285
|
+
});
|
|
211
286
|
}
|
|
212
287
|
}
|
|
213
|
-
const updated = await
|
|
288
|
+
const updated = await updateScopedRig(deps.db, grant, rig.id, {
|
|
214
289
|
...(name !== undefined ? { name } : {}),
|
|
215
290
|
...(payload.description !== undefined ? { description: payload.description } : {}),
|
|
291
|
+
allowOrganization: options.allowOrganization === true,
|
|
292
|
+
});
|
|
293
|
+
await recordRigAuditEvent(deps.db, {
|
|
294
|
+
grant,
|
|
295
|
+
action: "rig.updated",
|
|
296
|
+
rigId: rig.id,
|
|
216
297
|
});
|
|
217
|
-
await recordRigAuditEvent(deps.db, { grant, action: "rig.updated", rigId: rig.id });
|
|
218
298
|
return updated;
|
|
219
299
|
}
|
|
220
300
|
|
|
@@ -222,18 +302,27 @@ export async function deleteRigForApi(
|
|
|
222
302
|
deps: RigServices,
|
|
223
303
|
grant: AccessGrant,
|
|
224
304
|
rig: Rig,
|
|
305
|
+
options: { allowOrganization?: boolean } = {},
|
|
225
306
|
): Promise<void> {
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
throw new HTTPException(409, {
|
|
230
|
-
message: `rig is referenced by ${deleted.activeSessionCount} active session(s); it cannot be deleted`,
|
|
307
|
+
try {
|
|
308
|
+
await revokeScopedRig(deps.db, grant, rig.id, {
|
|
309
|
+
allowOrganization: options.allowOrganization === true,
|
|
231
310
|
});
|
|
311
|
+
} catch (error) {
|
|
312
|
+
let current: unknown = error;
|
|
313
|
+
while (current instanceof Error) {
|
|
314
|
+
if (current.message.includes("active sessions")) {
|
|
315
|
+
throw new HTTPException(409, { message: current.message });
|
|
316
|
+
}
|
|
317
|
+
current = current.cause;
|
|
318
|
+
}
|
|
319
|
+
throw error;
|
|
232
320
|
}
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
321
|
+
await recordRigAuditEvent(deps.db, {
|
|
322
|
+
grant,
|
|
323
|
+
action: "rig.deleted",
|
|
324
|
+
rigId: rig.id,
|
|
325
|
+
});
|
|
237
326
|
}
|
|
238
327
|
|
|
239
328
|
// Records a proposed change against the rig's CURRENT active version (the base
|
|
@@ -247,15 +336,18 @@ export async function proposeRigChangeForApi(
|
|
|
247
336
|
request: ProposeRigChangeRequest,
|
|
248
337
|
options: { proposedBy?: string } = {},
|
|
249
338
|
): Promise<RigChange> {
|
|
250
|
-
const workspaceId =
|
|
339
|
+
const workspaceId = rig.workspaceId;
|
|
251
340
|
if (!rig.activeVersion) {
|
|
252
|
-
throw new HTTPException(422, {
|
|
341
|
+
throw new HTTPException(422, {
|
|
342
|
+
message: "rig has no active version to base a change on",
|
|
343
|
+
});
|
|
253
344
|
}
|
|
254
345
|
if (request.kind === "definition_edit") {
|
|
255
346
|
assertUniqueCheckNames(request.payload.checks);
|
|
256
347
|
await assertVariableSetsExist(
|
|
257
348
|
deps.db,
|
|
258
|
-
|
|
349
|
+
grant,
|
|
350
|
+
rig.scope,
|
|
259
351
|
request.payload.defaultVariableSetIds ?? undefined,
|
|
260
352
|
);
|
|
261
353
|
}
|
|
@@ -342,18 +434,22 @@ export async function promoteSetupAppendChange(
|
|
|
342
434
|
});
|
|
343
435
|
}
|
|
344
436
|
if (change.status !== "proposed" && change.status !== "verifying") {
|
|
345
|
-
throw new HTTPException(409, {
|
|
437
|
+
throw new HTTPException(409, {
|
|
438
|
+
message: `rig change is ${change.status}; cannot promote`,
|
|
439
|
+
});
|
|
346
440
|
}
|
|
347
441
|
if (!change.baseVersionId) {
|
|
348
442
|
throw new HTTPException(422, { message: "rig change has no base version" });
|
|
349
443
|
}
|
|
350
|
-
const base = await getRigVersion(deps.db,
|
|
444
|
+
const base = await getRigVersion(deps.db, rig.workspaceId, rig.id, change.baseVersionId);
|
|
351
445
|
if (!base) {
|
|
352
446
|
throw new HTTPException(404, { message: "base rig version not found" });
|
|
353
447
|
}
|
|
354
448
|
const payload = change.payload as { command?: unknown; note?: unknown };
|
|
355
449
|
if (typeof payload.command !== "string" || !payload.command.trim()) {
|
|
356
|
-
throw new HTTPException(422, {
|
|
450
|
+
throw new HTTPException(422, {
|
|
451
|
+
message: "setup_append change is missing command",
|
|
452
|
+
});
|
|
357
453
|
}
|
|
358
454
|
const nextDefinition = {
|
|
359
455
|
image: base.image,
|
|
@@ -364,7 +460,7 @@ export async function promoteSetupAppendChange(
|
|
|
364
460
|
};
|
|
365
461
|
const { version, change: updated } = await promoteChangeWithActiveCas(
|
|
366
462
|
deps,
|
|
367
|
-
|
|
463
|
+
rig.workspaceId,
|
|
368
464
|
rig.id,
|
|
369
465
|
change.id,
|
|
370
466
|
{
|
|
@@ -382,13 +478,21 @@ export async function promoteSetupAppendChange(
|
|
|
382
478
|
grant,
|
|
383
479
|
action: "rig.change.merged",
|
|
384
480
|
rigId: rig.id,
|
|
385
|
-
metadata: {
|
|
481
|
+
metadata: {
|
|
482
|
+
changeId: change.id,
|
|
483
|
+
versionId: version.id,
|
|
484
|
+
version: version.version,
|
|
485
|
+
},
|
|
386
486
|
});
|
|
387
487
|
await recordRigAuditEvent(deps.db, {
|
|
388
488
|
grant,
|
|
389
489
|
action: "rig.version.promoted",
|
|
390
490
|
rigId: rig.id,
|
|
391
|
-
metadata: {
|
|
491
|
+
metadata: {
|
|
492
|
+
changeId: change.id,
|
|
493
|
+
versionId: version.id,
|
|
494
|
+
version: version.version,
|
|
495
|
+
},
|
|
392
496
|
});
|
|
393
497
|
return { change: updated, version };
|
|
394
498
|
}
|
|
@@ -400,10 +504,14 @@ export async function promoteVerifiedDefinitionEditChangeForApi(
|
|
|
400
504
|
change: RigChange,
|
|
401
505
|
): Promise<{ change: RigChange; version: RigVersion }> {
|
|
402
506
|
if (change.kind !== "definition_edit") {
|
|
403
|
-
throw new HTTPException(422, {
|
|
507
|
+
throw new HTTPException(422, {
|
|
508
|
+
message: "only definition_edit changes use explicit promote",
|
|
509
|
+
});
|
|
404
510
|
}
|
|
405
511
|
if (change.status !== "proposed") {
|
|
406
|
-
throw new HTTPException(409, {
|
|
512
|
+
throw new HTTPException(409, {
|
|
513
|
+
message: `rig change is ${change.status}; cannot promote`,
|
|
514
|
+
});
|
|
407
515
|
}
|
|
408
516
|
if (change.verification?.passed !== true) {
|
|
409
517
|
throw new HTTPException(422, {
|
|
@@ -413,7 +521,7 @@ export async function promoteVerifiedDefinitionEditChangeForApi(
|
|
|
413
521
|
if (!change.baseVersionId) {
|
|
414
522
|
throw new HTTPException(422, { message: "rig change has no base version" });
|
|
415
523
|
}
|
|
416
|
-
const base = await getRigVersion(deps.db,
|
|
524
|
+
const base = await getRigVersion(deps.db, rig.workspaceId, rig.id, change.baseVersionId);
|
|
417
525
|
if (!base) {
|
|
418
526
|
throw new HTTPException(404, { message: "base rig version not found" });
|
|
419
527
|
}
|
|
@@ -439,7 +547,7 @@ export async function promoteVerifiedDefinitionEditChangeForApi(
|
|
|
439
547
|
};
|
|
440
548
|
const { version, change: updated } = await promoteChangeWithActiveCas(
|
|
441
549
|
deps,
|
|
442
|
-
|
|
550
|
+
rig.workspaceId,
|
|
443
551
|
rig.id,
|
|
444
552
|
change.id,
|
|
445
553
|
{
|
|
@@ -457,13 +565,21 @@ export async function promoteVerifiedDefinitionEditChangeForApi(
|
|
|
457
565
|
grant,
|
|
458
566
|
action: "rig.change.merged",
|
|
459
567
|
rigId: rig.id,
|
|
460
|
-
metadata: {
|
|
568
|
+
metadata: {
|
|
569
|
+
changeId: change.id,
|
|
570
|
+
versionId: version.id,
|
|
571
|
+
version: version.version,
|
|
572
|
+
},
|
|
461
573
|
});
|
|
462
574
|
await recordRigAuditEvent(deps.db, {
|
|
463
575
|
grant,
|
|
464
576
|
action: "rig.version.promoted",
|
|
465
577
|
rigId: rig.id,
|
|
466
|
-
metadata: {
|
|
578
|
+
metadata: {
|
|
579
|
+
changeId: change.id,
|
|
580
|
+
versionId: version.id,
|
|
581
|
+
version: version.version,
|
|
582
|
+
},
|
|
467
583
|
});
|
|
468
584
|
return { change: updated, version };
|
|
469
585
|
}
|
|
@@ -480,13 +596,14 @@ export async function createRigVersionForApi(
|
|
|
480
596
|
assertUniqueCheckNames(payload.checks);
|
|
481
597
|
await assertVariableSetsExist(
|
|
482
598
|
deps.db,
|
|
483
|
-
grant
|
|
599
|
+
grant,
|
|
600
|
+
rig.scope,
|
|
484
601
|
payload.defaultVariableSetIds ?? undefined,
|
|
485
602
|
);
|
|
486
603
|
const base = rig.activeVersion;
|
|
487
604
|
const version = await createRigVersion(
|
|
488
605
|
deps.db,
|
|
489
|
-
|
|
606
|
+
rig.workspaceId,
|
|
490
607
|
rig.id,
|
|
491
608
|
{
|
|
492
609
|
image: payload.image === undefined ? base.image : payload.image,
|
|
@@ -516,7 +633,7 @@ export async function activateRigVersionForApi(
|
|
|
516
633
|
rig: Rig,
|
|
517
634
|
versionId: string,
|
|
518
635
|
): Promise<RigVersion> {
|
|
519
|
-
const workspaceId =
|
|
636
|
+
const workspaceId = rig.workspaceId;
|
|
520
637
|
const version = await activateRigVersion(deps.db, workspaceId, rig.id, versionId);
|
|
521
638
|
await recordRigAuditEvent(deps.db, {
|
|
522
639
|
grant,
|