@geminixiang/mama 0.1.10 → 0.2.0-beta.1

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 (151) hide show
  1. package/README.md +80 -23
  2. package/dist/adapter.d.ts +11 -9
  3. package/dist/adapter.d.ts.map +1 -1
  4. package/dist/adapter.js.map +1 -1
  5. package/dist/adapters/discord/bot.d.ts +2 -2
  6. package/dist/adapters/discord/bot.d.ts.map +1 -1
  7. package/dist/adapters/discord/bot.js +33 -21
  8. package/dist/adapters/discord/bot.js.map +1 -1
  9. package/dist/adapters/discord/context.d.ts.map +1 -1
  10. package/dist/adapters/discord/context.js +20 -13
  11. package/dist/adapters/discord/context.js.map +1 -1
  12. package/dist/adapters/slack/bot.d.ts +13 -4
  13. package/dist/adapters/slack/bot.d.ts.map +1 -1
  14. package/dist/adapters/slack/bot.js +98 -43
  15. package/dist/adapters/slack/bot.js.map +1 -1
  16. package/dist/adapters/slack/context.d.ts.map +1 -1
  17. package/dist/adapters/slack/context.js +25 -20
  18. package/dist/adapters/slack/context.js.map +1 -1
  19. package/dist/adapters/telegram/bot.d.ts +4 -2
  20. package/dist/adapters/telegram/bot.d.ts.map +1 -1
  21. package/dist/adapters/telegram/bot.js +143 -58
  22. package/dist/adapters/telegram/bot.js.map +1 -1
  23. package/dist/adapters/telegram/context.d.ts +1 -1
  24. package/dist/adapters/telegram/context.d.ts.map +1 -1
  25. package/dist/adapters/telegram/context.js +124 -29
  26. package/dist/adapters/telegram/context.js.map +1 -1
  27. package/dist/agent.d.ts +7 -4
  28. package/dist/agent.d.ts.map +1 -1
  29. package/dist/agent.js +303 -89
  30. package/dist/agent.js.map +1 -1
  31. package/dist/bindings.d.ts +63 -0
  32. package/dist/bindings.d.ts.map +1 -0
  33. package/dist/bindings.js +94 -0
  34. package/dist/bindings.js.map +1 -0
  35. package/dist/config.d.ts +34 -4
  36. package/dist/config.d.ts.map +1 -1
  37. package/dist/config.js +98 -38
  38. package/dist/config.js.map +1 -1
  39. package/dist/context.d.ts +8 -6
  40. package/dist/context.d.ts.map +1 -1
  41. package/dist/context.js +23 -14
  42. package/dist/context.js.map +1 -1
  43. package/dist/events.d.ts +4 -0
  44. package/dist/events.d.ts.map +1 -1
  45. package/dist/events.js +20 -5
  46. package/dist/events.js.map +1 -1
  47. package/dist/execution-resolver.d.ts +20 -0
  48. package/dist/execution-resolver.d.ts.map +1 -0
  49. package/dist/execution-resolver.js +51 -0
  50. package/dist/execution-resolver.js.map +1 -0
  51. package/dist/instrument.d.ts +2 -0
  52. package/dist/instrument.d.ts.map +1 -0
  53. package/dist/instrument.js +14 -0
  54. package/dist/instrument.js.map +1 -0
  55. package/dist/link-server.d.ts +16 -0
  56. package/dist/link-server.d.ts.map +1 -0
  57. package/dist/link-server.js +839 -0
  58. package/dist/link-server.js.map +1 -0
  59. package/dist/link-token.d.ts +32 -0
  60. package/dist/link-token.d.ts.map +1 -0
  61. package/dist/link-token.js +68 -0
  62. package/dist/link-token.js.map +1 -0
  63. package/dist/log.d.ts +3 -2
  64. package/dist/log.d.ts.map +1 -1
  65. package/dist/log.js +10 -9
  66. package/dist/log.js.map +1 -1
  67. package/dist/login.d.ts +29 -0
  68. package/dist/login.d.ts.map +1 -0
  69. package/dist/login.js +164 -0
  70. package/dist/login.js.map +1 -0
  71. package/dist/main.d.ts +1 -1
  72. package/dist/main.d.ts.map +1 -1
  73. package/dist/main.js +322 -82
  74. package/dist/main.js.map +1 -1
  75. package/dist/provisioner.d.ts +93 -0
  76. package/dist/provisioner.d.ts.map +1 -0
  77. package/dist/provisioner.js +336 -0
  78. package/dist/provisioner.js.map +1 -0
  79. package/dist/sandbox/container.d.ts +15 -0
  80. package/dist/sandbox/container.d.ts.map +1 -0
  81. package/dist/sandbox/container.js +122 -0
  82. package/dist/sandbox/container.js.map +1 -0
  83. package/dist/sandbox/errors.d.ts +6 -0
  84. package/dist/sandbox/errors.d.ts.map +1 -0
  85. package/dist/sandbox/errors.js +11 -0
  86. package/dist/sandbox/errors.js.map +1 -0
  87. package/dist/sandbox/firecracker.d.ts +16 -0
  88. package/dist/sandbox/firecracker.d.ts.map +1 -0
  89. package/dist/sandbox/firecracker.js +206 -0
  90. package/dist/sandbox/firecracker.js.map +1 -0
  91. package/dist/sandbox/host.d.ts +12 -0
  92. package/dist/sandbox/host.d.ts.map +1 -0
  93. package/dist/sandbox/host.js +89 -0
  94. package/dist/sandbox/host.js.map +1 -0
  95. package/dist/sandbox/image.d.ts +5 -0
  96. package/dist/sandbox/image.d.ts.map +1 -0
  97. package/dist/sandbox/image.js +30 -0
  98. package/dist/sandbox/image.js.map +1 -0
  99. package/dist/sandbox/index.d.ts +20 -0
  100. package/dist/sandbox/index.d.ts.map +1 -0
  101. package/dist/sandbox/index.js +51 -0
  102. package/dist/sandbox/index.js.map +1 -0
  103. package/dist/sandbox/types.d.ts +51 -0
  104. package/dist/sandbox/types.d.ts.map +1 -0
  105. package/dist/sandbox/types.js +2 -0
  106. package/dist/sandbox/types.js.map +1 -0
  107. package/dist/sandbox/utils.d.ts +4 -0
  108. package/dist/sandbox/utils.d.ts.map +1 -0
  109. package/dist/sandbox/utils.js +51 -0
  110. package/dist/sandbox/utils.js.map +1 -0
  111. package/dist/sandbox.d.ts +1 -39
  112. package/dist/sandbox.d.ts.map +1 -1
  113. package/dist/sandbox.js +1 -286
  114. package/dist/sandbox.js.map +1 -1
  115. package/dist/sentry.d.ts +31 -0
  116. package/dist/sentry.d.ts.map +1 -0
  117. package/dist/sentry.js +205 -0
  118. package/dist/sentry.js.map +1 -0
  119. package/dist/session-store.d.ts +72 -0
  120. package/dist/session-store.d.ts.map +1 -0
  121. package/dist/session-store.js +186 -0
  122. package/dist/session-store.js.map +1 -0
  123. package/dist/store.d.ts +1 -1
  124. package/dist/store.d.ts.map +1 -1
  125. package/dist/store.js +8 -8
  126. package/dist/store.js.map +1 -1
  127. package/dist/tools/event.d.ts +21 -0
  128. package/dist/tools/event.d.ts.map +1 -0
  129. package/dist/tools/event.js +103 -0
  130. package/dist/tools/event.js.map +1 -0
  131. package/dist/tools/index.d.ts +6 -1
  132. package/dist/tools/index.d.ts.map +1 -1
  133. package/dist/tools/index.js +5 -1
  134. package/dist/tools/index.js.map +1 -1
  135. package/dist/ui-copy.d.ts +11 -0
  136. package/dist/ui-copy.d.ts.map +1 -0
  137. package/dist/ui-copy.js +33 -0
  138. package/dist/ui-copy.js.map +1 -0
  139. package/dist/vault-routing.d.ts +10 -0
  140. package/dist/vault-routing.d.ts.map +1 -0
  141. package/dist/vault-routing.js +58 -0
  142. package/dist/vault-routing.js.map +1 -0
  143. package/dist/vault.d.ts +106 -0
  144. package/dist/vault.d.ts.map +1 -0
  145. package/dist/vault.js +389 -0
  146. package/dist/vault.js.map +1 -0
  147. package/dist/vault.test.d.ts +2 -0
  148. package/dist/vault.test.d.ts.map +1 -0
  149. package/dist/vault.test.js +67 -0
  150. package/dist/vault.test.js.map +1 -0
  151. package/package.json +13 -11
package/dist/sandbox.js CHANGED
@@ -1,287 +1,2 @@
1
- import { spawn } from "child_process";
2
- export function parseSandboxArg(value) {
3
- if (value === "host") {
4
- return { type: "host" };
5
- }
6
- if (value.startsWith("docker:")) {
7
- const container = value.slice("docker:".length);
8
- if (!container) {
9
- console.error("Error: docker sandbox requires container name (e.g., docker:mama-sandbox)");
10
- process.exit(1);
11
- }
12
- return { type: "docker", container };
13
- }
14
- if (value.startsWith("firecracker:")) {
15
- const arg = value.slice("firecracker:".length);
16
- // Format: firecracker:<vm-id>:<host-path>[:<ssh-user>[:<ssh-port>]]
17
- // Example: firecracker:vm1:/home/user/workspace
18
- // firecracker:vm1:/home/user/workspace:root
19
- // firecracker:vm1:/home/user/workspace:root:22
20
- const parts = arg.split(":");
21
- if (parts.length < 2) {
22
- console.error("Error: firecracker sandbox requires vm-id and host-path\n" +
23
- "Usage: firecracker:<vm-id>:<host-path>[:<ssh-user>[:<ssh-port>]]\n" +
24
- "Example: firecracker:vm1:/home/user/workspace");
25
- process.exit(1);
26
- }
27
- const vmId = parts[0];
28
- const hostPath = parts[1];
29
- const sshUser = parts[2] || "root";
30
- const sshPort = parts[3] ? parseInt(parts[3], 10) : 22;
31
- if (!vmId || !hostPath) {
32
- console.error("Error: firecracker sandbox requires vm-id and host-path");
33
- process.exit(1);
34
- }
35
- if (isNaN(sshPort) || sshPort <= 0 || sshPort > 65535) {
36
- console.error("Error: invalid SSH port");
37
- process.exit(1);
38
- }
39
- return { type: "firecracker", vmId, hostPath, sshUser, sshPort };
40
- }
41
- console.error(`Error: Invalid sandbox type '${value}'. Use 'host', 'docker:<container-name>', or 'firecracker:<vm-id>:<host-path>'`);
42
- process.exit(1);
43
- }
44
- export async function validateSandbox(config) {
45
- if (config.type === "host") {
46
- return;
47
- }
48
- if (config.type === "docker") {
49
- // Check if Docker is available
50
- try {
51
- await execSimple("docker", ["--version"]);
52
- }
53
- catch {
54
- console.error("Error: Docker is not installed or not in PATH");
55
- process.exit(1);
56
- }
57
- // Check if container exists and is running
58
- try {
59
- const result = await execSimple("docker", [
60
- "inspect",
61
- "-f",
62
- "{{.State.Running}}",
63
- config.container,
64
- ]);
65
- if (result.trim() !== "true") {
66
- console.error(`Error: Container '${config.container}' is not running.`);
67
- console.error(`Start it with: docker start ${config.container}`);
68
- process.exit(1);
69
- }
70
- }
71
- catch {
72
- console.error(`Error: Container '${config.container}' does not exist.`);
73
- console.error("Create it with: ./docker.sh create <data-dir>");
74
- process.exit(1);
75
- }
76
- console.log(` Docker container '${config.container}' is running.`);
77
- return;
78
- }
79
- if (config.type === "firecracker") {
80
- // Check if fc-agent or firecracker CLI is available
81
- try {
82
- await execSimple("fc-agent", ["--version"]);
83
- }
84
- catch {
85
- // Try alternative: firecracker
86
- try {
87
- await execSimple("firecracker", ["--version"]);
88
- }
89
- catch {
90
- console.error("Error: Firecracker tools (fc-agent or firecracker) not found in PATH");
91
- console.error("Install firecracker: https://github.com/firecracker-microvm/firecracker");
92
- process.exit(1);
93
- }
94
- }
95
- // Check if VM is running using fc-agent
96
- try {
97
- const result = await execSimple("fc-agent", ["status", config.vmId]);
98
- if (!result.includes("running") && !result.includes("Running")) {
99
- console.error(`Error: Firecracker VM '${config.vmId}' is not running.`);
100
- console.error(`Start it with: fc-agent start ${config.vmId}`);
101
- process.exit(1);
102
- }
103
- }
104
- catch {
105
- // Try alternative: firecracker-ctl or direct check
106
- try {
107
- await execSimple("firecracker-ctl", ["status", config.vmId]);
108
- }
109
- catch {
110
- console.error(`Warning: Could not verify if VM '${config.vmId}' is running.`);
111
- console.error("Make sure the VM is started before running mama.");
112
- }
113
- }
114
- // Verify host path exists
115
- try {
116
- await execSimple("ls", ["-d", config.hostPath]);
117
- }
118
- catch {
119
- console.error(`Error: Host path '${config.hostPath}' does not exist.`);
120
- process.exit(1);
121
- }
122
- console.log(` Firecracker VM '${config.vmId}' configured with workspace '${config.hostPath}'.`);
123
- return;
124
- }
125
- }
126
- function execSimple(cmd, args) {
127
- return new Promise((resolve, reject) => {
128
- const child = spawn(cmd, args, { stdio: ["ignore", "pipe", "pipe"] });
129
- let stdout = "";
130
- let stderr = "";
131
- child.stdout?.on("data", (d) => {
132
- stdout += d;
133
- });
134
- child.stderr?.on("data", (d) => {
135
- stderr += d;
136
- });
137
- child.on("close", (code) => {
138
- if (code === 0)
139
- resolve(stdout);
140
- else
141
- reject(new Error(stderr || `Exit code ${code}`));
142
- });
143
- });
144
- }
145
- /**
146
- * Create an executor that runs commands either on host, in Docker container, or in Firecracker VM
147
- */
148
- export function createExecutor(config) {
149
- if (config.type === "host") {
150
- return new HostExecutor();
151
- }
152
- if (config.type === "docker") {
153
- return new DockerExecutor(config.container);
154
- }
155
- return new FirecrackerExecutor(config.vmId, config.hostPath, config.sshUser, config.sshPort);
156
- }
157
- class HostExecutor {
158
- async exec(command, options) {
159
- return new Promise((resolve, reject) => {
160
- const shell = process.platform === "win32" ? "cmd" : "sh";
161
- const shellArgs = process.platform === "win32" ? ["/c"] : ["-c"];
162
- const child = spawn(shell, [...shellArgs, command], {
163
- detached: true,
164
- stdio: ["ignore", "pipe", "pipe"],
165
- });
166
- let stdout = "";
167
- let stderr = "";
168
- let timedOut = false;
169
- const timeoutHandle = options?.timeout && options.timeout > 0
170
- ? setTimeout(() => {
171
- timedOut = true;
172
- killProcessTree(child.pid);
173
- }, options.timeout * 1000)
174
- : undefined;
175
- const onAbort = () => {
176
- if (child.pid)
177
- killProcessTree(child.pid);
178
- };
179
- if (options?.signal) {
180
- if (options.signal.aborted) {
181
- onAbort();
182
- }
183
- else {
184
- options.signal.addEventListener("abort", onAbort, { once: true });
185
- }
186
- }
187
- child.stdout?.on("data", (data) => {
188
- stdout += data.toString();
189
- if (stdout.length > 10 * 1024 * 1024) {
190
- stdout = stdout.slice(0, 10 * 1024 * 1024);
191
- }
192
- });
193
- child.stderr?.on("data", (data) => {
194
- stderr += data.toString();
195
- if (stderr.length > 10 * 1024 * 1024) {
196
- stderr = stderr.slice(0, 10 * 1024 * 1024);
197
- }
198
- });
199
- child.on("close", (code) => {
200
- if (timeoutHandle)
201
- clearTimeout(timeoutHandle);
202
- if (options?.signal) {
203
- options.signal.removeEventListener("abort", onAbort);
204
- }
205
- if (options?.signal?.aborted) {
206
- reject(new Error(`${stdout}\n${stderr}\nCommand aborted`.trim()));
207
- return;
208
- }
209
- if (timedOut) {
210
- reject(new Error(`${stdout}\n${stderr}\nCommand timed out after ${options?.timeout} seconds`.trim()));
211
- return;
212
- }
213
- resolve({ stdout, stderr, code: code ?? 0 });
214
- });
215
- });
216
- }
217
- getWorkspacePath(hostPath) {
218
- return hostPath;
219
- }
220
- }
221
- class DockerExecutor {
222
- constructor(container) {
223
- this.container = container;
224
- }
225
- async exec(command, options) {
226
- // Wrap command for docker exec
227
- const dockerCmd = `docker exec ${this.container} sh -c ${shellEscape(command)}`;
228
- const hostExecutor = new HostExecutor();
229
- return hostExecutor.exec(dockerCmd, options);
230
- }
231
- getWorkspacePath(_hostPath) {
232
- // Docker container sees /workspace
233
- return "/workspace";
234
- }
235
- }
236
- class FirecrackerExecutor {
237
- constructor(vmId, hostPath, sshUser = "root", sshPort = 22) {
238
- this.vmId = vmId;
239
- this.hostPath = hostPath;
240
- this.sshUser = sshUser;
241
- this.sshPort = sshPort;
242
- }
243
- async exec(command, options) {
244
- // Use direct SSH to execute command in the Firecracker VM
245
- // The workspace inside the VM is expected to be mounted at /workspace
246
- const sshCmd = this.sshPort === 22
247
- ? `ssh -o StrictHostKeyChecking=no -o ConnectTimeout=10 ${this.sshUser}@${this.vmId} sh -c ${shellEscape(command)}`
248
- : `ssh -o StrictHostKeyChecking=no -o ConnectTimeout=10 -p ${this.sshPort} ${this.sshUser}@${this.vmId} sh -c ${shellEscape(command)}`;
249
- const hostExecutor = new HostExecutor();
250
- return hostExecutor.exec(sshCmd, options);
251
- }
252
- getWorkspacePath(_hostPath) {
253
- // Firecracker VM sees /workspace (assumes hostPath is mounted there)
254
- return "/workspace";
255
- }
256
- }
257
- function killProcessTree(pid) {
258
- if (process.platform === "win32") {
259
- try {
260
- spawn("taskkill", ["/F", "/T", "/PID", String(pid)], {
261
- stdio: "ignore",
262
- detached: true,
263
- });
264
- }
265
- catch {
266
- // Ignore errors
267
- }
268
- }
269
- else {
270
- try {
271
- process.kill(-pid, "SIGKILL");
272
- }
273
- catch {
274
- try {
275
- process.kill(pid, "SIGKILL");
276
- }
277
- catch {
278
- // Process already dead
279
- }
280
- }
281
- }
282
- }
283
- function shellEscape(s) {
284
- // Escape for passing to sh -c
285
- return `'${s.replace(/'/g, "'\\''")}'`;
286
- }
1
+ export * from "./sandbox/index.js";
287
2
  //# sourceMappingURL=sandbox.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"sandbox.js","sourceRoot":"","sources":["../src/sandbox.ts"],"names":[],"mappings":"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","sourcesContent":["import { spawn } from \"child_process\";\n\nexport type SandboxConfig =\n | { type: \"host\" }\n | { type: \"docker\"; container: string }\n | { type: \"firecracker\"; vmId: string; hostPath: string; sshUser?: string; sshPort?: number };\n\nexport function parseSandboxArg(value: string): SandboxConfig {\n if (value === \"host\") {\n return { type: \"host\" };\n }\n if (value.startsWith(\"docker:\")) {\n const container = value.slice(\"docker:\".length);\n if (!container) {\n console.error(\"Error: docker sandbox requires container name (e.g., docker:mama-sandbox)\");\n process.exit(1);\n }\n return { type: \"docker\", container };\n }\n if (value.startsWith(\"firecracker:\")) {\n const arg = value.slice(\"firecracker:\".length);\n // Format: firecracker:<vm-id>:<host-path>[:<ssh-user>[:<ssh-port>]]\n // Example: firecracker:vm1:/home/user/workspace\n // firecracker:vm1:/home/user/workspace:root\n // firecracker:vm1:/home/user/workspace:root:22\n const parts = arg.split(\":\");\n if (parts.length < 2) {\n console.error(\n \"Error: firecracker sandbox requires vm-id and host-path\\n\" +\n \"Usage: firecracker:<vm-id>:<host-path>[:<ssh-user>[:<ssh-port>]]\\n\" +\n \"Example: firecracker:vm1:/home/user/workspace\",\n );\n process.exit(1);\n }\n const vmId = parts[0];\n const hostPath = parts[1];\n const sshUser = parts[2] || \"root\";\n const sshPort = parts[3] ? parseInt(parts[3], 10) : 22;\n\n if (!vmId || !hostPath) {\n console.error(\"Error: firecracker sandbox requires vm-id and host-path\");\n process.exit(1);\n }\n if (isNaN(sshPort) || sshPort <= 0 || sshPort > 65535) {\n console.error(\"Error: invalid SSH port\");\n process.exit(1);\n }\n return { type: \"firecracker\", vmId, hostPath, sshUser, sshPort };\n }\n console.error(\n `Error: Invalid sandbox type '${value}'. Use 'host', 'docker:<container-name>', or 'firecracker:<vm-id>:<host-path>'`,\n );\n process.exit(1);\n}\n\nexport async function validateSandbox(config: SandboxConfig): Promise<void> {\n if (config.type === \"host\") {\n return;\n }\n\n if (config.type === \"docker\") {\n // Check if Docker is available\n try {\n await execSimple(\"docker\", [\"--version\"]);\n } catch {\n console.error(\"Error: Docker is not installed or not in PATH\");\n process.exit(1);\n }\n\n // Check if container exists and is running\n try {\n const result = await execSimple(\"docker\", [\n \"inspect\",\n \"-f\",\n \"{{.State.Running}}\",\n config.container,\n ]);\n if (result.trim() !== \"true\") {\n console.error(`Error: Container '${config.container}' is not running.`);\n console.error(`Start it with: docker start ${config.container}`);\n process.exit(1);\n }\n } catch {\n console.error(`Error: Container '${config.container}' does not exist.`);\n console.error(\"Create it with: ./docker.sh create <data-dir>\");\n process.exit(1);\n }\n\n console.log(` Docker container '${config.container}' is running.`);\n return;\n }\n\n if (config.type === \"firecracker\") {\n // Check if fc-agent or firecracker CLI is available\n try {\n await execSimple(\"fc-agent\", [\"--version\"]);\n } catch {\n // Try alternative: firecracker\n try {\n await execSimple(\"firecracker\", [\"--version\"]);\n } catch {\n console.error(\"Error: Firecracker tools (fc-agent or firecracker) not found in PATH\");\n console.error(\"Install firecracker: https://github.com/firecracker-microvm/firecracker\");\n process.exit(1);\n }\n }\n\n // Check if VM is running using fc-agent\n try {\n const result = await execSimple(\"fc-agent\", [\"status\", config.vmId]);\n if (!result.includes(\"running\") && !result.includes(\"Running\")) {\n console.error(`Error: Firecracker VM '${config.vmId}' is not running.`);\n console.error(`Start it with: fc-agent start ${config.vmId}`);\n process.exit(1);\n }\n } catch {\n // Try alternative: firecracker-ctl or direct check\n try {\n await execSimple(\"firecracker-ctl\", [\"status\", config.vmId]);\n } catch {\n console.error(`Warning: Could not verify if VM '${config.vmId}' is running.`);\n console.error(\"Make sure the VM is started before running mama.\");\n }\n }\n\n // Verify host path exists\n try {\n await execSimple(\"ls\", [\"-d\", config.hostPath]);\n } catch {\n console.error(`Error: Host path '${config.hostPath}' does not exist.`);\n process.exit(1);\n }\n\n console.log(\n ` Firecracker VM '${config.vmId}' configured with workspace '${config.hostPath}'.`,\n );\n return;\n }\n}\n\nfunction execSimple(cmd: string, args: string[]): Promise<string> {\n return new Promise((resolve, reject) => {\n const child = spawn(cmd, args, { stdio: [\"ignore\", \"pipe\", \"pipe\"] });\n let stdout = \"\";\n let stderr = \"\";\n child.stdout?.on(\"data\", (d) => {\n stdout += d;\n });\n child.stderr?.on(\"data\", (d) => {\n stderr += d;\n });\n child.on(\"close\", (code) => {\n if (code === 0) resolve(stdout);\n else reject(new Error(stderr || `Exit code ${code}`));\n });\n });\n}\n\n/**\n * Create an executor that runs commands either on host, in Docker container, or in Firecracker VM\n */\nexport function createExecutor(config: SandboxConfig): Executor {\n if (config.type === \"host\") {\n return new HostExecutor();\n }\n if (config.type === \"docker\") {\n return new DockerExecutor(config.container);\n }\n return new FirecrackerExecutor(config.vmId, config.hostPath, config.sshUser, config.sshPort);\n}\n\nexport interface Executor {\n /**\n * Execute a bash command\n */\n exec(command: string, options?: ExecOptions): Promise<ExecResult>;\n\n /**\n * Get the workspace path prefix for this executor\n * Host: returns the actual path\n * Docker: returns /workspace\n */\n getWorkspacePath(hostPath: string): string;\n}\n\nexport interface ExecOptions {\n timeout?: number;\n signal?: AbortSignal;\n}\n\nexport interface ExecResult {\n stdout: string;\n stderr: string;\n code: number;\n}\n\nclass HostExecutor implements Executor {\n async exec(command: string, options?: ExecOptions): Promise<ExecResult> {\n return new Promise((resolve, reject) => {\n const shell = process.platform === \"win32\" ? \"cmd\" : \"sh\";\n const shellArgs = process.platform === \"win32\" ? [\"/c\"] : [\"-c\"];\n\n const child = spawn(shell, [...shellArgs, command], {\n detached: true,\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n });\n\n let stdout = \"\";\n let stderr = \"\";\n let timedOut = false;\n\n const timeoutHandle =\n options?.timeout && options.timeout > 0\n ? setTimeout(() => {\n timedOut = true;\n killProcessTree(child.pid!);\n }, options.timeout * 1000)\n : undefined;\n\n const onAbort = () => {\n if (child.pid) killProcessTree(child.pid);\n };\n\n if (options?.signal) {\n if (options.signal.aborted) {\n onAbort();\n } else {\n options.signal.addEventListener(\"abort\", onAbort, { once: true });\n }\n }\n\n child.stdout?.on(\"data\", (data) => {\n stdout += data.toString();\n if (stdout.length > 10 * 1024 * 1024) {\n stdout = stdout.slice(0, 10 * 1024 * 1024);\n }\n });\n\n child.stderr?.on(\"data\", (data) => {\n stderr += data.toString();\n if (stderr.length > 10 * 1024 * 1024) {\n stderr = stderr.slice(0, 10 * 1024 * 1024);\n }\n });\n\n child.on(\"close\", (code) => {\n if (timeoutHandle) clearTimeout(timeoutHandle);\n if (options?.signal) {\n options.signal.removeEventListener(\"abort\", onAbort);\n }\n\n if (options?.signal?.aborted) {\n reject(new Error(`${stdout}\\n${stderr}\\nCommand aborted`.trim()));\n return;\n }\n\n if (timedOut) {\n reject(\n new Error(\n `${stdout}\\n${stderr}\\nCommand timed out after ${options?.timeout} seconds`.trim(),\n ),\n );\n return;\n }\n\n resolve({ stdout, stderr, code: code ?? 0 });\n });\n });\n }\n\n getWorkspacePath(hostPath: string): string {\n return hostPath;\n }\n}\n\nclass DockerExecutor implements Executor {\n constructor(private container: string) {}\n\n async exec(command: string, options?: ExecOptions): Promise<ExecResult> {\n // Wrap command for docker exec\n const dockerCmd = `docker exec ${this.container} sh -c ${shellEscape(command)}`;\n const hostExecutor = new HostExecutor();\n return hostExecutor.exec(dockerCmd, options);\n }\n\n getWorkspacePath(_hostPath: string): string {\n // Docker container sees /workspace\n return \"/workspace\";\n }\n}\n\nclass FirecrackerExecutor implements Executor {\n constructor(\n private vmId: string,\n private hostPath: string,\n private sshUser: string = \"root\",\n private sshPort: number = 22,\n ) {}\n\n async exec(command: string, options?: ExecOptions): Promise<ExecResult> {\n // Use direct SSH to execute command in the Firecracker VM\n // The workspace inside the VM is expected to be mounted at /workspace\n const sshCmd =\n this.sshPort === 22\n ? `ssh -o StrictHostKeyChecking=no -o ConnectTimeout=10 ${this.sshUser}@${this.vmId} sh -c ${shellEscape(command)}`\n : `ssh -o StrictHostKeyChecking=no -o ConnectTimeout=10 -p ${this.sshPort} ${this.sshUser}@${this.vmId} sh -c ${shellEscape(command)}`;\n const hostExecutor = new HostExecutor();\n return hostExecutor.exec(sshCmd, options);\n }\n\n getWorkspacePath(_hostPath: string): string {\n // Firecracker VM sees /workspace (assumes hostPath is mounted there)\n return \"/workspace\";\n }\n}\n\nfunction killProcessTree(pid: number): void {\n if (process.platform === \"win32\") {\n try {\n spawn(\"taskkill\", [\"/F\", \"/T\", \"/PID\", String(pid)], {\n stdio: \"ignore\",\n detached: true,\n });\n } catch {\n // Ignore errors\n }\n } else {\n try {\n process.kill(-pid, \"SIGKILL\");\n } catch {\n try {\n process.kill(pid, \"SIGKILL\");\n } catch {\n // Process already dead\n }\n }\n }\n}\n\nfunction shellEscape(s: string): string {\n // Escape for passing to sh -c\n return `'${s.replace(/'/g, \"'\\\\''\")}'`;\n}\n"]}
1
+ {"version":3,"file":"sandbox.js","sourceRoot":"","sources":["../src/sandbox.ts"],"names":[],"mappings":"AAAA,cAAc,oBAAoB,CAAC","sourcesContent":["export * from \"./sandbox/index.js\";\n"]}
@@ -0,0 +1,31 @@
1
+ import type { Breadcrumb, ErrorEvent, Event, EventHint, Scope } from "@sentry/node";
2
+ export interface SentryRunScopeContext {
3
+ conversationId: string;
4
+ sessionKey: string;
5
+ messageId: string;
6
+ platform: string;
7
+ userId: string;
8
+ userName?: string;
9
+ threadTs?: string;
10
+ provider?: string;
11
+ model?: string;
12
+ isEvent?: boolean;
13
+ }
14
+ export declare function createSentryInitOptions(dsn?: string): {
15
+ dsn: string | undefined;
16
+ environment: string;
17
+ enabled: boolean;
18
+ sendDefaultPii: boolean;
19
+ tracesSampleRate: number;
20
+ includeLocalVariables: boolean;
21
+ enableLogs: boolean;
22
+ beforeSend(event: ErrorEvent, hint: EventHint): ErrorEvent | null;
23
+ beforeBreadcrumb(breadcrumb: Breadcrumb): Breadcrumb | null;
24
+ };
25
+ export declare function applyRunScope(scope: Scope, context: SentryRunScopeContext): void;
26
+ export declare function metricAttributes(attributes: Record<string, string | number | boolean | undefined>): Record<string, string | number | boolean>;
27
+ export declare function addLifecycleBreadcrumb(message: string, data?: Record<string, string | number | boolean | undefined>): void;
28
+ export declare function sanitizeEvent<T extends Event>(event: T, _hint?: EventHint): T | null;
29
+ export declare function sanitizeBreadcrumb(breadcrumb: Breadcrumb): Breadcrumb | null;
30
+ export declare function sanitizeValue(value: unknown, key?: string, depth?: number): unknown;
31
+ //# sourceMappingURL=sentry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sentry.d.ts","sourceRoot":"","sources":["../src/sentry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AA6CpF,MAAM,WAAW,qBAAqB;IACpC,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,wBAAgB,uBAAuB,CAAC,GAAG,CAAC,EAAE,MAAM;;;;;;;;sBAS9B,UAAU,QAAQ,SAAS,GAAG,UAAU,GAAG,IAAI;iCAGpC,UAAU,GAAG,UAAU,GAAG,IAAI;EAI9D;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,qBAAqB,GAAG,IAAI,CAuBhF;AAED,wBAAgB,gBAAgB,CAC9B,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,SAAS,CAAC,GAChE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,CAO3C;AAED,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,MAAM,EACf,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,SAAS,CAAC,GAC3D,IAAI,CAON;AAED,wBAAgB,aAAa,CAAC,CAAC,SAAS,KAAK,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,CAAC,EAAE,SAAS,GAAG,CAAC,GAAG,IAAI,CA2CpF;AAED,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,UAAU,GAAG,UAAU,GAAG,IAAI,CAU5E;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,KAAK,SAAI,GAAG,OAAO,CAwB9E","sourcesContent":["import type { Breadcrumb, ErrorEvent, Event, EventHint, Scope } from \"@sentry/node\";\nimport * as Sentry from \"@sentry/node\";\n\nconst REDACTED = \"[REDACTED]\";\nconst REDACTED_PATH = \"[REDACTED_PATH]\";\nconst MAX_STRING_LENGTH = 256;\nconst MAX_DEPTH = 4;\n\nconst SENSITIVE_KEYS = new Set([\n \"args\",\n \"attachment\",\n \"attachments\",\n \"authorization\",\n \"body\",\n \"content\",\n \"contents\",\n \"cookie\",\n \"cookies\",\n \"filePath\",\n \"headers\",\n \"image\",\n \"imageAttachments\",\n \"images\",\n \"localPath\",\n \"messages\",\n \"newUserMessage\",\n \"path\",\n \"paths\",\n \"prompt\",\n \"response\",\n \"result\",\n \"systemPrompt\",\n \"text\",\n \"thinking\",\n]);\n\nconst ABSOLUTE_PATH_PATTERN =\n /(?:\\/Users\\/[^\\s\"'`]+|\\/workspace\\/[^\\s\"'`]+|\\/tmp\\/[^\\s\"'`]+|\\/var\\/folders\\/[^\\s\"'`]+|[A-Za-z]:\\\\[^\\s\"'`]+)/;\nconst TOKEN_PATTERNS = [\n /\\bsk-[A-Za-z0-9_-]{12,}\\b/,\n /\\bxox[a-z]-[A-Za-z0-9-]{10,}\\b/,\n /\\bAIza[0-9A-Za-z_-]{20,}\\b/,\n /\\bgh[pousr]_[A-Za-z0-9]{20,}\\b/,\n];\n\nexport interface SentryRunScopeContext {\n conversationId: string;\n sessionKey: string;\n messageId: string;\n platform: string;\n userId: string;\n userName?: string;\n threadTs?: string;\n provider?: string;\n model?: string;\n isEvent?: boolean;\n}\n\nexport function createSentryInitOptions(dsn?: string) {\n return {\n dsn,\n environment: process.env.SENTRY_ENVIRONMENT ?? \"production\",\n enabled: Boolean(dsn) && process.env.SENTRY_ENABLED !== \"false\",\n sendDefaultPii: false,\n tracesSampleRate: process.env.NODE_ENV === \"development\" ? 1.0 : 1.0,\n includeLocalVariables: false,\n enableLogs: true,\n beforeSend(event: ErrorEvent, hint: EventHint): ErrorEvent | null {\n return sanitizeEvent(event, hint);\n },\n beforeBreadcrumb(breadcrumb: Breadcrumb): Breadcrumb | null {\n return sanitizeBreadcrumb(breadcrumb);\n },\n };\n}\n\nexport function applyRunScope(scope: Scope, context: SentryRunScopeContext): void {\n scope.setTag(\"channel_id\", context.conversationId);\n scope.setTag(\"session_key\", context.sessionKey);\n scope.setTag(\"platform\", context.platform);\n scope.setTag(\"is_event\", String(Boolean(context.isEvent)));\n if (context.threadTs) scope.setTag(\"thread_ts\", context.threadTs);\n if (context.provider) scope.setTag(\"provider\", context.provider);\n if (context.model) scope.setTag(\"model\", context.model);\n\n scope.setUser({\n id: context.userId,\n username: context.userName,\n });\n scope.setContext(\"agent_run\", {\n conversationId: context.conversationId,\n sessionKey: context.sessionKey,\n messageId: context.messageId,\n threadTs: context.threadTs,\n platform: context.platform,\n provider: context.provider,\n model: context.model,\n isEvent: Boolean(context.isEvent),\n });\n}\n\nexport function metricAttributes(\n attributes: Record<string, string | number | boolean | undefined>,\n): Record<string, string | number | boolean> {\n return Object.fromEntries(\n Object.entries(attributes).filter((entry): entry is [string, string | number | boolean] => {\n const [, value] = entry;\n return value !== undefined;\n }),\n );\n}\n\nexport function addLifecycleBreadcrumb(\n message: string,\n data?: Record<string, string | number | boolean | undefined>,\n): void {\n Sentry.addBreadcrumb({\n category: \"agent.lifecycle\",\n message,\n level: \"info\",\n data: data ? metricAttributes(data) : undefined,\n });\n}\n\nexport function sanitizeEvent<T extends Event>(event: T, _hint?: EventHint): T | null {\n const sanitized: T = {\n ...event,\n breadcrumbs: event.breadcrumbs\n ?.map((breadcrumb) => sanitizeBreadcrumb(breadcrumb))\n .filter((breadcrumb): breadcrumb is Breadcrumb => breadcrumb !== null),\n extra: sanitizeValue(event.extra) as T[\"extra\"],\n contexts: sanitizeValue(event.contexts) as T[\"contexts\"],\n request: sanitizeRequest(event.request),\n user: undefined,\n server_name: undefined,\n };\n\n if (sanitized.message) {\n sanitized.message = sanitizeString(sanitized.message);\n }\n\n if (sanitized.logentry) {\n sanitized.logentry = {\n ...sanitized.logentry,\n message: sanitized.logentry.message ? sanitizeString(sanitized.logentry.message) : undefined,\n };\n }\n\n if (sanitized.exception?.values) {\n sanitized.exception.values = sanitized.exception.values.map((value) => ({\n ...value,\n value: value.value ? sanitizeString(value.value) : value.value,\n stacktrace: value.stacktrace\n ? {\n ...value.stacktrace,\n frames: value.stacktrace.frames?.map((frame) => ({\n ...frame,\n filename: frame.filename ? sanitizeString(frame.filename) : frame.filename,\n abs_path: frame.abs_path ? sanitizeString(frame.abs_path) : frame.abs_path,\n vars: undefined,\n })),\n }\n : value.stacktrace,\n }));\n }\n\n return sanitized;\n}\n\nexport function sanitizeBreadcrumb(breadcrumb: Breadcrumb): Breadcrumb | null {\n if (breadcrumb.category === \"console\") {\n return null;\n }\n\n return {\n ...breadcrumb,\n message: breadcrumb.message ? sanitizeString(breadcrumb.message) : breadcrumb.message,\n data: sanitizeValue(breadcrumb.data) as Breadcrumb[\"data\"],\n };\n}\n\nexport function sanitizeValue(value: unknown, key?: string, depth = 0): unknown {\n if (value == null) return value;\n if (depth > MAX_DEPTH) return \"[Truncated]\";\n\n if (isSensitiveKey(key)) {\n return summarizeValue(value, key);\n }\n\n if (typeof value === \"string\") {\n return sanitizeString(value);\n }\n\n if (Array.isArray(value)) {\n return value.slice(0, 20).map((entry) => sanitizeValue(entry, key, depth + 1));\n }\n\n if (typeof value === \"object\") {\n const entries = Object.entries(value as Record<string, unknown>).map(\n ([entryKey, entryValue]) => [entryKey, sanitizeValue(entryValue, entryKey, depth + 1)],\n );\n return Object.fromEntries(entries);\n }\n\n return value;\n}\n\nfunction sanitizeRequest(request: Event[\"request\"]): Event[\"request\"] {\n if (!request) return request;\n\n return {\n ...request,\n data: request.data ? summarizeValue(request.data, \"body\") : undefined,\n headers: undefined,\n cookies: undefined,\n };\n}\n\nfunction isSensitiveKey(key?: string): boolean {\n if (!key) return false;\n return SENSITIVE_KEYS.has(key);\n}\n\nfunction summarizeValue(value: unknown, key?: string): string {\n const label = key ?? \"field\";\n if (typeof value === \"string\") {\n return `[Redacted ${label}; length=${value.length}]`;\n }\n if (Array.isArray(value)) {\n return `[Redacted ${label}; items=${value.length}]`;\n }\n if (value && typeof value === \"object\") {\n return `[Redacted ${label}; keys=${Object.keys(value as Record<string, unknown>).length}]`;\n }\n return `[Redacted ${label}]`;\n}\n\nfunction sanitizeString(value: string): string {\n let sanitized = value.replace(new RegExp(ABSOLUTE_PATH_PATTERN, \"g\"), REDACTED_PATH);\n for (const pattern of TOKEN_PATTERNS) {\n sanitized = sanitized.replace(new RegExp(pattern, \"g\"), REDACTED);\n }\n if (sanitized.length > MAX_STRING_LENGTH) {\n return `${sanitized.slice(0, MAX_STRING_LENGTH)}… [truncated ${sanitized.length - MAX_STRING_LENGTH} chars]`;\n }\n return sanitized;\n}\n"]}
package/dist/sentry.js ADDED
@@ -0,0 +1,205 @@
1
+ import * as Sentry from "@sentry/node";
2
+ const REDACTED = "[REDACTED]";
3
+ const REDACTED_PATH = "[REDACTED_PATH]";
4
+ const MAX_STRING_LENGTH = 256;
5
+ const MAX_DEPTH = 4;
6
+ const SENSITIVE_KEYS = new Set([
7
+ "args",
8
+ "attachment",
9
+ "attachments",
10
+ "authorization",
11
+ "body",
12
+ "content",
13
+ "contents",
14
+ "cookie",
15
+ "cookies",
16
+ "filePath",
17
+ "headers",
18
+ "image",
19
+ "imageAttachments",
20
+ "images",
21
+ "localPath",
22
+ "messages",
23
+ "newUserMessage",
24
+ "path",
25
+ "paths",
26
+ "prompt",
27
+ "response",
28
+ "result",
29
+ "systemPrompt",
30
+ "text",
31
+ "thinking",
32
+ ]);
33
+ const ABSOLUTE_PATH_PATTERN = /(?:\/Users\/[^\s"'`]+|\/workspace\/[^\s"'`]+|\/tmp\/[^\s"'`]+|\/var\/folders\/[^\s"'`]+|[A-Za-z]:\\[^\s"'`]+)/;
34
+ const TOKEN_PATTERNS = [
35
+ /\bsk-[A-Za-z0-9_-]{12,}\b/,
36
+ /\bxox[a-z]-[A-Za-z0-9-]{10,}\b/,
37
+ /\bAIza[0-9A-Za-z_-]{20,}\b/,
38
+ /\bgh[pousr]_[A-Za-z0-9]{20,}\b/,
39
+ ];
40
+ export function createSentryInitOptions(dsn) {
41
+ return {
42
+ dsn,
43
+ environment: process.env.SENTRY_ENVIRONMENT ?? "production",
44
+ enabled: Boolean(dsn) && process.env.SENTRY_ENABLED !== "false",
45
+ sendDefaultPii: false,
46
+ tracesSampleRate: process.env.NODE_ENV === "development" ? 1.0 : 1.0,
47
+ includeLocalVariables: false,
48
+ enableLogs: true,
49
+ beforeSend(event, hint) {
50
+ return sanitizeEvent(event, hint);
51
+ },
52
+ beforeBreadcrumb(breadcrumb) {
53
+ return sanitizeBreadcrumb(breadcrumb);
54
+ },
55
+ };
56
+ }
57
+ export function applyRunScope(scope, context) {
58
+ scope.setTag("channel_id", context.conversationId);
59
+ scope.setTag("session_key", context.sessionKey);
60
+ scope.setTag("platform", context.platform);
61
+ scope.setTag("is_event", String(Boolean(context.isEvent)));
62
+ if (context.threadTs)
63
+ scope.setTag("thread_ts", context.threadTs);
64
+ if (context.provider)
65
+ scope.setTag("provider", context.provider);
66
+ if (context.model)
67
+ scope.setTag("model", context.model);
68
+ scope.setUser({
69
+ id: context.userId,
70
+ username: context.userName,
71
+ });
72
+ scope.setContext("agent_run", {
73
+ conversationId: context.conversationId,
74
+ sessionKey: context.sessionKey,
75
+ messageId: context.messageId,
76
+ threadTs: context.threadTs,
77
+ platform: context.platform,
78
+ provider: context.provider,
79
+ model: context.model,
80
+ isEvent: Boolean(context.isEvent),
81
+ });
82
+ }
83
+ export function metricAttributes(attributes) {
84
+ return Object.fromEntries(Object.entries(attributes).filter((entry) => {
85
+ const [, value] = entry;
86
+ return value !== undefined;
87
+ }));
88
+ }
89
+ export function addLifecycleBreadcrumb(message, data) {
90
+ Sentry.addBreadcrumb({
91
+ category: "agent.lifecycle",
92
+ message,
93
+ level: "info",
94
+ data: data ? metricAttributes(data) : undefined,
95
+ });
96
+ }
97
+ export function sanitizeEvent(event, _hint) {
98
+ const sanitized = {
99
+ ...event,
100
+ breadcrumbs: event.breadcrumbs
101
+ ?.map((breadcrumb) => sanitizeBreadcrumb(breadcrumb))
102
+ .filter((breadcrumb) => breadcrumb !== null),
103
+ extra: sanitizeValue(event.extra),
104
+ contexts: sanitizeValue(event.contexts),
105
+ request: sanitizeRequest(event.request),
106
+ user: undefined,
107
+ server_name: undefined,
108
+ };
109
+ if (sanitized.message) {
110
+ sanitized.message = sanitizeString(sanitized.message);
111
+ }
112
+ if (sanitized.logentry) {
113
+ sanitized.logentry = {
114
+ ...sanitized.logentry,
115
+ message: sanitized.logentry.message ? sanitizeString(sanitized.logentry.message) : undefined,
116
+ };
117
+ }
118
+ if (sanitized.exception?.values) {
119
+ sanitized.exception.values = sanitized.exception.values.map((value) => ({
120
+ ...value,
121
+ value: value.value ? sanitizeString(value.value) : value.value,
122
+ stacktrace: value.stacktrace
123
+ ? {
124
+ ...value.stacktrace,
125
+ frames: value.stacktrace.frames?.map((frame) => ({
126
+ ...frame,
127
+ filename: frame.filename ? sanitizeString(frame.filename) : frame.filename,
128
+ abs_path: frame.abs_path ? sanitizeString(frame.abs_path) : frame.abs_path,
129
+ vars: undefined,
130
+ })),
131
+ }
132
+ : value.stacktrace,
133
+ }));
134
+ }
135
+ return sanitized;
136
+ }
137
+ export function sanitizeBreadcrumb(breadcrumb) {
138
+ if (breadcrumb.category === "console") {
139
+ return null;
140
+ }
141
+ return {
142
+ ...breadcrumb,
143
+ message: breadcrumb.message ? sanitizeString(breadcrumb.message) : breadcrumb.message,
144
+ data: sanitizeValue(breadcrumb.data),
145
+ };
146
+ }
147
+ export function sanitizeValue(value, key, depth = 0) {
148
+ if (value == null)
149
+ return value;
150
+ if (depth > MAX_DEPTH)
151
+ return "[Truncated]";
152
+ if (isSensitiveKey(key)) {
153
+ return summarizeValue(value, key);
154
+ }
155
+ if (typeof value === "string") {
156
+ return sanitizeString(value);
157
+ }
158
+ if (Array.isArray(value)) {
159
+ return value.slice(0, 20).map((entry) => sanitizeValue(entry, key, depth + 1));
160
+ }
161
+ if (typeof value === "object") {
162
+ const entries = Object.entries(value).map(([entryKey, entryValue]) => [entryKey, sanitizeValue(entryValue, entryKey, depth + 1)]);
163
+ return Object.fromEntries(entries);
164
+ }
165
+ return value;
166
+ }
167
+ function sanitizeRequest(request) {
168
+ if (!request)
169
+ return request;
170
+ return {
171
+ ...request,
172
+ data: request.data ? summarizeValue(request.data, "body") : undefined,
173
+ headers: undefined,
174
+ cookies: undefined,
175
+ };
176
+ }
177
+ function isSensitiveKey(key) {
178
+ if (!key)
179
+ return false;
180
+ return SENSITIVE_KEYS.has(key);
181
+ }
182
+ function summarizeValue(value, key) {
183
+ const label = key ?? "field";
184
+ if (typeof value === "string") {
185
+ return `[Redacted ${label}; length=${value.length}]`;
186
+ }
187
+ if (Array.isArray(value)) {
188
+ return `[Redacted ${label}; items=${value.length}]`;
189
+ }
190
+ if (value && typeof value === "object") {
191
+ return `[Redacted ${label}; keys=${Object.keys(value).length}]`;
192
+ }
193
+ return `[Redacted ${label}]`;
194
+ }
195
+ function sanitizeString(value) {
196
+ let sanitized = value.replace(new RegExp(ABSOLUTE_PATH_PATTERN, "g"), REDACTED_PATH);
197
+ for (const pattern of TOKEN_PATTERNS) {
198
+ sanitized = sanitized.replace(new RegExp(pattern, "g"), REDACTED);
199
+ }
200
+ if (sanitized.length > MAX_STRING_LENGTH) {
201
+ return `${sanitized.slice(0, MAX_STRING_LENGTH)}… [truncated ${sanitized.length - MAX_STRING_LENGTH} chars]`;
202
+ }
203
+ return sanitized;
204
+ }
205
+ //# sourceMappingURL=sentry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sentry.js","sourceRoot":"","sources":["../src/sentry.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Breadcrumb, ErrorEvent, Event, EventHint, Scope } from \"@sentry/node\";\nimport * as Sentry from \"@sentry/node\";\n\nconst REDACTED = \"[REDACTED]\";\nconst REDACTED_PATH = \"[REDACTED_PATH]\";\nconst MAX_STRING_LENGTH = 256;\nconst MAX_DEPTH = 4;\n\nconst SENSITIVE_KEYS = new Set([\n \"args\",\n \"attachment\",\n \"attachments\",\n \"authorization\",\n \"body\",\n \"content\",\n \"contents\",\n \"cookie\",\n \"cookies\",\n \"filePath\",\n \"headers\",\n \"image\",\n \"imageAttachments\",\n \"images\",\n \"localPath\",\n \"messages\",\n \"newUserMessage\",\n \"path\",\n \"paths\",\n \"prompt\",\n \"response\",\n \"result\",\n \"systemPrompt\",\n \"text\",\n \"thinking\",\n]);\n\nconst ABSOLUTE_PATH_PATTERN =\n /(?:\\/Users\\/[^\\s\"'`]+|\\/workspace\\/[^\\s\"'`]+|\\/tmp\\/[^\\s\"'`]+|\\/var\\/folders\\/[^\\s\"'`]+|[A-Za-z]:\\\\[^\\s\"'`]+)/;\nconst TOKEN_PATTERNS = [\n /\\bsk-[A-Za-z0-9_-]{12,}\\b/,\n /\\bxox[a-z]-[A-Za-z0-9-]{10,}\\b/,\n /\\bAIza[0-9A-Za-z_-]{20,}\\b/,\n /\\bgh[pousr]_[A-Za-z0-9]{20,}\\b/,\n];\n\nexport interface SentryRunScopeContext {\n conversationId: string;\n sessionKey: string;\n messageId: string;\n platform: string;\n userId: string;\n userName?: string;\n threadTs?: string;\n provider?: string;\n model?: string;\n isEvent?: boolean;\n}\n\nexport function createSentryInitOptions(dsn?: string) {\n return {\n dsn,\n environment: process.env.SENTRY_ENVIRONMENT ?? \"production\",\n enabled: Boolean(dsn) && process.env.SENTRY_ENABLED !== \"false\",\n sendDefaultPii: false,\n tracesSampleRate: process.env.NODE_ENV === \"development\" ? 1.0 : 1.0,\n includeLocalVariables: false,\n enableLogs: true,\n beforeSend(event: ErrorEvent, hint: EventHint): ErrorEvent | null {\n return sanitizeEvent(event, hint);\n },\n beforeBreadcrumb(breadcrumb: Breadcrumb): Breadcrumb | null {\n return sanitizeBreadcrumb(breadcrumb);\n },\n };\n}\n\nexport function applyRunScope(scope: Scope, context: SentryRunScopeContext): void {\n scope.setTag(\"channel_id\", context.conversationId);\n scope.setTag(\"session_key\", context.sessionKey);\n scope.setTag(\"platform\", context.platform);\n scope.setTag(\"is_event\", String(Boolean(context.isEvent)));\n if (context.threadTs) scope.setTag(\"thread_ts\", context.threadTs);\n if (context.provider) scope.setTag(\"provider\", context.provider);\n if (context.model) scope.setTag(\"model\", context.model);\n\n scope.setUser({\n id: context.userId,\n username: context.userName,\n });\n scope.setContext(\"agent_run\", {\n conversationId: context.conversationId,\n sessionKey: context.sessionKey,\n messageId: context.messageId,\n threadTs: context.threadTs,\n platform: context.platform,\n provider: context.provider,\n model: context.model,\n isEvent: Boolean(context.isEvent),\n });\n}\n\nexport function metricAttributes(\n attributes: Record<string, string | number | boolean | undefined>,\n): Record<string, string | number | boolean> {\n return Object.fromEntries(\n Object.entries(attributes).filter((entry): entry is [string, string | number | boolean] => {\n const [, value] = entry;\n return value !== undefined;\n }),\n );\n}\n\nexport function addLifecycleBreadcrumb(\n message: string,\n data?: Record<string, string | number | boolean | undefined>,\n): void {\n Sentry.addBreadcrumb({\n category: \"agent.lifecycle\",\n message,\n level: \"info\",\n data: data ? metricAttributes(data) : undefined,\n });\n}\n\nexport function sanitizeEvent<T extends Event>(event: T, _hint?: EventHint): T | null {\n const sanitized: T = {\n ...event,\n breadcrumbs: event.breadcrumbs\n ?.map((breadcrumb) => sanitizeBreadcrumb(breadcrumb))\n .filter((breadcrumb): breadcrumb is Breadcrumb => breadcrumb !== null),\n extra: sanitizeValue(event.extra) as T[\"extra\"],\n contexts: sanitizeValue(event.contexts) as T[\"contexts\"],\n request: sanitizeRequest(event.request),\n user: undefined,\n server_name: undefined,\n };\n\n if (sanitized.message) {\n sanitized.message = sanitizeString(sanitized.message);\n }\n\n if (sanitized.logentry) {\n sanitized.logentry = {\n ...sanitized.logentry,\n message: sanitized.logentry.message ? sanitizeString(sanitized.logentry.message) : undefined,\n };\n }\n\n if (sanitized.exception?.values) {\n sanitized.exception.values = sanitized.exception.values.map((value) => ({\n ...value,\n value: value.value ? sanitizeString(value.value) : value.value,\n stacktrace: value.stacktrace\n ? {\n ...value.stacktrace,\n frames: value.stacktrace.frames?.map((frame) => ({\n ...frame,\n filename: frame.filename ? sanitizeString(frame.filename) : frame.filename,\n abs_path: frame.abs_path ? sanitizeString(frame.abs_path) : frame.abs_path,\n vars: undefined,\n })),\n }\n : value.stacktrace,\n }));\n }\n\n return sanitized;\n}\n\nexport function sanitizeBreadcrumb(breadcrumb: Breadcrumb): Breadcrumb | null {\n if (breadcrumb.category === \"console\") {\n return null;\n }\n\n return {\n ...breadcrumb,\n message: breadcrumb.message ? sanitizeString(breadcrumb.message) : breadcrumb.message,\n data: sanitizeValue(breadcrumb.data) as Breadcrumb[\"data\"],\n };\n}\n\nexport function sanitizeValue(value: unknown, key?: string, depth = 0): unknown {\n if (value == null) return value;\n if (depth > MAX_DEPTH) return \"[Truncated]\";\n\n if (isSensitiveKey(key)) {\n return summarizeValue(value, key);\n }\n\n if (typeof value === \"string\") {\n return sanitizeString(value);\n }\n\n if (Array.isArray(value)) {\n return value.slice(0, 20).map((entry) => sanitizeValue(entry, key, depth + 1));\n }\n\n if (typeof value === \"object\") {\n const entries = Object.entries(value as Record<string, unknown>).map(\n ([entryKey, entryValue]) => [entryKey, sanitizeValue(entryValue, entryKey, depth + 1)],\n );\n return Object.fromEntries(entries);\n }\n\n return value;\n}\n\nfunction sanitizeRequest(request: Event[\"request\"]): Event[\"request\"] {\n if (!request) return request;\n\n return {\n ...request,\n data: request.data ? summarizeValue(request.data, \"body\") : undefined,\n headers: undefined,\n cookies: undefined,\n };\n}\n\nfunction isSensitiveKey(key?: string): boolean {\n if (!key) return false;\n return SENSITIVE_KEYS.has(key);\n}\n\nfunction summarizeValue(value: unknown, key?: string): string {\n const label = key ?? \"field\";\n if (typeof value === \"string\") {\n return `[Redacted ${label}; length=${value.length}]`;\n }\n if (Array.isArray(value)) {\n return `[Redacted ${label}; items=${value.length}]`;\n }\n if (value && typeof value === \"object\") {\n return `[Redacted ${label}; keys=${Object.keys(value as Record<string, unknown>).length}]`;\n }\n return `[Redacted ${label}]`;\n}\n\nfunction sanitizeString(value: string): string {\n let sanitized = value.replace(new RegExp(ABSOLUTE_PATH_PATTERN, \"g\"), REDACTED_PATH);\n for (const pattern of TOKEN_PATTERNS) {\n sanitized = sanitized.replace(new RegExp(pattern, \"g\"), REDACTED);\n }\n if (sanitized.length > MAX_STRING_LENGTH) {\n return `${sanitized.slice(0, MAX_STRING_LENGTH)}… [truncated ${sanitized.length - MAX_STRING_LENGTH} chars]`;\n }\n return sanitized;\n}\n"]}