@larkup/sandbox 0.1.3 → 0.1.5
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/.turbo/turbo-build.log +1 -1
- package/.turbo/turbo-test.log +18 -0
- package/.turbo/turbo-type-check.log +4 -0
- package/CHANGELOG.md +14 -0
- package/dist/local-runner.js +27 -1
- package/dist/providers/daytona.js +1 -1
- package/dist/registry.js +1 -1
- package/package.json +3 -3
- package/src/local-runner.test.ts +11 -1
- package/src/local-runner.ts +40 -9
- package/src/providers/daytona.test.ts +16 -5
- package/src/providers/daytona.ts +1 -1
- package/src/registry.ts +1 -1
package/.turbo/turbo-build.log
CHANGED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
|
|
2
|
+
> @larkup/sandbox@0.1.5 test /Users/abdelrahmanabounida/Desktop/my-ai-startup/larkup/larkup/packages/sandbox
|
|
3
|
+
> vitest run
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
[1m[30m[46m RUN [49m[39m[22m [36mv4.1.11 [39m[90m/Users/abdelrahmanabounida/Desktop/my-ai-startup/larkup/larkup/packages/sandbox[39m
|
|
7
|
+
|
|
8
|
+
[32m✓[39m src/remote-exec.test.ts [2m([22m[2m14 tests[22m[2m)[22m[32m 116[2mms[22m[39m
|
|
9
|
+
[32m✓[39m src/providers/flyio.test.ts [2m([22m[2m6 tests[22m[2m)[22m[32m 9[2mms[22m[39m
|
|
10
|
+
[32m✓[39m src/providers/modal.test.ts [2m([22m[2m4 tests[22m[2m)[22m[32m 10[2mms[22m[39m
|
|
11
|
+
[32m✓[39m src/providers/daytona.test.ts [2m([22m[2m5 tests[22m[2m)[22m[32m 4[2mms[22m[39m
|
|
12
|
+
[32m✓[39m src/providers/vercel.test.ts [2m([22m[2m6 tests[22m[2m)[22m[32m 36[2mms[22m[39m
|
|
13
|
+
[32m✓[39m src/providers/e2b.test.ts [2m([22m[2m9 tests[22m[2m)[22m[32m 45[2mms[22m[39m
|
|
14
|
+
[32m✓[39m src/providers/northflank.test.ts [2m([22m[2m7 tests[22m[2m)[22m[32m 39[2mms[22m[39m
|
|
15
|
+
[32m✓[39m src/local-runner.test.ts [2m([22m[2m2 tests[22m[2m)[22m[33m 505[2mms[22m[39m
|
|
16
|
+
[33m[2m✓[22m[39m runs JavaScript without Docker and returns output artifacts [33m 359[2mms[22m[39m
|
|
17
|
+
[32m✓[39m src/providers/cloudflare.test.ts [2m([22m[2m6 tests[22m[2m)[22m[32m 14[2mms[22m[39m
|
|
18
|
+
ELIFECYCLE Test failed. See above for more details.
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# @larkup/sandbox
|
|
2
2
|
|
|
3
|
+
## 0.1.5
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 848cc4c: Route spreadsheet questions through structured table queries before optional code analysis, validate local Python analysis dependencies before exposing code tools, and index every non-empty Excel worksheet as a separate dataset.
|
|
8
|
+
|
|
9
|
+
## 0.1.4
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- 847b502: Keep global installs and updates quiet and compatible by aligning Apache Arrow with LanceDB's
|
|
14
|
+
supported peer range, shipping the marketplace TypeScript runtime loader, and using the renamed
|
|
15
|
+
Daytona SDK package. Public tarballs also scrub local project state from all traced workspaces.
|
|
16
|
+
|
|
3
17
|
## 0.1.3
|
|
4
18
|
|
|
5
19
|
### Patch Changes
|
package/dist/local-runner.js
CHANGED
|
@@ -5,6 +5,10 @@ import path from 'node:path';
|
|
|
5
5
|
import { spawn } from 'node:child_process';
|
|
6
6
|
import { getMimeType } from './mime.js';
|
|
7
7
|
let pythonCommand;
|
|
8
|
+
// Keep this list in sync with the capabilities advertised by executeAnalysis
|
|
9
|
+
// and analyzeCorpusWithCode. A bare Python installation is not an analysis
|
|
10
|
+
// runtime, so it must not make those tools appear available.
|
|
11
|
+
const ANALYSIS_PYTHON_MODULES = ['numpy', 'pandas', 'matplotlib', 'scipy', 'sklearn', 'seaborn'];
|
|
8
12
|
function run(command, args, cwd, timeoutMs = 10_000) {
|
|
9
13
|
return new Promise((resolve, reject) => {
|
|
10
14
|
const child = spawn(command.executable, [...command.args, ...args], {
|
|
@@ -38,7 +42,7 @@ function run(command, args, cwd, timeoutMs = 10_000) {
|
|
|
38
42
|
resolve({
|
|
39
43
|
stdout,
|
|
40
44
|
stderr: timedOut ? `${stderr}\nExecution timed out.`.trim() : stderr,
|
|
41
|
-
exitCode: timedOut ? 124 : code ?? 1,
|
|
45
|
+
exitCode: timedOut ? 124 : (code ?? 1),
|
|
42
46
|
});
|
|
43
47
|
});
|
|
44
48
|
});
|
|
@@ -115,6 +119,28 @@ export async function checkLocalRuntime() {
|
|
|
115
119
|
error: 'Local code execution needs Python 3. Install Python or choose Docker or a remote sandbox provider.',
|
|
116
120
|
};
|
|
117
121
|
}
|
|
122
|
+
try {
|
|
123
|
+
const dependencyCheck = await run(python, [
|
|
124
|
+
'-c',
|
|
125
|
+
`import importlib.util\nmodules = ${JSON.stringify(ANALYSIS_PYTHON_MODULES)}\nmissing = [name for name in modules if importlib.util.find_spec(name) is None]\nprint(','.join(missing))\nraise SystemExit(bool(missing))`,
|
|
126
|
+
]);
|
|
127
|
+
if (dependencyCheck.exitCode !== 0) {
|
|
128
|
+
const missing = dependencyCheck.stdout.trim() || 'required analysis packages';
|
|
129
|
+
return {
|
|
130
|
+
status: 'error',
|
|
131
|
+
backend: 'local',
|
|
132
|
+
error: `Local Python is missing ${missing}. Install the Larkup analysis dependencies ` +
|
|
133
|
+
`(for example: pip install ${ANALYSIS_PYTHON_MODULES.join(' ')}) or choose Docker or a remote sandbox provider.`,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
catch {
|
|
138
|
+
return {
|
|
139
|
+
status: 'error',
|
|
140
|
+
backend: 'local',
|
|
141
|
+
error: 'Larkup could not verify the Python analysis dependencies. Choose Docker or a remote sandbox provider.',
|
|
142
|
+
};
|
|
143
|
+
}
|
|
118
144
|
return { status: 'ready', backend: 'local' };
|
|
119
145
|
}
|
|
120
146
|
/** Executes trusted code in a temporary host workspace and collects its output. */
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
*/
|
|
11
11
|
import { DEFAULT_TIMEOUT_MS, failedResult, runScriptedExecution, safeCleanup, withTimeout, } from '../remote-exec.js';
|
|
12
12
|
async function loadSdk() {
|
|
13
|
-
return (await import('@
|
|
13
|
+
return (await import('@daytona/sdk'));
|
|
14
14
|
}
|
|
15
15
|
function requireFields(credentials) {
|
|
16
16
|
const apiKey = credentials.apiKey?.trim();
|
package/dist/registry.js
CHANGED
|
@@ -114,7 +114,7 @@ export const SANDBOX_PROVIDERS = {
|
|
|
114
114
|
id: 'daytona',
|
|
115
115
|
label: 'Daytona',
|
|
116
116
|
description: 'Elastic infrastructure for running AI-generated code in fast, isolated sandboxes.',
|
|
117
|
-
sdkPackage: '@
|
|
117
|
+
sdkPackage: '@daytona/sdk',
|
|
118
118
|
docsUrl: 'https://www.daytona.io/docs/en/sandboxes/',
|
|
119
119
|
icon: '/icons/daytonaio.png',
|
|
120
120
|
executionSupport: 'full',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@larkup/sandbox",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -40,8 +40,8 @@
|
|
|
40
40
|
"optionalDependencies": {
|
|
41
41
|
"e2b": "^2.39.0",
|
|
42
42
|
"@vercel/sandbox": "^3.0.0",
|
|
43
|
-
"modal": "^0.
|
|
44
|
-
"@
|
|
43
|
+
"modal": "^0.10.0",
|
|
44
|
+
"@daytona/sdk": "^0.207.0",
|
|
45
45
|
"@browserbasehq/sdk": "^2.17.0",
|
|
46
46
|
"playwright-core": "^1.62.1",
|
|
47
47
|
"@fly/sprites": "^0.2.0",
|
package/src/local-runner.test.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, expect, it } from 'vitest';
|
|
2
|
-
import { executeLocally } from './local-runner.js';
|
|
2
|
+
import { checkLocalRuntime, executeLocally } from './local-runner.js';
|
|
3
3
|
|
|
4
4
|
describe('executeLocally', () => {
|
|
5
5
|
it('runs JavaScript without Docker and returns output artifacts', async () => {
|
|
@@ -19,4 +19,14 @@ describe('executeLocally', () => {
|
|
|
19
19
|
expect.objectContaining({ name: 'result.txt', mimeType: 'text/plain' }),
|
|
20
20
|
]);
|
|
21
21
|
});
|
|
22
|
+
|
|
23
|
+
it('does not mark a bare Python installation as analysis-ready', async () => {
|
|
24
|
+
const health = await checkLocalRuntime();
|
|
25
|
+
expect(health.backend).toBe('local');
|
|
26
|
+
if (health.status === 'ready') {
|
|
27
|
+
expect(health.error).toBeUndefined();
|
|
28
|
+
} else {
|
|
29
|
+
expect(health.error).toMatch(/Python|analysis dependenc/i);
|
|
30
|
+
}
|
|
31
|
+
});
|
|
22
32
|
});
|
package/src/local-runner.ts
CHANGED
|
@@ -16,6 +16,11 @@ type Command = { executable: string; args: string[] };
|
|
|
16
16
|
|
|
17
17
|
let pythonCommand: Command | null | undefined;
|
|
18
18
|
|
|
19
|
+
// Keep this list in sync with the capabilities advertised by executeAnalysis
|
|
20
|
+
// and analyzeCorpusWithCode. A bare Python installation is not an analysis
|
|
21
|
+
// runtime, so it must not make those tools appear available.
|
|
22
|
+
const ANALYSIS_PYTHON_MODULES = ['numpy', 'pandas', 'matplotlib', 'scipy', 'sklearn', 'seaborn'];
|
|
23
|
+
|
|
19
24
|
function run(command: Command, args: string[], cwd?: string, timeoutMs = 10_000) {
|
|
20
25
|
return new Promise<{ stdout: string; stderr: string; exitCode: number }>((resolve, reject) => {
|
|
21
26
|
const child = spawn(command.executable, [...command.args, ...args], {
|
|
@@ -49,7 +54,7 @@ function run(command: Command, args: string[], cwd?: string, timeoutMs = 10_000)
|
|
|
49
54
|
resolve({
|
|
50
55
|
stdout,
|
|
51
56
|
stderr: timedOut ? `${stderr}\nExecution timed out.`.trim() : stderr,
|
|
52
|
-
exitCode: timedOut ? 124 : code ?? 1,
|
|
57
|
+
exitCode: timedOut ? 124 : (code ?? 1),
|
|
53
58
|
});
|
|
54
59
|
});
|
|
55
60
|
});
|
|
@@ -60,14 +65,14 @@ async function resolvePython(): Promise<Command | null> {
|
|
|
60
65
|
const candidates: Command[] = process.env.LARKUP_PYTHON
|
|
61
66
|
? [{ executable: process.env.LARKUP_PYTHON, args: [] }]
|
|
62
67
|
: process.platform === 'win32'
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
68
|
+
? [
|
|
69
|
+
{ executable: 'py', args: ['-3'] },
|
|
70
|
+
{ executable: 'python', args: [] },
|
|
71
|
+
]
|
|
72
|
+
: [
|
|
73
|
+
{ executable: 'python3', args: [] },
|
|
74
|
+
{ executable: 'python', args: [] },
|
|
75
|
+
];
|
|
71
76
|
|
|
72
77
|
for (const candidate of candidates) {
|
|
73
78
|
try {
|
|
@@ -134,6 +139,32 @@ export async function checkLocalRuntime(): Promise<SandboxHealthCheck> {
|
|
|
134
139
|
'Local code execution needs Python 3. Install Python or choose Docker or a remote sandbox provider.',
|
|
135
140
|
};
|
|
136
141
|
}
|
|
142
|
+
|
|
143
|
+
try {
|
|
144
|
+
const dependencyCheck = await run(python, [
|
|
145
|
+
'-c',
|
|
146
|
+
`import importlib.util\nmodules = ${JSON.stringify(
|
|
147
|
+
ANALYSIS_PYTHON_MODULES,
|
|
148
|
+
)}\nmissing = [name for name in modules if importlib.util.find_spec(name) is None]\nprint(','.join(missing))\nraise SystemExit(bool(missing))`,
|
|
149
|
+
]);
|
|
150
|
+
if (dependencyCheck.exitCode !== 0) {
|
|
151
|
+
const missing = dependencyCheck.stdout.trim() || 'required analysis packages';
|
|
152
|
+
return {
|
|
153
|
+
status: 'error',
|
|
154
|
+
backend: 'local',
|
|
155
|
+
error:
|
|
156
|
+
`Local Python is missing ${missing}. Install the Larkup analysis dependencies ` +
|
|
157
|
+
`(for example: pip install ${ANALYSIS_PYTHON_MODULES.join(' ')}) or choose Docker or a remote sandbox provider.`,
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
} catch {
|
|
161
|
+
return {
|
|
162
|
+
status: 'error',
|
|
163
|
+
backend: 'local',
|
|
164
|
+
error:
|
|
165
|
+
'Larkup could not verify the Python analysis dependencies. Choose Docker or a remote sandbox provider.',
|
|
166
|
+
};
|
|
167
|
+
}
|
|
137
168
|
return { status: 'ready', backend: 'local' };
|
|
138
169
|
}
|
|
139
170
|
|
|
@@ -5,7 +5,7 @@ const deleteMock = vi.fn();
|
|
|
5
5
|
const createMock = vi.fn();
|
|
6
6
|
const listMock = vi.fn();
|
|
7
7
|
|
|
8
|
-
vi.mock('@
|
|
8
|
+
vi.mock('@daytona/sdk', () => ({
|
|
9
9
|
Daytona: vi.fn().mockImplementation(function () {
|
|
10
10
|
return { create: createMock, list: listMock };
|
|
11
11
|
}),
|
|
@@ -18,12 +18,17 @@ beforeEach(() => {
|
|
|
18
18
|
deleteMock.mockReset();
|
|
19
19
|
createMock.mockReset();
|
|
20
20
|
listMock.mockReset();
|
|
21
|
-
createMock.mockResolvedValue({
|
|
21
|
+
createMock.mockResolvedValue({
|
|
22
|
+
process: { executeCommand: executeCommandMock },
|
|
23
|
+
delete: deleteMock,
|
|
24
|
+
});
|
|
22
25
|
});
|
|
23
26
|
|
|
24
27
|
describe('daytonaAdapter.verifyCredentials', () => {
|
|
25
28
|
it('requires an API key', async () => {
|
|
26
|
-
await expect(daytonaAdapter.verifyCredentials({})).rejects.toThrow(
|
|
29
|
+
await expect(daytonaAdapter.verifyCredentials({})).rejects.toThrow(
|
|
30
|
+
'Daytona API key is required.',
|
|
31
|
+
);
|
|
27
32
|
});
|
|
28
33
|
|
|
29
34
|
it('uses list() as the cheap credential check', async () => {
|
|
@@ -38,7 +43,10 @@ describe('daytonaAdapter.verifyCredentials', () => {
|
|
|
38
43
|
describe('daytonaAdapter.execute — merged stdout/stderr', () => {
|
|
39
44
|
it('puts successful output in stdout only', async () => {
|
|
40
45
|
executeCommandMock.mockResolvedValue({ exitCode: 0, result: 'all good\n' });
|
|
41
|
-
const result = await daytonaAdapter.execute(
|
|
46
|
+
const result = await daytonaAdapter.execute(
|
|
47
|
+
{ code: 'print(1)', language: 'python' },
|
|
48
|
+
{ apiKey: 'k' },
|
|
49
|
+
);
|
|
42
50
|
expect(result.stdout).toBe('all good\n');
|
|
43
51
|
expect(result.stderr).toBe('');
|
|
44
52
|
expect(result.exitCode).toBe(0);
|
|
@@ -46,7 +54,10 @@ describe('daytonaAdapter.execute — merged stdout/stderr', () => {
|
|
|
46
54
|
|
|
47
55
|
it('mirrors the combined output into stderr when the command fails', async () => {
|
|
48
56
|
executeCommandMock.mockResolvedValue({ exitCode: 1, result: 'Traceback...\n' });
|
|
49
|
-
const result = await daytonaAdapter.execute(
|
|
57
|
+
const result = await daytonaAdapter.execute(
|
|
58
|
+
{ code: 'raise ValueError()', language: 'python' },
|
|
59
|
+
{ apiKey: 'k' },
|
|
60
|
+
);
|
|
50
61
|
expect(result.stdout).toBe('Traceback...\n');
|
|
51
62
|
expect(result.stderr).toBe('Traceback...\n');
|
|
52
63
|
expect(result.exitCode).toBe(1);
|
package/src/providers/daytona.ts
CHANGED
|
@@ -48,7 +48,7 @@ interface DaytonaModule {
|
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
async function loadSdk(): Promise<DaytonaModule> {
|
|
51
|
-
return (await import('@
|
|
51
|
+
return (await import('@daytona/sdk')) as unknown as DaytonaModule;
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
function requireFields(credentials: Record<string, string>): { apiKey: string; apiUrl?: string } {
|
package/src/registry.ts
CHANGED
|
@@ -126,7 +126,7 @@ export const SANDBOX_PROVIDERS: Record<
|
|
|
126
126
|
label: 'Daytona',
|
|
127
127
|
description:
|
|
128
128
|
'Elastic infrastructure for running AI-generated code in fast, isolated sandboxes.',
|
|
129
|
-
sdkPackage: '@
|
|
129
|
+
sdkPackage: '@daytona/sdk',
|
|
130
130
|
docsUrl: 'https://www.daytona.io/docs/en/sandboxes/',
|
|
131
131
|
icon: '/icons/daytonaio.png',
|
|
132
132
|
executionSupport: 'full',
|