@01.software/init
Advanced tools
| #!/usr/bin/env node | ||
| // src/file-ops.ts | ||
| var envLineRegexCache = /* @__PURE__ */ new Map(); | ||
| function envLineRegex(name) { | ||
| let re = envLineRegexCache.get(name); | ||
| if (!re) { | ||
| const escaped = name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); | ||
| re = new RegExp(`^${escaped}=(.*)$`, "m"); | ||
| envLineRegexCache.set(name, re); | ||
| } | ||
| return re; | ||
| } | ||
| function readEnvValue(content, name) { | ||
| const m = content.match(envLineRegex(name)); | ||
| return m ? m[1] : null; | ||
| } | ||
| function setEnvValue(content, name, value) { | ||
| const re = envLineRegex(name); | ||
| if (re.test(content)) return content.replace(re, `${name}=${value}`); | ||
| const sep = content.length === 0 || content.endsWith("\n") ? "" : "\n"; | ||
| return content + sep + `${name}=${value} | ||
| `; | ||
| } | ||
| var OWNED_MCP_TOML_SECTION = "mcp_servers.01software"; | ||
| function tomlSectionName(line) { | ||
| const match = line.trim().match(/^\[([^\]]+)\]$/); | ||
| return match ? match[1] : null; | ||
| } | ||
| function isOwnedMcpTomlSection(sectionName) { | ||
| return sectionName === OWNED_MCP_TOML_SECTION || sectionName.startsWith(`${OWNED_MCP_TOML_SECTION}.`); | ||
| } | ||
| function replaceTomlMcpSection(content, newSection) { | ||
| const lines = content.split("\n"); | ||
| const kept = []; | ||
| let inOurSection = false; | ||
| for (const line of lines) { | ||
| const sectionName = tomlSectionName(line); | ||
| if (sectionName) { | ||
| inOurSection = isOwnedMcpTomlSection(sectionName); | ||
| if (inOurSection) continue; | ||
| } | ||
| if (!inOurSection) kept.push(line); | ||
| } | ||
| let result = kept.join("\n").replace(/\n+$/, ""); | ||
| if (result.length > 0) result += "\n"; | ||
| result += newSection.startsWith("\n") ? newSection : "\n" + newSection; | ||
| return result; | ||
| } | ||
| export { | ||
| readEnvValue, | ||
| setEnvValue, | ||
| replaceTomlMcpSection | ||
| }; | ||
| //# sourceMappingURL=chunk-JT3G6B66.js.map |
| {"version":3,"sources":["../src/file-ops.ts"],"sourcesContent":["// Pure helpers for in-place file content manipulation. Kept side-effect-free\n// so they can be unit-tested without touching the filesystem or prompts.\n\n// ── .env merge ───────────────────────────────────────────────────────\n\nconst envLineRegexCache = new Map<string, RegExp>()\n\nfunction envLineRegex(name: string): RegExp {\n let re = envLineRegexCache.get(name)\n if (!re) {\n const escaped = name.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\n re = new RegExp(`^${escaped}=(.*)$`, 'm')\n envLineRegexCache.set(name, re)\n }\n return re\n}\n\nexport function readEnvValue(content: string, name: string): string | null {\n const m = content.match(envLineRegex(name))\n return m ? m[1] : null\n}\n\nexport function setEnvValue(content: string, name: string, value: string): string {\n const re = envLineRegex(name)\n if (re.test(content)) return content.replace(re, `${name}=${value}`)\n const sep = content.length === 0 || content.endsWith('\\n') ? '' : '\\n'\n return content + sep + `${name}=${value}\\n`\n}\n\n// ── Codex TOML manipulation ──────────────────────────────────────────\n\nconst OWNED_MCP_TOML_SECTION = 'mcp_servers.01software'\n\nfunction tomlSectionName(line: string): string | null {\n const match = line.trim().match(/^\\[([^\\]]+)\\]$/)\n return match ? match[1] : null\n}\n\nfunction isOwnedMcpTomlSection(sectionName: string): boolean {\n return (\n sectionName === OWNED_MCP_TOML_SECTION ||\n sectionName.startsWith(`${OWNED_MCP_TOML_SECTION}.`)\n )\n}\n\n/** Removes the existing `[mcp_servers.01software]` block and sub-blocks from a\n * TOML document, then appends the provided section. */\nexport function replaceTomlMcpSection(content: string, newSection: string): string {\n const lines = content.split('\\n')\n const kept: string[] = []\n let inOurSection = false\n for (const line of lines) {\n const sectionName = tomlSectionName(line)\n if (sectionName) {\n inOurSection = isOwnedMcpTomlSection(sectionName)\n if (inOurSection) continue\n }\n if (!inOurSection) kept.push(line)\n }\n let result = kept.join('\\n').replace(/\\n+$/, '')\n if (result.length > 0) result += '\\n'\n // newSection already starts with a blank line; keep it that way\n result += newSection.startsWith('\\n') ? newSection : '\\n' + newSection\n return result\n}\n"],"mappings":";;;AAKA,IAAM,oBAAoB,oBAAI,IAAoB;AAElD,SAAS,aAAa,MAAsB;AAC1C,MAAI,KAAK,kBAAkB,IAAI,IAAI;AACnC,MAAI,CAAC,IAAI;AACP,UAAM,UAAU,KAAK,QAAQ,uBAAuB,MAAM;AAC1D,SAAK,IAAI,OAAO,IAAI,OAAO,UAAU,GAAG;AACxC,sBAAkB,IAAI,MAAM,EAAE;AAAA,EAChC;AACA,SAAO;AACT;AAEO,SAAS,aAAa,SAAiB,MAA6B;AACzE,QAAM,IAAI,QAAQ,MAAM,aAAa,IAAI,CAAC;AAC1C,SAAO,IAAI,EAAE,CAAC,IAAI;AACpB;AAEO,SAAS,YAAY,SAAiB,MAAc,OAAuB;AAChF,QAAM,KAAK,aAAa,IAAI;AAC5B,MAAI,GAAG,KAAK,OAAO,EAAG,QAAO,QAAQ,QAAQ,IAAI,GAAG,IAAI,IAAI,KAAK,EAAE;AACnE,QAAM,MAAM,QAAQ,WAAW,KAAK,QAAQ,SAAS,IAAI,IAAI,KAAK;AAClE,SAAO,UAAU,MAAM,GAAG,IAAI,IAAI,KAAK;AAAA;AACzC;AAIA,IAAM,yBAAyB;AAE/B,SAAS,gBAAgB,MAA6B;AACpD,QAAM,QAAQ,KAAK,KAAK,EAAE,MAAM,gBAAgB;AAChD,SAAO,QAAQ,MAAM,CAAC,IAAI;AAC5B;AAEA,SAAS,sBAAsB,aAA8B;AAC3D,SACE,gBAAgB,0BAChB,YAAY,WAAW,GAAG,sBAAsB,GAAG;AAEvD;AAIO,SAAS,sBAAsB,SAAiB,YAA4B;AACjF,QAAM,QAAQ,QAAQ,MAAM,IAAI;AAChC,QAAM,OAAiB,CAAC;AACxB,MAAI,eAAe;AACnB,aAAW,QAAQ,OAAO;AACxB,UAAM,cAAc,gBAAgB,IAAI;AACxC,QAAI,aAAa;AACf,qBAAe,sBAAsB,WAAW;AAChD,UAAI,aAAc;AAAA,IACpB;AACA,QAAI,CAAC,aAAc,MAAK,KAAK,IAAI;AAAA,EACnC;AACA,MAAI,SAAS,KAAK,KAAK,IAAI,EAAE,QAAQ,QAAQ,EAAE;AAC/C,MAAI,OAAO,SAAS,EAAG,WAAU;AAEjC,YAAU,WAAW,WAAW,IAAI,IAAI,aAAa,OAAO;AAC5D,SAAO;AACT;","names":[]} |
| #!/usr/bin/env node | ||
| // ../auth-contracts/dist/index.js | ||
| var MCP_RESOURCE_AUDIENCE = "https://mcp.01.software/mcp"; | ||
| // src/templates.ts | ||
| function getClientTemplate(env, publishableKeyEnvVar) { | ||
| if (env === "nextjs") { | ||
| return `import { createClient } from '@01.software/sdk' | ||
| export const client = createClient({ | ||
| publishableKey: process.env.${publishableKeyEnvVar}!, | ||
| }) | ||
| `; | ||
| } | ||
| if (env === "react-cra") { | ||
| return `import { createClient } from '@01.software/sdk' | ||
| export const client = createClient({ | ||
| publishableKey: process.env.${publishableKeyEnvVar}!, | ||
| }) | ||
| `; | ||
| } | ||
| if (env === "vanilla") { | ||
| return `import { createClient } from '@01.software/sdk' | ||
| // Replace 'YOUR_PUBLISHABLE_KEY' with your actual publishable key from the 01.software console | ||
| export const client = createClient({ | ||
| publishableKey: 'YOUR_PUBLISHABLE_KEY', | ||
| }) | ||
| `; | ||
| } | ||
| return `import { createClient } from '@01.software/sdk' | ||
| export const client = createClient({ | ||
| publishableKey: import.meta.env.${publishableKeyEnvVar}, | ||
| }) | ||
| `; | ||
| } | ||
| function getAnalyticsTemplate(env, publishableKeyEnvVar) { | ||
| if (env === "vanilla") { | ||
| return `import { createAnalytics } from '@01.software/sdk/analytics' | ||
| // Replace 'YOUR_PUBLISHABLE_KEY' with your actual publishable key from the 01.software console | ||
| export const analytics = createAnalytics({ | ||
| publishableKey: 'YOUR_PUBLISHABLE_KEY', | ||
| }) | ||
| `; | ||
| } | ||
| const publishableKeyExpression = env === "react-vite" ? `import.meta.env.${publishableKeyEnvVar}` : `process.env.${publishableKeyEnvVar}!`; | ||
| return `import { createAnalytics } from '@01.software/sdk/analytics' | ||
| export const analytics = createAnalytics({ | ||
| publishableKey: ${publishableKeyExpression}, | ||
| }) | ||
| `; | ||
| } | ||
| function getQueryProviderTemplate(env) { | ||
| const useClientDirective = env === "nextjs" ? "'use client'\n\n" : ""; | ||
| return `${useClientDirective}import { QueryClientProvider } from '@tanstack/react-query' | ||
| import { client } from './client' | ||
| export function QueryProvider({ children }: { children: React.ReactNode }) { | ||
| return ( | ||
| <QueryClientProvider client={client.queryClient}> | ||
| {children} | ||
| </QueryClientProvider> | ||
| ) | ||
| } | ||
| `; | ||
| } | ||
| function getServerTemplate(env, publishableKeyEnvVar, secretKeyEnvVar) { | ||
| if (env === "edge") { | ||
| return `import { createServerClient } from '@01.software/sdk' | ||
| // Edge runtime: pass your env bindings here | ||
| // e.g. Cloudflare Workers: use env.SOFTWARE_PUBLISHABLE_KEY from the handler context | ||
| // e.g. Vercel Edge: use process.env.${publishableKeyEnvVar} | ||
| export function createEdgeClient(publishableKey: string, secretKey: string) { | ||
| return createServerClient({ publishableKey, secretKey }) | ||
| } | ||
| `; | ||
| } | ||
| return `import { createServerClient } from '@01.software/sdk' | ||
| /** | ||
| * Runtime guard: this module contains secret keys and must never be | ||
| * imported from client-side (browser) code. If the bundler accidentally | ||
| * includes it in a client bundle, this throws at module-evaluation time. | ||
| */ | ||
| if (typeof window !== 'undefined') { | ||
| throw new Error( | ||
| 'lib/software/server.ts must not be imported in client-side code \u2014 it contains secret keys.', | ||
| ) | ||
| } | ||
| type ServerClient = ReturnType<typeof createServerClient> | ||
| let cachedClient: ServerClient | null = null | ||
| function createConfiguredClient(): ServerClient { | ||
| const publishableKey = process.env.${publishableKeyEnvVar} | ||
| const secretKey = process.env.${secretKeyEnvVar} | ||
| if (!publishableKey || !secretKey) { | ||
| throw new Error( | ||
| 'Server client requires ${publishableKeyEnvVar} and ${secretKeyEnvVar}.', | ||
| ) | ||
| } | ||
| return createServerClient({ publishableKey, secretKey }) | ||
| } | ||
| export function getServerClient(): ServerClient { | ||
| cachedClient ??= createConfiguredClient() | ||
| return cachedClient | ||
| } | ||
| /** | ||
| * Lazy Proxy: the underlying client is created only on first property access, | ||
| * so importing this file has no side effects until you actually call it. | ||
| */ | ||
| export const serverClient = new Proxy({} as ServerClient, { | ||
| get(_target, prop, receiver) { | ||
| const target = getServerClient() | ||
| const value = Reflect.get(target as object, prop, receiver) | ||
| return typeof value === 'function' ? value.bind(target) : value | ||
| }, | ||
| }) | ||
| `; | ||
| } | ||
| function getEnvContent(publishableKey, secretKey, publishableKeyEnvVar, secretKeyEnvVar) { | ||
| let content = ` | ||
| # 01.software | ||
| ${publishableKeyEnvVar}=${publishableKey} | ||
| `; | ||
| if (secretKeyEnvVar) { | ||
| content += `${secretKeyEnvVar}=${secretKey} | ||
| `; | ||
| } | ||
| return content; | ||
| } | ||
| function getMcpServerEntry(client = "generic") { | ||
| if (client === "windsurf") { | ||
| return { | ||
| serverUrl: MCP_RESOURCE_AUDIENCE | ||
| }; | ||
| } | ||
| return { | ||
| type: "http", | ||
| url: MCP_RESOURCE_AUDIENCE | ||
| }; | ||
| } | ||
| function getMcpConfigTemplate(client = "generic") { | ||
| return JSON.stringify( | ||
| { mcpServers: { "01software": getMcpServerEntry(client) } }, | ||
| null, | ||
| 2 | ||
| ) + "\n"; | ||
| } | ||
| function getCodexMcpTomlSection() { | ||
| return ` | ||
| [mcp_servers.01software] | ||
| url = "${MCP_RESOURCE_AUDIENCE}" | ||
| `; | ||
| } | ||
| var CODEX_MCP_SECTION_MARKER = "[mcp_servers.01software]"; | ||
| export { | ||
| getClientTemplate, | ||
| getAnalyticsTemplate, | ||
| getQueryProviderTemplate, | ||
| getServerTemplate, | ||
| getEnvContent, | ||
| getMcpServerEntry, | ||
| getMcpConfigTemplate, | ||
| getCodexMcpTomlSection, | ||
| CODEX_MCP_SECTION_MARKER | ||
| }; | ||
| //# sourceMappingURL=chunk-S3KHPWCE.js.map |
| {"version":3,"sources":["../../auth-contracts/src/index.ts","../src/templates.ts"],"sourcesContent":["export const MCP_RESOURCE_AUDIENCE = 'https://mcp.01.software/mcp'\n\nexport const MCP_OAUTH_ISSUER = 'https://01.software'\n\nexport const MCP_PROTECTED_RESOURCE_METADATA_PATH =\n '/.well-known/oauth-protected-resource/mcp'\n\nexport const MCP_AUTHORIZATION_SERVER_METADATA_PATH =\n '/.well-known/oauth-authorization-server'\n\nexport const MCP_TENANT_CLAIM = 'tenant_id'\n\nexport const MCP_TENANT_ROLE_CLAIM = 'tenant_role'\n\nexport const MCP_SCOPES = {\n read: 'mcp:read',\n write: 'mcp:write',\n} as const\n\nexport const MCP_CONSOLE_SERVICE_AUDIENCE =\n 'https://api.01.software/internal/mcp'\n\nexport const MCP_CONSOLE_SERVICE_SCOPE = 'console:mcp_proxy'\n\nexport const MCP_SERVICE_TOKEN_LIFETIME_SECONDS = 60\n","import type { ProjectEnv } from './detect'\nimport { MCP_RESOURCE_AUDIENCE } from '@01.software/auth-contracts'\n\n// ── Client template (browser) ────────────────────────────────────────\n\nexport function getClientTemplate(env: ProjectEnv, publishableKeyEnvVar: string): string {\n if (env === 'nextjs') {\n return `import { createClient } from '@01.software/sdk'\n\nexport const client = createClient({\n publishableKey: process.env.${publishableKeyEnvVar}!,\n})\n`\n }\n\n if (env === 'react-cra') {\n return `import { createClient } from '@01.software/sdk'\n\nexport const client = createClient({\n publishableKey: process.env.${publishableKeyEnvVar}!,\n})\n`\n }\n\n if (env === 'vanilla') {\n return `import { createClient } from '@01.software/sdk'\n\n// Replace 'YOUR_PUBLISHABLE_KEY' with your actual publishable key from the 01.software console\nexport const client = createClient({\n publishableKey: 'YOUR_PUBLISHABLE_KEY',\n})\n`\n }\n\n // react-vite (import.meta.env)\n return `import { createClient } from '@01.software/sdk'\n\nexport const client = createClient({\n publishableKey: import.meta.env.${publishableKeyEnvVar},\n})\n`\n}\n\n// ── Analytics template (browser) ─────────────────────────────────────\n\nexport function getAnalyticsTemplate(\n env: ProjectEnv,\n publishableKeyEnvVar: string,\n): string {\n if (env === 'vanilla') {\n return `import { createAnalytics } from '@01.software/sdk/analytics'\n\n// Replace 'YOUR_PUBLISHABLE_KEY' with your actual publishable key from the 01.software console\nexport const analytics = createAnalytics({\n publishableKey: 'YOUR_PUBLISHABLE_KEY',\n})\n`\n }\n\n const publishableKeyExpression =\n env === 'react-vite'\n ? `import.meta.env.${publishableKeyEnvVar}`\n : `process.env.${publishableKeyEnvVar}!`\n\n return `import { createAnalytics } from '@01.software/sdk/analytics'\n\nexport const analytics = createAnalytics({\n publishableKey: ${publishableKeyExpression},\n})\n`\n}\n\n// ── Query Provider template ──────────────────────────────────────────\n\nexport function getQueryProviderTemplate(env: ProjectEnv): string {\n const useClientDirective = env === 'nextjs' ? \"'use client'\\n\\n\" : ''\n\n return `${useClientDirective}import { QueryClientProvider } from '@tanstack/react-query'\nimport { client } from './client'\n\nexport function QueryProvider({ children }: { children: React.ReactNode }) {\n return (\n <QueryClientProvider client={client.queryClient}>\n {children}\n </QueryClientProvider>\n )\n}\n`\n}\n\n// ── Server template ──────────────────────────────────────────────────\n\nexport function getServerTemplate(\n env: ProjectEnv,\n publishableKeyEnvVar: string,\n secretKeyEnvVar: string,\n): string {\n if (env === 'edge') {\n return `import { createServerClient } from '@01.software/sdk'\n\n// Edge runtime: pass your env bindings here\n// e.g. Cloudflare Workers: use env.SOFTWARE_PUBLISHABLE_KEY from the handler context\n// e.g. Vercel Edge: use process.env.${publishableKeyEnvVar}\nexport function createEdgeClient(publishableKey: string, secretKey: string) {\n return createServerClient({ publishableKey, secretKey })\n}\n`\n }\n\n return `import { createServerClient } from '@01.software/sdk'\n\n/**\n * Runtime guard: this module contains secret keys and must never be\n * imported from client-side (browser) code. If the bundler accidentally\n * includes it in a client bundle, this throws at module-evaluation time.\n */\nif (typeof window !== 'undefined') {\n throw new Error(\n 'lib/software/server.ts must not be imported in client-side code — it contains secret keys.',\n )\n}\n\ntype ServerClient = ReturnType<typeof createServerClient>\n\nlet cachedClient: ServerClient | null = null\n\nfunction createConfiguredClient(): ServerClient {\n const publishableKey = process.env.${publishableKeyEnvVar}\n const secretKey = process.env.${secretKeyEnvVar}\n\n if (!publishableKey || !secretKey) {\n throw new Error(\n 'Server client requires ${publishableKeyEnvVar} and ${secretKeyEnvVar}.',\n )\n }\n\n return createServerClient({ publishableKey, secretKey })\n}\n\nexport function getServerClient(): ServerClient {\n cachedClient ??= createConfiguredClient()\n return cachedClient\n}\n\n/**\n * Lazy Proxy: the underlying client is created only on first property access,\n * so importing this file has no side effects until you actually call it.\n */\nexport const serverClient = new Proxy({} as ServerClient, {\n get(_target, prop, receiver) {\n const target = getServerClient()\n const value = Reflect.get(target as object, prop, receiver)\n return typeof value === 'function' ? value.bind(target) : value\n },\n})\n`\n}\n\n// ── Env file content ─────────────────────────────────────────────────\n\nexport function getEnvContent(\n publishableKey: string,\n secretKey: string,\n publishableKeyEnvVar: string,\n secretKeyEnvVar: string | null,\n): string {\n let content = `\\n# 01.software\\n${publishableKeyEnvVar}=${publishableKey}\\n`\n if (secretKeyEnvVar) {\n content += `${secretKeyEnvVar}=${secretKey}\\n`\n }\n return content\n}\n\n// ── MCP config (JSON) ────────────────────────────────────────────────\n\nexport type McpJsonClient = 'generic' | 'windsurf'\n\nexport function getMcpServerEntry(client: McpJsonClient = 'generic') {\n if (client === 'windsurf') {\n return {\n serverUrl: MCP_RESOURCE_AUDIENCE,\n }\n }\n\n return {\n type: 'http' as const,\n url: MCP_RESOURCE_AUDIENCE,\n }\n}\n\nexport function getMcpConfigTemplate(client: McpJsonClient = 'generic'): string {\n return (\n JSON.stringify(\n { mcpServers: { '01software': getMcpServerEntry(client) } },\n null,\n 2,\n ) + '\\n'\n )\n}\n\n// ── MCP config (TOML — Codex CLI) ────────────────────────────────────\n\n/** Codex CLI `[mcp_servers.01software]` block. Idempotent marker: the header\n * line. Intended to be appended to `~/.codex/config.toml`. */\nexport function getCodexMcpTomlSection(): string {\n return `\n[mcp_servers.01software]\nurl = \"${MCP_RESOURCE_AUDIENCE}\"\n`\n}\n\nexport const CODEX_MCP_SECTION_MARKER = '[mcp_servers.01software]'\n"],"mappings":";;;AAAO,IAAM,wBAAwB;;;ACK9B,SAAS,kBAAkB,KAAiB,sBAAsC;AACvF,MAAI,QAAQ,UAAU;AACpB,WAAO;AAAA;AAAA;AAAA,gCAGqB,oBAAoB;AAAA;AAAA;AAAA,EAGlD;AAEA,MAAI,QAAQ,aAAa;AACvB,WAAO;AAAA;AAAA;AAAA,gCAGqB,oBAAoB;AAAA;AAAA;AAAA,EAGlD;AAEA,MAAI,QAAQ,WAAW;AACrB,WAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOT;AAGA,SAAO;AAAA;AAAA;AAAA,oCAG2B,oBAAoB;AAAA;AAAA;AAGxD;AAIO,SAAS,qBACd,KACA,sBACQ;AACR,MAAI,QAAQ,WAAW;AACrB,WAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOT;AAEA,QAAM,2BACJ,QAAQ,eACJ,mBAAmB,oBAAoB,KACvC,eAAe,oBAAoB;AAEzC,SAAO;AAAA;AAAA;AAAA,oBAGW,wBAAwB;AAAA;AAAA;AAG5C;AAIO,SAAS,yBAAyB,KAAyB;AAChE,QAAM,qBAAqB,QAAQ,WAAW,qBAAqB;AAEnE,SAAO,GAAG,kBAAkB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAW9B;AAIO,SAAS,kBACd,KACA,sBACA,iBACQ;AACR,MAAI,QAAQ,QAAQ;AAClB,WAAO;AAAA;AAAA;AAAA;AAAA,8CAImC,oBAAoB;AAAA;AAAA;AAAA;AAAA;AAAA,EAKhE;AAEA,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,uCAkB8B,oBAAoB;AAAA,kCACzB,eAAe;AAAA;AAAA;AAAA;AAAA,gCAIjB,oBAAoB,QAAQ,eAAe;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAwB3E;AAIO,SAAS,cACd,gBACA,WACA,sBACA,iBACQ;AACR,MAAI,UAAU;AAAA;AAAA,EAAoB,oBAAoB,IAAI,cAAc;AAAA;AACxE,MAAI,iBAAiB;AACnB,eAAW,GAAG,eAAe,IAAI,SAAS;AAAA;AAAA,EAC5C;AACA,SAAO;AACT;AAMO,SAAS,kBAAkB,SAAwB,WAAW;AACnE,MAAI,WAAW,YAAY;AACzB,WAAO;AAAA,MACL,WAAW;AAAA,IACb;AAAA,EACF;AAEA,SAAO;AAAA,IACL,MAAM;AAAA,IACN,KAAK;AAAA,EACP;AACF;AAEO,SAAS,qBAAqB,SAAwB,WAAmB;AAC9E,SACE,KAAK;AAAA,IACH,EAAE,YAAY,EAAE,cAAc,kBAAkB,MAAM,EAAE,EAAE;AAAA,IAC1D;AAAA,IACA;AAAA,EACF,IAAI;AAER;AAMO,SAAS,yBAAiC;AAC/C,SAAO;AAAA;AAAA,SAEA,qBAAqB;AAAA;AAE9B;AAEO,IAAM,2BAA2B;","names":[]} |
| #!/usr/bin/env node | ||
| // src/ai-docs.ts | ||
| function normalizeActiveCollections(collections) { | ||
| if (Array.isArray(collections?.active)) return collections.active; | ||
| return []; | ||
| } | ||
| function generateClaudeMd(ctx) { | ||
| const featuresSection = ctx.features && ctx.features.length > 0 ? ctx.features.map((f) => `- ${f}`).join("\n") : "- See console"; | ||
| const collectionsSection = ctx.collections && ctx.collections.length > 0 ? ctx.collections.join(", ") : "Run `01 schema list`"; | ||
| return `# 01.software SDK \u2014 ${ctx.tenantName} | ||
| ## Connection | ||
| - Publishable Key: \`NEXT_PUBLIC_SOFTWARE_PUBLISHABLE_KEY\` (env) | ||
| - Secret Key: \`SOFTWARE_SECRET_KEY\` (env) | ||
| - MCP: \`.mcp.json\` | ||
| ## Active Features | ||
| ${featuresSection} | ||
| ## Active Collections | ||
| ${collectionsSection} | ||
| ## MCP Quick Reference | ||
| | Tool | Use | | ||
| |------|-----| | ||
| | \`query-collection\` | List/filter documents | | ||
| | \`create-collection\` | Create documents | | ||
| | \`update-field-config\` | Hide unused fields | | ||
| | \`get-tenant-context\` | Show active features & collections | | ||
| ## CLI | ||
| - \`01 query <collection>\` \u2014 query data | ||
| - \`01 schema show <collection>\` \u2014 inspect fields | ||
| - \`01 schema list\` \u2014 list all collections | ||
| ## Initial Setup | ||
| Run \`/01software-field-config\` in Claude Code to configure field visibility for your use case. | ||
| Use \`get-collection-schema\` or \`01 schema show <collection>\` for live field introspection instead of relying on this document as a schema snapshot. | ||
| `; | ||
| } | ||
| function getSkillFiles() { | ||
| return [ | ||
| { | ||
| dirName: "01software-field-config", | ||
| content: `--- | ||
| name: 01software-field-config | ||
| description: Configure field visibility for this tenant \u2014 hide unused collections and fields via MCP | ||
| disable-model-invocation: true | ||
| --- | ||
| Steps: | ||
| 1. Use \`list-configurable-fields\` to see current visibility settings | ||
| 2. Identify fields/collections not needed for your use case | ||
| 3. Use \`update-field-config\` to hide them | ||
| Common setups: | ||
| - Blog only: hide \`ecommerce\`, \`customers\`, \`videos\` collections | ||
| - Store: hide \`articles\`, \`documents\`, \`galleries\`, \`canvas\` collections | ||
| - Minimal: hide all except the collections you actively use | ||
| Ask me: "Show current field config" or "Hide ecommerce fields" | ||
| ` | ||
| }, | ||
| { | ||
| dirName: "01software-query", | ||
| content: `--- | ||
| name: 01software-query | ||
| description: Query 01.software collections via MCP or CLI with filter, sort, and pagination examples | ||
| --- | ||
| Query collections using the MCP \`query-collection\` tool or CLI. | ||
| MCP examples: | ||
| - List products: \`query-collection\` with collection="products", limit=10 | ||
| - Filter by status: add where={"status":{"equals":"published"}} | ||
| - Sort by date: sort="-createdAt" | ||
| - Paginate: page=2, limit=20 | ||
| CLI examples: | ||
| - \`01 query products --limit 10\` | ||
| - \`01 query orders --where '{"status":{"equals":"paid"}}'\` | ||
| - \`01 schema show products\` \u2014 inspect available fields | ||
| SDK (server): | ||
| \`\`\`typescript | ||
| const { docs } = await serverClient.collection('products').find({ | ||
| where: { status: { equals: 'published' } }, | ||
| sort: '-createdAt', | ||
| limit: 10, | ||
| }) | ||
| \`\`\` | ||
| ` | ||
| }, | ||
| { | ||
| dirName: "01software-order-flow", | ||
| content: `--- | ||
| name: 01software-order-flow | ||
| description: Order lifecycle reference \u2014 create, pay, fulfill, and return flows for 01.software | ||
| --- | ||
| Complete order flow from creation to fulfillment. | ||
| States: pending \u2192 paid \u2192 preparing \u2192 shipped \u2192 delivered \u2192 confirmed | ||
| 1. Create order: \`create-order\` with orderNumber, customerSnapshot, orderProducts, totalAmount | ||
| 2. Mark paid: \`update-order\` with status="paid" (after payment gateway confirms) | ||
| 3. Fulfill: \`create-fulfillment\` with items and carrier/trackingNumber | ||
| 4. Returns: \`create-return\` or \`return-with-refund\` (atomic) | ||
| Free orders: omit paymentId, totalAmount=0 \u2192 auto-transitions to paid | ||
| CLI: \`01 order create --help\` for full options | ||
| ` | ||
| }, | ||
| { | ||
| dirName: "01software-schema", | ||
| content: `--- | ||
| name: 01software-schema | ||
| description: Inspect 01.software collection schemas and available fields via MCP or CLI | ||
| --- | ||
| Inspect collection schemas to understand available fields. | ||
| MCP: use \`get-collection-schema\` with collection | ||
| CLI: | ||
| - \`01 schema list\` \u2014 all available collections | ||
| - \`01 schema show <collection>\` \u2014 field names, types, required status | ||
| Common collections: products, orders, customers, articles, documents, images | ||
| Use \`get-tenant-context\` to see which collections are active for this tenant. | ||
| ` | ||
| } | ||
| ]; | ||
| } | ||
| async function fetchTenantContext(publishableKey, secretKey) { | ||
| try { | ||
| const apiUrl = process.env.SOFTWARE_API_URL || "https://api.01.software"; | ||
| const res = await fetch(`${apiUrl}/api/tenants/context`, { | ||
| headers: { | ||
| "X-Publishable-Key": publishableKey, | ||
| Authorization: `Bearer ${secretKey}` | ||
| } | ||
| }); | ||
| if (!res.ok) return null; | ||
| const data = await res.json(); | ||
| return { | ||
| tenantName: data.tenant?.name || "", | ||
| features: data.features || [], | ||
| collections: normalizeActiveCollections(data.collections) | ||
| }; | ||
| } catch { | ||
| return null; | ||
| } | ||
| } | ||
| export { | ||
| generateClaudeMd, | ||
| getSkillFiles, | ||
| fetchTenantContext | ||
| }; | ||
| //# sourceMappingURL=chunk-T3A5SLEJ.js.map |
| {"version":3,"sources":["../src/ai-docs.ts"],"sourcesContent":["export interface TenantContext {\n tenantName: string\n features?: string[]\n collections?: string[]\n}\n\ninterface TenantContextApiResponse {\n tenant?: { name?: string }\n features?: string[]\n collections?: { active?: string[]; inactive?: string[] }\n}\n\nfunction normalizeActiveCollections(\n collections: TenantContextApiResponse['collections'],\n): string[] {\n if (Array.isArray(collections?.active)) return collections.active\n return []\n}\n\n// ── CLAUDE.md ────────────────────────────────────────────────────────\n\nexport function generateClaudeMd(ctx: TenantContext): string {\n const featuresSection =\n ctx.features && ctx.features.length > 0\n ? ctx.features.map((f) => `- ${f}`).join('\\n')\n : '- See console'\n\n const collectionsSection =\n ctx.collections && ctx.collections.length > 0\n ? ctx.collections.join(', ')\n : 'Run `01 schema list`'\n\n return `# 01.software SDK — ${ctx.tenantName}\n\n## Connection\n- Publishable Key: \\`NEXT_PUBLIC_SOFTWARE_PUBLISHABLE_KEY\\` (env)\n- Secret Key: \\`SOFTWARE_SECRET_KEY\\` (env)\n- MCP: \\`.mcp.json\\`\n\n## Active Features\n${featuresSection}\n\n## Active Collections\n${collectionsSection}\n\n## MCP Quick Reference\n| Tool | Use |\n|------|-----|\n| \\`query-collection\\` | List/filter documents |\n| \\`create-collection\\` | Create documents |\n| \\`update-field-config\\` | Hide unused fields |\n| \\`get-tenant-context\\` | Show active features & collections |\n\n## CLI\n- \\`01 query <collection>\\` — query data\n- \\`01 schema show <collection>\\` — inspect fields\n- \\`01 schema list\\` — list all collections\n\n## Initial Setup\nRun \\`/01software-field-config\\` in Claude Code to configure field visibility for your use case.\nUse \\`get-collection-schema\\` or \\`01 schema show <collection>\\` for live field introspection instead of relying on this document as a schema snapshot.\n`\n}\n\n// ── Skill files ──────────────────────────────────────────────────────\n\nexport function getSkillFiles(): Array<{ dirName: string; content: string }> {\n return [\n {\n dirName: '01software-field-config',\n content: `---\nname: 01software-field-config\ndescription: Configure field visibility for this tenant — hide unused collections and fields via MCP\ndisable-model-invocation: true\n---\n\nSteps:\n1. Use \\`list-configurable-fields\\` to see current visibility settings\n2. Identify fields/collections not needed for your use case\n3. Use \\`update-field-config\\` to hide them\n\nCommon setups:\n- Blog only: hide \\`ecommerce\\`, \\`customers\\`, \\`videos\\` collections\n- Store: hide \\`articles\\`, \\`documents\\`, \\`galleries\\`, \\`canvas\\` collections\n- Minimal: hide all except the collections you actively use\n\nAsk me: \"Show current field config\" or \"Hide ecommerce fields\"\n`,\n },\n {\n dirName: '01software-query',\n content: `---\nname: 01software-query\ndescription: Query 01.software collections via MCP or CLI with filter, sort, and pagination examples\n---\n\nQuery collections using the MCP \\`query-collection\\` tool or CLI.\n\nMCP examples:\n- List products: \\`query-collection\\` with collection=\"products\", limit=10\n- Filter by status: add where={\"status\":{\"equals\":\"published\"}}\n- Sort by date: sort=\"-createdAt\"\n- Paginate: page=2, limit=20\n\nCLI examples:\n- \\`01 query products --limit 10\\`\n- \\`01 query orders --where '{\"status\":{\"equals\":\"paid\"}}'\\`\n- \\`01 schema show products\\` — inspect available fields\n\nSDK (server):\n\\`\\`\\`typescript\nconst { docs } = await serverClient.collection('products').find({\n where: { status: { equals: 'published' } },\n sort: '-createdAt',\n limit: 10,\n})\n\\`\\`\\`\n`,\n },\n {\n dirName: '01software-order-flow',\n content: `---\nname: 01software-order-flow\ndescription: Order lifecycle reference — create, pay, fulfill, and return flows for 01.software\n---\n\nComplete order flow from creation to fulfillment.\n\nStates: pending → paid → preparing → shipped → delivered → confirmed\n\n1. Create order: \\`create-order\\` with orderNumber, customerSnapshot, orderProducts, totalAmount\n2. Mark paid: \\`update-order\\` with status=\"paid\" (after payment gateway confirms)\n3. Fulfill: \\`create-fulfillment\\` with items and carrier/trackingNumber\n4. Returns: \\`create-return\\` or \\`return-with-refund\\` (atomic)\n\nFree orders: omit paymentId, totalAmount=0 → auto-transitions to paid\n\nCLI: \\`01 order create --help\\` for full options\n`,\n },\n {\n dirName: '01software-schema',\n content: `---\nname: 01software-schema\ndescription: Inspect 01.software collection schemas and available fields via MCP or CLI\n---\n\nInspect collection schemas to understand available fields.\n\nMCP: use \\`get-collection-schema\\` with collection\n\nCLI:\n- \\`01 schema list\\` — all available collections\n- \\`01 schema show <collection>\\` — field names, types, required status\n\nCommon collections: products, orders, customers, articles, documents, images\nUse \\`get-tenant-context\\` to see which collections are active for this tenant.\n`,\n },\n ]\n}\n\n// ── Tenant context fetch ─────────────────────────────────────────────\n\nexport async function fetchTenantContext(\n publishableKey: string,\n secretKey: string,\n): Promise<TenantContext | null> {\n try {\n const apiUrl = process.env.SOFTWARE_API_URL || 'https://api.01.software'\n // secretKey is now an opaque sk01_/pat01_ bearer token — send it directly.\n const res = await fetch(`${apiUrl}/api/tenants/context`, {\n headers: {\n 'X-Publishable-Key': publishableKey,\n Authorization: `Bearer ${secretKey}`,\n },\n })\n if (!res.ok) return null\n const data = (await res.json()) as TenantContextApiResponse\n return {\n tenantName: data.tenant?.name || '',\n features: data.features || [],\n collections: normalizeActiveCollections(data.collections),\n }\n } catch {\n return null\n }\n}\n"],"mappings":";;;AAYA,SAAS,2BACP,aACU;AACV,MAAI,MAAM,QAAQ,aAAa,MAAM,EAAG,QAAO,YAAY;AAC3D,SAAO,CAAC;AACV;AAIO,SAAS,iBAAiB,KAA4B;AAC3D,QAAM,kBACJ,IAAI,YAAY,IAAI,SAAS,SAAS,IAClC,IAAI,SAAS,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,EAAE,KAAK,IAAI,IAC3C;AAEN,QAAM,qBACJ,IAAI,eAAe,IAAI,YAAY,SAAS,IACxC,IAAI,YAAY,KAAK,IAAI,IACzB;AAEN,SAAO,4BAAuB,IAAI,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQ5C,eAAe;AAAA;AAAA;AAAA,EAGf,kBAAkB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAmBpB;AAIO,SAAS,gBAA6D;AAC3E,SAAO;AAAA,IACL;AAAA,MACE,SAAS;AAAA,MACT,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAkBX;AAAA,IACA;AAAA,MACE,SAAS;AAAA,MACT,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IA2BX;AAAA,IACA;AAAA,MACE,SAAS;AAAA,MACT,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAkBX;AAAA,IACA;AAAA,MACE,SAAS;AAAA,MACT,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAgBX;AAAA,EACF;AACF;AAIA,eAAsB,mBACpB,gBACA,WAC+B;AAC/B,MAAI;AACF,UAAM,SAAS,QAAQ,IAAI,oBAAoB;AAE/C,UAAM,MAAM,MAAM,MAAM,GAAG,MAAM,wBAAwB;AAAA,MACvD,SAAS;AAAA,QACP,qBAAqB;AAAA,QACrB,eAAe,UAAU,SAAS;AAAA,MACpC;AAAA,IACF,CAAC;AACD,QAAI,CAAC,IAAI,GAAI,QAAO;AACpB,UAAM,OAAQ,MAAM,IAAI,KAAK;AAC7B,WAAO;AAAA,MACL,YAAY,KAAK,QAAQ,QAAQ;AAAA,MACjC,UAAU,KAAK,YAAY,CAAC;AAAA,MAC5B,aAAa,2BAA2B,KAAK,WAAW;AAAA,IAC1D;AAAA,EACF,QAAQ;AACN,WAAO;AAAA,EACT;AACF;","names":[]} |
+1
-1
@@ -6,3 +6,3 @@ #!/usr/bin/env node | ||
| getSkillFiles | ||
| } from "./chunk-SRLZ5OIV.js"; | ||
| } from "./chunk-T3A5SLEJ.js"; | ||
| export { | ||
@@ -9,0 +9,0 @@ fetchTenantContext, |
+1
-3
| #!/usr/bin/env node | ||
| import { | ||
| extractTomlApiKey, | ||
| readEnvValue, | ||
| replaceTomlMcpSection, | ||
| setEnvValue | ||
| } from "./chunk-VOEXMD2S.js"; | ||
| } from "./chunk-JT3G6B66.js"; | ||
| export { | ||
| extractTomlApiKey, | ||
| readEnvValue, | ||
@@ -11,0 +9,0 @@ replaceTomlMcpSection, |
+51
-64
@@ -6,11 +6,11 @@ #!/usr/bin/env node | ||
| getSkillFiles | ||
| } from "./chunk-SRLZ5OIV.js"; | ||
| } from "./chunk-T3A5SLEJ.js"; | ||
| import { | ||
| extractTomlApiKey, | ||
| readEnvValue, | ||
| replaceTomlMcpSection, | ||
| setEnvValue | ||
| } from "./chunk-VOEXMD2S.js"; | ||
| } from "./chunk-JT3G6B66.js"; | ||
| import { | ||
| CODEX_MCP_SECTION_MARKER, | ||
| getAnalyticsTemplate, | ||
| getClientTemplate, | ||
@@ -23,3 +23,3 @@ getCodexMcpTomlSection, | ||
| getServerTemplate | ||
| } from "./chunk-OEAQV63E.js"; | ||
| } from "./chunk-S3KHPWCE.js"; | ||
@@ -245,3 +245,3 @@ // src/index.ts | ||
| name: "method", | ||
| message: "API keys:", | ||
| message: "SDK credentials:", | ||
| choices: [ | ||
@@ -462,3 +462,2 @@ { title: "Browser login (recommended)", value: "browser" }, | ||
| var SECRET_KEY_ENV_VAR = "SOFTWARE_SECRET_KEY"; | ||
| var API_KEY_PLACEHOLDER = "YOUR_API_KEY"; | ||
| async function init(cwd, info, answers) { | ||
@@ -491,2 +490,8 @@ const { packageManager, srcDir } = info; | ||
| ); | ||
| await writeFileWithPolicy( | ||
| cwd, | ||
| path2.join(libDir, "analytics.ts"), | ||
| getAnalyticsTemplate(env, publishableKeyEnvVar), | ||
| plan.policy | ||
| ); | ||
| } | ||
@@ -550,5 +555,4 @@ if (wantsReactQuery) { | ||
| if (answers.aiTools.length > 0) { | ||
| const apiKey = secretKey || API_KEY_PLACEHOLDER; | ||
| for (const tool of answers.aiTools) { | ||
| await writeMcpConfig(tool, cwd, apiKey, plan.policy); | ||
| await writeMcpConfig(tool, cwd); | ||
| } | ||
@@ -658,3 +662,3 @@ addToGitignore(cwd, answers.aiTools); | ||
| name: "file", | ||
| message: "Which env file should I write API keys to?", | ||
| message: "Which env file should I write SDK credentials to?", | ||
| choices, | ||
@@ -753,3 +757,3 @@ initial | ||
| envFile, | ||
| fromBrowserAuth ? pc2.dim("(API keys)") : "" | ||
| fromBrowserAuth ? pc2.dim("(SDK credentials)") : "" | ||
| ); | ||
@@ -787,3 +791,9 @@ } else { | ||
| const p = path2.join(home, ".codeium", "windsurf", "mcp_config.json"); | ||
| return { kind: "json", absolutePath: p, displayPath: p, gitignoreEntry: null }; | ||
| return { | ||
| kind: "json", | ||
| absolutePath: p, | ||
| jsonClient: "windsurf", | ||
| displayPath: p, | ||
| gitignoreEntry: null | ||
| }; | ||
| } | ||
@@ -802,3 +812,3 @@ case "codex": { | ||
| } | ||
| async function writeMcpConfig(tool, cwd, apiKey, policy) { | ||
| async function writeMcpConfig(tool, cwd) { | ||
| const loc = resolveMcpLocation(tool, cwd); | ||
@@ -811,10 +821,10 @@ if (!loc) { | ||
| if (loc.kind === "json") { | ||
| await writeJsonMcp(loc, apiKey, policy); | ||
| writeJsonMcp(loc); | ||
| } else { | ||
| await writeTomlMcp(loc, apiKey, policy); | ||
| writeTomlMcp(loc); | ||
| } | ||
| } | ||
| async function writeJsonMcp(loc, apiKey, policy) { | ||
| function writeJsonMcp(loc) { | ||
| if (!fs2.existsSync(loc.absolutePath)) { | ||
| fs2.writeFileSync(loc.absolutePath, getMcpConfigTemplate(apiKey)); | ||
| fs2.writeFileSync(loc.absolutePath, getMcpConfigTemplate(loc.jsonClient)); | ||
| console.log(pc2.green(" Created"), loc.displayPath); | ||
@@ -830,25 +840,14 @@ return; | ||
| } | ||
| const existingApiKey = existing.mcpServers?.["01software"]?.headers?.["x-api-key"]; | ||
| if (existingApiKey === apiKey) { | ||
| const nextEntry = getMcpServerEntry(loc.jsonClient); | ||
| if (existing.mcpServers?.["01software"] && JSON.stringify(existing.mcpServers["01software"]) === JSON.stringify(nextEntry)) { | ||
| console.log(pc2.dim(" Unchanged"), loc.displayPath); | ||
| return; | ||
| } | ||
| if (apiKey === API_KEY_PLACEHOLDER && existingApiKey && existingApiKey !== API_KEY_PLACEHOLDER) { | ||
| console.log(pc2.dim(" Kept existing API key in"), loc.displayPath); | ||
| return; | ||
| } | ||
| if (existingApiKey) { | ||
| const shouldUpdate = await confirmKeyUpdate(loc.displayPath, policy); | ||
| if (!shouldUpdate) { | ||
| console.log(pc2.yellow(" Skipped"), loc.displayPath, pc2.dim("(kept existing API key)")); | ||
| return; | ||
| } | ||
| } | ||
| existing.mcpServers = existing.mcpServers || {}; | ||
| existing.mcpServers["01software"] = getMcpServerEntry(apiKey); | ||
| existing.mcpServers["01software"] = nextEntry; | ||
| fs2.writeFileSync(loc.absolutePath, JSON.stringify(existing, null, 2) + "\n"); | ||
| console.log(pc2.green(" Updated"), loc.displayPath); | ||
| } | ||
| async function writeTomlMcp(loc, apiKey, policy) { | ||
| const section = getCodexMcpTomlSection(apiKey); | ||
| function writeTomlMcp(loc) { | ||
| const section = getCodexMcpTomlSection(); | ||
| if (!fs2.existsSync(loc.absolutePath)) { | ||
@@ -866,30 +865,10 @@ fs2.writeFileSync(loc.absolutePath, section.trimStart()); | ||
| } | ||
| const existingApiKey = extractTomlApiKey(existing); | ||
| if (existingApiKey === apiKey) { | ||
| const replaced = replaceTomlMcpSection(existing, section); | ||
| if (replaced === existing) { | ||
| console.log(pc2.dim(" Unchanged"), loc.displayPath); | ||
| return; | ||
| } | ||
| if (apiKey === API_KEY_PLACEHOLDER && existingApiKey && existingApiKey !== API_KEY_PLACEHOLDER) { | ||
| console.log(pc2.dim(" Kept existing API key in"), loc.displayPath); | ||
| return; | ||
| } | ||
| const shouldUpdate = await confirmKeyUpdate(loc.displayPath, policy); | ||
| if (!shouldUpdate) { | ||
| console.log(pc2.yellow(" Skipped"), loc.displayPath, pc2.dim("(kept existing API key)")); | ||
| return; | ||
| } | ||
| const replaced = replaceTomlMcpSection(existing, section); | ||
| fs2.writeFileSync(loc.absolutePath, replaced); | ||
| console.log(pc2.green(" Updated"), loc.displayPath); | ||
| } | ||
| async function confirmKeyUpdate(displayPath, policy) { | ||
| if (policy === "overwrite") return true; | ||
| const { confirm } = await prompts2({ | ||
| type: "confirm", | ||
| name: "confirm", | ||
| message: `${displayPath} has a different 01software API key. Update?`, | ||
| initial: true | ||
| }); | ||
| return !!confirm; | ||
| } | ||
| function addToGitignore(cwd, tools) { | ||
@@ -906,3 +885,3 @@ const entries = []; | ||
| if (toAdd.length === 0) return; | ||
| const content = "\n# MCP configs (contain API key)\n" + toAdd.join("\n") + "\n"; | ||
| const content = "\n# MCP configs\n" + toAdd.join("\n") + "\n"; | ||
| if (fs2.existsSync(gitignorePath)) { | ||
@@ -1071,2 +1050,7 @@ fs2.appendFileSync(gitignorePath, content); | ||
| console.log(); | ||
| console.log(pc3.dim(" Optional: start browser analytics with the generated helper:")); | ||
| console.log(); | ||
| console.log(pc3.cyan(" import { analytics } from '@/lib/software/analytics'")); | ||
| console.log(pc3.cyan(" analytics.track('signup')")); | ||
| console.log(); | ||
| } else if (env === "react-vite" || env === "react-cra") { | ||
@@ -1078,2 +1062,7 @@ console.log(pc3.dim(" Wrap your app entry with QueryProvider:")); | ||
| console.log(); | ||
| console.log(pc3.dim(" Optional: start browser analytics with the generated helper:")); | ||
| console.log(); | ||
| console.log(pc3.cyan(" import { analytics } from './lib/software/analytics'")); | ||
| console.log(pc3.cyan(" analytics.track('signup')")); | ||
| console.log(); | ||
| } else if (env === "vanilla") { | ||
@@ -1083,4 +1072,6 @@ console.log(pc3.dim(" Replace YOUR_PUBLISHABLE_KEY in lib/software/client.ts")); | ||
| console.log(pc3.cyan(" import { client } from './lib/software/client'")); | ||
| console.log(pc3.cyan(" const posts = await client.from('posts').find()")); | ||
| console.log(pc3.cyan(" const articles = await client.from('articles').find()")); | ||
| console.log(); | ||
| console.log(pc3.dim(" Optional: wire the analytics helper in lib/software/analytics.ts")); | ||
| console.log(); | ||
| } else if (env === "node") { | ||
@@ -1090,3 +1081,3 @@ console.log(pc3.dim(" Use the server client:")); | ||
| console.log(pc3.cyan(" import { serverClient } from './lib/software/server'")); | ||
| console.log(pc3.cyan(" const posts = await serverClient.from('posts').find()")); | ||
| console.log(pc3.cyan(" const articles = await serverClient.from('articles').find()")); | ||
| console.log(); | ||
@@ -1103,11 +1094,7 @@ } else if (env === "edge") { | ||
| if (missingPublishableKey || missingSecretKey) { | ||
| console.log(pc3.dim(" Update .env with your API keys")); | ||
| console.log(pc3.dim(" Update .env with your SDK credentials")); | ||
| console.log(); | ||
| } | ||
| if (answers.aiTools.length > 0 && (!answers.publishableKey || !answers.secretKey)) { | ||
| console.log( | ||
| pc3.dim( | ||
| " Update MCP config x-api-key with your sk01_... or pat01_... bearer token" | ||
| ) | ||
| ); | ||
| if (answers.aiTools.length > 0) { | ||
| console.log(pc3.dim(" MCP config uses OAuth discovery.")); | ||
| console.log(); | ||
@@ -1114,0 +1101,0 @@ } |
| #!/usr/bin/env node | ||
| import { | ||
| CODEX_MCP_SECTION_MARKER, | ||
| getAnalyticsTemplate, | ||
| getClientTemplate, | ||
@@ -11,5 +12,6 @@ getCodexMcpTomlSection, | ||
| getServerTemplate | ||
| } from "./chunk-OEAQV63E.js"; | ||
| } from "./chunk-S3KHPWCE.js"; | ||
| export { | ||
| CODEX_MCP_SECTION_MARKER, | ||
| getAnalyticsTemplate, | ||
| getClientTemplate, | ||
@@ -16,0 +18,0 @@ getCodexMcpTomlSection, |
+3
-2
| { | ||
| "name": "@01.software/init", | ||
| "version": "0.6.1", | ||
| "version": "0.7.0", | ||
| "description": "Initialize 01.software SDK in your project (Next.js, React, Vanilla JS, Node.js, Edge)", | ||
@@ -17,3 +17,4 @@ "type": "module", | ||
| "picocolors": "^1.1.1", | ||
| "prompts": "^2.4.2" | ||
| "prompts": "^2.4.2", | ||
| "@01.software/auth-contracts": "0.1.0" | ||
| }, | ||
@@ -20,0 +21,0 @@ "devDependencies": { |
| #!/usr/bin/env node | ||
| // src/templates.ts | ||
| function getClientTemplate(env, publishableKeyEnvVar) { | ||
| if (env === "nextjs") { | ||
| return `import { createClient } from '@01.software/sdk' | ||
| export const client = createClient({ | ||
| publishableKey: process.env.${publishableKeyEnvVar}!, | ||
| }) | ||
| `; | ||
| } | ||
| if (env === "react-cra") { | ||
| return `import { createClient } from '@01.software/sdk' | ||
| export const client = createClient({ | ||
| publishableKey: process.env.${publishableKeyEnvVar}!, | ||
| }) | ||
| `; | ||
| } | ||
| if (env === "vanilla") { | ||
| return `import { createClient } from '@01.software/sdk' | ||
| // Replace 'YOUR_PUBLISHABLE_KEY' with your actual publishable key from the 01.software console | ||
| export const client = createClient({ | ||
| publishableKey: 'YOUR_PUBLISHABLE_KEY', | ||
| }) | ||
| `; | ||
| } | ||
| return `import { createClient } from '@01.software/sdk' | ||
| export const client = createClient({ | ||
| publishableKey: import.meta.env.${publishableKeyEnvVar}, | ||
| }) | ||
| `; | ||
| } | ||
| function getQueryProviderTemplate(env) { | ||
| const useClientDirective = env === "nextjs" ? "'use client'\n\n" : ""; | ||
| return `${useClientDirective}import { QueryClientProvider } from '@tanstack/react-query' | ||
| import { client } from './client' | ||
| export function QueryProvider({ children }: { children: React.ReactNode }) { | ||
| return ( | ||
| <QueryClientProvider client={client.queryClient}> | ||
| {children} | ||
| </QueryClientProvider> | ||
| ) | ||
| } | ||
| `; | ||
| } | ||
| function getServerTemplate(env, publishableKeyEnvVar, secretKeyEnvVar) { | ||
| if (env === "edge") { | ||
| return `import { createServerClient } from '@01.software/sdk' | ||
| // Edge runtime: pass your env bindings here | ||
| // e.g. Cloudflare Workers: use env.SOFTWARE_PUBLISHABLE_KEY from the handler context | ||
| // e.g. Vercel Edge: use process.env.${publishableKeyEnvVar} | ||
| export function createEdgeClient(publishableKey: string, secretKey: string) { | ||
| return createServerClient({ publishableKey, secretKey }) | ||
| } | ||
| `; | ||
| } | ||
| return `import { createServerClient } from '@01.software/sdk' | ||
| export const serverClient = createServerClient({ | ||
| publishableKey: process.env.${publishableKeyEnvVar}!, | ||
| secretKey: process.env.${secretKeyEnvVar}!, | ||
| }) | ||
| `; | ||
| } | ||
| function getEnvContent(publishableKey, secretKey, publishableKeyEnvVar, secretKeyEnvVar) { | ||
| let content = ` | ||
| # 01.software | ||
| ${publishableKeyEnvVar}=${publishableKey} | ||
| `; | ||
| if (secretKeyEnvVar) { | ||
| content += `${secretKeyEnvVar}=${secretKey} | ||
| `; | ||
| } | ||
| return content; | ||
| } | ||
| function getMcpServerEntry(apiKey) { | ||
| return { | ||
| type: "http", | ||
| url: "https://mcp.01.software/mcp", | ||
| headers: { "x-api-key": apiKey } | ||
| }; | ||
| } | ||
| function getMcpConfigTemplate(apiKey) { | ||
| return JSON.stringify( | ||
| { mcpServers: { "01software": getMcpServerEntry(apiKey) } }, | ||
| null, | ||
| 2 | ||
| ) + "\n"; | ||
| } | ||
| function escapeTomlString(s) { | ||
| return s.replace(/\\/g, "\\\\").replace(/"/g, '\\"'); | ||
| } | ||
| function getCodexMcpTomlSection(apiKey) { | ||
| return ` | ||
| [mcp_servers.01software] | ||
| url = "https://mcp.01.software/mcp" | ||
| [mcp_servers.01software.headers] | ||
| x-api-key = "${escapeTomlString(apiKey)}" | ||
| `; | ||
| } | ||
| var CODEX_MCP_SECTION_MARKER = "[mcp_servers.01software]"; | ||
| export { | ||
| getClientTemplate, | ||
| getQueryProviderTemplate, | ||
| getServerTemplate, | ||
| getEnvContent, | ||
| getMcpServerEntry, | ||
| getMcpConfigTemplate, | ||
| getCodexMcpTomlSection, | ||
| CODEX_MCP_SECTION_MARKER | ||
| }; | ||
| //# sourceMappingURL=chunk-OEAQV63E.js.map |
| {"version":3,"sources":["../src/templates.ts"],"sourcesContent":["import type { ProjectEnv } from './detect'\n\n// ── Client template (browser) ────────────────────────────────────────\n\nexport function getClientTemplate(env: ProjectEnv, publishableKeyEnvVar: string): string {\n if (env === 'nextjs') {\n return `import { createClient } from '@01.software/sdk'\n\nexport const client = createClient({\n publishableKey: process.env.${publishableKeyEnvVar}!,\n})\n`\n }\n\n if (env === 'react-cra') {\n return `import { createClient } from '@01.software/sdk'\n\nexport const client = createClient({\n publishableKey: process.env.${publishableKeyEnvVar}!,\n})\n`\n }\n\n if (env === 'vanilla') {\n return `import { createClient } from '@01.software/sdk'\n\n// Replace 'YOUR_PUBLISHABLE_KEY' with your actual publishable key from the 01.software console\nexport const client = createClient({\n publishableKey: 'YOUR_PUBLISHABLE_KEY',\n})\n`\n }\n\n // react-vite (import.meta.env)\n return `import { createClient } from '@01.software/sdk'\n\nexport const client = createClient({\n publishableKey: import.meta.env.${publishableKeyEnvVar},\n})\n`\n}\n\n// ── Query Provider template ──────────────────────────────────────────\n\nexport function getQueryProviderTemplate(env: ProjectEnv): string {\n const useClientDirective = env === 'nextjs' ? \"'use client'\\n\\n\" : ''\n\n return `${useClientDirective}import { QueryClientProvider } from '@tanstack/react-query'\nimport { client } from './client'\n\nexport function QueryProvider({ children }: { children: React.ReactNode }) {\n return (\n <QueryClientProvider client={client.queryClient}>\n {children}\n </QueryClientProvider>\n )\n}\n`\n}\n\n// ── Server template ──────────────────────────────────────────────────\n\nexport function getServerTemplate(\n env: ProjectEnv,\n publishableKeyEnvVar: string,\n secretKeyEnvVar: string,\n): string {\n if (env === 'edge') {\n return `import { createServerClient } from '@01.software/sdk'\n\n// Edge runtime: pass your env bindings here\n// e.g. Cloudflare Workers: use env.SOFTWARE_PUBLISHABLE_KEY from the handler context\n// e.g. Vercel Edge: use process.env.${publishableKeyEnvVar}\nexport function createEdgeClient(publishableKey: string, secretKey: string) {\n return createServerClient({ publishableKey, secretKey })\n}\n`\n }\n\n return `import { createServerClient } from '@01.software/sdk'\n\nexport const serverClient = createServerClient({\n publishableKey: process.env.${publishableKeyEnvVar}!,\n secretKey: process.env.${secretKeyEnvVar}!,\n})\n`\n}\n\n// ── Env file content ─────────────────────────────────────────────────\n\nexport function getEnvContent(\n publishableKey: string,\n secretKey: string,\n publishableKeyEnvVar: string,\n secretKeyEnvVar: string | null,\n): string {\n let content = `\\n# 01.software\\n${publishableKeyEnvVar}=${publishableKey}\\n`\n if (secretKeyEnvVar) {\n content += `${secretKeyEnvVar}=${secretKey}\\n`\n }\n return content\n}\n\n// ── MCP config (JSON) ────────────────────────────────────────────────\n\nexport function getMcpServerEntry(apiKey: string) {\n return {\n type: 'http' as const,\n url: 'https://mcp.01.software/mcp',\n headers: { 'x-api-key': apiKey },\n }\n}\n\nexport function getMcpConfigTemplate(apiKey: string): string {\n return (\n JSON.stringify(\n { mcpServers: { '01software': getMcpServerEntry(apiKey) } },\n null,\n 2,\n ) + '\\n'\n )\n}\n\n// ── MCP config (TOML — Codex CLI) ────────────────────────────────────\n\nfunction escapeTomlString(s: string): string {\n return s.replace(/\\\\/g, '\\\\\\\\').replace(/\"/g, '\\\\\"')\n}\n\n/** Codex CLI `[mcp_servers.01software]` block. Idempotent marker: the header\n * line. Intended to be appended to `~/.codex/config.toml`. */\nexport function getCodexMcpTomlSection(apiKey: string): string {\n return `\n[mcp_servers.01software]\nurl = \"https://mcp.01.software/mcp\"\n\n[mcp_servers.01software.headers]\nx-api-key = \"${escapeTomlString(apiKey)}\"\n`\n}\n\nexport const CODEX_MCP_SECTION_MARKER = '[mcp_servers.01software]'\n"],"mappings":";;;AAIO,SAAS,kBAAkB,KAAiB,sBAAsC;AACvF,MAAI,QAAQ,UAAU;AACpB,WAAO;AAAA;AAAA;AAAA,gCAGqB,oBAAoB;AAAA;AAAA;AAAA,EAGlD;AAEA,MAAI,QAAQ,aAAa;AACvB,WAAO;AAAA;AAAA;AAAA,gCAGqB,oBAAoB;AAAA;AAAA;AAAA,EAGlD;AAEA,MAAI,QAAQ,WAAW;AACrB,WAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOT;AAGA,SAAO;AAAA;AAAA;AAAA,oCAG2B,oBAAoB;AAAA;AAAA;AAGxD;AAIO,SAAS,yBAAyB,KAAyB;AAChE,QAAM,qBAAqB,QAAQ,WAAW,qBAAqB;AAEnE,SAAO,GAAG,kBAAkB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAW9B;AAIO,SAAS,kBACd,KACA,sBACA,iBACQ;AACR,MAAI,QAAQ,QAAQ;AAClB,WAAO;AAAA;AAAA;AAAA;AAAA,8CAImC,oBAAoB;AAAA;AAAA;AAAA;AAAA;AAAA,EAKhE;AAEA,SAAO;AAAA;AAAA;AAAA,gCAGuB,oBAAoB;AAAA,2BACzB,eAAe;AAAA;AAAA;AAG1C;AAIO,SAAS,cACd,gBACA,WACA,sBACA,iBACQ;AACR,MAAI,UAAU;AAAA;AAAA,EAAoB,oBAAoB,IAAI,cAAc;AAAA;AACxE,MAAI,iBAAiB;AACnB,eAAW,GAAG,eAAe,IAAI,SAAS;AAAA;AAAA,EAC5C;AACA,SAAO;AACT;AAIO,SAAS,kBAAkB,QAAgB;AAChD,SAAO;AAAA,IACL,MAAM;AAAA,IACN,KAAK;AAAA,IACL,SAAS,EAAE,aAAa,OAAO;AAAA,EACjC;AACF;AAEO,SAAS,qBAAqB,QAAwB;AAC3D,SACE,KAAK;AAAA,IACH,EAAE,YAAY,EAAE,cAAc,kBAAkB,MAAM,EAAE,EAAE;AAAA,IAC1D;AAAA,IACA;AAAA,EACF,IAAI;AAER;AAIA,SAAS,iBAAiB,GAAmB;AAC3C,SAAO,EAAE,QAAQ,OAAO,MAAM,EAAE,QAAQ,MAAM,KAAK;AACrD;AAIO,SAAS,uBAAuB,QAAwB;AAC7D,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA,eAKM,iBAAiB,MAAM,CAAC;AAAA;AAEvC;AAEO,IAAM,2BAA2B;","names":[]} |
| #!/usr/bin/env node | ||
| // src/ai-docs.ts | ||
| function normalizeActiveCollections(collections) { | ||
| if (Array.isArray(collections?.active)) return collections.active; | ||
| return []; | ||
| } | ||
| function generateClaudeMd(ctx) { | ||
| const featuresSection = ctx.features && ctx.features.length > 0 ? ctx.features.map((f) => `- ${f}`).join("\n") : "- See console"; | ||
| const collectionsSection = ctx.collections && ctx.collections.length > 0 ? ctx.collections.join(", ") : "Run `01 schema list`"; | ||
| return `# 01.software SDK \u2014 ${ctx.tenantName} | ||
| ## Connection | ||
| - Publishable Key: \`NEXT_PUBLIC_SOFTWARE_PUBLISHABLE_KEY\` (env) | ||
| - Secret Key: \`SOFTWARE_SECRET_KEY\` (env) | ||
| - MCP: \`.mcp.json\` | ||
| ## Active Features | ||
| ${featuresSection} | ||
| ## Active Collections | ||
| ${collectionsSection} | ||
| ## MCP Quick Reference | ||
| | Tool | Use | | ||
| |------|-----| | ||
| | \`query-collection\` | List/filter documents | | ||
| | \`create-collection\` | Create documents | | ||
| | \`update-field-config\` | Hide unused fields | | ||
| | \`get-tenant-context\` | Show active features & collections | | ||
| ## CLI | ||
| - \`01 query <collection>\` \u2014 query data | ||
| - \`01 schema show <collection>\` \u2014 inspect fields | ||
| - \`01 schema list\` \u2014 list all collections | ||
| ## Initial Setup | ||
| Run \`/01software-field-config\` in Claude Code to configure field visibility for your use case. | ||
| Use \`get-collection-schema\` or \`01 schema show <collection>\` for live field introspection instead of relying on this document as a schema snapshot. | ||
| `; | ||
| } | ||
| function getSkillFiles() { | ||
| return [ | ||
| { | ||
| dirName: "01software-field-config", | ||
| content: `--- | ||
| name: 01software-field-config | ||
| description: Configure field visibility for this tenant \u2014 hide unused collections and fields via MCP | ||
| disable-model-invocation: true | ||
| --- | ||
| Steps: | ||
| 1. Use \`list-configurable-fields\` to see current visibility settings | ||
| 2. Identify fields/collections not needed for your use case | ||
| 3. Use \`update-field-config\` to hide them | ||
| Common setups: | ||
| - Blog only: hide \`ecommerce\`, \`customers\`, \`videos\` collections | ||
| - Store: hide \`posts\`, \`documents\`, \`galleries\`, \`canvas\` collections | ||
| - Minimal: hide all except the collections you actively use | ||
| Ask me: "Show current field config" or "Hide ecommerce fields" | ||
| ` | ||
| }, | ||
| { | ||
| dirName: "01software-query", | ||
| content: `--- | ||
| name: 01software-query | ||
| description: Query 01.software collections via MCP or CLI with filter, sort, and pagination examples | ||
| --- | ||
| Query collections using the MCP \`query-collection\` tool or CLI. | ||
| MCP examples: | ||
| - List products: \`query-collection\` with collection="products", limit=10 | ||
| - Filter by status: add where={"status":{"equals":"published"}} | ||
| - Sort by date: sort="-createdAt" | ||
| - Paginate: page=2, limit=20 | ||
| CLI examples: | ||
| - \`01 query products --limit 10\` | ||
| - \`01 query orders --where '{"status":{"equals":"paid"}}'\` | ||
| - \`01 schema show products\` \u2014 inspect available fields | ||
| SDK (server): | ||
| \`\`\`typescript | ||
| const { docs } = await serverClient.collection('products').find({ | ||
| where: { status: { equals: 'published' } }, | ||
| sort: '-createdAt', | ||
| limit: 10, | ||
| }) | ||
| \`\`\` | ||
| ` | ||
| }, | ||
| { | ||
| dirName: "01software-order-flow", | ||
| content: `--- | ||
| name: 01software-order-flow | ||
| description: Order lifecycle reference \u2014 create, pay, fulfill, and return flows for 01.software | ||
| --- | ||
| Complete order flow from creation to fulfillment. | ||
| States: pending \u2192 paid \u2192 preparing \u2192 shipped \u2192 delivered \u2192 confirmed | ||
| 1. Create order: \`create-order\` with orderNumber, customerSnapshot, orderProducts, totalAmount | ||
| 2. Mark paid: \`update-order\` with status="paid" (after payment gateway confirms) | ||
| 3. Fulfill: \`create-fulfillment\` with items and carrier/trackingNumber | ||
| 4. Returns: \`create-return\` or \`return-with-refund\` (atomic) | ||
| Free orders: omit paymentId, totalAmount=0 \u2192 auto-transitions to paid | ||
| CLI: \`01 order create --help\` for full options | ||
| ` | ||
| }, | ||
| { | ||
| dirName: "01software-schema", | ||
| content: `--- | ||
| name: 01software-schema | ||
| description: Inspect 01.software collection schemas and available fields via MCP or CLI | ||
| --- | ||
| Inspect collection schemas to understand available fields. | ||
| MCP: use \`get-collection-schema\` with collection | ||
| CLI: | ||
| - \`01 schema list\` \u2014 all available collections | ||
| - \`01 schema show <collection>\` \u2014 field names, types, required status | ||
| Common collections: products, orders, customers, posts, documents, images | ||
| Use \`get-tenant-context\` to see which collections are active for this tenant. | ||
| ` | ||
| } | ||
| ]; | ||
| } | ||
| async function fetchTenantContext(publishableKey, secretKey) { | ||
| try { | ||
| const apiUrl = process.env.SOFTWARE_API_URL || "https://api.01.software"; | ||
| const res = await fetch(`${apiUrl}/api/tenants/context`, { | ||
| headers: { | ||
| "X-Publishable-Key": publishableKey, | ||
| Authorization: `Bearer ${secretKey}` | ||
| } | ||
| }); | ||
| if (!res.ok) return null; | ||
| const data = await res.json(); | ||
| return { | ||
| tenantName: data.tenant?.name || "", | ||
| features: data.features || [], | ||
| collections: normalizeActiveCollections(data.collections) | ||
| }; | ||
| } catch { | ||
| return null; | ||
| } | ||
| } | ||
| export { | ||
| generateClaudeMd, | ||
| getSkillFiles, | ||
| fetchTenantContext | ||
| }; | ||
| //# sourceMappingURL=chunk-SRLZ5OIV.js.map |
| {"version":3,"sources":["../src/ai-docs.ts"],"sourcesContent":["export interface TenantContext {\n tenantName: string\n features?: string[]\n collections?: string[]\n}\n\ninterface TenantContextApiResponse {\n tenant?: { name?: string }\n features?: string[]\n collections?: { active?: string[]; inactive?: string[] }\n}\n\nfunction normalizeActiveCollections(\n collections: TenantContextApiResponse['collections'],\n): string[] {\n if (Array.isArray(collections?.active)) return collections.active\n return []\n}\n\n// ── CLAUDE.md ────────────────────────────────────────────────────────\n\nexport function generateClaudeMd(ctx: TenantContext): string {\n const featuresSection =\n ctx.features && ctx.features.length > 0\n ? ctx.features.map((f) => `- ${f}`).join('\\n')\n : '- See console'\n\n const collectionsSection =\n ctx.collections && ctx.collections.length > 0\n ? ctx.collections.join(', ')\n : 'Run `01 schema list`'\n\n return `# 01.software SDK — ${ctx.tenantName}\n\n## Connection\n- Publishable Key: \\`NEXT_PUBLIC_SOFTWARE_PUBLISHABLE_KEY\\` (env)\n- Secret Key: \\`SOFTWARE_SECRET_KEY\\` (env)\n- MCP: \\`.mcp.json\\`\n\n## Active Features\n${featuresSection}\n\n## Active Collections\n${collectionsSection}\n\n## MCP Quick Reference\n| Tool | Use |\n|------|-----|\n| \\`query-collection\\` | List/filter documents |\n| \\`create-collection\\` | Create documents |\n| \\`update-field-config\\` | Hide unused fields |\n| \\`get-tenant-context\\` | Show active features & collections |\n\n## CLI\n- \\`01 query <collection>\\` — query data\n- \\`01 schema show <collection>\\` — inspect fields\n- \\`01 schema list\\` — list all collections\n\n## Initial Setup\nRun \\`/01software-field-config\\` in Claude Code to configure field visibility for your use case.\nUse \\`get-collection-schema\\` or \\`01 schema show <collection>\\` for live field introspection instead of relying on this document as a schema snapshot.\n`\n}\n\n// ── Skill files ──────────────────────────────────────────────────────\n\nexport function getSkillFiles(): Array<{ dirName: string; content: string }> {\n return [\n {\n dirName: '01software-field-config',\n content: `---\nname: 01software-field-config\ndescription: Configure field visibility for this tenant — hide unused collections and fields via MCP\ndisable-model-invocation: true\n---\n\nSteps:\n1. Use \\`list-configurable-fields\\` to see current visibility settings\n2. Identify fields/collections not needed for your use case\n3. Use \\`update-field-config\\` to hide them\n\nCommon setups:\n- Blog only: hide \\`ecommerce\\`, \\`customers\\`, \\`videos\\` collections\n- Store: hide \\`posts\\`, \\`documents\\`, \\`galleries\\`, \\`canvas\\` collections\n- Minimal: hide all except the collections you actively use\n\nAsk me: \"Show current field config\" or \"Hide ecommerce fields\"\n`,\n },\n {\n dirName: '01software-query',\n content: `---\nname: 01software-query\ndescription: Query 01.software collections via MCP or CLI with filter, sort, and pagination examples\n---\n\nQuery collections using the MCP \\`query-collection\\` tool or CLI.\n\nMCP examples:\n- List products: \\`query-collection\\` with collection=\"products\", limit=10\n- Filter by status: add where={\"status\":{\"equals\":\"published\"}}\n- Sort by date: sort=\"-createdAt\"\n- Paginate: page=2, limit=20\n\nCLI examples:\n- \\`01 query products --limit 10\\`\n- \\`01 query orders --where '{\"status\":{\"equals\":\"paid\"}}'\\`\n- \\`01 schema show products\\` — inspect available fields\n\nSDK (server):\n\\`\\`\\`typescript\nconst { docs } = await serverClient.collection('products').find({\n where: { status: { equals: 'published' } },\n sort: '-createdAt',\n limit: 10,\n})\n\\`\\`\\`\n`,\n },\n {\n dirName: '01software-order-flow',\n content: `---\nname: 01software-order-flow\ndescription: Order lifecycle reference — create, pay, fulfill, and return flows for 01.software\n---\n\nComplete order flow from creation to fulfillment.\n\nStates: pending → paid → preparing → shipped → delivered → confirmed\n\n1. Create order: \\`create-order\\` with orderNumber, customerSnapshot, orderProducts, totalAmount\n2. Mark paid: \\`update-order\\` with status=\"paid\" (after payment gateway confirms)\n3. Fulfill: \\`create-fulfillment\\` with items and carrier/trackingNumber\n4. Returns: \\`create-return\\` or \\`return-with-refund\\` (atomic)\n\nFree orders: omit paymentId, totalAmount=0 → auto-transitions to paid\n\nCLI: \\`01 order create --help\\` for full options\n`,\n },\n {\n dirName: '01software-schema',\n content: `---\nname: 01software-schema\ndescription: Inspect 01.software collection schemas and available fields via MCP or CLI\n---\n\nInspect collection schemas to understand available fields.\n\nMCP: use \\`get-collection-schema\\` with collection\n\nCLI:\n- \\`01 schema list\\` — all available collections\n- \\`01 schema show <collection>\\` — field names, types, required status\n\nCommon collections: products, orders, customers, posts, documents, images\nUse \\`get-tenant-context\\` to see which collections are active for this tenant.\n`,\n },\n ]\n}\n\n// ── Tenant context fetch ─────────────────────────────────────────────\n\nexport async function fetchTenantContext(\n publishableKey: string,\n secretKey: string,\n): Promise<TenantContext | null> {\n try {\n const apiUrl = process.env.SOFTWARE_API_URL || 'https://api.01.software'\n // secretKey is now an opaque sk01_/pat01_ bearer token — send it directly.\n const res = await fetch(`${apiUrl}/api/tenants/context`, {\n headers: {\n 'X-Publishable-Key': publishableKey,\n Authorization: `Bearer ${secretKey}`,\n },\n })\n if (!res.ok) return null\n const data = (await res.json()) as TenantContextApiResponse\n return {\n tenantName: data.tenant?.name || '',\n features: data.features || [],\n collections: normalizeActiveCollections(data.collections),\n }\n } catch {\n return null\n }\n}\n"],"mappings":";;;AAYA,SAAS,2BACP,aACU;AACV,MAAI,MAAM,QAAQ,aAAa,MAAM,EAAG,QAAO,YAAY;AAC3D,SAAO,CAAC;AACV;AAIO,SAAS,iBAAiB,KAA4B;AAC3D,QAAM,kBACJ,IAAI,YAAY,IAAI,SAAS,SAAS,IAClC,IAAI,SAAS,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,EAAE,KAAK,IAAI,IAC3C;AAEN,QAAM,qBACJ,IAAI,eAAe,IAAI,YAAY,SAAS,IACxC,IAAI,YAAY,KAAK,IAAI,IACzB;AAEN,SAAO,4BAAuB,IAAI,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQ5C,eAAe;AAAA;AAAA;AAAA,EAGf,kBAAkB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAmBpB;AAIO,SAAS,gBAA6D;AAC3E,SAAO;AAAA,IACL;AAAA,MACE,SAAS;AAAA,MACT,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAkBX;AAAA,IACA;AAAA,MACE,SAAS;AAAA,MACT,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IA2BX;AAAA,IACA;AAAA,MACE,SAAS;AAAA,MACT,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAkBX;AAAA,IACA;AAAA,MACE,SAAS;AAAA,MACT,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAgBX;AAAA,EACF;AACF;AAIA,eAAsB,mBACpB,gBACA,WAC+B;AAC/B,MAAI;AACF,UAAM,SAAS,QAAQ,IAAI,oBAAoB;AAE/C,UAAM,MAAM,MAAM,MAAM,GAAG,MAAM,wBAAwB;AAAA,MACvD,SAAS;AAAA,QACP,qBAAqB;AAAA,QACrB,eAAe,UAAU,SAAS;AAAA,MACpC;AAAA,IACF,CAAC;AACD,QAAI,CAAC,IAAI,GAAI,QAAO;AACpB,UAAM,OAAQ,MAAM,IAAI,KAAK;AAC7B,WAAO;AAAA,MACL,YAAY,KAAK,QAAQ,QAAQ;AAAA,MACjC,UAAU,KAAK,YAAY,CAAC;AAAA,MAC5B,aAAa,2BAA2B,KAAK,WAAW;AAAA,IAC1D;AAAA,EACF,QAAQ;AACN,WAAO;AAAA,EACT;AACF;","names":[]} |
| #!/usr/bin/env node | ||
| // src/file-ops.ts | ||
| var envLineRegexCache = /* @__PURE__ */ new Map(); | ||
| function envLineRegex(name) { | ||
| let re = envLineRegexCache.get(name); | ||
| if (!re) { | ||
| const escaped = name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); | ||
| re = new RegExp(`^${escaped}=(.*)$`, "m"); | ||
| envLineRegexCache.set(name, re); | ||
| } | ||
| return re; | ||
| } | ||
| function readEnvValue(content, name) { | ||
| const m = content.match(envLineRegex(name)); | ||
| return m ? m[1] : null; | ||
| } | ||
| function setEnvValue(content, name, value) { | ||
| const re = envLineRegex(name); | ||
| if (re.test(content)) return content.replace(re, `${name}=${value}`); | ||
| const sep = content.length === 0 || content.endsWith("\n") ? "" : "\n"; | ||
| return content + sep + `${name}=${value} | ||
| `; | ||
| } | ||
| var TOML_SECTION_PREFIX = "[mcp_servers.01software"; | ||
| function extractTomlApiKey(content) { | ||
| const headerIdx = content.indexOf(`${TOML_SECTION_PREFIX}.headers]`); | ||
| if (headerIdx < 0) return null; | ||
| const slice = content.slice(headerIdx); | ||
| const m = slice.match(/^x-api-key\s*=\s*"((?:[^"\\]|\\.)*)"/m); | ||
| if (!m) return null; | ||
| return m[1].replace(/\\"/g, '"').replace(/\\\\/g, "\\"); | ||
| } | ||
| function replaceTomlMcpSection(content, newSection) { | ||
| const lines = content.split("\n"); | ||
| const kept = []; | ||
| let inOurSection = false; | ||
| for (const line of lines) { | ||
| const trimmed = line.trim(); | ||
| if (trimmed.startsWith(TOML_SECTION_PREFIX)) { | ||
| inOurSection = true; | ||
| continue; | ||
| } | ||
| if (inOurSection && /^\[[^\[]/.test(trimmed)) { | ||
| inOurSection = false; | ||
| } | ||
| if (!inOurSection) kept.push(line); | ||
| } | ||
| let result = kept.join("\n").replace(/\n+$/, ""); | ||
| if (result.length > 0) result += "\n"; | ||
| result += newSection.startsWith("\n") ? newSection : "\n" + newSection; | ||
| return result; | ||
| } | ||
| export { | ||
| readEnvValue, | ||
| setEnvValue, | ||
| extractTomlApiKey, | ||
| replaceTomlMcpSection | ||
| }; | ||
| //# sourceMappingURL=chunk-VOEXMD2S.js.map |
| {"version":3,"sources":["../src/file-ops.ts"],"sourcesContent":["// Pure helpers for in-place file content manipulation. Kept side-effect-free\n// so they can be unit-tested without touching the filesystem or prompts.\n\n// ── .env merge ───────────────────────────────────────────────────────\n\nconst envLineRegexCache = new Map<string, RegExp>()\n\nfunction envLineRegex(name: string): RegExp {\n let re = envLineRegexCache.get(name)\n if (!re) {\n const escaped = name.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\n re = new RegExp(`^${escaped}=(.*)$`, 'm')\n envLineRegexCache.set(name, re)\n }\n return re\n}\n\nexport function readEnvValue(content: string, name: string): string | null {\n const m = content.match(envLineRegex(name))\n return m ? m[1] : null\n}\n\nexport function setEnvValue(content: string, name: string, value: string): string {\n const re = envLineRegex(name)\n if (re.test(content)) return content.replace(re, `${name}=${value}`)\n const sep = content.length === 0 || content.endsWith('\\n') ? '' : '\\n'\n return content + sep + `${name}=${value}\\n`\n}\n\n// ── Codex TOML manipulation ──────────────────────────────────────────\n\nconst TOML_SECTION_PREFIX = '[mcp_servers.01software'\n\n/** Pulls the current `x-api-key` value from `[mcp_servers.01software.headers]`.\n * Returns null if the section or key is absent. */\nexport function extractTomlApiKey(content: string): string | null {\n const headerIdx = content.indexOf(`${TOML_SECTION_PREFIX}.headers]`)\n if (headerIdx < 0) return null\n const slice = content.slice(headerIdx)\n const m = slice.match(/^x-api-key\\s*=\\s*\"((?:[^\"\\\\]|\\\\.)*)\"/m)\n if (!m) return null\n // Reverse the same escaping used when writing (escape backslash + quote)\n return m[1].replace(/\\\\\"/g, '\"').replace(/\\\\\\\\/g, '\\\\')\n}\n\n/** Removes the existing `[mcp_servers.01software]` block (and its `.headers`\n * sub-block) from a TOML document, then appends the provided section. */\nexport function replaceTomlMcpSection(content: string, newSection: string): string {\n const lines = content.split('\\n')\n const kept: string[] = []\n let inOurSection = false\n for (const line of lines) {\n const trimmed = line.trim()\n if (trimmed.startsWith(TOML_SECTION_PREFIX)) {\n inOurSection = true\n continue\n }\n // Any other top-level header ends our section\n if (inOurSection && /^\\[[^\\[]/.test(trimmed)) {\n inOurSection = false\n }\n if (!inOurSection) kept.push(line)\n }\n let result = kept.join('\\n').replace(/\\n+$/, '')\n if (result.length > 0) result += '\\n'\n // newSection already starts with a blank line; keep it that way\n result += newSection.startsWith('\\n') ? newSection : '\\n' + newSection\n return result\n}\n"],"mappings":";;;AAKA,IAAM,oBAAoB,oBAAI,IAAoB;AAElD,SAAS,aAAa,MAAsB;AAC1C,MAAI,KAAK,kBAAkB,IAAI,IAAI;AACnC,MAAI,CAAC,IAAI;AACP,UAAM,UAAU,KAAK,QAAQ,uBAAuB,MAAM;AAC1D,SAAK,IAAI,OAAO,IAAI,OAAO,UAAU,GAAG;AACxC,sBAAkB,IAAI,MAAM,EAAE;AAAA,EAChC;AACA,SAAO;AACT;AAEO,SAAS,aAAa,SAAiB,MAA6B;AACzE,QAAM,IAAI,QAAQ,MAAM,aAAa,IAAI,CAAC;AAC1C,SAAO,IAAI,EAAE,CAAC,IAAI;AACpB;AAEO,SAAS,YAAY,SAAiB,MAAc,OAAuB;AAChF,QAAM,KAAK,aAAa,IAAI;AAC5B,MAAI,GAAG,KAAK,OAAO,EAAG,QAAO,QAAQ,QAAQ,IAAI,GAAG,IAAI,IAAI,KAAK,EAAE;AACnE,QAAM,MAAM,QAAQ,WAAW,KAAK,QAAQ,SAAS,IAAI,IAAI,KAAK;AAClE,SAAO,UAAU,MAAM,GAAG,IAAI,IAAI,KAAK;AAAA;AACzC;AAIA,IAAM,sBAAsB;AAIrB,SAAS,kBAAkB,SAAgC;AAChE,QAAM,YAAY,QAAQ,QAAQ,GAAG,mBAAmB,WAAW;AACnE,MAAI,YAAY,EAAG,QAAO;AAC1B,QAAM,QAAQ,QAAQ,MAAM,SAAS;AACrC,QAAM,IAAI,MAAM,MAAM,uCAAuC;AAC7D,MAAI,CAAC,EAAG,QAAO;AAEf,SAAO,EAAE,CAAC,EAAE,QAAQ,QAAQ,GAAG,EAAE,QAAQ,SAAS,IAAI;AACxD;AAIO,SAAS,sBAAsB,SAAiB,YAA4B;AACjF,QAAM,QAAQ,QAAQ,MAAM,IAAI;AAChC,QAAM,OAAiB,CAAC;AACxB,MAAI,eAAe;AACnB,aAAW,QAAQ,OAAO;AACxB,UAAM,UAAU,KAAK,KAAK;AAC1B,QAAI,QAAQ,WAAW,mBAAmB,GAAG;AAC3C,qBAAe;AACf;AAAA,IACF;AAEA,QAAI,gBAAgB,WAAW,KAAK,OAAO,GAAG;AAC5C,qBAAe;AAAA,IACjB;AACA,QAAI,CAAC,aAAc,MAAK,KAAK,IAAI;AAAA,EACnC;AACA,MAAI,SAAS,KAAK,KAAK,IAAI,EAAE,QAAQ,QAAQ,EAAE;AAC/C,MAAI,OAAO,SAAS,EAAG,WAAU;AAEjC,YAAU,WAAW,WAAW,IAAI,IAAI,aAAa,OAAO;AAC5D,SAAO;AACT;","names":[]} |
Sorry, the diff of this file is too big to display
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
146740
2.01%1481
2.28%3
50%+ Added