@bevel-software/platform-core-backend 0.14.0 → 0.15.1
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/THIRD-PARTY-NOTICES.md +6 -6
- package/dist/core/catalog-cache-invalidation.d.ts +29 -0
- package/dist/core/catalog-cache-invalidation.d.ts.map +1 -0
- package/dist/core/catalog-cache-invalidation.js +71 -0
- package/dist/core/catalog-cache-invalidation.js.map +1 -0
- package/dist/core/create-core-server.d.ts.map +1 -1
- package/dist/core/create-core-server.js +99 -8
- package/dist/core/create-core-server.js.map +1 -1
- package/dist/core/create-core-services.d.ts +18 -1
- package/dist/core/create-core-services.d.ts.map +1 -1
- package/dist/core/create-core-services.js +96 -43
- package/dist/core/create-core-services.js.map +1 -1
- package/dist/modules/access/access-control.interface.d.ts +18 -6
- package/dist/modules/access/access-control.interface.d.ts.map +1 -1
- package/dist/modules/access/access-control.service.d.ts +26 -1
- package/dist/modules/access/access-control.service.d.ts.map +1 -1
- package/dist/modules/access/access-control.service.js +213 -71
- package/dist/modules/access/access-control.service.js.map +1 -1
- package/dist/modules/access/access.routes.d.ts.map +1 -1
- package/dist/modules/access/access.routes.js +59 -7
- package/dist/modules/access/access.routes.js.map +1 -1
- package/dist/modules/access-model/access-grammar.d.ts +51 -1
- package/dist/modules/access-model/access-grammar.d.ts.map +1 -1
- package/dist/modules/access-model/access-grammar.js +95 -1
- package/dist/modules/access-model/access-grammar.js.map +1 -1
- package/dist/modules/access-model/group-files.d.ts.map +1 -1
- package/dist/modules/access-model/group-files.js +4 -1
- package/dist/modules/access-model/group-files.js.map +1 -1
- package/dist/modules/access-model/plugin-principals.d.ts +49 -0
- package/dist/modules/access-model/plugin-principals.d.ts.map +1 -0
- package/dist/modules/access-model/plugin-principals.js +125 -0
- package/dist/modules/access-model/plugin-principals.js.map +1 -0
- package/dist/modules/database/core-schema.d.ts +326 -0
- package/dist/modules/database/core-schema.d.ts.map +1 -1
- package/dist/modules/database/core-schema.js +52 -0
- package/dist/modules/database/core-schema.js.map +1 -1
- package/dist/modules/kb-sync/index.d.ts +6 -0
- package/dist/modules/kb-sync/index.d.ts.map +1 -0
- package/dist/modules/kb-sync/index.js +5 -0
- package/dist/modules/kb-sync/index.js.map +1 -0
- package/dist/modules/kb-sync/kb-sync.interface.d.ts +77 -0
- package/dist/modules/kb-sync/kb-sync.interface.d.ts.map +1 -0
- package/dist/modules/kb-sync/kb-sync.interface.js +2 -0
- package/dist/modules/kb-sync/kb-sync.interface.js.map +1 -0
- package/dist/modules/kb-sync/kb-sync.routes.d.ts +70 -0
- package/dist/modules/kb-sync/kb-sync.routes.d.ts.map +1 -0
- package/dist/modules/kb-sync/kb-sync.routes.js +135 -0
- package/dist/modules/kb-sync/kb-sync.routes.js.map +1 -0
- package/dist/modules/kb-sync/kb-sync.service.d.ts +45 -0
- package/dist/modules/kb-sync/kb-sync.service.d.ts.map +1 -0
- package/dist/modules/kb-sync/kb-sync.service.js +183 -0
- package/dist/modules/kb-sync/kb-sync.service.js.map +1 -0
- package/dist/modules/kb-sync/sync-auth.d.ts +59 -0
- package/dist/modules/kb-sync/sync-auth.d.ts.map +1 -0
- package/dist/modules/kb-sync/sync-auth.js +62 -0
- package/dist/modules/kb-sync/sync-auth.js.map +1 -0
- package/dist/modules/kb-sync/sync-payload.d.ts +32 -0
- package/dist/modules/kb-sync/sync-payload.d.ts.map +1 -0
- package/dist/modules/kb-sync/sync-payload.js +105 -0
- package/dist/modules/kb-sync/sync-payload.js.map +1 -0
- package/dist/modules/marketplace/git-http.routes.d.ts +42 -0
- package/dist/modules/marketplace/git-http.routes.d.ts.map +1 -0
- package/dist/modules/marketplace/git-http.routes.js +190 -0
- package/dist/modules/marketplace/git-http.routes.js.map +1 -0
- package/dist/modules/marketplace/github-facade/github-facade-admin.routes.d.ts +23 -0
- package/dist/modules/marketplace/github-facade/github-facade-admin.routes.d.ts.map +1 -0
- package/dist/modules/marketplace/github-facade/github-facade-admin.routes.js +59 -0
- package/dist/modules/marketplace/github-facade/github-facade-admin.routes.js.map +1 -0
- package/dist/modules/marketplace/github-facade/github-facade-codes.store.d.ts +59 -0
- package/dist/modules/marketplace/github-facade/github-facade-codes.store.d.ts.map +1 -0
- package/dist/modules/marketplace/github-facade/github-facade-codes.store.js +96 -0
- package/dist/modules/marketplace/github-facade/github-facade-codes.store.js.map +1 -0
- package/dist/modules/marketplace/github-facade/github-facade-credentials.service.d.ts +99 -0
- package/dist/modules/marketplace/github-facade/github-facade-credentials.service.d.ts.map +1 -0
- package/dist/modules/marketplace/github-facade/github-facade-credentials.service.js +147 -0
- package/dist/modules/marketplace/github-facade/github-facade-credentials.service.js.map +1 -0
- package/dist/modules/marketplace/github-facade/github-facade.routes.d.ts +47 -0
- package/dist/modules/marketplace/github-facade/github-facade.routes.d.ts.map +1 -0
- package/dist/modules/marketplace/github-facade/github-facade.routes.js +228 -0
- package/dist/modules/marketplace/github-facade/github-facade.routes.js.map +1 -0
- package/dist/modules/marketplace/github-facade/github-facade.service.d.ts +109 -0
- package/dist/modules/marketplace/github-facade/github-facade.service.d.ts.map +1 -0
- package/dist/modules/marketplace/github-facade/github-facade.service.js +172 -0
- package/dist/modules/marketplace/github-facade/github-facade.service.js.map +1 -0
- package/dist/modules/marketplace/github-facade/index.d.ts +6 -0
- package/dist/modules/marketplace/github-facade/index.d.ts.map +1 -0
- package/dist/modules/marketplace/github-facade/index.js +6 -0
- package/dist/modules/marketplace/github-facade/index.js.map +1 -0
- package/dist/modules/marketplace/index.d.ts +4 -0
- package/dist/modules/marketplace/index.d.ts.map +1 -0
- package/dist/modules/marketplace/index.js +4 -0
- package/dist/modules/marketplace/index.js.map +1 -0
- package/dist/modules/marketplace/marketplace-repo.service.d.ts +122 -0
- package/dist/modules/marketplace/marketplace-repo.service.d.ts.map +1 -0
- package/dist/modules/marketplace/marketplace-repo.service.js +300 -0
- package/dist/modules/marketplace/marketplace-repo.service.js.map +1 -0
- package/dist/modules/mcp/oauth/bevel-oauth-provider.d.ts.map +1 -1
- package/dist/modules/mcp/oauth/bevel-oauth-provider.js +4 -0
- package/dist/modules/mcp/oauth/bevel-oauth-provider.js.map +1 -1
- package/dist/modules/mcp/oauth/oauth-consent.routes.d.ts +14 -8
- package/dist/modules/mcp/oauth/oauth-consent.routes.d.ts.map +1 -1
- package/dist/modules/mcp/oauth/oauth-consent.routes.js +13 -1
- package/dist/modules/mcp/oauth/oauth-consent.routes.js.map +1 -1
- package/dist/modules/mcp/oauth/oauth-state.d.ts +13 -2
- package/dist/modules/mcp/oauth/oauth-state.d.ts.map +1 -1
- package/dist/modules/mcp/oauth/oauth-state.js +3 -1
- package/dist/modules/mcp/oauth/oauth-state.js.map +1 -1
- package/dist/modules/plugins/compile/compile-marketplace.d.ts +97 -0
- package/dist/modules/plugins/compile/compile-marketplace.d.ts.map +1 -0
- package/dist/modules/plugins/compile/compile-marketplace.js +240 -0
- package/dist/modules/plugins/compile/compile-marketplace.js.map +1 -0
- package/dist/modules/plugins/compile/marketplace-compiler.service.d.ts +66 -0
- package/dist/modules/plugins/compile/marketplace-compiler.service.d.ts.map +1 -0
- package/dist/modules/plugins/compile/marketplace-compiler.service.js +132 -0
- package/dist/modules/plugins/compile/marketplace-compiler.service.js.map +1 -0
- package/dist/modules/plugins/discovery/bundle-dialect/bundle.source.d.ts +37 -0
- package/dist/modules/plugins/discovery/bundle-dialect/bundle.source.d.ts.map +1 -0
- package/dist/modules/plugins/discovery/bundle-dialect/bundle.source.js +135 -0
- package/dist/modules/plugins/discovery/bundle-dialect/bundle.source.js.map +1 -0
- package/dist/modules/plugins/discovery/bundle-dialect/registry.d.ts +45 -0
- package/dist/modules/plugins/discovery/bundle-dialect/registry.d.ts.map +1 -0
- package/dist/modules/plugins/discovery/bundle-dialect/registry.js +147 -0
- package/dist/modules/plugins/discovery/bundle-dialect/registry.js.map +1 -0
- package/dist/modules/plugins/discovery/kb-plugin-source.d.ts +37 -0
- package/dist/modules/plugins/discovery/kb-plugin-source.d.ts.map +1 -0
- package/dist/modules/plugins/discovery/kb-plugin-source.js +107 -0
- package/dist/modules/plugins/discovery/kb-plugin-source.js.map +1 -0
- package/dist/modules/plugins/discovery/native.source.d.ts +15 -0
- package/dist/modules/plugins/discovery/native.source.d.ts.map +1 -0
- package/dist/modules/plugins/discovery/native.source.js +102 -0
- package/dist/modules/plugins/discovery/native.source.js.map +1 -0
- package/dist/modules/plugins/discovery/plugin-source.d.ts +96 -0
- package/dist/modules/plugins/discovery/plugin-source.d.ts.map +1 -0
- package/dist/modules/plugins/discovery/plugin-source.js +2 -0
- package/dist/modules/plugins/discovery/plugin-source.js.map +1 -0
- package/dist/modules/plugins/index.d.ts +10 -1
- package/dist/modules/plugins/index.d.ts.map +1 -1
- package/dist/modules/plugins/index.js +8 -0
- package/dist/modules/plugins/index.js.map +1 -1
- package/dist/modules/plugins/plugin-links.d.ts +66 -0
- package/dist/modules/plugins/plugin-links.d.ts.map +1 -0
- package/dist/modules/plugins/plugin-links.js +160 -0
- package/dist/modules/plugins/plugin-links.js.map +1 -0
- package/dist/modules/plugins/plugin-links.service.d.ts +114 -0
- package/dist/modules/plugins/plugin-links.service.d.ts.map +1 -0
- package/dist/modules/plugins/plugin-links.service.js +304 -0
- package/dist/modules/plugins/plugin-links.service.js.map +1 -0
- package/dist/modules/plugins/plugin-provision.service.d.ts +39 -3
- package/dist/modules/plugins/plugin-provision.service.d.ts.map +1 -1
- package/dist/modules/plugins/plugin-provision.service.js +152 -52
- package/dist/modules/plugins/plugin-provision.service.js.map +1 -1
- package/dist/modules/plugins/plugin-rename.service.d.ts +69 -0
- package/dist/modules/plugins/plugin-rename.service.d.ts.map +1 -0
- package/dist/modules/plugins/plugin-rename.service.js +245 -0
- package/dist/modules/plugins/plugin-rename.service.js.map +1 -0
- package/dist/modules/plugins/plugins.contract.d.ts +27 -1
- package/dist/modules/plugins/plugins.contract.d.ts.map +1 -1
- package/dist/modules/plugins/plugins.routes.d.ts +7 -1
- package/dist/modules/plugins/plugins.routes.d.ts.map +1 -1
- package/dist/modules/plugins/plugins.routes.js +120 -12
- package/dist/modules/plugins/plugins.routes.js.map +1 -1
- package/dist/modules/plugins/plugins.service.d.ts +50 -3
- package/dist/modules/plugins/plugins.service.d.ts.map +1 -1
- package/dist/modules/plugins/plugins.service.js +108 -47
- package/dist/modules/plugins/plugins.service.js.map +1 -1
- package/dist/modules/settings/deployment-settings.service.d.ts +9 -0
- package/dist/modules/settings/deployment-settings.service.d.ts.map +1 -1
- package/dist/modules/settings/deployment-settings.service.js +82 -2
- package/dist/modules/settings/deployment-settings.service.js.map +1 -1
- package/dist/modules/settings/setup.routes.d.ts +26 -0
- package/dist/modules/settings/setup.routes.d.ts.map +1 -1
- package/dist/modules/settings/setup.routes.js +9 -1
- package/dist/modules/settings/setup.routes.js.map +1 -1
- package/dist/modules/skills/index.d.ts +1 -0
- package/dist/modules/skills/index.d.ts.map +1 -1
- package/dist/modules/skills/index.js +1 -0
- package/dist/modules/skills/index.js.map +1 -1
- package/dist/modules/skills/pending-skills.service.d.ts.map +1 -1
- package/dist/modules/skills/pending-skills.service.js +9 -4
- package/dist/modules/skills/pending-skills.service.js.map +1 -1
- package/dist/modules/skills/skill-access-requests.routes.d.ts +38 -0
- package/dist/modules/skills/skill-access-requests.routes.d.ts.map +1 -0
- package/dist/modules/skills/skill-access-requests.routes.js +168 -0
- package/dist/modules/skills/skill-access-requests.routes.js.map +1 -0
- package/dist/modules/skills/skills.contract.d.ts +31 -0
- package/dist/modules/skills/skills.contract.d.ts.map +1 -1
- package/dist/modules/skills/skills.routes.d.ts +28 -3
- package/dist/modules/skills/skills.routes.d.ts.map +1 -1
- package/dist/modules/skills/skills.routes.js +42 -3
- package/dist/modules/skills/skills.routes.js.map +1 -1
- package/dist/modules/skills/skills.service.d.ts +4 -0
- package/dist/modules/skills/skills.service.d.ts.map +1 -1
- package/dist/modules/skills/skills.service.js +83 -22
- package/dist/modules/skills/skills.service.js.map +1 -1
- package/dist/modules/tool-auth/external-api-key.interface.d.ts +18 -1
- package/dist/modules/tool-auth/external-api-key.interface.d.ts.map +1 -1
- package/dist/modules/tool-auth/external-api-key.interface.js +2 -1
- package/dist/modules/tool-auth/external-api-key.interface.js.map +1 -1
- package/dist/modules/tool-auth/external-api-key.service.d.ts +13 -4
- package/dist/modules/tool-auth/external-api-key.service.d.ts.map +1 -1
- package/dist/modules/tool-auth/external-api-key.service.js +26 -9
- package/dist/modules/tool-auth/external-api-key.service.js.map +1 -1
- package/dist/modules/tool-manuals/mcp-json-discovery.d.ts +36 -0
- package/dist/modules/tool-manuals/mcp-json-discovery.d.ts.map +1 -1
- package/dist/modules/tool-manuals/mcp-json-discovery.js +130 -92
- package/dist/modules/tool-manuals/mcp-json-discovery.js.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.service.d.ts +6 -1
- package/dist/modules/tool-manuals/tool-manuals.service.d.ts.map +1 -1
- package/dist/modules/tool-manuals/tool-manuals.service.js +18 -25
- package/dist/modules/tool-manuals/tool-manuals.service.js.map +1 -1
- package/dist/modules/workflow/git/git.service.d.ts +40 -2
- package/dist/modules/workflow/git/git.service.d.ts.map +1 -1
- package/dist/modules/workflow/git/git.service.js +227 -54
- package/dist/modules/workflow/git/git.service.js.map +1 -1
- package/dist/modules/workflow/git/pull-request.service.d.ts +6 -0
- package/dist/modules/workflow/git/pull-request.service.d.ts.map +1 -1
- package/dist/modules/workflow/git/pull-request.service.js +8 -0
- package/dist/modules/workflow/git/pull-request.service.js.map +1 -1
- package/dist/modules/workflow/workflow.service.d.ts +52 -1
- package/dist/modules/workflow/workflow.service.d.ts.map +1 -1
- package/dist/modules/workflow/workflow.service.js +242 -9
- package/dist/modules/workflow/workflow.service.js.map +1 -1
- package/dist/modules/workspace/startup/steps/plugin-manifests.step.d.ts +35 -0
- package/dist/modules/workspace/startup/steps/plugin-manifests.step.d.ts.map +1 -0
- package/dist/modules/workspace/startup/steps/plugin-manifests.step.js +121 -0
- package/dist/modules/workspace/startup/steps/plugin-manifests.step.js.map +1 -0
- package/dist/modules/workspace/startup/steps/seed-tree.d.ts.map +1 -1
- package/dist/modules/workspace/startup/steps/seed-tree.js +46 -2
- package/dist/modules/workspace/startup/steps/seed-tree.js.map +1 -1
- package/dist/modules/workspace/startup/steps/template-files.step.d.ts.map +1 -1
- package/dist/modules/workspace/startup/steps/template-files.step.js +112 -25
- package/dist/modules/workspace/startup/steps/template-files.step.js.map +1 -1
- package/dist/modules/workspace/workspace.routes.d.ts.map +1 -1
- package/dist/modules/workspace/workspace.routes.js +9 -9
- package/dist/modules/workspace/workspace.routes.js.map +1 -1
- package/dist/modules/workspace/workspace.service.d.ts +33 -0
- package/dist/modules/workspace/workspace.service.d.ts.map +1 -1
- package/dist/modules/workspace/workspace.service.js +110 -16
- package/dist/modules/workspace/workspace.service.js.map +1 -1
- package/dist/shared/domain-errors.d.ts +20 -0
- package/dist/shared/domain-errors.d.ts.map +1 -1
- package/dist/shared/domain-errors.js +29 -0
- package/dist/shared/domain-errors.js.map +1 -1
- package/dist/shared/fs-errors.d.ts +8 -0
- package/dist/shared/fs-errors.d.ts.map +1 -0
- package/dist/shared/fs-errors.js +11 -0
- package/dist/shared/fs-errors.js.map +1 -0
- package/dist/shared/fs-walk.d.ts +20 -6
- package/dist/shared/fs-walk.d.ts.map +1 -1
- package/dist/shared/fs-walk.js +35 -27
- package/dist/shared/fs-walk.js.map +1 -1
- package/dist/shared/kb-walk.d.ts +52 -0
- package/dist/shared/kb-walk.d.ts.map +1 -0
- package/dist/shared/kb-walk.js +76 -0
- package/dist/shared/kb-walk.js.map +1 -0
- package/dist/shared/path-order.d.ts +18 -0
- package/dist/shared/path-order.d.ts.map +1 -0
- package/dist/shared/path-order.js +35 -0
- package/dist/shared/path-order.js.map +1 -0
- package/dist/shared/ttl-cache.d.ts +28 -1
- package/dist/shared/ttl-cache.d.ts.map +1 -1
- package/dist/shared/ttl-cache.js +33 -1
- package/dist/shared/ttl-cache.js.map +1 -1
- package/kb-template/.bevelignore +1 -1
- package/kb-template/AGENTS.md +95 -30
- package/kb-template/Skills/.gitkeep +0 -0
- package/migrations/0005_github_facade.sql +25 -0
- package/migrations/meta/0005_snapshot.json +1717 -0
- package/migrations/meta/_journal.json +7 -0
- package/package.json +6 -6
- package/src/__tests__/kb-layout-config.test.ts +110 -0
- package/src/core/__tests__/catalog-cache-invalidation.test.ts +146 -0
- package/src/core/catalog-cache-invalidation.ts +92 -0
- package/src/core/create-core-server.ts +123 -7
- package/src/core/create-core-services.ts +162 -41
- package/src/modules/access/__tests__/access-control.invalidate-race.test.ts +86 -0
- package/src/modules/access/__tests__/access-control.service.test.ts +19 -3
- package/src/modules/access/__tests__/access-plugin-principals.test.ts +368 -0
- package/src/modules/access/__tests__/groups-admin.service.test.ts +8 -0
- package/src/modules/access/access-control.interface.ts +20 -9
- package/src/modules/access/access-control.service.ts +240 -78
- package/src/modules/access/access.routes.ts +96 -14
- package/src/modules/access-model/__tests__/access-grammar.test.ts +18 -0
- package/src/modules/access-model/__tests__/plugin-token-canonical.test.ts +40 -0
- package/src/modules/access-model/access-grammar.ts +114 -2
- package/src/modules/access-model/group-files.ts +4 -0
- package/src/modules/access-model/plugin-principals.ts +137 -0
- package/src/modules/database/core-schema.ts +54 -0
- package/src/modules/kb-sync/__tests__/kb-sync.routes.test.ts +297 -0
- package/src/modules/kb-sync/__tests__/kb-sync.service.test.ts +295 -0
- package/src/modules/kb-sync/__tests__/sync-auth.test.ts +118 -0
- package/src/modules/kb-sync/__tests__/sync-payload.test.ts +148 -0
- package/src/modules/kb-sync/index.ts +19 -0
- package/src/modules/kb-sync/kb-sync.interface.ts +79 -0
- package/src/modules/kb-sync/kb-sync.routes.ts +197 -0
- package/src/modules/kb-sync/kb-sync.service.ts +201 -0
- package/src/modules/kb-sync/sync-auth.ts +118 -0
- package/src/modules/kb-sync/sync-payload.ts +143 -0
- package/src/modules/marketplace/__tests__/github-facade.test.ts +443 -0
- package/src/modules/marketplace/__tests__/marketplace-git.test.ts +179 -0
- package/src/modules/marketplace/git-http.routes.ts +193 -0
- package/src/modules/marketplace/github-facade/github-facade-admin.routes.ts +78 -0
- package/src/modules/marketplace/github-facade/github-facade-codes.store.ts +145 -0
- package/src/modules/marketplace/github-facade/github-facade-credentials.service.ts +208 -0
- package/src/modules/marketplace/github-facade/github-facade.routes.ts +268 -0
- package/src/modules/marketplace/github-facade/github-facade.service.ts +221 -0
- package/src/modules/marketplace/github-facade/index.ts +25 -0
- package/src/modules/marketplace/index.ts +3 -0
- package/src/modules/marketplace/marketplace-repo.service.ts +337 -0
- package/src/modules/mcp/oauth/bevel-oauth-provider.ts +3 -0
- package/src/modules/mcp/oauth/oauth-consent.routes.ts +24 -2
- package/src/modules/mcp/oauth/oauth-state.ts +15 -3
- package/src/modules/plugins/__tests__/bundle-dialect.e2e.test.ts +163 -0
- package/src/modules/plugins/__tests__/compile-marketplace.test.ts +294 -0
- package/src/modules/plugins/__tests__/plugin-index.service.test.ts +56 -12
- package/src/modules/plugins/__tests__/plugin-links.service.test.ts +287 -0
- package/src/modules/plugins/__tests__/plugin-provision.service.test.ts +205 -5
- package/src/modules/plugins/__tests__/plugin-rename.service.test.ts +394 -0
- package/src/modules/plugins/__tests__/plugins.routes.test.ts +80 -33
- package/src/modules/plugins/compile/compile-marketplace.ts +403 -0
- package/src/modules/plugins/compile/marketplace-compiler.service.ts +150 -0
- package/src/modules/plugins/discovery/__tests__/kb-plugin-source.test.ts +329 -0
- package/src/modules/plugins/discovery/bundle-dialect/bundle.source.ts +140 -0
- package/src/modules/plugins/discovery/bundle-dialect/registry.ts +175 -0
- package/src/modules/plugins/discovery/kb-plugin-source.ts +114 -0
- package/src/modules/plugins/discovery/native.source.ts +122 -0
- package/src/modules/plugins/discovery/plugin-source.ts +99 -0
- package/src/modules/plugins/index.ts +10 -0
- package/src/modules/plugins/plugin-links.service.ts +363 -0
- package/src/modules/plugins/plugin-links.ts +184 -0
- package/src/modules/plugins/plugin-provision.service.ts +160 -54
- package/src/modules/plugins/plugin-rename.service.ts +298 -0
- package/src/modules/plugins/plugins.contract.ts +28 -1
- package/src/modules/plugins/plugins.routes.ts +128 -11
- package/src/modules/plugins/plugins.service.ts +106 -53
- package/src/modules/settings/__tests__/deployment-settings.service.test.ts +57 -0
- package/src/modules/settings/__tests__/setup.routes.sync-status.test.ts +77 -0
- package/src/modules/settings/deployment-settings.service.ts +95 -2
- package/src/modules/settings/setup.routes.ts +24 -0
- package/src/modules/skills/__tests__/skill-access-requests.routes.test.ts +128 -0
- package/src/modules/skills/__tests__/skills.service.test.ts +85 -0
- package/src/modules/skills/index.ts +1 -0
- package/src/modules/skills/pending-skills.service.ts +7 -5
- package/src/modules/skills/skill-access-requests.routes.ts +199 -0
- package/src/modules/skills/skills.contract.ts +32 -0
- package/src/modules/skills/skills.routes.ts +59 -3
- package/src/modules/skills/skills.service.ts +84 -22
- package/src/modules/tool-auth/external-api-key.interface.ts +20 -1
- package/src/modules/tool-auth/external-api-key.service.ts +31 -13
- package/src/modules/tool-manuals/__tests__/mcp-json-discovery.test.ts +24 -1
- package/src/modules/tool-manuals/__tests__/tool-manuals.cli.test.ts +2 -0
- package/src/modules/tool-manuals/mcp-json-discovery.ts +154 -88
- package/src/modules/tool-manuals/tool-manuals.service.ts +14 -23
- package/src/modules/workflow/__tests__/preserve-roles-yaml.test.ts +1 -1
- package/src/modules/workflow/__tests__/workflow.service.commitFileWhileLocked.test.ts +1 -0
- package/src/modules/workflow/__tests__/workflow.service.facade.test.ts +2 -2
- package/src/modules/workflow/__tests__/workflow.service.releaseLock.test.ts +119 -2
- package/src/modules/workflow/__tests__/workflow.service.sync.test.ts +437 -0
- package/src/modules/workflow/git/__tests__/git.service.postMergePull.test.ts +3 -3
- package/src/modules/workflow/git/__tests__/git.service.pull.test.ts +47 -4
- package/src/modules/workflow/git/__tests__/git.service.syncFromRemote.test.ts +253 -0
- package/src/modules/workflow/git/git.service.ts +239 -56
- package/src/modules/workflow/git/pull-request.service.ts +9 -0
- package/src/modules/workflow/workflow.service.ts +260 -9
- package/src/modules/workspace/__tests__/workspace.routes.read-gate.test.ts +6 -7
- package/src/modules/workspace/__tests__/workspace.service.list-cloned.test.ts +93 -0
- package/src/modules/workspace/__tests__/workspace.service.missing-branch.test.ts +28 -0
- package/src/modules/workspace/__tests__/workspace.service.read-filter.test.ts +42 -12
- package/src/modules/workspace/startup/steps/__tests__/steps.test.ts +219 -4
- package/src/modules/workspace/startup/steps/plugin-manifests.step.ts +129 -0
- package/src/modules/workspace/startup/steps/seed-tree.ts +44 -2
- package/src/modules/workspace/startup/steps/template-files.step.ts +131 -27
- package/src/modules/workspace/workspace.routes.ts +9 -17
- package/src/modules/workspace/workspace.service.ts +107 -16
- package/src/shared/__tests__/filename.test.ts +0 -0
- package/src/shared/__tests__/fs-walk.test.ts +22 -1
- package/src/shared/__tests__/kb-walk.test.ts +107 -0
- package/src/shared/__tests__/path-order.test.ts +24 -0
- package/src/shared/__tests__/ttl-cache.test.ts +55 -0
- package/src/shared/domain-errors.ts +29 -0
- package/src/shared/fs-errors.ts +10 -0
- package/src/shared/fs-walk.ts +38 -23
- package/src/shared/kb-walk.ts +95 -0
- package/src/shared/path-order.ts +33 -0
- package/src/shared/ttl-cache.ts +33 -1
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { assertValidBranchName } from '../kb-fs/branch-name.js';
|
|
2
|
+
const HEADS = 'refs/heads/';
|
|
3
|
+
function isRecord(v) {
|
|
4
|
+
return typeof v === 'object' && v !== null && !Array.isArray(v);
|
|
5
|
+
}
|
|
6
|
+
function branchFromRef(ref) {
|
|
7
|
+
if (typeof ref !== 'string' || !ref.startsWith(HEADS))
|
|
8
|
+
return null;
|
|
9
|
+
const name = ref.slice(HEADS.length);
|
|
10
|
+
return isValidBranch(name) ? name : null;
|
|
11
|
+
}
|
|
12
|
+
function isValidBranch(name) {
|
|
13
|
+
try {
|
|
14
|
+
assertValidBranchName(name);
|
|
15
|
+
return true;
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
function unique(names) {
|
|
22
|
+
return [...new Set(names)];
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* How an invalid explicit entry is echoed back. Only primitives are
|
|
26
|
+
* stringified: `String(obj)` on a JSON object carrying `"toString": 1` throws
|
|
27
|
+
* ("Cannot convert object to primitive value"), which would turn a caller's
|
|
28
|
+
* bad body into our 500.
|
|
29
|
+
*/
|
|
30
|
+
function describeInvalid(entry) {
|
|
31
|
+
if (entry === null)
|
|
32
|
+
return 'null';
|
|
33
|
+
switch (typeof entry) {
|
|
34
|
+
case 'string':
|
|
35
|
+
return entry.trim() === '' ? '(empty)' : entry;
|
|
36
|
+
case 'number':
|
|
37
|
+
case 'boolean':
|
|
38
|
+
case 'bigint':
|
|
39
|
+
return String(entry);
|
|
40
|
+
default:
|
|
41
|
+
return Array.isArray(entry) ? '(array)' : '(object)';
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
export function parseSyncPayload(body) {
|
|
45
|
+
if (!isRecord(body))
|
|
46
|
+
return { source: 'none', branches: 'all', invalid: [] };
|
|
47
|
+
if ('branches' in body) {
|
|
48
|
+
const raw = body.branches;
|
|
49
|
+
if (!Array.isArray(raw))
|
|
50
|
+
return { source: 'explicit', branches: 'all', invalid: [] };
|
|
51
|
+
const valid = [];
|
|
52
|
+
const invalid = [];
|
|
53
|
+
for (const entry of raw) {
|
|
54
|
+
if (typeof entry !== 'string' || !entry.trim()) {
|
|
55
|
+
invalid.push(describeInvalid(entry));
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
const name = entry.trim();
|
|
59
|
+
(isValidBranch(name) ? valid : invalid).push(name);
|
|
60
|
+
}
|
|
61
|
+
return { source: 'explicit', branches: unique(valid), invalid };
|
|
62
|
+
}
|
|
63
|
+
// Azure DevOps service hook. `eventType` names the event; the branches sit
|
|
64
|
+
// under `resource` in a per-event shape.
|
|
65
|
+
const eventType = body.eventType;
|
|
66
|
+
if (typeof eventType === 'string' && isRecord(body.resource)) {
|
|
67
|
+
const resource = body.resource;
|
|
68
|
+
if (eventType === 'git.push') {
|
|
69
|
+
const updates = Array.isArray(resource.refUpdates) ? resource.refUpdates : [];
|
|
70
|
+
// A DELETION (new object id all zeros) names its branch too: syncing it
|
|
71
|
+
// is what finds the branch gone and retires the stale clone, instead of
|
|
72
|
+
// leaving it to fail on some unrelated full sync later.
|
|
73
|
+
const branches = updates
|
|
74
|
+
.map((u) => (isRecord(u) ? branchFromRef(u.name) : null))
|
|
75
|
+
.filter((b) => b !== null);
|
|
76
|
+
return { source: 'azure-devops', branches: unique(branches), invalid: [] };
|
|
77
|
+
}
|
|
78
|
+
if (eventType.startsWith('git.pullrequest.')) {
|
|
79
|
+
const branches = [branchFromRef(resource.targetRefName), branchFromRef(resource.sourceRefName)]
|
|
80
|
+
.filter((b) => b !== null);
|
|
81
|
+
return { source: 'azure-devops', branches: unique(branches), invalid: [] };
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
// GitLab events all carry `object_kind`. Only a push (or a tag push, which
|
|
85
|
+
// names no branch) is a statement about a ref; anything else with a stray
|
|
86
|
+
// `ref` — a merge request, a pipeline — is not, and takes the "everything"
|
|
87
|
+
// fallback rather than being read as a push of that ref.
|
|
88
|
+
if (typeof body.object_kind === 'string') {
|
|
89
|
+
if (body.object_kind !== 'push' && body.object_kind !== 'tag_push') {
|
|
90
|
+
return { source: 'none', branches: 'all', invalid: [] };
|
|
91
|
+
}
|
|
92
|
+
// A deletion (a push to the null sha) names its branch like any push: the
|
|
93
|
+
// sync finds it gone and retires the clone.
|
|
94
|
+
const branch = branchFromRef(body.ref);
|
|
95
|
+
return { source: 'gitlab', branches: branch ? [branch] : [], invalid: [] };
|
|
96
|
+
}
|
|
97
|
+
// GitHub / Gitea push: `ref` at the top level, no `object_kind`. A deletion
|
|
98
|
+
// (`deleted: true`) names its branch like any push, for the same reason.
|
|
99
|
+
if (typeof body.ref === 'string') {
|
|
100
|
+
const branch = branchFromRef(body.ref);
|
|
101
|
+
return { source: 'github', branches: branch ? [branch] : [], invalid: [] };
|
|
102
|
+
}
|
|
103
|
+
return { source: 'none', branches: 'all', invalid: [] };
|
|
104
|
+
}
|
|
105
|
+
//# sourceMappingURL=sync-payload.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sync-payload.js","sourceRoot":"","sources":["../../../src/modules/kb-sync/sync-payload.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import express from 'express';
|
|
2
|
+
import type { AuthUser } from '@bevel-software/platform-shared';
|
|
3
|
+
import type { MarketplaceRepoService } from './marketplace-repo.service.js';
|
|
4
|
+
import '../tool-auth/external-api-key.interface.js';
|
|
5
|
+
/** How a bearer string resolves to a user — the connection-key service's shape. */
|
|
6
|
+
export interface MarketplaceKeyResolver {
|
|
7
|
+
looksLikeExternalApiKey(token: string): boolean;
|
|
8
|
+
verifyAndLoadToken(plaintext: string): Promise<{
|
|
9
|
+
tokenId: string;
|
|
10
|
+
user: AuthUser;
|
|
11
|
+
} | null>;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* The per-user marketplace as a git remote:
|
|
15
|
+
*
|
|
16
|
+
* git clone https://key:<connection-key>@<host>/git/marketplace.git
|
|
17
|
+
*
|
|
18
|
+
* Anything that speaks git's smart HTTP protocol — Claude Code, Codex, `npx
|
|
19
|
+
* skills`, plain git — clones a repository whose `main` is the caller's
|
|
20
|
+
* compiled tree. The route authenticates, brings the caller's namespace up to
|
|
21
|
+
* date (`MarketplaceRepoService.ensureCompiled`), and hands the request to
|
|
22
|
+
* `git http-backend` with `GIT_NAMESPACE` set, so git itself serves the
|
|
23
|
+
* protocol and only that namespace's refs exist as far as the client can tell.
|
|
24
|
+
*
|
|
25
|
+
* AUTH: HTTP Basic with the connection key as the password (the username is
|
|
26
|
+
* anything — git sends whatever the URL carried), or the key as a Bearer
|
|
27
|
+
* token. The same keys the MCP endpoint takes, the same service resolving
|
|
28
|
+
* them, and the same metering stamp (`req.externalApiKeyId`), so a key's
|
|
29
|
+
* `lastUsedAt` moves when an agent refreshes its plugins. A 401 challenges
|
|
30
|
+
* with Basic so a client without credentials prompts for them.
|
|
31
|
+
*
|
|
32
|
+
* READ-ONLY: the receive-pack service is refused here by name, and the bare
|
|
33
|
+
* repository pins `http.receivepack=false` for good measure — a push would
|
|
34
|
+
* otherwise be enabled by http-backend the moment REMOTE_USER is set.
|
|
35
|
+
*/
|
|
36
|
+
export declare function createMarketplaceGitRoutes(deps: {
|
|
37
|
+
repo: MarketplaceRepoService;
|
|
38
|
+
keys: MarketplaceKeyResolver;
|
|
39
|
+
/** The URL path the router is mounted at, e.g. `/git`. Used for PATH_INFO. */
|
|
40
|
+
mountPath: string;
|
|
41
|
+
}): express.Router;
|
|
42
|
+
//# sourceMappingURL=git-http.routes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git-http.routes.d.ts","sourceRoot":"","sources":["../../../src/modules/marketplace/git-http.routes.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,SAAS,CAAC;AAE9B,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iCAAiC,CAAC;AAChE,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,+BAA+B,CAAC;AAC5E,OAAO,4CAA4C,CAAC;AAEpD,mFAAmF;AACnF,MAAM,WAAW,sBAAsB;IACrC,uBAAuB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;IAChD,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,QAAQ,CAAA;KAAE,GAAG,IAAI,CAAC,CAAC;CAC5F;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,0BAA0B,CAAC,IAAI,EAAE;IAC/C,IAAI,EAAE,sBAAsB,CAAC;IAC7B,IAAI,EAAE,sBAAsB,CAAC;IAC7B,8EAA8E;IAC9E,SAAS,EAAE,MAAM,CAAC;CACnB,GAAG,OAAO,CAAC,MAAM,CAwJjB"}
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import express from 'express';
|
|
2
|
+
import { spawn } from 'node:child_process';
|
|
3
|
+
import '../tool-auth/external-api-key.interface.js'; // Express Request augmentation (req.externalApiKeyId)
|
|
4
|
+
/**
|
|
5
|
+
* The per-user marketplace as a git remote:
|
|
6
|
+
*
|
|
7
|
+
* git clone https://key:<connection-key>@<host>/git/marketplace.git
|
|
8
|
+
*
|
|
9
|
+
* Anything that speaks git's smart HTTP protocol — Claude Code, Codex, `npx
|
|
10
|
+
* skills`, plain git — clones a repository whose `main` is the caller's
|
|
11
|
+
* compiled tree. The route authenticates, brings the caller's namespace up to
|
|
12
|
+
* date (`MarketplaceRepoService.ensureCompiled`), and hands the request to
|
|
13
|
+
* `git http-backend` with `GIT_NAMESPACE` set, so git itself serves the
|
|
14
|
+
* protocol and only that namespace's refs exist as far as the client can tell.
|
|
15
|
+
*
|
|
16
|
+
* AUTH: HTTP Basic with the connection key as the password (the username is
|
|
17
|
+
* anything — git sends whatever the URL carried), or the key as a Bearer
|
|
18
|
+
* token. The same keys the MCP endpoint takes, the same service resolving
|
|
19
|
+
* them, and the same metering stamp (`req.externalApiKeyId`), so a key's
|
|
20
|
+
* `lastUsedAt` moves when an agent refreshes its plugins. A 401 challenges
|
|
21
|
+
* with Basic so a client without credentials prompts for them.
|
|
22
|
+
*
|
|
23
|
+
* READ-ONLY: the receive-pack service is refused here by name, and the bare
|
|
24
|
+
* repository pins `http.receivepack=false` for good measure — a push would
|
|
25
|
+
* otherwise be enabled by http-backend the moment REMOTE_USER is set.
|
|
26
|
+
*/
|
|
27
|
+
export function createMarketplaceGitRoutes(deps) {
|
|
28
|
+
const { repo, keys, mountPath } = deps;
|
|
29
|
+
const router = express.Router();
|
|
30
|
+
const repoRoute = `/${repo.repoName}`;
|
|
31
|
+
const challenge = (res, message) => {
|
|
32
|
+
res.setHeader('WWW-Authenticate', 'Basic realm="hexis-marketplace"');
|
|
33
|
+
res.status(401).type('text/plain').send(message);
|
|
34
|
+
};
|
|
35
|
+
/** The connection key out of Basic (`user:key`, `key:` or `:key`) or Bearer. */
|
|
36
|
+
const keyOf = (header) => {
|
|
37
|
+
if (!header)
|
|
38
|
+
return null;
|
|
39
|
+
const space = header.indexOf(' ');
|
|
40
|
+
if (space < 0)
|
|
41
|
+
return null;
|
|
42
|
+
const scheme = header.slice(0, space).toLowerCase();
|
|
43
|
+
const value = header.slice(space + 1).trim();
|
|
44
|
+
if (scheme === 'bearer')
|
|
45
|
+
return value || null;
|
|
46
|
+
if (scheme !== 'basic')
|
|
47
|
+
return null;
|
|
48
|
+
let decoded;
|
|
49
|
+
try {
|
|
50
|
+
decoded = Buffer.from(value, 'base64').toString('utf-8');
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
const colon = decoded.indexOf(':');
|
|
56
|
+
const user = colon < 0 ? decoded : decoded.slice(0, colon);
|
|
57
|
+
const pass = colon < 0 ? '' : decoded.slice(colon + 1);
|
|
58
|
+
// Either half may carry the key: `https://key:<k>@` puts it in the
|
|
59
|
+
// password, `https://<k>@` in the username.
|
|
60
|
+
if (pass && keys.looksLikeExternalApiKey(pass))
|
|
61
|
+
return pass;
|
|
62
|
+
if (user && keys.looksLikeExternalApiKey(user))
|
|
63
|
+
return user;
|
|
64
|
+
return pass || user || null;
|
|
65
|
+
};
|
|
66
|
+
router.all(`${repoRoute}{/*rest}`, async (req, res) => {
|
|
67
|
+
const service = String(req.query.service ?? '');
|
|
68
|
+
const rest = `/${[].concat(req.params.rest ?? []).join('/')}`;
|
|
69
|
+
// Refuse writes before touching anything — by service name on info/refs,
|
|
70
|
+
// and by endpoint on the POST.
|
|
71
|
+
if (service === 'git-receive-pack' || rest === '/git-receive-pack') {
|
|
72
|
+
res.status(403).type('text/plain').send('This repository is read-only.');
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
const key = keyOf(req.headers.authorization);
|
|
76
|
+
if (!key || !keys.looksLikeExternalApiKey(key)) {
|
|
77
|
+
challenge(res, 'A connection key is required: https://key:<connection-key>@<host>/git/marketplace.git');
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
let resolved;
|
|
81
|
+
try {
|
|
82
|
+
resolved = await keys.verifyAndLoadToken(key);
|
|
83
|
+
}
|
|
84
|
+
catch (err) {
|
|
85
|
+
console.error('[marketplace] connection-key verification failed:', err);
|
|
86
|
+
res.status(500).type('text/plain').send('Authentication backend unavailable');
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
if (!resolved) {
|
|
90
|
+
challenge(res, 'Invalid or revoked connection key');
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
req.userId = resolved.user.id;
|
|
94
|
+
req.userEmail = resolved.user.email;
|
|
95
|
+
req.externalApiKeyId = resolved.tokenId;
|
|
96
|
+
let ensured;
|
|
97
|
+
try {
|
|
98
|
+
ensured = await repo.ensureCompiled({ id: resolved.user.id, email: resolved.user.email });
|
|
99
|
+
}
|
|
100
|
+
catch (err) {
|
|
101
|
+
console.error('[marketplace] compile failed:', err);
|
|
102
|
+
res.status(500).type('text/plain').send('Could not prepare your marketplace');
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
// CGI, as git documents it. PATH_INFO is the path below GIT_PROJECT_ROOT
|
|
106
|
+
// — `/marketplace.git/info/refs` — regardless of where we are mounted.
|
|
107
|
+
const query = req.url.includes('?') ? req.url.slice(req.url.indexOf('?') + 1) : '';
|
|
108
|
+
const env = {
|
|
109
|
+
...process.env,
|
|
110
|
+
GIT_PROJECT_ROOT: repo.projectRoot,
|
|
111
|
+
GIT_HTTP_EXPORT_ALL: '1',
|
|
112
|
+
GIT_NAMESPACE: ensured.namespace,
|
|
113
|
+
PATH_INFO: `${repoRoute}${rest}`,
|
|
114
|
+
REQUEST_METHOD: req.method,
|
|
115
|
+
QUERY_STRING: query,
|
|
116
|
+
CONTENT_TYPE: req.headers['content-type'] ?? '',
|
|
117
|
+
REMOTE_USER: resolved.user.email,
|
|
118
|
+
REMOTE_ADDR: req.ip ?? '',
|
|
119
|
+
SERVER_PROTOCOL: 'HTTP/1.1',
|
|
120
|
+
HTTP_HOST: req.headers.host ?? '',
|
|
121
|
+
SCRIPT_NAME: mountPath,
|
|
122
|
+
};
|
|
123
|
+
if (req.headers['content-length'])
|
|
124
|
+
env.CONTENT_LENGTH = String(req.headers['content-length']);
|
|
125
|
+
if (req.headers['content-encoding'])
|
|
126
|
+
env.HTTP_CONTENT_ENCODING = String(req.headers['content-encoding']);
|
|
127
|
+
if (req.headers['git-protocol'])
|
|
128
|
+
env.HTTP_GIT_PROTOCOL = String(req.headers['git-protocol']);
|
|
129
|
+
const child = spawn('git', ['http-backend'], { env, stdio: ['pipe', 'pipe', 'pipe'] });
|
|
130
|
+
child.stderr.on('data', (chunk) => console.warn(`[marketplace] http-backend: ${chunk.toString('utf-8').trim()}`));
|
|
131
|
+
child.on('error', (err) => {
|
|
132
|
+
console.error('[marketplace] could not run git http-backend:', err);
|
|
133
|
+
if (!res.headersSent)
|
|
134
|
+
res.status(500).type('text/plain').send('git is unavailable');
|
|
135
|
+
});
|
|
136
|
+
req.pipe(child.stdin);
|
|
137
|
+
req.on('aborted', () => child.kill());
|
|
138
|
+
// Parse the CGI header block (`Status:`, `Content-Type:`, …) off stdout,
|
|
139
|
+
// then stream the body straight through.
|
|
140
|
+
let headerBuf = Buffer.alloc(0);
|
|
141
|
+
let headersDone = false;
|
|
142
|
+
// Backpressure: a slow client must not make Node buffer a whole pack. One
|
|
143
|
+
// path for every body write, the remainder of the header chunk included.
|
|
144
|
+
const writeBody = (bytes) => {
|
|
145
|
+
if (!res.write(bytes)) {
|
|
146
|
+
child.stdout.pause();
|
|
147
|
+
res.once('drain', () => child.stdout.resume());
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
child.stdout.on('data', (chunk) => {
|
|
151
|
+
if (headersDone) {
|
|
152
|
+
writeBody(chunk);
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
headerBuf = Buffer.concat([headerBuf, chunk]);
|
|
156
|
+
const cut = headerBuf.indexOf('\r\n\r\n') >= 0 ? headerBuf.indexOf('\r\n\r\n') : headerBuf.indexOf('\n\n');
|
|
157
|
+
if (cut < 0)
|
|
158
|
+
return;
|
|
159
|
+
const sepLen = headerBuf.indexOf('\r\n\r\n') >= 0 ? 4 : 2;
|
|
160
|
+
const head = headerBuf.subarray(0, cut).toString('utf-8');
|
|
161
|
+
let status = 200;
|
|
162
|
+
for (const line of head.split(/\r?\n/)) {
|
|
163
|
+
const colon = line.indexOf(':');
|
|
164
|
+
if (colon < 0)
|
|
165
|
+
continue;
|
|
166
|
+
const name = line.slice(0, colon).trim();
|
|
167
|
+
const value = line.slice(colon + 1).trim();
|
|
168
|
+
if (name.toLowerCase() === 'status')
|
|
169
|
+
status = Number.parseInt(value, 10) || 200;
|
|
170
|
+
else
|
|
171
|
+
res.setHeader(name, value);
|
|
172
|
+
}
|
|
173
|
+
res.status(status);
|
|
174
|
+
headersDone = true;
|
|
175
|
+
const body = headerBuf.subarray(cut + sepLen);
|
|
176
|
+
if (body.length > 0)
|
|
177
|
+
writeBody(body);
|
|
178
|
+
});
|
|
179
|
+
child.stdout.on('end', () => {
|
|
180
|
+
if (!headersDone) {
|
|
181
|
+
if (!res.headersSent)
|
|
182
|
+
res.status(502).type('text/plain').send('git http-backend produced no response');
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
res.end();
|
|
186
|
+
});
|
|
187
|
+
});
|
|
188
|
+
return router;
|
|
189
|
+
}
|
|
190
|
+
//# sourceMappingURL=git-http.routes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git-http.routes.js","sourceRoot":"","sources":["../../../src/modules/marketplace/git-http.routes.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import express from 'express';
|
|
2
|
+
import '../../auth/auth.middleware.js';
|
|
3
|
+
import { type GitHubFacadeCredentialsService } from './github-facade-credentials.service.js';
|
|
4
|
+
export interface GitHubFacadeAdminRoutesDeps {
|
|
5
|
+
credentials: GitHubFacadeCredentialsService;
|
|
6
|
+
isAdmin(email: string | undefined): Promise<boolean>;
|
|
7
|
+
/** The deployment's public address; only its host is shown. */
|
|
8
|
+
publicUrl: string;
|
|
9
|
+
/** The marketplace URL people paste — the same one Claude Code clones. */
|
|
10
|
+
marketplaceUrl: string;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* What an Owner needs to register this deployment in Claude's admin settings
|
|
14
|
+
* as a GitHub Enterprise Server, in the fields that form has — and the one
|
|
15
|
+
* verb on it, rotate. Admins only: the client secret is what makes claude.ai
|
|
16
|
+
* trust the token exchange. Never cached: after a rotation or a sign-out no
|
|
17
|
+
* copy of the old secrets may linger in a browser or a proxy.
|
|
18
|
+
*
|
|
19
|
+
* GET /api/admin/github-facade the credentials and the two URLs
|
|
20
|
+
* POST /api/admin/github-facade/rotate new credentials, all of them
|
|
21
|
+
*/
|
|
22
|
+
export declare function createGitHubFacadeAdminRoutes(deps: GitHubFacadeAdminRoutesDeps): express.Router;
|
|
23
|
+
//# sourceMappingURL=github-facade-admin.routes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"github-facade-admin.routes.d.ts","sourceRoot":"","sources":["../../../../src/modules/marketplace/github-facade/github-facade-admin.routes.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,SAAS,CAAC;AAC9B,OAAO,+BAA+B,CAAC;AACvC,OAAO,EAEL,KAAK,8BAA8B,EACpC,MAAM,wCAAwC,CAAC;AAEhD,MAAM,WAAW,2BAA2B;IAC1C,WAAW,EAAE,8BAA8B,CAAC;IAC5C,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACrD,+DAA+D;IAC/D,SAAS,EAAE,MAAM,CAAC;IAClB,0EAA0E;IAC1E,cAAc,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;;;;GASG;AACH,wBAAgB,6BAA6B,CAAC,IAAI,EAAE,2BAA2B,GAAG,OAAO,CAAC,MAAM,CAmD/F"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import express from 'express';
|
|
2
|
+
import '../../auth/auth.middleware.js'; // Express Request.userId / userEmail augmentation
|
|
3
|
+
import { GitHubFacadeUnavailableError, } from './github-facade-credentials.service.js';
|
|
4
|
+
/**
|
|
5
|
+
* What an Owner needs to register this deployment in Claude's admin settings
|
|
6
|
+
* as a GitHub Enterprise Server, in the fields that form has — and the one
|
|
7
|
+
* verb on it, rotate. Admins only: the client secret is what makes claude.ai
|
|
8
|
+
* trust the token exchange. Never cached: after a rotation or a sign-out no
|
|
9
|
+
* copy of the old secrets may linger in a browser or a proxy.
|
|
10
|
+
*
|
|
11
|
+
* GET /api/admin/github-facade the credentials and the two URLs
|
|
12
|
+
* POST /api/admin/github-facade/rotate new credentials, all of them
|
|
13
|
+
*/
|
|
14
|
+
export function createGitHubFacadeAdminRoutes(deps) {
|
|
15
|
+
const router = express.Router();
|
|
16
|
+
const requireAdmin = async (req, res, next) => {
|
|
17
|
+
if (!(await deps.isAdmin(req.userEmail))) {
|
|
18
|
+
res.status(403).json({ error: 'Admins only' });
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
next();
|
|
22
|
+
};
|
|
23
|
+
const describe = async () => {
|
|
24
|
+
const creds = await deps.credentials.ensure();
|
|
25
|
+
return {
|
|
26
|
+
host: new URL(deps.publicUrl).host,
|
|
27
|
+
appId: creds.appId,
|
|
28
|
+
clientId: creds.clientId,
|
|
29
|
+
clientSecret: creds.clientSecret,
|
|
30
|
+
webhookSecret: creds.webhookSecret,
|
|
31
|
+
privateKeyPem: creds.privateKeyPem,
|
|
32
|
+
marketplaceUrl: deps.marketplaceUrl,
|
|
33
|
+
createdAt: creds.createdAt.getTime(),
|
|
34
|
+
rotatedAt: creds.rotatedAt ? creds.rotatedAt.getTime() : null,
|
|
35
|
+
};
|
|
36
|
+
};
|
|
37
|
+
const send = async (res, body) => {
|
|
38
|
+
try {
|
|
39
|
+
const payload = await body();
|
|
40
|
+
res.setHeader('Cache-Control', 'no-store');
|
|
41
|
+
res.json(payload);
|
|
42
|
+
}
|
|
43
|
+
catch (err) {
|
|
44
|
+
if (err instanceof GitHubFacadeUnavailableError) {
|
|
45
|
+
res.status(409).json({ error: err.message });
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
console.error('[github-facade admin]', err);
|
|
49
|
+
res.status(500).json({ error: 'Internal error' });
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
router.get('/admin/github-facade', requireAdmin, (_req, res) => send(res, describe));
|
|
53
|
+
router.post('/admin/github-facade/rotate', requireAdmin, (_req, res) => send(res, async () => {
|
|
54
|
+
await deps.credentials.rotate();
|
|
55
|
+
return describe();
|
|
56
|
+
}));
|
|
57
|
+
return router;
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=github-facade-admin.routes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"github-facade-admin.routes.js","sourceRoot":"","sources":["../../../../src/modules/marketplace/github-facade/github-facade-admin.routes.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,SAAS,CAAC;AAC9B,OAAO,+BAA+B,CAAC,CAAC,kDAAkD;AAC1F,OAAO,EACL,4BAA4B,GAE7B,MAAM,wCAAwC,CAAC;AAWhD;;;;;;;;;GASG;AACH,MAAM,UAAU,6BAA6B,CAAC,IAAiC;IAC7E,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAEhC,MAAM,YAAY,GAA2B,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;QACpE,IAAI,CAAC,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC;YACzC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,aAAa,EAAE,CAAC,CAAC;YAC/C,OAAO;QACT,CAAC;QACD,IAAI,EAAE,CAAC;IACT,CAAC,CAAC;IAEF,MAAM,QAAQ,GAAG,KAAK,IAAI,EAAE;QAC1B,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,MAAM,EAAE,CAAC;QAC9C,OAAO;YACL,IAAI,EAAE,IAAI,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,IAAI;YAClC,KAAK,EAAE,KAAK,CAAC,KAAK;YAClB,QAAQ,EAAE,KAAK,CAAC,QAAQ;YACxB,YAAY,EAAE,KAAK,CAAC,YAAY;YAChC,aAAa,EAAE,KAAK,CAAC,aAAa;YAClC,aAAa,EAAE,KAAK,CAAC,aAAa;YAClC,cAAc,EAAE,IAAI,CAAC,cAAc;YACnC,SAAS,EAAE,KAAK,CAAC,SAAS,CAAC,OAAO,EAAE;YACpC,SAAS,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,IAAI;SAC9D,CAAC;IACJ,CAAC,CAAC;IAEF,MAAM,IAAI,GAAG,KAAK,EAAE,GAAqB,EAAE,IAA4B,EAAE,EAAE;QACzE,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,MAAM,IAAI,EAAE,CAAC;YAC7B,GAAG,CAAC,SAAS,CAAC,eAAe,EAAE,UAAU,CAAC,CAAC;YAC3C,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACpB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,GAAG,YAAY,4BAA4B,EAAE,CAAC;gBAChD,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;gBAC7C,OAAO;YACT,CAAC;YACD,OAAO,CAAC,KAAK,CAAC,uBAAuB,EAAE,GAAG,CAAC,CAAC;YAC5C,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,gBAAgB,EAAE,CAAC,CAAC;QACpD,CAAC;IACH,CAAC,CAAC;IAEF,MAAM,CAAC,GAAG,CAAC,sBAAsB,EAAE,YAAY,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC,CAAC;IAErF,MAAM,CAAC,IAAI,CAAC,6BAA6B,EAAE,YAAY,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE,CACrE,IAAI,CAAC,GAAG,EAAE,KAAK,IAAI,EAAE;QACnB,MAAM,IAAI,CAAC,WAAW,CAAC,MAAM,EAAE,CAAC;QAChC,OAAO,QAAQ,EAAE,CAAC;IACpB,CAAC,CAAC,CACH,CAAC;IAEF,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type { Database } from '../../database/connection.js';
|
|
2
|
+
/**
|
|
3
|
+
* A one-time code the consent page issued, waiting for Anthropic's backend
|
|
4
|
+
* to exchange it. Looked up by the code's hash; the plaintext travels once,
|
|
5
|
+
* in the redirect.
|
|
6
|
+
*/
|
|
7
|
+
export interface PendingFacadeCode {
|
|
8
|
+
codeHash: string;
|
|
9
|
+
userId: string;
|
|
10
|
+
clientId: string;
|
|
11
|
+
redirectUri: string;
|
|
12
|
+
expiresAt: Date;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Where pending codes live. The database in production, so the consent
|
|
16
|
+
* finish on one replica and the exchange on another see the same row.
|
|
17
|
+
*
|
|
18
|
+
* The rule "one live code per person" is the TABLE'S: the person is its
|
|
19
|
+
* primary key, so issuing is an upsert — the newest code overwrites the last
|
|
20
|
+
* in one statement, two finishes racing each other cannot both leave a row
|
|
21
|
+
* behind, and the table holds at most one row per user, which goes when the
|
|
22
|
+
* user does. That is the whole bound; nothing here ever sweeps, and no
|
|
23
|
+
* rotation of the client id can strand a row, because the client id is data
|
|
24
|
+
* the exchange checks rather than part of the key. Spending is one
|
|
25
|
+
* conditional update: the first exchange wins, on whichever replica, and
|
|
26
|
+
* every later attempt finds the code spent. Nothing in memory takes part in
|
|
27
|
+
* either guarantee.
|
|
28
|
+
*/
|
|
29
|
+
export interface GitHubFacadeCodeStore {
|
|
30
|
+
/** Store a fresh code, replacing whatever the same person had. */
|
|
31
|
+
put(code: PendingFacadeCode): Promise<void>;
|
|
32
|
+
/** The live (unspent, unexpired) code with this hash, without spending it. */
|
|
33
|
+
peek(codeHash: string): Promise<PendingFacadeCode | null>;
|
|
34
|
+
/** Spend the code if it is live; null when it was not (unknown, spent, expired). */
|
|
35
|
+
consume(codeHash: string): Promise<PendingFacadeCode | null>;
|
|
36
|
+
}
|
|
37
|
+
export declare class DbGitHubFacadeCodeStore implements GitHubFacadeCodeStore {
|
|
38
|
+
private readonly db;
|
|
39
|
+
constructor(db: Database);
|
|
40
|
+
put(code: PendingFacadeCode): Promise<void>;
|
|
41
|
+
peek(codeHash: string): Promise<PendingFacadeCode | null>;
|
|
42
|
+
consume(codeHash: string): Promise<PendingFacadeCode | null>;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* For tests: the same rules over a map keyed the way the table is. Share one
|
|
46
|
+
* instance to model shared storage. Check-and-mark is synchronous, as the
|
|
47
|
+
* database's conditional update is atomic: two exchanges of one code cannot
|
|
48
|
+
* both win here either.
|
|
49
|
+
*/
|
|
50
|
+
export declare class MemoryGitHubFacadeCodeStore implements GitHubFacadeCodeStore {
|
|
51
|
+
private readonly rows;
|
|
52
|
+
put(code: PendingFacadeCode): Promise<void>;
|
|
53
|
+
peek(codeHash: string): Promise<PendingFacadeCode | null>;
|
|
54
|
+
consume(codeHash: string): Promise<PendingFacadeCode | null>;
|
|
55
|
+
/** How many rows the store holds — at most one per person. */
|
|
56
|
+
get size(): number;
|
|
57
|
+
private findLive;
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=github-facade-codes.store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"github-facade-codes.store.d.ts","sourceRoot":"","sources":["../../../../src/modules/marketplace/github-facade/github-facade-codes.store.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,8BAA8B,CAAC;AAG7D;;;;GAIG;AACH,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,IAAI,CAAC;CACjB;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,WAAW,qBAAqB;IACpC,kEAAkE;IAClE,GAAG,CAAC,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5C,8EAA8E;IAC9E,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC,CAAC;IAC1D,oFAAoF;IACpF,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC,CAAC;CAC9D;AAED,qBAAa,uBAAwB,YAAW,qBAAqB;IACvD,OAAO,CAAC,QAAQ,CAAC,EAAE;gBAAF,EAAE,EAAE,QAAQ;IAEnC,GAAG,CAAC,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;IAe3C,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC;IASzD,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC;CAQnE;AAqBD;;;;;GAKG;AACH,qBAAa,2BAA4B,YAAW,qBAAqB;IACvE,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAsE;IAErF,GAAG,CAAC,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;IAI3C,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC;IAKzD,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC;IAOlE,8DAA8D;IAC9D,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,OAAO,CAAC,QAAQ;CAOjB"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { and, eq, gt, isNull } from 'drizzle-orm';
|
|
2
|
+
import { githubFacadeCodes } from '../../database/schema.js';
|
|
3
|
+
export class DbGitHubFacadeCodeStore {
|
|
4
|
+
db;
|
|
5
|
+
constructor(db) {
|
|
6
|
+
this.db = db;
|
|
7
|
+
}
|
|
8
|
+
async put(code) {
|
|
9
|
+
const fresh = {
|
|
10
|
+
clientId: code.clientId,
|
|
11
|
+
codeHash: code.codeHash,
|
|
12
|
+
redirectUri: code.redirectUri,
|
|
13
|
+
expiresAt: code.expiresAt,
|
|
14
|
+
consumedAt: null,
|
|
15
|
+
createdAt: new Date(),
|
|
16
|
+
};
|
|
17
|
+
await this.db
|
|
18
|
+
.insert(githubFacadeCodes)
|
|
19
|
+
.values({ userId: code.userId, ...fresh })
|
|
20
|
+
.onConflictDoUpdate({ target: githubFacadeCodes.userId, set: fresh });
|
|
21
|
+
}
|
|
22
|
+
async peek(codeHash) {
|
|
23
|
+
const [row] = await this.db
|
|
24
|
+
.select()
|
|
25
|
+
.from(githubFacadeCodes)
|
|
26
|
+
.where(live(codeHash))
|
|
27
|
+
.limit(1);
|
|
28
|
+
return row ? toPending(row) : null;
|
|
29
|
+
}
|
|
30
|
+
async consume(codeHash) {
|
|
31
|
+
const [row] = await this.db
|
|
32
|
+
.update(githubFacadeCodes)
|
|
33
|
+
.set({ consumedAt: new Date() })
|
|
34
|
+
.where(live(codeHash))
|
|
35
|
+
.returning();
|
|
36
|
+
return row ? toPending(row) : null;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
/** The one definition of "live": this hash, unspent, unexpired. */
|
|
40
|
+
function live(codeHash) {
|
|
41
|
+
return and(eq(githubFacadeCodes.codeHash, codeHash), isNull(githubFacadeCodes.consumedAt), gt(githubFacadeCodes.expiresAt, new Date()));
|
|
42
|
+
}
|
|
43
|
+
function toPending(row) {
|
|
44
|
+
return {
|
|
45
|
+
codeHash: row.codeHash,
|
|
46
|
+
userId: row.userId,
|
|
47
|
+
clientId: row.clientId,
|
|
48
|
+
redirectUri: row.redirectUri,
|
|
49
|
+
expiresAt: row.expiresAt,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* For tests: the same rules over a map keyed the way the table is. Share one
|
|
54
|
+
* instance to model shared storage. Check-and-mark is synchronous, as the
|
|
55
|
+
* database's conditional update is atomic: two exchanges of one code cannot
|
|
56
|
+
* both win here either.
|
|
57
|
+
*/
|
|
58
|
+
export class MemoryGitHubFacadeCodeStore {
|
|
59
|
+
rows = new Map();
|
|
60
|
+
async put(code) {
|
|
61
|
+
this.rows.set(code.userId, { ...code, consumedAt: null });
|
|
62
|
+
}
|
|
63
|
+
async peek(codeHash) {
|
|
64
|
+
const row = this.findLive(codeHash);
|
|
65
|
+
return row ? strip(row) : null;
|
|
66
|
+
}
|
|
67
|
+
async consume(codeHash) {
|
|
68
|
+
const row = this.findLive(codeHash);
|
|
69
|
+
if (!row)
|
|
70
|
+
return null;
|
|
71
|
+
row.consumedAt = new Date();
|
|
72
|
+
return strip(row);
|
|
73
|
+
}
|
|
74
|
+
/** How many rows the store holds — at most one per person. */
|
|
75
|
+
get size() {
|
|
76
|
+
return this.rows.size;
|
|
77
|
+
}
|
|
78
|
+
findLive(codeHash) {
|
|
79
|
+
const now = Date.now();
|
|
80
|
+
for (const row of this.rows.values()) {
|
|
81
|
+
if (row.codeHash === codeHash && row.consumedAt === null && row.expiresAt.getTime() > now)
|
|
82
|
+
return row;
|
|
83
|
+
}
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
function strip(row) {
|
|
88
|
+
return {
|
|
89
|
+
codeHash: row.codeHash,
|
|
90
|
+
userId: row.userId,
|
|
91
|
+
clientId: row.clientId,
|
|
92
|
+
redirectUri: row.redirectUri,
|
|
93
|
+
expiresAt: row.expiresAt,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=github-facade-codes.store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"github-facade-codes.store.js","sourceRoot":"","sources":["../../../../src/modules/marketplace/github-facade/github-facade-codes.store.ts"],"names":[],"mappings":"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"}
|