@opengeni/api-router 0.30.3 → 2.2.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/api-websocket.d.ts +18 -0
- package/dist/app.js +1 -1
- package/dist/auth/managed-auth.d.ts +6 -0
- package/dist/{chunk-WCXHI3FF.js → chunk-3TP54PPX.js} +26692 -15389
- package/dist/chunk-3TP54PPX.js.map +1 -0
- package/dist/codemode.d.ts +8 -1
- package/dist/company-brain-okf.d.ts +24 -0
- package/dist/connected-machine-computer-access.d.ts +11 -0
- package/dist/connection-authority-owner.d.ts +59 -0
- package/dist/connection-ownership.d.ts +47 -0
- package/dist/controller-data-plane.d.ts +16 -0
- package/dist/editable-artifact-websocket.d.ts +5 -15
- package/dist/editable-artifact-workspace-files.d.ts +1 -1
- package/dist/http/api-error.d.ts +10 -0
- package/dist/http/cors.d.ts +5 -0
- package/dist/http/interaction-control-error.d.ts +13 -0
- package/dist/http/sse.d.ts +2 -3
- package/dist/index.d.ts +5 -2
- package/dist/index.js +326 -82
- package/dist/index.js.map +1 -1
- package/dist/integrations/atlassian.d.ts +17 -0
- package/dist/integrations/fiken.d.ts +7 -2
- package/dist/integrations/google-drive.d.ts +18 -0
- package/dist/integrations/oauth-client.d.ts +22 -3
- package/dist/integrations/oauth-profiles.d.ts +195 -0
- package/dist/integrations/personal-github-repositories.d.ts +45 -0
- package/dist/integrations/personal-github.d.ts +40 -0
- package/dist/integrations/pr-review-provider.d.ts +22 -0
- package/dist/integrations/provider-oauth.d.ts +8 -0
- package/dist/integrations/slack-app-home.d.ts +24 -0
- package/dist/integrations/slack-bot.d.ts +157 -9
- package/dist/integrations/slack-interactions.d.ts +39 -5
- package/dist/integrations/slack-routing.d.ts +120 -0
- package/dist/integrations/social-oauth.d.ts +5 -0
- package/dist/interaction-frame-proxy.d.ts +68 -0
- package/dist/mcp/company-brain-governed-writes.d.ts +26 -0
- package/dist/mcp/company-profile-agent-admin.d.ts +44 -0
- package/dist/mcp/documents.d.ts +1 -0
- package/dist/mcp/remember.d.ts +28 -0
- package/dist/mcp/request-abort.d.ts +19 -0
- package/dist/mcp/scheduled-task-view.d.ts +4 -4
- package/dist/mcp/server.d.ts +15 -2
- package/dist/mcp/session-view.d.ts +4 -0
- package/dist/mcp/session-wait.d.ts +137 -0
- package/dist/routes/automations.d.ts +4 -0
- package/dist/routes/billing.d.ts +5 -0
- package/dist/routes/browser-sessions.d.ts +4 -1
- package/dist/routes/company-brain.d.ts +3 -0
- package/dist/routes/company-profile.d.ts +2 -1
- package/dist/routes/connection-authorities.d.ts +3 -0
- package/dist/routes/personal-github-git-broker.d.ts +29 -0
- package/dist/routes/personal-github.d.ts +3 -0
- package/dist/routes/pr-review.d.ts +3 -0
- package/dist/routes/sessions.d.ts +1 -0
- package/dist/routes/user-resource-authorities.d.ts +3 -0
- package/dist/routes/workspace-capture.d.ts +10 -2
- package/dist/routes/workspace-learning.d.ts +3 -0
- package/dist/routes/workspaces.d.ts +19 -0
- package/dist/sandbox/channel-a.d.ts +11 -1
- package/dist/sandbox/connection-authority.d.ts +3 -0
- package/dist/sandbox/enrollment.d.ts +2 -0
- package/dist/sandbox/machines.d.ts +2 -2
- package/dist/sandbox/metrics-ingestion.d.ts +10 -20
- package/dist/sandbox/viewer.d.ts +24 -1
- package/dist/sandbox-file-artifacts.d.ts +11 -0
- package/dist/scheduled-task-deletion.d.ts +14 -0
- package/dist/slack-reaction-files.d.ts +1 -1
- package/dist/temporal-schedule-cleanup.d.ts +1 -0
- package/package.json +20 -18
- package/src/api-websocket.ts +23 -0
- package/src/app.ts +161 -23
- package/src/auth/managed-auth.ts +25 -3
- package/src/codemode.ts +53 -23
- package/src/codex-realtime.ts +8 -2
- package/src/company-brain-okf.ts +340 -0
- package/src/connected-machine-computer-access.ts +33 -0
- package/src/connection-authority-owner.ts +61 -0
- package/src/connection-ownership.ts +180 -0
- package/src/controller-data-plane.ts +47 -0
- package/src/editable-artifact-native-kernel.ts +26 -15
- package/src/editable-artifact-office-import.ts +28 -3
- package/src/editable-artifact-production.ts +13 -7
- package/src/editable-artifact-websocket.ts +11 -18
- package/src/editable-artifact-workspace-files.ts +31 -6
- package/src/http/api-error.ts +28 -0
- package/src/http/auth.ts +4 -0
- package/src/http/cors.ts +35 -0
- package/src/http/interaction-control-error.ts +164 -0
- package/src/http/sse.ts +112 -66
- package/src/index.ts +46 -6
- package/src/integrations/atlassian.ts +146 -35
- package/src/integrations/fiken.ts +102 -22
- package/src/integrations/google-drive.ts +301 -92
- package/src/integrations/oauth-client.ts +272 -143
- package/src/integrations/oauth-profiles.ts +477 -0
- package/src/integrations/personal-github-repositories.ts +445 -0
- package/src/integrations/personal-github.ts +705 -0
- package/src/integrations/pr-review-provider.ts +246 -0
- package/src/integrations/provider-oauth.ts +121 -5
- package/src/integrations/slack-app-home.ts +300 -0
- package/src/integrations/slack-bot.ts +342 -57
- package/src/integrations/slack-interactions.ts +1898 -259
- package/src/integrations/slack-routing.ts +337 -0
- package/src/integrations/social-oauth.ts +25 -0
- package/src/interaction-frame-proxy.ts +409 -0
- package/src/mcp/company-brain-governed-writes.ts +262 -0
- package/src/mcp/company-profile-agent-admin.ts +205 -0
- package/src/mcp/documents.ts +37 -6
- package/src/mcp/files.ts +16 -1
- package/src/mcp/remember.ts +181 -0
- package/src/mcp/request-abort.ts +44 -0
- package/src/mcp/scheduled-task-view.ts +1 -0
- package/src/mcp/server.ts +719 -369
- package/src/mcp/session-view.ts +54 -0
- package/src/mcp/session-wait.ts +554 -0
- package/src/model-catalog.ts +20 -12
- package/src/routes/api-integrations.ts +4 -0
- package/src/routes/api-keys.ts +1 -0
- package/src/routes/automations.ts +534 -0
- package/src/routes/billing.ts +57 -1
- package/src/routes/browser-sessions.ts +329 -79
- package/src/routes/channels.ts +17 -1
- package/src/routes/codex.ts +50 -0
- package/src/routes/company-brain.ts +367 -0
- package/src/routes/company-profile.ts +1 -1
- package/src/routes/computer-sessions.ts +320 -93
- package/src/routes/connection-authorities.ts +139 -0
- package/src/routes/connections.ts +144 -34
- package/src/routes/documents.ts +353 -4
- package/src/routes/editable-artifacts.ts +11 -3
- package/src/routes/enrollments.ts +116 -26
- package/src/routes/environments.ts +127 -55
- package/src/routes/files.ts +72 -11
- package/src/routes/install.ts +82 -28
- package/src/routes/integration-facets.ts +29 -0
- package/src/routes/interaction-resources.ts +20 -2
- package/src/routes/machines.ts +275 -13
- package/src/routes/organization-memberships.ts +717 -28
- package/src/routes/packs.ts +4 -3
- package/src/routes/personal-github-git-broker.ts +785 -0
- package/src/routes/personal-github.ts +319 -0
- package/src/routes/pr-review.ts +531 -0
- package/src/routes/rigs.ts +104 -39
- package/src/routes/scheduled-tasks.ts +19 -21
- package/src/routes/sessions.ts +775 -38
- package/src/routes/social.ts +14 -2
- package/src/routes/supergrok.ts +23 -4
- package/src/routes/transcription-recordings.ts +8 -2
- package/src/routes/user-resource-authorities.ts +138 -0
- package/src/routes/workspace-artifacts.ts +4 -1
- package/src/routes/workspace-capture.ts +21 -0
- package/src/routes/workspace-learning.ts +228 -0
- package/src/routes/workspaces.ts +59 -6
- package/src/sandbox/auth-callout.ts +38 -12
- package/src/sandbox/channel-a.ts +242 -19
- package/src/sandbox/connection-authority.ts +3 -0
- package/src/sandbox/enrollment.ts +15 -3
- package/src/sandbox/machines.ts +186 -64
- package/src/sandbox/metrics-ingestion.ts +220 -58
- package/src/sandbox/viewer.ts +243 -19
- package/src/sandbox-file-artifacts.ts +329 -0
- package/src/scheduled-task-deletion.ts +127 -0
- package/src/slack-reaction-files.ts +16 -5
- package/src/temporal-schedule-cleanup.ts +13 -0
- package/dist/chunk-WCXHI3FF.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
import {
|
|
2
|
+
InteractionFrameProxyTransport,
|
|
2
3
|
SlackBotProviderError,
|
|
4
|
+
cleanupScheduledTaskConnectorAuthorization,
|
|
3
5
|
createAppComposition,
|
|
4
6
|
createOpenGeniSlackBotInteractionClient,
|
|
5
7
|
startSlackInteractionPump,
|
|
6
8
|
startTemporalScheduleCleanupPump
|
|
7
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-3TP54PPX.js";
|
|
8
10
|
|
|
9
11
|
// src/index.ts
|
|
10
12
|
import {
|
|
11
13
|
dbSearchPath,
|
|
12
14
|
getSettings,
|
|
15
|
+
resolveFirstPartyDelegationSecret,
|
|
13
16
|
resolveNatsCalloutConfig,
|
|
14
17
|
resolveNatsControlPlaneAuth,
|
|
15
18
|
retryStartupDependency,
|
|
@@ -68,14 +71,20 @@ import {
|
|
|
68
71
|
resolveEnrollmentSigningSecret
|
|
69
72
|
} from "@opengeni/config";
|
|
70
73
|
import { verifyEnrollmentBearer } from "@opengeni/contracts";
|
|
71
|
-
import { getEnrollment } from "@opengeni/db";
|
|
74
|
+
import { claimEnrollmentConnection, getEnrollment } from "@opengeni/db";
|
|
72
75
|
import {
|
|
73
76
|
createResponderConnection,
|
|
74
77
|
decodeAuthRequest,
|
|
75
78
|
mintAuthResponse,
|
|
76
79
|
mintUserJwt,
|
|
80
|
+
parseAgentConnectionName,
|
|
77
81
|
workspaceAgentPermissions
|
|
78
82
|
} from "@opengeni/events";
|
|
83
|
+
|
|
84
|
+
// src/sandbox/connection-authority.ts
|
|
85
|
+
var AGENT_CONNECTION_LEASE_MS = 2e4;
|
|
86
|
+
|
|
87
|
+
// src/sandbox/auth-callout.ts
|
|
79
88
|
var AUTH_CALLOUT_SUBJECT = "$SYS.REQ.USER.AUTH";
|
|
80
89
|
var NATS_USER_JWT_TTL_SECONDS = 5 * 60;
|
|
81
90
|
async function handleAuthorizationRequest(deps, requestBytes) {
|
|
@@ -121,7 +130,29 @@ async function handleAuthorizationRequest(deps, requestBytes) {
|
|
|
121
130
|
if (enrollment.credentialGeneration !== claims.credentialGeneration) {
|
|
122
131
|
return deny("enrollment credential generation mismatch");
|
|
123
132
|
}
|
|
124
|
-
const
|
|
133
|
+
const connectionInstanceId = parseAgentConnectionName(decoded.name);
|
|
134
|
+
if (!connectionInstanceId) {
|
|
135
|
+
return deny("missing or invalid agent connection instance");
|
|
136
|
+
}
|
|
137
|
+
const claim = await claimEnrollmentConnection(deps.db, {
|
|
138
|
+
workspaceId: claims.workspaceId,
|
|
139
|
+
enrollmentId: claims.enrollmentId,
|
|
140
|
+
credentialGeneration: claims.credentialGeneration,
|
|
141
|
+
connectionInstanceId,
|
|
142
|
+
leaseMs: AGENT_CONNECTION_LEASE_MS
|
|
143
|
+
});
|
|
144
|
+
if (!claim.claimed) {
|
|
145
|
+
deps.observability?.warn?.("auth-callout: denied a duplicate live runner", {
|
|
146
|
+
workspaceId: claims.workspaceId,
|
|
147
|
+
agentId: claims.agentId
|
|
148
|
+
});
|
|
149
|
+
return deny("another runner instance currently owns this enrollment");
|
|
150
|
+
}
|
|
151
|
+
const permissions = workspaceAgentPermissions(
|
|
152
|
+
claims.workspaceId,
|
|
153
|
+
claims.agentId,
|
|
154
|
+
connectionInstanceId
|
|
155
|
+
);
|
|
125
156
|
const nowSeconds = Math.floor(Date.now() / 1e3);
|
|
126
157
|
const userJwt = mintUserJwt({
|
|
127
158
|
userPublicKey: decoded.userNkey,
|
|
@@ -145,7 +176,8 @@ async function handleAuthorizationRequest(deps, requestBytes) {
|
|
|
145
176
|
});
|
|
146
177
|
deps.observability?.info?.("auth-callout: granted a workspace-scoped NATS credential", {
|
|
147
178
|
workspaceId: claims.workspaceId,
|
|
148
|
-
agentId: claims.agentId
|
|
179
|
+
agentId: claims.agentId,
|
|
180
|
+
connectionGeneration: claim.connectionGeneration
|
|
149
181
|
});
|
|
150
182
|
return Buffer.from(response, "utf8");
|
|
151
183
|
}
|
|
@@ -170,35 +202,44 @@ async function startAuthCalloutResponder(deps, natsUrl) {
|
|
|
170
202
|
import {
|
|
171
203
|
clearEnrollmentWentOffline,
|
|
172
204
|
disconnectAttachedBrowserDevices,
|
|
205
|
+
advanceEnrollmentAgentUpdate,
|
|
173
206
|
getEnrollment as getEnrollment2,
|
|
207
|
+
getLiveEnrollmentConnection,
|
|
174
208
|
ingestMachineMetricsSample,
|
|
175
209
|
reconcileAttachedBrowserInventory,
|
|
210
|
+
releaseEnrollmentConnection,
|
|
211
|
+
renewEnrollmentConnection,
|
|
176
212
|
sessionsWithActiveOpOnEnrollment,
|
|
177
213
|
setEnrollmentDisplayState,
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
touchEnrollmentLastSeen
|
|
214
|
+
setEnrollmentAgentRuntime,
|
|
215
|
+
setEnrollmentOpStreamState
|
|
181
216
|
} from "@opengeni/db";
|
|
182
217
|
import {
|
|
183
218
|
AttachedBrowserInventorySnapshot as AttachedBrowserInventoryContract
|
|
184
219
|
} from "@opengeni/contracts";
|
|
185
220
|
import { appendAndPublishEvents } from "@opengeni/events";
|
|
221
|
+
import { normalizeConnectedMachineWorkspaceRoot } from "@opengeni/runtime/sandbox";
|
|
186
222
|
import {
|
|
187
223
|
AgentEvent,
|
|
224
|
+
AgentUpdateStage,
|
|
188
225
|
Arch,
|
|
189
226
|
GoingOfflineReason,
|
|
190
227
|
Hello,
|
|
191
228
|
Os,
|
|
192
229
|
goingOfflineReasonToJSON
|
|
193
230
|
} from "@opengeni/agent-proto";
|
|
194
|
-
var AGENT_EVENTS_SUBJECT = "agent.*.*.events";
|
|
195
|
-
var AGENT_HELLO_SUBJECT = "agent.*.*.hello";
|
|
231
|
+
var AGENT_EVENTS_SUBJECT = "agent.*.*.connection.*.events";
|
|
232
|
+
var AGENT_HELLO_SUBJECT = "agent.*.*.connection.*.hello";
|
|
196
233
|
function parseAgentSubject(subject, tail) {
|
|
197
234
|
const parts = subject.split(".");
|
|
198
|
-
if (parts.length !==
|
|
235
|
+
if (parts.length !== 6 || parts[0] !== "agent" || parts[3] !== "connection" || !parts[4] || parts[5] !== tail) {
|
|
199
236
|
return null;
|
|
200
237
|
}
|
|
201
|
-
return {
|
|
238
|
+
return {
|
|
239
|
+
workspaceId: parts[1],
|
|
240
|
+
agentId: parts[2],
|
|
241
|
+
connectionInstanceId: parts[4]
|
|
242
|
+
};
|
|
202
243
|
}
|
|
203
244
|
function parseAgentEventSubject(subject) {
|
|
204
245
|
return parseAgentSubject(subject, "events");
|
|
@@ -294,19 +335,10 @@ async function ingestAttachedBrowserInventory(db, input) {
|
|
|
294
335
|
snapshot: wireAttachedBrowserInventoryToContract(input.inventory)
|
|
295
336
|
});
|
|
296
337
|
}
|
|
297
|
-
async function
|
|
298
|
-
const enrollment = await getEnrollment2(db, input.workspaceId, input.agentId);
|
|
299
|
-
if (!enrollment) {
|
|
300
|
-
return { ingested: false, seriesAppended: false };
|
|
301
|
-
}
|
|
338
|
+
async function ingestHeartbeatMetrics(db, input) {
|
|
302
339
|
const sample = wireSampleToDbSample(input.sample);
|
|
303
|
-
await touchEnrollmentLastSeen(db, {
|
|
304
|
-
accountId: enrollment.accountId,
|
|
305
|
-
workspaceId: input.workspaceId,
|
|
306
|
-
enrollmentId: input.agentId
|
|
307
|
-
});
|
|
308
340
|
const result = await ingestMachineMetricsSample(db, {
|
|
309
|
-
accountId:
|
|
341
|
+
accountId: input.accountId,
|
|
310
342
|
workspaceId: input.workspaceId,
|
|
311
343
|
enrollmentId: input.agentId,
|
|
312
344
|
sample
|
|
@@ -356,17 +388,19 @@ async function handleAgentEventPayload(db, observability, payload, subject, bus)
|
|
|
356
388
|
labels: { reason }
|
|
357
389
|
});
|
|
358
390
|
try {
|
|
359
|
-
const
|
|
360
|
-
if (
|
|
361
|
-
await
|
|
362
|
-
accountId:
|
|
391
|
+
const enrollment2 = await getEnrollment2(db, ids.workspaceId, ids.agentId);
|
|
392
|
+
if (enrollment2) {
|
|
393
|
+
const released = await releaseEnrollmentConnection(db, {
|
|
394
|
+
accountId: enrollment2.accountId,
|
|
363
395
|
workspaceId: ids.workspaceId,
|
|
364
396
|
enrollmentId: ids.agentId,
|
|
397
|
+
connectionInstanceId: ids.connectionInstanceId,
|
|
365
398
|
reason
|
|
366
399
|
});
|
|
400
|
+
if (!released.released) return;
|
|
367
401
|
try {
|
|
368
402
|
await disconnectAttachedBrowserDevices(db, {
|
|
369
|
-
accountId:
|
|
403
|
+
accountId: enrollment2.accountId,
|
|
370
404
|
workspaceId: ids.workspaceId,
|
|
371
405
|
enrollmentId: ids.agentId
|
|
372
406
|
});
|
|
@@ -408,10 +442,75 @@ async function handleAgentEventPayload(db, observability, payload, subject, bus)
|
|
|
408
442
|
}
|
|
409
443
|
return;
|
|
410
444
|
}
|
|
445
|
+
if (event.event?.$case === "agentUpdateProgress") {
|
|
446
|
+
const enrollment2 = await getEnrollment2(db, ids.workspaceId, ids.agentId).catch(() => null);
|
|
447
|
+
if (!enrollment2 || enrollment2.connectionInstanceId !== ids.connectionInstanceId) return;
|
|
448
|
+
const progress = event.event.agentUpdateProgress;
|
|
449
|
+
const status = (() => {
|
|
450
|
+
switch (progress.stage) {
|
|
451
|
+
case AgentUpdateStage.AGENT_UPDATE_STAGE_ACCEPTED:
|
|
452
|
+
return "accepted";
|
|
453
|
+
case AgentUpdateStage.AGENT_UPDATE_STAGE_WAITING_FOR_IDLE:
|
|
454
|
+
return "waiting_for_idle";
|
|
455
|
+
case AgentUpdateStage.AGENT_UPDATE_STAGE_DOWNLOADING:
|
|
456
|
+
return "downloading";
|
|
457
|
+
case AgentUpdateStage.AGENT_UPDATE_STAGE_VERIFYING:
|
|
458
|
+
return "verifying";
|
|
459
|
+
case AgentUpdateStage.AGENT_UPDATE_STAGE_APPLYING:
|
|
460
|
+
return "applying";
|
|
461
|
+
case AgentUpdateStage.AGENT_UPDATE_STAGE_RESTARTING:
|
|
462
|
+
return "restarting";
|
|
463
|
+
case AgentUpdateStage.AGENT_UPDATE_STAGE_FAILED:
|
|
464
|
+
return "failed";
|
|
465
|
+
default:
|
|
466
|
+
return null;
|
|
467
|
+
}
|
|
468
|
+
})();
|
|
469
|
+
if (!status) return;
|
|
470
|
+
try {
|
|
471
|
+
await advanceEnrollmentAgentUpdate(db, {
|
|
472
|
+
accountId: enrollment2.accountId,
|
|
473
|
+
workspaceId: ids.workspaceId,
|
|
474
|
+
enrollmentId: ids.agentId,
|
|
475
|
+
connectionInstanceId: ids.connectionInstanceId,
|
|
476
|
+
connectionGeneration: enrollment2.connectionGeneration,
|
|
477
|
+
operationId: progress.operationId,
|
|
478
|
+
status,
|
|
479
|
+
expectedBinarySha256: /^[0-9a-f]{64}$/.test(progress.expectedBinarySha256) ? progress.expectedBinarySha256 : null,
|
|
480
|
+
errorCode: progress.errorCode || null,
|
|
481
|
+
retryable: progress.retryable,
|
|
482
|
+
rolledBack: progress.rolledBack
|
|
483
|
+
});
|
|
484
|
+
} catch (error) {
|
|
485
|
+
observability?.warn?.("Failed to ingest a machine self-update progress event", {
|
|
486
|
+
subject,
|
|
487
|
+
error: error instanceof Error ? error.message : String(error)
|
|
488
|
+
});
|
|
489
|
+
}
|
|
490
|
+
return;
|
|
491
|
+
}
|
|
411
492
|
if (event.event?.$case !== "heartbeat") {
|
|
412
493
|
return;
|
|
413
494
|
}
|
|
414
495
|
const heartbeat = event.event.heartbeat;
|
|
496
|
+
const enrollment = await getEnrollment2(db, ids.workspaceId, ids.agentId).catch(() => null);
|
|
497
|
+
if (!enrollment || enrollment.connectionInstanceId !== ids.connectionInstanceId) return;
|
|
498
|
+
try {
|
|
499
|
+
const renewed = await renewEnrollmentConnection(db, {
|
|
500
|
+
accountId: enrollment.accountId,
|
|
501
|
+
workspaceId: ids.workspaceId,
|
|
502
|
+
enrollmentId: ids.agentId,
|
|
503
|
+
connectionInstanceId: ids.connectionInstanceId,
|
|
504
|
+
leaseMs: AGENT_CONNECTION_LEASE_MS
|
|
505
|
+
});
|
|
506
|
+
if (!renewed.renewed) return;
|
|
507
|
+
} catch (error) {
|
|
508
|
+
observability?.warn?.("Failed to renew a machine connection heartbeat", {
|
|
509
|
+
subject,
|
|
510
|
+
error: error instanceof Error ? error.message : String(error)
|
|
511
|
+
});
|
|
512
|
+
return;
|
|
513
|
+
}
|
|
415
514
|
if (heartbeat.attachedBrowserInventory) {
|
|
416
515
|
try {
|
|
417
516
|
await ingestAttachedBrowserInventory(db, {
|
|
@@ -429,7 +528,8 @@ async function handleAgentEventPayload(db, observability, payload, subject, bus)
|
|
|
429
528
|
const metrics = heartbeat.metrics;
|
|
430
529
|
if (!metrics) return;
|
|
431
530
|
try {
|
|
432
|
-
await
|
|
531
|
+
await ingestHeartbeatMetrics(db, {
|
|
532
|
+
accountId: enrollment.accountId,
|
|
433
533
|
workspaceId: ids.workspaceId,
|
|
434
534
|
agentId: ids.agentId,
|
|
435
535
|
sample: metrics
|
|
@@ -464,6 +564,33 @@ function helloDesktopUnavailableReason(hello) {
|
|
|
464
564
|
function helloReportsOpStream(hello) {
|
|
465
565
|
return hello.capabilities?.opStream === true;
|
|
466
566
|
}
|
|
567
|
+
function helloRuntimeCapabilities(hello) {
|
|
568
|
+
const caps = hello.capabilities;
|
|
569
|
+
if (!caps) return {};
|
|
570
|
+
return {
|
|
571
|
+
exec: caps.exec === true,
|
|
572
|
+
filesystem: caps.filesystem === true,
|
|
573
|
+
git: caps.git === true,
|
|
574
|
+
pty: caps.pty === true,
|
|
575
|
+
desktop: caps.desktop === true,
|
|
576
|
+
opStream: caps.opStream === true,
|
|
577
|
+
browserBridge: caps.browserBridge === true,
|
|
578
|
+
operationResourcePolicy: caps.operationResourcePolicy === true,
|
|
579
|
+
operationCpuQuota: caps.operationCpuQuota === true
|
|
580
|
+
};
|
|
581
|
+
}
|
|
582
|
+
function helloCompletedUpdate(hello) {
|
|
583
|
+
if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(
|
|
584
|
+
hello.completedUpdateOperationId
|
|
585
|
+
) || !/^(?:v)?\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/.test(hello.completedUpdateTargetVersion) || !/^[0-9a-f]{64}$/.test(hello.completedUpdateBinarySha256)) {
|
|
586
|
+
return null;
|
|
587
|
+
}
|
|
588
|
+
return {
|
|
589
|
+
operationId: hello.completedUpdateOperationId,
|
|
590
|
+
targetVersion: hello.completedUpdateTargetVersion,
|
|
591
|
+
binarySha256: hello.completedUpdateBinarySha256
|
|
592
|
+
};
|
|
593
|
+
}
|
|
467
594
|
async function refreshEnrollmentDisplay(db, input) {
|
|
468
595
|
const desktopUnavailableReason = input.desktopUnavailableReason ?? null;
|
|
469
596
|
const enrollment = await getEnrollment2(db, input.workspaceId, input.agentId);
|
|
@@ -478,7 +605,8 @@ async function refreshEnrollmentDisplay(db, input) {
|
|
|
478
605
|
workspaceId: input.workspaceId,
|
|
479
606
|
enrollmentId: input.agentId,
|
|
480
607
|
hasDisplay: input.hasDisplay,
|
|
481
|
-
desktopUnavailableReason
|
|
608
|
+
desktopUnavailableReason,
|
|
609
|
+
...input.connectionInstanceId ? { connectionInstanceId: input.connectionInstanceId } : {}
|
|
482
610
|
});
|
|
483
611
|
}
|
|
484
612
|
async function refreshEnrollmentOpStream(db, input) {
|
|
@@ -493,7 +621,8 @@ async function refreshEnrollmentOpStream(db, input) {
|
|
|
493
621
|
accountId: enrollment.accountId,
|
|
494
622
|
workspaceId: input.workspaceId,
|
|
495
623
|
enrollmentId: input.agentId,
|
|
496
|
-
opStream: input.opStream
|
|
624
|
+
opStream: input.opStream,
|
|
625
|
+
...input.connectionInstanceId ? { connectionInstanceId: input.connectionInstanceId } : {}
|
|
497
626
|
});
|
|
498
627
|
}
|
|
499
628
|
async function handleHelloPayload(db, observability, payload, subject, bus) {
|
|
@@ -511,17 +640,53 @@ async function handleHelloPayload(db, observability, payload, subject, bus) {
|
|
|
511
640
|
});
|
|
512
641
|
return;
|
|
513
642
|
}
|
|
643
|
+
const authority = await getLiveEnrollmentConnection(db, ids.workspaceId, ids.agentId).catch(
|
|
644
|
+
() => null
|
|
645
|
+
);
|
|
646
|
+
if (!authority || authority.connectionInstanceId !== ids.connectionInstanceId) {
|
|
647
|
+
observability?.warn?.("Ignored a Hello from a superseded runner instance", {
|
|
648
|
+
workspaceId: ids.workspaceId,
|
|
649
|
+
agentId: ids.agentId
|
|
650
|
+
});
|
|
651
|
+
return;
|
|
652
|
+
}
|
|
653
|
+
let workspaceRoot;
|
|
654
|
+
if (hello.workspaceRoot.length > 0) {
|
|
655
|
+
try {
|
|
656
|
+
workspaceRoot = normalizeConnectedMachineWorkspaceRoot(hello.workspaceRoot, authority.os);
|
|
657
|
+
} catch (error) {
|
|
658
|
+
observability?.warn?.("Ignored an invalid Connected Machine workspace root", {
|
|
659
|
+
workspaceId: ids.workspaceId,
|
|
660
|
+
agentId: ids.agentId,
|
|
661
|
+
error: error instanceof Error ? error.message : String(error)
|
|
662
|
+
});
|
|
663
|
+
}
|
|
664
|
+
}
|
|
514
665
|
try {
|
|
515
666
|
await refreshEnrollmentDisplay(db, {
|
|
516
667
|
workspaceId: ids.workspaceId,
|
|
517
668
|
agentId: ids.agentId,
|
|
518
669
|
hasDisplay: helloReportsDisplay(hello),
|
|
519
|
-
desktopUnavailableReason: helloDesktopUnavailableReason(hello)
|
|
670
|
+
desktopUnavailableReason: helloDesktopUnavailableReason(hello),
|
|
671
|
+
connectionInstanceId: ids.connectionInstanceId
|
|
520
672
|
});
|
|
521
673
|
await refreshEnrollmentOpStream(db, {
|
|
522
674
|
workspaceId: ids.workspaceId,
|
|
523
675
|
agentId: ids.agentId,
|
|
524
|
-
opStream: helloReportsOpStream(hello)
|
|
676
|
+
opStream: helloReportsOpStream(hello),
|
|
677
|
+
connectionInstanceId: ids.connectionInstanceId
|
|
678
|
+
});
|
|
679
|
+
await setEnrollmentAgentRuntime(db, {
|
|
680
|
+
accountId: authority.accountId,
|
|
681
|
+
workspaceId: ids.workspaceId,
|
|
682
|
+
enrollmentId: ids.agentId,
|
|
683
|
+
connectionInstanceId: ids.connectionInstanceId,
|
|
684
|
+
agentVersion: hello.agentVersion.trim() || null,
|
|
685
|
+
binarySha256: /^[0-9a-f]{64}$/.test(hello.binarySha256) ? hello.binarySha256 : null,
|
|
686
|
+
updateChannel: hello.updateChannel === "stable" || hello.updateChannel === "beta" ? hello.updateChannel : null,
|
|
687
|
+
capabilities: helloRuntimeCapabilities(hello),
|
|
688
|
+
...workspaceRoot !== void 0 ? { workspaceRoot } : {},
|
|
689
|
+
completedUpdate: helloCompletedUpdate(hello)
|
|
525
690
|
});
|
|
526
691
|
} catch (error) {
|
|
527
692
|
observability?.warn?.("Failed to refresh an enrollment's capabilities from a Hello", {
|
|
@@ -530,23 +695,21 @@ async function handleHelloPayload(db, observability, payload, subject, bus) {
|
|
|
530
695
|
});
|
|
531
696
|
}
|
|
532
697
|
try {
|
|
533
|
-
const
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
);
|
|
549
|
-
}
|
|
698
|
+
const { cleared } = await clearEnrollmentWentOffline(db, {
|
|
699
|
+
accountId: authority.accountId,
|
|
700
|
+
workspaceId: ids.workspaceId,
|
|
701
|
+
enrollmentId: ids.agentId,
|
|
702
|
+
connectionInstanceId: ids.connectionInstanceId
|
|
703
|
+
});
|
|
704
|
+
if (cleared && bus) {
|
|
705
|
+
await fanOutMachineLinkEvents(
|
|
706
|
+
db,
|
|
707
|
+
bus,
|
|
708
|
+
observability,
|
|
709
|
+
ids.workspaceId,
|
|
710
|
+
ids.agentId,
|
|
711
|
+
(activeTurnId) => [{ type: "machine.link.restored", turnId: activeTurnId, payload: {} }]
|
|
712
|
+
);
|
|
550
713
|
}
|
|
551
714
|
} catch (error) {
|
|
552
715
|
observability?.warn?.("Failed to clear a machine going-offline marker on a Hello", {
|
|
@@ -741,7 +904,7 @@ import {
|
|
|
741
904
|
encodeEditableArtifactLiveServerWireFrame
|
|
742
905
|
} from "@opengeni/core";
|
|
743
906
|
var EDITABLE_ARTIFACT_LIVE_WEBSOCKET_PATH = "/v1/editable-artifacts/live";
|
|
744
|
-
var EDITABLE_ARTIFACT_LIVE_WEBSOCKET_PROTOCOL = "opengeni-artifact-
|
|
907
|
+
var EDITABLE_ARTIFACT_LIVE_WEBSOCKET_PROTOCOL = "opengeni-artifact-v2";
|
|
745
908
|
var EDITABLE_ARTIFACT_LIVE_WEBSOCKET_MAX_MESSAGE_BYTES = 8 * 1024 * 1024 + 64 * 1024;
|
|
746
909
|
var MAX_QUEUED_MESSAGES = 64;
|
|
747
910
|
var MAX_QUEUED_BYTES = 12 * 1024 * 1024;
|
|
@@ -999,7 +1162,6 @@ import {
|
|
|
999
1162
|
editableArtifactId
|
|
1000
1163
|
} from "@opengeni/core/editable-artifacts";
|
|
1001
1164
|
import {
|
|
1002
|
-
EDITABLE_ARTIFACT_LIVE_PROTOCOL_VERSION,
|
|
1003
1165
|
EditableArtifactApplication,
|
|
1004
1166
|
EditableArtifactCompatibilityError,
|
|
1005
1167
|
EditableArtifactLiveServer,
|
|
@@ -1007,6 +1169,7 @@ import {
|
|
|
1007
1169
|
SystemEditableArtifactLiveScheduler,
|
|
1008
1170
|
WebCryptoEditableArtifactLiveTokens
|
|
1009
1171
|
} from "@opengeni/core/editable-artifact-live";
|
|
1172
|
+
import { currentEditableArtifactCompatibility } from "@opengeni/contracts/editable-artifacts";
|
|
1010
1173
|
import {
|
|
1011
1174
|
PostgresEditableArtifactLiveReadStore,
|
|
1012
1175
|
PostgresEditableArtifactLiveTicketStore,
|
|
@@ -1093,6 +1256,13 @@ import {
|
|
|
1093
1256
|
} from "@opengeni/contracts/editable-artifact-causal-frontier";
|
|
1094
1257
|
import { decodeEditableArtifactSerializedCommit } from "@opengeni/contracts/editable-artifact-serialized-commit";
|
|
1095
1258
|
import { decodeEditableArtifactMutationIntent } from "@opengeni/contracts/editable-artifacts";
|
|
1259
|
+
import {
|
|
1260
|
+
COMMITTED_TRANSACTION_PROTOCOL_VERSION,
|
|
1261
|
+
DOCUMENT_ARTIFACT_MODEL_SCHEMA_VERSION,
|
|
1262
|
+
PRESENTATION_ARTIFACT_MODEL_SCHEMA_VERSION,
|
|
1263
|
+
SPREADSHEET_ARTIFACT_MODEL_SCHEMA_VERSION,
|
|
1264
|
+
SPREADSHEET_COLLABORATION_SNAPSHOT_VERSION
|
|
1265
|
+
} from "@opengeni/contracts/editable-artifact-versions";
|
|
1096
1266
|
import {
|
|
1097
1267
|
MAX_EDITABLE_ARTIFACT_SNAPSHOT_BYTES,
|
|
1098
1268
|
causalFrontiersEqual,
|
|
@@ -1101,9 +1271,6 @@ import {
|
|
|
1101
1271
|
editableArtifactReplicaId,
|
|
1102
1272
|
editableArtifactStateHash
|
|
1103
1273
|
} from "@opengeni/core";
|
|
1104
|
-
var MODEL_SCHEMA_VERSION = 1;
|
|
1105
|
-
var OPERATION_PROTOCOL_VERSION = 1;
|
|
1106
|
-
var CRDT_STATE_VERSION = 1;
|
|
1107
1274
|
var SNAPSHOT_CHUNK_BYTES = 1024 * 1024;
|
|
1108
1275
|
async function loadVerifiedNativeArtifactRuntimeBinding() {
|
|
1109
1276
|
const location = await doctorConfiguredArtifactRuntime();
|
|
@@ -1136,7 +1303,7 @@ var NativeEditableArtifactKernelAdapter = class {
|
|
|
1136
1303
|
modality: "spreadsheet",
|
|
1137
1304
|
committedTransactionBytes,
|
|
1138
1305
|
kernelVersion: this.runtime.buildIdentity,
|
|
1139
|
-
modelSchemaVersion:
|
|
1306
|
+
modelSchemaVersion: SPREADSHEET_ARTIFACT_MODEL_SCHEMA_VERSION
|
|
1140
1307
|
});
|
|
1141
1308
|
}
|
|
1142
1309
|
if (session.modality !== request.modality) {
|
|
@@ -1148,7 +1315,7 @@ var NativeEditableArtifactKernelAdapter = class {
|
|
|
1148
1315
|
nativeReceiptBytes,
|
|
1149
1316
|
resultingStateHash: editableArtifactStateHash(session.stateHash()),
|
|
1150
1317
|
kernelVersion: this.runtime.buildIdentity,
|
|
1151
|
-
modelSchemaVersion:
|
|
1318
|
+
modelSchemaVersion: modelSchemaVersionFor(request.modality)
|
|
1152
1319
|
});
|
|
1153
1320
|
} finally {
|
|
1154
1321
|
session.dispose();
|
|
@@ -1202,7 +1369,7 @@ var NativeEditableArtifactKernelAdapter = class {
|
|
|
1202
1369
|
),
|
|
1203
1370
|
stateHash: editableArtifactStateHash(session.stateHash()),
|
|
1204
1371
|
coveredHeadSequence: 0,
|
|
1205
|
-
modelSchemaVersion:
|
|
1372
|
+
modelSchemaVersion: modelSchemaVersionFor(input.modality),
|
|
1206
1373
|
kernelVersion: this.runtime.buildIdentity
|
|
1207
1374
|
};
|
|
1208
1375
|
if (session.modality === "spreadsheet") {
|
|
@@ -1210,8 +1377,8 @@ var NativeEditableArtifactKernelAdapter = class {
|
|
|
1210
1377
|
...common,
|
|
1211
1378
|
modality: "spreadsheet",
|
|
1212
1379
|
coveredCausalFrontier: domainFrontier(session.frontier()),
|
|
1213
|
-
operationProtocolVersion:
|
|
1214
|
-
crdtStateVersion:
|
|
1380
|
+
operationProtocolVersion: COMMITTED_TRANSACTION_PROTOCOL_VERSION,
|
|
1381
|
+
crdtStateVersion: SPREADSHEET_COLLABORATION_SNAPSHOT_VERSION
|
|
1215
1382
|
});
|
|
1216
1383
|
}
|
|
1217
1384
|
return Object.freeze({
|
|
@@ -1249,7 +1416,7 @@ var NativeEditableArtifactKernelAdapter = class {
|
|
|
1249
1416
|
),
|
|
1250
1417
|
stateHash: editableArtifactStateHash(session.stateHash()),
|
|
1251
1418
|
coveredHeadSequence: input.state.artifact.headSequence,
|
|
1252
|
-
modelSchemaVersion:
|
|
1419
|
+
modelSchemaVersion: modelSchemaVersionFor(input.state.modality),
|
|
1253
1420
|
kernelVersion: this.runtime.buildIdentity
|
|
1254
1421
|
};
|
|
1255
1422
|
if (session.modality === "spreadsheet") {
|
|
@@ -1258,8 +1425,8 @@ var NativeEditableArtifactKernelAdapter = class {
|
|
|
1258
1425
|
...common,
|
|
1259
1426
|
modality: session.modality,
|
|
1260
1427
|
coveredCausalFrontier: domainFrontier(session.frontier()),
|
|
1261
|
-
operationProtocolVersion:
|
|
1262
|
-
crdtStateVersion:
|
|
1428
|
+
operationProtocolVersion: COMMITTED_TRANSACTION_PROTOCOL_VERSION,
|
|
1429
|
+
crdtStateVersion: SPREADSHEET_COLLABORATION_SNAPSHOT_VERSION
|
|
1263
1430
|
});
|
|
1264
1431
|
}
|
|
1265
1432
|
if (session.modality !== input.state.modality) throw new Error("Native modality mismatch");
|
|
@@ -1312,12 +1479,12 @@ var NativeEditableArtifactKernelAdapter = class {
|
|
|
1312
1479
|
canonicalContentHash,
|
|
1313
1480
|
coveredHeadSequence,
|
|
1314
1481
|
stateHash,
|
|
1315
|
-
modelSchemaVersion:
|
|
1482
|
+
modelSchemaVersion: SPREADSHEET_ARTIFACT_MODEL_SCHEMA_VERSION,
|
|
1316
1483
|
kernelVersion: this.runtime.buildIdentity,
|
|
1317
1484
|
fullCrdtStateVerified: true,
|
|
1318
1485
|
coveredCausalFrontier,
|
|
1319
|
-
operationProtocolVersion:
|
|
1320
|
-
crdtStateVersion:
|
|
1486
|
+
operationProtocolVersion: COMMITTED_TRANSACTION_PROTOCOL_VERSION,
|
|
1487
|
+
crdtStateVersion: SPREADSHEET_COLLABORATION_SNAPSHOT_VERSION,
|
|
1321
1488
|
...replayedTarget2 ? { replayedTarget: replayedTarget2 } : {}
|
|
1322
1489
|
});
|
|
1323
1490
|
}
|
|
@@ -1339,7 +1506,7 @@ var NativeEditableArtifactKernelAdapter = class {
|
|
|
1339
1506
|
canonicalContentHash,
|
|
1340
1507
|
coveredHeadSequence,
|
|
1341
1508
|
stateHash,
|
|
1342
|
-
modelSchemaVersion:
|
|
1509
|
+
modelSchemaVersion: modelSchemaVersionFor(session.modality),
|
|
1343
1510
|
kernelVersion: this.runtime.buildIdentity,
|
|
1344
1511
|
nativeRevision,
|
|
1345
1512
|
...replayedTarget ? { replayedTarget } : {}
|
|
@@ -1349,6 +1516,10 @@ var NativeEditableArtifactKernelAdapter = class {
|
|
|
1349
1516
|
}
|
|
1350
1517
|
}
|
|
1351
1518
|
};
|
|
1519
|
+
function modelSchemaVersionFor(modality) {
|
|
1520
|
+
if (modality === "spreadsheet") return SPREADSHEET_ARTIFACT_MODEL_SCHEMA_VERSION;
|
|
1521
|
+
return modality === "document" ? DOCUMENT_ARTIFACT_MODEL_SCHEMA_VERSION : PRESENTATION_ARTIFACT_MODEL_SCHEMA_VERSION;
|
|
1522
|
+
}
|
|
1352
1523
|
function assertRuntime(candidate, location) {
|
|
1353
1524
|
if (typeof candidate !== "object" || candidate === null) {
|
|
1354
1525
|
throw new Error("Verified artifact facade returned no runtime");
|
|
@@ -1581,7 +1752,8 @@ import {
|
|
|
1581
1752
|
import { requireLiveAgentAttemptAuthorization } from "@opengeni/core";
|
|
1582
1753
|
import { getFilesForSubject } from "@opengeni/db";
|
|
1583
1754
|
import {
|
|
1584
|
-
MAX_BOUNDED_OBJECT_CHUNK_BYTES
|
|
1755
|
+
MAX_BOUNDED_OBJECT_CHUNK_BYTES,
|
|
1756
|
+
retryWhileMissing
|
|
1585
1757
|
} from "@opengeni/storage";
|
|
1586
1758
|
var SNAPSHOT_MIME = "application/vnd.opengeni.editable-artifact-snapshot";
|
|
1587
1759
|
var EditableArtifactOfficeImportAdapter = class {
|
|
@@ -1712,7 +1884,19 @@ function officeFilenameMatches(filename, modality) {
|
|
|
1712
1884
|
}
|
|
1713
1885
|
async function readVerifiedWorkspaceFile(storage, file, signal) {
|
|
1714
1886
|
throwIfAborted2(signal);
|
|
1715
|
-
const before = await
|
|
1887
|
+
const before = await retryWhileMissing(async () => {
|
|
1888
|
+
try {
|
|
1889
|
+
return await storage.headFile(file);
|
|
1890
|
+
} catch (error) {
|
|
1891
|
+
if (typeof storage.fileExists === "function" && !await storage.fileExists(file)) {
|
|
1892
|
+
return null;
|
|
1893
|
+
}
|
|
1894
|
+
throw error;
|
|
1895
|
+
}
|
|
1896
|
+
});
|
|
1897
|
+
if (!before) {
|
|
1898
|
+
throw new EditableArtifactOfficeImportError("source_changed");
|
|
1899
|
+
}
|
|
1716
1900
|
if (before.ContentLength !== file.sizeBytes || before.ContentType !== file.contentType || before.Metadata?.sha256 !== file.sha256 || typeof before.VersionToken !== "string" || before.VersionToken.length < 1) {
|
|
1717
1901
|
throw new EditableArtifactOfficeImportError("source_changed");
|
|
1718
1902
|
}
|
|
@@ -1721,14 +1905,26 @@ async function readVerifiedWorkspaceFile(storage, file, signal) {
|
|
|
1721
1905
|
for (let start = 0; start < file.sizeBytes; start += MAX_BOUNDED_OBJECT_CHUNK_BYTES) {
|
|
1722
1906
|
throwIfAborted2(signal);
|
|
1723
1907
|
const end = Math.min(file.sizeBytes, start + MAX_BOUNDED_OBJECT_CHUNK_BYTES) - 1;
|
|
1724
|
-
const chunk = await storage.getFileRange(file, { start, end });
|
|
1908
|
+
const chunk = await retryWhileMissing(async () => storage.getFileRange(file, { start, end }));
|
|
1725
1909
|
if (!chunk || chunk.byteLength !== end - start + 1) {
|
|
1726
1910
|
throw new EditableArtifactOfficeImportError("source_changed");
|
|
1727
1911
|
}
|
|
1728
1912
|
bytes.set(chunk, start);
|
|
1729
1913
|
digest.update(chunk);
|
|
1730
1914
|
}
|
|
1731
|
-
const after = await
|
|
1915
|
+
const after = await retryWhileMissing(async () => {
|
|
1916
|
+
try {
|
|
1917
|
+
return await storage.headFile(file);
|
|
1918
|
+
} catch (error) {
|
|
1919
|
+
if (typeof storage.fileExists === "function" && !await storage.fileExists(file)) {
|
|
1920
|
+
return null;
|
|
1921
|
+
}
|
|
1922
|
+
throw error;
|
|
1923
|
+
}
|
|
1924
|
+
});
|
|
1925
|
+
if (!after) {
|
|
1926
|
+
throw new EditableArtifactOfficeImportError("source_changed");
|
|
1927
|
+
}
|
|
1732
1928
|
const sha256 = digest.digest("hex");
|
|
1733
1929
|
if (after.ContentLength !== before.ContentLength || after.ContentType !== before.ContentType || after.Metadata?.sha256 !== before.Metadata?.sha256 || after.VersionToken !== before.VersionToken || sha256 !== file.sha256) {
|
|
1734
1930
|
throw new EditableArtifactOfficeImportError("source_changed");
|
|
@@ -1768,6 +1964,7 @@ function throwIfAborted2(signal) {
|
|
|
1768
1964
|
// src/editable-artifact-workspace-files.ts
|
|
1769
1965
|
import { createHash as createHash3 } from "crypto";
|
|
1770
1966
|
import { completeFileUpload, prepareGeneratedWorkspaceFile } from "@opengeni/db";
|
|
1967
|
+
import { retryWhileMissing as retryWhileMissing2 } from "@opengeni/storage";
|
|
1771
1968
|
var UPLOAD_INTENT_TTL_MS = 60 * 6e4;
|
|
1772
1969
|
var UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/u;
|
|
1773
1970
|
var EditableArtifactWorkspaceFileAdapter = class {
|
|
@@ -1824,7 +2021,7 @@ var EditableArtifactWorkspaceFileAdapter = class {
|
|
|
1824
2021
|
if (existing) {
|
|
1825
2022
|
assertStoredExport(existing, download.byteSize, download.mimeType, sha256);
|
|
1826
2023
|
} else {
|
|
1827
|
-
await storage.putObjectStreamIfAbsent({
|
|
2024
|
+
const created = await storage.putObjectStreamIfAbsent({
|
|
1828
2025
|
key: objectKey,
|
|
1829
2026
|
contentType: download.mimeType,
|
|
1830
2027
|
chunks: download.chunks(input.signal ? { signal: input.signal } : {}),
|
|
@@ -1832,18 +2029,28 @@ var EditableArtifactWorkspaceFileAdapter = class {
|
|
|
1832
2029
|
sha256,
|
|
1833
2030
|
...input.signal ? { signal: input.signal } : {}
|
|
1834
2031
|
});
|
|
1835
|
-
|
|
1836
|
-
|
|
2032
|
+
if (!created) {
|
|
2033
|
+
await assertVisibleExport(
|
|
2034
|
+
storage,
|
|
2035
|
+
objectKey,
|
|
2036
|
+
download.byteSize,
|
|
2037
|
+
download.mimeType,
|
|
2038
|
+
sha256,
|
|
2039
|
+
input.signal
|
|
2040
|
+
);
|
|
2041
|
+
}
|
|
1837
2042
|
}
|
|
1838
2043
|
await download.assertUnchanged(input.signal);
|
|
1839
2044
|
throwIfAborted3(input.signal);
|
|
1840
2045
|
file = await this.completeFile(this.dependencies.db, input.scope.workspaceId, uploadId);
|
|
1841
2046
|
} else {
|
|
1842
|
-
|
|
1843
|
-
|
|
2047
|
+
await assertVisibleExport(
|
|
2048
|
+
storage,
|
|
2049
|
+
objectKey,
|
|
1844
2050
|
download.byteSize,
|
|
1845
2051
|
download.mimeType,
|
|
1846
|
-
sha256
|
|
2052
|
+
sha256,
|
|
2053
|
+
input.signal
|
|
1847
2054
|
);
|
|
1848
2055
|
await download.assertUnchanged(input.signal);
|
|
1849
2056
|
}
|
|
@@ -1867,6 +2074,13 @@ var EditableArtifactWorkspaceFileAdapter = class {
|
|
|
1867
2074
|
}
|
|
1868
2075
|
}
|
|
1869
2076
|
};
|
|
2077
|
+
async function assertVisibleExport(storage, objectKey, byteSize, contentType, sha256, signal) {
|
|
2078
|
+
const head = await retryWhileMissing2(
|
|
2079
|
+
async () => await storage.headObject(objectKey),
|
|
2080
|
+
signal ? { signal } : {}
|
|
2081
|
+
);
|
|
2082
|
+
assertStoredExport(head, byteSize, contentType, sha256);
|
|
2083
|
+
}
|
|
1870
2084
|
function assertStoredExport(head, byteSize, contentType, sha256) {
|
|
1871
2085
|
if (!head || head.ContentLength !== byteSize || head.ContentType !== contentType || head.Metadata?.sha256 !== sha256 || typeof head.VersionToken !== "string" || head.VersionToken.length < 1) {
|
|
1872
2086
|
throw new Error("Workspace export object differs from its durable materialization");
|
|
@@ -1998,7 +2212,8 @@ async function createStandaloneEditableArtifactApplication(input) {
|
|
|
1998
2212
|
});
|
|
1999
2213
|
const compatibility = Object.freeze({
|
|
2000
2214
|
assertCompatible(request) {
|
|
2001
|
-
|
|
2215
|
+
const current = currentEditableArtifactCompatibility(request.artifact.modality);
|
|
2216
|
+
if (request.modality !== request.artifact.modality || request.liveProtocolVersion !== current.liveProtocolVersion || request.modelSchemaVersion !== current.modelSchemaVersion || request.snapshotVersion !== current.snapshotVersion || request.commandProtocolVersion !== current.commandProtocolVersion || request.committedTransactionProtocolVersion !== current.committedTransactionProtocolVersion) {
|
|
2002
2217
|
throw new EditableArtifactCompatibilityError();
|
|
2003
2218
|
}
|
|
2004
2219
|
}
|
|
@@ -2117,7 +2332,8 @@ var RandomEditableArtifactDurableExportIds = class {
|
|
|
2117
2332
|
}
|
|
2118
2333
|
};
|
|
2119
2334
|
function materializationProfiles(capabilities) {
|
|
2120
|
-
|
|
2335
|
+
const spreadsheetCompatibility = currentEditableArtifactCompatibility("spreadsheet");
|
|
2336
|
+
if (capabilities.protocol !== "OGAMC001" || capabilities.runtimeKind !== "native" || capabilities.maxOutputBytes !== EDITABLE_ARTIFACT_EXPORT_MAX_DOWNLOAD_BYTES || capabilities.codecVersions["opengeni.xlsx"] === void 0 || capabilities.supportedModelSchemaVersions[0] !== spreadsheetCompatibility.modelSchemaVersion || capabilities.supportedOperationProtocolVersions[0] !== spreadsheetCompatibility.committedTransactionProtocolVersion || capabilities.supportedSnapshotProtocolVersions[0] !== spreadsheetCompatibility.snapshotVersion) {
|
|
2121
2337
|
throw new Error("Verified artifact materializer profile is incompatible");
|
|
2122
2338
|
}
|
|
2123
2339
|
const codecVersion = capabilities.codecVersions["opengeni.xlsx"];
|
|
@@ -2207,6 +2423,19 @@ async function createTemporalWorkflowClient(settings, db) {
|
|
|
2207
2423
|
namespace: settings.temporalNamespace
|
|
2208
2424
|
});
|
|
2209
2425
|
const client = {
|
|
2426
|
+
triggerAutomationRun: async ({ accountId, workspaceId, runId }) => {
|
|
2427
|
+
try {
|
|
2428
|
+
await temporal.workflow.start("automationRunWorkflow", {
|
|
2429
|
+
taskQueue: settings.temporalTaskQueue,
|
|
2430
|
+
workflowId: `automation-run:${runId}`,
|
|
2431
|
+
workflowIdReusePolicy: "REJECT_DUPLICATE",
|
|
2432
|
+
args: [{ accountId, workspaceId, runId }]
|
|
2433
|
+
});
|
|
2434
|
+
} catch (error) {
|
|
2435
|
+
if (isWorkflowAlreadyStarted(error)) return;
|
|
2436
|
+
throw error;
|
|
2437
|
+
}
|
|
2438
|
+
},
|
|
2210
2439
|
signalUserMessage: async ({ eventId, workflowId }) => {
|
|
2211
2440
|
await temporal.workflow.getHandle(workflowId).signal("userMessage", eventId);
|
|
2212
2441
|
},
|
|
@@ -2396,6 +2625,7 @@ async function startApi() {
|
|
|
2396
2625
|
const observability = createObservability(settings, { component: "api" });
|
|
2397
2626
|
const searchPath = dbSearchPath(settings);
|
|
2398
2627
|
const dbClient = createDb(settings.databaseUrl, {
|
|
2628
|
+
max: 32,
|
|
2399
2629
|
...searchPath ? { searchPath } : {},
|
|
2400
2630
|
rlsStrategy: settings.rlsStrategy
|
|
2401
2631
|
});
|
|
@@ -2406,10 +2636,17 @@ async function startApi() {
|
|
|
2406
2636
|
const databasePosture = {
|
|
2407
2637
|
rlsStrategy: settings.rlsStrategy,
|
|
2408
2638
|
expectedRole: settings.runtimeDatabaseRole,
|
|
2409
|
-
targetSchema: settings.dbSchema.trim() || "public"
|
|
2639
|
+
targetSchema: settings.dbSchema.trim() || "public",
|
|
2640
|
+
organizationTenancyCanonicalActivationEnabled: settings.organizationTenancyCanonicalActivationEnabled
|
|
2410
2641
|
};
|
|
2411
2642
|
const controlPlaneAuth = resolveNatsControlPlaneAuth(settings);
|
|
2412
2643
|
try {
|
|
2644
|
+
const { ensureModalRegistryImage } = await import("@opengeni/runtime/sandbox");
|
|
2645
|
+
await retryStartupDependency(
|
|
2646
|
+
"Modal private-registry image",
|
|
2647
|
+
() => ensureModalRegistryImage(settings),
|
|
2648
|
+
{ ...retryOptions, onRetry }
|
|
2649
|
+
);
|
|
2413
2650
|
await retryStartupDependency(
|
|
2414
2651
|
"PostgreSQL runtime posture",
|
|
2415
2652
|
() => assertRuntimeDatabasePosture(dbClient.db, databasePosture),
|
|
@@ -2482,11 +2719,17 @@ async function startApi() {
|
|
|
2482
2719
|
});
|
|
2483
2720
|
}
|
|
2484
2721
|
const artifactWebSockets = new EditableArtifactWebSocketTransport(routeDeps.editableArtifacts);
|
|
2722
|
+
const interactionFrameProxies = new InteractionFrameProxyTransport(
|
|
2723
|
+
resolveFirstPartyDelegationSecret(settings)
|
|
2724
|
+
);
|
|
2485
2725
|
const server = Bun.serve({
|
|
2486
2726
|
hostname: settings.apiHost,
|
|
2487
2727
|
port: settings.apiPort,
|
|
2488
2728
|
idleTimeout: 255,
|
|
2489
2729
|
fetch: (request, bunServer) => {
|
|
2730
|
+
if (interactionFrameProxies.handles(request)) {
|
|
2731
|
+
return interactionFrameProxies.upgrade(request, bunServer);
|
|
2732
|
+
}
|
|
2490
2733
|
if (artifactWebSockets.handles(request)) {
|
|
2491
2734
|
return artifactWebSockets.upgrade(request, bunServer);
|
|
2492
2735
|
}
|
|
@@ -2496,15 +2739,16 @@ async function startApi() {
|
|
|
2496
2739
|
maxPayloadLength: EDITABLE_ARTIFACT_LIVE_WEBSOCKET_MAX_MESSAGE_BYTES,
|
|
2497
2740
|
backpressureLimit: 16 * 1024 * 1024,
|
|
2498
2741
|
closeOnBackpressureLimit: true,
|
|
2499
|
-
open: (socket) =>
|
|
2500
|
-
message: (socket, message) =>
|
|
2501
|
-
close: (socket) =>
|
|
2742
|
+
open: (socket) => socket.data.attach(socket),
|
|
2743
|
+
message: (socket, message) => socket.data.receive(message),
|
|
2744
|
+
close: (socket) => socket.data.transportClosed()
|
|
2502
2745
|
}
|
|
2503
2746
|
});
|
|
2504
2747
|
const stopSlackInteractionPump = settings.slackSigningSecret ? startSlackInteractionPump(routeDeps) : void 0;
|
|
2505
2748
|
const stopMemorySlackPublicationPump = startMemorySlackPublicationPump(routeDeps);
|
|
2506
2749
|
const stopTemporalScheduleCleanupPump = startTemporalScheduleCleanupPump({
|
|
2507
2750
|
db: dbClient.db,
|
|
2751
|
+
cleanupConnectorAuthorization: async (claim) => await cleanupScheduledTaskConnectorAuthorization(routeDeps, claim),
|
|
2508
2752
|
deleteSchedule: async (temporalScheduleId) => {
|
|
2509
2753
|
await workflowClient.client.deleteScheduledTaskSchedule({ temporalScheduleId });
|
|
2510
2754
|
},
|