@botlr/cli 0.0.0-stage → 0.1.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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/config.ts","../src/lock.ts","../src/manifest.ts","../src/status.ts","../src/init.ts","../src/client.ts","../src/commands/pull.ts","../src/commands/push.ts","../src/commands/list.ts","../src/commands/status.ts","../src/commands/init.ts"],"sourcesContent":["import { access, readFile } from \"node:fs/promises\";\nimport path from \"node:path\";\n\nexport const CONFIG_FILE = \"botlr.config.json\";\nexport const DEFAULT_FRAMEWORK = \"react-next\";\nexport const DEFAULT_SECTIONS_DIR = \"components/sections\";\n\n/** `botlr.config.json` */\nexport interface BotlrCliConfigFile {\n baseUrl?: string;\n appId?: string;\n framework?: string;\n sectionsDir?: string;\n}\n\nexport interface ResolvedConfig {\n /** Directory holding `botlr.config.json` (or the cwd when there is none). Paths resolve against it. */\n root: string;\n configPath: string | null;\n baseUrl: string;\n appId: string;\n framework: string;\n /** Absolute path of the sections directory. */\n sectionsDir: string;\n /** Site token, `BOTLR_API_KEY`. */\n apiKey: string | undefined;\n /** Personal access token, `BOTLR_PAT`. */\n personalToken: string | undefined;\n}\n\nexport interface ConfigOverrides {\n framework?: string;\n /** Sections directory, relative to the cwd (as typed on the command line). */\n dir?: string;\n baseUrl?: string;\n appId?: string;\n}\n\nexport class ConfigError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"ConfigError\";\n }\n}\n\nasync function exists(file: string): Promise<boolean> {\n try {\n await access(file);\n return true;\n } catch {\n return false;\n }\n}\n\n/** The nearest `botlr.config.json` at or above `startDir`, or null. */\nexport async function findConfigFile(startDir: string): Promise<string | null> {\n let dir = path.resolve(startDir);\n for (;;) {\n const candidate = path.join(dir, CONFIG_FILE);\n if (await exists(candidate)) return candidate;\n const parent = path.dirname(dir);\n if (parent === dir) return null;\n dir = parent;\n }\n}\n\nexport async function readConfigFile(file: string): Promise<BotlrCliConfigFile> {\n const raw = await readFile(file, \"utf8\");\n try {\n const parsed = JSON.parse(raw) as unknown;\n if (!parsed || typeof parsed !== \"object\" || Array.isArray(parsed)) {\n throw new ConfigError(`${file} must hold a JSON object`);\n }\n return parsed as BotlrCliConfigFile;\n } catch (error) {\n if (error instanceof ConfigError) throw error;\n throw new ConfigError(`${file} is not valid JSON: ${(error as Error).message}`);\n }\n}\n\n/**\n * The effective config: command-line overrides, then environment\n * (`BOTLR_BASE_URL`, `BOTLR_APP_ID`, `BOTLR_FRAMEWORK`, `BOTLR_SECTIONS_DIR`),\n * then `botlr.config.json`, then defaults. Tokens come from `BOTLR_API_KEY`\n * and `BOTLR_PAT` only.\n */\nexport async function loadConfig(\n cwd: string,\n overrides: ConfigOverrides = {},\n env: NodeJS.ProcessEnv = process.env,\n): Promise<ResolvedConfig> {\n const configPath = await findConfigFile(cwd);\n const file = configPath ? await readConfigFile(configPath) : {};\n const root = configPath ? path.dirname(configPath) : path.resolve(cwd);\n\n const baseUrl = overrides.baseUrl ?? env.BOTLR_BASE_URL ?? file.baseUrl;\n const appId = overrides.appId ?? env.BOTLR_APP_ID ?? file.appId;\n if (!baseUrl) {\n throw new ConfigError(`No baseUrl: set it in ${CONFIG_FILE} or BOTLR_BASE_URL`);\n }\n if (!appId) {\n throw new ConfigError(`No appId: set it in ${CONFIG_FILE} or BOTLR_APP_ID`);\n }\n\n const sectionsDir = overrides.dir\n ? path.resolve(cwd, overrides.dir)\n : path.resolve(root, env.BOTLR_SECTIONS_DIR ?? file.sectionsDir ?? DEFAULT_SECTIONS_DIR);\n\n return {\n root,\n configPath,\n baseUrl: baseUrl.replace(/\\/+$/, \"\"),\n appId,\n framework: overrides.framework ?? env.BOTLR_FRAMEWORK ?? file.framework ?? DEFAULT_FRAMEWORK,\n sectionsDir,\n apiKey: env.BOTLR_API_KEY || undefined,\n personalToken: env.BOTLR_PAT || undefined,\n };\n}\n","import { mkdir, readFile, writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport type { SectionSourceScope } from \"@botlr/sdk\";\n\nexport const LOCK_DIR = \".botlr\";\nexport const LOCK_FILE = \"sections.lock.json\";\n\nexport interface LockEntry {\n scope: SectionSourceScope;\n framework: string;\n version: number;\n contentHash: string;\n /** ISO timestamp of the pull or push that wrote the entry. */\n pulledAt: string;\n}\n\n/** `.botlr/sections.lock.json`: one entry per section name. */\nexport type SectionsLock = Record<string, LockEntry>;\n\nexport function lockPath(root: string): string {\n return path.join(root, LOCK_DIR, LOCK_FILE);\n}\n\nexport async function readLock(root: string): Promise<SectionsLock> {\n try {\n const raw = await readFile(lockPath(root), \"utf8\");\n const parsed = JSON.parse(raw) as unknown;\n if (!parsed || typeof parsed !== \"object\" || Array.isArray(parsed)) return {};\n return parsed as SectionsLock;\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") return {};\n throw error;\n }\n}\n\nexport async function writeLock(root: string, lock: SectionsLock): Promise<void> {\n const file = lockPath(root);\n await mkdir(path.dirname(file), { recursive: true });\n const sorted = Object.fromEntries(Object.entries(lock).sort(([a], [b]) => a.localeCompare(b)));\n await writeFile(file, `${JSON.stringify(sorted, null, 2)}\\n`, \"utf8\");\n}\n\nexport async function updateLock(root: string, name: string, entry: LockEntry): Promise<SectionsLock> {\n const lock = await readLock(root);\n lock[name] = entry;\n await writeLock(root, lock);\n return lock;\n}\n","import { mkdir, readFile, readdir, writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport {\n isValidSectionPath,\n stripManifestState,\n type SectionManifest,\n type SectionSourceFileInput,\n} from \"@botlr/sdk\";\n\nexport const MANIFEST_SUFFIX = \".botlr.json\";\n\nexport class ManifestError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"ManifestError\";\n }\n}\n\nexport function manifestPath(sectionsDir: string, name: string): string {\n return path.join(sectionsDir, `${name}${MANIFEST_SUFFIX}`);\n}\n\n/** Section names that have a manifest in `sectionsDir`, sorted. */\nexport async function listManifests(sectionsDir: string): Promise<string[]> {\n let entries: string[];\n try {\n entries = await readdir(sectionsDir);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") return [];\n throw error;\n }\n return entries\n .filter((entry) => entry.endsWith(MANIFEST_SUFFIX))\n .map((entry) => entry.slice(0, -MANIFEST_SUFFIX.length))\n .sort();\n}\n\nexport async function readManifest(sectionsDir: string, name: string): Promise<SectionManifest> {\n const file = manifestPath(sectionsDir, name);\n let raw: string;\n try {\n raw = await readFile(file, \"utf8\");\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") {\n throw new ManifestError(`No manifest for '${name}': expected ${file}`);\n }\n throw error;\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch (error) {\n throw new ManifestError(`${file} is not valid JSON: ${(error as Error).message}`);\n }\n return validateManifest(parsed, file);\n}\n\nexport function validateManifest(value: unknown, where = \"manifest\"): SectionManifest {\n if (!value || typeof value !== \"object\" || Array.isArray(value)) {\n throw new ManifestError(`${where} must be a JSON object`);\n }\n const m = value as Record<string, unknown>;\n if (typeof m.section !== \"string\" || !m.section) throw new ManifestError(`${where}: 'section' is required`);\n if (typeof m.entry !== \"string\" || !m.entry) throw new ManifestError(`${where}: 'entry' is required`);\n if (!Array.isArray(m.files) || m.files.length === 0 || !m.files.every((f) => typeof f === \"string\")) {\n throw new ManifestError(`${where}: 'files' must list at least one path`);\n }\n const files = m.files as string[];\n for (const file of files) {\n if (!isValidSectionPath(file)) throw new ManifestError(`${where}: invalid file path '${file}'`);\n }\n if (!files.includes(m.entry)) throw new ManifestError(`${where}: 'entry' (${m.entry}) must be listed in 'files'`);\n if (m.framework !== undefined && typeof m.framework !== \"string\") {\n throw new ManifestError(`${where}: 'framework' must be a string`);\n }\n return m as unknown as SectionManifest;\n}\n\n/** Write `<name>.botlr.json` without any pull state. */\nexport async function writeManifest(sectionsDir: string, name: string, manifest: SectionManifest): Promise<string> {\n const file = manifestPath(sectionsDir, name);\n await mkdir(sectionsDir, { recursive: true });\n await writeFile(file, `${JSON.stringify(stripManifestState(manifest), null, 2)}\\n`, \"utf8\");\n return file;\n}\n\n/** The files a manifest lists, read from `sectionsDir`, in manifest order. */\nexport async function readSectionFiles(\n sectionsDir: string,\n manifest: SectionManifest,\n): Promise<SectionSourceFileInput[]> {\n return Promise.all(\n manifest.files.map(async (relative) => {\n const file = safeJoin(sectionsDir, relative);\n try {\n return { path: relative, content: await readFile(file, \"utf8\") };\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") {\n throw new ManifestError(`Missing file '${relative}' listed in ${manifest.section}${MANIFEST_SUFFIX}`);\n }\n throw error;\n }\n }),\n );\n}\n\n/** Write pulled files under `sectionsDir`, creating sub-directories. */\nexport async function writeSectionFiles(sectionsDir: string, files: SectionSourceFileInput[]): Promise<string[]> {\n const written: string[] = [];\n for (const file of files) {\n const target = safeJoin(sectionsDir, file.path);\n await mkdir(path.dirname(target), { recursive: true });\n await writeFile(target, file.content, \"utf8\");\n written.push(target);\n }\n return written;\n}\n\n/** `relative` joined under `dir`, refusing paths the bridge would not accept. */\nexport function safeJoin(dir: string, relative: string): string {\n if (!isValidSectionPath(relative)) throw new ManifestError(`Invalid file path '${relative}'`);\n const target = path.resolve(dir, relative);\n const base = path.resolve(dir);\n if (target !== base && !target.startsWith(base + path.sep)) {\n throw new ManifestError(`File path '${relative}' escapes ${dir}`);\n }\n return target;\n}\n","import type { SectionSourceScopeSummary, SectionSourceSummary } from \"@botlr/sdk\";\nimport type { LockEntry } from \"./lock\";\n\n/**\n * - `clean`: local files equal the lock, and the lock is the server's latest\n * - `modified`: local files changed since the pull, server unchanged\n * - `behind`: local files equal the lock, but the server has a newer version\n * - `diverged`: both changed\n * - `untracked`: a manifest with no lock entry (never pulled or pushed from here)\n */\nexport type SectionStatus = \"clean\" | \"modified\" | \"behind\" | \"diverged\" | \"untracked\";\n\nexport interface StatusInput {\n /** Content hash of the local files listed in the manifest. */\n localHash: string;\n lock: LockEntry | undefined;\n /** Latest version on the server for the lock's scope; null when the server has none. */\n remote: SectionSourceScopeSummary | null | undefined;\n}\n\nexport function computeStatus({ localHash, lock, remote }: StatusInput): SectionStatus {\n if (!lock) return \"untracked\";\n const localChanged = localHash !== lock.contentHash;\n const remoteChanged = remote != null && remote.contentHash !== lock.contentHash;\n if (localChanged && remoteChanged) return \"diverged\";\n if (localChanged) return \"modified\";\n if (remoteChanged) return \"behind\";\n return \"clean\";\n}\n\n/** The server summary that corresponds to a lock entry's scope. */\nexport function remoteForLock(\n summary: SectionSourceSummary | undefined,\n lock: LockEntry | undefined,\n): SectionSourceScopeSummary | null | undefined {\n if (!summary) return undefined;\n if (!lock) return summary.effective === \"shared\" ? summary.shared : summary.instance;\n return lock.scope === \"shared\" ? summary.shared : summary.instance;\n}\n","/**\n * `botlr sections init`: derive first manifests from a satellite app's\n * section components. It reads the registry map\n * (`publisher-section-registry.ts`) to learn which slug maps to which\n * component file, and each component's inline `XDefinition` export for the\n * title, category, description and icon. Nothing is pushed.\n */\nimport { readFile, readdir } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport type { SectionManifest } from \"@botlr/sdk\";\n\nexport const REGISTRY_FILE = \"publisher-section-registry.ts\";\n\nexport interface RegistryEntry {\n slug: string;\n exportName: string;\n /** Module specifier as imported, for example `./Hero` */\n importPath: string;\n}\n\n/** Parse `import { X } from \"./File\"` plus `slug: X` map entries from a registry source. */\nexport function parseRegistry(source: string): RegistryEntry[] {\n const importedFrom = new Map<string, string>();\n const importRe = /import\\s*(?:type\\s*)?\\{([^}]*)\\}\\s*from\\s*[\"']([^\"']+)[\"']/g;\n for (const match of source.matchAll(importRe)) {\n const names = match[1]!;\n const spec = match[2]!;\n for (const raw of names.split(\",\")) {\n const name = raw.trim().split(/\\s+as\\s+/).pop()?.trim();\n if (name) importedFrom.set(name, spec);\n }\n }\n\n const bodyMatch = source.match(/=\\s*\\{([\\s\\S]*?)\\n\\}\\s*;?/);\n if (!bodyMatch) return [];\n const entries: RegistryEntry[] = [];\n const entryRe = /^\\s*(?:\"([^\"]+)\"|'([^']+)'|([A-Za-z0-9_$-]+))\\s*:\\s*([A-Za-z0-9_$]+)\\s*,?\\s*(?:\\/\\/.*)?$/gm;\n for (const match of bodyMatch[1]!.matchAll(entryRe)) {\n const slug = match[1] ?? match[2] ?? match[3]!;\n const exportName = match[4]!;\n const importPath = importedFrom.get(exportName);\n if (!importPath || !importPath.startsWith(\".\")) continue;\n entries.push({ slug, exportName, importPath });\n }\n return entries;\n}\n\nexport interface DefinitionMeta {\n name?: string;\n heading?: string;\n category?: string;\n description?: string;\n icon?: string;\n}\n\n/** Scalar fields of an `export const XDefinition = { ... }` block, when the file has one. */\nexport function parseDefinition(source: string): DefinitionMeta | null {\n const match = source.match(/export\\s+const\\s+\\w+Definition\\s*=\\s*\\{([\\s\\S]*?)\\n\\}/);\n if (!match) return null;\n const body = match[1]!;\n const field = (key: string): string | undefined => {\n const m = body.match(new RegExp(`(?:^|\\\\n)\\\\s*${key}\\\\s*:\\\\s*(?:\"([^\"]*)\"|'([^']*)'|\\`([^\\`]*)\\`)`));\n return m ? (m[1] ?? m[2] ?? m[3]) : undefined;\n };\n return {\n name: field(\"name\"),\n heading: field(\"heading\"),\n category: field(\"category\"),\n description: field(\"description\"),\n icon: field(\"icon\"),\n };\n}\n\n/** Module specifiers a source file imports, split into relative and package imports. */\nexport function parseImports(source: string): { internal: string[]; npm: string[] } {\n const internal = new Set<string>();\n const npm = new Set<string>();\n const re = /(?:import|export)\\s+(?:[^\"';]*?\\s+from\\s+)?[\"']([^\"']+)[\"']|import\\s*\\(\\s*[\"']([^\"']+)[\"']\\s*\\)/g;\n for (const match of source.matchAll(re)) {\n const spec = match[1] ?? match[2];\n if (!spec) continue;\n if (spec.startsWith(\".\") || spec.startsWith(\"@/\") || spec.startsWith(\"~/\")) internal.add(spec);\n else npm.add(spec.startsWith(\"@\") ? spec.split(\"/\").slice(0, 2).join(\"/\") : spec.split(\"/\")[0]!);\n }\n return { internal: [...internal].sort(), npm: [...npm].sort() };\n}\n\nconst SOURCE_EXTENSIONS = [\".tsx\", \".ts\", \".jsx\", \".js\"];\n\nasync function resolveImport(sectionsDir: string, spec: string): Promise<string | null> {\n const base = path.resolve(sectionsDir, spec);\n for (const ext of SOURCE_EXTENSIONS) {\n try {\n await readFile(base + ext);\n return base + ext;\n } catch {\n // try the next extension\n }\n }\n return null;\n}\n\nexport interface DerivedManifest {\n name: string;\n manifest: SectionManifest;\n /** The component file, absolute. */\n entryFile: string;\n}\n\nexport interface DeriveOptions {\n framework: string;\n /** npm dependency ranges to record, usually from the app's package.json. */\n npmVersions?: Record<string, string>;\n}\n\n/** One manifest per registry entry whose component file exists. */\nexport async function deriveManifests(sectionsDir: string, options: DeriveOptions): Promise<DerivedManifest[]> {\n const registrySource = await readFile(path.join(sectionsDir, REGISTRY_FILE), \"utf8\");\n const entries = parseRegistry(registrySource);\n const out: DerivedManifest[] = [];\n for (const entry of entries) {\n const entryFile = await resolveImport(sectionsDir, entry.importPath);\n if (!entryFile) continue;\n const source = await readFile(entryFile, \"utf8\");\n const meta = parseDefinition(source);\n const imports = parseImports(source);\n const relative = path.relative(sectionsDir, entryFile).split(path.sep).join(\"/\");\n const npm: Record<string, string> = {};\n for (const pkg of imports.npm) {\n if (pkg === \"react\" || pkg === \"next\") continue;\n npm[pkg] = options.npmVersions?.[pkg] ?? \"*\";\n }\n const manifest: SectionManifest = {\n section: entry.slug,\n framework: options.framework,\n entry: relative,\n exportName: entry.exportName,\n files: [relative],\n ...(meta?.heading ? { title: meta.heading } : {}),\n ...(meta?.description ? { description: meta.description } : {}),\n ...(meta?.category ? { category: meta.category } : {}),\n ...(meta?.icon ? { icon: meta.icon } : {}),\n dependencies: {\n ...(Object.keys(npm).length ? { npm } : {}),\n ...(imports.internal.length ? { internal: imports.internal } : {}),\n },\n };\n out.push({ name: entry.slug, manifest, entryFile });\n }\n return out;\n}\n\n/** `dependencies` + `devDependencies` of the nearest package.json above `dir`, for version ranges. */\nexport async function readNpmVersions(dir: string): Promise<Record<string, string>> {\n let current = path.resolve(dir);\n for (;;) {\n try {\n const raw = await readFile(path.join(current, \"package.json\"), \"utf8\");\n const pkg = JSON.parse(raw) as { dependencies?: Record<string, string>; devDependencies?: Record<string, string> };\n return { ...pkg.devDependencies, ...pkg.dependencies };\n } catch {\n const parent = path.dirname(current);\n if (parent === current) return {};\n current = parent;\n }\n }\n}\n\nexport async function hasRegistry(sectionsDir: string): Promise<boolean> {\n try {\n return (await readdir(sectionsDir)).includes(REGISTRY_FILE);\n } catch {\n return false;\n }\n}\n","import { createBotlrClient, type BotlrClient } from \"@botlr/sdk\";\nimport { ConfigError, type ResolvedConfig } from \"./config\";\n\n/** A client from the resolved config. `needPersonalToken` makes a missing `BOTLR_PAT` an error up front. */\nexport function clientFor(config: ResolvedConfig, options: { needPersonalToken?: boolean } = {}): BotlrClient {\n if (!config.apiKey && !options.needPersonalToken) {\n // Reads work without a token against a dev server; warn once so a 401 is no surprise.\n process.stderr.write(\"botlr: BOTLR_API_KEY is not set, requests go out without a site token\\n\");\n }\n if (options.needPersonalToken && !config.personalToken) {\n throw new ConfigError(\"Pushing needs BOTLR_PAT: a personal access token with the write:sections scope\");\n }\n return createBotlrClient({\n baseUrl: config.baseUrl,\n appId: config.appId,\n apiKey: config.apiKey,\n personalToken: config.personalToken,\n });\n}\n","import { contentHash, type SectionSourcePullResponse, type SectionSourceScope } from \"@botlr/sdk\";\nimport type { ResolvedConfig } from \"../config\";\nimport { clientFor } from \"../client\";\nimport { readLock, writeLock, type LockEntry } from \"../lock\";\nimport { readManifest, readSectionFiles, writeManifest, writeSectionFiles } from \"../manifest\";\n\nexport interface PullOptions {\n /** Section names; empty with `all: true` pulls every section the server lists. */\n names: string[];\n all?: boolean;\n version?: number;\n /** Pull the shared row even when the app has its own. */\n shared?: boolean;\n /** Overwrite local files that changed since the last pull. */\n force?: boolean;\n}\n\nexport interface PullResult {\n name: string;\n scope: SectionSourceScope;\n version: number;\n files: string[];\n /** Set when the section was skipped instead of written. */\n skipped?: string;\n}\n\n/** Content hash of the local files, or null when the manifest or a file is missing. */\nasync function localHash(sectionsDir: string, name: string): Promise<string | null> {\n try {\n const manifest = await readManifest(sectionsDir, name);\n return await contentHash(await readSectionFiles(sectionsDir, manifest));\n } catch {\n return null;\n }\n}\n\nexport async function pullSections(config: ResolvedConfig, options: PullOptions): Promise<PullResult[]> {\n const client = clientFor(config);\n const scope: SectionSourceScope | undefined = options.shared ? \"shared\" : undefined;\n\n let names = options.names;\n if (options.all) {\n const listing = await client.sections.list({ framework: config.framework });\n names = listing.sections\n .filter((s) => (options.shared ? s.shared : s.instance || s.shared))\n .map((s) => s.name);\n }\n if (names.length === 0) return [];\n\n const lock = await readLock(config.root);\n const results: PullResult[] = [];\n\n for (const name of names) {\n const entry = lock[name];\n if (entry && !options.force) {\n const current = await localHash(config.sectionsDir, name);\n if (current !== null && current !== entry.contentHash) {\n results.push({ name, scope: entry.scope, version: entry.version, files: [], skipped: \"modified locally, use --force to overwrite\" });\n continue;\n }\n }\n\n const response: SectionSourcePullResponse = await client.sections.pull(name, {\n framework: config.framework,\n version: options.version,\n scope,\n });\n const { source } = response;\n\n const files = await writeSectionFiles(config.sectionsDir, source.files);\n await writeManifest(config.sectionsDir, name, source.manifest);\n\n const newEntry: LockEntry = {\n scope: source.scope,\n framework: source.framework,\n version: source.version,\n contentHash: source.contentHash,\n pulledAt: new Date().toISOString(),\n };\n lock[name] = newEntry;\n results.push({ name, scope: source.scope, version: source.version, files });\n }\n\n await writeLock(config.root, lock);\n return results;\n}\n","import {\n BotlrConflictError,\n contentHash,\n type SectionSourcePushResponse,\n type SectionSourceScope,\n} from \"@botlr/sdk\";\nimport type { ResolvedConfig } from \"../config\";\nimport { clientFor } from \"../client\";\nimport { readLock, updateLock } from \"../lock\";\nimport { ManifestError, readManifest, readSectionFiles } from \"../manifest\";\n\nexport interface PushOptions {\n name: string;\n message?: string;\n shared?: boolean;\n create?: boolean;\n /** Omit `baseVersion` so the server skips the conflict check. */\n force?: boolean;\n}\n\nexport interface PushResult {\n name: string;\n created: boolean;\n scope: SectionSourceScope;\n version: number;\n contentHash: string;\n}\n\nexport class PushConflict extends Error {\n constructor(\n message: string,\n public readonly latest: { version: number; contentHash: string } | null,\n ) {\n super(message);\n this.name = \"PushConflict\";\n }\n}\n\nexport async function pushSection(config: ResolvedConfig, options: PushOptions): Promise<PushResult> {\n const client = clientFor(config, { needPersonalToken: true });\n const { name } = options;\n const manifest = await readManifest(config.sectionsDir, name);\n if (manifest.section !== name) {\n throw new ManifestError(`${name}.botlr.json says section '${manifest.section}', expected '${name}'`);\n }\n if (manifest.framework && manifest.framework !== config.framework) {\n throw new ManifestError(\n `${name}.botlr.json is for framework '${manifest.framework}', the config says '${config.framework}'`,\n );\n }\n manifest.framework = config.framework;\n\n const files = await readSectionFiles(config.sectionsDir, manifest);\n const localHash = await contentHash(files);\n const targetScope: SectionSourceScope = options.shared ? \"shared\" : \"instance\";\n\n let baseVersion: number | undefined;\n if (!options.force) {\n const lock = (await readLock(config.root))[name];\n if (lock && lock.scope === targetScope) {\n baseVersion = lock.version;\n } else {\n // No lock for this scope: base on what the server has right now.\n const listing = await client.sections.list({ framework: config.framework });\n const summary = listing.sections.find((s) => s.name === name);\n const remote = summary ? (targetScope === \"shared\" ? summary.shared : summary.instance) : null;\n baseVersion = remote?.version ?? 0;\n if (remote && lock === undefined && remote.contentHash === localHash) {\n // Already on the server, nothing to push; let the server answer 200.\n }\n }\n }\n\n let response: SectionSourcePushResponse;\n try {\n response = await client.sections.push(name, {\n framework: config.framework,\n manifest,\n files,\n shared: options.shared ?? false,\n baseVersion,\n message: options.message,\n create: options.create ?? false,\n });\n } catch (error) {\n if (error instanceof BotlrConflictError) {\n throw new PushConflict(error.message, error.latest);\n }\n throw error;\n }\n\n await updateLock(config.root, name, {\n scope: response.scope,\n framework: config.framework,\n version: response.version,\n contentHash: response.contentHash,\n pulledAt: new Date().toISOString(),\n });\n\n return {\n name,\n created: response.created,\n scope: response.scope,\n version: response.version,\n contentHash: response.contentHash,\n };\n}\n","import type { SectionSourceSummary } from \"@botlr/sdk\";\nimport type { ResolvedConfig } from \"../config\";\nimport { clientFor } from \"../client\";\n\nexport async function listSections(config: ResolvedConfig): Promise<SectionSourceSummary[]> {\n const client = clientFor(config);\n const listing = await client.sections.list({ framework: config.framework });\n return listing.sections;\n}\n","import { contentHash, type SectionSourceSummary } from \"@botlr/sdk\";\nimport type { ResolvedConfig } from \"../config\";\nimport { clientFor } from \"../client\";\nimport { readLock, type LockEntry } from \"../lock\";\nimport { listManifests, readManifest, readSectionFiles } from \"../manifest\";\nimport { computeStatus, remoteForLock, type SectionStatus } from \"../status\";\n\nexport interface SectionStatusRow {\n name: string;\n status: SectionStatus | \"error\";\n local: number | null;\n remote: number | null;\n scope: string;\n detail?: string;\n}\n\nexport async function sectionStatuses(\n config: ResolvedConfig,\n options: { names?: string[]; offline?: boolean } = {},\n): Promise<SectionStatusRow[]> {\n const names = options.names?.length ? options.names : await listManifests(config.sectionsDir);\n const lock = await readLock(config.root);\n\n let summaries = new Map<string, SectionSourceSummary>();\n if (!options.offline) {\n const listing = await clientFor(config).sections.list({ framework: config.framework });\n summaries = new Map(listing.sections.map((s) => [s.name, s]));\n }\n\n const rows: SectionStatusRow[] = [];\n for (const name of names) {\n const entry: LockEntry | undefined = lock[name];\n const summary = summaries.get(name);\n const remote = remoteForLock(summary, entry);\n try {\n const manifest = await readManifest(config.sectionsDir, name);\n const localHash = await contentHash(await readSectionFiles(config.sectionsDir, manifest));\n rows.push({\n name,\n status: computeStatus({ localHash, lock: entry, remote }),\n local: entry?.version ?? null,\n remote: remote?.version ?? null,\n scope: entry?.scope ?? summary?.effective ?? \"-\",\n });\n } catch (error) {\n rows.push({\n name,\n status: \"error\",\n local: entry?.version ?? null,\n remote: remote?.version ?? null,\n scope: entry?.scope ?? \"-\",\n detail: (error as Error).message,\n });\n }\n }\n return rows;\n}\n","import type { ResolvedConfig } from \"../config\";\nimport { deriveManifests, hasRegistry, readNpmVersions, REGISTRY_FILE } from \"../init\";\nimport { listManifests, writeManifest } from \"../manifest\";\n\nexport interface InitOptions {\n /** Overwrite manifests that already exist. */\n force?: boolean;\n /** Only derive, write nothing. */\n dryRun?: boolean;\n}\n\nexport interface InitResult {\n name: string;\n file: string;\n entry: string;\n written: boolean;\n}\n\n/**\n * Derive `<slug>.botlr.json` for every component the registry maps. The\n * manifests are a starting point: review them, then `botlr push <name>`\n * (with `--create` for sections the dashboard does not know yet).\n */\nexport async function initSections(config: ResolvedConfig, options: InitOptions = {}): Promise<InitResult[]> {\n if (!(await hasRegistry(config.sectionsDir))) {\n throw new Error(`No ${REGISTRY_FILE} in ${config.sectionsDir}; sections init reads the registry map to find components`);\n }\n const existing = new Set(await listManifests(config.sectionsDir));\n const npmVersions = await readNpmVersions(config.sectionsDir);\n const derived = await deriveManifests(config.sectionsDir, { framework: config.framework, npmVersions });\n\n const results: InitResult[] = [];\n for (const { name, manifest, entryFile } of derived) {\n const skip = existing.has(name) && !options.force;\n let file = `${name}.botlr.json`;\n if (!skip && !options.dryRun) {\n file = await writeManifest(config.sectionsDir, name, manifest);\n }\n results.push({ name, file, entry: entryFile, written: !skip && !options.dryRun });\n }\n return results;\n}\n"],"mappings":"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","names":["readFile","path","mkdir","readFile","writeFile","path","localHash","readFile","readdir","path","contentHash","localHash","contentHash","contentHash","localHash","contentHash"]}
package/package.json CHANGED
@@ -1,6 +1,57 @@
1
1
  {
2
2
  "name": "@botlr/cli",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "0.1.0",
4
+ "description": "The botlr command line: pull and push section source between a Botlr site and an app's codebase.",
5
+ "license": "MIT",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "https://github.com/botlr-io/botlr.git",
9
+ "directory": "packages/cli"
10
+ },
11
+ "homepage": "https://github.com/botlr-io/botlr/tree/main/packages/cli#readme",
12
+ "keywords": [
13
+ "botlr",
14
+ "cms",
15
+ "cli",
16
+ "sections"
17
+ ],
18
+ "type": "module",
19
+ "bin": {
20
+ "botlr": "./dist/cli.js"
21
+ },
22
+ "main": "./dist/index.js",
23
+ "types": "./dist/index.d.ts",
24
+ "exports": {
25
+ ".": {
26
+ "types": "./dist/index.d.ts",
27
+ "import": "./dist/index.js"
28
+ },
29
+ "./package.json": "./package.json"
30
+ },
31
+ "files": [
32
+ "dist",
33
+ "CHANGELOG.md",
34
+ "README.md"
35
+ ],
36
+ "publishConfig": {
37
+ "access": "public"
38
+ },
39
+ "engines": {
40
+ "node": ">=20"
41
+ },
42
+ "dependencies": {
43
+ "commander": "^14.0.0",
44
+ "@botlr/sdk": "^0.1.0"
45
+ },
46
+ "devDependencies": {
47
+ "@types/node": "^22.15.0",
48
+ "tsup": "^8.5.0",
49
+ "typescript": "~5.9.3",
50
+ "vitest": "^3.2.4"
51
+ },
52
+ "scripts": {
53
+ "build": "tsup",
54
+ "typecheck": "tsc -p tsconfig.json",
55
+ "test": "vitest run"
56
+ }
6
57
  }