@zodiaceco/sdk 2.2.0 → 2.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/README.md +97 -6
  2. package/dist/{actions-tA_Oe_kC.d.mts → actions-Cri7InV-.d.mts} +57 -4
  3. package/dist/actions.d.mts +1 -1
  4. package/dist/actions.mjs +18 -2
  5. package/dist/actions.mjs.map +1 -1
  6. package/dist/allow/index.d.mts +1 -1
  7. package/dist/allow/index.mjs +2 -2
  8. package/dist/{allow-CrDzDtxx.mjs → allow-DX3iyizB.mjs} +2 -2
  9. package/dist/{allow-CrDzDtxx.mjs.map → allow-DX3iyizB.mjs.map} +1 -1
  10. package/dist/cli/config.mjs +1 -1
  11. package/dist/cli/push.d.mts +24 -0
  12. package/dist/cli/push.mjs +36 -0
  13. package/dist/cli/push.mjs.map +1 -0
  14. package/dist/cli.mjs +15 -6
  15. package/dist/cli.mjs.map +1 -1
  16. package/dist/{config-D0FqysME.mjs → config-BaChz2bq.mjs} +10 -5
  17. package/dist/config-BaChz2bq.mjs.map +1 -0
  18. package/dist/{index-CnH26Z_A.d.mts → index-D49x1ozw.d.mts} +1 -1
  19. package/dist/index.d.mts +34 -5
  20. package/dist/index.mjs +8 -180
  21. package/dist/index.mjs.map +1 -1
  22. package/dist/{networks-B0H4Uoyh.mjs → networks-UKtJ8QDG.mjs} +1 -1
  23. package/dist/{networks-B0H4Uoyh.mjs.map → networks-UKtJ8QDG.mjs.map} +1 -1
  24. package/dist/paths-LsD6ULk0.mjs +34 -0
  25. package/dist/paths-LsD6ULk0.mjs.map +1 -0
  26. package/dist/{permissionEntries-D3fSVhwO.mjs → permissionEntries-C4dAwZBb.mjs} +3 -1
  27. package/dist/permissionEntries-C4dAwZBb.mjs.map +1 -0
  28. package/dist/push-DdnHsLpO.mjs +356 -0
  29. package/dist/push-DdnHsLpO.mjs.map +1 -0
  30. package/package.json +7 -3
  31. package/dist/api-kKgzHQlu.mjs +0 -158
  32. package/dist/api-kKgzHQlu.mjs.map +0 -1
  33. package/dist/config-D0FqysME.mjs.map +0 -1
  34. package/dist/permissionEntries-D3fSVhwO.mjs.map +0 -1
package/dist/cli.mjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.mjs","names":["join","loadDotenv"],"sources":["../src/cli/checkForUpdate.ts","../src/cli/commands/init.ts","../src/cli/commands/pullOrg.ts","../src/allow/fetch.ts","../src/allow/codegen.ts","../src/cli/commands/pullContracts.ts","../src/cli/run.ts","../src/cli/index.ts"],"sourcesContent":["import { existsSync, readFileSync } from 'node:fs'\nimport { dirname, join } from 'node:path'\nimport { fileURLToPath } from 'node:url'\nimport { findProjectRoot } from './projectRoot'\n\nconst PACKAGE = '@zodiaceco/sdk'\nconst REGISTRY_URL = `https://registry.npmjs.org/${PACKAGE}/latest`\nconst TIMEOUT_MS = 2_000\n\ntype Env = Record<string, string | undefined>\n\ntype CheckForUpdateOptions = {\n cwd?: string\n env?: Env\n}\n\n/**\n * Whether a newer release of this package is out, as the notice to print — or\n * `null`, which is also the answer to every failure. The check is a courtesy\n * on the way out of a command, so nothing here may throw or block for long,\n * and a run without a terminal to read the notice skips it entirely.\n */\nexport const checkForUpdate = async ({\n cwd = process.cwd(),\n env = process.env,\n}: CheckForUpdateOptions = {}): Promise<string | null> => {\n if (env.CI) {\n return null\n }\n\n try {\n const current = ownVersion()\n const latest = await fetchLatestVersion()\n\n if (latest == null || !isNewer(latest, current)) {\n return null\n }\n\n return [\n `A newer version of ${PACKAGE} is available (${current} → ${latest}). Update with:`,\n ` ${updateCommand(detectPackageManager(cwd, env))}`,\n ].join('\\n')\n } catch {\n return null\n }\n}\n\n/**\n * The version of the package this module runs from. Walks up to the\n * `package.json` that names it, which holds from `src/` and from `dist/`\n * alike — and finds this package, not the caller's, when the CLI runs\n * inside another project.\n */\nexport const ownVersion = (): string => {\n let dir = dirname(fileURLToPath(import.meta.url))\n\n while (true) {\n const manifest = join(dir, 'package.json')\n\n if (existsSync(manifest)) {\n const { name, version } = JSON.parse(readFileSync(manifest, 'utf-8'))\n\n if (name === PACKAGE && typeof version === 'string') {\n return version\n }\n }\n\n const parent = dirname(dir)\n if (parent === dir) {\n throw new Error(`Could not find the package.json of ${PACKAGE}`)\n }\n dir = parent\n }\n}\n\nconst fetchLatestVersion = async (): Promise<string | null> => {\n const response = await fetch(REGISTRY_URL, {\n signal: AbortSignal.timeout(TIMEOUT_MS),\n })\n\n if (!response.ok) {\n return null\n }\n\n const { version } = (await response.json()) as { version?: unknown }\n\n return typeof version === 'string' ? version : null\n}\n\nconst isNewer = (latest: string, current: string): boolean => {\n const a = parseVersion(latest)\n const b = parseVersion(current)\n\n if (a == null || b == null) {\n return false\n }\n\n for (let i = 0; i < 3; i++) {\n if (a[i] !== b[i]) {\n return a[i] > b[i]\n }\n }\n\n return false\n}\n\nconst parseVersion = (version: string): [number, number, number] | null => {\n const match = /^(\\d+)\\.(\\d+)\\.(\\d+)/.exec(version)\n\n if (match == null) {\n return null\n }\n\n return [Number(match[1]), Number(match[2]), Number(match[3])]\n}\n\ntype PackageManager = 'npm' | 'pnpm' | 'yarn' | 'bun'\n\n/**\n * The package manager an update should go through: whichever one launched\n * this process, or failing that, whichever one's lockfile the project\n * carries. `npm` when neither says anything.\n */\nconst detectPackageManager = (cwd: string, env: Env): PackageManager => {\n const agent = env.npm_config_user_agent\n\n if (agent != null) {\n const fromAgent = PACKAGE_MANAGERS.find((packageManager) =>\n agent.startsWith(`${packageManager}/`)\n )\n\n if (fromAgent != null) {\n return fromAgent\n }\n }\n\n const root = findProjectRoot(cwd)\n const fromLockfile = LOCKFILES.find(([lockfile]) =>\n existsSync(join(root, lockfile))\n )\n\n return fromLockfile == null ? 'npm' : fromLockfile[1]\n}\n\nconst PACKAGE_MANAGERS = ['pnpm', 'yarn', 'bun', 'npm'] as const\n\nconst LOCKFILES: [string, PackageManager][] = [\n ['bun.lock', 'bun'],\n ['bun.lockb', 'bun'],\n ['pnpm-lock.yaml', 'pnpm'],\n ['yarn.lock', 'yarn'],\n ['package-lock.json', 'npm'],\n]\n\nconst updateCommand = (packageManager: PackageManager): string =>\n packageManager === 'npm'\n ? `npm install ${PACKAGE}@latest`\n : `${packageManager} add ${PACKAGE}@latest`\n","import { createHash, randomBytes } from 'node:crypto'\nimport {\n createServer,\n type IncomingMessage,\n type ServerResponse,\n} from 'node:http'\nimport { existsSync, readFileSync, writeFileSync } from 'node:fs'\nimport { basename, join, resolve } from 'node:path'\nimport open from 'open'\nimport { ensureConfigStub } from '../config'\nimport { findProjectRoot } from '../projectRoot'\n\nconst DEFAULT_APP_URL = 'https://app.zodiac.eco'\nconst CALLBACK_TIMEOUT_MS = 5 * 60 * 1000 // 5 minutes\n\ntype InitOptions = {\n rootDir?: string\n /** Override the Zodiac app base URL (defaults to ZODIAC_APP_URL env or app.zodiac.eco). */\n appUrl?: string\n}\n\nexport const init = async (options: InitOptions = {}): Promise<string> => {\n const rootDir = options.rootDir ?? findProjectRoot()\n const appUrl = (\n options.appUrl ??\n process.env.ZODIAC_APP_URL ??\n DEFAULT_APP_URL\n ).replace(/\\/$/, '')\n\n const label = basename(resolve(rootDir)) || 'zodiac-cli'\n\n // PKCE: keep `code_verifier` private to this process. Send only the\n // SHA-256 hash (`code_challenge`) over the wire.\n const codeVerifier = randomBytes(32).toString('base64url')\n const codeChallenge = createHash('sha256')\n .update(codeVerifier)\n .digest('base64url')\n\n // `state` binds the redirect we receive on the loopback to this CLI\n // invocation, defending against a stray local process trying to inject\n // an auth code into our callback.\n const state = randomBytes(32).toString('base64url')\n\n const { port, waitForCode, close } = await startCallbackServer({\n expectedState: state,\n appUrl,\n })\n\n const callbackUrl = `http://127.0.0.1:${port}/callback`\n const authUrl = new URL('/cli-auth', appUrl)\n authUrl.searchParams.set('callback', callbackUrl)\n authUrl.searchParams.set('state', state)\n authUrl.searchParams.set('code_challenge', codeChallenge)\n authUrl.searchParams.set('label', label)\n\n console.log(\n `Opening ${authUrl} in your browser. Approve the request to receive an API key.`\n )\n await open(authUrl.toString())\n\n let code: string\n try {\n code = await waitForCode(CALLBACK_TIMEOUT_MS)\n } finally {\n await close()\n }\n\n const apiKey = await exchangeCodeForKey(appUrl, code, codeVerifier)\n\n const envPath = join(rootDir, '.env')\n const apiUrl = `${appUrl}/api/v1`\n writeEnv(envPath, { ZODIAC_API_KEY: apiKey, ZODIAC_API_URL: apiUrl })\n\n // So a same-run `pull` targets the org we just authorized, not the default.\n process.env.ZODIAC_API_KEY = apiKey\n process.env.ZODIAC_API_URL = apiUrl\n\n console.log(`✅ API key written to ${envPath}`)\n\n const configPath = join(rootDir, 'zodiac.config.ts')\n if (ensureConfigStub(configPath)) {\n console.log(`✅ Created ${configPath}`)\n }\n\n return apiKey\n}\n\nconst exchangeCodeForKey = async (\n appUrl: string,\n code: string,\n codeVerifier: string\n): Promise<string> => {\n const response = await fetch(`${appUrl}/cli-auth/exchange`, {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ code, code_verifier: codeVerifier }),\n })\n\n if (!response.ok) {\n let detail = ''\n try {\n const body = (await response.json()) as { error?: string }\n detail = body.error ? `: ${body.error}` : ''\n } catch {\n // ignore — fall through to a generic message\n }\n throw new Error(\n `Failed to exchange auth code (${response.status})${detail}`\n )\n }\n\n const { key } = (await response.json()) as { key: unknown }\n if (typeof key !== 'string' || !key.startsWith('zodiac_')) {\n throw new Error(\n 'Exchange endpoint returned an unexpected response (missing or malformed key)'\n )\n }\n return key\n}\n\ntype StartServerOptions = {\n expectedState: string\n appUrl: string\n}\n\ntype StartServerResult = {\n port: number\n waitForCode: (timeoutMs: number) => Promise<string>\n close: () => Promise<void>\n}\n\nconst startCallbackServer = async ({\n expectedState,\n appUrl,\n}: StartServerOptions): Promise<StartServerResult> => {\n let resolveCode: (code: string) => void = () => {}\n let rejectCode: (err: Error) => void = () => {}\n const codePromise = new Promise<string>((res, rej) => {\n resolveCode = res\n rejectCode = rej\n })\n\n const handler = (req: IncomingMessage, res: ServerResponse) => {\n if (req.method !== 'GET' || !req.url?.startsWith('/callback')) {\n res.statusCode = 404\n res.end()\n return\n }\n\n const url = new URL(req.url, `http://127.0.0.1`)\n const code = url.searchParams.get('code')\n const state = url.searchParams.get('state')\n\n if (state !== expectedState) {\n // A stray request — could be a stale browser tab, a malicious\n // local process, etc. Silently 403 and keep waiting for the right\n // one. Don't reject the CLI promise.\n respondHtml(\n res,\n 403,\n renderHtml(\n 'Mismatched state',\n `<p>This callback didn't match the running <code>zodiac init</code> session. You can close this tab.</p>`\n )\n )\n return\n }\n\n if (typeof code !== 'string' || code.length === 0) {\n respondHtml(\n res,\n 400,\n renderHtml(\n 'Missing code',\n `<p>The callback URL didn't include an auth <code>code</code>. Please re-run <code>zodiac init</code>.</p>`\n )\n )\n return\n }\n\n respondHtml(\n res,\n 200,\n renderHtml(\n 'Authorized',\n `<p>Your CLI now has its API key. You can close this tab and return to your terminal.</p>\n <p><a href=\"${escapeHtml(appUrl)}\">Back to Zodiac</a></p>`\n )\n )\n resolveCode(code)\n }\n\n const server = createServer(handler)\n await new Promise<void>((res) => server.listen(0, '127.0.0.1', res))\n const address = server.address()\n if (address == null || typeof address === 'string') {\n throw new Error('Failed to bind loopback callback server')\n }\n const port = address.port\n\n const close = (): Promise<void> =>\n new Promise<void>((resolve) => {\n server.close(() => resolve())\n })\n\n const waitForCode = (timeoutMs: number) =>\n Promise.race<string>([\n codePromise,\n new Promise<string>((_, rej) =>\n setTimeout(() => {\n rejectCode(new Error(`Timed out waiting for CLI auth callback`))\n rej(new Error(`Timed out waiting for CLI auth callback`))\n }, timeoutMs)\n ),\n ])\n\n return { port, waitForCode, close }\n}\n\nconst respondHtml = (res: ServerResponse, status: number, body: string) => {\n // Force the connection to close after the response so the browser\n // doesn't keep waiting on a keep-alive socket once the CLI exits.\n // Resolve only after 'finish' (response handed to the OS) so the\n // bytes can flush before the process tears down.\n res.setHeader('Connection', 'close')\n res.setHeader('content-type', 'text/html; charset=utf-8')\n res.statusCode = status\n res.end(body)\n}\n\nconst renderHtml = (title: string, bodyHtml: string) => `<!doctype html>\n<html lang=\"en\">\n <head>\n <meta charset=\"utf-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n <title>${escapeHtml(title)} — Zodiac CLI</title>\n <style>\n body { font: 16px/1.5 -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif; max-width: 32rem; margin: 4rem auto; padding: 0 1rem; color: #1f2937; }\n h1 { font-weight: 300; font-size: 1.75rem; margin-bottom: 1rem; }\n code { background: #f3f4f6; padding: 0.125rem 0.375rem; border-radius: 0.25rem; }\n a { color: #1d4ed8; }\n </style>\n </head>\n <body>\n <h1>${escapeHtml(title)}</h1>\n ${bodyHtml}\n </body>\n</html>\n`\n\nconst escapeHtml = (value: string) =>\n value\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;')\n .replace(/'/g, '&#39;')\n\nconst writeEnv = (envPath: string, vars: Record<string, string>): void => {\n let contents = existsSync(envPath) ? readFileSync(envPath, 'utf8') : ''\n\n for (const [key, value] of Object.entries(vars)) {\n contents = upsertEnvLine(contents, key, value)\n }\n\n writeFileSync(envPath, contents, 'utf8')\n}\n\nconst upsertEnvLine = (\n contents: string,\n key: string,\n value: string\n): string => {\n const line = `${key}=${value}`\n const lines = contents.split(/\\r?\\n/)\n const idx = lines.findIndex((l) => new RegExp(`^\\\\s*${key}\\\\s*=`).test(l))\n\n if (idx >= 0) {\n lines[idx] = line\n return lines.join('\\n')\n }\n\n return contents.length === 0 || contents.endsWith('\\n')\n ? `${contents}${line}\\n`\n : `${contents}\\n${line}\\n`\n}\n","import type {\n Address,\n ChainId,\n ResolveConstellationPayload,\n} from '@zodiaceco/api-types'\nimport type { ResolvedConfig } from '../config'\nimport { ApiClient } from '../../api'\nimport { getAddress } from 'ethers'\nimport {\n ModuleKind,\n Project,\n ScriptTarget,\n VariableDeclarationKind,\n} from 'ts-morph'\nimport { mkdirSync, writeFileSync } from 'fs'\nimport { join } from 'path'\nimport { resolveZodiacDir } from '../../paths'\n\nconst toLiteral = (value: unknown, indent = 0): string => {\n const pad = ' '.repeat(indent)\n const childPad = ' '.repeat(indent + 1)\n\n if (value === null) return 'null'\n if (typeof value === 'bigint') return `${value}n`\n if (typeof value === 'string') return JSON.stringify(value)\n if (typeof value === 'number' || typeof value === 'boolean')\n return String(value)\n if (Array.isArray(value)) {\n if (value.length === 0) return '[]'\n return `[\\n${value.map((v) => `${childPad}${toLiteral(v, indent + 1)}`).join(',\\n')},\\n${pad}]`\n }\n if (typeof value === 'object') {\n const entries = Object.entries(value as Record<string, unknown>)\n if (entries.length === 0) return '{}'\n const props = entries.map(\n ([k, v]) =>\n `${childPad}${isBareKey(k) ? k : JSON.stringify(k)}: ${toLiteral(v, indent + 1)}`\n )\n return `{\\n${props.join(',\\n')},\\n${pad}}`\n }\n return String(value)\n}\n\ntype NodeType = 'SAFE' | 'ROLES' | 'DELAY'\n\n/**\n * An account written as the specification node that names it and declares\n * nothing else.\n *\n * The `ref` is required of every node and has to be distinct, but nothing\n * reads it back — the result comes aligned with the request.\n *\n * Spelled out per type rather than passed through: `/accounts` answers with\n * the same three names as an enum, whose members are types of their own, and\n * a node is only a node once it says which of the three it is.\n */\nconst asNodeReference = (\n {\n type,\n chain,\n address,\n }: { type: string; chain: ChainId; address: Lowercase<Address> },\n index: number\n): ResolveConstellationPayload['specification'][number] => {\n const node = { ref: `account_${index}`, chain, address }\n\n switch (asNodeType(type)) {\n case 'SAFE':\n return { ...node, type: 'SAFE' }\n case 'ROLES':\n return { ...node, type: 'ROLES' }\n case 'DELAY':\n return { ...node, type: 'DELAY' }\n }\n}\n\nconst asNodeType = (type: string): NodeType => {\n if (type === 'SAFE' || type === 'ROLES' || type === 'DELAY') {\n return type\n }\n\n throw new Error(`Cannot describe an account of type \"${type}\"`)\n}\n\n/**\n * Whether a key can be written without quotes. Chain ids are grouping keys in\n * the generated data, and an unquoted number keys the `as const` type by chain\n * id — quoted, it would key by a string the callers never hold.\n */\nconst isBareKey = (key: string): boolean =>\n /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(key) || /^\\d+$/.test(key)\n\nexport const pullOrg = async (config: ResolvedConfig) => {\n const client = new ApiClient({\n apiKey: config.apiKey,\n })\n\n const [users, workspaceAccounts] = await Promise.all([\n client.listUsers(),\n client.listAccounts(),\n ])\n\n // The generated accounts carry their onchain state so a node reads as the\n // account it names and completes in an editor. Only what a constellation\n // declares is ever pushed back, so this is for reading, not a declaration.\n //\n // Each account is named by its address and nothing else. `/resolve`\n // resolves a node it has no address for against the setup safe of whoever\n // holds the API key — which describes the account *that* caller would\n // deploy, not the one being asked about — and it merges declarations over\n // what it reads. Sending an address and no declarations leaves it nothing\n // to derive and nothing to merge, so the answer is the account itself, the\n // same for every key.\n //\n // Asking about every listed account is safe for the same reason: `/accounts`\n // admits one only once it has been seen to hold code, so every address here\n // is one that can be read from chain.\n const allAccounts = workspaceAccounts.flatMap((ws) => ws.accounts)\n const resolved = new Map<\n string,\n Awaited<ReturnType<typeof client.resolveConstellation>>['result'][number]\n >()\n\n if (allAccounts.length > 0) {\n const { result } = await client.resolveConstellation(\n workspaceAccounts[0].workspaceId, // any workspace works for the resolve route\n {\n specification: allAccounts.map(asNodeReference),\n }\n )\n\n // Aligned with the request, entry for entry.\n allAccounts.forEach((account, index) => {\n resolved.set(account.id, result[index])\n })\n }\n\n // Group accounts by type and then by chain into separate bracket-access\n // namespaces: `safes[chain]`, `rolesMods[chain]`, `delays[chain]`. This way\n // `eth.safe[...]` IntelliSense only suggests SAFE labels deployed on the\n // constellation's own chain, and the label-collision suffix only kicks in\n // where a label is genuinely ambiguous — same type, same chain.\n type AccountsByChain = Record<number, Record<string, unknown>>\n\n const accountsRecord: Record<string, unknown> = {}\n for (const ws of workspaceAccounts) {\n const safes: AccountsByChain = {}\n const rolesMods: AccountsByChain = {}\n const delays: AccountsByChain = {}\n\n const bucketsByType = {\n SAFE: safes,\n ROLES: rolesMods,\n DELAY: delays,\n } as const\n\n const isNodeType = (type: string): type is NodeType =>\n type === 'SAFE' || type === 'ROLES' || type === 'DELAY'\n\n // Accounts we only know by address — Safe owners, external role\n // members, route targets — carry no label. They can't key a\n // label-addressed namespace, so they stay out of the codegen.\n // Vault entries always carry one.\n\n // Count labels per namespace so we only suffix collisions a constellation\n // could actually run into. Two safes named `Treasury` on different chains\n // never compete for a key, because a constellation only ever sees one of\n // those chains.\n const namespaceKey = (type: NodeType, chain: number) => `${type}:${chain}`\n const labelCounts = new Map<string, Map<string, number>>()\n\n for (const account of ws.accounts) {\n const { label } = account\n if (label == null || !isNodeType(account.type)) continue\n const key = namespaceKey(account.type, account.chain)\n let counts = labelCounts.get(key)\n if (!counts) {\n counts = new Map()\n labelCounts.set(key, counts)\n }\n counts.set(label, (counts.get(label) ?? 0) + 1)\n }\n\n for (const account of ws.accounts) {\n const { label } = account\n if (label == null || !isNodeType(account.type)) continue\n const onChain = resolved.get(account.id)\n const counts = labelCounts.get(namespaceKey(account.type, account.chain))\n const key =\n (counts?.get(label) ?? 0) > 1\n ? `${label} (${getAddress(account.address)})`\n : label\n const byChain = bucketsByType[account.type]\n const byLabel = (byChain[account.chain] ??= {})\n byLabel[key] = {\n id: account.id,\n label,\n address: account.address,\n chain: account.chain,\n vault: account.vault,\n ...(onChain?.type === 'SAFE' && {\n threshold: onChain.threshold,\n owners: [...onChain.owners],\n modules: [...onChain.modules],\n }),\n }\n }\n\n accountsRecord[ws.workspaceName] = {\n workspaceId: ws.workspaceId,\n workspaceName: ws.workspaceName,\n safes,\n rolesMods,\n delays,\n }\n }\n\n const nameCount = new Map<string, number>()\n for (const user of users) {\n nameCount.set(user.fullName, (nameCount.get(user.fullName) ?? 0) + 1)\n }\n\n const usersRecord: Record<string, unknown> = {}\n for (const user of users) {\n const handle =\n nameCount.get(user.fullName)! > 1\n ? `${user.fullName} (${user.id})`\n : user.fullName\n usersRecord[handle] = {\n id: user.id,\n fullName: user.fullName,\n personalSafes: user.personalSafes,\n }\n }\n\n const outDir = resolveZodiacDir(config.rootDir)\n\n mkdirSync(outDir, { recursive: true })\n\n // Pin CJS so `require()` works regardless of the parent package.json's type\n writeFileSync(\n join(outDir, 'package.json'),\n JSON.stringify(\n {\n type: 'commonjs',\n main: 'index.js',\n types: 'index.d.ts',\n },\n null,\n 2\n )\n )\n\n // Use ts-morph to generate TS, then emit JS + d.ts\n const project = new Project({\n compilerOptions: {\n declaration: true,\n module: ModuleKind.CommonJS,\n target: ScriptTarget.ESNext,\n outDir,\n },\n useInMemoryFileSystem: true,\n })\n\n const sourceFile = project.createSourceFile('index.ts', '')\n\n sourceFile.addVariableStatement({\n isExported: true,\n declarationKind: VariableDeclarationKind.Const,\n declarations: [\n {\n name: 'users',\n initializer: `${toLiteral(usersRecord)} as const`,\n },\n ],\n })\n\n sourceFile.addVariableStatement({\n isExported: true,\n declarationKind: VariableDeclarationKind.Const,\n declarations: [\n {\n name: 'accounts',\n initializer: `${toLiteral(accountsRecord)} as const`,\n },\n ],\n })\n\n const emitResult = sourceFile.getEmitOutput()\n for (const outputFile of emitResult.getOutputFiles()) {\n const filePath = outputFile.getFilePath()\n const fileName = filePath.includes('.d.ts') ? 'index.d.ts' : 'index.js'\n let contents = outputFile.getText()\n // Augment the SDK's global `ZodiacGeneratedCodegen` interface so\n // `constellation()`'s default type parameter picks up these literal\n // shapes automatically.\n if (fileName === 'index.d.ts') {\n contents += `\ndeclare global {\n interface ZodiacGeneratedCodegen {\n users: typeof users;\n accounts: typeof accounts;\n }\n}\n`\n }\n writeFileSync(join(outDir, fileName), contents)\n }\n}\n","import { chainIdFor, type ChainPrefix } from './networks'\n\nexport type AbiFragment = Record<string, any>\nexport type Abi = AbiFragment[]\n\ntype Implementation = { abi?: unknown }\ntype Account = Implementation & {\n proxy?: unknown\n implementation?: Implementation\n}\n\n// Returns null on any failure so callers can fall back to a manual ABI file.\nexport async function fetchAbi(\n chainId: number,\n address: `0x${string}`\n): Promise<Abi | null> {\n const url = `https://api.abi.pub/v1/chains/${chainId}/accounts/${address}`\n let account: Account\n try {\n const resp = await fetch(url)\n if (!resp.ok) return null\n account = (await resp.json()) as Account\n } catch {\n return null\n }\n if (account == null || typeof account !== 'object') return null\n\n // For a proxy the endpoint reports the proxy under `proxy` and the contract\n // that actually holds the functions under `implementation`. Permissions are\n // written against the implementation ABI.\n const abi = account.proxy ? account.implementation?.abi : account.abi\n if (!Array.isArray(abi) || abi.length === 0) return null\n return abi as Abi\n}\n\nexport const fetchAbiForPrefix = (\n prefix: ChainPrefix,\n address: `0x${string}`\n) => fetchAbi(chainIdFor(prefix), address)\n","import fs from 'node:fs'\nimport path from 'node:path'\nimport { walkContracts, readAbi, type ContractNode } from './abi'\nimport type { Abi, AbiFragment } from './fetch'\n\n// Emits named tuple elements per ABI input — this is the reason we don't rely\n// on viem/abitype, which produces unnamed tuples (microsoft/TypeScript#44939).\nexport function generateAllowTypes(\n abisDir: string,\n contractsConfig: Record<string, any>\n): string {\n type Tree = Map<string, Tree | { node: ContractNode; abi: Abi }>\n\n const root: Tree = new Map()\n for (const node of walkContracts(contractsConfig)) {\n const abi = readAbi(abisDir, node)\n if (!abi) continue\n insertIntoTree(root, [node.chain, ...node.segments], { node, abi })\n }\n\n const out: string[] = []\n out.push('// AUTO-GENERATED by `zodiac pull-contracts`. Do not edit.')\n out.push('/* eslint-disable */')\n out.push('')\n out.push(\n `import type { Scoping, Options, EVERYTHING, FunctionPermission, TargetPermission } from \"@zodiaceco/sdk/allow\";`\n )\n out.push('')\n out.push('declare global {')\n out.push(' interface AllowKit ' + renderTree(root, ' '))\n out.push('}')\n out.push('')\n out.push('export {};')\n out.push('')\n return out.join('\\n')\n}\n\nfunction insertIntoTree(\n tree: Map<string, any>,\n segments: string[],\n leaf: { node: ContractNode; abi: Abi }\n): void {\n const [first, ...rest] = segments\n if (!first) throw new Error('empty segments')\n if (rest.length === 0) {\n tree.set(first, leaf)\n return\n }\n let child = tree.get(first)\n if (!child || child.node) {\n child = new Map()\n tree.set(first, child)\n }\n insertIntoTree(child, rest, leaf)\n}\n\nfunction renderTree(tree: Map<string, any>, indent: string): string {\n const lines: string[] = ['{']\n for (const [key, value] of tree) {\n const safeKey = renderPropKey(key)\n if (value instanceof Map) {\n lines.push(`${indent} ${safeKey}: ${renderTree(value, indent + ' ')};`)\n } else {\n const { node, abi } = value as { node: ContractNode; abi: Abi }\n lines.push(\n `${indent} ${safeKey}: ${renderContractType(node, abi, indent + ' ')};`\n )\n }\n }\n lines.push(`${indent}}`)\n return lines.join('\\n')\n}\n\nfunction renderPropKey(name: string): string {\n return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(name) ? name : JSON.stringify(name)\n}\n\nfunction renderContractType(\n node: ContractNode,\n abi: Abi,\n indent: string\n): string {\n const members: string[] = []\n members.push(\n `${indent} [EVERYTHING]: (options?: Options) => TargetPermission;`\n )\n\n // Members mirror what the runtime proxy can resolve, which is whatever\n // `Interface.getFunction` accepts. A name it would find ambiguous is not a\n // member there — the proxy returns undefined for it — so an overloaded name\n // is emitted once per full signature and never bare. Declaring the bare name\n // anyway is what used to make `allow.eth.susds.deposit(…)` type-check and\n // then fail at runtime.\n for (const overloads of groupByName(abi).values()) {\n const overloaded = overloads.length > 1\n for (const fragment of overloads) {\n const key = overloaded\n ? signatureFor(fragment)\n : (fragment.name as string)\n members.push(renderFunctionSignature(fragment, key, indent + ' '))\n }\n }\n return ['{', ...members, `${indent}}`].join('\\n')\n}\n\n/**\n * Permissionable fragments grouped by function name, in ABI order. Two\n * fragments that render the same signature are the same function listed twice,\n * so they collapse — emitting the key twice would not compile.\n */\nfunction groupByName(abi: Abi): Map<string, AbiFragment[]> {\n const byName = new Map<string, AbiFragment[]>()\n for (const fragment of abi) {\n if (fragment.type !== 'function') continue\n if (\n fragment.stateMutability === 'view' ||\n fragment.stateMutability === 'pure'\n ) {\n continue\n }\n const name = fragment.name as string\n if (!name) continue\n const overloads = byName.get(name) ?? []\n if (overloads.some((f) => signatureFor(f) === signatureFor(fragment))) {\n continue\n }\n byName.set(name, [...overloads, fragment])\n }\n return byName\n}\n\n/** The canonical `name(type,…)` form, which is how `Interface.getFunction`\n * addresses one overload of a name. */\nfunction signatureFor(fragment: AbiFragment): string {\n const inputs = (fragment.inputs as AbiFragment[]) ?? []\n return `${fragment.name}(${inputs.map(canonicalTypeFor).join(',')})`\n}\n\nfunction canonicalTypeFor(fragment: AbiFragment): string {\n const type = fragment.type as string\n\n const arrayMatch = /^(.*)\\[(\\d*)\\]$/.exec(type)\n if (arrayMatch) {\n const inner: AbiFragment = { ...fragment, type: arrayMatch[1] }\n return `${canonicalTypeFor(inner)}[${arrayMatch[2]}]`\n }\n\n if (type === 'tuple') {\n const components = (fragment.components as AbiFragment[]) ?? []\n return `(${components.map(canonicalTypeFor).join(',')})`\n }\n\n return type\n}\n\nfunction renderFunctionSignature(\n fragment: AbiFragment,\n key: string,\n indent: string\n): string {\n const name = renderPropKey(key)\n const params: string[] = []\n for (const input of (fragment.inputs as AbiFragment[]) ?? []) {\n const paramName = sanitizeParamName(\n input.name || `arg${params.length}`,\n params.length\n )\n params.push(`${paramName}?: Scoping<${tsTypeFor(input)}>`)\n }\n params.push(`options?: Options`)\n return `${indent}${name}: (${params.join(', ')}) => FunctionPermission;`\n}\n\nfunction sanitizeParamName(name: string, index: number): string {\n const cleaned = name.replace(/[^A-Za-z0-9_$]/g, '_')\n if (!cleaned || /^[0-9]/.test(cleaned)) return `arg${index}`\n // Named tuple element labels can't collide with TS reserved words.\n const reserved = new Set([\n 'function',\n 'class',\n 'new',\n 'number',\n 'string',\n 'object',\n 'boolean',\n 'symbol',\n 'default',\n 'return',\n 'this',\n 'void',\n 'delete',\n 'in',\n 'of',\n 'for',\n 'while',\n 'switch',\n 'case',\n 'if',\n 'else',\n 'null',\n 'true',\n 'false',\n 'undefined',\n 'any',\n 'never',\n 'unknown',\n ])\n return reserved.has(cleaned) ? `_${cleaned}` : cleaned\n}\n\nfunction tsTypeFor(fragment: AbiFragment): string {\n const type = fragment.type as string\n\n const arrayMatch = /^(.*)\\[(\\d*)\\]$/.exec(type)\n if (arrayMatch) {\n const inner: AbiFragment = { ...fragment, type: arrayMatch[1] }\n return `readonly (${tsTypeFor(inner)})[]`\n }\n\n if (type === 'tuple') {\n const components = (fragment.components as AbiFragment[]) ?? []\n if (components.length === 0) return 'Record<string, unknown>'\n const fields = components.map((c, i) => {\n const key = sanitizeParamName(c.name || `f${i}`, i)\n return `${renderPropKey(key)}: ${tsTypeFor(c)}`\n })\n return `{ ${fields.join('; ')} }`\n }\n\n if (type === 'address') return '`0x${string}`'\n if (type === 'bool') return 'boolean'\n if (type === 'string') return 'string'\n if (type === 'bytes') return 'import(\"ethers\").BytesLike'\n if (/^bytes\\d+$/.test(type)) return '`0x${string}`'\n if (/^u?int\\d*$/.test(type)) return 'import(\"ethers\").BigNumberish'\n return 'unknown'\n}\n\nexport function writeGenerated(outFile: string, source: string): void {\n fs.mkdirSync(path.dirname(outFile), { recursive: true })\n fs.writeFileSync(outFile, source, 'utf8')\n}\n","import fs from 'node:fs'\nimport path from 'node:path'\nimport type { ResolvedConfig } from '../config'\nimport { resolveAbisDir } from '../config'\nimport { resolveZodiacDir } from '../../paths'\nimport { abiFilePath, walkContracts, writeAbi } from '../../allow/abi'\nimport { fetchAbi } from '../../allow/fetch'\nimport { chainIdFor } from '../../allow/networks'\nimport { generateAllowTypes, writeGenerated } from '../../allow/codegen'\n\ntype Status = 'ok' | 'fetched' | 'missing'\n\nexport const pullContracts = async (config: ResolvedConfig) => {\n if (!config.contracts || Object.keys(config.contracts).length === 0) {\n console.log('No contracts defined in config, skipping.')\n return\n }\n\n const abisDir = resolveAbisDir(config)\n const generatedFile = path.join(\n resolveZodiacDir(config.rootDir),\n 'allow.d.ts'\n )\n\n let missing = 0\n let fetched = 0\n let existing = 0\n\n for (const node of walkContracts(config.contracts)) {\n const file = abiFilePath(abisDir, node)\n\n if (fs.existsSync(file)) {\n existing++\n report(node.chain, node.segments, node.address, 'ok', file)\n continue\n }\n\n let chainId: number\n try {\n chainId = chainIdFor(node.chain)\n } catch (error) {\n missing++\n report(\n node.chain,\n node.segments,\n node.address,\n 'missing',\n file,\n (error as Error).message\n )\n continue\n }\n\n const abi = await fetchAbi(chainId, node.address)\n if (!abi) {\n missing++\n report(\n node.chain,\n node.segments,\n node.address,\n 'missing',\n file,\n `api.abi.pub returned no ABI for chain ${chainId}`\n )\n continue\n }\n writeAbi(abisDir, node, abi)\n fetched++\n report(node.chain, node.segments, node.address, 'fetched', file)\n }\n\n console.log('')\n console.log(\n `Contracts summary: ${existing} existing, ${fetched} fetched, ${missing} missing.`\n )\n if (missing > 0) {\n console.log('')\n console.log('Missing ABIs must be provided manually. Paste the contract')\n console.log(\n 'ABI JSON at the paths listed above and re-run `zodiac pull-contracts`.'\n )\n }\n\n const source = generateAllowTypes(abisDir, config.contracts)\n writeGenerated(generatedFile, source)\n console.log('')\n console.log(`Wrote typings to ${path.relative(process.cwd(), generatedFile)}`)\n\n if (missing > 0) process.exit(1)\n}\n\nfunction report(\n chain: string,\n segments: string[],\n address: string,\n status: Status,\n file: string,\n reason?: string\n) {\n const label = `${chain}.${segments.join('.')}`.padEnd(40, ' ')\n const tag = {\n ok: ' cached ',\n fetched: ' fetched ',\n missing: ' MISSING ',\n }[status]\n const suffix = reason ? ` — ${reason}` : ''\n console.log(`${tag} ${label} ${address}${suffix}`)\n if (status === 'missing') {\n console.log(` → paste ABI at ${file}`)\n }\n}\n","import { Command } from 'commander'\nimport { config as loadDotenv } from 'dotenv'\nimport { checkForUpdate, ownVersion } from './checkForUpdate'\nimport { init } from './commands/init'\nimport { loadConfig } from './config'\nimport { pullOrg } from './commands/pullOrg'\nimport { pullContracts } from './commands/pullContracts'\n\n// Load `.env` from the current working directory before reading any env vars.\nloadDotenv({ quiet: true })\n\nconst loadConfigOrInit = (configPath: string) =>\n loadConfig(configPath, {\n createIfMissing: true,\n onMissingKey: async (rootDir) => {\n console.log(\n 'No ZODIAC_API_KEY found. Starting authorization to mint one for this directory…'\n )\n return init({ rootDir })\n },\n })\n\nexport const run = async (argv: string[] = process.argv) => {\n const program = new Command()\n\n program\n .name('zodiac')\n .description('Zodiac SDK CLI – pull org data and contract ABIs')\n .version(ownVersion())\n .option(\n '-c, --config <path>',\n 'path to the config file',\n 'zodiac.config.ts'\n )\n\n // Started before the command so the registry round trip rides along with\n // the command's own work, printed after it so the notice is the last thing\n // on screen — and not at all when the command fails, where it would only\n // distract from the error. On stderr, so piped output stays clean.\n let updateNotice: Promise<string | null> = Promise.resolve(null)\n\n program.hook('preAction', () => {\n updateNotice = checkForUpdate()\n })\n\n program.hook('postAction', async () => {\n const notice = await updateNotice\n\n if (notice != null) {\n console.warn(`\\n${notice}`)\n }\n })\n\n program\n .command('pull')\n .description(\n 'Fetch Zodiac org data and contract ABIs, generating typed SDK functions. Authorizes this directory automatically on first run.'\n )\n .action(async (_opts, cmd) => {\n const config = await loadConfigOrInit(cmd.optsWithGlobals().config)\n await Promise.all([pullOrg(config), pullContracts(config)])\n })\n\n program\n .command('pull-org')\n .description('Fetch Zodiac users and accounts, generate TypeScript types')\n .action(async (_opts, cmd) => {\n const config = await loadConfigOrInit(cmd.optsWithGlobals().config)\n await pullOrg(config)\n })\n\n program\n .command('pull-contracts')\n .description('Fetch contract ABIs, generate typed permissions kit')\n .action(async (_opts, cmd) => {\n const config = await loadConfigOrInit(cmd.optsWithGlobals().config)\n await pullContracts(config)\n })\n\n program\n .command('init')\n .description(\n 'Re-authorize this directory / mint a new API key (run automatically by `pull` on first use).'\n )\n .option(\n '--app-url <url>',\n 'Override the Zodiac app URL (defaults to ZODIAC_APP_URL or app.zodiac.eco)'\n )\n .action(async (opts) => {\n await init({ appUrl: opts.appUrl })\n })\n\n await program.parseAsync(argv)\n}\n","#!/usr/bin/env node\nimport { run } from './run'\n\nrun().then(\n () => {\n process.exit(0)\n },\n (error: unknown) => {\n if (error) console.error(error)\n process.exit(1)\n }\n)\n"],"mappings":"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"}
1
+ {"version":3,"file":"cli.mjs","names":["join","loadDotenv"],"sources":["../src/cli/checkForUpdate.ts","../src/cli/commands/init.ts","../src/cli/commands/pullOrg.ts","../src/allow/fetch.ts","../src/allow/codegen.ts","../src/cli/commands/pullContracts.ts","../src/cli/run.ts","../src/cli/index.ts"],"sourcesContent":["import { existsSync, readFileSync } from 'node:fs'\nimport { dirname, join } from 'node:path'\nimport { fileURLToPath } from 'node:url'\nimport { findProjectRoot } from './projectRoot'\n\nconst PACKAGE = '@zodiaceco/sdk'\nconst REGISTRY_URL = `https://registry.npmjs.org/${PACKAGE}/latest`\nconst TIMEOUT_MS = 2_000\n\ntype Env = Record<string, string | undefined>\n\ntype CheckForUpdateOptions = {\n cwd?: string\n env?: Env\n}\n\n/**\n * Whether a newer release of this package is out, as the notice to print — or\n * `null`, which is also the answer to every failure. The check is a courtesy\n * on the way out of a command, so nothing here may throw or block for long,\n * and a run without a terminal to read the notice skips it entirely.\n */\nexport const checkForUpdate = async ({\n cwd = process.cwd(),\n env = process.env,\n}: CheckForUpdateOptions = {}): Promise<string | null> => {\n if (env.CI) {\n return null\n }\n\n try {\n const current = ownVersion()\n const latest = await fetchLatestVersion()\n\n if (latest == null || !isNewer(latest, current)) {\n return null\n }\n\n return [\n `A newer version of ${PACKAGE} is available (${current} → ${latest}). Update with:`,\n ` ${updateCommand(detectPackageManager(cwd, env))}`,\n ].join('\\n')\n } catch {\n return null\n }\n}\n\n/**\n * The version of the package this module runs from. Walks up to the\n * `package.json` that names it, which holds from `src/` and from `dist/`\n * alike — and finds this package, not the caller's, when the CLI runs\n * inside another project.\n */\nexport const ownVersion = (): string => {\n let dir = dirname(fileURLToPath(import.meta.url))\n\n while (true) {\n const manifest = join(dir, 'package.json')\n\n if (existsSync(manifest)) {\n const { name, version } = JSON.parse(readFileSync(manifest, 'utf-8'))\n\n if (name === PACKAGE && typeof version === 'string') {\n return version\n }\n }\n\n const parent = dirname(dir)\n if (parent === dir) {\n throw new Error(`Could not find the package.json of ${PACKAGE}`)\n }\n dir = parent\n }\n}\n\nconst fetchLatestVersion = async (): Promise<string | null> => {\n const response = await fetch(REGISTRY_URL, {\n signal: AbortSignal.timeout(TIMEOUT_MS),\n })\n\n if (!response.ok) {\n return null\n }\n\n const { version } = (await response.json()) as { version?: unknown }\n\n return typeof version === 'string' ? version : null\n}\n\nconst isNewer = (latest: string, current: string): boolean => {\n const a = parseVersion(latest)\n const b = parseVersion(current)\n\n if (a == null || b == null) {\n return false\n }\n\n for (let i = 0; i < 3; i++) {\n if (a[i] !== b[i]) {\n return a[i] > b[i]\n }\n }\n\n return false\n}\n\nconst parseVersion = (version: string): [number, number, number] | null => {\n const match = /^(\\d+)\\.(\\d+)\\.(\\d+)/.exec(version)\n\n if (match == null) {\n return null\n }\n\n return [Number(match[1]), Number(match[2]), Number(match[3])]\n}\n\ntype PackageManager = 'npm' | 'pnpm' | 'yarn' | 'bun'\n\n/**\n * The package manager an update should go through: whichever one launched\n * this process, or failing that, whichever one's lockfile the project\n * carries. `npm` when neither says anything.\n */\nconst detectPackageManager = (cwd: string, env: Env): PackageManager => {\n const agent = env.npm_config_user_agent\n\n if (agent != null) {\n const fromAgent = PACKAGE_MANAGERS.find((packageManager) =>\n agent.startsWith(`${packageManager}/`)\n )\n\n if (fromAgent != null) {\n return fromAgent\n }\n }\n\n const root = findProjectRoot(cwd)\n const fromLockfile = LOCKFILES.find(([lockfile]) =>\n existsSync(join(root, lockfile))\n )\n\n return fromLockfile == null ? 'npm' : fromLockfile[1]\n}\n\nconst PACKAGE_MANAGERS = ['pnpm', 'yarn', 'bun', 'npm'] as const\n\nconst LOCKFILES: [string, PackageManager][] = [\n ['bun.lock', 'bun'],\n ['bun.lockb', 'bun'],\n ['pnpm-lock.yaml', 'pnpm'],\n ['yarn.lock', 'yarn'],\n ['package-lock.json', 'npm'],\n]\n\nconst updateCommand = (packageManager: PackageManager): string =>\n packageManager === 'npm'\n ? `npm install ${PACKAGE}@latest`\n : `${packageManager} add ${PACKAGE}@latest`\n","import { createHash, randomBytes } from 'node:crypto'\nimport {\n createServer,\n type IncomingMessage,\n type ServerResponse,\n} from 'node:http'\nimport { existsSync, readFileSync, writeFileSync } from 'node:fs'\nimport { basename, join, resolve } from 'node:path'\nimport open from 'open'\nimport { ensureConfigStub } from '../config'\nimport { findProjectRoot } from '../projectRoot'\n\nconst DEFAULT_APP_URL = 'https://app.zodiac.eco'\nconst CALLBACK_TIMEOUT_MS = 5 * 60 * 1000 // 5 minutes\n\ntype InitOptions = {\n rootDir?: string\n /** Override the Zodiac app base URL (defaults to ZODIAC_APP_URL env or app.zodiac.eco). */\n appUrl?: string\n}\n\nexport const init = async (options: InitOptions = {}): Promise<string> => {\n const rootDir = options.rootDir ?? findProjectRoot()\n const appUrl = (\n options.appUrl ??\n process.env.ZODIAC_APP_URL ??\n DEFAULT_APP_URL\n ).replace(/\\/$/, '')\n\n const label = basename(resolve(rootDir)) || 'zodiac-cli'\n\n // PKCE: keep `code_verifier` private to this process. Send only the\n // SHA-256 hash (`code_challenge`) over the wire.\n const codeVerifier = randomBytes(32).toString('base64url')\n const codeChallenge = createHash('sha256')\n .update(codeVerifier)\n .digest('base64url')\n\n // `state` binds the redirect we receive on the loopback to this CLI\n // invocation, defending against a stray local process trying to inject\n // an auth code into our callback.\n const state = randomBytes(32).toString('base64url')\n\n const { port, waitForCode, close } = await startCallbackServer({\n expectedState: state,\n appUrl,\n })\n\n const callbackUrl = `http://127.0.0.1:${port}/callback`\n const authUrl = new URL('/cli-auth', appUrl)\n authUrl.searchParams.set('callback', callbackUrl)\n authUrl.searchParams.set('state', state)\n authUrl.searchParams.set('code_challenge', codeChallenge)\n authUrl.searchParams.set('label', label)\n\n console.log(\n `Opening ${authUrl} in your browser. Approve the request to receive an API key.`\n )\n await open(authUrl.toString())\n\n let code: string\n try {\n code = await waitForCode(CALLBACK_TIMEOUT_MS)\n } finally {\n await close()\n }\n\n const apiKey = await exchangeCodeForKey(appUrl, code, codeVerifier)\n\n const envPath = join(rootDir, '.env')\n const apiUrl = `${appUrl}/api/v1`\n writeEnv(envPath, { ZODIAC_API_KEY: apiKey, ZODIAC_API_URL: apiUrl })\n\n // So a same-run `pull` targets the org we just authorized, not the default.\n process.env.ZODIAC_API_KEY = apiKey\n process.env.ZODIAC_API_URL = apiUrl\n\n console.log(`✅ API key written to ${envPath}`)\n\n const configPath = join(rootDir, 'zodiac.config.ts')\n if (ensureConfigStub(configPath)) {\n console.log(`✅ Created ${configPath}`)\n }\n\n return apiKey\n}\n\nconst exchangeCodeForKey = async (\n appUrl: string,\n code: string,\n codeVerifier: string\n): Promise<string> => {\n const response = await fetch(`${appUrl}/cli-auth/exchange`, {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ code, code_verifier: codeVerifier }),\n })\n\n if (!response.ok) {\n let detail = ''\n try {\n const body = (await response.json()) as { error?: string }\n detail = body.error ? `: ${body.error}` : ''\n } catch {\n // ignore — fall through to a generic message\n }\n throw new Error(\n `Failed to exchange auth code (${response.status})${detail}`\n )\n }\n\n const { key } = (await response.json()) as { key: unknown }\n if (typeof key !== 'string' || !key.startsWith('zodiac_')) {\n throw new Error(\n 'Exchange endpoint returned an unexpected response (missing or malformed key)'\n )\n }\n return key\n}\n\ntype StartServerOptions = {\n expectedState: string\n appUrl: string\n}\n\ntype StartServerResult = {\n port: number\n waitForCode: (timeoutMs: number) => Promise<string>\n close: () => Promise<void>\n}\n\nconst startCallbackServer = async ({\n expectedState,\n appUrl,\n}: StartServerOptions): Promise<StartServerResult> => {\n let resolveCode: (code: string) => void = () => {}\n let rejectCode: (err: Error) => void = () => {}\n const codePromise = new Promise<string>((res, rej) => {\n resolveCode = res\n rejectCode = rej\n })\n\n const handler = (req: IncomingMessage, res: ServerResponse) => {\n if (req.method !== 'GET' || !req.url?.startsWith('/callback')) {\n res.statusCode = 404\n res.end()\n return\n }\n\n const url = new URL(req.url, `http://127.0.0.1`)\n const code = url.searchParams.get('code')\n const state = url.searchParams.get('state')\n\n if (state !== expectedState) {\n // A stray request — could be a stale browser tab, a malicious\n // local process, etc. Silently 403 and keep waiting for the right\n // one. Don't reject the CLI promise.\n respondHtml(\n res,\n 403,\n renderHtml(\n 'Mismatched state',\n `<p>This callback didn't match the running <code>zodiac init</code> session. You can close this tab.</p>`\n )\n )\n return\n }\n\n if (typeof code !== 'string' || code.length === 0) {\n respondHtml(\n res,\n 400,\n renderHtml(\n 'Missing code',\n `<p>The callback URL didn't include an auth <code>code</code>. Please re-run <code>zodiac init</code>.</p>`\n )\n )\n return\n }\n\n respondHtml(\n res,\n 200,\n renderHtml(\n 'Authorized',\n `<p>Your CLI now has its API key. You can close this tab and return to your terminal.</p>\n <p><a href=\"${escapeHtml(appUrl)}\">Back to Zodiac</a></p>`\n )\n )\n resolveCode(code)\n }\n\n const server = createServer(handler)\n await new Promise<void>((res) => server.listen(0, '127.0.0.1', res))\n const address = server.address()\n if (address == null || typeof address === 'string') {\n throw new Error('Failed to bind loopback callback server')\n }\n const port = address.port\n\n const close = (): Promise<void> =>\n new Promise<void>((resolve) => {\n server.close(() => resolve())\n })\n\n const waitForCode = (timeoutMs: number) =>\n Promise.race<string>([\n codePromise,\n new Promise<string>((_, rej) =>\n setTimeout(() => {\n rejectCode(new Error(`Timed out waiting for CLI auth callback`))\n rej(new Error(`Timed out waiting for CLI auth callback`))\n }, timeoutMs)\n ),\n ])\n\n return { port, waitForCode, close }\n}\n\nconst respondHtml = (res: ServerResponse, status: number, body: string) => {\n // Force the connection to close after the response so the browser\n // doesn't keep waiting on a keep-alive socket once the CLI exits.\n // Resolve only after 'finish' (response handed to the OS) so the\n // bytes can flush before the process tears down.\n res.setHeader('Connection', 'close')\n res.setHeader('content-type', 'text/html; charset=utf-8')\n res.statusCode = status\n res.end(body)\n}\n\nconst renderHtml = (title: string, bodyHtml: string) => `<!doctype html>\n<html lang=\"en\">\n <head>\n <meta charset=\"utf-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n <title>${escapeHtml(title)} — Zodiac CLI</title>\n <style>\n body { font: 16px/1.5 -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif; max-width: 32rem; margin: 4rem auto; padding: 0 1rem; color: #1f2937; }\n h1 { font-weight: 300; font-size: 1.75rem; margin-bottom: 1rem; }\n code { background: #f3f4f6; padding: 0.125rem 0.375rem; border-radius: 0.25rem; }\n a { color: #1d4ed8; }\n </style>\n </head>\n <body>\n <h1>${escapeHtml(title)}</h1>\n ${bodyHtml}\n </body>\n</html>\n`\n\nconst escapeHtml = (value: string) =>\n value\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;')\n .replace(/'/g, '&#39;')\n\nconst writeEnv = (envPath: string, vars: Record<string, string>): void => {\n let contents = existsSync(envPath) ? readFileSync(envPath, 'utf8') : ''\n\n for (const [key, value] of Object.entries(vars)) {\n contents = upsertEnvLine(contents, key, value)\n }\n\n writeFileSync(envPath, contents, 'utf8')\n}\n\nconst upsertEnvLine = (\n contents: string,\n key: string,\n value: string\n): string => {\n const line = `${key}=${value}`\n const lines = contents.split(/\\r?\\n/)\n const idx = lines.findIndex((l) => new RegExp(`^\\\\s*${key}\\\\s*=`).test(l))\n\n if (idx >= 0) {\n lines[idx] = line\n return lines.join('\\n')\n }\n\n return contents.length === 0 || contents.endsWith('\\n')\n ? `${contents}${line}\\n`\n : `${contents}\\n${line}\\n`\n}\n","import type {\n Address,\n ChainId,\n ResolveConstellationPayload,\n} from '@zodiaceco/api-types'\nimport type { ResolvedConfig } from '../config'\nimport { ApiClient } from '../../api'\nimport { getAddress } from 'ethers'\nimport {\n ModuleKind,\n Project,\n ScriptTarget,\n VariableDeclarationKind,\n} from 'ts-morph'\nimport { mkdirSync, writeFileSync } from 'fs'\nimport { join } from 'path'\nimport { resolveZodiacDir } from '../../paths'\n\nconst toLiteral = (value: unknown, indent = 0): string => {\n const pad = ' '.repeat(indent)\n const childPad = ' '.repeat(indent + 1)\n\n if (value === null) return 'null'\n if (typeof value === 'bigint') return `${value}n`\n if (typeof value === 'string') return JSON.stringify(value)\n if (typeof value === 'number' || typeof value === 'boolean')\n return String(value)\n if (Array.isArray(value)) {\n if (value.length === 0) return '[]'\n return `[\\n${value.map((v) => `${childPad}${toLiteral(v, indent + 1)}`).join(',\\n')},\\n${pad}]`\n }\n if (typeof value === 'object') {\n const entries = Object.entries(value as Record<string, unknown>)\n if (entries.length === 0) return '{}'\n const props = entries.map(\n ([k, v]) =>\n `${childPad}${isBareKey(k) ? k : JSON.stringify(k)}: ${toLiteral(v, indent + 1)}`\n )\n return `{\\n${props.join(',\\n')},\\n${pad}}`\n }\n return String(value)\n}\n\ntype NodeType = 'SAFE' | 'ROLES' | 'DELAY'\n\n/**\n * An account written as the specification node that names it and declares\n * nothing else.\n *\n * The `ref` is required of every node and has to be distinct, but nothing\n * reads it back — the result comes aligned with the request.\n *\n * Spelled out per type rather than passed through: `/accounts` answers with\n * the same three names as an enum, whose members are types of their own, and\n * a node is only a node once it says which of the three it is.\n */\nconst asNodeReference = (\n {\n type,\n chain,\n address,\n }: { type: string; chain: ChainId; address: Lowercase<Address> },\n index: number\n): ResolveConstellationPayload['specification'][number] => {\n const node = { ref: `account_${index}`, chain, address }\n\n switch (asNodeType(type)) {\n case 'SAFE':\n return { ...node, type: 'SAFE' }\n case 'ROLES':\n return { ...node, type: 'ROLES' }\n case 'DELAY':\n return { ...node, type: 'DELAY' }\n }\n}\n\nconst asNodeType = (type: string): NodeType => {\n if (type === 'SAFE' || type === 'ROLES' || type === 'DELAY') {\n return type\n }\n\n throw new Error(`Cannot describe an account of type \"${type}\"`)\n}\n\n/**\n * Whether a key can be written without quotes. Chain ids are grouping keys in\n * the generated data, and an unquoted number keys the `as const` type by chain\n * id — quoted, it would key by a string the callers never hold.\n */\nconst isBareKey = (key: string): boolean =>\n /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(key) || /^\\d+$/.test(key)\n\nexport const pullOrg = async (config: ResolvedConfig) => {\n const client = new ApiClient({\n apiKey: config.apiKey,\n })\n\n const [users, workspaceAccounts] = await Promise.all([\n client.listUsers(),\n client.listAccounts(),\n ])\n\n // The generated accounts carry their onchain state so a node reads as the\n // account it names and completes in an editor. Only what a constellation\n // declares is ever pushed back, so this is for reading, not a declaration.\n //\n // Each account is named by its address and nothing else. `/resolve`\n // resolves a node it has no address for against the setup safe of whoever\n // holds the API key — which describes the account *that* caller would\n // deploy, not the one being asked about — and it merges declarations over\n // what it reads. Sending an address and no declarations leaves it nothing\n // to derive and nothing to merge, so the answer is the account itself, the\n // same for every key.\n //\n // Asking about every listed account is safe for the same reason: `/accounts`\n // admits one only once it has been seen to hold code, so every address here\n // is one that can be read from chain.\n const allAccounts = workspaceAccounts.flatMap((ws) => ws.accounts)\n const resolved = new Map<\n string,\n Awaited<ReturnType<typeof client.resolveConstellation>>['result'][number]\n >()\n\n if (allAccounts.length > 0) {\n const { result } = await client.resolveConstellation(\n workspaceAccounts[0].workspaceId, // any workspace works for the resolve route\n {\n specification: allAccounts.map(asNodeReference),\n }\n )\n\n // Aligned with the request, entry for entry.\n allAccounts.forEach((account, index) => {\n resolved.set(account.id, result[index])\n })\n }\n\n // Group accounts by type and then by chain into separate bracket-access\n // namespaces: `safes[chain]`, `rolesMods[chain]`, `delays[chain]`. This way\n // `eth.safe[...]` IntelliSense only suggests SAFE labels deployed on the\n // constellation's own chain, and the label-collision suffix only kicks in\n // where a label is genuinely ambiguous — same type, same chain.\n type AccountsByChain = Record<number, Record<string, unknown>>\n\n const accountsRecord: Record<string, unknown> = {}\n for (const ws of workspaceAccounts) {\n const safes: AccountsByChain = {}\n const rolesMods: AccountsByChain = {}\n const delays: AccountsByChain = {}\n\n const bucketsByType = {\n SAFE: safes,\n ROLES: rolesMods,\n DELAY: delays,\n } as const\n\n const isNodeType = (type: string): type is NodeType =>\n type === 'SAFE' || type === 'ROLES' || type === 'DELAY'\n\n // Accounts we only know by address — Safe owners, external role\n // members, route targets — carry no label. They can't key a\n // label-addressed namespace, so they stay out of the codegen.\n // Vault entries always carry one.\n\n // Count labels per namespace so we only suffix collisions a constellation\n // could actually run into. Two safes named `Treasury` on different chains\n // never compete for a key, because a constellation only ever sees one of\n // those chains.\n const namespaceKey = (type: NodeType, chain: number) => `${type}:${chain}`\n const labelCounts = new Map<string, Map<string, number>>()\n\n for (const account of ws.accounts) {\n const { label } = account\n if (label == null || !isNodeType(account.type)) continue\n const key = namespaceKey(account.type, account.chain)\n let counts = labelCounts.get(key)\n if (!counts) {\n counts = new Map()\n labelCounts.set(key, counts)\n }\n counts.set(label, (counts.get(label) ?? 0) + 1)\n }\n\n for (const account of ws.accounts) {\n const { label } = account\n if (label == null || !isNodeType(account.type)) continue\n const onChain = resolved.get(account.id)\n const counts = labelCounts.get(namespaceKey(account.type, account.chain))\n const key =\n (counts?.get(label) ?? 0) > 1\n ? `${label} (${getAddress(account.address)})`\n : label\n const byChain = bucketsByType[account.type]\n const byLabel = (byChain[account.chain] ??= {})\n byLabel[key] = {\n id: account.id,\n label,\n address: account.address,\n chain: account.chain,\n vault: account.vault,\n ...(onChain?.type === 'SAFE' && {\n threshold: onChain.threshold,\n owners: [...onChain.owners],\n modules: [...onChain.modules],\n }),\n }\n }\n\n accountsRecord[ws.workspaceName] = {\n workspaceId: ws.workspaceId,\n workspaceName: ws.workspaceName,\n safes,\n rolesMods,\n delays,\n }\n }\n\n const nameCount = new Map<string, number>()\n for (const user of users) {\n nameCount.set(user.fullName, (nameCount.get(user.fullName) ?? 0) + 1)\n }\n\n const usersRecord: Record<string, unknown> = {}\n for (const user of users) {\n const handle =\n nameCount.get(user.fullName)! > 1\n ? `${user.fullName} (${user.id})`\n : user.fullName\n usersRecord[handle] = {\n id: user.id,\n fullName: user.fullName,\n personalSafes: user.personalSafes,\n }\n }\n\n const outDir = resolveZodiacDir(config.rootDir)\n\n mkdirSync(outDir, { recursive: true })\n\n // Pin CJS so `require()` works regardless of the parent package.json's type\n writeFileSync(\n join(outDir, 'package.json'),\n JSON.stringify(\n {\n type: 'commonjs',\n main: 'index.js',\n types: 'index.d.ts',\n },\n null,\n 2\n )\n )\n\n // Use ts-morph to generate TS, then emit JS + d.ts\n const project = new Project({\n compilerOptions: {\n declaration: true,\n module: ModuleKind.CommonJS,\n target: ScriptTarget.ESNext,\n outDir,\n },\n useInMemoryFileSystem: true,\n })\n\n const sourceFile = project.createSourceFile('index.ts', '')\n\n sourceFile.addVariableStatement({\n isExported: true,\n declarationKind: VariableDeclarationKind.Const,\n declarations: [\n {\n name: 'users',\n initializer: `${toLiteral(usersRecord)} as const`,\n },\n ],\n })\n\n sourceFile.addVariableStatement({\n isExported: true,\n declarationKind: VariableDeclarationKind.Const,\n declarations: [\n {\n name: 'accounts',\n initializer: `${toLiteral(accountsRecord)} as const`,\n },\n ],\n })\n\n const emitResult = sourceFile.getEmitOutput()\n for (const outputFile of emitResult.getOutputFiles()) {\n const filePath = outputFile.getFilePath()\n const fileName = filePath.includes('.d.ts') ? 'index.d.ts' : 'index.js'\n let contents = outputFile.getText()\n // Augment the SDK's global `ZodiacGeneratedCodegen` interface so\n // `constellation()`'s default type parameter picks up these literal\n // shapes automatically.\n if (fileName === 'index.d.ts') {\n contents += `\ndeclare global {\n interface ZodiacGeneratedCodegen {\n users: typeof users;\n accounts: typeof accounts;\n }\n}\n`\n }\n writeFileSync(join(outDir, fileName), contents)\n }\n}\n","import { chainIdFor, type ChainPrefix } from './networks'\n\nexport type AbiFragment = Record<string, any>\nexport type Abi = AbiFragment[]\n\ntype Implementation = { abi?: unknown }\ntype Account = Implementation & {\n proxy?: unknown\n implementation?: Implementation\n}\n\n// Returns null on any failure so callers can fall back to a manual ABI file.\nexport async function fetchAbi(\n chainId: number,\n address: `0x${string}`\n): Promise<Abi | null> {\n const url = `https://api.abi.pub/v1/chains/${chainId}/accounts/${address}`\n let account: Account\n try {\n const resp = await fetch(url)\n if (!resp.ok) return null\n account = (await resp.json()) as Account\n } catch {\n return null\n }\n if (account == null || typeof account !== 'object') return null\n\n // For a proxy the endpoint reports the proxy under `proxy` and the contract\n // that actually holds the functions under `implementation`. Permissions are\n // written against the implementation ABI.\n const abi = account.proxy ? account.implementation?.abi : account.abi\n if (!Array.isArray(abi) || abi.length === 0) return null\n return abi as Abi\n}\n\nexport const fetchAbiForPrefix = (\n prefix: ChainPrefix,\n address: `0x${string}`\n) => fetchAbi(chainIdFor(prefix), address)\n","import fs from 'node:fs'\nimport path from 'node:path'\nimport { walkContracts, readAbi, type ContractNode } from './abi'\nimport type { Abi, AbiFragment } from './fetch'\n\n// Emits named tuple elements per ABI input — this is the reason we don't rely\n// on viem/abitype, which produces unnamed tuples (microsoft/TypeScript#44939).\nexport function generateAllowTypes(\n abisDir: string,\n contractsConfig: Record<string, any>\n): string {\n type Tree = Map<string, Tree | { node: ContractNode; abi: Abi }>\n\n const root: Tree = new Map()\n for (const node of walkContracts(contractsConfig)) {\n const abi = readAbi(abisDir, node)\n if (!abi) continue\n insertIntoTree(root, [node.chain, ...node.segments], { node, abi })\n }\n\n const out: string[] = []\n out.push('// AUTO-GENERATED by `zodiac pull-contracts`. Do not edit.')\n out.push('/* eslint-disable */')\n out.push('')\n out.push(\n `import type { Scoping, Options, EVERYTHING, FunctionPermission, TargetPermission } from \"@zodiaceco/sdk/allow\";`\n )\n out.push('')\n out.push('declare global {')\n out.push(' interface AllowKit ' + renderTree(root, ' '))\n out.push('}')\n out.push('')\n out.push('export {};')\n out.push('')\n return out.join('\\n')\n}\n\nfunction insertIntoTree(\n tree: Map<string, any>,\n segments: string[],\n leaf: { node: ContractNode; abi: Abi }\n): void {\n const [first, ...rest] = segments\n if (!first) throw new Error('empty segments')\n if (rest.length === 0) {\n tree.set(first, leaf)\n return\n }\n let child = tree.get(first)\n if (!child || child.node) {\n child = new Map()\n tree.set(first, child)\n }\n insertIntoTree(child, rest, leaf)\n}\n\nfunction renderTree(tree: Map<string, any>, indent: string): string {\n const lines: string[] = ['{']\n for (const [key, value] of tree) {\n const safeKey = renderPropKey(key)\n if (value instanceof Map) {\n lines.push(`${indent} ${safeKey}: ${renderTree(value, indent + ' ')};`)\n } else {\n const { node, abi } = value as { node: ContractNode; abi: Abi }\n lines.push(\n `${indent} ${safeKey}: ${renderContractType(node, abi, indent + ' ')};`\n )\n }\n }\n lines.push(`${indent}}`)\n return lines.join('\\n')\n}\n\nfunction renderPropKey(name: string): string {\n return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(name) ? name : JSON.stringify(name)\n}\n\nfunction renderContractType(\n node: ContractNode,\n abi: Abi,\n indent: string\n): string {\n const members: string[] = []\n members.push(\n `${indent} [EVERYTHING]: (options?: Options) => TargetPermission;`\n )\n\n // Members mirror what the runtime proxy can resolve, which is whatever\n // `Interface.getFunction` accepts. A name it would find ambiguous is not a\n // member there — the proxy returns undefined for it — so an overloaded name\n // is emitted once per full signature and never bare. Declaring the bare name\n // anyway is what used to make `allow.eth.susds.deposit(…)` type-check and\n // then fail at runtime.\n for (const overloads of groupByName(abi).values()) {\n const overloaded = overloads.length > 1\n for (const fragment of overloads) {\n const key = overloaded\n ? signatureFor(fragment)\n : (fragment.name as string)\n members.push(renderFunctionSignature(fragment, key, indent + ' '))\n }\n }\n return ['{', ...members, `${indent}}`].join('\\n')\n}\n\n/**\n * Permissionable fragments grouped by function name, in ABI order. Two\n * fragments that render the same signature are the same function listed twice,\n * so they collapse — emitting the key twice would not compile.\n */\nfunction groupByName(abi: Abi): Map<string, AbiFragment[]> {\n const byName = new Map<string, AbiFragment[]>()\n for (const fragment of abi) {\n if (fragment.type !== 'function') continue\n if (\n fragment.stateMutability === 'view' ||\n fragment.stateMutability === 'pure'\n ) {\n continue\n }\n const name = fragment.name as string\n if (!name) continue\n const overloads = byName.get(name) ?? []\n if (overloads.some((f) => signatureFor(f) === signatureFor(fragment))) {\n continue\n }\n byName.set(name, [...overloads, fragment])\n }\n return byName\n}\n\n/** The canonical `name(type,…)` form, which is how `Interface.getFunction`\n * addresses one overload of a name. */\nfunction signatureFor(fragment: AbiFragment): string {\n const inputs = (fragment.inputs as AbiFragment[]) ?? []\n return `${fragment.name}(${inputs.map(canonicalTypeFor).join(',')})`\n}\n\nfunction canonicalTypeFor(fragment: AbiFragment): string {\n const type = fragment.type as string\n\n const arrayMatch = /^(.*)\\[(\\d*)\\]$/.exec(type)\n if (arrayMatch) {\n const inner: AbiFragment = { ...fragment, type: arrayMatch[1] }\n return `${canonicalTypeFor(inner)}[${arrayMatch[2]}]`\n }\n\n if (type === 'tuple') {\n const components = (fragment.components as AbiFragment[]) ?? []\n return `(${components.map(canonicalTypeFor).join(',')})`\n }\n\n return type\n}\n\nfunction renderFunctionSignature(\n fragment: AbiFragment,\n key: string,\n indent: string\n): string {\n const name = renderPropKey(key)\n const params: string[] = []\n for (const input of (fragment.inputs as AbiFragment[]) ?? []) {\n const paramName = sanitizeParamName(\n input.name || `arg${params.length}`,\n params.length\n )\n params.push(`${paramName}?: Scoping<${tsTypeFor(input)}>`)\n }\n params.push(`options?: Options`)\n return `${indent}${name}: (${params.join(', ')}) => FunctionPermission;`\n}\n\nfunction sanitizeParamName(name: string, index: number): string {\n const cleaned = name.replace(/[^A-Za-z0-9_$]/g, '_')\n if (!cleaned || /^[0-9]/.test(cleaned)) return `arg${index}`\n // Named tuple element labels can't collide with TS reserved words.\n const reserved = new Set([\n 'function',\n 'class',\n 'new',\n 'number',\n 'string',\n 'object',\n 'boolean',\n 'symbol',\n 'default',\n 'return',\n 'this',\n 'void',\n 'delete',\n 'in',\n 'of',\n 'for',\n 'while',\n 'switch',\n 'case',\n 'if',\n 'else',\n 'null',\n 'true',\n 'false',\n 'undefined',\n 'any',\n 'never',\n 'unknown',\n ])\n return reserved.has(cleaned) ? `_${cleaned}` : cleaned\n}\n\nfunction tsTypeFor(fragment: AbiFragment): string {\n const type = fragment.type as string\n\n const arrayMatch = /^(.*)\\[(\\d*)\\]$/.exec(type)\n if (arrayMatch) {\n const inner: AbiFragment = { ...fragment, type: arrayMatch[1] }\n return `readonly (${tsTypeFor(inner)})[]`\n }\n\n if (type === 'tuple') {\n const components = (fragment.components as AbiFragment[]) ?? []\n if (components.length === 0) return 'Record<string, unknown>'\n const fields = components.map((c, i) => {\n const key = sanitizeParamName(c.name || `f${i}`, i)\n return `${renderPropKey(key)}: ${tsTypeFor(c)}`\n })\n return `{ ${fields.join('; ')} }`\n }\n\n if (type === 'address') return '`0x${string}`'\n if (type === 'bool') return 'boolean'\n if (type === 'string') return 'string'\n if (type === 'bytes') return 'import(\"ethers\").BytesLike'\n if (/^bytes\\d+$/.test(type)) return '`0x${string}`'\n if (/^u?int\\d*$/.test(type)) return 'import(\"ethers\").BigNumberish'\n return 'unknown'\n}\n\nexport function writeGenerated(outFile: string, source: string): void {\n fs.mkdirSync(path.dirname(outFile), { recursive: true })\n fs.writeFileSync(outFile, source, 'utf8')\n}\n","import fs from 'node:fs'\nimport path from 'node:path'\nimport type { ResolvedConfig } from '../config'\nimport { resolveAbisDir } from '../config'\nimport { resolveZodiacDir } from '../../paths'\nimport { abiFilePath, walkContracts, writeAbi } from '../../allow/abi'\nimport { fetchAbi } from '../../allow/fetch'\nimport { chainIdFor } from '../../allow/networks'\nimport { generateAllowTypes, writeGenerated } from '../../allow/codegen'\n\ntype Status = 'ok' | 'fetched' | 'missing'\n\nexport const pullContracts = async (config: ResolvedConfig) => {\n if (!config.contracts || Object.keys(config.contracts).length === 0) {\n console.log('No contracts defined in config, skipping.')\n return\n }\n\n const abisDir = resolveAbisDir(config)\n const generatedFile = path.join(\n resolveZodiacDir(config.rootDir),\n 'allow.d.ts'\n )\n\n let missing = 0\n let fetched = 0\n let existing = 0\n\n for (const node of walkContracts(config.contracts)) {\n const file = abiFilePath(abisDir, node)\n\n if (fs.existsSync(file)) {\n existing++\n report(node.chain, node.segments, node.address, 'ok', file)\n continue\n }\n\n let chainId: number\n try {\n chainId = chainIdFor(node.chain)\n } catch (error) {\n missing++\n report(\n node.chain,\n node.segments,\n node.address,\n 'missing',\n file,\n (error as Error).message\n )\n continue\n }\n\n const abi = await fetchAbi(chainId, node.address)\n if (!abi) {\n missing++\n report(\n node.chain,\n node.segments,\n node.address,\n 'missing',\n file,\n `api.abi.pub returned no ABI for chain ${chainId}`\n )\n continue\n }\n writeAbi(abisDir, node, abi)\n fetched++\n report(node.chain, node.segments, node.address, 'fetched', file)\n }\n\n console.log('')\n console.log(\n `Contracts summary: ${existing} existing, ${fetched} fetched, ${missing} missing.`\n )\n if (missing > 0) {\n console.log('')\n console.log('Missing ABIs must be provided manually. Paste the contract')\n console.log(\n 'ABI JSON at the paths listed above and re-run `zodiac pull-contracts`.'\n )\n }\n\n const source = generateAllowTypes(abisDir, config.contracts)\n writeGenerated(generatedFile, source)\n console.log('')\n console.log(`Wrote typings to ${path.relative(process.cwd(), generatedFile)}`)\n\n if (missing > 0) process.exit(1)\n}\n\nfunction report(\n chain: string,\n segments: string[],\n address: string,\n status: Status,\n file: string,\n reason?: string\n) {\n const label = `${chain}.${segments.join('.')}`.padEnd(40, ' ')\n const tag = {\n ok: ' cached ',\n fetched: ' fetched ',\n missing: ' MISSING ',\n }[status]\n const suffix = reason ? ` — ${reason}` : ''\n console.log(`${tag} ${label} ${address}${suffix}`)\n if (status === 'missing') {\n console.log(` → paste ABI at ${file}`)\n }\n}\n","import { Command } from 'commander'\nimport { config as loadDotenv } from 'dotenv'\nimport { checkForUpdate, ownVersion } from './checkForUpdate'\nimport { init } from './commands/init'\nimport { loadConfig } from './config'\nimport { pullOrg } from './commands/pullOrg'\nimport { pullContracts } from './commands/pullContracts'\nimport { pushEntrypoint } from './commands/push'\n\n// Load `.env` from the current working directory before reading any env vars.\nloadDotenv({ quiet: true })\n\nconst loadConfigOrInit = (configPath: string) =>\n loadConfig(configPath, {\n createIfMissing: true,\n onMissingKey: async (rootDir) => {\n console.log(\n 'No ZODIAC_API_KEY found. Starting authorization to mint one for this directory…'\n )\n return init({ rootDir })\n },\n })\n\nexport const run = async (argv: string[] = process.argv) => {\n const program = new Command()\n\n program\n .name('zodiac')\n .description(\n 'Zodiac SDK CLI – pull org data and contract ABIs, push constellations'\n )\n .version(ownVersion())\n .option(\n '-c, --config <path>',\n 'path to the config file',\n 'zodiac.config.ts'\n )\n\n // Started before the command so the registry round trip rides along with\n // the command's own work, printed after it so the notice is the last thing\n // on screen — and not at all when the command fails, where it would only\n // distract from the error. On stderr, so piped output stays clean.\n let updateNotice: Promise<string | null> = Promise.resolve(null)\n\n program.hook('preAction', () => {\n updateNotice = checkForUpdate()\n })\n\n program.hook('postAction', async () => {\n const notice = await updateNotice\n\n // A command that reports its own failure sets the exit code instead of\n // throwing, which still runs this hook.\n if (notice != null && !process.exitCode) {\n console.warn(`\\n${notice}`)\n }\n })\n\n program\n .command('pull')\n .description(\n 'Fetch Zodiac org data and contract ABIs, generating typed SDK functions. Authorizes this directory automatically on first run.'\n )\n .action(async (_opts, cmd) => {\n const config = await loadConfigOrInit(cmd.optsWithGlobals().config)\n await Promise.all([pullOrg(config), pullContracts(config)])\n })\n\n program\n .command('pull-org')\n .description('Fetch Zodiac users and accounts, generate TypeScript types')\n .action(async (_opts, cmd) => {\n const config = await loadConfigOrInit(cmd.optsWithGlobals().config)\n await pullOrg(config)\n })\n\n program\n .command('pull-contracts')\n .description('Fetch contract ABIs, generate typed permissions kit')\n .action(async (_opts, cmd) => {\n const config = await loadConfigOrInit(cmd.optsWithGlobals().config)\n await pullContracts(config)\n })\n\n program\n .command('push')\n .description(\n 'Push the nodes an entrypoint exports to Zodiac and open them for review. Projects that set up globals for their entrypoint call `pushEntrypoint` from `@zodiaceco/sdk/cli/push` after that setup instead.'\n )\n .argument(\n '[entrypoint]',\n 'module exporting the nodes (default: constellation/index.ts in the project root)'\n )\n .option('--no-open', 'do not open the review pages in a browser')\n .action(\n async (entrypoint: string | undefined, opts: { open: boolean }, cmd) => {\n await pushEntrypoint({\n entrypoint,\n openInBrowser: opts.open,\n config: cmd.optsWithGlobals().config,\n })\n }\n )\n\n program\n .command('init')\n .description(\n 'Re-authorize this directory / mint a new API key (run automatically by `pull` on first use).'\n )\n .option(\n '--app-url <url>',\n 'Override the Zodiac app URL (defaults to ZODIAC_APP_URL or app.zodiac.eco)'\n )\n .action(async (opts) => {\n await init({ appUrl: opts.appUrl })\n })\n\n await program.parseAsync(argv)\n}\n","#!/usr/bin/env node\nimport { run } from './run'\n\nrun().then(\n () => {\n // A command that reports its own failure, like a refused push, sets the\n // exit code instead of throwing.\n process.exit(process.exitCode ?? 0)\n },\n (error: unknown) => {\n if (error) console.error(error)\n process.exit(1)\n }\n)\n"],"mappings":"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"}
@@ -1,5 +1,6 @@
1
1
  import { existsSync, writeFileSync } from "node:fs";
2
2
  import { dirname, resolve } from "node:path";
3
+ import { config } from "dotenv";
3
4
  import { pathToFileURL } from "url";
4
5
  import { dirname as dirname$1, resolve as resolve$1 } from "path";
5
6
  //#region src/cli/projectRoot.ts
@@ -83,12 +84,16 @@ async function loadConfig(configPath = DEFAULT_CONFIG_PATH, options = {}) {
83
84
  if (error?.code === "ERR_MODULE_NOT_FOUND" || error?.code === "ENOENT") throw new Error(`Config file not found: ${absolutePath}`);
84
85
  throw error;
85
86
  }
86
- const config = mod.default ?? mod.config;
87
- if (!config) throw new Error(`Config file must export a default value or a named "config" export: ${absolutePath}`);
87
+ const config$1 = mod.default ?? mod.config;
88
+ if (!config$1) throw new Error(`Config file must export a default value or a named "config" export: ${absolutePath}`);
88
89
  const rootDir = dirname$1(absolutePath);
89
- const apiKey = await resolveApiKey(config, rootDir, options);
90
+ config({
91
+ path: resolve$1(rootDir, ".env"),
92
+ quiet: true
93
+ });
94
+ const apiKey = await resolveApiKey(config$1, rootDir, options);
90
95
  return {
91
- ...config,
96
+ ...config$1,
92
97
  apiKey,
93
98
  rootDir
94
99
  };
@@ -123,4 +128,4 @@ function resolveAbisDir(config) {
123
128
  //#endregion
124
129
  export { resolveAbisDir as a, loadConfig as i, defineConfig as n, findProjectRoot as o, ensureConfigStub as r, DEFAULT_ABIS_DIR as t };
125
130
 
126
- //# sourceMappingURL=config-D0FqysME.mjs.map
131
+ //# sourceMappingURL=config-BaChz2bq.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-BaChz2bq.mjs","names":["resolve","config","dirname"],"sources":["../src/cli/projectRoot.ts","../src/cli/config.ts"],"sourcesContent":["import { existsSync } from 'node:fs'\nimport { dirname, resolve } from 'node:path'\n\n/**\n * Walk up from `startDir` looking for a `package.json`. Returns the\n * directory that contains it. Falls back to `startDir` if no ancestor\n * has a `package.json` (e.g. when running outside any project).\n */\nexport const findProjectRoot = (startDir: string = process.cwd()): string => {\n const absoluteStart = resolve(startDir)\n let dir = absoluteStart\n\n while (true) {\n if (existsSync(resolve(dir, 'package.json'))) {\n return dir\n }\n const parent = dirname(dir)\n if (parent === dir) {\n return absoluteStart\n }\n dir = parent\n }\n}\n","import { config as loadDotenv } from 'dotenv'\nimport { existsSync, writeFileSync } from 'node:fs'\nimport { pathToFileURL } from 'url'\nimport { dirname, resolve } from 'path'\nimport { findProjectRoot } from './projectRoot'\n\nexport type Contracts = {\n [chain: string]: ContractsNode\n}\nexport type ContractsNode = `0x${string}` | { [name: string]: ContractsNode }\n\nexport interface ZodiacConfig {\n /**\n * API key authorizing this directory against a Zodiac org.\n *\n * Optional — defaults to `process.env.ZODIAC_API_KEY` (populated by\n * `zodiac init`). Set this explicitly only when you need to override\n * the env var from inside the config file.\n */\n apiKey?: `zodiac_${string}`\n /**\n * Contracts the `allow` kit should know about, keyed by chain prefix.\n * Nested objects are allowed for grouping related addresses.\n */\n contracts?: Contracts\n /**\n * Directory where fetched ABIs are stored and read from.\n * Resolved relative to the project root (config file's directory).\n * Defaults to `./abis`.\n */\n abisDir?: string\n}\n\n/** User-provided config plus the resolved API key + project root. */\nexport interface ResolvedConfig extends Omit<ZodiacConfig, 'apiKey'> {\n apiKey: `zodiac_${string}`\n rootDir: string\n}\n\n/**\n * Loose base used as the *inference* constraint for `defineConfig`.\n * `contracts` is `Record<string, unknown>` here so `const T` can preserve the\n * caller's exact address literals rather than collapsing them into the\n * recursive `ContractsNode` union.\n */\ntype DefineConfigInput = {\n apiKey?: `zodiac_${string}`\n contracts?: Record<string, unknown>\n abisDir?: string\n}\n\n/**\n * Recursive leaf-level check: every leaf in `contracts` must be\n * `` `0x${string}` ``; any other value collapses the branch to `never`,\n * which surfaces as a type error at the call site.\n */\ntype ValidateContracts<C> = {\n [K in keyof C]: C[K] extends `0x${string}`\n ? C[K]\n : C[K] extends object\n ? ValidateContracts<C[K]>\n : never\n}\n\nexport const defineConfig = <const T extends DefineConfigInput>(\n config: T & {\n contracts?: ValidateContracts<NonNullable<T['contracts']>>\n }\n): T => config\n\nconst CONFIG_BASENAME = 'zodiac.config'\n\n/**\n * Extensions probed (in priority order) when the user doesn't pass an\n * explicit `--config` path. Mirrors what vite / vitest / tailwind accept.\n */\nconst CONFIG_EXTENSIONS = [\n '.ts',\n '.mts',\n '.cts',\n '.js',\n '.mjs',\n '.cjs',\n] as const\n\nconst DEFAULT_CONFIG_PATH = `${CONFIG_BASENAME}.ts`\n\nconst CONFIG_STUB = `import { defineConfig } from \"@zodiaceco/sdk/cli/config\";\n\nexport default defineConfig({\n contracts: {\n // Add contracts the \\`allow\\` kit should know about, keyed by chain prefix.\n // Run \\`zodiac pull-contracts\\` after editing.\n //\n // eth: {\n // weth: \"0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2\",\n // },\n },\n});\n`\n\ntype LoadConfigOptions = {\n /**\n * Called when neither `config.apiKey` nor `process.env.ZODIAC_API_KEY` is\n * set. Receives the resolved project root and must return a freshly minted\n * API key. Typically wired to the interactive `init()` flow.\n *\n * If omitted, `loadConfig` throws when no key is found.\n */\n onMissingKey?: (rootDir: string) => Promise<string>\n /**\n * When the config file doesn't exist, write a minimal stub before\n * loading. Used by the CLI's `pull` commands so first-time setup\n * works without the user having to scaffold the file themselves.\n */\n createIfMissing?: boolean\n}\n\n/**\n * Write a starter `zodiac.config.ts` if no file exists at `absolutePath`.\n * Returns `true` if a file was written.\n */\nexport const ensureConfigStub = (absolutePath: string): boolean => {\n if (existsSync(absolutePath)) return false\n writeFileSync(absolutePath, CONFIG_STUB, 'utf8')\n return true\n}\n\n/**\n * Turn a (possibly-default) config path into an absolute path under\n * `projectRoot`. When the caller didn't pass an explicit path, probe\n * for `zodiac.config.{ts,mts,cts,js,mjs,cjs}` and use whichever exists;\n * fall back to the canonical `.ts` name so a not-found error is\n * still phrased in terms users recognise.\n */\nconst resolveConfigPath = (projectRoot: string, configPath: string): string => {\n const explicit = resolve(projectRoot, configPath)\n if (configPath !== DEFAULT_CONFIG_PATH) return explicit\n\n for (const ext of CONFIG_EXTENSIONS) {\n const candidate = resolve(projectRoot, `${CONFIG_BASENAME}${ext}`)\n if (existsSync(candidate)) return candidate\n }\n return explicit\n}\n\nexport async function loadConfig(\n configPath: string = DEFAULT_CONFIG_PATH,\n options: LoadConfigOptions = {}\n): Promise<ResolvedConfig> {\n // Resolve relative paths (including the default) against the nearest\n // package.json ancestor — that's where users expect the config to live,\n // regardless of which subdir they ran the CLI from.\n const projectRoot = findProjectRoot()\n const absolutePath = resolveConfigPath(projectRoot, configPath)\n\n if (options.createIfMissing && ensureConfigStub(absolutePath)) {\n console.log(`✅ Created ${absolutePath}`)\n }\n\n let mod: Record<string, unknown>\n try {\n mod = await import(pathToFileURL(absolutePath).href)\n } catch (error: any) {\n if (error?.code === 'ERR_MODULE_NOT_FOUND' || error?.code === 'ENOENT') {\n throw new Error(`Config file not found: ${absolutePath}`)\n }\n throw error\n }\n\n const config = (mod.default ?? mod.config) as ZodiacConfig | undefined\n if (!config) {\n throw new Error(\n `Config file must export a default value or a named \"config\" export: ${absolutePath}`\n )\n }\n\n const rootDir = dirname(absolutePath)\n\n // `init` writes the API key and URL to the project's `.env`, which a CLI run\n // from a subdirectory would not have loaded. Variables already set win.\n loadDotenv({ path: resolve(rootDir, '.env'), quiet: true })\n\n const apiKey = await resolveApiKey(config, rootDir, options)\n\n return { ...config, apiKey, rootDir }\n}\n\nconst resolveApiKey = async (\n config: ZodiacConfig,\n rootDir: string,\n { onMissingKey }: LoadConfigOptions\n): Promise<`zodiac_${string}`> => {\n if (config.apiKey != null) {\n if (!isApiKey(config.apiKey)) {\n throw new Error(\n '`apiKey` in zodiac.config.ts is malformed: a valid Zodiac API key starts with \"zodiac_\". Either remove the field to use ZODIAC_API_KEY, or run `zodiac init` to mint a fresh key.'\n )\n }\n return config.apiKey\n }\n\n const fromEnv = process.env.ZODIAC_API_KEY\n if (fromEnv != null && fromEnv !== '') {\n if (!isApiKey(fromEnv)) {\n throw new Error(\n 'ZODIAC_API_KEY is set but malformed: a valid Zodiac API key starts with \"zodiac_\". Run `zodiac init` to mint a fresh key.'\n )\n }\n return fromEnv\n }\n\n if (onMissingKey == null) {\n throw new Error(\n 'No Zodiac API key found. Set ZODIAC_API_KEY in your environment, or run `zodiac init` to generate one.'\n )\n }\n\n const minted = await onMissingKey(rootDir)\n if (!isApiKey(minted)) {\n throw new Error(\n `onMissingKey returned an invalid Zodiac API key (expected a value starting with \"zodiac_\").`\n )\n }\n return minted\n}\n\nconst isApiKey = (value: string | undefined): value is `zodiac_${string}` =>\n value != null && value.startsWith('zodiac_')\n\nexport const DEFAULT_ABIS_DIR = 'abis'\n\n/**\n * Resolves the absolute path to the ABIs directory. Only `rootDir` (and\n * optionally `abisDir`) are read, so the parameter is intentionally narrower\n * than `ResolvedConfig`: userland code can pass `{ ...config, rootDir }`\n * without first satisfying `apiKey` (normally supplied via the\n * `ZODIAC_API_KEY` env var at runtime).\n */\nexport function resolveAbisDir(config: {\n rootDir: string\n abisDir?: string\n}): string {\n return resolve(config.rootDir, config.abisDir ?? DEFAULT_ABIS_DIR)\n}\n"],"mappings":";;;;;;;;;;;AAQA,MAAa,mBAAmB,WAAmB,QAAQ,KAAK,KAAa;CAC3E,MAAM,gBAAgB,QAAQ,SAAS;CACvC,IAAI,MAAM;AAEV,QAAO,MAAM;AACX,MAAI,WAAW,QAAQ,KAAK,eAAe,CAAC,CAC1C,QAAO;EAET,MAAM,SAAS,QAAQ,IAAI;AAC3B,MAAI,WAAW,IACb,QAAO;AAET,QAAM;;;;;AC4CV,MAAa,gBACX,WAGM;AAER,MAAM,kBAAkB;;;;;AAMxB,MAAM,oBAAoB;CACxB;CACA;CACA;CACA;CACA;CACA;CACD;AAED,MAAM,sBAAsB,GAAG,gBAAgB;AAE/C,MAAM,cAAc;;;;;;;;;;;;;;;;;AAmCpB,MAAa,oBAAoB,iBAAkC;AACjE,KAAI,WAAW,aAAa,CAAE,QAAO;AACrC,eAAc,cAAc,aAAa,OAAO;AAChD,QAAO;;;;;;;;;AAUT,MAAM,qBAAqB,aAAqB,eAA+B;CAC7E,MAAM,WAAWA,UAAQ,aAAa,WAAW;AACjD,KAAI,eAAe,oBAAqB,QAAO;AAE/C,MAAK,MAAM,OAAO,mBAAmB;EACnC,MAAM,YAAYA,UAAQ,aAAa,GAAG,kBAAkB,MAAM;AAClE,MAAI,WAAW,UAAU,CAAE,QAAO;;AAEpC,QAAO;;AAGT,eAAsB,WACpB,aAAqB,qBACrB,UAA6B,EAAE,EACN;CAKzB,MAAM,eAAe,kBADD,iBAAiB,EACe,WAAW;AAE/D,KAAI,QAAQ,mBAAmB,iBAAiB,aAAa,CAC3D,SAAQ,IAAI,aAAa,eAAe;CAG1C,IAAI;AACJ,KAAI;AACF,QAAM,MAAM,OAAO,cAAc,aAAa,CAAC;UACxC,OAAY;AACnB,MAAI,OAAO,SAAS,0BAA0B,OAAO,SAAS,SAC5D,OAAM,IAAI,MAAM,0BAA0B,eAAe;AAE3D,QAAM;;CAGR,MAAMC,WAAU,IAAI,WAAW,IAAI;AACnC,KAAI,CAACA,SACH,OAAM,IAAI,MACR,uEAAuE,eACxE;CAGH,MAAM,UAAUC,UAAQ,aAAa;AAIrC,QAAW;EAAE,MAAMF,UAAQ,SAAS,OAAO;EAAE,OAAO;EAAM,CAAC;CAE3D,MAAM,SAAS,MAAM,cAAcC,UAAQ,SAAS,QAAQ;AAE5D,QAAO;EAAE,GAAGA;EAAQ;EAAQ;EAAS;;AAGvC,MAAM,gBAAgB,OACpB,QACA,SACA,EAAE,mBAC8B;AAChC,KAAI,OAAO,UAAU,MAAM;AACzB,MAAI,CAAC,SAAS,OAAO,OAAO,CAC1B,OAAM,IAAI,MACR,sLACD;AAEH,SAAO,OAAO;;CAGhB,MAAM,UAAU,QAAQ,IAAI;AAC5B,KAAI,WAAW,QAAQ,YAAY,IAAI;AACrC,MAAI,CAAC,SAAS,QAAQ,CACpB,OAAM,IAAI,MACR,8HACD;AAEH,SAAO;;AAGT,KAAI,gBAAgB,KAClB,OAAM,IAAI,MACR,yGACD;CAGH,MAAM,SAAS,MAAM,aAAa,QAAQ;AAC1C,KAAI,CAAC,SAAS,OAAO,CACnB,OAAM,IAAI,MACR,8FACD;AAEH,QAAO;;AAGT,MAAM,YAAY,UAChB,SAAS,QAAQ,MAAM,WAAW,UAAU;AAE9C,MAAa,mBAAmB;;;;;;;;AAShC,SAAgB,eAAe,QAGpB;AACT,QAAOD,UAAQ,OAAO,SAAS,OAAO,WAAA,OAA4B"}
@@ -60,4 +60,4 @@ type ChainPrefix = keyof typeof CHAIN_IDS;
60
60
  declare const chainIdFor: (prefix: string) => number;
61
61
  //#endregion
62
62
  export { EVERYTHING as a, Scoping as c, ConditionFunction as i, TargetPermission as l, ChainPrefix as n, FunctionPermission as o, chainIdFor as r, Options as s, CHAIN_IDS as t, buildAllowKit as u };
63
- //# sourceMappingURL=index-CnH26Z_A.d.mts.map
63
+ //# sourceMappingURL=index-D49x1ozw.d.mts.map
package/dist/index.d.mts CHANGED
@@ -1,8 +1,8 @@
1
1
  /// <reference path="./zodiac-os-codegen.d.ts" />
2
- import { a as PermissionEntry, c as ConstellationNode, l as constellation, s as Permissions } from "./actions-tA_Oe_kC.mjs";
3
- import { a as EVERYTHING, c as Scoping, i as ConditionFunction, l as TargetPermission, n as ChainPrefix, o as FunctionPermission, s as Options, u as buildAllowKit } from "./index-CnH26Z_A.mjs";
2
+ import { a as PermissionEntry, c as ConstellationNode, l as constellation, s as Permissions } from "./actions-Cri7InV-.mjs";
3
+ import { a as EVERYTHING, c as Scoping, i as ConditionFunction, l as TargetPermission, n as ChainPrefix, o as FunctionPermission, s as Options, u as buildAllowKit } from "./index-D49x1ozw.mjs";
4
4
  import { Annotation, Permission, PermissionSet, c, encodeKey, forAll } from "zodiac-roles-sdk";
5
- import { ApplyConstellationPayload, ApplyConstellationResult, ListAccountsResult, ListUsersResult, ResolveConstellationPayload, ResolveConstellationResult } from "@zodiaceco/api-types";
5
+ import { ApiErrorIssue, ApplyConstellationPayload, ApplyConstellationResult, ListAccountsResult, ListUsersResult, ResolveConstellationPayload, ResolveConstellationResult } from "@zodiaceco/api-types";
6
6
  import { UUID } from "crypto";
7
7
 
8
8
  //#region src/api.d.ts
@@ -51,10 +51,28 @@ declare class ApiClient {
51
51
  */
52
52
  resolveConstellation(workspaceId: UUID, payload: ResolveConstellationPayload): Promise<ResolveConstellationResult>;
53
53
  }
54
+ declare class ApiRequestError extends Error {
55
+ readonly status: number;
56
+ readonly statusText: string;
57
+ readonly details?: unknown;
58
+ /** What the API refused, when it could name each problem. */
59
+ readonly issues: ApiErrorIssue[];
60
+ /** The API's own summary of the problem. */
61
+ readonly reason: string;
62
+ constructor(message: string, opts: {
63
+ status: number;
64
+ statusText: string;
65
+ details?: unknown;
66
+ cause?: unknown;
67
+ });
68
+ private static composeMessage;
69
+ toString(): string;
70
+ }
54
71
  //#endregion
55
72
  //#region src/push.d.ts
56
73
  type PushOpts = {
57
- /** API client instance. Defaults to a client configured from environment variables. */api?: ApiClient;
74
+ /** API client instance. Defaults to a client configured from environment variables. */api?: ApiClient; /** API key for the default client, instead of `ZODIAC_API_KEY`. */
75
+ apiKey?: string;
58
76
  };
59
77
  /**
60
78
  * Applies the given nodes as the next revision of the constellations they
@@ -81,6 +99,17 @@ type PushOpts = {
81
99
  declare function push(nodes: ConstellationNode[] | {
82
100
  [key: string]: ConstellationNode;
83
101
  }, opts?: PushOpts): Promise<ApplyConstellationResult[]>;
102
+ /**
103
+ * Zodiac refused a constellation as it was pushed. Each issue names the node
104
+ * it concerns by the name it was pushed under, followed by the path within it.
105
+ */
106
+ declare class ConstellationRejectedError extends Error {
107
+ readonly issues: {
108
+ path: string;
109
+ message: string;
110
+ }[];
111
+ constructor(label: string, error: ApiRequestError, nameNode: (index: number) => string);
112
+ }
84
113
  //#endregion
85
- export { type Annotation, type ChainPrefix, type ConditionFunction, EVERYTHING, type FunctionPermission, type Options, type Permission, type PermissionEntry, type PermissionSet, type Permissions, type Scoping, type TargetPermission, buildAllowKit, c, constellation, encodeKey, forAll, push };
114
+ export { type Annotation, type ChainPrefix, type ConditionFunction, ConstellationRejectedError, EVERYTHING, type FunctionPermission, type Options, type Permission, type PermissionEntry, type PermissionSet, type Permissions, type Scoping, type TargetPermission, buildAllowKit, c, constellation, encodeKey, forAll, push };
86
115
  //# sourceMappingURL=index.d.mts.map
package/dist/index.mjs CHANGED
@@ -1,8 +1,7 @@
1
- import { n as resolveZodiacDir, t as ApiClient } from "./api-kKgzHQlu.mjs";
2
- import { i as toAnnotation, n as isDeFiKitEntry, t as assertRecipientChains } from "./permissionEntries-D3fSVhwO.mjs";
3
- import { n as EVERYTHING, t as buildAllowKit } from "./allow-CrDzDtxx.mjs";
1
+ import { t as resolveZodiacDir } from "./paths-LsD6ULk0.mjs";
2
+ import { n as push, t as ConstellationRejectedError } from "./push-DdnHsLpO.mjs";
3
+ import { n as EVERYTHING, t as buildAllowKit } from "./allow-DX3iyizB.mjs";
4
4
  import { createRequire } from "module";
5
- import { invariant } from "@epic-web/invariant";
6
5
  import { c, encodeKey, forAll } from "zodiac-roles-sdk";
7
6
  //#region src/constellation.ts
8
7
  function loadCodegen() {
@@ -22,6 +21,7 @@ function loadCodegen() {
22
21
  * const dao = eth.safe['GG DAO'] // the safe of that name on chain 1
23
22
  * const roles = eth.roles['GG DAO'] // the roles mod of that name
24
23
  * const newSafe = eth.safe['New Safe']({ nonce: 0n, threshold: 2, owners: [...], modules: [...] })
24
+ * const timelock = eth.delay['Timelock']({ nonce: 0n, target: dao, cooldown: 86400n, expiration: 0n })
25
25
  * ```
26
26
  *
27
27
  * Names come from the last `pull-org`, so an account deployed since then is
@@ -32,10 +32,12 @@ function constellation(opts, internal) {
32
32
  const ws = codegen.accounts[opts.workspace];
33
33
  const safesByLabel = {};
34
34
  const rolesByLabel = {};
35
+ const delaysByLabel = {};
35
36
  if (ws) {
36
37
  const onThisChain = (byChain) => byChain[opts.chain] ?? {};
37
38
  for (const [label, account] of Object.entries(onThisChain(ws.safes))) safesByLabel[label] = account;
38
39
  for (const [label, account] of Object.entries(onThisChain(ws.rolesMods))) rolesByLabel[label] = account;
40
+ for (const [label, account] of Object.entries(onThisChain(ws.delays))) delaysByLabel[label] = account;
39
41
  }
40
42
  const meta = {
41
43
  label: opts.label,
@@ -88,185 +90,11 @@ function constellation(opts, internal) {
88
90
  return {
89
91
  safe: entityAccessor(safesByLabel, "SAFE"),
90
92
  roles: entityAccessor(rolesByLabel, "ROLES"),
93
+ delay: entityAccessor(delaysByLabel, "DELAY"),
91
94
  user: userAccessor()
92
95
  };
93
96
  }
94
97
  //#endregion
95
- //#region src/push.ts
96
- /**
97
- * Applies the given nodes as the next revision of the constellations they
98
- * belong to, and answers with a url per constellation to review and deploy.
99
- *
100
- * Each node travels with what it was actually given, never with the onchain
101
- * state a `pull` filled it with for reading — so a node passed as a bare
102
- * reference declares nothing and leaves the account it names alone. Nodes
103
- * naming each other travel as references and are substituted for addresses
104
- * once those are derived at deploy.
105
- *
106
- * Passing an object names each node by its key; passing an array names them by
107
- * position. Either way the name is the node's `ref`, which is how the app
108
- * follows a node from one revision to the next.
109
- *
110
- * ```ts
111
- * const eth = constellation({ workspace: 'GG', label: 'my constellation', chain: 1 })
112
- * const dao = eth.safe['GG DAO']
113
- * const roles = eth.roles['New Roles']({ nonce: 0n, target: dao, owner: dao, avatar: dao })
114
- *
115
- * await push({ dao, roles })
116
- * ```
117
- */
118
- async function push(nodes, opts) {
119
- const api = opts?.api ?? new ApiClient();
120
- const refs = deriveRefs(nodes);
121
- const groups = /* @__PURE__ */ new Map();
122
- for (const node of refs.byIdentity.keys()) {
123
- const meta = node._constellation;
124
- invariant(meta, `Node "${node.label}" is not associated with a constellation`);
125
- const key = `${meta.workspaceId}:${meta.chain}:${meta.label}`;
126
- let group = groups.get(key);
127
- if (!group) {
128
- group = {
129
- meta,
130
- nodes: []
131
- };
132
- groups.set(key, group);
133
- }
134
- group.nodes.push(node);
135
- }
136
- const results = [];
137
- for (const { meta, nodes: groupNodes } of groups.values()) {
138
- const specification = groupNodes.map((n) => nodeToSpec(n, refs));
139
- const result = await api.applyConstellation(meta.workspaceId, {
140
- label: meta.label,
141
- chain: meta.chain,
142
- specification
143
- });
144
- results.push(result);
145
- }
146
- return results;
147
- }
148
- /**
149
- * What a ref may be spelled like. Refs come from export names and object keys,
150
- * so any identifier a node can be declared under has to survive the push —
151
- * except one opening with `$`, which is how a node references another. A list
152
- * of nodes refs them by position, so a plain index counts too.
153
- */
154
- const IDENTIFIER = /^(?:[a-zA-Z_][a-zA-Z0-9_$]*|\d+)$/;
155
- function labelKey(node) {
156
- return `${node._constellation.workspaceId}:${node.type}:${node.label}`;
157
- }
158
- function deriveRefs(nodes) {
159
- const byIdentity = /* @__PURE__ */ new Map();
160
- const byLabel = /* @__PURE__ */ new Map();
161
- const register = (node, ref) => {
162
- invariant(IDENTIFIER.test(ref), `Invalid ref "${ref}": a ref is a JavaScript identifier, and cannot start with "$"`);
163
- byIdentity.set(node, ref);
164
- byLabel.set(labelKey(node), ref);
165
- };
166
- if (Array.isArray(nodes)) for (let i = 0; i < nodes.length; i++) {
167
- const node = nodes[i];
168
- invariant(isConstellationNode(node), `unexpected node input at index: ${i}`);
169
- register(node, `${i}`);
170
- }
171
- else for (const [key, node] of Object.entries(nodes)) {
172
- invariant(isConstellationNode(node), `unexpected node input under key: ${key}`);
173
- register(node, key);
174
- }
175
- return {
176
- byIdentity,
177
- byLabel
178
- };
179
- }
180
- /**
181
- * What situates a node rather than configures it: which account it is, and how
182
- * the workspace holds it. Always sent, even for a reference that declares
183
- * nothing — none of it reaches the onchain diff, and leaving it out would lose
184
- * the label and the vault the node belongs to.
185
- *
186
- * `address` above all. It is what the pull resolved the label to, and the only
187
- * thing that says which account this is: a node without one is a creation, and
188
- * its address is derived from whoever deploys. Drop it and a second deployer
189
- * provisions a duplicate under the same label rather than reconfiguring the
190
- * account that is already there.
191
- */
192
- const IDENTITY_FIELDS = new Set([
193
- "type",
194
- "chain",
195
- "address",
196
- "label",
197
- "vault"
198
- ]);
199
- function nodeToSpec(node, refs) {
200
- const { id, _constellation, _declared, ...rest } = node;
201
- const declared = Array.isArray(_declared) ? new Set(_declared) : null;
202
- const spec = {};
203
- for (const [key, value] of Object.entries(rest)) {
204
- if (declared != null && !IDENTITY_FIELDS.has(key) && !declared.has(key)) continue;
205
- if (node.type === "ROLES" && key === "roles" && value != null) {
206
- spec.roles = describeRoles(value, node.chain, refs);
207
- continue;
208
- }
209
- spec[key] = resolveRefs(value, refs);
210
- }
211
- spec.ref = refs.byIdentity.get(node);
212
- invariant(spec.ref != null, "ref not found");
213
- return stringifyBigints(spec);
214
- }
215
- /**
216
- * Entries travel to the API as they were written — parameters and labels — and
217
- * are compiled when the constellation is deployed. Only a DeFi Kit entry is
218
- * rendered here, into the annotation uri that names the chain it was written
219
- * for; the permissions behind it are fetched from that uri at deploy.
220
- */
221
- function describeRoles(roles, chain, refs) {
222
- return Object.fromEntries(Object.entries(roles).map(([key, def]) => {
223
- if (def == null) return [key, null];
224
- return [key, {
225
- key,
226
- members: resolveRefs(def.members, refs),
227
- permissions: def.permissions.map((entry) => {
228
- assertRecipientChains(entry, chain);
229
- return resolveRefs(isDeFiKitEntry(entry) ? {
230
- label: entry.label,
231
- annotation: toAnnotation(entry, chain)
232
- } : entry, refs);
233
- })
234
- }];
235
- }));
236
- }
237
- function resolveRefs(value, refs) {
238
- if (isConstellationNode(value)) {
239
- const ref = refs.byIdentity.get(value) ?? refs.byLabel.get(labelKey(value));
240
- invariant(ref != null, `Node "${value.label}" is referenced but not included in the push() call`);
241
- return `$${ref}`;
242
- }
243
- if (Array.isArray(value)) return value.map((v) => resolveRefs(v, refs));
244
- if (typeof value === "object" && value !== null) {
245
- const resolved = {};
246
- for (const [k, v] of Object.entries(value)) resolved[k] = resolveRefs(v, refs);
247
- return resolved;
248
- }
249
- if (typeof value === "string" && /^0x[0-9a-fA-F]{40}$/.test(value)) return value.toLowerCase();
250
- return value;
251
- }
252
- function stringifyBigints(value) {
253
- if (typeof value === "bigint") return value.toString();
254
- if (Array.isArray(value)) return value.map(stringifyBigints);
255
- if (typeof value === "object" && value !== null) {
256
- const result = {};
257
- for (const [k, v] of Object.entries(value)) result[k] = stringifyBigints(v);
258
- return result;
259
- }
260
- return value;
261
- }
262
- function isConstellationNode(value) {
263
- if (typeof value === "function" || typeof value === "object") {
264
- const obj = value;
265
- return obj != null && typeof obj.type === "string" && typeof obj.chain === "number" && typeof obj._constellation === "object";
266
- }
267
- return false;
268
- }
269
- //#endregion
270
- export { EVERYTHING, buildAllowKit, c, constellation, encodeKey, forAll, push };
98
+ export { ConstellationRejectedError, EVERYTHING, buildAllowKit, c, constellation, encodeKey, forAll, push };
271
99
 
272
100
  //# sourceMappingURL=index.mjs.map