@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.
Files changed (106) hide show
  1. package/.opencode/plugin/daytona/core/logger.d.ts +15 -0
  2. package/.opencode/plugin/daytona/core/logger.js +65 -0
  3. package/.opencode/plugin/daytona/core/logger.js.map +1 -0
  4. package/.opencode/plugin/daytona/core/logger.ts +71 -0
  5. package/.opencode/plugin/daytona/core/project-data-storage.d.ts +63 -0
  6. package/.opencode/plugin/daytona/core/project-data-storage.js +213 -0
  7. package/.opencode/plugin/daytona/core/project-data-storage.js.map +1 -0
  8. package/.opencode/plugin/daytona/core/project-data-storage.ts +234 -0
  9. package/.opencode/plugin/daytona/core/session-manager.d.ts +74 -0
  10. package/.opencode/plugin/daytona/core/session-manager.js +441 -0
  11. package/.opencode/plugin/daytona/core/session-manager.js.map +1 -0
  12. package/.opencode/plugin/daytona/core/session-manager.ts +471 -0
  13. package/.opencode/plugin/daytona/core/toast.d.ts +47 -0
  14. package/.opencode/plugin/daytona/core/toast.js +70 -0
  15. package/.opencode/plugin/daytona/core/toast.js.map +1 -0
  16. package/.opencode/plugin/daytona/core/toast.ts +96 -0
  17. package/.opencode/plugin/daytona/core/types.d.ts +53 -0
  18. package/.opencode/plugin/daytona/core/types.js +12 -0
  19. package/.opencode/plugin/daytona/core/types.js.map +1 -0
  20. package/.opencode/plugin/daytona/core/types.ts +72 -0
  21. package/.opencode/plugin/daytona/git/host-git-manager.d.ts +58 -0
  22. package/.opencode/plugin/daytona/git/host-git-manager.js +342 -0
  23. package/.opencode/plugin/daytona/git/host-git-manager.js.map +1 -0
  24. package/.opencode/plugin/daytona/git/host-git-manager.ts +372 -0
  25. package/.opencode/plugin/daytona/git/index.d.ts +5 -0
  26. package/.opencode/plugin/daytona/git/index.js +6 -0
  27. package/.opencode/plugin/daytona/git/index.js.map +1 -0
  28. package/.opencode/plugin/daytona/git/index.ts +6 -0
  29. package/.opencode/plugin/daytona/git/sandbox-git-manager.d.ts +22 -0
  30. package/.opencode/plugin/daytona/git/sandbox-git-manager.js +80 -0
  31. package/.opencode/plugin/daytona/git/sandbox-git-manager.js.map +1 -0
  32. package/.opencode/plugin/daytona/git/sandbox-git-manager.ts +92 -0
  33. package/.opencode/plugin/daytona/git/session-git-manager.d.ts +54 -0
  34. package/.opencode/plugin/daytona/git/session-git-manager.js +184 -0
  35. package/.opencode/plugin/daytona/git/session-git-manager.js.map +1 -0
  36. package/.opencode/plugin/daytona/git/session-git-manager.ts +203 -0
  37. package/.opencode/plugin/daytona/index.d.ts +125 -0
  38. package/.opencode/plugin/daytona/index.js +60 -0
  39. package/.opencode/plugin/daytona/index.js.map +1 -0
  40. package/.opencode/plugin/daytona/index.ts +73 -0
  41. package/.opencode/plugin/daytona/plugins/custom-tools.d.ts +122 -0
  42. package/.opencode/plugin/daytona/plugins/custom-tools.js +18 -0
  43. package/.opencode/plugin/daytona/plugins/custom-tools.js.map +1 -0
  44. package/.opencode/plugin/daytona/plugins/custom-tools.ts +21 -0
  45. package/.opencode/plugin/daytona/plugins/session-events.d.ts +10 -0
  46. package/.opencode/plugin/daytona/plugins/session-events.js +63 -0
  47. package/.opencode/plugin/daytona/plugins/session-events.js.map +1 -0
  48. package/.opencode/plugin/daytona/plugins/session-events.ts +61 -0
  49. package/.opencode/plugin/daytona/plugins/system-transform.d.ts +10 -0
  50. package/.opencode/plugin/daytona/plugins/system-transform.js +38 -0
  51. package/.opencode/plugin/daytona/plugins/system-transform.js.map +1 -0
  52. package/.opencode/plugin/daytona/plugins/system-transform.ts +43 -0
  53. package/.opencode/plugin/daytona/tools/bash.d.ts +19 -0
  54. package/.opencode/plugin/daytona/tools/bash.js +42 -0
  55. package/.opencode/plugin/daytona/tools/bash.js.map +1 -0
  56. package/.opencode/plugin/daytona/tools/bash.ts +51 -0
  57. package/.opencode/plugin/daytona/tools/edit.d.ts +21 -0
  58. package/.opencode/plugin/daytona/tools/edit.js +33 -0
  59. package/.opencode/plugin/daytona/tools/edit.js.map +1 -0
  60. package/.opencode/plugin/daytona/tools/edit.ts +44 -0
  61. package/.opencode/plugin/daytona/tools/get-preview-url.d.ts +17 -0
  62. package/.opencode/plugin/daytona/tools/get-preview-url.js +17 -0
  63. package/.opencode/plugin/daytona/tools/get-preview-url.js.map +1 -0
  64. package/.opencode/plugin/daytona/tools/get-preview-url.ts +26 -0
  65. package/.opencode/plugin/daytona/tools/git-sync.d.ts +12 -0
  66. package/.opencode/plugin/daytona/tools/git-sync.js +26 -0
  67. package/.opencode/plugin/daytona/tools/git-sync.js.map +1 -0
  68. package/.opencode/plugin/daytona/tools/git-sync.ts +36 -0
  69. package/.opencode/plugin/daytona/tools/glob.d.ts +17 -0
  70. package/.opencode/plugin/daytona/tools/glob.js +21 -0
  71. package/.opencode/plugin/daytona/tools/glob.js.map +1 -0
  72. package/.opencode/plugin/daytona/tools/glob.ts +30 -0
  73. package/.opencode/plugin/daytona/tools/grep.d.ts +17 -0
  74. package/.opencode/plugin/daytona/tools/grep.js +29 -0
  75. package/.opencode/plugin/daytona/tools/grep.js.map +1 -0
  76. package/.opencode/plugin/daytona/tools/grep.ts +39 -0
  77. package/.opencode/plugin/daytona/tools/ls.d.ts +17 -0
  78. package/.opencode/plugin/daytona/tools/ls.js +22 -0
  79. package/.opencode/plugin/daytona/tools/ls.js.map +1 -0
  80. package/.opencode/plugin/daytona/tools/ls.ts +32 -0
  81. package/.opencode/plugin/daytona/tools/multiedit.d.ts +25 -0
  82. package/.opencode/plugin/daytona/tools/multiedit.js +37 -0
  83. package/.opencode/plugin/daytona/tools/multiedit.js.map +1 -0
  84. package/.opencode/plugin/daytona/tools/multiedit.ts +54 -0
  85. package/.opencode/plugin/daytona/tools/read.d.ts +17 -0
  86. package/.opencode/plugin/daytona/tools/read.js +18 -0
  87. package/.opencode/plugin/daytona/tools/read.js.map +1 -0
  88. package/.opencode/plugin/daytona/tools/read.ts +27 -0
  89. package/.opencode/plugin/daytona/tools/veris-receipt.d.ts +26 -0
  90. package/.opencode/plugin/daytona/tools/veris-receipt.js +76 -0
  91. package/.opencode/plugin/daytona/tools/veris-receipt.js.map +1 -0
  92. package/.opencode/plugin/daytona/tools/veris-receipt.ts +103 -0
  93. package/.opencode/plugin/daytona/tools/write.d.ts +19 -0
  94. package/.opencode/plugin/daytona/tools/write.js +18 -0
  95. package/.opencode/plugin/daytona/tools/write.js.map +1 -0
  96. package/.opencode/plugin/daytona/tools/write.ts +27 -0
  97. package/.opencode/plugin/daytona/tools.d.ts +119 -0
  98. package/.opencode/plugin/daytona/tools.js +35 -0
  99. package/.opencode/plugin/daytona/tools.js.map +1 -0
  100. package/.opencode/plugin/daytona/tools.ts +45 -0
  101. package/.opencode/plugin/index.d.ts +9 -0
  102. package/.opencode/plugin/index.js +10 -0
  103. package/.opencode/plugin/index.js.map +1 -0
  104. package/.opencode/plugin/index.ts +11 -0
  105. package/README.md +87 -0
  106. package/package.json +52 -0
@@ -0,0 +1,122 @@
1
+ /**
2
+ * Copyright Daytona Platforms Inc.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+ import type { PluginInput } from '@opencode-ai/plugin';
6
+ import type { DaytonaSessionManager } from '../core/session-manager';
7
+ /**
8
+ * Custom tools for Daytona sandbox: file ops, command execution, search.
9
+ */
10
+ export declare function customTools(ctx: PluginInput, sessionManager: DaytonaSessionManager): Promise<{
11
+ bash: {
12
+ description: string;
13
+ args: {
14
+ command: import("zod").ZodString;
15
+ background: import("zod").ZodOptional<import("zod").ZodBoolean>;
16
+ };
17
+ execute(args: {
18
+ command: string;
19
+ background?: boolean;
20
+ }, ctx: import("@opencode-ai/plugin").ToolContext): Promise<string>;
21
+ };
22
+ read: {
23
+ description: string;
24
+ args: {
25
+ filePath: import("zod").ZodString;
26
+ };
27
+ execute(args: {
28
+ filePath: string;
29
+ }, ctx: import("@opencode-ai/plugin").ToolContext): Promise<string>;
30
+ };
31
+ write: {
32
+ description: string;
33
+ args: {
34
+ filePath: import("zod").ZodString;
35
+ content: import("zod").ZodString;
36
+ };
37
+ execute(args: {
38
+ filePath: string;
39
+ content: string;
40
+ }, ctx: import("@opencode-ai/plugin").ToolContext): Promise<string>;
41
+ };
42
+ edit: {
43
+ description: string;
44
+ args: {
45
+ filePath: import("zod").ZodString;
46
+ oldString: import("zod").ZodString;
47
+ newString: import("zod").ZodString;
48
+ };
49
+ execute(args: {
50
+ filePath: string;
51
+ oldString: string;
52
+ newString: string;
53
+ }, ctx: import("@opencode-ai/plugin").ToolContext): Promise<string>;
54
+ };
55
+ multiedit: {
56
+ description: string;
57
+ args: {
58
+ filePath: import("zod").ZodString;
59
+ edits: import("zod").ZodArray<import("zod").ZodObject<{
60
+ oldString: import("zod").ZodString;
61
+ newString: import("zod").ZodString;
62
+ }, import("zod/v4/core").$strip>>;
63
+ };
64
+ execute(args: {
65
+ filePath: string;
66
+ edits: Array<{
67
+ oldString: string;
68
+ newString: string;
69
+ }>;
70
+ }, ctx: import("@opencode-ai/plugin").ToolContext): Promise<string>;
71
+ };
72
+ ls: {
73
+ description: string;
74
+ args: {
75
+ dirPath: import("zod").ZodOptional<import("zod").ZodString>;
76
+ };
77
+ execute(args: {
78
+ dirPath?: string;
79
+ }, ctx: import("@opencode-ai/plugin").ToolContext): Promise<string>;
80
+ };
81
+ glob: {
82
+ description: string;
83
+ args: {
84
+ pattern: import("zod").ZodString;
85
+ };
86
+ execute(args: {
87
+ pattern: string;
88
+ }, ctx: import("@opencode-ai/plugin").ToolContext): Promise<string>;
89
+ };
90
+ grep: {
91
+ description: string;
92
+ args: {
93
+ pattern: import("zod").ZodString;
94
+ };
95
+ execute(args: {
96
+ pattern: string;
97
+ }, ctx: import("@opencode-ai/plugin").ToolContext): Promise<string>;
98
+ };
99
+ getPreviewURL: {
100
+ description: string;
101
+ args: {
102
+ port: import("zod").ZodNumber;
103
+ };
104
+ execute(args: {
105
+ port: number;
106
+ }, ctx: import("@opencode-ai/plugin").ToolContext): Promise<string>;
107
+ };
108
+ gitSync: {
109
+ description: string;
110
+ args: {};
111
+ execute(_args: {}, ctx: import("@opencode-ai/plugin").ToolContext): Promise<string>;
112
+ };
113
+ verisReceipt: {
114
+ description: string;
115
+ args: {
116
+ service: import("zod").ZodOptional<import("zod").ZodString>;
117
+ };
118
+ execute(args: {
119
+ service?: string;
120
+ }, ctx: import("@opencode-ai/plugin").ToolContext): Promise<string>;
121
+ };
122
+ }>;
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Copyright Daytona Platforms Inc.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+ import { createDaytonaTools } from '../tools';
6
+ import { logger } from '../core/logger';
7
+ /**
8
+ * Custom tools for Daytona sandbox: file ops, command execution, search.
9
+ */
10
+ export async function customTools(ctx, sessionManager) {
11
+ logger.info('OpenCode started with Daytona plugin');
12
+ const projectId = ctx.project.id;
13
+ // Active worktree (not ctx.project.worktree, which is the first-seen checkout persisted
14
+ // per project); see the matching comment in plugins/session-events.ts.
15
+ const worktree = ctx.worktree;
16
+ return createDaytonaTools(sessionManager, projectId, worktree, ctx);
17
+ }
18
+ //# sourceMappingURL=custom-tools.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"custom-tools.js","sourceRoot":"","sources":["custom-tools.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAA;AAC7C,OAAO,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAA;AAGvC;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,GAAgB,EAAE,cAAqC;IACvF,MAAM,CAAC,IAAI,CAAC,sCAAsC,CAAC,CAAA;IACnD,MAAM,SAAS,GAAG,GAAG,CAAC,OAAO,CAAC,EAAE,CAAA;IAChC,wFAAwF;IACxF,uEAAuE;IACvE,MAAM,QAAQ,GAAG,GAAG,CAAC,QAAQ,CAAA;IAC7B,OAAO,kBAAkB,CAAC,cAAc,EAAE,SAAS,EAAE,QAAQ,EAAE,GAAG,CAAC,CAAA;AACrE,CAAC"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Copyright Daytona Platforms Inc.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ import type { PluginInput } from '@opencode-ai/plugin'
7
+ import { createDaytonaTools } from '../tools'
8
+ import { logger } from '../core/logger'
9
+ import type { DaytonaSessionManager } from '../core/session-manager'
10
+
11
+ /**
12
+ * Custom tools for Daytona sandbox: file ops, command execution, search.
13
+ */
14
+ export async function customTools(ctx: PluginInput, sessionManager: DaytonaSessionManager) {
15
+ logger.info('OpenCode started with Daytona plugin')
16
+ const projectId = ctx.project.id
17
+ // Active worktree (not ctx.project.worktree, which is the first-seen checkout persisted
18
+ // per project); see the matching comment in plugins/session-events.ts.
19
+ const worktree = ctx.worktree
20
+ return createDaytonaTools(sessionManager, projectId, worktree, ctx)
21
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Copyright Daytona Platforms Inc.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+ import type { PluginInput } from '@opencode-ai/plugin';
6
+ import type { DaytonaSessionManager } from '../core/session-manager';
7
+ /**
8
+ * Handles OpenCode session events.
9
+ */
10
+ export declare function eventHandlers(ctx: PluginInput, sessionManager: DaytonaSessionManager, repoPath: string): Promise<(args: any) => Promise<void>>;
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Copyright Daytona Platforms Inc.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+ import { SessionGitManager } from '../git/session-git-manager';
6
+ import { EVENT_TYPE_SESSION_DELETED, EVENT_TYPE_SESSION_IDLE } from '../core/types';
7
+ import { toast } from '../core/toast';
8
+ import { logger } from '../core/logger';
9
+ /**
10
+ * Handles OpenCode session events.
11
+ */
12
+ export async function eventHandlers(ctx, sessionManager, repoPath) {
13
+ const projectId = ctx.project.id;
14
+ // Use the ACTIVE worktree, not ctx.project.worktree: the project worktree is persisted
15
+ // the first time a project is opened and never updated, so in linked-worktree setups
16
+ // (`git worktree add`) it can point at a different checkout than the one this OpenCode
17
+ // instance is running in. Git syncs must land in the active checkout.
18
+ const worktree = ctx.worktree;
19
+ return async (args) => {
20
+ const event = args.event;
21
+ if (event.type === EVENT_TYPE_SESSION_DELETED) {
22
+ const sessionId = event.properties.info.id;
23
+ try {
24
+ const deleted = await sessionManager.deleteSandbox(sessionId, projectId);
25
+ if (deleted) {
26
+ toast.show({ title: 'Session deleted', message: 'Sandbox deleted successfully.', variant: 'success' });
27
+ }
28
+ }
29
+ catch (err) {
30
+ toast.show({ title: 'Delete failed', message: err?.message || 'Failed to delete sandbox.', variant: 'error' });
31
+ throw err;
32
+ }
33
+ }
34
+ else if (event.type === EVENT_TYPE_SESSION_IDLE) {
35
+ const sessionId = event.properties.sessionID;
36
+ const start = Date.now();
37
+ try {
38
+ // The WHOLE pipeline is enqueued (synchronously, before any await) so that a
39
+ // dispose() or delete arriving while the sandbox is still being resolved cannot
40
+ // observe an empty queue and proceed mid-operation.
41
+ const didSync = await SessionGitManager.enqueueSessionSync(sessionId, async () => {
42
+ // Re-checked inside the queue entry: a deletion may have completed while this
43
+ // callback waited its turn, and syncing must not resurrect the sandbox.
44
+ if (sessionManager.isSessionDeleting(sessionId))
45
+ return false;
46
+ const sandbox = await sessionManager.getSandbox(sessionId, projectId, worktree, ctx);
47
+ const branchNumber = sessionManager.getBranchNumberForSandbox(projectId, sandbox.id);
48
+ if (!branchNumber)
49
+ return false;
50
+ const sessionGit = new SessionGitManager(sandbox, repoPath, worktree, branchNumber);
51
+ return sessionGit.autoCommitAndPull(ctx);
52
+ });
53
+ logger.info(`[idle] done sessionId=${sessionId} synced=${didSync} in ${Date.now() - start}ms`);
54
+ }
55
+ catch (err) {
56
+ // autoCommitAndPull already shows a toast; only log here to avoid a duplicate
57
+ // error toast and noisy propagation out of the idle event hook.
58
+ logger.error(`[idle] error sessionId=${sessionId} in ${Date.now() - start}ms: ${err}`);
59
+ }
60
+ }
61
+ };
62
+ }
63
+ //# sourceMappingURL=session-events.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-events.js","sourceRoot":"","sources":["session-events.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Copyright Daytona Platforms Inc.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ import type { PluginInput } from '@opencode-ai/plugin'
7
+ import { SessionGitManager } from '../git/session-git-manager'
8
+ import { EVENT_TYPE_SESSION_DELETED, EVENT_TYPE_SESSION_IDLE, type EventSessionDeleted } from '../core/types'
9
+ import { toast } from '../core/toast'
10
+ import { logger } from '../core/logger'
11
+ import type { DaytonaSessionManager } from '../core/session-manager'
12
+
13
+ /**
14
+ * Handles OpenCode session events.
15
+ */
16
+ export async function eventHandlers(ctx: PluginInput, sessionManager: DaytonaSessionManager, repoPath: string) {
17
+ const projectId = ctx.project.id
18
+ // Use the ACTIVE worktree, not ctx.project.worktree: the project worktree is persisted
19
+ // the first time a project is opened and never updated, so in linked-worktree setups
20
+ // (`git worktree add`) it can point at a different checkout than the one this OpenCode
21
+ // instance is running in. Git syncs must land in the active checkout.
22
+ const worktree = ctx.worktree
23
+ return async (args: any) => {
24
+ const event = args.event
25
+ if (event.type === EVENT_TYPE_SESSION_DELETED) {
26
+ const sessionId = (event as EventSessionDeleted).properties.info.id
27
+ try {
28
+ const deleted = await sessionManager.deleteSandbox(sessionId, projectId)
29
+ if (deleted) {
30
+ toast.show({ title: 'Session deleted', message: 'Sandbox deleted successfully.', variant: 'success' })
31
+ }
32
+ } catch (err: any) {
33
+ toast.show({ title: 'Delete failed', message: err?.message || 'Failed to delete sandbox.', variant: 'error' })
34
+ throw err
35
+ }
36
+ } else if (event.type === EVENT_TYPE_SESSION_IDLE) {
37
+ const sessionId = event.properties.sessionID
38
+ const start = Date.now()
39
+ try {
40
+ // The WHOLE pipeline is enqueued (synchronously, before any await) so that a
41
+ // dispose() or delete arriving while the sandbox is still being resolved cannot
42
+ // observe an empty queue and proceed mid-operation.
43
+ const didSync = await SessionGitManager.enqueueSessionSync(sessionId, async () => {
44
+ // Re-checked inside the queue entry: a deletion may have completed while this
45
+ // callback waited its turn, and syncing must not resurrect the sandbox.
46
+ if (sessionManager.isSessionDeleting(sessionId)) return false
47
+ const sandbox = await sessionManager.getSandbox(sessionId, projectId, worktree, ctx)
48
+ const branchNumber = sessionManager.getBranchNumberForSandbox(projectId, sandbox.id)
49
+ if (!branchNumber) return false
50
+ const sessionGit = new SessionGitManager(sandbox, repoPath, worktree, branchNumber)
51
+ return sessionGit.autoCommitAndPull(ctx)
52
+ })
53
+ logger.info(`[idle] done sessionId=${sessionId} synced=${didSync} in ${Date.now() - start}ms`)
54
+ } catch (err: any) {
55
+ // autoCommitAndPull already shows a toast; only log here to avoid a duplicate
56
+ // error toast and noisy propagation out of the idle event hook.
57
+ logger.error(`[idle] error sessionId=${sessionId} in ${Date.now() - start}ms: ${err}`)
58
+ }
59
+ }
60
+ }
61
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Copyright Daytona Platforms Inc.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+ import type { PluginInput } from '@opencode-ai/plugin';
6
+ import type { ExperimentalChatSystemTransformInput, ExperimentalChatSystemTransformOutput } from '../core/types';
7
+ /**
8
+ * Adds Daytona-specific instructions to the system prompt.
9
+ */
10
+ export declare function systemPromptTransform(ctx: PluginInput, repoPath: string): Promise<(input: ExperimentalChatSystemTransformInput, output: ExperimentalChatSystemTransformOutput) => Promise<void>>;
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Copyright Daytona Platforms Inc.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+ /**
6
+ * Adds Daytona-specific instructions to the system prompt.
7
+ */
8
+ export async function systemPromptTransform(ctx, repoPath) {
9
+ return async (input, output) => {
10
+ output.system.push([
11
+ '## Daytona Sandbox Integration',
12
+ 'This session is integrated with a Daytona sandbox.',
13
+ `The main project repository is located at: ${repoPath}.`,
14
+ 'Bash commands will run in this directory.',
15
+ 'Put all projects in the project directory. Do NOT try to use the current working directory of the host system.',
16
+ "When executing long-running commands, use the 'background' option to run them asynchronously.",
17
+ 'Before showing a preview URL, ensure the server is running in the sandbox on that port.',
18
+ 'When the user asks to sync, hand off, or finalize changes, run the gitSync tool and report its result.',
19
+ '',
20
+ '## Veris Twin',
21
+ "This sandbox's outbound calls to external vendor APIs are answered by a Veris twin:",
22
+ 'a stateful fake that speaks the real protocol at the real hostname. Code under test',
23
+ 'is unmodified — production hostnames, production credentials, production client',
24
+ 'libraries — and nothing you run here reaches the real vendor. Hosts the twin does',
25
+ 'not answer for are BLOCKED, not silently forwarded, so a connection error to an',
26
+ 'external API is information: that dependency is not part of this environment.',
27
+ '',
28
+ 'A passing run proves nothing on its own. A test suite that skipped its integration',
29
+ 'and a test suite that exercised it look identical from inside the sandbox, and so',
30
+ 'does a call you believe you made but did not. Only the receipt distinguishes them.',
31
+ 'So: after any change meant to reach an external API, run the verisReceipt tool and',
32
+ 'read what the twin actually received. If the receipt is empty, the change does not',
33
+ 'work yet — report that, do not report success. Cite the receipt when you claim an',
34
+ 'integration works.',
35
+ ].join('\n'));
36
+ };
37
+ }
38
+ //# sourceMappingURL=system-transform.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"system-transform.js","sourceRoot":"","sources":["system-transform.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB,CAAC,GAAgB,EAAE,QAAgB;IAC5E,OAAO,KAAK,EAAE,KAA2C,EAAE,MAA6C,EAAE,EAAE;QAC1G,MAAM,CAAC,MAAM,CAAC,IAAI,CAChB;YACE,gCAAgC;YAChC,oDAAoD;YACpD,8CAA8C,QAAQ,GAAG;YACzD,2CAA2C;YAC3C,gHAAgH;YAChH,+FAA+F;YAC/F,yFAAyF;YACzF,wGAAwG;YACxG,EAAE;YACF,eAAe;YACf,qFAAqF;YACrF,qFAAqF;YACrF,iFAAiF;YACjF,mFAAmF;YACnF,iFAAiF;YACjF,+EAA+E;YAC/E,EAAE;YACF,oFAAoF;YACpF,mFAAmF;YACnF,oFAAoF;YACpF,oFAAoF;YACpF,oFAAoF;YACpF,mFAAmF;YACnF,oBAAoB;SACrB,CAAC,IAAI,CAAC,IAAI,CAAC,CACb,CAAA;IACH,CAAC,CAAA;AACH,CAAC"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Copyright Daytona Platforms Inc.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ import type { PluginInput } from '@opencode-ai/plugin'
7
+ import type { ExperimentalChatSystemTransformInput, ExperimentalChatSystemTransformOutput } from '../core/types'
8
+
9
+ /**
10
+ * Adds Daytona-specific instructions to the system prompt.
11
+ */
12
+ export async function systemPromptTransform(ctx: PluginInput, repoPath: string) {
13
+ return async (input: ExperimentalChatSystemTransformInput, output: ExperimentalChatSystemTransformOutput) => {
14
+ output.system.push(
15
+ [
16
+ '## Daytona Sandbox Integration',
17
+ 'This session is integrated with a Daytona sandbox.',
18
+ `The main project repository is located at: ${repoPath}.`,
19
+ 'Bash commands will run in this directory.',
20
+ 'Put all projects in the project directory. Do NOT try to use the current working directory of the host system.',
21
+ "When executing long-running commands, use the 'background' option to run them asynchronously.",
22
+ 'Before showing a preview URL, ensure the server is running in the sandbox on that port.',
23
+ 'When the user asks to sync, hand off, or finalize changes, run the gitSync tool and report its result.',
24
+ '',
25
+ '## Veris Twin',
26
+ "This sandbox's outbound calls to external vendor APIs are answered by a Veris twin:",
27
+ 'a stateful fake that speaks the real protocol at the real hostname. Code under test',
28
+ 'is unmodified — production hostnames, production credentials, production client',
29
+ 'libraries — and nothing you run here reaches the real vendor. Hosts the twin does',
30
+ 'not answer for are BLOCKED, not silently forwarded, so a connection error to an',
31
+ 'external API is information: that dependency is not part of this environment.',
32
+ '',
33
+ 'A passing run proves nothing on its own. A test suite that skipped its integration',
34
+ 'and a test suite that exercised it look identical from inside the sandbox, and so',
35
+ 'does a call you believe you made but did not. Only the receipt distinguishes them.',
36
+ 'So: after any change meant to reach an external API, run the verisReceipt tool and',
37
+ 'read what the twin actually received. If the receipt is empty, the change does not',
38
+ 'work yet — report that, do not report success. Cite the receipt when you claim an',
39
+ 'integration works.',
40
+ ].join('\n'),
41
+ )
42
+ }
43
+ }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Copyright Daytona Platforms Inc.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+ import { z } from 'zod';
6
+ import type { PluginInput } from '@opencode-ai/plugin';
7
+ import type { ToolContext } from '@opencode-ai/plugin/tool';
8
+ import type { DaytonaSessionManager } from '../core/session-manager';
9
+ export declare const bashTool: (sessionManager: DaytonaSessionManager, projectId: string, worktree: string, pluginCtx: PluginInput, repoPath: string) => {
10
+ description: string;
11
+ args: {
12
+ command: z.ZodString;
13
+ background: z.ZodOptional<z.ZodBoolean>;
14
+ };
15
+ execute(args: {
16
+ command: string;
17
+ background?: boolean;
18
+ }, ctx: ToolContext): Promise<string>;
19
+ };
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Copyright Daytona Platforms Inc.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+ import { z } from 'zod';
6
+ import { DaytonaNotFoundError } from '@daytona/sdk';
7
+ export const bashTool = (sessionManager, projectId, worktree, pluginCtx, repoPath) => ({
8
+ description: 'Executes shell commands in a Daytona sandbox',
9
+ args: {
10
+ command: z.string(),
11
+ background: z.boolean().optional(),
12
+ },
13
+ async execute(args, ctx) {
14
+ const sessionId = ctx.sessionID;
15
+ const sandbox = await sessionManager.getSandbox(sessionId, projectId, worktree, pluginCtx);
16
+ if (args.background) {
17
+ const execSessionId = `exec-session-${sessionId}`;
18
+ try {
19
+ await sandbox.process.getSession(execSessionId);
20
+ }
21
+ catch (err) {
22
+ if (!(err instanceof DaytonaNotFoundError)) {
23
+ throw err;
24
+ }
25
+ await sandbox.process.createSession(execSessionId);
26
+ }
27
+ await sandbox.process.executeSessionCommand(execSessionId, {
28
+ command: `cd ${repoPath}`,
29
+ });
30
+ const result = await sandbox.process.executeSessionCommand(execSessionId, {
31
+ command: args.command,
32
+ runAsync: true,
33
+ });
34
+ return `Command started in background (cmdId: ${result.cmdId})`;
35
+ }
36
+ else {
37
+ const result = await sandbox.process.executeCommand(args.command, repoPath);
38
+ return `Exit code: ${result.exitCode}\n${result.result}`;
39
+ }
40
+ },
41
+ });
42
+ //# sourceMappingURL=bash.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bash.js","sourceRoot":"","sources":["bash.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAA;AAKnD,MAAM,CAAC,MAAM,QAAQ,GAAG,CACtB,cAAqC,EACrC,SAAiB,EACjB,QAAgB,EAChB,SAAsB,EACtB,QAAgB,EAChB,EAAE,CAAC,CAAC;IACJ,WAAW,EAAE,8CAA8C;IAC3D,IAAI,EAAE;QACJ,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE;QACnB,UAAU,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE;KACnC;IACD,KAAK,CAAC,OAAO,CAAC,IAA+C,EAAE,GAAgB;QAC7E,MAAM,SAAS,GAAG,GAAG,CAAC,SAAS,CAAA;QAC/B,MAAM,OAAO,GAAG,MAAM,cAAc,CAAC,UAAU,CAAC,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,SAAS,CAAC,CAAA;QAE1F,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YACpB,MAAM,aAAa,GAAG,gBAAgB,SAAS,EAAE,CAAA;YACjD,IAAI,CAAC;gBACH,MAAM,OAAO,CAAC,OAAO,CAAC,UAAU,CAAC,aAAa,CAAC,CAAA;YACjD,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,IAAI,CAAC,CAAC,GAAG,YAAY,oBAAoB,CAAC,EAAE,CAAC;oBAC3C,MAAM,GAAG,CAAA;gBACX,CAAC;gBACD,MAAM,OAAO,CAAC,OAAO,CAAC,aAAa,CAAC,aAAa,CAAC,CAAA;YACpD,CAAC;YACD,MAAM,OAAO,CAAC,OAAO,CAAC,qBAAqB,CAAC,aAAa,EAAE;gBACzD,OAAO,EAAE,MAAM,QAAQ,EAAE;aAC1B,CAAC,CAAA;YACF,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC,qBAAqB,CAAC,aAAa,EAAE;gBACxE,OAAO,EAAE,IAAI,CAAC,OAAO;gBACrB,QAAQ,EAAE,IAAI;aACf,CAAC,CAAA;YACF,OAAO,yCAAyC,MAAM,CAAC,KAAK,GAAG,CAAA;QACjE,CAAC;aAAM,CAAC;YACN,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC,cAAc,CAAC,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAA;YAC3E,OAAO,cAAc,MAAM,CAAC,QAAQ,KAAK,MAAM,CAAC,MAAM,EAAE,CAAA;QAC1D,CAAC;IACH,CAAC;CACF,CAAC,CAAA"}
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Copyright Daytona Platforms Inc.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ import { z } from 'zod'
7
+ import { DaytonaNotFoundError } from '@daytona/sdk'
8
+ import type { PluginInput } from '@opencode-ai/plugin'
9
+ import type { ToolContext } from '@opencode-ai/plugin/tool'
10
+ import type { DaytonaSessionManager } from '../core/session-manager'
11
+
12
+ export const bashTool = (
13
+ sessionManager: DaytonaSessionManager,
14
+ projectId: string,
15
+ worktree: string,
16
+ pluginCtx: PluginInput,
17
+ repoPath: string,
18
+ ) => ({
19
+ description: 'Executes shell commands in a Daytona sandbox',
20
+ args: {
21
+ command: z.string(),
22
+ background: z.boolean().optional(),
23
+ },
24
+ async execute(args: { command: string; background?: boolean }, ctx: ToolContext) {
25
+ const sessionId = ctx.sessionID
26
+ const sandbox = await sessionManager.getSandbox(sessionId, projectId, worktree, pluginCtx)
27
+
28
+ if (args.background) {
29
+ const execSessionId = `exec-session-${sessionId}`
30
+ try {
31
+ await sandbox.process.getSession(execSessionId)
32
+ } catch (err) {
33
+ if (!(err instanceof DaytonaNotFoundError)) {
34
+ throw err
35
+ }
36
+ await sandbox.process.createSession(execSessionId)
37
+ }
38
+ await sandbox.process.executeSessionCommand(execSessionId, {
39
+ command: `cd ${repoPath}`,
40
+ })
41
+ const result = await sandbox.process.executeSessionCommand(execSessionId, {
42
+ command: args.command,
43
+ runAsync: true,
44
+ })
45
+ return `Command started in background (cmdId: ${result.cmdId})`
46
+ } else {
47
+ const result = await sandbox.process.executeCommand(args.command, repoPath)
48
+ return `Exit code: ${result.exitCode}\n${result.result}`
49
+ }
50
+ },
51
+ })
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Copyright Daytona Platforms Inc.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+ import { z } from 'zod';
6
+ import type { PluginInput } from '@opencode-ai/plugin';
7
+ import type { ToolContext } from '@opencode-ai/plugin/tool';
8
+ import type { DaytonaSessionManager } from '../core/session-manager';
9
+ export declare const editTool: (sessionManager: DaytonaSessionManager, projectId: string, worktree: string, pluginCtx: PluginInput) => {
10
+ description: string;
11
+ args: {
12
+ filePath: z.ZodString;
13
+ oldString: z.ZodString;
14
+ newString: z.ZodString;
15
+ };
16
+ execute(args: {
17
+ filePath: string;
18
+ oldString: string;
19
+ newString: string;
20
+ }, ctx: ToolContext): Promise<string>;
21
+ };
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Copyright Daytona Platforms Inc.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+ import { z } from 'zod';
6
+ export const editTool = (sessionManager, projectId, worktree, pluginCtx) => ({
7
+ description: 'Replaces text in a file in Daytona sandbox',
8
+ args: {
9
+ filePath: z.string(),
10
+ oldString: z.string(),
11
+ newString: z.string(),
12
+ },
13
+ async execute(args, ctx) {
14
+ const sandbox = await sessionManager.getSandbox(ctx.sessionID, projectId, worktree, pluginCtx);
15
+ const buffer = await sandbox.fs.downloadFile(args.filePath);
16
+ const decoder = new TextDecoder();
17
+ const content = decoder.decode(buffer);
18
+ if (args.oldString === '') {
19
+ throw new Error(`oldString must be non-empty; refusing to prepend to ${args.filePath}.`);
20
+ }
21
+ const occurrences = content.split(args.oldString).length - 1;
22
+ if (occurrences === 0) {
23
+ throw new Error(`oldString not found in ${args.filePath}; no changes were made.`);
24
+ }
25
+ if (occurrences > 1) {
26
+ throw new Error(`oldString is ambiguous (${occurrences} matches) in ${args.filePath}; no changes were made.`);
27
+ }
28
+ const newContent = content.replace(args.oldString, args.newString);
29
+ await sandbox.fs.uploadFile(Buffer.from(newContent), args.filePath);
30
+ return `Edited ${args.filePath}`;
31
+ },
32
+ });
33
+ //# sourceMappingURL=edit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"edit.js","sourceRoot":"","sources":["edit.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAKvB,MAAM,CAAC,MAAM,QAAQ,GAAG,CACtB,cAAqC,EACrC,SAAiB,EACjB,QAAgB,EAChB,SAAsB,EACtB,EAAE,CAAC,CAAC;IACJ,WAAW,EAAE,4CAA4C;IACzD,IAAI,EAAE;QACJ,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE;QACpB,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE;QACrB,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE;KACtB;IACD,KAAK,CAAC,OAAO,CAAC,IAAgE,EAAE,GAAgB;QAC9F,MAAM,OAAO,GAAG,MAAM,cAAc,CAAC,UAAU,CAAC,GAAG,CAAC,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,SAAS,CAAC,CAAA;QAC9F,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;QAC3D,MAAM,OAAO,GAAG,IAAI,WAAW,EAAE,CAAA;QACjC,MAAM,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;QACtC,IAAI,IAAI,CAAC,SAAS,KAAK,EAAE,EAAE,CAAC;YAC1B,MAAM,IAAI,KAAK,CAAC,uDAAuD,IAAI,CAAC,QAAQ,GAAG,CAAC,CAAA;QAC1F,CAAC;QACD,MAAM,WAAW,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,MAAM,GAAG,CAAC,CAAA;QAC5D,IAAI,WAAW,KAAK,CAAC,EAAE,CAAC;YACtB,MAAM,IAAI,KAAK,CAAC,0BAA0B,IAAI,CAAC,QAAQ,yBAAyB,CAAC,CAAA;QACnF,CAAC;QACD,IAAI,WAAW,GAAG,CAAC,EAAE,CAAC;YACpB,MAAM,IAAI,KAAK,CACb,2BAA2B,WAAW,gBAAgB,IAAI,CAAC,QAAQ,yBAAyB,CAC7F,CAAA;QACH,CAAC;QACD,MAAM,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,SAAS,CAAC,CAAA;QAClE,MAAM,OAAO,CAAC,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAA;QACnE,OAAO,UAAU,IAAI,CAAC,QAAQ,EAAE,CAAA;IAClC,CAAC;CACF,CAAC,CAAA"}
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Copyright Daytona Platforms Inc.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ import { z } from 'zod'
7
+ import type { PluginInput } from '@opencode-ai/plugin'
8
+ import type { ToolContext } from '@opencode-ai/plugin/tool'
9
+ import type { DaytonaSessionManager } from '../core/session-manager'
10
+
11
+ export const editTool = (
12
+ sessionManager: DaytonaSessionManager,
13
+ projectId: string,
14
+ worktree: string,
15
+ pluginCtx: PluginInput,
16
+ ) => ({
17
+ description: 'Replaces text in a file in Daytona sandbox',
18
+ args: {
19
+ filePath: z.string(),
20
+ oldString: z.string(),
21
+ newString: z.string(),
22
+ },
23
+ async execute(args: { filePath: string; oldString: string; newString: string }, ctx: ToolContext) {
24
+ const sandbox = await sessionManager.getSandbox(ctx.sessionID, projectId, worktree, pluginCtx)
25
+ const buffer = await sandbox.fs.downloadFile(args.filePath)
26
+ const decoder = new TextDecoder()
27
+ const content = decoder.decode(buffer)
28
+ if (args.oldString === '') {
29
+ throw new Error(`oldString must be non-empty; refusing to prepend to ${args.filePath}.`)
30
+ }
31
+ const occurrences = content.split(args.oldString).length - 1
32
+ if (occurrences === 0) {
33
+ throw new Error(`oldString not found in ${args.filePath}; no changes were made.`)
34
+ }
35
+ if (occurrences > 1) {
36
+ throw new Error(
37
+ `oldString is ambiguous (${occurrences} matches) in ${args.filePath}; no changes were made.`,
38
+ )
39
+ }
40
+ const newContent = content.replace(args.oldString, args.newString)
41
+ await sandbox.fs.uploadFile(Buffer.from(newContent), args.filePath)
42
+ return `Edited ${args.filePath}`
43
+ },
44
+ })
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Copyright Daytona Platforms Inc.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+ import { z } from 'zod';
6
+ import type { PluginInput } from '@opencode-ai/plugin';
7
+ import type { ToolContext } from '@opencode-ai/plugin/tool';
8
+ import type { DaytonaSessionManager } from '../core/session-manager';
9
+ export declare const getPreviewURLTool: (sessionManager: DaytonaSessionManager, projectId: string, worktree: string, pluginCtx: PluginInput) => {
10
+ description: string;
11
+ args: {
12
+ port: z.ZodNumber;
13
+ };
14
+ execute(args: {
15
+ port: number;
16
+ }, ctx: ToolContext): Promise<string>;
17
+ };