From aa5af3e51e8942d0393f10f64850ff631c2c65b1 Mon Sep 17 00:00:00 2001 From: Carey Janecka Date: Sat, 14 Mar 2026 23:57:54 -0700 Subject: [PATCH 1/2] feat(codestorage): add initial repo provider for clone/push runtime --- packages/plugin-codestorage/package.json | 23 +++ packages/plugin-codestorage/plugin.yaml | 4 + packages/plugin-codestorage/src/repo.ts | 188 +++++++++++++++++++++++ packages/worker/package.json | 1 + packages/worker/src/repos/packages.ts | 5 +- pnpm-lock.yaml | 41 ++--- 6 files changed, 235 insertions(+), 27 deletions(-) create mode 100644 packages/plugin-codestorage/package.json create mode 100644 packages/plugin-codestorage/plugin.yaml create mode 100644 packages/plugin-codestorage/src/repo.ts diff --git a/packages/plugin-codestorage/package.json b/packages/plugin-codestorage/package.json new file mode 100644 index 00000000..0f78e3c5 --- /dev/null +++ b/packages/plugin-codestorage/package.json @@ -0,0 +1,23 @@ +{ + "name": "@valet/plugin-codestorage", + "version": "0.0.1", + "private": true, + "type": "module", + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "exports": { + "./repo": "./src/repo.ts" + }, + "scripts": { + "build": "tsc", + "typecheck": "tsc --noEmit", + "test": "vitest run" + }, + "dependencies": { + "@valet/sdk": "workspace:*" + }, + "devDependencies": { + "typescript": "^5.3.3", + "vitest": "^4.0.18" + } +} diff --git a/packages/plugin-codestorage/plugin.yaml b/packages/plugin-codestorage/plugin.yaml new file mode 100644 index 00000000..d164c803 --- /dev/null +++ b/packages/plugin-codestorage/plugin.yaml @@ -0,0 +1,4 @@ +name: codestorage +version: 0.0.1 +description: code.storage repo provider for git clone/push session runtime +icon: "🧱" diff --git a/packages/plugin-codestorage/src/repo.ts b/packages/plugin-codestorage/src/repo.ts new file mode 100644 index 00000000..5bc470ff --- /dev/null +++ b/packages/plugin-codestorage/src/repo.ts @@ -0,0 +1,188 @@ +import type { + RepoProvider, + RepoCredential, + RepoList, + RepoValidation, +} from '@valet/sdk/repos'; + +const CODESTORAGE_URL_RE = /(?:https?:\/\/)?(?:[^@]+@)?([a-z0-9._-]+\.code\.storage)\/(.+?)(?:\.git)?$/i; + +type ParsedRepoUrl = { + host: string; + path: string; + cloneUrl: string; +}; + +function parseCodeStorageRepoUrl(repoUrl: string): ParsedRepoUrl | null { + const normalized = repoUrl.trim(); + const match = normalized.match(CODESTORAGE_URL_RE); + if (!match) return null; + + const host = match[1]; + const path = match[2].replace(/^\/+/, '').replace(/\.git$/i, ''); + return { + host, + path, + cloneUrl: `https://${host}/${path}.git`, + }; +} + +function withCodeStorageUsername(url: string): string { + // code.storage docs use username "t" with bearer/password token auth. + // Keep this in the URL to ensure git credential lookups include a username. + return url.replace(/^https:\/\//i, 'https://t@'); +} + +function parseApiBase(credential: RepoCredential): string { + const explicit = credential.metadata?.apiBase || credential.metadata?.api_base; + if (explicit) return explicit.replace(/\/$/, ''); + + const issuer = credential.metadata?.issuer; + if (issuer) return `https://api.${issuer}.code.storage/api/v1`; + + return ''; +} + +function asToken(credential: RepoCredential): string { + const token = credential.accessToken || credential.metadata?.token || credential.metadata?.access_token; + if (!token) throw new Error('code.storage repo provider requires an access token'); + return token; +} + +async function codestorageFetch(apiBase: string, path: string, token: string): Promise { + return fetch(`${apiBase}${path}`, { + headers: { + Authorization: `Bearer ${token}`, + Accept: 'application/json', + 'User-Agent': 'Valet', + }, + }); +} + +export const codestorageRepoProvider: RepoProvider = { + id: 'codestorage', + displayName: 'code.storage', + icon: 'package', + supportsOrgLevel: true, + supportsPersonalLevel: true, + urlPatterns: [/\.code\.storage\//i], + + async listRepos(credential: RepoCredential, opts?): Promise { + const apiBase = parseApiBase(credential); + const token = asToken(credential); + + // Initial backend-first support: if API base isn't configured yet, + // skip list UX gracefully while clone/push remains fully supported. + if (!apiBase) return { repos: [], hasMore: false }; + + const page = opts?.page || 1; + const limit = 30; + const q = opts?.search?.trim(); + const query = new URLSearchParams({ limit: String(limit), page: String(page) }); + if (q) query.set('q', q); + + const res = await codestorageFetch(apiBase, `/repos?${query.toString()}`, token); + if (!res.ok) { + throw new Error(`code.storage list repos failed: ${res.status}`); + } + + const data = (await res.json()) as { items?: any[]; repos?: any[]; nextCursor?: string; hasMore?: boolean }; + const repos = (data.items || data.repos || []).map((r: any) => { + const fullName = r.full_name || r.fullName || r.name || ''; + const parsed = parseCodeStorageRepoUrl(r.clone_url || r.cloneUrl || r.url || ''); + return { + id: typeof r.id === 'number' ? r.id : undefined, + name: r.name || fullName.split('/').pop() || fullName, + fullName, + url: r.url || (parsed ? parsed.cloneUrl.replace(/\.git$/, '') : ''), + cloneUrl: r.clone_url || r.cloneUrl || parsed?.cloneUrl || '', + defaultBranch: r.default_branch || r.defaultBranch || 'main', + private: r.private ?? true, + description: r.description ?? null, + updatedAt: r.updated_at || r.updatedAt, + language: r.language ?? null, + }; + }).filter((r: any) => r.fullName && r.cloneUrl); + + return { + repos, + hasMore: Boolean(data.hasMore || data.nextCursor || repos.length === limit), + }; + }, + + async validateRepo(credential: RepoCredential, repoUrl: string): Promise { + const parsed = parseCodeStorageRepoUrl(repoUrl); + if (!parsed) { + return { accessible: false, error: 'Invalid code.storage repository URL' }; + } + + const apiBase = parseApiBase(credential); + const token = asToken(credential); + + if (!apiBase) { + // Clone/push-only bootstrap mode: URL is syntactically valid and token exists. + return { + accessible: true, + permissions: { push: true, pull: true, admin: false }, + fullName: parsed.path, + defaultBranch: 'main', + private: true, + cloneUrl: parsed.cloneUrl, + }; + } + + const encodedPath = encodeURIComponent(parsed.path); + const res = await codestorageFetch(apiBase, `/repos/${encodedPath}`, token); + if (!res.ok) { + return { accessible: false, error: `Repository not accessible: ${res.status}` }; + } + + const data = (await res.json()) as { + full_name?: string; + default_branch?: string; + private?: boolean; + clone_url?: string; + permissions?: { push?: boolean; pull?: boolean; admin?: boolean }; + }; + + return { + accessible: true, + permissions: { + push: data.permissions?.push ?? true, + pull: data.permissions?.pull ?? true, + admin: data.permissions?.admin ?? false, + }, + fullName: data.full_name || parsed.path, + defaultBranch: data.default_branch || 'main', + private: data.private ?? true, + cloneUrl: data.clone_url || parsed.cloneUrl, + }; + }, + + async assembleSessionEnv(_credential: RepoCredential, opts) { + const parsed = parseCodeStorageRepoUrl(opts.repoUrl); + if (!parsed) { + throw new Error('Invalid code.storage repository URL'); + } + + return { + envVars: { + REPO_URL: withCodeStorageUsername(parsed.cloneUrl), + ...(opts.branch ? { REPO_BRANCH: opts.branch } : {}), + ...(opts.ref ? { REPO_REF: opts.ref } : {}), + }, + gitConfig: { + 'user.name': opts.gitUser.name, + 'user.email': opts.gitUser.email, + }, + }; + }, + + async mintToken(credential: RepoCredential) { + const token = asToken(credential); + return { + accessToken: token, + expiresAt: credential.expiresAt, + }; + }, +}; diff --git a/packages/worker/package.json b/packages/worker/package.json index 5fe9976f..c6ae724e 100644 --- a/packages/worker/package.json +++ b/packages/worker/package.json @@ -20,6 +20,7 @@ "dependencies": { "@valet/sdk": "workspace:*", "@valet/plugin-cloudflare": "workspace:*", + "@valet/plugin-codestorage": "workspace:*", "@valet/plugin-figma": "workspace:*", "@valet/plugin-deepwiki": "workspace:*", "@valet/plugin-email-auth": "workspace:*", diff --git a/packages/worker/src/repos/packages.ts b/packages/worker/src/repos/packages.ts index 9e9f8054..7c2c61f9 100644 --- a/packages/worker/src/repos/packages.ts +++ b/packages/worker/src/repos/packages.ts @@ -1,6 +1,7 @@ // AUTO-GENERATED by scripts/generate-plugin-registry.ts — do not edit import type { RepoProvider } from '@valet/sdk/repos'; -import { githubRepoProvider as rp0 } from '@valet/plugin-github/repo'; +import { codestorageRepoProvider as rp0 } from '@valet/plugin-codestorage/repo'; +import { githubRepoProvider as rp1 } from '@valet/plugin-github/repo'; -export const installedRepoProviders: RepoProvider[] = [rp0]; +export const installedRepoProviders: RepoProvider[] = [rp0, rp1]; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index dae72e90..5a17e7e5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -143,6 +143,19 @@ importers: specifier: ^4.0.18 version: 4.0.18(@types/node@25.0.9)(jiti@1.21.7)(tsx@4.21.0) + packages/plugin-codestorage: + dependencies: + '@valet/sdk': + specifier: workspace:* + version: link:../sdk + devDependencies: + typescript: + specifier: ^5.3.3 + version: 5.9.3 + vitest: + specifier: ^4.0.18 + version: 4.0.18(@types/node@25.0.9)(jiti@1.21.7)(tsx@4.21.0) + packages/plugin-deepwiki: dependencies: '@valet/sdk': @@ -512,6 +525,9 @@ importers: '@valet/plugin-cloudflare': specifier: workspace:* version: link:../plugin-cloudflare + '@valet/plugin-codestorage': + specifier: workspace:* + version: link:../plugin-codestorage '@valet/plugin-deepwiki': specifier: workspace:* version: link:../plugin-deepwiki @@ -1442,79 +1458,67 @@ packages: resolution: {integrity: sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==} cpu: [arm64] os: [linux] - libc: [glibc] '@img/sharp-libvips-linux-arm@1.0.5': resolution: {integrity: sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==} cpu: [arm] os: [linux] - libc: [glibc] '@img/sharp-libvips-linux-s390x@1.0.4': resolution: {integrity: sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==} cpu: [s390x] os: [linux] - libc: [glibc] '@img/sharp-libvips-linux-x64@1.0.4': resolution: {integrity: sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==} cpu: [x64] os: [linux] - libc: [glibc] '@img/sharp-libvips-linuxmusl-arm64@1.0.4': resolution: {integrity: sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==} cpu: [arm64] os: [linux] - libc: [musl] '@img/sharp-libvips-linuxmusl-x64@1.0.4': resolution: {integrity: sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==} cpu: [x64] os: [linux] - libc: [musl] '@img/sharp-linux-arm64@0.33.5': resolution: {integrity: sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [linux] - libc: [glibc] '@img/sharp-linux-arm@0.33.5': resolution: {integrity: sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm] os: [linux] - libc: [glibc] '@img/sharp-linux-s390x@0.33.5': resolution: {integrity: sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [s390x] os: [linux] - libc: [glibc] '@img/sharp-linux-x64@0.33.5': resolution: {integrity: sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [linux] - libc: [glibc] '@img/sharp-linuxmusl-arm64@0.33.5': resolution: {integrity: sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [linux] - libc: [musl] '@img/sharp-linuxmusl-x64@0.33.5': resolution: {integrity: sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [linux] - libc: [musl] '@img/sharp-wasm32@0.33.5': resolution: {integrity: sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==} @@ -2017,79 +2021,66 @@ packages: resolution: {integrity: sha512-Rn3n+FUk2J5VWx+ywrG/HGPTD9jXNbicRtTM11e/uorplArnXZYsVifnPPqNNP5BsO3roI4n8332ukpY/zN7rQ==} cpu: [arm] os: [linux] - libc: [glibc] '@rollup/rollup-linux-arm-musleabihf@4.55.1': resolution: {integrity: sha512-grPNWydeKtc1aEdrJDWk4opD7nFtQbMmV7769hiAaYyUKCT1faPRm2av8CX1YJsZ4TLAZcg9gTR1KvEzoLjXkg==} cpu: [arm] os: [linux] - libc: [musl] '@rollup/rollup-linux-arm64-gnu@4.55.1': resolution: {integrity: sha512-a59mwd1k6x8tXKcUxSyISiquLwB5pX+fJW9TkWU46lCqD/GRDe9uDN31jrMmVP3feI3mhAdvcCClhV8V5MhJFQ==} cpu: [arm64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-arm64-musl@4.55.1': resolution: {integrity: sha512-puS1MEgWX5GsHSoiAsF0TYrpomdvkaXm0CofIMG5uVkP6IBV+ZO9xhC5YEN49nsgYo1DuuMquF9+7EDBVYu4uA==} cpu: [arm64] os: [linux] - libc: [musl] '@rollup/rollup-linux-loong64-gnu@4.55.1': resolution: {integrity: sha512-r3Wv40in+lTsULSb6nnoudVbARdOwb2u5fpeoOAZjFLznp6tDU8kd+GTHmJoqZ9lt6/Sys33KdIHUaQihFcu7g==} cpu: [loong64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-loong64-musl@4.55.1': resolution: {integrity: sha512-MR8c0+UxAlB22Fq4R+aQSPBayvYa3+9DrwG/i1TKQXFYEaoW3B5b/rkSRIypcZDdWjWnpcvxbNaAJDcSbJU3Lw==} cpu: [loong64] os: [linux] - libc: [musl] '@rollup/rollup-linux-ppc64-gnu@4.55.1': resolution: {integrity: sha512-3KhoECe1BRlSYpMTeVrD4sh2Pw2xgt4jzNSZIIPLFEsnQn9gAnZagW9+VqDqAHgm1Xc77LzJOo2LdigS5qZ+gw==} cpu: [ppc64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-ppc64-musl@4.55.1': resolution: {integrity: sha512-ziR1OuZx0vdYZZ30vueNZTg73alF59DicYrPViG0NEgDVN8/Jl87zkAPu4u6VjZST2llgEUjaiNl9JM6HH1Vdw==} cpu: [ppc64] os: [linux] - libc: [musl] '@rollup/rollup-linux-riscv64-gnu@4.55.1': resolution: {integrity: sha512-uW0Y12ih2XJRERZ4jAfKamTyIHVMPQnTZcQjme2HMVDAHY4amf5u414OqNYC+x+LzRdRcnIG1YodLrrtA8xsxw==} cpu: [riscv64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-riscv64-musl@4.55.1': resolution: {integrity: sha512-u9yZ0jUkOED1BFrqu3BwMQoixvGHGZ+JhJNkNKY/hyoEgOwlqKb62qu+7UjbPSHYjiVy8kKJHvXKv5coH4wDeg==} cpu: [riscv64] os: [linux] - libc: [musl] '@rollup/rollup-linux-s390x-gnu@4.55.1': resolution: {integrity: sha512-/0PenBCmqM4ZUd0190j7J0UsQ/1nsi735iPRakO8iPciE7BQ495Y6msPzaOmvx0/pn+eJVVlZrNrSh4WSYLxNg==} cpu: [s390x] os: [linux] - libc: [glibc] '@rollup/rollup-linux-x64-gnu@4.55.1': resolution: {integrity: sha512-a8G4wiQxQG2BAvo+gU6XrReRRqj+pLS2NGXKm8io19goR+K8lw269eTrPkSdDTALwMmJp4th2Uh0D8J9bEV1vg==} cpu: [x64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-x64-musl@4.55.1': resolution: {integrity: sha512-bD+zjpFrMpP/hqkfEcnjXWHMw5BIghGisOKPj+2NaNDuVT+8Ds4mPf3XcPHuat1tz89WRL+1wbcxKY3WSbiT7w==} cpu: [x64] os: [linux] - libc: [musl] '@rollup/rollup-openbsd-x64@4.55.1': resolution: {integrity: sha512-eLXw0dOiqE4QmvikfQ6yjgkg/xDM+MdU9YJuP4ySTibXU0oAvnEWXt7UDJmD4UkYialMfOGFPJnIHSe/kdzPxg==} From c78ca56195155c00252a05a066266307b785d315 Mon Sep 17 00:00:00 2001 From: Carey Janecka Date: Tue, 17 Mar 2026 00:51:44 -0700 Subject: [PATCH 2/2] fix(codestorage): address PR review feedback - Add missing tsconfig.json and wire references in root + worker tsconfigs - Regenerate plugin registries instead of hand-editing auto-generated file - Add comments explaining bootstrap-mode validation tradeoffs - Add comments explaining credential helper flow (Runner git-setup.ts) - Remove redundant .git strip in parseCodeStorageRepoUrl --- packages/plugin-codestorage/src/repo.ts | 12 ++++++++++-- packages/plugin-codestorage/tsconfig.json | 10 ++++++++++ packages/worker/src/plugins/content-registry.ts | 8 ++++++++ packages/worker/tsconfig.json | 1 + tsconfig.json | 1 + 5 files changed, 30 insertions(+), 2 deletions(-) create mode 100644 packages/plugin-codestorage/tsconfig.json diff --git a/packages/plugin-codestorage/src/repo.ts b/packages/plugin-codestorage/src/repo.ts index 5bc470ff..012c6c83 100644 --- a/packages/plugin-codestorage/src/repo.ts +++ b/packages/plugin-codestorage/src/repo.ts @@ -19,7 +19,7 @@ function parseCodeStorageRepoUrl(repoUrl: string): ParsedRepoUrl | null { if (!match) return null; const host = match[1]; - const path = match[2].replace(/^\/+/, '').replace(/\.git$/i, ''); + const path = match[2].replace(/^\/+/, ''); return { host, path, @@ -120,7 +120,11 @@ export const codestorageRepoProvider: RepoProvider = { const token = asToken(credential); if (!apiBase) { - // Clone/push-only bootstrap mode: URL is syntactically valid and token exists. + // Bootstrap mode: no API base configured, so we can't verify the repo + // server-side. We accept the URL if it parses and a token exists. A typo'd + // URL or revoked token will surface as a clone-time error, which is acceptable + // for this initial backend-first rollout — full validation requires apiBase + // (derived from credential metadata) to be configured. return { accessible: true, permissions: { push: true, pull: true, admin: false }, @@ -160,6 +164,10 @@ export const codestorageRepoProvider: RepoProvider = { }, async assembleSessionEnv(_credential: RepoCredential, opts) { + // Note: the minted token is not embedded here. The Runner's git-setup.ts + // configures a global git credential.helper that calls back to the Runner + // gateway (/git/credentials), which invokes mintToken() on-demand. The t@ + // username in the clone URL ensures git triggers a credential lookup. const parsed = parseCodeStorageRepoUrl(opts.repoUrl); if (!parsed) { throw new Error('Invalid code.storage repository URL'); diff --git a/packages/plugin-codestorage/tsconfig.json b/packages/plugin-codestorage/tsconfig.json new file mode 100644 index 00000000..8753cc9c --- /dev/null +++ b/packages/plugin-codestorage/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "./dist", + "rootDir": "./src", + "types": [] + }, + "include": ["src/**/*"], + "references": [{ "path": "../sdk" }, { "path": "../shared" }] +} diff --git a/packages/worker/src/plugins/content-registry.ts b/packages/worker/src/plugins/content-registry.ts index 19ffdb8c..c8f110b0 100644 --- a/packages/worker/src/plugins/content-registry.ts +++ b/packages/worker/src/plugins/content-registry.ts @@ -250,6 +250,14 @@ timeout 10 agent-browser --headed wait --text "Welcome" # Wait for specific capabilities: ["actions"], artifacts: [], }, + { + name: "codestorage", + version: "0.0.1", + description: "code.storage repo provider for git clone/push session runtime", + icon: "🧱", + capabilities: [], + artifacts: [], + }, { name: "deepwiki", version: "0.0.1", diff --git a/packages/worker/tsconfig.json b/packages/worker/tsconfig.json index 13dbdddd..9ea6add1 100644 --- a/packages/worker/tsconfig.json +++ b/packages/worker/tsconfig.json @@ -11,6 +11,7 @@ { "path": "../shared" }, { "path": "../sdk" }, { "path": "../plugin-cloudflare" }, + { "path": "../plugin-codestorage" }, { "path": "../plugin-figma" }, { "path": "../plugin-deepwiki" }, { "path": "../plugin-email-auth" }, diff --git a/tsconfig.json b/tsconfig.json index 56a875ee..208e8593 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -35,6 +35,7 @@ { "path": "./packages/plugin-email-auth" }, { "path": "./packages/plugin-google-auth" }, { "path": "./packages/plugin-telegram" }, + { "path": "./packages/plugin-codestorage" }, { "path": "./packages/worker" } ] }