{"_id":"@ancher-ai/sdk","_rev":"6-cd59458a3ec87fb055b0d3cf2d86a67e","name":"@ancher-ai/sdk","dist-tags":{"latest":"0.3.1"},"versions":{"0.1.0":{"name":"@ancher-ai/sdk","version":"0.1.0","license":"Apache-2.0","_id":"@ancher-ai/sdk@0.1.0","maintainers":[{"name":"why-ancher","email":"why@streamify.one"},{"name":"yajun.wang.streamify","email":"yajun.wang@streamify.one"}],"homepage":"https://ancher.ai","bugs":{"url":"https://github.com/streamify-one/ancher-sdk-typescript/issues"},"dist":{"shasum":"008d631f28469c64f7825c1e614d8aa67d55784b","tarball":"https://registry.npmjs.org/@ancher-ai/sdk/-/sdk-0.1.0.tgz","fileCount":42,"integrity":"sha512-u+qH4NfQMSWamS2/xHunFLyb9xT75UhE1krvw8L1Xt7q4dWTBQMxFXKrR6Vmv9Z5pbz/+fUGv62GgZ3ouOm5Uw==","signatures":[{"sig":"MEUCIQCs9Y3hmYM1YuTapPCu9JyzUYLBFF1OC+XkMPp5zDctvAIgZ8StxJaJDcQIl8HNJeystQonWvIiBxwGj+8NgqNj/mw=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":4652063},"main":"./dist/index.cjs","type":"module","_from":"file:/tmp/ancher-sdk-release/ancher-ai-sdk-0.1.0.tgz","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}},"./oauth2":{"import":{"types":"./dist/oauth2.d.ts","default":"./dist/oauth2.js"},"require":{"types":"./dist/oauth2.d.cts","default":"./dist/oauth2.cjs"}},"./tanstack":{"import":{"types":"./dist/tanstack.d.ts","default":"./dist/tanstack.js"},"require":{"types":"./dist/tanstack.d.cts","default":"./dist/tanstack.cjs"}},"./contracts":{"import":{"types":"./dist/contracts.d.ts","default":"./dist/contracts.js"},"require":{"types":"./dist/contracts.d.cts","default":"./dist/contracts.cjs"}}},"scripts":{"dev":"tsup --watch","test":"vitest run","build":"tsup && node scripts/fix-tanstack-declarations.mjs","generate":"node scripts/fetch-openapi.js && typed-openapi openapi.json --output src/api/generated/api.client.ts --tanstack","typecheck":"tsc --noEmit","verify:package":"pnpm build && node scripts/verify-package.mjs"},"_npmUser":{"name":"yajun.wang.streamify","email":"yajun.wang@streamify.one"},"_resolved":"/tmp/ancher-sdk-release/ancher-ai-sdk-0.1.0.tgz","_integrity":"sha512-u+qH4NfQMSWamS2/xHunFLyb9xT75UhE1krvw8L1Xt7q4dWTBQMxFXKrR6Vmv9Z5pbz/+fUGv62GgZ3ouOm5Uw==","repository":{"url":"git+https://github.com/streamify-one/ancher-sdk-typescript.git","type":"git"},"_npmVersion":"12.0.2","description":"Fully-typed TypeScript SDK for the Ancher API, generated from the OpenAPI spec.","directories":{},"sideEffects":false,"_nodeVersion":"24.16.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.3.0","vitest":"4.1.10","typescript":"~5.9.3","@types/react":"^19.2.18","typed-openapi":"^2.2.7","@tanstack/react-query":"^5.99.0"},"peerDependencies":{"@tanstack/react-query":">=5"},"peerDependenciesMeta":{"@tanstack/react-query":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/sdk_0.1.0_1787874347364_0.8024190846548818","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@ancher-ai/sdk","version":"0.1.1","license":"Apache-2.0","_id":"@ancher-ai/sdk@0.1.1","maintainers":[{"name":"why-ancher","email":"why@streamify.one"},{"name":"yajun.wang.streamify","email":"yajun.wang@streamify.one"}],"homepage":"https://ancher.ai","bugs":{"url":"https://github.com/streamify-one/ancher-sdk-typescript/issues"},"dist":{"shasum":"c8c0ad03ae2f6fc15599cb1c1b459b7f8a20ba01","tarball":"https://registry.npmjs.org/@ancher-ai/sdk/-/sdk-0.1.1.tgz","fileCount":42,"integrity":"sha512-MmbWlTTOEjl1ofrnKv91ArWWkbN+maE9SFCaF6ZxVNm1aVFYTQ9glC2QY4Ag+rsGSF41J4zfLl8cwSQjDGoVaQ==","signatures":[{"sig":"MEUCIEf7BIhu8fo5aiBrS2ACshJ5etgum3RRTMIIzwiw/iiEAiEA1Ixlrz3c3SU1OZoeDIExlQDww+GWgMmexPB/4NfQosE=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":4652063},"main":"./dist/index.cjs","type":"module","_from":"file:/home/runner/work/_temp/ancher-ai-sdk-0.1.1.tgz","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}},"./oauth2":{"import":{"types":"./dist/oauth2.d.ts","default":"./dist/oauth2.js"},"require":{"types":"./dist/oauth2.d.cts","default":"./dist/oauth2.cjs"}},"./tanstack":{"import":{"types":"./dist/tanstack.d.ts","default":"./dist/tanstack.js"},"require":{"types":"./dist/tanstack.d.cts","default":"./dist/tanstack.cjs"}},"./contracts":{"import":{"types":"./dist/contracts.d.ts","default":"./dist/contracts.js"},"require":{"types":"./dist/contracts.d.cts","default":"./dist/contracts.cjs"}}},"scripts":{"dev":"tsup --watch","test":"vitest run","build":"tsup && node scripts/fix-tanstack-declarations.mjs","generate":"node scripts/fetch-openapi.js && typed-openapi openapi.json --output src/api/generated/api.client.ts --tanstack","typecheck":"tsc --noEmit","verify:package":"pnpm build && node scripts/verify-package.mjs"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:8f0dc3d4-e806-48aa-ac68-c5135a0962b6"}},"_resolved":"/home/runner/work/_temp/ancher-ai-sdk-0.1.1.tgz","_integrity":"sha512-MmbWlTTOEjl1ofrnKv91ArWWkbN+maE9SFCaF6ZxVNm1aVFYTQ9glC2QY4Ag+rsGSF41J4zfLl8cwSQjDGoVaQ==","repository":{"url":"git+https://github.com/streamify-one/ancher-sdk-typescript.git","type":"git"},"_npmVersion":"12.0.2","description":"Fully-typed TypeScript SDK for the Ancher API, generated from the OpenAPI spec.","directories":{},"sideEffects":false,"_nodeVersion":"24.19.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.3.0","vitest":"4.1.10","typescript":"~5.9.3","@types/react":"^19.2.18","typed-openapi":"^2.2.7","@tanstack/react-query":"^5.99.0"},"peerDependencies":{"@tanstack/react-query":">=5"},"peerDependenciesMeta":{"@tanstack/react-query":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/sdk_0.1.1_1787883108563_0.5076356509581694","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@ancher-ai/sdk","version":"0.2.0","license":"Apache-2.0","_id":"@ancher-ai/sdk@0.2.0","maintainers":[{"name":"why-ancher","email":"why@streamify.one"},{"name":"yajun.wang.streamify","email":"yajun.wang@streamify.one"}],"homepage":"https://ancher.ai","bugs":{"url":"https://github.com/streamify-one/ancher-sdk-typescript/issues"},"dist":{"shasum":"ade058a3f041ace0da744e191d28359a495851d0","tarball":"https://registry.npmjs.org/@ancher-ai/sdk/-/sdk-0.2.0.tgz","fileCount":42,"integrity":"sha512-ftTYPtM35Wy/pL56SmSubRHUX7rM5EdUVNobfWM6gkyPrgSPIczVUct+GT4nBSE6IbvTH1qqjNiJQWGbPEP54A==","signatures":[{"sig":"MEYCIQDIFMFYyKYezBmqCDbHDFBCaI5u+bIHktbCWN49qvrhnAIhAO3Grt89DUYgHJS4y7ClA35be4aTIjOaW0dKYf7PSy1X","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":4716813},"main":"./dist/index.cjs","type":"module","_from":"file:/home/runner/work/_temp/ancher-ai-sdk-0.2.0.tgz","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}},"./oauth2":{"import":{"types":"./dist/oauth2.d.ts","default":"./dist/oauth2.js"},"require":{"types":"./dist/oauth2.d.cts","default":"./dist/oauth2.cjs"}},"./tanstack":{"import":{"types":"./dist/tanstack.d.ts","default":"./dist/tanstack.js"},"require":{"types":"./dist/tanstack.d.cts","default":"./dist/tanstack.cjs"}},"./contracts":{"import":{"types":"./dist/contracts.d.ts","default":"./dist/contracts.js"},"require":{"types":"./dist/contracts.d.cts","default":"./dist/contracts.cjs"}}},"scripts":{"dev":"tsup --watch","test":"vitest run","build":"tsup && node scripts/fix-tanstack-declarations.mjs","generate":"node scripts/fetch-openapi.js && typed-openapi openapi.json --output src/api/generated/api.client.ts --tanstack","typecheck":"tsc --noEmit","verify:package":"pnpm build && node scripts/verify-package.mjs"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:8f0dc3d4-e806-48aa-ac68-c5135a0962b6"}},"_resolved":"/home/runner/work/_temp/ancher-ai-sdk-0.2.0.tgz","_integrity":"sha512-ftTYPtM35Wy/pL56SmSubRHUX7rM5EdUVNobfWM6gkyPrgSPIczVUct+GT4nBSE6IbvTH1qqjNiJQWGbPEP54A==","repository":{"url":"git+https://github.com/streamify-one/ancher-sdk-typescript.git","type":"git"},"_npmVersion":"12.0.2","description":"Fully-typed TypeScript SDK for the Ancher API, generated from the OpenAPI spec.","directories":{},"sideEffects":false,"_nodeVersion":"24.19.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.3.0","vitest":"4.1.10","typescript":"~5.9.3","@types/react":"^19.2.18","typed-openapi":"^2.2.7","@tanstack/react-query":"^5.99.0"},"peerDependencies":{"@tanstack/react-query":">=5"},"peerDependenciesMeta":{"@tanstack/react-query":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/sdk_0.2.0_1787890462794_0.9155514091079047","host":"s3://npm-registry-packages-npm-production"}},"0.2.1":{"name":"@ancher-ai/sdk","version":"0.2.1","license":"Apache-2.0","_id":"@ancher-ai/sdk@0.2.1","maintainers":[{"name":"why-ancher","email":"why@streamify.one"},{"name":"yajun.wang.streamify","email":"yajun.wang@streamify.one"}],"homepage":"https://ancher.ai","bugs":{"url":"https://github.com/streamify-one/ancher-sdk-typescript/issues"},"dist":{"shasum":"398360b1bb2271612604bb6bd46c91a3e057e3e7","tarball":"https://registry.npmjs.org/@ancher-ai/sdk/-/sdk-0.2.1.tgz","fileCount":42,"integrity":"sha512-GuuRTUeLb1JEE9jDhquGTsXOx1NKFkYDZOxg+OkEq+1nu1OoKVkp8ITEsB+aId2umm2w9eknVTJ19FtMI3yJKA==","signatures":[{"sig":"MEQCIFP0/YrvFjRkYXtUGBCbjDfplK79fwojRs5SlKYPOzm1AiA8T986Dk1/4nTi+lrTVjOgdXoaJUlcD9nB9x29/nVINQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":4728883},"main":"./dist/index.cjs","type":"module","_from":"file:/home/runner/work/_temp/ancher-ai-sdk-0.2.1.tgz","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}},"./oauth2":{"import":{"types":"./dist/oauth2.d.ts","default":"./dist/oauth2.js"},"require":{"types":"./dist/oauth2.d.cts","default":"./dist/oauth2.cjs"}},"./tanstack":{"import":{"types":"./dist/tanstack.d.ts","default":"./dist/tanstack.js"},"require":{"types":"./dist/tanstack.d.cts","default":"./dist/tanstack.cjs"}},"./contracts":{"import":{"types":"./dist/contracts.d.ts","default":"./dist/contracts.js"},"require":{"types":"./dist/contracts.d.cts","default":"./dist/contracts.cjs"}}},"scripts":{"dev":"tsup --watch","test":"vitest run","build":"tsup && node scripts/fix-tanstack-declarations.mjs","generate":"node scripts/fetch-openapi.js && typed-openapi openapi.json --output src/api/generated/api.client.ts --tanstack","typecheck":"tsc --noEmit","verify:package":"pnpm build && node scripts/verify-package.mjs"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:8f0dc3d4-e806-48aa-ac68-c5135a0962b6"}},"_resolved":"/home/runner/work/_temp/ancher-ai-sdk-0.2.1.tgz","_integrity":"sha512-GuuRTUeLb1JEE9jDhquGTsXOx1NKFkYDZOxg+OkEq+1nu1OoKVkp8ITEsB+aId2umm2w9eknVTJ19FtMI3yJKA==","repository":{"url":"git+https://github.com/streamify-one/ancher-sdk-typescript.git","type":"git"},"_npmVersion":"12.0.2","description":"Fully-typed TypeScript SDK for the Ancher API, generated from the OpenAPI spec.","directories":{},"sideEffects":false,"_nodeVersion":"24.19.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.3.0","vitest":"4.1.10","typescript":"~5.9.3","@types/react":"^19.2.18","typed-openapi":"^2.2.7","@tanstack/react-query":"^5.99.0"},"peerDependencies":{"@tanstack/react-query":">=5"},"peerDependenciesMeta":{"@tanstack/react-query":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/sdk_0.2.1_1787959075318_0.8944949062753826","host":"s3://npm-registry-packages-npm-production"}},"0.3.0":{"name":"@ancher-ai/sdk","version":"0.3.0","license":"Apache-2.0","_id":"@ancher-ai/sdk@0.3.0","maintainers":[{"name":"why-ancher","email":"why@streamify.one"},{"name":"yajun.wang.streamify","email":"yajun.wang@streamify.one"}],"homepage":"https://ancher.ai","bugs":{"url":"https://github.com/streamify-one/ancher-sdk-typescript/issues"},"dist":{"shasum":"16317cbf2ef9c75442c3e7b1468ba9d0ade8ee37","tarball":"https://registry.npmjs.org/@ancher-ai/sdk/-/sdk-0.3.0.tgz","fileCount":42,"integrity":"sha512-GICdu1qEg9ZT0oZzHpqhTIwtor5nozlS/352oCcI5/c+l6M40z4xY/RJrLIGatCfdpGbBYbB3r2/XaQ3WKH1KA==","signatures":[{"sig":"MEUCIQDoAXrcuRBi16iye+xh5xYADWUFiZ3CgqyjpJfOia+PEwIgUuSWkgioSuzFdQWHxJ/0EsKdFPeSxuByXC5R34CDIZE=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEQCIEFBLOKvWOgYsyNEsqlJEfoE1v8c2gUCV6NsbnQfjCFuAiBPv8IdqGZ+bdNSf5JGVqs56zZnHnmMA379SR7qA+V6cA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":4847138},"main":"./dist/index.cjs","type":"module","_from":"file:/home/runner/work/_temp/ancher-ai-sdk-0.3.0.tgz","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}},"./oauth2":{"import":{"types":"./dist/oauth2.d.ts","default":"./dist/oauth2.js"},"require":{"types":"./dist/oauth2.d.cts","default":"./dist/oauth2.cjs"}},"./tanstack":{"import":{"types":"./dist/tanstack.d.ts","default":"./dist/tanstack.js"},"require":{"types":"./dist/tanstack.d.cts","default":"./dist/tanstack.cjs"}},"./contracts":{"import":{"types":"./dist/contracts.d.ts","default":"./dist/contracts.js"},"require":{"types":"./dist/contracts.d.cts","default":"./dist/contracts.cjs"}}},"scripts":{"dev":"tsup --watch","test":"vitest run","build":"tsup && node scripts/fix-tanstack-declarations.mjs","generate":"node scripts/fetch-openapi.js && typed-openapi openapi.json --output src/api/generated/api.client.ts --tanstack","typecheck":"tsc --noEmit","verify:package":"pnpm build && node scripts/verify-package.mjs"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:8f0dc3d4-e806-48aa-ac68-c5135a0962b6"}},"_resolved":"/home/runner/work/_temp/ancher-ai-sdk-0.3.0.tgz","_integrity":"sha512-GICdu1qEg9ZT0oZzHpqhTIwtor5nozlS/352oCcI5/c+l6M40z4xY/RJrLIGatCfdpGbBYbB3r2/XaQ3WKH1KA==","repository":{"url":"git+https://github.com/streamify-one/ancher-sdk-typescript.git","type":"git"},"_npmVersion":"12.0.2","description":"Fully-typed TypeScript SDK for the Ancher API, generated from the OpenAPI spec.","directories":{},"sideEffects":false,"_nodeVersion":"24.20.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.3.0","vitest":"4.1.11","typescript":"~5.9.3","@types/react":"^19.2.18","typed-openapi":"^2.2.7","@tanstack/react-query":"^5.99.0"},"peerDependencies":{"@tanstack/react-query":">=5"},"peerDependenciesMeta":{"@tanstack/react-query":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/sdk_0.3.0_1789424042643_0.21844930350931402","host":"s3://npm-registry-packages-npm-production"}},"0.3.1":{"_id":"@ancher-ai/sdk@0.3.1","bugs":{"url":"https://github.com/streamify-one/ancher-sdk-typescript/issues"},"dist":{"shasum":"bc1ad1a07ed92f89de31abd2816782a0d9fcd2dd","tarball":"https://registry.npmjs.org/@ancher-ai/sdk/-/sdk-0.3.1.tgz","fileCount":42,"integrity":"sha512-sCy+Ks2n+2lWK4ezE7apdiIL5LrNvFujEb7iLI31GGeloahyOQ2Ej9fD2WrqywGo+FEassnawPE3QNQT07ncwg==","signatures":[{"sig":"MEQCIGgifwzlxO1g/CKHX4X9iv5RtUDvyrqxtJqn3dgapOxOAiBcegNhoHJdYzJ1HASGeTT1VleXb5BahHJ7JLHLVQfmSA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIEOHnWK2FEBEY+oef93qS0wjoge9ahFJqVtc4MFlzZd8AiBUAzwBqQbZZ2l8HYOcI6WrC3ENYsekKGgorkR5/Zi3pg=="}],"unpackedSize":4880001},"main":"./dist/index.cjs","name":"@ancher-ai/sdk","type":"module","_from":"file:/home/runner/work/_temp/ancher-ai-sdk-0.3.1.tgz","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}},"./oauth2":{"import":{"types":"./dist/oauth2.d.ts","default":"./dist/oauth2.js"},"require":{"types":"./dist/oauth2.d.cts","default":"./dist/oauth2.cjs"}},"./tanstack":{"import":{"types":"./dist/tanstack.d.ts","default":"./dist/tanstack.js"},"require":{"types":"./dist/tanstack.d.cts","default":"./dist/tanstack.cjs"}},"./contracts":{"import":{"types":"./dist/contracts.d.ts","default":"./dist/contracts.js"},"require":{"types":"./dist/contracts.d.cts","default":"./dist/contracts.cjs"}}},"license":"Apache-2.0","scripts":{"dev":"tsup --watch","test":"vitest run","build":"tsup && node scripts/fix-tanstack-declarations.mjs","generate":"node scripts/fetch-openapi.js && typed-openapi openapi.json --output src/api/generated/api.client.ts --tanstack","typecheck":"tsc --noEmit","verify:package":"pnpm build && node scripts/verify-package.mjs"},"version":"0.3.1","_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:8f0dc3d4-e806-48aa-ac68-c5135a0962b6"}},"homepage":"https://ancher.ai","_resolved":"/home/runner/work/_temp/ancher-ai-sdk-0.3.1.tgz","_integrity":"sha512-sCy+Ks2n+2lWK4ezE7apdiIL5LrNvFujEb7iLI31GGeloahyOQ2Ej9fD2WrqywGo+FEassnawPE3QNQT07ncwg==","repository":{"url":"git+https://github.com/streamify-one/ancher-sdk-typescript.git","type":"git"},"_npmVersion":"12.0.2","description":"Fully-typed TypeScript SDK for the Ancher API, generated from the OpenAPI spec.","directories":{},"maintainers":[{"name":"why-ancher","email":"why@streamify.one"},{"name":"yajun.wang.streamify","email":"yajun.wang@streamify.one"}],"sideEffects":false,"_nodeVersion":"24.20.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.3.0","vitest":"4.1.11","typescript":"~5.9.3","@types/react":"^19.2.18","typed-openapi":"^2.2.7","@tanstack/react-query":"^5.99.0"},"peerDependencies":{"@tanstack/react-query":">=5"},"peerDependenciesMeta":{"@tanstack/react-query":{"optional":true}},"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/sdk_0.3.1_1789548589489_0.43071019254409304"}}},"time":{"created":"2026-08-27T23:45:47.237Z","modified":"2026-09-16T08:49:49.809Z","0.1.0":"2026-08-27T23:45:47.551Z","0.1.1":"2026-08-28T02:11:48.831Z","0.2.0":"2026-08-28T04:14:22.964Z","0.2.1":"2026-08-28T23:17:55.509Z","0.3.0":"2026-09-14T22:14:02.743Z","0.3.1":"2026-09-16T08:49:49.619Z"},"bugs":{"url":"https://github.com/streamify-one/ancher-sdk-typescript/issues"},"license":"Apache-2.0","homepage":"https://ancher.ai","repository":{"url":"git+https://github.com/streamify-one/ancher-sdk-typescript.git","type":"git"},"description":"Fully-typed TypeScript SDK for the Ancher API, generated from the OpenAPI spec.","maintainers":[{"name":"why-ancher","email":"why@streamify.one"},{"name":"yajun.wang.streamify","email":"yajun.wang@streamify.one"}],"readme":"# @ancher-ai/sdk\n\nA fully-typed TypeScript SDK for the **Ancher API**.\n\nEvery path, method, request body, and response is typed straight from the\nOpenAPI spec via codegen, and fronted by a small configurable transport that\nhandles auth, CSRF, silent token refresh, and error normalization. The SDK is\nbuilt for **code and headless machine clients** — server-side (static API key),\nCLI/native (OAuth2 access token), or any custom scheme: authentication is just\na set of injectable hooks. Interactive account flows (registration, email\nverification, password reset, Google/Apple social login) are deliberately not\npart of the SDK surface — those belong to the product web app.\n\nThis package was extracted from the `streamify-one-design-system` app — it\ncombines that app's `pnpm generate` codegen pipeline with its `src/lib/api-client.ts`\ntransport, generalized into a standalone, runtime-agnostic library.\n\n## Two layers\n\n- **Raw API layer** — `createAncherClient(config)` → `{ api, upload, config }`.\n  The generated typed client, 1:1 with the OpenAPI spec, plus the transport. No\n  business logic; every endpoint is a typed `client.api.<method>(path, …)` call.\n- **SDK layer** — `createAncherSdk(config | client)` → one ergonomic **entity\n  repository** per resource (`sdk.Note`, `sdk.File`, …). Repositories return\n  **plain typed data** (the wire shapes from `@ancher-ai/sdk/contracts` — no model\n  classes, no instance methods) and wrap multi-step flows into single calls\n  (e.g. `sdk.File.upload` runs the three-step presigned upload). Every\n  operation lives on the repository; per-record mutations take the id first\n  (`sdk.Note.update(id, patch)`, `sdk.Note.delete(id)`). Lists take a\n  TypeScript-native `{ where, orderBy, … }` options object (see\n  [Listing, filtering, counting](#listing-filtering-counting)). The raw client\n  stays reachable as `sdk.client` for anything the SDK layer doesn't wrap.\n\nUse the SDK layer by default; drop to the raw layer for endpoints it doesn't\ncover. The SDK layer is deliberately thin.\n\nTypes (entity aliases like `Note`, list-option types like `NoteListOptions`,\nand the runtime enum constants) ship from the **`@ancher-ai/sdk/contracts`**\nentrypoint.\n\n## Install\n\n```bash\npnpm add @ancher-ai/sdk\n# optional, only if you use the TanStack Query integration:\npnpm add @tanstack/react-query\n```\n\n## Quick start\n\n`baseUrl` is optional and defaults to `https://api.ancher.ai` (exported as\n`ANCHER_BASE_URL`) — pass it only to target another environment.\n\n### Server-side (static API key)\n\n```ts\nimport { createAncherClient } from '@ancher-ai/sdk'\n\nconst client = createAncherClient({\n  apiKey: process.env.ANCHER_API_KEY,\n  credentials: 'omit', // token auth, no cookies\n  // baseUrl defaults to https://api.ancher.ai; pass an origin to target another env:\n  // baseUrl: 'https://api.staging.ancher.ai',  // origin only — paths carry /api/v1\n})\n\n// Fully typed — path, query, body, and response are all checked.\nconst notes = await client.api.get('/api/v1/notes/', { query: { limit: 20 } })\nconst note = await client.api.post('/api/v1/notes/text', { body: { text: 'Hello' } })\n```\n\n### OAuth2 (standard authorization server / third parties)\n\nThe `oauth2` preset is for an actual OAuth2 server — Authorization Code + PKCE,\nconfidential clients (`clientSecret`), token endpoint + refresh — **without the\ntransport ever running the redirect dance** (your app owns the login UI). Its\ntoken lifecycle rides on `createTokenManager`.\n\n```ts\nimport { createAncherClient } from '@ancher-ai/sdk'\nimport { createOAuth2Auth } from '@ancher-ai/sdk/oauth2'\n\nconst oauth = createOAuth2Auth({\n  authorizationEndpoint: 'https://issuer.example.com/oauth2/authorize',\n  tokenEndpoint: 'https://issuer.example.com/oauth2/token',\n  clientId: 'my-app',\n  redirectUri: 'myapp://callback',\n  store: secureTokenStore,\n})\n\nconst { url, codeVerifier, state } = await oauth.getAuthorizationUrl()\n// host opens `url`; on redirect, verify `state`, then:\nawait oauth.exchangeCode({ code, codeVerifier })\n\nconst client = createAncherClient({ ...oauth.authConfig })\n```\n\n> **Why no username/password auth method?** The SDK is a headless surface:\n> interactive login (passwords, Google/Apple ID tokens) belongs to the product\n> web app. Machine clients use `apiKey` (server-to-server) or the OAuth2\n> preset's tokens (CLI/native) — the SDK stores **tokens, never a password**,\n> and the refresh token renews the session. The core client surface is\n> password-free, so the right choice is the obvious one.\n\n#### Pluggable token storage\n\n`store` is a `TokenStore` (`get`/`set`, both may be async). The SDK defaults to\nin-memory and ships **no** runtime-specific store — implement one for your\nclient. Tokens are plain JSON (`expiresAt` is epoch-ms, not a `Date`), so they\nserialize without any custom (de)serialization, and `set(null)` is the clear\nsignal (`logout` uses it).\n\nBrowser extension — back it with `chrome.storage.session`, and build the store\n(and the OAuth client that owns it) **only in the background service worker**:\n\n```ts\n// Background service worker ONLY — never a content script or page context.\nimport type { OAuth2TokenStore } from '@ancher-ai/sdk/oauth2'\n\nconst chromeStorageTokenStore: OAuth2TokenStore = {\n  async get() {\n    const { ancherTokens } = await chrome.storage.session.get('ancherTokens')\n    return ancherTokens ?? null\n  },\n  async set(tokens) {\n    if (tokens) await chrome.storage.session.set({ ancherTokens: tokens })\n    else await chrome.storage.session.remove('ancherTokens')\n  },\n}\n```\n\n> **Why `storage.session`, not `storage.local`?** `storage.session` is\n> restricted to trusted contexts by default (`TRUSTED_CONTEXTS`) — content\n> scripts, which share a page with arbitrary site code, can never read the\n> tokens. `storage.local` is readable from every extension context and survives\n> on disk. Keep the default access level (don't call `setAccessLevel`), and let\n> other contexts reach the API by messaging the worker rather than building\n> their own store. The trade-off: `storage.session` is cleared when the browser\n> restarts, so users sign in again per browser session. If you need sign-in to\n> survive restarts, treat that as a deliberate security decision — weigh the\n> broader exposure of `storage.local` (or encrypt-at-rest schemes) explicitly\n> rather than reaching for it as the default.\n\n> **Extension gotcha — centralize auth in the service worker.** The refresh\n> de-dup is per-manager-instance (per JS context). If the service worker, popup,\n> and content scripts each build their own client, a simultaneous expiry can fire\n> one refresh *per context* — a race with rotating refresh tokens. Build the\n> client once in the service worker and have other contexts call it via\n> `chrome.runtime.sendMessage`. (This is a context-sharing concern, not a storage\n> one — a shared `chrome.storage` store doesn't fix it.)\n\nOn mobile, implement the same interface over secure storage (Keychain /\nKeystore / Expo SecureStore).\n\n### Custom token source (`getAccessToken`)\n\nFor any token that isn't a static key — a rotating session token, a token from\nyour own store — provide `getAccessToken` directly. It's re-invoked on every\nrequest (and every retry after `refreshSession`), so always return the *current*\ntoken:\n\n```ts\nconst client = createAncherClient({\n  credentials: 'omit',\n  getAccessToken: () => tokenStore.getAccessToken(), // sync or async\n  refreshSession: async () => tokenStore.refresh(),  // return true to retry once\n})\n```\n\n#### Proactive refresh (`getSessionExpiresAt`)\n\nThe transport refreshes **reactively** on a 401 (retry once), and — when you\nalso provide `getSessionExpiresAt` — **proactively**: any request issued within\n`refreshLeewaySeconds` (default 120) of the expiry awaits a de-duplicated\n`refreshSession()` first, so the request never pays the 401 round trip. Failed\nrefreshes back off for ~30 s; an unknown (`null`) expiry disables the proactive\npath and leaves the reactive one. This works for token *and* cookie sessions —\na cookie host tracks expiry itself and returns it here:\n\n```ts\nconst client = createAncherClient({\n  refreshSession: async () => refreshMyCookieSession(), // e.g. PUT /web-session\n  getSessionExpiresAt: () => sessionExpiresAtMs,        // epoch-ms or null\n})\n```\n\nFor a hand-built request that bypasses the transport, run the same guarded\ncheck first with `client.ensureFreshSession()`. Don't set `getSessionExpiresAt`\nby hand for `createTokenManager`/OAuth2-preset clients — their `authConfig`\nalready supplies it from the token store.\n\nNeed proactive (pre-expiry) renewal, de-dup, and storage but with your own\nrefresh call? Use `createTokenManager` directly — it's the lifecycle core behind\nthe `oauth2` preset, and returns a ready-to-spread `authConfig`:\n\n```ts\nimport { createTokenManager } from '@ancher-ai/sdk'\n\nconst manager = createTokenManager({\n  refresh: async (current) => exchangeMyRefreshToken(current.refreshToken), // → ManagedTokens | null\n  store: myTokenStore, // optional; in-memory by default\n})\nawait manager.setTokens(initialTokens)\nconst client = createAncherClient({ ...manager.authConfig })\n```\n\n### TanStack Query\n\n```ts\nimport { createAncherClient } from '@ancher-ai/sdk'\nimport { createTanstackClient } from '@ancher-ai/sdk/tanstack'\nimport { useQuery } from '@tanstack/react-query'\n\nconst client = createAncherClient(/* ... */)\nconst tq = createTanstackClient(client)\n\nfunction Notes() {\n  const { data } = useQuery(tq.get('/api/v1/notes/', { query: { limit: 20 } }).queryOptions)\n  // ...\n}\n```\n\n### File upload\n\nUploading is a three-step **presigned S3 flow** (request URL → `PUT` bytes to S3\n→ finalize). The SDK layer wraps all three into one call:\n\n```ts\nimport { createAncherSdk } from '@ancher-ai/sdk'\n\nconst sdk = createAncherSdk({ apiKey: process.env.ANCHER_API_KEY })\nconst file = await sdk.File.upload(blob) // → plain File data ({ id, filename, size, mimetype, … })\n\n// For a Blob (no `.name`/`.type`), pass them explicitly:\nawait sdk.File.upload(blob, { filename: 'report.pdf', mimetype: 'application/pdf' })\n\n// Metadata by id:\nconst meta = await sdk.File.get(fileId)\n```\n\nUnder the hood that is exactly the raw-layer sequence below — reach for it\ndirectly only if you need to interleave the steps (custom S3 PUT, progress, etc.):\n\n```ts\n// 1. Ask the API for a presigned upload URL (typed JSON endpoint).\nconst { upload_url, s3_key } = await client.api.post('/api/v1/files/presigned-urls', {\n  body: { filename: file.name, mimetype: file.type },\n})\n\n// 2. PUT the raw bytes straight to S3 — NOT through the SDK. No Authorization\n//    header or cookies; Content-Type must match the `mimetype` from step 1.\nconst put = await fetch(upload_url, {\n  method: 'PUT',\n  headers: { 'Content-Type': file.type },\n  body: file,\n})\nif (!put.ok) throw new Error(`S3 upload failed: ${put.status}`)\n\n// 3. Finalize — the API fetches the object, hashes it, and creates the record.\nconst uploaded = await client.api.post('/api/v1/files/completions', {\n  body: { s3_key, filename: file.name },\n}) // → Schemas.File: { id, filename, size, mimetype, … }\n```\n\n> Presigned URLs are short-lived (60–3600s, default 300), and the API sniffs the\n> real MIME type from the bytes on completion — your `mimetype` is a hint, not\n> authoritative. You can optionally confirm integrity afterward with\n> `POST /api/v1/files/{file_id}/verifications`.\n\n#### Direct multipart (small files)\n\nFor a one-shot small upload you can skip the dance and post multipart straight to\nthe API with the typed `sdk.File.uploadDirect`:\n\n```ts\nconst uploaded = await sdk.File.uploadDirect(file, {\n  onProgress: (pct) => console.log(pct), // 0–100; uses XMLHttpRequest when set. 100 only on success\n  // public: true,                       // create publicly readable (default false)\n  // signal: abortController.signal,     // optional cancellation\n})\n```\n\nUnderneath sits `client.upload`, a standalone `multipart/form-data` helper (the\ngenerated client is JSON-only) for endpoints without a typed wrapper — you pass\nthe response type, and the endpoint string is unchecked. It sends one field\n(default `file`, override via `fieldName`; extra scalar fields via `fields`) and\nshares the same auth / refresh / activation / error handling as the rest of the\nclient.\n\nOn **React Native** pass the platform's file descriptor instead of a `Blob` —\nthe native networking layer streams the file, so no bytes cross into JS:\n\n```ts\n// `NativeFilePart` — only React Native's FormData understands it.\nawait sdk.File.uploadDirect({ uri: asset.uri, name: 'photo.jpg', type: 'image/jpeg' }, { onProgress })\nawait sdk.File.uploadBatch([{ uri, name, type }, { uri, name, type }])\n```\n\n## Entities (SDK layer)\n\n`createAncherSdk(config | client)` exposes a repository per entity. Repositories\nhold **every** operation for their resource — `create`/`get`/`list` plus\nid-keyed per-record mutations (`update(id, patch)`, `delete(id)`, …) — and\nreturn plain typed data:\n\n```ts\nimport { createAncherSdk } from '@ancher-ai/sdk'\nimport { NoteStatus, type Note } from '@ancher-ai/sdk/contracts'\n\nconst sdk = createAncherSdk({ apiKey: process.env.ANCHER_API_KEY })\n\nconst note = await sdk.Note.createFromUrl({ text: 'https://example.com' }) // plain Note\nawait sdk.Note.update(note.id, { title: 'Renamed' })  // PATCH, id first\nawait sdk.Note.delete(note.id)                                 // DELETE\n\nconst page = await sdk.Note.list({ where: { status: NoteStatus.Ready }, limit: 20 }) // Page<Note>\n```\n\n### File slots\n\nNever read `note.files` / `note.article.files` / `artifact.files` directly —\nthe file-slot accessors in `@ancher-ai/sdk/contracts` are the one place the\nAPI's resolution rules live, and they accept every payload shape the API has\nor will have (the ≤ v1.4 `files` map, v1.5.0's typed `content_file` /\n`thumbnail_file` / … slots, and the post-flatten note with `origin_files`\nhoisted onto it), so the restructure is an SDK bump and nothing else:\n\n```ts\nimport {\n  getFileContentKey,\n  getFileRevisionNumber,\n  getFileUrl,\n  getNoteContentFile,\n  getNoteOriginFiles,\n  getNoteOwnContentFile,\n  getNoteThumbnailFile,\n  getArtifactThumbnailFile,\n} from '@ancher-ai/sdk/contracts'\n\nconst thumbnailUrl = getFileUrl(getNoteThumbnailFile(note))   // note → article → signed image content\nconst origins = getNoteOriginFiles(note)                       // N files, API order\nconst editTarget = getNoteOwnContentFile(note)                 // the PUT-able, note-owned copy only\nconst revision = getFileRevisionNumber(editTarget)             // save-preflight snapshot\nconst cardImage = getFileUrl(getArtifactThumbnailFile(artifact)) // thumbnail → display (check the mimetype before <img>)\nconst cacheKey = getFileContentKey(file)                       // `id:content_hash` — changes with the bytes, not the URL\n```\n\nEach accessor documents the `app/schemas/*.py` property it mirrors. Two\ndeliberate steps beyond the backend: note accessors fall back to the article's\nfile (the API does not), and the thumbnail falls back to a signed image content\nfile (direct image uploads), then to any image in the note's own map (which\ncarries `generated` files on API ≤ v1.4). `getFileUrl` is `undefined` for a note's\n`content` (served by `GET /notes/{id}/content`) and for markdown/HTML artifact\ncontent — fetch those through the content endpoints. The accessors are generic\non the file type, so a client with its own generated `Note`/`File` gets its own\n`File` back. `asFileMap` remains exported but is deprecated.\n\nTwo more file reads cover what the v1.5.0 payload moved:\n\n```ts\nimport { getFileImageSize, getNoteOriginDisplayFile } from '@ancher-ai/sdk/contracts'\n\nconst size = getFileImageSize(file)                          // { width, height } from File.metadata (≤ v1.4: revision.s3_object.meta)\nconst rendition = getNoteOriginDisplayFile(note, originFile) // the display PDF that belongs to THIS origin, by parent_file_id\n```\n\n### Note source fields\n\nv1.5.0 hides the nested `article` and hoists its provenance onto the note\n(`url`, `site_name`, `author`, `published_date`, `language`, plus the\noverride-resolved `title` / `description`). Read them through the source\naccessors, which fall back to the nested article on older payloads, instead\nof `note.article.*`. (`published_date` is typed `string` but arrives as Unix\nseconds, like every API datetime — normalize before `new Date`.)\n\n```ts\nimport {\n  getNoteAuthor,\n  getNoteErrorMessage,\n  getNoteProcessingStatus,\n  getNoteSiteName,\n  getNoteSource,\n  getNoteSourceUrl,\n  getNoteTitle,\n} from '@ancher-ai/sdk/contracts'\n\nconst title = getNoteTitle(note)               // user override → article title\nconst url = getNoteSourceUrl(note)             // absent for text / file notes\nconst label = getNoteSiteName(note) ?? host     // banner label\nconst status = getNoteProcessingStatus(note)   // article-side queued/processing/error won on ≤ v1.4\n```\n\n`getNoteSource` is the one that reads the article first: the ≤ v1.4 note-level\n`source` could be a stale literal default.\n\n### Listing, filtering, counting\n\nEvery criteria list endpoint exposes the same typed surface:\n\n- **`list(options?)`** → `Promise<Page<X>>` — one page.\n- **`count(where?)`** → `Promise<number>` — matching-record count (a `limit: 1`\n  list reading `total`).\n- **`iterate(options?)`** → `AsyncGenerator<X>` — items lazily across pages.\n\n`options` is `{ where?, orderBy?, limit?, offset? }` for a first/offset page,\n**or** `{ cursor }` (optionally with a matching `limit`) for a continuation\npage. The per-entity option\ntypes (`NoteListOptions`, `NoteWhere`, `NoteOrderBy`, …) are exported from\n`@ancher-ai/sdk/contracts` and are *derived from the generated OpenAPI criteria\ntypes*, so each endpoint's exact filter surface (which fields exist, which\nrelations are filterable) flows through codegen and is checked at compile time.\n\n```ts\nimport { likePattern, NoteStatus } from '@ancher-ai/sdk/contracts'\n\nconst page = await sdk.Note.list({\n  where: {\n    status: NoteStatus.Ready,                    // scalar shorthand = equals\n    reaction: null,                              // null shorthand   = isNull: true\n    created_at: { gte: new Date('2026-01-01') }, // Date accepted on *_at / *_date fields\n    title: { ilike: likePattern(searchTerm) },   // escaped substring search\n    tags: { id: { in: tagIds } },                // nested relation (endpoints that support it)\n    OR: [{ title: { ilike: q } }, { description: { ilike: q } }],\n  },\n  orderBy: ['-updated_at', '-id'],               // signed sort keys; a single key may be bare\n  limit: 20,\n})\n\n// Continuation: the cursor already encodes criteria/order/limit — send it ALONE.\nif (page.has_more && page.next_cursor) {\n  const next = await sdk.Note.list({ cursor: page.next_cursor })\n}\n\n// Count without fetching items:\nconst readyCount = await sdk.Note.count({ status: NoteStatus.Ready })\n\n// Iterate across pages lazily — `break` stops network traffic; `limit` is the\n// per-page fetch size. Terminates on `has_more === false` (the API mints a\n// next_cursor even on the final page, so never loop on cursor truthiness).\nfor await (const note of sdk.Note.iterate({ where: { status: NoteStatus.Ready }, limit: 100 })) {\n  if (isEnough(note)) break\n}\n```\n\n**`where` semantics** (translated to the wire criteria DSL by the SDK):\n\n- **Field keys mirror the wire names** (`created_at`, `site_name`, `url`, …).\n  A bare scalar means *equals*; `null` means *is unset* (`isNull: true`).\n- **Operator objects** use camelCase Prisma-style names: `equals`, `not`, `in`,\n  `notIn`, `lt`, `lte`, `gt`, `gte`, `contains`, `notContains`, `startsWith`,\n  `endsWith`, `like`, `ilike`, `notLike`, `isNull`. The SDK renames them to the\n  wire operators (`lte`/`gte` become the wire's `le`/`ge`, `equals` → `eq`, …).\n- **`{ equals: null }`** → `isNull: true`; **`{ not: null }`** → `isNull: false`.\n  `null` in any other operand position throws a `TypeError` (the API would\n  silently drop the filter and return everything).\n- **`undefined` values are skipped** at field/operator positions, so\n  conditional filters are just `status: onlyReady ? NoteStatus.Ready :\n  undefined` — no conditional spreads. One caveat: an `AND`/`OR` *branch* whose\n  every filter is undefined throws a `TypeError` (silently changing what the\n  group matches would be worse) — conditionalize the array\n  (`OR: cond ? [a, b] : [a]`), not a branch's fields.\n- **`AND: [...]` / `OR: [...]` / `NOT: {...}`** compose sub-criteria (`NOT`\n  takes a *single* object). Multi-field `NOT` means **none-of**:\n  `NOT: { a, b }` excludes rows matching `a` and rows matching `b` (¬a ∧ ¬b) —\n  unlike Prisma's object form, which negates the conjunction. To negate a\n  conjunction, De Morgan it with per-field negative operators\n  (`OR: [{ a: { not: … } }, …]`). Empty `AND: []` / `OR: []` throw a\n  `TypeError` (the API would silently match everything). Nested **relation** filters\n  (`tags: { id: { in } }`) are only legal at the root of a `where` — the types\n  enforce this, matching the wire contract.\n- **`in: []` matches nothing** and `notIn: []` matches everything — both are\n  forwarded verbatim, not optimized away.\n- **Dates**: datetime operands accept `Date` (serialized to ISO 8601, including\n  inside `in`/`notIn` arrays) or a pre-formatted ISO string. Note the asymmetry:\n  entity timestamps in *responses* are Unix **seconds** — convert with\n  `new Date(note.created_at * 1000)` before using one as a criteria operand.\n- **`likePattern(term)`** escapes the LIKE wildcards (`%`, `_`, `\\`) and wraps\n  the term in `%…%` — always use it when passing user input to `like`/`ilike`,\n  otherwise a user searching `\"100%\"` gets wildcard semantics.\n- **`orderBy`** takes the same signed strings as the wire (`'-updated_at'`\n  desc, `'+title'` asc), single or array; the per-entity `XOrderBy` unions are\n  generated so invalid keys fail typecheck.\n- **A cursor cannot carry criteria or ordering.** `{ cursor }` with any of\n  `where`/`orderBy`/`offset` throws a `TypeError` client-side (the API rejects\n  the combination with a 422 — the cursor already encodes them). `limit` is\n  allowed alongside a cursor when it matches the cursor's encoded page size,\n  so re-running the same call with the cursor appended keeps working.\n- **Unknown top-level option keys throw** a `TypeError` — stale raw-DSL params\n  (`order_by`, `and`, `status: { eq }`, …) fail loudly instead of silently\n  returning an unfiltered list.\n\nScoped lists take their path params first and the same typed options:\n`sdk.Note.suggestedCollections(noteId, options?)`,\n`sdk.Collection.notes(collectionId, options?)`,\n`sdk.Collection.artifacts(collectionId, options?)`,\n`sdk.Collection.suggestedNotes(collectionId, options?)`,\n`sdk.Message.list(conversationId, options?)`, and\n`sdk.File.revisions(noteId, fileId, options?)`. Each returns a single page;\n`count`/`iterate` exist on the top-level repositories.\n\n### Runtime enums\n\nEnum-like unions ship as **const objects with a same-name type** from\n`@ancher-ai/sdk/contracts`. Values are the plain wire strings, so\n`note.status === 'ready'` still typechecks — use the constant where it reads\nbetter, and `isEnumValue` to validate untyped input (CLI flags, URL params):\n\n```ts\nimport { isEnumValue, NoteStatus } from '@ancher-ai/sdk/contracts'\n\nif (note.status === NoteStatus.Ready) { /* … */ }\n\nconst raw: string = searchParams.get('status') ?? ''\nif (isEnumValue(NoteStatus, raw)) {\n  // raw: NoteStatus\n}\n```\n\nEach constant carries a two-directional compile-time drift check against the\ngenerated schema union (`satisfies` catches extra values, an `Expect<Eq<…>>`\nline catches missing ones), so a codegen refresh that changes the API union\nbreaks `pnpm typecheck` until the constant is updated — the enums can never\nsilently drift from the API.\n\nAvailable: `NoteStatus` (+ `ArticleStatus` alias), `ReactionType`,\n`PaletteColor`, `CollectionStatus`, `MessageRole`, `ConversationRunStatus`,\n`ClarificationAction`, `ClarificationResolutionStatus`, `SelectedContentType`,\n`ResourceAttachmentType`, `NotificationType`, `NotificationStatus`,\n`DailyDigestStatus`, `PinType`, `RecommendationStatus`, `RecommendationAction`,\n`SuggestionStatus`, `OAuthProvider`, `BillingProvider`, `BillingInterval`,\n`PlanKind`, `SubscriptionStatus`, `PlanChangeKind`, `CheckoutSessionStatus`,\n`CheckoutPaymentStatus`, `CheckoutMode`, `DiscountRedemptionOutcome`. Derived\nrequest-body unions: `SuggestionResolution` (`SuggestionStatus` minus\n`'pending'`), `NotificationStatusUpdate` (`NotificationStatus` minus\n`'unread'`).\n\n### Entity repositories\n\n| `sdk.X` | Operations |\n| --- | --- |\n| `Note` | `list`/`count`/`iterate`, `createFromText`/`FromUrl`/`FromFile`/`FromArtifact`/`FromConversation`/`FromMessage`, `get`, `getBySlug`, `getContent(id, options?)` (raw `Response`), `getFile(noteId, fileId)`, `updateFileContent(noteId, fileId, file, options?)`, `displayPresignedUrl(id, options?)`, `downloadDisplay(id, options?)`, `filePresignedUrl(noteId, fileId, options?)`, `downloadFile(noteId, fileId, options?)`, `suggestedCollections(noteId, options?)`, `update(id, patch)`, `delete(id)`, `retry(id)`, `copy(id)`, `setTags(id, body)` |\n| `File` | `upload` (presigned 3-step), `uploadDirect(file, options?)` (one-shot multipart with `onProgress`/`signal`), `uploadBatch(files, options?)` (multipart), `get`, `verify(id)`, `presignedUrl(id, options?)`, `download(id, options?)`, `delete(id)`, `revisions(noteId, fileId, options?)`, `revertRevision(noteId, fileId, revisionId)` |\n| `User` | `me`, `update`, `preferences`/`updatePreferences`, `demographic`/`updateDemographic`, `featureFlags`, `completeTutorial`, `changePassword`, `submitActivationCode`, `regenerateInvitationCode`, `delete` |\n| `ApiKey` | `list()` (no params → `ApiKey[]`), `create`¹, `delete(id)` (revoke) |\n| `Artifact` | `list`/`count`/`iterate`, `get`, `getBySlug`, `getContent(id, options?)` (raw `Response`), `create`, `update(id, patch)`, `updateContent(id, file, options?)`, `delete(id)`, `presignedUrl(id, options?)`, `filePresignedUrl(artifactId, fileId, options?)`, `download(id, options?)` |\n| `Tag` | `list`/`count`/`iterate`, `create`, `update(id, patch)`, `delete(id)` |\n| `Collection` | `list`/`count`/`iterate`, `get`, `create`, `update(id, patch)`, `delete(id)`, `notes(id, options?)` + `addNote`/`setNotes`/`removeNote`, `artifacts(id, options?)` + `addArtifact`/`setArtifacts`/`removeArtifact`, `suggestedNotes(id, options?)` |\n| `Conversation` | `list`/`count`/`iterate`, `get`, `start`²/`send`², `startChat`²/`chat`²/`stream`², `interrupt(id)`, `update(id, patch)`, `delete(id)` |\n| `Message` | `list(conversationId, options?)`, `get(conversationId, id)`, `update(conversationId, id, patch)` |\n| `Pinned` | `list`/`count`/`iterate`, `pin`, `reorder`, `unpin(entityId)` |\n| `Notification` | `list`/`count`/`iterate`, `setStatus(id, body)`, `markRead(id)`, `markDismissed(id)` |\n| `Suggestion` | `list`/`count`/`iterate`, `update(id, body)`, `accept(id)`, `dismiss(id)`, `acceptMany(ids)`, `dismissMany(ids)` |\n| `Recommendation` | `list`/`count`/`iterate`, `save(id)`, `dismiss(id)`, `notInterested(id)` |\n| `DailyDigest` | `list`/`count`/`iterate`, `get` (read-only — digests are generated server-side) |\n| `Session` | `list`/`count`/`iterate`, `current` (token session), `revoke(id)`, `revokeAll` |\n| `Connection` | `list()` (no params → `Connection[]`), `connect`, `delete(id)` |\n\n**Action services** (singleton, no entity — return typed results directly):\n\n| `sdk.X` | Operations |\n| --- | --- |\n| `Billing` | `credits`, `subscription`, `redeemDiscountCode`, `createCheckoutSession`, `getCheckoutSession`, `changePlan`, `createPortalSession`, `verifyPurchase`, `plans` |\n| `Device` | `setNotificationToken` |\n| `Onboarding` | `status`, `claimReward(task)` (onboarding checklist + its credit rewards) |\n| `Retrieval` | `notes`, `chunks` (RAG) |\n| `ImagePrompt` | `generate` (multipart) |\n| `TextSelection` | `explain`, `summarize`, `translate` (selection toolbar) |\n\n¹ `ApiKey.create` returns `ApiKeyCreateResponse` — it carries the one-time\nplaintext secret; surface it immediately, never persist it.\n² Chat is streaming. `start`/`send` POST and return a `ConversationRunReceipt`\n(HTTP 202) for fire-and-forget; `chat`/`startChat`/`stream` consume the SSE\nstream as structured **`ChatEvent`**s (see below).\n\n> **Coverage.** Every product operation in the spec has a typed SDK method,\n> with one deliberate exception class: **interactive account and browser-auth\n> flows** are left to the raw `client.api`. That covers registration\n> (`POST /users`), email verification (`PUT /users/verification`,\n> `POST /users/verification-requests`), the password-reset trio\n> (`/users/password-reset-*`, `PUT /users/password`), the `/web-session`\n> cookie-session lifecycle (incl. Google/Apple web login), the token-session\n> login/refresh/logout routes (`POST`/`PUT`/`DELETE /session`,\n> `POST /session/{provider}` — the OAuth2 preset owns machine tokens instead),\n> the external-connection OAuth **callback** redirect, and\n> `POST /web-verification` — human flows that belong to the product apps, not\n> a headless SDK.\n\n### Chat streaming\n\nThe conversation SSE stream is surfaced as a typed `ChatEvent` discriminated\nunion — no raw envelopes. The live assistant text, tool activity, citations, and\nclarifications are fanned out into structured events, each text/tool event\ncarrying a typed `AgentRun` (with `isMain` to tell the user-visible agent from\nsub-agents). Consume it as an **async iterator** or with **callbacks**.\n\n```ts\n// Async iterator — send a message and stream the structured reply:\nfor await (const event of sdk.Conversation.chat(conversationId, { content: 'Summarize my notes' })) {\n  switch (event.type) {\n    case 'text':       process.stdout.write(event.text); break\n    case 'tool-call':  console.log('↳', event.toolName, event.args); break\n    case 'citations':  console.log(event.citations.map((c) => c.url)); break\n    case 'done':       console.log('\\n', event.finishReason); break\n    case 'error':      throw new Error(event.message)\n  }\n}\n\n// Callbacks — `consumeChat` drives the stream and accumulates main-agent text:\nimport { consumeChat } from '@ancher-ai/sdk'\n\nconst { text, finishReason } = await consumeChat(sdk.Conversation.stream(conversationId), {\n  onText: (t) => append(t),\n  onToolCall: (call, run) => trace(run.agentName, call.toolName),\n  onClarificationRequested: (c) => askUser(c),\n})\n\n// Start a brand-new conversation and stream in one call:\nfor await (const event of sdk.Conversation.startChat({ content: 'Hi' })) { /* … */ }\n\n// Resume after a disconnect, and interrupt:\nconst controller = new AbortController()\nconst stream = sdk.Conversation.stream(id, { after: lastEventId, signal: controller.signal })\nawait sdk.Conversation.interrupt(id) // stops the active run (surfaces as `done`, finishReason 'cancelled')\n```\n\n`ChatEvent` types: `text`, `thinking`, `tool-call`, `tool-return`, `narration`,\n`review-rejected`, `citations`, `resource-updated`, `clarification-requested`,\n`clarification-resolved`, `done`, `error`. Unmapped internal trace events are\ndropped, so you only ever see meaningful, typed events.\n\n#### Bring your own transport (React Native)\n\nReact Native's built-in `fetch` has no streaming `body`, so the SSE helpers\nabove cannot read it there (`parseChatStream` throws rather than yielding an\nempty run — and only once the whole run has finished, since that `fetch`\nresolves on load). Injecting a streaming `fetch` via `config.fetch` lifts that;\notherwise bring your own transport:\n`prepareConversationStream` resolves the URL and auth headers **without\nsending**; open the request over your own transport (e.g. `XMLHttpRequest`),\nkeep the SDK's 401 → refresh → retry policy with `sendWithSessionRefresh`, and\nrun the received text through `processSSEBuffer` — the buffer-level SSE\nreducer. It returns the incomplete tail, which you must carry into the next\ncall (a frame split across two chunks is otherwise lost), and it emits the\n`SSEHandlers` event vocabulary (`onText`, `onToolCall`, …), not the typed\n`ChatEvent`s of `streamConversation`:\n\n```ts\nimport {\n  createInitialStreamState,\n  prepareConversationStream,\n  processSSEBuffer,\n  sendWithSessionRefresh,\n} from '@ancher-ai/sdk'\n\nconst request = prepareConversationStream(client, receipt.stream_url)\nconst opened = await sendWithSessionRefresh(\n  client.config,\n  async () => openXhr(request.url, await request.headers(), signal), // resolves once headers arrive → { status, … }\n  signal\n)\nif (opened.status !== 200) { /* … */ }\n\n// The opener owns cancellation — `sendWithSessionRefresh` only uses `signal`\n// to bound the refresh waits and to skip the replay after a cancel:\nfunction openXhr(url, headers, signal) {\n  if (signal?.aborted) throw signal.reason ?? new Error('Aborted') // a listener never fires retroactively\n  const xhr = new XMLHttpRequest()\n  signal?.addEventListener('abort', () => xhr.abort(), { once: true })\n  // … open, set headers, resolve { status } once the headers arrive\n}\n\n// Parsing: keep the tail between chunks.\nconst state = createInitialStreamState()\nlet buffer = ''\nonChunk = chunk => {\n  buffer = processSSEBuffer(buffer + chunk, state, handlers)\n}\n```\n\n`request.headers()` must be called anew on every attempt — a replay after a\nrefresh needs the refreshed credentials and a fresh `traceparent` span. On a\n401 the SDK refreshes and calls your opener *again* while the first request is\nstill open, so abort it (or ignore its later callbacks); a denied/unreachable\nrefresh *returns* the 401 result instead of throwing — check `opened.status`.\nThe 403 activation gate and `onError` do not run on this path.\n\n## Configuration\n\n`createAncherClient(config)` — every browser/app concern is an injectable hook,\nso the same client runs in a browser, a Node service, an edge worker, or a test.\n\n| Option | Purpose |\n| --- | --- |\n| `baseUrl` | API **origin** only, e.g. `https://api.ancher.ai` (no `/api/v1`, no trailing slash — generated paths already carry the prefix). Defaults to `ANCHER_BASE_URL` (`https://api.ancher.ai`). |\n| `fetch` | Custom `fetch` (Node, undici, mock). Defaults to global `fetch`. |\n| `credentials` | Request credentials mode. Defaults to `'include'`. |\n| `getAccessToken` | Returns the current bearer token (OAuth2 access token, rotating mobile token, …). Re-invoked per request; takes precedence over `apiKey`. |\n| `apiKey` / `apiKeyHeader` | Static bearer token — sugar for a constant `getAccessToken`. Defaults to `Authorization: Bearer`. |\n| `defaultHeaders` | Headers merged into every request. |\n| `getCsrfToken` | Returns the CSRF token → `X-CSRF-Token`. |\n| `getDeviceId` | Returns a device ID → `x-device-id`. |\n| `getTimezone` | Returns an IANA timezone → `x-timezone`. |\n| `timeoutMs` | Per-request deadline in ms (also bounds the proactive/reactive session refresh). Off by default. Rejects with a `TimeoutError` — also on React Native, whose `AbortController` cannot carry an abort reason. |\n| `refreshSession` | Called on 401; return `true` to retry once, `'denied'` (server rejected it) or `'unreachable'` (network error / 5xx) otherwise. `classifySessionRefresh(response)` maps a refresh response onto those. |\n| `onSessionExpired` | Called when a 401 could not be recovered because `refreshSession` returned `'denied'` — the session is dead, so clear client state and send the user to sign in. Never fires for `'unreachable'`. |\n| `getSessionExpiresAt` | Epoch-ms expiry of the session credential (or `null` = unknown). Enables proactive refresh before requests issued within the leeway. Supplied automatically by `createTokenManager`'s `authConfig`. |\n| `refreshLeewaySeconds` | Refresh this many seconds before `getSessionExpiresAt`. Default 120. |\n| `onActivationRequired` | Called on the 403 activation gate (`API-USR010`); return `'retry'`. |\n| `onError` | Side effect on every error (e.g. open insufficient-credits dialog on `API-BIS002`). |\n\n## Errors\n\nNon-2xx responses are normalized into `AncherApiError` (`message`, `status`,\n`code`, `details`, `body`). Helpers:\n\n```ts\nimport {\n  AncherApiError,\n  isAncherApiError,\n  isInsufficientCreditsError,   // HTTP 402 / API-BIS002\n  isActivationRequiredError,    // HTTP 403 / API-USR010\n} from '@ancher-ai/sdk'\n```\n\n> When you call with `withResponse: true`, the error response is returned (not\n> thrown) so you can inspect `.status` / `.data`; `onError` still fires.\n\nA **success** response can throw too, on every transport (the JSON client and\n`client.upload` alike): a JSON body that does not parse, or an empty JSON body\non a status other than `204`/`205`, is a truncated or broken response and\nsurfaces as an `AncherApiError` carrying that success `status` (e.g. `200`)\nrather than resolving `undefined` typed as the entity. This is a rule about\nJSON bodies — `text/*` responses come back verbatim and binary ones as an\n`ArrayBuffer`, empty or not.\n\n## Regenerating from the API (`pnpm generate`)\n\nThe typed client under `src/api/generated/` is **codegen output** — never\nhand-edit it. To refresh against the live API:\n\n```bash\npnpm generate\n```\n\nThis runs two steps (same pipeline as the source app):\n\n1. `node scripts/fetch-openapi.js [url]` — downloads the OpenAPI spec to\n   `openapi.json` and rewrites it: strips ignored tags (`Feishu`, `Slack`,\n   `OAuth2`), the transport-injected header params (`x-csrf-token`,\n   `x-device-id`, `x-timezone`), and the `x-data-frame-schema` extensions that\n   the codegen ref parser can't resolve. Default URL:\n   `https://api.ancher.ai/api/v1/openapi.json` (override with a `<url>` argument\n   or the `ANCHER_OPENAPI_URL` env var).\n2. `typed-openapi openapi.json --output src/api/generated/api.client.ts --tanstack` —\n   regenerates `api.client.ts` (the typed `ApiClient`) and `tanstack.client.ts`\n   (the `TanstackQueryApiClient`).\n\nThe hand-written enum constants and `Where` types in `src/contracts/` are\ndrift-checked against the generated output at compile time, so if a refresh\nchanges an enum union or names a field like a filter operator, `pnpm typecheck`\nfails and points at what to update.\n\n## Build\n\n```bash\npnpm build       # tsup → dist (ESM + CJS + .d.ts): index, oauth2, tanstack, contracts\npnpm typecheck   # tsc --noEmit\npnpm verify:package # pack + install + strict external TS/ESM/CJS consumer smoke test\n```\n\n## Publishing\n\nThe first npm release is a one-time manual bootstrap because npm requires the\npackage to exist before its GitHub Actions Trusted Publisher can be configured.\nFrom `apps/sdk`, after setting `package.json` to the version being released:\n\n```bash\npnpm typecheck\npnpm test\npnpm verify:package\npnpm pack\nnpm publish ancher-ai-sdk-X.Y.Z.tgz --access public\n```\n\nThe publishing npm account must have write access to the `@ancher-ai` scope and\nmeet npm's 2FA requirements. After that first publish, configure the package's\nGitHub Actions Trusted Publisher with owner `streamify-one`, repository\n`streamify-one-design-system`, workflow `publish-npm.yml`, and no environment.\n\nLater releases are automated: update the package version, then publish a GitHub\nRelease tagged `sdk-vX.Y.Z`. The release workflow requires the tag version to\nmatch `package.json`, reruns the SDK gates and external-package smoke test, packs\nthe package with pnpm, and publishes the tarball to npm over OIDC.\n\n## Layout\n\n```\nsrc/\n├── index.ts                Public surface (raw layer + repository layer re-exports)\n├── api/                    RAW layer — typed client + transport\n│   ├── client.ts           createAncherClient\n│   ├── config.ts           AncherClientConfig + ANCHER_BASE_URL\n│   ├── transport.ts        Fetcher: CSRF/device/tz headers, criteria encoding, error normalization\n│   ├── auth.ts             Shared auth + request lifecycle (token header, 401/403 retry)\n│   ├── token-manager.ts    Token lifecycle core (proactive/reactive refresh, dedup, storage)\n│   ├── upload.ts           Multipart upload helper (+ progress)\n│   ├── errors.ts           AncherApiError + code helpers\n│   ├── error-codes.ts      Generated error-code catalog — do not edit\n│   └── generated/          Codegen output — do not edit (api.client.ts, tanstack.client.ts)\n├── contracts/              Type surface + runtime helpers (@ancher-ai/sdk/contracts)\n│   ├── index.ts            Barrel — the one import source for consumers\n│   ├── schemas.ts          Re-export of the generated Schemas namespace\n│   ├── common.ts           Page, UUID, BaseEntity, endpoint-query helpers\n│   ├── query.ts            Typed list surface: Where/ListOptions operators + likePattern\n│   ├── assert.ts           Expect/Eq drift-check helpers + isEnumValue\n│   └── <entity>.ts         Per-domain aliases, enum constants, XWhere/XOrderBy/XListOptions\n│                           (note, artifact, collection, conversation, notification, pin,\n│                            recommendation, suggestion, file, billing, auth, user, …)\n├── repositories/           Repository layer — plain-data repos over client.api\n│   ├── base.ts             Page<T> + createListSurface (list/count/iterate factory)\n│   ├── query.ts            buildListQuery: {where,orderBy,…} → wire criteria DSL\n│   ├── index.ts            Re-exports\n│   └── <entity>.ts         createXRepository (note, file, user, api-key, artifact, tag,\n│                            collection, conversation, message, pinned, notification,\n│                            suggestion, recommendation, session, connection)\n├── create-ancher-sdk.ts    createAncherSdk → { client, Note, File, User, … }\n├── services.ts             Action/singleton services (Billing, Device, Retrieval, ImagePrompt, TextSelection)\n├── chat.ts                 Structured SSE chat-stream consumer (ChatEvent, consumeChat)\n├── presets/\n│   └── oauth2.ts           OAuth2 preset — PKCE, token exchange (optional entry)\n└── tanstack.ts             TanStack Query integration (optional entry)\n```\n","readmeFilename":"README.md"}