@codeworksh/harness 0.2.0 → 0.3.0-dev.20261013131320

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.
@@ -1 +0,0 @@
1
- {"version":3,"file":"daytona-ByTHZelc.mjs","names":["make","SandboxIO","SandboxInstance","quoteArgv","quote","SandboxFileSystem","resolveCwd","ShellError","RemoteFileSystem.make","Shell","SandboxResource","EnvDaytona.SPILL_PATH","EnvDaytona.mountCwd","EnvDaytona.transport"],"sources":["../../src/sandboxes/daytona/fs.ts","../../src/sandboxes/daytona/provider.ts","../../src/sandboxes/daytona/index.ts"],"sourcesContent":["import { posix } from \"../../util/posix.ts\";\nimport { SandboxFileSystem } from \"../../sandbox/fs/filesystem.ts\";\n\nexport type FileStat = SandboxFileSystem.FileStat;\n\nexport interface Interface extends SandboxFileSystem.Provider {\n\treadonly lstat?: (path: string) => Promise<FileStat>;\n}\n\nexport interface Options {\n\treadonly cwd?: string;\n}\n\nconst resolvePath = (path: string, options?: Options) => {\n\tconst normalized = posix.normalize(path);\n\tif (options?.cwd === undefined || posix.isAbsolute(normalized)) return normalized;\n\treturn posix.normalize(posix.join(options.cwd, normalized));\n};\n\n/** Resolve provider paths without adding policy or mutable working-directory state. */\nexport const make = (provider: Interface, options?: Options): SandboxFileSystem.Provider => {\n\tconst resolve = (path: string) => resolvePath(path, options);\n\n\treturn {\n\t\treadFile: (path) => provider.readFile(resolve(path)),\n\t\treadFileBuffer: (path) => provider.readFileBuffer(resolve(path)),\n\t\treadBytes: (path, offset, length) => provider.readBytes(resolve(path), offset, length),\n\t\twriteFile: (path, content) => provider.writeFile(resolve(path), content),\n\t\tstat: (path) => provider.stat(resolve(path)),\n\t\t...(provider.lstat === undefined ? {} : { lstat: (path: string) => provider.lstat!(resolve(path)) }),\n\t\treaddir: (path) => provider.readdir(resolve(path)),\n\t\texists: (path) => provider.exists(resolve(path)),\n\t\tmkdir: (path, mkdirOptions) => provider.mkdir(resolve(path), mkdirOptions),\n\t\trm: (path, rmOptions) => provider.rm(resolve(path), rmOptions),\n\t\trealpath: (path) => provider.realpath(resolve(path)),\n\t\tscanLines: (path, scanOptions) => provider.scanLines(resolve(path), scanOptions),\n\t};\n};\n\nexport const withProvider = make;\n","/* oxlint-disable effecttsgo/async-function -- Daytona's SDK boundary is Promise-based. */\nimport { Context, DateTime, Effect, Layer, Option, Schema } from \"effect\";\nimport { Buffer } from \"node:buffer\";\nimport { posix } from \"../../util/posix.ts\";\nimport { sanitizeError } from \"../../sandbox/errors.ts\";\nimport { SandboxFileSystem } from \"../../sandbox/fs/filesystem.ts\";\nimport { SandboxInstance } from \"../../sandbox/instance.ts\";\nimport { SandboxIO } from \"../../sandbox/io.ts\";\nimport { SandboxResource } from \"../../sandbox/resource.ts\";\nimport { type ISandboxExe, quote, quoteArgv, resolveCwd, Shell, ShellError } from \"../../sandbox/shell/shell.ts\";\nimport * as RemoteFileSystem from \"./fs.ts\";\n\ntype CodeLanguage = import(\"@daytona/sdk\").CodeLanguage;\ntype Daytona = import(\"@daytona/sdk\").Daytona;\ntype FileInfo = import(\"@daytona/sdk\").FileInfo;\ntype Image = import(\"@daytona/sdk\").Image;\ntype RemoteSandbox = import(\"@daytona/sdk\").Sandbox;\ntype Resources = import(\"@daytona/sdk\").Resources;\n\n/** Fallback when Daytona cannot report a snapshot/image-specific work directory. */\nexport const DEFAULT_CWD = \"/home/daytona\";\n/** Where oversized tool output is written inside the sandbox. */\nexport const SPILL_PATH = \"/tmp\";\n\n/**\n * The mount cwd for a Daytona namespace.\n *\n * An absolute override replaces the namespace default outright, so the\n * `getWorkDir()` round-trip is skipped: it would discover a value we then throw\n * away. Otherwise the sandbox's own work directory is the default, and\n * {@link DEFAULT_CWD} covers a snapshot or image that reports none.\n *\n * Taken as a thunk rather than read off the sandbox so all three branches are\n * testable without provisioning one — this decides where every Daytona mount\n * roots, and §8.1 makes a wrong answer here resolve silently rather than fail.\n */\nexport const mountCwd = async (\n\tcwd: string | undefined,\n\tgetWorkDir: () => Promise<string | undefined>,\n): Promise<string> => {\n\tconst defaultCwd = posix.isAbsolute(cwd ?? \"\") ? DEFAULT_CWD : ((await getWorkDir()) ?? DEFAULT_CWD);\n\treturn SandboxIO.resolveMountCwd(defaultCwd, cwd);\n};\n\nexport class DaytonaError extends Schema.TaggedError<DaytonaError>()(\"DaytonaError\", {\n\tsanitized: SandboxInstance.PersistedError,\n}) {}\n\nexport interface Options {\n\t/** API key. Falls back to the `DAYTONA_API_KEY` env var when omitted. */\n\treadonly apiKey?: string | undefined;\n\t/** API URL. Falls back to `DAYTONA_API_URL` / the SDK default. */\n\treadonly apiUrl?: string | undefined;\n\t/** Target region. Falls back to `DAYTONA_TARGET` / the SDK default. */\n\treadonly target?: string | undefined;\n\t/** Reuse an existing sandbox by id or name instead of creating one. */\n\treadonly sandboxId?: string | undefined;\n\t/** Durable instance identity for this namespace. Supplied by the Controller. */\n\treadonly instanceId?: SandboxInstance.ID | undefined;\n\t/** Snapshot to create the sandbox from. */\n\treadonly snapshot?: string | undefined;\n\t/** Image (registry reference or declarative `Image`) to create the sandbox from. */\n\treadonly image?: string | Image | undefined;\n\t/** Runtime used for code execution. Defaults to `\"typescript\"`. */\n\treadonly language?: CodeLanguage | string | undefined;\n\t/** Environment variables baked into the sandbox. */\n\treadonly envVars?: Record<string, string> | undefined;\n\t/** Resource allocation (cpu / memory / disk). */\n\treadonly resources?: Resources | undefined;\n\t/** OS user to run as inside the sandbox. */\n\treadonly user?: string | undefined;\n\t/**\n\t * Mount working directory. Relative values resolve against `getWorkDir()`;\n\t * omitted values use it, with `/home/daytona` as the provider fallback.\n\t */\n\treadonly cwd?: string | undefined;\n\t/** Idle minutes before the sandbox auto-stops. */\n\treadonly autoStopInterval?: number | undefined;\n\t/** Per-command timeout in seconds. 0 means no timeout. */\n\treadonly execTimeout?: number | undefined;\n}\n\ninterface RemoteState {\n\treadonly sandbox: RemoteSandbox;\n\treadonly cwd: string;\n}\n\nclass Remote extends Context.Service<Remote, RemoteState>()(\"@codeworksh/harness/sandboxes/daytona/provider/Remote\") {}\n\nconst assertCommandSucceeded = (command: string, result: { exitCode: number; result?: string }) => {\n\tif (result.exitCode !== 0) throw new Error(result.result || `command failed (${result.exitCode}): ${command}`);\n};\n\nconst dateFrom = (value: string | undefined) => {\n\tif (value === undefined) return undefined;\n\treturn Option.getOrUndefined(DateTime.make(value).pipe(Option.map(DateTime.toDateUtc)));\n};\n\nexport const createSandbox = (daytona: Daytona, options: Options) => {\n\tconst base = {\n\t\tlanguage: options.language ?? \"typescript\",\n\t\t...(options.envVars === undefined ? {} : { envVars: options.envVars }),\n\t\t...(options.user === undefined ? {} : { user: options.user }),\n\t\t...(options.autoStopInterval === undefined ? {} : { autoStopInterval: options.autoStopInterval }),\n\t\tautoDeleteInterval: -1,\n\t};\n\treturn options.image !== undefined\n\t\t? daytona.create({\n\t\t\t\t...base,\n\t\t\t\timage: options.image,\n\t\t\t\t...(options.resources === undefined ? {} : { resources: options.resources }),\n\t\t\t})\n\t\t: daytona.create({ ...base, ...(options.snapshot === undefined ? {} : { snapshot: options.snapshot }) });\n};\n\nconst remote = (options: Options) =>\n\tLayer.effect(\n\t\tRemote,\n\t\tEffect.tryPromise({\n\t\t\ttry: async (): Promise<RemoteState> => {\n\t\t\t\tconst { Daytona } = await import(\"@daytona/sdk\");\n\t\t\t\tconst daytona = new Daytona({\n\t\t\t\t\t...(options.apiKey === undefined ? {} : { apiKey: options.apiKey }),\n\t\t\t\t\t...(options.apiUrl === undefined ? {} : { apiUrl: options.apiUrl }),\n\t\t\t\t\t...(options.target === undefined ? {} : { target: options.target }),\n\t\t\t\t});\n\t\t\t\tconst sandbox = options.sandboxId\n\t\t\t\t\t? await daytona.get(options.sandboxId)\n\t\t\t\t\t: await createSandbox(daytona, options);\n\t\t\t\treturn {\n\t\t\t\t\tsandbox,\n\t\t\t\t\tcwd: await mountCwd(options.cwd, () => sandbox.getWorkDir()),\n\t\t\t\t};\n\t\t\t},\n\t\t\tcatch: (cause) => new DaytonaError({ sanitized: sanitizeError(cause) }),\n\t\t}),\n\t);\n\nexport const statsFrom = (info: FileInfo): RemoteFileSystem.FileStat => {\n\tconst symlink = info.mode === undefined ? undefined : info.mode.startsWith(\"l\");\n\tconst mtime = dateFrom(info.modifiedAt ?? info.modTime);\n\n\t// omit size/mtime/isSymbolicLink the toolbox did not report — never fabricate\n\treturn {\n\t\tisFile: !info.isDir && symlink !== true,\n\t\tisDirectory: info.isDir,\n\t\t...(symlink === undefined ? {} : { isSymbolicLink: symlink }),\n\t\t...(info.size === undefined ? {} : { size: info.size }),\n\t\t...(mtime === undefined ? {} : { mtime }),\n\t};\n};\n\ntype RemoteFilesystemProvider = Pick<\n\tRemoteFileSystem.Interface,\n\t| \"readFile\"\n\t| \"readFileBuffer\"\n\t| \"readBytes\"\n\t| \"writeFile\"\n\t| \"stat\"\n\t| \"lstat\"\n\t| \"readdir\"\n\t| \"exists\"\n\t| \"mkdir\"\n\t| \"rm\"\n\t| \"realpath\"\n\t| \"scanLines\"\n>;\n\nconst providerFrom = (sandbox: RemoteSandbox, options: Options) => {\n\tconst run = async (argv: ReadonlyArray<string>) => {\n\t\tconst result = await sandbox.process.executeCommand(quoteArgv(argv), options.cwd, undefined, options.execTimeout);\n\t\treturn { exitCode: result.exitCode, stdout: result.result ?? \"\" };\n\t};\n\tconst filesystem: RemoteFilesystemProvider = {\n\t\treadFile: async (path: string) => (await sandbox.fs.downloadFile(path)).toString(\"utf8\"),\n\t\treadFileBuffer: async (path: string) => new Uint8Array(await sandbox.fs.downloadFile(path)),\n\t\twriteFile: (path: string, content: string | Uint8Array) =>\n\t\t\tsandbox.fs.uploadFile(typeof content === \"string\" ? Buffer.from(content, \"utf8\") : Buffer.from(content), path),\n\t\tstat: async (path: string) => statsFrom(await sandbox.fs.getFileDetails(path)),\n\t\t// The toolbox file-details endpoint follows symlinks. Detect the entry\n\t\t// with the sandbox shell first so lstat never reports target metadata as\n\t\t// if it described the link itself.\n\t\tlstat: async (path: string) => {\n\t\t\tconst command = `test -L ${quote(path)}`;\n\t\t\tconst result = await sandbox.process.executeCommand(command, options.cwd, undefined, options.execTimeout);\n\t\t\tif (result.exitCode === 0) {\n\t\t\t\treturn { isFile: false, isDirectory: false, isSymbolicLink: true };\n\t\t\t}\n\t\t\tif (result.exitCode === 1) return statsFrom(await sandbox.fs.getFileDetails(path));\n\t\t\tassertCommandSucceeded(command, result);\n\t\t\tthrow new Error(`unreachable lstat result for ${path}`);\n\t\t},\n\t\treaddir: async (path: string) => (await sandbox.fs.listFiles(path)).map((entry) => entry.name),\n\t\t// Only a genuine 404 means \"absent\". Auth, rate-limit, and transport\n\t\t// failures propagate: a caller that deletes records on absence must not\n\t\t// be told a path is gone because the API was briefly unreachable.\n\t\texists: async (path: string) => {\n\t\t\ttry {\n\t\t\t\tawait sandbox.fs.getFileDetails(path);\n\t\t\t\treturn true;\n\t\t\t} catch (cause) {\n\t\t\t\tconst { DaytonaNotFoundError } = await import(\"@daytona/sdk\");\n\t\t\t\tif (cause instanceof DaytonaNotFoundError) return false;\n\t\t\t\tthrow cause;\n\t\t\t}\n\t\t},\n\t\tmkdir: async (path: string, mkdirOptions?: { recursive?: boolean }) => {\n\t\t\tif (!mkdirOptions?.recursive) {\n\t\t\t\tawait sandbox.fs.createFolder(path, \"755\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst command = `mkdir -p ${quote(path)}`;\n\t\t\tconst result = await sandbox.process.executeCommand(command, options.cwd, undefined, options.execTimeout);\n\t\t\tassertCommandSucceeded(command, result);\n\t\t},\n\t\trm: async (path: string, rmOptions?: { recursive?: boolean; force?: boolean }) => {\n\t\t\tif (rmOptions?.force && !(await filesystem.exists(path))) return;\n\t\t\ttry {\n\t\t\t\tawait sandbox.fs.deleteFile(path, rmOptions?.recursive);\n\t\t\t} catch (cause) {\n\t\t\t\tif (rmOptions?.force && !(await filesystem.exists(path))) return;\n\t\t\t\tthrow cause;\n\t\t\t}\n\t\t},\n\t\trealpath: async (path: string) => {\n\t\t\tfor (const script of SandboxFileSystem.realpathScripts) {\n\t\t\t\tconst command = quoteArgv([\"sh\", \"-c\", script, \"_\", path]);\n\t\t\t\tconst result = await sandbox.process.executeCommand(command, options.cwd, undefined, options.execTimeout);\n\t\t\t\tif (result.exitCode === 0) return (result.result ?? \"\").trimEnd();\n\t\t\t}\n\t\t\t// same shape `fs.stat` rejects with, so `isNotFoundError` recognises it\n\t\t\tthrow Object.assign(new Error(`ENOENT: no such file or directory, realpath '${path}'`), { code: \"ENOENT\" });\n\t\t},\n\t\tscanLines: SandboxFileSystem.Scan.viaShell(run),\n\t\treadBytes: SandboxFileSystem.readBytesViaShell(run),\n\t};\n\n\treturn filesystem;\n};\n\n// Daytona's execute API folds stderr into `result` and reports a single exit\n// code, so the shell surfaces the combined output as stdout and leaves stderr\n// empty rather than inventing a split.\nconst runCommand = (\n\tsandbox: RemoteSandbox,\n\toptions: Options,\n\tcommand: string,\n\topts?: { env?: Record<string, string>; cwd?: string },\n) =>\n\tEffect.tryPromise({\n\t\ttry: () =>\n\t\t\tsandbox.process.executeCommand(command, resolveCwd(options.cwd, opts?.cwd), opts?.env, options.execTimeout),\n\t\tcatch: (cause) => new ShellError({ command, cause }),\n\t}).pipe(Effect.map((response) => ({ stdout: response.result ?? \"\", stderr: \"\", exitCode: response.exitCode })));\n\nconst exec =\n\t(sandbox: RemoteSandbox, options: Options): ISandboxExe[\"exec\"] =>\n\t(command, opts) =>\n\t\trunCommand(sandbox, options, command, opts);\n\n// `executeCommand` takes a single string, so the vector is quoted here rather\n// than spawned; the per-call cwd rides the toolbox's own cwd argument instead\n// of a `cd` prefix.\nconst execArgv =\n\t(sandbox: RemoteSandbox, options: Options): ISandboxExe[\"execArgv\"] =>\n\t(argv, opts) =>\n\t\trunCommand(sandbox, options, quoteArgv(argv), opts);\n\nconst filesystemLayer = (options: Options) =>\n\tLayer.effect(\n\t\tSandboxFileSystem.Service,\n\t\tEffect.map(Remote, ({ sandbox, cwd }) => {\n\t\t\tconst mounted = { ...options, cwd };\n\t\t\treturn SandboxFileSystem.fromProvider(RemoteFileSystem.make(providerFrom(sandbox, mounted), { cwd }));\n\t\t}),\n\t);\n\nconst shellLayer = (options: Options) =>\n\tLayer.effect(\n\t\tShell,\n\t\tEffect.map(Remote, ({ sandbox, cwd }) => {\n\t\t\tconst mounted = { ...options, cwd };\n\t\t\treturn Shell.of({ exec: exec(sandbox, mounted), execArgv: execArgv(sandbox, mounted) });\n\t\t}),\n\t);\n\n/**\n * Cwd-neutral IO attachment for a lifecycle driver.\n *\n * Mount wrappers supply an absolute cwd to every public operation. Internal\n * filesystem helper commands already receive absolute paths, so the transport\n * itself keeps no mutable working-directory state and owns no resource\n * finalizer.\n */\nexport const transport = (\n\tsandbox: RemoteSandbox,\n\toptions: Pick<Options, \"execTimeout\"> = {},\n): Layer.Layer<SandboxFileSystem.Service | Shell> =>\n\tLayer.merge(\n\t\tLayer.succeed(\n\t\t\tSandboxFileSystem.Service,\n\t\t\tSandboxFileSystem.fromProvider(RemoteFileSystem.make(providerFrom(sandbox, options))),\n\t\t),\n\t\tLayer.succeed(\n\t\t\tShell,\n\t\t\tShell.of({\n\t\t\t\texec: exec(sandbox, options),\n\t\t\t\texecArgv: execArgv(sandbox, options),\n\t\t\t}),\n\t\t),\n\t);\n\n// Daytona's locator is the sandbox id. See `SandboxResource` for why this is a\n// shared tag rather than a Daytona-specific one.\nconst resourceLayer = Layer.effect(\n\tSandboxResource.Service,\n\tEffect.map(Remote, ({ sandbox }) => ({ providerResourceId: sandbox.id })),\n);\n\n// Identity is per remote sandbox, not per provider: two sandboxes both rooted at\n// the same directory must not share persisted directory records.\n//\n// The id is minted here only when the caller names none. A durable id is the\n// control plane's to mint and record — deriving one from the provider's own\n// locator is what §6.1 forbids — so a caller that needs the namespace to survive\n// a restart passes `instanceId` rather than relying on this.\nconst identityLayer = (options: Options) =>\n\tLayer.effect(\n\t\tSandboxIO.Current,\n\t\tEffect.map(Remote, ({ cwd }) =>\n\t\t\tSandboxIO.remote({\n\t\t\t\tdriver: \"daytona\",\n\t\t\t\tid: options.instanceId ?? SandboxInstance.ID.create(),\n\t\t\t\tdefaultCwd: cwd,\n\t\t\t\tspillPath: SPILL_PATH,\n\t\t\t}),\n\t\t),\n\t);\n\n/**\n * A Daytona sandbox provides the runtime filesystem service directly plus\n * the sandbox's native remote shell. It intentionally does not provide VFS:\n * remote filesystems have no synchronous filesystem surface.\n */\nexport const layer = (options: Options = {}): Layer.Layer<SandboxIO.Provides | SandboxResource.Service, DaytonaError> =>\n\tLayer.mergeAll(\n\t\tSandboxIO.withMutation(Layer.mergeAll(filesystemLayer(options), shellLayer(options), identityLayer(options))),\n\t\tresourceLayer,\n\t).pipe(Layer.provide(remote(options)));\n\nexport const services = layer;\n","import { Effect, Layer, Option, Schema } from \"effect\";\nimport { SandboxDriver, SandboxInstance, SandboxProvider } from \"../../sandbox.ts\";\nimport * as EnvDaytona from \"./provider.ts\";\n\nexport const Options = Schema.Struct({\n\tapiKey: Schema.optional(Schema.String),\n\tapiUrl: Schema.optional(Schema.String),\n\ttarget: Schema.optional(Schema.String),\n});\nexport type Options = typeof Options.Type;\nexport const ClientOptions = Options;\nexport type ClientOptions = Options;\n\nexport const ResourcesConfig = Schema.Struct({\n\tcpu: Schema.optional(Schema.Finite),\n\tgpu: Schema.optional(Schema.Finite),\n\tmemory: Schema.optional(Schema.Finite),\n\tdisk: Schema.optional(Schema.Finite),\n});\nexport type ResourcesConfig = typeof ResourcesConfig.Type;\n\nexport const CreateConfig = Schema.Struct({\n\tsnapshot: Schema.optional(Schema.String),\n\timage: Schema.optional(Schema.String),\n\tlanguage: Schema.optional(Schema.String),\n\tenvVars: Schema.optional(Schema.Record(Schema.String, Schema.String)),\n\tresources: Schema.optional(ResourcesConfig),\n\tuser: Schema.optional(Schema.String),\n\tcwd: Schema.optional(Schema.String),\n\tautoStopInterval: Schema.optional(Schema.Finite),\n\texecTimeout: Schema.optional(Schema.Finite),\n});\nexport type CreateConfig = typeof CreateConfig.Type;\n\nexport const RuntimeConfig = Schema.Struct({\n\tdefaultCwd: SandboxDriver.AbsolutePath,\n\tspillPath: Schema.optional(SandboxDriver.AbsolutePath),\n\tuser: Schema.optional(Schema.String),\n\texecTimeout: Schema.optional(Schema.Finite),\n});\nexport type RuntimeConfig = typeof RuntimeConfig.Type;\n\nconst name = SandboxDriver.Name.make(\"daytona\");\ntype DaytonaSdk = typeof import(\"@daytona/sdk\");\ntype RemoteSandbox = import(\"@daytona/sdk\").Sandbox;\ntype Resources = import(\"@daytona/sdk\").Resources;\n\nconst statusFrom = (\n\tstate: RemoteSandbox[\"state\"],\n): {\n\treadonly status: SandboxInstance.Status;\n\treadonly providerStatus: string;\n} => {\n\tconst providerStatus = state ?? \"unknown\";\n\treturn {\n\t\tstatus:\n\t\t\tstate === \"stopped\" || state === \"archived\"\n\t\t\t\t? \"offline\"\n\t\t\t\t: state === \"stopping\" || state === \"archiving\" || state === \"snapshotting\" || state === \"destroying\"\n\t\t\t\t\t? \"suspending\"\n\t\t\t\t\t: state === \"destroyed\"\n\t\t\t\t\t\t? \"unavail\"\n\t\t\t\t\t\t: state === \"error\" || state === \"build_failed\" || state === \"unknown\"\n\t\t\t\t\t\t\t? \"faulted\"\n\t\t\t\t\t\t\t: \"online\",\n\t\tproviderStatus,\n\t};\n};\n\nconst shouldWake = (sandbox: RemoteSandbox): boolean => sandbox.state === \"stopped\" || sandbox.state === \"archived\";\n\nexport const make = (\n\tclient: ClientOptions = {},\n): SandboxDriver.Driver<CreateConfig, RuntimeConfig> & SandboxDriver.Registration => {\n\tconst redact = SandboxProvider.makeRedactor([client.apiKey ?? \"\"]);\n\tconst daytona = (sdk: DaytonaSdk) =>\n\t\tnew sdk.Daytona({\n\t\t\t...(client.apiKey === undefined ? {} : { apiKey: client.apiKey }),\n\t\t\t...(client.apiUrl === undefined ? {} : { apiUrl: client.apiUrl }),\n\t\t\t...(client.target === undefined ? {} : { target: client.target }),\n\t\t});\n\n\tconst attempt = <A>(\n\t\toperation: string,\n\t\trun: (sdk: DaytonaSdk) => Promise<A>,\n\t): Effect.Effect<A, SandboxProvider.SandboxProviderError> =>\n\t\tEffect.suspend(() => {\n\t\t\tlet sdk: DaytonaSdk | undefined;\n\t\t\treturn Effect.tryPromise({\n\t\t\t\ttry: () =>\n\t\t\t\t\timport(\"@daytona/sdk\").then((loaded) => {\n\t\t\t\t\t\tsdk = loaded;\n\t\t\t\t\t\treturn run(loaded);\n\t\t\t\t\t}),\n\t\t\t\tcatch: (cause) =>\n\t\t\t\t\tSandboxProvider.providerError({\n\t\t\t\t\t\tdriver: name,\n\t\t\t\t\t\toperation,\n\t\t\t\t\t\tcause,\n\t\t\t\t\t\tredact,\n\t\t\t\t\t\tnotFound: sdk !== undefined && cause instanceof sdk.DaytonaNotFoundError,\n\t\t\t\t\t}),\n\t\t\t});\n\t\t});\n\n\tconst get = (providerResourceId: string, operation: string) =>\n\t\tattempt(operation, (sdk) => daytona(sdk).get(providerResourceId));\n\n\tconst refresh = (sandbox: RemoteSandbox, operation: string) =>\n\t\tattempt(operation, () => sandbox.refreshData()).pipe(Effect.as(sandbox));\n\n\tconst observed = (sandbox: RemoteSandbox): SandboxDriver.Observed => ({\n\t\t...statusFrom(sandbox.state),\n\t\tmetadata: {\n\t\t\ttarget: sandbox.target,\n\t\t},\n\t});\n\n\tconst wake = (sandbox: RemoteSandbox, operation: string) =>\n\t\tshouldWake(sandbox)\n\t\t\t? attempt(operation, () => sandbox.start()).pipe(Effect.map(() => sandbox))\n\t\t\t: Effect.succeed(sandbox);\n\n\tconst runtime = (\n\t\tdefaultCwd: string,\n\t\tinput: { readonly user?: string | undefined; readonly execTimeout?: number | undefined },\n\t) => ({\n\t\tdefaultCwd: SandboxDriver.AbsolutePath.make(defaultCwd),\n\t\tspillPath: SandboxDriver.AbsolutePath.make(EnvDaytona.SPILL_PATH),\n\t\t...(input.user === undefined ? {} : { user: input.user }),\n\t\t...(input.execTimeout === undefined ? {} : { execTimeout: input.execTimeout }),\n\t});\n\n\treturn SandboxDriver.driver({\n\t\tname,\n\t\tkind: \"remote\",\n\t\tcapabilities: {\n\t\t\tinspect: true,\n\t\t\treattach: true,\n\t\t\twake: true,\n\t\t\tstop: true,\n\t\t\tdestroy: true,\n\t\t\t// Installed SDK 0.187.0 has no cancellation signal on\n\t\t\t// executeCommand; session execution cannot carry cwd/env safely.\n\t\t\tcancels: false,\n\t\t},\n\t\tcreateConfigCodec: CreateConfig,\n\t\truntimeConfigCodec: RuntimeConfig,\n\t\tcreate: ({ instanceId, config }) =>\n\t\t\tEffect.gen(function* () {\n\t\t\t\tconst base = {\n\t\t\t\t\tlanguage: config.language ?? \"typescript\",\n\t\t\t\t\t...(config.envVars === undefined ? {} : { envVars: config.envVars }),\n\t\t\t\t\t...(config.user === undefined ? {} : { user: config.user }),\n\t\t\t\t\t...(config.autoStopInterval === undefined ? {} : { autoStopInterval: config.autoStopInterval }),\n\t\t\t\t\tautoDeleteInterval: -1,\n\t\t\t\t\tlabels: {\n\t\t\t\t\t\t\"codework-instance\": instanceId,\n\t\t\t\t\t\t\"codework-managed\": \"true\",\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t\tconst sandbox = yield* attempt(\"create\", (loaded) => {\n\t\t\t\t\tconst sdk = daytona(loaded);\n\t\t\t\t\treturn config.image === undefined\n\t\t\t\t\t\t? sdk.create({ ...base, ...(config.snapshot === undefined ? {} : { snapshot: config.snapshot }) })\n\t\t\t\t\t\t: sdk.create({\n\t\t\t\t\t\t\t\t...base,\n\t\t\t\t\t\t\t\timage: config.image,\n\t\t\t\t\t\t\t\t...(config.resources === undefined ? {} : { resources: config.resources as Resources }),\n\t\t\t\t\t\t\t});\n\t\t\t\t});\n\t\t\t\tconst defaultCwd = yield* attempt(\"create.cwd\", () =>\n\t\t\t\t\tEnvDaytona.mountCwd(config.cwd, () => sandbox.getWorkDir()),\n\t\t\t\t);\n\t\t\t\tconst state = statusFrom(sandbox.state);\n\t\t\t\treturn {\n\t\t\t\t\tproviderResourceId: sandbox.id,\n\t\t\t\t\tproviderStatus: state.providerStatus,\n\t\t\t\t\truntimeConfig: runtime(defaultCwd, config),\n\t\t\t\t\tmetadata: {\n\t\t\t\t\t\ttarget: sandbox.target,\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t}),\n\t\truntimeConfigFor: ({ providerResourceId, overrides }) =>\n\t\t\tEffect.gen(function* () {\n\t\t\t\tconst sandbox = yield* get(providerResourceId, \"runtimeConfigFor\");\n\t\t\t\tconst defaultCwd =\n\t\t\t\t\toverrides?.defaultCwd ??\n\t\t\t\t\tSandboxDriver.AbsolutePath.make(\n\t\t\t\t\t\tyield* attempt(\"runtimeConfigFor.cwd\", () =>\n\t\t\t\t\t\t\tEnvDaytona.mountCwd(undefined, () => sandbox.getWorkDir()),\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\t\t\t\treturn {\n\t\t\t\t\tdefaultCwd,\n\t\t\t\t\tspillPath: overrides?.spillPath ?? SandboxDriver.AbsolutePath.make(EnvDaytona.SPILL_PATH),\n\t\t\t\t\t...(overrides?.user === undefined ? { user: sandbox.user } : { user: overrides.user }),\n\t\t\t\t\t...(overrides?.execTimeout === undefined ? {} : { execTimeout: overrides.execTimeout }),\n\t\t\t\t};\n\t\t\t}),\n\t\tattach: (input) =>\n\t\t\tLayer.unwrap(\n\t\t\t\tEffect.gen(function* () {\n\t\t\t\t\tconst sandbox = yield* get(\n\t\t\t\t\t\tOption.getOrElse(input.providerResourceId, () => input.id),\n\t\t\t\t\t\t\"attach\",\n\t\t\t\t\t);\n\t\t\t\t\tyield* wake(sandbox, \"attach.wake\");\n\t\t\t\t\treturn EnvDaytona.transport(\n\t\t\t\t\t\tsandbox,\n\t\t\t\t\t\tinput.runtimeConfig.execTimeout === undefined\n\t\t\t\t\t\t\t? undefined\n\t\t\t\t\t\t\t: { execTimeout: input.runtimeConfig.execTimeout },\n\t\t\t\t\t);\n\t\t\t\t}),\n\t\t\t),\n\t\tinspect: (input) =>\n\t\t\tEffect.flatMap(\n\t\t\t\tget(\n\t\t\t\t\tOption.getOrElse(input.providerResourceId, () => input.id),\n\t\t\t\t\t\"inspect\",\n\t\t\t\t),\n\t\t\t\t(sandbox) => Effect.map(refresh(sandbox, \"inspect.refresh\"), observed),\n\t\t\t),\n\t\twake: (input) =>\n\t\t\tEffect.flatMap(\n\t\t\t\tget(\n\t\t\t\t\tOption.getOrElse(input.providerResourceId, () => input.id),\n\t\t\t\t\t\"wake\",\n\t\t\t\t),\n\t\t\t\t(sandbox) => Effect.map(wake(sandbox, \"wake.start\"), observed),\n\t\t\t),\n\t\tstop: (input) =>\n\t\t\tEffect.flatMap(\n\t\t\t\tget(\n\t\t\t\t\tOption.getOrElse(input.providerResourceId, () => input.id),\n\t\t\t\t\t\"stop\",\n\t\t\t\t),\n\t\t\t\t(sandbox) => attempt(\"stop\", () => sandbox.stop()).pipe(Effect.map(() => observed(sandbox))),\n\t\t\t),\n\t\tdestroy: (input) =>\n\t\t\tEffect.flatMap(\n\t\t\t\tget(\n\t\t\t\t\tOption.getOrElse(input.providerResourceId, () => input.id),\n\t\t\t\t\t\"destroy\",\n\t\t\t\t),\n\t\t\t\t(sandbox) => attempt(\"destroy\", () => sandbox.delete()),\n\t\t\t),\n\t});\n};\n\nconst sandbox = SandboxDriver.module({\n\tapiVersion: SandboxDriver.apiVersion,\n\tname,\n\toptions: Options,\n\tmake,\n});\n\nexport const config = (value: CreateConfig) => ({ driver: \"daytona\" as const, config: value });\n\nexport default sandbox;\n"],"mappings":"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"}
@@ -1,263 +0,0 @@
1
- import { Effect, Schema } from "effect";
2
- import { Worker } from "node:worker_threads";
3
- //#region src/tool/image/exif.ts
4
- /**
5
- * EXIF orientation for JPEG and WebP. Photon decodes pixels as stored, so a phone
6
- * photo taken sideways would reach the model sideways without this.
7
- */
8
- const readOrientationFromTiff = (bytes, tiffStart) => {
9
- if (tiffStart + 8 > bytes.length) return 1;
10
- const le = (byte(bytes, tiffStart) << 8 | byte(bytes, tiffStart + 1)) === 18761;
11
- const read16 = (pos) => le ? byte(bytes, pos) | byte(bytes, pos + 1) << 8 : byte(bytes, pos) << 8 | byte(bytes, pos + 1);
12
- const read32 = (pos) => le ? (byte(bytes, pos) | byte(bytes, pos + 1) << 8 | byte(bytes, pos + 2) << 16 | byte(bytes, pos + 3) << 24) >>> 0 : (byte(bytes, pos) << 24 | byte(bytes, pos + 1) << 16 | byte(bytes, pos + 2) << 8 | byte(bytes, pos + 3)) >>> 0;
13
- const ifdStart = tiffStart + read32(tiffStart + 4);
14
- if (ifdStart + 2 > bytes.length) return 1;
15
- const entryCount = read16(ifdStart);
16
- for (let index = 0; index < entryCount; index++) {
17
- const entryPos = ifdStart + 2 + index * 12;
18
- if (entryPos + 12 > bytes.length) return 1;
19
- if (read16(entryPos) === 274) {
20
- const value = read16(entryPos + 8);
21
- return value >= 1 && value <= 8 ? value : 1;
22
- }
23
- }
24
- return 1;
25
- };
26
- const findJpegTiffOffset = (bytes) => {
27
- let offset = 2;
28
- while (offset < bytes.length - 1) {
29
- if (bytes[offset] !== 255) return -1;
30
- const marker = bytes[offset + 1];
31
- if (marker === 255) {
32
- offset++;
33
- continue;
34
- }
35
- if (marker === 225) {
36
- const segmentStart = offset + 4;
37
- if (segmentStart + 6 > bytes.length) return -1;
38
- if (hasExifHeader(bytes, segmentStart)) return segmentStart + 6;
39
- }
40
- if (offset + 4 > bytes.length) return -1;
41
- offset += 2 + (byte(bytes, offset + 2) << 8 | byte(bytes, offset + 3));
42
- }
43
- return -1;
44
- };
45
- const findWebpTiffOffset = (bytes) => {
46
- let offset = 12;
47
- while (offset + 8 <= bytes.length) {
48
- const chunkId = String.fromCharCode(...bytes.subarray(offset, offset + 4));
49
- const chunkSize = byte(bytes, offset + 4) | byte(bytes, offset + 5) << 8 | byte(bytes, offset + 6) << 16 | byte(bytes, offset + 7) << 24;
50
- const dataStart = offset + 8;
51
- if (chunkId === "EXIF") {
52
- if (dataStart + chunkSize > bytes.length) return -1;
53
- return chunkSize >= 6 && hasExifHeader(bytes, dataStart) ? dataStart + 6 : dataStart;
54
- }
55
- offset = dataStart + chunkSize + chunkSize % 2;
56
- }
57
- return -1;
58
- };
59
- const hasExifHeader = (bytes, offset) => [
60
- 69,
61
- 120,
62
- 105,
63
- 102,
64
- 0,
65
- 0
66
- ].every((value, index) => bytes[offset + index] === value);
67
- const orientationOf = (bytes) => {
68
- let tiffOffset = -1;
69
- if (bytes[0] === 255 && bytes[1] === 216) tiffOffset = findJpegTiffOffset(bytes);
70
- else if (String.fromCharCode(...bytes.subarray(0, 4)) === "RIFF" && String.fromCharCode(...bytes.subarray(8, 12)) === "WEBP") tiffOffset = findWebpTiffOffset(bytes);
71
- return tiffOffset === -1 ? 1 : readOrientationFromTiff(bytes, tiffOffset);
72
- };
73
- const byte = (bytes, index) => bytes[index] ?? 0;
74
- const rotate90 = (photon, image, dstIndex) => {
75
- const w = image.get_width();
76
- const h = image.get_height();
77
- const src = image.get_raw_pixels();
78
- const dst = new Uint8Array(src.length);
79
- for (let y = 0; y < h; y++) for (let x = 0; x < w; x++) dst.set(src.subarray((y * w + x) * 4, (y * w + x) * 4 + 4), dstIndex(x, y, w, h) * 4);
80
- return new photon.PhotonImage(dst, h, w);
81
- };
82
- const clockwise = (x, y, _w, h) => x * h + (h - 1 - y);
83
- const counterClockwise = (x, y, w, h) => (w - 1 - x) * h + y;
84
- /** Flips mutate `image`; rotations return a new image, which the caller frees alongside the old one. */
85
- const applyOrientation = (photon, image, originalBytes) => {
86
- switch (orientationOf(originalBytes)) {
87
- case 2:
88
- photon.fliph(image);
89
- return image;
90
- case 3:
91
- photon.fliph(image);
92
- photon.flipv(image);
93
- return image;
94
- case 4:
95
- photon.flipv(image);
96
- return image;
97
- case 5: {
98
- const rotated = rotate90(photon, image, clockwise);
99
- photon.fliph(rotated);
100
- return rotated;
101
- }
102
- case 6: return rotate90(photon, image, clockwise);
103
- case 7: {
104
- const rotated = rotate90(photon, image, counterClockwise);
105
- photon.fliph(rotated);
106
- return rotated;
107
- }
108
- case 8: return rotate90(photon, image, counterClockwise);
109
- default: return image;
110
- }
111
- };
112
- //#endregion
113
- //#region src/tool/image/photon.ts
114
- let loading;
115
- /** Photon, loaded once on first use; `undefined` when its WASM cannot load, so images degrade instead of failing reads. */
116
- const load = Effect.promise(() => {
117
- loading ??= import("@silvia-odwyer/photon-node").then((module) => module, () => void 0);
118
- return loading;
119
- });
120
- /** Decode with EXIF orientation applied. The caller frees the result. */
121
- const decode = (photon, bytes) => {
122
- const raw = photon.PhotonImage.new_from_byteslice(bytes);
123
- const oriented = applyOrientation(photon, raw, bytes);
124
- if (oriented !== raw) raw.free();
125
- return oriented;
126
- };
127
- //#endregion
128
- //#region src/tool/image/resize.ts
129
- const defaults = {
130
- maxWidth: 2e3,
131
- maxHeight: 2e3,
132
- maxBytes: 4718592,
133
- jpegQuality: 80
134
- };
135
- /** Tags worker replies: Node posts its own messages on the worker channel too (e.g. under `--watch`). */
136
- const REPLY = "codework:photon-image-resize";
137
- const base64 = (bytes) => {
138
- const data = Buffer.from(bytes).toString("base64");
139
- return {
140
- data,
141
- size: data.length
142
- };
143
- };
144
- /**
145
- * Fit an image within the dimensions and encoded size, in this thread. `null`
146
- * when Photon is unavailable or nothing fits. Fits by: scaling to the max
147
- * dimensions, keeping the smaller of PNG and JPEG, lowering JPEG quality, then
148
- * shrinking by a quarter at a time down to 1x1.
149
- */
150
- const resizeInProcess = (input, mimeType, options = defaults) => Effect.map(load, (photon) => {
151
- if (photon === void 0) return null;
152
- let image;
153
- try {
154
- image = decode(photon, input);
155
- const originalWidth = image.get_width();
156
- const originalHeight = image.get_height();
157
- const inputSize = Math.ceil(input.byteLength / 3) * 4;
158
- if (originalWidth <= options.maxWidth && originalHeight <= options.maxHeight && inputSize < options.maxBytes) return {
159
- data: Buffer.from(input).toString("base64"),
160
- mimeType,
161
- originalWidth,
162
- originalHeight,
163
- width: originalWidth,
164
- height: originalHeight,
165
- wasResized: false
166
- };
167
- let width = originalWidth;
168
- let height = originalHeight;
169
- if (width > options.maxWidth) {
170
- height = Math.max(1, Math.round(height * options.maxWidth / width));
171
- width = options.maxWidth;
172
- }
173
- if (height > options.maxHeight) {
174
- width = Math.max(1, Math.round(width * options.maxHeight / height));
175
- height = options.maxHeight;
176
- }
177
- const qualities = [.../* @__PURE__ */ new Set([
178
- options.jpegQuality,
179
- 85,
180
- 70,
181
- 55,
182
- 40
183
- ])];
184
- while (true) {
185
- const resized = photon.resize(image, width, height, photon.SamplingFilter.Lanczos3);
186
- try {
187
- const fit = [{
188
- ...base64(resized.get_bytes()),
189
- mimeType: "image/png"
190
- }, ...qualities.map((quality) => ({
191
- ...base64(resized.get_bytes_jpeg(quality)),
192
- mimeType: "image/jpeg"
193
- }))].find((candidate) => candidate.size < options.maxBytes);
194
- if (fit !== void 0) return {
195
- data: fit.data,
196
- mimeType: fit.mimeType,
197
- originalWidth,
198
- originalHeight,
199
- width,
200
- height,
201
- wasResized: true
202
- };
203
- } finally {
204
- resized.free();
205
- }
206
- const nextWidth = Math.max(1, Math.floor(width * .75));
207
- const nextHeight = Math.max(1, Math.floor(height * .75));
208
- if (nextWidth === width && nextHeight === height) return null;
209
- width = nextWidth;
210
- height = nextHeight;
211
- }
212
- } catch {
213
- return null;
214
- } finally {
215
- image?.free();
216
- }
217
- });
218
- const workerUrl = () => import.meta.url.endsWith(".ts") ? new URL("./worker.ts", import.meta.url) : new URL("./tool/image/worker.mjs", import.meta.url);
219
- /** The worker could not resize; the caller falls back to this thread. */
220
- var WorkerError = class extends Schema.TaggedError()("ImageResizeWorkerError", { message: Schema.String }) {};
221
- const inWorker = (input, mimeType, options) => Effect.callback((resume) => {
222
- let worker;
223
- try {
224
- worker = new Worker(workerUrl());
225
- } catch (cause) {
226
- resume(Effect.fail(new WorkerError({ message: String(cause) })));
227
- return;
228
- }
229
- const done = (effect) => {
230
- resume(effect);
231
- worker.terminate().catch(() => void 0);
232
- };
233
- worker.on("message", (message) => {
234
- const reply = message;
235
- if (reply?.type !== "codework:photon-image-resize") return;
236
- done("error" in reply ? Effect.fail(new WorkerError({ message: reply.error })) : Effect.succeed(reply.result));
237
- });
238
- worker.once("error", (error) => done(Effect.fail(new WorkerError({ message: error.message }))));
239
- worker.once("exit", (code) => done(Effect.fail(new WorkerError({ message: `Image resize worker exited with code ${code}` }))));
240
- const bytes = new Uint8Array(input);
241
- worker.postMessage({
242
- input: bytes,
243
- mimeType,
244
- options
245
- }, [bytes.buffer]);
246
- return Effect.promise(() => worker.terminate());
247
- });
248
- /**
249
- * {@link resizeInProcess} on a worker thread, so decoding and encoding a large
250
- * image never blocks the harness's event loop. Falls back to this thread when a
251
- * worker cannot start, so a read still works.
252
- */
253
- const resize = (input, mimeType, options = defaults) => inWorker(input, mimeType, options).pipe(Effect.catch(() => resizeInProcess(input, mimeType, options)));
254
- /** Tells the model how to map coordinates on a resized image back to the original. */
255
- const dimensionNote = (result) => {
256
- if (!result.wasResized) return void 0;
257
- const scale = result.originalWidth / result.width;
258
- return `[Image: original ${result.originalWidth}x${result.originalHeight}, displayed at ${result.width}x${result.height}. Multiply coordinates by ${scale.toFixed(2)} to map to original image.]`;
259
- };
260
- //#endregion
261
- export { decode as a, resizeInProcess as i, dimensionNote as n, load as o, resize as r, REPLY as t };
262
-
263
- //# sourceMappingURL=resize-Dwv5dCgY.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"resize-Dwv5dCgY.mjs","names":[],"sources":["../../src/tool/image/exif.ts","../../src/tool/image/photon.ts","../../src/tool/image/resize.ts"],"sourcesContent":["import type { Photon, PhotonImage } from \"./photon.ts\";\n\n/**\n * EXIF orientation for JPEG and WebP. Photon decodes pixels as stored, so a phone\n * photo taken sideways would reach the model sideways without this.\n */\n\nconst readOrientationFromTiff = (bytes: Uint8Array, tiffStart: number): number => {\n\tif (tiffStart + 8 > bytes.length) return 1;\n\tconst le = ((byte(bytes, tiffStart) << 8) | byte(bytes, tiffStart + 1)) === 0x4949;\n\tconst read16 = (pos: number): number =>\n\t\tle ? byte(bytes, pos) | (byte(bytes, pos + 1) << 8) : (byte(bytes, pos) << 8) | byte(bytes, pos + 1);\n\tconst read32 = (pos: number): number =>\n\t\tle\n\t\t\t? (byte(bytes, pos) |\n\t\t\t\t\t(byte(bytes, pos + 1) << 8) |\n\t\t\t\t\t(byte(bytes, pos + 2) << 16) |\n\t\t\t\t\t(byte(bytes, pos + 3) << 24)) >>>\n\t\t\t\t0\n\t\t\t: ((byte(bytes, pos) << 24) |\n\t\t\t\t\t(byte(bytes, pos + 1) << 16) |\n\t\t\t\t\t(byte(bytes, pos + 2) << 8) |\n\t\t\t\t\tbyte(bytes, pos + 3)) >>>\n\t\t\t\t0;\n\n\tconst ifdStart = tiffStart + read32(tiffStart + 4);\n\tif (ifdStart + 2 > bytes.length) return 1;\n\tconst entryCount = read16(ifdStart);\n\tfor (let index = 0; index < entryCount; index++) {\n\t\tconst entryPos = ifdStart + 2 + index * 12;\n\t\tif (entryPos + 12 > bytes.length) return 1;\n\t\tif (read16(entryPos) === 0x0112) {\n\t\t\tconst value = read16(entryPos + 8);\n\t\t\treturn value >= 1 && value <= 8 ? value : 1;\n\t\t}\n\t}\n\treturn 1;\n};\n\nconst findJpegTiffOffset = (bytes: Uint8Array): number => {\n\tlet offset = 2;\n\twhile (offset < bytes.length - 1) {\n\t\tif (bytes[offset] !== 0xff) return -1;\n\t\tconst marker = bytes[offset + 1];\n\t\tif (marker === 0xff) {\n\t\t\toffset++;\n\t\t\tcontinue;\n\t\t}\n\t\tif (marker === 0xe1) {\n\t\t\tconst segmentStart = offset + 4;\n\t\t\tif (segmentStart + 6 > bytes.length) return -1;\n\t\t\tif (hasExifHeader(bytes, segmentStart)) return segmentStart + 6;\n\t\t}\n\t\tif (offset + 4 > bytes.length) return -1;\n\t\toffset += 2 + ((byte(bytes, offset + 2) << 8) | byte(bytes, offset + 3));\n\t}\n\treturn -1;\n};\n\nconst findWebpTiffOffset = (bytes: Uint8Array): number => {\n\tlet offset = 12;\n\twhile (offset + 8 <= bytes.length) {\n\t\tconst chunkId = String.fromCharCode(...bytes.subarray(offset, offset + 4));\n\t\tconst chunkSize =\n\t\t\tbyte(bytes, offset + 4) |\n\t\t\t(byte(bytes, offset + 5) << 8) |\n\t\t\t(byte(bytes, offset + 6) << 16) |\n\t\t\t(byte(bytes, offset + 7) << 24);\n\t\tconst dataStart = offset + 8;\n\t\tif (chunkId === \"EXIF\") {\n\t\t\tif (dataStart + chunkSize > bytes.length) return -1;\n\t\t\t// Some WebP files carry an \"Exif\\0\\0\" prefix before the TIFF header.\n\t\t\treturn chunkSize >= 6 && hasExifHeader(bytes, dataStart) ? dataStart + 6 : dataStart;\n\t\t}\n\t\t// RIFF chunks are padded to an even size.\n\t\toffset = dataStart + chunkSize + (chunkSize % 2);\n\t}\n\treturn -1;\n};\n\nconst hasExifHeader = (bytes: Uint8Array, offset: number): boolean =>\n\t[0x45, 0x78, 0x69, 0x66, 0x00, 0x00].every((value, index) => bytes[offset + index] === value);\n\nconst orientationOf = (bytes: Uint8Array): number => {\n\tlet tiffOffset = -1;\n\tif (bytes[0] === 0xff && bytes[1] === 0xd8) tiffOffset = findJpegTiffOffset(bytes);\n\telse if (\n\t\tString.fromCharCode(...bytes.subarray(0, 4)) === \"RIFF\" &&\n\t\tString.fromCharCode(...bytes.subarray(8, 12)) === \"WEBP\"\n\t)\n\t\ttiffOffset = findWebpTiffOffset(bytes);\n\treturn tiffOffset === -1 ? 1 : readOrientationFromTiff(bytes, tiffOffset);\n};\n\nconst byte = (bytes: Uint8Array, index: number): number => bytes[index] ?? 0;\n\ntype DstIndex = (x: number, y: number, w: number, h: number) => number;\n\nconst rotate90 = (photon: Photon, image: PhotonImage, dstIndex: DstIndex): PhotonImage => {\n\tconst w = image.get_width();\n\tconst h = image.get_height();\n\tconst src = image.get_raw_pixels();\n\tconst dst = new Uint8Array(src.length);\n\tfor (let y = 0; y < h; y++) {\n\t\tfor (let x = 0; x < w; x++) {\n\t\t\tdst.set(src.subarray((y * w + x) * 4, (y * w + x) * 4 + 4), dstIndex(x, y, w, h) * 4);\n\t\t}\n\t}\n\treturn new photon.PhotonImage(dst, h, w);\n};\n\nconst clockwise: DstIndex = (x, y, _w, h) => x * h + (h - 1 - y);\nconst counterClockwise: DstIndex = (x, y, w, h) => (w - 1 - x) * h + y;\n\n/** Flips mutate `image`; rotations return a new image, which the caller frees alongside the old one. */\nexport const applyOrientation = (photon: Photon, image: PhotonImage, originalBytes: Uint8Array): PhotonImage => {\n\tswitch (orientationOf(originalBytes)) {\n\t\tcase 2:\n\t\t\tphoton.fliph(image);\n\t\t\treturn image;\n\t\tcase 3:\n\t\t\tphoton.fliph(image);\n\t\t\tphoton.flipv(image);\n\t\t\treturn image;\n\t\tcase 4:\n\t\t\tphoton.flipv(image);\n\t\t\treturn image;\n\t\tcase 5: {\n\t\t\tconst rotated = rotate90(photon, image, clockwise);\n\t\t\tphoton.fliph(rotated);\n\t\t\treturn rotated;\n\t\t}\n\t\tcase 6:\n\t\t\treturn rotate90(photon, image, clockwise);\n\t\tcase 7: {\n\t\t\tconst rotated = rotate90(photon, image, counterClockwise);\n\t\t\tphoton.fliph(rotated);\n\t\t\treturn rotated;\n\t\t}\n\t\tcase 8:\n\t\t\treturn rotate90(photon, image, counterClockwise);\n\t\tdefault:\n\t\t\treturn image;\n\t}\n};\n","import { Effect } from \"effect\";\nimport { applyOrientation } from \"./exif.ts\";\n\nexport type Photon = typeof import(\"@silvia-odwyer/photon-node\");\nexport type PhotonImage = import(\"@silvia-odwyer/photon-node\").PhotonImage;\n\nlet loading: Promise<Photon | undefined> | undefined;\n\n/** Photon, loaded once on first use; `undefined` when its WASM cannot load, so images degrade instead of failing reads. */\nexport const load: Effect.Effect<Photon | undefined> = Effect.promise(() => {\n\tloading ??= import(\"@silvia-odwyer/photon-node\").then(\n\t\t(module) => module,\n\t\t() => undefined,\n\t);\n\treturn loading;\n});\n\n/** Decode with EXIF orientation applied. The caller frees the result. */\nexport const decode = (photon: Photon, bytes: Uint8Array): PhotonImage => {\n\tconst raw = photon.PhotonImage.new_from_byteslice(bytes);\n\tconst oriented = applyOrientation(photon, raw, bytes);\n\tif (oriented !== raw) raw.free();\n\treturn oriented;\n};\n","import { Effect, Schema } from \"effect\";\nimport { Worker } from \"node:worker_threads\";\nimport { decode, load } from \"./photon.ts\";\n\nexport interface ResizeOptions {\n\treadonly maxWidth: number;\n\treadonly maxHeight: number;\n\t/** Most bytes of base64 payload. */\n\treadonly maxBytes: number;\n\treadonly jpegQuality: number;\n}\n\nexport interface Resized {\n\t/** base64 */\n\treadonly data: string;\n\treadonly mimeType: string;\n\treadonly originalWidth: number;\n\treadonly originalHeight: number;\n\treadonly width: number;\n\treadonly height: number;\n\treadonly wasResized: boolean;\n}\n\n// 4.5MB of base64 leaves headroom below Anthropic's 5MB image limit.\nexport const defaults: ResizeOptions = {\n\tmaxWidth: 2000,\n\tmaxHeight: 2000,\n\tmaxBytes: 4.5 * 1024 * 1024,\n\tjpegQuality: 80,\n};\n\n/** Tags worker replies: Node posts its own messages on the worker channel too (e.g. under `--watch`). */\nexport const REPLY = \"codework:photon-image-resize\";\n\nexport type Reply = { type: typeof REPLY; result: Resized | null } | { type: typeof REPLY; error: string };\n\nconst base64 = (bytes: Uint8Array) => {\n\tconst data = Buffer.from(bytes).toString(\"base64\");\n\treturn { data, size: data.length };\n};\n\n/**\n * Fit an image within the dimensions and encoded size, in this thread. `null`\n * when Photon is unavailable or nothing fits. Fits by: scaling to the max\n * dimensions, keeping the smaller of PNG and JPEG, lowering JPEG quality, then\n * shrinking by a quarter at a time down to 1x1.\n */\nexport const resizeInProcess = (\n\tinput: Uint8Array,\n\tmimeType: string,\n\toptions: ResizeOptions = defaults,\n): Effect.Effect<Resized | null> =>\n\tEffect.map(load, (photon) => {\n\t\tif (photon === undefined) return null;\n\t\tlet image: ReturnType<typeof decode> | undefined;\n\t\ttry {\n\t\t\timage = decode(photon, input);\n\t\t\tconst originalWidth = image.get_width();\n\t\t\tconst originalHeight = image.get_height();\n\t\t\tconst inputSize = Math.ceil(input.byteLength / 3) * 4;\n\t\t\tif (originalWidth <= options.maxWidth && originalHeight <= options.maxHeight && inputSize < options.maxBytes) {\n\t\t\t\treturn {\n\t\t\t\t\tdata: Buffer.from(input).toString(\"base64\"),\n\t\t\t\t\tmimeType,\n\t\t\t\t\toriginalWidth,\n\t\t\t\t\toriginalHeight,\n\t\t\t\t\twidth: originalWidth,\n\t\t\t\t\theight: originalHeight,\n\t\t\t\t\twasResized: false,\n\t\t\t\t};\n\t\t\t}\n\n\t\t\t// A side never scales below one pixel, or a thin image would vanish.\n\t\t\tlet width = originalWidth;\n\t\t\tlet height = originalHeight;\n\t\t\tif (width > options.maxWidth) {\n\t\t\t\theight = Math.max(1, Math.round((height * options.maxWidth) / width));\n\t\t\t\twidth = options.maxWidth;\n\t\t\t}\n\t\t\tif (height > options.maxHeight) {\n\t\t\t\twidth = Math.max(1, Math.round((width * options.maxHeight) / height));\n\t\t\t\theight = options.maxHeight;\n\t\t\t}\n\n\t\t\tconst qualities = [...new Set([options.jpegQuality, 85, 70, 55, 40])];\n\t\t\twhile (true) {\n\t\t\t\tconst resized = photon.resize(image, width, height, photon.SamplingFilter.Lanczos3);\n\t\t\t\ttry {\n\t\t\t\t\tconst candidates = [\n\t\t\t\t\t\t{ ...base64(resized.get_bytes()), mimeType: \"image/png\" },\n\t\t\t\t\t\t...qualities.map((quality) => ({\n\t\t\t\t\t\t\t...base64(resized.get_bytes_jpeg(quality)),\n\t\t\t\t\t\t\tmimeType: \"image/jpeg\",\n\t\t\t\t\t\t})),\n\t\t\t\t\t];\n\t\t\t\t\tconst fit = candidates.find((candidate) => candidate.size < options.maxBytes);\n\t\t\t\t\tif (fit !== undefined) {\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tdata: fit.data,\n\t\t\t\t\t\t\tmimeType: fit.mimeType,\n\t\t\t\t\t\t\toriginalWidth,\n\t\t\t\t\t\t\toriginalHeight,\n\t\t\t\t\t\t\twidth,\n\t\t\t\t\t\t\theight,\n\t\t\t\t\t\t\twasResized: true,\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t} finally {\n\t\t\t\t\tresized.free();\n\t\t\t\t}\n\t\t\t\tconst nextWidth = Math.max(1, Math.floor(width * 0.75));\n\t\t\t\tconst nextHeight = Math.max(1, Math.floor(height * 0.75));\n\t\t\t\tif (nextWidth === width && nextHeight === height) return null;\n\t\t\t\twidth = nextWidth;\n\t\t\t\theight = nextHeight;\n\t\t\t}\n\t\t} catch {\n\t\t\treturn null;\n\t\t} finally {\n\t\t\timage?.free();\n\t\t}\n\t});\n\n// From source the worker sits beside this module; packed, this module lands in a\n// chunk at the package's output root while the worker keeps its own entry path.\nconst workerUrl = (): URL =>\n\timport.meta.url.endsWith(\".ts\")\n\t\t? new URL(\"./worker.ts\", import.meta.url)\n\t\t: new URL(\"./tool/image/worker.mjs\", import.meta.url);\n\n/** The worker could not resize; the caller falls back to this thread. */\nclass WorkerError extends Schema.TaggedError<WorkerError>()(\"ImageResizeWorkerError\", { message: Schema.String }) {}\n\nconst inWorker = (input: Uint8Array, mimeType: string, options: ResizeOptions) =>\n\tEffect.callback<Resized | null, WorkerError>((resume) => {\n\t\tlet worker: Worker;\n\t\ttry {\n\t\t\tworker = new Worker(workerUrl());\n\t\t} catch (cause) {\n\t\t\t// e.g. a Node permission model without worker access: resize in this thread instead.\n\t\t\tresume(Effect.fail(new WorkerError({ message: String(cause) })));\n\t\t\treturn;\n\t\t}\n\t\tconst done = (effect: Effect.Effect<Resized | null, WorkerError>) => {\n\t\t\tresume(effect);\n\t\t\tvoid worker.terminate().catch(() => undefined);\n\t\t};\n\t\tworker.on(\"message\", (message: unknown) => {\n\t\t\tconst reply = message as Partial<Reply> | null;\n\t\t\tif (reply?.type !== REPLY) return;\n\t\t\tdone(\n\t\t\t\t\"error\" in reply\n\t\t\t\t\t? Effect.fail(new WorkerError({ message: reply.error }))\n\t\t\t\t\t: Effect.succeed((reply as { result: Resized | null }).result),\n\t\t\t);\n\t\t});\n\t\tworker.once(\"error\", (error: Error) => done(Effect.fail(new WorkerError({ message: error.message }))));\n\t\tworker.once(\"exit\", (code) =>\n\t\t\tdone(Effect.fail(new WorkerError({ message: `Image resize worker exited with code ${code}` }))),\n\t\t);\n\t\t// Transferring detaches the buffer, so hand over a copy and leave the caller's bytes intact.\n\t\tconst bytes = new Uint8Array(input);\n\t\tworker.postMessage({ input: bytes, mimeType, options }, [bytes.buffer]);\n\t\treturn Effect.promise(() => worker.terminate());\n\t});\n\n/**\n * {@link resizeInProcess} on a worker thread, so decoding and encoding a large\n * image never blocks the harness's event loop. Falls back to this thread when a\n * worker cannot start, so a read still works.\n */\nexport const resize = (input: Uint8Array, mimeType: string, options: ResizeOptions = defaults) =>\n\tinWorker(input, mimeType, options).pipe(Effect.catch(() => resizeInProcess(input, mimeType, options)));\n\n/** Tells the model how to map coordinates on a resized image back to the original. */\nexport const dimensionNote = (result: Resized): string | undefined => {\n\tif (!result.wasResized) return undefined;\n\tconst scale = result.originalWidth / result.width;\n\treturn `[Image: original ${result.originalWidth}x${result.originalHeight}, displayed at ${result.width}x${result.height}. Multiply coordinates by ${scale.toFixed(2)} to map to original image.]`;\n};\n"],"mappings":"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"}