@askdkc/kiokuko 0.1.16 → 0.1.19
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/README.ja.md +91 -112
- package/README.ko.md +90 -102
- package/README.md +103 -260
- package/README.zh-CN.md +88 -102
- package/dist/agent-file/atomic-write.d.ts +90 -3
- package/dist/agent-file/atomic-write.d.ts.map +1 -1
- package/dist/agent-file/atomic-write.js +990 -14
- package/dist/agent-file/atomic-write.js.map +1 -1
- package/dist/agent-file/managed-block.d.ts +12 -1
- package/dist/agent-file/managed-block.d.ts.map +1 -1
- package/dist/agent-file/managed-block.js +49 -4
- package/dist/agent-file/managed-block.js.map +1 -1
- package/dist/agent-file/render.d.ts +1 -1
- package/dist/agent-file/render.d.ts.map +1 -1
- package/dist/agent-file/render.js +10 -7
- package/dist/agent-file/render.js.map +1 -1
- package/dist/akinator/agent-task.d.ts +14 -24
- package/dist/akinator/agent-task.d.ts.map +1 -1
- package/dist/akinator/agent-task.js +460 -103
- package/dist/akinator/agent-task.js.map +1 -1
- package/dist/akinator/capabilities.d.ts +52 -7
- package/dist/akinator/capabilities.d.ts.map +1 -1
- package/dist/akinator/capabilities.js +226 -28
- package/dist/akinator/capabilities.js.map +1 -1
- package/dist/akinator/capability-binding.d.ts +11 -0
- package/dist/akinator/capability-binding.d.ts.map +1 -0
- package/dist/akinator/capability-binding.js +56 -0
- package/dist/akinator/capability-binding.js.map +1 -0
- package/dist/akinator/domain.js +2 -2
- package/dist/akinator/domain.js.map +1 -1
- package/dist/akinator/knowledge-path.d.ts.map +1 -1
- package/dist/akinator/knowledge-path.js +409 -39
- package/dist/akinator/knowledge-path.js.map +1 -1
- package/dist/akinator/orchestrator.d.ts +3 -4
- package/dist/akinator/orchestrator.d.ts.map +1 -1
- package/dist/akinator/orchestrator.js +1 -4
- package/dist/akinator/orchestrator.js.map +1 -1
- package/dist/akinator/service.d.ts +0 -4
- package/dist/akinator/service.d.ts.map +1 -1
- package/dist/akinator/service.js +19 -35
- package/dist/akinator/service.js.map +1 -1
- package/dist/akinator/skill-discovery-attempt.d.ts +20 -0
- package/dist/akinator/skill-discovery-attempt.d.ts.map +1 -0
- package/dist/akinator/skill-discovery-attempt.js +390 -0
- package/dist/akinator/skill-discovery-attempt.js.map +1 -0
- package/dist/akinator/store.d.ts.map +1 -1
- package/dist/akinator/store.js +107 -52
- package/dist/akinator/store.js.map +1 -1
- package/dist/akinator/types.d.ts +0 -12
- package/dist/akinator/types.d.ts.map +1 -1
- package/dist/bin/kiokuko.js +0 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +561 -144
- package/dist/cli.js.map +1 -1
- package/dist/client/server-client.d.ts +5 -3
- package/dist/client/server-client.d.ts.map +1 -1
- package/dist/client/server-client.js +486 -181
- package/dist/client/server-client.js.map +1 -1
- package/dist/commands/agent.d.ts +2 -0
- package/dist/commands/agent.d.ts.map +1 -1
- package/dist/commands/agent.js +861 -86
- package/dist/commands/agent.js.map +1 -1
- package/dist/commands/backup.d.ts +2 -1
- package/dist/commands/backup.d.ts.map +1 -1
- package/dist/commands/backup.js +64 -8
- package/dist/commands/backup.js.map +1 -1
- package/dist/commands/doctor.d.ts +5 -1
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +70 -26
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/export.d.ts +0 -4
- package/dist/commands/export.d.ts.map +1 -1
- package/dist/commands/export.js +191 -32
- package/dist/commands/export.js.map +1 -1
- package/dist/commands/import.d.ts +10 -1
- package/dist/commands/import.d.ts.map +1 -1
- package/dist/commands/import.js +592 -113
- package/dist/commands/import.js.map +1 -1
- package/dist/commands/init.d.ts +19 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +569 -22
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/purge.d.ts.map +1 -1
- package/dist/commands/purge.js +3 -0
- package/dist/commands/purge.js.map +1 -1
- package/dist/commands/setup.d.ts +34 -8
- package/dist/commands/setup.d.ts.map +1 -1
- package/dist/commands/setup.js +418 -86
- package/dist/commands/setup.js.map +1 -1
- package/dist/commands/skills.d.ts +9 -0
- package/dist/commands/skills.d.ts.map +1 -0
- package/dist/commands/skills.js +317 -0
- package/dist/commands/skills.js.map +1 -0
- package/dist/commands/use.d.ts +10 -2
- package/dist/commands/use.d.ts.map +1 -1
- package/dist/commands/use.js +851 -73
- package/dist/commands/use.js.map +1 -1
- package/dist/config/paths.d.ts +11 -1
- package/dist/config/paths.d.ts.map +1 -1
- package/dist/config/paths.js +64 -29
- package/dist/config/paths.js.map +1 -1
- package/dist/config/project-config.d.ts +1 -0
- package/dist/config/project-config.d.ts.map +1 -1
- package/dist/config/project-config.js +70 -32
- package/dist/config/project-config.js.map +1 -1
- package/dist/context/broker.d.ts +30 -14
- package/dist/context/broker.d.ts.map +1 -1
- package/dist/context/broker.js +628 -150
- package/dist/context/broker.js.map +1 -1
- package/dist/context/delivery.d.ts +8 -22
- package/dist/context/delivery.d.ts.map +1 -1
- package/dist/context/delivery.js +204 -152
- package/dist/context/delivery.js.map +1 -1
- package/dist/context/feedback.d.ts +11 -12
- package/dist/context/feedback.d.ts.map +1 -1
- package/dist/context/feedback.js +250 -268
- package/dist/context/feedback.js.map +1 -1
- package/dist/context/origin.d.ts +9 -0
- package/dist/context/origin.d.ts.map +1 -0
- package/dist/context/origin.js +15 -0
- package/dist/context/origin.js.map +1 -0
- package/dist/context/ranking.d.ts +4 -13
- package/dist/context/ranking.d.ts.map +1 -1
- package/dist/context/ranking.js +21 -5
- package/dist/context/ranking.js.map +1 -1
- package/dist/context/recommendations.d.ts +0 -5
- package/dist/context/recommendations.d.ts.map +1 -1
- package/dist/context/recommendations.js +0 -1
- package/dist/context/recommendations.js.map +1 -1
- package/dist/context/run-state.d.ts +26 -0
- package/dist/context/run-state.d.ts.map +1 -0
- package/dist/context/run-state.js +399 -0
- package/dist/context/run-state.js.map +1 -0
- package/dist/context/scoped-broker.d.ts +19 -6
- package/dist/context/scoped-broker.d.ts.map +1 -1
- package/dist/context/scoped-broker.js +530 -131
- package/dist/context/scoped-broker.js.map +1 -1
- package/dist/context/selection-state.d.ts +19 -0
- package/dist/context/selection-state.d.ts.map +1 -0
- package/dist/context/selection-state.js +250 -0
- package/dist/context/selection-state.js.map +1 -0
- package/dist/db/adapter.d.ts +5 -3
- package/dist/db/adapter.d.ts.map +1 -1
- package/dist/db/adapter.js +6 -3
- package/dist/db/adapter.js.map +1 -1
- package/dist/db/capabilities.d.ts.map +1 -1
- package/dist/db/capabilities.js +34 -16
- package/dist/db/capabilities.js.map +1 -1
- package/dist/db/connection.d.ts +8 -0
- package/dist/db/connection.d.ts.map +1 -1
- package/dist/db/connection.js +55 -26
- package/dist/db/connection.js.map +1 -1
- package/dist/db/migrate.d.ts +28 -7
- package/dist/db/migrate.d.ts.map +1 -1
- package/dist/db/migrate.js +106 -78
- package/dist/db/migrate.js.map +1 -1
- package/dist/db/sqlite-retry.d.ts +11 -0
- package/dist/db/sqlite-retry.d.ts.map +1 -0
- package/dist/db/sqlite-retry.js +66 -0
- package/dist/db/sqlite-retry.js.map +1 -0
- package/dist/db/transaction.d.ts +13 -0
- package/dist/db/transaction.d.ts.map +1 -1
- package/dist/db/transaction.js +53 -24
- package/dist/db/transaction.js.map +1 -1
- package/dist/db/upgrade-backup.d.ts +58 -2
- package/dist/db/upgrade-backup.d.ts.map +1 -1
- package/dist/db/upgrade-backup.js +757 -28
- package/dist/db/upgrade-backup.js.map +1 -1
- package/dist/errors.js +1 -1
- package/dist/errors.js.map +1 -1
- package/dist/gateway/agent-service.d.ts +4 -1
- package/dist/gateway/agent-service.d.ts.map +1 -1
- package/dist/gateway/agent-service.js +71 -58
- package/dist/gateway/agent-service.js.map +1 -1
- package/dist/gateway/checkpoint-service.d.ts +1 -0
- package/dist/gateway/checkpoint-service.d.ts.map +1 -1
- package/dist/gateway/checkpoint-service.js +47 -24
- package/dist/gateway/checkpoint-service.js.map +1 -1
- package/dist/ledger/archive.d.ts +1 -4
- package/dist/ledger/archive.d.ts.map +1 -1
- package/dist/ledger/archive.js +134 -54
- package/dist/ledger/archive.js.map +1 -1
- package/dist/ledger/maintenance.d.ts +0 -11
- package/dist/ledger/maintenance.d.ts.map +1 -1
- package/dist/ledger/maintenance.js +41 -44
- package/dist/ledger/maintenance.js.map +1 -1
- package/dist/ledger/projection.d.ts +0 -1
- package/dist/ledger/projection.d.ts.map +1 -1
- package/dist/ledger/projection.js.map +1 -1
- package/dist/ledger/promotion.d.ts +0 -9
- package/dist/ledger/promotion.d.ts.map +1 -1
- package/dist/ledger/promotion.js.map +1 -1
- package/dist/ledger/query.d.ts +1 -15
- package/dist/ledger/query.d.ts.map +1 -1
- package/dist/ledger/query.js.map +1 -1
- package/dist/ledger/store.d.ts +0 -1
- package/dist/ledger/store.d.ts.map +1 -1
- package/dist/ledger/store.js +11 -8
- package/dist/ledger/store.js.map +1 -1
- package/dist/ledger/types.d.ts +0 -4
- package/dist/ledger/types.d.ts.map +1 -1
- package/dist/ledger/types.js.map +1 -1
- package/dist/ledger/validate.d.ts.map +1 -1
- package/dist/ledger/validate.js +0 -5
- package/dist/ledger/validate.js.map +1 -1
- package/dist/mcp/bounded-stdio-transport.d.ts +23 -0
- package/dist/mcp/bounded-stdio-transport.d.ts.map +1 -0
- package/dist/mcp/bounded-stdio-transport.js +109 -0
- package/dist/mcp/bounded-stdio-transport.js.map +1 -0
- package/dist/mcp/server.d.ts +4 -0
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +111 -75
- package/dist/mcp/server.js.map +1 -1
- package/dist/memory/audit.d.ts +0 -1
- package/dist/memory/audit.d.ts.map +1 -1
- package/dist/memory/audit.js +0 -1
- package/dist/memory/audit.js.map +1 -1
- package/dist/memory/curator.d.ts +45 -1
- package/dist/memory/curator.d.ts.map +1 -1
- package/dist/memory/curator.js +290 -68
- package/dist/memory/curator.js.map +1 -1
- package/dist/memory/entries.d.ts +12 -2
- package/dist/memory/entries.d.ts.map +1 -1
- package/dist/memory/entries.js +198 -114
- package/dist/memory/entries.js.map +1 -1
- package/dist/memory/federated-retrieval.d.ts +78 -0
- package/dist/memory/federated-retrieval.d.ts.map +1 -0
- package/dist/memory/federated-retrieval.js +441 -0
- package/dist/memory/federated-retrieval.js.map +1 -0
- package/dist/memory/hybrid-retrieval.d.ts +5 -11
- package/dist/memory/hybrid-retrieval.d.ts.map +1 -1
- package/dist/memory/hybrid-retrieval.js +78 -58
- package/dist/memory/hybrid-retrieval.js.map +1 -1
- package/dist/memory/lifecycle.d.ts.map +1 -1
- package/dist/memory/lifecycle.js +17 -13
- package/dist/memory/lifecycle.js.map +1 -1
- package/dist/memory/portability.d.ts +10 -0
- package/dist/memory/portability.d.ts.map +1 -0
- package/dist/memory/portability.js +44 -0
- package/dist/memory/portability.js.map +1 -0
- package/dist/memory/rebuild-search.d.ts +5 -0
- package/dist/memory/rebuild-search.d.ts.map +1 -1
- package/dist/memory/rebuild-search.js +101 -76
- package/dist/memory/rebuild-search.js.map +1 -1
- package/dist/memory/retrieval-query.d.ts +0 -5
- package/dist/memory/retrieval-query.d.ts.map +1 -1
- package/dist/memory/retrieval-query.js +0 -5
- package/dist/memory/retrieval-query.js.map +1 -1
- package/dist/memory/retrieval.d.ts +11 -0
- package/dist/memory/retrieval.d.ts.map +1 -1
- package/dist/memory/retrieval.js +34 -120
- package/dist/memory/retrieval.js.map +1 -1
- package/dist/memory/revisions.d.ts +76 -1
- package/dist/memory/revisions.d.ts.map +1 -1
- package/dist/memory/revisions.js +539 -35
- package/dist/memory/revisions.js.map +1 -1
- package/dist/memory/scoped-memory.d.ts +22 -13
- package/dist/memory/scoped-memory.d.ts.map +1 -1
- package/dist/memory/scoped-memory.js +248 -139
- package/dist/memory/scoped-memory.js.map +1 -1
- package/dist/memory/secrets.d.ts +2 -0
- package/dist/memory/secrets.d.ts.map +1 -1
- package/dist/memory/secrets.js +36 -0
- package/dist/memory/secrets.js.map +1 -1
- package/dist/memory/structured-memory.d.ts +8 -1
- package/dist/memory/structured-memory.d.ts.map +1 -1
- package/dist/memory/structured-memory.js +74 -20
- package/dist/memory/structured-memory.js.map +1 -1
- package/dist/memory/workspaces.d.ts +9 -0
- package/dist/memory/workspaces.d.ts.map +1 -1
- package/dist/memory/workspaces.js +67 -0
- package/dist/memory/workspaces.js.map +1 -1
- package/dist/package-version.d.ts +2 -0
- package/dist/package-version.d.ts.map +1 -0
- package/dist/package-version.js +7 -0
- package/dist/package-version.js.map +1 -0
- package/dist/repository/binding.d.ts +5 -0
- package/dist/repository/binding.d.ts.map +1 -1
- package/dist/repository/binding.js +117 -17
- package/dist/repository/binding.js.map +1 -1
- package/dist/repository/framework-version.d.ts +8 -0
- package/dist/repository/framework-version.d.ts.map +1 -0
- package/dist/repository/framework-version.js +29 -0
- package/dist/repository/framework-version.js.map +1 -0
- package/dist/repository/identity-value.d.ts +4 -0
- package/dist/repository/identity-value.d.ts.map +1 -0
- package/dist/repository/identity-value.js +24 -0
- package/dist/repository/identity-value.js.map +1 -0
- package/dist/repository/identity.d.ts.map +1 -1
- package/dist/repository/identity.js +2 -0
- package/dist/repository/identity.js.map +1 -1
- package/dist/repository/project-fingerprint.d.ts +33 -0
- package/dist/repository/project-fingerprint.d.ts.map +1 -0
- package/dist/repository/project-fingerprint.js +429 -0
- package/dist/repository/project-fingerprint.js.map +1 -0
- package/dist/repository/remote-url.d.ts +0 -1
- package/dist/repository/remote-url.d.ts.map +1 -1
- package/dist/repository/remote-url.js +0 -1
- package/dist/repository/remote-url.js.map +1 -1
- package/dist/runtime-version.d.ts +2 -1
- package/dist/runtime-version.d.ts.map +1 -1
- package/dist/runtime-version.js +21 -4
- package/dist/runtime-version.js.map +1 -1
- package/dist/serialization/boundary-json.d.ts +20 -0
- package/dist/serialization/boundary-json.d.ts.map +1 -0
- package/dist/serialization/boundary-json.js +129 -0
- package/dist/serialization/boundary-json.js.map +1 -0
- package/dist/serialization/envelope.js +1 -1
- package/dist/serialization/envelope.js.map +1 -1
- package/dist/serialization/validate.d.ts +7 -0
- package/dist/serialization/validate.d.ts.map +1 -1
- package/dist/serialization/validate.js +53 -13
- package/dist/serialization/validate.js.map +1 -1
- package/dist/server/app.js +1 -1
- package/dist/server/app.js.map +1 -1
- package/dist/server/http.d.ts +4 -3
- package/dist/server/http.d.ts.map +1 -1
- package/dist/server/http.js +90 -50
- package/dist/server/http.js.map +1 -1
- package/dist/server/idempotency.d.ts +0 -6
- package/dist/server/idempotency.d.ts.map +1 -1
- package/dist/server/idempotency.js.map +1 -1
- package/dist/server/router.d.ts.map +1 -1
- package/dist/server/router.js +21 -4
- package/dist/server/router.js.map +1 -1
- package/dist/server/routes/agent-capability-gate.d.ts +40 -0
- package/dist/server/routes/agent-capability-gate.d.ts.map +1 -0
- package/dist/server/routes/agent-capability-gate.js +182 -0
- package/dist/server/routes/agent-capability-gate.js.map +1 -0
- package/dist/server/routes/agent-events.d.ts.map +1 -1
- package/dist/server/routes/agent-events.js +8 -1
- package/dist/server/routes/agent-events.js.map +1 -1
- package/dist/server/routes/agent-intake.d.ts.map +1 -1
- package/dist/server/routes/agent-intake.js +10 -2
- package/dist/server/routes/agent-intake.js.map +1 -1
- package/dist/server/routes/agent-promotions.d.ts.map +1 -1
- package/dist/server/routes/agent-promotions.js +5 -2
- package/dist/server/routes/agent-promotions.js.map +1 -1
- package/dist/server/routes/agent-runs.d.ts.map +1 -1
- package/dist/server/routes/agent-runs.js +18 -4
- package/dist/server/routes/agent-runs.js.map +1 -1
- package/dist/server/routes/request-binding.d.ts +5 -0
- package/dist/server/routes/request-binding.d.ts.map +1 -0
- package/dist/server/routes/request-binding.js +49 -0
- package/dist/server/routes/request-binding.js.map +1 -0
- package/dist/server/routes/task5-support.d.ts +1 -8
- package/dist/server/routes/task5-support.d.ts.map +1 -1
- package/dist/server/routes/task5-support.js +1 -17
- package/dist/server/routes/task5-support.js.map +1 -1
- package/dist/server/routes/task5.d.ts.map +1 -1
- package/dist/server/routes/task5.js +106 -36
- package/dist/server/routes/task5.js.map +1 -1
- package/dist/server/write-queue.js +2 -2
- package/dist/server/write-queue.js.map +1 -1
- package/dist/setup/claude-config.d.ts +2 -1
- package/dist/setup/claude-config.d.ts.map +1 -1
- package/dist/setup/claude-config.js +54 -4
- package/dist/setup/claude-config.js.map +1 -1
- package/dist/setup/client-detection.d.ts +7 -2
- package/dist/setup/client-detection.d.ts.map +1 -1
- package/dist/setup/client-detection.js +27 -9
- package/dist/setup/client-detection.js.map +1 -1
- package/dist/setup/hermes-config.d.ts +2 -1
- package/dist/setup/hermes-config.d.ts.map +1 -1
- package/dist/setup/hermes-config.js +53 -12
- package/dist/setup/hermes-config.js.map +1 -1
- package/dist/setup/legacy-client-cleanup.d.ts +6 -0
- package/dist/setup/legacy-client-cleanup.d.ts.map +1 -0
- package/dist/setup/legacy-client-cleanup.js +152 -0
- package/dist/setup/legacy-client-cleanup.js.map +1 -0
- package/dist/setup/managed-text.d.ts.map +1 -1
- package/dist/setup/managed-text.js +11 -1
- package/dist/setup/managed-text.js.map +1 -1
- package/dist/setup/opencode-config.d.ts +2 -1
- package/dist/setup/opencode-config.d.ts.map +1 -1
- package/dist/setup/opencode-config.js +57 -5
- package/dist/setup/opencode-config.js.map +1 -1
- package/dist/setup/render.d.ts +2 -1
- package/dist/setup/render.d.ts.map +1 -1
- package/dist/setup/render.js +91 -17
- package/dist/setup/render.js.map +1 -1
- package/dist/setup/standard-skills.d.ts +16 -2
- package/dist/setup/standard-skills.d.ts.map +1 -1
- package/dist/setup/standard-skills.js +37 -13
- package/dist/setup/standard-skills.js.map +1 -1
- package/dist/setup/strict-json.d.ts +7 -0
- package/dist/setup/strict-json.d.ts.map +1 -0
- package/dist/setup/strict-json.js +98 -0
- package/dist/setup/strict-json.js.map +1 -0
- package/dist/setup/strict-toml.d.ts +7 -0
- package/dist/setup/strict-toml.d.ts.map +1 -0
- package/dist/setup/strict-toml.js +470 -0
- package/dist/setup/strict-toml.js.map +1 -0
- package/dist/skills/cache.d.ts +15 -0
- package/dist/skills/cache.d.ts.map +1 -0
- package/dist/skills/cache.js +19 -0
- package/dist/skills/cache.js.map +1 -0
- package/dist/skills/candidate-ranking.d.ts +15 -0
- package/dist/skills/candidate-ranking.d.ts.map +1 -0
- package/dist/skills/candidate-ranking.js +52 -0
- package/dist/skills/candidate-ranking.js.map +1 -0
- package/dist/skills/chunking.d.ts +11 -0
- package/dist/skills/chunking.d.ts.map +1 -0
- package/dist/skills/chunking.js +291 -0
- package/dist/skills/chunking.js.map +1 -0
- package/dist/skills/config.d.ts +19 -0
- package/dist/skills/config.d.ts.map +1 -0
- package/dist/skills/config.js +103 -0
- package/dist/skills/config.js.map +1 -0
- package/dist/skills/discovery-service.d.ts +26 -0
- package/dist/skills/discovery-service.d.ts.map +1 -0
- package/dist/skills/discovery-service.js +921 -0
- package/dist/skills/discovery-service.js.map +1 -0
- package/dist/skills/external-transport.d.ts +2 -0
- package/dist/skills/external-transport.d.ts.map +1 -0
- package/dist/skills/external-transport.js +30 -0
- package/dist/skills/external-transport.js.map +1 -0
- package/dist/skills/find.d.ts +16 -0
- package/dist/skills/find.d.ts.map +1 -0
- package/dist/skills/find.js +58 -0
- package/dist/skills/find.js.map +1 -0
- package/dist/skills/gap-detection.d.ts +23 -0
- package/dist/skills/gap-detection.d.ts.map +1 -0
- package/dist/skills/gap-detection.js +62 -0
- package/dist/skills/gap-detection.js.map +1 -0
- package/dist/skills/import-preparation.d.ts +4 -0
- package/dist/skills/import-preparation.d.ts.map +1 -0
- package/dist/skills/import-preparation.js +20 -0
- package/dist/skills/import-preparation.js.map +1 -0
- package/dist/skills/materialization-authority.d.ts +11 -0
- package/dist/skills/materialization-authority.d.ts.map +1 -0
- package/dist/skills/materialization-authority.js +92 -0
- package/dist/skills/materialization-authority.js.map +1 -0
- package/dist/skills/materialization-service.d.ts +22 -0
- package/dist/skills/materialization-service.d.ts.map +1 -0
- package/dist/skills/materialization-service.js +101 -0
- package/dist/skills/materialization-service.js.map +1 -0
- package/dist/skills/official-catalog.d.ts +31 -0
- package/dist/skills/official-catalog.d.ts.map +1 -0
- package/dist/skills/official-catalog.js +110 -0
- package/dist/skills/official-catalog.js.map +1 -0
- package/dist/skills/providers/schema.d.ts +30 -0
- package/dist/skills/providers/schema.d.ts.map +1 -0
- package/dist/skills/providers/schema.js +354 -0
- package/dist/skills/providers/schema.js.map +1 -0
- package/dist/skills/providers/skills-sh-compat.d.ts +17 -0
- package/dist/skills/providers/skills-sh-compat.d.ts.map +1 -0
- package/dist/skills/providers/skills-sh-compat.js +71 -0
- package/dist/skills/providers/skills-sh-compat.js.map +1 -0
- package/dist/skills/providers/skills-sh-v1.d.ts +23 -0
- package/dist/skills/providers/skills-sh-v1.d.ts.map +1 -0
- package/dist/skills/providers/skills-sh-v1.js +134 -0
- package/dist/skills/providers/skills-sh-v1.js.map +1 -0
- package/dist/skills/query-builder.d.ts +18 -0
- package/dist/skills/query-builder.d.ts.map +1 -0
- package/dist/skills/query-builder.js +93 -0
- package/dist/skills/query-builder.js.map +1 -0
- package/dist/skills/source/errors.d.ts +9 -0
- package/dist/skills/source/errors.d.ts.map +1 -0
- package/dist/skills/source/errors.js +15 -0
- package/dist/skills/source/errors.js.map +1 -0
- package/dist/skills/source/frontmatter.d.ts +9 -0
- package/dist/skills/source/frontmatter.d.ts.map +1 -0
- package/dist/skills/source/frontmatter.js +77 -0
- package/dist/skills/source/frontmatter.js.map +1 -0
- package/dist/skills/source/github-fetcher.d.ts +20 -0
- package/dist/skills/source/github-fetcher.d.ts.map +1 -0
- package/dist/skills/source/github-fetcher.js +292 -0
- package/dist/skills/source/github-fetcher.js.map +1 -0
- package/dist/skills/source/snapshot-validator.d.ts +28 -0
- package/dist/skills/source/snapshot-validator.d.ts.map +1 -0
- package/dist/skills/source/snapshot-validator.js +186 -0
- package/dist/skills/source/snapshot-validator.js.map +1 -0
- package/dist/skills/store.d.ts +181 -0
- package/dist/skills/store.d.ts.map +1 -0
- package/dist/skills/store.js +1710 -0
- package/dist/skills/store.js.map +1 -0
- package/dist/skills/types.d.ts +169 -0
- package/dist/skills/types.d.ts.map +1 -0
- package/dist/skills/types.js +2 -0
- package/dist/skills/types.js.map +1 -0
- package/dist/web/i18n.d.ts +56 -8
- package/dist/web/i18n.d.ts.map +1 -1
- package/dist/web/i18n.js +224 -32
- package/dist/web/i18n.js.map +1 -1
- package/dist/web/server.d.ts.map +1 -1
- package/dist/web/server.js +458 -42
- package/dist/web/server.js.map +1 -1
- package/dist/web/ui.d.ts +4 -0
- package/dist/web/ui.d.ts.map +1 -1
- package/dist/web/ui.js +697 -61
- package/dist/web/ui.js.map +1 -1
- package/migrations/008_federated_memory.sql +46 -0
- package/migrations/009_external_skill_discovery.sql +126 -0
- package/package.json +6 -3
- package/skills/kiokuko-single-purpose-functions/SKILL.md +288 -0
- package/skills/kiokuko-single-purpose-functions/references/kiokuko-patterns.md +344 -0
- package/skills/kiokuko-single-purpose-functions/references/review-checklist.md +97 -0
- package/skills/kiokuko-ui-design-soul/SKILL.md +1186 -28
- package/templates/AGENTS.md +8 -7
- package/dist/client/runtime-discovery.d.ts +0 -15
- package/dist/client/runtime-discovery.d.ts.map +0 -1
- package/dist/client/runtime-discovery.js +0 -45
- package/dist/client/runtime-discovery.js.map +0 -1
- package/dist/commands/call.d.ts +0 -7
- package/dist/commands/call.d.ts.map +0 -1
- package/dist/commands/call.js +0 -10
- package/dist/commands/call.js.map +0 -1
- package/dist/commands/link.d.ts +0 -6
- package/dist/commands/link.d.ts.map +0 -1
- package/dist/commands/link.js +0 -6
- package/dist/commands/link.js.map +0 -1
- package/dist/commands/promote.d.ts +0 -6
- package/dist/commands/promote.d.ts.map +0 -1
- package/dist/commands/promote.js +0 -6
- package/dist/commands/promote.js.map +0 -1
- package/dist/commands/read.d.ts +0 -6
- package/dist/commands/read.d.ts.map +0 -1
- package/dist/commands/read.js +0 -4
- package/dist/commands/read.js.map +0 -1
- package/dist/commands/recall.d.ts +0 -6
- package/dist/commands/recall.d.ts.map +0 -1
- package/dist/commands/recall.js +0 -4
- package/dist/commands/recall.js.map +0 -1
- package/dist/commands/record.d.ts +0 -6
- package/dist/commands/record.d.ts.map +0 -1
- package/dist/commands/record.js +0 -4
- package/dist/commands/record.js.map +0 -1
- package/dist/commands/search.d.ts +0 -6
- package/dist/commands/search.d.ts.map +0 -1
- package/dist/commands/search.js +0 -4
- package/dist/commands/search.js.map +0 -1
- package/dist/commands/supersede.d.ts +0 -6
- package/dist/commands/supersede.d.ts.map +0 -1
- package/dist/commands/supersede.js +0 -6
- package/dist/commands/supersede.js.map +0 -1
- package/dist/knowledge/sources.d.ts +0 -72
- package/dist/knowledge/sources.d.ts.map +0 -1
- package/dist/knowledge/sources.js +0 -388
- package/dist/knowledge/sources.js.map +0 -1
- package/dist/setup/opencode-evidence.d.ts +0 -37
- package/dist/setup/opencode-evidence.d.ts.map +0 -1
- package/dist/setup/opencode-evidence.js +0 -58
- package/dist/setup/opencode-evidence.js.map +0 -1
- package/dist/setup/opencode-loop-guard.d.ts +0 -9
- package/dist/setup/opencode-loop-guard.d.ts.map +0 -1
- package/dist/setup/opencode-loop-guard.js +0 -248
- package/dist/setup/opencode-loop-guard.js.map +0 -1
|
@@ -1,43 +1,772 @@
|
|
|
1
|
-
import { randomBytes } from 'node:crypto';
|
|
2
|
-
import {
|
|
1
|
+
import { createHash, randomBytes } from 'node:crypto';
|
|
2
|
+
import { spawnSync } from 'node:child_process';
|
|
3
|
+
import { constants } from 'node:fs';
|
|
4
|
+
import { lstat, mkdir, open, realpath } from 'node:fs/promises';
|
|
3
5
|
import { DatabaseSync } from 'node:sqlite';
|
|
4
6
|
import path from 'node:path';
|
|
5
7
|
import { KiokukoError } from '../errors.js';
|
|
8
|
+
import { isWellFormedUnicode } from '../serialization/boundary-json.js';
|
|
9
|
+
function portableDatabaseLabel(databasePath) {
|
|
10
|
+
const basename = path.basename(databasePath);
|
|
11
|
+
const sanitized = basename
|
|
12
|
+
.toLowerCase()
|
|
13
|
+
.replace(/[^a-z0-9_-]+/gu, '-')
|
|
14
|
+
.replace(/^-+|-+$/gu, '')
|
|
15
|
+
.slice(0, 32) || 'file';
|
|
16
|
+
const hash = createHash('sha256').update(basename, 'utf8').digest('hex').slice(0, 16);
|
|
17
|
+
// The fixed `db-` prefix prevents Windows device-name collisions even when
|
|
18
|
+
// the source basename is a valid POSIX name such as CON or NUL.
|
|
19
|
+
return `db-${sanitized}-${hash}`;
|
|
20
|
+
}
|
|
6
21
|
function backupName(databasePath, fromVersion, toVersion) {
|
|
7
22
|
const timestamp = new Date().toISOString().replace(/[^0-9]/g, '').slice(0, 17);
|
|
8
23
|
const nonce = randomBytes(8).toString('hex');
|
|
9
|
-
return `${
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
24
|
+
return `${portableDatabaseLabel(databasePath)}.pre-upgrade-v${fromVersion}-to-v${toVersion}-${timestamp}-${nonce}.sqlite3`;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Resolve the pathname-safety flags as one mandatory POSIX capability. A
|
|
28
|
+
* supported non-Windows runtime must provide every flag; dropping one to zero
|
|
29
|
+
* would silently remove the directory, no-follow, or nonblocking guarantee.
|
|
30
|
+
*/
|
|
31
|
+
export function requirePosixBackupOpenFlags(platform = process.platform, openFlags = constants) {
|
|
32
|
+
if (platform === 'win32')
|
|
33
|
+
return undefined;
|
|
34
|
+
function required(name) {
|
|
35
|
+
const value = openFlags[name];
|
|
36
|
+
if (typeof value !== 'number' || !Number.isSafeInteger(value) || value <= 0) {
|
|
37
|
+
throw new KiokukoError('INTEGRITY_ERROR', 'Required POSIX backup open flags are unavailable', { flag: name });
|
|
38
|
+
}
|
|
39
|
+
return value;
|
|
40
|
+
}
|
|
41
|
+
return Object.freeze({
|
|
42
|
+
directory: required('O_DIRECTORY'),
|
|
43
|
+
noFollow: required('O_NOFOLLOW'),
|
|
44
|
+
nonBlock: required('O_NONBLOCK'),
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
function isAlreadyExists(error) {
|
|
48
|
+
return error instanceof Error
|
|
49
|
+
&& 'code' in error
|
|
50
|
+
&& error.code === 'EEXIST';
|
|
51
|
+
}
|
|
52
|
+
async function requireCanonicalDirectoryChain(directory) {
|
|
53
|
+
const parsed = path.parse(directory);
|
|
54
|
+
const relative = path.relative(parsed.root, directory);
|
|
55
|
+
let current = parsed.root;
|
|
56
|
+
for (const component of relative.split(path.sep).filter((value) => value.length > 0)) {
|
|
57
|
+
current = path.join(current, component);
|
|
58
|
+
const status = await lstat(current);
|
|
59
|
+
if (!status.isDirectory() || status.isSymbolicLink()) {
|
|
60
|
+
throw new KiokukoError('INTEGRITY_ERROR', 'Backup directory path contains a non-directory or symbolic link');
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
async function bindBackupDirectory(databasePath, posixFlags) {
|
|
65
|
+
const databaseDirectory = await realpath(path.dirname(databasePath));
|
|
66
|
+
await requireCanonicalDirectoryChain(databaseDirectory);
|
|
67
|
+
const directory = path.join(databaseDirectory, 'backups');
|
|
68
|
+
try {
|
|
69
|
+
await mkdir(directory, { mode: 0o700 });
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
if (!isAlreadyExists(error))
|
|
73
|
+
throw error;
|
|
74
|
+
}
|
|
75
|
+
const before = await lstat(directory, { bigint: true });
|
|
76
|
+
if (!before.isDirectory() || before.isSymbolicLink()) {
|
|
77
|
+
throw new KiokukoError('INTEGRITY_ERROR', 'Backup directory must be a non-symbolic-link directory');
|
|
78
|
+
}
|
|
79
|
+
let handle;
|
|
80
|
+
try {
|
|
81
|
+
if (posixFlags !== undefined) {
|
|
82
|
+
handle = await open(directory, constants.O_RDONLY | posixFlags.directory | posixFlags.noFollow);
|
|
83
|
+
const opened = await handle.stat({ bigint: true });
|
|
84
|
+
if (!opened.isDirectory() || opened.dev !== before.dev || opened.ino !== before.ino) {
|
|
85
|
+
throw new KiokukoError('CONFLICT', 'Backup directory changed while it was being opened');
|
|
86
|
+
}
|
|
87
|
+
await handle.chmod(0o700);
|
|
88
|
+
}
|
|
89
|
+
const secured = handle === undefined
|
|
90
|
+
? await lstat(directory, { bigint: true })
|
|
91
|
+
: await handle.stat({ bigint: true });
|
|
92
|
+
if (!secured.isDirectory() || secured.dev !== before.dev || secured.ino !== before.ino) {
|
|
93
|
+
throw new KiokukoError('CONFLICT', 'Backup directory changed while it was being secured');
|
|
94
|
+
}
|
|
95
|
+
const binding = {
|
|
96
|
+
directory,
|
|
97
|
+
device: secured.dev,
|
|
98
|
+
inode: secured.ino,
|
|
99
|
+
owner: secured.uid,
|
|
100
|
+
group: secured.gid,
|
|
101
|
+
mode: secured.mode,
|
|
102
|
+
privateDirectory: true,
|
|
103
|
+
...(handle === undefined ? {} : { handle }),
|
|
104
|
+
};
|
|
105
|
+
await requireBackupDirectory(binding);
|
|
106
|
+
return binding;
|
|
107
|
+
}
|
|
108
|
+
catch (error) {
|
|
109
|
+
if (handle === undefined)
|
|
110
|
+
throw error;
|
|
111
|
+
const pendingHandle = handle;
|
|
112
|
+
handle = undefined;
|
|
113
|
+
try {
|
|
114
|
+
await pendingHandle.close();
|
|
115
|
+
}
|
|
116
|
+
catch (closeError) {
|
|
117
|
+
throw new AggregateError([error, closeError], 'Backup directory binding failed and closing it also failed');
|
|
118
|
+
}
|
|
119
|
+
throw error;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
async function bindExistingOutputDirectory(destination, posixFlags) {
|
|
123
|
+
const directory = path.dirname(destination);
|
|
124
|
+
await requireCanonicalDirectoryChain(directory);
|
|
125
|
+
const before = await lstat(directory, { bigint: true });
|
|
126
|
+
if (!before.isDirectory() || before.isSymbolicLink()) {
|
|
127
|
+
throw new KiokukoError('INTEGRITY_ERROR', 'Backup output directory must be a non-symbolic-link directory');
|
|
128
|
+
}
|
|
129
|
+
let handle;
|
|
130
|
+
try {
|
|
131
|
+
if (posixFlags !== undefined) {
|
|
132
|
+
handle = await open(directory, constants.O_RDONLY | posixFlags.directory | posixFlags.noFollow);
|
|
133
|
+
const opened = await handle.stat({ bigint: true });
|
|
134
|
+
if (!opened.isDirectory() || opened.dev !== before.dev || opened.ino !== before.ino) {
|
|
135
|
+
throw new KiokukoError('CONFLICT', 'Backup output directory changed while it was being opened');
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
const secured = handle === undefined
|
|
139
|
+
? await lstat(directory, { bigint: true })
|
|
140
|
+
: await handle.stat({ bigint: true });
|
|
141
|
+
if (!secured.isDirectory() || secured.dev !== before.dev || secured.ino !== before.ino) {
|
|
142
|
+
throw new KiokukoError('CONFLICT', 'Backup output directory changed while it was being bound');
|
|
143
|
+
}
|
|
144
|
+
const binding = {
|
|
145
|
+
directory,
|
|
146
|
+
device: secured.dev,
|
|
147
|
+
inode: secured.ino,
|
|
148
|
+
owner: secured.uid,
|
|
149
|
+
group: secured.gid,
|
|
150
|
+
mode: secured.mode,
|
|
151
|
+
privateDirectory: false,
|
|
152
|
+
...(handle === undefined ? {} : { handle }),
|
|
153
|
+
};
|
|
154
|
+
await requireBackupDirectory(binding);
|
|
155
|
+
return binding;
|
|
156
|
+
}
|
|
157
|
+
catch (error) {
|
|
158
|
+
if (handle === undefined)
|
|
159
|
+
throw error;
|
|
160
|
+
const pendingHandle = handle;
|
|
161
|
+
handle = undefined;
|
|
22
162
|
try {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
163
|
+
await pendingHandle.close();
|
|
164
|
+
}
|
|
165
|
+
catch (closeError) {
|
|
166
|
+
throw new AggregateError([error, closeError], 'Backup output directory binding failed and closing it also failed');
|
|
167
|
+
}
|
|
168
|
+
throw error;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
async function requireBackupDirectory(binding) {
|
|
172
|
+
await requireCanonicalDirectoryChain(path.dirname(binding.directory));
|
|
173
|
+
const status = await lstat(binding.directory, { bigint: true });
|
|
174
|
+
if (!status.isDirectory()
|
|
175
|
+
|| status.isSymbolicLink()
|
|
176
|
+
|| status.dev !== binding.device
|
|
177
|
+
|| status.ino !== binding.inode
|
|
178
|
+
|| status.uid !== binding.owner
|
|
179
|
+
|| status.gid !== binding.group
|
|
180
|
+
|| status.mode !== binding.mode
|
|
181
|
+
|| (binding.privateDirectory && process.platform !== 'win32' && (status.mode & 511n) !== 448n)) {
|
|
182
|
+
throw new KiokukoError('CONFLICT', 'Backup output directory changed during backup creation');
|
|
183
|
+
}
|
|
184
|
+
if (binding.handle !== undefined) {
|
|
185
|
+
const opened = await binding.handle.stat({ bigint: true });
|
|
186
|
+
if (!opened.isDirectory()
|
|
187
|
+
|| opened.dev !== binding.device
|
|
188
|
+
|| opened.ino !== binding.inode
|
|
189
|
+
|| opened.uid !== binding.owner
|
|
190
|
+
|| opened.gid !== binding.group
|
|
191
|
+
|| opened.mode !== binding.mode
|
|
192
|
+
|| (binding.privateDirectory && process.platform !== 'win32' && (opened.mode & 511n) !== 448n)) {
|
|
193
|
+
throw new KiokukoError('CONFLICT', 'Backup output directory changed during backup creation');
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
const WRITER_DEADLINE_MS = 30_000;
|
|
198
|
+
const WRITER_PROTOCOL_LIMIT_BYTES = 64 * 1024;
|
|
199
|
+
const BOUND_WRITER_SCRIPT = String.raw `
|
|
200
|
+
const { createHash } = require('node:crypto');
|
|
201
|
+
const {
|
|
202
|
+
closeSync,
|
|
203
|
+
constants,
|
|
204
|
+
fchmodSync,
|
|
205
|
+
fstatSync,
|
|
206
|
+
fsyncSync,
|
|
207
|
+
lstatSync,
|
|
208
|
+
openSync,
|
|
209
|
+
readFileSync,
|
|
210
|
+
readSync,
|
|
211
|
+
statSync,
|
|
212
|
+
writeFileSync,
|
|
213
|
+
} = require('node:fs');
|
|
214
|
+
const path = require('node:path');
|
|
215
|
+
|
|
216
|
+
function plain(value) {
|
|
217
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value)) return false;
|
|
218
|
+
const prototype = Object.getPrototypeOf(value);
|
|
219
|
+
return prototype === Object.prototype || prototype === null;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
function exact(value, keys) {
|
|
223
|
+
const actual = Object.keys(value).sort();
|
|
224
|
+
const expected = [...keys].sort();
|
|
225
|
+
return actual.length === expected.length
|
|
226
|
+
&& actual.every((key, index) => key === expected[index]);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
function unsigned(value) {
|
|
230
|
+
if (typeof value !== 'string' || !/^(?:0|[1-9][0-9]*)$/.test(value)) {
|
|
231
|
+
throw new Error('Bound backup request contains an invalid integer');
|
|
232
|
+
}
|
|
233
|
+
return BigInt(value);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function parseDirectory(value) {
|
|
237
|
+
if (!plain(value) || !exact(value, ['device', 'inode', 'owner', 'group', 'mode'])) {
|
|
238
|
+
throw new Error('Bound backup directory identity is invalid');
|
|
239
|
+
}
|
|
240
|
+
return {
|
|
241
|
+
device: unsigned(value.device),
|
|
242
|
+
inode: unsigned(value.inode),
|
|
243
|
+
owner: unsigned(value.owner),
|
|
244
|
+
group: unsigned(value.group),
|
|
245
|
+
mode: unsigned(value.mode),
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
function portableDestination(value) {
|
|
250
|
+
const stem = value.split('.', 1)[0].replace(/[ .]+$/u, '').toUpperCase();
|
|
251
|
+
return !/[<>:"\/\\|?*\u0000-\u001F]/u.test(value)
|
|
252
|
+
&& !/[\uD800-\uDFFF]/u.test(value)
|
|
253
|
+
&& !/[ .]$/u.test(value)
|
|
254
|
+
&& !/^(?:CON|PRN|AUX|NUL|COM[1-9¹²³]|LPT[1-9¹²³]|CONIN\$|CONOUT\$)$/u.test(stem);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
function parseRequest() {
|
|
258
|
+
const value = JSON.parse(process.argv[1]);
|
|
259
|
+
if (!plain(value)
|
|
260
|
+
|| !exact(value, [
|
|
261
|
+
'destination', 'directory', 'enforcePosixFileMode', 'requirePrivateDirectory', 'sha256', 'size',
|
|
262
|
+
])
|
|
263
|
+
|| typeof value.destination !== 'string'
|
|
264
|
+
|| value.destination.length === 0
|
|
265
|
+
|| value.destination.includes('\0')
|
|
266
|
+
|| value.destination.includes('/')
|
|
267
|
+
|| value.destination.includes('\\')
|
|
268
|
+
|| value.destination === '.'
|
|
269
|
+
|| value.destination === '..'
|
|
270
|
+
|| path.basename(value.destination) !== value.destination
|
|
271
|
+
|| !portableDestination(value.destination)
|
|
272
|
+
|| typeof value.enforcePosixFileMode !== 'boolean'
|
|
273
|
+
|| typeof value.requirePrivateDirectory !== 'boolean'
|
|
274
|
+
|| typeof value.sha256 !== 'string'
|
|
275
|
+
|| !/^[0-9a-f]{64}$/.test(value.sha256)) {
|
|
276
|
+
throw new Error('Bound backup request is invalid');
|
|
277
|
+
}
|
|
278
|
+
return {
|
|
279
|
+
destination: value.destination,
|
|
280
|
+
directory: parseDirectory(value.directory),
|
|
281
|
+
enforcePosixFileMode: value.enforcePosixFileMode,
|
|
282
|
+
requirePrivateDirectory: value.requirePrivateDirectory,
|
|
283
|
+
sha256: value.sha256,
|
|
284
|
+
size: unsigned(value.size),
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
function requireDirectory(request) {
|
|
289
|
+
const status = statSync('.', { bigint: true });
|
|
290
|
+
if (!status.isDirectory()
|
|
291
|
+
|| status.dev !== request.directory.device
|
|
292
|
+
|| status.ino !== request.directory.inode
|
|
293
|
+
|| status.uid !== request.directory.owner
|
|
294
|
+
|| status.gid !== request.directory.group
|
|
295
|
+
|| status.mode !== request.directory.mode
|
|
296
|
+
|| (request.requirePrivateDirectory && (status.mode & 0o777n) !== 0o700n)) {
|
|
297
|
+
throw new Error('Bound backup directory identity changed');
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
function requiredOpenFlag(name) {
|
|
302
|
+
const value = constants[name];
|
|
303
|
+
if (!Number.isSafeInteger(value) || value <= 0) {
|
|
304
|
+
throw new Error('Required POSIX backup open flag is unavailable: ' + name);
|
|
305
|
+
}
|
|
306
|
+
return value;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
function descriptorHash(descriptor, size) {
|
|
310
|
+
const hash = createHash('sha256');
|
|
311
|
+
const buffer = Buffer.allocUnsafe(64 * 1024);
|
|
312
|
+
let offset = 0n;
|
|
313
|
+
while (offset < size) {
|
|
314
|
+
const length = Number(size - offset > BigInt(buffer.length) ? BigInt(buffer.length) : size - offset);
|
|
315
|
+
const bytesRead = readSync(descriptor, buffer, 0, length, Number(offset));
|
|
316
|
+
if (bytesRead === 0) throw new Error('Bound backup output was truncated');
|
|
317
|
+
hash.update(buffer.subarray(0, bytesRead));
|
|
318
|
+
offset += BigInt(bytesRead);
|
|
319
|
+
}
|
|
320
|
+
return hash.digest('hex');
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
function serializedError(error) {
|
|
324
|
+
return {
|
|
325
|
+
name: error instanceof Error ? error.name : 'Error',
|
|
326
|
+
message: error instanceof Error ? error.message : 'Bound backup writer failed',
|
|
327
|
+
...(typeof error?.code === 'string' ? { code: error.code } : {}),
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
let descriptor;
|
|
332
|
+
let result;
|
|
333
|
+
let failure;
|
|
334
|
+
try {
|
|
335
|
+
const request = parseRequest();
|
|
336
|
+
const bytes = readFileSync(0);
|
|
337
|
+
if (BigInt(bytes.length) !== request.size
|
|
338
|
+
|| createHash('sha256').update(bytes).digest('hex') !== request.sha256) {
|
|
339
|
+
throw new Error('Bound backup input bytes do not match their attestation');
|
|
340
|
+
}
|
|
341
|
+
requireDirectory(request);
|
|
342
|
+
const noFollow = request.enforcePosixFileMode ? requiredOpenFlag('O_NOFOLLOW') : 0;
|
|
343
|
+
descriptor = openSync(
|
|
344
|
+
request.destination,
|
|
345
|
+
constants.O_CREAT | constants.O_EXCL | constants.O_RDWR
|
|
346
|
+
| noFollow,
|
|
347
|
+
0o600,
|
|
348
|
+
);
|
|
349
|
+
if (request.enforcePosixFileMode) fchmodSync(descriptor, 0o600);
|
|
350
|
+
writeFileSync(descriptor, bytes);
|
|
351
|
+
fsyncSync(descriptor);
|
|
352
|
+
const opened = fstatSync(descriptor, { bigint: true });
|
|
353
|
+
if (!opened.isFile()
|
|
354
|
+
|| opened.size !== request.size
|
|
355
|
+
|| opened.nlink !== 1n
|
|
356
|
+
|| (request.enforcePosixFileMode && (opened.mode & 0o777n) !== 0o600n)
|
|
357
|
+
|| descriptorHash(descriptor, opened.size) !== request.sha256) {
|
|
358
|
+
throw new Error('Bound backup output failed descriptor attestation');
|
|
359
|
+
}
|
|
360
|
+
closeSync(descriptor);
|
|
361
|
+
descriptor = undefined;
|
|
362
|
+
const named = lstatSync(request.destination, { bigint: true });
|
|
363
|
+
if (!named.isFile()
|
|
364
|
+
|| named.isSymbolicLink()
|
|
365
|
+
|| named.dev !== opened.dev
|
|
366
|
+
|| named.ino !== opened.ino
|
|
367
|
+
|| named.uid !== opened.uid
|
|
368
|
+
|| named.gid !== opened.gid
|
|
369
|
+
|| named.mode !== opened.mode
|
|
370
|
+
|| named.size !== opened.size
|
|
371
|
+
|| named.nlink !== opened.nlink) {
|
|
372
|
+
throw new Error('Bound backup output changed before handoff');
|
|
373
|
+
}
|
|
374
|
+
requireDirectory(request);
|
|
375
|
+
result = {
|
|
376
|
+
ok: true,
|
|
377
|
+
artifact: {
|
|
378
|
+
device: opened.dev.toString(),
|
|
379
|
+
inode: opened.ino.toString(),
|
|
380
|
+
owner: opened.uid.toString(),
|
|
381
|
+
group: opened.gid.toString(),
|
|
382
|
+
mode: opened.mode.toString(),
|
|
383
|
+
size: opened.size.toString(),
|
|
384
|
+
linkCount: opened.nlink.toString(),
|
|
385
|
+
sha256: request.sha256,
|
|
386
|
+
},
|
|
387
|
+
};
|
|
388
|
+
} catch (error) {
|
|
389
|
+
failure = error;
|
|
390
|
+
}
|
|
391
|
+
if (descriptor !== undefined) {
|
|
392
|
+
try {
|
|
393
|
+
closeSync(descriptor);
|
|
394
|
+
} catch (closeError) {
|
|
395
|
+
failure = failure === undefined
|
|
396
|
+
? closeError
|
|
397
|
+
: new AggregateError([failure, closeError], 'Bound backup write and descriptor close both failed');
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
if (failure !== undefined) result = { ok: false, error: serializedError(failure) };
|
|
401
|
+
process.stdout.write(JSON.stringify(result));
|
|
402
|
+
`;
|
|
403
|
+
function sha256(bytes) {
|
|
404
|
+
return createHash('sha256').update(bytes).digest('hex');
|
|
405
|
+
}
|
|
406
|
+
function normalizedSerializedSnapshot(database) {
|
|
407
|
+
const serialized = Buffer.from(database.serializeDatabase());
|
|
408
|
+
if (serialized.length < 100
|
|
409
|
+
|| !serialized.subarray(0, 16).equals(Buffer.from('SQLite format 3\0', 'binary'))
|
|
410
|
+
|| !((serialized[18] === 1 && serialized[19] === 1)
|
|
411
|
+
|| (serialized[18] === 2 && serialized[19] === 2))) {
|
|
412
|
+
throw new KiokukoError('INTEGRITY_ERROR', 'Serialized SQLite backup has an invalid file header');
|
|
413
|
+
}
|
|
414
|
+
// sqlite3_serialize preserves the WAL read/write header bytes. The serialized
|
|
415
|
+
// image already contains the connection's complete logical snapshot, so set
|
|
416
|
+
// the standalone image to rollback-journal format before deserializing it.
|
|
417
|
+
serialized[18] = 1;
|
|
418
|
+
serialized[19] = 1;
|
|
419
|
+
const verification = new DatabaseSync(':memory:', { enableForeignKeyConstraints: true });
|
|
420
|
+
let failed = false;
|
|
421
|
+
let operationError;
|
|
422
|
+
try {
|
|
423
|
+
const methods = verification;
|
|
424
|
+
if (typeof methods.deserialize !== 'function' || typeof methods.serialize !== 'function') {
|
|
425
|
+
throw new Error('SQLite serialization requires Node.js 24.16.0 or newer');
|
|
426
|
+
}
|
|
427
|
+
methods.deserialize(serialized);
|
|
428
|
+
const integrity = verification.prepare('PRAGMA integrity_check').get();
|
|
429
|
+
if (integrity?.integrity_check !== 'ok')
|
|
430
|
+
throw new Error('backup integrity check failed');
|
|
431
|
+
if (verification.prepare('PRAGMA foreign_key_check').all().length > 0) {
|
|
432
|
+
throw new Error('backup foreign-key integrity check failed');
|
|
433
|
+
}
|
|
434
|
+
const normalized = Buffer.from(methods.serialize());
|
|
435
|
+
if (normalized.length < 100 || normalized[18] !== 1 || normalized[19] !== 1) {
|
|
436
|
+
throw new Error('Serialized SQLite backup did not normalize to a standalone image');
|
|
28
437
|
}
|
|
29
|
-
|
|
438
|
+
return normalized;
|
|
439
|
+
}
|
|
440
|
+
catch (error) {
|
|
441
|
+
failed = true;
|
|
442
|
+
operationError = error;
|
|
443
|
+
throw error;
|
|
444
|
+
}
|
|
445
|
+
finally {
|
|
446
|
+
try {
|
|
30
447
|
verification.close();
|
|
31
448
|
}
|
|
32
|
-
|
|
449
|
+
catch (closeError) {
|
|
450
|
+
if (failed) {
|
|
451
|
+
throw new AggregateError([operationError, closeError], 'Serialized backup verification failed and closing it also failed');
|
|
452
|
+
}
|
|
453
|
+
throw closeError;
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
function isPlainObject(value) {
|
|
458
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value))
|
|
459
|
+
return false;
|
|
460
|
+
const prototype = Object.getPrototypeOf(value);
|
|
461
|
+
return prototype === Object.prototype || prototype === null;
|
|
462
|
+
}
|
|
463
|
+
function exactKeys(value, keys) {
|
|
464
|
+
const actual = Object.keys(value).sort();
|
|
465
|
+
const expected = [...keys].sort();
|
|
466
|
+
return actual.length === expected.length
|
|
467
|
+
&& actual.every((key, index) => key === expected[index]);
|
|
468
|
+
}
|
|
469
|
+
function parseUnsigned(value) {
|
|
470
|
+
if (typeof value !== 'string' || !/^(?:0|[1-9][0-9]*)$/u.test(value)) {
|
|
471
|
+
throw new Error('Bound backup writer returned an invalid integer');
|
|
472
|
+
}
|
|
473
|
+
return BigInt(value);
|
|
474
|
+
}
|
|
475
|
+
function writeBoundArtifact(binding, fileName, bytes, posixFlags) {
|
|
476
|
+
const request = {
|
|
477
|
+
destination: fileName,
|
|
478
|
+
directory: {
|
|
479
|
+
device: binding.device.toString(),
|
|
480
|
+
inode: binding.inode.toString(),
|
|
481
|
+
owner: binding.owner.toString(),
|
|
482
|
+
group: binding.group.toString(),
|
|
483
|
+
mode: binding.mode.toString(),
|
|
484
|
+
},
|
|
485
|
+
enforcePosixFileMode: posixFlags !== undefined,
|
|
486
|
+
requirePrivateDirectory: binding.privateDirectory && posixFlags !== undefined,
|
|
487
|
+
sha256: sha256(bytes),
|
|
488
|
+
size: bytes.length.toString(),
|
|
489
|
+
};
|
|
490
|
+
const child = spawnSync(process.execPath, ['--input-type=commonjs', '--eval', BOUND_WRITER_SCRIPT, JSON.stringify(request)], {
|
|
491
|
+
cwd: binding.directory,
|
|
492
|
+
// The worker uses only built-in modules and an absolute executable path.
|
|
493
|
+
// Do not inherit preload, loader, search-path, or application environment.
|
|
494
|
+
env: {},
|
|
495
|
+
shell: false,
|
|
496
|
+
input: bytes,
|
|
497
|
+
timeout: WRITER_DEADLINE_MS,
|
|
498
|
+
maxBuffer: WRITER_PROTOCOL_LIMIT_BYTES,
|
|
499
|
+
});
|
|
500
|
+
if (child.error !== undefined)
|
|
501
|
+
throw child.error;
|
|
502
|
+
if (child.status !== 0 || child.signal !== null) {
|
|
503
|
+
throw new Error('Bound backup writer subprocess failed');
|
|
504
|
+
}
|
|
505
|
+
let parsed;
|
|
506
|
+
try {
|
|
507
|
+
parsed = JSON.parse(child.stdout.toString('utf8'));
|
|
33
508
|
}
|
|
34
509
|
catch (error) {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
throw new
|
|
510
|
+
if (error instanceof SyntaxError)
|
|
511
|
+
throw new Error('Bound backup writer returned invalid JSON');
|
|
512
|
+
throw error;
|
|
513
|
+
}
|
|
514
|
+
if (!isPlainObject(parsed) || typeof parsed.ok !== 'boolean') {
|
|
515
|
+
throw new Error('Bound backup writer returned an invalid result');
|
|
516
|
+
}
|
|
517
|
+
if (!parsed.ok) {
|
|
518
|
+
if (!exactKeys(parsed, ['error', 'ok']) || !isPlainObject(parsed.error)
|
|
519
|
+
|| !Object.hasOwn(parsed.error, 'name')
|
|
520
|
+
|| !Object.hasOwn(parsed.error, 'message')
|
|
521
|
+
|| Object.keys(parsed.error).some((key) => !['code', 'message', 'name'].includes(key))
|
|
522
|
+
|| typeof parsed.error.name !== 'string'
|
|
523
|
+
|| typeof parsed.error.message !== 'string'
|
|
524
|
+
|| (Object.hasOwn(parsed.error, 'code') && typeof parsed.error.code !== 'string')) {
|
|
525
|
+
throw new Error('Bound backup writer returned an invalid result');
|
|
526
|
+
}
|
|
527
|
+
if (parsed.error.code === 'EEXIST') {
|
|
528
|
+
throw new KiokukoError('CONFLICT', 'Backup destination already exists');
|
|
529
|
+
}
|
|
530
|
+
throw Object.assign(new Error(parsed.error.message), {
|
|
531
|
+
name: parsed.error.name,
|
|
532
|
+
...(typeof parsed.error.code === 'string' ? { code: parsed.error.code } : {}),
|
|
533
|
+
});
|
|
534
|
+
}
|
|
535
|
+
if (!exactKeys(parsed, ['artifact', 'ok']) || !isPlainObject(parsed.artifact)
|
|
536
|
+
|| !exactKeys(parsed.artifact, [
|
|
537
|
+
'device', 'group', 'inode', 'linkCount', 'mode', 'owner', 'sha256', 'size',
|
|
538
|
+
])
|
|
539
|
+
|| typeof parsed.artifact.sha256 !== 'string'
|
|
540
|
+
|| parsed.artifact.sha256 !== request.sha256) {
|
|
541
|
+
throw new Error('Bound backup writer returned an invalid artifact attestation');
|
|
542
|
+
}
|
|
543
|
+
const attestation = Object.freeze({
|
|
544
|
+
device: parseUnsigned(parsed.artifact.device),
|
|
545
|
+
inode: parseUnsigned(parsed.artifact.inode),
|
|
546
|
+
owner: parseUnsigned(parsed.artifact.owner),
|
|
547
|
+
group: parseUnsigned(parsed.artifact.group),
|
|
548
|
+
mode: parseUnsigned(parsed.artifact.mode),
|
|
549
|
+
size: parseUnsigned(parsed.artifact.size),
|
|
550
|
+
linkCount: parseUnsigned(parsed.artifact.linkCount),
|
|
551
|
+
sha256: parsed.artifact.sha256,
|
|
552
|
+
});
|
|
553
|
+
if (attestation.size !== BigInt(bytes.length)
|
|
554
|
+
|| attestation.linkCount !== 1n
|
|
555
|
+
|| (process.platform !== 'win32' && (attestation.mode & 511n) !== 384n)) {
|
|
556
|
+
throw new Error('Bound backup writer returned an invalid artifact attestation');
|
|
557
|
+
}
|
|
558
|
+
return attestation;
|
|
559
|
+
}
|
|
560
|
+
async function descriptorSha256(handle, size) {
|
|
561
|
+
if (size < 0n || size > BigInt(Number.MAX_SAFE_INTEGER)) {
|
|
562
|
+
throw new KiokukoError('INTEGRITY_ERROR', 'Serialized backup artifact size is unsupported');
|
|
563
|
+
}
|
|
564
|
+
const expectedLength = Number(size);
|
|
565
|
+
const hash = createHash('sha256');
|
|
566
|
+
const buffer = Buffer.allocUnsafe(64 * 1024);
|
|
567
|
+
let offset = 0;
|
|
568
|
+
while (offset < expectedLength) {
|
|
569
|
+
const length = Math.min(buffer.length, expectedLength - offset);
|
|
570
|
+
const { bytesRead } = await handle.read(buffer, 0, length, offset);
|
|
571
|
+
if (bytesRead === 0) {
|
|
572
|
+
throw new KiokukoError('CONFLICT', 'Serialized backup artifact changed during verification');
|
|
573
|
+
}
|
|
574
|
+
hash.update(buffer.subarray(0, bytesRead));
|
|
575
|
+
offset += bytesRead;
|
|
576
|
+
}
|
|
577
|
+
return hash.digest('hex');
|
|
578
|
+
}
|
|
579
|
+
function sameArtifactStatus(status, attestation) {
|
|
580
|
+
return status.isFile()
|
|
581
|
+
&& status.dev === attestation.device
|
|
582
|
+
&& status.ino === attestation.inode
|
|
583
|
+
&& status.uid === attestation.owner
|
|
584
|
+
&& status.gid === attestation.group
|
|
585
|
+
&& status.mode === attestation.mode
|
|
586
|
+
&& status.size === attestation.size
|
|
587
|
+
&& status.nlink === attestation.linkCount
|
|
588
|
+
&& status.size > 0n
|
|
589
|
+
&& status.nlink === 1n
|
|
590
|
+
&& (process.platform === 'win32' || (status.mode & 511n) === 384n);
|
|
591
|
+
}
|
|
592
|
+
async function requireBoundArtifact(output, attestation, posixFlags) {
|
|
593
|
+
let handle;
|
|
594
|
+
let operationFailed = false;
|
|
595
|
+
let operationError;
|
|
596
|
+
try {
|
|
597
|
+
handle = await open(output, posixFlags === undefined
|
|
598
|
+
? constants.O_RDONLY
|
|
599
|
+
: constants.O_RDONLY | posixFlags.noFollow | posixFlags.nonBlock);
|
|
600
|
+
const opened = await handle.stat({ bigint: true });
|
|
601
|
+
const named = await lstat(output, { bigint: true });
|
|
602
|
+
if (!sameArtifactStatus(opened, attestation)
|
|
603
|
+
|| !named.isFile()
|
|
604
|
+
|| named.isSymbolicLink()
|
|
605
|
+
|| named.dev !== opened.dev
|
|
606
|
+
|| named.ino !== opened.ino
|
|
607
|
+
|| named.uid !== opened.uid
|
|
608
|
+
|| named.gid !== opened.gid
|
|
609
|
+
|| named.mode !== opened.mode
|
|
610
|
+
|| named.size !== opened.size
|
|
611
|
+
|| named.nlink !== opened.nlink
|
|
612
|
+
|| await descriptorSha256(handle, opened.size) !== attestation.sha256) {
|
|
613
|
+
throw new KiokukoError('CONFLICT', 'Serialized backup artifact changed before parent verification');
|
|
614
|
+
}
|
|
615
|
+
const after = await handle.stat({ bigint: true });
|
|
616
|
+
if (!sameArtifactStatus(after, attestation)) {
|
|
617
|
+
throw new KiokukoError('CONFLICT', 'Serialized backup artifact changed during parent verification');
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
catch (error) {
|
|
621
|
+
operationFailed = true;
|
|
622
|
+
operationError = error;
|
|
623
|
+
throw error;
|
|
624
|
+
}
|
|
625
|
+
finally {
|
|
626
|
+
if (handle !== undefined) {
|
|
627
|
+
try {
|
|
628
|
+
await handle.close();
|
|
629
|
+
}
|
|
630
|
+
catch (closeError) {
|
|
631
|
+
if (operationFailed) {
|
|
632
|
+
throw new AggregateError([operationError, closeError], 'Serialized backup verification failed and closing its descriptor also failed');
|
|
633
|
+
}
|
|
634
|
+
throw closeError;
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
function directoryAttestation(binding) {
|
|
640
|
+
return Object.freeze({
|
|
641
|
+
path: binding.directory,
|
|
642
|
+
device: binding.device,
|
|
643
|
+
inode: binding.inode,
|
|
644
|
+
owner: binding.owner,
|
|
645
|
+
group: binding.group,
|
|
646
|
+
mode: binding.mode,
|
|
647
|
+
});
|
|
648
|
+
}
|
|
649
|
+
async function normalizedDestination(destination) {
|
|
650
|
+
if (typeof destination !== 'string'
|
|
651
|
+
|| destination.length === 0
|
|
652
|
+
|| destination.includes('\0')
|
|
653
|
+
|| !isWellFormedUnicode(destination)
|
|
654
|
+
|| /[\\/]$/u.test(destination)) {
|
|
655
|
+
throw new KiokukoError('VALIDATION_ERROR', 'Backup destination must be a non-empty pathname');
|
|
656
|
+
}
|
|
657
|
+
const requestedOutput = path.resolve(destination);
|
|
658
|
+
const fileName = path.basename(requestedOutput);
|
|
659
|
+
const portableStem = fileName.split('.', 1)[0]?.replace(/[ .]+$/u, '').toUpperCase();
|
|
660
|
+
if (fileName.length === 0
|
|
661
|
+
|| fileName === '.'
|
|
662
|
+
|| fileName === '..'
|
|
663
|
+
|| /[<>:"\/\\|?*\u0000-\u001F]/u.test(fileName)
|
|
664
|
+
|| /[ .]$/u.test(fileName)
|
|
665
|
+
|| portableStem === undefined
|
|
666
|
+
|| /^(?:CON|PRN|AUX|NUL|COM[1-9¹²³]|LPT[1-9¹²³]|CONIN\$|CONOUT\$)$/u.test(portableStem)) {
|
|
667
|
+
throw new KiokukoError('VALIDATION_ERROR', 'Backup destination must name one portable standalone file');
|
|
668
|
+
}
|
|
669
|
+
const directory = await realpath(path.dirname(requestedOutput));
|
|
670
|
+
await requireCanonicalDirectoryChain(directory);
|
|
671
|
+
return { output: path.join(directory, fileName), fileName };
|
|
672
|
+
}
|
|
673
|
+
async function syncBackupDirectory(binding) {
|
|
674
|
+
if (process.platform !== 'win32' && binding.handle !== undefined) {
|
|
675
|
+
await binding.handle.sync();
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
/**
|
|
679
|
+
* Serialize the exact already-open source and install one create-only backup.
|
|
680
|
+
* The output directory must already exist and is never chmodded by this path.
|
|
681
|
+
*/
|
|
682
|
+
export async function createSerializedBackupArtifact(database, destination, hooks = {}) {
|
|
683
|
+
const posixFlags = requirePosixBackupOpenFlags();
|
|
684
|
+
const { output, fileName } = await normalizedDestination(destination);
|
|
685
|
+
await hooks.validateDestination?.(output);
|
|
686
|
+
let directoryBinding;
|
|
687
|
+
let operationFailed = false;
|
|
688
|
+
let operationError;
|
|
689
|
+
try {
|
|
690
|
+
await hooks.beforeSerialization?.();
|
|
691
|
+
const bytes = normalizedSerializedSnapshot(database);
|
|
692
|
+
directoryBinding = await bindExistingOutputDirectory(output, posixFlags);
|
|
693
|
+
await hooks.afterDirectoryBound?.();
|
|
694
|
+
await hooks.beforeArtifactWrite?.(output);
|
|
695
|
+
const artifact = writeBoundArtifact(directoryBinding, fileName, bytes, posixFlags);
|
|
696
|
+
await requireBoundArtifact(output, artifact, posixFlags);
|
|
697
|
+
await hooks.afterArtifactWritten?.(output);
|
|
698
|
+
await requireBoundArtifact(output, artifact, posixFlags);
|
|
699
|
+
await requireBackupDirectory(directoryBinding);
|
|
700
|
+
await syncBackupDirectory(directoryBinding);
|
|
701
|
+
return Object.freeze({
|
|
702
|
+
path: output,
|
|
703
|
+
directory: directoryAttestation(directoryBinding),
|
|
704
|
+
artifact,
|
|
705
|
+
});
|
|
706
|
+
}
|
|
707
|
+
catch (error) {
|
|
708
|
+
operationFailed = true;
|
|
709
|
+
operationError = error;
|
|
710
|
+
throw error;
|
|
711
|
+
}
|
|
712
|
+
finally {
|
|
713
|
+
if (directoryBinding?.handle !== undefined) {
|
|
714
|
+
try {
|
|
715
|
+
await directoryBinding.handle.close();
|
|
716
|
+
}
|
|
717
|
+
catch (closeError) {
|
|
718
|
+
if (operationFailed) {
|
|
719
|
+
throw new AggregateError([operationError, closeError], 'Serialized backup creation failed and closing its directory also failed');
|
|
720
|
+
}
|
|
721
|
+
throw closeError;
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
export async function createPreMigrationBackup(database, databasePath, fromVersion, toVersion, hooks = {}) {
|
|
727
|
+
const posixFlags = requirePosixBackupOpenFlags();
|
|
728
|
+
let directoryBinding;
|
|
729
|
+
let operationFailed = false;
|
|
730
|
+
let operationError;
|
|
731
|
+
try {
|
|
732
|
+
await hooks.beforeSerialization?.();
|
|
733
|
+
const bytes = normalizedSerializedSnapshot(database);
|
|
734
|
+
directoryBinding = await bindBackupDirectory(databasePath, posixFlags);
|
|
735
|
+
const fileName = backupName(databasePath, fromVersion, toVersion);
|
|
736
|
+
const output = path.join(directoryBinding.directory, fileName);
|
|
737
|
+
await hooks.afterDirectoryBound?.();
|
|
738
|
+
await hooks.beforeArtifactWrite?.(output);
|
|
739
|
+
const artifact = writeBoundArtifact(directoryBinding, fileName, bytes, posixFlags);
|
|
740
|
+
await requireBoundArtifact(output, artifact, posixFlags);
|
|
741
|
+
await hooks.afterArtifactWritten?.(output);
|
|
742
|
+
await requireBoundArtifact(output, artifact, posixFlags);
|
|
743
|
+
await requireBackupDirectory(directoryBinding);
|
|
744
|
+
await syncBackupDirectory(directoryBinding);
|
|
745
|
+
return Object.freeze({
|
|
746
|
+
path: output,
|
|
747
|
+
directory: directoryAttestation(directoryBinding),
|
|
748
|
+
artifact,
|
|
749
|
+
});
|
|
750
|
+
}
|
|
751
|
+
catch (error) {
|
|
752
|
+
const failure = new KiokukoError('DATABASE_ERROR', 'Could not create and verify the pre-migration backup; the database was not migrated');
|
|
753
|
+
Object.defineProperty(failure, 'cause', { value: error });
|
|
754
|
+
operationFailed = true;
|
|
755
|
+
operationError = failure;
|
|
756
|
+
throw failure;
|
|
757
|
+
}
|
|
758
|
+
finally {
|
|
759
|
+
if (directoryBinding?.handle !== undefined) {
|
|
760
|
+
try {
|
|
761
|
+
await directoryBinding.handle.close();
|
|
762
|
+
}
|
|
763
|
+
catch (closeError) {
|
|
764
|
+
if (operationFailed) {
|
|
765
|
+
throw new AggregateError([operationError, closeError], 'Pre-migration backup failed and closing its directory also failed');
|
|
766
|
+
}
|
|
767
|
+
throw closeError;
|
|
768
|
+
}
|
|
769
|
+
}
|
|
41
770
|
}
|
|
42
771
|
}
|
|
43
772
|
//# sourceMappingURL=upgrade-backup.js.map
|