From 3ef53864bad245a86453a13e901f4159f1e114c8 Mon Sep 17 00:00:00 2001 From: "Ralph Sto. Domingo" Date: Thu, 24 Sep 2026 12:04:00 +0800 Subject: [PATCH 1/8] fix(dbt-tools): move to dbt-integration 0.3.13 so the dbt adapter starts under Bun `dbt-tools` resolved `@altimateai/dbt-integration` 0.2.14, which spawns its Python child through `python-bridge`. Under Bun that module fails to load (`bluebird.promisifyAll(child_process)` throws on `ChildProcess.prototype.stdin`), and past that, Bun's IPC to a non-JS child closes after the first message. So the dbt-first path of `sql_execute` never ran; the adapter silently fell back. 0.3.x vendors the bridge and talks newline JSON over a plain fd-3 pipe, with no `python-bridge` or `bluebird` in the tree. - `adapter.ts`: the 0.3.x constructors take `UnitTestParser` and `SemanticModelParser`; the Fusion integration no longer takes the cloud-variant detector (the Cloud integration still does). - `copy-python.ts`: the vendored bridge resolves its script from the bundle's own location at runtime, so the `python-bridge` `__dirname` patch is gone; the build now fails if a build-time path is baked in. `build-adversarial.test.ts` tested only that patch and is removed; `build-integrity.test.ts` and the release smoke check assert the new invariant. Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_0172qrhMa5TQgETASi5hxMqD --- .github/workflows/release.yml | 6 +- bun.lock | 82 +-- packages/dbt-tools/package.json | 2 +- packages/dbt-tools/script/copy-python.ts | 29 +- packages/dbt-tools/src/adapter.ts | 5 +- .../dbt-tools/test/build-adversarial.test.ts | 484 ------------------ .../dbt-tools/test/build-integrity.test.ts | 12 +- packages/opencode/script/publish.ts | 6 +- 8 files changed, 50 insertions(+), 576 deletions(-) delete mode 100644 packages/dbt-tools/test/build-adversarial.test.ts diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 24fa9a1319..3c8ae0c86b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -286,9 +286,9 @@ jobs: echo "::error::dbt-tools bundle contains hardcoded absolute path in __dirname" exit 1 fi - # Verify __dirname was patched to runtime resolution - if ! grep -q 'import.meta.dirname' packages/dbt-tools/dist/index.js; then - echo "::error::dbt-tools bundle missing import.meta.dirname patch" + # Verify the bridge script resolves relative to the bundle at runtime + if ! grep -q 'fileURLToPath(import.meta.url)' packages/dbt-tools/dist/index.js; then + echo "::error::dbt-tools bundle does not resolve node_python_bridge.py at runtime" exit 1 fi echo "dbt-tools smoke test passed" diff --git a/bun.lock b/bun.lock index 0495f7f8cd..8c5a0bc1ce 100644 --- a/bun.lock +++ b/bun.lock @@ -150,7 +150,7 @@ "altimate-dbt": "./bin/altimate-dbt", }, "dependencies": { - "@altimateai/dbt-integration": "^0.2.2", + "@altimateai/dbt-integration": "^0.3.13", }, "devDependencies": { "@tsconfig/bun": "catalog:", @@ -654,7 +654,7 @@ "@altimateai/altimate-core-win32-x64-msvc": ["@altimateai/altimate-core-win32-x64-msvc@0.7.0", "", { "os": "win32", "cpu": "x64" }, "sha512-GrykrvnilY1yZCgusNxVTvYr5ENkD1vpgjGib2qX/yvJVs0Nb59/JsSLzvA9XqAt2FVf7OcZ5ttH11OkxuFivg=="], - "@altimateai/dbt-integration": ["@altimateai/dbt-integration@0.2.14", "", { "dependencies": { "@altimateai/altimate-core": "0.1.6", "node-abort-controller": "^3.1.1", "node-fetch": "^3.3.2", "python-bridge": "^1.1.0", "semver": "^7.6.3", "yaml": "^2.5.0" }, "peerDependencies": { "patch-package": "^8.0.0" } }, "sha512-44hFBx3gXss2RUlRZE9cIpAocffB3miAzpjvmma1EED0jiOhyrgmzGsw3RXEZqs73OuLYpDehixvRUt1vTfcfQ=="], + "@altimateai/dbt-integration": ["@altimateai/dbt-integration@0.3.13", "", { "dependencies": { "@altimateai/altimate-core": "0.5.0", "node-abort-controller": "^3.1.1", "node-fetch": "^3.3.2", "semver": "^7.6.3", "yaml": "^2.5.0" } }, "sha512-9X7zEQnuEM5QqPOfBk8qTeb4IhsZbEUCaxTsfipLuhrvHQcfEvuUh07scw6VyZg6Dx/OKbpiV0DDpAMcyFkwWg=="], "@altimateai/dbt-tools": ["@altimateai/dbt-tools@workspace:packages/dbt-tools"], @@ -1518,8 +1518,6 @@ "@vercel/oidc": ["@vercel/oidc@3.2.0", "", {}, "sha512-UycprH3T6n3jH0k44NHMa7pnFHGu/N05MjojYr+Mc6I7obkoLIJujSWwin1pCvdy/eOxrI/l3uDLQsmcrOb4ug=="], - "@yarnpkg/lockfile": ["@yarnpkg/lockfile@1.1.0", "", {}, "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ=="], - "@zip.js/zip.js": ["@zip.js/zip.js@2.7.62", "", {}, "sha512-OaLvZ8j4gCkLn048ypkZu29KX30r8/OfFF2w4Jo5WXFr+J04J+lzJ5TKZBVgFXhlvSkqNFQdfnY1Q8TMTCyBVA=="], "abbrev": ["abbrev@4.0.0", "", {}, "sha512-a1wflyaL0tHtJSmLSOVybYhy22vRih4eduhhrkcjgrWGnRfrZtovJ2FRjxuTtkkj47O/baf0R86QU5OuYpz8fA=="], @@ -1634,8 +1632,6 @@ "bl": ["bl@6.1.6", "", { "dependencies": { "@types/readable-stream": "^4.0.0", "buffer": "^6.0.3", "inherits": "^2.0.4", "readable-stream": "^4.2.0" } }, "sha512-jLsPgN/YSvPUg9UX0Kd73CXpm2Psg9FxMeCSXnk3WBO3CMT10JMwijubhGfHCnFu6TPn1ei3b975dxv7K2pWVg=="], - "bluebird": ["bluebird@3.7.2", "", {}, "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg=="], - "bn.js": ["bn.js@4.12.3", "", {}, "sha512-fGTi3gxV/23FTYdAoUtLYp6qySe2KE3teyZitipKNRuVYcBkoP/bB3guXN/XVKUe9mxCHXnc9C4ocyz8OmgN0g=="], "body-parser": ["body-parser@2.2.2", "", { "dependencies": { "bytes": "^3.1.2", "content-type": "^1.0.5", "debug": "^4.4.3", "http-errors": "^2.0.0", "iconv-lite": "^0.7.0", "on-finished": "^2.4.1", "qs": "^6.14.1", "raw-body": "^3.0.1", "type-is": "^2.0.1" } }, "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA=="], @@ -1678,8 +1674,6 @@ "cacache": ["cacache@20.0.4", "", { "dependencies": { "@npmcli/fs": "^5.0.0", "fs-minipass": "^3.0.0", "glob": "^13.0.0", "lru-cache": "^11.1.0", "minipass": "^7.0.3", "minipass-collect": "^2.0.1", "minipass-flush": "^1.0.5", "minipass-pipeline": "^1.2.4", "p-map": "^7.0.2", "ssri": "^13.0.0" } }, "sha512-M3Lab8NPYlZU2exsL3bMVvMrMqgwCnMWfdZbK28bn3pK6APT/Te/I8hjRPNu1uwORY9a1eEQoifXbKPQMfMTOA=="], - "call-bind": ["call-bind@1.0.9", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "get-intrinsic": "^1.3.0", "set-function-length": "^1.2.2" } }, "sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ=="], - "call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="], "call-bound": ["call-bound@1.0.4", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" } }, "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg=="], @@ -1720,9 +1714,9 @@ "color": ["color@5.0.3", "", { "dependencies": { "color-convert": "^3.1.3", "color-string": "^2.1.3" } }, "sha512-ezmVcLR3xAVp8kYOm4GS45ZLLgIE6SPAFoduLr6hTDajwb3KZ2F46gulK3XpcwRFb5KKGCSezCBAY4Dw4HsyXA=="], - "color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], + "color-convert": ["color-convert@3.1.3", "", { "dependencies": { "color-name": "^2.0.0" } }, "sha512-fasDH2ont2GqF5HpyO4w0+BcewlhHEZOFn9c1ckZdHpJ56Qb7MHhH/IcJZbBGgvdtwdwNbLvxiBEdg336iA9Sg=="], - "color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="], + "color-name": ["color-name@2.1.0", "", {}, "sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg=="], "color-string": ["color-string@2.1.4", "", { "dependencies": { "color-name": "^2.0.0" } }, "sha512-Bb6Cq8oq0IjDOe8wJmi4JeNn763Xs9cfrBcaylK1tPypWzyoy2G3l90v9k64kjphl/ZJjPIShFztenRomi8WTg=="], @@ -1788,8 +1782,6 @@ "default-browser-id": ["default-browser-id@5.0.1", "", {}, "sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q=="], - "define-data-property": ["define-data-property@1.1.4", "", { "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", "gopd": "^1.0.1" } }, "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A=="], - "define-lazy-prop": ["define-lazy-prop@3.0.0", "", {}, "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg=="], "defu": ["defu@6.1.7", "", {}, "sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ=="], @@ -1964,8 +1956,6 @@ "find-up": ["find-up@3.0.0", "", { "dependencies": { "locate-path": "^3.0.0" } }, "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg=="], - "find-yarn-workspace-root": ["find-yarn-workspace-root@2.0.0", "", { "dependencies": { "micromatch": "^4.0.2" } }, "sha512-1IMnbjt4KzsQfnhnzNd8wUEgXZ44IzZaZmnLYx7D5FZlaHt2gW20Cri8Q+E/t5tIj4+epTBub+2Zxu/vNILzqQ=="], - "flatbuffers": ["flatbuffers@23.5.26", "", {}, "sha512-vE+SI9vrJDwi1oETtTIFldC/o9GsVKRM+s6EL0nQgxXlYV1Vc4Tk30hj4xGICftInKQKj1F3up2n8UbIVobISQ=="], "fn.name": ["fn.name@1.1.0", "", {}, "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw=="], @@ -1982,8 +1972,6 @@ "fresh": ["fresh@2.0.0", "", {}, "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A=="], - "fs-extra": ["fs-extra@10.1.0", "", { "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ=="], - "fs-minipass": ["fs-minipass@3.0.3", "", { "dependencies": { "minipass": "^7.0.3" } }, "sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw=="], "fs.realpath": ["fs.realpath@1.0.0", "", {}, "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="], @@ -2042,8 +2030,6 @@ "has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="], - "has-property-descriptors": ["has-property-descriptors@1.0.2", "", { "dependencies": { "es-define-property": "^1.0.0" } }, "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg=="], - "has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="], "has-tostringtag": ["has-tostringtag@1.0.2", "", { "dependencies": { "has-symbols": "^1.0.3" } }, "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw=="], @@ -2144,7 +2130,7 @@ "is64bit": ["is64bit@2.0.0", "", { "dependencies": { "system-architecture": "^0.1.0" } }, "sha512-jv+8jaWCl0g2lSBkNSVXdzfBA0npK1HGC2KtWM9FumFRoGS94g3NbCCLVnCYHLjp4GrW2KZeeSTMo5ddtznmGw=="], - "isarray": ["isarray@2.0.5", "", {}, "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="], + "isarray": ["isarray@1.0.0", "", {}, "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="], "isexe": ["isexe@4.0.0", "", {}, "sha512-FFUtZMpoZ8RqHS3XeXEmHWLA4thH+ZxCv2lOiPIn1Xc7CxrqhWzNSDzD+/chS/zbYezmiwWLdQC09JdQKmthOw=="], @@ -2182,8 +2168,6 @@ "json-schema-typed": ["json-schema-typed@8.0.2", "", {}, "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA=="], - "json-stable-stringify": ["json-stable-stringify@1.3.0", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.4", "isarray": "^2.0.5", "jsonify": "^0.0.1", "object-keys": "^1.1.1" } }, "sha512-qtYiSSFlwot9XHtF9bD9c7rwKjr+RecWT//ZnPvSmEjpV5mmPOCN4j8UjY5hbjNkOwZ/jQv3J6R1/pL7RwgMsg=="], - "json-stringify-nice": ["json-stringify-nice@1.1.4", "", {}, "sha512-5Z5RFW63yxReJ7vANgW6eZFGWaQvnPE3WNmZoOJrSkGju2etKA2L5rrOa1sm877TVTFt57A80BH1bArcmlLfPw=="], "json-with-bigint": ["json-with-bigint@3.5.8", "", {}, "sha512-eq/4KP6K34kwa7TcFdtvnftvHCD9KvHOGGICWwMFc4dOOKF5t4iYqnfLK8otCRCRv06FXOzGGyqE8h8ElMvvdw=="], @@ -2192,10 +2176,6 @@ "jsonc-parser": ["jsonc-parser@3.3.1", "", {}, "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ=="], - "jsonfile": ["jsonfile@6.2.1", "", { "dependencies": { "universalify": "^2.0.0" }, "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q=="], - - "jsonify": ["jsonify@0.0.1", "", {}, "sha512-2/Ki0GcmuqSrgFyelQq9M05y7PS0mEwuIzrf3f1fPqkVDVRvZrPZtVSMHxdgo8Aq0sxAOb/cr2aqqA3LeWHVPg=="], - "jsonparse": ["jsonparse@1.3.1", "", {}, "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg=="], "jsonwebtoken": ["jsonwebtoken@9.0.3", "", { "dependencies": { "jws": "^4.0.1", "lodash.includes": "^4.3.0", "lodash.isboolean": "^3.0.3", "lodash.isinteger": "^4.0.4", "lodash.isnumber": "^3.0.3", "lodash.isplainobject": "^4.0.6", "lodash.isstring": "^4.0.1", "lodash.once": "^4.0.0", "ms": "^2.1.1", "semver": "^7.5.4" } }, "sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g=="], @@ -2212,8 +2192,6 @@ "kind-of": ["kind-of@6.0.3", "", {}, "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw=="], - "klaw-sync": ["klaw-sync@6.0.0", "", { "dependencies": { "graceful-fs": "^4.1.11" } }, "sha512-nIeuVSzdCCs6TDPTqI8w1Yre34sSq7AkZ4B3sfOBbI2CgVSB4Du4aLQijFU2+lhAFCwt9+42Hel6lQNIv6AntQ=="], - "kubernetes-types": ["kubernetes-types@1.30.0", "", {}, "sha512-Dew1okvhM/SQcIa2rcgujNndZwU8VnSapDgdxlYoB84ZlpAD43U6KLAFqYo17ykSFGHNPrg0qry0bP+GJd9v7Q=="], "kuler": ["kuler@2.0.0", "", {}, "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A=="], @@ -2402,8 +2380,6 @@ "object-inspect": ["object-inspect@1.13.4", "", {}, "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew=="], - "object-keys": ["object-keys@1.1.1", "", {}, "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA=="], - "ohash": ["ohash@2.0.11", "", {}, "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ=="], "oidc-token-hash": ["oidc-token-hash@5.2.0", "", {}, "sha512-6gj2m8cJZ+iSW8bm0FXdGF0YhIQbKrfP4yWTNzxc31U6MOjfEmB1rHvlYvxI1B7t7BCi1F2vYTT6YhtQRG4hxw=="], @@ -2470,8 +2446,6 @@ "partial-json": ["partial-json@0.1.7", "", {}, "sha512-Njv/59hHaokb/hRUjce3Hdv12wd60MtM9Z5Olmn+nehe0QDAsRtRbJPvJ0Z91TusF0SuZRIvnM+S4l6EIP8leA=="], - "patch-package": ["patch-package@8.0.1", "", { "dependencies": { "@yarnpkg/lockfile": "^1.1.0", "chalk": "^4.1.2", "ci-info": "^3.7.0", "cross-spawn": "^7.0.3", "find-yarn-workspace-root": "^2.0.0", "fs-extra": "^10.0.0", "json-stable-stringify": "^1.0.2", "klaw-sync": "^6.0.0", "minimist": "^1.2.6", "open": "^7.4.2", "semver": "^7.5.3", "slash": "^2.0.0", "tmp": "^0.2.4", "yaml": "^2.2.2" }, "bin": { "patch-package": "index.js" } }, "sha512-VsKRIA8f5uqHQ7NGhwIna6Bx6D9s/1iXlA1hthBVBEbkq+t4kXD0HHt+rJhf/Z+Ci0F/HCB2hvn0qLdLG+Qxlw=="], - "path-exists": ["path-exists@3.0.0", "", {}, "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ=="], "path-expression-matcher": ["path-expression-matcher@1.5.0", "", {}, "sha512-cbrerZV+6rvdQrrD+iGMcZFEiiSrbv9Tfdkvnusy6y0x0GKBXREFg/Y65GhIfm0tnLntThhzCnfKwp1WRjeCyQ=="], @@ -2574,8 +2548,6 @@ "pure-rand": ["pure-rand@8.4.0", "", {}, "sha512-IoM8YF/jY0hiugFo/wOWqfmarlE6J0wc6fDK1PhftMk7MGhVZl88sZimmqBBFomLOCSmcCCpsfj7wXASCpvK9A=="], - "python-bridge": ["python-bridge@1.1.0", "", { "dependencies": { "bluebird": "^3.5.0" } }, "sha512-qjQ0QB8p9cn/XDeILQH0aP307hV58lrmv0Opjyub68Um7FHdF+ZXlTqyxNkKaXOFk2QSkScoPWwn7U9GGnrkeQ=="], - "q": ["q@1.5.1", "", {}, "sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw=="], "qs": ["qs@6.15.2", "", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-Rzq0KEyX/w/tEybncDgdkZrJgVUsUMk3xjh3t5bv3S1HTAtg+uOYt72+ZfwiQwKdysThkTBdL/rTi6HDmX9Ddw=="], @@ -2674,8 +2646,6 @@ "set-cookie-parser": ["set-cookie-parser@2.7.2", "", {}, "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw=="], - "set-function-length": ["set-function-length@1.2.2", "", { "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", "function-bind": "^1.1.2", "get-intrinsic": "^1.2.4", "gopd": "^1.0.1", "has-property-descriptors": "^1.0.2" } }, "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg=="], - "setprototypeof": ["setprototypeof@1.2.0", "", {}, "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="], "shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="], @@ -2700,8 +2670,6 @@ "sisteransi": ["sisteransi@1.0.5", "", {}, "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg=="], - "slash": ["slash@2.0.0", "", {}, "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A=="], - "smart-buffer": ["smart-buffer@4.2.0", "", {}, "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg=="], "snowflake-sdk": ["snowflake-sdk@2.4.3", "", { "dependencies": { "@aws-crypto/sha256-js": "^5.2.0", "@aws-sdk/client-s3": "~3.1051.0", "@aws-sdk/client-sts": "~3.1051.0", "@aws-sdk/credential-provider-node": "~3.972.43", "@aws-sdk/ec2-metadata-service": "~3.1051.0", "@azure/identity": "^4.10.1", "@azure/storage-blob": "12.26.x", "@smithy/node-http-handler": "^4.4.9", "@smithy/protocol-http": "^5.3.8", "@smithy/signature-v4": "^5.3.8", "@techteamer/ocsp": "1.0.1", "asn1.js": "^5.0.0", "asn1.js-rfc2560": "^5.0.0", "asn1.js-rfc5280": "^3.0.0", "axios": "^1.15.1", "big-integer": "^1.6.43", "bignumber.js": "^9.1.2", "expand-tilde": "^2.0.2", "fast-xml-parser": "^5.4.1", "fastest-levenshtein": "^1.0.16", "generic-pool": "^3.8.2", "google-auth-library": "^10.1.0", "https-proxy-agent": "^7.0.2", "jsonwebtoken": "^9.0.3", "mime-types": "^2.1.29", "moment": "^2.29.4", "moment-timezone": "^0.5.15", "oauth4webapi": "^3.0.1", "open": "^7.3.1", "simple-lru-cache": "^0.0.2", "toml": "^3.0.0", "winston": "^3.1.0" } }, "sha512-R9f4CL34RcgAL/8o1iOfbfUk1Dc58eHTKFt9IqVUqnnBdiyu583m81nh7c4tTiB3/TeJGIi1BTRJONV+K5tfxQ=="], @@ -2808,8 +2776,6 @@ "tinyglobby": ["tinyglobby@0.2.17", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.4" } }, "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g=="], - "tmp": ["tmp@0.2.7", "", {}, "sha512-e0votIpp4Uo2AJYSzVHV6xCcawuiez3DzqDAbrTc3YxBkplN6e+dM13ZeIcZnDg/QpSuU2zfZ3rzwY8ukEnaXw=="], - "to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="], "toad-cache": ["toad-cache@3.7.1", "", {}, "sha512-5DXWzE4Vz7xNHsv+xQ+MGfJYyC78Aok3tEr0MNwHoRf7vZnga1mQXZ4/Nsodld4VR6Wd+VhfmqnNrsRJyYPfrQ=="], @@ -2890,8 +2856,6 @@ "universal-user-agent": ["universal-user-agent@7.0.3", "", {}, "sha512-TmnEAEAsBJVZM/AADELsK76llnwcf9vMKuPz8JflO1frO8Lchitr0fNaN9d+Ap0BjKtqWqd/J17qeDnXh8CL2A=="], - "universalify": ["universalify@2.0.1", "", {}, "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw=="], - "unpipe": ["unpipe@1.0.0", "", {}, "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="], "unzip-stream": ["unzip-stream@0.3.4", "", { "dependencies": { "binary": "^0.3.0", "mkdirp": "^0.5.1" } }, "sha512-PyofABPVv+d7fL7GOpusx7eRT9YETY2X04PhwbSipdj6bMxVCFJrr+nm0Mxqbf9hUiTin/UsnuFWBXlDZFy0Cw=="], @@ -3064,7 +3028,7 @@ "@ai-sdk/vercel/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.21", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-MtFUYI1/8mgDvRmaBDjbLJPFFrMG777AvSgyIFQtZHIMzm88R/12vYBBpnk7pfiWLFE1DSZzY4WDYzGbKAcmiw=="], - "@altimateai/dbt-integration/@altimateai/altimate-core": ["@altimateai/altimate-core@0.1.6", "", { "optionalDependencies": { "@altimateai/altimate-core-darwin-arm64": "0.1.6", "@altimateai/altimate-core-darwin-x64": "0.1.6", "@altimateai/altimate-core-linux-arm64-gnu": "0.1.6", "@altimateai/altimate-core-linux-x64-gnu": "0.1.6", "@altimateai/altimate-core-win32-x64-msvc": "0.1.6" } }, "sha512-Kl0hjT88Q56AdGxKJyCcPElxcpZYDYmLhDHK7ZeZIn2oVaXyynExLcIHn+HktUe9USuWtba3tZA/52jJsMyrGg=="], + "@altimateai/dbt-integration/@altimateai/altimate-core": ["@altimateai/altimate-core@0.5.0", "", { "optionalDependencies": { "@altimateai/altimate-core-darwin-arm64": "0.5.0", "@altimateai/altimate-core-darwin-x64": "0.5.0", "@altimateai/altimate-core-linux-arm64-gnu": "0.5.0", "@altimateai/altimate-core-linux-x64-gnu": "0.5.0", "@altimateai/altimate-core-win32-x64-msvc": "0.5.0" } }, "sha512-HxhZ7Q+MhsfyvQjgBEZjtXRc4XBs5WVQ/p6bjOoayl65CLcJ4MnYCjmubPAaCtOX1K3IPB+F7rLnqJMtILEqRA=="], "@altimateai/dbt-integration/yaml": ["yaml@2.9.0", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA=="], @@ -3240,10 +3204,6 @@ "chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], - "color/color-convert": ["color-convert@3.1.3", "", { "dependencies": { "color-name": "^2.0.0" } }, "sha512-fasDH2ont2GqF5HpyO4w0+BcewlhHEZOFn9c1ckZdHpJ56Qb7MHhH/IcJZbBGgvdtwdwNbLvxiBEdg336iA9Sg=="], - - "color-string/color-name": ["color-name@2.1.0", "", {}, "sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg=="], - "command-line-usage/array-back": ["array-back@6.2.3", "", {}, "sha512-SGDvmg6QTYiTxCBkYVmThcoa67uLl35pyzRHdpCGBOcqFy6BtwnphoFPk7LhJshD+Yk1Kt35WGWeZPTgwR4Fhw=="], "command-line-usage/typical": ["typical@7.3.0", "", {}, "sha512-ya4mg/30vm+DOWfBg4YK3j2WD6TWtRkCbasOJr40CseYENzCUby/7rIvXA99JGsQHeNxLbnXdyLLxKSv3tauFw=="], @@ -3342,12 +3302,6 @@ "parse5/entities": ["entities@6.0.1", "", {}, "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g=="], - "patch-package/ci-info": ["ci-info@3.9.0", "", {}, "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ=="], - - "patch-package/open": ["open@7.4.2", "", { "dependencies": { "is-docker": "^2.0.0", "is-wsl": "^2.1.1" } }, "sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q=="], - - "patch-package/yaml": ["yaml@2.9.0", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA=="], - "proxy-addr/ipaddr.js": ["ipaddr.js@1.9.1", "", {}, "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="], "proxy-agent/lru-cache": ["lru-cache@7.18.3", "", {}, "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA=="], @@ -3452,15 +3406,15 @@ "@ai-sdk/vercel/@ai-sdk/provider-utils/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="], - "@altimateai/dbt-integration/@altimateai/altimate-core/@altimateai/altimate-core-darwin-arm64": ["@altimateai/altimate-core-darwin-arm64@0.1.6", "", { "os": "darwin", "cpu": "arm64" }, "sha512-lcndcluAsWMdI0fq2xwgukxFBwoISqKeLWBMjRAhIlCU0qVO+sR/UGUj2FiKZHIWmwgHAou3V5K2fKoYMh9PdQ=="], + "@altimateai/dbt-integration/@altimateai/altimate-core/@altimateai/altimate-core-darwin-arm64": ["@altimateai/altimate-core-darwin-arm64@0.5.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-8UhgkTa/VovAoattlVZOKLnQxN8Yw77BNIzr08KSeKh+0qqJqwb46Ub0I8SbNQAeGOyrmsTr5bmUkUhmkcU8Qw=="], - "@altimateai/dbt-integration/@altimateai/altimate-core/@altimateai/altimate-core-darwin-x64": ["@altimateai/altimate-core-darwin-x64@0.1.6", "", { "os": "darwin", "cpu": "x64" }, "sha512-pYD0Yj/j7SKrU5BY3utPGM4ImoZcV/6yJ7cTFGQXONj3Ikmoo1FdUZR5/CgZE7CCYAa0T9pjOfxB1rLD1B1fxQ=="], + "@altimateai/dbt-integration/@altimateai/altimate-core/@altimateai/altimate-core-darwin-x64": ["@altimateai/altimate-core-darwin-x64@0.5.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-6rXtiu32A9AgJ4fhHv78oxucRz5BjttOIF8S0tBgDDQjHtesyQV5cr84JsZPNXyQCIBHU/dWS3Hgu4NwtjHfQg=="], - "@altimateai/dbt-integration/@altimateai/altimate-core/@altimateai/altimate-core-linux-arm64-gnu": ["@altimateai/altimate-core-linux-arm64-gnu@0.1.6", "", { "os": "linux", "cpu": "arm64" }, "sha512-VDxrk3z30cWJfIudpiGbAz+PFpYB2OE+XSyOPFN+GI7K9NOd5RtC7e9pG50nMF55Wz5R5Yp0ywa/dL+QpUp8SA=="], + "@altimateai/dbt-integration/@altimateai/altimate-core/@altimateai/altimate-core-linux-arm64-gnu": ["@altimateai/altimate-core-linux-arm64-gnu@0.5.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-ebELtWOJVGQeGT64LC5qMp+FLxOao9o2t5uFTa/eMKgDpnfkRb+nkcuQIaeXxUVcTZ4JDeEyAuC8WyrY8ZN81g=="], - "@altimateai/dbt-integration/@altimateai/altimate-core/@altimateai/altimate-core-linux-x64-gnu": ["@altimateai/altimate-core-linux-x64-gnu@0.1.6", "", { "os": "linux", "cpu": "x64" }, "sha512-wXWYNxGhUBBaf2b6dsvuD1ZuiTLQZbKg4/fckrQELuW+BrsN1+nzna+0IZ9FTATYfF1OatpqVun9QyBkvlQn+Q=="], + "@altimateai/dbt-integration/@altimateai/altimate-core/@altimateai/altimate-core-linux-x64-gnu": ["@altimateai/altimate-core-linux-x64-gnu@0.5.0", "", { "os": "linux", "cpu": "x64" }, "sha512-sNLNn1SQnCosbyuSj3QOm3RRmQNoq2pCrEIHPSZf+Ml2l2BFN0zxISgvxdrhx94wZo37ITFWWPar9uL6HP0yBA=="], - "@altimateai/dbt-integration/@altimateai/altimate-core/@altimateai/altimate-core-win32-x64-msvc": ["@altimateai/altimate-core-win32-x64-msvc@0.1.6", "", { "os": "win32", "cpu": "x64" }, "sha512-6Sbneg0DLHMmo1lDVd9oDgGtqPJpDUXZvXwAbGb7eoh+vUmXMxABA43//hBbwkMVsWKClKjv1KXSKp44shrUiw=="], + "@altimateai/dbt-integration/@altimateai/altimate-core/@altimateai/altimate-core-win32-x64-msvc": ["@altimateai/altimate-core-win32-x64-msvc@0.5.0", "", { "os": "win32", "cpu": "x64" }, "sha512-9mf9lF7xDdfRr2XQ1otOTyI5nYpQ9PD1cN49KZdx/hLFO2x1EssWO43YDIoTHDN9T4FZR3CxgOiF6OkQjaz5RA=="], "@aws-crypto/sha1-browser/@smithy/util-utf8/@smithy/util-buffer-from": ["@smithy/util-buffer-from@2.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA=="], @@ -3556,7 +3510,7 @@ "c12/chokidar/readdirp": ["readdirp@5.0.0", "", {}, "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ=="], - "color/color-convert/color-name": ["color-name@2.1.0", "", {}, "sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg=="], + "chalk/ansi-styles/color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], "cross-fetch/node-fetch/whatwg-url": ["whatwg-url@5.0.0", "", { "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" } }, "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw=="], @@ -3568,8 +3522,6 @@ "gauge/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], - "lazystream/readable-stream/isarray": ["isarray@1.0.0", "", {}, "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="], - "lazystream/readable-stream/safe-buffer": ["safe-buffer@5.1.2", "", {}, "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="], "lazystream/readable-stream/string_decoder": ["string_decoder@1.1.1", "", { "dependencies": { "safe-buffer": "~5.1.0" } }, "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg=="], @@ -3624,10 +3576,6 @@ "openid-client/lru-cache/yallist": ["yallist@4.0.0", "", {}, "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="], - "patch-package/open/is-docker": ["is-docker@2.2.1", "", { "bin": { "is-docker": "cli.js" } }, "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ=="], - - "patch-package/open/is-wsl": ["is-wsl@2.2.0", "", { "dependencies": { "is-docker": "^2.0.0" } }, "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww=="], - "readdir-glob/minimatch/brace-expansion": ["brace-expansion@2.1.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA=="], "rimraf/glob/minimatch": ["minimatch@3.1.5", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w=="], @@ -3646,6 +3594,8 @@ "wide-align/string-width/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + "wrap-ansi-cjs/ansi-styles/color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], + "wrap-ansi-cjs/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], "wrap-ansi-cjs/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], @@ -3682,6 +3632,8 @@ "babel-plugin-module-resolver/glob/path-scurry/minipass": ["minipass@7.1.3", "", {}, "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A=="], + "chalk/ansi-styles/color-convert/color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="], + "cross-fetch/node-fetch/whatwg-url/tr46": ["tr46@0.0.3", "", {}, "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="], "cross-fetch/node-fetch/whatwg-url/webidl-conversions": ["webidl-conversions@3.0.1", "", {}, "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="], @@ -3708,6 +3660,8 @@ "wide-align/string-width/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], + "wrap-ansi-cjs/ansi-styles/color-convert/color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="], + "archiver-utils/glob/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], "babel-plugin-module-resolver/glob/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], diff --git a/packages/dbt-tools/package.json b/packages/dbt-tools/package.json index 88801d319b..ebc20646e2 100644 --- a/packages/dbt-tools/package.json +++ b/packages/dbt-tools/package.json @@ -13,7 +13,7 @@ "test:e2e": "bun test test/e2e/ --timeout 300000" }, "dependencies": { - "@altimateai/dbt-integration": "^0.2.2" + "@altimateai/dbt-integration": "^0.3.13" }, "devDependencies": { "@tsconfig/bun": "catalog:", diff --git a/packages/dbt-tools/script/copy-python.ts b/packages/dbt-tools/script/copy-python.ts index 8e93fd27f3..f200c3ae50 100644 --- a/packages/dbt-tools/script/copy-python.ts +++ b/packages/dbt-tools/script/copy-python.ts @@ -1,4 +1,4 @@ -import { cpSync, existsSync, readFileSync, writeFileSync } from "fs" +import { cpSync, existsSync, readFileSync } from "fs" import { dirname, join } from "path" const dist = join(import.meta.dir, "..", "dist") @@ -20,23 +20,16 @@ if (!existsSync(bridgePy)) { cpSync(bridgePy, join(dist, "node_python_bridge.py")) console.log(`Copied node_python_bridge.py → dist/`) -// 3. Fix the hardcoded __dirname that bun bakes at compile time. -// Replace it with a runtime resolution so the bridge script is found -// relative to the built index.js, not the CI runner's node_modules. +// 3. dbt-integration resolves node_python_bridge.py at runtime from its own +// location (`fileURLToPath(import.meta.url)`), which in this bundle is dist/, +// where step 2 put the script. Fail the build if the bundle ever carries a +// path baked in at build time instead. const indexPath = join(dist, "index.js") -let code = readFileSync(indexPath, "utf8") -const pattern = /var __dirname\s*=\s*"[^"]*python-bridge[^"]*"/ -if (pattern.test(code)) { - // import.meta.dirname is supported by Bun and Node >= 20.11.0. - // Fallback via __require handles older runtimes where import.meta.dirname is unavailable. - const replacement = `var __dirname = typeof import.meta.dirname === "string" ? import.meta.dirname : __require("path").dirname(__require("url").fileURLToPath(import.meta.url))` - code = code.replace(pattern, replacement) - writeFileSync(indexPath, code) - console.log(`Patched __dirname in dist/index.js`) -} else { - const found = code.match(/var __dirname[^;]*/)?.[0] ?? "(not found)" - console.error(`ERROR: could not find python-bridge __dirname to patch — the bundle format may have changed`) - console.error(` Pattern: ${pattern}`) - console.error(` Nearest match: ${found}`) +const code = readFileSync(indexPath, "utf8") +const baked = /var __dirname\s*=\s*"(?:[A-Za-z]:\\\\|\/)/ +if (!code.includes("fileURLToPath(import.meta.url)") || !code.includes(`"node_python_bridge.py"`) || baked.test(code)) { + console.error(`ERROR: dist/index.js does not resolve node_python_bridge.py relative to itself at runtime`) + console.error(` Has the @altimateai/dbt-integration bundle format changed?`) process.exit(1) } +console.log(`Verified dist/index.js resolves node_python_bridge.py at runtime`) diff --git a/packages/dbt-tools/src/adapter.ts b/packages/dbt-tools/src/adapter.ts index ac906b8804..5f9939da16 100644 --- a/packages/dbt-tools/src/adapter.ts +++ b/packages/dbt-tools/src/adapter.ts @@ -15,6 +15,8 @@ import { GraphParser, SourceParser, TestParser, + UnitTestParser, + SemanticModelParser, ExposureParser, FunctionParser, DocParser, @@ -181,7 +183,6 @@ export async function create(cfg: Config): Promise diag, defer, changed, - cloudVariantDetector, ) const adapter = new DBTProjectIntegrationAdapter( @@ -200,11 +201,13 @@ export async function create(cfg: Config): Promise new GraphParser(term), new SourceParser(term), new TestParser(term), + new UnitTestParser(term), new ExposureParser(term), new FunctionParser(term), new DocParser(term), term, new ModelDepthParser(term, client, config), + new SemanticModelParser(term), ) await adapter.initialize() diff --git a/packages/dbt-tools/test/build-adversarial.test.ts b/packages/dbt-tools/test/build-adversarial.test.ts deleted file mode 100644 index c5cfe343d8..0000000000 --- a/packages/dbt-tools/test/build-adversarial.test.ts +++ /dev/null @@ -1,484 +0,0 @@ -import { describe, test, expect, beforeAll } from "bun:test" -import { readFileSync, writeFileSync, mkdtempSync, cpSync, existsSync } from "fs" -import { join, resolve } from "path" -import { tmpdir } from "os" -import { $ } from "bun" - -const dist = join(import.meta.dir, "../dist") -const scriptDir = join(import.meta.dir, "../script") - -// ─── Helpers ───────────────────────────────────────────────────────── -// Simulate what copy-python.ts does against arbitrary bundle content, -// without touching the real dist/index.js. -function runPatchLogic(bundleContent: string): { patched: boolean; output: string } { - const pattern = /var __dirname\s*=\s*"[^"]*python-bridge[^"]*"/ - if (pattern.test(bundleContent)) { - const replacement = `var __dirname = typeof import.meta.dirname === "string" ? import.meta.dirname : __require("path").dirname(__require("url").fileURLToPath(import.meta.url))` - return { patched: true, output: bundleContent.replace(pattern, replacement) } - } - return { patched: false, output: bundleContent } -} - -// ─── Adversarial: Regex edge cases ────────────────────────────────── -describe("adversarial: patch regex", () => { - test("matches typical CI runner path (Linux)", () => { - const bundle = `var __dirname = "/home/runner/work/altimate-code/node_modules/.bun/python-bridge@1.1.0/node_modules/python-bridge"` - const result = runPatchLogic(bundle) - expect(result.patched).toBe(true) - expect(result.output).toContain("import.meta.dirname") - expect(result.output).not.toContain("/home/runner") - }) - - test("matches macOS dev path", () => { - const bundle = `var __dirname = "/Users/dev/code/node_modules/python-bridge"` - const result = runPatchLogic(bundle) - expect(result.patched).toBe(true) - expect(result.output).not.toContain("/Users/dev") - }) - - test("matches Windows CI path", () => { - const bundle = `var __dirname = "C:\\Users\\runneradmin\\work\\node_modules\\python-bridge"` - const result = runPatchLogic(bundle) - expect(result.patched).toBe(true) - expect(result.output).not.toContain("C:\\Users") - }) - - test("matches path with @scope in node_modules", () => { - const bundle = `var __dirname = "/opt/ci/node_modules/.bun/python-bridge@2.0.0/node_modules/python-bridge"` - const result = runPatchLogic(bundle) - expect(result.patched).toBe(true) - }) - - test("does NOT match unrelated __dirname (no python-bridge)", () => { - const bundle = `var __dirname = "/home/runner/work/some-other-module"` - const result = runPatchLogic(bundle) - expect(result.patched).toBe(false) - // The original hardcoded path is preserved — this is CORRECT behavior. - // The patch should only touch the python-bridge dirname. - }) - - test("does NOT match __dirname that's already patched", () => { - const bundle = `var __dirname = typeof import.meta.dirname === "string" ? import.meta.dirname : __require("path").dirname(__require("url").fileURLToPath(import.meta.url))` - const result = runPatchLogic(bundle) - expect(result.patched).toBe(false) - }) - - test("handles extra whitespace in assignment", () => { - const bundle = `var __dirname = "/some/path/to/python-bridge"` - const result = runPatchLogic(bundle) - expect(result.patched).toBe(true) - }) - - test("only patches FIRST match (non-global)", () => { - const bundle = [`var __dirname = "/first/path/python-bridge"`, `var __dirname = "/second/path/python-bridge"`].join( - "\n", - ) - const result = runPatchLogic(bundle) - expect(result.patched).toBe(true) - // First one is patched - expect(result.output).toContain("import.meta.dirname") - // Second one survives — this is a known limitation - expect(result.output).toContain("/second/path/python-bridge") - }) - - test("does NOT match if quotes are single quotes", () => { - const bundle = `var __dirname = '/home/runner/python-bridge'` - const result = runPatchLogic(bundle) - // Bun uses double quotes, so single-quote paths should not match - expect(result.patched).toBe(false) - }) - - test("does NOT match let or const declarations", () => { - const bundle = `const __dirname = "/home/runner/python-bridge"` - const result = runPatchLogic(bundle) - // Pattern specifically matches `var __dirname` — const/let won't match - expect(result.patched).toBe(false) - }) -}) - -// ─── Adversarial: Built output invariants ─────────────────────────── -describe("adversarial: built bundle invariants", () => { - beforeAll(async () => { - // Ensure we have a fresh build - if (!existsSync(join(dist, "index.js"))) { - await $`bun run build`.cwd(join(import.meta.dir, "..")) - } - }) - - test("exactly ONE __dirname assignment exists in bundle", () => { - const code = readFileSync(join(dist, "index.js"), "utf8") - const matches = code.match(/var __dirname\s*=/g) - expect(matches).not.toBeNull() - expect(matches!.length).toBe(1) - }) - - test("__dirname is used to resolve PYTHON_BRIDGE_SCRIPT", () => { - const code = readFileSync(join(dist, "index.js"), "utf8") - expect(code).toContain('path.join(__dirname, "node_python_bridge.py")') - }) - - test("no CI runner paths leaked anywhere in bundle", () => { - const code = readFileSync(join(dist, "index.js"), "utf8") - // Common CI runner path prefixes - expect(code).not.toContain("/home/runner/work/") - expect(code).not.toContain("/github/workspace/") - expect(code).not.toContain("D:\\a\\altimate-code\\") - }) - - test("patched __dirname includes both primary and fallback paths", () => { - const code = readFileSync(join(dist, "index.js"), "utf8") - const line = code.split("\n").find((l) => l.includes("var __dirname")) - expect(line).toBeDefined() - // Primary: import.meta.dirname - expect(line).toContain("import.meta.dirname") - // Fallback: fileURLToPath - expect(line).toContain("fileURLToPath") - // Uses __require (Bun's bundled require) - expect(line).toContain("__require") - }) - - test("node_python_bridge.py in dist matches the source", () => { - const resolved = require.resolve("@altimateai/dbt-integration") - const sourcePy = join(require("path").dirname(resolved), "node_python_bridge.py") - if (!existsSync(sourcePy)) return // skip if source not available - - const source = readFileSync(sourcePy, "utf8") - const copied = readFileSync(join(dist, "node_python_bridge.py"), "utf8") - expect(copied).toBe(source) - }) - - test("altimate_python_packages directory was copied", () => { - expect(existsSync(join(dist, "altimate_python_packages"))).toBe(true) - }) -}) - -// ─── Adversarial: Runtime resolution simulation ───────────────────── -describe("adversarial: runtime resolution", () => { - test("patched __dirname evaluates to a real directory at runtime", () => { - // import.meta.dirname should resolve to THIS test file's directory - // In the real bundle, it would resolve to dist/ - const dirname = import.meta.dir - expect(typeof dirname).toBe("string") - expect(dirname.length).toBeGreaterThan(0) - // Validate it's an actual directory, not a stale/hardcoded path - expect(existsSync(dirname)).toBe(true) - }) - - test("node_python_bridge.py is findable relative to dist/index.js", () => { - // This is the critical runtime check: __dirname should be dist/, - // and node_python_bridge.py should be in dist/ - const bridgePath = join(dist, "node_python_bridge.py") - expect(existsSync(bridgePath)).toBe(true) - - // Verify it's actually a Python file, not garbage - const content = readFileSync(bridgePath, "utf8") - expect(content).toContain("import") // Python imports - expect(content.length).toBeGreaterThan(100) // not truncated - }) - - test("bundle can be loaded from a DIFFERENT directory without path errors", async () => { - // Simulate running the binary from /tmp — __dirname should still - // resolve to where index.js lives, not the CWD - const tmpDir = mkdtempSync(join(tmpdir(), "adversarial-")) - const originalCwd = process.cwd() - - try { - process.chdir(tmpDir) - - // The patched code uses import.meta.dirname which is compile-time - // resolved to the file's actual location, not CWD. Verify this. - const indexPath = join(dist, "index.js") - expect(existsSync(indexPath)).toBe(true) - - // Read the bundle and verify the __dirname line doesn't reference CWD - const code = readFileSync(indexPath, "utf8") - expect(code).not.toContain(tmpDir) - } finally { - process.chdir(originalCwd) - } - }) -}) - -// ─── Adversarial: Double-patch protection ─────────────────────────── -describe("adversarial: idempotency", () => { - test("running the patch twice does not corrupt the bundle", () => { - const original = `var __dirname = "/ci/path/python-bridge"` - - // First patch - const first = runPatchLogic(original) - expect(first.patched).toBe(true) - - // Second patch on already-patched output — should be a no-op - const second = runPatchLogic(first.output) - expect(second.patched).toBe(false) - expect(second.output).toBe(first.output) - }) - - test("patch output is syntactically valid JS", () => { - const bundle = `var __dirname = "/home/runner/python-bridge";` - const result = runPatchLogic(bundle) - - // The patched code should not have unmatched quotes or parens - const openParens = (result.output.match(/\(/g) || []).length - const closeParens = (result.output.match(/\)/g) || []).length - expect(openParens).toBe(closeParens) - - const openQuotes = (result.output.match(/"/g) || []).length - expect(openQuotes % 2).toBe(0) // even number of quotes - }) -}) - -// ─── Adversarial: CI smoke test alignment ─────────────────────────── -describe("adversarial: CI smoke test parity", () => { - test("CI regex catches what build-integrity test catches", () => { - // CI uses: grep -qE 'var __dirname\\s*=\\s*"(/|[A-Za-z]:\\\\)' (shell regex) - // Test uses: /var __dirname\s*=\s*"(?:[A-Za-z]:\\\\|\/)/ - // Both should flag the same hardcoded paths - - const linuxPath = `var __dirname = "/home/runner/python-bridge"` - const windowsPath = `var __dirname = "C:\\\\Users\\\\runner\\\\python-bridge"` - const patchedPath = `var __dirname = typeof import.meta.dirname === "string" ? import.meta.dirname : __require("path").dirname(__require("url").fileURLToPath(import.meta.url))` - - const ciRegex = /var __dirname\s*=\s*"(\/|[A-Za-z]:\\)/ - const testRegex = /var __dirname\s*=\s*"(?:[A-Za-z]:\\\\|\/)/ - - // Both should flag hardcoded paths - expect(ciRegex.test(linuxPath)).toBe(true) - expect(testRegex.test(linuxPath)).toBe(true) - - expect(ciRegex.test(windowsPath)).toBe(true) - expect(testRegex.test(windowsPath)).toBe(true) - - // Neither should flag the patched version - expect(ciRegex.test(patchedPath)).toBe(false) - expect(testRegex.test(patchedPath)).toBe(false) - }) -}) - -// ─── Adversarial: copy-python.ts script execution ─────────────────── -describe("adversarial: copy-python.ts as a real script", () => { - test("build script exits 0 on success", async () => { - const result = await $`bun run build`.cwd(join(import.meta.dir, "..")).nothrow() - expect(result.exitCode).toBe(0) - expect(result.stderr.toString()).not.toContain("ERROR") - }) - - test("build script prints all three progress lines", async () => { - const result = await $`bun run build`.cwd(join(import.meta.dir, "..")).nothrow() - const output = result.stderr.toString() + result.stdout.toString() - expect(output).toContain("Copied altimate_python_packages") - expect(output).toContain("Copied node_python_bridge.py") - expect(output).toContain("Patched __dirname") - }) - - test("consecutive builds produce identical dist/index.js", async () => { - await $`bun run build`.cwd(join(import.meta.dir, "..")) - const first = readFileSync(join(dist, "index.js"), "utf8") - - await $`bun run build`.cwd(join(import.meta.dir, "..")) - const second = readFileSync(join(dist, "index.js"), "utf8") - - expect(first).toBe(second) - }) -}) - -// ─── Adversarial: bundle runtime structure ────────────────────────── -describe("adversarial: bundle runtime structure", () => { - beforeAll(async () => { - if (!existsSync(join(dist, "index.js"))) { - await $`bun run build`.cwd(join(import.meta.dir, "..")) - } - }) - - test("__require is defined via createRequire (not a Bun-only global)", () => { - const code = readFileSync(join(dist, "index.js"), "utf8") - // __require must be created from Node's standard createRequire - expect(code).toContain('import { createRequire } from "node:module"') - expect(code).toMatch(/var __require\s*=.*createRequire\(import\.meta\.url\)/) - }) - - test("__dirname lives inside __commonJS wrapper (correct scope)", () => { - const code = readFileSync(join(dist, "index.js"), "utf8") - // Find the python-bridge module wrapper - const bridgeStart = code.indexOf("// ../../node_modules/.bun/python-bridge") - expect(bridgeStart).toBeGreaterThan(-1) - - const dirnamePos = code.indexOf("var __dirname", bridgeStart) - expect(dirnamePos).toBeGreaterThan(bridgeStart) - - // __dirname should come BEFORE PYTHON_BRIDGE_SCRIPT in the same scope - const bridgeScriptPos = code.indexOf("PYTHON_BRIDGE_SCRIPT", dirnamePos) - expect(bridgeScriptPos).toBeGreaterThan(dirnamePos) - }) - - test("PYTHON_BRIDGE_SCRIPT resolves node_python_bridge.py via __dirname", () => { - const code = readFileSync(join(dist, "index.js"), "utf8") - // The bridge script path must use __dirname, not a hardcoded path - expect(code).toMatch(/PYTHON_BRIDGE_SCRIPT\s*=\s*path\.join\(__dirname,\s*"node_python_bridge\.py"\)/) - }) - - test("python bridge spawn uses PYTHON_BRIDGE_SCRIPT variable", () => { - const code = readFileSync(join(dist, "index.js"), "utf8") - // The spawn call must use the variable, not an inline path - expect(code).toContain("spawn(intepreter, [PYTHON_BRIDGE_SCRIPT]") - }) - - test("no other hardcoded python-bridge paths survive in bundle", () => { - const code = readFileSync(join(dist, "index.js"), "utf8") - // After patching, the only python-bridge references should be: - // 1. The comment line (// ../../node_modules/.bun/python-bridge@...) - // 2. The require_python_bridge function name - // 3. String literals for error messages - // There should be NO hardcoded filesystem paths to python-bridge - const lines = code.split("\n") - for (const line of lines) { - if (line.includes("python-bridge") && !line.trimStart().startsWith("//")) { - // This line references python-bridge — it must NOT be a hardcoded path - expect(line).not.toMatch(/["'](\/|[A-Z]:\\)[^"']*python-bridge/) - } - } - }) -}) - -// ─── Adversarial: publish pipeline ────────────────────────────────── -describe("adversarial: publish.ts copies patched artifacts", () => { - beforeAll(async () => { - if (!existsSync(join(dist, "index.js"))) { - await $`bun run build`.cwd(join(import.meta.dir, "..")) - } - }) - - test("dist/index.js is patched BEFORE publish.ts copies it", () => { - // publish.ts calls `bun run build` on dbt-tools, then copies dist/index.js. - // If copy-python.ts runs as part of build, the copied file must be patched. - const code = readFileSync(join(dist, "index.js"), "utf8") - expect(code).toContain("import.meta.dirname") - expect(code).not.toMatch(/var __dirname\s*=\s*"(?:[A-Za-z]:\\\\|\/)/) - }) - - test("dist/node_python_bridge.py is non-empty and valid Python", () => { - const py = readFileSync(join(dist, "node_python_bridge.py"), "utf8") - expect(py.length).toBeGreaterThan(500) // real file, not a stub - // Must contain the IPC message handling that the JS bridge talks to - expect(py).toContain("def") - // Must handle the JSON-RPC protocol - expect(py).toMatch(/json|JSON/) - }) -}) - -// ─── Adversarial: mutation testing (what if the fix is removed?) ───── -describe("adversarial: mutation testing", () => { - test("unpatched bundle WOULD contain a hardcoded absolute path", () => { - // Build raw bundle WITHOUT running copy-python.ts - // We simulate this by checking what bun build alone produces - const code = readFileSync(join(dist, "index.js"), "utf8") - - // The patched line should exist — if we remove the patch, the hardcoded - // path would return. Verify the patch is structurally present. - const patchedLine = code.split("\n").find((l) => l.includes("var __dirname") && l.includes("import.meta.dirname")) - expect(patchedLine).toBeDefined() - - // The patched line must have the ternary structure - expect(patchedLine).toContain("typeof import.meta.dirname") - expect(patchedLine).toContain("?") - expect(patchedLine).toContain(":") - expect(patchedLine).toContain("__require") - }) - - test("removing import.meta.dirname from replacement would break detection", () => { - // The CI smoke test and build-integrity test both look for "import.meta.dirname". - // If someone changes the replacement string to not include it, both guards catch it. - const brokenReplacement = `var __dirname = __require("path").dirname(__require("url").fileURLToPath(import.meta.url))` - // build-integrity.test.ts check: - expect(brokenReplacement).not.toContain("import.meta.dirname") - // This WOULD fail the integrity test — proving the guard works - }) - - test("removing the existence check would crash on missing bridge file", () => { - // Verify the existence check is actually in the script - const script = readFileSync(join(scriptDir, "copy-python.ts"), "utf8") - expect(script).toContain("existsSync(bridgePy)") - expect(script).toContain("process.exit(1)") - }) -}) - -// ─── Adversarial: regex catastrophic backtracking ─────────────────── -describe("adversarial: regex performance", () => { - test("patch regex does not catastrophically backtrack on large input", () => { - // Craft a pathological input that could cause ReDoS with a bad regex - const huge = `var __dirname = "${"a".repeat(100_000)}"` - const start = performance.now() - runPatchLogic(huge) - const elapsed = performance.now() - start - // Must complete in under 100ms even on 100KB input - expect(elapsed).toBeLessThan(100) - }) - - test("patch regex handles bundle with many var declarations", () => { - // Simulate a large bundle with thousands of var declarations - const lines = Array.from({ length: 10_000 }, (_, i) => `var x${i} = "${i}";`) - lines.push(`var __dirname = "/ci/path/python-bridge"`) - lines.push(`var PYTHON_BRIDGE_SCRIPT = path.join(__dirname, "node_python_bridge.py");`) - const bundle = lines.join("\n") - - const start = performance.now() - const result = runPatchLogic(bundle) - const elapsed = performance.now() - start - - expect(result.patched).toBe(true) - expect(elapsed).toBeLessThan(200) // reasonable for 10K lines - }) -}) - -// ─── Adversarial: path injection / malformed paths ────────────────── -describe("adversarial: malformed inputs", () => { - test("regex handles path with special regex characters", () => { - // Paths with characters that are special in regex: . + * ? [ ] ( ) - const bundle = `var __dirname = "/home/runner/work/node_modules/.bun/python-bridge@1.1.0+build.123/node_modules/python-bridge"` - const result = runPatchLogic(bundle) - expect(result.patched).toBe(true) - }) - - test("regex handles path with unicode characters", () => { - const bundle = `var __dirname = "/home/用户/项目/node_modules/python-bridge"` - const result = runPatchLogic(bundle) - expect(result.patched).toBe(true) - }) - - test("regex handles path with spaces", () => { - const bundle = `var __dirname = "/home/runner/my project/node_modules/python-bridge"` - const result = runPatchLogic(bundle) - expect(result.patched).toBe(true) - }) - - test("regex handles empty path with python-bridge", () => { - const bundle = `var __dirname = "python-bridge"` - const result = runPatchLogic(bundle) - expect(result.patched).toBe(true) - }) - - test("patch does NOT corrupt surrounding code", () => { - const before = `console.log("before");\n` - const target = `var __dirname = "/ci/python-bridge";\n` - const after = `var SCRIPT = path.join(__dirname, "bridge.py");\n` - const bundle = before + target + after - - const result = runPatchLogic(bundle) - // Before and after lines must survive untouched - expect(result.output).toContain(`console.log("before");`) - expect(result.output).toContain(`var SCRIPT = path.join(__dirname, "bridge.py");`) - // Target line is replaced - expect(result.output).toContain("import.meta.dirname") - expect(result.output).not.toContain("/ci/python-bridge") - }) - - test("patch preserves semicolons and line structure", () => { - const bundle = `var __dirname = "/ci/python-bridge";` - const result = runPatchLogic(bundle) - // The replacement should end where the original ended - // Original: var __dirname = "..."; → var __dirname = typeof ...; - // The semicolon after the closing quote is NOT part of the match, - // so it should survive - expect(result.output).toMatch(/\);$/) - }) -}) diff --git a/packages/dbt-tools/test/build-integrity.test.ts b/packages/dbt-tools/test/build-integrity.test.ts index f2bb589a09..9997193cbd 100644 --- a/packages/dbt-tools/test/build-integrity.test.ts +++ b/packages/dbt-tools/test/build-integrity.test.ts @@ -1,6 +1,6 @@ import { describe, test, expect, beforeAll } from "bun:test" import { existsSync, readFileSync } from "fs" -import { join } from "path" +import { dirname, join } from "path" import { $ } from "bun" const dist = join(import.meta.dir, "../dist") @@ -21,8 +21,14 @@ describe("build integrity", () => { expect(code).not.toMatch(/var __dirname\s*=\s*"(?:[A-Za-z]:\\\\|\/)/) }) - test("__dirname is patched to runtime resolution", () => { + test("the bridge script resolves relative to the bundle at runtime", () => { const code = readFileSync(join(dist, "index.js"), "utf8") - expect(code).toContain("import.meta.dirname") + expect(code).toContain("fileURLToPath(import.meta.url)") + expect(code).toContain(`"node_python_bridge.py"`) + }) + + test("node_python_bridge.py in dist is dbt-integration's copy", () => { + const shipped = join(dirname(require.resolve("@altimateai/dbt-integration")), "node_python_bridge.py") + expect(readFileSync(join(dist, "node_python_bridge.py"), "utf8")).toBe(readFileSync(shipped, "utf8")) }) }) diff --git a/packages/opencode/script/publish.ts b/packages/opencode/script/publish.ts index a5e3830959..7b251b90d7 100755 --- a/packages/opencode/script/publish.ts +++ b/packages/opencode/script/publish.ts @@ -73,8 +73,10 @@ async function copyAssets(targetDir: string) { await $`cp ../dbt-tools/bin/altimate-dbt ${targetDir}/dbt-tools/bin/altimate-dbt` await $`mkdir -p ${targetDir}/dbt-tools/dist` await $`cp ../dbt-tools/dist/index.js ${targetDir}/dbt-tools/dist/` - // node_python_bridge.py must live next to index.js — the patched __dirname - // resolves to this directory at runtime (see copy-python.ts) + // altimate_change start — dbt-integration's vendored bridge finds its script at runtime + // node_python_bridge.py must live next to index.js — dbt-integration resolves + // it from the bundle's own location at runtime (see copy-python.ts) + // altimate_change end await $`cp ../dbt-tools/dist/node_python_bridge.py ${targetDir}/dbt-tools/dist/` // A package.json with "type": "module" must be present so Node loads // dist/index.js as ESM instead of CJS. We synthesize a minimal one rather From 4c7ea0e95096684c4dbe31b6eaa0010a9edab8c9 Mon Sep 17 00:00:00 2001 From: "Ralph Sto. Domingo" Date: Tue, 29 Sep 2026 12:11:44 +0800 Subject: [PATCH 2/8] test(dbt-tools): keep the build checks that still apply, and run the published package from elsewhere Restores the checks from the removed patch tests that do not depend on the patch: no build-machine path anywhere in the bundle, altimate_python_packages copied, __require from createRequire, and byte-identical consecutive builds. Adds a runtime check the old suite only approximated: the package laid out as publish.ts ships it, run by Node from another directory, starts the bridge script next to its own bundle. A stand-in python records the spawn, so the test needs neither Python nor dbt. Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_0172qrhMa5TQgETASi5hxMqD --- .../dbt-tools/test/build-integrity.test.ts | 93 ++++++++++++++++++- 1 file changed, 90 insertions(+), 3 deletions(-) diff --git a/packages/dbt-tools/test/build-integrity.test.ts b/packages/dbt-tools/test/build-integrity.test.ts index 9997193cbd..6e866fa73d 100644 --- a/packages/dbt-tools/test/build-integrity.test.ts +++ b/packages/dbt-tools/test/build-integrity.test.ts @@ -1,14 +1,17 @@ import { describe, test, expect, beforeAll } from "bun:test" -import { existsSync, readFileSync } from "fs" +import { chmodSync, cpSync, existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "fs" +import { tmpdir } from "os" import { dirname, join } from "path" import { $ } from "bun" -const dist = join(import.meta.dir, "../dist") +const pkg = join(import.meta.dir, "..") +const dist = join(pkg, "dist") +const checkout = join(pkg, "../..") describe("build integrity", () => { beforeAll(async () => { // Rebuild to test the actual build output - await $`bun run build`.cwd(join(import.meta.dir, "..")) + await $`bun run build`.cwd(pkg) }) test("node_python_bridge.py exists in dist", () => { @@ -31,4 +34,88 @@ describe("build integrity", () => { const shipped = join(dirname(require.resolve("@altimateai/dbt-integration")), "node_python_bridge.py") expect(readFileSync(join(dist, "node_python_bridge.py"), "utf8")).toBe(readFileSync(shipped, "utf8")) }) + + test("no build-machine path appears anywhere in the bundle", () => { + const code = readFileSync(join(dist, "index.js"), "utf8") + expect(code).not.toContain(checkout) + // CI runner checkouts on Linux, containers and Windows + expect(code).not.toContain("/home/runner/work/") + expect(code).not.toContain("/github/workspace/") + expect(code).not.toContain("D:\\a\\altimate-code\\") + }) + + test("altimate_python_packages is copied next to the bundle", () => { + expect(existsSync(join(dist, "altimate_python_packages"))).toBe(true) + }) + + test("__require comes from createRequire, so the bundle runs under Node", () => { + const code = readFileSync(join(dist, "index.js"), "utf8") + expect(code).toContain('import { createRequire } from "node:module"') + expect(code).toMatch(/var __require\s*=.*createRequire\(import\.meta\.url\)/) + }) + + // The published package (see opencode/script/publish.ts copyAssets) is run by + // Node from wherever npm installed it. A stand-in `python` records what it is + // asked to run and answers the CLI's prerequisite checks, so this follows the + // real startup as far as spawning the bridge, without Python or dbt installed. + test.skipIf(process.platform === "win32")( + "the published package starts the bridge script next to its own bundle, from any directory", + async () => { + const root = mkdtempSync(join(tmpdir(), "dbt-tools-published-")) + try { + const installed = join(root, "install", "dbt-tools") + mkdirSync(join(installed, "bin"), { recursive: true }) + mkdirSync(join(installed, "dist"), { recursive: true }) + cpSync(join(pkg, "bin/altimate-dbt"), join(installed, "bin/altimate-dbt")) + cpSync(join(dist, "index.js"), join(installed, "dist/index.js")) + cpSync(join(dist, "node_python_bridge.py"), join(installed, "dist/node_python_bridge.py")) + writeFileSync(join(installed, "package.json"), JSON.stringify({ type: "module" })) + + const argvLog = join(root, "argv.log") + const python = join(root, "python") + writeFileSync( + python, + [ + "#!/bin/sh", + `printf '%s\\n' "$*" >> '${argvLog}'`, + 'case "$1" in', + ' --version) echo "Python 3.11.9"; exit 0 ;;', + ' -c) echo "1.10.0"; exit 0 ;;', + "esac", + "exit 3", + ].join("\n") + "\n", + ) + chmodSync(python, 0o755) + const project = join(root, "project") + mkdirSync(project) + writeFileSync(join(project, "dbt_project.yml"), "name: p\nversion: '1.0'\nprofile: p\n") + const home = join(root, "home") + mkdirSync(join(home, ".altimate-code"), { recursive: true }) + writeFileSync( + join(home, ".altimate-code", "dbt.json"), + JSON.stringify({ projectRoot: project, pythonPath: python, dbtIntegration: "corecommand", queryLimit: 500 }), + ) + + await $`node ${join(installed, "bin/altimate-dbt")} info` + .cwd(root) + .env({ ...process.env, HOME: home }) + .quiet() + .nothrow() + + const script = join(installed, "dist", "node_python_bridge.py") + const runs = readFileSync(argvLog, "utf8").split("\n") + expect(runs).toContain(script) + expect(existsSync(script)).toBe(true) + } finally { + rmSync(root, { recursive: true, force: true }) + } + }, + ) + + // Last: it rebuilds dist, which the tests above read. + test("consecutive builds produce an identical bundle", async () => { + const first = readFileSync(join(dist, "index.js"), "utf8") + await $`bun run build`.cwd(pkg).quiet() + expect(readFileSync(join(dist, "index.js"), "utf8")).toBe(first) + }) }) From ccc92cc43124a9459a3bec32932531e77771263c Mon Sep 17 00:00:00 2001 From: "Ralph Sto. Domingo" Date: Wed, 14 Oct 2026 21:14:21 +0800 Subject: [PATCH 3/8] fix(dbt-tools): require dbt-integration ^0.3.15 so the compiled binary starts the bridge 0.3.15 writes the bridge script to a temporary directory when it runs from a single-file executable, instead of looking for it in `/$bunfs`. The lockfile pins 0.3.15; Bun applies `minimumReleaseAge` only when it resolves a version. Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_0172qrhMa5TQgETASi5hxMqD --- bun.lock | 4 ++-- packages/dbt-tools/package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/bun.lock b/bun.lock index 8c5a0bc1ce..66ab0e520f 100644 --- a/bun.lock +++ b/bun.lock @@ -150,7 +150,7 @@ "altimate-dbt": "./bin/altimate-dbt", }, "dependencies": { - "@altimateai/dbt-integration": "^0.3.13", + "@altimateai/dbt-integration": "^0.3.15", }, "devDependencies": { "@tsconfig/bun": "catalog:", @@ -654,7 +654,7 @@ "@altimateai/altimate-core-win32-x64-msvc": ["@altimateai/altimate-core-win32-x64-msvc@0.7.0", "", { "os": "win32", "cpu": "x64" }, "sha512-GrykrvnilY1yZCgusNxVTvYr5ENkD1vpgjGib2qX/yvJVs0Nb59/JsSLzvA9XqAt2FVf7OcZ5ttH11OkxuFivg=="], - "@altimateai/dbt-integration": ["@altimateai/dbt-integration@0.3.13", "", { "dependencies": { "@altimateai/altimate-core": "0.5.0", "node-abort-controller": "^3.1.1", "node-fetch": "^3.3.2", "semver": "^7.6.3", "yaml": "^2.5.0" } }, "sha512-9X7zEQnuEM5QqPOfBk8qTeb4IhsZbEUCaxTsfipLuhrvHQcfEvuUh07scw6VyZg6Dx/OKbpiV0DDpAMcyFkwWg=="], + "@altimateai/dbt-integration": ["@altimateai/dbt-integration@0.3.15", "", { "dependencies": { "@altimateai/altimate-core": "0.5.0", "node-abort-controller": "^3.1.1", "node-fetch": "^3.3.2", "semver": "^7.6.3", "yaml": "^2.5.0" } }, "sha512-E+k0yyvxxcoobWA0wViFZJ/hzQI7LivmhxuzeIOJ6FlAl29/WAei6MbfKhzIH6HK4fZkKB3eMRaMEiTksTFXAA=="], "@altimateai/dbt-tools": ["@altimateai/dbt-tools@workspace:packages/dbt-tools"], diff --git a/packages/dbt-tools/package.json b/packages/dbt-tools/package.json index ebc20646e2..9e33d9fe16 100644 --- a/packages/dbt-tools/package.json +++ b/packages/dbt-tools/package.json @@ -13,7 +13,7 @@ "test:e2e": "bun test test/e2e/ --timeout 300000" }, "dependencies": { - "@altimateai/dbt-integration": "^0.3.13" + "@altimateai/dbt-integration": "^0.3.15" }, "devDependencies": { "@tsconfig/bun": "catalog:", From 97f6c2c4fd83c9c2a85012ee77783cf9bae162ff Mon Sep 17 00:00:00 2001 From: "Ralph Sto. Domingo" Date: Thu, 15 Oct 2026 13:10:51 +0800 Subject: [PATCH 4/8] test(dbt-tools): compare the bridge path against the install dir's real path Node loads the published bundle by its real path. On macOS `tmpdir()` is under the `/var` symlink, so the published-package test expected `/var/...` while the bridge ran from `/private/var/...`. Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_0172qrhMa5TQgETASi5hxMqD --- packages/dbt-tools/test/build-integrity.test.ts | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/packages/dbt-tools/test/build-integrity.test.ts b/packages/dbt-tools/test/build-integrity.test.ts index 6e866fa73d..5744f20b2f 100644 --- a/packages/dbt-tools/test/build-integrity.test.ts +++ b/packages/dbt-tools/test/build-integrity.test.ts @@ -1,5 +1,15 @@ import { describe, test, expect, beforeAll } from "bun:test" -import { chmodSync, cpSync, existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "fs" +import { + chmodSync, + cpSync, + existsSync, + mkdirSync, + mkdtempSync, + readFileSync, + realpathSync, + rmSync, + writeFileSync, +} from "fs" import { tmpdir } from "os" import { dirname, join } from "path" import { $ } from "bun" @@ -102,7 +112,8 @@ describe("build integrity", () => { .quiet() .nothrow() - const script = join(installed, "dist", "node_python_bridge.py") + // Node loads the bundle by its real path; on macOS `tmpdir()` is under the `/var` symlink. + const script = join(realpathSync(installed), "dist", "node_python_bridge.py") const runs = readFileSync(argvLog, "utf8").split("\n") expect(runs).toContain(script) expect(existsSync(script)).toBe(true) From ada3263edf9c7d2345670d9052d176527e0c2b3a Mon Sep 17 00:00:00 2001 From: "Ralph Sto. Domingo" Date: Fri, 30 Oct 2026 07:42:53 +0800 Subject: [PATCH 5/8] fix(dbt-tools): say what the bundle check checks The build check, its test and the release smoke check only find the references the bridge path is built from. The relocation test checks where the script is found. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01VEmKQtZqePG8x1GSELVNdE --- .github/workflows/release.yml | 4 ++-- packages/dbt-tools/script/copy-python.ts | 10 ++++++---- packages/dbt-tools/test/build-integrity.test.ts | 2 +- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3c8ae0c86b..a99548bd78 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -286,9 +286,9 @@ jobs: echo "::error::dbt-tools bundle contains hardcoded absolute path in __dirname" exit 1 fi - # Verify the bridge script resolves relative to the bundle at runtime + # The bridge path is built at runtime from the bundle's own URL if ! grep -q 'fileURLToPath(import.meta.url)' packages/dbt-tools/dist/index.js; then - echo "::error::dbt-tools bundle does not resolve node_python_bridge.py at runtime" + echo "::error::dbt-tools bundle lacks fileURLToPath(import.meta.url), which the bridge path is built from" exit 1 fi echo "dbt-tools smoke test passed" diff --git a/packages/dbt-tools/script/copy-python.ts b/packages/dbt-tools/script/copy-python.ts index f200c3ae50..bcb185ba50 100644 --- a/packages/dbt-tools/script/copy-python.ts +++ b/packages/dbt-tools/script/copy-python.ts @@ -22,14 +22,16 @@ console.log(`Copied node_python_bridge.py → dist/`) // 3. dbt-integration resolves node_python_bridge.py at runtime from its own // location (`fileURLToPath(import.meta.url)`), which in this bundle is dist/, -// where step 2 put the script. Fail the build if the bundle ever carries a -// path baked in at build time instead. +// where step 2 put the script. This only checks that the bundle still has +// the references that path is built from and no path baked in at build time; +// test/build-integrity.test.ts runs the packaged CLI from another directory +// to check where the script is found. const indexPath = join(dist, "index.js") const code = readFileSync(indexPath, "utf8") const baked = /var __dirname\s*=\s*"(?:[A-Za-z]:\\\\|\/)/ if (!code.includes("fileURLToPath(import.meta.url)") || !code.includes(`"node_python_bridge.py"`) || baked.test(code)) { - console.error(`ERROR: dist/index.js does not resolve node_python_bridge.py relative to itself at runtime`) + console.error(`ERROR: dist/index.js lacks the references the bridge path is built from, or has a build-time path`) console.error(` Has the @altimateai/dbt-integration bundle format changed?`) process.exit(1) } -console.log(`Verified dist/index.js resolves node_python_bridge.py at runtime`) +console.log(`Checked dist/index.js: bridge path references present, no build-time path`) diff --git a/packages/dbt-tools/test/build-integrity.test.ts b/packages/dbt-tools/test/build-integrity.test.ts index 5744f20b2f..be63d9d467 100644 --- a/packages/dbt-tools/test/build-integrity.test.ts +++ b/packages/dbt-tools/test/build-integrity.test.ts @@ -34,7 +34,7 @@ describe("build integrity", () => { expect(code).not.toMatch(/var __dirname\s*=\s*"(?:[A-Za-z]:\\\\|\/)/) }) - test("the bridge script resolves relative to the bundle at runtime", () => { + test("the bundle keeps the references the bridge path is built from at runtime", () => { const code = readFileSync(join(dist, "index.js"), "utf8") expect(code).toContain("fileURLToPath(import.meta.url)") expect(code).toContain(`"node_python_bridge.py"`) From 963111866f2cfd8af9befdd1c5a54fc71a9652d1 Mon Sep 17 00:00:00 2001 From: "Ralph Sto. Domingo" Date: Fri, 30 Oct 2026 08:40:57 +0800 Subject: [PATCH 6/8] fix(dbt-tools): give dbt Cloud mode its dbt Core command engine In 0.3.x every dbt Cloud variant but fusion-stable runs on a dbt Core command engine, which needs the Python strategy, configuration and client; without them every command failed. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01VEmKQtZqePG8x1GSELVNdE --- packages/dbt-tools/src/adapter.ts | 15 +-- .../dbt-tools/test/build-integrity.test.ts | 104 +++++++++++------- 2 files changed, 65 insertions(+), 54 deletions(-) diff --git a/packages/dbt-tools/src/adapter.ts b/packages/dbt-tools/src/adapter.ts index 5f9939da16..303f5203c9 100644 --- a/packages/dbt-tools/src/adapter.ts +++ b/packages/dbt-tools/src/adapter.ts @@ -143,6 +143,8 @@ export async function create(cfg: Config): Promise defer, changed, cloudVariantDetector, + // Every variant but fusion-stable runs on a dbt Core command engine, which needs these. + { pythonDBTCommandExecutionStrategy: python, dbtConfiguration: config, dbtIntegrationClient: client }, ) const command = ( @@ -172,18 +174,7 @@ export async function create(cfg: Config): Promise defer: DeferConfig, changed: () => void, ): DBTProjectIntegration => - new DBTFusionCommandProjectIntegration( - infra, - factory, - cli, - runtime, - provider, - term, - root, - diag, - defer, - changed, - ) + new DBTFusionCommandProjectIntegration(infra, factory, cli, runtime, provider, term, root, diag, defer, changed) const adapter = new DBTProjectIntegrationAdapter( config, diff --git a/packages/dbt-tools/test/build-integrity.test.ts b/packages/dbt-tools/test/build-integrity.test.ts index be63d9d467..a1265f6290 100644 --- a/packages/dbt-tools/test/build-integrity.test.ts +++ b/packages/dbt-tools/test/build-integrity.test.ts @@ -68,53 +68,73 @@ describe("build integrity", () => { // Node from wherever npm installed it. A stand-in `python` records what it is // asked to run and answers the CLI's prerequisite checks, so this follows the // real startup as far as spawning the bridge, without Python or dbt installed. + async function startPublished(root: string, dbtIntegration: string) { + const installed = join(root, "install", "dbt-tools") + mkdirSync(join(installed, "bin"), { recursive: true }) + mkdirSync(join(installed, "dist"), { recursive: true }) + cpSync(join(pkg, "bin/altimate-dbt"), join(installed, "bin/altimate-dbt")) + cpSync(join(dist, "index.js"), join(installed, "dist/index.js")) + cpSync(join(dist, "node_python_bridge.py"), join(installed, "dist/node_python_bridge.py")) + writeFileSync(join(installed, "package.json"), JSON.stringify({ type: "module" })) + + const argvLog = join(root, "argv.log") + const python = join(root, "python") + writeFileSync( + python, + [ + "#!/bin/sh", + `printf '%s\\n' "$*" >> '${argvLog}'`, + 'case "$1" in', + ' --version) echo "Python 3.11.9"; exit 0 ;;', + ' -c) echo "1.10.0"; exit 0 ;;', + "esac", + "exit 3", + ].join("\n") + "\n", + ) + chmodSync(python, 0o755) + const project = join(root, "project") + mkdirSync(project) + writeFileSync(join(project, "dbt_project.yml"), "name: p\nversion: '1.0'\nprofile: p\n") + // Under Node, HOME is the home directory, so there is no ~/.dbt/dbt_cloud.yml either. + const home = join(root, "home") + mkdirSync(join(home, ".altimate-code"), { recursive: true }) + writeFileSync( + join(home, ".altimate-code", "dbt.json"), + JSON.stringify({ projectRoot: project, pythonPath: python, dbtIntegration, queryLimit: 500 }), + ) + + await $`node ${join(installed, "bin/altimate-dbt")} info` + .cwd(root) + .env({ ...process.env, HOME: home }) + .quiet() + .nothrow() + + // Node loads the bundle by its real path; on macOS `tmpdir()` is under the `/var` symlink. + const script = join(realpathSync(installed), "dist", "node_python_bridge.py") + return { script, runs: readFileSync(argvLog, "utf8").split("\n") } + } + + // A dbt Cloud variant other than fusion-stable, here an unknown one, runs on a dbt Core + // command engine, which starts the bridge. Without that engine's dependencies it throws first. + test.skipIf(process.platform === "win32")( + "the published package starts dbt Cloud mode's dbt Core command engine", + async () => { + const root = mkdtempSync(join(tmpdir(), "dbt-tools-cloud-")) + try { + const { script, runs } = await startPublished(root, "cloud") + expect(runs).toContain(script) + } finally { + rmSync(root, { recursive: true, force: true }) + } + }, + ) + test.skipIf(process.platform === "win32")( "the published package starts the bridge script next to its own bundle, from any directory", async () => { const root = mkdtempSync(join(tmpdir(), "dbt-tools-published-")) try { - const installed = join(root, "install", "dbt-tools") - mkdirSync(join(installed, "bin"), { recursive: true }) - mkdirSync(join(installed, "dist"), { recursive: true }) - cpSync(join(pkg, "bin/altimate-dbt"), join(installed, "bin/altimate-dbt")) - cpSync(join(dist, "index.js"), join(installed, "dist/index.js")) - cpSync(join(dist, "node_python_bridge.py"), join(installed, "dist/node_python_bridge.py")) - writeFileSync(join(installed, "package.json"), JSON.stringify({ type: "module" })) - - const argvLog = join(root, "argv.log") - const python = join(root, "python") - writeFileSync( - python, - [ - "#!/bin/sh", - `printf '%s\\n' "$*" >> '${argvLog}'`, - 'case "$1" in', - ' --version) echo "Python 3.11.9"; exit 0 ;;', - ' -c) echo "1.10.0"; exit 0 ;;', - "esac", - "exit 3", - ].join("\n") + "\n", - ) - chmodSync(python, 0o755) - const project = join(root, "project") - mkdirSync(project) - writeFileSync(join(project, "dbt_project.yml"), "name: p\nversion: '1.0'\nprofile: p\n") - const home = join(root, "home") - mkdirSync(join(home, ".altimate-code"), { recursive: true }) - writeFileSync( - join(home, ".altimate-code", "dbt.json"), - JSON.stringify({ projectRoot: project, pythonPath: python, dbtIntegration: "corecommand", queryLimit: 500 }), - ) - - await $`node ${join(installed, "bin/altimate-dbt")} info` - .cwd(root) - .env({ ...process.env, HOME: home }) - .quiet() - .nothrow() - - // Node loads the bundle by its real path; on macOS `tmpdir()` is under the `/var` symlink. - const script = join(realpathSync(installed), "dist", "node_python_bridge.py") - const runs = readFileSync(argvLog, "utf8").split("\n") + const { script, runs } = await startPublished(root, "corecommand") expect(runs).toContain(script) expect(existsSync(script)).toBe(true) } finally { From c0f6a9995818822a1ea447958e65a014ef65de21 Mon Sep 17 00:00:00 2001 From: "Ralph Sto. Domingo" Date: Fri, 30 Oct 2026 22:34:39 +0800 Subject: [PATCH 7/8] test(dbt-tools): start the adapter under Bun in CI and tighten the release check A Bun test starts the adapter in each mode with a stand-in python; it fails on 0.2.14. The dbt-first test stops skipping the old python-bridge error, the release smoke check greps for the bridge name and the checkout path, and stale comments are updated. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01VEmKQtZqePG8x1GSELVNdE --- .github/workflows/release.yml | 10 ++++ packages/dbt-tools/package.json | 2 +- packages/dbt-tools/src/index.ts | 6 +-- packages/dbt-tools/test/adapter-bun.test.ts | 40 ++++++++++++++++ .../dbt-tools/test/build-integrity.test.ts | 46 +++++++++---------- packages/dbt-tools/test/stand-in-python.ts | 28 +++++++++++ .../test/altimate/dbt-first-execution.test.ts | 33 +------------ 7 files changed, 105 insertions(+), 60 deletions(-) create mode 100644 packages/dbt-tools/test/adapter-bun.test.ts create mode 100644 packages/dbt-tools/test/stand-in-python.ts diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a99548bd78..a4a52e4dc2 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -291,6 +291,16 @@ jobs: echo "::error::dbt-tools bundle lacks fileURLToPath(import.meta.url), which the bridge path is built from" exit 1 fi + # ...joined with the bare script name, not a literal path ending in it + if ! grep -qF '"node_python_bridge.py"' packages/dbt-tools/dist/index.js; then + echo "::error::dbt-tools bundle lacks the \"node_python_bridge.py\" name the bridge path is joined from" + exit 1 + fi + # No path from this checkout baked into the bundle + if [ -z "$GITHUB_WORKSPACE" ] || grep -qF "$GITHUB_WORKSPACE" packages/dbt-tools/dist/index.js; then + echo "::error::dbt-tools bundle contains the build checkout path, or GITHUB_WORKSPACE is unset" + exit 1 + fi echo "dbt-tools smoke test passed" - name: Free disk space for artifact download + npm publish diff --git a/packages/dbt-tools/package.json b/packages/dbt-tools/package.json index 9e33d9fe16..f0e88502ff 100644 --- a/packages/dbt-tools/package.json +++ b/packages/dbt-tools/package.json @@ -9,7 +9,7 @@ "scripts": { "build": "bun build src/index.ts --outdir dist --target node --format esm && bun run script/copy-python.ts", "typecheck": "tsc --noEmit", - "test": "bun test test/cli.test.ts test/config.test.ts test/dbt-cli.test.ts test/dbt-resolve.test.ts test/build-integrity.test.ts --timeout 30000", + "test": "bun test test/cli.test.ts test/config.test.ts test/dbt-cli.test.ts test/dbt-resolve.test.ts test/adapter-bun.test.ts test/build-integrity.test.ts --timeout 30000", "test:e2e": "bun test test/e2e/ --timeout 300000" }, "dependencies": { diff --git a/packages/dbt-tools/src/index.ts b/packages/dbt-tools/src/index.ts index 6a84df73f7..5a1d7f6621 100644 --- a/packages/dbt-tools/src/index.ts +++ b/packages/dbt-tools/src/index.ts @@ -39,7 +39,7 @@ function diagnose(err: Error): { error: string; fix?: string } { const msg = err.message || String(err) if (msg.includes("private field") || msg.includes("#stdin") || msg.includes("#stdout")) { return { - error: "Internal error: Bun runtime incompatibility with python-bridge. Please report this issue.", + error: "Internal error: Bun runtime incompatibility in the dbt adapter. Please report this issue.", fix: "Try updating: bun install, or file a bug at https://github.com/AltimateAI/altimate-code/issues", } } @@ -109,7 +109,7 @@ function bail(err: unknown): never { process.exit(1) } -// Catch unhandled rejections (bluebird throws outside normal promise chains) +// An error that escapes main() still ends as a JSON error on stdout process.on("unhandledRejection", bail) process.on("uncaughtException", bail) @@ -142,7 +142,7 @@ async function main() { const { configure } = await import("./dbt-cli") configure({ pythonPath: cfg.pythonPath, projectRoot: cfg.projectRoot }) - // Lazy import to avoid loading python-bridge until needed + // Lazy import: the adapter loads dbt-integration, which commands above don't need let adapter try { const { create } = await import("./adapter") diff --git a/packages/dbt-tools/test/adapter-bun.test.ts b/packages/dbt-tools/test/adapter-bun.test.ts new file mode 100644 index 0000000000..a5eaa0af75 --- /dev/null +++ b/packages/dbt-tools/test/adapter-bun.test.ts @@ -0,0 +1,40 @@ +import { describe, test, expect } from "bun:test" +import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "fs" +import { tmpdir } from "os" +import { dirname, join } from "path" +import { create } from "../src/adapter" +import { standInPython } from "./stand-in-python" + +// The published-package tests run the bundle under Node. The TUI and the compiled +// binary run the adapter under Bun, the runtime the bridge used to fail on, so this +// starts it there. +describe.skipIf(process.platform === "win32")("the adapter under Bun", () => { + const script = join(dirname(require.resolve("@altimateai/dbt-integration")), "node_python_bridge.py") + + // The stand-in fails the bridge run. The dbt Core modes then reject with the bridge's + // report of that exit, and dbt Cloud mode resolves anyway. A failure before the + // spawn rejects with its own message. + test.each([ + ["core", "rejects"], + ["corecommand", "rejects"], + ["cloud", "starts"], + ])("%s mode spawns the bridge script and %s", async (dbtIntegration, outcome) => { + const root = mkdtempSync(join(tmpdir(), "dbt-tools-bun-")) + try { + const { python, runs } = standInPython(root) + const project = join(root, "project") + mkdirSync(project) + writeFileSync(join(project, "dbt_project.yml"), "name: p\nversion: '1.0'\nprofile: p\n") + + const started = create({ projectRoot: project, pythonPath: python, dbtIntegration, queryLimit: 500 }) + if (outcome === "rejects") { + await expect(started).rejects.toThrow("Python process closed with exit code 3") + } else { + await (await started).dispose() + } + expect(runs()).toContain(script) + } finally { + rmSync(root, { recursive: true, force: true }) + } + }) +}) diff --git a/packages/dbt-tools/test/build-integrity.test.ts b/packages/dbt-tools/test/build-integrity.test.ts index a1265f6290..84d48e0b28 100644 --- a/packages/dbt-tools/test/build-integrity.test.ts +++ b/packages/dbt-tools/test/build-integrity.test.ts @@ -1,6 +1,5 @@ import { describe, test, expect, beforeAll } from "bun:test" import { - chmodSync, cpSync, existsSync, mkdirSync, @@ -13,6 +12,7 @@ import { import { tmpdir } from "os" import { dirname, join } from "path" import { $ } from "bun" +import { standInPython } from "./stand-in-python" const pkg = join(import.meta.dir, "..") const dist = join(pkg, "dist") @@ -65,9 +65,8 @@ describe("build integrity", () => { }) // The published package (see opencode/script/publish.ts copyAssets) is run by - // Node from wherever npm installed it. A stand-in `python` records what it is - // asked to run and answers the CLI's prerequisite checks, so this follows the - // real startup as far as spawning the bridge, without Python or dbt installed. + // Node from wherever npm installed it. With the stand-in `python`, this follows + // the real startup as far as spawning the bridge, without Python or dbt installed. async function startPublished(root: string, dbtIntegration: string) { const installed = join(root, "install", "dbt-tools") mkdirSync(join(installed, "bin"), { recursive: true }) @@ -77,21 +76,7 @@ describe("build integrity", () => { cpSync(join(dist, "node_python_bridge.py"), join(installed, "dist/node_python_bridge.py")) writeFileSync(join(installed, "package.json"), JSON.stringify({ type: "module" })) - const argvLog = join(root, "argv.log") - const python = join(root, "python") - writeFileSync( - python, - [ - "#!/bin/sh", - `printf '%s\\n' "$*" >> '${argvLog}'`, - 'case "$1" in', - ' --version) echo "Python 3.11.9"; exit 0 ;;', - ' -c) echo "1.10.0"; exit 0 ;;', - "esac", - "exit 3", - ].join("\n") + "\n", - ) - chmodSync(python, 0o755) + const { python, runs } = standInPython(root) const project = join(root, "project") mkdirSync(project) writeFileSync(join(project, "dbt_project.yml"), "name: p\nversion: '1.0'\nprofile: p\n") @@ -103,7 +88,7 @@ describe("build integrity", () => { JSON.stringify({ projectRoot: project, pythonPath: python, dbtIntegration, queryLimit: 500 }), ) - await $`node ${join(installed, "bin/altimate-dbt")} info` + const cli = await $`node ${join(installed, "bin/altimate-dbt")} info` .cwd(root) .env({ ...process.env, HOME: home }) .quiet() @@ -111,17 +96,23 @@ describe("build integrity", () => { // Node loads the bundle by its real path; on macOS `tmpdir()` is under the `/var` symlink. const script = join(realpathSync(installed), "dist", "node_python_bridge.py") - return { script, runs: readFileSync(argvLog, "utf8").split("\n") } + return { script, runs: runs(), exitCode: cli.exitCode, output: cli.stdout.toString() } } + // The stand-in `python` is a `#!/bin/sh` script. + const posixOnly = test.skipIf(process.platform === "win32") + // A dbt Cloud variant other than fusion-stable, here an unknown one, runs on a dbt Core // command engine, which starts the bridge. Without that engine's dependencies it throws first. - test.skipIf(process.platform === "win32")( + posixOnly( "the published package starts dbt Cloud mode's dbt Core command engine", async () => { const root = mkdtempSync(join(tmpdir(), "dbt-tools-cloud-")) try { - const { script, runs } = await startPublished(root, "cloud") + const { script, runs, exitCode, output } = await startPublished(root, "cloud") + // `info` answers from the project once the engine's bridge has failed. A failure + // before the spawn exits 1, and the comparison shows the CLI's message. + expect({ exitCode, output }).toMatchObject({ exitCode: 0 }) expect(runs).toContain(script) } finally { rmSync(root, { recursive: true, force: true }) @@ -129,12 +120,17 @@ describe("build integrity", () => { }, ) - test.skipIf(process.platform === "win32")( + posixOnly( "the published package starts the bridge script next to its own bundle, from any directory", async () => { const root = mkdtempSync(join(tmpdir(), "dbt-tools-published-")) try { - const { script, runs } = await startPublished(root, "corecommand") + const { script, runs, exitCode, output } = await startPublished(root, "corecommand") + // The CLI fails on the stand-in's exit, so after the spawn. + expect({ exitCode, output }).toMatchObject({ + exitCode: 1, + output: expect.stringContaining("Python process closed with exit code 3"), + }) expect(runs).toContain(script) expect(existsSync(script)).toBe(true) } finally { diff --git a/packages/dbt-tools/test/stand-in-python.ts b/packages/dbt-tools/test/stand-in-python.ts new file mode 100644 index 0000000000..ac7498dd1f --- /dev/null +++ b/packages/dbt-tools/test/stand-in-python.ts @@ -0,0 +1,28 @@ +import { chmodSync, existsSync, readFileSync, writeFileSync } from "fs" +import { join } from "path" + +/** + * A stand-in `python` in `root` that records what it is asked to run and answers + * the CLI's prerequisite checks. Every other run, the bridge's included, exits 3, + * so a test follows startup as far as spawning the bridge without Python or dbt + * installed. It is a `#!/bin/sh` script, so the tests that use it skip Windows. + */ +export function standInPython(root: string) { + const argvLog = join(root, "argv.log") + const python = join(root, "python") + writeFileSync( + python, + [ + "#!/bin/sh", + `printf '%s\\n' "$*" >> '${argvLog}'`, + 'case "$1" in', + ' --version) echo "Python 3.11.9"; exit 0 ;;', + ' -c) echo "1.10.0"; exit 0 ;;', + "esac", + "exit 3", + ].join("\n") + "\n", + ) + chmodSync(python, 0o755) + const runs = () => (existsSync(argvLog) ? readFileSync(argvLog, "utf8").split("\n") : []) + return { python, runs } +} diff --git a/packages/opencode/test/altimate/dbt-first-execution.test.ts b/packages/opencode/test/altimate/dbt-first-execution.test.ts index 1133f39113..d1f7a61427 100644 --- a/packages/opencode/test/altimate/dbt-first-execution.test.ts +++ b/packages/opencode/test/altimate/dbt-first-execution.test.ts @@ -179,37 +179,8 @@ describe.skipIf(!HAS_DBT)("dbt-First SQL Execution E2E", () => { return } - // altimate_change: importing the adapter pulls in python-bridge, whose - // bluebird `promisifyAll` over child_process throws under the Bun runtime - // (private-field `#stdin` access) at MODULE LOAD — see dbt-tools/src/index.ts - // diagnose(). That is an environment/dependency limitation, not a logic - // regression, and only surfaces locally where a real dbt config is present - // (CI has none and skips above). Skip rather than weaken; the assertion still - // runs wherever python-bridge loads cleanly. - const isPyBridgeIncompat = (e: unknown) => { - const msg = (e as Error)?.message ?? String(e) - return msg.includes("#stdin") || msg.includes("#stdout") || msg.includes("private field") - } - let create: (typeof import("../../../dbt-tools/src/adapter"))["create"] - try { - ;({ create } = await import("../../../dbt-tools/src/adapter")) - } catch (e) { - if (isPyBridgeIncompat(e)) { - console.log(" python-bridge/Bun incompatibility at import — skipping adapter assertion:", (e as Error).message) - return - } - throw e - } - let adapter - try { - adapter = await create(cfg) - } catch (e) { - if (isPyBridgeIncompat(e)) { - console.log(" python-bridge/Bun incompatibility — skipping adapter creation assertion:", (e as Error).message) - return - } - throw e - } + const { create } = await import("../../../dbt-tools/src/adapter") + const adapter = await create(cfg) expect(adapter).toBeTruthy() expect(adapter.immediatelyExecuteSQL).toBeInstanceOf(Function) }, 30000) From 5018286fac0c531a062b0ee22d84a68b3df1805d Mon Sep 17 00:00:00 2001 From: "Ralph Sto. Domingo" Date: Fri, 30 Oct 2026 23:16:38 +0800 Subject: [PATCH 8/8] test(dbt-tools): run the Bun case as the CLI with a temp HOME The in-process case read this machine's ~/.dbt/dbt_cloud.yml: Bun's os.homedir() ignores a runtime HOME change. The stand-in quotes an apostrophe in its log path, and diagnose() loses the python-bridge branch nothing can reach now. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01VEmKQtZqePG8x1GSELVNdE --- packages/dbt-tools/src/index.ts | 6 ---- packages/dbt-tools/test/adapter-bun.test.ts | 40 +++++++++++++-------- packages/dbt-tools/test/stand-in-python.ts | 2 +- 3 files changed, 26 insertions(+), 22 deletions(-) diff --git a/packages/dbt-tools/src/index.ts b/packages/dbt-tools/src/index.ts index 5a1d7f6621..15389ff12e 100644 --- a/packages/dbt-tools/src/index.ts +++ b/packages/dbt-tools/src/index.ts @@ -37,12 +37,6 @@ function flag(args: string[], name: string): string | undefined { function diagnose(err: Error): { error: string; fix?: string } { const msg = err.message || String(err) - if (msg.includes("private field") || msg.includes("#stdin") || msg.includes("#stdout")) { - return { - error: "Internal error: Bun runtime incompatibility in the dbt adapter. Please report this issue.", - fix: "Try updating: bun install, or file a bug at https://github.com/AltimateAI/altimate-code/issues", - } - } if (msg.includes("IPC channel") || msg.includes("ERR_IPC_CHANNEL_CLOSED")) { return { error: "Failed to start Python bridge. The dbt Python process exited unexpectedly.", diff --git a/packages/dbt-tools/test/adapter-bun.test.ts b/packages/dbt-tools/test/adapter-bun.test.ts index a5eaa0af75..c38055ae9b 100644 --- a/packages/dbt-tools/test/adapter-bun.test.ts +++ b/packages/dbt-tools/test/adapter-bun.test.ts @@ -2,36 +2,46 @@ import { describe, test, expect } from "bun:test" import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "fs" import { tmpdir } from "os" import { dirname, join } from "path" -import { create } from "../src/adapter" +import { $ } from "bun" import { standInPython } from "./stand-in-python" +const pkg = join(import.meta.dir, "..") + // The published-package tests run the bundle under Node. The TUI and the compiled // binary run the adapter under Bun, the runtime the bridge used to fail on, so this -// starts it there. +// runs the CLI from source under Bun. HOME is a temp dir from the start, so no +// ~/.dbt/dbt_cloud.yml on this machine picks dbt Cloud mode's variant. describe.skipIf(process.platform === "win32")("the adapter under Bun", () => { const script = join(dirname(require.resolve("@altimateai/dbt-integration")), "node_python_bridge.py") - // The stand-in fails the bridge run. The dbt Core modes then reject with the bridge's - // report of that exit, and dbt Cloud mode resolves anyway. A failure before the - // spawn rejects with its own message. + // The stand-in fails the bridge run. The dbt Core modes then exit 1 with the bridge's + // report of that exit; `info` in dbt Cloud mode still answers from the project. A + // failure before the spawn shows its own message in the comparison. test.each([ - ["core", "rejects"], - ["corecommand", "rejects"], - ["cloud", "starts"], - ])("%s mode spawns the bridge script and %s", async (dbtIntegration, outcome) => { + ["core", { exitCode: 1, output: expect.stringContaining("Python process closed with exit code 3") }], + ["corecommand", { exitCode: 1, output: expect.stringContaining("Python process closed with exit code 3") }], + ["cloud", { exitCode: 0 }], + ])("%s mode spawns the bridge script", async (dbtIntegration, expected) => { const root = mkdtempSync(join(tmpdir(), "dbt-tools-bun-")) try { const { python, runs } = standInPython(root) const project = join(root, "project") mkdirSync(project) writeFileSync(join(project, "dbt_project.yml"), "name: p\nversion: '1.0'\nprofile: p\n") + const home = join(root, "home") + mkdirSync(join(home, ".altimate-code"), { recursive: true }) + writeFileSync( + join(home, ".altimate-code", "dbt.json"), + JSON.stringify({ projectRoot: project, pythonPath: python, dbtIntegration, queryLimit: 500 }), + ) + + const cli = await $`${process.execPath} ${join(pkg, "src/index.ts")} info` + .cwd(root) + .env({ ...process.env, HOME: home }) + .quiet() + .nothrow() - const started = create({ projectRoot: project, pythonPath: python, dbtIntegration, queryLimit: 500 }) - if (outcome === "rejects") { - await expect(started).rejects.toThrow("Python process closed with exit code 3") - } else { - await (await started).dispose() - } + expect({ exitCode: cli.exitCode, output: cli.stdout.toString() }).toMatchObject(expected) expect(runs()).toContain(script) } finally { rmSync(root, { recursive: true, force: true }) diff --git a/packages/dbt-tools/test/stand-in-python.ts b/packages/dbt-tools/test/stand-in-python.ts index ac7498dd1f..53e9e0d81a 100644 --- a/packages/dbt-tools/test/stand-in-python.ts +++ b/packages/dbt-tools/test/stand-in-python.ts @@ -14,7 +14,7 @@ export function standInPython(root: string) { python, [ "#!/bin/sh", - `printf '%s\\n' "$*" >> '${argvLog}'`, + `printf '%s\\n' "$*" >> '${argvLog.replaceAll("'", "'\\''")}'`, 'case "$1" in', ' --version) echo "Python 3.11.9"; exit 0 ;;', ' -c) echo "1.10.0"; exit 0 ;;',