@01.software/init 0.9.1 → 0.10.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 (119) hide show
  1. package/dist/ai-docs.d.ts +13 -0
  2. package/dist/ai-docs.js +1 -1
  3. package/dist/browser-auth-CJDrpp5T.d.ts +11 -0
  4. package/dist/{chunk-UA7WNT2F.js → chunk-4LHYICUL.js} +1 -1
  5. package/dist/chunk-4LHYICUL.js.map +1 -0
  6. package/dist/{chunk-TBGKXE3Q.js → chunk-NJ4X7VNK.js} +5 -5
  7. package/dist/chunk-NJ4X7VNK.js.map +1 -0
  8. package/dist/{chunk-5K2CB2Y5.js → chunk-Q6MSORYN.js} +14 -37
  9. package/dist/chunk-Q6MSORYN.js.map +1 -0
  10. package/dist/chunk-STM4DKVZ.js +183 -0
  11. package/dist/chunk-STM4DKVZ.js.map +1 -0
  12. package/dist/{chunk-2IGKOSK7.js → chunk-WDWJ73KP.js} +41 -215
  13. package/dist/chunk-WDWJ73KP.js.map +1 -0
  14. package/dist/create-app-templates/ecommerce/AGENTS.md +88 -0
  15. package/dist/create-app-templates/ecommerce/CHANGELOG.md +30 -0
  16. package/dist/create-app-templates/ecommerce/CLAUDE.md +1 -0
  17. package/dist/create-app-templates/ecommerce/README.md +139 -0
  18. package/dist/create-app-templates/ecommerce/app/api/auth/login/route.ts +30 -0
  19. package/dist/create-app-templates/ecommerce/app/api/auth/logout/route.ts +18 -0
  20. package/dist/create-app-templates/ecommerce/app/api/auth/register/route.ts +41 -0
  21. package/dist/create-app-templates/ecommerce/app/api/cart/clear/route.ts +12 -0
  22. package/dist/create-app-templates/ecommerce/app/api/cart/items/route.ts +45 -0
  23. package/dist/create-app-templates/ecommerce/app/api/cart/route.ts +14 -0
  24. package/dist/create-app-templates/ecommerce/app/api/checkout/payment-return/route.ts +86 -0
  25. package/dist/create-app-templates/ecommerce/app/api/checkout/reconcile/route.ts +50 -0
  26. package/dist/create-app-templates/ecommerce/app/api/checkout/route.ts +41 -0
  27. package/dist/create-app-templates/ecommerce/app/cart/page.tsx +10 -0
  28. package/dist/create-app-templates/ecommerce/app/checkout/page.tsx +10 -0
  29. package/dist/create-app-templates/ecommerce/app/checkout/success/page.tsx +34 -0
  30. package/dist/create-app-templates/ecommerce/app/favicon.ico +0 -0
  31. package/dist/create-app-templates/ecommerce/app/globals.css +67 -0
  32. package/dist/create-app-templates/ecommerce/app/layout.tsx +23 -0
  33. package/dist/create-app-templates/ecommerce/app/login/page.tsx +11 -0
  34. package/dist/create-app-templates/ecommerce/app/page.tsx +5 -0
  35. package/dist/create-app-templates/ecommerce/app/products/[slug]/page.tsx +46 -0
  36. package/dist/create-app-templates/ecommerce/app/products/page.tsx +45 -0
  37. package/dist/create-app-templates/ecommerce/app/register/page.tsx +11 -0
  38. package/dist/create-app-templates/ecommerce/app/webhook/payment/route.ts +20 -0
  39. package/dist/create-app-templates/ecommerce/app-config.ts +54 -0
  40. package/dist/create-app-templates/ecommerce/components/auth/auth-form.tsx +109 -0
  41. package/dist/create-app-templates/ecommerce/components/cart/cart-content.tsx +119 -0
  42. package/dist/create-app-templates/ecommerce/components/checkout/checkout-form.tsx +267 -0
  43. package/dist/create-app-templates/ecommerce/components/checkout/checkout-reconcile.tsx +78 -0
  44. package/dist/create-app-templates/ecommerce/components/layout/account-nav.tsx +48 -0
  45. package/dist/create-app-templates/ecommerce/components/layout/account-slot.tsx +12 -0
  46. package/dist/create-app-templates/ecommerce/components/layout/cart-link.tsx +13 -0
  47. package/dist/create-app-templates/ecommerce/components/layout/page-shell.tsx +11 -0
  48. package/dist/create-app-templates/ecommerce/components/layout/site-header.tsx +22 -0
  49. package/dist/create-app-templates/ecommerce/components/product/add-to-cart.tsx +116 -0
  50. package/dist/create-app-templates/ecommerce/components/product/product-card.tsx +50 -0
  51. package/dist/create-app-templates/ecommerce/components/product/product-gallery.tsx +39 -0
  52. package/dist/create-app-templates/ecommerce/data/mock-catalog.json +173 -0
  53. package/dist/create-app-templates/ecommerce/eslint.config.mjs +18 -0
  54. package/dist/create-app-templates/ecommerce/lib/cart/cookie.ts +40 -0
  55. package/dist/create-app-templates/ecommerce/lib/cart/normalize.ts +32 -0
  56. package/dist/create-app-templates/ecommerce/lib/cart/parse-cart-request.ts +56 -0
  57. package/dist/create-app-templates/ecommerce/lib/cart/route-helpers.ts +17 -0
  58. package/dist/create-app-templates/ecommerce/lib/cart/select-provider.ts +44 -0
  59. package/dist/create-app-templates/ecommerce/lib/cart/server-cart.ts +96 -0
  60. package/dist/create-app-templates/ecommerce/lib/cart/sync-on-login.server.ts +34 -0
  61. package/dist/create-app-templates/ecommerce/lib/cart/use-cart.tsx +151 -0
  62. package/dist/create-app-templates/ecommerce/lib/checkout/checkout-errors.ts +22 -0
  63. package/dist/create-app-templates/ecommerce/lib/checkout/checkout-provider.ts +28 -0
  64. package/dist/create-app-templates/ecommerce/lib/checkout/parse-checkout-payload.ts +76 -0
  65. package/dist/create-app-templates/ecommerce/lib/checkout/start-checkout.ts +63 -0
  66. package/dist/create-app-templates/ecommerce/lib/checkout/types.ts +3 -0
  67. package/dist/create-app-templates/ecommerce/lib/commerce/adapters/mock.ts +336 -0
  68. package/dist/create-app-templates/ecommerce/lib/commerce/adapters/software-mappers.ts +312 -0
  69. package/dist/create-app-templates/ecommerce/lib/commerce/adapters/software.ts +913 -0
  70. package/dist/create-app-templates/ecommerce/lib/commerce/product-summary.ts +37 -0
  71. package/dist/create-app-templates/ecommerce/lib/commerce/provider.server.ts +60 -0
  72. package/dist/create-app-templates/ecommerce/lib/commerce/provider.ts +96 -0
  73. package/dist/create-app-templates/ecommerce/lib/commerce/stock.ts +37 -0
  74. package/dist/create-app-templates/ecommerce/lib/commerce/types.ts +206 -0
  75. package/dist/create-app-templates/ecommerce/lib/commerce/variant-selection.ts +23 -0
  76. package/dist/create-app-templates/ecommerce/lib/customer/auth-actions.ts +131 -0
  77. package/dist/create-app-templates/ecommerce/lib/customer/cart-sync.ts +44 -0
  78. package/dist/create-app-templates/ecommerce/lib/customer/client.server.ts +109 -0
  79. package/dist/create-app-templates/ecommerce/lib/customer/current-customer.ts +15 -0
  80. package/dist/create-app-templates/ecommerce/lib/customer/route-guard.ts +58 -0
  81. package/dist/create-app-templates/ecommerce/lib/customer/route-helpers.ts +75 -0
  82. package/dist/create-app-templates/ecommerce/lib/customer/session.ts +108 -0
  83. package/dist/create-app-templates/ecommerce/lib/format.ts +7 -0
  84. package/dist/create-app-templates/ecommerce/lib/payment/adapters/mock.ts +84 -0
  85. package/dist/create-app-templates/ecommerce/lib/payment/adapters/portone.ts +254 -0
  86. package/dist/create-app-templates/ecommerce/lib/payment/adapters/tosspayments.ts +287 -0
  87. package/dist/create-app-templates/ecommerce/lib/payment/amount-gate.ts +86 -0
  88. package/dist/create-app-templates/ecommerce/lib/payment/provider.server.ts +51 -0
  89. package/dist/create-app-templates/ecommerce/lib/payment/provider.ts +18 -0
  90. package/dist/create-app-templates/ecommerce/lib/payment/sync-order-payment.ts +96 -0
  91. package/dist/create-app-templates/ecommerce/lib/payment/types.ts +71 -0
  92. package/dist/create-app-templates/ecommerce/lib/server-only-guard.ts +20 -0
  93. package/dist/create-app-templates/ecommerce/next-env.d.ts +6 -0
  94. package/dist/create-app-templates/ecommerce/next.config.ts +16 -0
  95. package/dist/create-app-templates/ecommerce/package.json +33 -0
  96. package/dist/create-app-templates/ecommerce/postcss.config.mjs +7 -0
  97. package/dist/create-app-templates/ecommerce/tests/customer-auth.test.ts +263 -0
  98. package/dist/create-app-templates/ecommerce/tests/customer-cart.test.ts +392 -0
  99. package/dist/create-app-templates/ecommerce/tests/domain.test.ts +1537 -0
  100. package/dist/create-app-templates/ecommerce/tsconfig.json +35 -0
  101. package/dist/create-app-templates/registry.json +66 -0
  102. package/dist/create-app.d.ts +40 -0
  103. package/dist/create-app.js +652 -0
  104. package/dist/create-app.js.map +1 -0
  105. package/dist/detect-Bjxp9wcS.d.ts +13 -0
  106. package/dist/file-ops.d.ts +21 -0
  107. package/dist/file-ops.js +1 -1
  108. package/dist/index.d.ts +2 -0
  109. package/dist/index.js +6 -5
  110. package/dist/index.js.map +1 -1
  111. package/dist/init.d.ts +40 -0
  112. package/dist/init.js +5 -4
  113. package/dist/templates.d.ts +27 -0
  114. package/dist/templates.js +1 -1
  115. package/package.json +31 -15
  116. package/dist/chunk-2IGKOSK7.js.map +0 -1
  117. package/dist/chunk-5K2CB2Y5.js.map +0 -1
  118. package/dist/chunk-TBGKXE3Q.js.map +0 -1
  119. package/dist/chunk-UA7WNT2F.js.map +0 -1
@@ -0,0 +1,13 @@
1
+ interface TenantContext {
2
+ tenantName: string;
3
+ features?: string[];
4
+ collections?: string[];
5
+ }
6
+ declare function generateClaudeMd(ctx: TenantContext): string;
7
+ declare function getSkillFiles(): Array<{
8
+ dirName: string;
9
+ content: string;
10
+ }>;
11
+ declare function fetchTenantContext(publishableKey: string, secretKey: string): Promise<TenantContext | null>;
12
+
13
+ export { type TenantContext, fetchTenantContext, generateClaudeMd, getSkillFiles };
package/dist/ai-docs.js CHANGED
@@ -3,7 +3,7 @@ import {
3
3
  fetchTenantContext,
4
4
  generateClaudeMd,
5
5
  getSkillFiles
6
- } from "./chunk-5K2CB2Y5.js";
6
+ } from "./chunk-Q6MSORYN.js";
7
7
  export {
8
8
  fetchTenantContext,
9
9
  generateClaudeMd,
@@ -0,0 +1,11 @@
1
+ declare function startBrowserAuth(options?: {
2
+ webUrl?: string;
3
+ tenantId?: string;
4
+ }): Promise<{
5
+ publishableKey: string;
6
+ secretKey: string;
7
+ tenantName: string;
8
+ tenantId?: string;
9
+ }>;
10
+
11
+ export { startBrowserAuth as s };
@@ -115,4 +115,4 @@ export {
115
115
  setEnvValue,
116
116
  replaceTomlMcpSection
117
117
  };
118
- //# sourceMappingURL=chunk-UA7WNT2F.js.map
118
+ //# sourceMappingURL=chunk-4LHYICUL.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/file-ops.ts"],"sourcesContent":["// Helpers for `.env*` / Codex TOML / secret-bearing global config writes.\n// The pure string utilities (`readEnvValue`, `setEnvValue`,\n// `replaceTomlMcpSection`) are unit-tested without filesystem access; the\n// secret-write helpers (`writeEnvFile`, `chmodSecretFile`,\n// `writeSecretGlobalConfig`) are tested against an isolated tmpdir.\n\nimport fs from 'node:fs'\nimport path from 'node:path'\nimport { randomBytes } from 'node:crypto'\n\n// ── Secret-bearing writes ────────────────────────────────────────────\n\n/** POSIX file mode for secret-bearing files (`.env*`, global auth config). */\nexport const SECRET_FILE_MODE = 0o600\n/** POSIX directory mode for parents of global auth-bearing config. */\nexport const SECRET_DIR_MODE = 0o700\n\nconst isWin32 = process.platform === 'win32'\n\nfunction writeFileWithSecretMode(target: string, content: string): void {\n fs.writeFileSync(target, content, isWin32 ? undefined : { mode: SECRET_FILE_MODE })\n}\n\nfunction mkdirWithSecretMode(dir: string): void {\n fs.mkdirSync(\n dir,\n isWin32 ? { recursive: true } : { recursive: true, mode: SECRET_DIR_MODE },\n )\n}\n\n/** Write a `.env*` file with mode `0o600` on POSIX. Skips mode on Windows\n * (secret-handling caveats in `packages/init/AGENTS.md`). */\nexport function writeEnvFile(target: string, content: string): void {\n writeFileWithSecretMode(target, content)\n}\n\n/** Apply secret-file mode to an existing file (no-op on Windows). Useful\n * after merging an `.env*` file that already existed. */\nexport function chmodSecretFile(target: string): void {\n if (isWin32) return\n try {\n fs.chmodSync(target, SECRET_FILE_MODE)\n } catch {\n // Mode tightening is best-effort; the spec waives the requirement on\n // platforms that reject the chmod.\n }\n}\n\n/** Write a global auth-bearing config (e.g. `~/.codex/config.toml`) using\n * symlink-safe atomic rename. Throws when the existing target is a symlink,\n * has multiple hard links, or is not a regular file. */\nexport function writeSecretGlobalConfig(target: string, content: string): void {\n mkdirWithSecretMode(path.dirname(target))\n\n // Reject symlinks / non-regular files / multi-hardlink targets before any write.\n if (fs.existsSync(target)) {\n const stat = fs.lstatSync(target)\n if (stat.isSymbolicLink()) {\n throw new Error(\n `Refusing to write secret-bearing config through a symlink: ${target}. ` +\n `Remove the symlink and re-run init.`,\n )\n }\n if (!stat.isFile()) {\n throw new Error(\n `Refusing to write secret-bearing config to non-regular file: ${target}.`,\n )\n }\n if (!isWin32 && stat.nlink > 1) {\n throw new Error(\n `Refusing to write secret-bearing config to file with multiple hard links: ${target}.`,\n )\n }\n }\n\n // Atomic temp-file replacement: write tmp with 0o600, then rename to target.\n const tmp = `${target}.${randomBytes(6).toString('hex')}.tmp`\n writeFileWithSecretMode(tmp, content)\n try {\n fs.renameSync(tmp, target)\n } catch (err) {\n // Best-effort cleanup of the tmp file if rename failed (e.g. EXDEV).\n try {\n fs.unlinkSync(tmp)\n } catch {\n // ignore\n }\n throw err\n }\n}\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":"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","names":[]}
@@ -56,11 +56,11 @@ export const analytics = createAnalytics({
56
56
  function getQueryProviderTemplate(env) {
57
57
  const useClientDirective = env === "nextjs" ? "'use client'\n\n" : "";
58
58
  return `${useClientDirective}import { QueryClientProvider } from '@tanstack/react-query'
59
- import { client } from './client'
59
+ import { getQueryClient } from '@01.software/sdk/query'
60
60
 
61
61
  export function QueryProvider({ children }: { children: React.ReactNode }) {
62
62
  return (
63
- <QueryClientProvider client={client.queryClient}>
63
+ <QueryClientProvider client={getQueryClient()}>
64
64
  {children}
65
65
  </QueryClientProvider>
66
66
  )
@@ -69,7 +69,7 @@ export function QueryProvider({ children }: { children: React.ReactNode }) {
69
69
  }
70
70
  function getServerTemplate(env, publishableKeyEnvVar, secretKeyEnvVar) {
71
71
  if (env === "edge") {
72
- return `import { createServerClient } from '@01.software/sdk'
72
+ return `import { createServerClient } from '@01.software/sdk/server'
73
73
 
74
74
  // Edge runtime: pass your env bindings here
75
75
  // e.g. Cloudflare Workers: use env.SOFTWARE_PUBLISHABLE_KEY from the handler context
@@ -79,7 +79,7 @@ export function createEdgeClient(publishableKey: string, secretKey: string) {
79
79
  }
80
80
  `;
81
81
  }
82
- return `import { createServerClient } from '@01.software/sdk'
82
+ return `import { createServerClient } from '@01.software/sdk/server'
83
83
 
84
84
  /**
85
85
  * Runtime guard: this module contains secret keys and must never be
@@ -180,4 +180,4 @@ export {
180
180
  getCodexMcpTomlSection,
181
181
  CODEX_MCP_SECTION_MARKER
182
182
  };
183
- //# sourceMappingURL=chunk-TBGKXE3Q.js.map
183
+ //# sourceMappingURL=chunk-NJ4X7VNK.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/templates.ts"],"sourcesContent":["import type { ProjectEnv } from './detect'\n\nconst MCP_RESOURCE_AUDIENCE = 'https://mcp.01.software/mcp'\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 { getQueryClient } from '@01.software/sdk/query'\n\nexport function QueryProvider({ children }: { children: React.ReactNode }) {\n return (\n <QueryClientProvider client={getQueryClient()}>\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/server'\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/server'\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' | 'vscode'\n\n/** VS Code's MCP config root key is `servers`; all other JSON clients use\n * `mcpServers` (MCP config caveats in `packages/init/AGENTS.md`). */\nexport function getMcpRootKey(client: McpJsonClient = 'generic'): 'servers' | 'mcpServers' {\n return client === 'vscode' ? 'servers' : 'mcpServers'\n}\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 const rootKey = getMcpRootKey(client)\n return (\n JSON.stringify(\n { [rootKey]: { '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":";;;AAEA,IAAM,wBAAwB;AAIvB,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;AAQO,SAAS,cAAc,SAAwB,WAAqC;AACzF,SAAO,WAAW,WAAW,YAAY;AAC3C;AAEO,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,QAAM,UAAU,cAAc,MAAM;AACpC,SACE,KAAK;AAAA,IACH,EAAE,CAAC,OAAO,GAAG,EAAE,cAAc,kBAAkB,MAAM,EAAE,EAAE;AAAA,IACzD;AAAA,IACA;AAAA,EACF,IAAI;AAER;AAMO,SAAS,yBAAiC;AAC/C,SAAO;AAAA;AAAA,SAEA,qBAAqB;AAAA;AAE9B;AAEO,IAAM,2BAA2B;","names":[]}
@@ -8,12 +8,7 @@ function normalizeActiveCollections(collections) {
8
8
  var HTTP_MCP_TOOLS = [
9
9
  "get-collection-schema",
10
10
  "get-tenant-context",
11
- "list-configurable-fields",
12
- "update-field-config",
13
- "sdk-get-recipe",
14
- "sdk-search-docs",
15
- "sdk-get-auth-setup",
16
- "sdk-get-collection-pattern"
11
+ "check-feature-progress"
17
12
  ];
18
13
  function generateClaudeMd(ctx) {
19
14
  const featuresSection = ctx.features && ctx.features.length > 0 ? ctx.features.map((f) => `- ${f}`).join("\n") : "- See console";
@@ -35,47 +30,28 @@ ${featuresSection}
35
30
  ${collectionsSection}
36
31
 
37
32
  ## Hosted HTTP MCP Quick Reference
38
- The hosted OAuth MCP server exposes these 8 tools:
33
+ The hosted OAuth MCP server exposes these ${HTTP_MCP_TOOLS.length} tools:
39
34
 
40
35
  ${HTTP_MCP_TOOLS.map((tool) => `- \`${tool}\``).join("\n")}
41
36
 
42
37
  Use \`get-collection-schema\` for live field introspection before generating code.
43
- Use SDK/server code or local CLI stdio for collection reads, order workflows, and cart workflows.
38
+ Use the SDK/server client, API, or the \`01\` CLI for collection reads/writes,
39
+ cart workflows, and order/fulfillment/transaction operations that are not
40
+ exposed on hosted MCP.
44
41
 
45
42
  ## CLI
46
43
  - \`01 query <collection>\` \u2014 query data
47
44
  - \`01 schema show <collection>\` \u2014 inspect fields
48
45
  - \`01 schema list\` \u2014 list all collections
49
- - \`npx @01.software/cli mcp\` \u2014 start trusted local stdio MCP for full server-key workflows
46
+ - \`01 order\`, \`01 cart\`, \`01 transaction\`, \`01 product\` \u2014 commerce workflows
50
47
 
51
48
  ## Initial Setup
52
- Run \`/01software-field-config\` in Claude Code to configure field visibility for your use case.
49
+ Configure field visibility (hide unused collections/fields) from the Console admin panel.
53
50
  Use \`get-collection-schema\` or \`01 schema show <collection>\` for live field introspection instead of relying on this document as a schema snapshot.
54
51
  `;
55
52
  }
56
53
  function getSkillFiles() {
57
54
  return [
58
- {
59
- dirName: "01software-field-config",
60
- content: `---
61
- name: 01software-field-config
62
- description: Configure field visibility for this tenant \u2014 hide unused collections and fields via MCP
63
- disable-model-invocation: true
64
- ---
65
-
66
- Steps:
67
- 1. Use \`list-configurable-fields\` to see current visibility settings
68
- 2. Identify fields/collections not needed for your use case
69
- 3. Use \`update-field-config\` to hide them
70
-
71
- Common setups:
72
- - Blog only: hide \`ecommerce\`, \`customers\`, \`videos\` collections
73
- - Store: hide \`articles\`, \`documents\`, \`galleries\`, \`canvas\` collections
74
- - Minimal: hide all except the collections you actively use
75
-
76
- Ask me: "Show current field config" or "Hide ecommerce fields"
77
- `
78
- },
79
55
  {
80
56
  dirName: "01software-query",
81
57
  content: `---
@@ -83,11 +59,12 @@ name: 01software-query
83
59
  description: Query 01.software collections via SDK or CLI with filter, sort, and pagination examples
84
60
  ---
85
61
 
86
- Hosted HTTP MCP does not expose collection query tools. Use the SDK/server client or CLI for collection queries.
62
+ Hosted HTTP MCP exposes live schema and tenant context tools. Use the
63
+ SDK/server client or CLI for collection reads, filtering, and pagination.
87
64
 
88
65
  HTTP MCP:
89
66
  - Use \`get-collection-schema\` before assuming fields.
90
- - Use \`sdk-get-collection-pattern\` for collection-specific code patterns.
67
+ - Use \`get-tenant-context\` to see active collections and hidden fields.
91
68
 
92
69
  CLI examples:
93
70
  - \`01 query products --limit 10\`
@@ -122,9 +99,9 @@ States: pending \u2192 paid \u2192 preparing \u2192 shipped \u2192 delivered \u2
122
99
 
123
100
  Free orders: omit paymentId, totalAmount=0 \u2192 auto-transitions to paid
124
101
 
125
- Hosted HTTP MCP is for schema, tenant context, field config, and SDK guidance.
126
- For trusted local stdio MCP workflows, run \`npx @01.software/cli mcp\`.
127
- CLI: \`01 order create --help\` for full options
102
+ Hosted HTTP MCP does not expose SDK-backed order mutations. Checkout, returns,
103
+ and granular order/fulfillment/transaction steps run through the SDK/server
104
+ client, API, or the \`01\` CLI (\`01 order create --help\` for full options).
128
105
  `
129
106
  },
130
107
  {
@@ -174,4 +151,4 @@ export {
174
151
  getSkillFiles,
175
152
  fetchTenantContext
176
153
  };
177
- //# sourceMappingURL=chunk-5K2CB2Y5.js.map
154
+ //# sourceMappingURL=chunk-Q6MSORYN.js.map
@@ -0,0 +1 @@
1
+ {"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// Hosted HTTP MCP is the single thin surface for shell-less AI clients\n// (ADR 0009 + ADR 0031): only Console-service tools that execute under OAuth\n// request context are advertised. SDK-backed collection/commerce workflows,\n// SDK guidance, and field-config admin tooling live in the 01 CLI, API/SDK, and\n// Console, not on hosted MCP.\n// Keep this list in sync with MCP_TOOL_CONTRACT in @01.software/contracts.\nconst HTTP_MCP_TOOLS = [\n 'get-collection-schema',\n 'get-tenant-context',\n 'check-feature-progress',\n] as const\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- Agent discovery: \\`https://01.software/llms.txt\\`\n- Agent skill: \\`https://docs.01.software/skill.md\\`\n- OpenAPI: \\`https://docs.01.software/api/openapi\\`\n\n## Active Features\n${featuresSection}\n\n## Active Collections\n${collectionsSection}\n\n## Hosted HTTP MCP Quick Reference\nThe hosted OAuth MCP server exposes these ${HTTP_MCP_TOOLS.length} tools:\n\n${HTTP_MCP_TOOLS.map((tool) => `- \\`${tool}\\``).join('\\n')}\n\nUse \\`get-collection-schema\\` for live field introspection before generating code.\nUse the SDK/server client, API, or the \\`01\\` CLI for collection reads/writes,\ncart workflows, and order/fulfillment/transaction operations that are not\nexposed on hosted MCP.\n\n## CLI\n- \\`01 query <collection>\\` — query data\n- \\`01 schema show <collection>\\` — inspect fields\n- \\`01 schema list\\` — list all collections\n- \\`01 order\\`, \\`01 cart\\`, \\`01 transaction\\`, \\`01 product\\` — commerce workflows\n\n## Initial Setup\nConfigure field visibility (hide unused collections/fields) from the Console admin panel.\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-query',\n content: `---\nname: 01software-query\ndescription: Query 01.software collections via SDK or CLI with filter, sort, and pagination examples\n---\n\nHosted HTTP MCP exposes live schema and tenant context tools. Use the\nSDK/server client or CLI for collection reads, filtering, and pagination.\n\nHTTP MCP:\n- Use \\`get-collection-schema\\` before assuming fields.\n- Use \\`get-tenant-context\\` to see active collections and hidden fields.\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.collections.from('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 the order from server code with the SDK/server client or Order API.\n2. Mark paid only after the payment gateway confirms.\n3. Create fulfillment with items and carrier/trackingNumber from a trusted server path.\n4. Handle returns and refunds from a trusted server path.\n\nFree orders: omit paymentId, totalAmount=0 → auto-transitions to paid\n\nHosted HTTP MCP does not expose SDK-backed order mutations. Checkout, returns,\nand granular order/fulfillment/transaction steps run through the SDK/server\nclient, API, or the \\`01\\` CLI (\\`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;AAQA,IAAM,iBAAiB;AAAA,EACrB;AAAA,EACA;AAAA,EACA;AACF;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;AAAA;AAAA;AAAA,EAW5C,eAAe;AAAA;AAAA;AAAA,EAGf,kBAAkB;AAAA;AAAA;AAAA,4CAGwB,eAAe,MAAM;AAAA;AAAA,EAE/D,eAAe,IAAI,CAAC,SAAS,OAAO,IAAI,IAAI,EAAE,KAAK,IAAI,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAiB1D;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;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IA0BX;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,IAoBX;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":[]}
@@ -0,0 +1,183 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/browser-auth.ts
4
+ import { randomBytes } from "crypto";
5
+ import { createServer } from "http";
6
+ import { execFile, exec } from "child_process";
7
+ import { platform } from "os";
8
+ import { URL } from "url";
9
+ import pc from "picocolors";
10
+ var DEFAULT_WEB_URL = process.env.SOFTWARE_WEB_URL || "https://01.software";
11
+ var TIMEOUT_MS = 5 * 60 * 1e3;
12
+ function escapeHtml(s) {
13
+ return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
14
+ }
15
+ function openBrowser(url) {
16
+ const os = platform();
17
+ const onError = () => {
18
+ console.log(
19
+ pc.yellow(
20
+ `Could not open browser automatically. Open this URL manually:
21
+ ${url}`
22
+ )
23
+ );
24
+ };
25
+ if (os === "win32") {
26
+ exec(`start "" "${url}"`, (err) => {
27
+ if (err) onError();
28
+ });
29
+ } else {
30
+ const cmd = os === "darwin" ? "open" : "xdg-open";
31
+ execFile(cmd, [url], (err) => {
32
+ if (err) onError();
33
+ });
34
+ }
35
+ }
36
+ var PAGE_STYLE = `*{margin:0;box-sizing:border-box}
37
+ body{font-family:system-ui,-apple-system,sans-serif;display:flex;justify-content:center;align-items:center;min-height:100vh;background:#fff;color:#252525}
38
+ @media(prefers-color-scheme:dark){body{background:#252525;color:#f5f5f5}}
39
+ .card{text-align:center;padding:2rem 2.5rem;border-radius:10px;max-width:380px;width:100%}
40
+ .icon{width:40px;height:40px;margin:0 auto 1rem;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:1.25rem}
41
+ .icon.ok{background:rgba(0,0,0,.05);color:#252525}
42
+ .icon.err{background:rgba(220,38,38,.08);color:#dc2626}
43
+ @media(prefers-color-scheme:dark){.icon.ok{background:rgba(255,255,255,.08);color:#f5f5f5}}
44
+ h1{font-size:.875rem;font-weight:600;margin-bottom:.375rem}
45
+ p{font-size:.75rem;color:#737373;line-height:1.5}`;
46
+ var SUCCESS_HTML = `<!DOCTYPE html>
47
+ <html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width"><title>Login</title>
48
+ <style>${PAGE_STYLE}</style>
49
+ </head><body><div class="card"><div class="icon ok">\u2713</div><h1>Authenticated</h1><p>You can close this tab and return to the terminal.</p></div></body></html>`;
50
+ var ERROR_HTML = (msg) => `<!DOCTYPE html>
51
+ <html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width"><title>Login Error</title>
52
+ <style>${PAGE_STYLE}</style>
53
+ </head><body><div class="card"><div class="icon err">!</div><h1>Authentication failed</h1><p>${escapeHtml(msg)}</p></div></body></html>`;
54
+ async function exchangeCode(webUrl, code) {
55
+ const url = `${webUrl}/api/cli/exchange`;
56
+ try {
57
+ const res = await fetch(url, {
58
+ method: "POST",
59
+ headers: { "Content-Type": "application/json" },
60
+ body: JSON.stringify({ code })
61
+ });
62
+ if (!res.ok) {
63
+ const body = await res.text().catch(() => "");
64
+ console.error(
65
+ pc.red(
66
+ `Exchange failed: HTTP ${res.status} from ${url}${body ? ` \u2014 ${body.slice(0, 200)}` : ""}`
67
+ )
68
+ );
69
+ return null;
70
+ }
71
+ const data = await res.json();
72
+ if (typeof data.publishableKey !== "string" || typeof data.secretKey !== "string" || typeof data.tenantName !== "string" || typeof data.tenantId !== "string") {
73
+ console.error(pc.red(`Exchange failed: malformed response from ${url}`));
74
+ return null;
75
+ }
76
+ return {
77
+ publishableKey: data.publishableKey,
78
+ secretKey: data.secretKey,
79
+ tenantName: data.tenantName,
80
+ tenantId: data.tenantId
81
+ };
82
+ } catch (err) {
83
+ console.error(
84
+ pc.red(
85
+ `Exchange request to ${url} failed: ${err instanceof Error ? err.message : String(err)}`
86
+ )
87
+ );
88
+ return null;
89
+ }
90
+ }
91
+ async function startBrowserAuth(options) {
92
+ const state = randomBytes(32).toString("hex");
93
+ const webUrl = options?.webUrl ?? DEFAULT_WEB_URL;
94
+ return new Promise((resolve, reject) => {
95
+ const server = createServer((req, res) => {
96
+ if (!req.url) {
97
+ res.writeHead(400).end();
98
+ return;
99
+ }
100
+ const url = new URL(req.url, `http://localhost`);
101
+ if (url.pathname !== "/callback" || req.method !== "GET") {
102
+ res.writeHead(404).end();
103
+ return;
104
+ }
105
+ const error = url.searchParams.get("error");
106
+ if (error) {
107
+ res.writeHead(200, { "Content-Type": "text/html; charset=utf-8", Connection: "close" }).end(ERROR_HTML(error));
108
+ console.error(pc.red(`Login failed: ${error}`));
109
+ cleanup(new Error(`Login failed: ${error}`));
110
+ return;
111
+ }
112
+ const code = url.searchParams.get("code");
113
+ const receivedState = url.searchParams.get("state");
114
+ if (!code || !receivedState) {
115
+ res.writeHead(400, { "Content-Type": "text/html; charset=utf-8", Connection: "close" }).end(ERROR_HTML("Missing code or state."));
116
+ cleanup(new Error("Login failed: missing code or state."));
117
+ return;
118
+ }
119
+ if (receivedState !== state) {
120
+ res.writeHead(403, { "Content-Type": "text/html; charset=utf-8", Connection: "close" }).end(ERROR_HTML("State mismatch."));
121
+ console.error(pc.red("Login failed: state mismatch."));
122
+ cleanup(new Error("Login failed: state mismatch."));
123
+ return;
124
+ }
125
+ exchangeCode(webUrl, code).then((creds) => {
126
+ if (!creds) {
127
+ res.writeHead(400, { "Content-Type": "text/html; charset=utf-8", Connection: "close" }).end(ERROR_HTML("Invalid or expired code."));
128
+ cleanup(new Error("Login failed: code exchange failed."));
129
+ return;
130
+ }
131
+ res.writeHead(200, { "Content-Type": "text/html; charset=utf-8", Connection: "close" }).end(SUCCESS_HTML);
132
+ console.log(pc.green(`
133
+ Logged in successfully!`));
134
+ console.log(pc.dim(`Tenant: ${creds.tenantName}`));
135
+ cleanup(null, creds);
136
+ });
137
+ });
138
+ let timeout;
139
+ let completed = false;
140
+ function cleanup(err, result) {
141
+ if (completed) return;
142
+ completed = true;
143
+ clearTimeout(timeout);
144
+ server.closeAllConnections?.();
145
+ server.close(() => {
146
+ if (err) {
147
+ reject(err);
148
+ } else {
149
+ resolve(result);
150
+ }
151
+ });
152
+ }
153
+ server.listen(0, "127.0.0.1", () => {
154
+ const addr = server.address();
155
+ if (!addr || typeof addr === "string") {
156
+ reject(new Error("Failed to start local server."));
157
+ return;
158
+ }
159
+ const port = addr.port;
160
+ timeout = setTimeout(() => {
161
+ console.error(pc.red("\nLogin timed out (5 minutes). Please try again."));
162
+ cleanup(new Error("Login timed out"));
163
+ }, TIMEOUT_MS);
164
+ const params = new URLSearchParams({ port: String(port), state });
165
+ if (options?.tenantId) {
166
+ params.set("tenantId", options.tenantId);
167
+ }
168
+ const loginUrl = `${webUrl}/cli-auth?${params.toString()}`;
169
+ console.log(pc.dim("Opening browser for login..."));
170
+ console.log(pc.dim(`If the browser does not open, visit:
171
+ ${loginUrl}`));
172
+ openBrowser(loginUrl);
173
+ });
174
+ server.on("error", (err) => {
175
+ reject(err);
176
+ });
177
+ });
178
+ }
179
+
180
+ export {
181
+ startBrowserAuth
182
+ };
183
+ //# sourceMappingURL=chunk-STM4DKVZ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/browser-auth.ts"],"sourcesContent":["import { randomBytes } from 'node:crypto'\nimport { createServer } from 'node:http'\nimport { execFile, exec } from 'node:child_process'\nimport { platform } from 'node:os'\nimport { URL } from 'node:url'\nimport pc from 'picocolors'\n\nconst DEFAULT_WEB_URL = process.env.SOFTWARE_WEB_URL || 'https://01.software'\nconst TIMEOUT_MS = 5 * 60 * 1000 // 5 minutes\n\nfunction escapeHtml(s: string): string {\n return s\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;')\n}\n\nfunction openBrowser(url: string): void {\n const os = platform()\n\n const onError = () => {\n console.log(\n pc.yellow(\n `Could not open browser automatically. Open this URL manually:\\n${url}`,\n ),\n )\n }\n\n if (os === 'win32') {\n exec(`start \"\" \"${url}\"`, (err) => {\n if (err) onError()\n })\n } else {\n const cmd = os === 'darwin' ? 'open' : 'xdg-open'\n execFile(cmd, [url], (err) => {\n if (err) onError()\n })\n }\n}\n\nconst PAGE_STYLE = `*{margin:0;box-sizing:border-box}\nbody{font-family:system-ui,-apple-system,sans-serif;display:flex;justify-content:center;align-items:center;min-height:100vh;background:#fff;color:#252525}\n@media(prefers-color-scheme:dark){body{background:#252525;color:#f5f5f5}}\n.card{text-align:center;padding:2rem 2.5rem;border-radius:10px;max-width:380px;width:100%}\n.icon{width:40px;height:40px;margin:0 auto 1rem;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:1.25rem}\n.icon.ok{background:rgba(0,0,0,.05);color:#252525}\n.icon.err{background:rgba(220,38,38,.08);color:#dc2626}\n@media(prefers-color-scheme:dark){.icon.ok{background:rgba(255,255,255,.08);color:#f5f5f5}}\nh1{font-size:.875rem;font-weight:600;margin-bottom:.375rem}\np{font-size:.75rem;color:#737373;line-height:1.5}`\n\nconst SUCCESS_HTML = `<!DOCTYPE html>\n<html lang=\"en\"><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width\"><title>Login</title>\n<style>${PAGE_STYLE}</style>\n</head><body><div class=\"card\"><div class=\"icon ok\">\\u2713</div><h1>Authenticated</h1><p>You can close this tab and return to the terminal.</p></div></body></html>`\n\nconst ERROR_HTML = (msg: string) => `<!DOCTYPE html>\n<html lang=\"en\"><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width\"><title>Login Error</title>\n<style>${PAGE_STYLE}</style>\n</head><body><div class=\"card\"><div class=\"icon err\">!</div><h1>Authentication failed</h1><p>${escapeHtml(msg)}</p></div></body></html>`\n\ninterface ExchangeResponse {\n publishableKey: string\n secretKey: string\n tenantName: string\n tenantId: string\n}\n\nasync function exchangeCode(\n webUrl: string,\n code: string,\n): Promise<ExchangeResponse | null> {\n const url = `${webUrl}/api/cli/exchange`\n try {\n const res = await fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ code }),\n })\n if (!res.ok) {\n const body = await res.text().catch(() => '')\n console.error(\n pc.red(\n `Exchange failed: HTTP ${res.status} from ${url}${body ? ` — ${body.slice(0, 200)}` : ''}`,\n ),\n )\n return null\n }\n const data = (await res.json()) as Partial<ExchangeResponse>\n if (\n typeof data.publishableKey !== 'string' ||\n typeof data.secretKey !== 'string' ||\n typeof data.tenantName !== 'string' ||\n typeof data.tenantId !== 'string'\n ) {\n console.error(pc.red(`Exchange failed: malformed response from ${url}`))\n return null\n }\n return {\n publishableKey: data.publishableKey,\n secretKey: data.secretKey,\n tenantName: data.tenantName,\n tenantId: data.tenantId,\n }\n } catch (err) {\n console.error(\n pc.red(\n `Exchange request to ${url} failed: ${err instanceof Error ? err.message : String(err)}`,\n ),\n )\n return null\n }\n}\n\nexport async function startBrowserAuth(options?: {\n webUrl?: string\n tenantId?: string\n}): Promise<{\n publishableKey: string\n secretKey: string\n tenantName: string\n tenantId?: string\n}> {\n const state = randomBytes(32).toString('hex')\n const webUrl = options?.webUrl ?? DEFAULT_WEB_URL\n\n return new Promise((resolve, reject) => {\n const server = createServer((req, res) => {\n if (!req.url) {\n res.writeHead(400).end()\n return\n }\n\n const url = new URL(req.url, `http://localhost`)\n\n if (url.pathname !== '/callback' || req.method !== 'GET') {\n res.writeHead(404).end()\n return\n }\n\n const error = url.searchParams.get('error')\n if (error) {\n res\n .writeHead(200, { 'Content-Type': 'text/html; charset=utf-8', Connection: 'close' })\n .end(ERROR_HTML(error))\n console.error(pc.red(`Login failed: ${error}`))\n cleanup(new Error(`Login failed: ${error}`))\n return\n }\n\n const code = url.searchParams.get('code')\n const receivedState = url.searchParams.get('state')\n\n if (!code || !receivedState) {\n res\n .writeHead(400, { 'Content-Type': 'text/html; charset=utf-8', Connection: 'close' })\n .end(ERROR_HTML('Missing code or state.'))\n cleanup(new Error('Login failed: missing code or state.'))\n return\n }\n\n if (receivedState !== state) {\n res\n .writeHead(403, { 'Content-Type': 'text/html; charset=utf-8', Connection: 'close' })\n .end(ERROR_HTML('State mismatch.'))\n console.error(pc.red('Login failed: state mismatch.'))\n cleanup(new Error('Login failed: state mismatch.'))\n return\n }\n\n exchangeCode(webUrl, code).then((creds) => {\n if (!creds) {\n res\n .writeHead(400, { 'Content-Type': 'text/html; charset=utf-8', Connection: 'close' })\n .end(ERROR_HTML('Invalid or expired code.'))\n cleanup(new Error('Login failed: code exchange failed.'))\n return\n }\n\n res\n .writeHead(200, { 'Content-Type': 'text/html; charset=utf-8', Connection: 'close' })\n .end(SUCCESS_HTML)\n\n console.log(pc.green(`\\nLogged in successfully!`))\n console.log(pc.dim(`Tenant: ${creds.tenantName}`))\n\n cleanup(null, creds)\n })\n })\n\n let timeout: ReturnType<typeof setTimeout>\n let completed = false\n\n function cleanup(err: Error | null, result?: ExchangeResponse) {\n if (completed) return\n completed = true\n clearTimeout(timeout)\n server.closeAllConnections?.()\n server.close(() => {\n if (err) {\n reject(err)\n } else {\n resolve(result!)\n }\n })\n }\n\n server.listen(0, '127.0.0.1', () => {\n const addr = server.address()\n if (!addr || typeof addr === 'string') {\n reject(new Error('Failed to start local server.'))\n return\n }\n\n const port = addr.port\n\n timeout = setTimeout(() => {\n console.error(pc.red('\\nLogin timed out (5 minutes). Please try again.'))\n cleanup(new Error('Login timed out'))\n }, TIMEOUT_MS)\n\n const params = new URLSearchParams({ port: String(port), state })\n if (options?.tenantId) {\n params.set('tenantId', options.tenantId)\n }\n const loginUrl = `${webUrl}/cli-auth?${params.toString()}`\n\n console.log(pc.dim('Opening browser for login...'))\n console.log(pc.dim(`If the browser does not open, visit:\\n${loginUrl}`))\n openBrowser(loginUrl)\n })\n\n server.on('error', (err) => {\n reject(err)\n })\n })\n}\n"],"mappings":"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","names":[]}