@opengeni/runtime 0.5.0 → 0.6.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.
@@ -1,6 +1,6 @@
1
1
  export { collectSandboxEnvironment, parseExposedPorts } from '@opengeni/config';
2
2
  export { CAPABILITY_DESCRIPTORS, CapabilityDescriptor, DESKTOP_STREAM_PORT, StreamTokenPayload, StreamTokenPayload as StreamTokenPayloadType, TERMINAL_STREAM_PORT } from '@opengeni/contracts';
3
3
  import '@openai/agents/sandbox';
4
- export { A as ActiveBackendResolverDeps, a as ActiveBackendUnresolvableError, b as ActivePointer, C as ChannelAConflictError, c as ChannelAEmitter, d as ChannelAExecArgs, e as ChannelAExecResult, f as ChannelANotFoundError, g as ChannelASession, h as ChannelAUnsupportedError, i as ChannelAValidationError, j as ControlRpc, D as DEFAULT_DESKTOP_GEOMETRY, k as DISPLAY_STACK_TIMEOUT_MS, l as DesktopGeometry, m as DisplayStackError, n as DisplayStackUnsupportedError, E as EnsureDisplayStackOptions, o as EnsureDisplayStackResult, p as EnsureTerminalServerOptions, q as EnsureTerminalServerResult, r as EstablishedSandboxSession, s as ExposeStreamPortInput, t as ExposeStreamPortResult, u as ExposedPortEndpoint, F as FinalizeRecordingResult, L as LiveModalSandboxLeaseAttribution, M as MintStreamTokenInput, v as MockAgentResponder, w as MockAgentResponderOptions, x as MockExecHandler, y as ModalOrphanSweepResult, z as ModalSandboxAttribution, N as NatsControlRpc, B as NatsRequestConnection, G as NegotiationContext, H as NumstatEntry, P as PROVIDER_REGISTRY, I as ProviderConstructionContext, J as ProviderRegistration, K as RecordingCodec, O as RecordingContentType, Q as RecordingError, S as RecordingProcess, T as RecordingUnavailableError, U as ResolvedActiveBackend, V as RoutableBackendSession, W as RoutableSandbox, X as RoutingSandboxSession, Y as RoutingSandboxSessionDeps, Z as RoutingTransitionEvent, _ as RoutingUnsupportedError, $ as SELFHOSTED_DEFAULT_TIMEOUT_MS, a0 as SELFHOSTED_RECONNECT_WINDOW_MS, a1 as SELFHOSTED_RELAY_STREAM_PATH, a2 as STREAM_PORT, a3 as STREAM_TOKEN_DEFAULT_TTL_SECONDS, a4 as SandboxChannelAService, a5 as SandboxChannelAServiceOptions, a6 as SandboxConfigError, a7 as SandboxCreatedCallback, a8 as SandboxProviderUnavailableError, a9 as SelfhostedApplyDiff, aa as SelfhostedControlError, ab as SelfhostedEditor, ac as SelfhostedEnrollment, ad as SelfhostedExecArgs, ae as SelfhostedExecResult, af as SelfhostedImageOutput, ag as SelfhostedLivenessState, ah as SelfhostedNegotiationInput, ai as SelfhostedRelayConfig, aj as SelfhostedSandboxClient, ak as SelfhostedSession, al as SelfhostedSessionBuild, am as SelfhostedSessionDeps, an as SelfhostedSessionState, ao as SelfhostedUnavailableReason, ap as StartRecordingInput, aq as StreamPortUnavailableError, ar as TERMINAL_SERVER_TIMEOUT_MS, as as TerminalServerError, at as TerminalServerUnsupportedError, au as agentErrorToControlError, av as assertDescriptorRegistryInvariants, aw as assertProviderRegistryInvariants, ax as assertSafeRelPath, ay as backendSupportsOs, az as buildDisplayStackScript, aA as buildSelfhostedBackendSession, aB as buildStreamUrl, aC as buildTerminalServerScript, aD as contentTypeForCodec, aE as createSandboxClient, aF as createSandboxClientForBackend, aG as decodeModalSnapshotId, aH as deletePriorPersistedSnapshot, aI as deleteRecordingArtifacts, aJ as deserializeSandboxSessionStateEnvelope, aK as desktopCapableBackend, aL as ensureDisplayStack, aM as ensureTerminalServer, aN as establishSandboxSessionFromEnvelope, aO as exposeStreamPort, aP as extForCodec, aQ as isExecSessionLostBanner, aR as isProviderSandboxNotFoundError, aS as isSelfhostedProviderNotFoundError, aT as isWorkspaceEscapeError, aU as makeActiveBackendResolver, aV as mintStreamToken, aW as modalSandboxAttributionEnvironment, aX as modalSandboxAttributionTags, aY as negotiateCapabilities, aZ as negotiateSelfhostedCapabilities, a_ as offlineAgentError, a$ as offlineControlResponse, b0 as parseExecBannerSessionId, b1 as parseNumstatZ, b2 as parsePorcelainV2, b3 as parseUnifiedPatch, b4 as readRecordingBytes, b5 as readWorkspaceArchiveFromEnvelopeSessionState, b6 as recordingStorageKey, b7 as restoredSandboxSessionStateFromEntry, b8 as sandboxStateEntryFromRunState, b9 as selectBackend, ba as selfhostedLiveness, bb as serializeEstablishedSandboxEnvelope, bc as setSelfhostedApplyDiff, bd as startRecording, be as stopRecording, bf as stripExecBanner, bg as subjectFor, bh as sweepModalOrphanSandboxes, bi as tagModalSandbox, bj as tearDownDisplayStack, bk as tearDownTerminalServer, bl as terminateModalSandboxById, bm as timeoutAgentError, bn as timeoutControlResponse, bo as verifyStreamToken } from '../index-BrdnxaAW.js';
4
+ export { A as ActiveBackendResolverDeps, a as ActiveBackendUnresolvableError, b as ActivePointer, C as ChannelAConflictError, c as ChannelAEmitter, d as ChannelAExecArgs, e as ChannelAExecResult, f as ChannelANotFoundError, g as ChannelASession, h as ChannelAUnsupportedError, i as ChannelAValidationError, j as ControlRpc, D as DEFAULT_DESKTOP_GEOMETRY, k as DISPLAY_STACK_TIMEOUT_MS, l as DesktopGeometry, m as DisplayStackError, n as DisplayStackUnsupportedError, E as EnsureDisplayStackOptions, o as EnsureDisplayStackResult, p as EnsureTerminalServerOptions, q as EnsureTerminalServerResult, r as EstablishedSandboxSession, s as ExposeStreamPortInput, t as ExposeStreamPortResult, u as ExposedPortEndpoint, F as FinalizeRecordingResult, L as LiveModalSandboxLeaseAttribution, M as MintStreamTokenInput, v as MockAgentResponder, w as MockAgentResponderOptions, x as MockExecHandler, y as ModalModuleLoader, z as ModalOrphanSweepResult, B as ModalSandboxAttribution, N as NatsControlRpc, G as NatsRequestConnection, H as NegotiationContext, I as NumstatEntry, P as PROVIDER_REGISTRY, J as ProviderConstructionContext, K as ProviderRegistration, O as RecordingCodec, Q as RecordingContentType, S as RecordingError, T as RecordingProcess, U as RecordingUnavailableError, V as ResolvedActiveBackend, W as RoutableBackendSession, X as RoutableSandbox, Y as RoutingSandboxSession, Z as RoutingSandboxSessionDeps, _ as RoutingTransitionEvent, $ as RoutingUnsupportedError, a0 as SELFHOSTED_DEFAULT_TIMEOUT_MS, a1 as SELFHOSTED_RECONNECT_WINDOW_MS, a2 as SELFHOSTED_RELAY_STREAM_PATH, a3 as STREAM_PORT, a4 as STREAM_TOKEN_DEFAULT_TTL_SECONDS, a5 as SandboxChannelAService, a6 as SandboxChannelAServiceOptions, a7 as SandboxConfigError, a8 as SandboxCreatedCallback, a9 as SandboxProviderUnavailableError, aa as SelfhostedApplyDiff, ab as SelfhostedControlError, ac as SelfhostedEditor, ad as SelfhostedEnrollment, ae as SelfhostedExecArgs, af as SelfhostedExecResult, ag as SelfhostedImageOutput, ah as SelfhostedLivenessState, ai as SelfhostedNegotiationInput, aj as SelfhostedRelayConfig, ak as SelfhostedSandboxClient, al as SelfhostedSession, am as SelfhostedSessionBuild, an as SelfhostedSessionDeps, ao as SelfhostedSessionState, ap as SelfhostedUnavailableReason, aq as StartRecordingInput, ar as StreamPortUnavailableError, as as TERMINAL_SERVER_TIMEOUT_MS, at as TerminalServerError, au as TerminalServerUnsupportedError, av as agentErrorToControlError, aw as assertDescriptorRegistryInvariants, ax as assertProviderRegistryInvariants, ay as assertSafeRelPath, az as backendSupportsOs, aA as buildDisplayStackScript, aB as buildSelfhostedBackendSession, aC as buildStreamUrl, aD as buildTerminalServerScript, aE as contentTypeForCodec, aF as createSandboxClient, aG as createSandboxClientForBackend, aH as decodeModalSnapshotId, aI as deletePriorPersistedSnapshot, aJ as deleteRecordingArtifacts, aK as deserializeSandboxSessionStateEnvelope, aL as desktopCapableBackend, aM as ensureDisplayStack, aN as ensureModalRegistryImage, aO as ensureTerminalServer, aP as establishSandboxSessionFromEnvelope, aQ as exposeStreamPort, aR as extForCodec, aS as isExecSessionLostBanner, aT as isProviderSandboxNotFoundError, aU as isSelfhostedProviderNotFoundError, aV as isWorkspaceEscapeError, aW as makeActiveBackendResolver, aX as mintStreamToken, aY as modalSandboxAttributionEnvironment, aZ as modalSandboxAttributionTags, a_ as negotiateCapabilities, a$ as negotiateSelfhostedCapabilities, b0 as offlineAgentError, b1 as offlineControlResponse, b2 as parseExecBannerSessionId, b3 as parseNumstatZ, b4 as parsePorcelainV2, b5 as parseUnifiedPatch, b6 as readRecordingBytes, b7 as readWorkspaceArchiveFromEnvelopeSessionState, b8 as recordingStorageKey, b9 as restoredSandboxSessionStateFromEntry, ba as sandboxStateEntryFromRunState, bb as selectBackend, bc as selfhostedLiveness, bd as serializeEstablishedSandboxEnvelope, be as setSelfhostedApplyDiff, bf as startRecording, bg as stopRecording, bh as stripExecBanner, bi as subjectFor, bj as sweepModalOrphanSandboxes, bk as tagModalSandbox, bl as tearDownDisplayStack, bm as tearDownTerminalServer, bn as terminateModalSandboxById, bo as timeoutAgentError, bp as timeoutControlResponse, bq as verifyStreamToken } from '../index-otZkoLYq.js';
5
5
  import 'modal';
6
6
  import '@opengeni/agent-proto';
@@ -53,6 +53,7 @@ import {
53
53
  deserializeSandboxSessionStateEnvelope,
54
54
  desktopCapableBackend,
55
55
  ensureDisplayStack,
56
+ ensureModalRegistryImage,
56
57
  ensureTerminalServer,
57
58
  establishSandboxSessionFromEnvelope,
58
59
  exposeStreamPort,
@@ -95,7 +96,7 @@ import {
95
96
  timeoutAgentError,
96
97
  timeoutControlResponse,
97
98
  verifyStreamToken
98
- } from "../chunk-Q36E4MDM.js";
99
+ } from "../chunk-BHA2SU3H.js";
99
100
  export {
100
101
  ActiveBackendUnresolvableError,
101
102
  CAPABILITY_DESCRIPTORS,
@@ -151,6 +152,7 @@ export {
151
152
  deserializeSandboxSessionStateEnvelope,
152
153
  desktopCapableBackend,
153
154
  ensureDisplayStack,
155
+ ensureModalRegistryImage,
154
156
  ensureTerminalServer,
155
157
  establishSandboxSessionFromEnvelope,
156
158
  exposeStreamPort,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opengeni/runtime",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "type": "module",
5
5
  "license": "Apache-2.0",
6
6
  "main": "./dist/index.js",
@@ -31,7 +31,7 @@
31
31
  },
32
32
  "dependencies": {
33
33
  "@opengeni/agent-proto": "^0.2.1",
34
- "@opengeni/config": "^0.3.0",
34
+ "@opengeni/config": "^0.4.0",
35
35
  "@opengeni/contracts": "^0.9.0",
36
36
  "@openai/agents": "^0.11.6",
37
37
  "@openai/agents-extensions": "^0.11.6",
@@ -55,12 +55,14 @@ export {
55
55
  type ProviderConstructionContext,
56
56
  } from "./providers";
57
57
  export {
58
+ ensureModalRegistryImage,
58
59
  modalSandboxAttributionEnvironment,
59
60
  modalSandboxAttributionTags,
60
61
  sweepModalOrphanSandboxes,
61
62
  tagModalSandbox,
62
63
  terminateModalSandboxById,
63
64
  type LiveModalSandboxLeaseAttribution,
65
+ type ModalModuleLoader,
64
66
  type ModalOrphanSweepResult,
65
67
  type ModalSandboxAttribution,
66
68
  } from "./providers/modal";
@@ -87,8 +87,9 @@ export const modalProvider: ProviderRegistration = {
87
87
  if (settings.modalWorkspacePersistence) {
88
88
  options.workspacePersistence = settings.modalWorkspacePersistence;
89
89
  }
90
- if (settings.modalImageRef) {
91
- options.image = ModalImageSelector.fromTag(settings.modalImageRef);
90
+ const imageSelector = resolveModalImageSelector(settings);
91
+ if (imageSelector) {
92
+ options.image = imageSelector;
92
93
  }
93
94
  if (settings.modalTokenId) {
94
95
  options.tokenId = settings.modalTokenId;
@@ -106,6 +107,110 @@ export const modalProvider: ProviderRegistration = {
106
107
  type ModalModule = typeof import("modal");
107
108
  type ModalClientLike = InstanceType<ModalModule["ModalClient"]>;
108
109
 
110
+ // --- Private-registry image resolution (OPENGENI_MODAL_IMAGE_REGISTRY_SECRET) ------
111
+ //
112
+ // The Agents-extension Modal backend resolves `modalImageRef` via
113
+ // `Image.fromRegistry(tag)` with NO secret, so it can only pull PUBLIC images. To run
114
+ // a PRIVATE image we resolve the named Modal Secret and pre-build the authenticated
115
+ // `fromRegistry(tag, secret)` image ONCE per process, then hand the provider `build`
116
+ // a `ModalImageSelector.fromImage(...)`. `build` is synchronous and modal is imported
117
+ // lazily (never loaded for non-modal backends), so resolution can't happen inside
118
+ // `build`; the worker boot awaits `ensureModalRegistryImage` first and `build` reads
119
+ // the settled result. Modal images are lazy, workspace-scoped definitions, so an image
120
+ // built by this module's client is usable by the ModalSandboxClient's own client.
121
+
122
+ /** Loader seam so unit tests can inject a fake modal module. */
123
+ export type ModalModuleLoader = () => Promise<
124
+ Pick<ModalModule, "ModalClient" | "Secret">
125
+ >;
126
+
127
+ const defaultModalLoader: ModalModuleLoader = () => import("modal");
128
+
129
+ /** Settled, synchronously-readable resolved images, keyed per config. */
130
+ const resolvedRegistryImages = new Map<string, unknown>();
131
+ /** In-flight resolutions, for cross-call de-duplication. */
132
+ const inFlightRegistryImages = new Map<string, Promise<void>>();
133
+
134
+ function registryImageCacheKey(settings: Settings): string {
135
+ return [
136
+ settings.modalImageRef ?? "",
137
+ settings.modalImageRegistrySecret ?? "",
138
+ settings.modalEnvironment ?? "",
139
+ ].join("|");
140
+ }
141
+
142
+ /**
143
+ * Resolve + cache the private-registry Modal image. No-op unless BOTH
144
+ * `modalImageRef` and `modalImageRegistrySecret` are set. Memoized per
145
+ * (imageRef, secret, environment) so it runs once per worker process. Awaited at
146
+ * worker boot BEFORE the first sandbox is created; `build` then reads the resolved
147
+ * image and otherwise falls back to the public `fromTag` path.
148
+ */
149
+ export async function ensureModalRegistryImage(
150
+ settings: Settings,
151
+ loadModal: ModalModuleLoader = defaultModalLoader,
152
+ ): Promise<void> {
153
+ if (!settings.modalImageRegistrySecret || !settings.modalImageRef) {
154
+ return;
155
+ }
156
+ const key = registryImageCacheKey(settings);
157
+ if (resolvedRegistryImages.has(key)) {
158
+ return;
159
+ }
160
+ let pending = inFlightRegistryImages.get(key);
161
+ if (!pending) {
162
+ pending = (async () => {
163
+ const modal = await loadModal();
164
+ const client = new modal.ModalClient(modalClientOptions(settings));
165
+ const secret = await modal.Secret.fromName(
166
+ settings.modalImageRegistrySecret!,
167
+ settings.modalEnvironment ? { environment: settings.modalEnvironment } : undefined,
168
+ );
169
+ // fromRegistry is synchronous and returns a lazy image definition (built
170
+ // server-side at sandbox create); the resolved secretId travels with it.
171
+ const image = client.images.fromRegistry(settings.modalImageRef!, secret);
172
+ resolvedRegistryImages.set(key, image);
173
+ })().finally(() => {
174
+ inFlightRegistryImages.delete(key);
175
+ });
176
+ inFlightRegistryImages.set(key, pending);
177
+ }
178
+ await pending;
179
+ }
180
+
181
+ /** The resolved private-registry image for these settings, or undefined if none. */
182
+ function cachedModalRegistryImage(settings: Settings): unknown | undefined {
183
+ if (!settings.modalImageRegistrySecret || !settings.modalImageRef) {
184
+ return undefined;
185
+ }
186
+ return resolvedRegistryImages.get(registryImageCacheKey(settings));
187
+ }
188
+
189
+ /**
190
+ * Choose the image selector for a Modal sandbox client from settings. Returns:
191
+ * - `fromImage(resolved)` when a private-registry secret is configured AND the
192
+ * image has been resolved (ensureModalRegistryImage ran at worker boot);
193
+ * - `fromTag(modalImageRef)` for the public path (no secret, or cold cache — the
194
+ * resume/attach paths never pull an image so the tag branch is harmless there);
195
+ * - `undefined` when no image ref is set (Modal uses its default image).
196
+ * Exported for unit tests.
197
+ */
198
+ export function resolveModalImageSelector(settings: Settings): ModalImageSelector | undefined {
199
+ if (!settings.modalImageRef) {
200
+ return undefined;
201
+ }
202
+ const registryImage = cachedModalRegistryImage(settings);
203
+ return registryImage
204
+ ? ModalImageSelector.fromImage(registryImage as Parameters<typeof ModalImageSelector.fromImage>[0])
205
+ : ModalImageSelector.fromTag(settings.modalImageRef);
206
+ }
207
+
208
+ /** Test-only: clear the resolved/in-flight image caches. */
209
+ export function __resetModalRegistryImageCacheForTest(): void {
210
+ resolvedRegistryImages.clear();
211
+ inFlightRegistryImages.clear();
212
+ }
213
+
109
214
  function modalClientOptions(settings: Settings): ConstructorParameters<ModalModule["ModalClient"]>[0] {
110
215
  return {
111
216
  ...(settings.modalTokenId ? { tokenId: settings.modalTokenId } : {}),