@tpmjs/tools-sprites-exec 0.1.2 → 0.1.3

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 (3) hide show
  1. package/README.md +17 -0
  2. package/dist/index.js +104 -18
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -37,6 +37,23 @@ const pythonResult = await spritesExecTool.execute({
37
37
  cmd: 'python3',
38
38
  stdin: 'print("Hello from stdin!")'
39
39
  });
40
+
41
+ // For shell features (pipes, redirects, etc), use bash -c
42
+ const shellResult = await spritesExecTool.execute({
43
+ name: 'my-sandbox',
44
+ cmd: 'bash -c "echo hello > /tmp/test.txt && cat /tmp/test.txt"'
45
+ });
46
+ ```
47
+
48
+ ## Shell Commands
49
+
50
+ Commands are executed directly (like `exec.Command` in Go), not through a shell. This means shell operators like `|`, `>`, `>>`, `&&` won't work directly.
51
+
52
+ For shell features, wrap your command with `bash -c`:
53
+ ```typescript
54
+ // Won't work: cmd: 'echo hello > file.txt'
55
+ // Use instead:
56
+ cmd: 'bash -c "echo hello > file.txt"'
40
57
  ```
41
58
 
42
59
  ## Input Parameters
package/dist/index.js CHANGED
@@ -2,6 +2,10 @@ import { tool, jsonSchema } from 'ai';
2
2
 
3
3
  // src/index.ts
4
4
  var SPRITES_API_BASE = "https://api.sprites.dev/v1";
5
+ var STREAM_STDIN = 0;
6
+ var STREAM_STDOUT = 1;
7
+ var STREAM_STDERR = 2;
8
+ var STREAM_EXIT = 3;
5
9
  function getSpritesToken() {
6
10
  const token = process.env.SPRITES_TOKEN;
7
11
  if (!token) {
@@ -11,6 +15,84 @@ function getSpritesToken() {
11
15
  }
12
16
  return token;
13
17
  }
18
+ function parseCommand(cmdString) {
19
+ const args = [];
20
+ let current = "";
21
+ let inSingleQuote = false;
22
+ let inDoubleQuote = false;
23
+ let escaped = false;
24
+ for (let i = 0; i < cmdString.length; i++) {
25
+ const char = cmdString[i];
26
+ if (escaped) {
27
+ current += char;
28
+ escaped = false;
29
+ continue;
30
+ }
31
+ if (char === "\\" && !inSingleQuote) {
32
+ escaped = true;
33
+ continue;
34
+ }
35
+ if (char === "'" && !inDoubleQuote) {
36
+ inSingleQuote = !inSingleQuote;
37
+ continue;
38
+ }
39
+ if (char === '"' && !inSingleQuote) {
40
+ inDoubleQuote = !inDoubleQuote;
41
+ continue;
42
+ }
43
+ if (char === " " && !inSingleQuote && !inDoubleQuote) {
44
+ if (current.length > 0) {
45
+ args.push(current);
46
+ current = "";
47
+ }
48
+ continue;
49
+ }
50
+ current += char;
51
+ }
52
+ if (current.length > 0) {
53
+ args.push(current);
54
+ }
55
+ return args;
56
+ }
57
+ function parseBinaryResponse(buffer) {
58
+ let stdout = "";
59
+ let stderr = "";
60
+ let exitCode = 0;
61
+ const decoder = new TextDecoder();
62
+ let i = 0;
63
+ while (i < buffer.length) {
64
+ const streamId = buffer[i];
65
+ i++;
66
+ if (streamId === STREAM_EXIT) {
67
+ if (i < buffer.length) {
68
+ exitCode = buffer[i];
69
+ i++;
70
+ }
71
+ continue;
72
+ }
73
+ let end = i;
74
+ while (end < buffer.length) {
75
+ const nextByte = buffer[end];
76
+ if (nextByte <= STREAM_EXIT) {
77
+ break;
78
+ }
79
+ end++;
80
+ }
81
+ const payload = decoder.decode(buffer.slice(i, end));
82
+ switch (streamId) {
83
+ case STREAM_STDIN:
84
+ break;
85
+ case STREAM_STDOUT:
86
+ stdout += payload;
87
+ break;
88
+ case STREAM_STDERR:
89
+ stderr += payload;
90
+ break;
91
+ }
92
+ i = end;
93
+ }
94
+ return { stdout, stderr, exitCode };
95
+ }
14
96
  var spritesExecTool = tool({
15
97
  description: "Execute a command inside a sprite and return the output. Supports stdin input for interactive commands. Returns exit code, stdout, stderr, and execution duration.",
16
98
  inputSchema: jsonSchema({
@@ -46,22 +128,29 @@ var spritesExecTool = tool({
46
128
  const token = getSpritesToken();
47
129
  const timeout = timeoutMs || 6e4;
48
130
  const startTime = Date.now();
131
+ const cmdParts = parseCommand(cmd);
132
+ if (cmdParts.length === 0) {
133
+ throw new Error("Command cannot be empty");
134
+ }
135
+ const url = new URL(`${SPRITES_API_BASE}/sprites/${encodeURIComponent(name)}/exec`);
136
+ for (const part of cmdParts) {
137
+ url.searchParams.append("cmd", part);
138
+ }
139
+ if (stdin) {
140
+ url.searchParams.set("stdin", "true");
141
+ }
49
142
  let response;
50
143
  try {
51
144
  const controller = new AbortController();
52
145
  const timeoutId = setTimeout(() => controller.abort(), timeout);
53
- const body = { cmd };
54
- if (stdin) {
55
- body.stdin = stdin;
56
- }
57
- response = await fetch(`${SPRITES_API_BASE}/sprites/${encodeURIComponent(name)}/exec`, {
146
+ response = await fetch(url.toString(), {
58
147
  method: "POST",
59
148
  headers: {
60
149
  Authorization: `Bearer ${token}`,
61
- "Content-Type": "application/json",
62
- "User-Agent": "TPMJS/1.0"
150
+ "User-Agent": "TPMJS/1.0",
151
+ ...stdin ? { "Content-Type": "application/octet-stream" } : {}
63
152
  },
64
- body: JSON.stringify(body),
153
+ body: stdin || void 0,
65
154
  signal: controller.signal
66
155
  });
67
156
  clearTimeout(timeoutId);
@@ -87,17 +176,14 @@ var spritesExecTool = tool({
87
176
  `Failed to execute command in sprite "${name}": HTTP ${response.status} - ${errorText}`
88
177
  );
89
178
  }
90
- let data;
91
- try {
92
- data = await response.json();
93
- } catch {
94
- throw new Error("Failed to parse response from Sprites API");
95
- }
179
+ const arrayBuffer = await response.arrayBuffer();
180
+ const buffer = new Uint8Array(arrayBuffer);
181
+ const { stdout, stderr, exitCode } = parseBinaryResponse(buffer);
96
182
  return {
97
- exitCode: data.exitCode ?? data.exit_code ?? 0,
98
- stdout: data.stdout || "",
99
- stderr: data.stderr || "",
100
- duration: data.duration || duration
183
+ exitCode,
184
+ stdout,
185
+ stderr,
186
+ duration
101
187
  };
102
188
  }
103
189
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tpmjs/tools-sprites-exec",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "Execute a command inside a sprite and return the output with exit code",
5
5
  "type": "module",
6
6
  "keywords": [