diff --git a/packages/sandbox-tenki/README.md b/packages/sandbox-tenki/README.md index 77de4d045..bcde0f63e 100644 --- a/packages/sandbox-tenki/README.md +++ b/packages/sandbox-tenki/README.md @@ -104,7 +104,7 @@ Tenki sessions are billed resources — call `sandbox.destroy()` (or `workspace. Tenki reports a fork/exec/wait failure as a _completed_ run carrying an `errno` (`ENOENT`, `EACCES`, `EMFILE`, …) rather than as an error, and the process itself never writes anything — so an exit code alone cannot tell "command not found" from "ran and failed silently". `WorkspaceSandboxResult` has no field for that errno, so the adapter appends it to `stderr` as a single line: -``` +```text tenki: exec failed: ENOENT (errno 2), reason=exec_failed ``` diff --git a/packages/sandbox-tenki/package.json b/packages/sandbox-tenki/package.json index 9a87b3021..98e2f8b9c 100644 --- a/packages/sandbox-tenki/package.json +++ b/packages/sandbox-tenki/package.json @@ -3,7 +3,7 @@ "description": "VoltAgent Tenki sandbox provider", "version": "2.0.0", "dependencies": { - "@tenkicloud/sandbox": "^0.5.1" + "@tenkicloud/sandbox": "^0.5.4" }, "devDependencies": { "@types/node": "^24.2.1", diff --git a/packages/sandbox-tenki/src/index.ts b/packages/sandbox-tenki/src/index.ts index 0f0ab37b5..8d5383c77 100644 --- a/packages/sandbox-tenki/src/index.ts +++ b/packages/sandbox-tenki/src/index.ts @@ -1,3 +1,4 @@ export { TenkiSandbox } from "./sandbox"; export type { TenkiSandboxOptions, TenkiSandboxInstance } from "./sandbox"; export { createTenkiToolkit } from "./tools"; +export type { TenkiToolkitSandbox } from "./tools"; diff --git a/packages/sandbox-tenki/src/sandbox.spec.ts b/packages/sandbox-tenki/src/sandbox.spec.ts index 0096a0648..0bf8146b8 100644 --- a/packages/sandbox-tenki/src/sandbox.spec.ts +++ b/packages/sandbox-tenki/src/sandbox.spec.ts @@ -50,6 +50,10 @@ type HandleOptions = { killThrowsWith?: unknown; rejectWith?: unknown; errorStdout?: boolean; + /** Error the stdout stream after its chunks were delivered (mid-read death). */ + errorStdoutMidStream?: boolean; + /** Override the aggregate stdout bytes on the resolved result. */ + resultStdout?: string; }; const makeHandle = (options: HandleOptions = {}) => { @@ -68,6 +72,8 @@ const makeHandle = (options: HandleOptions = {}) => { killThrowsWith, rejectWith, errorStdout = false, + errorStdoutMidStream = false, + resultStdout, } = options; let stdoutCtl!: ReadableStreamDefaultController; @@ -102,7 +108,7 @@ const makeHandle = (options: HandleOptions = {}) => { durationMs, reason, errno, - stdout: concat(stdout), + stdout: resultStdout !== undefined ? enc.encode(resultStdout) : concat(stdout), stderr: concat(stderr), }; @@ -126,7 +132,12 @@ const makeHandle = (options: HandleOptions = {}) => { safeClose(stderrCtl); rejectResult(rejectWith); } else if (!hangUntilKill) { - if (!keepStreamsOpen) { + if (errorStdoutMidStream) { + // Erroring synchronously would discard the queued chunks; defer it a + // macrotask so the pump consumes them first, then hits the error. + setTimeout(() => stdoutCtl.error(new Error("stream died mid-read")), 0); + safeClose(stderrCtl); + } else if (!keepStreamsOpen) { safeClose(stdoutCtl); safeClose(stderrCtl); } @@ -152,17 +163,9 @@ const makeHandle = (options: HandleOptions = {}) => { } }); - const writeSpy = vi.fn(); - const stdin = new WritableStream({ - write(chunk) { - writeSpy(chunk); - }, - }); - return { stdout: stdoutStream, stderr: stderrStream, - stdin, pid: Promise.resolve(1), signal: vi.fn(async () => {}), kill, @@ -173,7 +176,6 @@ const makeHandle = (options: HandleOptions = {}) => { ) { return resultPromise.then(onfulfilled, onrejected); }, - _writeSpy: writeSpy, _rejectResult: rejectResult, }; }; @@ -211,7 +213,10 @@ const makeSession = (overrides: Record = {}) => { }; beforeEach(() => { - vi.clearAllMocks(); + // Reset implementations too (not just call history): tests install + // per-test `mocks.createAndWait` behaviors (e.g. never-resolving promises) + // that must not leak into later tests. + vi.resetAllMocks(); }); afterEach(() => { @@ -413,6 +418,45 @@ describe("TenkiSandbox.execute", () => { expect(result.stderr).toBe("side"); }); + it("falls back to the aggregate output when a stream dies mid-read", async () => { + // The pump captured only "par" before the transport failed; the resolved + // run's aggregate bytes are complete and must win over the partial buffer. + const session = makeSession({ + run: vi.fn(() => + makeHandle({ + stdout: ["par"], + resultStdout: "partial output\n", + errorStdoutMidStream: true, + }), + ), + }); + const sandbox = new TenkiSandbox({ session: session as never }); + + const result = await sandbox.execute({ command: "flaky" }); + + expect(result.exitCode).toBe(0); + expect(result.stdout).toBe("partial output\n"); + expect(result.stdoutTruncated).toBe(false); + }); + + it("applies the byte cap to the aggregate fallback after a mid-read failure", async () => { + const session = makeSession({ + run: vi.fn(() => + makeHandle({ + stdout: ["par"], + resultStdout: "partial output\n", + errorStdoutMidStream: true, + }), + ), + }); + const sandbox = new TenkiSandbox({ session: session as never }); + + const result = await sandbox.execute({ command: "flaky", maxOutputBytes: 7 }); + + expect(result.stdout).toBe("partial"); + expect(result.stdoutTruncated).toBe(true); + }); + it("returns an aborted result when the signal is already aborted", async () => { const session = makeSession(); const sandbox = new TenkiSandbox({ session: session as never }); @@ -715,6 +759,40 @@ describe("TenkiSandbox.execute", () => { expect(session.resume).toHaveBeenCalledOnce(); expect(session.run).not.toHaveBeenCalled(); }); + + it("bounds a hung resume RPC and skips queued resumes whose executes were canceled", async () => { + // A resume RPC with no transport deadline must not wedge the lifecycle + // queue: after both executes time out, the bounded wait expires, the + // second (abandoned) transition bails out without issuing its own resume, + // and the queue is usable again. + vi.useFakeTimers(); + const session = makeSession({ + state: "PAUSED", + resume: vi.fn(() => new Promise(() => {})), + }); + const sandbox = new TenkiSandbox({ session: session as never }); + + const first = sandbox.execute({ command: "a", timeoutMs: 10 }); + const second = sandbox.execute({ command: "b", timeoutMs: 10 }); + await vi.advanceTimersByTimeAsync(10); + const [firstResult, secondResult] = await Promise.all([first, second]); + + expect(firstResult.timedOut).toBe(true); + expect(secondResult.timedOut).toBe(true); + expect(session.resume).toHaveBeenCalledOnce(); + + // Expire the bounded wait: the hung transition rejects, the abandoned one + // skips its RPC (still exactly one resume call), and a fresh execute can + // resume and run. + await vi.advanceTimersByTimeAsync(180_000); + expect(session.resume).toHaveBeenCalledOnce(); + + session.resume.mockImplementation(async () => {}); + const result = await sandbox.execute({ command: "c" }); + expect(result.stdout).toBe("ok\n"); + expect(session.resume).toHaveBeenCalledTimes(2); + expect(session.run).toHaveBeenCalledOnce(); + }); }); describe("TenkiSandbox lifecycle", () => { diff --git a/packages/sandbox-tenki/src/sandbox.ts b/packages/sandbox-tenki/src/sandbox.ts index 7978169a1..afa9112fd 100644 --- a/packages/sandbox-tenki/src/sandbox.ts +++ b/packages/sandbox-tenki/src/sandbox.ts @@ -26,10 +26,22 @@ import { isCommandTimeoutError, normalizeEnv, resolveOutput, + resolveWithin, stringToReadableStream, timedOutResult, } from "./utils"; +/** + * Upper bound on how long a queued lifecycle transition waits for + * `session.resume()`. The resume RPC has no transport deadline, so without a + * bound one dead connection would hold {@link TenkiSandbox.lifecycleTransition} + * — and every later `stop()`/`start()`/`execute()` on a paused sandbox — + * hostage forever. Matches the SDK's own `waitResumed` default. On expiry the + * sandbox stays `paused`, and retrying is safe: the engine treats a resume on + * an already-RUNNING session as idempotent. + */ +const RESUME_TRANSITION_TIMEOUT_MS = 180_000; + /** * The underlying Tenki SDK session type, re-exported for consumers that reach * past the `WorkspaceSandbox` seam via {@link TenkiSandbox.getSandbox}. @@ -348,8 +360,15 @@ export class TenkiSandbox implements WorkspaceSandbox { /** * Resume the microVM when a previous {@link stop} paused it, returning the * sandbox to `ready` so commands can run again. No-op otherwise. + * + * `signal` is the requesting `execute()`'s cancellation: a canceled execute + * has already returned by the time its queued transition reaches the head of + * the queue, so the transition bails out instead of issuing a resume RPC + * nobody is waiting for. The RPC itself is bounded by + * {@link RESUME_TRANSITION_TIMEOUT_MS} so an unresponsive resume cannot wedge + * every later lifecycle transition. */ - private async resumeIfPaused(session: Session): Promise { + private async resumeIfPaused(session: Session, signal?: AbortSignal): Promise { return this.serializeLifecycleTransition(async () => { if (this.status === "destroyed" || this.session !== session) { throw new Error("Sandbox has been destroyed"); @@ -357,9 +376,16 @@ export class TenkiSandbox implements WorkspaceSandbox { if (!this.paused) { return; } + if (signal?.aborted) { + throw new Error("Sandbox resume canceled: the requesting execute() timed out or aborted"); + } const generation = this.generation; - await session.resume(); + await resolveWithin( + session.resume(), + RESUME_TRANSITION_TIMEOUT_MS, + `timed out waiting for session ${session.id} to resume`, + ); // Destruction eagerly invalidates the generation and drops the owned // session. A resume RPC may still finish afterward, but it must neither @@ -574,7 +600,9 @@ export class TenkiSandbox implements WorkspaceSandbox { } } } catch { - // ignore stream errors; result bytes are used as a fallback + // A dead pump leaves the buffer silently short; flag it so resolveOutput + // prefers the resolved run's complete aggregate bytes over partial ones. + buffer.failed = true; } finally { // Flush any bytes the decoder is still holding for a partial code point. if (decoder) { @@ -731,7 +759,9 @@ export class TenkiSandbox implements WorkspaceSandbox { // does not exist yet, so `requestKill()` would have nothing to kill. This // is the only await left between the guards and `session.run()`; the // `runOptions` build below is synchronous. - const resumed = await raceCancellation(this.resumeIfPaused(session)); + const resumed = await raceCancellation( + this.resumeIfPaused(session, cancellationController.signal), + ); if (resumed === cancellationMarker) { return cancellationResult(); } diff --git a/packages/sandbox-tenki/src/tools.spec.ts b/packages/sandbox-tenki/src/tools.spec.ts index 27d477746..ad3e9e2c6 100644 --- a/packages/sandbox-tenki/src/tools.spec.ts +++ b/packages/sandbox-tenki/src/tools.spec.ts @@ -1,44 +1,63 @@ import { describe, expect, it, vi } from "vitest"; -import type { TenkiSandbox } from "./sandbox"; -import { createTenkiToolkit } from "./tools"; +import { type TenkiToolkitSandbox, createTenkiToolkit } from "./tools"; -type PreviewParameters = { +type ToolParameters = { safeParse: (input: unknown) => { success: boolean }; }; -const getPreviewParameters = (): PreviewParameters => { - const sandbox = { +const getToolParameters = (name: string): ToolParameters => { + const sandbox: TenkiToolkitSandbox = { getSandbox: vi.fn(), authorizeSshKey: vi.fn(), - } as unknown as TenkiSandbox; - const previewTool = createTenkiToolkit(sandbox).tools.find( - (tool) => (tool as { name?: string }).name === "expose_preview_url", + }; + const tool = createTenkiToolkit(sandbox).tools.find( + (candidate) => (candidate as { name?: string }).name === name, ); - if (!previewTool) { - throw new Error("expose_preview_url tool not found"); + if (!tool) { + throw new Error(`${name} tool not found`); } - return (previewTool as { parameters: PreviewParameters }).parameters; + return (tool as { parameters: ToolParameters }).parameters; }; describe("createTenkiToolkit preview input schema", () => { it.each([1, 65535])("accepts boundary port %s", (port) => { - expect(getPreviewParameters().safeParse({ port }).success).toBe(true); + expect(getToolParameters("expose_preview_url").safeParse({ port }).success).toBe(true); }); it.each([0, -1, 65536, 1.5])("rejects invalid port %s", (port) => { - expect(getPreviewParameters().safeParse({ port }).success).toBe(false); + expect(getToolParameters("expose_preview_url").safeParse({ port }).success).toBe(false); }); it("accepts an omitted or positive integer TTL", () => { - const parameters = getPreviewParameters(); + const parameters = getToolParameters("expose_preview_url"); expect(parameters.safeParse({ port: 3000 }).success).toBe(true); expect(parameters.safeParse({ port: 3000, ttlMs: 1 }).success).toBe(true); }); it.each([0, -1, 1.5])("rejects invalid TTL %s", (ttlMs) => { - expect(getPreviewParameters().safeParse({ port: 3000, ttlMs }).success).toBe(false); + expect(getToolParameters("expose_preview_url").safeParse({ port: 3000, ttlMs }).success).toBe( + false, + ); + }); +}); + +describe("createTenkiToolkit ssh key input schema", () => { + it("accepts a single-line authorized_keys entry", () => { + expect( + getToolParameters("authorize_ssh_key").safeParse({ publicKey: "ssh-ed25519 AAAA user" }) + .success, + ).toBe(true); + }); + + it.each([ + ["empty", ""], + ["whitespace-only", " "], + ["multiline", "ssh-ed25519 AAAA\nssh-rsa BBBB"], + ["carriage return", "ssh-ed25519 AAAA\r\n"], + ])("rejects a %s public key", (_label, publicKey) => { + expect(getToolParameters("authorize_ssh_key").safeParse({ publicKey }).success).toBe(false); }); }); diff --git a/packages/sandbox-tenki/src/tools.ts b/packages/sandbox-tenki/src/tools.ts index 4193c3e6b..416309def 100644 --- a/packages/sandbox-tenki/src/tools.ts +++ b/packages/sandbox-tenki/src/tools.ts @@ -2,6 +2,11 @@ import { type Toolkit, createTool, createToolkit } from "@voltagent/core"; import { z } from "zod"; import type { TenkiSandbox } from "./sandbox"; +/** + * The slice of {@link TenkiSandbox} that {@link createTenkiToolkit} depends on. + */ +export type TenkiToolkitSandbox = Pick; + /** * Build a toolkit of Tenki-specific tools that reach past the * `WorkspaceSandbox` seam: exposing a preview URL for a port, and authorizing @@ -12,7 +17,7 @@ import type { TenkiSandbox } from "./sandbox"; * These are intentionally separate from the core `execute_command` adapter so a * consumer can opt in without them. */ -export function createTenkiToolkit(sandbox: TenkiSandbox): Toolkit { +export function createTenkiToolkit(sandbox: TenkiToolkitSandbox): Toolkit { const exposePreviewUrl = createTool({ name: "expose_preview_url", description: @@ -52,6 +57,12 @@ export function createTenkiToolkit(sandbox: TenkiSandbox): Toolkit { parameters: z.object({ publicKey: z .string() + // `updateSshAuthorizedKeys` would accept a blank or newline-carrying + // value verbatim; reject obvious non-entries before mutating the set. + .refine( + (value) => value.trim().length > 0 && !/[\r\n]/.test(value), + "publicKey must be a non-empty, single-line authorized_keys entry", + ) .describe("SSH public key in authorized_keys format (e.g. 'ssh-ed25519 AAAA... user')"), }), outputSchema: z.object({ diff --git a/packages/sandbox-tenki/src/utils.spec.ts b/packages/sandbox-tenki/src/utils.spec.ts index e333d8e96..e340bef98 100644 --- a/packages/sandbox-tenki/src/utils.spec.ts +++ b/packages/sandbox-tenki/src/utils.spec.ts @@ -1,5 +1,5 @@ import { CommandTimeoutError } from "@tenkicloud/sandbox"; -import { describe, expect, it } from "vitest"; +import { describe, expect, it, vi } from "vitest"; import { DEFAULT_MAX_OUTPUT_BYTES, DEFAULT_TIMEOUT_MS, @@ -13,6 +13,7 @@ import { isCommandTimeoutError, normalizeEnv, resolveOutput, + resolveWithin, stringToReadableStream, timedOutResult, truncateOutput, @@ -136,6 +137,34 @@ describe("OutputBuffer", () => { appendOutput(buffer, new Uint8Array([0x61, 0xf8]), 100); expect(resolveOutput(buffer, undefined, 100).content).toBe("a"); }); + + it("prefers the complete fallback over a buffer whose pump failed", () => { + const buffer = initOutputBuffer(); + appendOutput(buffer, "par", 100); + buffer.failed = true; + // The partial streamed bytes are silently short; the resolved run's + // aggregate output is authoritative. + expect(resolveOutput(buffer, "partial output", 100)).toEqual({ + content: "partial output", + truncated: false, + }); + }); + + it("still truncates the fallback used for a failed pump", () => { + const buffer = initOutputBuffer(); + buffer.failed = true; + expect(resolveOutput(buffer, "partial output", 7)).toEqual({ + content: "partial", + truncated: true, + }); + }); + + it("keeps the partial buffer when the pump failed but there is no fallback", () => { + const buffer = initOutputBuffer(); + appendOutput(buffer, "par", 100); + buffer.failed = true; + expect(resolveOutput(buffer, undefined, 100).content).toBe("par"); + }); }); describe("truncateOutput", () => { @@ -264,6 +293,16 @@ describe("formatRunDiagnostic", () => { expect(formatRunDiagnostic("nope")).toBeUndefined(); expect(formatRunDiagnostic({})).toBeUndefined(); }); + + it("collapses newlines in a multiline reason to keep the diagnostic one line", () => { + expect( + formatRunDiagnostic({ exitCode: 1, errno: 0, reason: "disk\r\nfull\nretry later\n" }), + ).toBe("tenki: run ended: reason=disk full retry later"); + }); + + it("treats a whitespace-and-newline-only reason as absent", () => { + expect(formatRunDiagnostic({ exitCode: 1, errno: 0, reason: " \r\n " })).toBeUndefined(); + }); }); describe("appendRunDiagnostic", () => { @@ -282,6 +321,42 @@ describe("appendRunDiagnostic", () => { }); }); +describe("resolveWithin", () => { + it("resolves with the operation before the deadline", async () => { + await expect(resolveWithin(Promise.resolve("ok"), 1_000, "too slow")).resolves.toBe("ok"); + }); + + it("propagates the operation's rejection", async () => { + await expect( + resolveWithin(Promise.reject(new Error("boom")), 1_000, "too slow"), + ).rejects.toThrow("boom"); + }); + + it("rejects with the given message when the operation outlives the deadline", async () => { + vi.useFakeTimers(); + try { + const pending = resolveWithin(new Promise(() => {}), 50, "too slow"); + const assertion = expect(pending).rejects.toThrow("too slow"); + await vi.advanceTimersByTimeAsync(50); + await assertion; + } finally { + vi.useRealTimers(); + } + }); + + it("tolerates timer handles without unref (browser-style timers)", async () => { + // Node's Timeout has unref(); a browser-style numeric handle does not. + const original = globalThis.setTimeout; + vi.stubGlobal("setTimeout", ((handler: () => void, ms?: number) => + Number(original(handler, ms))) as never); + try { + await expect(resolveWithin(Promise.resolve("ok"), 1_000, "too slow")).resolves.toBe("ok"); + } finally { + vi.unstubAllGlobals(); + } + }); +}); + describe("isCommandTimeoutError", () => { it("recognizes CommandTimeoutError", () => { expect(isCommandTimeoutError(new CommandTimeoutError("boom"))).toBe(true); diff --git a/packages/sandbox-tenki/src/utils.ts b/packages/sandbox-tenki/src/utils.ts index 5d0b1d2f7..70280971e 100644 --- a/packages/sandbox-tenki/src/utils.ts +++ b/packages/sandbox-tenki/src/utils.ts @@ -36,9 +36,19 @@ export type OutputBuffer = { chunks: Buffer[]; size: number; truncated: boolean; + /** + * The pump reading into this buffer died mid-stream, so the buffered bytes + * may be silently short of what the process actually wrote. + */ + failed: boolean; }; -export const initOutputBuffer = (): OutputBuffer => ({ chunks: [], size: 0, truncated: false }); +export const initOutputBuffer = (): OutputBuffer => ({ + chunks: [], + size: 0, + truncated: false, + failed: false, +}); export const appendOutput = (buffer: OutputBuffer, chunk: unknown, maxBytes: number): void => { if (maxBytes <= 0) { @@ -143,12 +153,17 @@ export const truncateOutput = ( /** * Resolve the final output for a stream: prefer the streamed buffer when we * captured anything, otherwise fall back to the aggregated bytes on the result. + * A buffer whose pump failed mid-stream may be silently short, so when the run + * resolved (its aggregate output is complete) the aggregate wins instead. */ export const resolveOutput = ( buffer: OutputBuffer, fallback: string | undefined, maxBytes: number, ): { content: string; truncated: boolean } => { + if (buffer.failed && fallback !== undefined) { + return truncateOutput(fallback, maxBytes); + } if (buffer.size > 0 || buffer.truncated) { return { content: toOutputString(buffer), truncated: buffer.truncated }; } @@ -259,7 +274,10 @@ export const formatRunDiagnostic = (result: unknown): string | undefined => { // `errno` is a non-optional proto scalar, so it arrives as 0 (not absent) // whenever the guest agent has nothing to report. const errno = typeof record.errno === "number" && record.errno !== 0 ? record.errno : undefined; - const rawReason = typeof record.reason === "string" ? record.reason.trim() : ""; + // `reason` is free-form guest-agent text folded into a single stderr line; + // collapse CR/LF so one diagnostic cannot inject extra lines. + const rawReason = + typeof record.reason === "string" ? record.reason.replace(/[\r\n]+/g, " ").trim() : ""; const reason = BENIGN_RUN_REASONS.has(rawReason.toLowerCase()) ? undefined : rawReason || undefined; @@ -298,6 +316,30 @@ export const appendRunDiagnostic = (stderr: string, diagnostic?: string): string return stderr.endsWith("\n") ? `${stderr}${diagnostic}\n` : `${stderr}\n${diagnostic}\n`; }; +/** + * Await `operation`, rejecting with `message` if it has not settled within + * `timeoutMs`. The operation itself is not (and cannot be) canceled — this + * bounds how long a caller waits on it, and the race keeps a late settlement + * observed. The deadline timer is unref'd where the runtime supports it so a + * never-settling operation does not keep the process alive. + */ +export const resolveWithin = async ( + operation: Promise, + timeoutMs: number, + message: string, +): Promise => { + let timeoutId: ReturnType | undefined; + const deadline = new Promise((_, reject) => { + timeoutId = setTimeout(() => reject(new Error(message)), timeoutMs); + timeoutId.unref?.(); + }); + try { + return await Promise.race([operation, deadline]); + } finally { + clearTimeout(timeoutId); + } +}; + /** * Is this the SDK's per-command timeout error? */ diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2f24be72c..9d06cd7cb 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -748,7 +748,7 @@ importers: dependencies: '@cerbos/grpc': specifier: ^0.23.0 - version: 0.23.7(@bufbuild/protobuf@2.10.1)(@cerbos/api@0.4.0) + version: 0.23.7(@bufbuild/protobuf@2.12.1)(@cerbos/api@0.4.0) '@modelcontextprotocol/sdk': specifier: ^1.12.1 version: 1.24.3(zod@3.25.76) @@ -3895,7 +3895,7 @@ importers: version: 1.0.1 '@gitlab/gitlab-ai-provider': specifier: ^3.1.1 - version: 3.1.1(@ai-sdk/provider-utils@4.0.1)(@ai-sdk/provider@3.0.4)(graphql@16.11.0)(ws@8.18.3) + version: 3.1.1(@ai-sdk/provider-utils@4.0.1)(@ai-sdk/provider@3.0.4)(graphql@16.11.0)(ws@8.21.1) '@modelcontextprotocol/sdk': specifier: ^1.12.1 version: 1.17.2 @@ -4302,7 +4302,7 @@ importers: dependencies: '@daytonaio/sdk': specifier: ^0.139.0 - version: 0.139.0(ws@8.18.3) + version: 0.139.0(ws@8.21.1) devDependencies: '@types/node': specifier: ^24.2.1 @@ -4339,8 +4339,8 @@ importers: packages/sandbox-tenki: dependencies: '@tenkicloud/sandbox': - specifier: ^0.5.1 - version: 0.5.1 + specifier: ^0.5.4 + version: 0.5.4 devDependencies: '@types/node': specifier: ^24.2.1 @@ -4723,7 +4723,7 @@ packages: resolution: {integrity: sha512-YlVmS8e53EZuMG68WvjNqzxoa/8NYCy3a8yoWsogPf1iZXa1RZ2WbQTi80xGzUnzluwxGSULlg7m7a1/8eXkkA==} dependencies: '@ag-ui/core': 0.0.41 - '@bufbuild/protobuf': 2.10.1 + '@bufbuild/protobuf': 2.12.1 '@protobuf-ts/protoc': 2.11.1 dev: true @@ -4731,7 +4731,7 @@ packages: resolution: {integrity: sha512-NDUwSgMnGEqxZGkWIJ1ge5t3Q7Kiddj360x2JAWaIfv9w+7tDJ0pmgyzf3/SXp605aY2wZiDLBtJ6jKZeg1lFg==} dependencies: '@ag-ui/core': 0.0.42 - '@bufbuild/protobuf': 2.10.1 + '@bufbuild/protobuf': 2.12.1 '@protobuf-ts/protoc': 2.11.1 /@ai-sdk/amazon-bedrock@3.0.7(zod@3.25.76): @@ -8293,10 +8293,10 @@ packages: /@bufbuild/protobuf@2.10.1: resolution: {integrity: sha512-ckS3+vyJb5qGpEYv/s1OebUHDi/xSNtfgw1wqKZo7MR9F2z+qXr0q5XagafAG/9O0QPVIUfST0smluYSTpYFkg==} + dev: false /@bufbuild/protobuf@2.12.1: resolution: {integrity: sha512-BvAMfS6LrgZiryOAZ4pBYucu4wG/Ei/9o9DZ9akbREnMLbPJiom2i8b9C8IsKErQoiKqVhrerzt3kOT/RrzLHg==} - dev: false /@bugsnag/browser@8.6.0: resolution: {integrity: sha512-7UGqTGnQqXUQ09gOlWbDTFUSbeLIIrP+hML3kTOq8Zdc8nP/iuOEflXGLV2TxWBWW8xIUPc928caFPr9EcaDuw==} @@ -8358,30 +8358,30 @@ packages: resolution: {integrity: sha512-eLxYFGuEyw0kfX0SjSLPKRYU90gXdO6PEaCa6MCmpQ9Z+yTEfPoMLLYrxS+BRumhVN2MAiBsO2tg2MAT3qyQbg==} engines: {node: '>= 20'} dependencies: - '@bufbuild/protobuf': 2.10.1 + '@bufbuild/protobuf': 2.12.1 dev: false - /@cerbos/core@0.26.0(@bufbuild/protobuf@2.10.1): + /@cerbos/core@0.26.0(@bufbuild/protobuf@2.12.1): resolution: {integrity: sha512-lyX9111LMU1sSCGT/dQvto1bnEXgDEKY4X2mxxhnfDHhcxmeEwgUq1Qp1XLOt/1eg3/RuWEEpbFh2saSa5Y9CQ==} engines: {node: '>= 20'} peerDependencies: '@bufbuild/protobuf': ^2.10.1 dependencies: - '@bufbuild/protobuf': 2.10.1 + '@bufbuild/protobuf': 2.12.1 '@cerbos/api': 0.4.0 uuid: 13.0.0 dev: false - /@cerbos/grpc@0.23.7(@bufbuild/protobuf@2.10.1)(@cerbos/api@0.4.0): + /@cerbos/grpc@0.23.7(@bufbuild/protobuf@2.12.1)(@cerbos/api@0.4.0): resolution: {integrity: sha512-3MgjW1YiuPGuVaEHM8CV0ykT8Evlns5nIiLPWLdpouiQWNTNai/mm1IPuDtqmf+F+lZRka9E+hoctMtVuf0/aA==} engines: {node: '>= 20'} peerDependencies: '@bufbuild/protobuf': ^2.10.1 '@cerbos/api': ^0.4.0 dependencies: - '@bufbuild/protobuf': 2.10.1 + '@bufbuild/protobuf': 2.12.1 '@cerbos/api': 0.4.0 - '@cerbos/core': 0.26.0(@bufbuild/protobuf@2.10.1) + '@cerbos/core': 0.26.0(@bufbuild/protobuf@2.12.1) '@grpc/grpc-js': 1.14.3 dev: false @@ -9260,7 +9260,7 @@ packages: - debug dev: false - /@daytonaio/sdk@0.139.0(ws@8.18.3): + /@daytonaio/sdk@0.139.0(ws@8.21.1): resolution: {integrity: sha512-67NSkhnl9NiUgBfheN5AtkH0/T5U+WTZmGlY2k+ujAAl/ntpyA/T/q+Pznk44oCJyM1O39OEWt/ugmAEyqRWLg==} dependencies: '@aws-sdk/client-s3': 3.985.0 @@ -9274,7 +9274,7 @@ packages: expand-tilde: 2.0.2 fast-glob: 3.3.3 form-data: 4.0.4 - isomorphic-ws: 5.0.0(ws@8.18.3) + isomorphic-ws: 5.0.0(ws@8.21.1) pathe: 2.0.3 shell-quote: 1.8.3 tar: 7.5.7 @@ -9326,6 +9326,7 @@ packages: dependencies: '@emnapi/wasi-threads': 1.2.2 tslib: 2.8.1 + dev: false optional: true /@emnapi/core@1.4.5: @@ -9335,6 +9336,15 @@ packages: tslib: 2.8.1 dev: true + /@emnapi/core@2.0.0-alpha.3: + resolution: {integrity: sha512-AZypUeJ/yByuxyS7BlSNRDOMLMlROYtjYdIAuBmJssVz1UJDSeYxLrdizhXCFYhedC5bqd/ASy8EuNXbVVXp9g==} + requiresBuild: true + dependencies: + '@emnapi/wasi-threads': 2.0.1 + tslib: 2.8.1 + dev: true + optional: true + /@emnapi/runtime@1.11.1: resolution: {integrity: sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==} requiresBuild: true @@ -9348,6 +9358,14 @@ packages: tslib: 2.8.1 dev: true + /@emnapi/runtime@2.0.0-alpha.3: + resolution: {integrity: sha512-hFPAhMUjJD9BSyCANEISPOogeXC9Zo9ZQl7L6vKnaVsMkCtzznaW/naYypeyl0Gv5rYfWYsZbpixTMpjDJzQeA==} + requiresBuild: true + dependencies: + tslib: 2.8.1 + dev: true + optional: true + /@emnapi/wasi-threads@1.0.4: resolution: {integrity: sha512-PJR+bOmMOPH8AtcTGAyYNiuJ3/Fcoj2XN/gBEWzDIKh254XO+mM9XoXHk5GNEhodxeMznbg7BlRojVbKN+gC6g==} dependencies: @@ -9359,6 +9377,15 @@ packages: requiresBuild: true dependencies: tslib: 2.8.1 + dev: false + optional: true + + /@emnapi/wasi-threads@2.0.1: + resolution: {integrity: sha512-9DsSk+o5NBX0CCJT8s0EROGSGxjR/tKu6aBTaVyq+SjAEQH4XcdcRxPBRzsBLizTTJ49MJjF+jgu3qnO9GLQcQ==} + requiresBuild: true + dependencies: + tslib: 2.8.1 + dev: true optional: true /@envelop/core@5.4.0: @@ -10078,7 +10105,7 @@ packages: mrmime: 2.0.1 open: 10.2.0 tinyglobby: 0.2.15 - ws: 8.18.3 + ws: 8.21.1 transitivePeerDependencies: - bufferutil - supports-color @@ -10242,7 +10269,7 @@ packages: resolution: {integrity: sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==} dev: true - /@gitlab/gitlab-ai-provider@3.1.1(@ai-sdk/provider-utils@4.0.1)(@ai-sdk/provider@3.0.4)(graphql@16.11.0)(ws@8.18.3): + /@gitlab/gitlab-ai-provider@3.1.1(@ai-sdk/provider-utils@4.0.1)(@ai-sdk/provider@3.0.4)(graphql@16.11.0)(ws@8.21.1): resolution: {integrity: sha512-7AtFrCflq2NzC99bj7YaqbQDCZyaScM1+L4ujllV5syiRTFE239Uhnd/yEkPXa7sUAnNRfN3CWusCkQ2zK/q9g==} engines: {node: '>=18'} peerDependencies: @@ -10254,7 +10281,7 @@ packages: '@anthropic-ai/sdk': 0.71.2(zod@3.25.76) '@anycable/core': 0.9.2 graphql-request: 6.1.0(graphql@16.11.0) - isomorphic-ws: 5.0.0(ws@8.18.3) + isomorphic-ws: 5.0.0(ws@8.21.1) socket.io-client: 4.8.3 vscode-jsonrpc: 8.2.1 zod: 3.25.76 @@ -10281,7 +10308,7 @@ packages: engines: {node: '>=18.0.0'} dependencies: google-auth-library: 9.15.1 - ws: 8.18.3 + ws: 8.21.1 transitivePeerDependencies: - bufferutil - encoding @@ -12116,7 +12143,7 @@ packages: resolution: {integrity: sha512-DtLWIH29onUYR00i0GlQ3UdcTRC6EP4u9w/h9LxpUZJWRMARk6dQwZ6Jkd+QdwVpuAOrdxt18v0K2uIYR3fwFg==} dependencies: '@types/ws': 8.18.1 - ws: 8.18.3 + ws: 8.21.1 transitivePeerDependencies: - bufferutil - utf-8-validate @@ -12552,6 +12579,21 @@ packages: '@emnapi/core': 1.11.1 '@emnapi/runtime': 1.11.1 '@tybys/wasm-util': 0.10.3 + dev: false + optional: true + + /@napi-rs/wasm-runtime@1.2.2(@emnapi/core@2.0.0-alpha.3)(@emnapi/runtime@2.0.0-alpha.3): + resolution: {integrity: sha512-JfB4kuJQjaoHuCTseIINHtHWeJnvgEcxjwA5t/Y00ZgaOO1Crz3fjT/p8kT28zA/Caz7oiUMn3d6H2yOVCVwuw==} + engines: {node: ^20.19.0 || ^22.13.0 || >=23.5.0} + requiresBuild: true + peerDependencies: + '@emnapi/core': ^1.7.1 || ^2.0.0-alpha.3 + '@emnapi/runtime': ^1.7.1 || ^2.0.0-alpha.3 + dependencies: + '@emnapi/core': 2.0.0-alpha.3 + '@emnapi/runtime': 2.0.0-alpha.3 + '@tybys/wasm-util': 0.10.3 + dev: true optional: true /@neon-rs/load@0.0.4: @@ -13523,7 +13565,7 @@ packages: vite-plugin-inspect: 11.3.3(@nuxt/kit@3.19.3)(vite@7.2.7) vite-plugin-vue-tracer: 1.0.1(vite@7.2.7)(vue@3.5.22) which: 5.0.0 - ws: 8.18.3 + ws: 8.21.1 transitivePeerDependencies: - bufferutil - supports-color @@ -15452,8 +15494,8 @@ packages: dev: false optional: true - /@oxc-project/types@0.139.0: - resolution: {integrity: sha512-r9gHphtCs+1M7J0pw6Sn/hh/Wpa/iQrOOkrNAlVLF/gHq+/CJmHIWKKUUhdWjcD6CIa8idarspCsASiXCXvFUw==} + /@oxc-project/types@0.142.0: + resolution: {integrity: sha512-7W+2q5AKQVU36fkaryontrHn3YDt1RyUYXatw9i5H8ocYe2sPKSFB6eS8WNPeRKiN1qAWWZUPm7gwFzJGrccqQ==} dev: true /@oxc-project/types@0.94.0: @@ -17665,8 +17707,8 @@ packages: /@repeaterjs/repeater@3.0.6: resolution: {integrity: sha512-Javneu5lsuhwNCryN+pXH93VPQ8g0dBX7wItHFgYiwQmzE1sVdg5tWHiOgHywzL2W21XQopa7IwIEnNbmeUJYA==} - /@rolldown/binding-android-arm64@1.1.5: - resolution: {integrity: sha512-lZg8fqIv2v7FF237bwMgzGZEJvGL79/s5knJ/i6FmsGF4XXlzccZ4jb+TrFIxtSSxFtIpdsgrPZeMk1I9AFcyQ==} + /@rolldown/binding-android-arm64@1.2.1: + resolution: {integrity: sha512-02hOeOSryYxVrOIphmLAsqnCJWxwlzFk+pEt/N/i6OgT3lShHO7xGCU5cpgchRDHboAEbSjzgGh+O/u1GswQmA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] @@ -17674,8 +17716,8 @@ packages: dev: true optional: true - /@rolldown/binding-darwin-arm64@1.1.5: - resolution: {integrity: sha512-51Bnx9pNiMRKSUNtBfySkNJ9vMU9Hh3I1ozDd6gyPPYzaXCfnptUcEZxXGYFn+ul2dtcMUiqGR1Yai2K10uoTw==} + /@rolldown/binding-darwin-arm64@1.2.1: + resolution: {integrity: sha512-fMsTOnN0OjFm3CyppWPitKnc8UlliVARUULW6cfU6AIqjdtgmSFWSk9vecHzZduv/yMWIHDlRhM1e8Iff9uAfA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] @@ -17683,8 +17725,8 @@ packages: dev: true optional: true - /@rolldown/binding-darwin-x64@1.1.5: - resolution: {integrity: sha512-Tm+gbfC0aHu1tBA/JvKQh32S0K6YgCHkiAF4/W6xX0K0RmNuc94VeK419dJoE65R5aRxmo+noZQSWrAMF6yb6g==} + /@rolldown/binding-darwin-x64@1.2.1: + resolution: {integrity: sha512-1wjKdz/XLGKHaTNHjQveQ/B23TKx4ItAqm1JbyVuvNPc4Ze0Fb48s49TAd/2zcplPl8okE/UbTgmlVfwT7eFeQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] @@ -17692,8 +17734,8 @@ packages: dev: true optional: true - /@rolldown/binding-freebsd-x64@1.1.5: - resolution: {integrity: sha512-JMzDKCCXq93YccG5gz3hvOs1oXRKAf0XYpfOS88e+wZrC8Iugj6j68867vrYZkvpDDpKn/KoKORThmchMpF6TA==} + /@rolldown/binding-freebsd-x64@1.2.1: + resolution: {integrity: sha512-Fa0jHR07E7YBN4vOEsbVf2briYNsuOowfLJaXULZM0ldMlaCaj2LJgLMbMe4iacRyZmvR8efFhgR9wKuGclQUg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] @@ -17701,8 +17743,8 @@ packages: dev: true optional: true - /@rolldown/binding-linux-arm-gnueabihf@1.1.5: - resolution: {integrity: sha512-uML21j2K5TfPGutKxub+M+nLjZIrWjXQ5Grx4lCe/nimTj9B4L63zHpjXLl4y0L3mcm2htEQIb06oCG/szerNw==} + /@rolldown/binding-linux-arm-gnueabihf@1.2.1: + resolution: {integrity: sha512-pzkgu1SSHGgRRyRZ4fbmSgmajbVt+epaLP99NDjFft69v/ypfTi6swBMiVdh2EkQ0OSnHE1lZDM7DRGkyAzUpA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] @@ -17710,8 +17752,8 @@ packages: dev: true optional: true - /@rolldown/binding-linux-arm64-gnu@1.1.5: - resolution: {integrity: sha512-navSiuTMogvnQoZoM/v+l3ZWo50/NTwSHSzheABx/RCnmUPaKwq9qSo4Br2OYRs21+Fz8uFqITZM3H4opOB0/Q==} + /@rolldown/binding-linux-arm64-gnu@1.2.1: + resolution: {integrity: sha512-QI5SEDY8cbiYWHx0VO4vIc3UlS6a32vXHjU8Qy/17adEmZIPuByJg13UEvo9c/UCiUkdcVWY83C+b+JrwnNyUg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] @@ -17719,8 +17761,8 @@ packages: dev: true optional: true - /@rolldown/binding-linux-arm64-musl@1.1.5: - resolution: {integrity: sha512-lAryqH7IteztmCXQXk0etKj4wBQ7Gx5S6LjKhsgp9zb8I5bsuvU/2llH1hDQcjsFeqIsovMVN339/8pUDDBXxA==} + /@rolldown/binding-linux-arm64-musl@1.2.1: + resolution: {integrity: sha512-Sm41FyCeXqmYcERoYOCbGIL5hNfd8w9LQ7Y61Bev48HkcjaJqV/iiVOaiDxjVTRMS+QKrZmD8cfPt4uMVnvM+A==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] @@ -17728,8 +17770,8 @@ packages: dev: true optional: true - /@rolldown/binding-linux-ppc64-gnu@1.1.5: - resolution: {integrity: sha512-fsK/sNBnxzBlL4O1JNrZakVQxPspqpED5dLtNsZS9oOKmtSpdNIzxH2kkol5HYTWJN47sE20ztMJPxfZ89qGOg==} + /@rolldown/binding-linux-ppc64-gnu@1.2.1: + resolution: {integrity: sha512-2x+WhXTGl9yJYPbltW/BSEPTVz9OIWQyER4N+gJEDWkkn904eRcBzELqh/Hf7K0w/ubGbKNMv0ZC+94QK/IFEg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] @@ -17737,8 +17779,8 @@ packages: dev: true optional: true - /@rolldown/binding-linux-s390x-gnu@1.1.5: - resolution: {integrity: sha512-gLYb4BIadlfTOYT5gO503n8zQjXflgzpD0FcyKh0Mzx3rqCZKnHoJWV9xe1KXUJ5lx2JfcSHr/mhzS0PC/McAA==} + /@rolldown/binding-linux-s390x-gnu@1.2.1: + resolution: {integrity: sha512-eEjmQpuRQayHPWWnywaWHkFT3ToPbP3RYy42VVd/B9aBGDA+Ol25EIWHxKQST3IiWJjikCWUF7KtbfqwZrzVwQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] @@ -17746,8 +17788,8 @@ packages: dev: true optional: true - /@rolldown/binding-linux-x64-gnu@1.1.5: - resolution: {integrity: sha512-FjcpEKUyJygHgs1o50VYNvkt5+7Le/VEdYt0AkRpkL33MnyQfwr8l5mXwMmfmTbyMPr5vJLC+8/Gd9gXnwU1QQ==} + /@rolldown/binding-linux-x64-gnu@1.2.1: + resolution: {integrity: sha512-/Orga1fZYkLc/56jBICcHrKchl8Z2UKdDSr3LG9ToWO1lQ6a4Livk9Xz+9WN91zsz5QR3XQz2NNoSDEvP6qadw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] @@ -17755,8 +17797,8 @@ packages: dev: true optional: true - /@rolldown/binding-linux-x64-musl@1.1.5: - resolution: {integrity: sha512-Me+PfPI2TMeOQk0gYWfLQZtTktrmzbr8cDboqX83XKc7UrgAi55gF+2dUkWdxd19n55Essp2yeca+O9N5rBxHg==} + /@rolldown/binding-linux-x64-musl@1.2.1: + resolution: {integrity: sha512-xxBJRL+0q0Kce7orznGWLuylHDY65vuARXZRpX+hPdv+DqK2c3NlCsVA98tlWzWNEE7yPqA/1NQ5nnCrj49Y5A==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] @@ -17764,8 +17806,8 @@ packages: dev: true optional: true - /@rolldown/binding-openharmony-arm64@1.1.5: - resolution: {integrity: sha512-yc5WrLzXks6zCQfn9Oxr8pORKyl/pF+QjHmW/Qx3qu0oyrrNC+y2JLTU1E2rcWYAmzlnqngWXHQjy51VzW70Vw==} + /@rolldown/binding-openharmony-arm64@1.2.1: + resolution: {integrity: sha512-M6AdXIXw3s+/8XpKMzdGDEXGS1S7kwUsy+rcTIUIOx5Ge4nXKCtAFHFV9YKkXvGcC5WMoTjAteLzlsQROVI0Yw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] @@ -17773,20 +17815,19 @@ packages: dev: true optional: true - /@rolldown/binding-wasm32-wasi@1.1.5: - resolution: {integrity: sha512-VbQGPX2b4r48TAMIM2cjgluIM1HYutm4pcTEJsle7iEP7sB1dFqtPLBVbdLAZCxy1txCcPxf4QFf4v8uvltPqA==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [wasm32] + /@rolldown/binding-wasm32-wasi@1.2.1: + resolution: {integrity: sha512-/TX0SoRGojHzSAHpfVBbavRVSazg5U3h3Y3VXfcc0cdugq6kxdqw8LPGFiPr+/7gE/60zRcsOY2Vi9b9eT0jww==} + engines: {node: ^20.19.0 || ^22.13.0 || >=23.5.0} requiresBuild: true dependencies: - '@emnapi/core': 1.11.1 - '@emnapi/runtime': 1.11.1 - '@napi-rs/wasm-runtime': 1.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1) + '@emnapi/core': 2.0.0-alpha.3 + '@emnapi/runtime': 2.0.0-alpha.3 + '@napi-rs/wasm-runtime': 1.2.2(@emnapi/core@2.0.0-alpha.3)(@emnapi/runtime@2.0.0-alpha.3) dev: true optional: true - /@rolldown/binding-win32-arm64-msvc@1.1.5: - resolution: {integrity: sha512-gHv82k63z4qpV5+Q1y/12KrK0ltWBukVDI8nZcbT7Tt/ZlOIVwppazneq0F93oDxTo3IgAMEDIoQh3E2n6mVsw==} + /@rolldown/binding-win32-arm64-msvc@1.2.1: + resolution: {integrity: sha512-EvRrivJieyHG+AO9lleZWgq+g0+S7oV2C51yuqlcyU/R9net+sI4Pj0F+lUoP2bEr6TWX3SqFaaS0SzfLxSzkw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] @@ -17794,8 +17835,8 @@ packages: dev: true optional: true - /@rolldown/binding-win32-x64-msvc@1.1.5: - resolution: {integrity: sha512-tTZuDBPw85tEN5PQi1pnEBzDy0Z49HtScLAbD5t6hyeU92A95pRWaSMw1GZZi/RwgSgUIl0xrSlXIT/9QzvYSA==} + /@rolldown/binding-win32-x64-msvc@1.2.1: + resolution: {integrity: sha512-Z4eCmn5QJ/5+azF9knpLWKfVd9aidn0mAe9TpJgvBLId9Ax3t0+JVxBmT25Bv7NBbVW1TZyKjQjQReouMeH5UQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] @@ -19087,7 +19128,7 @@ packages: '@supabase/node-fetch': 2.6.15 '@types/phoenix': 1.6.6 '@types/ws': 8.18.1 - ws: 8.18.3 + ws: 8.21.1 transitivePeerDependencies: - bufferutil - utf-8-validate @@ -19099,7 +19140,7 @@ packages: '@supabase/node-fetch': 2.6.15 '@types/phoenix': 1.6.6 '@types/ws': 8.18.1 - ws: 8.18.3 + ws: 8.21.1 transitivePeerDependencies: - bufferutil - utf-8-validate @@ -20205,8 +20246,8 @@ packages: - supports-color dev: false - /@tenkicloud/sandbox@0.5.1: - resolution: {integrity: sha512-2yz11vnOcmka3+69Yw/yT/EhErjifNmvPxhgAI635mOStEDq0uNY14ibQpVl5r4brsTC/oSy4ZvntBt5cOzkOQ==} + /@tenkicloud/sandbox@0.5.4: + resolution: {integrity: sha512-U/oV8TAB1rjpXHuo9DIrpxnd2tlO2MojNjzLjqKgx4+zkD4NjgpR6QzRv5awoXib0DND3bnu0qoQCIgsfcXTCw==} engines: {node: '>=18'} dependencies: '@bufbuild/protobuf': 2.12.1 @@ -30206,12 +30247,12 @@ packages: engines: {node: '>=0.10.0'} dev: true - /isomorphic-ws@5.0.0(ws@8.18.3): + /isomorphic-ws@5.0.0(ws@8.21.1): resolution: {integrity: sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw==} peerDependencies: ws: '*' dependencies: - ws: 8.18.3 + ws: 8.21.1 dev: false /istanbul-lib-coverage@3.2.2: @@ -37621,7 +37662,7 @@ packages: resolution: {integrity: sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==} dev: false - /rolldown-plugin-dts@0.16.11(rolldown@1.1.5)(typescript@5.9.2): + /rolldown-plugin-dts@0.16.11(rolldown@1.2.1)(typescript@5.9.2): resolution: {integrity: sha512-9IQDaPvPqTx3RjG2eQCK5GYZITo203BxKunGI80AGYicu1ySFTUyugicAaTZWRzFWh9DSnzkgNeMNbDWBbSs0w==} engines: {node: '>=20.18.0'} peerDependencies: @@ -37649,36 +37690,36 @@ packages: dts-resolver: 2.1.2 get-tsconfig: 4.10.1 magic-string: 0.30.19 - rolldown: 1.1.5 + rolldown: 1.2.1 typescript: 5.9.2 transitivePeerDependencies: - oxc-resolver - supports-color dev: true - /rolldown@1.1.5: - resolution: {integrity: sha512-t9z29cJjXf/vxQ8dyhCSpt6H6aSwHTk8cT5I3iy6SMXuFpk5mB6PL6XfC8PCwrPTx93udwKUm9HRteAlTGBLiA==} + /rolldown@1.2.1: + resolution: {integrity: sha512-4FKJhg8d3OiyQOA6Q1Q0hoFFpW9/OoX+VsHzpECsdsIZoOArrAK90gl59YK/Z+gnDel45bgJZK03ozH/9bCqEw==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true dependencies: - '@oxc-project/types': 0.139.0 + '@oxc-project/types': 0.142.0 '@rolldown/pluginutils': 1.0.0 optionalDependencies: - '@rolldown/binding-android-arm64': 1.1.5 - '@rolldown/binding-darwin-arm64': 1.1.5 - '@rolldown/binding-darwin-x64': 1.1.5 - '@rolldown/binding-freebsd-x64': 1.1.5 - '@rolldown/binding-linux-arm-gnueabihf': 1.1.5 - '@rolldown/binding-linux-arm64-gnu': 1.1.5 - '@rolldown/binding-linux-arm64-musl': 1.1.5 - '@rolldown/binding-linux-ppc64-gnu': 1.1.5 - '@rolldown/binding-linux-s390x-gnu': 1.1.5 - '@rolldown/binding-linux-x64-gnu': 1.1.5 - '@rolldown/binding-linux-x64-musl': 1.1.5 - '@rolldown/binding-openharmony-arm64': 1.1.5 - '@rolldown/binding-wasm32-wasi': 1.1.5 - '@rolldown/binding-win32-arm64-msvc': 1.1.5 - '@rolldown/binding-win32-x64-msvc': 1.1.5 + '@rolldown/binding-android-arm64': 1.2.1 + '@rolldown/binding-darwin-arm64': 1.2.1 + '@rolldown/binding-darwin-x64': 1.2.1 + '@rolldown/binding-freebsd-x64': 1.2.1 + '@rolldown/binding-linux-arm-gnueabihf': 1.2.1 + '@rolldown/binding-linux-arm64-gnu': 1.2.1 + '@rolldown/binding-linux-arm64-musl': 1.2.1 + '@rolldown/binding-linux-ppc64-gnu': 1.2.1 + '@rolldown/binding-linux-s390x-gnu': 1.2.1 + '@rolldown/binding-linux-x64-gnu': 1.2.1 + '@rolldown/binding-linux-x64-musl': 1.2.1 + '@rolldown/binding-openharmony-arm64': 1.2.1 + '@rolldown/binding-wasm32-wasi': 1.2.1 + '@rolldown/binding-win32-arm64-msvc': 1.2.1 + '@rolldown/binding-win32-x64-msvc': 1.2.1 dev: true /rollup-plugin-inject@3.0.2: @@ -39865,8 +39906,8 @@ packages: empathic: 2.0.0 hookable: 5.5.3 publint: 0.3.12 - rolldown: 1.1.5 - rolldown-plugin-dts: 0.16.11(rolldown@1.1.5)(typescript@5.9.2) + rolldown: 1.2.1 + rolldown-plugin-dts: 0.16.11(rolldown@1.2.1)(typescript@5.9.2) semver: 7.7.2 tinyexec: 1.0.1 tinyglobby: 0.2.15 diff --git a/website/docs/workspaces/sandbox.md b/website/docs/workspaces/sandbox.md index 68234ab1f..318a760b4 100644 --- a/website/docs/workspaces/sandbox.md +++ b/website/docs/workspaces/sandbox.md @@ -445,7 +445,7 @@ const workspace = new Workspace({ }); ``` -The API key defaults to the `TENKI_API_KEY` environment variable when omitted. Ordinary workspace API keys infer their workspace scope server-side, so omit `workspaceId` for normal usage. If you use trusted service credentials that can access multiple workspaces, pass `workspaceId` to select one explicitly: +The API key defaults to the `TENKI_API_KEY` (or `TENKI_AUTH_TOKEN`) environment variable when omitted. Ordinary workspace API keys infer their workspace scope server-side, so omit `workspaceId` for normal usage. If you use trusted service credentials that can access multiple workspaces, pass `workspaceId` to select one explicitly: ```ts const sandbox = new TenkiSandbox({