@axiom-lattice/microsandbox-service 0.0.2 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +17 -17
- package/CHANGELOG.md +12 -0
- package/dist/{chunk-D5L33FAA.mjs → chunk-AWCII44X.mjs} +28 -11
- package/dist/chunk-AWCII44X.mjs.map +1 -0
- package/dist/{chunk-CSOLG3XA.mjs → chunk-UTK3UZSJ.mjs} +40 -23
- package/dist/chunk-UTK3UZSJ.mjs.map +1 -0
- package/dist/cli.js +65 -22
- package/dist/cli.js.map +1 -1
- package/dist/cli.mjs +2 -2
- package/dist/index.d.mts +2 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +27 -2
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/server.js +65 -22
- package/dist/server.js.map +1 -1
- package/dist/server.mjs +2 -2
- package/package.json +3 -3
- package/src/__tests__/app.test.ts +142 -0
- package/src/__tests__/index.test.ts +3 -2
- package/src/__tests__/server-cli.test.ts +205 -0
- package/src/app.ts +29 -1
- package/src/lib/server-cli.ts +9 -3
- package/src/server.ts +35 -18
- package/dist/chunk-CSOLG3XA.mjs.map +0 -1
- package/dist/chunk-D5L33FAA.mjs.map +0 -1
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
> @axiom-lattice/microsandbox-service@0.0.
|
|
2
|
+
> @axiom-lattice/microsandbox-service@0.0.4 build /home/runner/work/agentic/agentic/packages/microsandbox-service
|
|
3
3
|
> tsup src/index.ts src/server.ts src/cli.ts --format cjs,esm --dts --clean --sourcemap
|
|
4
4
|
|
|
5
5
|
[34mCLI[39m Building entry: src/cli.ts, src/index.ts, src/server.ts
|
|
@@ -12,26 +12,26 @@
|
|
|
12
12
|
[32mESM[39m [1mdist/cli.mjs [22m[32m277.00 B[39m
|
|
13
13
|
[32mESM[39m [1mdist/index.mjs [22m[32m206.00 B[39m
|
|
14
14
|
[32mESM[39m [1mdist/server.mjs [22m[32m146.00 B[39m
|
|
15
|
-
[32mESM[39m [1mdist/chunk-
|
|
16
|
-
[32mESM[39m [1mdist/chunk-
|
|
17
|
-
[32mESM[39m [1mdist/index.mjs.map [22m[32m71.00 B[39m
|
|
15
|
+
[32mESM[39m [1mdist/chunk-UTK3UZSJ.mjs [22m[32m2.38 KB[39m
|
|
16
|
+
[32mESM[39m [1mdist/chunk-AWCII44X.mjs [22m[32m22.00 KB[39m
|
|
18
17
|
[32mESM[39m [1mdist/cli.mjs.map [22m[32m389.00 B[39m
|
|
18
|
+
[32mESM[39m [1mdist/index.mjs.map [22m[32m71.00 B[39m
|
|
19
19
|
[32mESM[39m [1mdist/server.mjs.map [22m[32m71.00 B[39m
|
|
20
|
-
[32mESM[39m [1mdist/chunk-
|
|
21
|
-
[32mESM[39m [1mdist/chunk-
|
|
22
|
-
[32mESM[39m ⚡️ Build success in
|
|
23
|
-
[32mCJS[39m [1mdist/cli.js [22m[
|
|
24
|
-
[32mCJS[39m [1mdist/index.js [22m[32m24.
|
|
25
|
-
[32mCJS[39m [1mdist/server.js [22m[
|
|
26
|
-
[32mCJS[39m [1mdist/cli.js.map [22m[
|
|
27
|
-
[32mCJS[39m [1mdist/index.js.map [22m[
|
|
28
|
-
[32mCJS[39m [1mdist/server.js.map [22m[
|
|
29
|
-
[32mCJS[39m ⚡️ Build success in
|
|
20
|
+
[32mESM[39m [1mdist/chunk-UTK3UZSJ.mjs.map [22m[32m5.08 KB[39m
|
|
21
|
+
[32mESM[39m [1mdist/chunk-AWCII44X.mjs.map [22m[32m50.12 KB[39m
|
|
22
|
+
[32mESM[39m ⚡️ Build success in 211ms
|
|
23
|
+
[32mCJS[39m [1mdist/cli.js [22m[32m26.73 KB[39m
|
|
24
|
+
[32mCJS[39m [1mdist/index.js [22m[32m24.93 KB[39m
|
|
25
|
+
[32mCJS[39m [1mdist/server.js [22m[32m27.05 KB[39m
|
|
26
|
+
[32mCJS[39m [1mdist/cli.js.map [22m[32m55.76 KB[39m
|
|
27
|
+
[32mCJS[39m [1mdist/index.js.map [22m[32m50.68 KB[39m
|
|
28
|
+
[32mCJS[39m [1mdist/server.js.map [22m[32m55.49 KB[39m
|
|
29
|
+
[32mCJS[39m ⚡️ Build success in 241ms
|
|
30
30
|
[34mDTS[39m Build start
|
|
31
|
-
[32mDTS[39m ⚡️ Build success in
|
|
31
|
+
[32mDTS[39m ⚡️ Build success in 21870ms
|
|
32
32
|
[32mDTS[39m [1mdist/cli.d.ts [22m[32m20.00 B[39m
|
|
33
33
|
[32mDTS[39m [1mdist/server.d.ts [22m[32m281.00 B[39m
|
|
34
|
-
[32mDTS[39m [1mdist/index.d.ts [22m[32m10.
|
|
34
|
+
[32mDTS[39m [1mdist/index.d.ts [22m[32m10.24 KB[39m
|
|
35
35
|
[32mDTS[39m [1mdist/cli.d.mts [22m[32m20.00 B[39m
|
|
36
36
|
[32mDTS[39m [1mdist/server.d.mts [22m[32m281.00 B[39m
|
|
37
|
-
[32mDTS[39m [1mdist/index.d.mts [22m[32m10.
|
|
37
|
+
[32mDTS[39m [1mdist/index.d.mts [22m[32m10.24 KB[39m
|
package/CHANGELOG.md
CHANGED
|
@@ -1,10 +1,3 @@
|
|
|
1
|
-
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
2
|
-
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
3
|
-
}) : x)(function(x) {
|
|
4
|
-
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
5
|
-
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
6
|
-
});
|
|
7
|
-
|
|
8
1
|
// src/services/SandboxRegistry.ts
|
|
9
2
|
import { Sandbox } from "microsandbox";
|
|
10
3
|
var DEFAULT_IMAGE = process.env.MICROSANDBOX_IMAGE ?? "daytonaio/sandbox:0.6.0";
|
|
@@ -663,12 +656,37 @@ var ImageService = class {
|
|
|
663
656
|
// src/app.ts
|
|
664
657
|
function buildApp({
|
|
665
658
|
runtimeService = new MicrosandboxRuntimeService(),
|
|
666
|
-
imageService = new ImageService()
|
|
659
|
+
imageService = new ImageService(),
|
|
660
|
+
apiKey
|
|
667
661
|
} = {}) {
|
|
668
662
|
const app = fastify({ logger: false });
|
|
669
|
-
app.register(cors, {
|
|
663
|
+
app.register(cors, {
|
|
664
|
+
delegator: (request, callback) => {
|
|
665
|
+
callback(null, {
|
|
666
|
+
origin: true,
|
|
667
|
+
methods: request.headers["access-control-request-method"] ?? "*",
|
|
668
|
+
allowedHeaders: request.headers["access-control-request-headers"]
|
|
669
|
+
});
|
|
670
|
+
}
|
|
671
|
+
});
|
|
670
672
|
app.register(sensible);
|
|
671
673
|
app.register(multipart);
|
|
674
|
+
if (apiKey) {
|
|
675
|
+
app.addHook("onRequest", async (request, reply) => {
|
|
676
|
+
if (request.method === "OPTIONS" || !request.url.startsWith("/api/")) {
|
|
677
|
+
return;
|
|
678
|
+
}
|
|
679
|
+
if (request.headers.authorization !== `Bearer ${apiKey}`) {
|
|
680
|
+
await reply.code(401).send({
|
|
681
|
+
success: false,
|
|
682
|
+
error: {
|
|
683
|
+
code: "UNAUTHORIZED",
|
|
684
|
+
message: "Unauthorized"
|
|
685
|
+
}
|
|
686
|
+
});
|
|
687
|
+
}
|
|
688
|
+
});
|
|
689
|
+
}
|
|
672
690
|
registerHealthRoutes(app);
|
|
673
691
|
registerSandboxRoutes(app, runtimeService);
|
|
674
692
|
registerImageRoutes(app, imageService);
|
|
@@ -680,9 +698,8 @@ function buildApp({
|
|
|
680
698
|
}
|
|
681
699
|
|
|
682
700
|
export {
|
|
683
|
-
__require,
|
|
684
701
|
SandboxRegistry,
|
|
685
702
|
MicrosandboxRuntimeService,
|
|
686
703
|
buildApp
|
|
687
704
|
};
|
|
688
|
-
//# sourceMappingURL=chunk-
|
|
705
|
+
//# sourceMappingURL=chunk-AWCII44X.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/services/SandboxRegistry.ts","../src/services/MicrosandboxRuntimeService.ts","../src/services/SandboxMetadataStore.ts","../src/app.ts","../src/lib/errors.ts","../src/lib/http.ts","../src/routes/health.ts","../src/controllers/images.ts","../src/schemas/images.ts","../src/routes/images.ts","../src/schemas/sandbox.ts","../src/controllers/sandbox.ts","../src/routes/sandbox.ts","../src/services/ImageService.ts"],"sourcesContent":["import { Sandbox } from \"microsandbox\";\n\nconst DEFAULT_IMAGE = process.env.MICROSANDBOX_IMAGE ?? \"daytonaio/sandbox:0.6.0\";\n\nexport class SandboxRegistry {\n private handles = new Map<string, unknown>();\n private creating = new Map<string, Promise<unknown>>();\n\n async ensure(name: string, createOptions?: Record<string, unknown>): Promise<unknown> {\n const cached = this.handles.get(name);\n if (cached) {\n return cached;\n }\n\n const inflight = this.creating.get(name);\n if (inflight) {\n return inflight;\n }\n\n const creation = (async () => {\n let native: unknown;\n const createConfig = {\n name,\n image: DEFAULT_IMAGE,\n ...(createOptions ?? {}),\n };\n\n try {\n const handle = await Sandbox.get(name);\n\n if (handle.status === \"running\") {\n native = await handle.connect();\n } else if (handle.status === \"stopped\") {\n native = await Sandbox.start(name);\n } else if (handle.status === \"crashed\" || handle.status === \"draining\") {\n await Sandbox.remove(name);\n }\n } catch {\n native = undefined;\n }\n\n if (!native) {\n native = await Sandbox.createDetached(createConfig);\n }\n\n this.handles.set(name, native);\n return native;\n })();\n\n this.creating.set(name, creation);\n void creation.finally(() => this.creating.delete(name));\n\n return creation;\n }\n\n get(name: string): unknown {\n const handle = this.handles.get(name);\n\n if (!handle) {\n throw new Error(`Sandbox ${name} not found`);\n }\n\n return handle;\n }\n\n delete(name: string): void {\n this.handles.delete(name);\n }\n}\n","import { Mount, Sandbox } from \"microsandbox\";\nimport type { EnsureSandboxInput, ListSandboxesQuery, ShellExecInput } from \"../schemas/sandbox\";\nimport { SandboxRegistry } from \"./SandboxRegistry\";\nimport {\n SandboxMetadataStore,\n type SandboxMetadataRecord,\n type SandboxMetadataStoreLike,\n} from \"./SandboxMetadataStore\";\n\ntype VolumeConfig = NonNullable<EnsureSandboxInput[\"volumes\"]>[string];\n\ntype RuntimeNative = {\n fs(): {\n readString(path: string): Promise<string>;\n write(path: string, data: Buffer): Promise<void>;\n list(\n path: string,\n options?: { recursive?: boolean }\n ): Promise<Array<{ path: string; kind: string; size?: number; modified?: unknown }>>;\n read(path: string): Promise<Buffer | Uint8Array | string>;\n };\n execWithConfig(config: {\n cmd: string;\n args?: string[];\n cwd?: string;\n timeoutMs?: number;\n }): Promise<{\n stdout(): string;\n stderr?(): string;\n code?: number;\n }>;\n stop(): Promise<void>;\n kill(): Promise<void>;\n};\n\nexport class MicrosandboxRuntimeService {\n constructor(private deps: { registry?: SandboxRegistry; metadataStore?: SandboxMetadataStoreLike } = {}) {}\n\n private get registry(): SandboxRegistry {\n return this.deps.registry ?? (this.deps.registry = new SandboxRegistry());\n }\n\n private get metadataStore(): SandboxMetadataStoreLike {\n return this.deps.metadataStore ?? (this.deps.metadataStore = new SandboxMetadataStore());\n }\n\n private toListItem(record: SandboxMetadataRecord): {\n name: string;\n status: string;\n image?: string;\n cpus?: number;\n memoryMib?: number;\n envCount: number;\n volumeCount: number;\n createdAt: string;\n updatedAt: string;\n } {\n return {\n name: record.name,\n status: record.status,\n image: record.image,\n cpus: record.cpus,\n memoryMib: record.memoryMib,\n envCount: Object.keys(record.env).length,\n volumeCount: Object.keys(record.volumes).length,\n createdAt: record.createdAt,\n updatedAt: record.updatedAt,\n };\n }\n\n private buildVolumes(volumes?: EnsureSandboxInput[\"volumes\"]): Record<string, unknown> | undefined {\n if (!volumes) {\n return undefined;\n }\n\n return Object.fromEntries(\n Object.entries(volumes).map(([mountPath, definition]) => [mountPath, this.buildVolume(definition)])\n );\n }\n\n private buildVolume(definition: VolumeConfig): unknown {\n if (definition.type === \"bind\") {\n return Mount.bind(definition.source, { readonly: definition.readonly ?? false });\n }\n\n if (definition.type === \"named\") {\n return Mount.named(definition.name, { readonly: definition.readonly ?? false });\n }\n\n return Mount.tmpfs({ sizeMib: definition.sizeMib });\n }\n\n private getNative(name: string): RuntimeNative {\n return this.registry.get(name) as RuntimeNative;\n }\n\n private async getOrEnsureNative(name: string): Promise<RuntimeNative> {\n try {\n return this.getNative(name);\n } catch {\n return (await this.registry.ensure(name)) as RuntimeNative;\n }\n }\n\n async ensureSandbox(name: string, input: EnsureSandboxInput): Promise<{\n name: string;\n status: string;\n }> {\n const image = input.image ?? process.env.MICROSANDBOX_IMAGE ?? \"python:3.11-slim\";\n const cpus = input.cpus ?? Number(process.env.MICROSANDBOX_CPUS ?? \"1\");\n const memoryMib = input.memoryMib ?? Number(process.env.MICROSANDBOX_MEMORY ?? \"512\");\n\n await this.registry.ensure(name, {\n image,\n cpus,\n memoryMib,\n env: input.env,\n volumes: this.buildVolumes(input.volumes),\n });\n\n const existing = await this.metadataStore.get(name);\n const now = new Date().toISOString();\n\n await this.metadataStore.upsert({\n name,\n status: \"running\",\n image,\n cpus,\n memoryMib,\n env: input.env ?? {},\n volumes: input.volumes ?? {},\n createdAt: existing?.createdAt ?? now,\n updatedAt: now,\n });\n\n return {\n name,\n status: \"running\",\n };\n }\n\n async startSandbox(name: string): Promise<{ name: string; status: string }> {\n await Sandbox.start(name);\n this.registry.delete(name);\n await this.registry.ensure(name);\n await this.metadataStore.patchStatus(name, \"running\");\n return { name, status: \"running\" };\n }\n\n async stopSandbox(name: string): Promise<{ name: string; status: string }> {\n const native = await this.getOrEnsureNative(name);\n await native.stop();\n this.registry.delete(name);\n await this.metadataStore.patchStatus(name, \"stopped\");\n return { name, status: \"stopped\" };\n }\n\n async killSandbox(name: string): Promise<{ name: string; status: string }> {\n const native = await this.getOrEnsureNative(name);\n await native.kill();\n this.registry.delete(name);\n await this.metadataStore.patchStatus(name, \"unknown\");\n return { name, status: \"unknown\" };\n }\n\n async deleteSandbox(name: string): Promise<{ name: string; status: string }> {\n try {\n await this.getNative(name).kill();\n } catch {\n // Ignore missing cached native handle before remove.\n }\n\n await Sandbox.remove(name);\n this.registry.delete(name);\n await this.metadataStore.remove(name);\n\n return { name, status: \"unknown\" };\n }\n\n async listSandboxes(_query: ListSandboxesQuery): Promise<{\n items: Array<{\n name: string;\n status: string;\n image?: string;\n cpus?: number;\n memoryMib?: number;\n envCount: number;\n volumeCount: number;\n createdAt: string;\n updatedAt: string;\n }>;\n total: number;\n }> {\n const items = (await this.metadataStore.list())\n .filter((record) => {\n if (_query.status && record.status !== _query.status) {\n return false;\n }\n\n if (_query.image && record.image !== _query.image) {\n return false;\n }\n\n if (_query.search) {\n const haystack = [record.name, record.image, ...Object.keys(record.env)]\n .filter((value): value is string => Boolean(value))\n .join(\" \")\n .toLowerCase();\n\n if (!haystack.includes(_query.search.toLowerCase())) {\n return false;\n }\n }\n\n return true;\n })\n .map((record) => this.toListItem(record));\n\n return { items, total: items.length };\n }\n\n async getSandbox(name: string): Promise<SandboxMetadataRecord | undefined> {\n return await this.metadataStore.get(name);\n }\n\n async getStatus(name: string): Promise<{ name: string; status: string }> {\n try {\n this.getNative(name);\n return { name, status: \"running\" };\n } catch {\n return { name, status: \"unknown\" };\n }\n }\n\n async readFile(sandboxName: string, path: string): Promise<{ path: string; content: string }> {\n const content = await this.getNative(sandboxName).fs().readString(path);\n return { path, content };\n }\n\n async writeFile(sandboxName: string, path: string, content: string): Promise<{ path: string }> {\n await this.getNative(sandboxName).fs().write(path, Buffer.from(content));\n return { path };\n }\n\n async listPath(\n sandboxName: string,\n path: string,\n recursive?: boolean\n ): Promise<{ entries: Array<{ path: string; type: string }> }> {\n const entries = await this.getNative(sandboxName).fs().list(path, { recursive });\n return {\n entries: entries.map((entry) => ({\n path: entry.path,\n type: entry.kind,\n })),\n };\n }\n\n async findFiles(sandboxName: string, path: string, pattern: string): Promise<{ files: string[] }> {\n const output = await this.getNative(sandboxName).execWithConfig({\n cmd: \"find\",\n args: [path, \"-name\", pattern, \"-type\", \"f\"],\n });\n return { files: output.stdout().split(\"\\n\").filter(Boolean) };\n }\n\n async searchInFile(\n sandboxName: string,\n path: string,\n query: string\n ): Promise<{ matches: Array<{ line: number; content: string }> }> {\n let stdout: string;\n\n try {\n const output = await this.getNative(sandboxName).execWithConfig({\n cmd: \"grep\",\n args: [\"-n\", \"-E\", query, path],\n });\n stdout = output.stdout();\n } catch (error) {\n if (typeof error === \"object\" && error !== null && \"code\" in error && error.code === 1) {\n return { matches: [] };\n }\n\n throw error;\n }\n\n return {\n matches: stdout\n .split(\"\\n\")\n .filter(Boolean)\n .map((line) => {\n const separator = line.indexOf(\":\");\n return {\n line: Number(line.slice(0, separator)),\n content: line.slice(separator + 1),\n };\n }),\n };\n }\n\n async replaceInFile(\n sandboxName: string,\n input: { path: string; search: string; replace: string }\n ): Promise<{ replaced: number }> {\n const fs = this.getNative(sandboxName).fs();\n const original = await fs.readString(input.path);\n\n if (!input.search) {\n return { replaced: 0 };\n }\n\n const occurrences = original.split(input.search).length - 1;\n if (occurrences === 0) {\n return { replaced: 0 };\n }\n\n const updated = original.split(input.search).join(input.replace);\n await fs.write(input.path, Buffer.from(updated));\n\n return { replaced: occurrences };\n }\n\n async uploadFile(sandboxName: string, path: string, contentBase64: string): Promise<{ path: string }> {\n await this.getNative(sandboxName).fs().write(path, Buffer.from(contentBase64, \"base64\"));\n return { path };\n }\n\n async downloadFile(sandboxName: string, path: string): Promise<{ path: string; contentBase64: string }> {\n const data = await this.getNative(sandboxName).fs().read(path);\n const buffer = Buffer.isBuffer(data) ? data : Buffer.from(data);\n return { path, contentBase64: buffer.toString(\"base64\") };\n }\n\n async execCommand(input: ShellExecInput): Promise<{ stdout: string; stderr: string; exitCode: number }> {\n const output = await this.getNative(input.sandboxName).execWithConfig({\n cmd: \"sh\",\n args: [\"-c\", input.command],\n cwd: input.exec_dir,\n timeoutMs: input.timeout ? input.timeout * 1000 : undefined,\n });\n\n return {\n stdout: output.stdout(),\n stderr: output.stderr ? output.stderr() : \"\",\n exitCode: output.code ?? 0,\n };\n }\n}\n","import { mkdir, readFile, writeFile } from \"node:fs/promises\";\nimport { dirname } from \"node:path\";\nimport type { EnsureSandboxInput } from \"../schemas/sandbox\";\n\nexport type SandboxStatus = \"running\" | \"stopped\" | \"unknown\";\n\nexport type SandboxMetadataRecord = {\n name: string;\n status: SandboxStatus;\n image?: string;\n cpus?: number;\n memoryMib?: number;\n env: Record<string, string>;\n volumes: NonNullable<EnsureSandboxInput[\"volumes\"]>;\n createdAt: string;\n updatedAt: string;\n};\n\ntype SandboxMetadataFile = {\n sandboxes: SandboxMetadataRecord[];\n};\n\nfunction isSandboxMetadataFile(value: unknown): value is SandboxMetadataFile {\n return typeof value === \"object\" && value !== null && \"sandboxes\" in value && Array.isArray(value.sandboxes);\n}\n\nexport interface SandboxMetadataStoreLike {\n list(): Promise<SandboxMetadataRecord[]>;\n get(name: string): Promise<SandboxMetadataRecord | undefined>;\n upsert(record: SandboxMetadataRecord): Promise<SandboxMetadataRecord>;\n patchStatus(name: string, status: SandboxStatus): Promise<void>;\n remove(name: string): Promise<void>;\n}\n\nexport class SandboxMetadataStore implements SandboxMetadataStoreLike {\n constructor(private readonly options: { filePath?: string } = {}) {}\n\n private get filePath(): string {\n return this.options.filePath ?? process.env.MICROSANDBOX_METADATA_FILE ?? \".microsandbox/metadata.json\";\n }\n\n private async readAll(): Promise<SandboxMetadataFile> {\n try {\n const content = await readFile(this.filePath, \"utf8\");\n const parsed: unknown = JSON.parse(content);\n\n if (!isSandboxMetadataFile(parsed)) {\n throw new Error(\"Invalid sandbox metadata file\");\n }\n\n return parsed;\n } catch (error) {\n if (typeof error === \"object\" && error !== null && \"code\" in error && error.code === \"ENOENT\") {\n return { sandboxes: [] };\n }\n\n throw error;\n }\n }\n\n private async writeAll(file: SandboxMetadataFile): Promise<void> {\n await mkdir(dirname(this.filePath), { recursive: true });\n await writeFile(this.filePath, JSON.stringify(file, null, 2));\n }\n\n async list(): Promise<SandboxMetadataRecord[]> {\n const file = await this.readAll();\n return file.sandboxes.sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));\n }\n\n async get(name: string): Promise<SandboxMetadataRecord | undefined> {\n const file = await this.readAll();\n return file.sandboxes.find((entry) => entry.name === name);\n }\n\n async upsert(record: SandboxMetadataRecord): Promise<SandboxMetadataRecord> {\n const file = await this.readAll();\n const next = file.sandboxes.filter((entry) => entry.name !== record.name);\n next.push(record);\n await this.writeAll({ sandboxes: next });\n return record;\n }\n\n async patchStatus(name: string, status: SandboxStatus): Promise<void> {\n const current = await this.get(name);\n if (!current) {\n return;\n }\n\n await this.upsert({ ...current, status, updatedAt: new Date().toISOString() });\n }\n\n async remove(name: string): Promise<void> {\n const file = await this.readAll();\n await this.writeAll({ sandboxes: file.sandboxes.filter((entry) => entry.name !== name) });\n }\n}\n","import cors from \"@fastify/cors\";\nimport multipart from \"@fastify/multipart\";\nimport sensible from \"@fastify/sensible\";\nimport fastify, { type FastifyInstance } from \"fastify\";\nimport { toErrorResponse } from \"./lib/errors\";\nimport { registerHealthRoutes } from \"./routes/health\";\nimport { registerImageRoutes } from \"./routes/images\";\nimport { registerSandboxRoutes } from \"./routes/sandbox\";\nimport { ImageService } from \"./services/ImageService\";\nimport { MicrosandboxRuntimeService } from \"./services/MicrosandboxRuntimeService\";\nimport type { RuntimeService } from \"./types/runtime-service\";\n\nexport function buildApp({\n runtimeService = new MicrosandboxRuntimeService(),\n imageService = new ImageService(),\n apiKey,\n}: {\n runtimeService?: RuntimeService;\n imageService?: ImageService;\n apiKey?: string;\n} = {}): FastifyInstance {\n const app = fastify({ logger: false });\n\n app.register(cors, {\n delegator: (request, callback) => {\n callback(null, {\n origin: true,\n methods: request.headers[\"access-control-request-method\"] ?? \"*\",\n allowedHeaders: request.headers[\"access-control-request-headers\"],\n });\n },\n });\n app.register(sensible);\n app.register(multipart);\n\n if (apiKey) {\n app.addHook(\"onRequest\", async (request, reply) => {\n if (request.method === \"OPTIONS\" || !request.url.startsWith(\"/api/\")) {\n return;\n }\n\n if (request.headers.authorization !== `Bearer ${apiKey}`) {\n await reply.code(401).send({\n success: false,\n error: {\n code: \"UNAUTHORIZED\",\n message: \"Unauthorized\",\n },\n });\n }\n });\n }\n\n registerHealthRoutes(app);\n registerSandboxRoutes(app, runtimeService);\n registerImageRoutes(app, imageService);\n\n app.setErrorHandler((error, _request, reply) => {\n const { statusCode, body } = toErrorResponse(error);\n reply.status(statusCode).send(body);\n });\n\n return app;\n}\n","export class HttpError extends Error {\n constructor(\n public statusCode: number,\n public code: string,\n message: string\n ) {\n super(message);\n }\n}\n\nexport function toErrorResponse(error: unknown): {\n statusCode: number;\n body: {\n success: false;\n error: {\n code: string;\n message: string;\n };\n };\n} {\n if (error instanceof HttpError) {\n return {\n statusCode: error.statusCode,\n body: {\n success: false,\n error: {\n code: error.code,\n message: error.message,\n },\n },\n };\n }\n\n return {\n statusCode: 500,\n body: {\n success: false,\n error: {\n code: \"INTERNAL_ERROR\",\n message: error instanceof Error ? error.message : String(error),\n },\n },\n };\n}\n","export type SuccessResponse<T> = {\n success: true;\n data: T;\n};\n\nexport function ok<T>(data: T): SuccessResponse<T> {\n return {\n success: true,\n data,\n };\n}\n","import type { FastifyInstance } from \"fastify\";\nimport { ok } from \"../lib/http\";\n\nexport function registerHealthRoutes(app: FastifyInstance): void {\n app.get(\"/health\", async () => ok({ status: \"ok\" }));\n}\n","import type { FastifyReply, FastifyRequest } from \"fastify\";\nimport { ZodError } from \"zod\";\nimport { HttpError } from \"../lib/errors\";\nimport { ok } from \"../lib/http\";\nimport { ImageService } from \"../services/ImageService\";\nimport { imageRefQuerySchema, pullImageSchema } from \"../schemas/images\";\n\nfunction parseOrThrow<T>(parse: () => T): T {\n try {\n return parse();\n } catch (error) {\n if (error instanceof ZodError) {\n throw new HttpError(400, \"INVALID_REQUEST\", error.issues[0]?.message ?? \"Invalid request\");\n }\n\n throw error;\n }\n}\n\nexport function createImageController(imageService: ImageService) {\n return {\n listImages: async (_request: FastifyRequest, reply: FastifyReply) => {\n return reply.send(ok(await imageService.listImages()));\n },\n pullImage: async (request: FastifyRequest<{ Body: unknown }>, reply: FastifyReply) => {\n const body = parseOrThrow(() => pullImageSchema.parse(request.body ?? {}));\n return reply.send(ok(await imageService.pullImage(body)));\n },\n getImage: async (request: FastifyRequest<{ Querystring: unknown }>, reply: FastifyReply) => {\n const query = parseOrThrow(() => imageRefQuerySchema.parse(request.query ?? {}));\n return reply.send(ok(await imageService.getImage(query.ref)));\n },\n deleteImage: async (request: FastifyRequest<{ Querystring: unknown }>, reply: FastifyReply) => {\n const query = parseOrThrow(() => imageRefQuerySchema.parse(request.query ?? {}));\n return reply.send(ok(await imageService.deleteImage(query.ref)));\n },\n };\n}\n","import z from \"zod\";\n\nexport const imageRefQuerySchema = z.object({\n ref: z.string().min(1),\n});\n\nexport const pullImageSchema = z.object({\n ref: z.string().min(1),\n});\n\nexport type ImageRefQuery = z.infer<typeof imageRefQuerySchema>;\nexport type PullImageInput = z.infer<typeof pullImageSchema>;\n","import type { FastifyInstance } from \"fastify\";\nimport { createImageController } from \"../controllers/images\";\nimport { ImageService } from \"../services/ImageService\";\n\nexport function registerImageRoutes(app: FastifyInstance, imageService: ImageService): void {\n const controller = createImageController(imageService);\n\n app.get(\"/api/images\", controller.listImages);\n app.post(\"/api/images/pull\", controller.pullImage);\n app.get(\"/api/images/detail\", controller.getImage);\n app.delete(\"/api/images/detail\", controller.deleteImage);\n}\n","import z from \"zod\";\n\nconst bindMountSchema = z.object({\n type: z.literal(\"bind\"),\n source: z.string().min(1),\n readonly: z.boolean().optional(),\n});\n\nconst namedMountSchema = z.object({\n type: z.literal(\"named\"),\n name: z.string().min(1),\n readonly: z.boolean().optional(),\n});\n\nconst tmpfsMountSchema = z.object({\n type: z.literal(\"tmpfs\"),\n sizeMib: z.number().int().positive().optional(),\n});\n\nexport const volumeSchema = z.union([\n bindMountSchema,\n namedMountSchema,\n tmpfsMountSchema,\n]);\n\nexport const ensureSandboxSchema = z.object({\n image: z.string().optional(),\n cpus: z.number().int().positive().optional(),\n memoryMib: z.number().int().positive().optional(),\n env: z.record(z.string()).optional(),\n volumes: z.record(volumeSchema).optional(),\n});\n\nexport const sandboxNameParamsSchema = z.object({\n name: z.string().min(1),\n});\n\nexport const listSandboxesQuerySchema = z.object({\n status: z.enum([\"running\", \"stopped\", \"unknown\"]).optional(),\n image: z.string().min(1).optional(),\n search: z.string().min(1).optional(),\n});\n\nconst sandboxAndPathSchema = z.object({\n sandboxName: z.string().min(1),\n path: z.string().min(1),\n});\n\nexport const readFileSchema = sandboxAndPathSchema;\n\nexport const writeFileSchema = sandboxAndPathSchema.extend({\n content: z.string(),\n});\n\nexport const listPathSchema = sandboxAndPathSchema.extend({\n recursive: z.boolean().optional(),\n});\n\nexport const findFilesSchema = sandboxAndPathSchema.extend({\n pattern: z.string().min(1),\n});\n\nexport const searchInFileSchema = sandboxAndPathSchema.extend({\n query: z.string().min(1),\n});\n\nexport const replaceInFileSchema = sandboxAndPathSchema.extend({\n search: z.string(),\n replace: z.string(),\n});\n\nexport const uploadFileSchema = sandboxAndPathSchema.and(\n z.union([\n z.object({ contentBase64: z.string() }),\n z.object({ content: z.string().transform((content) => Buffer.from(content).toString(\"base64\")) }),\n ])\n);\n\nexport const downloadFileSchema = sandboxAndPathSchema;\n\nexport const shellExecSchema = z.object({\n sandboxName: z.string().min(1),\n command: z.string().min(1),\n exec_dir: z.string().optional(),\n timeout: z.number().int().positive().optional(),\n});\n\nexport type EnsureSandboxInput = z.infer<typeof ensureSandboxSchema>;\nexport type ListSandboxesQuery = z.infer<typeof listSandboxesQuerySchema>;\nexport type ShellExecInput = z.infer<typeof shellExecSchema>;\n","import type { FastifyReply, FastifyRequest } from \"fastify\";\nimport {\n downloadFileSchema,\n ensureSandboxSchema,\n findFilesSchema,\n listSandboxesQuerySchema,\n listPathSchema,\n readFileSchema,\n replaceInFileSchema,\n sandboxNameParamsSchema,\n searchInFileSchema,\n shellExecSchema,\n uploadFileSchema,\n writeFileSchema,\n} from \"../schemas/sandbox\";\nimport { HttpError } from \"../lib/errors\";\nimport { ok } from \"../lib/http\";\nimport type { RuntimeService } from \"../types/runtime-service\";\n\nexport function createSandboxController(runtimeService: RuntimeService): {\n ensureSandbox(\n request: FastifyRequest<{ Params: { name: string }; Body: unknown }>,\n reply: FastifyReply\n ): Promise<FastifyReply>;\n startSandbox(\n request: FastifyRequest<{ Params: { name: string } }>,\n reply: FastifyReply\n ): Promise<FastifyReply>;\n stopSandbox(\n request: FastifyRequest<{ Params: { name: string } }>,\n reply: FastifyReply\n ): Promise<FastifyReply>;\n killSandbox(\n request: FastifyRequest<{ Params: { name: string } }>,\n reply: FastifyReply\n ): Promise<FastifyReply>;\n deleteSandbox(\n request: FastifyRequest<{ Params: { name: string } }>,\n reply: FastifyReply\n ): Promise<FastifyReply>;\n listSandboxes(\n request: FastifyRequest<{ Querystring: unknown }>,\n reply: FastifyReply\n ): Promise<FastifyReply>;\n getSandbox(\n request: FastifyRequest<{ Params: { name: string } }>,\n reply: FastifyReply\n ): Promise<FastifyReply>;\n getStatus(\n request: FastifyRequest<{ Params: { name: string } }>,\n reply: FastifyReply\n ): Promise<FastifyReply>;\n readFile(request: FastifyRequest<{ Body: unknown }>, reply: FastifyReply): Promise<FastifyReply>;\n writeFile(request: FastifyRequest<{ Body: unknown }>, reply: FastifyReply): Promise<FastifyReply>;\n listPath(request: FastifyRequest<{ Body: unknown }>, reply: FastifyReply): Promise<FastifyReply>;\n findFiles(request: FastifyRequest<{ Body: unknown }>, reply: FastifyReply): Promise<FastifyReply>;\n searchInFile(request: FastifyRequest<{ Body: unknown }>, reply: FastifyReply): Promise<FastifyReply>;\n replaceInFile(request: FastifyRequest<{ Body: unknown }>, reply: FastifyReply): Promise<FastifyReply>;\n uploadFile(request: FastifyRequest<{ Body: unknown }>, reply: FastifyReply): Promise<FastifyReply>;\n downloadFile(\n request: FastifyRequest<{ Querystring: unknown }>,\n reply: FastifyReply\n ): Promise<FastifyReply>;\n execCommand(request: FastifyRequest<{ Body: unknown }>, reply: FastifyReply): Promise<FastifyReply>;\n} {\n return {\n ensureSandbox: async (\n request: FastifyRequest<{ Params: { name: string }; Body: unknown }>,\n reply: FastifyReply\n ) => {\n const body = ensureSandboxSchema.parse(request.body ?? {});\n const result = await runtimeService.ensureSandbox(request.params.name, body);\n return reply.send(ok(result));\n },\n\n startSandbox: async (request, reply) => {\n return reply.send(ok(await runtimeService.startSandbox(request.params.name)));\n },\n\n stopSandbox: async (request, reply) => {\n return reply.send(ok(await runtimeService.stopSandbox(request.params.name)));\n },\n\n killSandbox: async (request, reply) => {\n return reply.send(ok(await runtimeService.killSandbox(request.params.name)));\n },\n\n deleteSandbox: async (request, reply) => {\n return reply.send(ok(await runtimeService.deleteSandbox(request.params.name)));\n },\n\n listSandboxes: async (request, reply) => {\n const query = listSandboxesQuerySchema.parse(request.query ?? {});\n return reply.send(ok(await runtimeService.listSandboxes(query)));\n },\n\n getSandbox: async (request, reply) => {\n const params = sandboxNameParamsSchema.parse(request.params ?? {});\n const sandbox = await runtimeService.getSandbox(params.name);\n\n if (!sandbox) {\n throw new HttpError(404, \"SANDBOX_NOT_FOUND\", `Sandbox '${params.name}' not found`);\n }\n\n return reply.send(ok(sandbox));\n },\n\n getStatus: async (request, reply) => {\n return reply.send(ok(await runtimeService.getStatus(request.params.name)));\n },\n\n readFile: async (request, reply) => {\n const body = readFileSchema.parse(request.body ?? {});\n return reply.send(ok(await runtimeService.readFile(body.sandboxName, body.path)));\n },\n\n writeFile: async (request, reply) => {\n const body = writeFileSchema.parse(request.body ?? {});\n return reply.send(ok(await runtimeService.writeFile(body.sandboxName, body.path, body.content)));\n },\n\n listPath: async (request, reply) => {\n const body = listPathSchema.parse(request.body ?? {});\n return reply.send(ok(await runtimeService.listPath(body.sandboxName, body.path, body.recursive)));\n },\n\n findFiles: async (request, reply) => {\n const body = findFilesSchema.parse(request.body ?? {});\n return reply.send(ok(await runtimeService.findFiles(body.sandboxName, body.path, body.pattern)));\n },\n\n searchInFile: async (request, reply) => {\n const body = searchInFileSchema.parse(request.body ?? {});\n return reply.send(ok(await runtimeService.searchInFile(body.sandboxName, body.path, body.query)));\n },\n\n replaceInFile: async (request, reply) => {\n const body = replaceInFileSchema.parse(request.body ?? {});\n return reply.send(\n ok(\n await runtimeService.replaceInFile(body.sandboxName, {\n path: body.path,\n search: body.search,\n replace: body.replace,\n })\n )\n );\n },\n\n uploadFile: async (request, reply) => {\n const body = uploadFileSchema.parse(request.body ?? {});\n return reply.send(ok(await runtimeService.uploadFile(body.sandboxName, body.path, body.contentBase64)));\n },\n\n downloadFile: async (request, reply) => {\n const query = downloadFileSchema.parse(request.query ?? {});\n return reply.send(ok(await runtimeService.downloadFile(query.sandboxName, query.path)));\n },\n\n execCommand: async (request, reply) => {\n const body = shellExecSchema.parse(request.body ?? {});\n return reply.send(ok(await runtimeService.execCommand(body)));\n },\n };\n}\n","import type { FastifyInstance } from \"fastify\";\nimport { createSandboxController } from \"../controllers/sandbox\";\nimport type { RuntimeService } from \"../types/runtime-service\";\n\nexport function registerSandboxRoutes(\n app: FastifyInstance,\n runtimeService: RuntimeService\n): void {\n const controller = createSandboxController(runtimeService);\n\n app.get(\"/api/sandboxes\", controller.listSandboxes);\n app.get(\"/api/sandboxes/:name\", controller.getSandbox);\n app.put(\"/api/sandboxes/:name\", controller.ensureSandbox);\n app.post(\"/api/sandboxes/:name/start\", controller.startSandbox);\n app.post(\"/api/sandboxes/:name/stop\", controller.stopSandbox);\n app.post(\"/api/sandboxes/:name/kill\", controller.killSandbox);\n app.delete(\"/api/sandboxes/:name\", controller.deleteSandbox);\n app.get(\"/api/sandboxes/:name/status\", controller.getStatus);\n\n app.post(\"/api/files/read\", controller.readFile);\n app.post(\"/api/files/write\", controller.writeFile);\n app.post(\"/api/files/list\", controller.listPath);\n app.post(\"/api/files/find\", controller.findFiles);\n app.post(\"/api/files/search\", controller.searchInFile);\n app.post(\"/api/files/replace\", controller.replaceInFile);\n app.post(\"/api/files/upload\", controller.uploadFile);\n app.get(\"/api/files/download\", controller.downloadFile);\n\n app.post(\"/api/shell/exec\", controller.execCommand);\n}\n","import type { PullImageInput } from \"../schemas/images\";\n\nexport type ImageRecord = {\n ref: string;\n sourceType: string;\n cached: boolean;\n size?: number;\n createdAt?: string;\n lastUsedAt?: string;\n};\n\ntype ImageBindings = {\n list(): Promise<ImageRecord[]>;\n pull(ref: string): Promise<ImageRecord>;\n inspect(ref: string): Promise<ImageRecord>;\n remove(ref: string): Promise<void>;\n};\n\nexport class ImageService {\n constructor(\n private readonly deps: {\n bindings?: ImageBindings;\n } = {}\n ) {}\n\n private get bindings(): ImageBindings {\n return (\n this.deps.bindings ?? {\n list: async () => [],\n pull: async (ref: string) => ({ ref, sourceType: \"oci\", cached: true }),\n inspect: async (ref: string) => ({ ref, sourceType: \"oci\", cached: true }),\n remove: async () => undefined,\n }\n );\n }\n\n async listImages(): Promise<{ items: ImageRecord[]; total: number }> {\n const items = await this.bindings.list();\n return { items, total: items.length };\n }\n\n async pullImage(input: PullImageInput): Promise<ImageRecord> {\n return await this.bindings.pull(input.ref);\n }\n\n async getImage(ref: string): Promise<ImageRecord> {\n return await this.bindings.inspect(ref);\n }\n\n async deleteImage(ref: string): Promise<{ ref: string }> {\n await this.bindings.remove(ref);\n return { ref };\n }\n}\n"],"mappings":"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","names":["Sandbox","Sandbox","z"]}
|
|
@@ -1,23 +1,25 @@
|
|
|
1
1
|
import {
|
|
2
|
-
__require,
|
|
3
2
|
buildApp
|
|
4
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-AWCII44X.mjs";
|
|
5
4
|
|
|
6
5
|
// src/lib/server-cli.ts
|
|
7
6
|
function parsePort(raw, source) {
|
|
8
7
|
const value = Number(raw);
|
|
9
|
-
if (!Number.isInteger(value) || value <= 0) {
|
|
8
|
+
if (!Number.isInteger(value) || value <= 0 || value > 65535) {
|
|
10
9
|
throw new Error(`Invalid value for ${source}: ${raw}`);
|
|
11
10
|
}
|
|
12
11
|
return value;
|
|
13
12
|
}
|
|
13
|
+
function isMissingOptionValue(raw) {
|
|
14
|
+
return !raw || raw.startsWith("--");
|
|
15
|
+
}
|
|
14
16
|
function parseServerArgs(argv) {
|
|
15
17
|
const parsed = {};
|
|
16
18
|
for (let index = 0; index < argv.length; index += 1) {
|
|
17
19
|
const token = argv[index];
|
|
18
20
|
if (token === "--port") {
|
|
19
21
|
const raw = argv[index + 1];
|
|
20
|
-
if (
|
|
22
|
+
if (isMissingOptionValue(raw)) {
|
|
21
23
|
throw new Error("Missing value for --port");
|
|
22
24
|
}
|
|
23
25
|
parsed.port = parsePort(raw, "--port");
|
|
@@ -26,7 +28,7 @@ function parseServerArgs(argv) {
|
|
|
26
28
|
}
|
|
27
29
|
if (token === "--host") {
|
|
28
30
|
const raw = argv[index + 1];
|
|
29
|
-
if (
|
|
31
|
+
if (isMissingOptionValue(raw)) {
|
|
30
32
|
throw new Error("Missing value for --host");
|
|
31
33
|
}
|
|
32
34
|
parsed.host = raw;
|
|
@@ -41,7 +43,8 @@ function resolveServerConfig({
|
|
|
41
43
|
}) {
|
|
42
44
|
return {
|
|
43
45
|
host: args.host ?? env.HOST ?? "0.0.0.0",
|
|
44
|
-
port: args.port ?? (env.PORT ? parsePort(env.PORT, "PORT") : 4002)
|
|
46
|
+
port: args.port ?? (env.PORT ? parsePort(env.PORT, "PORT") : 4002),
|
|
47
|
+
apiKey: env.MICROSANDBOX_API_KEY
|
|
45
48
|
};
|
|
46
49
|
}
|
|
47
50
|
|
|
@@ -49,35 +52,49 @@ function resolveServerConfig({
|
|
|
49
52
|
async function startServer({
|
|
50
53
|
argv = process.argv.slice(2),
|
|
51
54
|
env = process.env,
|
|
52
|
-
app
|
|
55
|
+
app
|
|
53
56
|
} = {}) {
|
|
54
57
|
const config = resolveServerConfig({ args: parseServerArgs(argv), env });
|
|
58
|
+
const resolvedApp = app ?? buildApp({ apiKey: config.apiKey });
|
|
59
|
+
const onSigInt = () => {
|
|
60
|
+
void shutdown(0);
|
|
61
|
+
};
|
|
62
|
+
const onSigTerm = () => {
|
|
63
|
+
void shutdown(0);
|
|
64
|
+
};
|
|
65
|
+
const removeSignalHandlers = () => {
|
|
66
|
+
process.off("SIGINT", onSigInt);
|
|
67
|
+
process.off("SIGTERM", onSigTerm);
|
|
68
|
+
};
|
|
69
|
+
const close = async () => {
|
|
70
|
+
removeSignalHandlers();
|
|
71
|
+
return resolvedApp.close();
|
|
72
|
+
};
|
|
73
|
+
const startedApp = {
|
|
74
|
+
listen: resolvedApp.listen.bind(resolvedApp),
|
|
75
|
+
close
|
|
76
|
+
};
|
|
55
77
|
const shutdown = async (code) => {
|
|
56
78
|
try {
|
|
57
|
-
await
|
|
79
|
+
await close();
|
|
58
80
|
process.exit(code);
|
|
59
81
|
} catch (error) {
|
|
60
82
|
console.error("Failed to shutdown microsandbox-service cleanly", error);
|
|
61
83
|
process.exit(1);
|
|
62
84
|
}
|
|
63
85
|
};
|
|
64
|
-
process.once("SIGINT",
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
})
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
}
|
|
73
|
-
if (__require.main === module) {
|
|
74
|
-
void startServer().catch((error) => {
|
|
75
|
-
console.error("Failed to start microsandbox-service", error);
|
|
76
|
-
process.exit(1);
|
|
77
|
-
});
|
|
86
|
+
process.once("SIGINT", onSigInt);
|
|
87
|
+
process.once("SIGTERM", onSigTerm);
|
|
88
|
+
try {
|
|
89
|
+
await resolvedApp.listen({ port: config.port, host: config.host });
|
|
90
|
+
return startedApp;
|
|
91
|
+
} catch (error) {
|
|
92
|
+
removeSignalHandlers();
|
|
93
|
+
throw error;
|
|
94
|
+
}
|
|
78
95
|
}
|
|
79
96
|
|
|
80
97
|
export {
|
|
81
98
|
startServer
|
|
82
99
|
};
|
|
83
|
-
//# sourceMappingURL=chunk-
|
|
100
|
+
//# sourceMappingURL=chunk-UTK3UZSJ.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/lib/server-cli.ts","../src/server.ts"],"sourcesContent":["export interface ParsedServerArgs {\n host?: string;\n port?: number;\n}\n\nexport interface ResolvedServerConfig {\n host: string;\n port: number;\n apiKey?: string;\n}\n\nfunction parsePort(raw: string, source: string): number {\n const value = Number(raw);\n if (!Number.isInteger(value) || value <= 0 || value > 65535) {\n throw new Error(`Invalid value for ${source}: ${raw}`);\n }\n return value;\n}\n\nfunction isMissingOptionValue(raw: string | undefined): raw is undefined {\n return !raw || raw.startsWith(\"--\");\n}\n\nexport function parseServerArgs(argv: string[]): ParsedServerArgs {\n const parsed: ParsedServerArgs = {};\n\n for (let index = 0; index < argv.length; index += 1) {\n const token = argv[index];\n\n if (token === \"--port\") {\n const raw = argv[index + 1];\n if (isMissingOptionValue(raw)) {\n throw new Error(\"Missing value for --port\");\n }\n parsed.port = parsePort(raw, \"--port\");\n index += 1;\n continue;\n }\n\n if (token === \"--host\") {\n const raw = argv[index + 1];\n if (isMissingOptionValue(raw)) {\n throw new Error(\"Missing value for --host\");\n }\n parsed.host = raw;\n index += 1;\n }\n }\n\n return parsed;\n}\n\nexport function resolveServerConfig({\n args,\n env,\n}: {\n args: ParsedServerArgs;\n env: NodeJS.ProcessEnv;\n}): ResolvedServerConfig {\n return {\n host: args.host ?? env.HOST ?? \"0.0.0.0\",\n port: args.port ?? (env.PORT ? parsePort(env.PORT, \"PORT\") : 4002),\n apiKey: env.MICROSANDBOX_API_KEY,\n };\n}\n","import type { FastifyInstance } from \"fastify\";\nimport { buildApp } from \"./app\";\nimport { parseServerArgs, resolveServerConfig } from \"./lib/server-cli\";\n\nexport async function startServer({\n argv = process.argv.slice(2),\n env = process.env,\n app,\n}: {\n argv?: string[];\n env?: NodeJS.ProcessEnv;\n app?: Pick<FastifyInstance, \"listen\" | \"close\">;\n} = {}): Promise<Pick<FastifyInstance, \"listen\" | \"close\">> {\n const config = resolveServerConfig({ args: parseServerArgs(argv), env });\n const resolvedApp = app ?? buildApp({ apiKey: config.apiKey });\n\n const onSigInt = () => {\n void shutdown(0);\n };\n\n const onSigTerm = () => {\n void shutdown(0);\n };\n\n const removeSignalHandlers = (): void => {\n process.off(\"SIGINT\", onSigInt);\n process.off(\"SIGTERM\", onSigTerm);\n };\n\n const close = async (): Promise<undefined> => {\n removeSignalHandlers();\n return resolvedApp.close();\n };\n\n const startedApp: Pick<FastifyInstance, \"listen\" | \"close\"> = {\n listen: resolvedApp.listen.bind(resolvedApp),\n close: close as FastifyInstance[\"close\"],\n };\n\n const shutdown = async (code: number): Promise<void> => {\n try {\n await close();\n process.exit(code);\n } catch (error) {\n console.error(\"Failed to shutdown microsandbox-service cleanly\", error);\n process.exit(1);\n }\n };\n\n process.once(\"SIGINT\", onSigInt);\n process.once(\"SIGTERM\", onSigTerm);\n\n try {\n await resolvedApp.listen({ port: config.port, host: config.host });\n return startedApp;\n } catch (error) {\n removeSignalHandlers();\n throw error;\n }\n}\n"],"mappings":";;;;;AAWA,SAAS,UAAU,KAAa,QAAwB;AACtD,QAAM,QAAQ,OAAO,GAAG;AACxB,MAAI,CAAC,OAAO,UAAU,KAAK,KAAK,SAAS,KAAK,QAAQ,OAAO;AAC3D,UAAM,IAAI,MAAM,qBAAqB,MAAM,KAAK,GAAG,EAAE;AAAA,EACvD;AACA,SAAO;AACT;AAEA,SAAS,qBAAqB,KAA2C;AACvE,SAAO,CAAC,OAAO,IAAI,WAAW,IAAI;AACpC;AAEO,SAAS,gBAAgB,MAAkC;AAChE,QAAM,SAA2B,CAAC;AAElC,WAAS,QAAQ,GAAG,QAAQ,KAAK,QAAQ,SAAS,GAAG;AACnD,UAAM,QAAQ,KAAK,KAAK;AAExB,QAAI,UAAU,UAAU;AACtB,YAAM,MAAM,KAAK,QAAQ,CAAC;AAC1B,UAAI,qBAAqB,GAAG,GAAG;AAC7B,cAAM,IAAI,MAAM,0BAA0B;AAAA,MAC5C;AACA,aAAO,OAAO,UAAU,KAAK,QAAQ;AACrC,eAAS;AACT;AAAA,IACF;AAEA,QAAI,UAAU,UAAU;AACtB,YAAM,MAAM,KAAK,QAAQ,CAAC;AAC1B,UAAI,qBAAqB,GAAG,GAAG;AAC7B,cAAM,IAAI,MAAM,0BAA0B;AAAA,MAC5C;AACA,aAAO,OAAO;AACd,eAAS;AAAA,IACX;AAAA,EACF;AAEA,SAAO;AACT;AAEO,SAAS,oBAAoB;AAAA,EAClC;AAAA,EACA;AACF,GAGyB;AACvB,SAAO;AAAA,IACL,MAAM,KAAK,QAAQ,IAAI,QAAQ;AAAA,IAC/B,MAAM,KAAK,SAAS,IAAI,OAAO,UAAU,IAAI,MAAM,MAAM,IAAI;AAAA,IAC7D,QAAQ,IAAI;AAAA,EACd;AACF;;;AC5DA,eAAsB,YAAY;AAAA,EAChC,OAAO,QAAQ,KAAK,MAAM,CAAC;AAAA,EAC3B,MAAM,QAAQ;AAAA,EACd;AACF,IAII,CAAC,GAAuD;AAC1D,QAAM,SAAS,oBAAoB,EAAE,MAAM,gBAAgB,IAAI,GAAG,IAAI,CAAC;AACvE,QAAM,cAAc,OAAO,SAAS,EAAE,QAAQ,OAAO,OAAO,CAAC;AAE7D,QAAM,WAAW,MAAM;AACrB,SAAK,SAAS,CAAC;AAAA,EACjB;AAEA,QAAM,YAAY,MAAM;AACtB,SAAK,SAAS,CAAC;AAAA,EACjB;AAEA,QAAM,uBAAuB,MAAY;AACvC,YAAQ,IAAI,UAAU,QAAQ;AAC9B,YAAQ,IAAI,WAAW,SAAS;AAAA,EAClC;AAEA,QAAM,QAAQ,YAAgC;AAC5C,yBAAqB;AACrB,WAAO,YAAY,MAAM;AAAA,EAC3B;AAEA,QAAM,aAAwD;AAAA,IAC5D,QAAQ,YAAY,OAAO,KAAK,WAAW;AAAA,IAC3C;AAAA,EACF;AAEA,QAAM,WAAW,OAAO,SAAgC;AACtD,QAAI;AACF,YAAM,MAAM;AACZ,cAAQ,KAAK,IAAI;AAAA,IACnB,SAAS,OAAO;AACd,cAAQ,MAAM,mDAAmD,KAAK;AACtE,cAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF;AAEA,UAAQ,KAAK,UAAU,QAAQ;AAC/B,UAAQ,KAAK,WAAW,SAAS;AAEjC,MAAI;AACF,UAAM,YAAY,OAAO,EAAE,MAAM,OAAO,MAAM,MAAM,OAAO,KAAK,CAAC;AACjE,WAAO;AAAA,EACT,SAAS,OAAO;AACd,yBAAqB;AACrB,UAAM;AAAA,EACR;AACF;","names":[]}
|
package/dist/cli.js
CHANGED
|
@@ -681,12 +681,37 @@ var MicrosandboxRuntimeService = class {
|
|
|
681
681
|
// src/app.ts
|
|
682
682
|
function buildApp({
|
|
683
683
|
runtimeService = new MicrosandboxRuntimeService(),
|
|
684
|
-
imageService = new ImageService()
|
|
684
|
+
imageService = new ImageService(),
|
|
685
|
+
apiKey
|
|
685
686
|
} = {}) {
|
|
686
687
|
const app = (0, import_fastify.default)({ logger: false });
|
|
687
|
-
app.register(import_cors.default, {
|
|
688
|
+
app.register(import_cors.default, {
|
|
689
|
+
delegator: (request, callback) => {
|
|
690
|
+
callback(null, {
|
|
691
|
+
origin: true,
|
|
692
|
+
methods: request.headers["access-control-request-method"] ?? "*",
|
|
693
|
+
allowedHeaders: request.headers["access-control-request-headers"]
|
|
694
|
+
});
|
|
695
|
+
}
|
|
696
|
+
});
|
|
688
697
|
app.register(import_sensible.default);
|
|
689
698
|
app.register(import_multipart.default);
|
|
699
|
+
if (apiKey) {
|
|
700
|
+
app.addHook("onRequest", async (request, reply) => {
|
|
701
|
+
if (request.method === "OPTIONS" || !request.url.startsWith("/api/")) {
|
|
702
|
+
return;
|
|
703
|
+
}
|
|
704
|
+
if (request.headers.authorization !== `Bearer ${apiKey}`) {
|
|
705
|
+
await reply.code(401).send({
|
|
706
|
+
success: false,
|
|
707
|
+
error: {
|
|
708
|
+
code: "UNAUTHORIZED",
|
|
709
|
+
message: "Unauthorized"
|
|
710
|
+
}
|
|
711
|
+
});
|
|
712
|
+
}
|
|
713
|
+
});
|
|
714
|
+
}
|
|
690
715
|
registerHealthRoutes(app);
|
|
691
716
|
registerSandboxRoutes(app, runtimeService);
|
|
692
717
|
registerImageRoutes(app, imageService);
|
|
@@ -700,18 +725,21 @@ function buildApp({
|
|
|
700
725
|
// src/lib/server-cli.ts
|
|
701
726
|
function parsePort(raw, source) {
|
|
702
727
|
const value = Number(raw);
|
|
703
|
-
if (!Number.isInteger(value) || value <= 0) {
|
|
728
|
+
if (!Number.isInteger(value) || value <= 0 || value > 65535) {
|
|
704
729
|
throw new Error(`Invalid value for ${source}: ${raw}`);
|
|
705
730
|
}
|
|
706
731
|
return value;
|
|
707
732
|
}
|
|
733
|
+
function isMissingOptionValue(raw) {
|
|
734
|
+
return !raw || raw.startsWith("--");
|
|
735
|
+
}
|
|
708
736
|
function parseServerArgs(argv) {
|
|
709
737
|
const parsed = {};
|
|
710
738
|
for (let index = 0; index < argv.length; index += 1) {
|
|
711
739
|
const token = argv[index];
|
|
712
740
|
if (token === "--port") {
|
|
713
741
|
const raw = argv[index + 1];
|
|
714
|
-
if (
|
|
742
|
+
if (isMissingOptionValue(raw)) {
|
|
715
743
|
throw new Error("Missing value for --port");
|
|
716
744
|
}
|
|
717
745
|
parsed.port = parsePort(raw, "--port");
|
|
@@ -720,7 +748,7 @@ function parseServerArgs(argv) {
|
|
|
720
748
|
}
|
|
721
749
|
if (token === "--host") {
|
|
722
750
|
const raw = argv[index + 1];
|
|
723
|
-
if (
|
|
751
|
+
if (isMissingOptionValue(raw)) {
|
|
724
752
|
throw new Error("Missing value for --host");
|
|
725
753
|
}
|
|
726
754
|
parsed.host = raw;
|
|
@@ -735,7 +763,8 @@ function resolveServerConfig({
|
|
|
735
763
|
}) {
|
|
736
764
|
return {
|
|
737
765
|
host: args.host ?? env.HOST ?? "0.0.0.0",
|
|
738
|
-
port: args.port ?? (env.PORT ? parsePort(env.PORT, "PORT") : 4002)
|
|
766
|
+
port: args.port ?? (env.PORT ? parsePort(env.PORT, "PORT") : 4002),
|
|
767
|
+
apiKey: env.MICROSANDBOX_API_KEY
|
|
739
768
|
};
|
|
740
769
|
}
|
|
741
770
|
|
|
@@ -743,32 +772,46 @@ function resolveServerConfig({
|
|
|
743
772
|
async function startServer({
|
|
744
773
|
argv = process.argv.slice(2),
|
|
745
774
|
env = process.env,
|
|
746
|
-
app
|
|
775
|
+
app
|
|
747
776
|
} = {}) {
|
|
748
777
|
const config = resolveServerConfig({ args: parseServerArgs(argv), env });
|
|
778
|
+
const resolvedApp = app ?? buildApp({ apiKey: config.apiKey });
|
|
779
|
+
const onSigInt = () => {
|
|
780
|
+
void shutdown(0);
|
|
781
|
+
};
|
|
782
|
+
const onSigTerm = () => {
|
|
783
|
+
void shutdown(0);
|
|
784
|
+
};
|
|
785
|
+
const removeSignalHandlers = () => {
|
|
786
|
+
process.off("SIGINT", onSigInt);
|
|
787
|
+
process.off("SIGTERM", onSigTerm);
|
|
788
|
+
};
|
|
789
|
+
const close = async () => {
|
|
790
|
+
removeSignalHandlers();
|
|
791
|
+
return resolvedApp.close();
|
|
792
|
+
};
|
|
793
|
+
const startedApp = {
|
|
794
|
+
listen: resolvedApp.listen.bind(resolvedApp),
|
|
795
|
+
close
|
|
796
|
+
};
|
|
749
797
|
const shutdown = async (code) => {
|
|
750
798
|
try {
|
|
751
|
-
await
|
|
799
|
+
await close();
|
|
752
800
|
process.exit(code);
|
|
753
801
|
} catch (error) {
|
|
754
802
|
console.error("Failed to shutdown microsandbox-service cleanly", error);
|
|
755
803
|
process.exit(1);
|
|
756
804
|
}
|
|
757
805
|
};
|
|
758
|
-
process.once("SIGINT",
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
})
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
}
|
|
767
|
-
if (require.main === module) {
|
|
768
|
-
void startServer().catch((error) => {
|
|
769
|
-
console.error("Failed to start microsandbox-service", error);
|
|
770
|
-
process.exit(1);
|
|
771
|
-
});
|
|
806
|
+
process.once("SIGINT", onSigInt);
|
|
807
|
+
process.once("SIGTERM", onSigTerm);
|
|
808
|
+
try {
|
|
809
|
+
await resolvedApp.listen({ port: config.port, host: config.host });
|
|
810
|
+
return startedApp;
|
|
811
|
+
} catch (error) {
|
|
812
|
+
removeSignalHandlers();
|
|
813
|
+
throw error;
|
|
814
|
+
}
|
|
772
815
|
}
|
|
773
816
|
|
|
774
817
|
// src/cli.ts
|