@wavexzore/sandbox 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 (149) hide show
  1. package/Dockerfile +14 -0
  2. package/LICENSE +661 -0
  3. package/NOTICE +3 -0
  4. package/README.md +153 -0
  5. package/dist/index.d.ts +9 -0
  6. package/dist/index.js +9 -0
  7. package/dist/sandbox/cli/install.d.ts +5 -0
  8. package/dist/sandbox/cli/install.js +335 -0
  9. package/dist/sandbox/cli/local-store.d.ts +87 -0
  10. package/dist/sandbox/cli/local-store.js +604 -0
  11. package/dist/sandbox/cli/opencode-config.d.ts +25 -0
  12. package/dist/sandbox/cli/opencode-config.js +240 -0
  13. package/dist/sandbox/cli/path.d.ts +64 -0
  14. package/dist/sandbox/cli/path.js +127 -0
  15. package/dist/sandbox/cli/types.d.ts +145 -0
  16. package/dist/sandbox/cli/types.js +6 -0
  17. package/dist/sandbox/cli/wavexzore-sandbox.d.ts +65 -0
  18. package/dist/sandbox/cli/wavexzore-sandbox.js +577 -0
  19. package/dist/sandbox/core/cli-helper.d.ts +19 -0
  20. package/dist/sandbox/core/cli-helper.js +64 -0
  21. package/dist/sandbox/core/docker-archive-utils.d.ts +3 -0
  22. package/dist/sandbox/core/docker-archive-utils.js +50 -0
  23. package/dist/sandbox/core/docker-sandbox.d.ts +51 -0
  24. package/dist/sandbox/core/docker-sandbox.js +675 -0
  25. package/dist/sandbox/core/edit/filediff.d.ts +16 -0
  26. package/dist/sandbox/core/edit/filediff.js +21 -0
  27. package/dist/sandbox/core/edit/index.d.ts +5 -0
  28. package/dist/sandbox/core/edit/index.js +5 -0
  29. package/dist/sandbox/core/edit/line-endings.d.ts +4 -0
  30. package/dist/sandbox/core/edit/line-endings.js +10 -0
  31. package/dist/sandbox/core/edit/lock.d.ts +1 -0
  32. package/dist/sandbox/core/edit/lock.js +18 -0
  33. package/dist/sandbox/core/edit/replace.d.ts +10 -0
  34. package/dist/sandbox/core/edit/replace.js +14 -0
  35. package/dist/sandbox/core/edit/replacers.d.ts +15 -0
  36. package/dist/sandbox/core/edit/replacers.js +241 -0
  37. package/dist/sandbox/core/logger.d.ts +15 -0
  38. package/dist/sandbox/core/logger.js +59 -0
  39. package/dist/sandbox/core/lsp/client.d.ts +63 -0
  40. package/dist/sandbox/core/lsp/client.js +533 -0
  41. package/dist/sandbox/core/lsp/config.d.ts +13 -0
  42. package/dist/sandbox/core/lsp/config.js +36 -0
  43. package/dist/sandbox/core/lsp/diagnostics.d.ts +12 -0
  44. package/dist/sandbox/core/lsp/diagnostics.js +65 -0
  45. package/dist/sandbox/core/lsp/index.d.ts +4 -0
  46. package/dist/sandbox/core/lsp/index.js +4 -0
  47. package/dist/sandbox/core/lsp/language.d.ts +24 -0
  48. package/dist/sandbox/core/lsp/language.js +249 -0
  49. package/dist/sandbox/core/lsp/manager.d.ts +77 -0
  50. package/dist/sandbox/core/lsp/manager.js +237 -0
  51. package/dist/sandbox/core/lsp/tooling.d.ts +14 -0
  52. package/dist/sandbox/core/lsp/tooling.js +78 -0
  53. package/dist/sandbox/core/patch-parser.d.ts +23 -0
  54. package/dist/sandbox/core/patch-parser.js +248 -0
  55. package/dist/sandbox/core/path-map.d.ts +9 -0
  56. package/dist/sandbox/core/path-map.js +73 -0
  57. package/dist/sandbox/core/project-data-storage.d.ts +42 -0
  58. package/dist/sandbox/core/project-data-storage.js +167 -0
  59. package/dist/sandbox/core/read/binary.d.ts +4 -0
  60. package/dist/sandbox/core/read/binary.js +80 -0
  61. package/dist/sandbox/core/read/format.d.ts +38 -0
  62. package/dist/sandbox/core/read/format.js +85 -0
  63. package/dist/sandbox/core/read/index.d.ts +3 -0
  64. package/dist/sandbox/core/read/index.js +3 -0
  65. package/dist/sandbox/core/read/permissions.d.ts +7 -0
  66. package/dist/sandbox/core/read/permissions.js +13 -0
  67. package/dist/sandbox/core/session-manager.d.ts +29 -0
  68. package/dist/sandbox/core/session-manager.js +338 -0
  69. package/dist/sandbox/core/shell/config.d.ts +7 -0
  70. package/dist/sandbox/core/shell/config.js +82 -0
  71. package/dist/sandbox/core/shell/output.d.ts +35 -0
  72. package/dist/sandbox/core/shell/output.js +80 -0
  73. package/dist/sandbox/core/shell/parser.d.ts +7 -0
  74. package/dist/sandbox/core/shell/parser.js +122 -0
  75. package/dist/sandbox/core/shell/permissions.d.ts +13 -0
  76. package/dist/sandbox/core/shell/permissions.js +33 -0
  77. package/dist/sandbox/core/shell/workdir.d.ts +4 -0
  78. package/dist/sandbox/core/shell/workdir.js +19 -0
  79. package/dist/sandbox/core/stream-utils.d.ts +23 -0
  80. package/dist/sandbox/core/stream-utils.js +97 -0
  81. package/dist/sandbox/core/toast.d.ts +47 -0
  82. package/dist/sandbox/core/toast.js +73 -0
  83. package/dist/sandbox/core/types.d.ts +159 -0
  84. package/dist/sandbox/core/types.js +11 -0
  85. package/dist/sandbox/core/write/bom.d.ts +8 -0
  86. package/dist/sandbox/core/write/bom.js +15 -0
  87. package/dist/sandbox/core/write/config.d.ts +14 -0
  88. package/dist/sandbox/core/write/config.js +188 -0
  89. package/dist/sandbox/core/write/diagnostics.d.ts +19 -0
  90. package/dist/sandbox/core/write/diagnostics.js +120 -0
  91. package/dist/sandbox/core/write/diff.d.ts +7 -0
  92. package/dist/sandbox/core/write/diff.js +21 -0
  93. package/dist/sandbox/core/write/formatter.d.ts +16 -0
  94. package/dist/sandbox/core/write/formatter.js +51 -0
  95. package/dist/sandbox/core/write/index.d.ts +6 -0
  96. package/dist/sandbox/core/write/index.js +5 -0
  97. package/dist/sandbox/core/write/permissions.d.ts +13 -0
  98. package/dist/sandbox/core/write/permissions.js +19 -0
  99. package/dist/sandbox/core/write/pipeline.d.ts +48 -0
  100. package/dist/sandbox/core/write/pipeline.js +229 -0
  101. package/dist/sandbox/core/write/read-tracker.d.ts +13 -0
  102. package/dist/sandbox/core/write/read-tracker.js +30 -0
  103. package/dist/sandbox/git/host-git-manager.d.ts +40 -0
  104. package/dist/sandbox/git/host-git-manager.js +278 -0
  105. package/dist/sandbox/git/index.d.ts +5 -0
  106. package/dist/sandbox/git/index.js +5 -0
  107. package/dist/sandbox/git/sandbox-git-manager.d.ts +14 -0
  108. package/dist/sandbox/git/sandbox-git-manager.js +54 -0
  109. package/dist/sandbox/git/session-git-manager.d.ts +18 -0
  110. package/dist/sandbox/git/session-git-manager.js +85 -0
  111. package/dist/sandbox/index.d.ts +205 -0
  112. package/dist/sandbox/index.js +70 -0
  113. package/dist/sandbox/plugins/custom-tools.d.ts +203 -0
  114. package/dist/sandbox/plugins/custom-tools.js +15 -0
  115. package/dist/sandbox/plugins/session-events.d.ts +10 -0
  116. package/dist/sandbox/plugins/session-events.js +56 -0
  117. package/dist/sandbox/plugins/system-transform.d.ts +10 -0
  118. package/dist/sandbox/plugins/system-transform.js +23 -0
  119. package/dist/sandbox/tools/bash-output.d.ts +17 -0
  120. package/dist/sandbox/tools/bash-output.js +35 -0
  121. package/dist/sandbox/tools/bash-status.d.ts +13 -0
  122. package/dist/sandbox/tools/bash-status.js +29 -0
  123. package/dist/sandbox/tools/bash-stop.d.ts +13 -0
  124. package/dist/sandbox/tools/bash-stop.js +28 -0
  125. package/dist/sandbox/tools/bash.d.ts +26 -0
  126. package/dist/sandbox/tools/bash.js +120 -0
  127. package/dist/sandbox/tools/edit.d.ts +20 -0
  128. package/dist/sandbox/tools/edit.js +87 -0
  129. package/dist/sandbox/tools/get-preview-url.d.ts +17 -0
  130. package/dist/sandbox/tools/get-preview-url.js +16 -0
  131. package/dist/sandbox/tools/glob.d.ts +17 -0
  132. package/dist/sandbox/tools/glob.js +23 -0
  133. package/dist/sandbox/tools/grep.d.ts +17 -0
  134. package/dist/sandbox/tools/grep.js +23 -0
  135. package/dist/sandbox/tools/ls.d.ts +17 -0
  136. package/dist/sandbox/tools/ls.js +21 -0
  137. package/dist/sandbox/tools/lsp.d.ts +41 -0
  138. package/dist/sandbox/tools/lsp.js +198 -0
  139. package/dist/sandbox/tools/multiedit.d.ts +24 -0
  140. package/dist/sandbox/tools/multiedit.js +83 -0
  141. package/dist/sandbox/tools/patch.d.ts +14 -0
  142. package/dist/sandbox/tools/patch.js +260 -0
  143. package/dist/sandbox/tools/read.d.ts +22 -0
  144. package/dist/sandbox/tools/read.js +105 -0
  145. package/dist/sandbox/tools/write.d.ts +16 -0
  146. package/dist/sandbox/tools/write.js +27 -0
  147. package/dist/sandbox/tools.d.ts +200 -0
  148. package/dist/sandbox/tools.js +43 -0
  149. package/package.json +55 -0
@@ -0,0 +1,80 @@
1
+ import { mkdirSync, writeFileSync } from 'fs';
2
+ import { homedir } from 'os';
3
+ import { join } from 'path';
4
+ export const DEFAULT_SHELL_TIMEOUT_MS = 120_000;
5
+ export const DEFAULT_SHELL_MAX_OUTPUT_BYTES = 200_000;
6
+ export const DEFAULT_SHELL_MAX_OUTPUT_LINES = 2_000;
7
+ export const DEFAULT_SHELL_METADATA_BYTES = 30_000;
8
+ export function previewOutput(output, maxBytes = DEFAULT_SHELL_METADATA_BYTES) {
9
+ if (Buffer.byteLength(output, 'utf8') <= maxBytes)
10
+ return output;
11
+ return tailBytes(output, maxBytes);
12
+ }
13
+ export function tailBytes(value, maxBytes) {
14
+ const buffer = Buffer.from(value, 'utf8');
15
+ if (buffer.length <= maxBytes)
16
+ return value;
17
+ return buffer
18
+ .subarray(buffer.length - maxBytes)
19
+ .toString('utf8')
20
+ .replace(/^\uFFFD+/, '');
21
+ }
22
+ export function truncateShellOutput(value, limits) {
23
+ const bytes = Buffer.byteLength(value, 'utf8');
24
+ const lines = value === '' ? 0 : value.split(/\r?\n/).length;
25
+ let output = value;
26
+ let truncated = false;
27
+ if (lines > limits.maxLines) {
28
+ output = output.split(/\r?\n/).slice(-limits.maxLines).join('\n');
29
+ truncated = true;
30
+ }
31
+ if (Buffer.byteLength(output, 'utf8') > limits.maxBytes) {
32
+ output = tailBytes(output, limits.maxBytes);
33
+ truncated = true;
34
+ }
35
+ return { output, truncated, bytes, lines };
36
+ }
37
+ export function formatShellOutput(input) {
38
+ const parts = [];
39
+ if (input.stdout)
40
+ parts.push(input.stdout.replace(/\s+$/, ''));
41
+ if (input.stderr)
42
+ parts.push(input.stderr.replace(/\s+$/, ''));
43
+ let output = parts.filter(Boolean).join('\n');
44
+ if (!output)
45
+ output = '(no output)';
46
+ const metadata = [
47
+ `exitCode: ${input.exitCode === null ? 'null' : input.exitCode}`,
48
+ `durationMs: ${input.durationMs}`,
49
+ ];
50
+ if (typeof input.stdoutBytes === 'number')
51
+ metadata.push(`stdoutBytes: ${input.stdoutBytes}`);
52
+ if (typeof input.stderrBytes === 'number')
53
+ metadata.push(`stderrBytes: ${input.stderrBytes}`);
54
+ if (typeof input.stdoutLines === 'number')
55
+ metadata.push(`stdoutLines: ${input.stdoutLines}`);
56
+ if (typeof input.stderrLines === 'number')
57
+ metadata.push(`stderrLines: ${input.stderrLines}`);
58
+ if (input.timedOut) {
59
+ metadata.push('timedOut: true', 'shell tool terminated the command after exceeding the timeout. If this command is expected to take longer and is not waiting for interactive input, retry with a larger timeout value in milliseconds.');
60
+ }
61
+ if (input.aborted)
62
+ metadata.push('aborted: true');
63
+ if (input.truncated)
64
+ metadata.push('truncated: true');
65
+ if (input.outputPath)
66
+ metadata.push(`fullOutputPath: ${input.outputPath}`);
67
+ return `${output}\n\n<shell_metadata>\n${metadata.join('\n')}\n</shell_metadata>`;
68
+ }
69
+ export function writeHostShellOutput(input) {
70
+ const xdgData = process.env.XDG_DATA_HOME || join(homedir(), '.local', 'share');
71
+ const dir = join(xdgData, 'opencode', 'storage', 'sandbox', 'shell-output', sanitizePathPart(input.sessionId));
72
+ mkdirSync(dir, { recursive: true });
73
+ const file = join(dir, `${sanitizePathPart(input.cmdId)}.log`);
74
+ const content = input.stderr ? `${input.stdout}\n\n[stderr]\n${input.stderr}` : input.stdout;
75
+ writeFileSync(file, content);
76
+ return file;
77
+ }
78
+ function sanitizePathPart(value) {
79
+ return value.replace(/[^a-zA-Z0-9_.-]/g, '_').slice(0, 120) || 'unknown';
80
+ }
@@ -0,0 +1,7 @@
1
+ export type ParsedShellCommand = {
2
+ text: string;
3
+ tokens: string[];
4
+ };
5
+ export declare function parseShellCommands(command: string, limit?: number): ParsedShellCommand[];
6
+ export declare function collectCommandPatterns(command: string): string[];
7
+ export declare function collectAlwaysPatterns(command: string): string[];
@@ -0,0 +1,122 @@
1
+ export function parseShellCommands(command, limit = 8) {
2
+ const trimmed = command.trim();
3
+ if (!trimmed)
4
+ return [];
5
+ const split = splitCommandSegments(trimmed, limit);
6
+ const segments = split.fallback ? [trimmed] : split.segments;
7
+ return segments
8
+ .map((text) => ({ text, tokens: tokenizeShellCommand(text) }))
9
+ .filter((item) => item.text.length > 0)
10
+ .slice(0, limit);
11
+ }
12
+ export function collectCommandPatterns(command) {
13
+ const commands = parseShellCommands(command);
14
+ return commands.length ? commands.map((item) => item.text) : [command.trim()].filter(Boolean);
15
+ }
16
+ export function collectAlwaysPatterns(command) {
17
+ return parseShellCommands(command)
18
+ .map((item) => item.tokens.slice(0, 2).join(' ').trim())
19
+ .filter(Boolean)
20
+ .map((prefix) => `${prefix} *`);
21
+ }
22
+ function splitCommandSegments(command, limit) {
23
+ const segments = [];
24
+ let current = '';
25
+ let quote;
26
+ let escaped = false;
27
+ for (let i = 0; i < command.length; i++) {
28
+ const char = command[i];
29
+ const next = command[i + 1];
30
+ if (escaped) {
31
+ current += char;
32
+ escaped = false;
33
+ continue;
34
+ }
35
+ if (char === '\\') {
36
+ current += char;
37
+ escaped = true;
38
+ continue;
39
+ }
40
+ if (quote) {
41
+ current += char;
42
+ if (char === quote)
43
+ quote = undefined;
44
+ continue;
45
+ }
46
+ if (char === '"' || char === "'") {
47
+ current += char;
48
+ quote = char;
49
+ continue;
50
+ }
51
+ if (char === ';' || char === '\n') {
52
+ pushSegment();
53
+ continue;
54
+ }
55
+ if ((char === '&' && next === '&') || (char === '|' && next === '|')) {
56
+ pushSegment();
57
+ i++;
58
+ continue;
59
+ }
60
+ if (char === '|') {
61
+ pushSegment();
62
+ continue;
63
+ }
64
+ current += char;
65
+ }
66
+ if (quote || escaped)
67
+ return { segments: [], fallback: true };
68
+ pushSegment();
69
+ return { segments: segments.slice(0, limit), fallback: false };
70
+ function pushSegment() {
71
+ const segment = current.trim();
72
+ current = '';
73
+ if (segment)
74
+ segments.push(segment);
75
+ }
76
+ }
77
+ function tokenizeShellCommand(command) {
78
+ const tokens = [];
79
+ let current = '';
80
+ let quote;
81
+ let escaped = false;
82
+ for (let i = 0; i < command.length; i++) {
83
+ const char = command[i];
84
+ if (escaped) {
85
+ current += char;
86
+ escaped = false;
87
+ continue;
88
+ }
89
+ if (char === '\\') {
90
+ escaped = true;
91
+ continue;
92
+ }
93
+ if (quote) {
94
+ if (char === quote)
95
+ quote = undefined;
96
+ else
97
+ current += char;
98
+ continue;
99
+ }
100
+ if (char === '"' || char === "'") {
101
+ quote = char;
102
+ continue;
103
+ }
104
+ if (/\s/.test(char)) {
105
+ pushToken();
106
+ continue;
107
+ }
108
+ if (char === '>' || char === '<') {
109
+ pushToken();
110
+ break;
111
+ }
112
+ current += char;
113
+ }
114
+ pushToken();
115
+ return tokens;
116
+ function pushToken() {
117
+ if (!current)
118
+ return;
119
+ tokens.push(current);
120
+ current = '';
121
+ }
122
+ }
@@ -0,0 +1,13 @@
1
+ import type { ToolContext } from '@opencode-ai/plugin/tool';
2
+ export declare function commandDescription(command: string, description?: string): string;
3
+ export declare function collectSimpleBashPatterns(command: string): string[];
4
+ export declare function collectSimpleBashAlwaysPatterns(command: string): string[];
5
+ export declare function requestBashPermission(input: {
6
+ ctx: ToolContext;
7
+ command: string;
8
+ description: string;
9
+ workdir: string;
10
+ timeoutMs: number;
11
+ background?: boolean;
12
+ sandboxId?: string;
13
+ }): Promise<void>;
@@ -0,0 +1,33 @@
1
+ import { collectAlwaysPatterns, collectCommandPatterns } from './parser.js';
2
+ export function commandDescription(command, description) {
3
+ const trimmed = description?.trim();
4
+ if (trimmed)
5
+ return trimmed;
6
+ const firstLine = command.trim().split(/\r?\n/, 1)[0] ?? command;
7
+ return firstLine.length > 80 ? `${firstLine.slice(0, 77)}...` : firstLine;
8
+ }
9
+ export function collectSimpleBashPatterns(command) {
10
+ return collectCommandPatterns(command);
11
+ }
12
+ export function collectSimpleBashAlwaysPatterns(command) {
13
+ return collectAlwaysPatterns(command);
14
+ }
15
+ export async function requestBashPermission(input) {
16
+ if (typeof input.ctx.ask !== 'function')
17
+ return;
18
+ const patterns = collectSimpleBashPatterns(input.command);
19
+ const always = collectSimpleBashAlwaysPatterns(input.command);
20
+ await input.ctx.ask({
21
+ permission: 'bash',
22
+ patterns,
23
+ always: always.length ? always : patterns,
24
+ metadata: {
25
+ command: input.command,
26
+ description: input.description,
27
+ workdir: input.workdir,
28
+ timeout: input.timeoutMs,
29
+ background: Boolean(input.background),
30
+ sandbox: input.sandboxId,
31
+ },
32
+ });
33
+ }
@@ -0,0 +1,4 @@
1
+ import type { PathMapper } from '../path-map.js';
2
+ export declare function resolveShellWorkdir(input: string | undefined, paths: PathMapper, opts?: {
3
+ allowOutsideProject?: boolean;
4
+ }): string;
@@ -0,0 +1,19 @@
1
+ import path from 'path';
2
+ import posix from 'path/posix';
3
+ function isInsideOrEqualPosix(target, root) {
4
+ const relative = posix.relative(root, target);
5
+ return relative === '' || (!relative.startsWith('..') && !posix.isAbsolute(relative));
6
+ }
7
+ export function resolveShellWorkdir(input, paths, opts) {
8
+ if (!input || input.trim() === '')
9
+ return paths.repoPath;
10
+ const containerPath = paths.toContainer(input);
11
+ const normalized = posix.normalize(containerPath);
12
+ if (!opts?.allowOutsideProject && !isInsideOrEqualPosix(normalized, paths.repoPath)) {
13
+ throw new Error(`workdir must be inside the mapped project (${paths.repoPath}). Received: ${input}. Use a path under the host worktree (${paths.worktree}) or ${paths.repoPath}.`);
14
+ }
15
+ if (!opts?.allowOutsideProject && path.isAbsolute(input) && !paths.isMapped(input)) {
16
+ throw new Error(`workdir is outside the mapped project: ${input}`);
17
+ }
18
+ return normalized;
19
+ }
@@ -0,0 +1,23 @@
1
+ import type Dockerode from 'dockerode';
2
+ export type DemuxedOutput = {
3
+ stdout: string;
4
+ stderr: string;
5
+ };
6
+ export declare function demuxStream(stream: NodeJS.ReadableStream): Promise<DemuxedOutput>;
7
+ export declare function execInContainer(container: Dockerode.Container, command: string[], workDir?: string, opts?: {
8
+ tty?: boolean;
9
+ }): Promise<{
10
+ exitCode: number;
11
+ stdout: string;
12
+ stderr: string;
13
+ }>;
14
+ export declare function parseLsOutput(output: string): Array<{
15
+ name: string;
16
+ isDirectory: boolean;
17
+ size: number;
18
+ }>;
19
+ export declare function parseGrepOutput(output: string): Array<{
20
+ file: string;
21
+ line: number;
22
+ content: string;
23
+ }>;
@@ -0,0 +1,97 @@
1
+ const HEADER_SIZE = 8;
2
+ const STREAM_STDOUT = 1;
3
+ const STREAM_STDERR = 2;
4
+ export function demuxStream(stream) {
5
+ return new Promise((resolve, reject) => {
6
+ let stdout = '';
7
+ let stderr = '';
8
+ let buffer = Buffer.alloc(0);
9
+ stream.on('data', (chunk) => {
10
+ buffer = Buffer.concat([buffer, chunk]);
11
+ while (buffer.length >= HEADER_SIZE) {
12
+ const header = buffer.subarray(0, HEADER_SIZE);
13
+ const streamType = header[0];
14
+ const payloadSize = header.readUInt32BE(4);
15
+ if (buffer.length < HEADER_SIZE + payloadSize)
16
+ break;
17
+ const payload = buffer.subarray(HEADER_SIZE, HEADER_SIZE + payloadSize);
18
+ const content = payload.toString('utf-8');
19
+ if (streamType === STREAM_STDOUT) {
20
+ stdout += content;
21
+ }
22
+ else if (streamType === STREAM_STDERR) {
23
+ stderr += content;
24
+ }
25
+ buffer = buffer.subarray(HEADER_SIZE + payloadSize);
26
+ }
27
+ });
28
+ stream.on('end', () => resolve({ stdout, stderr }));
29
+ stream.on('error', (err) => reject(err));
30
+ });
31
+ }
32
+ export async function execInContainer(container, command, workDir, opts) {
33
+ const tty = opts?.tty ?? true;
34
+ const exec = await container.exec({
35
+ Cmd: command,
36
+ WorkingDir: workDir,
37
+ AttachStdout: true,
38
+ AttachStderr: true,
39
+ Tty: tty,
40
+ });
41
+ const stream = await exec.start({ Tty: tty });
42
+ const output = tty ? { stdout: await readStreamFully(stream), stderr: '' } : await demuxStream(stream);
43
+ const inspectResult = await exec.inspect();
44
+ const exitCode = inspectResult.ExitCode ?? 1;
45
+ if (tty) {
46
+ return {
47
+ exitCode,
48
+ stdout: exitCode === 0 ? output.stdout : '',
49
+ stderr: exitCode !== 0 ? output.stdout : '',
50
+ };
51
+ }
52
+ return {
53
+ exitCode,
54
+ stdout: output.stdout,
55
+ stderr: output.stderr,
56
+ };
57
+ }
58
+ function readStreamFully(stream) {
59
+ return new Promise((resolve, reject) => {
60
+ const chunks = [];
61
+ stream.on('data', (chunk) => chunks.push(chunk));
62
+ stream.on('end', () => resolve(Buffer.concat(chunks).toString('utf-8').replace(/\r\n/g, '\n')));
63
+ stream.on('error', (err) => reject(err));
64
+ });
65
+ }
66
+ export function parseLsOutput(output) {
67
+ const lines = output.trim().split('\n').filter(Boolean);
68
+ return lines.map((line) => {
69
+ const parts = line.split(/\s+/);
70
+ const perms = parts[0] || '';
71
+ const size = parseInt(parts[4] || '0', 10);
72
+ const name = parts.slice(8).join(' ');
73
+ return {
74
+ name,
75
+ isDirectory: perms.startsWith('d'),
76
+ size: isNaN(size) ? 0 : size,
77
+ };
78
+ });
79
+ }
80
+ export function parseGrepOutput(output) {
81
+ const lines = output.trim().split('\n').filter(Boolean);
82
+ return lines.map((line) => {
83
+ const firstColon = line.indexOf(':');
84
+ const secondColon = line.indexOf(':', firstColon + 1);
85
+ if (firstColon === -1 || secondColon === -1) {
86
+ return { file: line, line: 0, content: '' };
87
+ }
88
+ const file = line.substring(0, firstColon);
89
+ const lineNum = parseInt(line.substring(firstColon + 1, secondColon), 10);
90
+ const content = line.substring(secondColon + 1);
91
+ return {
92
+ file,
93
+ line: isNaN(lineNum) ? 0 : lineNum,
94
+ content,
95
+ };
96
+ });
97
+ }
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Copyright Daytona Platforms Inc.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+ /**
6
+ * Global toast notification singleton
7
+ * Queues toasts to prevent showing multiple at the same time
8
+ */
9
+ type ToastVariant = 'success' | 'error' | 'warning' | 'info';
10
+ interface ToastOptions {
11
+ title: string;
12
+ message: string;
13
+ variant?: ToastVariant;
14
+ }
15
+ interface TuiShowToast {
16
+ showToast: (options: {
17
+ body: {
18
+ title: string;
19
+ message: string;
20
+ variant: ToastVariant;
21
+ };
22
+ }) => void;
23
+ }
24
+ declare class ToastManager {
25
+ private tui;
26
+ private queue;
27
+ private isShowing;
28
+ /**
29
+ * Initialize the toast manager with the TUI instance
30
+ */
31
+ initialize(tui: TuiShowToast | null | undefined): void;
32
+ /**
33
+ * Show a toast notification
34
+ * If a toast is currently showing, this will be queued
35
+ */
36
+ show(options: ToastOptions): void;
37
+ /**
38
+ * Process the toast queue, showing one toast at a time
39
+ */
40
+ private processQueue;
41
+ /**
42
+ * Clear all pending toasts
43
+ */
44
+ clear(): void;
45
+ }
46
+ export declare const toast: ToastManager;
47
+ export {};
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Copyright Daytona Platforms Inc.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+ class ToastManager {
6
+ tui = null;
7
+ queue = [];
8
+ isShowing = false;
9
+ /**
10
+ * Initialize the toast manager with the TUI instance
11
+ */
12
+ initialize(tui) {
13
+ this.tui = tui || null;
14
+ }
15
+ /**
16
+ * Show a toast notification
17
+ * If a toast is currently showing, this will be queued
18
+ */
19
+ show(options) {
20
+ const toast = {
21
+ variant: 'info',
22
+ ...options,
23
+ };
24
+ this.queue.push(toast);
25
+ this.processQueue();
26
+ }
27
+ /**
28
+ * Process the toast queue, showing one toast at a time
29
+ */
30
+ processQueue() {
31
+ if (this.isShowing || this.queue.length === 0) {
32
+ return;
33
+ }
34
+ if (!this.tui) {
35
+ // If TUI is not available, clear the queue
36
+ this.queue = [];
37
+ return;
38
+ }
39
+ this.isShowing = true;
40
+ const toast = this.queue.shift();
41
+ if (!toast) {
42
+ this.isShowing = false;
43
+ return;
44
+ }
45
+ try {
46
+ this.tui.showToast({
47
+ body: {
48
+ title: toast.title,
49
+ message: toast.message,
50
+ variant: toast.variant || 'info',
51
+ },
52
+ });
53
+ }
54
+ catch (err) {
55
+ // If showing fails, continue with next toast
56
+ console.error('Failed to show toast:', err);
57
+ }
58
+ // Wait a bit before showing the next toast to avoid overlap
59
+ // Most toasts are visible for 2-3 seconds, so we wait 2.5 seconds
60
+ setTimeout(() => {
61
+ this.isShowing = false;
62
+ this.processQueue();
63
+ }, 2500);
64
+ }
65
+ /**
66
+ * Clear all pending toasts
67
+ */
68
+ clear() {
69
+ this.queue = [];
70
+ }
71
+ }
72
+ // Export singleton instance
73
+ export const toast = new ToastManager();
@@ -0,0 +1,159 @@
1
+ /**
2
+ * Copyright Daytona Platforms Inc.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+ /**
6
+ * Type definitions and constants for the Docker sandbox OpenCode plugin
7
+ */
8
+ export type EventSessionDeleted = {
9
+ type: 'session.deleted';
10
+ properties: {
11
+ info: {
12
+ id: string;
13
+ };
14
+ };
15
+ };
16
+ export type EventSessionIdle = {
17
+ type: 'session.idle';
18
+ properties: {
19
+ sessionID: string;
20
+ };
21
+ };
22
+ export type ExperimentalChatSystemTransformInput = {
23
+ sessionID?: string;
24
+ model: any;
25
+ };
26
+ export type ExperimentalChatSystemTransformOutput = {
27
+ system: string[];
28
+ };
29
+ export declare const EVENT_TYPE_SESSION_DELETED = "session.deleted";
30
+ export declare const EVENT_TYPE_SESSION_IDLE = "session.idle";
31
+ export type LogLevel = 'INFO' | 'ERROR' | 'WARN';
32
+ export type SandboxInfo = {
33
+ id: string;
34
+ };
35
+ export type SessionInfo = {
36
+ sandboxId: string;
37
+ branchNumber?: number;
38
+ created: number;
39
+ lastAccessed: number;
40
+ };
41
+ export type ProjectSessionData = {
42
+ projectId: string;
43
+ worktree: string;
44
+ sessions: Record<string, SessionInfo>;
45
+ };
46
+ export type SessionSandboxMap = Map<string, Sandbox | SandboxInfo>;
47
+ export type SandboxCommandOptions = {
48
+ cwd?: string;
49
+ timeoutMs?: number;
50
+ signal?: AbortSignal;
51
+ sessionId?: string;
52
+ maxOutputBytes?: number;
53
+ maxOutputLines?: number;
54
+ tty?: boolean;
55
+ onUpdate?: (update: SandboxCommandUpdate) => void;
56
+ };
57
+ export type SandboxCommandUpdate = SandboxBackgroundOutput & {
58
+ durationMs: number;
59
+ };
60
+ export type SandboxProcessResult = {
61
+ exitCode: number | null;
62
+ result: string;
63
+ stdout?: string;
64
+ stderr?: string;
65
+ durationMs?: number;
66
+ timedOut?: boolean;
67
+ aborted?: boolean;
68
+ truncated?: boolean;
69
+ outputPath?: string;
70
+ stdoutBytes?: number;
71
+ stderrBytes?: number;
72
+ stdoutLines?: number;
73
+ stderrLines?: number;
74
+ cmdId?: string;
75
+ };
76
+ export type SandboxBackgroundCommandOptions = {
77
+ cwd?: string;
78
+ sessionId?: string;
79
+ };
80
+ export type SandboxBackgroundStartResult = {
81
+ cmdId: string;
82
+ runDir: string;
83
+ startedAt: number;
84
+ };
85
+ export type SandboxBackgroundStatus = {
86
+ cmdId: string;
87
+ status: 'running' | 'exited' | 'failed' | 'stopped' | 'unknown';
88
+ exitCode: number | null;
89
+ pid?: number;
90
+ runDir?: string;
91
+ };
92
+ export type SandboxBackgroundOutput = {
93
+ cmdId: string;
94
+ output: string;
95
+ stdout: string;
96
+ stderr: string;
97
+ stdoutBytes: number;
98
+ stderrBytes: number;
99
+ stdoutLines?: number;
100
+ stderrLines?: number;
101
+ truncated: boolean;
102
+ runDir?: string;
103
+ };
104
+ export type SandboxSessionResult = {
105
+ cmdId: string;
106
+ };
107
+ export type SandboxFileInfo = {
108
+ name: string;
109
+ isDirectory: boolean;
110
+ size: number;
111
+ };
112
+ export type SandboxFileSearchResult = {
113
+ files: string[];
114
+ };
115
+ export type SandboxMatch = {
116
+ file: string;
117
+ line: number;
118
+ content: string;
119
+ };
120
+ export type SandboxPreviewLink = {
121
+ url: string;
122
+ };
123
+ export interface Sandbox {
124
+ readonly id: string;
125
+ readonly state: string;
126
+ readonly process: {
127
+ executeCommand(command: string, cwd?: string | SandboxCommandOptions): Promise<SandboxProcessResult>;
128
+ startBackgroundCommand(command: string, opts?: SandboxBackgroundCommandOptions): Promise<SandboxBackgroundStartResult>;
129
+ getBackgroundCommand(cmdId: string): Promise<SandboxBackgroundStatus>;
130
+ readBackgroundCommand(cmdId: string, opts?: {
131
+ maxOutputBytes?: number;
132
+ maxOutputLines?: number;
133
+ }): Promise<SandboxBackgroundOutput>;
134
+ stopBackgroundCommand(cmdId: string): Promise<SandboxBackgroundStatus>;
135
+ createSession(sessionId: string): Promise<void>;
136
+ getSession(sessionId: string): Promise<void>;
137
+ executeSessionCommand(sessionId: string, opts: {
138
+ command: string;
139
+ runAsync?: boolean;
140
+ cwd?: string;
141
+ }): Promise<SandboxSessionResult>;
142
+ };
143
+ readonly fs: {
144
+ downloadFile(path: string): Promise<Buffer>;
145
+ uploadFile(content: Buffer, path: string): Promise<void>;
146
+ createFolder(path: string, mode?: string): Promise<void>;
147
+ listFiles(path: string): Promise<SandboxFileInfo[]>;
148
+ searchFiles(path: string, pattern: string): Promise<SandboxFileSearchResult>;
149
+ findFiles(path: string, pattern: string): Promise<SandboxMatch[]>;
150
+ };
151
+ getWorkDir(): Promise<string>;
152
+ getPreviewLink(port: number): Promise<SandboxPreviewLink>;
153
+ start(): Promise<void>;
154
+ refreshData(): Promise<void>;
155
+ delete(): Promise<void>;
156
+ }
157
+ export declare const LOG_LEVEL_INFO: LogLevel;
158
+ export declare const LOG_LEVEL_ERROR: LogLevel;
159
+ export declare const LOG_LEVEL_WARN: LogLevel;