@absolutejs/cli 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.
- package/LICENSE +93 -0
- package/README.md +136 -0
- package/bin/absolutejs.js +3 -0
- package/dist/cli.d.ts +24 -0
- package/dist/cli.js +660 -0
- package/dist/cli.js.map +15 -0
- package/dist/commands/deploy.d.ts +20 -0
- package/dist/commands/env.d.ts +20 -0
- package/dist/commands/secrets.d.ts +21 -0
- package/dist/index.d.ts +119 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +10 -0
- package/dist/loadConfig.d.ts +12 -0
- package/dist/utils/output.d.ts +10 -0
- package/package.json +69 -0
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/loadConfig.ts", "../src/utils/output.ts", "../src/commands/secrets.ts", "../src/commands/env.ts", "../src/commands/deploy.ts", "../src/cli.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"/**\n * Discover + load `absolutejs.config.ts` from the current working\n * directory (or its parents). Falls back to `absolutejs.config.js` /\n * `.mjs` if no .ts variant exists. Bun handles TS imports natively;\n * Node would need a loader.\n */\n\nimport { existsSync, statSync } from 'node:fs';\nimport { dirname, join, resolve } from 'node:path';\nimport type { AbsolutejsConfig } from './index';\n\nconst CONFIG_NAMES = [\n\t'absolutejs.config.ts',\n\t'absolutejs.config.mts',\n\t'absolutejs.config.js',\n\t'absolutejs.config.mjs'\n];\n\nconst findConfigPath = (startDir: string): string | undefined => {\n\tlet dir = resolve(startDir);\n\tfor (;;) {\n\t\tfor (const name of CONFIG_NAMES) {\n\t\t\tconst candidate = join(dir, name);\n\t\t\ttry {\n\t\t\t\tif (existsSync(candidate) && statSync(candidate).isFile()) {\n\t\t\t\t\treturn candidate;\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// keep walking\n\t\t\t}\n\t\t}\n\t\tconst parent = dirname(dir);\n\t\tif (parent === dir) return undefined;\n\t\tdir = parent;\n\t}\n};\n\nexport type LoadedConfig = {\n\tconfig: AbsolutejsConfig;\n\tpath: string;\n};\n\nexport const loadConfig = async (\n\tstartDir: string = process.cwd()\n): Promise<LoadedConfig> => {\n\tconst path = findConfigPath(startDir);\n\tif (path === undefined) {\n\t\tthrow new Error(\n\t\t\t`[absolutejs] no config file found.\\n` +\n\t\t\t\t`Looked for: ${CONFIG_NAMES.join(', ')} (walked up from ${startDir})\\n\\n` +\n\t\t\t\t`Create an absolutejs.config.ts with:\\n\\n` +\n\t\t\t\t` import { defineConfig } from '@absolutejs/cli';\\n` +\n\t\t\t\t` export default defineConfig({\\n` +\n\t\t\t\t` secrets: /* your SecretBroker */,\\n` +\n\t\t\t\t` deployments: [],\\n` +\n\t\t\t\t` });`\n\t\t);\n\t}\n\tlet mod: unknown;\n\ttry {\n\t\tmod = await import(path);\n\t} catch (error) {\n\t\tthrow new Error(\n\t\t\t`[absolutejs] failed to load ${path}: ${\n\t\t\t\terror instanceof Error ? error.message : String(error)\n\t\t\t}`\n\t\t);\n\t}\n\tconst config = (mod as { default?: AbsolutejsConfig }).default;\n\tif (config === undefined || typeof config !== 'object') {\n\t\tthrow new Error(\n\t\t\t`[absolutejs] ${path} must \\`export default defineConfig({...})\\``\n\t\t);\n\t}\n\treturn { config, path };\n};\n",
|
|
6
|
+
"/**\n * Output helpers — pretty tables for humans, raw JSON when `--json`.\n * No color escapes; CI logs render them as garbage.\n */\n\nexport type OutputMode = 'human' | 'json';\n\nexport const renderTable = (\n\theaders: string[],\n\trows: ReadonlyArray<ReadonlyArray<string>>\n): string => {\n\tif (rows.length === 0) {\n\t\treturn `${headers.join(' ')}\\n(no rows)\\n`;\n\t}\n\tconst widths = headers.map((header, columnIndex) => {\n\t\tconst cells = rows.map((row) => row[columnIndex] ?? '');\n\t\treturn Math.max(header.length, ...cells.map((cell) => cell.length));\n\t});\n\tconst formatRow = (row: ReadonlyArray<string>): string =>\n\t\trow\n\t\t\t.map((cell, columnIndex) => (cell ?? '').padEnd(widths[columnIndex] ?? 0))\n\t\t\t.join(' ')\n\t\t\t.trimEnd();\n\tconst lines = [formatRow(headers), formatRow(widths.map((w) => '-'.repeat(w)))];\n\tfor (const row of rows) lines.push(formatRow(row));\n\treturn `${lines.join('\\n')}\\n`;\n};\n\nexport const writeOut = (text: string): void => {\n\tprocess.stdout.write(text.endsWith('\\n') ? text : `${text}\\n`);\n};\n\nexport const writeJson = (value: unknown): void => {\n\tprocess.stdout.write(`${JSON.stringify(value, null, 2)}\\n`);\n};\n\nexport const writeErr = (text: string): void => {\n\tprocess.stderr.write(text.endsWith('\\n') ? text : `${text}\\n`);\n};\n\nexport const formatRelativeTime = (msAgo: number): string => {\n\tif (msAgo < 60_000) return `${Math.round(msAgo / 1000)}s ago`;\n\tif (msAgo < 3_600_000) return `${Math.round(msAgo / 60_000)}m ago`;\n\tif (msAgo < 86_400_000) return `${Math.round(msAgo / 3_600_000)}h ago`;\n\treturn `${Math.round(msAgo / 86_400_000)}d ago`;\n};\n",
|
|
7
|
+
"/**\n * `absolutejs secrets <verb>` — broker-level secret management.\n *\n * Verbs:\n * list — print known secret names + fingerprints\n * get <name> — print one value (requires --show)\n * set <name>=<value> — put a value via the adapter\n * rotate <name> — broker.rotate(name)\n *\n * Most verbs need a broker; `list`/`set` also need an adapter to\n * read/write the underlying store. The CLI prints clear errors when\n * the config doesn't provide what a verb needs.\n */\n\nimport type { AbsolutejsConfig } from '../index';\nimport {\n\trenderTable,\n\twriteErr,\n\twriteJson,\n\twriteOut,\n\ttype OutputMode\n} from '../utils/output';\n\nexport type SecretsArgs = {\n\tverb: string;\n\tpositional: string[];\n\tflags: Record<string, string | boolean>;\n};\n\nconst requireBroker = (config: AbsolutejsConfig) => {\n\tif (config.secrets === undefined) {\n\t\tthrow new Error(\n\t\t\t'config.secrets is not set — `secrets` verbs need a SecretBroker'\n\t\t);\n\t}\n\treturn config.secrets;\n};\n\nconst requireAdapter = (config: AbsolutejsConfig) => {\n\tif (config.secretAdapter === undefined) {\n\t\tthrow new Error(\n\t\t\t'config.secretAdapter is not set — pass the SecretAdapter you used to build the broker'\n\t\t);\n\t}\n\treturn config.secretAdapter;\n};\n\nexport const runSecrets = async (\n\tconfig: AbsolutejsConfig,\n\targs: SecretsArgs,\n\tmode: OutputMode\n): Promise<number> => {\n\tconst { verb, positional, flags } = args;\n\n\tswitch (verb) {\n\t\tcase 'list': {\n\t\t\tconst adapter = requireAdapter(config);\n\t\t\tconst broker = requireBroker(config);\n\t\t\tif (adapter.list === undefined) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t'the configured SecretAdapter does not implement `list()`'\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst names = await adapter.list();\n\t\t\tconst rows: string[][] = [];\n\t\t\tfor (const name of names.sort()) {\n\t\t\t\tconst value = await adapter.fetch(name);\n\t\t\t\tconst fingerprint =\n\t\t\t\t\tvalue === null ? '(empty)' : broker.fingerprint(value);\n\t\t\t\trows.push([name, fingerprint]);\n\t\t\t}\n\t\t\tif (mode === 'json') {\n\t\t\t\twriteJson(\n\t\t\t\t\trows.map(([name, fingerprint]) => ({ fingerprint, name }))\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\twriteOut(renderTable(['name', 'fingerprint'], rows));\n\t\t\t}\n\t\t\treturn 0;\n\t\t}\n\n\t\tcase 'get': {\n\t\t\tconst broker = requireBroker(config);\n\t\t\tconst name = positional[0];\n\t\t\tif (name === undefined) {\n\t\t\t\tthrow new Error('usage: secrets get <name> [--show]');\n\t\t\t}\n\t\t\tconst resolved = await broker.resolve(name);\n\t\t\tif (resolved === null) {\n\t\t\t\twriteErr(`(no value for ${name})`);\n\t\t\t\treturn 2;\n\t\t\t}\n\t\t\tif (mode === 'json') {\n\t\t\t\twriteJson({\n\t\t\t\t\tfingerprint: resolved.fingerprint,\n\t\t\t\t\tname,\n\t\t\t\t\tvalue: flags.show === true ? resolved.value : '(redacted; pass --show)'\n\t\t\t\t});\n\t\t\t} else if (flags.show === true) {\n\t\t\t\twriteOut(resolved.value);\n\t\t\t} else {\n\t\t\t\twriteOut(\n\t\t\t\t\t`${name}: fingerprint=${resolved.fingerprint} (pass --show to print plaintext)`\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn 0;\n\t\t}\n\n\t\tcase 'set': {\n\t\t\tconst adapter = requireAdapter(config);\n\t\t\tif (adapter.put === undefined) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t'the configured SecretAdapter does not implement `put()`'\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst pair = positional[0];\n\t\t\tif (pair === undefined) {\n\t\t\t\tthrow new Error('usage: secrets set <NAME>=<value>');\n\t\t\t}\n\t\t\tconst eq = pair.indexOf('=');\n\t\t\tif (eq <= 0) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t'usage: secrets set <NAME>=<value> (missing `=`)'\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst name = pair.slice(0, eq);\n\t\t\tconst value = pair.slice(eq + 1);\n\t\t\tawait adapter.put(name, value);\n\t\t\tif (mode === 'json') {\n\t\t\t\twriteJson({ name, set: true });\n\t\t\t} else {\n\t\t\t\twriteOut(`set ${name}`);\n\t\t\t}\n\t\t\treturn 0;\n\t\t}\n\n\t\tcase 'rotate': {\n\t\t\tconst broker = requireBroker(config);\n\t\t\tconst name = positional[0];\n\t\t\tif (name === undefined) {\n\t\t\t\tthrow new Error('usage: secrets rotate <name>');\n\t\t\t}\n\t\t\tconst rotated = await broker.rotate(name);\n\t\t\tif (mode === 'json') {\n\t\t\t\twriteJson({\n\t\t\t\t\tfingerprint: rotated.fingerprint,\n\t\t\t\t\tname,\n\t\t\t\t\trotated: true\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\twriteOut(`rotated ${name} (new fingerprint: ${rotated.fingerprint})`);\n\t\t\t}\n\t\t\treturn 0;\n\t\t}\n\n\t\tdefault:\n\t\t\tthrow new Error(\n\t\t\t\t`unknown secrets verb: \"${verb}\". try: list | get | set | rotate`\n\t\t\t);\n\t}\n};\n",
|
|
8
|
+
"/**\n * `absolutejs env <verb>` — env-file management against the remote\n * target for a deployment.\n *\n * Verbs:\n * push <stage> — resolve secrets + extras, atomic write\n * pull <stage> — read the remote env file as-is\n * diff <stage> — show diff between remote and what `push` would write\n *\n * Each verb resolves the deployment's lazy `target()` factory, so a\n * `secrets list` call doesn't accidentally provision a Hetzner box.\n */\n\nimport type { AbsolutejsConfig, CliDeployment } from '../index';\nimport {\n\trenderTable,\n\twriteErr,\n\twriteJson,\n\twriteOut,\n\ttype OutputMode\n} from '../utils/output';\n\nexport type EnvArgs = {\n\tverb: string;\n\tpositional: string[];\n\tflags: Record<string, string | boolean>;\n};\n\nconst KEY_PATTERN = /^[A-Z_][A-Z0-9_]*$/;\nconst NEEDS_QUOTING = /[\\s\"'`$\\\\#&|;<>(){}*?!]/;\n\nconst validateKey = (key: string): void => {\n\tif (!KEY_PATTERN.test(key)) {\n\t\tthrow new Error(`invalid env key \"${key}\" — must match /^[A-Z_][A-Z0-9_]*$/`);\n\t}\n};\n\nconst serializeLine = (key: string, value: string): string => {\n\tvalidateKey(key);\n\tif (value.includes('\\n') || value.includes('\\r')) {\n\t\tthrow new Error(\n\t\t\t`value for \"${key}\" contains a newline — env files cannot represent multi-line values`\n\t\t);\n\t}\n\tif (NEEDS_QUOTING.test(value) || value.startsWith('=') || value === '') {\n\t\tconst escaped = value.replaceAll('\\\\', '\\\\\\\\').replaceAll('\"', '\\\\\"');\n\t\treturn `${key}=\"${escaped}\"`;\n\t}\n\treturn `${key}=${value}`;\n};\n\nconst serializeEnvFile = (values: Record<string, string>): string => {\n\tconst lines: string[] = [];\n\tfor (const key of Object.keys(values).sort()) {\n\t\tconst value = values[key];\n\t\tif (value === undefined) continue;\n\t\tlines.push(serializeLine(key, value));\n\t}\n\treturn `${lines.join('\\n')}\\n`;\n};\n\nconst unquoteValue = (raw: string): string => {\n\tif (raw.length >= 2 && raw.startsWith('\"') && raw.endsWith('\"')) {\n\t\treturn raw.slice(1, -1).replaceAll('\\\\\"', '\"').replaceAll('\\\\\\\\', '\\\\');\n\t}\n\tif (raw.length >= 2 && raw.startsWith(\"'\") && raw.endsWith(\"'\")) {\n\t\treturn raw.slice(1, -1);\n\t}\n\treturn raw;\n};\n\nconst parseEnvFile = (text: string): Record<string, string> => {\n\tconst result: Record<string, string> = {};\n\tfor (const rawLine of text.split('\\n')) {\n\t\tconst line = rawLine.trim();\n\t\tif (line.length === 0 || line.startsWith('#')) continue;\n\t\tconst eq = line.indexOf('=');\n\t\tif (eq <= 0) continue; // tolerate stray malformed lines on `pull`\n\t\tconst key = line.slice(0, eq).trim();\n\t\tconst value = unquoteValue(line.slice(eq + 1).trim());\n\t\tif (KEY_PATTERN.test(key)) result[key] = value;\n\t}\n\treturn result;\n};\n\nconst findDeployment = (\n\tconfig: AbsolutejsConfig,\n\tstage: string\n): CliDeployment => {\n\tconst match = (config.deployments ?? []).find((d) => d.name === stage);\n\tif (match === undefined) {\n\t\tconst names = (config.deployments ?? []).map((d) => d.name);\n\t\tthrow new Error(\n\t\t\t`unknown deployment \"${stage}\". configured: ${names.length > 0 ? names.join(', ') : '(none)'}`\n\t\t);\n\t}\n\treturn match;\n};\n\nconst resolveValuesForDeployment = async (\n\tconfig: AbsolutejsConfig,\n\tdeployment: CliDeployment\n): Promise<Record<string, string>> => {\n\tconst merged: Record<string, string> = {};\n\tfor (const [key, value] of Object.entries(deployment.extras ?? {})) {\n\t\tvalidateKey(key);\n\t\tmerged[key] = value;\n\t}\n\tif ((deployment.secretNames ?? []).length > 0) {\n\t\tif (config.secrets === undefined) {\n\t\t\tthrow new Error(\n\t\t\t\t`deployment \"${deployment.name}\" declares secretNames but config.secrets is not set`\n\t\t\t);\n\t\t}\n\t\tfor (const name of deployment.secretNames ?? []) {\n\t\t\tconst resolved = await config.secrets.resolve(name);\n\t\t\tif (resolved === null) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`secret \"${name}\" not found in broker (deployment: ${deployment.name})`\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (merged[name] !== undefined) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`\"${name}\" defined in BOTH extras and secretNames for ${deployment.name}`\n\t\t\t\t);\n\t\t\t}\n\t\t\tmerged[name] = resolved.value;\n\t\t}\n\t}\n\treturn merged;\n};\n\nconst shellQuote = (value: string): string =>\n\t`'${value.replaceAll(\"'\", \"'\\\\''\")}'`;\n\nconst readRemoteFile = async (\n\tdeployment: CliDeployment\n): Promise<string | undefined> => {\n\tconst target = await deployment.target();\n\tconst sentinel = '__ABS_DEPLOY_ENV_ABSENT__';\n\tconst result = await target.exec(\n\t\t`if [ -f ${shellQuote(deployment.remotePath)} ]; then cat ${shellQuote(\n\t\t\tdeployment.remotePath\n\t\t)}; else echo ${sentinel}; fi`\n\t);\n\tif (result.exitCode !== 0) {\n\t\tthrow new Error(\n\t\t\t`failed to read ${deployment.remotePath}: exit ${result.exitCode}: ${result.stderr || result.stdout}`\n\t\t);\n\t}\n\tif (result.stdout.trim() === sentinel) return undefined;\n\treturn result.stdout;\n};\n\nconst writeRemoteFile = async (\n\tdeployment: CliDeployment,\n\tcontents: string\n): Promise<void> => {\n\tconst target = await deployment.target();\n\tconst tempPath = `${deployment.remotePath}.new.${Math.floor(Date.now() / 1000)}`;\n\tconst dir = deployment.remotePath.split('/').slice(0, -1).join('/') || '/';\n\tconst mkdir = await target.exec(`mkdir -p ${shellQuote(dir)}`);\n\tif (mkdir.exitCode !== 0) {\n\t\tthrow new Error(`mkdir ${dir} failed: ${mkdir.stderr || mkdir.stdout}`);\n\t}\n\tconst write = await target.exec(`cat > ${shellQuote(tempPath)}`, {\n\t\tstdin: contents\n\t});\n\tif (write.exitCode !== 0) {\n\t\tthrow new Error(`write to ${tempPath} failed: ${write.stderr || write.stdout}`);\n\t}\n\tconst mode = deployment.mode ?? '600';\n\tconst chmod = await target.exec(\n\t\t`chmod ${shellQuote(mode)} ${shellQuote(tempPath)}`\n\t);\n\tif (chmod.exitCode !== 0) {\n\t\tthrow new Error(`chmod failed: ${chmod.stderr || chmod.stdout}`);\n\t}\n\tif (deployment.owner !== undefined) {\n\t\tconst chown = await target.exec(\n\t\t\t`chown ${shellQuote(deployment.owner)} ${shellQuote(tempPath)}`\n\t\t);\n\t\tif (chown.exitCode !== 0) {\n\t\t\tthrow new Error(`chown failed: ${chown.stderr || chown.stdout}`);\n\t\t}\n\t}\n\tconst mv = await target.exec(\n\t\t`mv ${shellQuote(tempPath)} ${shellQuote(deployment.remotePath)}`\n\t);\n\tif (mv.exitCode !== 0) {\n\t\tthrow new Error(`mv failed: ${mv.stderr || mv.stdout}`);\n\t}\n\tif (deployment.reload !== undefined) {\n\t\tconst reload = await target.exec(deployment.reload);\n\t\tif (reload.exitCode !== 0) {\n\t\t\tthrow new Error(\n\t\t\t\t`reload command failed: ${reload.stderr || reload.stdout}`\n\t\t\t);\n\t\t}\n\t}\n};\n\nexport const runEnv = async (\n\tconfig: AbsolutejsConfig,\n\targs: EnvArgs,\n\tmode: OutputMode\n): Promise<number> => {\n\tconst { verb, positional, flags } = args;\n\tconst stage = positional[0];\n\tif (stage === undefined) {\n\t\tthrow new Error(`usage: env ${verb} <stage>`);\n\t}\n\tconst deployment = findDeployment(config, stage);\n\n\tswitch (verb) {\n\t\tcase 'pull': {\n\t\t\tconst remoteText = await readRemoteFile(deployment);\n\t\t\tif (remoteText === undefined) {\n\t\t\t\tif (mode === 'json') writeJson({ exists: false });\n\t\t\t\telse writeErr(`(no env file at ${deployment.remotePath})`);\n\t\t\t\treturn 2;\n\t\t\t}\n\t\t\tif (mode === 'json') {\n\t\t\t\twriteJson({\n\t\t\t\t\texists: true,\n\t\t\t\t\tpath: deployment.remotePath,\n\t\t\t\t\tvalues: parseEnvFile(remoteText)\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\twriteOut(remoteText);\n\t\t\t}\n\t\t\treturn 0;\n\t\t}\n\n\t\tcase 'push': {\n\t\t\tconst resolved = await resolveValuesForDeployment(config, deployment);\n\t\t\tconst next = serializeEnvFile(resolved);\n\t\t\tawait writeRemoteFile(deployment, next);\n\t\t\tconst message = `pushed ${Object.keys(resolved).length} keys to ${stage}:${deployment.remotePath}`;\n\t\t\tif (mode === 'json') {\n\t\t\t\twriteJson({\n\t\t\t\t\tkeys: Object.keys(resolved).sort(),\n\t\t\t\t\tpath: deployment.remotePath,\n\t\t\t\t\tpushed: true,\n\t\t\t\t\tstage\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\twriteOut(message);\n\t\t\t}\n\t\t\treturn 0;\n\t\t}\n\n\t\tcase 'diff': {\n\t\t\tconst remoteText = await readRemoteFile(deployment);\n\t\t\tconst remote = remoteText === undefined ? {} : parseEnvFile(remoteText);\n\t\t\tconst next = await resolveValuesForDeployment(config, deployment);\n\t\t\tconst allKeys = [\n\t\t\t\t...new Set([...Object.keys(remote), ...Object.keys(next)])\n\t\t\t].sort();\n\t\t\ttype DiffRow = { key: string; status: string; detail: string };\n\t\t\tconst rows: DiffRow[] = [];\n\t\t\tfor (const key of allKeys) {\n\t\t\t\tconst before = remote[key];\n\t\t\t\tconst after = next[key];\n\t\t\t\tif (before === undefined) {\n\t\t\t\t\trows.push({\n\t\t\t\t\t\tdetail: '(new)',\n\t\t\t\t\t\tkey,\n\t\t\t\t\t\tstatus: 'added'\n\t\t\t\t\t});\n\t\t\t\t} else if (after === undefined) {\n\t\t\t\t\trows.push({\n\t\t\t\t\t\tdetail: '(removed)',\n\t\t\t\t\t\tkey,\n\t\t\t\t\t\tstatus: 'removed'\n\t\t\t\t\t});\n\t\t\t\t} else if (before === after) {\n\t\t\t\t\tif (flags.all === true) {\n\t\t\t\t\t\trows.push({ detail: '(unchanged)', key, status: 'same' });\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\trows.push({\n\t\t\t\t\t\tdetail: `before ≠ after`,\n\t\t\t\t\t\tkey,\n\t\t\t\t\t\tstatus: 'changed'\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (mode === 'json') {\n\t\t\t\twriteJson({ diff: rows, stage });\n\t\t\t} else {\n\t\t\t\tif (rows.length === 0) {\n\t\t\t\t\twriteOut(`no differences (${allKeys.length} keys match)`);\n\t\t\t\t} else {\n\t\t\t\t\twriteOut(\n\t\t\t\t\t\trenderTable(\n\t\t\t\t\t\t\t['key', 'status', 'detail'],\n\t\t\t\t\t\t\trows.map((r) => [r.key, r.status, r.detail])\n\t\t\t\t\t\t)\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn 0;\n\t\t}\n\n\t\tdefault:\n\t\t\tthrow new Error(\n\t\t\t\t`unknown env verb: \"${verb}\". try: pull | push | diff`\n\t\t\t);\n\t}\n};\n",
|
|
9
|
+
"/**\n * `absolutejs deploy <verb>` — deploy-side ops over the existing\n * `@absolutejs/deploy` Deployer surface.\n *\n * Verbs:\n * releases <stage> — list release history for a stage\n * rollback <stage> [--to <id>] — rollback to <id> or previous\n * status <stage> — current release id + recent history\n *\n * Each deployment in the config may expose a `deployer()` factory.\n * Verbs that need it bail out clearly when it's absent.\n */\n\nimport type { AbsolutejsConfig, CliDeployer, CliDeployment } from '../index';\nimport {\n\tformatRelativeTime,\n\trenderTable,\n\twriteErr,\n\twriteJson,\n\twriteOut,\n\ttype OutputMode\n} from '../utils/output';\n\nexport type DeployArgs = {\n\tverb: string;\n\tpositional: string[];\n\tflags: Record<string, string | boolean>;\n};\n\nconst findDeployment = (\n\tconfig: AbsolutejsConfig,\n\tstage: string\n): CliDeployment => {\n\tconst match = (config.deployments ?? []).find((d) => d.name === stage);\n\tif (match === undefined) {\n\t\tconst names = (config.deployments ?? []).map((d) => d.name);\n\t\tthrow new Error(\n\t\t\t`unknown deployment \"${stage}\". configured: ${names.length > 0 ? names.join(', ') : '(none)'}`\n\t\t);\n\t}\n\treturn match;\n};\n\nconst requireDeployer = async (\n\tdeployment: CliDeployment\n): Promise<CliDeployer> => {\n\tif (deployment.deployer === undefined) {\n\t\tthrow new Error(\n\t\t\t`deployment \"${deployment.name}\" has no deployer() factory in config`\n\t\t);\n\t}\n\treturn deployment.deployer();\n};\n\nexport const runDeploy = async (\n\tconfig: AbsolutejsConfig,\n\targs: DeployArgs,\n\tmode: OutputMode\n): Promise<number> => {\n\tconst { verb, positional, flags } = args;\n\tconst stage = positional[0];\n\tif (stage === undefined) {\n\t\tthrow new Error(`usage: deploy ${verb} <stage>`);\n\t}\n\tconst deployment = findDeployment(config, stage);\n\tconst deployer = await requireDeployer(deployment);\n\n\tswitch (verb) {\n\t\tcase 'releases': {\n\t\t\tif (deployer.listReleases === undefined) {\n\t\t\t\tthrow new Error('the deployer for this stage does not implement listReleases()');\n\t\t\t}\n\t\t\tconst releases = await deployer.listReleases();\n\t\t\tif (mode === 'json') {\n\t\t\t\twriteJson({ releases, stage });\n\t\t\t} else {\n\t\t\t\tconst rows = releases.map((release) => [\n\t\t\t\t\trelease.active === true ? '*' : ' ',\n\t\t\t\t\trelease.id,\n\t\t\t\t\tnew Date(release.at).toISOString(),\n\t\t\t\t\tformatRelativeTime(Date.now() - release.at)\n\t\t\t\t]);\n\t\t\t\twriteOut(renderTable(['', 'id', 'at', 'age'], rows));\n\t\t\t}\n\t\t\treturn 0;\n\t\t}\n\n\t\tcase 'status': {\n\t\t\tconst releases = deployer.listReleases\n\t\t\t\t? await deployer.listReleases()\n\t\t\t\t: [];\n\t\t\tconst currentId = deployer.currentReleaseId\n\t\t\t\t? await deployer.currentReleaseId()\n\t\t\t\t: undefined;\n\t\t\tif (mode === 'json') {\n\t\t\t\twriteJson({\n\t\t\t\t\tcurrentReleaseId: currentId ?? null,\n\t\t\t\t\trecentReleases: releases.slice(0, 5),\n\t\t\t\t\tstage\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\twriteOut(`stage: ${stage}`);\n\t\t\t\twriteOut(`current release: ${currentId ?? '(unknown)'}`);\n\t\t\t\tif (releases.length > 0) {\n\t\t\t\t\twriteOut('\\nrecent releases:');\n\t\t\t\t\tconst rows = releases\n\t\t\t\t\t\t.slice(0, 5)\n\t\t\t\t\t\t.map((release) => [\n\t\t\t\t\t\t\trelease.active === true ? '*' : ' ',\n\t\t\t\t\t\t\trelease.id,\n\t\t\t\t\t\t\tformatRelativeTime(Date.now() - release.at)\n\t\t\t\t\t\t]);\n\t\t\t\t\twriteOut(renderTable(['', 'id', 'age'], rows));\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn 0;\n\t\t}\n\n\t\tcase 'rollback': {\n\t\t\tif (deployer.rollback === undefined) {\n\t\t\t\tthrow new Error('the deployer for this stage does not implement rollback()');\n\t\t\t}\n\t\t\tlet target = typeof flags.to === 'string' ? flags.to : undefined;\n\t\t\tif (target === undefined) {\n\t\t\t\t// --to-previous: find the second-most-recent release.\n\t\t\t\tif (deployer.listReleases === undefined) {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t'rollback without --to requires deployer.listReleases() to find the previous release'\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tconst releases = await deployer.listReleases();\n\t\t\t\tif (releases.length < 2) {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t`stage ${stage} has fewer than 2 releases — nothing to roll back to`\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tconst activeIndex = releases.findIndex((r) => r.active === true);\n\t\t\t\tconst previous =\n\t\t\t\t\tactiveIndex >= 0 && activeIndex + 1 < releases.length\n\t\t\t\t\t\t? releases[activeIndex + 1]\n\t\t\t\t\t\t: releases[1];\n\t\t\t\ttarget = previous?.id;\n\t\t\t\tif (target === undefined) {\n\t\t\t\t\tthrow new Error('could not determine previous release id');\n\t\t\t\t}\n\t\t\t}\n\t\t\tawait deployer.rollback(target);\n\t\t\tif (mode === 'json') {\n\t\t\t\twriteJson({ rolledBackTo: target, stage });\n\t\t\t} else {\n\t\t\t\twriteOut(`${stage}: rolled back to ${target}`);\n\t\t\t}\n\t\t\treturn 0;\n\t\t}\n\n\t\tdefault:\n\t\t\tthrow new Error(\n\t\t\t\t`unknown deploy verb: \"${verb}\". try: releases | status | rollback`\n\t\t\t);\n\t}\n};\n",
|
|
10
|
+
"/**\n * `absolutejs` binary entry. Parses argv, loads\n * `absolutejs.config.ts`, dispatches to a verb handler.\n *\n * Hand-rolled arg parser — no commander/yargs dep, keeping with the\n * substrate's zero-peer-dep posture.\n */\n\nimport { loadConfig } from './loadConfig';\nimport { runSecrets, type SecretsArgs } from './commands/secrets';\nimport { runEnv, type EnvArgs } from './commands/env';\nimport { runDeploy, type DeployArgs } from './commands/deploy';\nimport { writeErr, writeOut, type OutputMode } from './utils/output';\n\nexport type ParsedArgs = {\n\tcommand: string | undefined;\n\tverb: string | undefined;\n\tpositional: string[];\n\tflags: Record<string, string | boolean>;\n};\n\n/**\n * Parse `argv` into command + verb + positional + flags.\n *\n * Conventions:\n * absolutejs <command> <verb> <pos...> [--flag] [--flag=value] [--flag value]\n *\n * Bare `--flag` becomes `{flag: true}`. `--flag=value` and `--flag value`\n * both become `{flag: value}`. Positional args don't start with `-`.\n */\nexport const parseArgs = (argv: string[]): ParsedArgs => {\n\tconst positional: string[] = [];\n\tconst flags: Record<string, string | boolean> = {};\n\tlet command: string | undefined;\n\tlet verb: string | undefined;\n\tlet index = 0;\n\twhile (index < argv.length) {\n\t\tconst arg = argv[index] as string;\n\t\tif (arg.startsWith('--')) {\n\t\t\tconst body = arg.slice(2);\n\t\t\tconst eq = body.indexOf('=');\n\t\t\tif (eq >= 0) {\n\t\t\t\tflags[body.slice(0, eq)] = body.slice(eq + 1);\n\t\t\t} else {\n\t\t\t\tconst next = argv[index + 1];\n\t\t\t\tif (next !== undefined && !next.startsWith('-')) {\n\t\t\t\t\tflags[body] = next;\n\t\t\t\t\tindex += 1;\n\t\t\t\t} else {\n\t\t\t\t\tflags[body] = true;\n\t\t\t\t}\n\t\t\t}\n\t\t} else if (command === undefined) {\n\t\t\tcommand = arg;\n\t\t} else if (verb === undefined) {\n\t\t\tverb = arg;\n\t\t} else {\n\t\t\tpositional.push(arg);\n\t\t}\n\t\tindex += 1;\n\t}\n\treturn { command, flags, positional, verb };\n};\n\nconst HELP = `absolutejs — substrate CLI for the AbsoluteJS PaaS\n\nUSAGE\n absolutejs <command> <verb> [args...] [--flags]\n\nCOMMANDS\n secrets list list secret names + fingerprints from the broker\n secrets get <name> [--show] resolve one secret (--show prints plaintext)\n secrets set <NAME>=<value> put a value via the configured adapter\n secrets rotate <name> generate + persist a new value, fire onRotate\n\n env push <stage> resolve secrets + extras, atomic write remote env file\n env pull <stage> print the remote env file (or --json its values)\n env diff <stage> [--all] show what env push would change\n\n deploy releases <stage> list release history for a stage\n deploy status <stage> current release id + recent history\n deploy rollback <stage> [--to <id>] roll back to <id> or the previous release\n\nGLOBAL FLAGS\n --json machine-readable output\n --help this banner\n\nCONFIG\n Reads ./absolutejs.config.ts (walks parent dirs). Author it with:\n\n import { defineConfig } from '@absolutejs/cli';\n export default defineConfig({\n secrets: /* SecretBroker */,\n secretAdapter: /* SecretAdapter */,\n deployments: [\n { name: 'prod', target: () => ..., remotePath: '/etc/api.env',\n secretNames: ['STRIPE_KEY'], reload: 'systemctl reload api' }\n ],\n });`;\n\nexport const main = async (argv: string[]): Promise<number> => {\n\tconst args = parseArgs(argv);\n\tif (args.flags.help === true || args.command === undefined) {\n\t\twriteOut(HELP);\n\t\treturn args.command === undefined && args.flags.help !== true ? 1 : 0;\n\t}\n\n\tconst mode: OutputMode = args.flags.json === true ? 'json' : 'human';\n\tconst verb = args.verb;\n\tif (verb === undefined) {\n\t\twriteErr(`missing verb for \"${args.command}\". run \\`absolutejs --help\\``);\n\t\treturn 2;\n\t}\n\n\ttry {\n\t\tconst { config } = await loadConfig();\n\n\t\tswitch (args.command) {\n\t\t\tcase 'secrets':\n\t\t\t\treturn await runSecrets(\n\t\t\t\t\tconfig,\n\t\t\t\t\t{\n\t\t\t\t\t\tflags: args.flags,\n\t\t\t\t\t\tpositional: args.positional,\n\t\t\t\t\t\tverb\n\t\t\t\t\t} satisfies SecretsArgs,\n\t\t\t\t\tmode\n\t\t\t\t);\n\n\t\t\tcase 'env':\n\t\t\t\treturn await runEnv(\n\t\t\t\t\tconfig,\n\t\t\t\t\t{\n\t\t\t\t\t\tflags: args.flags,\n\t\t\t\t\t\tpositional: args.positional,\n\t\t\t\t\t\tverb\n\t\t\t\t\t} satisfies EnvArgs,\n\t\t\t\t\tmode\n\t\t\t\t);\n\n\t\t\tcase 'deploy':\n\t\t\t\treturn await runDeploy(\n\t\t\t\t\tconfig,\n\t\t\t\t\t{\n\t\t\t\t\t\tflags: args.flags,\n\t\t\t\t\t\tpositional: args.positional,\n\t\t\t\t\t\tverb\n\t\t\t\t\t} satisfies DeployArgs,\n\t\t\t\t\tmode\n\t\t\t\t);\n\n\t\t\tdefault:\n\t\t\t\twriteErr(\n\t\t\t\t\t`unknown command \"${args.command}\". try: secrets | env | deploy`\n\t\t\t\t);\n\t\t\t\treturn 2;\n\t\t}\n\t} catch (error) {\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\tif (mode === 'json') {\n\t\t\tprocess.stdout.write(`${JSON.stringify({ error: message })}\\n`);\n\t\t} else {\n\t\t\twriteErr(`error: ${message}`);\n\t\t}\n\t\treturn 1;\n\t}\n};\n"
|
|
11
|
+
],
|
|
12
|
+
"mappings": "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",
|
|
13
|
+
"debugId": "1486CB5B222DF56264756E2164756E21",
|
|
14
|
+
"names": []
|
|
15
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `absolutejs deploy <verb>` — deploy-side ops over the existing
|
|
3
|
+
* `@absolutejs/deploy` Deployer surface.
|
|
4
|
+
*
|
|
5
|
+
* Verbs:
|
|
6
|
+
* releases <stage> — list release history for a stage
|
|
7
|
+
* rollback <stage> [--to <id>] — rollback to <id> or previous
|
|
8
|
+
* status <stage> — current release id + recent history
|
|
9
|
+
*
|
|
10
|
+
* Each deployment in the config may expose a `deployer()` factory.
|
|
11
|
+
* Verbs that need it bail out clearly when it's absent.
|
|
12
|
+
*/
|
|
13
|
+
import type { AbsolutejsConfig } from '../index';
|
|
14
|
+
import { type OutputMode } from '../utils/output';
|
|
15
|
+
export type DeployArgs = {
|
|
16
|
+
verb: string;
|
|
17
|
+
positional: string[];
|
|
18
|
+
flags: Record<string, string | boolean>;
|
|
19
|
+
};
|
|
20
|
+
export declare const runDeploy: (config: AbsolutejsConfig, args: DeployArgs, mode: OutputMode) => Promise<number>;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `absolutejs env <verb>` — env-file management against the remote
|
|
3
|
+
* target for a deployment.
|
|
4
|
+
*
|
|
5
|
+
* Verbs:
|
|
6
|
+
* push <stage> — resolve secrets + extras, atomic write
|
|
7
|
+
* pull <stage> — read the remote env file as-is
|
|
8
|
+
* diff <stage> — show diff between remote and what `push` would write
|
|
9
|
+
*
|
|
10
|
+
* Each verb resolves the deployment's lazy `target()` factory, so a
|
|
11
|
+
* `secrets list` call doesn't accidentally provision a Hetzner box.
|
|
12
|
+
*/
|
|
13
|
+
import type { AbsolutejsConfig } from '../index';
|
|
14
|
+
import { type OutputMode } from '../utils/output';
|
|
15
|
+
export type EnvArgs = {
|
|
16
|
+
verb: string;
|
|
17
|
+
positional: string[];
|
|
18
|
+
flags: Record<string, string | boolean>;
|
|
19
|
+
};
|
|
20
|
+
export declare const runEnv: (config: AbsolutejsConfig, args: EnvArgs, mode: OutputMode) => Promise<number>;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `absolutejs secrets <verb>` — broker-level secret management.
|
|
3
|
+
*
|
|
4
|
+
* Verbs:
|
|
5
|
+
* list — print known secret names + fingerprints
|
|
6
|
+
* get <name> — print one value (requires --show)
|
|
7
|
+
* set <name>=<value> — put a value via the adapter
|
|
8
|
+
* rotate <name> — broker.rotate(name)
|
|
9
|
+
*
|
|
10
|
+
* Most verbs need a broker; `list`/`set` also need an adapter to
|
|
11
|
+
* read/write the underlying store. The CLI prints clear errors when
|
|
12
|
+
* the config doesn't provide what a verb needs.
|
|
13
|
+
*/
|
|
14
|
+
import type { AbsolutejsConfig } from '../index';
|
|
15
|
+
import { type OutputMode } from '../utils/output';
|
|
16
|
+
export type SecretsArgs = {
|
|
17
|
+
verb: string;
|
|
18
|
+
positional: string[];
|
|
19
|
+
flags: Record<string, string | boolean>;
|
|
20
|
+
};
|
|
21
|
+
export declare const runSecrets: (config: AbsolutejsConfig, args: SecretsArgs, mode: OutputMode) => Promise<number>;
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @absolutejs/cli — substrate CLI for the AbsoluteJS PaaS.
|
|
3
|
+
*
|
|
4
|
+
* Library entry: exports `defineConfig` for `absolutejs.config.ts`
|
|
5
|
+
* authors + the types the CLI verbs operate on. The CLI itself runs
|
|
6
|
+
* via the `absolutejs` binary (see `bin/absolutejs.js` and
|
|
7
|
+
* `src/cli.ts`).
|
|
8
|
+
*
|
|
9
|
+
* Composes with `@absolutejs/secrets` (broker, encrypted file
|
|
10
|
+
* adapter), `@absolutejs/deploy` (Target, Deployer, EnvDeployment),
|
|
11
|
+
* and any other substrate package that satisfies one of the narrow
|
|
12
|
+
* interfaces below.
|
|
13
|
+
*/
|
|
14
|
+
export type SecretValue = {
|
|
15
|
+
value: string;
|
|
16
|
+
fingerprint: string;
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Narrow SecretBroker interface — the SecretBroker from
|
|
20
|
+
* `@absolutejs/secrets` satisfies this structurally.
|
|
21
|
+
*/
|
|
22
|
+
export type CliSecretBroker = {
|
|
23
|
+
resolve: (name: string) => Promise<SecretValue | null>;
|
|
24
|
+
rotate: (name: string) => Promise<SecretValue>;
|
|
25
|
+
fingerprint: (value: string) => string;
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* Narrow SecretAdapter interface — for `secrets list` / `secrets set`,
|
|
29
|
+
* which need to write to the underlying store (broker doesn't expose
|
|
30
|
+
* `put`).
|
|
31
|
+
*/
|
|
32
|
+
export type CliSecretAdapter = {
|
|
33
|
+
fetch: (name: string) => Promise<string | null>;
|
|
34
|
+
list?: () => Promise<string[]>;
|
|
35
|
+
put?: (name: string, value: string) => Promise<void>;
|
|
36
|
+
remove?: (name: string) => Promise<void>;
|
|
37
|
+
};
|
|
38
|
+
export type CliTargetExec = (cmd: string, opts?: {
|
|
39
|
+
cwd?: string;
|
|
40
|
+
env?: Record<string, string>;
|
|
41
|
+
stdin?: string;
|
|
42
|
+
timeoutMs?: number;
|
|
43
|
+
}) => Promise<{
|
|
44
|
+
stdout: string;
|
|
45
|
+
stderr: string;
|
|
46
|
+
exitCode: number;
|
|
47
|
+
}>;
|
|
48
|
+
/** Narrow Target — @absolutejs/deploy's Target satisfies this. */
|
|
49
|
+
export type CliTarget = {
|
|
50
|
+
readonly description: string;
|
|
51
|
+
exec: CliTargetExec;
|
|
52
|
+
upload?: (localPath: string, remotePath: string, opts?: {
|
|
53
|
+
exclude?: string[];
|
|
54
|
+
deleteOrphans?: boolean;
|
|
55
|
+
}) => Promise<void>;
|
|
56
|
+
close?: () => Promise<void>;
|
|
57
|
+
};
|
|
58
|
+
/**
|
|
59
|
+
* A deployment entry — names a remote, declares which secrets it
|
|
60
|
+
* consumes, points at a remote env file. Modeled after
|
|
61
|
+
* `EnvDeployment` from `@absolutejs/deploy/env`.
|
|
62
|
+
*
|
|
63
|
+
* The `target` field is a FACTORY (lazy) so a `secrets list` call
|
|
64
|
+
* doesn't accidentally provision a Hetzner box. Only verbs that
|
|
65
|
+
* actually need a remote (env push/pull/diff, deploy rollback)
|
|
66
|
+
* invoke it.
|
|
67
|
+
*/
|
|
68
|
+
export type CliDeployment = {
|
|
69
|
+
/** Stage name — `'prod'`, `'staging'`, `'pr-123'`. */
|
|
70
|
+
name: string;
|
|
71
|
+
/** Lazy target factory. Resolved only when a verb needs it. */
|
|
72
|
+
target: () => Promise<CliTarget>;
|
|
73
|
+
/** Remote env file path. */
|
|
74
|
+
remotePath: string;
|
|
75
|
+
/** Names of secrets the broker should resolve into this file. */
|
|
76
|
+
secretNames?: ReadonlyArray<string>;
|
|
77
|
+
/** Non-secret env vars merged into the file. */
|
|
78
|
+
extras?: Record<string, string>;
|
|
79
|
+
/** File mode. Default `'600'`. */
|
|
80
|
+
mode?: string;
|
|
81
|
+
/** File owner. */
|
|
82
|
+
owner?: string;
|
|
83
|
+
/** Command run after the env file changes (e.g. `'systemctl reload api'`). */
|
|
84
|
+
reload?: string;
|
|
85
|
+
/**
|
|
86
|
+
* Optional Deployer factory — for `deploy rollback / releases / status`.
|
|
87
|
+
* Same lazy contract as `target`.
|
|
88
|
+
*/
|
|
89
|
+
deployer?: () => Promise<CliDeployer>;
|
|
90
|
+
};
|
|
91
|
+
/** Narrow Deployer interface — @absolutejs/deploy's Deployer satisfies. */
|
|
92
|
+
export type CliDeployer = {
|
|
93
|
+
listReleases?: () => Promise<ReadonlyArray<{
|
|
94
|
+
id: string;
|
|
95
|
+
at: number;
|
|
96
|
+
active?: boolean;
|
|
97
|
+
annotations?: Record<string, unknown>;
|
|
98
|
+
}>>;
|
|
99
|
+
rollback?: (releaseId: string) => Promise<void>;
|
|
100
|
+
currentReleaseId?: () => Promise<string | undefined>;
|
|
101
|
+
};
|
|
102
|
+
/** The shape of `absolutejs.config.ts`'s default export. */
|
|
103
|
+
export type AbsolutejsConfig = {
|
|
104
|
+
/** Optional broker — required for any `secrets` verb. */
|
|
105
|
+
secrets?: CliSecretBroker;
|
|
106
|
+
/**
|
|
107
|
+
* Optional adapter — required for `secrets list` / `secrets set`
|
|
108
|
+
* (broker.rotate/resolve don't expose put/list). Pass the same
|
|
109
|
+
* adapter you used to build the broker.
|
|
110
|
+
*/
|
|
111
|
+
secretAdapter?: CliSecretAdapter;
|
|
112
|
+
/** Stage deployments. Empty array is valid for `secrets`-only setups. */
|
|
113
|
+
deployments?: ReadonlyArray<CliDeployment>;
|
|
114
|
+
};
|
|
115
|
+
/**
|
|
116
|
+
* Author-facing helper for `absolutejs.config.ts`. Pure identity
|
|
117
|
+
* function; exists for type inference + future extension.
|
|
118
|
+
*/
|
|
119
|
+
export declare const defineConfig: (config: AbsolutejsConfig) => AbsolutejsConfig;
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/index.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"/**\n * @absolutejs/cli — substrate CLI for the AbsoluteJS PaaS.\n *\n * Library entry: exports `defineConfig` for `absolutejs.config.ts`\n * authors + the types the CLI verbs operate on. The CLI itself runs\n * via the `absolutejs` binary (see `bin/absolutejs.js` and\n * `src/cli.ts`).\n *\n * Composes with `@absolutejs/secrets` (broker, encrypted file\n * adapter), `@absolutejs/deploy` (Target, Deployer, EnvDeployment),\n * and any other substrate package that satisfies one of the narrow\n * interfaces below.\n */\n\n// =============================================================================\n// Narrow types — match the shapes from @absolutejs/secrets +\n// @absolutejs/deploy without importing them directly. Users pass\n// instances that satisfy these structurally.\n// =============================================================================\n\nexport type SecretValue = { value: string; fingerprint: string };\n\n/**\n * Narrow SecretBroker interface — the SecretBroker from\n * `@absolutejs/secrets` satisfies this structurally.\n */\nexport type CliSecretBroker = {\n\tresolve: (name: string) => Promise<SecretValue | null>;\n\trotate: (name: string) => Promise<SecretValue>;\n\tfingerprint: (value: string) => string;\n};\n\n/**\n * Narrow SecretAdapter interface — for `secrets list` / `secrets set`,\n * which need to write to the underlying store (broker doesn't expose\n * `put`).\n */\nexport type CliSecretAdapter = {\n\tfetch: (name: string) => Promise<string | null>;\n\tlist?: () => Promise<string[]>;\n\tput?: (name: string, value: string) => Promise<void>;\n\tremove?: (name: string) => Promise<void>;\n};\n\nexport type CliTargetExec = (\n\tcmd: string,\n\topts?: {\n\t\tcwd?: string;\n\t\tenv?: Record<string, string>;\n\t\tstdin?: string;\n\t\ttimeoutMs?: number;\n\t}\n) => Promise<{ stdout: string; stderr: string; exitCode: number }>;\n\n/** Narrow Target — @absolutejs/deploy's Target satisfies this. */\nexport type CliTarget = {\n\treadonly description: string;\n\texec: CliTargetExec;\n\tupload?: (\n\t\tlocalPath: string,\n\t\tremotePath: string,\n\t\topts?: { exclude?: string[]; deleteOrphans?: boolean }\n\t) => Promise<void>;\n\tclose?: () => Promise<void>;\n};\n\n/**\n * A deployment entry — names a remote, declares which secrets it\n * consumes, points at a remote env file. Modeled after\n * `EnvDeployment` from `@absolutejs/deploy/env`.\n *\n * The `target` field is a FACTORY (lazy) so a `secrets list` call\n * doesn't accidentally provision a Hetzner box. Only verbs that\n * actually need a remote (env push/pull/diff, deploy rollback)\n * invoke it.\n */\nexport type CliDeployment = {\n\t/** Stage name — `'prod'`, `'staging'`, `'pr-123'`. */\n\tname: string;\n\t/** Lazy target factory. Resolved only when a verb needs it. */\n\ttarget: () => Promise<CliTarget>;\n\t/** Remote env file path. */\n\tremotePath: string;\n\t/** Names of secrets the broker should resolve into this file. */\n\tsecretNames?: ReadonlyArray<string>;\n\t/** Non-secret env vars merged into the file. */\n\textras?: Record<string, string>;\n\t/** File mode. Default `'600'`. */\n\tmode?: string;\n\t/** File owner. */\n\towner?: string;\n\t/** Command run after the env file changes (e.g. `'systemctl reload api'`). */\n\treload?: string;\n\t/**\n\t * Optional Deployer factory — for `deploy rollback / releases / status`.\n\t * Same lazy contract as `target`.\n\t */\n\tdeployer?: () => Promise<CliDeployer>;\n};\n\n/** Narrow Deployer interface — @absolutejs/deploy's Deployer satisfies. */\nexport type CliDeployer = {\n\tlistReleases?: () => Promise<\n\t\tReadonlyArray<{\n\t\t\tid: string;\n\t\t\tat: number;\n\t\t\tactive?: boolean;\n\t\t\tannotations?: Record<string, unknown>;\n\t\t}>\n\t>;\n\trollback?: (releaseId: string) => Promise<void>;\n\tcurrentReleaseId?: () => Promise<string | undefined>;\n};\n\n/** The shape of `absolutejs.config.ts`'s default export. */\nexport type AbsolutejsConfig = {\n\t/** Optional broker — required for any `secrets` verb. */\n\tsecrets?: CliSecretBroker;\n\t/**\n\t * Optional adapter — required for `secrets list` / `secrets set`\n\t * (broker.rotate/resolve don't expose put/list). Pass the same\n\t * adapter you used to build the broker.\n\t */\n\tsecretAdapter?: CliSecretAdapter;\n\t/** Stage deployments. Empty array is valid for `secrets`-only setups. */\n\tdeployments?: ReadonlyArray<CliDeployment>;\n};\n\n/**\n * Author-facing helper for `absolutejs.config.ts`. Pure identity\n * function; exists for type inference + future extension.\n */\nexport const defineConfig = (config: AbsolutejsConfig): AbsolutejsConfig =>\n\tconfig;\n"
|
|
6
|
+
],
|
|
7
|
+
"mappings": ";;AAoIO,IAAM,eAAe,CAAC,WAC5B;",
|
|
8
|
+
"debugId": "552FB5388C7944C764756E2164756E21",
|
|
9
|
+
"names": []
|
|
10
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Discover + load `absolutejs.config.ts` from the current working
|
|
3
|
+
* directory (or its parents). Falls back to `absolutejs.config.js` /
|
|
4
|
+
* `.mjs` if no .ts variant exists. Bun handles TS imports natively;
|
|
5
|
+
* Node would need a loader.
|
|
6
|
+
*/
|
|
7
|
+
import type { AbsolutejsConfig } from './index';
|
|
8
|
+
export type LoadedConfig = {
|
|
9
|
+
config: AbsolutejsConfig;
|
|
10
|
+
path: string;
|
|
11
|
+
};
|
|
12
|
+
export declare const loadConfig: (startDir?: string) => Promise<LoadedConfig>;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Output helpers — pretty tables for humans, raw JSON when `--json`.
|
|
3
|
+
* No color escapes; CI logs render them as garbage.
|
|
4
|
+
*/
|
|
5
|
+
export type OutputMode = 'human' | 'json';
|
|
6
|
+
export declare const renderTable: (headers: string[], rows: ReadonlyArray<ReadonlyArray<string>>) => string;
|
|
7
|
+
export declare const writeOut: (text: string) => void;
|
|
8
|
+
export declare const writeJson: (value: unknown) => void;
|
|
9
|
+
export declare const writeErr: (text: string) => void;
|
|
10
|
+
export declare const formatRelativeTime: (msAgo: number) => string;
|
package/package.json
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@absolutejs/cli",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Substrate CLI for the AbsoluteJS PaaS — secrets / env / deploy verbs over @absolutejs/secrets, @absolutejs/deploy, and @absolutejs/audit. Config-file driven; sibling to @absolutejs/absolute (the framework CLI).",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "git+https://github.com/absolutejs/cli.git"
|
|
8
|
+
},
|
|
9
|
+
"main": "./dist/index.js",
|
|
10
|
+
"module": "./dist/index.js",
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"type": "module",
|
|
13
|
+
"license": "BSL-1.1",
|
|
14
|
+
"author": "Alex Kahn",
|
|
15
|
+
"bin": {
|
|
16
|
+
"absolutejs": "./bin/absolutejs.js"
|
|
17
|
+
},
|
|
18
|
+
"exports": {
|
|
19
|
+
".": {
|
|
20
|
+
"types": "./dist/index.d.ts",
|
|
21
|
+
"import": "./dist/index.js",
|
|
22
|
+
"default": "./dist/index.js"
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"publishConfig": {
|
|
26
|
+
"access": "public"
|
|
27
|
+
},
|
|
28
|
+
"files": [
|
|
29
|
+
"dist",
|
|
30
|
+
"bin",
|
|
31
|
+
"README.md"
|
|
32
|
+
],
|
|
33
|
+
"scripts": {
|
|
34
|
+
"build": "rm -rf dist && bun build src/index.ts src/cli.ts --outdir dist --root src --sourcemap --target=bun && tsc --project tsconfig.build.json",
|
|
35
|
+
"test": "bun test tests/",
|
|
36
|
+
"typecheck": "tsc --noEmit",
|
|
37
|
+
"format": "prettier --write \"./**/*.{ts,json,md}\"",
|
|
38
|
+
"check:package": "bun run typecheck && bun run build && bun run test",
|
|
39
|
+
"release": "bun run format && bun run check:package && bun publish"
|
|
40
|
+
},
|
|
41
|
+
"keywords": [
|
|
42
|
+
"absolutejs",
|
|
43
|
+
"cli",
|
|
44
|
+
"paas",
|
|
45
|
+
"deploy",
|
|
46
|
+
"secrets",
|
|
47
|
+
"rotation"
|
|
48
|
+
],
|
|
49
|
+
"peerDependencies": {
|
|
50
|
+
"@absolutejs/deploy": ">= 0.9.0",
|
|
51
|
+
"@absolutejs/secrets": ">= 0.5.0",
|
|
52
|
+
"bun-types": "^1.3.14"
|
|
53
|
+
},
|
|
54
|
+
"peerDependenciesMeta": {
|
|
55
|
+
"@absolutejs/deploy": {
|
|
56
|
+
"optional": true
|
|
57
|
+
},
|
|
58
|
+
"@absolutejs/secrets": {
|
|
59
|
+
"optional": true
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
"devDependencies": {
|
|
63
|
+
"@absolutejs/deploy": "^0.9.0",
|
|
64
|
+
"@absolutejs/secrets": "^0.5.0",
|
|
65
|
+
"@types/bun": "^1.3.14",
|
|
66
|
+
"prettier": "^3.8.3",
|
|
67
|
+
"typescript": "^5.9.0"
|
|
68
|
+
}
|
|
69
|
+
}
|