@nosana/node 1.1.62-rc → 1.1.64-rc

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/dist/.env +2 -3
  2. package/dist/.env.dev +1 -1
  3. package/dist/.env.prd +1 -1
  4. package/dist/package.json +3 -2
  5. package/dist/src/NodeManager/configs/NodeConfigs.d.ts +1 -0
  6. package/dist/src/NodeManager/configs/NodeConfigs.js +4 -1
  7. package/dist/src/NodeManager/configs/configs.js +2 -2
  8. package/dist/src/NodeManager/db/index.d.ts +23 -0
  9. package/dist/src/NodeManager/index.js +8 -3
  10. package/dist/src/NodeManager/node/api/ApiHandler.js +16 -3
  11. package/dist/src/NodeManager/node/api/middlewares/index.d.ts +0 -2
  12. package/dist/src/NodeManager/node/api/middlewares/index.js +0 -2
  13. package/dist/src/NodeManager/node/api/middlewares/rest/verifyJobOwnerSignatureMiddleware.js +1 -0
  14. package/dist/src/NodeManager/node/api/middlewares/ws/verifyWSJobOwnerSignatureMiddleware.js +1 -0
  15. package/dist/src/NodeManager/node/api/routes/get/node-info.js +9 -7
  16. package/dist/src/NodeManager/node/api/routes/post/index.d.ts +1 -0
  17. package/dist/src/NodeManager/node/api/routes/post/index.js +1 -0
  18. package/dist/src/NodeManager/node/api/routes/post/ssh-authorize.d.ts +17 -0
  19. package/dist/src/NodeManager/node/api/routes/post/ssh-authorize.js +53 -0
  20. package/dist/src/NodeManager/node/api/routes/wss/index.d.ts +2 -0
  21. package/dist/src/NodeManager/node/api/routes/wss/index.js +2 -0
  22. package/dist/src/NodeManager/node/api/routes/wss/log.js +2 -1
  23. package/dist/src/NodeManager/node/api/routes/wss/terminal/terminal.d.ts +21 -0
  24. package/dist/src/NodeManager/node/api/routes/wss/terminal/terminal.js +165 -0
  25. package/dist/src/NodeManager/node/api/routes/wss/terminal/terminalAuthorization.d.ts +18 -0
  26. package/dist/src/NodeManager/node/api/routes/wss/terminal/terminalAuthorization.js +61 -0
  27. package/dist/src/NodeManager/node/market/hostManager.d.ts +3 -3
  28. package/dist/src/NodeManager/node/resource/helpers/isAuthGatedResource.d.ts +9 -0
  29. package/dist/src/NodeManager/node/resource/helpers/isAuthGatedResource.js +11 -0
  30. package/dist/src/NodeManager/node/resource/image/imageManager.d.ts +14 -1
  31. package/dist/src/NodeManager/node/resource/image/imageManager.js +30 -4
  32. package/dist/src/NodeManager/node/resource/resourceManager.d.ts +1 -1
  33. package/dist/src/NodeManager/node/resource/resourceManager.js +21 -5
  34. package/dist/src/NodeManager/node/resource/volume/volumeManager.d.ts +31 -4
  35. package/dist/src/NodeManager/node/resource/volume/volumeManager.js +69 -12
  36. package/dist/src/NodeManager/provider/Provider.d.ts +29 -0
  37. package/dist/src/NodeManager/provider/Provider.js +143 -61
  38. package/dist/src/NodeManager/provider/SshAccessManager.d.ts +49 -0
  39. package/dist/src/NodeManager/provider/SshAccessManager.js +148 -0
  40. package/dist/src/NodeManager/provider/containerOrchestration/docker/createDockerRunOptions.d.ts +1 -1
  41. package/dist/src/NodeManager/provider/containerOrchestration/docker/createDockerRunOptions.js +6 -3
  42. package/dist/src/NodeManager/provider/containerOrchestration/docker/index.d.ts +5 -1
  43. package/dist/src/NodeManager/provider/containerOrchestration/docker/index.js +40 -2
  44. package/dist/src/NodeManager/provider/containerOrchestration/interface.d.ts +28 -1
  45. package/dist/src/NodeManager/provider/containerOrchestration/labels.d.ts +10 -0
  46. package/dist/src/NodeManager/provider/containerOrchestration/labels.js +12 -0
  47. package/dist/src/NodeManager/provider/containerOrchestration/podman/index.js +14 -9
  48. package/dist/src/NodeManager/provider/containerOrchestration/podman/utils/createPodmanRunOptions.d.ts +39 -0
  49. package/dist/src/NodeManager/provider/containerOrchestration/podman/utils/createPodmanRunOptions.js +16 -2
  50. package/dist/src/NodeManager/provider/sshSidecar.d.ts +66 -0
  51. package/dist/src/NodeManager/provider/sshSidecar.js +176 -0
  52. package/dist/src/NodeManager/provider/webTerminal.d.ts +24 -0
  53. package/dist/src/NodeManager/provider/webTerminal.js +98 -0
  54. package/dist/src/NodeManager/repository/NodeRepository.d.ts +5 -1
  55. package/dist/src/NodeManager/repository/NodeRepository.js +20 -0
  56. package/npm-shrinkwrap.json +28 -2
  57. package/package.json +3 -2
  58. package/dist/src/NodeManager/node/api/middlewares/ws/verifyWSMiddleware.d.ts +0 -3
  59. package/dist/src/NodeManager/node/api/middlewares/ws/verifyWSMiddleware.js +0 -20
  60. package/dist/src/NodeManager/node/api/middlewares/ws/verifyWSNodeOrJobOwnerSignatureMiddleware.d.ts +0 -3
  61. package/dist/src/NodeManager/node/api/middlewares/ws/verifyWSNodeOrJobOwnerSignatureMiddleware.js +0 -33
@@ -28,7 +28,7 @@ export declare class HostManager {
28
28
  };
29
29
  content: {
30
30
  "application/json": {
31
- status: "failed" | "success";
31
+ status: "success" | "failed";
32
32
  report?: {
33
33
  marketAddress: string;
34
34
  marketName?: string | undefined;
@@ -43,7 +43,7 @@ export declare class HostManager {
43
43
  } | undefined;
44
44
  };
45
45
  "multipart/form-data": {
46
- status: "failed" | "success";
46
+ status: "success" | "failed";
47
47
  report?: {
48
48
  marketAddress: string;
49
49
  marketName?: string | undefined;
@@ -58,7 +58,7 @@ export declare class HostManager {
58
58
  } | undefined;
59
59
  };
60
60
  "text/plain": {
61
- status: "failed" | "success";
61
+ status: "success" | "failed";
62
62
  report?: {
63
63
  marketAddress: string;
64
64
  marketName?: string | undefined;
@@ -0,0 +1,9 @@
1
+ import { Resource } from '@nosana/sdk/dist/types/resources.js';
2
+ /**
3
+ * Whether a resource is fetched from an auth-gated remote — S3 behind IAM
4
+ * credentials or a Hugging Face repo behind an access token. Such a resource is
5
+ * private to the credentials that fetched it, so its cached volume must be
6
+ * locked to the fetching project rather than shared: a later job must present
7
+ * its own credentials, not reuse a cache it was never entitled to.
8
+ */
9
+ export declare function isAuthGatedResource(resource: Resource): boolean;
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Whether a resource is fetched from an auth-gated remote — S3 behind IAM
3
+ * credentials or a Hugging Face repo behind an access token. Such a resource is
4
+ * private to the credentials that fetched it, so its cached volume must be
5
+ * locked to the fetching project rather than shared: a later job must present
6
+ * its own credentials, not reuse a cache it was never entitled to.
7
+ */
8
+ export function isAuthGatedResource(resource) {
9
+ return ((resource.type === 'S3' && !!resource.IAM) ||
10
+ (resource.type === 'HF' && !!resource.accessToken));
11
+ }
@@ -18,5 +18,18 @@ export declare class ImageManager {
18
18
  pullMarketRequiredImages(required_images: RequiredImage[]): Promise<void>;
19
19
  pruneImages(): Promise<void>;
20
20
  resyncImagesDB(): Promise<void>;
21
- setImage(image: string, isPrivate?: boolean): Promise<void>;
21
+ /**
22
+ * Whether a job for `project` must re-authenticate before it may use this
23
+ * image, rather than reuse the cached copy. True only for a private image the
24
+ * node last pulled for a *different* project: the daemon holds one copy per
25
+ * image reference, so the caller forces a pull, which re-verifies this
26
+ * project's credentials against the registry (reusing cached layers) and
27
+ * denies a job that cannot authenticate.
28
+ *
29
+ * The project that already owns the cached image, a public image, and a
30
+ * market-required image (shared across the market by design) all reuse the
31
+ * cache with no registry round-trip.
32
+ */
33
+ requiresReauth(image: string, project?: string): boolean;
34
+ setImage(image: string, isPrivate?: boolean, project?: string): Promise<void>;
22
35
  }
@@ -45,7 +45,7 @@ export class ImageManager {
45
45
  }
46
46
  }
47
47
  async resyncImagesDB() {
48
- for (const [image, { lastUsed, required, isPrivate }] of Object.entries(this.repository.getImagesResources())) {
48
+ for (const [image, { lastUsed, required }] of Object.entries(this.repository.getImagesResources())) {
49
49
  if (!(await this.containerOrchestration.hasImage(image))) {
50
50
  this.repository.deleteImageResource(image);
51
51
  continue;
@@ -54,19 +54,45 @@ export class ImageManager {
54
54
  this.market_required_images.includes(image)) {
55
55
  continue;
56
56
  }
57
+ // A private image is kept cached like any other and ages out on the same
58
+ // unused rule: cross-project reuse is gated at pull time by `project`
59
+ // (see requiresReauth), so it no longer has to be dropped on sight.
57
60
  const hoursSinceLastUsed = hoursSinceDate(new Date(lastUsed));
58
- if (hoursSinceLastUsed > 24 || isPrivate) {
61
+ if (hoursSinceLastUsed > 24) {
59
62
  await this.containerOrchestration.deleteImage(image);
60
63
  this.repository.deleteImageResource(image);
61
64
  }
62
65
  }
63
66
  }
64
- async setImage(image, isPrivate = false) {
67
+ /**
68
+ * Whether a job for `project` must re-authenticate before it may use this
69
+ * image, rather than reuse the cached copy. True only for a private image the
70
+ * node last pulled for a *different* project: the daemon holds one copy per
71
+ * image reference, so the caller forces a pull, which re-verifies this
72
+ * project's credentials against the registry (reusing cached layers) and
73
+ * denies a job that cannot authenticate.
74
+ *
75
+ * The project that already owns the cached image, a public image, and a
76
+ * market-required image (shared across the market by design) all reuse the
77
+ * cache with no registry round-trip.
78
+ */
79
+ requiresReauth(image, project) {
80
+ if (this.market_required_images.includes(image))
81
+ return false;
82
+ const entry = this.repository.getImageResource(image);
83
+ return !!entry?.isPrivate && entry.project !== project;
84
+ }
85
+ async setImage(image, isPrivate = false, project) {
65
86
  const imageObj = this.repository.getImageResource(image);
66
87
  this.repository.updateImageResource(image, {
67
88
  lastUsed: new Date(),
68
89
  usage: imageObj?.usage + 1 || 1,
69
- isPrivate,
90
+ // Never downgrade a known-private image to public — a later reuse by its
91
+ // owner arrives without credentials — and keep the owning project when
92
+ // this pull did not re-establish one. A different project claims the image
93
+ // only by passing its own project after a successful credentialed pull.
94
+ isPrivate: isPrivate || !!imageObj?.isPrivate,
95
+ project: project ?? imageObj?.project,
70
96
  });
71
97
  }
72
98
  }
@@ -45,7 +45,7 @@ export declare class ResourceManager {
45
45
  resyncResourcesDB(): Promise<void>;
46
46
  fetchMarketRequiredResources(market: Market | string): Promise<void>;
47
47
  prune(): Promise<void>;
48
- getResourceVolumes(resources: Resource[], controller: AbortController, job?: JobContext): Promise<{
48
+ getResourceVolumes(resources: Resource[], controller: AbortController, job?: JobContext, project?: string): Promise<{
49
49
  dest: string;
50
50
  name: string;
51
51
  readonly?: boolean;
@@ -1,5 +1,6 @@
1
1
  import { HostManager } from '../market/hostManager.js';
2
2
  import { createResourceName } from './helpers/createResourceName.js';
3
+ import { isAuthGatedResource } from './helpers/isAuthGatedResource.js';
3
4
  import { ImageManager } from './image/imageManager.js';
4
5
  import { VolumeManager } from './volume/volumeManager.js';
5
6
  /**
@@ -71,11 +72,26 @@ export class ResourceManager {
71
72
  await this.images.pruneImages();
72
73
  await this.volumes.pruneVolumes();
73
74
  }
74
- async getResourceVolumes(resources, controller, job) {
75
+ async getResourceVolumes(resources, controller, job, project) {
75
76
  const volumes = [];
76
77
  for (const resource of resources) {
77
- await this.volumes.createRemoteVolume(resource, controller, job);
78
- if ((await this.volumes.hasVolume(resource)) === false) {
78
+ const allowWrite = resource.type === 'S3' && !!resource.allowWrite;
79
+ // A writable mount can leave the job's bytes behind, and an auth-gated
80
+ // remote is private to the credentials that fetched it; either way the
81
+ // volume must be locked to this project rather than shared, so a different
82
+ // project pulls its own clean copy with its own credentials.
83
+ const lockToProject = allowWrite || isAuthGatedResource(resource);
84
+ // Locks the resource to this project (claiming the shared copy if one
85
+ // exists) so its volume is never reused across projects. Unlocked
86
+ // resources resolve to the shared key, untouched — unless this project
87
+ // already holds a claim, which is reused for every later job.
88
+ const resourceKey = this.volumes.resolveResourceKey(resource, project, lockToProject);
89
+ // A project-scoped key must keep carrying its project so a read-only
90
+ // reuse does not clear the claim; the shared key carries none. The key
91
+ // itself is the source of truth here, not this job's lock state.
92
+ const claimedForProject = resourceKey !== createResourceName(resource);
93
+ await this.volumes.createRemoteVolume(resource, controller, job, resourceKey, claimedForProject ? project : undefined);
94
+ if ((await this.volumes.hasVolume(resource, resourceKey)) === false) {
79
95
  const error = new Error(`Missing required resource ${createResourceName(resource)}.`);
80
96
  throw error;
81
97
  }
@@ -85,8 +101,8 @@ export class ResourceManager {
85
101
  ? '/root/.ollama/models'
86
102
  : resource.target
87
103
  : resource.target,
88
- name: await this.volumes.getVolume(resource),
89
- readonly: resource.type === 'S3' && resource.allowWrite ? false : true,
104
+ name: await this.volumes.getVolume(resource, resourceKey),
105
+ readonly: !allowWrite,
90
106
  });
91
107
  }
92
108
  return volumes;
@@ -52,11 +52,38 @@ export declare class VolumeManager {
52
52
  private progressBarReporter;
53
53
  constructor(containerOrchestration: ContainerOrchestrationInterface, repository: NodeRepository);
54
54
  pullMarketRequiredVolumes(remoteResources: RequiredResource[]): Promise<void>;
55
- createRemoteVolume(resource: RequiredResource, controller: AbortController, job?: JobContext): Promise<string>;
55
+ /**
56
+ * The DB key a job's resource must use, locking a resource to one project
57
+ * when it must not be shared across projects.
58
+ *
59
+ * Two things force a lock. A writable (`allowWrite`) mount can leave the job's
60
+ * own bytes in the cached volume, so a later job reusing it could be handed
61
+ * code an earlier project planted. An auth-gated resource is private to the
62
+ * credentials that fetched it, so a later job reusing the cache would read
63
+ * private data without presenting its own credentials. Either way the first
64
+ * such use claims the resource for its project — the volume is tagged and
65
+ * moved to a project-scoped key, which frees the shared key for the clean copy
66
+ * the next project downloads with its own credentials.
67
+ *
68
+ * Claiming renames the DB entry only; the Docker volume and its contents stay
69
+ * in place, so the claiming project inherits the already-downloaded data with
70
+ * no re-download. The next use of the shared key pays for a fresh pull.
71
+ *
72
+ * Once a project holds a claim, every later job of that project reuses it —
73
+ * read-only ones included, even public ones that would not have locked on
74
+ * their own. The claimed copy holds only what this project's own jobs put
75
+ * there, so reusing it leaks nothing across projects and spares the project a
76
+ * second download of a resource it already has.
77
+ *
78
+ * A resource with no claim for this project and nothing forcing one (a public,
79
+ * read-only mount) and market preloads stay on the shared key, reused freely.
80
+ */
81
+ resolveResourceKey(resource: Resource, project?: string, lockToProject?: boolean): string;
82
+ createRemoteVolume(resource: RequiredResource, controller: AbortController, job?: JobContext, resourceKey?: string, project?: string): Promise<string>;
56
83
  private runResourceManagerContainer;
57
- setVolume(bucket: string, volume: string): Promise<void>;
58
- hasVolume(resource: Resource): Promise<boolean>;
59
- getVolume(resource: RequiredResource | Resource): Promise<string>;
84
+ setVolume(bucket: string, volume: string, project?: string): Promise<void>;
85
+ hasVolume(resource: Resource, resourceKey?: string): Promise<boolean>;
86
+ getVolume(resource: RequiredResource | Resource, resourceKey?: string): Promise<string>;
60
87
  pruneVolumes(): Promise<void>;
61
88
  resyncResourcesDB(): Promise<void>;
62
89
  }
@@ -115,8 +115,63 @@ export class VolumeManager {
115
115
  }
116
116
  }
117
117
  }
118
- async createRemoteVolume(resource, controller, job) {
119
- const resourceName = createResourceName(resource);
118
+ /**
119
+ * The DB key a job's resource must use, locking a resource to one project
120
+ * when it must not be shared across projects.
121
+ *
122
+ * Two things force a lock. A writable (`allowWrite`) mount can leave the job's
123
+ * own bytes in the cached volume, so a later job reusing it could be handed
124
+ * code an earlier project planted. An auth-gated resource is private to the
125
+ * credentials that fetched it, so a later job reusing the cache would read
126
+ * private data without presenting its own credentials. Either way the first
127
+ * such use claims the resource for its project — the volume is tagged and
128
+ * moved to a project-scoped key, which frees the shared key for the clean copy
129
+ * the next project downloads with its own credentials.
130
+ *
131
+ * Claiming renames the DB entry only; the Docker volume and its contents stay
132
+ * in place, so the claiming project inherits the already-downloaded data with
133
+ * no re-download. The next use of the shared key pays for a fresh pull.
134
+ *
135
+ * Once a project holds a claim, every later job of that project reuses it —
136
+ * read-only ones included, even public ones that would not have locked on
137
+ * their own. The claimed copy holds only what this project's own jobs put
138
+ * there, so reusing it leaks nothing across projects and spares the project a
139
+ * second download of a resource it already has.
140
+ *
141
+ * A resource with no claim for this project and nothing forcing one (a public,
142
+ * read-only mount) and market preloads stay on the shared key, reused freely.
143
+ */
144
+ resolveResourceKey(resource, project, lockToProject = false) {
145
+ const sharedKey = createResourceName(resource);
146
+ if (!project) {
147
+ return sharedKey;
148
+ }
149
+ const claimedKey = `${sharedKey}::${project}`;
150
+ // Already claimed by this project on an earlier job — reuse it, writes and
151
+ // all, whatever this job's own lock state. This is what lets a later
152
+ // read-only job read back a copy an earlier writable job of the same
153
+ // project produced, rather than re-downloading it.
154
+ if (this.repository.getVolumeResource(claimedKey)) {
155
+ return claimedKey;
156
+ }
157
+ // No claim yet, and nothing forces one: an unlocked (public, read-only)
158
+ // resource stays on the shared key, reused freely across projects.
159
+ if (!lockToProject) {
160
+ return sharedKey;
161
+ }
162
+ // First locked use of this resource by this project. An unclaimed shared
163
+ // volume already holds it: claim it in place rather than re-downloading.
164
+ // Moving it to the project key frees the shared key for the next project's
165
+ // clean copy.
166
+ const shared = this.repository.getVolumeResource(sharedKey);
167
+ if (shared && !shared.project) {
168
+ this.repository.updateVolumeResource(claimedKey, { ...shared, project });
169
+ this.repository.deleteVolumeResource(sharedKey);
170
+ }
171
+ return claimedKey;
172
+ }
173
+ async createRemoteVolume(resource, controller, job, resourceKey, project) {
174
+ const resourceName = resourceKey ?? createResourceName(resource);
120
175
  // Checked before anything is allocated. Left to the download, an empty repo
121
176
  // becomes a request for `/api/models//tree/main` and comes back as a bare
122
177
  // 404 with nothing in it naming the resource at fault.
@@ -141,6 +196,7 @@ export class VolumeManager {
141
196
  lastUsed: new Date(),
142
197
  usage: 0,
143
198
  pending: true,
199
+ project,
144
200
  });
145
201
  }
146
202
  switch (resource.type) {
@@ -161,7 +217,7 @@ export class VolumeManager {
161
217
  await this.runResourceManagerContainer(volumeName, resourceName, args, controller, sync, job);
162
218
  }
163
219
  }
164
- this.setVolume(resourceName, volumeName);
220
+ this.setVolume(resourceName, volumeName, project);
165
221
  }
166
222
  catch (err) {
167
223
  throw new Error(err.message);
@@ -172,7 +228,7 @@ export class VolumeManager {
172
228
  const { repo, revision, files: hfFiles, accessToken, } = resource;
173
229
  const args = createHFArgs(volumeName, { repo, revision, files: hfFiles }, accessToken);
174
230
  await this.runResourceManagerContainer(volumeName, resourceName, args, controller, sync, job);
175
- this.setVolume(resourceName, volumeName);
231
+ this.setVolume(resourceName, volumeName, project);
176
232
  }
177
233
  catch (err) {
178
234
  throw new Error(err.message);
@@ -183,7 +239,7 @@ export class VolumeManager {
183
239
  const { model } = resource;
184
240
  const args = createOllamaArgs(volumeName, model);
185
241
  await this.runResourceManagerContainer(volumeName, resourceName, args, controller, sync, job);
186
- this.setVolume(resourceName, volumeName);
242
+ this.setVolume(resourceName, volumeName, project);
187
243
  }
188
244
  catch (err) {
189
245
  throw new Error(err.message);
@@ -284,7 +340,7 @@ export class VolumeManager {
284
340
  this.progressBarReporter.stop(`${syncing ? 'Synced' : 'Downloaded'} resource ${name} completed`);
285
341
  await this.containerOrchestration.stopAndDeleteContainer(container.id);
286
342
  }
287
- async setVolume(bucket, volume) {
343
+ async setVolume(bucket, volume, project) {
288
344
  const volumeObj = this.repository.getVolumeResource(bucket);
289
345
  this.repository.updateVolumeResource(bucket, {
290
346
  volume,
@@ -293,22 +349,23 @@ export class VolumeManager {
293
349
  usage: volumeObj?.usage + 1 || 1,
294
350
  // Only a completed sync reaches here, which is what pending means.
295
351
  pending: false,
352
+ project,
296
353
  });
297
354
  }
298
- async hasVolume(resource) {
299
- const volume = this.repository.getVolumeResource(createResourceName(resource))?.volume;
355
+ async hasVolume(resource, resourceKey) {
356
+ const key = resourceKey ?? createResourceName(resource);
357
+ const volume = this.repository.getVolumeResource(key)?.volume;
300
358
  if (!volume) {
301
359
  return false;
302
360
  }
303
361
  const dockerHasVolume = await this.containerOrchestration.hasVolume(volume);
304
362
  if (!dockerHasVolume) {
305
- this.repository.deleteVolumeResource(createResourceName(resource));
363
+ this.repository.deleteVolumeResource(key);
306
364
  }
307
365
  return dockerHasVolume;
308
366
  }
309
- async getVolume(resource) {
310
- return this.repository.getVolumeResource(createResourceName(resource))
311
- ?.volume;
367
+ async getVolume(resource, resourceKey) {
368
+ return this.repository.getVolumeResource(resourceKey ?? createResourceName(resource))?.volume;
312
369
  }
313
370
  async pruneVolumes() {
314
371
  const cachedVolumes = await this.containerOrchestration.listVolumes();
@@ -4,16 +4,45 @@ import EventEmitter from 'events';
4
4
  import { NodeRepository } from '../repository/NodeRepository.js';
5
5
  import { ResourceManager } from '../node/resource/resourceManager.js';
6
6
  import { ContainerOrchestrationInterface } from './containerOrchestration/interface.js';
7
+ import { type TerminalSession } from './webTerminal.js';
7
8
  export declare class Provider {
8
9
  containerOrchestration: ContainerOrchestrationInterface;
9
10
  private repository;
10
11
  private resourceManager;
11
12
  private readonly frpcImage;
13
+ private readonly sshAccess;
12
14
  private proxyStartupAbortController;
13
15
  constructor(containerOrchestration: ContainerOrchestrationInterface, repository: NodeRepository, resourceManager: ResourceManager);
14
16
  hasTrustedExecutionEnvCapability(): Promise<boolean>;
15
17
  stopReverseProxyApi(address: string): Promise<boolean>;
16
18
  setUpReverseProxyApi(address: string, port: string): Promise<boolean>;
19
+ generateApiFrpcProxies(address: string, port: string): any[];
20
+ /**
21
+ * Opens an interactive PTY exec session into a running job container for the
22
+ * wallet-authorized web terminal. The authorization check is performed by the
23
+ * caller (the terminal websocket route); this only enforces that the feature
24
+ * is enabled and that we can reach the runtime over a socket.
25
+ */
26
+ createJobTerminalSession(jobId: string, options?: {
27
+ op?: string;
28
+ cols?: number;
29
+ rows?: number;
30
+ }): Promise<TerminalSession>;
31
+ authorizeSshKeyForJob(jobId: string, publicKey: string, expiresAt?: string): Promise<void>;
32
+ listSshKeysForJob(jobId: string): Promise<import("../db/index.js").SshKeyGrant[]>;
33
+ revokeSshKeyForJob(jobId: string, publicKey: string): Promise<void>;
34
+ /**
35
+ * SSH requires the runtime to be reached over a socket the sidecar can mount
36
+ * to exec into the workload. Every SSH entry point, including the web
37
+ * terminal, checks this and nothing else.
38
+ */
39
+ private get sshEnabled();
40
+ private assertSshEnabled;
41
+ /** The SSH sidecars of a job, one per operation, created ones included. */
42
+ private getSshSidecars;
43
+ private hostSocketPath?;
44
+ /** The runtime socket as a host path, which is what a bind source must be. */
45
+ private resolveHostSocketPath;
17
46
  taskManagerContainerRunOperation(flow: Flow, op: Operation<'container/run'>, controller: AbortController, emitter: EventEmitter): Promise<void>;
18
47
  generateFrpcContainerConfig(name: string, networks: {
19
48
  [p: string]: {};