@ekanos/cli 0.1.5 → 0.1.6
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/README.md +79 -0
- package/dist/bin.js +14 -1
- package/dist/bin.js.map +1 -1
- package/dist/commands/dev.d.ts +4 -0
- package/dist/commands/dev.js +6 -20
- package/dist/commands/dev.js.map +1 -1
- package/dist/commands/init.d.ts +25 -0
- package/dist/commands/init.js +19 -8
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/publish.d.ts +13 -0
- package/dist/commands/publish.js +38 -15
- package/dist/commands/publish.js.map +1 -1
- package/dist/commands/status.js +9 -2
- package/dist/commands/status.js.map +1 -1
- package/dist/commands/upgrade.d.ts +47 -0
- package/dist/commands/upgrade.js +445 -0
- package/dist/commands/upgrade.js.map +1 -0
- package/dist/commands/validate.d.ts +5 -0
- package/dist/commands/validate.js +20 -6
- package/dist/commands/validate.js.map +1 -1
- package/dist/context.d.ts +9 -0
- package/dist/context.js +9 -0
- package/dist/context.js.map +1 -1
- package/dist/delegate.d.ts +28 -0
- package/dist/delegate.js +136 -0
- package/dist/delegate.js.map +1 -0
- package/dist/harness-scaffold.d.ts +21 -6
- package/dist/harness-scaffold.js +8 -5
- package/dist/harness-scaffold.js.map +1 -1
- package/dist/index.js +35 -2
- package/dist/index.js.map +1 -1
- package/dist/package-manager.d.ts +10 -0
- package/dist/package-manager.js +32 -0
- package/dist/package-manager.js.map +1 -1
- package/dist/schema-skew.d.ts +77 -0
- package/dist/schema-skew.js +163 -0
- package/dist/schema-skew.js.map +1 -0
- package/dist/toolchain-api.d.ts +54 -0
- package/dist/toolchain-api.js +58 -0
- package/dist/toolchain-api.js.map +1 -0
- package/dist/toolchain-resolve.d.ts +26 -0
- package/dist/toolchain-resolve.js +100 -0
- package/dist/toolchain-resolve.js.map +1 -0
- package/dist/toolchain.d.ts +21 -0
- package/dist/toolchain.js +16 -0
- package/dist/toolchain.js.map +1 -0
- package/dist/update-notice.d.ts +32 -0
- package/dist/update-notice.js +180 -0
- package/dist/update-notice.js.map +1 -0
- package/dist/validate-findings.d.ts +12 -0
- package/dist/validate-findings.js +12 -0
- package/dist/validate-findings.js.map +1 -1
- package/package.json +1 -1
- package/templates/AGENTS.md.tmpl +24 -0
- package/templates/CLAUDE.md.tmpl +2 -1
- package/templates/claude-skill.md.tmpl +13 -2
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["import * as fs from 'node:fs';\n\nimport { runDev } from './commands/dev';\nimport { runInit } from './commands/init';\nimport { runLogin } from './commands/login';\nimport { runLogout } from './commands/logout';\nimport { runPublish } from './commands/publish';\nimport { runSources } from './commands/sources';\nimport { runStatus } from './commands/status';\nimport { runTest } from './commands/test';\nimport { runUse } from './commands/use';\nimport { runValidate } from './commands/validate';\nimport { runWhoami } from './commands/whoami';\nimport { CliContext, type WriteStream } from './context';\nimport { toCliError, usageError } from './errors';\nimport { EXIT_CODES } from './exit-codes';\nimport { type FlagSpecs, parseArgv } from './parse-argv';\n\nexport { CliContext } from './context';\nexport { EXIT_CODES, ERROR_CODES } from './exit-codes';\nexport type { ExitCode, ErrorCode } from './exit-codes';\n\nconst GLOBAL_SPECS: FlagSpecs = {\n json: { type: 'boolean' },\n help: { type: 'boolean' },\n version: { type: 'boolean' },\n};\n\nconst COMMAND_SPECS: Record<string, FlagSpecs> = {\n init: {\n slug: { type: 'string' },\n dir: { type: 'string' },\n force: { type: 'boolean' },\n yes: { type: 'boolean' },\n },\n validate: {\n dir: { type: 'string' },\n },\n dev: {\n dir: { type: 'string' },\n port: { type: 'string' },\n host: { type: 'string' },\n force: { type: 'boolean' },\n start: { type: 'boolean' },\n },\n test: {\n dir: { type: 'string' },\n },\n login: {\n host: { type: 'string' },\n force: { type: 'boolean' },\n },\n logout: {\n host: { type: 'string' },\n all: { type: 'boolean' },\n },\n whoami: {\n host: { type: 'string' },\n },\n status: {\n host: { type: 'string' },\n dir: { type: 'string' },\n },\n sources: {\n host: { type: 'string' },\n dir: { type: 'string' },\n },\n use: {\n host: { type: 'string' },\n dir: { type: 'string' },\n },\n publish: {\n host: { type: 'string' },\n source: { type: 'string' },\n dir: { type: 'string' },\n yes: { type: 'boolean' },\n },\n};\n\n/** Commands that take a positional argument, and what it means. Every other command is positional-free. */\nconst POSITIONAL_COMMANDS: Record<string, string> = {\n use: 'source-slug',\n};\n\nconst COMMANDS = Object.keys(COMMAND_SPECS);\n\nconst USAGE = [\n 'Usage: ekanos <command> [options]',\n '',\n 'Commands:',\n ' init Scaffold a new integration project into ./<slug> (--slug <slug> --yes)',\n ' validate Validate ekanos.json + the integration definition (--json)',\n ' dev Scaffold .ekanos/harness and run it with your own Next',\n ' test Run the project test script via its package manager (--json)',\n ' login Authenticate against a Fusion host in a browser (--host <url>)',\n ' logout Revoke and delete the stored session (--host <url> | --all)',\n ' whoami Print the identity the stored session authenticates as, plus',\n ' a compact list of the sources you hold a seat on',\n \" status Show login state and this project's submissions on the host\",\n ' sources List every source you hold a developer/admin seat on',\n ' use Set this project\\'s publish target (\"ekanos use <source-slug>\")',\n ' publish Validate, pack and submit the integration to a Fusion host',\n '',\n 'Host resolution (logout, whoami, status, sources, use, publish):',\n ' --host, else EKANOS_HOST, else the \"host\" field in ekanos.json, else the',\n ' sole stored login. \"ekanos publish\" saves host and source to ekanos.json.',\n ' \"ekanos login\" never reads ekanos.json for a host (--host, else',\n ' EKANOS_HOST, else the sole stored login) — it is the verb that creates',\n ' credentials, so a committed file must never choose where they go.',\n '',\n 'Publish targeting (see \"ekanos sources\" / \"ekanos use\"):',\n ' publish refuses (FORBIDDEN) a source you hold no seat on. With multiple',\n ' seats and an implicit (ekanos.json) source, a human run asks to confirm',\n ' the target; a scripted/JSON run needs --source or --yes to proceed —',\n ' it never prompts and never guesses.',\n '',\n 'Global options:',\n ' --json Emit a single JSON envelope on stdout (auto-on when piped);',\n ' --no-json forces the human-readable form',\n ' --version Print the CLI version',\n ' --help Show this help',\n].join('\\n');\n\nexport interface RunOptions {\n stdout?: WriteStream;\n stderr?: WriteStream;\n env?: Record<string, string | undefined>;\n cwd?: string;\n /**\n * Test-only overrides for `publish`'s interactive-confirm gate — see\n * `PublishArgs` in `commands/publish.ts`. Never set these from real CLI\n * wiring; they exist so a test can exercise the human y/N confirm branch\n * without a real terminal.\n */\n stdinIsTTY?: boolean;\n confirm?: (question: string) => Promise<boolean>;\n}\n\n/**\n * The single entry point. Resolves the agent-native envelope once, dispatches\n * to a verb, and guarantees exactly one terminal emission (success or error).\n * Returns the process exit code — `bin.ts` is the only place that calls\n * `process.exit`, so this stays fully testable.\n */\nexport async function run(\n argv: readonly string[],\n options: RunOptions = {},\n): Promise<number> {\n const jsonFlag = scanJsonFlag(argv);\n const ctx = new CliContext({\n jsonFlag,\n stdout: options.stdout,\n stderr: options.stderr,\n env: options.env,\n });\n const cwd = options.cwd ?? process.cwd();\n const env = options.env ?? process.env;\n\n try {\n // `--version` is global and verb-independent, so it is answered before the\n // command is even resolved: `ekanos --version` and `ekanos validate\n // --version` both report the tool's version and exit 0.\n if (scanFlagPresence(argv, 'version')) {\n return emitVersion(ctx);\n }\n\n const { command, rest } = splitCommand(argv);\n\n // Top-level help / no command.\n if (!command) {\n if (scanFlagPresence(rest, 'help') || argv.length === 0) {\n return emitHelp(ctx);\n }\n // A leading token that is not a command (e.g. `--nonsense`).\n ctx.emitClaudeCodeHint({\n tool: 'ekanos',\n error: 'unknown-command',\n commands: COMMANDS,\n usage: USAGE,\n });\n throw usageError(\n `Unknown command or flag \"${argv[0]}\".`,\n `Run one of: ${COMMANDS.join(', ')}. See \"ekanos --help\".`,\n );\n }\n\n if (!COMMANDS.includes(command)) {\n ctx.emitClaudeCodeHint({\n tool: 'ekanos',\n error: 'unknown-command',\n given: command,\n commands: COMMANDS,\n usage: USAGE,\n });\n throw usageError(\n `Unknown command \"${command}\".`,\n `Run one of: ${COMMANDS.join(', ')}. See \"ekanos --help\".`,\n );\n }\n\n const specs: FlagSpecs = { ...GLOBAL_SPECS, ...COMMAND_SPECS[command] };\n const parsed = parseArgv(rest, specs);\n\n if (parsed.flags.help === true) {\n return emitCommandHelp(ctx, command);\n }\n\n const positionalName = POSITIONAL_COMMANDS[command];\n\n if (!positionalName && parsed.positionals.length > 0) {\n throw usageError(\n `\"${command}\" takes no positional arguments (got \"${parsed.positionals[0]}\").`,\n `Pass options as flags, e.g. \"--dir <path>\". See \"ekanos ${command} --help\".`,\n );\n }\n\n if (positionalName && parsed.positionals.length > 1) {\n throw usageError(\n `\"${command}\" takes at most one positional argument (a ` +\n `${positionalName}), got ${parsed.positionals.length}.`,\n `Pass exactly one, e.g. \"ekanos ${command} <${positionalName}>\". ` +\n `See \"ekanos ${command} --help\".`,\n );\n }\n\n const dir = (parsed.flags.dir as string | undefined) ?? cwd;\n\n switch (command) {\n case 'init':\n return runInit(ctx, {\n slug: parsed.flags.slug as string | undefined,\n dir: parsed.flags.dir as string | undefined,\n cwd,\n force: parsed.flags.force === true,\n yes: parsed.flags.yes === true,\n });\n case 'validate':\n return await runValidate(ctx, { dir });\n case 'dev':\n return await runDev(ctx, {\n dir,\n port: parsed.flags.port as string | undefined,\n host: parsed.flags.host as string | undefined,\n force: parsed.flags.force === true,\n start: parsed.flags.start as boolean | undefined,\n });\n case 'test':\n return await runTest(ctx, { dir, passthrough: parsed.passthrough });\n case 'login':\n return await runLogin(ctx, {\n host: parsed.flags.host as string | undefined,\n force: parsed.flags.force === true,\n cwd,\n env,\n });\n case 'logout':\n return await runLogout(ctx, {\n host: parsed.flags.host as string | undefined,\n all: parsed.flags.all === true,\n cwd,\n env,\n });\n case 'whoami':\n return await runWhoami(ctx, {\n host: parsed.flags.host as string | undefined,\n cwd,\n env,\n });\n case 'status':\n return await runStatus(ctx, {\n host: parsed.flags.host as string | undefined,\n dir,\n env,\n });\n case 'sources':\n return await runSources(ctx, {\n host: parsed.flags.host as string | undefined,\n dir,\n env,\n });\n case 'use':\n return await runUse(ctx, {\n host: parsed.flags.host as string | undefined,\n dir,\n slug: parsed.positionals[0],\n env,\n });\n case 'publish':\n return await runPublish(ctx, {\n host: parsed.flags.host as string | undefined,\n source: parsed.flags.source as string | undefined,\n dir,\n yes: parsed.flags.yes === true,\n env,\n stdinIsTTY: options.stdinIsTTY,\n confirm: options.confirm,\n });\n default:\n // Unreachable — COMMANDS gate above — but keeps the switch total.\n throw usageError(\n `Unknown command \"${command}\".`,\n `Run one of: ${COMMANDS.join(', ')}.`,\n );\n }\n } catch (error) {\n return ctx.fail(toCliError(error));\n }\n}\n\n/**\n * Presence of `--json` (or `--no-json`) BEFORE the first `--` passthrough.\n *\n * Tri-state on purpose. `undefined` means \"the user expressed no preference\",\n * which is what lets the context fall back to inferring the mode from a\n * non-TTY stdout or CLAUDECODE. Collapsing `--no-json` to `false` here would\n * make it indistinguishable from absence, and the flag could then never turn\n * JSON mode OFF in exactly the cases it exists for.\n */\nfunction scanJsonFlag(argv: readonly string[]): boolean | undefined {\n for (const token of argv) {\n if (token === '--') break;\n if (token === '--json') return true;\n if (token === '--no-json') return false;\n }\n return undefined;\n}\n\nfunction scanFlagPresence(argv: readonly string[], name: string): boolean {\n for (const token of argv) {\n if (token === '--') break;\n if (token === `--${name}`) return true;\n }\n return false;\n}\n\n/**\n * Split argv into the command (first bare token before `--`) and the rest\n * (everything else, order preserved, including global flags and the `--`\n * passthrough). Global flags are all booleans, so any bare token is the\n * command — no value-consumption ambiguity.\n */\nfunction splitCommand(argv: readonly string[]): {\n command: string | null;\n rest: string[];\n} {\n for (let i = 0; i < argv.length; i++) {\n const token = argv[i]!;\n if (token === '--') break;\n if (!token.startsWith('-')) {\n return {\n command: token,\n rest: [...argv.slice(0, i), ...argv.slice(i + 1)],\n };\n }\n }\n return { command: null, rest: [...argv] };\n}\n\n/**\n * The CLI's own version, read from the package manifest at runtime rather than\n * baked in by the build — one source of truth, so a release bump cannot leave\n * `--version` reporting a stale number. The path resolves identically from\n * `src/index.ts` and from the emitted `dist/index.js`, both of which sit one\n * directory below the package root.\n */\nfunction readVersion(): string {\n const manifest = new URL('../package.json', import.meta.url);\n const parsed: unknown = JSON.parse(fs.readFileSync(manifest, 'utf8'));\n\n return typeof parsed === 'object' &&\n parsed !== null &&\n 'version' in parsed &&\n typeof parsed.version === 'string'\n ? parsed.version\n : '0.0.0';\n}\n\nfunction emitVersion(ctx: CliContext): number {\n const version = readVersion();\n\n return ctx.succeed({ name: 'ekanos', version }, version);\n}\n\nfunction emitHelp(ctx: CliContext): number {\n ctx.emitClaudeCodeHint({\n tool: 'ekanos',\n commands: COMMANDS,\n usage: USAGE,\n exitCodes: EXIT_CODES,\n });\n return ctx.succeed({ usage: USAGE, commands: COMMANDS }, USAGE);\n}\n\nfunction emitCommandHelp(ctx: CliContext, command: string): number {\n const specs = COMMAND_SPECS[command] ?? {};\n const flags = Object.keys(specs).map((name) => `--${name}`);\n const help = `Usage: ekanos ${command} [${flags.join('] [')}]`;\n ctx.emitClaudeCodeHint({ tool: 'ekanos', command, flags });\n return ctx.succeed({ command, flags }, help);\n}\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["import * as fs from 'node:fs';\n\nimport { runDev } from './commands/dev';\nimport { runInit } from './commands/init';\nimport { runLogin } from './commands/login';\nimport { runLogout } from './commands/logout';\nimport { runPublish } from './commands/publish';\nimport { runSources } from './commands/sources';\nimport { runStatus } from './commands/status';\nimport { runTest } from './commands/test';\nimport { runUpgrade } from './commands/upgrade';\nimport { runUse } from './commands/use';\nimport { runValidate } from './commands/validate';\nimport { runWhoami } from './commands/whoami';\nimport { CliContext, type WriteStream } from './context';\nimport { toCliError, usageError } from './errors';\nimport { EXIT_CODES } from './exit-codes';\nimport { type FlagSpecs, parseArgv } from './parse-argv';\n\nexport { CliContext } from './context';\nexport { EXIT_CODES, ERROR_CODES } from './exit-codes';\nexport type { ExitCode, ErrorCode } from './exit-codes';\n\nconst GLOBAL_SPECS: FlagSpecs = {\n json: { type: 'boolean' },\n help: { type: 'boolean' },\n version: { type: 'boolean' },\n};\n\nconst COMMAND_SPECS: Record<string, FlagSpecs> = {\n init: {\n slug: { type: 'string' },\n dir: { type: 'string' },\n force: { type: 'boolean' },\n yes: { type: 'boolean' },\n },\n validate: {\n dir: { type: 'string' },\n },\n dev: {\n dir: { type: 'string' },\n port: { type: 'string' },\n host: { type: 'string' },\n force: { type: 'boolean' },\n start: { type: 'boolean' },\n },\n test: {\n dir: { type: 'string' },\n },\n login: {\n host: { type: 'string' },\n force: { type: 'boolean' },\n },\n logout: {\n host: { type: 'string' },\n all: { type: 'boolean' },\n },\n whoami: {\n host: { type: 'string' },\n },\n status: {\n host: { type: 'string' },\n dir: { type: 'string' },\n },\n sources: {\n host: { type: 'string' },\n dir: { type: 'string' },\n },\n use: {\n host: { type: 'string' },\n dir: { type: 'string' },\n },\n publish: {\n host: { type: 'string' },\n source: { type: 'string' },\n dir: { type: 'string' },\n yes: { type: 'boolean' },\n },\n upgrade: {\n dir: { type: 'string' },\n host: { type: 'string' },\n check: { type: 'boolean' },\n yes: { type: 'boolean' },\n // `--no-verify` is the negation of this — parse-argv's `--no-<bool>`\n // convention (same as `--no-json`/`--no-start`), not a flag of its own.\n verify: { type: 'boolean' },\n },\n};\n\n/** Commands that take a positional argument, and what it means. Every other command is positional-free. */\nconst POSITIONAL_COMMANDS: Record<string, string> = {\n use: 'source-slug',\n};\n\nconst COMMANDS = Object.keys(COMMAND_SPECS);\n\nconst USAGE = [\n 'Usage: ekanos <command> [options]',\n '',\n 'Commands:',\n ' init Scaffold a new integration project into ./<slug> (--slug <slug> --yes)',\n ' validate Validate ekanos.json + the integration definition (--json)',\n ' dev Scaffold .ekanos/harness and run it with your own Next',\n ' test Run the project test script via its package manager (--json)',\n ' login Authenticate against a Fusion host in a browser (--host <url>)',\n ' logout Revoke and delete the stored session (--host <url> | --all)',\n ' whoami Print the identity the stored session authenticates as, plus',\n ' a compact list of the sources you hold a seat on',\n \" status Show login state and this project's submissions on the host\",\n ' sources List every source you hold a developer/admin seat on',\n ' use Set this project\\'s publish target (\"ekanos use <source-slug>\")',\n ' publish Validate, pack and submit the integration to a Fusion host',\n ' upgrade Move this toolchain to a coherent target version, with',\n ' verify-and-rollback (--check --yes --no-verify --host <url>)',\n '',\n 'Keeping the toolchain current:',\n ' validate, dev and publish all check for a newer @ekanos/cli in the',\n ' background (never blocking, silenced by EKANOS_NO_UPDATE_CHECK) and warn',\n ' on stderr when one exists. Run \"ekanos upgrade --check\" any time a',\n ' validate finding smells like version skew (an unrecognized-key error you',\n ' are confident is correct) — it is read-only and safe to run freely. A',\n ' bare \"ekanos upgrade\" MUTATES the project (installs the target versions,',\n ' verifies with validate + the test script, rolls back on any failure).',\n '',\n 'Host resolution (logout, whoami, status, sources, use, publish, upgrade):',\n ' --host, else EKANOS_HOST, else the \"host\" field in ekanos.json, else the',\n ' sole stored login. \"ekanos publish\" saves host and source to ekanos.json.',\n ' \"ekanos login\" never reads ekanos.json for a host (--host, else',\n ' EKANOS_HOST, else the sole stored login) — it is the verb that creates',\n ' credentials, so a committed file must never choose where they go.',\n '',\n 'Publish targeting (see \"ekanos sources\" / \"ekanos use\"):',\n ' publish refuses (FORBIDDEN) a source you hold no seat on. With multiple',\n ' seats and an implicit (ekanos.json) source, a human run asks to confirm',\n ' the target; a scripted/JSON run needs --source or --yes to proceed —',\n ' it never prompts and never guesses.',\n '',\n 'Global options:',\n ' --json Emit a single JSON envelope on stdout (auto-on when piped);',\n ' --no-json forces the human-readable form',\n ' --version Print the CLI version',\n ' --help Show this help',\n].join('\\n');\n\nexport interface RunOptions {\n stdout?: WriteStream;\n stderr?: WriteStream;\n env?: Record<string, string | undefined>;\n cwd?: string;\n /**\n * Test-only overrides for `publish`'s interactive-confirm gate — see\n * `PublishArgs` in `commands/publish.ts`. Never set these from real CLI\n * wiring; they exist so a test can exercise the human y/N confirm branch\n * without a real terminal.\n */\n stdinIsTTY?: boolean;\n confirm?: (question: string) => Promise<boolean>;\n}\n\n/**\n * The single entry point. Resolves the agent-native envelope once, dispatches\n * to a verb, and guarantees exactly one terminal emission (success or error).\n * Returns the process exit code — `bin.ts` is the only place that calls\n * `process.exit`, so this stays fully testable.\n */\nexport async function run(\n argv: readonly string[],\n options: RunOptions = {},\n): Promise<number> {\n const jsonFlag = scanJsonFlag(argv);\n const ctx = new CliContext({\n jsonFlag,\n stdout: options.stdout,\n stderr: options.stderr,\n env: options.env,\n });\n const cwd = options.cwd ?? process.cwd();\n const env = options.env ?? process.env;\n\n try {\n // `--version` is global and verb-independent, so it is answered before the\n // command is even resolved: `ekanos --version` and `ekanos validate\n // --version` both report the tool's version and exit 0.\n if (scanFlagPresence(argv, 'version')) {\n return emitVersion(ctx);\n }\n\n const { command, rest } = splitCommand(argv);\n\n // Top-level help / no command.\n if (!command) {\n if (scanFlagPresence(rest, 'help') || argv.length === 0) {\n return emitHelp(ctx);\n }\n // A leading token that is not a command (e.g. `--nonsense`).\n ctx.emitClaudeCodeHint({\n tool: 'ekanos',\n error: 'unknown-command',\n commands: COMMANDS,\n usage: USAGE,\n });\n throw usageError(\n `Unknown command or flag \"${argv[0]}\".`,\n `Run one of: ${COMMANDS.join(', ')}. See \"ekanos --help\".`,\n );\n }\n\n if (!COMMANDS.includes(command)) {\n ctx.emitClaudeCodeHint({\n tool: 'ekanos',\n error: 'unknown-command',\n given: command,\n commands: COMMANDS,\n usage: USAGE,\n });\n throw usageError(\n `Unknown command \"${command}\".`,\n `Run one of: ${COMMANDS.join(', ')}. See \"ekanos --help\".`,\n );\n }\n\n const specs: FlagSpecs = { ...GLOBAL_SPECS, ...COMMAND_SPECS[command] };\n const parsed = parseArgv(rest, specs);\n\n if (parsed.flags.help === true) {\n return emitCommandHelp(ctx, command);\n }\n\n const positionalName = POSITIONAL_COMMANDS[command];\n\n if (!positionalName && parsed.positionals.length > 0) {\n throw usageError(\n `\"${command}\" takes no positional arguments (got \"${parsed.positionals[0]}\").`,\n `Pass options as flags, e.g. \"--dir <path>\". See \"ekanos ${command} --help\".`,\n );\n }\n\n if (positionalName && parsed.positionals.length > 1) {\n throw usageError(\n `\"${command}\" takes at most one positional argument (a ` +\n `${positionalName}), got ${parsed.positionals.length}.`,\n `Pass exactly one, e.g. \"ekanos ${command} <${positionalName}>\". ` +\n `See \"ekanos ${command} --help\".`,\n );\n }\n\n const dir = (parsed.flags.dir as string | undefined) ?? cwd;\n\n switch (command) {\n case 'init':\n return runInit(ctx, {\n slug: parsed.flags.slug as string | undefined,\n dir: parsed.flags.dir as string | undefined,\n cwd,\n force: parsed.flags.force === true,\n yes: parsed.flags.yes === true,\n });\n case 'validate':\n return await runValidate(ctx, { dir, env });\n case 'dev':\n return await runDev(ctx, {\n dir,\n port: parsed.flags.port as string | undefined,\n host: parsed.flags.host as string | undefined,\n force: parsed.flags.force === true,\n start: parsed.flags.start as boolean | undefined,\n env,\n });\n case 'test':\n return await runTest(ctx, { dir, passthrough: parsed.passthrough });\n case 'login':\n return await runLogin(ctx, {\n host: parsed.flags.host as string | undefined,\n force: parsed.flags.force === true,\n cwd,\n env,\n });\n case 'logout':\n return await runLogout(ctx, {\n host: parsed.flags.host as string | undefined,\n all: parsed.flags.all === true,\n cwd,\n env,\n });\n case 'whoami':\n return await runWhoami(ctx, {\n host: parsed.flags.host as string | undefined,\n cwd,\n env,\n });\n case 'status':\n return await runStatus(ctx, {\n host: parsed.flags.host as string | undefined,\n dir,\n env,\n });\n case 'sources':\n return await runSources(ctx, {\n host: parsed.flags.host as string | undefined,\n dir,\n env,\n });\n case 'use':\n return await runUse(ctx, {\n host: parsed.flags.host as string | undefined,\n dir,\n slug: parsed.positionals[0],\n env,\n });\n case 'publish':\n return await runPublish(ctx, {\n host: parsed.flags.host as string | undefined,\n source: parsed.flags.source as string | undefined,\n dir,\n yes: parsed.flags.yes === true,\n env,\n stdinIsTTY: options.stdinIsTTY,\n confirm: options.confirm,\n });\n case 'upgrade':\n return await runUpgrade(ctx, {\n dir,\n host: parsed.flags.host as string | undefined,\n env,\n check: parsed.flags.check === true,\n yes: parsed.flags.yes === true,\n noVerify: parsed.flags.verify === false,\n stdinIsTTY: options.stdinIsTTY,\n confirm: options.confirm,\n });\n default:\n // Unreachable — COMMANDS gate above — but keeps the switch total.\n throw usageError(\n `Unknown command \"${command}\".`,\n `Run one of: ${COMMANDS.join(', ')}.`,\n );\n }\n } catch (error) {\n return ctx.fail(toCliError(error));\n }\n}\n\n/**\n * Presence of `--json` (or `--no-json`) BEFORE the first `--` passthrough.\n *\n * Tri-state on purpose. `undefined` means \"the user expressed no preference\",\n * which is what lets the context fall back to inferring the mode from a\n * non-TTY stdout or CLAUDECODE. Collapsing `--no-json` to `false` here would\n * make it indistinguishable from absence, and the flag could then never turn\n * JSON mode OFF in exactly the cases it exists for.\n */\nfunction scanJsonFlag(argv: readonly string[]): boolean | undefined {\n for (const token of argv) {\n if (token === '--') break;\n if (token === '--json') return true;\n if (token === '--no-json') return false;\n }\n return undefined;\n}\n\nfunction scanFlagPresence(argv: readonly string[], name: string): boolean {\n for (const token of argv) {\n if (token === '--') break;\n if (token === `--${name}`) return true;\n }\n return false;\n}\n\n/**\n * Split argv into the command (first bare token before `--`) and the rest\n * (everything else, order preserved, including global flags and the `--`\n * passthrough). Global flags are all booleans, so any bare token is the\n * command — no value-consumption ambiguity.\n */\nfunction splitCommand(argv: readonly string[]): {\n command: string | null;\n rest: string[];\n} {\n for (let i = 0; i < argv.length; i++) {\n const token = argv[i]!;\n if (token === '--') break;\n if (!token.startsWith('-')) {\n return {\n command: token,\n rest: [...argv.slice(0, i), ...argv.slice(i + 1)],\n };\n }\n }\n return { command: null, rest: [...argv] };\n}\n\n/**\n * The CLI's own version, read from the package manifest at runtime rather than\n * baked in by the build — one source of truth, so a release bump cannot leave\n * `--version` reporting a stale number. The path resolves identically from\n * `src/index.ts` and from the emitted `dist/index.js`, both of which sit one\n * directory below the package root.\n */\nfunction readVersion(): string {\n const manifest = new URL('../package.json', import.meta.url);\n const parsed: unknown = JSON.parse(fs.readFileSync(manifest, 'utf8'));\n\n return typeof parsed === 'object' &&\n parsed !== null &&\n 'version' in parsed &&\n typeof parsed.version === 'string'\n ? parsed.version\n : '0.0.0';\n}\n\nfunction emitVersion(ctx: CliContext): number {\n const version = readVersion();\n\n return ctx.succeed({ name: 'ekanos', version }, version);\n}\n\nfunction emitHelp(ctx: CliContext): number {\n ctx.emitClaudeCodeHint({\n tool: 'ekanos',\n commands: COMMANDS,\n usage: USAGE,\n exitCodes: EXIT_CODES,\n });\n return ctx.succeed({ usage: USAGE, commands: COMMANDS }, USAGE);\n}\n\nfunction emitCommandHelp(ctx: CliContext, command: string): number {\n const specs = COMMAND_SPECS[command] ?? {};\n const flags = Object.keys(specs).map((name) => `--${name}`);\n const help = `Usage: ekanos ${command} [${flags.join('] [')}]`;\n ctx.emitClaudeCodeHint({ tool: 'ekanos', command, flags });\n return ctx.succeed({ command, flags }, help);\n}\n"]}
|
|
@@ -23,3 +23,13 @@ export declare function execArgs(pm: PackageManager, bin: string, args: readonly
|
|
|
23
23
|
export declare function formatCommand(bin: string, args: readonly string[]): string;
|
|
24
24
|
/** The install command that adds `packages` as dev dependencies. */
|
|
25
25
|
export declare function installCommand(pm: PackageManager, packages: readonly string[]): string;
|
|
26
|
+
/**
|
|
27
|
+
* The argv (no shell) for adding `packages` at EXACT versions — used by
|
|
28
|
+
* `ekanos upgrade`, which must land specific pinned versions, never "latest"
|
|
29
|
+
* or a range. `dev` selects `dependencies` vs `devDependencies`, matching
|
|
30
|
+
* wherever the project already declares the package: `upgrade` moves a
|
|
31
|
+
* package to a new version, it never relocates it between the two.
|
|
32
|
+
*/
|
|
33
|
+
export declare function installExactArgs(pm: PackageManager, packages: readonly string[], dev: boolean): string[];
|
|
34
|
+
/** The lockfile this package manager reads and writes, at the project root. */
|
|
35
|
+
export declare function lockfileName(pm: PackageManager): string;
|
package/dist/package-manager.js
CHANGED
|
@@ -54,4 +54,36 @@ export function installCommand(pm, packages) {
|
|
|
54
54
|
return `npm install -D ${spec}`;
|
|
55
55
|
}
|
|
56
56
|
}
|
|
57
|
+
/**
|
|
58
|
+
* The argv (no shell) for adding `packages` at EXACT versions — used by
|
|
59
|
+
* `ekanos upgrade`, which must land specific pinned versions, never "latest"
|
|
60
|
+
* or a range. `dev` selects `dependencies` vs `devDependencies`, matching
|
|
61
|
+
* wherever the project already declares the package: `upgrade` moves a
|
|
62
|
+
* package to a new version, it never relocates it between the two.
|
|
63
|
+
*/
|
|
64
|
+
export function installExactArgs(pm, packages, dev) {
|
|
65
|
+
switch (pm.name) {
|
|
66
|
+
case 'pnpm':
|
|
67
|
+
return ['add', ...(dev ? ['-D'] : []), ...packages];
|
|
68
|
+
case 'yarn':
|
|
69
|
+
return ['add', ...(dev ? ['-D'] : []), ...packages];
|
|
70
|
+
case 'bun':
|
|
71
|
+
return ['add', ...(dev ? ['-d'] : []), ...packages];
|
|
72
|
+
case 'npm':
|
|
73
|
+
return ['install', ...(dev ? ['-D'] : []), ...packages];
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
/** The lockfile this package manager reads and writes, at the project root. */
|
|
77
|
+
export function lockfileName(pm) {
|
|
78
|
+
switch (pm.name) {
|
|
79
|
+
case 'pnpm':
|
|
80
|
+
return 'pnpm-lock.yaml';
|
|
81
|
+
case 'yarn':
|
|
82
|
+
return 'yarn.lock';
|
|
83
|
+
case 'bun':
|
|
84
|
+
return 'bun.lock';
|
|
85
|
+
case 'npm':
|
|
86
|
+
return 'package-lock.json';
|
|
87
|
+
}
|
|
88
|
+
}
|
|
57
89
|
//# sourceMappingURL=package-manager.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"package-manager.js","sourceRoot":"","sources":["../src/package-manager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AASlC;;;;GAIG;AACH,MAAM,UAAU,oBAAoB,CAAC,UAAkB;IACrD,MAAM,GAAG,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC,CAAC;IACzE,IAAI,GAAG,CAAC,gBAAgB,CAAC;QAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC;IAChE,IAAI,GAAG,CAAC,WAAW,CAAC;QAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC;IAC3D,IAAI,GAAG,CAAC,WAAW,CAAC,IAAI,GAAG,CAAC,UAAU,CAAC;QAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC;IAC5E,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC;AACrC,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,QAAQ,CACtB,EAAkB,EAClB,GAAW,EACX,IAAuB;IAEvB,QAAQ,EAAE,CAAC,IAAI,EAAE,CAAC;QAChB,KAAK,MAAM;YACT,OAAO,CAAC,MAAM,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;QAChC,KAAK,MAAM;YACT,OAAO,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;QACxB,KAAK,KAAK;YACR,OAAO,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;QAC7B,KAAK,KAAK;YACR,OAAO,CAAC,MAAM,EAAE,IAAI,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;IACxC,CAAC;AACH,CAAC;AAED,+EAA+E;AAC/E,MAAM,UAAU,aAAa,CAAC,GAAW,EAAE,IAAuB;IAChE,OAAO,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAClC,CAAC;AAED,oEAAoE;AACpE,MAAM,UAAU,cAAc,CAC5B,EAAkB,EAClB,QAA2B;IAE3B,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAChC,QAAQ,EAAE,CAAC,IAAI,EAAE,CAAC;QAChB,KAAK,MAAM;YACT,OAAO,eAAe,IAAI,EAAE,CAAC;QAC/B,KAAK,MAAM;YACT,OAAO,eAAe,IAAI,EAAE,CAAC;QAC/B,KAAK,KAAK;YACR,OAAO,cAAc,IAAI,EAAE,CAAC;QAC9B,KAAK,KAAK;YACR,OAAO,kBAAkB,IAAI,EAAE,CAAC;IACpC,CAAC;AACH,CAAC","sourcesContent":["import * as fs from 'node:fs';\nimport * as path from 'node:path';\n\nexport type PackageManagerName = 'pnpm' | 'yarn' | 'bun' | 'npm';\n\nexport interface PackageManager {\n name: PackageManagerName;\n bin: string;\n}\n\n/**\n * Detect the package manager from the lockfile in the project root, falling\n * back to npm. Only the lockfile is authoritative — we do not parse\n * `packageManager` fields or probe $PATH.\n */\nexport function detectPackageManager(projectDir: string): PackageManager {\n const has = (file: string) => fs.existsSync(path.join(projectDir, file));\n if (has('pnpm-lock.yaml')) return { name: 'pnpm', bin: 'pnpm' };\n if (has('yarn.lock')) return { name: 'yarn', bin: 'yarn' };\n if (has('bun.lockb') || has('bun.lock')) return { name: 'bun', bin: 'bun' };\n return { name: 'npm', bin: 'npm' };\n}\n\n/**\n * The argv for running a project-local binary through a package manager.\n *\n * The harness shell has no `node_modules` of its own — `next` lives in the\n * partner's project root and resolves upward — so we cannot spawn the bin\n * directly by path without guessing a layout (pnpm's is not npm's). Every\n * package manager already knows how to find it; each just spells it\n * differently.\n */\nexport function execArgs(\n pm: PackageManager,\n bin: string,\n args: readonly string[],\n): string[] {\n switch (pm.name) {\n case 'pnpm':\n return ['exec', bin, ...args];\n case 'yarn':\n return [bin, ...args];\n case 'bun':\n return ['x', bin, ...args];\n case 'npm':\n return ['exec', '--', bin, ...args];\n }\n}\n\n/** The same invocation as a display string, for logs and the JSON envelope. */\nexport function formatCommand(bin: string, args: readonly string[]): string {\n return [bin, ...args].join(' ');\n}\n\n/** The install command that adds `packages` as dev dependencies. */\nexport function installCommand(\n pm: PackageManager,\n packages: readonly string[],\n): string {\n const spec = packages.join(' ');\n switch (pm.name) {\n case 'pnpm':\n return `pnpm add -D ${spec}`;\n case 'yarn':\n return `yarn add -D ${spec}`;\n case 'bun':\n return `bun add -d ${spec}`;\n case 'npm':\n return `npm install -D ${spec}`;\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"package-manager.js","sourceRoot":"","sources":["../src/package-manager.ts"],"names":[],"mappings":"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","sourcesContent":["import * as fs from 'node:fs';\nimport * as path from 'node:path';\n\nexport type PackageManagerName = 'pnpm' | 'yarn' | 'bun' | 'npm';\n\nexport interface PackageManager {\n name: PackageManagerName;\n bin: string;\n}\n\n/**\n * Detect the package manager from the lockfile in the project root, falling\n * back to npm. Only the lockfile is authoritative — we do not parse\n * `packageManager` fields or probe $PATH.\n */\nexport function detectPackageManager(projectDir: string): PackageManager {\n const has = (file: string) => fs.existsSync(path.join(projectDir, file));\n if (has('pnpm-lock.yaml')) return { name: 'pnpm', bin: 'pnpm' };\n if (has('yarn.lock')) return { name: 'yarn', bin: 'yarn' };\n if (has('bun.lockb') || has('bun.lock')) return { name: 'bun', bin: 'bun' };\n return { name: 'npm', bin: 'npm' };\n}\n\n/**\n * The argv for running a project-local binary through a package manager.\n *\n * The harness shell has no `node_modules` of its own — `next` lives in the\n * partner's project root and resolves upward — so we cannot spawn the bin\n * directly by path without guessing a layout (pnpm's is not npm's). Every\n * package manager already knows how to find it; each just spells it\n * differently.\n */\nexport function execArgs(\n pm: PackageManager,\n bin: string,\n args: readonly string[],\n): string[] {\n switch (pm.name) {\n case 'pnpm':\n return ['exec', bin, ...args];\n case 'yarn':\n return [bin, ...args];\n case 'bun':\n return ['x', bin, ...args];\n case 'npm':\n return ['exec', '--', bin, ...args];\n }\n}\n\n/** The same invocation as a display string, for logs and the JSON envelope. */\nexport function formatCommand(bin: string, args: readonly string[]): string {\n return [bin, ...args].join(' ');\n}\n\n/** The install command that adds `packages` as dev dependencies. */\nexport function installCommand(\n pm: PackageManager,\n packages: readonly string[],\n): string {\n const spec = packages.join(' ');\n switch (pm.name) {\n case 'pnpm':\n return `pnpm add -D ${spec}`;\n case 'yarn':\n return `yarn add -D ${spec}`;\n case 'bun':\n return `bun add -d ${spec}`;\n case 'npm':\n return `npm install -D ${spec}`;\n }\n}\n\n/**\n * The argv (no shell) for adding `packages` at EXACT versions — used by\n * `ekanos upgrade`, which must land specific pinned versions, never \"latest\"\n * or a range. `dev` selects `dependencies` vs `devDependencies`, matching\n * wherever the project already declares the package: `upgrade` moves a\n * package to a new version, it never relocates it between the two.\n */\nexport function installExactArgs(\n pm: PackageManager,\n packages: readonly string[],\n dev: boolean,\n): string[] {\n switch (pm.name) {\n case 'pnpm':\n return ['add', ...(dev ? ['-D'] : []), ...packages];\n case 'yarn':\n return ['add', ...(dev ? ['-D'] : []), ...packages];\n case 'bun':\n return ['add', ...(dev ? ['-d'] : []), ...packages];\n case 'npm':\n return ['install', ...(dev ? ['-D'] : []), ...packages];\n }\n}\n\n/** The lockfile this package manager reads and writes, at the project root. */\nexport function lockfileName(pm: PackageManager): string {\n switch (pm.name) {\n case 'pnpm':\n return 'pnpm-lock.yaml';\n case 'yarn':\n return 'yarn.lock';\n case 'bun':\n return 'bun.lock';\n case 'npm':\n return 'package-lock.json';\n }\n}\n"]}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import type { CliContext } from './context.js';
|
|
2
|
+
/**
|
|
3
|
+
* The schema-skew gate: `validate` and `publish` parse a partner's
|
|
4
|
+
* declaration with the REAL zod schemas from `@ekanos/integration-schema`,
|
|
5
|
+
* pinned to the EXACT version this build of `@ekanos/cli` was built against.
|
|
6
|
+
* A partner project can end up with a NEWER schema installed than the one
|
|
7
|
+
* this CLI validates with — `@ekanos/sdk` (or the project directly) got
|
|
8
|
+
* upgraded, the CLI did not — and that state is not merely "a little behind".
|
|
9
|
+
* It is actively WRONG: a field the newer schema added parses here as an
|
|
10
|
+
* `unrecognized_keys` zod issue, and the generic "host-assigned fields are
|
|
11
|
+
* never partner-authorable" hint tells the partner to delete code that is
|
|
12
|
+
* completely correct. That happened to a real partner. Failing loudly here
|
|
13
|
+
* beats answering falsely, so this gate runs BEFORE validate or publish do
|
|
14
|
+
* any of their own work — an incompatible pair produces no findings at all,
|
|
15
|
+
* rather than confidently wrong ones.
|
|
16
|
+
*
|
|
17
|
+
* The opposite skew — the project's schema is OLDER than the CLI's — is not
|
|
18
|
+
* dangerous in the same way (the CLI's validation is still correct; the
|
|
19
|
+
* partner just cannot use fields the CLI would recognize yet), so it warns
|
|
20
|
+
* instead of blocking.
|
|
21
|
+
*/
|
|
22
|
+
export declare const INTEGRATION_SCHEMA_PACKAGE = "@ekanos/integration-schema";
|
|
23
|
+
/**
|
|
24
|
+
* The version of `@ekanos/integration-schema` THIS CLI validates with.
|
|
25
|
+
*
|
|
26
|
+
* Two tiers, tried in order:
|
|
27
|
+
*
|
|
28
|
+
* 1. The exact version declared in this CLI's own `package.json`
|
|
29
|
+
* dependencies. In a published build this is a real pinned semver — `pnpm
|
|
30
|
+
* publish` rewrites the monorepo's `workspace:*` protocol to the exact
|
|
31
|
+
* version of the linked package at publish time, which is exactly the
|
|
32
|
+
* "declare it explicitly" contract `compatibility.ts` already commits to
|
|
33
|
+
* for the shell contract.
|
|
34
|
+
* 2. Failing that (this repo's own dev checkout, where the dependency is
|
|
35
|
+
* still the literal `workspace:*` string), fall back to whatever
|
|
36
|
+
* `@ekanos/integration-schema` this CLI itself resolves on disk — the
|
|
37
|
+
* package a workspace install actually links next to the CLI.
|
|
38
|
+
*
|
|
39
|
+
* Returns null — never throws — when neither resolves, which degrades the
|
|
40
|
+
* gate to a no-op exactly like "the project has no node_modules" does. A
|
|
41
|
+
* partner's validate/publish must never fail because THIS lookup couldn't
|
|
42
|
+
* find an answer.
|
|
43
|
+
*/
|
|
44
|
+
export declare function bundledSchemaVersion(): string | null;
|
|
45
|
+
export interface SchemaSkewResult {
|
|
46
|
+
status: 'ok' | 'project-newer' | 'project-older' | 'unresolvable';
|
|
47
|
+
cliSchemaVersion: string | null;
|
|
48
|
+
projectSchemaVersion: string | null;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* What the PROJECT resolves for `@ekanos/integration-schema`, compared
|
|
52
|
+
* against what this CLI bundles.
|
|
53
|
+
*
|
|
54
|
+
* The project rarely depends on the schema package directly — it is a
|
|
55
|
+
* transitive dependency of `@ekanos/sdk` — so two candidates are considered,
|
|
56
|
+
* per the task: a direct top-level resolution (works when a package manager
|
|
57
|
+
* hoists it, or when a partner added it explicitly) AND the version
|
|
58
|
+
* `@ekanos/sdk` itself declares a dependency on (its package.json is always a
|
|
59
|
+
* direct project dependency, so it always resolves at the top level even when
|
|
60
|
+
* the schema package's own copy does not). The greater of whichever resolves
|
|
61
|
+
* is what "the project's installed schema" means here — either could be the
|
|
62
|
+
* copy Node actually loads, and closing the gate on the newer of the two is
|
|
63
|
+
* the safe direction: it only ever asks a partner to upgrade a CLI that is
|
|
64
|
+
* genuinely behind.
|
|
65
|
+
*/
|
|
66
|
+
export declare function detectSchemaSkew(projectDir: string): SchemaSkewResult;
|
|
67
|
+
/**
|
|
68
|
+
* The gate itself. Call before `validate`/`publish` do anything else.
|
|
69
|
+
*
|
|
70
|
+
* Throws PRECONDITION_FAILED (never a validation finding — the definition
|
|
71
|
+
* cannot even be trusted to parse correctly in this state) when the project's
|
|
72
|
+
* schema is newer. Warns on stderr and returns normally for every other
|
|
73
|
+
* status, including the two that mean "nothing to say" (`ok`, `unresolvable`)
|
|
74
|
+
* so callers can uniformly branch on the returned result to decide whether to
|
|
75
|
+
* annotate their own JSON envelope.
|
|
76
|
+
*/
|
|
77
|
+
export declare function assertSchemaNotSkewed(ctx: CliContext, projectDir: string): SchemaSkewResult;
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import * as fs from 'node:fs';
|
|
2
|
+
import * as path from 'node:path';
|
|
3
|
+
import { compareVersions } from './compatibility.js';
|
|
4
|
+
import { preconditionError } from './errors.js';
|
|
5
|
+
import { readInstalledPackageManifest } from './harness-scaffold.js';
|
|
6
|
+
import { templatesDir } from './templates.js';
|
|
7
|
+
/**
|
|
8
|
+
* The schema-skew gate: `validate` and `publish` parse a partner's
|
|
9
|
+
* declaration with the REAL zod schemas from `@ekanos/integration-schema`,
|
|
10
|
+
* pinned to the EXACT version this build of `@ekanos/cli` was built against.
|
|
11
|
+
* A partner project can end up with a NEWER schema installed than the one
|
|
12
|
+
* this CLI validates with — `@ekanos/sdk` (or the project directly) got
|
|
13
|
+
* upgraded, the CLI did not — and that state is not merely "a little behind".
|
|
14
|
+
* It is actively WRONG: a field the newer schema added parses here as an
|
|
15
|
+
* `unrecognized_keys` zod issue, and the generic "host-assigned fields are
|
|
16
|
+
* never partner-authorable" hint tells the partner to delete code that is
|
|
17
|
+
* completely correct. That happened to a real partner. Failing loudly here
|
|
18
|
+
* beats answering falsely, so this gate runs BEFORE validate or publish do
|
|
19
|
+
* any of their own work — an incompatible pair produces no findings at all,
|
|
20
|
+
* rather than confidently wrong ones.
|
|
21
|
+
*
|
|
22
|
+
* The opposite skew — the project's schema is OLDER than the CLI's — is not
|
|
23
|
+
* dangerous in the same way (the CLI's validation is still correct; the
|
|
24
|
+
* partner just cannot use fields the CLI would recognize yet), so it warns
|
|
25
|
+
* instead of blocking.
|
|
26
|
+
*/
|
|
27
|
+
export const INTEGRATION_SCHEMA_PACKAGE = '@ekanos/integration-schema';
|
|
28
|
+
const SDK_PACKAGE = '@ekanos/sdk';
|
|
29
|
+
const SEMVER_LIKE = /^\d+\.\d+\.\d+/;
|
|
30
|
+
function looksLikeSemver(value) {
|
|
31
|
+
return typeof value === 'string' && SEMVER_LIKE.test(value);
|
|
32
|
+
}
|
|
33
|
+
function readJsonFile(filePath) {
|
|
34
|
+
try {
|
|
35
|
+
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
36
|
+
}
|
|
37
|
+
catch (_a) {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* The version of `@ekanos/integration-schema` THIS CLI validates with.
|
|
43
|
+
*
|
|
44
|
+
* Two tiers, tried in order:
|
|
45
|
+
*
|
|
46
|
+
* 1. The exact version declared in this CLI's own `package.json`
|
|
47
|
+
* dependencies. In a published build this is a real pinned semver — `pnpm
|
|
48
|
+
* publish` rewrites the monorepo's `workspace:*` protocol to the exact
|
|
49
|
+
* version of the linked package at publish time, which is exactly the
|
|
50
|
+
* "declare it explicitly" contract `compatibility.ts` already commits to
|
|
51
|
+
* for the shell contract.
|
|
52
|
+
* 2. Failing that (this repo's own dev checkout, where the dependency is
|
|
53
|
+
* still the literal `workspace:*` string), fall back to whatever
|
|
54
|
+
* `@ekanos/integration-schema` this CLI itself resolves on disk — the
|
|
55
|
+
* package a workspace install actually links next to the CLI.
|
|
56
|
+
*
|
|
57
|
+
* Returns null — never throws — when neither resolves, which degrades the
|
|
58
|
+
* gate to a no-op exactly like "the project has no node_modules" does. A
|
|
59
|
+
* partner's validate/publish must never fail because THIS lookup couldn't
|
|
60
|
+
* find an answer.
|
|
61
|
+
*/
|
|
62
|
+
export function bundledSchemaVersion() {
|
|
63
|
+
var _a;
|
|
64
|
+
const cliRoot = path.join(templatesDir(), '..');
|
|
65
|
+
const ownManifest = readJsonFile(path.join(cliRoot, 'package.json'));
|
|
66
|
+
const declared = typeof ownManifest === 'object' && ownManifest !== null
|
|
67
|
+
? ((_a = ownManifest
|
|
68
|
+
.dependencies) !== null && _a !== void 0 ? _a : {})[INTEGRATION_SCHEMA_PACKAGE]
|
|
69
|
+
: undefined;
|
|
70
|
+
if (looksLikeSemver(declared))
|
|
71
|
+
return declared;
|
|
72
|
+
const nested = readJsonFile(path.join(cliRoot, 'node_modules', '@ekanos', 'integration-schema', 'package.json'));
|
|
73
|
+
const nestedVersion = typeof nested === 'object' && nested !== null
|
|
74
|
+
? nested.version
|
|
75
|
+
: undefined;
|
|
76
|
+
return looksLikeSemver(nestedVersion) ? nestedVersion : null;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* What the PROJECT resolves for `@ekanos/integration-schema`, compared
|
|
80
|
+
* against what this CLI bundles.
|
|
81
|
+
*
|
|
82
|
+
* The project rarely depends on the schema package directly — it is a
|
|
83
|
+
* transitive dependency of `@ekanos/sdk` — so two candidates are considered,
|
|
84
|
+
* per the task: a direct top-level resolution (works when a package manager
|
|
85
|
+
* hoists it, or when a partner added it explicitly) AND the version
|
|
86
|
+
* `@ekanos/sdk` itself declares a dependency on (its package.json is always a
|
|
87
|
+
* direct project dependency, so it always resolves at the top level even when
|
|
88
|
+
* the schema package's own copy does not). The greater of whichever resolves
|
|
89
|
+
* is what "the project's installed schema" means here — either could be the
|
|
90
|
+
* copy Node actually loads, and closing the gate on the newer of the two is
|
|
91
|
+
* the safe direction: it only ever asks a partner to upgrade a CLI that is
|
|
92
|
+
* genuinely behind.
|
|
93
|
+
*/
|
|
94
|
+
export function detectSchemaSkew(projectDir) {
|
|
95
|
+
var _a;
|
|
96
|
+
const cliSchemaVersion = bundledSchemaVersion();
|
|
97
|
+
if (!cliSchemaVersion) {
|
|
98
|
+
return {
|
|
99
|
+
status: 'unresolvable',
|
|
100
|
+
cliSchemaVersion: null,
|
|
101
|
+
projectSchemaVersion: null,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
const direct = readInstalledPackageManifest(projectDir, INTEGRATION_SCHEMA_PACKAGE);
|
|
105
|
+
const sdk = readInstalledPackageManifest(projectDir, SDK_PACKAGE);
|
|
106
|
+
const sdkDeclaredSchema = sdk && typeof sdk.manifest === 'object' && sdk.manifest !== null
|
|
107
|
+
? ((_a = sdk.manifest
|
|
108
|
+
.dependencies) !== null && _a !== void 0 ? _a : {})[INTEGRATION_SCHEMA_PACKAGE]
|
|
109
|
+
: undefined;
|
|
110
|
+
const candidates = [direct === null || direct === void 0 ? void 0 : direct.version, sdkDeclaredSchema].filter(looksLikeSemver);
|
|
111
|
+
if (candidates.length === 0) {
|
|
112
|
+
return {
|
|
113
|
+
status: 'unresolvable',
|
|
114
|
+
cliSchemaVersion,
|
|
115
|
+
projectSchemaVersion: null,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
const projectSchemaVersion = candidates.reduce((max, candidate) => compareVersions(candidate, max) > 0 ? candidate : max);
|
|
119
|
+
const cmp = compareVersions(projectSchemaVersion, cliSchemaVersion);
|
|
120
|
+
return {
|
|
121
|
+
status: cmp > 0 ? 'project-newer' : cmp < 0 ? 'project-older' : 'ok',
|
|
122
|
+
cliSchemaVersion,
|
|
123
|
+
projectSchemaVersion,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* The gate itself. Call before `validate`/`publish` do anything else.
|
|
128
|
+
*
|
|
129
|
+
* Throws PRECONDITION_FAILED (never a validation finding — the definition
|
|
130
|
+
* cannot even be trusted to parse correctly in this state) when the project's
|
|
131
|
+
* schema is newer. Warns on stderr and returns normally for every other
|
|
132
|
+
* status, including the two that mean "nothing to say" (`ok`, `unresolvable`)
|
|
133
|
+
* so callers can uniformly branch on the returned result to decide whether to
|
|
134
|
+
* annotate their own JSON envelope.
|
|
135
|
+
*/
|
|
136
|
+
export function assertSchemaNotSkewed(ctx, projectDir) {
|
|
137
|
+
const result = detectSchemaSkew(projectDir);
|
|
138
|
+
if (result.status === 'project-newer') {
|
|
139
|
+
throw preconditionError(`The project's ${INTEGRATION_SCHEMA_PACKAGE}@${result.projectSchemaVersion} ` +
|
|
140
|
+
`is newer than ${result.cliSchemaVersion}, the version this ` +
|
|
141
|
+
`@ekanos/cli build validates with. Findings from "ekanos validate" ` +
|
|
142
|
+
'are UNRELIABLE in this state: a field the newer schema added ' +
|
|
143
|
+
'parses here as an unrecognized key, and the usual hint for that ' +
|
|
144
|
+
'tells you to delete code that is actually correct.',
|
|
145
|
+
// NEVER interpolate the schema version into a "@ekanos/cli@<version>"
|
|
146
|
+
// install spec — the two version completely independently (this build
|
|
147
|
+
// is cli 0.1.6 validating against schema 0.1.4). "ekanos upgrade"
|
|
148
|
+
// resolves the actual coherent set; the manual fallback can only ever
|
|
149
|
+
// be "latest", never a computed number.
|
|
150
|
+
'Upgrade the CLI: run "ekanos upgrade" (it resolves a coherent target ' +
|
|
151
|
+
'version set), or pin it explicitly by hand with ' +
|
|
152
|
+
'"npm i -D @ekanos/cli@latest". Then re-run "ekanos validate" or ' +
|
|
153
|
+
'"ekanos publish".');
|
|
154
|
+
}
|
|
155
|
+
if (result.status === 'project-older') {
|
|
156
|
+
ctx.warn(`ekanos: the installed ${INTEGRATION_SCHEMA_PACKAGE}@${result.projectSchemaVersion} ` +
|
|
157
|
+
`is older than ${result.cliSchemaVersion}, the version this CLI ` +
|
|
158
|
+
'validates with — some fields this CLI recognizes may not be usable ' +
|
|
159
|
+
'yet. Run "ekanos upgrade" to bring the toolchain in sync.');
|
|
160
|
+
}
|
|
161
|
+
return result;
|
|
162
|
+
}
|
|
163
|
+
//# sourceMappingURL=schema-skew.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema-skew.js","sourceRoot":"","sources":["../src/schema-skew.ts"],"names":[],"mappings":"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","sourcesContent":["import * as fs from 'node:fs';\nimport * as path from 'node:path';\n\nimport { compareVersions } from './compatibility';\nimport type { CliContext } from './context';\nimport { preconditionError } from './errors';\nimport { readInstalledPackageManifest } from './harness-scaffold';\nimport { templatesDir } from './templates';\n\n/**\n * The schema-skew gate: `validate` and `publish` parse a partner's\n * declaration with the REAL zod schemas from `@ekanos/integration-schema`,\n * pinned to the EXACT version this build of `@ekanos/cli` was built against.\n * A partner project can end up with a NEWER schema installed than the one\n * this CLI validates with — `@ekanos/sdk` (or the project directly) got\n * upgraded, the CLI did not — and that state is not merely \"a little behind\".\n * It is actively WRONG: a field the newer schema added parses here as an\n * `unrecognized_keys` zod issue, and the generic \"host-assigned fields are\n * never partner-authorable\" hint tells the partner to delete code that is\n * completely correct. That happened to a real partner. Failing loudly here\n * beats answering falsely, so this gate runs BEFORE validate or publish do\n * any of their own work — an incompatible pair produces no findings at all,\n * rather than confidently wrong ones.\n *\n * The opposite skew — the project's schema is OLDER than the CLI's — is not\n * dangerous in the same way (the CLI's validation is still correct; the\n * partner just cannot use fields the CLI would recognize yet), so it warns\n * instead of blocking.\n */\n\nexport const INTEGRATION_SCHEMA_PACKAGE = '@ekanos/integration-schema';\nconst SDK_PACKAGE = '@ekanos/sdk';\n\nconst SEMVER_LIKE = /^\\d+\\.\\d+\\.\\d+/;\n\nfunction looksLikeSemver(value: unknown): value is string {\n return typeof value === 'string' && SEMVER_LIKE.test(value);\n}\n\nfunction readJsonFile(filePath: string): unknown {\n try {\n return JSON.parse(fs.readFileSync(filePath, 'utf8'));\n } catch {\n return null;\n }\n}\n\n/**\n * The version of `@ekanos/integration-schema` THIS CLI validates with.\n *\n * Two tiers, tried in order:\n *\n * 1. The exact version declared in this CLI's own `package.json`\n * dependencies. In a published build this is a real pinned semver — `pnpm\n * publish` rewrites the monorepo's `workspace:*` protocol to the exact\n * version of the linked package at publish time, which is exactly the\n * \"declare it explicitly\" contract `compatibility.ts` already commits to\n * for the shell contract.\n * 2. Failing that (this repo's own dev checkout, where the dependency is\n * still the literal `workspace:*` string), fall back to whatever\n * `@ekanos/integration-schema` this CLI itself resolves on disk — the\n * package a workspace install actually links next to the CLI.\n *\n * Returns null — never throws — when neither resolves, which degrades the\n * gate to a no-op exactly like \"the project has no node_modules\" does. A\n * partner's validate/publish must never fail because THIS lookup couldn't\n * find an answer.\n */\nexport function bundledSchemaVersion(): string | null {\n const cliRoot = path.join(templatesDir(), '..');\n\n const ownManifest = readJsonFile(path.join(cliRoot, 'package.json'));\n const declared =\n typeof ownManifest === 'object' && ownManifest !== null\n ? ((ownManifest as { dependencies?: Record<string, unknown> })\n .dependencies ?? {})[INTEGRATION_SCHEMA_PACKAGE]\n : undefined;\n if (looksLikeSemver(declared)) return declared;\n\n const nested = readJsonFile(\n path.join(\n cliRoot,\n 'node_modules',\n '@ekanos',\n 'integration-schema',\n 'package.json',\n ),\n );\n const nestedVersion =\n typeof nested === 'object' && nested !== null\n ? (nested as { version?: unknown }).version\n : undefined;\n\n return looksLikeSemver(nestedVersion) ? nestedVersion : null;\n}\n\nexport interface SchemaSkewResult {\n status: 'ok' | 'project-newer' | 'project-older' | 'unresolvable';\n cliSchemaVersion: string | null;\n projectSchemaVersion: string | null;\n}\n\n/**\n * What the PROJECT resolves for `@ekanos/integration-schema`, compared\n * against what this CLI bundles.\n *\n * The project rarely depends on the schema package directly — it is a\n * transitive dependency of `@ekanos/sdk` — so two candidates are considered,\n * per the task: a direct top-level resolution (works when a package manager\n * hoists it, or when a partner added it explicitly) AND the version\n * `@ekanos/sdk` itself declares a dependency on (its package.json is always a\n * direct project dependency, so it always resolves at the top level even when\n * the schema package's own copy does not). The greater of whichever resolves\n * is what \"the project's installed schema\" means here — either could be the\n * copy Node actually loads, and closing the gate on the newer of the two is\n * the safe direction: it only ever asks a partner to upgrade a CLI that is\n * genuinely behind.\n */\nexport function detectSchemaSkew(projectDir: string): SchemaSkewResult {\n const cliSchemaVersion = bundledSchemaVersion();\n if (!cliSchemaVersion) {\n return {\n status: 'unresolvable',\n cliSchemaVersion: null,\n projectSchemaVersion: null,\n };\n }\n\n const direct = readInstalledPackageManifest(\n projectDir,\n INTEGRATION_SCHEMA_PACKAGE,\n );\n const sdk = readInstalledPackageManifest(projectDir, SDK_PACKAGE);\n const sdkDeclaredSchema =\n sdk && typeof sdk.manifest === 'object' && sdk.manifest !== null\n ? ((sdk.manifest as { dependencies?: Record<string, unknown> })\n .dependencies ?? {})[INTEGRATION_SCHEMA_PACKAGE]\n : undefined;\n\n const candidates = [direct?.version, sdkDeclaredSchema].filter(\n looksLikeSemver,\n );\n\n if (candidates.length === 0) {\n return {\n status: 'unresolvable',\n cliSchemaVersion,\n projectSchemaVersion: null,\n };\n }\n\n const projectSchemaVersion = candidates.reduce((max, candidate) =>\n compareVersions(candidate, max) > 0 ? candidate : max,\n );\n\n const cmp = compareVersions(projectSchemaVersion, cliSchemaVersion);\n\n return {\n status: cmp > 0 ? 'project-newer' : cmp < 0 ? 'project-older' : 'ok',\n cliSchemaVersion,\n projectSchemaVersion,\n };\n}\n\n/**\n * The gate itself. Call before `validate`/`publish` do anything else.\n *\n * Throws PRECONDITION_FAILED (never a validation finding — the definition\n * cannot even be trusted to parse correctly in this state) when the project's\n * schema is newer. Warns on stderr and returns normally for every other\n * status, including the two that mean \"nothing to say\" (`ok`, `unresolvable`)\n * so callers can uniformly branch on the returned result to decide whether to\n * annotate their own JSON envelope.\n */\nexport function assertSchemaNotSkewed(\n ctx: CliContext,\n projectDir: string,\n): SchemaSkewResult {\n const result = detectSchemaSkew(projectDir);\n\n if (result.status === 'project-newer') {\n throw preconditionError(\n `The project's ${INTEGRATION_SCHEMA_PACKAGE}@${result.projectSchemaVersion} ` +\n `is newer than ${result.cliSchemaVersion}, the version this ` +\n `@ekanos/cli build validates with. Findings from \"ekanos validate\" ` +\n 'are UNRELIABLE in this state: a field the newer schema added ' +\n 'parses here as an unrecognized key, and the usual hint for that ' +\n 'tells you to delete code that is actually correct.',\n // NEVER interpolate the schema version into a \"@ekanos/cli@<version>\"\n // install spec — the two version completely independently (this build\n // is cli 0.1.6 validating against schema 0.1.4). \"ekanos upgrade\"\n // resolves the actual coherent set; the manual fallback can only ever\n // be \"latest\", never a computed number.\n 'Upgrade the CLI: run \"ekanos upgrade\" (it resolves a coherent target ' +\n 'version set), or pin it explicitly by hand with ' +\n '\"npm i -D @ekanos/cli@latest\". Then re-run \"ekanos validate\" or ' +\n '\"ekanos publish\".',\n );\n }\n\n if (result.status === 'project-older') {\n ctx.warn(\n `ekanos: the installed ${INTEGRATION_SCHEMA_PACKAGE}@${result.projectSchemaVersion} ` +\n `is older than ${result.cliSchemaVersion}, the version this CLI ` +\n 'validates with — some fields this CLI recognizes may not be usable ' +\n 'yet. Run \"ekanos upgrade\" to bring the toolchain in sync.',\n );\n }\n\n return result;\n}\n"]}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { type ToolchainNote, type ToolchainVersions } from './toolchain.js';
|
|
2
|
+
/**
|
|
3
|
+
* The CLI's client for the Fusion toolchain-advisory route.
|
|
4
|
+
*
|
|
5
|
+
* ---------------------------------------------------------------------------
|
|
6
|
+
* WIRE CONTRACT — the seam between this package and the Fusion web app.
|
|
7
|
+
*
|
|
8
|
+
* The server side lives in `apps/web/app/api/partner/toolchain/route.ts` and
|
|
9
|
+
* ships on a DIFFERENT BRANCH from this file, same as every other route in
|
|
10
|
+
* `auth/fusion-api.ts` and `publish-api.ts`. Nothing here imports from
|
|
11
|
+
* `apps/web`; the two halves are coupled only by HTTP, and the tests stub
|
|
12
|
+
* `fetch`, never the route.
|
|
13
|
+
*
|
|
14
|
+
* GET /api/partner/toolchain [Authorization: Bearer <access_token>]
|
|
15
|
+
* → 200 { ok: true, data: {
|
|
16
|
+
* current: { cli, sdk, ui, integrationSchema, harness }, // the
|
|
17
|
+
* // coherent
|
|
18
|
+
* // set the
|
|
19
|
+
* // host
|
|
20
|
+
* // recommends
|
|
21
|
+
* minimum: { cli, sdk, ui, integrationSchema, harness }, // below
|
|
22
|
+
* // this the
|
|
23
|
+
* // host
|
|
24
|
+
* // refuses
|
|
25
|
+
* notes?: [{ level: "warn" | "info", message: string }],
|
|
26
|
+
* } }
|
|
27
|
+
* → any other status, or a body that fails to parse or match this shape
|
|
28
|
+
*
|
|
29
|
+
* This endpoint is treated as ADVISORY, not authoritative in the way the auth
|
|
30
|
+
* and submission routes are: a 404 (an older Fusion deployment that predates
|
|
31
|
+
* this route) or a 501 (deliberately unimplemented) are both entirely
|
|
32
|
+
* ordinary, and a 4xx/5xx/network failure/malformed body all mean exactly the
|
|
33
|
+
* same thing to every caller — "the endpoint is unavailable, fall back to the
|
|
34
|
+
* npm registry" — so this module never throws. There is no not-configured
|
|
35
|
+
* distinction to make here the way `cli-config` has: a caller with no way to
|
|
36
|
+
* reach this route at all (no host, no session) never calls it in the first
|
|
37
|
+
* place; see `toolchain-resolve.ts`.
|
|
38
|
+
*
|
|
39
|
+
* Unknown extra fields in the response are tolerated (only the fields above
|
|
40
|
+
* are read); a missing `notes` defaults to an empty array.
|
|
41
|
+
* ---------------------------------------------------------------------------
|
|
42
|
+
*/
|
|
43
|
+
export interface ToolchainReport {
|
|
44
|
+
current: ToolchainVersions;
|
|
45
|
+
minimum: ToolchainVersions;
|
|
46
|
+
notes: ToolchainNote[];
|
|
47
|
+
}
|
|
48
|
+
export type FetchToolchainResult = {
|
|
49
|
+
status: 'ok';
|
|
50
|
+
report: ToolchainReport;
|
|
51
|
+
} | {
|
|
52
|
+
status: 'unavailable';
|
|
53
|
+
};
|
|
54
|
+
export declare function fetchToolchainReport(host: string, accessToken: string): Promise<FetchToolchainResult>;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { request } from './auth/fusion-api.js';
|
|
2
|
+
import { TOOLCHAIN_PACKAGES, } from './toolchain.js';
|
|
3
|
+
export async function fetchToolchainReport(host, accessToken) {
|
|
4
|
+
try {
|
|
5
|
+
const response = await request(`${host}/api/partner/toolchain`, {
|
|
6
|
+
method: 'GET',
|
|
7
|
+
headers: {
|
|
8
|
+
accept: 'application/json',
|
|
9
|
+
authorization: `Bearer ${accessToken}`,
|
|
10
|
+
},
|
|
11
|
+
});
|
|
12
|
+
if (!response.ok)
|
|
13
|
+
return { status: 'unavailable' };
|
|
14
|
+
const body = await response.json();
|
|
15
|
+
const report = parseReport(body);
|
|
16
|
+
return report ? { status: 'ok', report } : { status: 'unavailable' };
|
|
17
|
+
}
|
|
18
|
+
catch (_a) {
|
|
19
|
+
// Network failure, timeout, or a body that is not even JSON — all read
|
|
20
|
+
// the same: the endpoint is unavailable this time, fall back to npm.
|
|
21
|
+
return { status: 'unavailable' };
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
function parseReport(body) {
|
|
25
|
+
if (typeof body !== 'object' || body === null)
|
|
26
|
+
return null;
|
|
27
|
+
const data = body.data;
|
|
28
|
+
if (typeof data !== 'object' || data === null)
|
|
29
|
+
return null;
|
|
30
|
+
const current = parseVersions(data.current);
|
|
31
|
+
const minimum = parseVersions(data.minimum);
|
|
32
|
+
if (!current || !minimum)
|
|
33
|
+
return null;
|
|
34
|
+
const rawNotes = data.notes;
|
|
35
|
+
const notes = Array.isArray(rawNotes) ? rawNotes.filter(isNote) : [];
|
|
36
|
+
return { current, minimum, notes };
|
|
37
|
+
}
|
|
38
|
+
function parseVersions(value) {
|
|
39
|
+
if (typeof value !== 'object' || value === null)
|
|
40
|
+
return null;
|
|
41
|
+
const record = value;
|
|
42
|
+
const result = {};
|
|
43
|
+
for (const key of Object.keys(TOOLCHAIN_PACKAGES)) {
|
|
44
|
+
const raw = record[key];
|
|
45
|
+
if (typeof raw !== 'string' || raw.length === 0)
|
|
46
|
+
return null;
|
|
47
|
+
result[key] = raw;
|
|
48
|
+
}
|
|
49
|
+
return result;
|
|
50
|
+
}
|
|
51
|
+
function isNote(value) {
|
|
52
|
+
if (typeof value !== 'object' || value === null)
|
|
53
|
+
return false;
|
|
54
|
+
const note = value;
|
|
55
|
+
return ((note.level === 'warn' || note.level === 'info') &&
|
|
56
|
+
typeof note.message === 'string');
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=toolchain-api.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"toolchain-api.js","sourceRoot":"","sources":["../src/toolchain-api.ts"],"names":[],"mappings":"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","sourcesContent":["import { request } from './auth/fusion-api';\nimport {\n TOOLCHAIN_PACKAGES,\n type ToolchainNote,\n type ToolchainVersions,\n} from './toolchain';\n\n/**\n * The CLI's client for the Fusion toolchain-advisory route.\n *\n * ---------------------------------------------------------------------------\n * WIRE CONTRACT — the seam between this package and the Fusion web app.\n *\n * The server side lives in `apps/web/app/api/partner/toolchain/route.ts` and\n * ships on a DIFFERENT BRANCH from this file, same as every other route in\n * `auth/fusion-api.ts` and `publish-api.ts`. Nothing here imports from\n * `apps/web`; the two halves are coupled only by HTTP, and the tests stub\n * `fetch`, never the route.\n *\n * GET /api/partner/toolchain [Authorization: Bearer <access_token>]\n * → 200 { ok: true, data: {\n * current: { cli, sdk, ui, integrationSchema, harness }, // the\n * // coherent\n * // set the\n * // host\n * // recommends\n * minimum: { cli, sdk, ui, integrationSchema, harness }, // below\n * // this the\n * // host\n * // refuses\n * notes?: [{ level: \"warn\" | \"info\", message: string }],\n * } }\n * → any other status, or a body that fails to parse or match this shape\n *\n * This endpoint is treated as ADVISORY, not authoritative in the way the auth\n * and submission routes are: a 404 (an older Fusion deployment that predates\n * this route) or a 501 (deliberately unimplemented) are both entirely\n * ordinary, and a 4xx/5xx/network failure/malformed body all mean exactly the\n * same thing to every caller — \"the endpoint is unavailable, fall back to the\n * npm registry\" — so this module never throws. There is no not-configured\n * distinction to make here the way `cli-config` has: a caller with no way to\n * reach this route at all (no host, no session) never calls it in the first\n * place; see `toolchain-resolve.ts`.\n *\n * Unknown extra fields in the response are tolerated (only the fields above\n * are read); a missing `notes` defaults to an empty array.\n * ---------------------------------------------------------------------------\n */\n\nexport interface ToolchainReport {\n current: ToolchainVersions;\n minimum: ToolchainVersions;\n notes: ToolchainNote[];\n}\n\nexport type FetchToolchainResult =\n { status: 'ok'; report: ToolchainReport } | { status: 'unavailable' };\n\nexport async function fetchToolchainReport(\n host: string,\n accessToken: string,\n): Promise<FetchToolchainResult> {\n try {\n const response = await request(`${host}/api/partner/toolchain`, {\n method: 'GET',\n headers: {\n accept: 'application/json',\n authorization: `Bearer ${accessToken}`,\n },\n });\n\n if (!response.ok) return { status: 'unavailable' };\n\n const body: unknown = await response.json();\n const report = parseReport(body);\n\n return report ? { status: 'ok', report } : { status: 'unavailable' };\n } catch {\n // Network failure, timeout, or a body that is not even JSON — all read\n // the same: the endpoint is unavailable this time, fall back to npm.\n return { status: 'unavailable' };\n }\n}\n\nfunction parseReport(body: unknown): ToolchainReport | null {\n if (typeof body !== 'object' || body === null) return null;\n const data = (body as { data?: unknown }).data;\n if (typeof data !== 'object' || data === null) return null;\n\n const current = parseVersions((data as { current?: unknown }).current);\n const minimum = parseVersions((data as { minimum?: unknown }).minimum);\n if (!current || !minimum) return null;\n\n const rawNotes = (data as { notes?: unknown }).notes;\n const notes = Array.isArray(rawNotes) ? rawNotes.filter(isNote) : [];\n\n return { current, minimum, notes };\n}\n\nfunction parseVersions(value: unknown): ToolchainVersions | null {\n if (typeof value !== 'object' || value === null) return null;\n const record = value as Record<string, unknown>;\n const result: Partial<ToolchainVersions> = {};\n\n for (const key of Object.keys(\n TOOLCHAIN_PACKAGES,\n ) as (keyof ToolchainVersions)[]) {\n const raw = record[key];\n if (typeof raw !== 'string' || raw.length === 0) return null;\n result[key] = raw;\n }\n\n return result as ToolchainVersions;\n}\n\nfunction isNote(value: unknown): value is ToolchainNote {\n if (typeof value !== 'object' || value === null) return false;\n const note = value as { level?: unknown; message?: unknown };\n return (\n (note.level === 'warn' || note.level === 'info') &&\n typeof note.message === 'string'\n );\n}\n"]}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { type ToolchainNote, type ToolchainVersions } from './toolchain.js';
|
|
2
|
+
export type ToolchainResolution = {
|
|
3
|
+
status: 'ok';
|
|
4
|
+
source: 'host';
|
|
5
|
+
target: ToolchainVersions;
|
|
6
|
+
minimum: ToolchainVersions;
|
|
7
|
+
notes: ToolchainNote[];
|
|
8
|
+
} | {
|
|
9
|
+
status: 'ok';
|
|
10
|
+
source: 'npm';
|
|
11
|
+
target: ToolchainVersions;
|
|
12
|
+
} | {
|
|
13
|
+
status: 'incoherent';
|
|
14
|
+
detail: string;
|
|
15
|
+
} | {
|
|
16
|
+
status: 'unavailable';
|
|
17
|
+
};
|
|
18
|
+
export interface ResolveToolchainParams {
|
|
19
|
+
host?: string;
|
|
20
|
+
session?: {
|
|
21
|
+
accessToken: string;
|
|
22
|
+
} | null;
|
|
23
|
+
/** Per-request timeout for the underlying fetches. */
|
|
24
|
+
timeoutMs?: number;
|
|
25
|
+
}
|
|
26
|
+
export declare function resolveToolchainTarget(params: ResolveToolchainParams): Promise<ToolchainResolution>;
|