@mastra/factory 0.11.1 → 0.12.0-alpha.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.
Files changed (112) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/dist/factory.d.ts +2 -22
  3. package/dist/factory.d.ts.map +1 -1
  4. package/dist/factory.js +15 -58
  5. package/dist/factory.js.map +1 -1
  6. package/dist/index.d.ts +2 -1
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/integrations/base.d.ts +5 -13
  9. package/dist/integrations/base.d.ts.map +1 -1
  10. package/dist/integrations/github/config.d.ts +4 -4
  11. package/dist/integrations/github/config.d.ts.map +1 -1
  12. package/dist/integrations/github/config.js +3 -3
  13. package/dist/integrations/github/config.js.map +1 -1
  14. package/dist/integrations/github/integration.d.ts.map +1 -1
  15. package/dist/integrations/github/integration.js +2 -4
  16. package/dist/integrations/github/integration.js.map +1 -1
  17. package/dist/integrations/github/reconcile-worker.d.ts +0 -5
  18. package/dist/integrations/github/reconcile-worker.d.ts.map +1 -1
  19. package/dist/integrations/github/reconcile-worker.js +0 -7
  20. package/dist/integrations/github/reconcile-worker.js.map +1 -1
  21. package/dist/integrations/github/routes.d.ts +6 -5
  22. package/dist/integrations/github/routes.d.ts.map +1 -1
  23. package/dist/integrations/github/routes.js +36 -263
  24. package/dist/integrations/github/routes.js.map +1 -1
  25. package/dist/integrations/github/sandbox-release.d.ts +17 -47
  26. package/dist/integrations/github/sandbox-release.d.ts.map +1 -1
  27. package/dist/integrations/github/sandbox-release.js +24 -103
  28. package/dist/integrations/github/sandbox-release.js.map +1 -1
  29. package/dist/integrations/github/sandbox.d.ts +25 -139
  30. package/dist/integrations/github/sandbox.d.ts.map +1 -1
  31. package/dist/integrations/github/sandbox.js +24 -212
  32. package/dist/integrations/github/sandbox.js.map +1 -1
  33. package/dist/integrations/github/webhook.d.ts.map +1 -1
  34. package/dist/integrations/github/webhook.js +1 -2
  35. package/dist/integrations/github/webhook.js.map +1 -1
  36. package/dist/integrations/platform/github/event-worker.d.ts +0 -2
  37. package/dist/integrations/platform/github/event-worker.d.ts.map +1 -1
  38. package/dist/integrations/platform/github/event-worker.js +0 -7
  39. package/dist/integrations/platform/github/event-worker.js.map +1 -1
  40. package/dist/integrations/platform/github/integration.d.ts.map +1 -1
  41. package/dist/integrations/platform/github/integration.js +5 -7
  42. package/dist/integrations/platform/github/integration.js.map +1 -1
  43. package/dist/routes/config.d.ts.map +1 -1
  44. package/dist/routes/config.js +3 -1
  45. package/dist/routes/config.js.map +1 -1
  46. package/dist/routes/fs.d.ts +4 -6
  47. package/dist/routes/fs.d.ts.map +1 -1
  48. package/dist/routes/fs.js +28 -34
  49. package/dist/routes/fs.js.map +1 -1
  50. package/dist/routes/skills.d.ts.map +1 -1
  51. package/dist/routes/skills.js +6 -3
  52. package/dist/routes/skills.js.map +1 -1
  53. package/dist/routes/surface.d.ts +4 -9
  54. package/dist/routes/surface.d.ts.map +1 -1
  55. package/dist/routes/surface.js +3 -5
  56. package/dist/routes/surface.js.map +1 -1
  57. package/dist/sandbox/materialization.d.ts +36 -0
  58. package/dist/sandbox/materialization.d.ts.map +1 -0
  59. package/dist/sandbox/materialization.js +18 -0
  60. package/dist/sandbox/materialization.js.map +1 -0
  61. package/dist/sandbox/session-retirement.d.ts +8 -6
  62. package/dist/sandbox/session-retirement.d.ts.map +1 -1
  63. package/dist/sandbox/session-retirement.js +28 -123
  64. package/dist/sandbox/session-retirement.js.map +1 -1
  65. package/dist/sandbox/session-sandbox.d.ts +117 -0
  66. package/dist/sandbox/session-sandbox.d.ts.map +1 -0
  67. package/dist/sandbox/session-sandbox.js +132 -0
  68. package/dist/sandbox/session-sandbox.js.map +1 -0
  69. package/dist/sandbox/workdir.d.ts +30 -0
  70. package/dist/sandbox/workdir.d.ts.map +1 -0
  71. package/dist/sandbox/workdir.js +48 -0
  72. package/dist/sandbox/workdir.js.map +1 -0
  73. package/dist/session/filesystem-capture.d.ts +2 -1
  74. package/dist/session/filesystem-capture.d.ts.map +1 -1
  75. package/dist/session/filesystem-capture.js +45 -4
  76. package/dist/session/filesystem-capture.js.map +1 -1
  77. package/dist/session/first-exec-capture.d.ts +1 -0
  78. package/dist/session/first-exec-capture.d.ts.map +1 -1
  79. package/dist/session/first-exec-capture.js +1 -1
  80. package/dist/session/first-exec-capture.js.map +1 -1
  81. package/dist/storage/domains/integrations/base.d.ts +1 -1
  82. package/dist/storage/domains/integrations/base.js +1 -1
  83. package/dist/storage/domains/integrations/base.js.map +1 -1
  84. package/dist/storage/domains/source-control/base.d.ts +0 -144
  85. package/dist/storage/domains/source-control/base.d.ts.map +1 -1
  86. package/dist/storage/domains/source-control/base.js +3 -290
  87. package/dist/storage/domains/source-control/base.js.map +1 -1
  88. package/dist/storage/domains/source-control/inmemory.d.ts +1 -63
  89. package/dist/storage/domains/source-control/inmemory.d.ts.map +1 -1
  90. package/dist/storage/domains/source-control/inmemory.js +0 -89
  91. package/dist/storage/domains/source-control/inmemory.js.map +1 -1
  92. package/dist/workspace.d.ts +5 -37
  93. package/dist/workspace.d.ts.map +1 -1
  94. package/dist/workspace.js +107 -245
  95. package/dist/workspace.js.map +1 -1
  96. package/package.json +4 -4
  97. package/dist/sandbox/base-checkpoint-triggers.d.ts +0 -77
  98. package/dist/sandbox/base-checkpoint-triggers.d.ts.map +0 -1
  99. package/dist/sandbox/base-checkpoint-triggers.js +0 -137
  100. package/dist/sandbox/base-checkpoint-triggers.js.map +0 -1
  101. package/dist/sandbox/base-checkpoint.d.ts +0 -59
  102. package/dist/sandbox/base-checkpoint.d.ts.map +0 -1
  103. package/dist/sandbox/base-checkpoint.js +0 -145
  104. package/dist/sandbox/base-checkpoint.js.map +0 -1
  105. package/dist/sandbox/fleet.d.ts +0 -258
  106. package/dist/sandbox/fleet.d.ts.map +0 -1
  107. package/dist/sandbox/fleet.js +0 -356
  108. package/dist/sandbox/fleet.js.map +0 -1
  109. package/dist/sandbox/reattach.d.ts +0 -3
  110. package/dist/sandbox/reattach.d.ts.map +0 -1
  111. package/dist/sandbox/reattach.js +0 -16
  112. package/dist/sandbox/reattach.js.map +0 -1
@@ -1,145 +0,0 @@
1
- import { materializeRepo, runWorktreeSetup, sh, shellQuote } from "../integrations/github/sandbox.js";
2
- import { createHash } from "crypto";
3
- //#region src/sandbox/base-checkpoint.ts
4
- /**
5
- * Base-checkpoint build job.
6
- *
7
- * Keeps a continuously-refreshed "base checkpoint" per connected repo: a
8
- * sandbox filesystem with the default branch already cloned and the repo's
9
- * setup command already run, snapshotted under `repo-<projectRepositoryId>`.
10
- * New sessions boot from this checkpoint and only pay `git fetch` + branch
11
- * checkout instead of a cold clone + install.
12
- *
13
- * Builds are triggered on repo connect, merges/pushes to the default branch,
14
- * and the reconcile sweep. Rapid triggers coalesce: only one build runs per
15
- * repo at a time, and a trigger arriving mid-build marks it dirty so exactly
16
- * one follow-up build runs afterwards.
17
- *
18
- * Providers without real checkpoint support (capability-driven via
19
- * `supportsCheckpoints`) skip the snapshot — sessions keep using the cold
20
- * path. A failed build never blocks sessions either; it only logs, and the
21
- * reconcile sweep retries later.
22
- */
23
- /** Provider checkpoint name for a repo's warm base image. */
24
- function baseCheckpointName(projectRepositoryId) {
25
- return `repo-${projectRepositoryId}`;
26
- }
27
- /** Stable hash of the setup command, used to invalidate stale checkpoints. */
28
- function hashSetupCommand(setupCommand) {
29
- if (!setupCommand) return null;
30
- return createHash("sha256").update(setupCommand).digest("hex");
31
- }
32
- /**
33
- * Coalescing runner for base-checkpoint builds. One instance per server;
34
- * `request()` is fire-and-forget safe (never throws).
35
- */
36
- var BaseCheckpointBuilder = class {
37
- #fleet;
38
- #logger;
39
- /** In-flight build per projectRepositoryId. */
40
- #inflight = /* @__PURE__ */ new Map();
41
- /** Repos re-triggered mid-build: run exactly one follow-up build. */
42
- #dirty = /* @__PURE__ */ new Map();
43
- constructor(options) {
44
- this.#fleet = options.fleet;
45
- this.#logger = options.logger;
46
- }
47
- /** True when a build for this repo is currently running. */
48
- isBuilding(projectRepositoryId) {
49
- return this.#inflight.has(projectRepositoryId);
50
- }
51
- /**
52
- * Request a (re)build of the repo's base checkpoint. Coalesces with any
53
- * in-flight build for the same repo. Resolves when this request's build
54
- * (or the follow-up build it folded into) settles. Never rejects.
55
- */
56
- request(job) {
57
- const key = job.projectRepositoryId;
58
- const existing = this.#inflight.get(key);
59
- if (existing) {
60
- this.#dirty.set(key, job);
61
- return existing.then(() => {
62
- return this.#inflight.get(key) ?? Promise.resolve();
63
- });
64
- }
65
- const run = this.#buildSafely(job).then(() => {
66
- this.#inflight.delete(key);
67
- const followUp = this.#dirty.get(key);
68
- if (followUp) {
69
- this.#dirty.delete(key);
70
- return this.request(followUp);
71
- }
72
- });
73
- this.#inflight.set(key, run);
74
- return run;
75
- }
76
- /** One build attempt; logs failures instead of throwing. */
77
- async #buildSafely(job) {
78
- try {
79
- await this.#build(job);
80
- } catch (error) {
81
- this.#logger?.warn?.(`Base-checkpoint build failed for ${job.repoFullName} (${job.projectRepositoryId}): ${error instanceof Error ? error.message : String(error)}`);
82
- }
83
- }
84
- async #build(job) {
85
- if (!this.#fleet.enabled) return;
86
- const checkpointName = baseCheckpointName(job.projectRepositoryId);
87
- let boundSandboxId = null;
88
- const binding = {
89
- get sandboxId() {
90
- return boundSandboxId;
91
- },
92
- checkpointName,
93
- setSandboxId: async (id) => {
94
- boundSandboxId = id;
95
- },
96
- clear: async () => {
97
- boundSandboxId = null;
98
- }
99
- };
100
- const token = await job.getToken();
101
- let sandbox;
102
- try {
103
- sandbox = await this.#fleet.ensureSandbox(binding, { GH_TOKEN: token }, void 0, {
104
- workingDirectory: job.workdir,
105
- ...job.actingUserId ? { actingUserId: job.actingUserId } : {}
106
- });
107
- if (!sandbox.supportsCheckpoints || !sandbox.snapshot) return;
108
- await materializeRepo({
109
- row: {
110
- id: job.projectRepositoryId,
111
- sandboxWorkdir: job.workdir,
112
- materializedAt: null
113
- },
114
- repoInfo: {
115
- repoFullName: job.repoFullName,
116
- defaultBranch: job.defaultBranch
117
- },
118
- sandbox,
119
- token,
120
- storage: { markMaterialized: async () => {} }
121
- });
122
- if (job.setupCommand) await runWorktreeSetup(sandbox, job.workdir, job.setupCommand);
123
- const head = await sh(sandbox, `git -C ${shellQuote(job.workdir)} rev-parse HEAD`);
124
- if (head.exitCode !== 0) throw new Error(`Failed to read HEAD after materialize: ${head.stderr}`);
125
- const sha = head.stdout.trim();
126
- await sandbox.snapshot();
127
- await job.storage.projectRepositories.setBaseCheckpoint({
128
- id: job.projectRepositoryId,
129
- checkpoint: {
130
- name: checkpointName,
131
- sha,
132
- builtAt: /* @__PURE__ */ new Date(),
133
- setupCommandHash: hashSetupCommand(job.setupCommand)
134
- },
135
- expectedSetupCommand: job.setupCommand
136
- });
137
- } finally {
138
- await this.#fleet.teardownSandbox(binding, sandbox).catch(() => {});
139
- }
140
- }
141
- };
142
- //#endregion
143
- export { BaseCheckpointBuilder, baseCheckpointName, hashSetupCommand };
144
-
145
- //# sourceMappingURL=base-checkpoint.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"base-checkpoint.js","names":["#fleet","#logger","#inflight","#dirty","#buildSafely","#build"],"sources":["../../src/sandbox/base-checkpoint.ts"],"sourcesContent":["/**\n * Base-checkpoint build job.\n *\n * Keeps a continuously-refreshed \"base checkpoint\" per connected repo: a\n * sandbox filesystem with the default branch already cloned and the repo's\n * setup command already run, snapshotted under `repo-<projectRepositoryId>`.\n * New sessions boot from this checkpoint and only pay `git fetch` + branch\n * checkout instead of a cold clone + install.\n *\n * Builds are triggered on repo connect, merges/pushes to the default branch,\n * and the reconcile sweep. Rapid triggers coalesce: only one build runs per\n * repo at a time, and a trigger arriving mid-build marks it dirty so exactly\n * one follow-up build runs afterwards.\n *\n * Providers without real checkpoint support (capability-driven via\n * `supportsCheckpoints`) skip the snapshot — sessions keep using the cold\n * path. A failed build never blocks sessions either; it only logs, and the\n * reconcile sweep retries later.\n */\n\nimport { createHash } from 'node:crypto';\nimport type { IMastraLogger } from '@mastra/core/logger';\nimport { materializeRepo, runWorktreeSetup, sh, shellQuote } from '../integrations/github/sandbox.js';\nimport type { SourceControlStorageHandle } from '../storage/domains/source-control/base.js';\nimport type { MaterializationSandbox, SandboxBindingStore, SandboxFleet } from './fleet.js';\n\n/** Provider checkpoint name for a repo's warm base image. */\nexport function baseCheckpointName(projectRepositoryId: string): string {\n return `repo-${projectRepositoryId}`;\n}\n\n/** Stable hash of the setup command, used to invalidate stale checkpoints. */\nexport function hashSetupCommand(setupCommand: string | null): string | null {\n if (!setupCommand) return null;\n return createHash('sha256').update(setupCommand).digest('hex');\n}\n\n/** Everything one build run needs; resolved by the trigger before enqueueing. */\nexport interface BaseCheckpointJob {\n projectRepositoryId: string;\n repoFullName: string;\n defaultBranch: string;\n setupCommand: string | null;\n /** Workdir the clone lives at — must match what sessions use. */\n workdir: string;\n /** Mint a fresh short-lived installation token for git operations. */\n getToken(): Promise<string>;\n storage: SourceControlStorageHandle;\n actingUserId?: string;\n}\n\n/**\n * Coalescing runner for base-checkpoint builds. One instance per server;\n * `request()` is fire-and-forget safe (never throws).\n */\nexport class BaseCheckpointBuilder {\n readonly #fleet: SandboxFleet;\n readonly #logger?: IMastraLogger;\n /** In-flight build per projectRepositoryId. */\n readonly #inflight = new Map<string, Promise<void>>();\n /** Repos re-triggered mid-build: run exactly one follow-up build. */\n readonly #dirty = new Map<string, BaseCheckpointJob>();\n\n constructor(options: { fleet: SandboxFleet; logger?: IMastraLogger }) {\n this.#fleet = options.fleet;\n this.#logger = options.logger;\n }\n\n /** True when a build for this repo is currently running. */\n isBuilding(projectRepositoryId: string): boolean {\n return this.#inflight.has(projectRepositoryId);\n }\n\n /**\n * Request a (re)build of the repo's base checkpoint. Coalesces with any\n * in-flight build for the same repo. Resolves when this request's build\n * (or the follow-up build it folded into) settles. Never rejects.\n */\n request(job: BaseCheckpointJob): Promise<void> {\n const key = job.projectRepositoryId;\n const existing = this.#inflight.get(key);\n if (existing) {\n // Fold into the in-flight build: remember the latest job so one\n // follow-up rebuild runs with fresh inputs once the current one ends.\n this.#dirty.set(key, job);\n return existing.then(() => {\n const pending = this.#inflight.get(key);\n return pending ?? Promise.resolve();\n });\n }\n\n const run = this.#buildSafely(job).then(() => {\n this.#inflight.delete(key);\n const followUp = this.#dirty.get(key);\n if (followUp) {\n this.#dirty.delete(key);\n return this.request(followUp);\n }\n });\n this.#inflight.set(key, run);\n return run;\n }\n\n /** One build attempt; logs failures instead of throwing. */\n async #buildSafely(job: BaseCheckpointJob): Promise<void> {\n try {\n await this.#build(job);\n } catch (error) {\n this.#logger?.warn?.(\n `Base-checkpoint build failed for ${job.repoFullName} (${job.projectRepositoryId}): ${\n error instanceof Error ? error.message : String(error)\n }`,\n );\n }\n }\n\n async #build(job: BaseCheckpointJob): Promise<void> {\n if (!this.#fleet.enabled) return;\n\n const checkpointName = baseCheckpointName(job.projectRepositoryId);\n // Ephemeral binding: the builder VM is never reattached to — it exists\n // only to produce the checkpoint. Incremental rebuilds still happen\n // because the provider seeds the fresh VM from the previous checkpoint\n // (same `checkpointName`), so materializeRepo pulls instead of cloning.\n let boundSandboxId: string | null = null;\n const binding: SandboxBindingStore = {\n get sandboxId() {\n return boundSandboxId;\n },\n checkpointName,\n setSandboxId: async id => {\n boundSandboxId = id;\n },\n clear: async () => {\n boundSandboxId = null;\n },\n };\n\n const token = await job.getToken();\n let sandbox: MaterializationSandbox | undefined;\n try {\n sandbox = await this.#fleet.ensureSandbox(binding, { GH_TOKEN: token }, undefined, {\n workingDirectory: job.workdir,\n ...(job.actingUserId ? { actingUserId: job.actingUserId } : {}),\n });\n\n if (!sandbox.supportsCheckpoints || !sandbox.snapshot) {\n // No real checkpoint support — nothing to build; sessions use the\n // existing cold path.\n return;\n }\n\n // Clone (cold) or pull (VM seeded from the previous checkpoint).\n await materializeRepo({\n row: { id: job.projectRepositoryId, sandboxWorkdir: job.workdir, materializedAt: null },\n repoInfo: { repoFullName: job.repoFullName, defaultBranch: job.defaultBranch },\n sandbox,\n token,\n storage: { markMaterialized: async () => {} },\n });\n\n if (job.setupCommand) {\n await runWorktreeSetup(sandbox, job.workdir, job.setupCommand);\n }\n\n const head = await sh(sandbox, `git -C ${shellQuote(job.workdir)} rev-parse HEAD`);\n if (head.exitCode !== 0) {\n throw new Error(`Failed to read HEAD after materialize: ${head.stderr}`);\n }\n const sha = head.stdout.trim();\n\n await sandbox.snapshot();\n\n await job.storage.projectRepositories.setBaseCheckpoint({\n id: job.projectRepositoryId,\n checkpoint: {\n name: checkpointName,\n sha,\n builtAt: new Date(),\n setupCommandHash: hashSetupCommand(job.setupCommand),\n },\n expectedSetupCommand: job.setupCommand,\n });\n } finally {\n // Best-effort teardown: the builder VM is single-use. Route cleanup\n // through the fleet so the live-sandbox budget is released as well.\n await this.#fleet.teardownSandbox(binding, sandbox).catch(() => {});\n }\n }\n}\n"],"mappings":"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"}
@@ -1,258 +0,0 @@
1
- /**
2
- * Project sandbox fleet: provisioning, reattach, teardown, and budgeting.
3
- *
4
- * Server-hosted projects never run on the web host itself. Each project gets
5
- * its own isolated sandbox (a `WorkspaceSandbox`, e.g. a Railway VM) `clone()`d
6
- * from the machine the factory was configured with. This module owns everything
7
- * about that fleet — which provider is active, where checkouts live inside a
8
- * sandbox, the idle window, the per-replica budget, and the
9
- * provision/reattach/teardown lifecycle — but knows nothing about what runs
10
- * inside a sandbox (git materialization lives with its feature, e.g. the
11
- * GitHub integration's `sandbox.ts`).
12
- *
13
- * The fleet is constructed once at boot with the machine config (or none, when
14
- * sandboxes are disabled) and handed to consumers — no global registry.
15
- * Persistence of the provider's reattach id is delegated to the caller via
16
- * {@link SandboxBindingStore}, so the fleet stays storage-agnostic. Tests can
17
- * swap the low-level construction via {@link SandboxFleet.setFactory}.
18
- */
19
- import type { WorkspaceSandbox } from '@mastra/core/workspace';
20
- /** Minimal command result shape sandbox consumers depend on. */
21
- export interface SandboxCommandResult {
22
- exitCode: number;
23
- stdout: string;
24
- stderr: string;
25
- }
26
- /**
27
- * Minimal live-sandbox surface fleet consumers need: an id, a way to start it,
28
- * a way to learn the provider's reattach id, and command execution.
29
- */
30
- export interface MaterializationSandbox {
31
- readonly id: string;
32
- /** Human-readable provider name, forwarded from the underlying sandbox. */
33
- readonly name?: string;
34
- /** Provider type discriminator, forwarded from the underlying sandbox. */
35
- readonly provider?: string;
36
- /** Sandbox usage instructions surfaced in tool descriptions. */
37
- getInstructions?(opts?: {
38
- requestContext?: unknown;
39
- }): string;
40
- /** Long-running process capability, when the provider supports it. */
41
- readonly processes?: WorkspaceSandbox['processes'];
42
- /** Mount capability, when the provider supports it. */
43
- readonly mounts?: WorkspaceSandbox['mounts'];
44
- start(): Promise<void>;
45
- getInfo(): Promise<{
46
- metadata?: Record<string, unknown>;
47
- }>;
48
- executeCommand(command: string, args?: string[], options?: {
49
- timeout?: number;
50
- env?: Record<string, string | undefined>;
51
- }): Promise<SandboxCommandResult>;
52
- /** Update an environment variable for future commands in this sandbox. */
53
- setEnvironmentVariable?(name: string, value: string): void;
54
- /** Tear down the underlying VM. Optional: providers without it are no-ops. */
55
- stop?(): Promise<void>;
56
- /** True when the provider persists real checkpoints (snapshot is not a no-op). */
57
- readonly supportsCheckpoints?: boolean;
58
- /** Persist the sandbox's current state under its bound checkpoint name. */
59
- snapshot?(): Promise<void>;
60
- /** Boot-only fallback checkpoint requested for this fresh provision. */
61
- seedCheckpointNameUsed?: string;
62
- }
63
- /** Options for building (or reattaching) one sandbox. */
64
- export interface SandboxCreateOptions {
65
- /** Reattach to this existing provider VM instead of provisioning a new one. */
66
- providerSandboxId?: string;
67
- /**
68
- * Environment variables for commands run in the sandbox. Adapter-level
69
- * only: merged into every `executeCommand`, never baked into the provider
70
- * VM (see `SandboxFleet.#build`).
71
- */
72
- env?: Record<string, string>;
73
- /** Provider working directory for this sandbox. */
74
- workingDirectory?: string;
75
- /** Idle teardown window (minutes). The provider stops the VM after this idle period. */
76
- idleTimeoutMinutes?: number;
77
- /** Provider checkpoint used to seed and preserve this sandbox's filesystem. */
78
- checkpointName?: string;
79
- /**
80
- * Boot-only fallback checkpoint used when `checkpointName` has no stored
81
- * state yet (e.g. the repo base checkpoint for a brand-new session).
82
- * Snapshots keep writing to `checkpointName`.
83
- */
84
- seedCheckpointName?: string;
85
- /** Opaque user subject attributed to provider API requests. */
86
- actingUserId?: string;
87
- }
88
- /**
89
- * A coarse-grained step of the sandbox-preparation flow, reported as it happens
90
- * so the UI can show the user what the server is doing instead of a static
91
- * "Preparing…" toast. `phase` is a stable machine token; `message` is
92
- * user-facing copy.
93
- */
94
- export interface PrepareProgress {
95
- phase: 'reattaching' | 'provisioning' | 'preparing-workspace' | 'cloning' | 'pulling' | 'finalizing' | 'done';
96
- message: string;
97
- }
98
- /** Callback invoked with each preparation step. Best-effort; never throws. */
99
- export type ProgressFn = (event: PrepareProgress) => void;
100
- /** Invoke a progress callback without letting it break the actual work. */
101
- export declare function reportProgress(onProgress: ProgressFn | undefined, event: PrepareProgress): void;
102
- /**
103
- * Factory that builds a (not-yet-started) sandbox. When `providerSandboxId` is
104
- * provided the sandbox should reattach to that existing VM instead of
105
- * provisioning a new one.
106
- */
107
- export type SandboxFactory = (opts: SandboxCreateOptions) => MaterializationSandbox;
108
- /** Raised when provisioning would exceed the per-replica sandbox budget. */
109
- export declare class SandboxBudgetError extends Error {
110
- readonly max: number;
111
- readonly code: "sandbox-budget-exceeded";
112
- constructor(max: number);
113
- }
114
- /** Optional knobs for provisioning/reattaching one sandbox. */
115
- export interface EnsureSandboxOptions {
116
- /** Provider working directory for this sandbox. */
117
- workingDirectory?: string;
118
- /** Opaque user subject attributed to provider API requests. */
119
- actingUserId?: string;
120
- }
121
- /**
122
- * Where a feature persists its sandbox binding. The fleet reads the stored
123
- * reattach id and writes updates through this seam so it stays agnostic of
124
- * the owning table (GitHub projects today, anything else tomorrow).
125
- */
126
- export interface SandboxBindingStore {
127
- /** Stored provider reattach id from a previous provisioning, if any. */
128
- readonly sandboxId: string | null;
129
- /** Provider checkpoint used to seed and preserve this sandbox's filesystem. */
130
- readonly checkpointName?: string;
131
- /** Boot-only fallback checkpoint (e.g. repo base checkpoint) for first provision. */
132
- readonly seedCheckpointName?: string;
133
- /** Persist a freshly provisioned provider id, or clear a stale one with `null`. */
134
- setSandboxId(id: string | null): Promise<void>;
135
- /** Clear all stored sandbox state (reattach id + materialization mark) on teardown. */
136
- clear(): Promise<void>;
137
- }
138
- /** Resolve a workdir under `root`, refusing any path that escapes the configured root. */
139
- export declare function resolveContainedLocalWorkdir(root: string, ...segments: string[]): string;
140
- /**
141
- * Factory-resolved sandbox runtime the fleet is constructed with: the machine
142
- * projects clone their per-project sandboxes from, plus the knobs the factory
143
- * resolved around it.
144
- */
145
- export interface SandboxFleetConfig {
146
- /**
147
- * Template machine (validated by the factory to implement `clone()`).
148
- * Never started — acts purely as the credential/default holder that
149
- * per-project sandboxes are cloned from.
150
- */
151
- machine: WorkspaceSandbox;
152
- /** In-sandbox base directory repos check out under (no trailing slash). */
153
- workdirBase: string;
154
- /** Per-replica cap on concurrently provisioned sandboxes. 0 = unlimited. */
155
- maxSandboxes?: number;
156
- }
157
- /**
158
- * The sandbox fleet for one deployment. Constructed once at boot — with a
159
- * config when a sandbox machine was configured, or without one when sandboxes
160
- * are disabled (every provisioning entry point then throws and
161
- * {@link enabled} reports `false` so features stay off).
162
- */
163
- export declare class SandboxFleet {
164
- #private;
165
- constructor(config?: SandboxFleetConfig);
166
- /**
167
- * True when a sandbox machine was configured. The factory validates the
168
- * machine implements `clone()` at boot, so a configured fleet is usable —
169
- * sandbox-backed projects stay off only when the slot was omitted.
170
- */
171
- get enabled(): boolean;
172
- /**
173
- * Name of the active sandbox provider — the configured machine's `provider`
174
- * discriminator (`'railway'`, `'local'`, …), or `'none'` when the fleet was
175
- * constructed without a config. Diagnostic only; feature gating goes
176
- * through {@link enabled}.
177
- */
178
- get provider(): string;
179
- /**
180
- * Usage instructions from the configured template machine, for surfacing in
181
- * tool descriptions before any per-session sandbox has materialized.
182
- * Empty string when no machine is configured or it exposes none.
183
- */
184
- getInstructions(): string;
185
- /**
186
- * Idle teardown window for provisioned sandboxes, in minutes; defaults to 30.
187
- * Read back from the machine's own config when it exposes one
188
- * (Railway's `idleTimeoutMinutes`) — the knob lives on the sandbox, the
189
- * fleet only needs it to schedule GC and stamp sandbox clones. Advisory:
190
- * providers without idle GC ignore it, and a re-open detects a torn-down VM
191
- * and re-provisions cleanly.
192
- */
193
- get idleMinutes(): number;
194
- /**
195
- * Per-replica cap on concurrently *provisioned* sandboxes. 0 means unlimited.
196
- * This is a lightweight per-process budget to keep a single replica from
197
- * exhausting provider quota — it is not a global, cross-replica scheduler
198
- * (that is a deferred follow-up).
199
- */
200
- get maxSandboxes(): number;
201
- /**
202
- * Count of sandboxes this fleet has freshly provisioned and not yet torn
203
- * down. Reattaches to existing VMs do not count (they reuse an already-billed
204
- * sandbox). Used to enforce {@link maxSandboxes}.
205
- */
206
- get liveCount(): number;
207
- /** For tests: reset the live-sandbox counter to a known state. */
208
- __resetLiveCount(value?: number): void;
209
- /** Override the sandbox factory (tests). */
210
- setFactory(factory: SandboxFactory): void;
211
- /** Reset to the default machine-cloning factory. */
212
- resetFactory(): void;
213
- /**
214
- * Compute the in-sandbox working directory for a repo: a nested
215
- * `<base>/<owner>/<name>` layout under the factory-resolved checkout base.
216
- * Nesting keeps same-name repos apart (`acme/api` vs `other/api`) — cloud
217
- * sandboxes are one-per-project so it's merely tidy there, but local
218
- * checkouts share one host root where it prevents collisions. Server-side
219
- * only; never derived from client input.
220
- */
221
- computeWorkdir(repoFullName: string): string;
222
- /**
223
- * Compute the host working directory for a local GitHub session checkout.
224
- * This is server-derived only: repo pieces are sanitized and the trusted
225
- * session id is kept as a single path segment under the configured local root.
226
- */
227
- computeLocalSessionWorkdir(repoFullName: string, sessionId: string): string;
228
- /**
229
- * Provision a new sandbox (persisting its provider id on first open) or
230
- * reattach to the stored one. Returns a started, live sandbox.
231
- *
232
- * Concurrent calls for the same binding coalesce onto one in-flight
233
- * provision/reattach and share its sandbox handle — N simultaneous requests
234
- * for one cold session (e.g. several browser tabs polling right after boot)
235
- * must not each fire their own `POST /sandbox` against the provider.
236
- * Failures are not cached: once the shared attempt settles, the next call
237
- * starts fresh.
238
- */
239
- ensureSandbox(store: SandboxBindingStore, onProgress?: ProgressFn): Promise<MaterializationSandbox>;
240
- ensureSandbox(store: SandboxBindingStore, env?: Record<string, string>, onProgress?: ProgressFn, options?: EnsureSandboxOptions): Promise<MaterializationSandbox>;
241
- /**
242
- * Tear down a sandbox binding: stop the live VM (best-effort) and clear the
243
- * persisted state through the binding store so the next open re-provisions
244
- * cleanly. Decrements the per-replica live-sandbox counter.
245
- *
246
- * @param store the binding to tear down
247
- * @param sandbox an already-reattached live sandbox to stop, when available
248
- */
249
- teardownSandbox(store: SandboxBindingStore, sandbox?: MaterializationSandbox): Promise<void>;
250
- /**
251
- * Reattach to an already-provisioned sandbox by its provider id and start it.
252
- * Used by the workspace seam when opening a project that was already
253
- * materialized (sandbox id + workdir carried on controller state), so no DB
254
- * round-trip is needed.
255
- */
256
- reattachSandbox(providerSandboxId: string, options?: EnsureSandboxOptions): Promise<MaterializationSandbox>;
257
- }
258
- //# sourceMappingURL=fleet.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"fleet.d.ts","sourceRoot":"","sources":["../../src/sandbox/fleet.ts"],"names":[],"mappings":"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"}