apexrest 0.4.0-beta.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/LICENSE +202 -0
- package/NOTICE +18 -0
- package/README.md +201 -0
- package/README.uk.md +201 -0
- package/dist/codex-compat/.agents/plugins/marketplace.json +23 -0
- package/dist/codex-compat/plugins/apexrest-apex/.codex-plugin/plugin.json +32 -0
- package/dist/codex-compat/plugins/apexrest-apex/.mcp.json +11 -0
- package/dist/codex-compat/plugins/apexrest-apex/LICENSE +202 -0
- package/dist/codex-compat/plugins/apexrest-apex/NOTICE +18 -0
- package/dist/codex-compat/plugins/apexrest-apex/assets/apexrest-icon.svg +27 -0
- package/dist/codex-compat/plugins/apexrest-apex/assets/apexrest-logo.svg +27 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/ords/OrdsBridge.java +323 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/panel/assets/LICENCE.txt +118 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/panel/assets/bulbasaur.png +0 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/panel/assets/charmander.png +0 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/panel/assets/mewtwo.png +0 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/panel/assets/pikachu.png +0 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/panel/assets/sources.json +36 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/panel/assets/squirtle.png +0 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/panel/index.html +68 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/panel/panel.css +805 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/panel/panel.js +951 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/playwright/auth.mjs +17 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/playwright/package-lock.json +57 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/playwright/package.json +9 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/references/ORACLE-LICENSE.txt +35 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/references/index.json +54432 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/references/oracle-snapshot.json +648 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/references/search.json +1 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/schemas/deploy-plan.schema.json +210 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/schemas/operations.schema.json +1721 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/schemas/policy.schema.json +63 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/schemas/project.schema.json +250 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/schemas/toolchain-lock.schema.json +118 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/schemas/vendor/mcp.schema.json +120 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/schemas/vendor/plugin.schema.json +65 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/templates/blank-app/README.md +5 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/templates/blank-app/README.uk.md +5 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/templates/blank-app/application/.apex/apexlang.json +3 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/templates/blank-app/application/application.apx +51 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/templates/blank-app/application/deployments/default.json +2 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/templates/blank-app/application/page-groups.apx +4 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/templates/blank-app/application/pages/p00000-global-page.apx +4 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/templates/blank-app/application/pages/p00001-home.apx +35 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/templates/blank-app/application/pages/p09999-login.apx +254 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/templates/blank-app/application/shared-components/authentications.apx +5 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/templates/blank-app/application/shared-components/authorizations.apx +11 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/templates/blank-app/application/shared-components/breadcrumbs.apx +18 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/templates/blank-app/application/shared-components/build-options.apx +4 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/templates/blank-app/application/shared-components/component-settings.apx +92 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/templates/blank-app/application/shared-components/lists.apx +81 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/templates/blank-app/application/shared-components/lovs.apx +20 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/templates/blank-app/application/shared-components/static-files/icons/app-icon-144-rounded.png +0 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/templates/blank-app/application/shared-components/static-files/icons/app-icon-192.png +0 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/templates/blank-app/application/shared-components/static-files/icons/app-icon-256-rounded.png +0 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/templates/blank-app/application/shared-components/static-files/icons/app-icon-32.png +0 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/templates/blank-app/application/shared-components/static-files/icons/app-icon-512.png +0 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/templates/blank-app/application/shared-components/static-files.apx +25 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/templates/blank-app/application/shared-components/themes/universal-theme/theme.apx +57 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/templates/blank-app/application/supporting-objects/deinstall-script.sql +0 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/templates/blank-app/application/supporting-objects/supporting-objects.apx +9 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/templates/customer-crm/README.md +7 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/templates/customer-crm/README.uk.md +7 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/templates/customer-crm/apex-overlay/pages/p00010-customers.apx +164 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/templates/customer-crm/apex-overlay/pages/p00020-dashboard.apx +54 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/templates/customer-crm/apex-overlay/pages/p00080-customer.apx +332 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/templates/customer-crm/project/src/database/migrations/0001__customers.sql +10 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/templates/customer-crm/project/src/database/packages/0010__customer_api.sql +28 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/templates/customer-crm/project/src/database/packages/0020__customer_validation_trigger.sql +10 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/templates/customer-crm/project/tests/e2e/customer-crud.spec.mjs +25 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/templates/customer-crm/project/tests/sql/test_customers.sql +49 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/templates/database-control/install.sql +17 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/templates/existing-app/README.md +9 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/templates/existing-app/README.uk.md +9 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/testkit/apex.mjs +58 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__@hono__node-server__LICENSE +21 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__@isaacs__fs-minipass__LICENSE +15 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__@modelcontextprotocol__sdk__LICENSE +21 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__@playwright__test__LICENSE +202 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__@playwright__test__NOTICE +5 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__@types__node__LICENSE +21 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__@types__yauzl__LICENSE +21 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__@typescript__typescript-darwin-arm64__LICENSE +55 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__@typescript__typescript-darwin-arm64__NOTICE.txt +436 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__accepts__LICENSE +23 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__ajv-formats__LICENSE +21 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__ajv__LICENSE +22 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__body-parser__LICENSE +23 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__body-parser__node_modules__content-type__LICENSE +22 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__bytes__LICENSE +23 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__call-bind-apply-helpers__LICENSE +21 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__call-bound__LICENSE +21 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__chownr__LICENSE.md +63 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__content-disposition__LICENSE +22 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__content-type__LICENSE +22 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__cookie-signature__LICENSE +22 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__cookie__LICENSE +24 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__cors__LICENSE +22 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__cross-spawn__LICENSE +21 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__debug__LICENSE +20 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__depd__LICENSE +22 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__dunder-proto__LICENSE +21 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__ee-first__LICENSE +22 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__encodeurl__LICENSE +22 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__es-define-property__LICENSE +21 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__es-errors__LICENSE +21 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__es-object-atoms__LICENSE +21 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__esbuild__LICENSE.md +21 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__escape-html__LICENSE +24 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__etag__LICENSE +22 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__eventsource-parser__LICENSE +21 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__eventsource__LICENSE +22 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__express-rate-limit__license +21 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__express__LICENSE +24 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__fast-deep-equal__LICENSE +21 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__fast-uri__LICENSE +30 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__fflate__LICENSE +21 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__finalhandler__LICENSE +22 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__forwarded__LICENSE +22 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__fresh__LICENSE +23 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__function-bind__LICENSE +20 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__get-intrinsic__LICENSE +21 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__get-proto__LICENSE +21 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__gopd__LICENSE +21 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__has-symbols__LICENSE +21 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__hasown__LICENSE +21 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__hono__LICENSE +21 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__http-errors__LICENSE +23 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__iconv-lite__LICENSE +21 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__inherits__LICENSE +16 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__ip-address__LICENSE +19 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__ipaddr.js__LICENSE +19 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__is-promise__LICENSE +19 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__isexe__LICENSE +15 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__jose__LICENSE.md +21 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__json-schema-traverse__LICENSE +21 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__json-schema-typed__LICENSE.md +57 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__marked__LICENSE +44 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__math-intrinsics__LICENSE +21 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__media-typer__LICENSE +22 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__merge-descriptors__license +11 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__mime-db__LICENSE +23 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__mime-types__LICENSE +23 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__minipass__LICENSE.md +55 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__minizlib__LICENSE +26 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__ms__license.md +21 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__negotiator__LICENSE +24 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__negotiator__node_modules__content-type__LICENSE +22 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__object-assign__license +21 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__object-inspect__LICENSE +21 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__on-finished__LICENSE +23 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__once__LICENSE +15 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__parseurl__LICENSE +24 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__path-key__license +9 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__path-to-regexp__LICENSE +21 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__pend__LICENSE +23 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__pkce-challenge__LICENSE +21 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__playwright-core__LICENSE +202 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__playwright-core__NOTICE +5 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__playwright__LICENSE +202 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__playwright__NOTICE +5 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__prettier__LICENSE +7 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__proxy-addr__LICENSE +22 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__qs__LICENSE.md +29 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__range-parser__LICENSE +23 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__raw-body__LICENSE +22 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__require-from-string__license +21 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__router__LICENSE +23 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__safer-buffer__LICENSE +21 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__send__LICENSE +23 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__serve-static__LICENSE +25 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__setprototypeof__LICENSE +13 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__shebang-command__license +9 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__shebang-regex__license +9 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__side-channel-list__LICENSE +21 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__side-channel-map__LICENSE +21 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__side-channel-weakmap__LICENSE +21 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__side-channel__LICENSE +21 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__statuses__LICENSE +23 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__tar__LICENSE.md +55 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__toidentifier__LICENSE +21 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__type-is__LICENSE +23 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__type-is__node_modules__content-type__LICENSE +22 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__typescript__LICENSE +55 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__typescript__NOTICE.txt +436 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__undici-types__LICENSE +21 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__unpipe__LICENSE +22 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__vary__LICENSE +22 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__which__LICENSE +15 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__wrappy__LICENSE +15 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__yallist__LICENSE.md +63 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__yauzl__LICENSE +21 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__zod-to-json-schema__LICENSE +15 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/third-party/node_modules__zod__LICENSE +21 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/toolchains/compatibility.json +50 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/toolchains/sources.lock.json +14 -0
- package/dist/codex-compat/plugins/apexrest-apex/resources/toolchains/toolchain.lock.json +204 -0
- package/dist/codex-compat/plugins/apexrest-apex/runtime/apexrest.mjs +226 -0
- package/dist/codex-compat/plugins/apexrest-apex/runtime/chunk-2SZCZZ3J.mjs +37111 -0
- package/dist/codex-compat/plugins/apexrest-apex/runtime/chunk-2VGE5Y5J.mjs +994 -0
- package/dist/codex-compat/plugins/apexrest-apex/runtime/chunk-3GSS6SJH.mjs +1424 -0
- package/dist/codex-compat/plugins/apexrest-apex/runtime/chunk-5GOWABKB.mjs +409 -0
- package/dist/codex-compat/plugins/apexrest-apex/runtime/chunk-ARMO5ZAQ.mjs +25 -0
- package/dist/codex-compat/plugins/apexrest-apex/runtime/chunk-ED2YPDPS.mjs +1123 -0
- package/dist/codex-compat/plugins/apexrest-apex/runtime/chunk-FPOEB42H.mjs +100 -0
- package/dist/codex-compat/plugins/apexrest-apex/runtime/chunk-GFRSRK3K.mjs +16 -0
- package/dist/codex-compat/plugins/apexrest-apex/runtime/chunk-ICWYSHJ7.mjs +19 -0
- package/dist/codex-compat/plugins/apexrest-apex/runtime/chunk-IPU64TJI.mjs +291 -0
- package/dist/codex-compat/plugins/apexrest-apex/runtime/chunk-JWX4UQAZ.mjs +14 -0
- package/dist/codex-compat/plugins/apexrest-apex/runtime/chunk-K3KT6RZY.mjs +1064 -0
- package/dist/codex-compat/plugins/apexrest-apex/runtime/chunk-QLRGI23I.mjs +1307 -0
- package/dist/codex-compat/plugins/apexrest-apex/runtime/chunk-RF4XQO6I.mjs +13 -0
- package/dist/codex-compat/plugins/apexrest-apex/runtime/chunk-VPCUV5QA.mjs +237 -0
- package/dist/codex-compat/plugins/apexrest-apex/runtime/chunk-WMJTK7ZB.mjs +201 -0
- package/dist/codex-compat/plugins/apexrest-apex/runtime/chunk-WROK6RQH.mjs +200 -0
- package/dist/codex-compat/plugins/apexrest-apex/runtime/chunk-XDJEGHUX.mjs +1179 -0
- package/dist/codex-compat/plugins/apexrest-apex/runtime/chunk-YK34QNHH.mjs +15 -0
- package/dist/codex-compat/plugins/apexrest-apex/runtime/chunk-Z6Y72JVG.mjs +37 -0
- package/dist/codex-compat/plugins/apexrest-apex/runtime/chunk-ZQFJ2BNZ.mjs +329 -0
- package/dist/codex-compat/plugins/apexrest-apex/runtime/mcp.mjs +15 -0
- package/dist/codex-compat/plugins/apexrest-apex/skills/apexrest-apexlang/SKILL.md +32 -0
- package/dist/codex-compat/plugins/apexrest-apex/skills/apexrest-apexlang/agents/openai.yaml +4 -0
- package/dist/codex-compat/plugins/apexrest-apex/skills/apexrest-apexlang/assets/p00020-filtered-components.apx +152 -0
- package/dist/codex-compat/plugins/apexrest-apex/skills/apexrest-apexlang/references/component-routes.md +35 -0
- package/dist/codex-compat/plugins/apexrest-apex/skills/apexrest-apexlang/references/native-dashboard.md +11 -0
- package/dist/codex-compat/plugins/apexrest-apex/skills/apexrest-database/SKILL.md +11 -0
- package/dist/codex-compat/plugins/apexrest-apex/skills/apexrest-database/agents/openai.yaml +4 -0
- package/dist/codex-compat/plugins/apexrest-apex/skills/apexrest-debug/SKILL.md +11 -0
- package/dist/codex-compat/plugins/apexrest-apex/skills/apexrest-debug/agents/openai.yaml +4 -0
- package/dist/codex-compat/plugins/apexrest-apex/skills/apexrest-deploy/SKILL.md +19 -0
- package/dist/codex-compat/plugins/apexrest-apex/skills/apexrest-deploy/agents/openai.yaml +4 -0
- package/dist/codex-compat/plugins/apexrest-apex/skills/apexrest-install-dependencies/SKILL.md +38 -0
- package/dist/codex-compat/plugins/apexrest-apex/skills/apexrest-install-dependencies/agents/openai.yaml +4 -0
- package/dist/codex-compat/plugins/apexrest-apex/skills/apexrest-menu/SKILL.md +27 -0
- package/dist/codex-compat/plugins/apexrest-apex/skills/apexrest-menu/agents/openai.yaml +4 -0
- package/dist/codex-compat/plugins/apexrest-apex/skills/apexrest-menu/references/cli-catalog.md +22 -0
- package/dist/codex-compat/plugins/apexrest-apex/skills/apexrest-panel/SKILL.md +20 -0
- package/dist/codex-compat/plugins/apexrest-apex/skills/apexrest-panel/agents/openai.yaml +4 -0
- package/dist/codex-compat/plugins/apexrest-apex/skills/apexrest-project/SKILL.md +9 -0
- package/dist/codex-compat/plugins/apexrest-apex/skills/apexrest-project/agents/openai.yaml +4 -0
- package/dist/codex-compat/plugins/apexrest-apex/skills/apexrest-review/SKILL.md +9 -0
- package/dist/codex-compat/plugins/apexrest-apex/skills/apexrest-review/agents/openai.yaml +4 -0
- package/dist/codex-compat/plugins/apexrest-apex/skills/apexrest-setup/SKILL.md +15 -0
- package/dist/codex-compat/plugins/apexrest-apex/skills/apexrest-setup/agents/openai.yaml +4 -0
- package/dist/codex-compat/plugins/apexrest-apex/skills/apexrest-setup/references/ords.md +3 -0
- package/dist/codex-compat/plugins/apexrest-apex/skills/apexrest-team/SKILL.md +24 -0
- package/dist/codex-compat/plugins/apexrest-apex/skills/apexrest-team/agents/openai.yaml +4 -0
- package/dist/codex-compat/plugins/apexrest-apex/skills/apexrest-test/SKILL.md +27 -0
- package/dist/codex-compat/plugins/apexrest-apex/skills/apexrest-test/agents/openai.yaml +4 -0
- package/dist/codex-compat/plugins/apexrest-apex/skills/apexrest-work/SKILL.md +17 -0
- package/dist/codex-compat/plugins/apexrest-apex/skills/apexrest-work/agents/openai.yaml +7 -0
- package/dist/resources/ords/OrdsBridge.java +323 -0
- package/dist/resources/panel/assets/LICENCE.txt +118 -0
- package/dist/resources/panel/assets/bulbasaur.png +0 -0
- package/dist/resources/panel/assets/charmander.png +0 -0
- package/dist/resources/panel/assets/mewtwo.png +0 -0
- package/dist/resources/panel/assets/pikachu.png +0 -0
- package/dist/resources/panel/assets/sources.json +36 -0
- package/dist/resources/panel/assets/squirtle.png +0 -0
- package/dist/resources/panel/index.html +68 -0
- package/dist/resources/panel/panel.css +805 -0
- package/dist/resources/panel/panel.js +951 -0
- package/dist/resources/playwright/auth.mjs +17 -0
- package/dist/resources/playwright/package-lock.json +57 -0
- package/dist/resources/playwright/package.json +9 -0
- package/dist/resources/references/ORACLE-LICENSE.txt +35 -0
- package/dist/resources/references/index.json +54432 -0
- package/dist/resources/references/oracle-snapshot.json +648 -0
- package/dist/resources/references/search.json +1 -0
- package/dist/resources/schemas/deploy-plan.schema.json +210 -0
- package/dist/resources/schemas/operations.schema.json +1721 -0
- package/dist/resources/schemas/policy.schema.json +63 -0
- package/dist/resources/schemas/project.schema.json +250 -0
- package/dist/resources/schemas/toolchain-lock.schema.json +118 -0
- package/dist/resources/schemas/vendor/mcp.schema.json +120 -0
- package/dist/resources/schemas/vendor/plugin.schema.json +65 -0
- package/dist/resources/templates/blank-app/README.md +5 -0
- package/dist/resources/templates/blank-app/README.uk.md +5 -0
- package/dist/resources/templates/blank-app/application/.apex/apexlang.json +3 -0
- package/dist/resources/templates/blank-app/application/application.apx +51 -0
- package/dist/resources/templates/blank-app/application/deployments/default.json +2 -0
- package/dist/resources/templates/blank-app/application/page-groups.apx +4 -0
- package/dist/resources/templates/blank-app/application/pages/p00000-global-page.apx +4 -0
- package/dist/resources/templates/blank-app/application/pages/p00001-home.apx +35 -0
- package/dist/resources/templates/blank-app/application/pages/p09999-login.apx +254 -0
- package/dist/resources/templates/blank-app/application/shared-components/authentications.apx +5 -0
- package/dist/resources/templates/blank-app/application/shared-components/authorizations.apx +11 -0
- package/dist/resources/templates/blank-app/application/shared-components/breadcrumbs.apx +18 -0
- package/dist/resources/templates/blank-app/application/shared-components/build-options.apx +4 -0
- package/dist/resources/templates/blank-app/application/shared-components/component-settings.apx +92 -0
- package/dist/resources/templates/blank-app/application/shared-components/lists.apx +81 -0
- package/dist/resources/templates/blank-app/application/shared-components/lovs.apx +20 -0
- package/dist/resources/templates/blank-app/application/shared-components/static-files/icons/app-icon-144-rounded.png +0 -0
- package/dist/resources/templates/blank-app/application/shared-components/static-files/icons/app-icon-192.png +0 -0
- package/dist/resources/templates/blank-app/application/shared-components/static-files/icons/app-icon-256-rounded.png +0 -0
- package/dist/resources/templates/blank-app/application/shared-components/static-files/icons/app-icon-32.png +0 -0
- package/dist/resources/templates/blank-app/application/shared-components/static-files/icons/app-icon-512.png +0 -0
- package/dist/resources/templates/blank-app/application/shared-components/static-files.apx +25 -0
- package/dist/resources/templates/blank-app/application/shared-components/themes/universal-theme/theme.apx +57 -0
- package/dist/resources/templates/blank-app/application/supporting-objects/deinstall-script.sql +0 -0
- package/dist/resources/templates/blank-app/application/supporting-objects/supporting-objects.apx +9 -0
- package/dist/resources/templates/customer-crm/README.md +7 -0
- package/dist/resources/templates/customer-crm/README.uk.md +7 -0
- package/dist/resources/templates/customer-crm/apex-overlay/pages/p00010-customers.apx +164 -0
- package/dist/resources/templates/customer-crm/apex-overlay/pages/p00020-dashboard.apx +54 -0
- package/dist/resources/templates/customer-crm/apex-overlay/pages/p00080-customer.apx +332 -0
- package/dist/resources/templates/customer-crm/project/src/database/migrations/0001__customers.sql +10 -0
- package/dist/resources/templates/customer-crm/project/src/database/packages/0010__customer_api.sql +28 -0
- package/dist/resources/templates/customer-crm/project/src/database/packages/0020__customer_validation_trigger.sql +10 -0
- package/dist/resources/templates/customer-crm/project/tests/e2e/customer-crud.spec.mjs +25 -0
- package/dist/resources/templates/customer-crm/project/tests/sql/test_customers.sql +49 -0
- package/dist/resources/templates/database-control/install.sql +17 -0
- package/dist/resources/templates/existing-app/README.md +9 -0
- package/dist/resources/templates/existing-app/README.uk.md +9 -0
- package/dist/resources/testkit/apex.mjs +58 -0
- package/dist/resources/third-party/node_modules__@hono__node-server__LICENSE +21 -0
- package/dist/resources/third-party/node_modules__@isaacs__fs-minipass__LICENSE +15 -0
- package/dist/resources/third-party/node_modules__@modelcontextprotocol__sdk__LICENSE +21 -0
- package/dist/resources/third-party/node_modules__@playwright__test__LICENSE +202 -0
- package/dist/resources/third-party/node_modules__@playwright__test__NOTICE +5 -0
- package/dist/resources/third-party/node_modules__@types__node__LICENSE +21 -0
- package/dist/resources/third-party/node_modules__@types__yauzl__LICENSE +21 -0
- package/dist/resources/third-party/node_modules__@typescript__typescript-darwin-arm64__LICENSE +55 -0
- package/dist/resources/third-party/node_modules__@typescript__typescript-darwin-arm64__NOTICE.txt +436 -0
- package/dist/resources/third-party/node_modules__accepts__LICENSE +23 -0
- package/dist/resources/third-party/node_modules__ajv-formats__LICENSE +21 -0
- package/dist/resources/third-party/node_modules__ajv__LICENSE +22 -0
- package/dist/resources/third-party/node_modules__body-parser__LICENSE +23 -0
- package/dist/resources/third-party/node_modules__body-parser__node_modules__content-type__LICENSE +22 -0
- package/dist/resources/third-party/node_modules__bytes__LICENSE +23 -0
- package/dist/resources/third-party/node_modules__call-bind-apply-helpers__LICENSE +21 -0
- package/dist/resources/third-party/node_modules__call-bound__LICENSE +21 -0
- package/dist/resources/third-party/node_modules__chownr__LICENSE.md +63 -0
- package/dist/resources/third-party/node_modules__content-disposition__LICENSE +22 -0
- package/dist/resources/third-party/node_modules__content-type__LICENSE +22 -0
- package/dist/resources/third-party/node_modules__cookie-signature__LICENSE +22 -0
- package/dist/resources/third-party/node_modules__cookie__LICENSE +24 -0
- package/dist/resources/third-party/node_modules__cors__LICENSE +22 -0
- package/dist/resources/third-party/node_modules__cross-spawn__LICENSE +21 -0
- package/dist/resources/third-party/node_modules__debug__LICENSE +20 -0
- package/dist/resources/third-party/node_modules__depd__LICENSE +22 -0
- package/dist/resources/third-party/node_modules__dunder-proto__LICENSE +21 -0
- package/dist/resources/third-party/node_modules__ee-first__LICENSE +22 -0
- package/dist/resources/third-party/node_modules__encodeurl__LICENSE +22 -0
- package/dist/resources/third-party/node_modules__es-define-property__LICENSE +21 -0
- package/dist/resources/third-party/node_modules__es-errors__LICENSE +21 -0
- package/dist/resources/third-party/node_modules__es-object-atoms__LICENSE +21 -0
- package/dist/resources/third-party/node_modules__esbuild__LICENSE.md +21 -0
- package/dist/resources/third-party/node_modules__escape-html__LICENSE +24 -0
- package/dist/resources/third-party/node_modules__etag__LICENSE +22 -0
- package/dist/resources/third-party/node_modules__eventsource-parser__LICENSE +21 -0
- package/dist/resources/third-party/node_modules__eventsource__LICENSE +22 -0
- package/dist/resources/third-party/node_modules__express-rate-limit__license +21 -0
- package/dist/resources/third-party/node_modules__express__LICENSE +24 -0
- package/dist/resources/third-party/node_modules__fast-deep-equal__LICENSE +21 -0
- package/dist/resources/third-party/node_modules__fast-uri__LICENSE +30 -0
- package/dist/resources/third-party/node_modules__fflate__LICENSE +21 -0
- package/dist/resources/third-party/node_modules__finalhandler__LICENSE +22 -0
- package/dist/resources/third-party/node_modules__forwarded__LICENSE +22 -0
- package/dist/resources/third-party/node_modules__fresh__LICENSE +23 -0
- package/dist/resources/third-party/node_modules__function-bind__LICENSE +20 -0
- package/dist/resources/third-party/node_modules__get-intrinsic__LICENSE +21 -0
- package/dist/resources/third-party/node_modules__get-proto__LICENSE +21 -0
- package/dist/resources/third-party/node_modules__gopd__LICENSE +21 -0
- package/dist/resources/third-party/node_modules__has-symbols__LICENSE +21 -0
- package/dist/resources/third-party/node_modules__hasown__LICENSE +21 -0
- package/dist/resources/third-party/node_modules__hono__LICENSE +21 -0
- package/dist/resources/third-party/node_modules__http-errors__LICENSE +23 -0
- package/dist/resources/third-party/node_modules__iconv-lite__LICENSE +21 -0
- package/dist/resources/third-party/node_modules__inherits__LICENSE +16 -0
- package/dist/resources/third-party/node_modules__ip-address__LICENSE +19 -0
- package/dist/resources/third-party/node_modules__ipaddr.js__LICENSE +19 -0
- package/dist/resources/third-party/node_modules__is-promise__LICENSE +19 -0
- package/dist/resources/third-party/node_modules__isexe__LICENSE +15 -0
- package/dist/resources/third-party/node_modules__jose__LICENSE.md +21 -0
- package/dist/resources/third-party/node_modules__json-schema-traverse__LICENSE +21 -0
- package/dist/resources/third-party/node_modules__json-schema-typed__LICENSE.md +57 -0
- package/dist/resources/third-party/node_modules__marked__LICENSE +44 -0
- package/dist/resources/third-party/node_modules__math-intrinsics__LICENSE +21 -0
- package/dist/resources/third-party/node_modules__media-typer__LICENSE +22 -0
- package/dist/resources/third-party/node_modules__merge-descriptors__license +11 -0
- package/dist/resources/third-party/node_modules__mime-db__LICENSE +23 -0
- package/dist/resources/third-party/node_modules__mime-types__LICENSE +23 -0
- package/dist/resources/third-party/node_modules__minipass__LICENSE.md +55 -0
- package/dist/resources/third-party/node_modules__minizlib__LICENSE +26 -0
- package/dist/resources/third-party/node_modules__ms__license.md +21 -0
- package/dist/resources/third-party/node_modules__negotiator__LICENSE +24 -0
- package/dist/resources/third-party/node_modules__negotiator__node_modules__content-type__LICENSE +22 -0
- package/dist/resources/third-party/node_modules__object-assign__license +21 -0
- package/dist/resources/third-party/node_modules__object-inspect__LICENSE +21 -0
- package/dist/resources/third-party/node_modules__on-finished__LICENSE +23 -0
- package/dist/resources/third-party/node_modules__once__LICENSE +15 -0
- package/dist/resources/third-party/node_modules__parseurl__LICENSE +24 -0
- package/dist/resources/third-party/node_modules__path-key__license +9 -0
- package/dist/resources/third-party/node_modules__path-to-regexp__LICENSE +21 -0
- package/dist/resources/third-party/node_modules__pend__LICENSE +23 -0
- package/dist/resources/third-party/node_modules__pkce-challenge__LICENSE +21 -0
- package/dist/resources/third-party/node_modules__playwright-core__LICENSE +202 -0
- package/dist/resources/third-party/node_modules__playwright-core__NOTICE +5 -0
- package/dist/resources/third-party/node_modules__playwright__LICENSE +202 -0
- package/dist/resources/third-party/node_modules__playwright__NOTICE +5 -0
- package/dist/resources/third-party/node_modules__prettier__LICENSE +7 -0
- package/dist/resources/third-party/node_modules__proxy-addr__LICENSE +22 -0
- package/dist/resources/third-party/node_modules__qs__LICENSE.md +29 -0
- package/dist/resources/third-party/node_modules__range-parser__LICENSE +23 -0
- package/dist/resources/third-party/node_modules__raw-body__LICENSE +22 -0
- package/dist/resources/third-party/node_modules__require-from-string__license +21 -0
- package/dist/resources/third-party/node_modules__router__LICENSE +23 -0
- package/dist/resources/third-party/node_modules__safer-buffer__LICENSE +21 -0
- package/dist/resources/third-party/node_modules__send__LICENSE +23 -0
- package/dist/resources/third-party/node_modules__serve-static__LICENSE +25 -0
- package/dist/resources/third-party/node_modules__setprototypeof__LICENSE +13 -0
- package/dist/resources/third-party/node_modules__shebang-command__license +9 -0
- package/dist/resources/third-party/node_modules__shebang-regex__license +9 -0
- package/dist/resources/third-party/node_modules__side-channel-list__LICENSE +21 -0
- package/dist/resources/third-party/node_modules__side-channel-map__LICENSE +21 -0
- package/dist/resources/third-party/node_modules__side-channel-weakmap__LICENSE +21 -0
- package/dist/resources/third-party/node_modules__side-channel__LICENSE +21 -0
- package/dist/resources/third-party/node_modules__statuses__LICENSE +23 -0
- package/dist/resources/third-party/node_modules__tar__LICENSE.md +55 -0
- package/dist/resources/third-party/node_modules__toidentifier__LICENSE +21 -0
- package/dist/resources/third-party/node_modules__type-is__LICENSE +23 -0
- package/dist/resources/third-party/node_modules__type-is__node_modules__content-type__LICENSE +22 -0
- package/dist/resources/third-party/node_modules__typescript__LICENSE +55 -0
- package/dist/resources/third-party/node_modules__typescript__NOTICE.txt +436 -0
- package/dist/resources/third-party/node_modules__undici-types__LICENSE +21 -0
- package/dist/resources/third-party/node_modules__unpipe__LICENSE +22 -0
- package/dist/resources/third-party/node_modules__vary__LICENSE +22 -0
- package/dist/resources/third-party/node_modules__which__LICENSE +15 -0
- package/dist/resources/third-party/node_modules__wrappy__LICENSE +15 -0
- package/dist/resources/third-party/node_modules__yallist__LICENSE.md +63 -0
- package/dist/resources/third-party/node_modules__yauzl__LICENSE +21 -0
- package/dist/resources/third-party/node_modules__zod-to-json-schema__LICENSE +15 -0
- package/dist/resources/third-party/node_modules__zod__LICENSE +21 -0
- package/dist/resources/toolchains/compatibility.json +50 -0
- package/dist/resources/toolchains/sources.lock.json +14 -0
- package/dist/resources/toolchains/toolchain.lock.json +204 -0
- package/dist/runtime/apexrest.mjs +226 -0
- package/dist/runtime/chunk-2SZCZZ3J.mjs +37111 -0
- package/dist/runtime/chunk-2VGE5Y5J.mjs +994 -0
- package/dist/runtime/chunk-3GSS6SJH.mjs +1424 -0
- package/dist/runtime/chunk-5GOWABKB.mjs +409 -0
- package/dist/runtime/chunk-ARMO5ZAQ.mjs +25 -0
- package/dist/runtime/chunk-ED2YPDPS.mjs +1123 -0
- package/dist/runtime/chunk-FPOEB42H.mjs +100 -0
- package/dist/runtime/chunk-GFRSRK3K.mjs +16 -0
- package/dist/runtime/chunk-ICWYSHJ7.mjs +19 -0
- package/dist/runtime/chunk-IPU64TJI.mjs +291 -0
- package/dist/runtime/chunk-JWX4UQAZ.mjs +14 -0
- package/dist/runtime/chunk-K3KT6RZY.mjs +1064 -0
- package/dist/runtime/chunk-QLRGI23I.mjs +1307 -0
- package/dist/runtime/chunk-RF4XQO6I.mjs +13 -0
- package/dist/runtime/chunk-VPCUV5QA.mjs +237 -0
- package/dist/runtime/chunk-WMJTK7ZB.mjs +201 -0
- package/dist/runtime/chunk-WROK6RQH.mjs +200 -0
- package/dist/runtime/chunk-XDJEGHUX.mjs +1179 -0
- package/dist/runtime/chunk-YK34QNHH.mjs +15 -0
- package/dist/runtime/chunk-Z6Y72JVG.mjs +37 -0
- package/dist/runtime/chunk-ZQFJ2BNZ.mjs +329 -0
- package/dist/runtime/mcp.mjs +15 -0
- package/package.json +70 -0
|
@@ -0,0 +1,1307 @@
|
|
|
1
|
+
import { createRequire as __createRequire } from 'node:module'; const require = __createRequire(import.meta.url);
|
|
2
|
+
import {
|
|
3
|
+
OracleAdapter,
|
|
4
|
+
environment,
|
|
5
|
+
external_exports,
|
|
6
|
+
managedHome,
|
|
7
|
+
parse,
|
|
8
|
+
policy,
|
|
9
|
+
refName,
|
|
10
|
+
requireTrust,
|
|
11
|
+
resolveConnection,
|
|
12
|
+
resourceRoot,
|
|
13
|
+
runProcess,
|
|
14
|
+
runtimeState,
|
|
15
|
+
sqlLiteral,
|
|
16
|
+
sqlclToken
|
|
17
|
+
} from "./chunk-2SZCZZ3J.mjs";
|
|
18
|
+
import {
|
|
19
|
+
Fault,
|
|
20
|
+
artifactPage,
|
|
21
|
+
atomicWrite,
|
|
22
|
+
canonical,
|
|
23
|
+
contained,
|
|
24
|
+
exists,
|
|
25
|
+
hash,
|
|
26
|
+
inventory,
|
|
27
|
+
readJson,
|
|
28
|
+
redact,
|
|
29
|
+
sanitized,
|
|
30
|
+
withLock,
|
|
31
|
+
writeJson
|
|
32
|
+
} from "./chunk-IPU64TJI.mjs";
|
|
33
|
+
|
|
34
|
+
// packages/core/src/testing.ts
|
|
35
|
+
import path4 from "node:path";
|
|
36
|
+
import { spawn } from "node:child_process";
|
|
37
|
+
import { mkdir as mkdir3, readFile as readFile3, cp as cp2, chmod } from "node:fs/promises";
|
|
38
|
+
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
39
|
+
|
|
40
|
+
// packages/core/src/artifacts.ts
|
|
41
|
+
import path from "node:path";
|
|
42
|
+
import { randomUUID } from "node:crypto";
|
|
43
|
+
import { readFile, readdir, rm, mkdir } from "node:fs/promises";
|
|
44
|
+
var ArtifactService = class {
|
|
45
|
+
constructor(ctx) {
|
|
46
|
+
this.ctx = ctx;
|
|
47
|
+
}
|
|
48
|
+
ctx;
|
|
49
|
+
async save(content, kind) {
|
|
50
|
+
return this.persist(redact(content), kind, "text");
|
|
51
|
+
}
|
|
52
|
+
async saveJson(value, kind) {
|
|
53
|
+
return this.persist(JSON.stringify(sanitized(value)), kind, "json");
|
|
54
|
+
}
|
|
55
|
+
async resultDirectory(create = false) {
|
|
56
|
+
const home = managedHome();
|
|
57
|
+
if (create) await mkdir(home, { recursive: true, mode: 448 });
|
|
58
|
+
if (!await exists(home)) return null;
|
|
59
|
+
return contained(home, path.join("results", hash(this.ctx.root)));
|
|
60
|
+
}
|
|
61
|
+
async persist(content, kind, format) {
|
|
62
|
+
const id = randomUUID(), directory = format === "json" ? await this.resultDirectory(true) : await contained(this.ctx.root, this.ctx.config.artifacts.directory);
|
|
63
|
+
await atomicWrite(path.join(directory, id + ".txt"), content);
|
|
64
|
+
await writeJson(path.join(directory, id + ".json"), {
|
|
65
|
+
id,
|
|
66
|
+
kind,
|
|
67
|
+
sha256: hash(content),
|
|
68
|
+
format,
|
|
69
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
70
|
+
expiresAt: new Date(Date.now() + this.ctx.config.artifacts.retentionDays * 864e5).toISOString(),
|
|
71
|
+
classification: "private-sanitized-text"
|
|
72
|
+
});
|
|
73
|
+
return id;
|
|
74
|
+
}
|
|
75
|
+
async read(id, offset = 0, limit = 4096) {
|
|
76
|
+
parse(external_exports.uuid(), id);
|
|
77
|
+
parse(external_exports.number().int().min(0).max(1e7), offset);
|
|
78
|
+
parse(external_exports.number().int().min(1).max(16384), limit);
|
|
79
|
+
const results = await this.resultDirectory();
|
|
80
|
+
const directory = results && await exists(path.join(results, id + ".json")) ? results : await contained(this.ctx.root, this.ctx.config.artifacts.directory);
|
|
81
|
+
const metadata = await readJson(await contained(directory, id + ".json"));
|
|
82
|
+
if (Date.parse(metadata.expiresAt) < Date.now())
|
|
83
|
+
throw new Fault("ARTIFACT_EXPIRED", "Artifact retention has expired.", 3);
|
|
84
|
+
if (/auth|wallet|credential/i.test(metadata.kind))
|
|
85
|
+
throw new Fault(
|
|
86
|
+
"PRIVATE_ARTIFACT_DENIED",
|
|
87
|
+
"Authentication and credential artifacts cannot be read through tools.",
|
|
88
|
+
4
|
|
89
|
+
);
|
|
90
|
+
const content = await readFile(await contained(directory, id + ".txt"), "utf8");
|
|
91
|
+
if (hash(content) !== metadata.sha256)
|
|
92
|
+
throw new Fault("ARTIFACT_CHANGED", "Artifact integrity check failed.", 5);
|
|
93
|
+
return artifactPage(content, metadata.format ?? "text", id, offset, limit);
|
|
94
|
+
}
|
|
95
|
+
async prune() {
|
|
96
|
+
const directories = [
|
|
97
|
+
await contained(this.ctx.root, this.ctx.config.artifacts.directory),
|
|
98
|
+
await this.resultDirectory()
|
|
99
|
+
];
|
|
100
|
+
let removed = 0;
|
|
101
|
+
for (const directory of directories) {
|
|
102
|
+
if (!directory || !await exists(directory)) continue;
|
|
103
|
+
for (const file of await readdir(directory))
|
|
104
|
+
if (/^[a-f0-9-]{36}\.json$/.test(file)) {
|
|
105
|
+
const metadata = await readJson(path.join(directory, file));
|
|
106
|
+
if (Date.parse(metadata.expiresAt) < Date.now()) {
|
|
107
|
+
await rm(path.join(directory, file));
|
|
108
|
+
await rm(path.join(directory, file.replace(".json", ".txt")), { force: true });
|
|
109
|
+
removed++;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return { removed };
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
// packages/core/src/deploy.ts
|
|
118
|
+
import path3 from "node:path";
|
|
119
|
+
import { readFile as readFile2, mkdir as mkdir2, cp, open } from "node:fs/promises";
|
|
120
|
+
import { randomUUID as randomUUID2, verify } from "node:crypto";
|
|
121
|
+
|
|
122
|
+
// packages/core/src/deployment-control.ts
|
|
123
|
+
import path2 from "node:path";
|
|
124
|
+
import { hostname } from "node:os";
|
|
125
|
+
import { rm as rm2 } from "node:fs/promises";
|
|
126
|
+
function coordination(env) {
|
|
127
|
+
const backend = env.deploymentControl ?? "local";
|
|
128
|
+
return {
|
|
129
|
+
backend,
|
|
130
|
+
scope: backend === "local" ? "managed-home-schema" : "database-application",
|
|
131
|
+
// Bind plans to the history store. Moving a plan to a fresh home is not migration recovery.
|
|
132
|
+
storeDigest: hash(
|
|
133
|
+
canonical(
|
|
134
|
+
backend === "local" ? { home: managedHome(), ...env.databaseIdentity, schema: env.parsingSchema } : { ...env.databaseIdentity, schema: env.parsingSchema }
|
|
135
|
+
)
|
|
136
|
+
)
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
var ownerSchema = external_exports.strictObject({
|
|
140
|
+
runId: external_exports.string(),
|
|
141
|
+
pid: external_exports.number().int().positive(),
|
|
142
|
+
hostname: external_exports.string(),
|
|
143
|
+
phase: external_exports.enum(["preparing", "writing"]),
|
|
144
|
+
createdAt: external_exports.string()
|
|
145
|
+
});
|
|
146
|
+
var historySchema = external_exports.array(
|
|
147
|
+
external_exports.strictObject({
|
|
148
|
+
version: external_exports.string(),
|
|
149
|
+
checksum: external_exports.string().regex(/^[a-f0-9]{64}$/),
|
|
150
|
+
status: external_exports.enum(["started", "succeeded"]),
|
|
151
|
+
run_id: external_exports.string()
|
|
152
|
+
})
|
|
153
|
+
);
|
|
154
|
+
var LocalDeploymentControl = class {
|
|
155
|
+
directory;
|
|
156
|
+
constructor(env) {
|
|
157
|
+
const key = hash(canonical({ ...env.databaseIdentity, schema: env.parsingSchema }));
|
|
158
|
+
this.directory = path2.join(managedHome(), "deployment-control", key);
|
|
159
|
+
}
|
|
160
|
+
file(name) {
|
|
161
|
+
return path2.join(this.directory, name);
|
|
162
|
+
}
|
|
163
|
+
async history() {
|
|
164
|
+
const file = this.file("history.json");
|
|
165
|
+
return await exists(file) ? parse(historySchema, await readJson(file)) : [];
|
|
166
|
+
}
|
|
167
|
+
async owner() {
|
|
168
|
+
const file = this.file("active.json");
|
|
169
|
+
return await exists(file) ? parse(ownerSchema, await readJson(file)) : void 0;
|
|
170
|
+
}
|
|
171
|
+
async acquire(runId) {
|
|
172
|
+
await withLock(this.file("control.lock"), async () => {
|
|
173
|
+
const owner = await this.owner();
|
|
174
|
+
if (owner) {
|
|
175
|
+
let dead = false;
|
|
176
|
+
if (owner.hostname === hostname()) {
|
|
177
|
+
try {
|
|
178
|
+
process.kill(owner.pid, 0);
|
|
179
|
+
} catch (e) {
|
|
180
|
+
dead = e.code === "ESRCH";
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
if (!dead || owner.phase === "writing")
|
|
184
|
+
throw new Fault(
|
|
185
|
+
"TARGET_LOCKED",
|
|
186
|
+
"A runner owns this schema or an interrupted write needs reconciliation. Preserve its control state.",
|
|
187
|
+
5,
|
|
188
|
+
"conflict"
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
await writeJson(this.file("active.json"), {
|
|
192
|
+
runId,
|
|
193
|
+
pid: process.pid,
|
|
194
|
+
hostname: hostname(),
|
|
195
|
+
phase: "preparing",
|
|
196
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
197
|
+
});
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
async assertOwner(runId) {
|
|
201
|
+
const owner = await this.owner();
|
|
202
|
+
if (owner?.runId !== runId || owner.pid !== process.pid || owner.hostname !== hostname())
|
|
203
|
+
throw new Fault(
|
|
204
|
+
"LEASE_LOST",
|
|
205
|
+
"Local deployment ownership is no longer confirmed.",
|
|
206
|
+
6,
|
|
207
|
+
"outcome_unknown"
|
|
208
|
+
);
|
|
209
|
+
return owner;
|
|
210
|
+
}
|
|
211
|
+
async markWriting(runId) {
|
|
212
|
+
await withLock(this.file("control.lock"), async () => {
|
|
213
|
+
await writeJson(this.file("active.json"), { ...await this.assertOwner(runId), phase: "writing" });
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
async migration(runId, version, checksum, status) {
|
|
217
|
+
await withLock(this.file("control.lock"), async () => {
|
|
218
|
+
await this.assertOwner(runId);
|
|
219
|
+
const history = await this.history(), previous = history.find((row) => row.version === version);
|
|
220
|
+
if (status === "started" ? Boolean(previous) : !previous || previous.run_id !== runId || previous.checksum !== checksum || previous.status !== "started")
|
|
221
|
+
throw new Fault(
|
|
222
|
+
"MIGRATION_HISTORY_CONFLICT",
|
|
223
|
+
"Migration history requires reconciliation; it cannot be replayed or overwritten.",
|
|
224
|
+
5
|
|
225
|
+
);
|
|
226
|
+
const next2 = history.filter((row) => row.version !== version);
|
|
227
|
+
next2.push({ version, checksum, status, run_id: runId });
|
|
228
|
+
await writeJson(
|
|
229
|
+
this.file("history.json"),
|
|
230
|
+
next2.sort((a, b) => a.version.localeCompare(b.version))
|
|
231
|
+
);
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
async release(runId) {
|
|
235
|
+
await withLock(this.file("control.lock"), async () => {
|
|
236
|
+
await this.assertOwner(runId);
|
|
237
|
+
await rm2(this.file("active.json"));
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
// packages/core/src/deploy.ts
|
|
243
|
+
var digestSchema = external_exports.string().regex(/^[a-f0-9]{64}$/);
|
|
244
|
+
var filesSchema = external_exports.record(external_exports.string(), digestSchema);
|
|
245
|
+
var deployPlanSchema = external_exports.strictObject({
|
|
246
|
+
schemaVersion: external_exports.literal(1),
|
|
247
|
+
id: external_exports.uuid(),
|
|
248
|
+
projectId: refName,
|
|
249
|
+
projectRoot: external_exports.string(),
|
|
250
|
+
environment: refName,
|
|
251
|
+
createdAt: external_exports.iso.datetime(),
|
|
252
|
+
expiresAt: external_exports.iso.datetime(),
|
|
253
|
+
sourceDigest: digestSchema,
|
|
254
|
+
sources: filesSchema,
|
|
255
|
+
configurationDigest: digestSchema,
|
|
256
|
+
toolchainDigest: digestSchema,
|
|
257
|
+
compiler: external_exports.string(),
|
|
258
|
+
targetDigest: digestSchema,
|
|
259
|
+
target: external_exports.record(external_exports.string(), external_exports.unknown()),
|
|
260
|
+
fingerprint: digestSchema,
|
|
261
|
+
migrationHistory: external_exports.array(external_exports.record(external_exports.string(), external_exports.unknown())),
|
|
262
|
+
coordination: external_exports.strictObject({
|
|
263
|
+
backend: external_exports.enum(["local", "database"]),
|
|
264
|
+
scope: external_exports.enum(["managed-home-schema", "database-application"]),
|
|
265
|
+
storeDigest: digestSchema
|
|
266
|
+
}),
|
|
267
|
+
scope: external_exports.literal("full-application-import"),
|
|
268
|
+
operations: external_exports.array(
|
|
269
|
+
external_exports.strictObject({
|
|
270
|
+
kind: external_exports.enum(["migration", "package", "import", "verify", "test"]),
|
|
271
|
+
file: external_exports.string().optional(),
|
|
272
|
+
sha256: digestSchema.optional()
|
|
273
|
+
})
|
|
274
|
+
),
|
|
275
|
+
risks: external_exports.array(external_exports.string()),
|
|
276
|
+
approval: external_exports.literal("external-policy-required"),
|
|
277
|
+
backupRequired: external_exports.boolean(),
|
|
278
|
+
digest: digestSchema,
|
|
279
|
+
restore: external_exports.strictObject({ backupId: external_exports.uuid(), checksum: digestSchema }).optional()
|
|
280
|
+
});
|
|
281
|
+
var next = {
|
|
282
|
+
planned: ["approved"],
|
|
283
|
+
approved: ["backing_up"],
|
|
284
|
+
backing_up: ["migrating"],
|
|
285
|
+
migrating: ["importing"],
|
|
286
|
+
importing: ["verifying"],
|
|
287
|
+
verifying: ["testing"],
|
|
288
|
+
testing: ["succeeded"],
|
|
289
|
+
succeeded: [],
|
|
290
|
+
failed: [],
|
|
291
|
+
outcome_unknown: []
|
|
292
|
+
};
|
|
293
|
+
function assertTransition(from, to) {
|
|
294
|
+
if (!next[from].includes(to) && !["failed", "outcome_unknown"].includes(to))
|
|
295
|
+
throw new Fault("INVALID_DEPLOY_STATE", `Invalid transition ${from} to ${to}.`, 5);
|
|
296
|
+
}
|
|
297
|
+
function planDigest(value) {
|
|
298
|
+
const { digest: _digest, ...unsigned } = value;
|
|
299
|
+
return hash(canonical(unsigned));
|
|
300
|
+
}
|
|
301
|
+
function targetDigest(env) {
|
|
302
|
+
return hash(
|
|
303
|
+
canonical({
|
|
304
|
+
...env.databaseIdentity,
|
|
305
|
+
workspace: env.workspace,
|
|
306
|
+
schema: env.parsingSchema,
|
|
307
|
+
applicationId: env.applicationId
|
|
308
|
+
})
|
|
309
|
+
);
|
|
310
|
+
}
|
|
311
|
+
function migrationRisk(sql) {
|
|
312
|
+
const risks = [];
|
|
313
|
+
if (/\b(?:drop|truncate|delete|revoke|grant)\b|\balter\s+(?:table|user|system|database)\b/i.test(sql))
|
|
314
|
+
risks.push("destructive-or-privileged-sql");
|
|
315
|
+
if (/^\s*(?:host|!|connect|conn|start|@|@@|exit|quit|script|javascript)\b/im.test(sql))
|
|
316
|
+
risks.push("sqlcl-script-control");
|
|
317
|
+
return risks;
|
|
318
|
+
}
|
|
319
|
+
async function authorizePlan(ctx, plan, env) {
|
|
320
|
+
await requireTrust(ctx.root);
|
|
321
|
+
if (plan.risks.some((r) => r !== "application-restore"))
|
|
322
|
+
throw new Fault(
|
|
323
|
+
"RECOVERY_REVIEW_REQUIRED",
|
|
324
|
+
"Destructive, authentication or unsupported changes need an explicit recovery implementation and reviewed external workflow.",
|
|
325
|
+
4,
|
|
326
|
+
"blocked"
|
|
327
|
+
);
|
|
328
|
+
if (env.kind === "production") {
|
|
329
|
+
if (process.env.CI !== "true" || !process.env.APEXREST_APPROVAL_PUBLIC_KEY_FILE || !process.env.APEXREST_APPROVAL_FILE)
|
|
330
|
+
throw new Fault(
|
|
331
|
+
"PRODUCTION_CI_REQUIRED",
|
|
332
|
+
"Production requires a protected CI runner and externally signed approval bound to this plan.",
|
|
333
|
+
4,
|
|
334
|
+
"blocked"
|
|
335
|
+
);
|
|
336
|
+
const attestation = parse(
|
|
337
|
+
external_exports.strictObject({
|
|
338
|
+
planDigest: digestSchema,
|
|
339
|
+
targetDigest: digestSchema,
|
|
340
|
+
expiresAt: external_exports.iso.datetime(),
|
|
341
|
+
reviewer: external_exports.string().min(1),
|
|
342
|
+
signature: external_exports.string().min(1)
|
|
343
|
+
}),
|
|
344
|
+
await readJson(process.env.APEXREST_APPROVAL_FILE)
|
|
345
|
+
);
|
|
346
|
+
const { signature, ...payload } = attestation;
|
|
347
|
+
if (payload.planDigest !== plan.digest || payload.targetDigest !== plan.targetDigest || Date.parse(payload.expiresAt) <= Date.now() || !verify(
|
|
348
|
+
null,
|
|
349
|
+
Buffer.from(canonical(payload)),
|
|
350
|
+
await readFile2(process.env.APEXREST_APPROVAL_PUBLIC_KEY_FILE),
|
|
351
|
+
Buffer.from(signature, "base64")
|
|
352
|
+
))
|
|
353
|
+
throw new Fault(
|
|
354
|
+
"APPROVAL_INVALID",
|
|
355
|
+
"External approval is invalid, expired or for another plan/target.",
|
|
356
|
+
4
|
|
357
|
+
);
|
|
358
|
+
return;
|
|
359
|
+
}
|
|
360
|
+
const grants = (await policy()).grants;
|
|
361
|
+
if (!grants.some(
|
|
362
|
+
(g) => g.projectRoot === ctx.root && g.targetDigest === plan.targetDigest && g.operations.includes("deploy") && Date.parse(g.expiresAt) > Date.now() && (plan.restore ? g.planDigest === plan.digest : !g.planDigest || g.planDigest === plan.digest)
|
|
363
|
+
))
|
|
364
|
+
throw new Fault(
|
|
365
|
+
"DEPLOY_APPROVAL_REQUIRED",
|
|
366
|
+
"A user-owned development grant for this project and target is required.",
|
|
367
|
+
4,
|
|
368
|
+
"blocked"
|
|
369
|
+
);
|
|
370
|
+
}
|
|
371
|
+
async function sourceInventory(ctx) {
|
|
372
|
+
const files = {};
|
|
373
|
+
for (const relative of [
|
|
374
|
+
ctx.config.application.sourceDir,
|
|
375
|
+
ctx.config.database.migrationsDir,
|
|
376
|
+
ctx.config.database.packagesDir,
|
|
377
|
+
ctx.config.database.testsDir,
|
|
378
|
+
ctx.config.tests.unitDir,
|
|
379
|
+
ctx.config.tests.apiDir,
|
|
380
|
+
ctx.config.tests.e2eDir
|
|
381
|
+
]) {
|
|
382
|
+
const dir = await contained(ctx.root, relative);
|
|
383
|
+
if (await exists(dir))
|
|
384
|
+
for (const [file, sha] of Object.entries(await inventory(dir))) files[relative + "/" + file] = sha;
|
|
385
|
+
}
|
|
386
|
+
for (const relative of [
|
|
387
|
+
"package.json",
|
|
388
|
+
"package-lock.json",
|
|
389
|
+
"playwright.config.ts",
|
|
390
|
+
"playwright.config.mjs"
|
|
391
|
+
])
|
|
392
|
+
if (await exists(path3.join(ctx.root, relative)))
|
|
393
|
+
files[relative] = hash(await readFile2(await contained(ctx.root, relative)));
|
|
394
|
+
return Object.fromEntries(Object.entries(files).sort());
|
|
395
|
+
}
|
|
396
|
+
var DeploymentService = class {
|
|
397
|
+
constructor(oracle = new OracleAdapter(), runTests) {
|
|
398
|
+
this.oracle = oracle;
|
|
399
|
+
this.runTests = runTests;
|
|
400
|
+
}
|
|
401
|
+
oracle;
|
|
402
|
+
runTests;
|
|
403
|
+
async history(env, connection) {
|
|
404
|
+
if (coordination(env).backend === "local") return new LocalDeploymentControl(env).history();
|
|
405
|
+
return this.oracle.jsonQuery(
|
|
406
|
+
"select version,checksum,status,run_id from apexrest_migrations order by version",
|
|
407
|
+
connection
|
|
408
|
+
);
|
|
409
|
+
}
|
|
410
|
+
async fingerprint(env, connection) {
|
|
411
|
+
const target = await this.oracle.verifyTarget(env, connection);
|
|
412
|
+
const history = await this.history(env, connection);
|
|
413
|
+
const exported = target.application ? await this.oracle.exportApplication(env, connection, "APEXLANG") : null;
|
|
414
|
+
return {
|
|
415
|
+
target,
|
|
416
|
+
history,
|
|
417
|
+
exported,
|
|
418
|
+
fingerprint: hash(canonical({ target, history, exportDigest: exported?.digest ?? null }))
|
|
419
|
+
};
|
|
420
|
+
}
|
|
421
|
+
async plan(ctx, name) {
|
|
422
|
+
await requireTrust(ctx.root);
|
|
423
|
+
const env = environment(ctx, name), connection = await resolveConnection(env.readConnectionRef);
|
|
424
|
+
const [targetCheck, sourceCheck] = await Promise.allSettled([
|
|
425
|
+
this.fingerprint(env, connection),
|
|
426
|
+
(async () => {
|
|
427
|
+
const sources2 = await sourceInventory(ctx);
|
|
428
|
+
const validation2 = await this.oracle.validate(
|
|
429
|
+
await contained(ctx.root, ctx.config.application.sourceDir)
|
|
430
|
+
);
|
|
431
|
+
const lock2 = await contained(ctx.root, ctx.config.toolchain.lockFile);
|
|
432
|
+
if (!await exists(lock2))
|
|
433
|
+
throw new Fault("TOOLCHAIN_LOCK_REQUIRED", "The project needs its pinned toolchain lock.", 3);
|
|
434
|
+
return { sources: sources2, validation: validation2, lock: lock2 };
|
|
435
|
+
})()
|
|
436
|
+
]);
|
|
437
|
+
if (targetCheck.status === "rejected") throw targetCheck.reason;
|
|
438
|
+
if (sourceCheck.status === "rejected") throw sourceCheck.reason;
|
|
439
|
+
const current = targetCheck.value, { sources, validation, lock } = sourceCheck.value, risks = [];
|
|
440
|
+
const operations = [];
|
|
441
|
+
const history = new Map(current.history.map((row) => [String(row.version), row]));
|
|
442
|
+
if (current.history.some((row) => row.status !== "succeeded"))
|
|
443
|
+
throw new Fault(
|
|
444
|
+
"MIGRATION_HISTORY_CONFLICT",
|
|
445
|
+
"An earlier migration has an unresolved outcome. Reconcile it before any new deployment.",
|
|
446
|
+
5
|
|
447
|
+
);
|
|
448
|
+
for (const [file, sha256] of Object.entries(sources)) {
|
|
449
|
+
const migration = file.startsWith(ctx.config.database.migrationsDir + "/");
|
|
450
|
+
const pkg = file.startsWith(ctx.config.database.packagesDir + "/");
|
|
451
|
+
if (!migration && !pkg) continue;
|
|
452
|
+
if (!file.endsWith(".sql"))
|
|
453
|
+
throw new Fault("UNSUPPORTED_DB_SOURCE", "Database execution directories accept .sql files only.", 3);
|
|
454
|
+
const sql = await readFile2(await contained(ctx.root, file), "utf8");
|
|
455
|
+
risks.push(...migrationRisk(sql).map((r) => `${r}:${file}`));
|
|
456
|
+
if (migration) {
|
|
457
|
+
const version = path3.basename(file);
|
|
458
|
+
if (!/^\d{4,}__[A-Za-z0-9_-]+\.sql$/.test(version))
|
|
459
|
+
throw new Fault(
|
|
460
|
+
"INVALID_MIGRATION_NAME",
|
|
461
|
+
"Use ordered immutable migration names such as 0001__customers.sql.",
|
|
462
|
+
2
|
|
463
|
+
);
|
|
464
|
+
const previous = history.get(version);
|
|
465
|
+
if (previous && (previous.checksum !== sha256 || previous.status !== "succeeded"))
|
|
466
|
+
throw new Fault(
|
|
467
|
+
"MIGRATION_HISTORY_CONFLICT",
|
|
468
|
+
"An existing migration changed checksum or has an unresolved outcome.",
|
|
469
|
+
5
|
|
470
|
+
);
|
|
471
|
+
if (!previous) operations.push({ kind: "migration", file, sha256 });
|
|
472
|
+
} else operations.push({ kind: "package", file, sha256 });
|
|
473
|
+
}
|
|
474
|
+
if (current.exported) {
|
|
475
|
+
const prefix = ctx.config.application.sourceDir + "/";
|
|
476
|
+
for (const [file, sha] of Object.entries(current.exported.files))
|
|
477
|
+
if (/authenticat|authoriz/i.test(file) && sources[prefix + file] !== sha)
|
|
478
|
+
risks.push("authentication-or-authorization-change");
|
|
479
|
+
}
|
|
480
|
+
operations.sort(
|
|
481
|
+
(a, b) => (a.kind === "migration" ? 0 : 1) - (b.kind === "migration" ? 0 : 1) || (a.file ?? "").localeCompare(b.file ?? "")
|
|
482
|
+
);
|
|
483
|
+
operations.push({ kind: "import" }, { kind: "verify" }, { kind: "test" });
|
|
484
|
+
const plan = {
|
|
485
|
+
schemaVersion: 1,
|
|
486
|
+
id: randomUUID2(),
|
|
487
|
+
projectId: ctx.config.projectId,
|
|
488
|
+
projectRoot: ctx.root,
|
|
489
|
+
environment: name,
|
|
490
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
491
|
+
expiresAt: new Date(Date.now() + 30 * 6e4).toISOString(),
|
|
492
|
+
sourceDigest: hash(canonical(sources)),
|
|
493
|
+
sources,
|
|
494
|
+
configurationDigest: hash(canonical(ctx.config)),
|
|
495
|
+
toolchainDigest: hash(await readFile2(lock)),
|
|
496
|
+
compiler: validation.compiler.version,
|
|
497
|
+
targetDigest: targetDigest(env),
|
|
498
|
+
target: current.target,
|
|
499
|
+
fingerprint: current.fingerprint,
|
|
500
|
+
migrationHistory: current.history,
|
|
501
|
+
coordination: coordination(env),
|
|
502
|
+
scope: "full-application-import",
|
|
503
|
+
operations,
|
|
504
|
+
risks: [...new Set(risks)],
|
|
505
|
+
approval: "external-policy-required",
|
|
506
|
+
backupRequired: !!current.target.application,
|
|
507
|
+
digest: "0".repeat(64)
|
|
508
|
+
};
|
|
509
|
+
plan.digest = planDigest(plan);
|
|
510
|
+
return plan;
|
|
511
|
+
}
|
|
512
|
+
async checkLocal(ctx, value) {
|
|
513
|
+
const plan = parse(deployPlanSchema, value), env = environment(ctx, plan.environment);
|
|
514
|
+
if (plan.sourceDigest !== hash(canonical(plan.sources)) || plan.digest !== planDigest(plan))
|
|
515
|
+
throw new Fault("PLAN_TAMPERED", "Plan digest verification failed.", 5);
|
|
516
|
+
if (plan.projectId !== ctx.config.projectId || plan.projectRoot !== ctx.root || plan.targetDigest !== targetDigest(env))
|
|
517
|
+
throw new Fault("PLAN_TARGET_MISMATCH", "Plan project or target differs from the current request.", 5);
|
|
518
|
+
if (Date.parse(plan.expiresAt) <= Date.now())
|
|
519
|
+
throw new Fault("PLAN_EXPIRED", "Create and review a new plan.", 5);
|
|
520
|
+
if (plan.sourceDigest !== hash(canonical(await sourceInventory(ctx))) || plan.configurationDigest !== hash(canonical(ctx.config)) || plan.toolchainDigest !== hash(await readFile2(await contained(ctx.root, ctx.config.toolchain.lockFile))))
|
|
521
|
+
throw new Fault("SOURCE_DRIFT", "Sources, configuration or toolchain lock changed after review.", 5);
|
|
522
|
+
if (plan.backupRequired !== Boolean(plan.target.application))
|
|
523
|
+
throw new Fault("PLAN_TAMPERED", "Backup requirement does not match reviewed target.", 5);
|
|
524
|
+
if (canonical(plan.coordination) !== canonical(coordination(env)))
|
|
525
|
+
throw new Fault(
|
|
526
|
+
"CONTROL_STORE_CHANGED",
|
|
527
|
+
"Deployment control mode or local history store changed. Re-plan using the original durable state.",
|
|
528
|
+
5
|
|
529
|
+
);
|
|
530
|
+
const expected = [];
|
|
531
|
+
if (!plan.restore) {
|
|
532
|
+
const history = new Map(plan.migrationHistory.map((row) => [String(row.version), row]));
|
|
533
|
+
for (const [file, sha256] of Object.entries(plan.sources)) {
|
|
534
|
+
const kind = file.startsWith(ctx.config.database.migrationsDir + "/") ? "migration" : file.startsWith(ctx.config.database.packagesDir + "/") ? "package" : void 0;
|
|
535
|
+
if (!kind) continue;
|
|
536
|
+
if (!file.endsWith(".sql"))
|
|
537
|
+
throw new Fault("UNSUPPORTED_DB_SOURCE", "Database sources must be SQL files.", 5);
|
|
538
|
+
const previous = history.get(path3.basename(file));
|
|
539
|
+
if (kind === "migration" && previous && (previous.checksum !== sha256 || previous.status !== "succeeded"))
|
|
540
|
+
throw new Fault("MIGRATION_HISTORY_CONFLICT", "Migration requires reconciliation.", 5);
|
|
541
|
+
if (kind !== "migration" || !previous) expected.push({ kind, file, sha256 });
|
|
542
|
+
const risks = migrationRisk(await readFile2(await contained(ctx.root, file), "utf8")).map(
|
|
543
|
+
(r) => `${r}:${file}`
|
|
544
|
+
);
|
|
545
|
+
if (risks.some((r) => !plan.risks.includes(r)))
|
|
546
|
+
throw new Fault("PLAN_TAMPERED", "Plan omits a SQL risk.", 5);
|
|
547
|
+
}
|
|
548
|
+
expected.sort(
|
|
549
|
+
(a, b) => (a.kind === "migration" ? 0 : 1) - (b.kind === "migration" ? 0 : 1) || (a.file ?? "").localeCompare(b.file ?? "")
|
|
550
|
+
);
|
|
551
|
+
}
|
|
552
|
+
expected.push({ kind: "import" }, { kind: "verify" }, { kind: "test" });
|
|
553
|
+
if (canonical(expected) !== canonical(plan.operations))
|
|
554
|
+
throw new Fault(
|
|
555
|
+
"PLAN_TAMPERED",
|
|
556
|
+
"Plan operations do not match reviewed sources and migration history.",
|
|
557
|
+
5
|
|
558
|
+
);
|
|
559
|
+
return { plan, env };
|
|
560
|
+
}
|
|
561
|
+
async lease(env, connection, runId, acquire) {
|
|
562
|
+
if (coordination(env).backend === "local") {
|
|
563
|
+
const control = new LocalDeploymentControl(env);
|
|
564
|
+
if (acquire) await control.acquire(runId);
|
|
565
|
+
else await control.assertOwner(runId);
|
|
566
|
+
return;
|
|
567
|
+
}
|
|
568
|
+
const key = sqlLiteral(targetDigest(env)), owner = sqlLiteral(runId);
|
|
569
|
+
if (acquire)
|
|
570
|
+
await this.oracle.session(
|
|
571
|
+
`begin
|
|
572
|
+
update apexrest_deploy_locks set owner_id=${owner}, lease_until=systimestamp+interval '90' second where target_key=${key} and lease_until<systimestamp and phase='preparing';
|
|
573
|
+
if sql%rowcount=0 then
|
|
574
|
+
begin insert into apexrest_deploy_locks(target_key,owner_id,lease_until) values(${key},${owner},systimestamp+interval '90' second);
|
|
575
|
+
exception when dup_val_on_index then raise_application_error(-20001,'APEXREST_TARGET_LOCKED'); end;
|
|
576
|
+
end if;
|
|
577
|
+
commit;
|
|
578
|
+
end;
|
|
579
|
+
/`,
|
|
580
|
+
connection,
|
|
581
|
+
true
|
|
582
|
+
);
|
|
583
|
+
else
|
|
584
|
+
await this.oracle.session(
|
|
585
|
+
`begin
|
|
586
|
+
update apexrest_deploy_locks set lease_until=systimestamp+interval '90' second where target_key=${key} and owner_id=${owner} and lease_until>systimestamp;
|
|
587
|
+
if sql%rowcount<>1 then raise_application_error(-20002,'APEXREST_LEASE_LOST'); end if;
|
|
588
|
+
commit;
|
|
589
|
+
end;
|
|
590
|
+
/`,
|
|
591
|
+
connection,
|
|
592
|
+
true
|
|
593
|
+
);
|
|
594
|
+
}
|
|
595
|
+
async apply(ctx, value, signal) {
|
|
596
|
+
if (signal?.aborted)
|
|
597
|
+
throw new Fault("CANCELLED", "Deployment cancelled before execution.", 6, "cancelled");
|
|
598
|
+
const { plan, env } = await this.checkLocal(ctx, value);
|
|
599
|
+
await authorizePlan(ctx, plan, env);
|
|
600
|
+
await this.oracle.requireMutationSupport();
|
|
601
|
+
const readConnection = await resolveConnection(env.readConnectionRef), deployConnection = await resolveConnection(env.deployConnectionRef);
|
|
602
|
+
const [deployTargetCheck, fingerprintCheck, capabilityCheck] = await Promise.allSettled([
|
|
603
|
+
this.oracle.verifyTarget(env, deployConnection),
|
|
604
|
+
this.fingerprint(env, readConnection),
|
|
605
|
+
this.oracle.requireCapability("import")
|
|
606
|
+
]);
|
|
607
|
+
if (deployTargetCheck.status === "rejected") throw deployTargetCheck.reason;
|
|
608
|
+
if (fingerprintCheck.status === "rejected") throw fingerprintCheck.reason;
|
|
609
|
+
if (fingerprintCheck.value.fingerprint !== plan.fingerprint)
|
|
610
|
+
throw new Fault("TARGET_DRIFT", "Target or migration history changed after review.", 5);
|
|
611
|
+
if (capabilityCheck.status === "rejected") throw capabilityCheck.reason;
|
|
612
|
+
const capability = capabilityCheck.value;
|
|
613
|
+
if (capability.version !== plan.compiler)
|
|
614
|
+
throw new Fault("COMPILER_DRIFT", "SQLcl version changed after plan.", 5);
|
|
615
|
+
if (signal?.aborted)
|
|
616
|
+
throw new Fault("CANCELLED", "Deployment cancelled before lease acquisition.", 6, "cancelled");
|
|
617
|
+
const runId = randomUUID2(), runs = path3.join(ctx.root, ".apexrest/deployments"), runDir = path3.join(runs, runId);
|
|
618
|
+
await mkdir2(runDir, { recursive: true, mode: 448 });
|
|
619
|
+
let state = "planned", writeStarted = false;
|
|
620
|
+
const controller = new AbortController();
|
|
621
|
+
const abort = () => controller.abort();
|
|
622
|
+
signal?.addEventListener("abort", abort, { once: true });
|
|
623
|
+
if (signal?.aborted) abort();
|
|
624
|
+
const record = async (nextState, details = {}) => {
|
|
625
|
+
assertTransition(state, nextState);
|
|
626
|
+
state = nextState;
|
|
627
|
+
const event = {
|
|
628
|
+
runId,
|
|
629
|
+
planId: plan.id,
|
|
630
|
+
planDigest: plan.digest,
|
|
631
|
+
targetDigest: plan.targetDigest,
|
|
632
|
+
state,
|
|
633
|
+
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
634
|
+
details
|
|
635
|
+
};
|
|
636
|
+
const journal = await open(path3.join(runDir, "journal.jsonl"), "a", 384);
|
|
637
|
+
try {
|
|
638
|
+
await journal.writeFile(JSON.stringify(event) + "\n");
|
|
639
|
+
await journal.sync();
|
|
640
|
+
} finally {
|
|
641
|
+
await journal.close();
|
|
642
|
+
}
|
|
643
|
+
await writeJson(path3.join(runDir, "state.json"), event);
|
|
644
|
+
};
|
|
645
|
+
await writeJson(path3.join(runDir, "plan.json"), plan);
|
|
646
|
+
await record("approved");
|
|
647
|
+
await this.lease(env, deployConnection, runId, true);
|
|
648
|
+
let renewing = false, leaseLost = false;
|
|
649
|
+
const heartbeat = coordination(env).backend === "database" ? setInterval(() => {
|
|
650
|
+
if (renewing) return;
|
|
651
|
+
renewing = true;
|
|
652
|
+
void this.lease(env, deployConnection, runId, false).catch(() => {
|
|
653
|
+
leaseLost = true;
|
|
654
|
+
controller.abort();
|
|
655
|
+
}).finally(() => {
|
|
656
|
+
renewing = false;
|
|
657
|
+
});
|
|
658
|
+
}, 2e4) : void 0;
|
|
659
|
+
heartbeat?.unref();
|
|
660
|
+
try {
|
|
661
|
+
await record("backing_up");
|
|
662
|
+
if (plan.backupRequired) {
|
|
663
|
+
const backup = await this.oracle.exportApplication(env, readConnection, "SQL");
|
|
664
|
+
const backupId = randomUUID2(), directory = path3.join(ctx.root, ".apexrest/backups", backupId);
|
|
665
|
+
await mkdir2(directory, { recursive: true, mode: 448 });
|
|
666
|
+
await cp(backup.directory, path3.join(directory, "application"), { recursive: true });
|
|
667
|
+
const files = await inventory(path3.join(directory, "application"));
|
|
668
|
+
if (!Object.keys(files).length || hash(canonical(files)) !== backup.digest)
|
|
669
|
+
throw new Fault("BACKUP_INVALID", "Backup copy failed checksum verification.", 1);
|
|
670
|
+
await writeJson(path3.join(directory, "backup.json"), {
|
|
671
|
+
schemaVersion: 1,
|
|
672
|
+
backupId,
|
|
673
|
+
targetDigest: plan.targetDigest,
|
|
674
|
+
environment: plan.environment,
|
|
675
|
+
digest: backup.digest,
|
|
676
|
+
files,
|
|
677
|
+
restoreProcedure: "Reviewed SQL export import; application metadata only. Schema/data recovery is separate."
|
|
678
|
+
});
|
|
679
|
+
}
|
|
680
|
+
await this.checkLocal(ctx, plan);
|
|
681
|
+
if ((await this.fingerprint(env, readConnection)).fingerprint !== plan.fingerprint)
|
|
682
|
+
throw new Fault("TARGET_DRIFT", "Target changed during backup.", 5);
|
|
683
|
+
const snapshot = path3.join(runDir, "snapshot");
|
|
684
|
+
await mkdir2(snapshot);
|
|
685
|
+
for (const [file, sha] of Object.entries(plan.sources)) {
|
|
686
|
+
const source = await contained(ctx.root, file), destination = await contained(snapshot, file);
|
|
687
|
+
await mkdir2(path3.dirname(destination), { recursive: true });
|
|
688
|
+
await cp(source, destination);
|
|
689
|
+
if (hash(await readFile2(destination)) !== sha)
|
|
690
|
+
throw new Fault("SOURCE_DRIFT", "Source changed while freezing deployment.", 5);
|
|
691
|
+
}
|
|
692
|
+
if (controller.signal.aborted)
|
|
693
|
+
throw new Fault("CANCELLED", "Deployment cancelled before writes.", 6, "cancelled");
|
|
694
|
+
await record("migrating");
|
|
695
|
+
if (coordination(env).backend === "local") await new LocalDeploymentControl(env).markWriting(runId);
|
|
696
|
+
else
|
|
697
|
+
await this.oracle.session(
|
|
698
|
+
`update apexrest_deploy_locks set phase='writing' where target_key=${sqlLiteral(plan.targetDigest)} and owner_id=${sqlLiteral(runId)};
|
|
699
|
+
commit;`,
|
|
700
|
+
deployConnection,
|
|
701
|
+
true
|
|
702
|
+
);
|
|
703
|
+
for (const operation of plan.operations.filter((o) => ["migration", "package"].includes(o.kind))) {
|
|
704
|
+
if (leaseLost || controller.signal.aborted)
|
|
705
|
+
throw new Fault(
|
|
706
|
+
"LEASE_OR_CANCELLATION",
|
|
707
|
+
"Execution was interrupted.",
|
|
708
|
+
6,
|
|
709
|
+
writeStarted ? "outcome_unknown" : "cancelled"
|
|
710
|
+
);
|
|
711
|
+
await this.lease(env, deployConnection, runId, false);
|
|
712
|
+
const file = await contained(snapshot, operation.file);
|
|
713
|
+
const version = sqlLiteral(path3.basename(file));
|
|
714
|
+
if (operation.kind === "migration") {
|
|
715
|
+
if (coordination(env).backend === "local")
|
|
716
|
+
await new LocalDeploymentControl(env).migration(
|
|
717
|
+
runId,
|
|
718
|
+
path3.basename(file),
|
|
719
|
+
operation.sha256,
|
|
720
|
+
"started"
|
|
721
|
+
);
|
|
722
|
+
else
|
|
723
|
+
await this.oracle.session(
|
|
724
|
+
`insert into apexrest_migrations(version,checksum,status,run_id) values(${version},${sqlLiteral(operation.sha256)},'started',${sqlLiteral(runId)});
|
|
725
|
+
commit;`,
|
|
726
|
+
deployConnection,
|
|
727
|
+
true
|
|
728
|
+
);
|
|
729
|
+
}
|
|
730
|
+
writeStarted = true;
|
|
731
|
+
await this.oracle.session(
|
|
732
|
+
`@${sqlclToken(file)}
|
|
733
|
+
prompt APEXREST_SCRIPT_COMPLETE`,
|
|
734
|
+
deployConnection,
|
|
735
|
+
true,
|
|
736
|
+
controller.signal
|
|
737
|
+
);
|
|
738
|
+
if (operation.kind === "migration") {
|
|
739
|
+
if (coordination(env).backend === "local")
|
|
740
|
+
await new LocalDeploymentControl(env).migration(
|
|
741
|
+
runId,
|
|
742
|
+
path3.basename(file),
|
|
743
|
+
operation.sha256,
|
|
744
|
+
"succeeded"
|
|
745
|
+
);
|
|
746
|
+
else
|
|
747
|
+
await this.oracle.session(
|
|
748
|
+
`update apexrest_migrations set status='succeeded',finished_at=systimestamp where version=${version} and run_id=${sqlLiteral(runId)};
|
|
749
|
+
commit;`,
|
|
750
|
+
deployConnection,
|
|
751
|
+
true
|
|
752
|
+
);
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
await record("importing");
|
|
756
|
+
await this.lease(env, deployConnection, runId, false);
|
|
757
|
+
await this.oracle.verifyTarget(env, deployConnection);
|
|
758
|
+
writeStarted = true;
|
|
759
|
+
if (plan.restore) {
|
|
760
|
+
const backupRoot = await contained(
|
|
761
|
+
ctx.root,
|
|
762
|
+
".apexrest/backups/" + plan.restore.backupId + "/application"
|
|
763
|
+
);
|
|
764
|
+
if (hash(canonical(await inventory(backupRoot))) !== plan.restore.checksum)
|
|
765
|
+
throw new Fault("BACKUP_INVALID", "Restore source changed after approval.", 5);
|
|
766
|
+
const frozen = path3.join(runDir, "restore");
|
|
767
|
+
await cp(backupRoot, frozen, { recursive: true });
|
|
768
|
+
const files = await inventory(frozen);
|
|
769
|
+
if (hash(canonical(files)) !== plan.restore.checksum)
|
|
770
|
+
throw new Fault("BACKUP_INVALID", "Restore copy changed.", 5);
|
|
771
|
+
const main = Object.keys(files).filter((f) => /^f\d+\.sql$/i.test(f));
|
|
772
|
+
if (main.length !== 1)
|
|
773
|
+
throw new Fault(
|
|
774
|
+
"RESTORE_LAYOUT_UNSUPPORTED",
|
|
775
|
+
"Restore requires one complete non-split Oracle SQL export.",
|
|
776
|
+
3
|
|
777
|
+
);
|
|
778
|
+
await this.oracle.restoreApplication(
|
|
779
|
+
env,
|
|
780
|
+
deployConnection,
|
|
781
|
+
path3.join(frozen, main[0]),
|
|
782
|
+
controller.signal
|
|
783
|
+
);
|
|
784
|
+
} else
|
|
785
|
+
await this.oracle.importApplication(
|
|
786
|
+
ctx,
|
|
787
|
+
env,
|
|
788
|
+
deployConnection,
|
|
789
|
+
path3.join(snapshot, ctx.config.application.sourceDir),
|
|
790
|
+
controller.signal
|
|
791
|
+
);
|
|
792
|
+
await record("verifying");
|
|
793
|
+
const target = await this.oracle.verifyTarget(env, readConnection);
|
|
794
|
+
if (!target.application || String(target.application.alias).toLowerCase() !== String(
|
|
795
|
+
plan.restore ? plan.target.application.alias : ctx.config.application.alias
|
|
796
|
+
).toLowerCase())
|
|
797
|
+
throw new Fault("POST_DEPLOY_IDENTITY_FAILED", "Expected imported app was not found.", 1);
|
|
798
|
+
await record("testing");
|
|
799
|
+
if (ctx.config.tests.requiredSuites.length) {
|
|
800
|
+
if (!this.runTests)
|
|
801
|
+
throw new Fault("TEST_RUNNER_REQUIRED", "Required post-deploy tests are unavailable.", 3);
|
|
802
|
+
await this.checkLocal(ctx, plan);
|
|
803
|
+
const tests = await this.runTests(ctx, plan.environment);
|
|
804
|
+
if (!tests.ok)
|
|
805
|
+
throw new Fault("POST_DEPLOY_TEST_FAILED", "Required post-deploy suites did not pass.", 1);
|
|
806
|
+
}
|
|
807
|
+
await record("succeeded");
|
|
808
|
+
return { runId, state, directory: runDir };
|
|
809
|
+
} catch (error) {
|
|
810
|
+
const unknown = writeStarted && (!(error instanceof Fault) || error.exitCode === 6 || leaseLost);
|
|
811
|
+
await record(unknown ? "outcome_unknown" : "failed", {
|
|
812
|
+
code: error instanceof Fault ? error.code : "UNEXPECTED_FAILURE"
|
|
813
|
+
});
|
|
814
|
+
if (unknown)
|
|
815
|
+
throw new Fault(
|
|
816
|
+
"OUTCOME_UNKNOWN",
|
|
817
|
+
`Deployment ${runId} requires reconciliation before retry.`,
|
|
818
|
+
6,
|
|
819
|
+
"outcome_unknown"
|
|
820
|
+
);
|
|
821
|
+
throw error;
|
|
822
|
+
} finally {
|
|
823
|
+
if (heartbeat) clearInterval(heartbeat);
|
|
824
|
+
signal?.removeEventListener("abort", abort);
|
|
825
|
+
if (state !== "outcome_unknown") {
|
|
826
|
+
if (coordination(env).backend === "local")
|
|
827
|
+
await new LocalDeploymentControl(env).release(runId).catch(() => {
|
|
828
|
+
});
|
|
829
|
+
else
|
|
830
|
+
await this.oracle.session(
|
|
831
|
+
`delete from apexrest_deploy_locks where target_key=${sqlLiteral(plan.targetDigest)} and owner_id=${sqlLiteral(runId)};
|
|
832
|
+
commit;`,
|
|
833
|
+
deployConnection,
|
|
834
|
+
true
|
|
835
|
+
).catch(() => {
|
|
836
|
+
});
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
async reconcile(ctx, runId) {
|
|
841
|
+
parse(external_exports.uuid(), runId);
|
|
842
|
+
const directory = await contained(ctx.root, ".apexrest/deployments/" + runId);
|
|
843
|
+
const plan = parse(deployPlanSchema, await readJson(path3.join(directory, "plan.json"))), env = environment(ctx, plan.environment);
|
|
844
|
+
const current = await this.fingerprint(env, await resolveConnection(env.readConnectionRef));
|
|
845
|
+
const state = await readJson(path3.join(directory, "state.json"));
|
|
846
|
+
return {
|
|
847
|
+
runId,
|
|
848
|
+
state,
|
|
849
|
+
currentTarget: current.target,
|
|
850
|
+
currentFingerprint: current.fingerprint,
|
|
851
|
+
targetUnchanged: current.fingerprint === plan.fingerprint,
|
|
852
|
+
history: current.history,
|
|
853
|
+
retryAllowed: false,
|
|
854
|
+
nextActions: [
|
|
855
|
+
"Review target export and migration history. Reconciliation does not assume process termination rolled back Oracle."
|
|
856
|
+
]
|
|
857
|
+
};
|
|
858
|
+
}
|
|
859
|
+
async restorePlan(ctx, backupId) {
|
|
860
|
+
parse(external_exports.uuid(), backupId);
|
|
861
|
+
const directory = await contained(ctx.root, ".apexrest/backups/" + backupId);
|
|
862
|
+
const backup = await readJson(path3.join(directory, "backup.json"));
|
|
863
|
+
const files = await inventory(path3.join(directory, "application"));
|
|
864
|
+
if (hash(canonical(files)) !== backup.digest)
|
|
865
|
+
throw new Fault("BACKUP_INVALID", "Backup digest does not match.", 5);
|
|
866
|
+
const plan = await this.plan(ctx, backup.environment);
|
|
867
|
+
if (plan.targetDigest !== backup.targetDigest)
|
|
868
|
+
throw new Fault("BACKUP_TARGET_MISMATCH", "Backup belongs to another target.", 5);
|
|
869
|
+
plan.restore = { backupId, checksum: backup.digest };
|
|
870
|
+
plan.risks = ["application-restore"];
|
|
871
|
+
plan.operations = [{ kind: "import" }, { kind: "verify" }, { kind: "test" }];
|
|
872
|
+
plan.digest = planDigest(plan);
|
|
873
|
+
return plan;
|
|
874
|
+
}
|
|
875
|
+
};
|
|
876
|
+
|
|
877
|
+
// packages/core/src/testing.ts
|
|
878
|
+
function qualityGate(results, required) {
|
|
879
|
+
return results.every((r) => !["failed", "cancelled"].includes(r.status)) && required.every(
|
|
880
|
+
(s) => results.some(
|
|
881
|
+
(r) => r.suite === s && r.status === "passed" && r.tests > r.skipped && r.failures === 0 && r.skipped === 0
|
|
882
|
+
)
|
|
883
|
+
);
|
|
884
|
+
}
|
|
885
|
+
function parseJUnit(xml) {
|
|
886
|
+
if (/<!DOCTYPE|<!ENTITY/i.test(xml))
|
|
887
|
+
throw new Fault("UNSAFE_REPORT", "DTD/entities are not allowed in test reports.", 1);
|
|
888
|
+
const cases = [...xml.matchAll(/<testcase\b[^>]*?\/>|<testcase\b[^>]*>[\s\S]*?<\/testcase>/g)].map(
|
|
889
|
+
(m) => m[0]
|
|
890
|
+
);
|
|
891
|
+
return {
|
|
892
|
+
tests: cases.length,
|
|
893
|
+
failures: cases.filter((c) => /<(?:failure|error)\b/.test(c)).length,
|
|
894
|
+
skipped: cases.filter((c) => /<skipped\b/.test(c)).length
|
|
895
|
+
};
|
|
896
|
+
}
|
|
897
|
+
function allowedOrigin(url, origins) {
|
|
898
|
+
const u = new URL(url);
|
|
899
|
+
if (!["https:", "http:"].includes(u.protocol) || u.username || u.password || u.hostname.endsWith(".invalid") || u.protocol === "http:" && !["localhost", "127.0.0.1", "[::1]"].includes(u.hostname) || !origins.includes(u.origin))
|
|
900
|
+
throw new Fault("ORIGIN_DENIED", "The target origin is outside the configured allowlist.", 4);
|
|
901
|
+
return u;
|
|
902
|
+
}
|
|
903
|
+
var TestService = class {
|
|
904
|
+
constructor(oracle = new OracleAdapter()) {
|
|
905
|
+
this.oracle = oracle;
|
|
906
|
+
}
|
|
907
|
+
oracle;
|
|
908
|
+
async authorize(ctx, name) {
|
|
909
|
+
await requireTrust(ctx.root);
|
|
910
|
+
const env = environment(ctx, name);
|
|
911
|
+
if (env.kind === "production" || !ctx.config.tests.mutationAllowedEnvironments.includes(name))
|
|
912
|
+
throw new Fault(
|
|
913
|
+
"TEST_MUTATION_DENIED",
|
|
914
|
+
"Remote tests require a non-production environment explicitly allowed for mutations.",
|
|
915
|
+
4,
|
|
916
|
+
"blocked"
|
|
917
|
+
);
|
|
918
|
+
if (!(await policy()).grants.some(
|
|
919
|
+
(g) => g.projectRoot === ctx.root && g.targetDigest === targetDigest(env) && g.operations.includes("test") && Date.parse(g.expiresAt) > Date.now()
|
|
920
|
+
))
|
|
921
|
+
throw new Fault(
|
|
922
|
+
"TEST_APPROVAL_REQUIRED",
|
|
923
|
+
"User-owned policy must authorize tests for this exact target.",
|
|
924
|
+
4,
|
|
925
|
+
"blocked"
|
|
926
|
+
);
|
|
927
|
+
allowedOrigin(env.baseUrl, [new URL(env.baseUrl).origin, ...env.allowedOrigins]);
|
|
928
|
+
return env;
|
|
929
|
+
}
|
|
930
|
+
async run(ctx, suite, envName, signal, headed = false) {
|
|
931
|
+
try {
|
|
932
|
+
await requireTrust(ctx.root);
|
|
933
|
+
const dir = await contained(
|
|
934
|
+
ctx.root,
|
|
935
|
+
suite === "sql" ? ctx.config.database.testsDir : ctx.config.tests[`${suite}Dir`]
|
|
936
|
+
);
|
|
937
|
+
if (!await exists(dir)) return { suite, status: "not_configured", tests: 0, failures: 0, skipped: 0 };
|
|
938
|
+
const files = Object.keys(await inventory(dir));
|
|
939
|
+
if (!files.length) return { suite, status: "empty", tests: 0, failures: 0, skipped: 0 };
|
|
940
|
+
const artifacts = new ArtifactService(ctx);
|
|
941
|
+
if (suite === "unit") {
|
|
942
|
+
const tests = files.filter((f) => /\.(?:test|spec)\.(?:mjs|js|ts)$/.test(f));
|
|
943
|
+
if (!tests.length) return { suite, status: "empty", tests: 0, failures: 0, skipped: 0 };
|
|
944
|
+
const result2 = await runProcess({
|
|
945
|
+
executable: process.execPath,
|
|
946
|
+
args: [
|
|
947
|
+
"--experimental-strip-types",
|
|
948
|
+
"--test",
|
|
949
|
+
"--test-reporter=junit",
|
|
950
|
+
...tests.map((f) => path4.join(dir, f))
|
|
951
|
+
],
|
|
952
|
+
cwd: ctx.root,
|
|
953
|
+
...signal ? { signal } : {},
|
|
954
|
+
timeoutMs: 18e4
|
|
955
|
+
});
|
|
956
|
+
const counts2 = parseJUnit(result2.stdout);
|
|
957
|
+
const artifactId2 = await artifacts.save(result2.stdout + result2.stderr, "unit-report");
|
|
958
|
+
return {
|
|
959
|
+
suite,
|
|
960
|
+
status: result2.timedOut || result2.cancelled ? "cancelled" : !counts2.tests ? "empty" : result2.code === 0 && !counts2.failures ? "passed" : "failed",
|
|
961
|
+
...counts2,
|
|
962
|
+
artifactId: artifactId2
|
|
963
|
+
};
|
|
964
|
+
}
|
|
965
|
+
if (!envName) throw new Fault("ENVIRONMENT_REQUIRED", "Remote test suites require --env.", 2);
|
|
966
|
+
const env = await this.authorize(ctx, envName);
|
|
967
|
+
if (suite === "sql") {
|
|
968
|
+
await this.oracle.requireMutationSupport();
|
|
969
|
+
const connection = await resolveConnection(env.deployConnectionRef);
|
|
970
|
+
await this.oracle.verifyTarget(env, connection);
|
|
971
|
+
const framework = await this.oracle.jsonQuery(
|
|
972
|
+
"select owner,object_name from all_objects where object_name='UT' and object_type='PACKAGE'",
|
|
973
|
+
connection
|
|
974
|
+
);
|
|
975
|
+
if (!framework.length)
|
|
976
|
+
return {
|
|
977
|
+
suite,
|
|
978
|
+
status: "dependency_missing",
|
|
979
|
+
tests: 0,
|
|
980
|
+
failures: 0,
|
|
981
|
+
skipped: 0,
|
|
982
|
+
diagnostic: "utPLSQL is absent. Review a separate framework installation plan."
|
|
983
|
+
};
|
|
984
|
+
for (const file of files.filter((f) => f.endsWith(".sql")).sort())
|
|
985
|
+
await this.oracle.session(`@${sqlclToken(await contained(dir, file))}`, connection, true, signal);
|
|
986
|
+
const result2 = await this.oracle.session(
|
|
987
|
+
`set serveroutput on size unlimited
|
|
988
|
+
begin
|
|
989
|
+
ut.run(${sqlLiteral(env.parsingSchema)}, ut_junit_reporter());
|
|
990
|
+
end;
|
|
991
|
+
/`,
|
|
992
|
+
connection,
|
|
993
|
+
true,
|
|
994
|
+
signal
|
|
995
|
+
);
|
|
996
|
+
const counts2 = parseJUnit(result2.output), artifactId2 = await artifacts.save(result2.output, "utplsql-junit");
|
|
997
|
+
return {
|
|
998
|
+
suite,
|
|
999
|
+
status: !counts2.tests ? "empty" : counts2.failures ? "failed" : "passed",
|
|
1000
|
+
...counts2,
|
|
1001
|
+
artifactId: artifactId2
|
|
1002
|
+
};
|
|
1003
|
+
}
|
|
1004
|
+
const state = await runtimeState();
|
|
1005
|
+
if (!state.playwright || !state.node)
|
|
1006
|
+
return {
|
|
1007
|
+
suite,
|
|
1008
|
+
status: "dependency_missing",
|
|
1009
|
+
tests: 0,
|
|
1010
|
+
failures: 0,
|
|
1011
|
+
skipped: 0,
|
|
1012
|
+
diagnostic: "Run apexrest setup to install pinned Playwright and Chromium."
|
|
1013
|
+
};
|
|
1014
|
+
const runId = randomUUID3(), runnerRoot = path4.resolve(state.playwright, "../../../.."), run = path4.join(runnerRoot, "runs", runId);
|
|
1015
|
+
await mkdir3(run, { recursive: true, mode: 448 });
|
|
1016
|
+
await cp2(dir, path4.join(run, "tests"), { recursive: true });
|
|
1017
|
+
await cp2(path4.join(resourceRoot(), "testkit"), path4.join(run, "testkit"), { recursive: true });
|
|
1018
|
+
const auth = path4.join(ctx.root, ".apexrest/auth", envName, "state.json"), authMeta = auth + ".meta.json";
|
|
1019
|
+
if (suite === "e2e" && (!await exists(auth) || !await exists(authMeta) || Date.parse((await readJson(authMeta)).expiresAt) < Date.now()))
|
|
1020
|
+
return {
|
|
1021
|
+
suite,
|
|
1022
|
+
status: "blocked",
|
|
1023
|
+
tests: 0,
|
|
1024
|
+
failures: 0,
|
|
1025
|
+
skipped: 0,
|
|
1026
|
+
diagnostic: "Authenticated browser state is missing or expired. Run test auth interactively."
|
|
1027
|
+
};
|
|
1028
|
+
await atomicWrite(
|
|
1029
|
+
path4.join(run, "playwright.config.mjs"),
|
|
1030
|
+
`export default ${JSON.stringify({ testDir: "./tests", forbidOnly: true, retries: 0, timeout: 3e4, workers: 1, reporter: [["json", { outputFile: path4.join(run, "report.json") }]], use: { baseURL: env.baseUrl, browserName: "chromium", serviceWorkers: "block", trace: "off", screenshot: "off", video: "off", ...suite === "e2e" ? { storageState: auth } : {} } })};
|
|
1031
|
+
`
|
|
1032
|
+
);
|
|
1033
|
+
const result = await runProcess({
|
|
1034
|
+
executable: state.node,
|
|
1035
|
+
args: [
|
|
1036
|
+
state.playwright,
|
|
1037
|
+
"test",
|
|
1038
|
+
"--config",
|
|
1039
|
+
path4.join(run, "playwright.config.mjs"),
|
|
1040
|
+
...headed ? ["--headed"] : []
|
|
1041
|
+
],
|
|
1042
|
+
cwd: run,
|
|
1043
|
+
env: {
|
|
1044
|
+
...process.env,
|
|
1045
|
+
PLAYWRIGHT_BROWSERS_PATH: path4.join(managedHome(), "browsers"),
|
|
1046
|
+
APEXREST_ALLOWED_ORIGINS: JSON.stringify([new URL(env.baseUrl).origin, ...env.allowedOrigins]),
|
|
1047
|
+
APEXREST_EXPECTED_MARKER: env.expectedMarker ?? ""
|
|
1048
|
+
},
|
|
1049
|
+
timeoutMs: 3e5,
|
|
1050
|
+
...signal ? { signal } : {}
|
|
1051
|
+
});
|
|
1052
|
+
const reportFile = path4.join(run, "report.json");
|
|
1053
|
+
if (!await exists(reportFile))
|
|
1054
|
+
return {
|
|
1055
|
+
suite,
|
|
1056
|
+
status: result.timedOut || result.cancelled ? "cancelled" : "failed",
|
|
1057
|
+
tests: 0,
|
|
1058
|
+
failures: 1,
|
|
1059
|
+
skipped: 0,
|
|
1060
|
+
diagnostic: redact(result.stderr).slice(0, 2e3)
|
|
1061
|
+
};
|
|
1062
|
+
const report = await readJson(reportFile);
|
|
1063
|
+
const stats = report.stats, counts = {
|
|
1064
|
+
tests: stats.expected + stats.unexpected + stats.flaky + stats.skipped,
|
|
1065
|
+
failures: stats.unexpected + stats.flaky + (report.errors?.length ?? 0),
|
|
1066
|
+
skipped: stats.skipped
|
|
1067
|
+
};
|
|
1068
|
+
const artifactId = await artifacts.save(await readFile3(reportFile, "utf8"), "playwright-report");
|
|
1069
|
+
return {
|
|
1070
|
+
suite,
|
|
1071
|
+
status: result.timedOut || result.cancelled ? "cancelled" : !counts.tests ? "empty" : result.code === 0 && !counts.failures ? "passed" : "failed",
|
|
1072
|
+
...counts,
|
|
1073
|
+
artifactId
|
|
1074
|
+
};
|
|
1075
|
+
} catch (e) {
|
|
1076
|
+
return {
|
|
1077
|
+
suite,
|
|
1078
|
+
status: e instanceof Fault && e.exitCode === 3 ? "dependency_missing" : e instanceof Fault && e.exitCode === 6 ? "cancelled" : "blocked",
|
|
1079
|
+
tests: 0,
|
|
1080
|
+
failures: 0,
|
|
1081
|
+
skipped: 0,
|
|
1082
|
+
diagnostic: redact(e instanceof Error ? e.message : "Test runner failed.")
|
|
1083
|
+
};
|
|
1084
|
+
}
|
|
1085
|
+
}
|
|
1086
|
+
async all(ctx, name, signal) {
|
|
1087
|
+
const results = [];
|
|
1088
|
+
for (const suite of ["unit", "sql", "api", "e2e"])
|
|
1089
|
+
results.push(await this.run(ctx, suite, name, signal));
|
|
1090
|
+
const runId = randomUUID3(), ok = qualityGate(results, ctx.config.tests.requiredSuites);
|
|
1091
|
+
const report = {
|
|
1092
|
+
runId,
|
|
1093
|
+
ok,
|
|
1094
|
+
environment: name,
|
|
1095
|
+
results,
|
|
1096
|
+
required: ctx.config.tests.requiredSuites,
|
|
1097
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1098
|
+
};
|
|
1099
|
+
await writeJson(path4.join(ctx.root, ".apexrest/test-runs", runId + ".json"), report);
|
|
1100
|
+
return { ok, data: report };
|
|
1101
|
+
}
|
|
1102
|
+
async auth(ctx, name) {
|
|
1103
|
+
const env = await this.authorize(ctx, name), state = await runtimeState();
|
|
1104
|
+
if (!state.playwright || !state.node)
|
|
1105
|
+
throw new Fault("SETUP_REQUIRED", "Install Playwright through setup first.", 3);
|
|
1106
|
+
if (!process.stdin.isTTY)
|
|
1107
|
+
throw new Fault(
|
|
1108
|
+
"INTERACTIVE_LOGIN_REQUIRED",
|
|
1109
|
+
"Run test auth in a local interactive terminal. Do not send passwords to Codex.",
|
|
1110
|
+
4
|
|
1111
|
+
);
|
|
1112
|
+
const destination = path4.join(ctx.root, ".apexrest/auth", name, "state.json");
|
|
1113
|
+
await mkdir3(path4.dirname(destination), { recursive: true, mode: 448 });
|
|
1114
|
+
const helper = path4.join(path4.resolve(state.playwright, "../../../.."), "auth.mjs");
|
|
1115
|
+
await cp2(path4.join(resourceRoot(), "playwright/auth.mjs"), helper);
|
|
1116
|
+
const code = await new Promise((resolve, reject) => {
|
|
1117
|
+
const child = spawn(
|
|
1118
|
+
state.node,
|
|
1119
|
+
[
|
|
1120
|
+
helper,
|
|
1121
|
+
destination,
|
|
1122
|
+
env.baseUrl,
|
|
1123
|
+
JSON.stringify([new URL(env.baseUrl).origin, ...env.allowedOrigins])
|
|
1124
|
+
],
|
|
1125
|
+
{
|
|
1126
|
+
cwd: ctx.root,
|
|
1127
|
+
env: { ...process.env, PLAYWRIGHT_BROWSERS_PATH: path4.join(managedHome(), "browsers") },
|
|
1128
|
+
stdio: ["inherit", "ignore", "inherit"]
|
|
1129
|
+
}
|
|
1130
|
+
);
|
|
1131
|
+
child.once("error", reject);
|
|
1132
|
+
child.once("exit", resolve);
|
|
1133
|
+
});
|
|
1134
|
+
if (code !== 0 || !await exists(destination))
|
|
1135
|
+
throw new Fault("AUTH_NOT_SAVED", "Login did not save browser state.", 4);
|
|
1136
|
+
await chmod(destination, 384);
|
|
1137
|
+
await writeJson(destination + ".meta.json", {
|
|
1138
|
+
expiresAt: new Date(Date.now() + 8 * 36e5).toISOString(),
|
|
1139
|
+
origin: new URL(env.baseUrl).origin
|
|
1140
|
+
});
|
|
1141
|
+
return { state: "stored", expiresInHours: 8, authentication: "verified-by-required-E2E-marker" };
|
|
1142
|
+
}
|
|
1143
|
+
};
|
|
1144
|
+
|
|
1145
|
+
// packages/core/src/team-schema.ts
|
|
1146
|
+
var workOptionsSchema = external_exports.strictObject({
|
|
1147
|
+
executionMode: external_exports.enum(["single", "team"]).default("single"),
|
|
1148
|
+
browserMode: external_exports.enum(["codex", "external"]).default("codex"),
|
|
1149
|
+
developers: external_exports.number().int().min(1).max(3).default(1),
|
|
1150
|
+
sandbox: external_exports.enum(["read-only", "workspace-write"]).default("workspace-write"),
|
|
1151
|
+
timeoutSeconds: external_exports.number().int().min(30).max(3600).default(900)
|
|
1152
|
+
});
|
|
1153
|
+
var workPreferencesSchema = workOptionsSchema.extend({
|
|
1154
|
+
// Legacy executionMode: team was also written by unrelated settings saves.
|
|
1155
|
+
// Only this explicit settings opt-in authorizes new multi-agent runs.
|
|
1156
|
+
multiAgentEnabled: external_exports.boolean().default(false)
|
|
1157
|
+
});
|
|
1158
|
+
var teamStartSchema = external_exports.strictObject({
|
|
1159
|
+
executionMode: workPreferencesSchema.shape.executionMode.removeDefault().optional(),
|
|
1160
|
+
browserMode: workPreferencesSchema.shape.browserMode.removeDefault().optional(),
|
|
1161
|
+
developers: workPreferencesSchema.shape.developers.removeDefault().optional(),
|
|
1162
|
+
sandbox: workPreferencesSchema.shape.sandbox.removeDefault().optional(),
|
|
1163
|
+
timeoutSeconds: workPreferencesSchema.shape.timeoutSeconds.removeDefault().optional(),
|
|
1164
|
+
project: external_exports.string().optional(),
|
|
1165
|
+
task: external_exports.string().trim().min(1).max(16e3)
|
|
1166
|
+
});
|
|
1167
|
+
var resolvedTeamStartSchema = workOptionsSchema.extend({
|
|
1168
|
+
project: external_exports.string().optional(),
|
|
1169
|
+
task: teamStartSchema.shape.task
|
|
1170
|
+
});
|
|
1171
|
+
var queuedWorkSchema = resolvedTeamStartSchema.extend({
|
|
1172
|
+
multiAgentEnabled: external_exports.boolean().default(false)
|
|
1173
|
+
});
|
|
1174
|
+
function recordedExecutionMode(state) {
|
|
1175
|
+
if (state.executionMode === "single" || state.executionMode === "team") return state.executionMode;
|
|
1176
|
+
const reviewed = [state.reviews, state.qa].some((rows) => Array.isArray(rows) && rows.length > 0);
|
|
1177
|
+
const peers = Array.isArray(state.members) && state.members.some(
|
|
1178
|
+
(member) => member && ["manager", "qa", "developer-2", "developer-3"].includes(member.role)
|
|
1179
|
+
);
|
|
1180
|
+
return reviewed || peers ? "team" : "single";
|
|
1181
|
+
}
|
|
1182
|
+
var teamIdSchema = external_exports.strictObject({ project: external_exports.string().optional(), id: external_exports.uuid() });
|
|
1183
|
+
var workStartSchema = teamStartSchema.extend({ requestId: external_exports.uuid() });
|
|
1184
|
+
var teamWaitSchema = teamIdSchema.extend({
|
|
1185
|
+
cursor: external_exports.string().regex(/^[a-f0-9]{64}$/).optional(),
|
|
1186
|
+
waitSeconds: external_exports.number().int().min(1).max(30).default(25)
|
|
1187
|
+
});
|
|
1188
|
+
var teamMessageSchema = teamIdSchema.extend({ message: external_exports.string().trim().min(1).max(8e3) });
|
|
1189
|
+
var planningSchema = external_exports.strictObject({
|
|
1190
|
+
plan: external_exports.string().min(1).max(6e3),
|
|
1191
|
+
complexity: external_exports.enum(["simple", "standard", "complex"]),
|
|
1192
|
+
reason: external_exports.string().min(1).max(600)
|
|
1193
|
+
});
|
|
1194
|
+
var reviewSchema = external_exports.strictObject({
|
|
1195
|
+
decision: external_exports.enum(["approve", "revise"]),
|
|
1196
|
+
summary: external_exports.string().min(1).max(4e3),
|
|
1197
|
+
findings: external_exports.array(external_exports.string().min(1).max(2e3)).max(20)
|
|
1198
|
+
});
|
|
1199
|
+
var routedReviewSchema = reviewSchema.extend({
|
|
1200
|
+
revisionCause: external_exports.enum(["none", "implementation", "prerequisite"])
|
|
1201
|
+
});
|
|
1202
|
+
var qaSchema = external_exports.strictObject({
|
|
1203
|
+
decision: external_exports.enum(["pass", "fail", "blocked"]),
|
|
1204
|
+
summary: external_exports.string().min(1).max(4e3),
|
|
1205
|
+
checks: external_exports.array(
|
|
1206
|
+
external_exports.strictObject({
|
|
1207
|
+
name: external_exports.string().min(1).max(500),
|
|
1208
|
+
status: external_exports.enum(["passed", "failed", "not_run"]),
|
|
1209
|
+
evidence: external_exports.string().min(1).max(2e3)
|
|
1210
|
+
})
|
|
1211
|
+
).min(1).max(20)
|
|
1212
|
+
});
|
|
1213
|
+
|
|
1214
|
+
// packages/core/src/work-preferences.ts
|
|
1215
|
+
async function workPreferences(root) {
|
|
1216
|
+
const file = await contained(root, ".apexrest/panel/preferences.json");
|
|
1217
|
+
const preferences = parse(workPreferencesSchema, await exists(file) ? await readJson(file) : {});
|
|
1218
|
+
if (!preferences.multiAgentEnabled) preferences.executionMode = "single";
|
|
1219
|
+
return preferences;
|
|
1220
|
+
}
|
|
1221
|
+
async function resolveWorkRequest(root, input) {
|
|
1222
|
+
const fields = parse(teamStartSchema, input);
|
|
1223
|
+
const overrides = Object.fromEntries(Object.entries(fields).filter(([, value]) => value !== void 0));
|
|
1224
|
+
const { multiAgentEnabled, ...preferences } = await workPreferences(root);
|
|
1225
|
+
const request = resolvedTeamStartSchema.parse({
|
|
1226
|
+
...preferences,
|
|
1227
|
+
...overrides,
|
|
1228
|
+
project: root
|
|
1229
|
+
});
|
|
1230
|
+
if (request.executionMode === "team" && !multiAgentEnabled)
|
|
1231
|
+
throw new Fault(
|
|
1232
|
+
"MULTI_AGENT_DISABLED",
|
|
1233
|
+
"Multi-agent work is disabled. The user must explicitly enable multiAgentEnabled in Settings before starting a team.",
|
|
1234
|
+
2
|
|
1235
|
+
);
|
|
1236
|
+
if (request.executionMode === "single") request.developers = 1;
|
|
1237
|
+
return request;
|
|
1238
|
+
}
|
|
1239
|
+
|
|
1240
|
+
// packages/core/src/browser.ts
|
|
1241
|
+
function browserInstructions(mode) {
|
|
1242
|
+
return `Interactive APEX verification browser: ${mode}. ` + (mode === "external" ? "Use apexrest_browser_open for the explicit environment to open the system browser. Use available external-browser controls and the user's interactive SSO session. Do not substitute the Codex in-app browser." : "Use apexrest_browser_open for the explicit environment, then open its URL with the host Codex in-app browser controls. Do not substitute an external browser.") + " Opening a URL is not verification. Inspect the affected interactions with controls for the selected browser; if those controls or authentication are unavailable, report the check as not_run and the exact limitation. Never read or copy browser profiles, cookies or credentials. Login is interactive, without login capture. Automated Playwright suites remain separate and do not inherit system-browser or Codex cookies.";
|
|
1243
|
+
}
|
|
1244
|
+
function externalBrowserCommand(url, platform = process.platform) {
|
|
1245
|
+
if (platform === "darwin") return { executable: "/usr/bin/open", args: [url] };
|
|
1246
|
+
if (platform === "win32")
|
|
1247
|
+
return {
|
|
1248
|
+
executable: "powershell.exe",
|
|
1249
|
+
args: [
|
|
1250
|
+
"-NoProfile",
|
|
1251
|
+
"-NonInteractive",
|
|
1252
|
+
"-Command",
|
|
1253
|
+
"Start-Process -FilePath '" + url.replaceAll("'", "''") + "'"
|
|
1254
|
+
]
|
|
1255
|
+
};
|
|
1256
|
+
return { executable: "xdg-open", args: [url] };
|
|
1257
|
+
}
|
|
1258
|
+
async function openVerificationBrowser(ctx, name, launch = runProcess, browserMode) {
|
|
1259
|
+
await requireTrust(ctx.root);
|
|
1260
|
+
const target = environment(ctx, name);
|
|
1261
|
+
const url = allowedOrigin(target.baseUrl, [
|
|
1262
|
+
new URL(target.baseUrl).origin,
|
|
1263
|
+
...target.allowedOrigins
|
|
1264
|
+
]).toString();
|
|
1265
|
+
const pinned = process.env.APEXREST_TEAM_WORKER === "1" ? process.env.APEXREST_BROWSER_MODE : void 0;
|
|
1266
|
+
const mode = pinned === "external" || pinned === "codex" ? pinned : browserMode ?? (await workPreferences(ctx.root)).browserMode;
|
|
1267
|
+
const common = {
|
|
1268
|
+
browserMode: mode,
|
|
1269
|
+
environment: name,
|
|
1270
|
+
url,
|
|
1271
|
+
verified: false,
|
|
1272
|
+
nextAction: browserInstructions(mode)
|
|
1273
|
+
};
|
|
1274
|
+
if (mode === "codex") return { ...common, status: "host_action_required" };
|
|
1275
|
+
const result = await launch({ ...externalBrowserCommand(url), cwd: ctx.root, timeoutMs: 1e4 });
|
|
1276
|
+
if (result.code !== 0 || result.timedOut || result.cancelled)
|
|
1277
|
+
throw new Fault(
|
|
1278
|
+
"BROWSER_OPEN_FAILED",
|
|
1279
|
+
"The system browser could not be opened. Check the local desktop session; no browser verification was performed.",
|
|
1280
|
+
3,
|
|
1281
|
+
"blocked"
|
|
1282
|
+
);
|
|
1283
|
+
return { ...common, status: "opened" };
|
|
1284
|
+
}
|
|
1285
|
+
|
|
1286
|
+
export {
|
|
1287
|
+
workPreferencesSchema,
|
|
1288
|
+
teamStartSchema,
|
|
1289
|
+
resolvedTeamStartSchema,
|
|
1290
|
+
queuedWorkSchema,
|
|
1291
|
+
recordedExecutionMode,
|
|
1292
|
+
teamIdSchema,
|
|
1293
|
+
workStartSchema,
|
|
1294
|
+
teamWaitSchema,
|
|
1295
|
+
teamMessageSchema,
|
|
1296
|
+
planningSchema,
|
|
1297
|
+
routedReviewSchema,
|
|
1298
|
+
qaSchema,
|
|
1299
|
+
DeploymentService,
|
|
1300
|
+
ArtifactService,
|
|
1301
|
+
TestService,
|
|
1302
|
+
workPreferences,
|
|
1303
|
+
resolveWorkRequest,
|
|
1304
|
+
browserInstructions,
|
|
1305
|
+
externalBrowserCommand,
|
|
1306
|
+
openVerificationBrowser
|
|
1307
|
+
};
|