@elie-laloum/outpost 4.0.0 → 4.2.0
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/CHANGELOG.md +25 -0
- package/README.md +10 -6
- package/SECURITY.md +2 -0
- package/dist/adapters/agents/codex-adapter.js +2 -0
- package/dist/adapters/agents/codex-adapter.js.map +1 -1
- package/dist/adapters/agents/codex-provider.d.ts +2 -0
- package/dist/adapters/agents/codex-provider.js +32 -0
- package/dist/adapters/agents/codex-provider.js.map +1 -0
- package/dist/adapters/agents/codex-request.js +2 -1
- package/dist/adapters/agents/codex-request.js.map +1 -1
- package/dist/adapters/agents/gemini-request.js +2 -0
- package/dist/adapters/agents/gemini-request.js.map +1 -1
- package/dist/adapters/agents/settings.types.d.ts +5 -0
- package/dist/adapters/agents/settings.types.js.map +1 -1
- package/dist/adapters/models/openai-compatible.constants.d.ts +4 -0
- package/dist/adapters/models/openai-compatible.constants.js +10 -0
- package/dist/adapters/models/openai-compatible.constants.js.map +1 -0
- package/dist/adapters/models/openai-compatible.d.ts +3 -0
- package/dist/adapters/models/openai-compatible.js +78 -0
- package/dist/adapters/models/openai-compatible.js.map +1 -0
- package/dist/adapters/models/openai-compatible.types.d.ts +14 -0
- package/dist/adapters/models/openai-compatible.types.js +2 -0
- package/dist/adapters/models/openai-compatible.types.js.map +1 -0
- package/dist/adapters/models/openai-http.d.ts +1 -0
- package/dist/adapters/models/openai-http.js +30 -0
- package/dist/adapters/models/openai-http.js.map +1 -0
- package/dist/adapters/models/openai-request.d.ts +4 -0
- package/dist/adapters/models/openai-request.js +46 -0
- package/dist/adapters/models/openai-request.js.map +1 -0
- package/dist/adapters/models/openai-response.d.ts +3 -0
- package/dist/adapters/models/openai-response.js +65 -0
- package/dist/adapters/models/openai-response.js.map +1 -0
- package/dist/application/execution.types.d.ts +2 -0
- package/dist/application/execution.types.js.map +1 -1
- package/dist/application/outpost.types.d.ts +5 -0
- package/dist/application/outpost.types.js.map +1 -1
- package/dist/application/recovery-archive.d.ts +4 -0
- package/dist/application/recovery-archive.js +29 -0
- package/dist/application/recovery-archive.js.map +1 -0
- package/dist/application/recovery-archive.types.d.ts +10 -0
- package/dist/application/recovery-archive.types.js +2 -0
- package/dist/application/recovery-archive.types.js.map +1 -0
- package/dist/application/recovery-inspection.js +3 -0
- package/dist/application/recovery-inspection.js.map +1 -1
- package/dist/application/recovery-inspection.types.d.ts +2 -0
- package/dist/application/recovery-inspection.types.js.map +1 -1
- package/dist/application/recovery-retention.d.ts +2 -1
- package/dist/application/recovery-retention.js +9 -1
- package/dist/application/recovery-retention.js.map +1 -1
- package/dist/application/recovery-retention.types.d.ts +6 -0
- package/dist/application/recovery-retention.types.js.map +1 -1
- package/dist/application/remote-backup.js +8 -0
- package/dist/application/remote-backup.js.map +1 -1
- package/dist/application/remote-workspace.types.d.ts +2 -0
- package/dist/application/remote-workspace.types.js.map +1 -1
- package/dist/application/sandbox-agents.js +2 -1
- package/dist/application/sandbox-agents.js.map +1 -1
- package/dist/application/sandbox-dispatch.js +9 -0
- package/dist/application/sandbox-dispatch.js.map +1 -1
- package/dist/application/sandbox-provision.js +6 -0
- package/dist/application/sandbox-provision.js.map +1 -1
- package/dist/application/storage-reservation.js +3 -0
- package/dist/application/storage-reservation.js.map +1 -1
- package/dist/application/transport-inspection.constants.d.ts +2 -0
- package/dist/application/transport-inspection.constants.js +10 -0
- package/dist/application/transport-inspection.constants.js.map +1 -0
- package/dist/application/transport-inspection.d.ts +2 -0
- package/dist/application/transport-inspection.js +103 -0
- package/dist/application/transport-inspection.js.map +1 -0
- package/dist/application/transport-retention.d.ts +4 -0
- package/dist/application/transport-retention.js +153 -0
- package/dist/application/transport-retention.js.map +1 -0
- package/dist/cli/commands.js +55 -18
- package/dist/cli/commands.js.map +1 -1
- package/dist/cli/init-authentication.constants.d.ts +13 -0
- package/dist/cli/init-authentication.constants.js +10 -0
- package/dist/cli/init-authentication.constants.js.map +1 -0
- package/dist/cli/init-authentication.d.ts +4 -0
- package/dist/cli/init-authentication.js +43 -0
- package/dist/cli/init-authentication.js.map +1 -0
- package/dist/cli/init-command.js +74 -15
- package/dist/cli/init-command.js.map +1 -1
- package/dist/cli/main.constants.d.ts +102 -2
- package/dist/cli/main.constants.js +148 -46
- package/dist/cli/main.constants.js.map +1 -1
- package/dist/cli/scaffold-files.js +4 -3
- package/dist/cli/scaffold-files.js.map +1 -1
- package/dist/cli/scaffold-validation.js +19 -0
- package/dist/cli/scaffold-validation.js.map +1 -1
- package/dist/cli/scaffold.constants.d.ts +0 -5
- package/dist/cli/scaffold.constants.js +0 -5
- package/dist/cli/scaffold.constants.js.map +1 -1
- package/dist/cli/scaffold.js +13 -0
- package/dist/cli/scaffold.js.map +1 -1
- package/dist/cli/scaffold.types.d.ts +3 -0
- package/dist/cli/scaffold.types.js.map +1 -1
- package/dist/cli/starters.js +9 -2
- package/dist/cli/starters.js.map +1 -1
- package/dist/domain/conversation.types.d.ts +2 -0
- package/dist/domain/conversation.types.js.map +1 -1
- package/dist/domain/model.types.d.ts +15 -0
- package/dist/domain/model.types.js +2 -0
- package/dist/domain/model.types.js.map +1 -0
- package/dist/domain/transport.constants.d.ts +2 -0
- package/dist/domain/transport.constants.js +3 -0
- package/dist/domain/transport.constants.js.map +1 -0
- package/dist/domain/transport.d.ts +6 -0
- package/dist/domain/transport.js +21 -0
- package/dist/domain/transport.js.map +1 -0
- package/dist/domain/transport.types.d.ts +31 -0
- package/dist/domain/transport.types.js +2 -0
- package/dist/domain/transport.types.js.map +1 -0
- package/dist/index.d.ts +18 -1
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -1
- package/dist/infrastructure/artifact-store.js +10 -0
- package/dist/infrastructure/artifact-store.js.map +1 -1
- package/dist/infrastructure/artifact-store.types.d.ts +3 -1
- package/dist/infrastructure/artifact-store.types.js.map +1 -1
- package/dist/infrastructure/conversations/capture.js +1 -1
- package/dist/infrastructure/conversations/capture.js.map +1 -1
- package/dist/infrastructure/conversations.d.ts +2 -0
- package/dist/infrastructure/conversations.js +2 -0
- package/dist/infrastructure/conversations.js.map +1 -1
- package/dist/infrastructure/inspection-file.js +3 -3
- package/dist/infrastructure/inspection-file.js.map +1 -1
- package/dist/infrastructure/inspection-hash.js +4 -3
- package/dist/infrastructure/inspection-hash.js.map +1 -1
- package/dist/infrastructure/inspection-path.d.ts +1 -0
- package/dist/infrastructure/inspection-path.js +18 -0
- package/dist/infrastructure/inspection-path.js.map +1 -0
- package/dist/infrastructure/journal.js +6 -0
- package/dist/infrastructure/journal.js.map +1 -1
- package/dist/infrastructure/journal.types.d.ts +3 -0
- package/dist/infrastructure/journal.types.js.map +1 -1
- package/dist/infrastructure/local-transport.d.ts +3 -0
- package/dist/infrastructure/local-transport.js +146 -0
- package/dist/infrastructure/local-transport.js.map +1 -0
- package/dist/infrastructure/local-transport.types.d.ts +3 -0
- package/dist/infrastructure/local-transport.types.js +2 -0
- package/dist/infrastructure/local-transport.types.js.map +1 -0
- package/dist/infrastructure/resource-activity-inspection.d.ts +2 -1
- package/dist/infrastructure/resource-activity-inspection.js +2 -2
- package/dist/infrastructure/resource-activity-inspection.js.map +1 -1
- package/dist/infrastructure/resource-activity.js +42 -14
- package/dist/infrastructure/resource-activity.js.map +1 -1
- package/dist/infrastructure/resource-activity.types.d.ts +2 -0
- package/dist/infrastructure/resource-activity.types.js.map +1 -1
- package/dist/infrastructure/s3-transport.d.ts +4 -0
- package/dist/infrastructure/s3-transport.js +144 -0
- package/dist/infrastructure/s3-transport.js.map +1 -0
- package/dist/infrastructure/s3-transport.types.d.ts +6 -0
- package/dist/infrastructure/s3-transport.types.js +2 -0
- package/dist/infrastructure/s3-transport.types.js.map +1 -0
- package/dist/infrastructure/storage-inventory.types.d.ts +2 -1
- package/dist/infrastructure/storage-inventory.types.js.map +1 -1
- package/dist/infrastructure/storage-reservations.types.d.ts +2 -0
- package/dist/infrastructure/storage-reservations.types.js.map +1 -1
- package/dist/infrastructure/task-queue-bullmq-resources.d.ts +13 -0
- package/dist/infrastructure/task-queue-bullmq-resources.js +118 -0
- package/dist/infrastructure/task-queue-bullmq-resources.js.map +1 -0
- package/dist/infrastructure/task-queue-bullmq-state.d.ts +5 -0
- package/dist/infrastructure/task-queue-bullmq-state.js +26 -0
- package/dist/infrastructure/task-queue-bullmq-state.js.map +1 -0
- package/dist/infrastructure/task-queue-bullmq.constants.d.ts +7 -0
- package/dist/infrastructure/task-queue-bullmq.constants.js +79 -0
- package/dist/infrastructure/task-queue-bullmq.constants.js.map +1 -0
- package/dist/infrastructure/task-queue-bullmq.d.ts +3 -0
- package/dist/infrastructure/task-queue-bullmq.js +124 -0
- package/dist/infrastructure/task-queue-bullmq.js.map +1 -0
- package/dist/infrastructure/task-queue-bullmq.types.d.ts +17 -0
- package/dist/infrastructure/task-queue-bullmq.types.js +2 -0
- package/dist/infrastructure/task-queue-bullmq.types.js.map +1 -0
- package/dist/infrastructure/transport-archive.constants.d.ts +6 -0
- package/dist/infrastructure/transport-archive.constants.js +7 -0
- package/dist/infrastructure/transport-archive.constants.js.map +1 -0
- package/dist/infrastructure/transport-archive.d.ts +5 -0
- package/dist/infrastructure/transport-archive.js +157 -0
- package/dist/infrastructure/transport-archive.js.map +1 -0
- package/dist/infrastructure/transport-archive.types.d.ts +17 -0
- package/dist/infrastructure/transport-archive.types.js +2 -0
- package/dist/infrastructure/transport-archive.types.js.map +1 -0
- package/dist/infrastructure/transport-artifact-store.d.ts +3 -0
- package/dist/infrastructure/transport-artifact-store.js +31 -0
- package/dist/infrastructure/transport-artifact-store.js.map +1 -0
- package/dist/infrastructure/transport-artifact-store.types.d.ts +4 -0
- package/dist/infrastructure/transport-artifact-store.types.js +2 -0
- package/dist/infrastructure/transport-artifact-store.types.js.map +1 -0
- package/dist/infrastructure/transport-checkpoint.d.ts +5 -0
- package/dist/infrastructure/transport-checkpoint.js +105 -0
- package/dist/infrastructure/transport-checkpoint.js.map +1 -0
- package/dist/infrastructure/transport-checkpoint.types.d.ts +9 -0
- package/dist/infrastructure/transport-checkpoint.types.js +2 -0
- package/dist/infrastructure/transport-checkpoint.types.js.map +1 -0
- package/dist/infrastructure/transport-conversations.d.ts +4 -0
- package/dist/infrastructure/transport-conversations.js +69 -0
- package/dist/infrastructure/transport-conversations.js.map +1 -0
- package/dist/infrastructure/transport-conversations.types.d.ts +4 -0
- package/dist/infrastructure/transport-conversations.types.js +2 -0
- package/dist/infrastructure/transport-conversations.types.js.map +1 -0
- package/dist/infrastructure/transport-envelope.d.ts +7 -0
- package/dist/infrastructure/transport-envelope.js +47 -0
- package/dist/infrastructure/transport-envelope.js.map +1 -0
- package/dist/infrastructure/transport-journal.d.ts +6 -0
- package/dist/infrastructure/transport-journal.js +93 -0
- package/dist/infrastructure/transport-journal.js.map +1 -0
- package/dist/infrastructure/transport-journal.types.d.ts +10 -0
- package/dist/infrastructure/transport-journal.types.js +2 -0
- package/dist/infrastructure/transport-journal.types.js.map +1 -0
- package/dist/infrastructure/transport-json.d.ts +6 -0
- package/dist/infrastructure/transport-json.js +46 -0
- package/dist/infrastructure/transport-json.js.map +1 -0
- package/dist/infrastructure/transport-reservations.d.ts +5 -0
- package/dist/infrastructure/transport-reservations.js +89 -0
- package/dist/infrastructure/transport-reservations.js.map +1 -0
- package/dist/infrastructure/transport-reservations.types.d.ts +4 -0
- package/dist/infrastructure/transport-reservations.types.js +2 -0
- package/dist/infrastructure/transport-reservations.types.js.map +1 -0
- package/dist/infrastructure/transport.constants.d.ts +8 -0
- package/dist/infrastructure/transport.constants.js +9 -0
- package/dist/infrastructure/transport.constants.js.map +1 -0
- package/dist/infrastructure/workflow-checkpoint.js +7 -0
- package/dist/infrastructure/workflow-checkpoint.js.map +1 -1
- package/dist/infrastructure/workflow-checkpoint.types.d.ts +3 -1
- package/dist/infrastructure/workflow-checkpoint.types.js.map +1 -1
- package/dist/providers/agents.d.ts +1 -1
- package/dist/providers/agents.js.map +1 -1
- package/dist/providers/daytona-command.constants.d.ts +2 -0
- package/dist/providers/daytona-command.constants.js +13 -0
- package/dist/providers/daytona-command.constants.js.map +1 -0
- package/dist/providers/daytona-command.js +9 -3
- package/dist/providers/daytona-command.js.map +1 -1
- package/dist/providers/daytona-output.d.ts +4 -0
- package/dist/providers/daytona-output.js +37 -0
- package/dist/providers/daytona-output.js.map +1 -0
- package/dist/providers/daytona-terminal.js +3 -1
- package/dist/providers/daytona-terminal.js.map +1 -1
- package/dist/providers/vercel-command.js +2 -2
- package/dist/providers/vercel-command.js.map +1 -1
- package/package.json +31 -6
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resource-activity.js","sourceRoot":"","sources":["../../src/infrastructure/resource-activity.ts"],"names":[],"mappings":"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","sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { mkdir, open, realpath, rename, rm, opendir } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { invariant } from \"../domain/errors.ts\";\nimport { localProcessIdentity } from \"./git/process-identity.ts\";\nimport { readInspectionFile } from \"./inspection-file.ts\";\nimport { lockStorageMutation } from \"./storage-mutation-lock.ts\";\nimport { resourceActivityDefaults as defaults } from \"./resource-activity.constants.ts\";\nimport type {\n ResourceActivity,\n ResourceActivityOptions,\n ResourceActivityRecord,\n ResourceOperationResult,\n} from \"./resource-activity.types.ts\";\n\nasync function activityDirectory(repository: string): Promise<string> {\n let path = repository;\n for (const part of [\".outpost\", \"locks\", defaults.directory]) {\n path = join(path, part);\n await mkdir(path, { recursive: true, mode: 0o700 });\n invariant(\n (await realpath(path)) === path,\n \"Resource activity directory must not use symlinks\",\n );\n }\n return path;\n}\n\nexport async function registerResourceActivity(\n options: ResourceActivityOptions,\n): Promise<ResourceActivity> {\n for (const text of [options.workspace, options.provider])\n invariant(\n text.length > 0 && text.length <= defaults.maxText,\n \"Resource activity metadata exceeds its size limit\",\n );\n const directory = await activityDirectory(options.repository);\n const id = randomUUID();\n const path = join(directory, `${id}.json`);\n const createdAt = new Date().toISOString();\n const identity = await localProcessIdentity();\n let record: ResourceActivityRecord = {\n version: 1,\n id,\n pid: process.pid,\n ...(identity ? { identity } : {}),\n provider: options.provider,\n placement: options.placement,\n workspace: options.workspace,\n createdAt,\n updatedAt: createdAt,\n phase: \"allocating\",\n operations: [],\n };\n const unlock = await lockStorageMutation(options.repository);\n try {\n let count = 0;\n for await (const _entry of await opendir(directory))\n invariant(\n ++count < defaults.maxRecords,\n \"Too many resource activity records; inspect retained ownership before continuing\",\n );\n const file = await open(path, \"wx\", 0o600);\n try {\n await file.writeFile(JSON.stringify(record));\n await file.sync();\n } finally {\n await file.close();\n }\n } finally {\n await unlock();\n }\n let pending = Promise.resolve();\n let removed = false;\n function serialize(action: () => Promise<void>): Promise<void> {\n const next = pending.then(action);\n pending = next.catch(() => undefined);\n return next;\n }\n async function verify(): Promise<void> {\n invariant(\n (await realpath(directory)) === directory,\n \"Resource activity directory changed\",\n );\n invariant(!removed, \"Resource activity is closed\");\n const current = (\n await readInspectionFile(path, defaults.maxRecordBytes)\n ).toString(\"utf8\");\n invariant(\n current === JSON.stringify(record),\n \"Resource activity identity changed; refusing mutation\",\n );\n }\n function update(next: () => ResourceActivityRecord): Promise<void> {\n return serialize(async () => {\n await verify();\n const value = { ...next(), updatedAt: new Date().toISOString() };\n const data = JSON.stringify(value);\n invariant(\n Buffer.byteLength(data) <= defaults.maxRecordBytes,\n \"Resource activity record exceeds its size limit\",\n );\n const temporary = `${path}.${randomUUID()}.tmp`;\n const file = await open(temporary, \"wx\", 0o600);\n try {\n await file.writeFile(data);\n await file.sync();\n await file.close();\n await rename(temporary, path);\n record = value;\n } finally {\n await file.close();\n await rm(temporary, { force: true });\n }\n });\n }\n return {\n async idle() {\n await pending;\n return record.operations.length === 0;\n },\n phase: (phase) => update(() => ({ ...record, phase })),\n async run(kind, action) {\n const operation = {\n id: randomUUID(),\n kind,\n startedAt: new Date().toISOString(),\n };\n await update(() => {\n const active = record.operations.find((value) => value.kind === kind);\n const operations = record.operations.filter(\n (value) => value.kind !== kind,\n );\n operations.push({\n kind,\n startedAt: active?.startedAt ?? operation.startedAt,\n count: (active?.count ?? 0) + 1,\n });\n return { ...record, operations };\n });\n let outcome: ResourceOperationResult[\"outcome\"] = \"completed\";\n try {\n return await action();\n } catch (cause) {\n outcome = \"failed\";\n throw cause;\n } finally {\n const result = {\n ...operation,\n outcome,\n count: 1,\n finishedAt: new Date().toISOString(),\n };\n try {\n await update(() => ({\n ...record,\n operations: record.operations.flatMap((value) => {\n if (value.kind !== kind) return [value];\n if (value.count === 1) return [];\n return [{ ...value, count: value.count - 1 }];\n }),\n lastOperation: result,\n ...(outcome === \"failed\" ? { lastFailure: result } : {}),\n }));\n } catch (cause) {\n if (outcome !== \"failed\") throw cause;\n }\n }\n },\n remove: () =>\n serialize(async () => {\n if (removed) return;\n invariant(\n record.operations.length === 0,\n \"Resource operations remain unsettled; activity record retained\",\n );\n try {\n await verify();\n await rm(path);\n } catch (cause) {\n if (!(\n cause &&\n typeof cause === \"object\" &&\n \"code\" in cause &&\n cause.code === \"ENOENT\"\n ))\n throw cause;\n }\n removed = true;\n }),\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"resource-activity.js","sourceRoot":"","sources":["../../src/infrastructure/resource-activity.ts"],"names":[],"mappings":"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","sourcesContent":["import { TransportConflict } from \"../domain/transport.ts\";\nimport { jsonBytes } from \"./transport-json.ts\";\nimport { randomUUID } from \"node:crypto\";\nimport { mkdir, open, realpath, rename, rm, opendir } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { invariant } from \"../domain/errors.ts\";\nimport { localProcessIdentity } from \"./git/process-identity.ts\";\nimport { readInspectionFile } from \"./inspection-file.ts\";\nimport { lockStorageMutation } from \"./storage-mutation-lock.ts\";\nimport { resourceActivityDefaults as defaults } from \"./resource-activity.constants.ts\";\nimport type {\n ResourceActivity,\n ResourceActivityOptions,\n ResourceActivityRecord,\n ResourceOperationResult,\n} from \"./resource-activity.types.ts\";\n\nasync function activityDirectory(repository: string): Promise<string> {\n let path = repository;\n for (const part of [\".outpost\", \"locks\", defaults.directory]) {\n path = join(path, part);\n await mkdir(path, { recursive: true, mode: 0o700 });\n invariant(\n (await realpath(path)) === path,\n \"Resource activity directory must not use symlinks\",\n );\n }\n return path;\n}\n\nexport async function registerResourceActivity(\n options: ResourceActivityOptions,\n): Promise<ResourceActivity> {\n for (const text of [options.workspace, options.provider])\n invariant(\n text.length > 0 && text.length <= defaults.maxText,\n \"Resource activity metadata exceeds its size limit\",\n );\n const remote = options.transporter;\n const directory = remote ? \"\" : await activityDirectory(options.repository);\n let revision: string | undefined;\n const id = randomUUID();\n const path = remote ? `resources/${id}.json` : join(directory, `${id}.json`);\n const createdAt = new Date().toISOString();\n const identity = await localProcessIdentity();\n let record: ResourceActivityRecord = {\n version: 1,\n id,\n pid: process.pid,\n ...(identity ? { identity } : {}),\n provider: options.provider,\n placement: options.placement,\n workspace: options.workspace,\n createdAt,\n updatedAt: createdAt,\n phase: \"allocating\",\n operations: [],\n };\n const unlock = remote\n ? async () => {}\n : await lockStorageMutation(options.repository);\n try {\n if (remote) {\n revision = (\n await remote.write(path, jsonBytes(record), { ifRevision: null })\n ).revision;\n }\n if (!remote) {\n let count = 0;\n for await (const _entry of await opendir(directory))\n invariant(\n ++count < defaults.maxRecords,\n \"Too many resource activity records; inspect retained ownership before continuing\",\n );\n const file = await open(path, \"wx\", 0o600);\n try {\n await file.writeFile(JSON.stringify(record));\n await file.sync();\n } finally {\n await file.close();\n }\n }\n } finally {\n await unlock();\n }\n let pending = Promise.resolve();\n let removed = false;\n function serialize(action: () => Promise<void>): Promise<void> {\n const next = pending.then(action);\n pending = next.catch(() => undefined);\n return next;\n }\n async function verify(): Promise<void> {\n invariant(!removed, \"Resource activity is closed\");\n if (remote) {\n if ((await remote.read(path))?.revision !== revision)\n throw new TransportConflict(path);\n return;\n }\n invariant(\n remote || (await realpath(directory)) === directory,\n \"Resource activity directory changed\",\n );\n invariant(!removed, \"Resource activity is closed\");\n const current = (\n await readInspectionFile(path, defaults.maxRecordBytes)\n ).toString(\"utf8\");\n invariant(\n current === JSON.stringify(record),\n \"Resource activity identity changed; refusing mutation\",\n );\n }\n function update(next: () => ResourceActivityRecord): Promise<void> {\n return serialize(async () => {\n await verify();\n const value = { ...next(), updatedAt: new Date().toISOString() };\n const data = JSON.stringify(value);\n invariant(\n Buffer.byteLength(data) <= defaults.maxRecordBytes,\n \"Resource activity record exceeds its size limit\",\n );\n if (remote) {\n invariant(revision, \"Resource revision unavailable\");\n revision = (\n await remote.write(path, jsonBytes(value), { ifRevision: revision })\n ).revision;\n record = value;\n return;\n }\n const temporary = `${path}.${randomUUID()}.tmp`;\n const file = await open(temporary, \"wx\", 0o600);\n try {\n await file.writeFile(data);\n await file.sync();\n await file.close();\n await rename(temporary, path);\n record = value;\n } finally {\n await file.close();\n await rm(temporary, { force: true });\n }\n });\n }\n return {\n async idle() {\n await pending;\n return record.operations.length === 0;\n },\n phase: (phase) => update(() => ({ ...record, phase })),\n async run(kind, action) {\n const operation = {\n id: randomUUID(),\n kind,\n startedAt: new Date().toISOString(),\n };\n await update(() => {\n const active = record.operations.find((value) => value.kind === kind);\n const operations = record.operations.filter(\n (value) => value.kind !== kind,\n );\n operations.push({\n kind,\n startedAt: active?.startedAt ?? operation.startedAt,\n count: (active?.count ?? 0) + 1,\n });\n return { ...record, operations };\n });\n let outcome: ResourceOperationResult[\"outcome\"] = \"completed\";\n try {\n return await action();\n } catch (cause) {\n outcome = \"failed\";\n throw cause;\n } finally {\n const result = {\n ...operation,\n outcome,\n count: 1,\n finishedAt: new Date().toISOString(),\n };\n try {\n await update(() => ({\n ...record,\n operations: record.operations.flatMap((value) => {\n if (value.kind !== kind) return [value];\n if (value.count === 1) return [];\n return [{ ...value, count: value.count - 1 }];\n }),\n lastOperation: result,\n ...(outcome === \"failed\" ? { lastFailure: result } : {}),\n }));\n } catch (cause) {\n if (outcome !== \"failed\") throw cause;\n }\n }\n },\n remove: () =>\n serialize(async () => {\n if (removed) return;\n invariant(\n record.operations.length === 0,\n \"Resource operations remain unsettled; activity record retained\",\n );\n try {\n await verify();\n if (remote) {\n invariant(revision, \"Resource revision unavailable\");\n await remote.remove(path, { ifRevision: revision });\n } else await rm(path);\n } catch (cause) {\n if (!(\n cause &&\n typeof cause === \"object\" &&\n \"code\" in cause &&\n cause.code === \"ENOENT\"\n ))\n throw cause;\n }\n removed = true;\n }),\n };\n}\n"]}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { Transport } from "../domain/transport.types.ts";
|
|
1
2
|
import type { LocalProcessIdentity, LockOwnership } from "./git/process-identity.types.ts";
|
|
2
3
|
import type { StorageIssue } from "./storage-inventory.types.ts";
|
|
3
4
|
import type { resourceOperationKinds, resourcePhases } from "./resource-activity.constants.ts";
|
|
@@ -35,6 +36,7 @@ export interface ResourceActivity {
|
|
|
35
36
|
remove(): Promise<void>;
|
|
36
37
|
}
|
|
37
38
|
export interface ResourceActivityOptions {
|
|
39
|
+
readonly transporter?: Transport;
|
|
38
40
|
readonly repository: string;
|
|
39
41
|
readonly workspace: string;
|
|
40
42
|
readonly provider: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resource-activity.types.js","sourceRoot":"","sources":["../../src/infrastructure/resource-activity.types.ts"],"names":[],"mappings":"","sourcesContent":["import type {\n LocalProcessIdentity,\n LockOwnership,\n} from \"./git/process-identity.types.ts\";\nimport type { StorageIssue } from \"./storage-inventory.types.ts\";\nimport type {\n resourceOperationKinds,\n resourcePhases,\n} from \"./resource-activity.constants.ts\";\n\nexport type ResourcePhase = (typeof resourcePhases)[number];\nexport type ResourceOperationKind = (typeof resourceOperationKinds)[number];\nexport interface ResourceOperation {\n readonly count: number;\n readonly kind: ResourceOperationKind;\n readonly startedAt: string;\n}\nexport interface ResourceOperationResult extends ResourceOperation {\n readonly id: string;\n readonly finishedAt: string;\n readonly outcome: \"completed\" | \"failed\";\n}\nexport interface ResourceActivityRecord {\n readonly version: 1;\n readonly id: string;\n readonly pid: number;\n readonly identity?: LocalProcessIdentity;\n readonly provider: string;\n readonly placement: \"mounted\" | \"remote\" | \"host\";\n readonly workspace: string;\n readonly createdAt: string;\n readonly updatedAt: string;\n readonly phase: ResourcePhase;\n readonly operations: readonly ResourceOperation[];\n readonly lastOperation?: ResourceOperationResult;\n readonly lastFailure?: ResourceOperationResult;\n}\nexport interface ResourceActivity {\n idle(): Promise<boolean>;\n phase(phase: ResourcePhase): Promise<void>;\n run<T>(kind: ResourceOperationKind, action: () => Promise<T>): Promise<T>;\n remove(): Promise<void>;\n}\nexport interface ResourceActivityOptions {\n readonly repository: string;\n readonly workspace: string;\n readonly provider: string;\n readonly placement: ResourceActivityRecord[\"placement\"];\n}\nexport interface ResourceInspectionEntry {\n readonly path: string;\n readonly record?: ResourceActivityRecord;\n readonly ownership: LockOwnership;\n}\nexport interface ResourceInspection {\n readonly scope: \"recorded-sandboxes\";\n readonly complete: boolean;\n readonly entries: readonly ResourceInspectionEntry[];\n readonly issues: readonly StorageIssue[];\n}\n"]}
|
|
1
|
+
{"version":3,"file":"resource-activity.types.js","sourceRoot":"","sources":["../../src/infrastructure/resource-activity.types.ts"],"names":[],"mappings":"","sourcesContent":["import type { Transport } from \"../domain/transport.types.ts\";\nimport type {\n LocalProcessIdentity,\n LockOwnership,\n} from \"./git/process-identity.types.ts\";\nimport type { StorageIssue } from \"./storage-inventory.types.ts\";\nimport type {\n resourceOperationKinds,\n resourcePhases,\n} from \"./resource-activity.constants.ts\";\n\nexport type ResourcePhase = (typeof resourcePhases)[number];\nexport type ResourceOperationKind = (typeof resourceOperationKinds)[number];\nexport interface ResourceOperation {\n readonly count: number;\n readonly kind: ResourceOperationKind;\n readonly startedAt: string;\n}\nexport interface ResourceOperationResult extends ResourceOperation {\n readonly id: string;\n readonly finishedAt: string;\n readonly outcome: \"completed\" | \"failed\";\n}\nexport interface ResourceActivityRecord {\n readonly version: 1;\n readonly id: string;\n readonly pid: number;\n readonly identity?: LocalProcessIdentity;\n readonly provider: string;\n readonly placement: \"mounted\" | \"remote\" | \"host\";\n readonly workspace: string;\n readonly createdAt: string;\n readonly updatedAt: string;\n readonly phase: ResourcePhase;\n readonly operations: readonly ResourceOperation[];\n readonly lastOperation?: ResourceOperationResult;\n readonly lastFailure?: ResourceOperationResult;\n}\nexport interface ResourceActivity {\n idle(): Promise<boolean>;\n phase(phase: ResourcePhase): Promise<void>;\n run<T>(kind: ResourceOperationKind, action: () => Promise<T>): Promise<T>;\n remove(): Promise<void>;\n}\nexport interface ResourceActivityOptions {\n readonly transporter?: Transport;\n readonly repository: string;\n readonly workspace: string;\n readonly provider: string;\n readonly placement: ResourceActivityRecord[\"placement\"];\n}\nexport interface ResourceInspectionEntry {\n readonly path: string;\n readonly record?: ResourceActivityRecord;\n readonly ownership: LockOwnership;\n}\nexport interface ResourceInspection {\n readonly scope: \"recorded-sandboxes\";\n readonly complete: boolean;\n readonly entries: readonly ResourceInspectionEntry[];\n readonly issues: readonly StorageIssue[];\n}\n"]}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { Transport } from "../domain/transport.types.ts";
|
|
2
|
+
import type { S3TransportOptions } from "./s3-transport.types.ts";
|
|
3
|
+
export type { S3TransportOptions } from "./s3-transport.types.ts";
|
|
4
|
+
export declare function s3Transport(options: S3TransportOptions): Transport;
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { GetObjectCommand, PutObjectCommand, DeleteObjectCommand, ListObjectsV2Command, } from "@aws-sdk/client-s3";
|
|
2
|
+
import { TransportConflict, transportKey, transportCondition, } from "../domain/transport.js";
|
|
3
|
+
import { decodeObject, encodeObject, readLimit } from "./transport-envelope.js";
|
|
4
|
+
import { transportDefaults } from "./transport.constants.js";
|
|
5
|
+
function status(error) {
|
|
6
|
+
if (!error || typeof error !== "object" || !("$metadata" in error))
|
|
7
|
+
return undefined;
|
|
8
|
+
const meta = error.$metadata;
|
|
9
|
+
return meta &&
|
|
10
|
+
typeof meta === "object" &&
|
|
11
|
+
"httpStatusCode" in meta &&
|
|
12
|
+
typeof meta.httpStatusCode === "number"
|
|
13
|
+
? meta.httpStatusCode
|
|
14
|
+
: undefined;
|
|
15
|
+
}
|
|
16
|
+
export function s3Transport(options) {
|
|
17
|
+
if (!options.bucket.trim())
|
|
18
|
+
throw new Error("S3 bucket must not be empty");
|
|
19
|
+
const prefix = options.prefix
|
|
20
|
+
? `${transportKey(options.prefix.replace(/\/$/, ""))}/`
|
|
21
|
+
: "";
|
|
22
|
+
const target = (key) => ({
|
|
23
|
+
Bucket: options.bucket,
|
|
24
|
+
Key: `${prefix}${transportKey(key)}`,
|
|
25
|
+
});
|
|
26
|
+
return {
|
|
27
|
+
name: "s3",
|
|
28
|
+
async read(key, settings = {}) {
|
|
29
|
+
settings.signal?.throwIfAborted();
|
|
30
|
+
const maxBytes = readLimit(settings.maxBytes);
|
|
31
|
+
try {
|
|
32
|
+
const value = await options.client.send(new GetObjectCommand(target(key)), settings.signal ? { abortSignal: settings.signal } : {});
|
|
33
|
+
if (!value.Body)
|
|
34
|
+
throw new Error("Missing S3 object body");
|
|
35
|
+
const reader = value.Body.transformToWebStream().getReader();
|
|
36
|
+
try {
|
|
37
|
+
if (!value.ETag ||
|
|
38
|
+
(value.ContentLength ?? Infinity) >
|
|
39
|
+
maxBytes + transportDefaults.headerBytes)
|
|
40
|
+
throw new Error("Invalid or oversized S3 object");
|
|
41
|
+
const chunks = [];
|
|
42
|
+
let size = 0;
|
|
43
|
+
for (;;) {
|
|
44
|
+
settings.signal?.throwIfAborted();
|
|
45
|
+
const chunk = await reader.read();
|
|
46
|
+
if (chunk.done)
|
|
47
|
+
break;
|
|
48
|
+
size += chunk.value.byteLength;
|
|
49
|
+
if (size > maxBytes + transportDefaults.headerBytes)
|
|
50
|
+
throw new Error("S3 object exceeds byte limit");
|
|
51
|
+
chunks.push(chunk.value);
|
|
52
|
+
}
|
|
53
|
+
return {
|
|
54
|
+
...decodeObject(Buffer.concat(chunks), key, maxBytes),
|
|
55
|
+
revision: value.ETag,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
finally {
|
|
59
|
+
await reader.cancel().catch(() => { });
|
|
60
|
+
reader.releaseLock();
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
if (status(error) === 404)
|
|
65
|
+
return undefined;
|
|
66
|
+
throw error;
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
async write(key, bytes, settings) {
|
|
70
|
+
transportCondition(settings.ifRevision);
|
|
71
|
+
settings.signal?.throwIfAborted();
|
|
72
|
+
const { entry, data } = encodeObject(key, bytes);
|
|
73
|
+
try {
|
|
74
|
+
const value = await options.client.send(new PutObjectCommand({
|
|
75
|
+
...target(key),
|
|
76
|
+
Body: data,
|
|
77
|
+
...(settings.ifRevision === null
|
|
78
|
+
? { IfNoneMatch: "*" }
|
|
79
|
+
: { IfMatch: settings.ifRevision }),
|
|
80
|
+
}), settings.signal ? { abortSignal: settings.signal } : {});
|
|
81
|
+
if (!value.ETag)
|
|
82
|
+
throw new Error("Missing S3 revision after write");
|
|
83
|
+
return { ...entry, revision: value.ETag };
|
|
84
|
+
}
|
|
85
|
+
catch (error) {
|
|
86
|
+
if ([409, 412].includes(status(error) ?? 0))
|
|
87
|
+
throw new TransportConflict(key);
|
|
88
|
+
throw error;
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
async remove(key, settings) {
|
|
92
|
+
transportCondition(settings.ifRevision);
|
|
93
|
+
settings.signal?.throwIfAborted();
|
|
94
|
+
if (settings.ifRevision === null)
|
|
95
|
+
throw new Error("S3 removal requires an existing revision");
|
|
96
|
+
try {
|
|
97
|
+
await options.client.send(new DeleteObjectCommand({
|
|
98
|
+
...target(key),
|
|
99
|
+
IfMatch: settings.ifRevision,
|
|
100
|
+
}), settings.signal ? { abortSignal: settings.signal } : {});
|
|
101
|
+
}
|
|
102
|
+
catch (error) {
|
|
103
|
+
if ([404, 409, 412].includes(status(error) ?? 0))
|
|
104
|
+
throw new TransportConflict(key);
|
|
105
|
+
throw error;
|
|
106
|
+
}
|
|
107
|
+
},
|
|
108
|
+
async *list(filter = "", settings = {}) {
|
|
109
|
+
if (filter)
|
|
110
|
+
transportKey(filter.replace(/\/$/, ""));
|
|
111
|
+
let cursor;
|
|
112
|
+
const seen = new Set();
|
|
113
|
+
do {
|
|
114
|
+
settings.signal?.throwIfAborted();
|
|
115
|
+
const page = await options.client.send(new ListObjectsV2Command({
|
|
116
|
+
Bucket: options.bucket,
|
|
117
|
+
Prefix: prefix + filter,
|
|
118
|
+
...(cursor ? { ContinuationToken: cursor } : {}),
|
|
119
|
+
}), settings.signal ? { abortSignal: settings.signal } : {});
|
|
120
|
+
for (const item of page.Contents ?? []) {
|
|
121
|
+
if (!item.Key?.startsWith(prefix + filter) ||
|
|
122
|
+
!item.ETag ||
|
|
123
|
+
!item.LastModified ||
|
|
124
|
+
item.Size === undefined ||
|
|
125
|
+
item.Size < transportDefaults.headerBytes)
|
|
126
|
+
throw new Error("Invalid S3 listing entry");
|
|
127
|
+
const key = transportKey(item.Key.slice(prefix.length));
|
|
128
|
+
yield {
|
|
129
|
+
key,
|
|
130
|
+
revision: item.ETag,
|
|
131
|
+
size: item.Size - transportDefaults.headerBytes,
|
|
132
|
+
modifiedAt: item.LastModified.toISOString(),
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
cursor = page.IsTruncated ? page.NextContinuationToken : undefined;
|
|
136
|
+
if (page.IsTruncated && (!cursor || seen.has(cursor)))
|
|
137
|
+
throw new Error("Invalid S3 pagination cursor");
|
|
138
|
+
if (cursor)
|
|
139
|
+
seen.add(cursor);
|
|
140
|
+
} while (cursor);
|
|
141
|
+
},
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
//# sourceMappingURL=s3-transport.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"s3-transport.js","sourceRoot":"","sources":["../../src/infrastructure/s3-transport.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n GetObjectCommand,\n PutObjectCommand,\n DeleteObjectCommand,\n ListObjectsV2Command,\n} from \"@aws-sdk/client-s3\";\nimport {\n TransportConflict,\n transportKey,\n transportCondition,\n} from \"../domain/transport.ts\";\nimport type { Transport } from \"../domain/transport.types.ts\";\nimport type { S3TransportOptions } from \"./s3-transport.types.ts\";\nimport { decodeObject, encodeObject, readLimit } from \"./transport-envelope.ts\";\nimport { transportDefaults } from \"./transport.constants.ts\";\n\nexport type { S3TransportOptions } from \"./s3-transport.types.ts\";\n\nfunction status(error: unknown): number | undefined {\n if (!error || typeof error !== \"object\" || !(\"$metadata\" in error))\n return undefined;\n const meta = error.$metadata;\n return meta &&\n typeof meta === \"object\" &&\n \"httpStatusCode\" in meta &&\n typeof meta.httpStatusCode === \"number\"\n ? meta.httpStatusCode\n : undefined;\n}\n\nexport function s3Transport(options: S3TransportOptions): Transport {\n if (!options.bucket.trim()) throw new Error(\"S3 bucket must not be empty\");\n const prefix = options.prefix\n ? `${transportKey(options.prefix.replace(/\\/$/, \"\"))}/`\n : \"\";\n const target = (key: string) => ({\n Bucket: options.bucket,\n Key: `${prefix}${transportKey(key)}`,\n });\n return {\n name: \"s3\",\n async read(key, settings = {}) {\n settings.signal?.throwIfAborted();\n const maxBytes = readLimit(settings.maxBytes);\n try {\n const value = await options.client.send(\n new GetObjectCommand(target(key)),\n settings.signal ? { abortSignal: settings.signal } : {},\n );\n if (!value.Body) throw new Error(\"Missing S3 object body\");\n const reader = value.Body.transformToWebStream().getReader();\n try {\n if (\n !value.ETag ||\n (value.ContentLength ?? Infinity) >\n maxBytes + transportDefaults.headerBytes\n )\n throw new Error(\"Invalid or oversized S3 object\");\n const chunks: Uint8Array[] = [];\n let size = 0;\n for (;;) {\n settings.signal?.throwIfAborted();\n const chunk = await reader.read();\n if (chunk.done) break;\n size += chunk.value.byteLength;\n if (size > maxBytes + transportDefaults.headerBytes)\n throw new Error(\"S3 object exceeds byte limit\");\n chunks.push(chunk.value);\n }\n return {\n ...decodeObject(Buffer.concat(chunks), key, maxBytes),\n revision: value.ETag,\n };\n } finally {\n await reader.cancel().catch(() => {});\n reader.releaseLock();\n }\n } catch (error) {\n if (status(error) === 404) return undefined;\n throw error;\n }\n },\n async write(key, bytes, settings) {\n transportCondition(settings.ifRevision);\n settings.signal?.throwIfAborted();\n const { entry, data } = encodeObject(key, bytes);\n try {\n const value = await options.client.send(\n new PutObjectCommand({\n ...target(key),\n Body: data,\n ...(settings.ifRevision === null\n ? { IfNoneMatch: \"*\" }\n : { IfMatch: settings.ifRevision }),\n }),\n settings.signal ? { abortSignal: settings.signal } : {},\n );\n if (!value.ETag) throw new Error(\"Missing S3 revision after write\");\n return { ...entry, revision: value.ETag };\n } catch (error) {\n if ([409, 412].includes(status(error) ?? 0))\n throw new TransportConflict(key);\n throw error;\n }\n },\n async remove(key, settings) {\n transportCondition(settings.ifRevision);\n settings.signal?.throwIfAborted();\n if (settings.ifRevision === null)\n throw new Error(\"S3 removal requires an existing revision\");\n try {\n await options.client.send(\n new DeleteObjectCommand({\n ...target(key),\n IfMatch: settings.ifRevision,\n }),\n settings.signal ? { abortSignal: settings.signal } : {},\n );\n } catch (error) {\n if ([404, 409, 412].includes(status(error) ?? 0))\n throw new TransportConflict(key);\n throw error;\n }\n },\n async *list(filter = \"\", settings = {}) {\n if (filter) transportKey(filter.replace(/\\/$/, \"\"));\n let cursor: string | undefined;\n const seen = new Set<string>();\n do {\n settings.signal?.throwIfAborted();\n const page = await options.client.send(\n new ListObjectsV2Command({\n Bucket: options.bucket,\n Prefix: prefix + filter,\n ...(cursor ? { ContinuationToken: cursor } : {}),\n }),\n settings.signal ? { abortSignal: settings.signal } : {},\n );\n for (const item of page.Contents ?? []) {\n if (\n !item.Key?.startsWith(prefix + filter) ||\n !item.ETag ||\n !item.LastModified ||\n item.Size === undefined ||\n item.Size < transportDefaults.headerBytes\n )\n throw new Error(\"Invalid S3 listing entry\");\n const key = transportKey(item.Key.slice(prefix.length));\n yield {\n key,\n revision: item.ETag,\n size: item.Size - transportDefaults.headerBytes,\n modifiedAt: item.LastModified.toISOString(),\n };\n }\n cursor = page.IsTruncated ? page.NextContinuationToken : undefined;\n if (page.IsTruncated && (!cursor || seen.has(cursor)))\n throw new Error(\"Invalid S3 pagination cursor\");\n if (cursor) seen.add(cursor);\n } while (cursor);\n },\n };\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"s3-transport.types.js","sourceRoot":"","sources":["../../src/infrastructure/s3-transport.types.ts"],"names":[],"mappings":"","sourcesContent":["import type { S3Client } from \"@aws-sdk/client-s3\";\n\nexport interface S3TransportOptions {\n readonly client: S3Client;\n readonly bucket: string;\n readonly prefix?: string;\n}\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type StorageCategoryName = "recovery" | "logs" | "locks" | "workspaces";
|
|
1
|
+
export type StorageCategoryName = "recovery" | "logs" | "locks" | "workspaces" | "artifacts" | "checkpoints" | "conversations" | "reservations" | "resources";
|
|
2
2
|
export type StorageEntryKind = "file" | "directory" | "symlink" | "other" | "unknown";
|
|
3
3
|
export interface StorageIssue {
|
|
4
4
|
readonly path: string;
|
|
@@ -12,6 +12,7 @@ export interface StorageUsage {
|
|
|
12
12
|
other: number;
|
|
13
13
|
}
|
|
14
14
|
export interface StorageEntry extends StorageUsage {
|
|
15
|
+
readonly revision?: string;
|
|
15
16
|
readonly name: string;
|
|
16
17
|
readonly path: string;
|
|
17
18
|
kind: StorageEntryKind;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"storage-inventory.types.js","sourceRoot":"","sources":["../../src/infrastructure/storage-inventory.types.ts"],"names":[],"mappings":"","sourcesContent":["export type StorageCategoryName
|
|
1
|
+
{"version":3,"file":"storage-inventory.types.js","sourceRoot":"","sources":["../../src/infrastructure/storage-inventory.types.ts"],"names":[],"mappings":"","sourcesContent":["export type StorageCategoryName =\n | \"recovery\"\n | \"logs\"\n | \"locks\"\n | \"workspaces\"\n | \"artifacts\"\n | \"checkpoints\"\n | \"conversations\"\n | \"reservations\"\n | \"resources\";\nexport type StorageEntryKind =\n \"file\" | \"directory\" | \"symlink\" | \"other\" | \"unknown\";\n\nexport interface StorageIssue {\n readonly path: string;\n readonly code: string;\n}\nexport interface StorageUsage {\n bytes: number;\n files: number;\n directories: number;\n symlinks: number;\n other: number;\n}\nexport interface StorageEntry extends StorageUsage {\n readonly revision?: string;\n readonly name: string;\n readonly path: string;\n kind: StorageEntryKind;\n modifiedAt?: string;\n complete: boolean;\n}\nexport interface StorageCategory {\n readonly name: StorageCategoryName;\n readonly path: string;\n readonly entries: readonly StorageEntry[];\n}\nexport interface StorageInventory {\n readonly root: string;\n readonly categories: readonly StorageCategory[];\n readonly usage: Readonly<StorageUsage>;\n readonly issues: readonly StorageIssue[];\n readonly complete: boolean;\n readonly scannedEntries: number;\n readonly maxEntries: number;\n}\nexport interface StorageScan {\n remaining: number;\n readonly issues: StorageIssue[];\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"storage-reservations.types.js","sourceRoot":"","sources":["../../src/infrastructure/storage-reservations.types.ts"],"names":[],"mappings":"","sourcesContent":["
|
|
1
|
+
{"version":3,"file":"storage-reservations.types.js","sourceRoot":"","sources":["../../src/infrastructure/storage-reservations.types.ts"],"names":[],"mappings":"","sourcesContent":["import type { Transport } from \"../domain/transport.types.ts\";\n\nexport interface StorageReservationOptions {\n readonly transporter?: Transport;\n readonly maxBytes: number;\n readonly reserveBytes: number;\n readonly maxEntries?: number;\n readonly signal?: AbortSignal;\n}\n\nexport interface StorageReservation {\n readonly id: string;\n readonly repository: string;\n readonly reserveBytes: number;\n release(): Promise<void>;\n [Symbol.asyncDispose](): Promise<void>;\n}\n\nexport interface StorageReservationRecord {\n readonly version: 1;\n readonly id: string;\n readonly pid: number;\n readonly identity?: unknown;\n readonly reserveBytes: number;\n readonly createdAt: string;\n}\n"]}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Queue } from "bullmq";
|
|
2
|
+
import type { QueueRequest, QueueResult } from "../domain/task-queue.types.ts";
|
|
3
|
+
import type { BullMQTaskQueueOptions, BullMQHandlerQueue } from "./task-queue-bullmq.types.ts";
|
|
4
|
+
export declare function openBullMQResources(options: BullMQTaskQueueOptions): Promise<{
|
|
5
|
+
root: Queue<QueueRequest, QueueResult, string, QueueRequest, QueueResult, string>;
|
|
6
|
+
prefix: string;
|
|
7
|
+
nativeName: (handler: string) => string;
|
|
8
|
+
nativeKey: (handler: string, id: string) => string;
|
|
9
|
+
handlerQueue: (handler: string) => Promise<BullMQHandlerQueue>;
|
|
10
|
+
perform: <T>(operation: () => Promise<T>) => Promise<T>;
|
|
11
|
+
report: (error: unknown) => void;
|
|
12
|
+
close(): Promise<void>;
|
|
13
|
+
}>;
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { Queue, Worker } from "bullmq";
|
|
2
|
+
import { queueNumber, queueString } from "../domain/task-queue.js";
|
|
3
|
+
import { bullMQKey } from "./task-queue-bullmq-state.js";
|
|
4
|
+
import { bullMQConnectionTimeoutMs, bullMQConnectionRetries, bullMQRetryDelayMs, bullMQDefaultPrefix, bullMQDefaultStalledIntervalMs, } from "./task-queue-bullmq.constants.js";
|
|
5
|
+
export async function openBullMQResources(options) {
|
|
6
|
+
const name = bullMQKey(queueString(options.name));
|
|
7
|
+
const prefix = queueString(options.prefix ?? bullMQDefaultPrefix);
|
|
8
|
+
const stalledInterval = queueNumber(options.stalledIntervalMs ?? bullMQDefaultStalledIntervalMs);
|
|
9
|
+
if (!stalledInterval)
|
|
10
|
+
throw new Error("BullMQ stalled interval must be positive");
|
|
11
|
+
if (options.connection.keyPrefix)
|
|
12
|
+
throw new Error("Use the BullMQ queue prefix instead of connection.keyPrefix");
|
|
13
|
+
const connection = {
|
|
14
|
+
connectTimeout: bullMQConnectionTimeoutMs,
|
|
15
|
+
commandTimeout: bullMQConnectionTimeoutMs,
|
|
16
|
+
retryStrategy: (attempt) => attempt <= bullMQConnectionRetries ? attempt * bullMQRetryDelayMs : null,
|
|
17
|
+
...options.connection,
|
|
18
|
+
maxRetriesPerRequest: 1,
|
|
19
|
+
};
|
|
20
|
+
const root = new Queue(name, {
|
|
21
|
+
connection,
|
|
22
|
+
prefix,
|
|
23
|
+
});
|
|
24
|
+
const handlers = new Map();
|
|
25
|
+
const pending = new Set();
|
|
26
|
+
let closing;
|
|
27
|
+
function report(error) {
|
|
28
|
+
try {
|
|
29
|
+
options.onError?.(error instanceof Error ? error : new Error(String(error)));
|
|
30
|
+
}
|
|
31
|
+
catch { }
|
|
32
|
+
}
|
|
33
|
+
root.on("error", report);
|
|
34
|
+
try {
|
|
35
|
+
await root.waitUntilReady();
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
await root.close();
|
|
39
|
+
throw error;
|
|
40
|
+
}
|
|
41
|
+
function nativeName(handler) {
|
|
42
|
+
return `${name}-${bullMQKey(handler)}`;
|
|
43
|
+
}
|
|
44
|
+
function nativeKey(handler, id) {
|
|
45
|
+
return `${prefix}:${nativeName(handler)}:${bullMQKey(id)}`;
|
|
46
|
+
}
|
|
47
|
+
function handlerQueue(handler) {
|
|
48
|
+
const existing = handlers.get(handler);
|
|
49
|
+
if (existing)
|
|
50
|
+
return existing;
|
|
51
|
+
const created = (async () => {
|
|
52
|
+
const queue = new Queue(nativeName(handler), {
|
|
53
|
+
connection,
|
|
54
|
+
prefix,
|
|
55
|
+
});
|
|
56
|
+
const worker = new Worker(nativeName(handler), null, {
|
|
57
|
+
connection: { ...connection, maxRetriesPerRequest: null },
|
|
58
|
+
prefix,
|
|
59
|
+
autorun: false,
|
|
60
|
+
skipLockRenewal: true,
|
|
61
|
+
stalledInterval,
|
|
62
|
+
maxStalledCount: Number.MAX_SAFE_INTEGER,
|
|
63
|
+
});
|
|
64
|
+
queue.on("error", report);
|
|
65
|
+
worker.on("error", report);
|
|
66
|
+
try {
|
|
67
|
+
await Promise.all([queue.waitUntilReady(), worker.waitUntilReady()]);
|
|
68
|
+
await worker.startStalledCheckTimer();
|
|
69
|
+
return { queue, worker };
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
await Promise.all([queue.close(), worker.close(true)]);
|
|
73
|
+
throw error;
|
|
74
|
+
}
|
|
75
|
+
})();
|
|
76
|
+
handlers.set(handler, created);
|
|
77
|
+
void created.catch(() => {
|
|
78
|
+
if (handlers.get(handler) === created)
|
|
79
|
+
handlers.delete(handler);
|
|
80
|
+
});
|
|
81
|
+
return created;
|
|
82
|
+
}
|
|
83
|
+
function perform(operation) {
|
|
84
|
+
if (closing)
|
|
85
|
+
return Promise.reject(new Error("BullMQ task queue is closed"));
|
|
86
|
+
const promise = operation();
|
|
87
|
+
pending.add(promise);
|
|
88
|
+
void promise.then(() => pending.delete(promise), () => pending.delete(promise));
|
|
89
|
+
return promise;
|
|
90
|
+
}
|
|
91
|
+
return {
|
|
92
|
+
root,
|
|
93
|
+
prefix,
|
|
94
|
+
nativeName,
|
|
95
|
+
nativeKey,
|
|
96
|
+
handlerQueue,
|
|
97
|
+
perform,
|
|
98
|
+
report,
|
|
99
|
+
close() {
|
|
100
|
+
closing ??= (async () => {
|
|
101
|
+
await Promise.allSettled([...pending]);
|
|
102
|
+
const opened = await Promise.allSettled([...handlers.values()]);
|
|
103
|
+
const resources = opened.flatMap((entry) => entry.status === "fulfilled"
|
|
104
|
+
? [entry.value.worker, entry.value.queue]
|
|
105
|
+
: []);
|
|
106
|
+
const results = await Promise.allSettled(resources.map((resource) => resource.close()));
|
|
107
|
+
await root.close();
|
|
108
|
+
const errors = results
|
|
109
|
+
.filter((entry) => entry.status === "rejected")
|
|
110
|
+
.map((entry) => entry.reason);
|
|
111
|
+
if (errors.length)
|
|
112
|
+
throw new AggregateError(errors, "Failed to close BullMQ task queue");
|
|
113
|
+
})();
|
|
114
|
+
return closing;
|
|
115
|
+
},
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
//# sourceMappingURL=task-queue-bullmq-resources.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"task-queue-bullmq-resources.js","sourceRoot":"","sources":["../../src/infrastructure/task-queue-bullmq-resources.ts"],"names":[],"mappings":"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","sourcesContent":["import { Queue, Worker } from \"bullmq\";\nimport { queueNumber, queueString } from \"../domain/task-queue.ts\";\nimport type { QueueRequest, QueueResult } from \"../domain/task-queue.types.ts\";\nimport type {\n BullMQTaskQueueOptions,\n BullMQHandlerQueue,\n} from \"./task-queue-bullmq.types.ts\";\nimport { bullMQKey } from \"./task-queue-bullmq-state.ts\";\nimport {\n bullMQConnectionTimeoutMs,\n bullMQConnectionRetries,\n bullMQRetryDelayMs,\n bullMQDefaultPrefix,\n bullMQDefaultStalledIntervalMs,\n} from \"./task-queue-bullmq.constants.ts\";\n\nexport async function openBullMQResources(options: BullMQTaskQueueOptions) {\n const name = bullMQKey(queueString(options.name));\n const prefix = queueString(options.prefix ?? bullMQDefaultPrefix);\n const stalledInterval = queueNumber(\n options.stalledIntervalMs ?? bullMQDefaultStalledIntervalMs,\n );\n if (!stalledInterval)\n throw new Error(\"BullMQ stalled interval must be positive\");\n if (options.connection.keyPrefix)\n throw new Error(\n \"Use the BullMQ queue prefix instead of connection.keyPrefix\",\n );\n const connection = {\n connectTimeout: bullMQConnectionTimeoutMs,\n commandTimeout: bullMQConnectionTimeoutMs,\n retryStrategy: (attempt: number) =>\n attempt <= bullMQConnectionRetries ? attempt * bullMQRetryDelayMs : null,\n ...options.connection,\n maxRetriesPerRequest: 1,\n };\n const root = new Queue<QueueRequest, QueueResult>(name, {\n connection,\n prefix,\n });\n const handlers = new Map<string, Promise<BullMQHandlerQueue>>();\n const pending = new Set<Promise<unknown>>();\n let closing: Promise<void> | undefined;\n function report(error: unknown) {\n try {\n options.onError?.(\n error instanceof Error ? error : new Error(String(error)),\n );\n } catch {}\n }\n root.on(\"error\", report);\n try {\n await root.waitUntilReady();\n } catch (error) {\n await root.close();\n throw error;\n }\n function nativeName(handler: string) {\n return `${name}-${bullMQKey(handler)}`;\n }\n function nativeKey(handler: string, id: string) {\n return `${prefix}:${nativeName(handler)}:${bullMQKey(id)}`;\n }\n function handlerQueue(handler: string) {\n const existing = handlers.get(handler);\n if (existing) return existing;\n const created = (async () => {\n const queue = new Queue<QueueRequest, QueueResult>(nativeName(handler), {\n connection,\n prefix,\n });\n const worker = new Worker<QueueRequest, QueueResult>(\n nativeName(handler),\n null,\n {\n connection: { ...connection, maxRetriesPerRequest: null },\n prefix,\n autorun: false,\n skipLockRenewal: true,\n stalledInterval,\n maxStalledCount: Number.MAX_SAFE_INTEGER,\n },\n );\n queue.on(\"error\", report);\n worker.on(\"error\", report);\n try {\n await Promise.all([queue.waitUntilReady(), worker.waitUntilReady()]);\n await worker.startStalledCheckTimer();\n return { queue, worker };\n } catch (error) {\n await Promise.all([queue.close(), worker.close(true)]);\n throw error;\n }\n })();\n handlers.set(handler, created);\n void created.catch(() => {\n if (handlers.get(handler) === created) handlers.delete(handler);\n });\n return created;\n }\n function perform<T>(operation: () => Promise<T>): Promise<T> {\n if (closing)\n return Promise.reject(new Error(\"BullMQ task queue is closed\"));\n const promise = operation();\n pending.add(promise);\n void promise.then(\n () => pending.delete(promise),\n () => pending.delete(promise),\n );\n return promise;\n }\n return {\n root,\n prefix,\n nativeName,\n nativeKey,\n handlerQueue,\n perform,\n report,\n close() {\n closing ??= (async () => {\n await Promise.allSettled([...pending]);\n const opened = await Promise.allSettled([...handlers.values()]);\n const resources = opened.flatMap((entry) =>\n entry.status === \"fulfilled\"\n ? [entry.value.worker, entry.value.queue]\n : [],\n );\n const results = await Promise.allSettled(\n resources.map((resource) => resource.close()),\n );\n await root.close();\n const errors = results\n .filter((entry) => entry.status === \"rejected\")\n .map((entry) => entry.reason);\n if (errors.length)\n throw new AggregateError(errors, \"Failed to close BullMQ task queue\");\n })();\n return closing;\n },\n };\n}\n"]}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { queueJob, queueObject, queueString } from "../domain/task-queue.js";
|
|
3
|
+
export function bullMQKey(value) {
|
|
4
|
+
return createHash("sha256").update(value).digest("hex");
|
|
5
|
+
}
|
|
6
|
+
export function bullMQState(value) {
|
|
7
|
+
if (value === null)
|
|
8
|
+
return undefined;
|
|
9
|
+
if (!Array.isArray(value) || value.length !== 3)
|
|
10
|
+
throw new Error("Invalid BullMQ queue state");
|
|
11
|
+
const [request, metadata, result] = value;
|
|
12
|
+
if (typeof request !== "string" ||
|
|
13
|
+
typeof metadata !== "string" ||
|
|
14
|
+
typeof result !== "string")
|
|
15
|
+
throw new Error("Invalid BullMQ queue state");
|
|
16
|
+
const meta = queueObject(JSON.parse(metadata));
|
|
17
|
+
return {
|
|
18
|
+
job: queueJob({
|
|
19
|
+
...queueObject(JSON.parse(request)),
|
|
20
|
+
...meta,
|
|
21
|
+
...(result ? { result: JSON.parse(result) } : {}),
|
|
22
|
+
}),
|
|
23
|
+
token: meta.token === undefined ? undefined : queueString(meta.token),
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=task-queue-bullmq-state.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"task-queue-bullmq-state.js","sourceRoot":"","sources":["../../src/infrastructure/task-queue-bullmq-state.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAE7E,MAAM,UAAU,SAAS,CAAC,KAAa;IACrC,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAC1D,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,KAAc;IACxC,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,SAAS,CAAC;IACrC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAC7C,MAAM,IAAI,KAAK,CAAC,4BAA4B,CAAC,CAAC;IAChD,MAAM,CAAC,OAAO,EAAE,QAAQ,EAAE,MAAM,CAAC,GAAc,KAAK,CAAC;IACrD,IACE,OAAO,OAAO,KAAK,QAAQ;QAC3B,OAAO,QAAQ,KAAK,QAAQ;QAC5B,OAAO,MAAM,KAAK,QAAQ;QAE1B,MAAM,IAAI,KAAK,CAAC,4BAA4B,CAAC,CAAC;IAChD,MAAM,IAAI,GAAG,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC;IAC/C,OAAO;QACL,GAAG,EAAE,QAAQ,CAAC;YACZ,GAAG,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YACnC,GAAG,IAAI;YACP,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAClD,CAAC;QACF,KAAK,EAAE,IAAI,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC;KACtE,CAAC;AACJ,CAAC","sourcesContent":["import { createHash } from \"node:crypto\";\nimport { queueJob, queueObject, queueString } from \"../domain/task-queue.ts\";\n\nexport function bullMQKey(value: string): string {\n return createHash(\"sha256\").update(value).digest(\"hex\");\n}\n\nexport function bullMQState(value: unknown) {\n if (value === null) return undefined;\n if (!Array.isArray(value) || value.length !== 3)\n throw new Error(\"Invalid BullMQ queue state\");\n const [request, metadata, result]: unknown[] = value;\n if (\n typeof request !== \"string\" ||\n typeof metadata !== \"string\" ||\n typeof result !== \"string\"\n )\n throw new Error(\"Invalid BullMQ queue state\");\n const meta = queueObject(JSON.parse(metadata));\n return {\n job: queueJob({\n ...queueObject(JSON.parse(request)),\n ...meta,\n ...(result ? { result: JSON.parse(result) } : {}),\n }),\n token: meta.token === undefined ? undefined : queueString(meta.token),\n };\n}\n"]}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare const bullMQDefaultPrefix = "outpost";
|
|
2
|
+
export declare const bullMQDefaultStalledIntervalMs = 1000;
|
|
3
|
+
export declare const bullMQClaimBatchSize = 100;
|
|
4
|
+
export declare const bullMQConnectionTimeoutMs = 10000;
|
|
5
|
+
export declare const bullMQConnectionRetries = 3;
|
|
6
|
+
export declare const bullMQRetryDelayMs = 100;
|
|
7
|
+
export declare const bullMQStateScript = "\nlocal operation = ARGV[1]\nlocal request = redis.call('HGET', KEYS[1], 'request')\nif operation == 'enqueue' then\n if request and request ~= ARGV[2] then\n return redis.error_reply('Queue identity already has a different request')\n end\n if not request then\n request = ARGV[2]\n redis.call('HSET', KEYS[1], 'request', request, 'deadline', ARGV[3], 'meta', '{\"status\":\"pending\",\"fence\":0}')\n end\nend\nif not request then\n if operation == 'get' then return nil end\n return redis.error_reply('Queue job does not exist')\nend\nlocal meta = cjson.decode(redis.call('HGET', KEYS[1], 'meta'))\nlocal clock = redis.call('TIME')\nlocal now = tonumber(clock[1]) * 1000 + math.floor(tonumber(clock[2]) / 1000)\nlocal deadline = tonumber(redis.call('HGET', KEYS[1], 'deadline'))\nlocal function live()\n return meta.status == 'pending' or meta.status == 'active'\nend\nlocal function save()\n redis.call('HSET', KEYS[1], 'meta', cjson.encode(meta))\nend\nif live() and deadline and deadline <= now then\n meta.status = 'cancelled'\n meta.fence = meta.fence + 1\n save()\nend\nlocal function owned()\n return meta.status == 'active' and meta.worker == ARGV[2]\n and meta.fence == tonumber(ARGV[3]) and meta.expires > now\n and redis.call('GET', KEYS[2]) == meta.token\nend\nlocal function extend(duration)\n meta.expires = now + duration\n if deadline then meta.expires = math.min(meta.expires, deadline) end\n redis.call('PEXPIRE', KEYS[2], meta.expires - now)\n redis.call('SREM', KEYS[3], ARGV[5])\nend\nif operation == 'claim' and live() then\n if redis.call('GET', KEYS[2]) ~= ARGV[3] then\n return redis.error_reply('Stale queue lease')\n end\n meta.status = 'active'\n meta.worker = ARGV[2]\n meta.token = ARGV[3]\n meta.fence = meta.fence + 1\n extend(tonumber(ARGV[4]))\n save()\nend\nif operation == 'renew' or operation == 'complete' then\n if not owned() then return redis.error_reply('Stale queue lease') end\n if operation == 'renew' then extend(tonumber(ARGV[4])) end\n if operation == 'complete' then\n meta.status = ARGV[5]\n redis.call('HSET', KEYS[1], 'result', ARGV[4])\n end\n save()\nend\nif operation == 'cancel' then\n if meta.fence ~= tonumber(ARGV[2]) then return redis.error_reply('Stale queue fence') end\n if live() then\n meta.status = 'cancelled'\n meta.fence = meta.fence + 1\n save()\n end\nend\nreturn {request, cjson.encode(meta), redis.call('HGET', KEYS[1], 'result') or ''}\n";
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
export const bullMQDefaultPrefix = "outpost";
|
|
2
|
+
export const bullMQDefaultStalledIntervalMs = 1000;
|
|
3
|
+
export const bullMQClaimBatchSize = 100;
|
|
4
|
+
export const bullMQConnectionTimeoutMs = 10_000;
|
|
5
|
+
export const bullMQConnectionRetries = 3;
|
|
6
|
+
export const bullMQRetryDelayMs = 100;
|
|
7
|
+
export const bullMQStateScript = `
|
|
8
|
+
local operation = ARGV[1]
|
|
9
|
+
local request = redis.call('HGET', KEYS[1], 'request')
|
|
10
|
+
if operation == 'enqueue' then
|
|
11
|
+
if request and request ~= ARGV[2] then
|
|
12
|
+
return redis.error_reply('Queue identity already has a different request')
|
|
13
|
+
end
|
|
14
|
+
if not request then
|
|
15
|
+
request = ARGV[2]
|
|
16
|
+
redis.call('HSET', KEYS[1], 'request', request, 'deadline', ARGV[3], 'meta', '{"status":"pending","fence":0}')
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
if not request then
|
|
20
|
+
if operation == 'get' then return nil end
|
|
21
|
+
return redis.error_reply('Queue job does not exist')
|
|
22
|
+
end
|
|
23
|
+
local meta = cjson.decode(redis.call('HGET', KEYS[1], 'meta'))
|
|
24
|
+
local clock = redis.call('TIME')
|
|
25
|
+
local now = tonumber(clock[1]) * 1000 + math.floor(tonumber(clock[2]) / 1000)
|
|
26
|
+
local deadline = tonumber(redis.call('HGET', KEYS[1], 'deadline'))
|
|
27
|
+
local function live()
|
|
28
|
+
return meta.status == 'pending' or meta.status == 'active'
|
|
29
|
+
end
|
|
30
|
+
local function save()
|
|
31
|
+
redis.call('HSET', KEYS[1], 'meta', cjson.encode(meta))
|
|
32
|
+
end
|
|
33
|
+
if live() and deadline and deadline <= now then
|
|
34
|
+
meta.status = 'cancelled'
|
|
35
|
+
meta.fence = meta.fence + 1
|
|
36
|
+
save()
|
|
37
|
+
end
|
|
38
|
+
local function owned()
|
|
39
|
+
return meta.status == 'active' and meta.worker == ARGV[2]
|
|
40
|
+
and meta.fence == tonumber(ARGV[3]) and meta.expires > now
|
|
41
|
+
and redis.call('GET', KEYS[2]) == meta.token
|
|
42
|
+
end
|
|
43
|
+
local function extend(duration)
|
|
44
|
+
meta.expires = now + duration
|
|
45
|
+
if deadline then meta.expires = math.min(meta.expires, deadline) end
|
|
46
|
+
redis.call('PEXPIRE', KEYS[2], meta.expires - now)
|
|
47
|
+
redis.call('SREM', KEYS[3], ARGV[5])
|
|
48
|
+
end
|
|
49
|
+
if operation == 'claim' and live() then
|
|
50
|
+
if redis.call('GET', KEYS[2]) ~= ARGV[3] then
|
|
51
|
+
return redis.error_reply('Stale queue lease')
|
|
52
|
+
end
|
|
53
|
+
meta.status = 'active'
|
|
54
|
+
meta.worker = ARGV[2]
|
|
55
|
+
meta.token = ARGV[3]
|
|
56
|
+
meta.fence = meta.fence + 1
|
|
57
|
+
extend(tonumber(ARGV[4]))
|
|
58
|
+
save()
|
|
59
|
+
end
|
|
60
|
+
if operation == 'renew' or operation == 'complete' then
|
|
61
|
+
if not owned() then return redis.error_reply('Stale queue lease') end
|
|
62
|
+
if operation == 'renew' then extend(tonumber(ARGV[4])) end
|
|
63
|
+
if operation == 'complete' then
|
|
64
|
+
meta.status = ARGV[5]
|
|
65
|
+
redis.call('HSET', KEYS[1], 'result', ARGV[4])
|
|
66
|
+
end
|
|
67
|
+
save()
|
|
68
|
+
end
|
|
69
|
+
if operation == 'cancel' then
|
|
70
|
+
if meta.fence ~= tonumber(ARGV[2]) then return redis.error_reply('Stale queue fence') end
|
|
71
|
+
if live() then
|
|
72
|
+
meta.status = 'cancelled'
|
|
73
|
+
meta.fence = meta.fence + 1
|
|
74
|
+
save()
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
return {request, cjson.encode(meta), redis.call('HGET', KEYS[1], 'result') or ''}
|
|
78
|
+
`;
|
|
79
|
+
//# sourceMappingURL=task-queue-bullmq.constants.js.map
|