@veris-ai/daytona-opencode 0.1.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/.opencode/plugin/daytona/core/logger.d.ts +15 -0
- package/.opencode/plugin/daytona/core/logger.js +65 -0
- package/.opencode/plugin/daytona/core/logger.js.map +1 -0
- package/.opencode/plugin/daytona/core/logger.ts +71 -0
- package/.opencode/plugin/daytona/core/project-data-storage.d.ts +63 -0
- package/.opencode/plugin/daytona/core/project-data-storage.js +213 -0
- package/.opencode/plugin/daytona/core/project-data-storage.js.map +1 -0
- package/.opencode/plugin/daytona/core/project-data-storage.ts +234 -0
- package/.opencode/plugin/daytona/core/session-manager.d.ts +74 -0
- package/.opencode/plugin/daytona/core/session-manager.js +441 -0
- package/.opencode/plugin/daytona/core/session-manager.js.map +1 -0
- package/.opencode/plugin/daytona/core/session-manager.ts +471 -0
- package/.opencode/plugin/daytona/core/toast.d.ts +47 -0
- package/.opencode/plugin/daytona/core/toast.js +70 -0
- package/.opencode/plugin/daytona/core/toast.js.map +1 -0
- package/.opencode/plugin/daytona/core/toast.ts +96 -0
- package/.opencode/plugin/daytona/core/types.d.ts +53 -0
- package/.opencode/plugin/daytona/core/types.js +12 -0
- package/.opencode/plugin/daytona/core/types.js.map +1 -0
- package/.opencode/plugin/daytona/core/types.ts +72 -0
- package/.opencode/plugin/daytona/git/host-git-manager.d.ts +58 -0
- package/.opencode/plugin/daytona/git/host-git-manager.js +342 -0
- package/.opencode/plugin/daytona/git/host-git-manager.js.map +1 -0
- package/.opencode/plugin/daytona/git/host-git-manager.ts +372 -0
- package/.opencode/plugin/daytona/git/index.d.ts +5 -0
- package/.opencode/plugin/daytona/git/index.js +6 -0
- package/.opencode/plugin/daytona/git/index.js.map +1 -0
- package/.opencode/plugin/daytona/git/index.ts +6 -0
- package/.opencode/plugin/daytona/git/sandbox-git-manager.d.ts +22 -0
- package/.opencode/plugin/daytona/git/sandbox-git-manager.js +80 -0
- package/.opencode/plugin/daytona/git/sandbox-git-manager.js.map +1 -0
- package/.opencode/plugin/daytona/git/sandbox-git-manager.ts +92 -0
- package/.opencode/plugin/daytona/git/session-git-manager.d.ts +54 -0
- package/.opencode/plugin/daytona/git/session-git-manager.js +184 -0
- package/.opencode/plugin/daytona/git/session-git-manager.js.map +1 -0
- package/.opencode/plugin/daytona/git/session-git-manager.ts +203 -0
- package/.opencode/plugin/daytona/index.d.ts +125 -0
- package/.opencode/plugin/daytona/index.js +60 -0
- package/.opencode/plugin/daytona/index.js.map +1 -0
- package/.opencode/plugin/daytona/index.ts +73 -0
- package/.opencode/plugin/daytona/plugins/custom-tools.d.ts +122 -0
- package/.opencode/plugin/daytona/plugins/custom-tools.js +18 -0
- package/.opencode/plugin/daytona/plugins/custom-tools.js.map +1 -0
- package/.opencode/plugin/daytona/plugins/custom-tools.ts +21 -0
- package/.opencode/plugin/daytona/plugins/session-events.d.ts +10 -0
- package/.opencode/plugin/daytona/plugins/session-events.js +63 -0
- package/.opencode/plugin/daytona/plugins/session-events.js.map +1 -0
- package/.opencode/plugin/daytona/plugins/session-events.ts +61 -0
- package/.opencode/plugin/daytona/plugins/system-transform.d.ts +10 -0
- package/.opencode/plugin/daytona/plugins/system-transform.js +38 -0
- package/.opencode/plugin/daytona/plugins/system-transform.js.map +1 -0
- package/.opencode/plugin/daytona/plugins/system-transform.ts +43 -0
- package/.opencode/plugin/daytona/tools/bash.d.ts +19 -0
- package/.opencode/plugin/daytona/tools/bash.js +42 -0
- package/.opencode/plugin/daytona/tools/bash.js.map +1 -0
- package/.opencode/plugin/daytona/tools/bash.ts +51 -0
- package/.opencode/plugin/daytona/tools/edit.d.ts +21 -0
- package/.opencode/plugin/daytona/tools/edit.js +33 -0
- package/.opencode/plugin/daytona/tools/edit.js.map +1 -0
- package/.opencode/plugin/daytona/tools/edit.ts +44 -0
- package/.opencode/plugin/daytona/tools/get-preview-url.d.ts +17 -0
- package/.opencode/plugin/daytona/tools/get-preview-url.js +17 -0
- package/.opencode/plugin/daytona/tools/get-preview-url.js.map +1 -0
- package/.opencode/plugin/daytona/tools/get-preview-url.ts +26 -0
- package/.opencode/plugin/daytona/tools/git-sync.d.ts +12 -0
- package/.opencode/plugin/daytona/tools/git-sync.js +26 -0
- package/.opencode/plugin/daytona/tools/git-sync.js.map +1 -0
- package/.opencode/plugin/daytona/tools/git-sync.ts +36 -0
- package/.opencode/plugin/daytona/tools/glob.d.ts +17 -0
- package/.opencode/plugin/daytona/tools/glob.js +21 -0
- package/.opencode/plugin/daytona/tools/glob.js.map +1 -0
- package/.opencode/plugin/daytona/tools/glob.ts +30 -0
- package/.opencode/plugin/daytona/tools/grep.d.ts +17 -0
- package/.opencode/plugin/daytona/tools/grep.js +29 -0
- package/.opencode/plugin/daytona/tools/grep.js.map +1 -0
- package/.opencode/plugin/daytona/tools/grep.ts +39 -0
- package/.opencode/plugin/daytona/tools/ls.d.ts +17 -0
- package/.opencode/plugin/daytona/tools/ls.js +22 -0
- package/.opencode/plugin/daytona/tools/ls.js.map +1 -0
- package/.opencode/plugin/daytona/tools/ls.ts +32 -0
- package/.opencode/plugin/daytona/tools/multiedit.d.ts +25 -0
- package/.opencode/plugin/daytona/tools/multiedit.js +37 -0
- package/.opencode/plugin/daytona/tools/multiedit.js.map +1 -0
- package/.opencode/plugin/daytona/tools/multiedit.ts +54 -0
- package/.opencode/plugin/daytona/tools/read.d.ts +17 -0
- package/.opencode/plugin/daytona/tools/read.js +18 -0
- package/.opencode/plugin/daytona/tools/read.js.map +1 -0
- package/.opencode/plugin/daytona/tools/read.ts +27 -0
- package/.opencode/plugin/daytona/tools/veris-receipt.d.ts +26 -0
- package/.opencode/plugin/daytona/tools/veris-receipt.js +76 -0
- package/.opencode/plugin/daytona/tools/veris-receipt.js.map +1 -0
- package/.opencode/plugin/daytona/tools/veris-receipt.ts +103 -0
- package/.opencode/plugin/daytona/tools/write.d.ts +19 -0
- package/.opencode/plugin/daytona/tools/write.js +18 -0
- package/.opencode/plugin/daytona/tools/write.js.map +1 -0
- package/.opencode/plugin/daytona/tools/write.ts +27 -0
- package/.opencode/plugin/daytona/tools.d.ts +119 -0
- package/.opencode/plugin/daytona/tools.js +35 -0
- package/.opencode/plugin/daytona/tools.js.map +1 -0
- package/.opencode/plugin/daytona/tools.ts +45 -0
- package/.opencode/plugin/index.d.ts +9 -0
- package/.opencode/plugin/index.js +10 -0
- package/.opencode/plugin/index.js.map +1 -0
- package/.opencode/plugin/index.ts +11 -0
- package/README.md +87 -0
- package/package.json +52 -0
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright Daytona Platforms Inc.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Manages Daytona sandbox sessions and persists session-sandbox mappings
|
|
7
|
+
* Stores data per-project in ~/.local/share/opencode/storage/daytona/{projectId}.json
|
|
8
|
+
*/
|
|
9
|
+
import { type Sandbox } from '@veris-ai/daytona';
|
|
10
|
+
import type { PluginInput } from '@opencode-ai/plugin';
|
|
11
|
+
export declare class DaytonaSessionManager {
|
|
12
|
+
private readonly apiKey;
|
|
13
|
+
private readonly dataStorage;
|
|
14
|
+
private sessionSandboxes;
|
|
15
|
+
private readonly deletingSessions;
|
|
16
|
+
private readonly deletionPromises;
|
|
17
|
+
private currentProjectId?;
|
|
18
|
+
readonly repoPath: string;
|
|
19
|
+
/** Snapshot new sandboxes are created from; undefined uses Daytona's default snapshot. */
|
|
20
|
+
readonly snapshot?: string;
|
|
21
|
+
constructor(apiKey: string, storageDir: string, repoPath: string, snapshot?: string);
|
|
22
|
+
/**
|
|
23
|
+
* Check if a sandbox is fully initialized (has process property)
|
|
24
|
+
*/
|
|
25
|
+
private isFullyInitialized;
|
|
26
|
+
/**
|
|
27
|
+
* Check if a sandbox is partially initialized (has id but not process)
|
|
28
|
+
*/
|
|
29
|
+
private isPartiallyInitialized;
|
|
30
|
+
/**
|
|
31
|
+
* Load sessions for a specific project into memory
|
|
32
|
+
*/
|
|
33
|
+
private loadProjectSessions;
|
|
34
|
+
/**
|
|
35
|
+
* Set the current project context
|
|
36
|
+
*/
|
|
37
|
+
setProjectContext(projectId: string): void;
|
|
38
|
+
/**
|
|
39
|
+
* Get branch number for a sandbox
|
|
40
|
+
*/
|
|
41
|
+
getBranchNumberForSandbox(projectId: string, sandboxId: string): number | undefined;
|
|
42
|
+
/**
|
|
43
|
+
* Get or create a sandbox for the given session ID
|
|
44
|
+
*/
|
|
45
|
+
getSandbox(sessionId: string, projectId: string, worktree: string, pluginCtx?: PluginInput): Promise<Sandbox>;
|
|
46
|
+
/**
|
|
47
|
+
* Delete the sandbox associated with the given session ID
|
|
48
|
+
*/
|
|
49
|
+
deleteSandbox(sessionId: string, projectId: string): Promise<boolean>;
|
|
50
|
+
private deleteSandboxInner;
|
|
51
|
+
/**
|
|
52
|
+
* Pull not-yet-synced sandbox changes into the local repo before the sandbox is
|
|
53
|
+
* destroyed. Throws — aborting deletion so the sandbox is preserved — when unsynced
|
|
54
|
+
* changes cannot be pulled, including when the local repository itself is no longer
|
|
55
|
+
* accessible (a silent skip there would destroy the only copy of the work). A sandbox
|
|
56
|
+
* that is not running is deleted without being started: anything in it was either
|
|
57
|
+
* synced while it ran or is abandoned by the explicit delete.
|
|
58
|
+
*
|
|
59
|
+
* Runs inside the session's sync queue; it must NOT enqueue (that would deadlock).
|
|
60
|
+
*/
|
|
61
|
+
private syncBeforeDelete;
|
|
62
|
+
/**
|
|
63
|
+
* Read-only check for a session→sandbox mapping (memory or storage); never creates,
|
|
64
|
+
* migrates, or connects.
|
|
65
|
+
*/
|
|
66
|
+
hasSandbox(sessionId: string, projectId: string): boolean;
|
|
67
|
+
isSessionDeleting(sessionId: string): boolean;
|
|
68
|
+
/**
|
|
69
|
+
* Guard for registration points that follow an await: deletion may have started (and
|
|
70
|
+
* finished) while a sandbox was being refreshed or reconnected, and persisting the
|
|
71
|
+
* mapping afterwards would resurrect state for a session that no longer exists.
|
|
72
|
+
*/
|
|
73
|
+
private ensureNotDeleted;
|
|
74
|
+
}
|
|
@@ -0,0 +1,441 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright Daytona Platforms Inc.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Manages Daytona sandbox sessions and persists session-sandbox mappings
|
|
7
|
+
* Stores data per-project in ~/.local/share/opencode/storage/daytona/{projectId}.json
|
|
8
|
+
*/
|
|
9
|
+
// The Veris fork's entire diff to this file is the module named below.
|
|
10
|
+
//
|
|
11
|
+
// @veris-ai/daytona re-exports all of @daytona/sdk and overrides only
|
|
12
|
+
// `Daytona`, so every `new Daytona({ apiKey })` in this file now returns a
|
|
13
|
+
// client whose create() also registers the Veris snapshot, provisions a twin,
|
|
14
|
+
// sets the domain allowlist, and waits for veris-proxy to bind — and whose
|
|
15
|
+
// get() rehydrates that surface on reconnect. The sandboxes handed back carry
|
|
16
|
+
// `.veris` for receipts, and their delete() removes the twin as well.
|
|
17
|
+
//
|
|
18
|
+
// DaytonaNotFoundError and DaytonaValidationError below are the SAME class
|
|
19
|
+
// objects @daytona/sdk exports (it is a peer dependency of @veris-ai/daytona),
|
|
20
|
+
// so the `instanceof` checks further down keep working — including against
|
|
21
|
+
// errors raised in tools/bash.ts, which still imports from '@daytona/sdk'.
|
|
22
|
+
import { Daytona, DaytonaNotFoundError, DaytonaValidationError, } from '@veris-ai/daytona';
|
|
23
|
+
import { logger } from './logger';
|
|
24
|
+
import { SessionGitManager } from '../git/session-git-manager';
|
|
25
|
+
import { DaytonaSandboxGitManager } from '../git/sandbox-git-manager';
|
|
26
|
+
import { ProjectDataStorage } from './project-data-storage';
|
|
27
|
+
import { toast } from './toast';
|
|
28
|
+
export class DaytonaSessionManager {
|
|
29
|
+
apiKey;
|
|
30
|
+
dataStorage;
|
|
31
|
+
sessionSandboxes;
|
|
32
|
+
// Sessions whose sandbox teardown has begun. getSandbox creates sandboxes on demand,
|
|
33
|
+
// so without this tombstone a sync queued behind a deletion would resurrect a fresh
|
|
34
|
+
// sandbox for a session that no longer exists (invisible, billed, never cleaned up).
|
|
35
|
+
deletingSessions = new Set();
|
|
36
|
+
deletionPromises = new Map();
|
|
37
|
+
currentProjectId;
|
|
38
|
+
repoPath;
|
|
39
|
+
/** Snapshot new sandboxes are created from; undefined uses Daytona's default snapshot. */
|
|
40
|
+
snapshot;
|
|
41
|
+
constructor(apiKey, storageDir, repoPath, snapshot) {
|
|
42
|
+
this.apiKey = apiKey;
|
|
43
|
+
this.dataStorage = new ProjectDataStorage(storageDir);
|
|
44
|
+
this.repoPath = repoPath;
|
|
45
|
+
this.snapshot = snapshot?.trim() || undefined;
|
|
46
|
+
this.sessionSandboxes = new Map();
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Check if a sandbox is fully initialized (has process property)
|
|
50
|
+
*/
|
|
51
|
+
isFullyInitialized(sandbox) {
|
|
52
|
+
return sandbox !== undefined && 'process' in sandbox;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Check if a sandbox is partially initialized (has id but not process)
|
|
56
|
+
*/
|
|
57
|
+
isPartiallyInitialized(sandbox) {
|
|
58
|
+
return sandbox !== undefined && 'id' in sandbox && !('process' in sandbox);
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Load sessions for a specific project into memory
|
|
62
|
+
*/
|
|
63
|
+
loadProjectSessions(projectId) {
|
|
64
|
+
const projectData = this.dataStorage.load(projectId);
|
|
65
|
+
if (projectData) {
|
|
66
|
+
for (const [sessionId, sessionInfo] of Object.entries(projectData.sessions)) {
|
|
67
|
+
this.sessionSandboxes.set(sessionId, { id: sessionInfo.sandboxId });
|
|
68
|
+
}
|
|
69
|
+
logger.info(`Loaded ${Object.keys(projectData.sessions).length} sessions for project ${projectId}`);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Set the current project context
|
|
74
|
+
*/
|
|
75
|
+
setProjectContext(projectId) {
|
|
76
|
+
if (this.currentProjectId !== projectId) {
|
|
77
|
+
this.currentProjectId = projectId;
|
|
78
|
+
this.sessionSandboxes.clear();
|
|
79
|
+
this.loadProjectSessions(projectId);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Get branch number for a sandbox
|
|
84
|
+
*/
|
|
85
|
+
getBranchNumberForSandbox(projectId, sandboxId) {
|
|
86
|
+
return this.dataStorage.getBranchNumberForSandbox(projectId, sandboxId);
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Get or create a sandbox for the given session ID
|
|
90
|
+
*/
|
|
91
|
+
async getSandbox(sessionId, projectId, worktree, pluginCtx) {
|
|
92
|
+
if (pluginCtx?.client?.tui) {
|
|
93
|
+
toast.initialize(pluginCtx.client.tui);
|
|
94
|
+
}
|
|
95
|
+
if (this.deletingSessions.has(sessionId)) {
|
|
96
|
+
throw new Error(`Session ${sessionId} is deleted; not creating a new sandbox for it.`);
|
|
97
|
+
}
|
|
98
|
+
if (!this.apiKey) {
|
|
99
|
+
logger.error('DAYTONA_API_KEY is not set. Cannot create or retrieve sandbox.');
|
|
100
|
+
toast.show({
|
|
101
|
+
title: 'Sandbox error',
|
|
102
|
+
message: 'DAYTONA_API_KEY is not set. Please set the environment variable to use Daytona sandboxes.',
|
|
103
|
+
variant: 'error',
|
|
104
|
+
});
|
|
105
|
+
throw new Error('DAYTONA_API_KEY is not set. Please set the environment variable to use Daytona sandboxes.');
|
|
106
|
+
}
|
|
107
|
+
// Veris coordinates get the same treatment as the Daytona key, and for the
|
|
108
|
+
// same reason: this is the first tool call of a session, and a legible
|
|
109
|
+
// message here is the difference between "set one env var" and a stack
|
|
110
|
+
// trace out of sandbox creation.
|
|
111
|
+
//
|
|
112
|
+
// Deliberately fatal rather than degrading to a plain Daytona sandbox. A
|
|
113
|
+
// sandbox without a twin reaches the REAL vendor, and an agent cannot tell
|
|
114
|
+
// the difference — so an unset key has to stop the session, not quietly
|
|
115
|
+
// change what it is doing.
|
|
116
|
+
for (const [name, hint] of [
|
|
117
|
+
['VERIS_API_KEY', 'Get one at https://studio.veris.ai'],
|
|
118
|
+
['VERIS_ENVIRONMENT_ID', 'A Veris environment decides which vendor services your twin gets'],
|
|
119
|
+
]) {
|
|
120
|
+
if (process.env[name])
|
|
121
|
+
continue;
|
|
122
|
+
const message = `${name} is not set, so this sandbox would reach real vendor APIs. ${hint}.`;
|
|
123
|
+
logger.error(message);
|
|
124
|
+
toast.show({ title: 'Veris not configured', message, variant: 'error' });
|
|
125
|
+
throw new Error(message);
|
|
126
|
+
}
|
|
127
|
+
// Load project sessions if needed
|
|
128
|
+
this.setProjectContext(projectId);
|
|
129
|
+
const existing = this.sessionSandboxes.get(sessionId);
|
|
130
|
+
// If we have a fully initialized sandbox, reuse it
|
|
131
|
+
if (this.isFullyInitialized(existing)) {
|
|
132
|
+
// Refresh sandbox state and ensure it's running
|
|
133
|
+
await existing.refreshData();
|
|
134
|
+
if (existing.state !== 'started') {
|
|
135
|
+
logger.info(`Starting sandbox ${existing.id} (current state: ${existing.state})`);
|
|
136
|
+
await existing.start();
|
|
137
|
+
}
|
|
138
|
+
this.ensureNotDeleted(sessionId);
|
|
139
|
+
this.dataStorage.updateSession(projectId, worktree, sessionId, existing.id);
|
|
140
|
+
return existing;
|
|
141
|
+
}
|
|
142
|
+
// If we have a sandboxId but not a full sandbox object, reconnect to it
|
|
143
|
+
if (this.isPartiallyInitialized(existing)) {
|
|
144
|
+
try {
|
|
145
|
+
logger.info(`Reconnecting to existing sandbox: ${existing.id}`);
|
|
146
|
+
const daytona = new Daytona({ apiKey: this.apiKey });
|
|
147
|
+
const reconnectStart = Date.now();
|
|
148
|
+
logger.info(`Daytona get begin sandboxId=${existing.id}`);
|
|
149
|
+
const sandbox = await daytona.get(existing.id);
|
|
150
|
+
logger.info(`Daytona get done sandboxId=${existing.id} in ${Date.now() - reconnectStart}ms`);
|
|
151
|
+
logger.info(`Starting sandbox begin sandboxId=${sandbox.id}`);
|
|
152
|
+
await sandbox.start();
|
|
153
|
+
logger.info(`Starting sandbox done sandboxId=${sandbox.id} in ${Date.now() - reconnectStart}ms`);
|
|
154
|
+
this.ensureNotDeleted(sessionId);
|
|
155
|
+
this.sessionSandboxes.set(sessionId, sandbox);
|
|
156
|
+
// Preserve branch number if it exists for this sandbox
|
|
157
|
+
let branchNumber = this.dataStorage.getBranchNumberForSandbox(projectId, sandbox.id);
|
|
158
|
+
if (!branchNumber) {
|
|
159
|
+
try {
|
|
160
|
+
branchNumber = SessionGitManager.allocateAndReserveBranchNumber(worktree);
|
|
161
|
+
}
|
|
162
|
+
catch {
|
|
163
|
+
// No local git repo (or git unavailable) shouldn't block sandbox usage.
|
|
164
|
+
branchNumber = undefined;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
this.dataStorage.updateSession(projectId, worktree, sessionId, sandbox.id, branchNumber);
|
|
168
|
+
toast.show({
|
|
169
|
+
title: 'Sandbox connected',
|
|
170
|
+
message: `Connected to existing sandbox.`,
|
|
171
|
+
variant: 'info',
|
|
172
|
+
});
|
|
173
|
+
// Even if git syncing is disabled, ensure the project directory exists in the sandbox.
|
|
174
|
+
if (!branchNumber) {
|
|
175
|
+
try {
|
|
176
|
+
await new DaytonaSandboxGitManager(sandbox, this.repoPath).ensureDirectory();
|
|
177
|
+
}
|
|
178
|
+
catch (err) {
|
|
179
|
+
logger.warn(`Failed to ensure sandbox project directory exists: ${err}`);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
return sandbox;
|
|
183
|
+
}
|
|
184
|
+
catch (err) {
|
|
185
|
+
// Only treat 404 as "sandbox is confirmed gone" — clear the mapping and fall through
|
|
186
|
+
// to create a replacement. For transient errors (network, auth, timeout, provisioning),
|
|
187
|
+
// preserve the mapping and propagate so the caller can retry later without losing the
|
|
188
|
+
// session→sandbox binding and its branchNumber.
|
|
189
|
+
if (err instanceof DaytonaNotFoundError) {
|
|
190
|
+
logger.error(`Sandbox ${existing.id} no longer exists; creating a replacement.`);
|
|
191
|
+
this.sessionSandboxes.delete(sessionId);
|
|
192
|
+
this.dataStorage.removeSession(projectId, worktree, sessionId);
|
|
193
|
+
}
|
|
194
|
+
else {
|
|
195
|
+
logger.error(`Failed to reconnect to sandbox ${existing.id}: ${err}`);
|
|
196
|
+
throw err;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
// If not in cache/storage for this project, try to recover from other projects and migrate.
|
|
201
|
+
if (!existing) {
|
|
202
|
+
const migrated = this.dataStorage.getSession(projectId, worktree, sessionId);
|
|
203
|
+
if (migrated?.sandboxId) {
|
|
204
|
+
logger.info(`Recovered session ${sessionId} for project ${projectId} (migrated from another project)`);
|
|
205
|
+
this.sessionSandboxes.set(sessionId, { id: migrated.sandboxId });
|
|
206
|
+
// Re-run getSandbox to go through the normal reconnect path.
|
|
207
|
+
return this.getSandbox(sessionId, projectId, worktree, pluginCtx);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
// Otherwise, create a new sandbox
|
|
211
|
+
logger.info(`Creating new sandbox for session: ${sessionId} in project: ${projectId}`);
|
|
212
|
+
const daytona = new Daytona({ apiKey: this.apiKey });
|
|
213
|
+
// Omit the key entirely when unset so Daytona applies its default snapshot.
|
|
214
|
+
const createParams = this.snapshot ? { snapshot: this.snapshot } : {};
|
|
215
|
+
const createStart = Date.now();
|
|
216
|
+
logger.info(`Daytona create begin sessionId=${sessionId} snapshot=${this.snapshot ?? '(default)'}`);
|
|
217
|
+
const waitingLog = setTimeout(() => {
|
|
218
|
+
logger.warn(`Daytona create still waiting after ${Date.now() - createStart}ms (sessionId=${sessionId})`);
|
|
219
|
+
}, 15_000);
|
|
220
|
+
const sandbox = await daytona
|
|
221
|
+
.create(createParams)
|
|
222
|
+
.catch((err) => {
|
|
223
|
+
// Only blame the snapshot when the request itself was rejected. The create
|
|
224
|
+
// params contain nothing but the snapshot name, so a validation error means
|
|
225
|
+
// Daytona refused that name (empirically: "Snapshot <name> not found" is a
|
|
226
|
+
// DaytonaValidationError). Auth, network, quota, and timeout failures are
|
|
227
|
+
// unrelated to DAYTONA_SNAPSHOT and propagate unattributed, as before.
|
|
228
|
+
if (this.snapshot && err instanceof DaytonaValidationError) {
|
|
229
|
+
logger.error(`Failed to create sandbox from snapshot '${this.snapshot}': ${err}`);
|
|
230
|
+
toast.show({
|
|
231
|
+
title: 'Sandbox error',
|
|
232
|
+
message: `Verify DAYTONA_SNAPSHOT names an available snapshot: ${err.message}`,
|
|
233
|
+
variant: 'error',
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
throw err;
|
|
237
|
+
})
|
|
238
|
+
.finally(() => clearTimeout(waitingLog));
|
|
239
|
+
logger.info(`Daytona create done sessionId=${sessionId} sandboxId=${sandbox.id} in ${Date.now() - createStart}ms`);
|
|
240
|
+
if (this.deletingSessions.has(sessionId)) {
|
|
241
|
+
// The session was deleted while creation was in flight. The fresh sandbox is not
|
|
242
|
+
// registered anywhere, so nothing else will ever clean it up - discard it here.
|
|
243
|
+
logger.warn(`Session ${sessionId} was deleted during sandbox creation; discarding sandbox ${sandbox.id}`);
|
|
244
|
+
try {
|
|
245
|
+
await sandbox.delete();
|
|
246
|
+
}
|
|
247
|
+
catch (err) {
|
|
248
|
+
logger.error(`Failed to discard sandbox ${sandbox.id} for deleted session ${sessionId}: ${err}`);
|
|
249
|
+
throw new Error(`Session ${sessionId} is deleted and discarding newly created sandbox ${sandbox.id} failed; if it still exists, delete it from the Daytona dashboard.`);
|
|
250
|
+
}
|
|
251
|
+
throw new Error(`Session ${sessionId} is deleted; the newly created sandbox was discarded.`);
|
|
252
|
+
}
|
|
253
|
+
this.sessionSandboxes.set(sessionId, sandbox);
|
|
254
|
+
// Get or assign branch number for this sandbox
|
|
255
|
+
let branchNumber = this.dataStorage.getBranchNumberForSandbox(projectId, sandbox.id);
|
|
256
|
+
if (!branchNumber) {
|
|
257
|
+
try {
|
|
258
|
+
branchNumber = SessionGitManager.allocateAndReserveBranchNumber(worktree);
|
|
259
|
+
}
|
|
260
|
+
catch (err) {
|
|
261
|
+
logger.warn(`allocateAndReserveBranchNumber failed sessionId=${sessionId}: ${err}`);
|
|
262
|
+
// No local git repo (or git unavailable) shouldn't block sandbox usage.
|
|
263
|
+
branchNumber = undefined;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
this.dataStorage.updateSession(projectId, worktree, sessionId, sandbox.id, branchNumber);
|
|
267
|
+
logger.info(`Sandbox created successfully: ${sandbox.id}${branchNumber ? ` with branch number ${branchNumber}` : ''}`);
|
|
268
|
+
// Initialize git repo in the sandbox and sync with host
|
|
269
|
+
try {
|
|
270
|
+
if (branchNumber) {
|
|
271
|
+
const sessionGit = new SessionGitManager(sandbox, this.repoPath, worktree, branchNumber);
|
|
272
|
+
await sessionGit.initializeAndSync(pluginCtx);
|
|
273
|
+
}
|
|
274
|
+
else {
|
|
275
|
+
// Git disabled; still ensure the directory exists so tools can operate.
|
|
276
|
+
await new DaytonaSandboxGitManager(sandbox, this.repoPath).ensureDirectory();
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
catch (err) {
|
|
280
|
+
logger.error(`Failed to initialize git repo or push local changes in sandbox: ${err}`);
|
|
281
|
+
toast.show({
|
|
282
|
+
title: 'Git error',
|
|
283
|
+
message: err?.message || 'Failed to initialize git repo in sandbox.',
|
|
284
|
+
variant: 'error',
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
// Deletion may have raced the initialization awaits above; the mapping was already
|
|
288
|
+
// registered, so the delete flow owns (and removes) the sandbox itself - returning
|
|
289
|
+
// it would hand callers a destroyed sandbox that fails confusingly on first use.
|
|
290
|
+
this.ensureNotDeleted(sessionId);
|
|
291
|
+
toast.show({
|
|
292
|
+
title: 'Sandbox created',
|
|
293
|
+
message: `Created new sandbox for session.`,
|
|
294
|
+
variant: 'success',
|
|
295
|
+
});
|
|
296
|
+
return sandbox;
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* Delete the sandbox associated with the given session ID
|
|
300
|
+
*/
|
|
301
|
+
async deleteSandbox(sessionId, projectId) {
|
|
302
|
+
// Concurrent deletes share one promise: a second teardown racing the first would
|
|
303
|
+
// observe the already-deleted sandbox, throw, and wrongly clear the tombstone.
|
|
304
|
+
const inFlight = this.deletionPromises.get(sessionId);
|
|
305
|
+
if (inFlight)
|
|
306
|
+
return inFlight;
|
|
307
|
+
// Tombstone first, removed again on failure: while set, no code path may create or
|
|
308
|
+
// reconnect a sandbox for this session. Kept after success on purpose - the session
|
|
309
|
+
// is gone, and any late event for it must no-op instead of resurrecting a sandbox.
|
|
310
|
+
this.deletingSessions.add(sessionId);
|
|
311
|
+
const run = (async () => {
|
|
312
|
+
try {
|
|
313
|
+
return await this.deleteSandboxInner(sessionId, projectId);
|
|
314
|
+
}
|
|
315
|
+
catch (err) {
|
|
316
|
+
this.deletingSessions.delete(sessionId);
|
|
317
|
+
throw err;
|
|
318
|
+
}
|
|
319
|
+
finally {
|
|
320
|
+
this.deletionPromises.delete(sessionId);
|
|
321
|
+
}
|
|
322
|
+
})();
|
|
323
|
+
this.deletionPromises.set(sessionId, run);
|
|
324
|
+
return run;
|
|
325
|
+
}
|
|
326
|
+
async deleteSandboxInner(sessionId, projectId) {
|
|
327
|
+
await SessionGitManager.waitForPendingSync(sessionId);
|
|
328
|
+
let sandbox = this.sessionSandboxes.get(sessionId);
|
|
329
|
+
// Read-only lookup so deleting never migrates sessions or rewrites project metadata.
|
|
330
|
+
const stored = this.dataStorage.findSession(sessionId);
|
|
331
|
+
let sandboxGone = false;
|
|
332
|
+
// If not in cache, try to load from storage and reconnect
|
|
333
|
+
if (!sandbox || this.isPartiallyInitialized(sandbox)) {
|
|
334
|
+
if (stored?.session.sandboxId) {
|
|
335
|
+
const daytona = new Daytona({ apiKey: this.apiKey });
|
|
336
|
+
try {
|
|
337
|
+
sandbox = await daytona.get(stored.session.sandboxId);
|
|
338
|
+
this.sessionSandboxes.set(sessionId, sandbox);
|
|
339
|
+
}
|
|
340
|
+
catch (err) {
|
|
341
|
+
if (err instanceof DaytonaNotFoundError) {
|
|
342
|
+
sandboxGone = true;
|
|
343
|
+
logger.warn(`Sandbox ${stored.session.sandboxId} no longer exists; clearing stale mapping.`);
|
|
344
|
+
}
|
|
345
|
+
else {
|
|
346
|
+
// Non-404: we cannot confirm the sandbox is gone. Surface the error so the
|
|
347
|
+
// event handler shows a "Delete failed" toast instead of silently reporting
|
|
348
|
+
// success while leaving a running sandbox on the Daytona account.
|
|
349
|
+
logger.error(`Failed to reconnect to sandbox ${stored.session.sandboxId}: ${err}`);
|
|
350
|
+
throw err;
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
else {
|
|
355
|
+
sandboxGone = true;
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
// Delete the sandbox if we have a fully initialized one
|
|
359
|
+
let deleted = false;
|
|
360
|
+
if (this.isFullyInitialized(sandbox)) {
|
|
361
|
+
// Final sync and deletion run as ONE queue entry, so a sync enqueued between the
|
|
362
|
+
// wait above and this point is drained first, and nothing can slot in between
|
|
363
|
+
// pulling the last changes and destroying the sandbox.
|
|
364
|
+
const target = sandbox;
|
|
365
|
+
await SessionGitManager.enqueueSessionSync(sessionId, async () => {
|
|
366
|
+
await this.syncBeforeDelete(target, stored);
|
|
367
|
+
logger.info(`Removing sandbox for session: ${sessionId}`);
|
|
368
|
+
await target.delete();
|
|
369
|
+
});
|
|
370
|
+
deleted = true;
|
|
371
|
+
sandboxGone = true;
|
|
372
|
+
logger.info(`Sandbox deleted successfully.`);
|
|
373
|
+
}
|
|
374
|
+
else {
|
|
375
|
+
logger.warn(`No sandbox found for session: ${sessionId}`);
|
|
376
|
+
}
|
|
377
|
+
// Clear the local mapping when the sandbox is gone (deleted or already absent) so a
|
|
378
|
+
// stale entry can't cause repeated reconnect failures. Preserve it on transient errors.
|
|
379
|
+
if (sandboxGone) {
|
|
380
|
+
this.sessionSandboxes.delete(sessionId);
|
|
381
|
+
const cleanupProjectId = stored?.projectId ?? projectId;
|
|
382
|
+
const cleanupWorktree = stored?.worktree ?? this.dataStorage.load(projectId)?.worktree ?? '';
|
|
383
|
+
this.dataStorage.removeSession(cleanupProjectId, cleanupWorktree, sessionId);
|
|
384
|
+
}
|
|
385
|
+
return deleted;
|
|
386
|
+
}
|
|
387
|
+
/**
|
|
388
|
+
* Pull not-yet-synced sandbox changes into the local repo before the sandbox is
|
|
389
|
+
* destroyed. Throws — aborting deletion so the sandbox is preserved — when unsynced
|
|
390
|
+
* changes cannot be pulled, including when the local repository itself is no longer
|
|
391
|
+
* accessible (a silent skip there would destroy the only copy of the work). A sandbox
|
|
392
|
+
* that is not running is deleted without being started: anything in it was either
|
|
393
|
+
* synced while it ran or is abandoned by the explicit delete.
|
|
394
|
+
*
|
|
395
|
+
* Runs inside the session's sync queue; it must NOT enqueue (that would deadlock).
|
|
396
|
+
*/
|
|
397
|
+
async syncBeforeDelete(sandbox, stored) {
|
|
398
|
+
const branchNumber = stored?.session.branchNumber;
|
|
399
|
+
if (!branchNumber || !stored?.worktree)
|
|
400
|
+
return;
|
|
401
|
+
await sandbox.refreshData();
|
|
402
|
+
if (sandbox.state !== 'started')
|
|
403
|
+
return;
|
|
404
|
+
const sessionGit = new SessionGitManager(sandbox, this.repoPath, stored.worktree, branchNumber);
|
|
405
|
+
if (!sessionGit.hasLocalRepo()) {
|
|
406
|
+
throw new Error(`Local repository at ${stored.worktree} is not accessible, so unsynced sandbox changes cannot be pulled; the sandbox was not deleted. Restore the repository or delete the sandbox from the Daytona dashboard.`);
|
|
407
|
+
}
|
|
408
|
+
try {
|
|
409
|
+
await sessionGit.autoCommitAndPull();
|
|
410
|
+
}
|
|
411
|
+
catch (err) {
|
|
412
|
+
throw new Error(`Sandbox has changes that could not be synced to the local repository; the sandbox was not deleted. ${err?.message ?? err}`);
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
/**
|
|
416
|
+
* Read-only check for a session→sandbox mapping (memory or storage); never creates,
|
|
417
|
+
* migrates, or connects.
|
|
418
|
+
*/
|
|
419
|
+
hasSandbox(sessionId, projectId) {
|
|
420
|
+
if (this.deletingSessions.has(sessionId))
|
|
421
|
+
return false;
|
|
422
|
+
this.setProjectContext(projectId);
|
|
423
|
+
if (this.sessionSandboxes.has(sessionId))
|
|
424
|
+
return true;
|
|
425
|
+
return this.dataStorage.findSession(sessionId) !== undefined;
|
|
426
|
+
}
|
|
427
|
+
isSessionDeleting(sessionId) {
|
|
428
|
+
return this.deletingSessions.has(sessionId);
|
|
429
|
+
}
|
|
430
|
+
/**
|
|
431
|
+
* Guard for registration points that follow an await: deletion may have started (and
|
|
432
|
+
* finished) while a sandbox was being refreshed or reconnected, and persisting the
|
|
433
|
+
* mapping afterwards would resurrect state for a session that no longer exists.
|
|
434
|
+
*/
|
|
435
|
+
ensureNotDeleted(sessionId) {
|
|
436
|
+
if (this.deletingSessions.has(sessionId)) {
|
|
437
|
+
throw new Error(`Session ${sessionId} was deleted while its sandbox was being prepared.`);
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
//# sourceMappingURL=session-manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-manager.js","sourceRoot":"","sources":["session-manager.ts"],"names":[],"mappings":"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"}
|