@ekanos/cli 0.3.3 → 0.3.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/dist/auth/session.js +13 -4
  2. package/dist/auth/session.js.map +1 -1
  3. package/dist/banner.d.ts +1 -1
  4. package/dist/banner.js +4 -13
  5. package/dist/banner.js.map +1 -1
  6. package/dist/commands/dev.js +22 -6
  7. package/dist/commands/dev.js.map +1 -1
  8. package/dist/commands/init.js +68 -30
  9. package/dist/commands/init.js.map +1 -1
  10. package/dist/commands/login.js +25 -14
  11. package/dist/commands/login.js.map +1 -1
  12. package/dist/commands/logout.js +30 -4
  13. package/dist/commands/logout.js.map +1 -1
  14. package/dist/commands/publish.js +25 -12
  15. package/dist/commands/publish.js.map +1 -1
  16. package/dist/commands/sources.js +26 -16
  17. package/dist/commands/sources.js.map +1 -1
  18. package/dist/commands/status.d.ts +1 -1
  19. package/dist/commands/status.js +52 -39
  20. package/dist/commands/status.js.map +1 -1
  21. package/dist/commands/test.js +24 -7
  22. package/dist/commands/test.js.map +1 -1
  23. package/dist/commands/upgrade.js +85 -53
  24. package/dist/commands/upgrade.js.map +1 -1
  25. package/dist/commands/use.js +10 -2
  26. package/dist/commands/use.js.map +1 -1
  27. package/dist/commands/validate.js +21 -3
  28. package/dist/commands/validate.js.map +1 -1
  29. package/dist/commands/whoami.js +16 -6
  30. package/dist/commands/whoami.js.map +1 -1
  31. package/dist/context.js +10 -3
  32. package/dist/context.js.map +1 -1
  33. package/dist/pnpm-builds.d.ts +15 -0
  34. package/dist/pnpm-builds.js +48 -0
  35. package/dist/pnpm-builds.js.map +1 -0
  36. package/dist/ui.d.ts +36 -0
  37. package/dist/ui.js +77 -2
  38. package/dist/ui.js.map +1 -1
  39. package/package.json +2 -2
  40. package/templates/AGENTS.md.tmpl +63 -0
  41. package/templates/package.json.tmpl +4 -4
  42. package/templates/pnpm-workspace.yaml.tmpl +12 -5
@@ -1 +1 @@
1
- {"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"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","sourcesContent":["import * as fs from 'node:fs';\nimport * as path from 'node:path';\n\nimport { printBanner } from '../banner';\nimport type { CliContext } from '../context';\nimport { copyTreeVerbatim } from '../copy-tree';\nimport { invalidStateError, usageError } from '../errors';\nimport type { ExitCode } from '../exit-codes';\nimport {\n type Manifest,\n type ManifestNote,\n mergeManifest,\n serializeManifest,\n} from '../merge-manifest';\nimport {\n type EkanosProject,\n docEntryProblem,\n serializeProject,\n} from '../project';\nimport { classifyPath, readFileNoFollow, writeFileNoFollow } from '../safe-fs';\nimport { readTemplate, templatesDir, toDisplayName } from '../templates';\nimport { canWizard, wizard } from '../wizard';\n\n/**\n * A file init did not write, and what the partner should do about it.\n *\n * Every ERROR path in this CLI carries an imperative hint; a skip did not, and\n * that asymmetry was the bug — `skipped package.json (already exists)` hid the\n * loss of eleven dependencies and the project's module type.\n */\nexport interface SkippedFile {\n file: string;\n reason: string;\n hint: string;\n}\n\nexport interface InitArgs {\n slug?: string;\n /**\n * Explicit target directory. When omitted, init scaffolds into `./<slug>`\n * under `cwd` — never into `cwd` itself. A real partner ran `init` in a\n * populated directory and got six files sprayed into it; `--dir .` is the\n * explicit opt-in for that.\n */\n dir?: string;\n cwd: string;\n force: boolean;\n /** Accepted for non-interactive ergonomics; init never prompts regardless. */\n yes: boolean;\n /**\n * Repeatable `--docs` entries — http(s) URLs or project-relative paths to\n * the target API's documentation, written into the scaffolded ekanos.json\n * as `docs`. Absent flag means the field is simply omitted; init never\n * prompts for it.\n */\n docs?: string[];\n /**\n * Test-only override for the clack wizard gate (`canWizard` in\n * `wizard.ts`) — never set from real CLI wiring. See `RunOptions` in\n * `index.ts`.\n */\n stdinIsTTY?: boolean;\n}\n\nconst SLUG_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;\n\n/**\n * The files init writes: template basename → destination path.\n *\n * `package.json` is handled separately (see `writeManifest`) because it is the\n * one file a partner plausibly already has — `npm init -y` before `ekanos\n * init` is the ordinary way to start — and skipping it silently costs them\n * every dependency and script.\n */\n/**\n * The agent-context files. The SDK's day-1 users are coding agents, so the\n * scaffold ships the authoring contract alongside the code: AGENTS.md is the\n * contract, CLAUDE.md points at it, and the Claude Code skill teaches the\n * iterate-validate-publish loop. Version-matched to the CLI because they live\n * in its templates.\n *\n * Exported so `ekanos upgrade` can regenerate exactly this set from the\n * NEWLY installed CLI's templates after a version bump — a stale AGENTS.md\n * teaches an agent yesterday's contract, which is as harmful as a stale\n * binary. One list, so `init` and `upgrade` can never disagree on what \"the\n * agent-context files\" even are.\n */\nexport const AGENT_CONTEXT_FILES: { template: string; dest: string }[] = [\n { template: 'AGENTS.md.tmpl', dest: 'AGENTS.md' },\n { template: 'CLAUDE.md.tmpl', dest: 'CLAUDE.md' },\n { template: 'claude-skill.md.tmpl', dest: '.claude/skills/ekanos/SKILL.md' },\n];\n\n/**\n * The docs-grounding artifacts, exported for the same reason as\n * `AGENT_CONTEXT_FILES`: `upgrade` must create these when they are missing\n * (an older project upgrading into a toolchain whose AGENTS.md and skills\n * now reference them), and one list keeps `init` and `upgrade` from ever\n * disagreeing on what they are. Unlike the agent-context files these are\n * NEVER regenerated — `docs/api-notes.md` is agent/partner-authored after\n * the scaffold and `.mcp.json` may carry local additions — only created\n * when absent.\n */\nexport const DOCS_GROUNDING_FILES: { template: string; dest: string }[] = [\n // The agent-authored snapshot of the target API — see the template itself\n // and AGENTS.md's \"API documentation\" section for the convention.\n { template: 'docs/api-notes.md.tmpl', dest: 'docs/api-notes.md' },\n // Per-project MCP config so Claude Code picks up Context7 (the docs\n // fallback in the resolution ladder) without any global setup.\n { template: 'mcp.json.tmpl', dest: '.mcp.json' },\n];\n\nconst FILES: { template: string; dest: string }[] = [\n { template: 'ekanos.json.tmpl', dest: 'ekanos.json' },\n { template: 'gitignore.tmpl', dest: '.gitignore' },\n // pnpm's build-script approval for esbuild (vitest's dependency). Without\n // it a fresh scaffold's `ekanos test` fails on the first run under pnpm —\n // see the template's own header.\n { template: 'pnpm-workspace.yaml.tmpl', dest: 'pnpm-workspace.yaml' },\n // Without this, the `typecheck` script below prints tsc's help and exits 1 —\n // and, worse, no type error in the scaffold ever surfaces.\n { template: 'tsconfig.json.tmpl', dest: 'tsconfig.json' },\n { template: 'vitest.config.ts.tmpl', dest: 'vitest.config.ts' },\n // Wired via package.json's \"prepare\" script — see setup-hooks.mjs.tmpl's\n // own header for what it does and why it tolerates a non-git directory.\n { template: 'scripts/setup-hooks.mjs.tmpl', dest: 'scripts/setup-hooks.mjs' },\n { template: 'src/integration.ts.tmpl', dest: 'src/integration.ts' },\n {\n template: 'src/integration.test.ts.tmpl',\n dest: 'src/integration.test.ts',\n },\n ...DOCS_GROUNDING_FILES,\n ...AGENT_CONTEXT_FILES,\n];\n\n/**\n * `init` — instantiate a project from the bundled template: ekanos.json, a\n * working minimal defineIntegration, a test, a vitest config (with the\n * required SDK inline block), a .gitignore, and a package.json with real\n * published deps. Idempotent: an existing file is left untouched (reported as\n * skipped) unless --force is passed. Never prompts — a missing --slug fails\n * with a usage error naming the flag.\n *\n * In human mode at a real TTY (`canWizard`), the scaffold runs behind a\n * clack `intro`/spinner/`outro` flow instead of the flat `ctx.log` lines\n * below — see `wizard.ts`. This is a restyle, not a new interaction: the\n * data written to disk and the JSON/human-non-TTY envelope are identical\n * either way, and nothing here ever waits on a prompt.\n */\nexport async function runInit(\n ctx: CliContext,\n args: InitArgs,\n): Promise<ExitCode> {\n const slug = args.slug;\n if (!slug) {\n throw usageError(\n 'init requires a slug.',\n 'Pass it explicitly: \"ekanos init --slug <your-integration> --yes\". ' +\n 'The CLI never prompts, so the slug must be on the command line.',\n );\n }\n if (!SLUG_PATTERN.test(slug)) {\n throw usageError(\n `Invalid slug \"${slug}\".`,\n 'Use kebab-case: lowercase [a-z0-9] segments joined by single hyphens, ' +\n 'e.g. \"acme-crm\".',\n );\n }\n\n // Same rule the ekanos.json schema enforces, applied at the flag so a bad\n // entry fails HERE (usage, exit 2) instead of scaffolding a project whose\n // very first `ekanos validate` cannot even load its config.\n const docs = args.docs ?? [];\n for (const entry of docs) {\n const problem = docEntryProblem(entry);\n if (problem) {\n throw usageError(\n `Invalid --docs value: ${problem}`,\n 'Pass --docs an http(s) URL or a project-relative path, e.g. ' +\n '\"--docs https://developer.example.com/openapi.json\" or ' +\n '\"--docs docs/api.md\". Repeat the flag for multiple entries.',\n );\n }\n }\n\n const useWizard = canWizard(ctx, { stdinIsTTY: args.stdinIsTTY });\n if (useWizard) wizard.intro('ekanos init');\n\n // Default to `./<slug>`, not the cwd: scaffolding six files into whatever\n // directory the partner happened to be sitting in is how a real project got\n // polluted. `--dir .` remains the explicit way to scaffold in place.\n const targetDir =\n args.dir !== undefined\n ? path.resolve(args.cwd, args.dir)\n : path.resolve(args.cwd, slug);\n const vars = { SLUG: slug, DISPLAY_NAME: toDisplayName(slug) };\n\n const created: string[] = [];\n const skipped: SkippedFile[] = [];\n\n // The project root is the guard for every write below: creating it raw is\n // fine (it is the boundary, not inside it), but everything under it goes\n // through safe-fs so a planted symlink refuses loudly instead of teleporting\n // a write outside the project. See safe-fs.ts for the threat model. The one\n // case the guard cannot see is the boundary ITSELF being a symlink — the\n // containment math is lexical against `targetDir`, so a planted link there\n // would relocate every \"contained\" write wholesale. Refuse it. (A symlinked\n // ANCESTOR — macOS's /tmp, a linked home — stays fine: only the leaf is\n // checked.)\n let targetStats: fs.Stats | null = null;\n try {\n targetStats = fs.lstatSync(targetDir);\n } catch {\n targetStats = null; // does not exist yet — ours to create\n }\n if (targetStats?.isSymbolicLink()) {\n throw invalidStateError(\n `The target directory ${targetDir} is a symlink, so the scaffold's ` +\n 'write-containment guarantees cannot hold there.',\n 'Scaffold into the real directory (pass its resolved path to --dir), ' +\n 'or remove the symlink first.',\n );\n }\n fs.mkdirSync(targetDir, { recursive: true });\n\n const spinner = useWizard ? wizard.spinner() : null;\n spinner?.start(`Scaffolding \"${slug}\"…`);\n\n for (const { template, dest } of FILES) {\n const destPath = path.join(targetDir, dest);\n const kind = classifyPath(destPath);\n // A symlink is never \"already exists\": skipping would keep the plant, and\n // writing through it is the attack — writeFileNoFollow refuses it.\n if (kind !== 'missing' && kind !== 'symlink' && !args.force) {\n skipped.push({\n file: dest,\n reason: 'it already exists',\n hint:\n `Compare it against the scaffold and merge anything you are ` +\n `missing, or re-run with --force to overwrite ${dest}.`,\n });\n continue;\n }\n\n writeFileNoFollow(\n targetDir,\n destPath,\n dest === 'ekanos.json'\n ? renderEkanosConfig(vars, docs)\n : readTemplate(template, vars),\n );\n created.push(dest);\n }\n\n const skills = copyBundledSkillTrees(targetDir, args.force);\n created.push(...skills.created);\n skipped.push(...skills.skipped);\n\n const manifest = writeManifest(targetDir, vars, args.force);\n if (manifest.kind === 'created') created.push('package.json');\n\n spinner?.stop(`Scaffolded \"${slug}\"`);\n\n if (useWizard) {\n // Per-file \"created\" detail stays collapsed into the count above — the\n // note surfaces only what needs a partner's attention: skips (they hid\n // real data loss before, see `SkippedFile`'s doc comment) and manifest\n // notes.\n wizard.note(wizardSummaryDetail({ created, skipped, manifest }), 'Summary');\n } else {\n for (const file of created) ctx.log(` created ${file}`);\n if (manifest.kind === 'merged') {\n ctx.log(\n ` merged package.json (added ${manifest.added.length} field${\n manifest.added.length === 1 ? '' : 's'\n })`,\n );\n }\n if (manifest.kind === 'unchanged') {\n ctx.log(' package.json already had everything the scaffold adds');\n }\n for (const skip of skipped) {\n ctx.log(` skipped ${skip.file} — ${skip.reason}. ${skip.hint}`);\n }\n for (const note of manifest.notes) {\n ctx.warn(` ${note.message} ${note.hint}`);\n }\n }\n\n const data = {\n slug,\n dir: targetDir,\n created,\n skipped,\n manifest: {\n action: manifest.kind,\n added: manifest.added,\n kept: manifest.kept,\n notes: manifest.notes,\n },\n };\n\n const summary = summarize({\n slug,\n dir: targetDir,\n created,\n skipped,\n manifestAction: manifest.kind,\n manifestAdded: manifest.added.length,\n noteCount: manifest.notes.length,\n });\n\n printBanner(ctx, 'full');\n\n if (useWizard) {\n const relDir = path.relative(args.cwd, targetDir) || '.';\n wizard.outro(`Next steps:\\n\\n cd ${relDir}\\n ekanos dev`);\n }\n\n return ctx.succeed(data, summary);\n}\n\n/**\n * The clack `note()` body for a successful wizard-mode scaffold — collapses\n * \"created\" down to a count (the flat per-file lines this replaces are the\n * `else` branch above) and surfaces only what needs a partner's attention:\n * skips and manifest notes, each with its full hint.\n */\nfunction wizardSummaryDetail(params: {\n created: string[];\n skipped: SkippedFile[];\n manifest: ManifestOutcome;\n}): string {\n const { created, skipped, manifest } = params;\n const lines: string[] = [\n `${created.length} file${created.length === 1 ? '' : 's'} written`,\n ];\n\n if (manifest.kind === 'merged') {\n lines.push(\n `package.json merged (+${manifest.added.length} field${\n manifest.added.length === 1 ? '' : 's'\n })`,\n );\n } else if (manifest.kind === 'unchanged') {\n lines.push('package.json already had everything the scaffold adds');\n }\n\n if (skipped.length > 0) {\n lines.push('', 'Skipped (already existed):');\n for (const skip of skipped) {\n lines.push(` ${skip.file} — ${skip.hint}`);\n }\n }\n\n for (const note of manifest.notes) {\n lines.push('', `⚠ ${note.message} ${note.hint}`);\n }\n\n return lines.join('\\n');\n}\n\n/**\n * The scaffolded ekanos.json: the template, plus the collected `--docs`\n * entries when there are any. Parse-then-serialize (rather than string\n * surgery) so the written file is exactly what `loadProject` will read back.\n */\nfunction renderEkanosConfig(\n vars: Readonly<Record<string, string>>,\n docs: string[],\n): string {\n const rendered = readTemplate('ekanos.json.tmpl', vars);\n if (docs.length === 0) return rendered;\n const project = JSON.parse(rendered) as EkanosProject;\n return serializeProject({ ...project, docs });\n}\n\n/**\n * Copy every bundled skill tree (`templates/skills/<name>/`) into the\n * scaffolded project at `.claude/skills/<name>/`, verbatim — see\n * `copyTreeVerbatim` for why these are bytes, never rendered templates. The\n * walk is generic: a skill directory added to the published templates ships\n * with no code change here. A CLI build with no `templates/skills/` at all\n * (today's) scaffolds nothing and reports nothing.\n *\n * `skillsRoot` is injectable for tests; real callers take the default.\n */\nexport function copyBundledSkillTrees(\n targetDir: string,\n force: boolean,\n skillsRoot = path.join(templatesDir(), 'skills'),\n onRefusal: 'throw' | 'collect' = 'throw',\n): { created: string[]; skipped: SkippedFile[]; refused: string[] } {\n if (!fs.existsSync(skillsRoot))\n return { created: [], skipped: [], refused: [] };\n\n const created: string[] = [];\n const skipped: SkippedFile[] = [];\n const refused: string[] = [];\n\n const skillDirs = fs\n .readdirSync(skillsRoot, { withFileTypes: true })\n .filter((entry) => entry.isDirectory())\n .map((entry) => entry.name)\n .sort();\n\n for (const name of skillDirs) {\n const destBase = path.join('.claude', 'skills', name);\n const result = copyTreeVerbatim(\n path.join(skillsRoot, name),\n path.join(targetDir, destBase),\n { force, root: targetDir, onRefusal },\n );\n created.push(...result.created.map((rel) => path.join(destBase, rel)));\n refused.push(...result.refused.map((rel) => path.join(destBase, rel)));\n skipped.push(\n ...result.skipped.map((rel) => {\n const file = path.join(destBase, rel);\n return {\n file,\n reason: 'it already exists',\n hint:\n `Compare it against the bundled skill and merge anything you ` +\n `are missing, or re-run with --force to overwrite ${file}.`,\n };\n }),\n );\n }\n\n return { created, skipped, refused };\n}\n\ntype ManifestOutcome = {\n kind: 'created' | 'merged' | 'unchanged';\n added: string[];\n kept: string[];\n notes: ManifestNote[];\n};\n\n/**\n * Write or MERGE package.json.\n *\n * A partner who ran `npm init -y` first had a manifest with none of the\n * scaffold's dependencies and `\"type\": \"commonjs\"` under ESM sources, and the\n * old behaviour reported that as one word: \"skipped\". Merging is additive — a\n * version or script they already set is theirs and is never touched — so the\n * ordinary `npm init -y` path now lands somewhere that installs and runs.\n */\nfunction writeManifest(\n targetDir: string,\n vars: Readonly<Record<string, string>>,\n force: boolean,\n): ManifestOutcome {\n const destPath = path.join(targetDir, 'package.json');\n const rendered = readTemplate('package.json.tmpl', vars);\n\n // readFileNoFollow refuses a symlinked package.json outright, so the write\n // below can never follow one either; null means genuinely absent.\n const existingRaw = readFileNoFollow(targetDir, destPath);\n\n if (existingRaw === null || force) {\n writeFileNoFollow(targetDir, destPath, rendered);\n return { kind: 'created', added: [], kept: [], notes: [] };\n }\n\n const template = JSON.parse(rendered) as Manifest;\n\n let existing: Manifest;\n try {\n existing = JSON.parse(existingRaw) as Manifest;\n } catch (error) {\n // Unparseable: do not guess, and do not clobber. Name the fix.\n return {\n kind: 'unchanged',\n added: [],\n kept: [],\n notes: [\n {\n field: '(root)',\n message: `package.json is not valid JSON: ${\n error instanceof Error ? error.message : String(error)\n }`,\n hint:\n 'Fix the JSON syntax, then re-run \"ekanos init\" to have the ' +\n \"scaffold's dependencies and scripts merged in.\",\n },\n ],\n };\n }\n\n const result = mergeManifest(existing, template);\n if (!result.changed) {\n return {\n kind: 'unchanged',\n added: [],\n kept: result.kept,\n notes: result.notes,\n };\n }\n\n writeFileNoFollow(targetDir, destPath, serializeManifest(result.manifest));\n return {\n kind: 'merged',\n added: result.added,\n kept: result.kept,\n notes: result.notes,\n };\n}\n\nfunction summarize(params: {\n slug: string;\n dir: string;\n created: string[];\n skipped: SkippedFile[];\n manifestAction: ManifestOutcome['kind'];\n manifestAdded: number;\n noteCount: number;\n}): string {\n const parts: string[] = [];\n if (params.created.length > 0) {\n parts.push(\n `${params.created.length} file${params.created.length === 1 ? '' : 's'} written`,\n );\n }\n if (params.manifestAction === 'merged') {\n parts.push(`${params.manifestAdded} package.json field(s) merged`);\n }\n if (params.skipped.length > 0) {\n parts.push(`${params.skipped.length} skipped`);\n }\n if (params.noteCount > 0) {\n parts.push(`${params.noteCount} thing(s) need your attention`);\n }\n\n if (parts.length === 0) {\n return `init: nothing to do — \"${params.slug}\" is already scaffolded in ${params.dir}.`;\n }\n return `init: scaffolded \"${params.slug}\" into ${params.dir} (${parts.join(', ')}).`;\n}\n"]}
1
+ {"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"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","sourcesContent":["import * as fs from 'node:fs';\nimport * as path from 'node:path';\n\nimport { printBanner } from '../banner';\nimport type { CliContext } from '../context';\nimport { copyTreeVerbatim } from '../copy-tree';\nimport { invalidStateError, usageError } from '../errors';\nimport type { ExitCode } from '../exit-codes';\nimport {\n type Manifest,\n type ManifestNote,\n mergeManifest,\n serializeManifest,\n} from '../merge-manifest';\nimport {\n type EkanosProject,\n docEntryProblem,\n serializeProject,\n} from '../project';\nimport { classifyPath, readFileNoFollow, writeFileNoFollow } from '../safe-fs';\nimport { readTemplate, templatesDir, toDisplayName } from '../templates';\nimport { type Ui, displayPath, pluralize } from '../ui';\nimport { canWizard, wizard } from '../wizard';\n\n/**\n * A file init did not write, and what the partner should do about it.\n *\n * Every ERROR path in this CLI carries an imperative hint; a skip did not, and\n * that asymmetry was the bug — `skipped package.json (already exists)` hid the\n * loss of eleven dependencies and the project's module type.\n */\nexport interface SkippedFile {\n file: string;\n reason: string;\n hint: string;\n}\n\nexport interface InitArgs {\n slug?: string;\n /**\n * Explicit target directory. When omitted, init scaffolds into `./<slug>`\n * under `cwd` — never into `cwd` itself. A real partner ran `init` in a\n * populated directory and got six files sprayed into it; `--dir .` is the\n * explicit opt-in for that.\n */\n dir?: string;\n cwd: string;\n force: boolean;\n /** Accepted for non-interactive ergonomics; init never prompts regardless. */\n yes: boolean;\n /**\n * Repeatable `--docs` entries — http(s) URLs or project-relative paths to\n * the target API's documentation, written into the scaffolded ekanos.json\n * as `docs`. Absent flag means the field is simply omitted; init never\n * prompts for it.\n */\n docs?: string[];\n /**\n * Test-only override for the clack wizard gate (`canWizard` in\n * `wizard.ts`) — never set from real CLI wiring. See `RunOptions` in\n * `index.ts`.\n */\n stdinIsTTY?: boolean;\n}\n\nconst SLUG_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;\n\n/**\n * The files init writes: template basename → destination path.\n *\n * `package.json` is handled separately (see `writeManifest`) because it is the\n * one file a partner plausibly already has — `npm init -y` before `ekanos\n * init` is the ordinary way to start — and skipping it silently costs them\n * every dependency and script.\n */\n/**\n * The agent-context files. The SDK's day-1 users are coding agents, so the\n * scaffold ships the authoring contract alongside the code: AGENTS.md is the\n * contract, CLAUDE.md points at it, and the Claude Code skill teaches the\n * iterate-validate-publish loop. Version-matched to the CLI because they live\n * in its templates.\n *\n * Exported so `ekanos upgrade` can regenerate exactly this set from the\n * NEWLY installed CLI's templates after a version bump — a stale AGENTS.md\n * teaches an agent yesterday's contract, which is as harmful as a stale\n * binary. One list, so `init` and `upgrade` can never disagree on what \"the\n * agent-context files\" even are.\n */\nexport const AGENT_CONTEXT_FILES: { template: string; dest: string }[] = [\n { template: 'AGENTS.md.tmpl', dest: 'AGENTS.md' },\n { template: 'CLAUDE.md.tmpl', dest: 'CLAUDE.md' },\n { template: 'claude-skill.md.tmpl', dest: '.claude/skills/ekanos/SKILL.md' },\n];\n\n/**\n * The docs-grounding artifacts, exported for the same reason as\n * `AGENT_CONTEXT_FILES`: `upgrade` must create these when they are missing\n * (an older project upgrading into a toolchain whose AGENTS.md and skills\n * now reference them), and one list keeps `init` and `upgrade` from ever\n * disagreeing on what they are. Unlike the agent-context files these are\n * NEVER regenerated — `docs/api-notes.md` is agent/partner-authored after\n * the scaffold and `.mcp.json` may carry local additions — only created\n * when absent.\n */\nexport const DOCS_GROUNDING_FILES: { template: string; dest: string }[] = [\n // The agent-authored snapshot of the target API — see the template itself\n // and AGENTS.md's \"API documentation\" section for the convention.\n { template: 'docs/api-notes.md.tmpl', dest: 'docs/api-notes.md' },\n // Per-project MCP config so Claude Code picks up Context7 (the docs\n // fallback in the resolution ladder) without any global setup.\n { template: 'mcp.json.tmpl', dest: '.mcp.json' },\n];\n\nconst FILES: { template: string; dest: string }[] = [\n { template: 'ekanos.json.tmpl', dest: 'ekanos.json' },\n { template: 'gitignore.tmpl', dest: '.gitignore' },\n // pnpm's build-script approval for esbuild (vitest's dependency). Without\n // it a fresh scaffold's `ekanos test` fails on the first run under pnpm —\n // see the template's own header.\n { template: 'pnpm-workspace.yaml.tmpl', dest: 'pnpm-workspace.yaml' },\n // Without this, the `typecheck` script below prints tsc's help and exits 1 —\n // and, worse, no type error in the scaffold ever surfaces.\n { template: 'tsconfig.json.tmpl', dest: 'tsconfig.json' },\n { template: 'vitest.config.ts.tmpl', dest: 'vitest.config.ts' },\n // Wired via package.json's \"prepare\" script — see setup-hooks.mjs.tmpl's\n // own header for what it does and why it tolerates a non-git directory.\n { template: 'scripts/setup-hooks.mjs.tmpl', dest: 'scripts/setup-hooks.mjs' },\n { template: 'src/integration.ts.tmpl', dest: 'src/integration.ts' },\n {\n template: 'src/integration.test.ts.tmpl',\n dest: 'src/integration.test.ts',\n },\n ...DOCS_GROUNDING_FILES,\n ...AGENT_CONTEXT_FILES,\n];\n\n/**\n * `init` — instantiate a project from the bundled template: ekanos.json, a\n * working minimal defineIntegration, a test, a vitest config (with the\n * required SDK inline block), a .gitignore, and a package.json with real\n * published deps. Idempotent: an existing file is left untouched (reported as\n * skipped) unless --force is passed. Never prompts — a missing --slug fails\n * with a usage error naming the flag.\n *\n * In human mode at a real TTY (`canWizard`), the scaffold runs behind a\n * clack `intro`/spinner/`outro` flow instead of the flat `ctx.log` lines\n * below — see `wizard.ts`. This is a restyle, not a new interaction: the\n * data written to disk and the JSON/human-non-TTY envelope are identical\n * either way, and nothing here ever waits on a prompt.\n */\nexport async function runInit(\n ctx: CliContext,\n args: InitArgs,\n): Promise<ExitCode> {\n const slug = args.slug;\n if (!slug) {\n throw usageError(\n 'init requires a slug.',\n 'Pass it explicitly: \"ekanos init --slug <your-integration> --yes\". ' +\n 'The CLI never prompts, so the slug must be on the command line.',\n );\n }\n if (!SLUG_PATTERN.test(slug)) {\n throw usageError(\n `Invalid slug \"${slug}\".`,\n 'Use kebab-case: lowercase [a-z0-9] segments joined by single hyphens, ' +\n 'e.g. \"acme-crm\".',\n );\n }\n\n // Same rule the ekanos.json schema enforces, applied at the flag so a bad\n // entry fails HERE (usage, exit 2) instead of scaffolding a project whose\n // very first `ekanos validate` cannot even load its config.\n const docs = args.docs ?? [];\n for (const entry of docs) {\n const problem = docEntryProblem(entry);\n if (problem) {\n throw usageError(\n `Invalid --docs value: ${problem}`,\n 'Pass --docs an http(s) URL or a project-relative path, e.g. ' +\n '\"--docs https://developer.example.com/openapi.json\" or ' +\n '\"--docs docs/api.md\". Repeat the flag for multiple entries.',\n );\n }\n }\n\n const useWizard = canWizard(ctx, { stdinIsTTY: args.stdinIsTTY });\n if (useWizard) wizard.intro('ekanos init');\n\n // Default to `./<slug>`, not the cwd: scaffolding six files into whatever\n // directory the partner happened to be sitting in is how a real project got\n // polluted. `--dir .` remains the explicit way to scaffold in place.\n const targetDir =\n args.dir !== undefined\n ? path.resolve(args.cwd, args.dir)\n : path.resolve(args.cwd, slug);\n const vars = { SLUG: slug, DISPLAY_NAME: toDisplayName(slug) };\n\n const created: string[] = [];\n const skipped: SkippedFile[] = [];\n\n // The project root is the guard for every write below: creating it raw is\n // fine (it is the boundary, not inside it), but everything under it goes\n // through safe-fs so a planted symlink refuses loudly instead of teleporting\n // a write outside the project. See safe-fs.ts for the threat model. The one\n // case the guard cannot see is the boundary ITSELF being a symlink — the\n // containment math is lexical against `targetDir`, so a planted link there\n // would relocate every \"contained\" write wholesale. Refuse it. (A symlinked\n // ANCESTOR — macOS's /tmp, a linked home — stays fine: only the leaf is\n // checked.)\n let targetStats: fs.Stats | null = null;\n try {\n targetStats = fs.lstatSync(targetDir);\n } catch {\n targetStats = null; // does not exist yet — ours to create\n }\n if (targetStats?.isSymbolicLink()) {\n throw invalidStateError(\n `The target directory ${targetDir} is a symlink, so the scaffold's ` +\n 'write-containment guarantees cannot hold there.',\n 'Scaffold into the real directory (pass its resolved path to --dir), ' +\n 'or remove the symlink first.',\n );\n }\n fs.mkdirSync(targetDir, { recursive: true });\n\n const spinner = useWizard ? wizard.spinner() : null;\n spinner?.start(`Scaffolding \"${slug}\"…`);\n\n for (const { template, dest } of FILES) {\n const destPath = path.join(targetDir, dest);\n const kind = classifyPath(destPath);\n // A symlink is never \"already exists\": skipping would keep the plant, and\n // writing through it is the attack — writeFileNoFollow refuses it.\n if (kind !== 'missing' && kind !== 'symlink' && !args.force) {\n skipped.push({\n file: dest,\n reason: 'it already exists',\n hint:\n `Compare it against the scaffold and merge anything you are ` +\n `missing, or re-run with --force to overwrite ${dest}.`,\n });\n continue;\n }\n\n writeFileNoFollow(\n targetDir,\n destPath,\n dest === 'ekanos.json'\n ? renderEkanosConfig(vars, docs)\n : readTemplate(template, vars),\n );\n created.push(dest);\n }\n\n const skills = copyBundledSkillTrees(targetDir, args.force);\n created.push(...skills.created);\n skipped.push(...skills.skipped);\n\n const manifest = writeManifest(targetDir, vars, args.force);\n if (manifest.kind === 'created') created.push('package.json');\n\n spinner?.stop(`Scaffolded \"${slug}\"`);\n\n if (useWizard) {\n // Per-file \"created\" detail stays collapsed into the count above — the\n // note surfaces only what needs a partner's attention: skips (they hid\n // real data loss before, see `SkippedFile`'s doc comment) and manifest\n // notes.\n wizard.note(wizardSummaryDetail({ created, skipped, manifest }), 'Summary');\n } else {\n for (const file of created) ctx.log(` created ${file}`);\n if (manifest.kind === 'merged') {\n ctx.log(\n ` merged package.json (added ${manifest.added.length} field${\n manifest.added.length === 1 ? '' : 's'\n })`,\n );\n }\n if (manifest.kind === 'unchanged') {\n ctx.log(' package.json already had everything the scaffold adds');\n }\n for (const skip of skipped) {\n ctx.log(` skipped ${skip.file} — ${skip.reason}. ${skip.hint}`);\n }\n for (const note of manifest.notes) {\n ctx.warn(` ${note.message} ${note.hint}`);\n }\n }\n\n const data = {\n slug,\n dir: targetDir,\n created,\n skipped,\n manifest: {\n action: manifest.kind,\n added: manifest.added,\n kept: manifest.kept,\n notes: manifest.notes,\n },\n };\n\n printBanner(ctx);\n\n if (useWizard) {\n const relDir = path.relative(args.cwd, targetDir) || '.';\n wizard.outro(`Next steps:\\n\\n cd ${relDir}\\n npm install\\n ekanos dev`);\n }\n\n // The wizard's note + outro already said everything; a trailing line would\n // only repeat them.\n return ctx.succeed(\n data,\n useWizard\n ? ''\n : humanResult(ctx.ui, {\n slug,\n dir: displayPath(targetDir, args.cwd),\n created: created.length,\n skipped: skipped.length,\n manifestAction: manifest.kind,\n manifestAdded: manifest.added.length,\n noteCount: manifest.notes.length,\n }),\n );\n}\n\n/**\n * The clack `note()` body for a successful wizard-mode scaffold — collapses\n * \"created\" down to a count (the flat per-file lines this replaces are the\n * `else` branch above) and surfaces only what needs a partner's attention:\n * skips and manifest notes, each with its full hint.\n */\nfunction wizardSummaryDetail(params: {\n created: string[];\n skipped: SkippedFile[];\n manifest: ManifestOutcome;\n}): string {\n const { created, skipped, manifest } = params;\n const lines: string[] = [\n `${created.length} file${created.length === 1 ? '' : 's'} written`,\n ];\n\n if (manifest.kind === 'merged') {\n lines.push(\n `package.json merged (+${manifest.added.length} field${\n manifest.added.length === 1 ? '' : 's'\n })`,\n );\n } else if (manifest.kind === 'unchanged') {\n lines.push('package.json already had everything the scaffold adds');\n }\n\n if (skipped.length > 0) {\n lines.push('', 'Skipped (already existed):');\n\n // Identical advice that only differs by file name is said once, not\n // once per file — twenty near-identical sentences hide the files.\n const groups = new Map<string, string[]>();\n for (const skip of skipped) {\n const key = skip.hint.split(skip.file).join('{file}');\n groups.set(key, [...(groups.get(key) ?? []), skip.file]);\n }\n\n for (const [hint, files] of groups) {\n if (files.length === 1) {\n lines.push(` ${files[0]} — ${hint.replace('{file}', files[0]!)}`);\n continue;\n }\n lines.push(...files.map((file) => ` ${file}`));\n lines.push(` ${hint.split('{file}').join('each file')}`);\n }\n }\n\n for (const note of manifest.notes) {\n lines.push('', `⚠ ${note.message} ${note.hint}`);\n }\n\n return lines.join('\\n');\n}\n\n/**\n * The scaffolded ekanos.json: the template, plus the collected `--docs`\n * entries when there are any. Parse-then-serialize (rather than string\n * surgery) so the written file is exactly what `loadProject` will read back.\n */\nfunction renderEkanosConfig(\n vars: Readonly<Record<string, string>>,\n docs: string[],\n): string {\n const rendered = readTemplate('ekanos.json.tmpl', vars);\n if (docs.length === 0) return rendered;\n const project = JSON.parse(rendered) as EkanosProject;\n return serializeProject({ ...project, docs });\n}\n\n/**\n * Copy every bundled skill tree (`templates/skills/<name>/`) into the\n * scaffolded project at `.claude/skills/<name>/`, verbatim — see\n * `copyTreeVerbatim` for why these are bytes, never rendered templates. The\n * walk is generic: a skill directory added to the published templates ships\n * with no code change here. A CLI build with no `templates/skills/` at all\n * (today's) scaffolds nothing and reports nothing.\n *\n * `skillsRoot` is injectable for tests; real callers take the default.\n */\nexport function copyBundledSkillTrees(\n targetDir: string,\n force: boolean,\n skillsRoot = path.join(templatesDir(), 'skills'),\n onRefusal: 'throw' | 'collect' = 'throw',\n): { created: string[]; skipped: SkippedFile[]; refused: string[] } {\n if (!fs.existsSync(skillsRoot))\n return { created: [], skipped: [], refused: [] };\n\n const created: string[] = [];\n const skipped: SkippedFile[] = [];\n const refused: string[] = [];\n\n const skillDirs = fs\n .readdirSync(skillsRoot, { withFileTypes: true })\n .filter((entry) => entry.isDirectory())\n .map((entry) => entry.name)\n .sort();\n\n for (const name of skillDirs) {\n const destBase = path.join('.claude', 'skills', name);\n const result = copyTreeVerbatim(\n path.join(skillsRoot, name),\n path.join(targetDir, destBase),\n { force, root: targetDir, onRefusal },\n );\n created.push(...result.created.map((rel) => path.join(destBase, rel)));\n refused.push(...result.refused.map((rel) => path.join(destBase, rel)));\n skipped.push(\n ...result.skipped.map((rel) => {\n const file = path.join(destBase, rel);\n return {\n file,\n reason: 'it already exists',\n hint:\n `Compare it against the bundled skill and merge anything you ` +\n `are missing, or re-run with --force to overwrite ${file}.`,\n };\n }),\n );\n }\n\n return { created, skipped, refused };\n}\n\ntype ManifestOutcome = {\n kind: 'created' | 'merged' | 'unchanged';\n added: string[];\n kept: string[];\n notes: ManifestNote[];\n};\n\n/**\n * Write or MERGE package.json.\n *\n * A partner who ran `npm init -y` first had a manifest with none of the\n * scaffold's dependencies and `\"type\": \"commonjs\"` under ESM sources, and the\n * old behaviour reported that as one word: \"skipped\". Merging is additive — a\n * version or script they already set is theirs and is never touched — so the\n * ordinary `npm init -y` path now lands somewhere that installs and runs.\n */\nfunction writeManifest(\n targetDir: string,\n vars: Readonly<Record<string, string>>,\n force: boolean,\n): ManifestOutcome {\n const destPath = path.join(targetDir, 'package.json');\n const rendered = readTemplate('package.json.tmpl', vars);\n\n // readFileNoFollow refuses a symlinked package.json outright, so the write\n // below can never follow one either; null means genuinely absent.\n const existingRaw = readFileNoFollow(targetDir, destPath);\n\n if (existingRaw === null || force) {\n writeFileNoFollow(targetDir, destPath, rendered);\n return { kind: 'created', added: [], kept: [], notes: [] };\n }\n\n const template = JSON.parse(rendered) as Manifest;\n\n let existing: Manifest;\n try {\n existing = JSON.parse(existingRaw) as Manifest;\n } catch (error) {\n // Unparseable: do not guess, and do not clobber. Name the fix.\n return {\n kind: 'unchanged',\n added: [],\n kept: [],\n notes: [\n {\n field: '(root)',\n message: `package.json is not valid JSON: ${\n error instanceof Error ? error.message : String(error)\n }`,\n hint:\n 'Fix the JSON syntax, then re-run \"ekanos init\" to have the ' +\n \"scaffold's dependencies and scripts merged in.\",\n },\n ],\n };\n }\n\n const result = mergeManifest(existing, template);\n if (!result.changed) {\n return {\n kind: 'unchanged',\n added: [],\n kept: result.kept,\n notes: result.notes,\n };\n }\n\n writeFileNoFollow(targetDir, destPath, serializeManifest(result.manifest));\n return {\n kind: 'merged',\n added: result.added,\n kept: result.kept,\n notes: result.notes,\n };\n}\n\nfunction humanResult(\n ui: Ui,\n params: {\n slug: string;\n dir: string;\n created: number;\n skipped: number;\n manifestAction: ManifestOutcome['kind'];\n manifestAdded: number;\n noteCount: number;\n },\n): string {\n const files = [\n ...(params.created > 0 ? [`${params.created} written`] : []),\n ...(params.manifestAction === 'merged'\n ? [`${pluralize(params.manifestAdded, 'package.json field')} merged`]\n : []),\n ...(params.skipped > 0 ? [`${params.skipped} skipped`] : []),\n ];\n const attention = params.skipped > 0 || params.noteCount > 0;\n\n if (files.length === 0 && params.noteCount === 0) {\n return ui.result({\n title: `${params.slug} is already scaffolded`,\n rows: [['Directory', params.dir]],\n });\n }\n\n return ui.result({\n title: `Scaffolded ${params.slug}`,\n tone: attention ? 'warn' : 'ok',\n rows: [\n ['Directory', params.dir],\n ...(files.length > 0 ? [['Files', files.join(' · ')] as const] : []),\n ...(params.noteCount > 0\n ? [\n [\n 'Attention',\n `${pluralize(params.noteCount, 'note')} above`,\n ] as const,\n ]\n : []),\n [\n 'Next',\n [\n ...(params.dir === '.' ? [] : [`cd ${params.dir}`]),\n 'npm install',\n 'ekanos dev',\n ],\n ],\n ],\n });\n}\n"]}
@@ -4,6 +4,7 @@ import { resolveAuthEnvironment, resolveIdentity } from '../auth/session.js';
4
4
  import { printBanner } from '../banner.js';
5
5
  import { CliError } from '../errors.js';
6
6
  import { ERROR_CODES } from '../exit-codes.js';
7
+ import { shortHost } from '../ui.js';
7
8
  import { canWizard, wizard } from '../wizard.js';
8
9
  /**
9
10
  * `login` — authenticate this machine against an Ekanos deployment via the
@@ -24,7 +25,7 @@ import { canWizard, wizard } from '../wizard.js';
24
25
  * non-TTY mode are byte-for-byte what they were before this existed.
25
26
  */
26
27
  export async function runLogin(ctx, args) {
27
- var _a, _b, _c, _d;
28
+ var _a, _b, _c, _d, _e;
28
29
  // No `projectDir`: login is the one verb that CREATES credentials, so its
29
30
  // host must come from something the developer chose (--host, EKANOS_HOST)
30
31
  // or already trusts (the sole stored login) — never from a committed
@@ -33,12 +34,6 @@ export async function runLogin(ctx, args) {
33
34
  // host because their bearer tokens are looked up per-host from the store:
34
35
  // an unfamiliar host simply has no session to present.
35
36
  const env = resolveAuthEnvironment(ctx, args.host, args.env);
36
- // Whether this host had a session stored BEFORE this command touched
37
- // anything — the sole signal for full banner (first login per host) vs
38
- // mini-mark (repeat), independent of --force or which branch below
39
- // actually produces the success.
40
- const hadSession = env.store.read(env.host) !== null;
41
- const bannerKind = hadSession ? 'mini' : 'full';
42
37
  const useWizard = canWizard(ctx, { stdinIsTTY: args.stdinIsTTY });
43
38
  if (useWizard)
44
39
  wizard.intro('ekanos login');
@@ -47,10 +42,10 @@ export async function runLogin(ctx, args) {
47
42
  if (existing) {
48
43
  const reusable = await tryReuse(env, existing);
49
44
  if (reusable) {
50
- printBanner(ctx, bannerKind);
45
+ printBanner(ctx);
51
46
  const email = (_a = reusable.user.email) !== null && _a !== void 0 ? _a : reusable.user.id;
52
47
  if (useWizard) {
53
- wizard.outro(`Already authenticated to ${env.host} as ${email}.`);
48
+ wizard.outro(`Already signed in as ${email} on ${shortHost(env.host)}.`);
54
49
  }
55
50
  return ctx.succeed({
56
51
  host: env.host,
@@ -58,9 +53,17 @@ export async function runLogin(ctx, args) {
58
53
  user: reusable.user,
59
54
  expiresAt: reusable.expiresAt,
60
55
  credentialsPath: env.store.filePath,
61
- }, `login: already authenticated to ${env.host} as ${email}.`);
56
+ }, useWizard
57
+ ? ''
58
+ : ctx.ui.result({
59
+ title: 'Already signed in',
60
+ rows: [
61
+ ['Account', email],
62
+ ['Host', shortHost(env.host)],
63
+ ],
64
+ }));
62
65
  }
63
- ctx.log('The stored session has expired. Starting a new login…');
66
+ ctx.log(ctx.ui.dim('The stored session has expired. Starting a new login…'));
64
67
  }
65
68
  }
66
69
  const config = await fetchCliAuthConfig(env.host);
@@ -97,9 +100,9 @@ export async function runLogin(ctx, args) {
97
100
  updatedAt: new Date().toISOString(),
98
101
  };
99
102
  env.store.write(env.host, stored);
100
- printBanner(ctx, bannerKind);
103
+ printBanner(ctx);
101
104
  if (useWizard) {
102
- wizard.outro(`Logged in to ${env.host}.`);
105
+ wizard.outro(`Signed in as ${(_d = session.email) !== null && _d !== void 0 ? _d : session.userId} on ${shortHost(env.host)}.`);
103
106
  }
104
107
  return ctx.succeed({
105
108
  host: env.host,
@@ -107,7 +110,15 @@ export async function runLogin(ctx, args) {
107
110
  user: { id: session.userId, email: session.email },
108
111
  expiresAt: session.expiresAt,
109
112
  credentialsPath: env.store.filePath,
110
- }, `login: authenticated to ${env.host} as ${(_d = session.email) !== null && _d !== void 0 ? _d : session.userId}.`);
113
+ }, useWizard
114
+ ? ''
115
+ : ctx.ui.result({
116
+ title: 'Signed in',
117
+ rows: [
118
+ ['Account', (_e = session.email) !== null && _e !== void 0 ? _e : session.userId],
119
+ ['Host', shortHost(env.host)],
120
+ ],
121
+ }));
111
122
  }
112
123
  /**
113
124
  * Probe a stored session. Returns null when it cannot be revived, so `login`
@@ -1 +1 @@
1
- {"version":3,"file":"login.js","sourceRoot":"","sources":["../../src/commands/login.ts"],"names":[],"mappings":"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","sourcesContent":["import type { StoredSession } from '../auth/credential-store';\nimport {\n pollForDeviceTokens,\n startDeviceAuthorization,\n} from '../auth/device-flow';\nimport { createEkanosSession, fetchCliAuthConfig } from '../auth/ekanos-api';\nimport { resolveAuthEnvironment, resolveIdentity } from '../auth/session';\nimport { printBanner } from '../banner';\nimport type { CliContext } from '../context';\nimport { CliError } from '../errors';\nimport { ERROR_CODES } from '../exit-codes';\nimport type { ExitCode } from '../exit-codes';\nimport { canWizard, wizard } from '../wizard';\n\nexport interface LoginArgs {\n host?: string;\n /** Start a new browser login even if a working session is already stored. */\n force: boolean;\n /** Where a project's ekanos.json may sit — read-only here, never written. */\n cwd: string;\n env: Record<string, string | undefined>;\n /**\n * Test-only override for the clack wizard gate (`canWizard` in\n * `wizard.ts`) — never set from real CLI wiring. See `RunOptions` in\n * `index.ts`.\n */\n stdinIsTTY?: boolean;\n}\n\n/**\n * `login` — authenticate this machine against an Ekanos deployment via the\n * Auth0 device authorization flow.\n *\n * Idempotent: with a stored session that still authenticates, it reports that\n * and exits 0 without opening a browser. `--force` skips the check.\n *\n * The user code and verification URL go to STDERR. They have to be visible\n * even when stdout is a pipe into an agent, and stdout in this CLI carries\n * exactly one JSON object.\n *\n * In human mode at a real TTY (`canWizard`), this runs behind a clack\n * `intro`/`outro` flow with a spinner over the approval wait — see\n * `wizard.ts`. The device code and URL themselves stay plain, copy-pasteable\n * text (a clack `note`, not a prompt) either way: a partner needs to select\n * and open them, not interact with them through clack. JSON mode and human\n * non-TTY mode are byte-for-byte what they were before this existed.\n */\nexport async function runLogin(\n ctx: CliContext,\n args: LoginArgs,\n): Promise<ExitCode> {\n // No `projectDir`: login is the one verb that CREATES credentials, so its\n // host must come from something the developer chose (--host, EKANOS_HOST)\n // or already trusts (the sole stored login) — never from a committed\n // ekanos.json, which an attacker can set in any cloned repo to point the\n // device flow at their own tenant. Every other verb may read the project\n // host because their bearer tokens are looked up per-host from the store:\n // an unfamiliar host simply has no session to present.\n const env = resolveAuthEnvironment(ctx, args.host, args.env);\n\n // Whether this host had a session stored BEFORE this command touched\n // anything — the sole signal for full banner (first login per host) vs\n // mini-mark (repeat), independent of --force or which branch below\n // actually produces the success.\n const hadSession = env.store.read(env.host) !== null;\n const bannerKind = hadSession ? 'mini' : 'full';\n\n const useWizard = canWizard(ctx, { stdinIsTTY: args.stdinIsTTY });\n if (useWizard) wizard.intro('ekanos login');\n\n if (!args.force) {\n const existing = env.store.read(env.host);\n\n if (existing) {\n const reusable = await tryReuse(env, existing);\n\n if (reusable) {\n printBanner(ctx, bannerKind);\n\n const email = reusable.user.email ?? reusable.user.id;\n if (useWizard) {\n wizard.outro(`Already authenticated to ${env.host} as ${email}.`);\n }\n\n return ctx.succeed(\n {\n host: env.host,\n status: 'already-authenticated',\n user: reusable.user,\n expiresAt: reusable.expiresAt,\n credentialsPath: env.store.filePath,\n },\n `login: already authenticated to ${env.host} as ${email}.`,\n );\n }\n\n ctx.log('The stored session has expired. Starting a new login…');\n }\n }\n\n const config = await fetchCliAuthConfig(env.host);\n const authorization = await startDeviceAuthorization(config);\n const openUrl =\n authorization.verificationUriComplete ?? authorization.verificationUri;\n\n if (useWizard) {\n const noteLines = [`Open: ${openUrl}`, `Code: ${authorization.userCode}`];\n if (authorization.verificationUriComplete) {\n noteLines.push(\n `(without the pre-filled code: ${authorization.verificationUri})`,\n );\n }\n wizard.note(noteLines.join('\\n'), 'Device login');\n } else {\n ctx.log('');\n ctx.log(` Open: ${openUrl}`);\n ctx.log(` Code: ${authorization.userCode}`);\n\n if (authorization.verificationUriComplete) {\n ctx.log(\n ` (without the pre-filled code: ${authorization.verificationUri})`,\n );\n }\n\n ctx.log('');\n ctx.log('Waiting for approval…');\n }\n\n const spinner = useWizard ? wizard.spinner() : null;\n spinner?.start('Waiting for approval…');\n\n const tokens = await pollForDeviceTokens(config, authorization);\n const session = await createEkanosSession(env.host, tokens.accessToken);\n\n spinner?.stop(`✓ authenticated as ${session.email ?? session.userId}`);\n\n const stored: StoredSession = {\n auth0RefreshToken: tokens.refreshToken,\n accessToken: session.accessToken,\n expiresAt: session.expiresAt,\n userId: session.userId,\n email: session.email,\n updatedAt: new Date().toISOString(),\n };\n\n env.store.write(env.host, stored);\n\n printBanner(ctx, bannerKind);\n\n if (useWizard) {\n wizard.outro(`Logged in to ${env.host}.`);\n }\n\n return ctx.succeed(\n {\n host: env.host,\n status: 'authenticated',\n user: { id: session.userId, email: session.email },\n expiresAt: session.expiresAt,\n credentialsPath: env.store.filePath,\n },\n `login: authenticated to ${env.host} as ${session.email ?? session.userId}.`,\n );\n}\n\n/**\n * Probe a stored session. Returns null when it cannot be revived, so `login`\n * falls through to a fresh device flow rather than failing — \"log in again\"\n * is the whole point of the command being run.\n *\n * Only auth failures are absorbed. A network or configuration error still\n * propagates: silently starting a browser login because the host was\n * unreachable would be the wrong recovery, and would hide the real fault.\n */\nasync function tryReuse(\n env: ReturnType<typeof resolveAuthEnvironment>,\n existing: StoredSession,\n): Promise<{\n user: { id: string; email: string | null };\n expiresAt: string;\n} | null> {\n try {\n const { identity, session } = await resolveIdentity(env, existing);\n\n return {\n user: { id: identity.id, email: identity.email },\n expiresAt: session.expiresAt,\n };\n } catch (error) {\n if (isAuthFailure(error)) {\n return null;\n }\n\n throw error;\n }\n}\n\nfunction isAuthFailure(error: unknown): boolean {\n return error instanceof CliError && error.code === ERROR_CODES.AUTH_REQUIRED;\n}\n"]}
1
+ {"version":3,"file":"login.js","sourceRoot":"","sources":["../../src/commands/login.ts"],"names":[],"mappings":"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","sourcesContent":["import type { StoredSession } from '../auth/credential-store';\nimport {\n pollForDeviceTokens,\n startDeviceAuthorization,\n} from '../auth/device-flow';\nimport { createEkanosSession, fetchCliAuthConfig } from '../auth/ekanos-api';\nimport { resolveAuthEnvironment, resolveIdentity } from '../auth/session';\nimport { printBanner } from '../banner';\nimport type { CliContext } from '../context';\nimport { CliError } from '../errors';\nimport { ERROR_CODES } from '../exit-codes';\nimport type { ExitCode } from '../exit-codes';\nimport { shortHost } from '../ui';\nimport { canWizard, wizard } from '../wizard';\n\nexport interface LoginArgs {\n host?: string;\n /** Start a new browser login even if a working session is already stored. */\n force: boolean;\n /** Where a project's ekanos.json may sit — read-only here, never written. */\n cwd: string;\n env: Record<string, string | undefined>;\n /**\n * Test-only override for the clack wizard gate (`canWizard` in\n * `wizard.ts`) — never set from real CLI wiring. See `RunOptions` in\n * `index.ts`.\n */\n stdinIsTTY?: boolean;\n}\n\n/**\n * `login` — authenticate this machine against an Ekanos deployment via the\n * Auth0 device authorization flow.\n *\n * Idempotent: with a stored session that still authenticates, it reports that\n * and exits 0 without opening a browser. `--force` skips the check.\n *\n * The user code and verification URL go to STDERR. They have to be visible\n * even when stdout is a pipe into an agent, and stdout in this CLI carries\n * exactly one JSON object.\n *\n * In human mode at a real TTY (`canWizard`), this runs behind a clack\n * `intro`/`outro` flow with a spinner over the approval wait — see\n * `wizard.ts`. The device code and URL themselves stay plain, copy-pasteable\n * text (a clack `note`, not a prompt) either way: a partner needs to select\n * and open them, not interact with them through clack. JSON mode and human\n * non-TTY mode are byte-for-byte what they were before this existed.\n */\nexport async function runLogin(\n ctx: CliContext,\n args: LoginArgs,\n): Promise<ExitCode> {\n // No `projectDir`: login is the one verb that CREATES credentials, so its\n // host must come from something the developer chose (--host, EKANOS_HOST)\n // or already trusts (the sole stored login) — never from a committed\n // ekanos.json, which an attacker can set in any cloned repo to point the\n // device flow at their own tenant. Every other verb may read the project\n // host because their bearer tokens are looked up per-host from the store:\n // an unfamiliar host simply has no session to present.\n const env = resolveAuthEnvironment(ctx, args.host, args.env);\n\n const useWizard = canWizard(ctx, { stdinIsTTY: args.stdinIsTTY });\n if (useWizard) wizard.intro('ekanos login');\n\n if (!args.force) {\n const existing = env.store.read(env.host);\n\n if (existing) {\n const reusable = await tryReuse(env, existing);\n\n if (reusable) {\n printBanner(ctx);\n\n const email = reusable.user.email ?? reusable.user.id;\n if (useWizard) {\n wizard.outro(\n `Already signed in as ${email} on ${shortHost(env.host)}.`,\n );\n }\n\n return ctx.succeed(\n {\n host: env.host,\n status: 'already-authenticated',\n user: reusable.user,\n expiresAt: reusable.expiresAt,\n credentialsPath: env.store.filePath,\n },\n useWizard\n ? ''\n : ctx.ui.result({\n title: 'Already signed in',\n rows: [\n ['Account', email],\n ['Host', shortHost(env.host)],\n ],\n }),\n );\n }\n\n ctx.log(\n ctx.ui.dim('The stored session has expired. Starting a new login…'),\n );\n }\n }\n\n const config = await fetchCliAuthConfig(env.host);\n const authorization = await startDeviceAuthorization(config);\n const openUrl =\n authorization.verificationUriComplete ?? authorization.verificationUri;\n\n if (useWizard) {\n const noteLines = [`Open: ${openUrl}`, `Code: ${authorization.userCode}`];\n if (authorization.verificationUriComplete) {\n noteLines.push(\n `(without the pre-filled code: ${authorization.verificationUri})`,\n );\n }\n wizard.note(noteLines.join('\\n'), 'Device login');\n } else {\n ctx.log('');\n ctx.log(` Open: ${openUrl}`);\n ctx.log(` Code: ${authorization.userCode}`);\n\n if (authorization.verificationUriComplete) {\n ctx.log(\n ` (without the pre-filled code: ${authorization.verificationUri})`,\n );\n }\n\n ctx.log('');\n ctx.log('Waiting for approval…');\n }\n\n const spinner = useWizard ? wizard.spinner() : null;\n spinner?.start('Waiting for approval…');\n\n const tokens = await pollForDeviceTokens(config, authorization);\n const session = await createEkanosSession(env.host, tokens.accessToken);\n\n spinner?.stop(`✓ authenticated as ${session.email ?? session.userId}`);\n\n const stored: StoredSession = {\n auth0RefreshToken: tokens.refreshToken,\n accessToken: session.accessToken,\n expiresAt: session.expiresAt,\n userId: session.userId,\n email: session.email,\n updatedAt: new Date().toISOString(),\n };\n\n env.store.write(env.host, stored);\n\n printBanner(ctx);\n\n if (useWizard) {\n wizard.outro(\n `Signed in as ${session.email ?? session.userId} on ${shortHost(env.host)}.`,\n );\n }\n\n return ctx.succeed(\n {\n host: env.host,\n status: 'authenticated',\n user: { id: session.userId, email: session.email },\n expiresAt: session.expiresAt,\n credentialsPath: env.store.filePath,\n },\n useWizard\n ? ''\n : ctx.ui.result({\n title: 'Signed in',\n rows: [\n ['Account', session.email ?? session.userId],\n ['Host', shortHost(env.host)],\n ],\n }),\n );\n}\n\n/**\n * Probe a stored session. Returns null when it cannot be revived, so `login`\n * falls through to a fresh device flow rather than failing — \"log in again\"\n * is the whole point of the command being run.\n *\n * Only auth failures are absorbed. A network or configuration error still\n * propagates: silently starting a browser login because the host was\n * unreachable would be the wrong recovery, and would hide the real fault.\n */\nasync function tryReuse(\n env: ReturnType<typeof resolveAuthEnvironment>,\n existing: StoredSession,\n): Promise<{\n user: { id: string; email: string | null };\n expiresAt: string;\n} | null> {\n try {\n const { identity, session } = await resolveIdentity(env, existing);\n\n return {\n user: { id: identity.id, email: identity.email },\n expiresAt: session.expiresAt,\n };\n } catch (error) {\n if (isAuthFailure(error)) {\n return null;\n }\n\n throw error;\n }\n}\n\nfunction isAuthFailure(error: unknown): boolean {\n return error instanceof CliError && error.code === ERROR_CODES.AUTH_REQUIRED;\n}\n"]}
@@ -1,6 +1,7 @@
1
1
  import { revokeRefreshToken } from '../auth/device-flow.js';
2
2
  import { fetchCliAuthConfig } from '../auth/ekanos-api.js';
3
3
  import { newStore, resolveAuthEnvironment } from '../auth/session.js';
4
+ import { pluralize, shortHost } from '../ui.js';
4
5
  /**
5
6
  * `logout` — revoke the stored refresh token at Auth0, then delete it locally.
6
7
  *
@@ -21,7 +22,12 @@ export async function runLogout(ctx, args) {
21
22
  });
22
23
  const session = env.store.read(env.host);
23
24
  if (!session) {
24
- return ctx.succeed({ host: env.host, cleared: false, revoked: false }, `logout: no stored session for ${env.host}.`);
25
+ return ctx.succeed({ host: env.host, cleared: false, revoked: false }, ctx.ui.result({
26
+ title: 'Not signed in',
27
+ tone: 'warn',
28
+ rows: [['Host', shortHost(env.host)]],
29
+ notes: ['Nothing was stored for this host, so nothing was removed.'],
30
+ }));
25
31
  }
26
32
  const revoked = await tryRevoke(ctx, env.host, session.auth0RefreshToken);
27
33
  env.store.clear(env.host);
@@ -30,7 +36,14 @@ export async function runLogout(ctx, args) {
30
36
  `could not be revoked at Auth0. It stays valid until it expires — ` +
31
37
  `revoke the session in Auth0 if this machine is compromised.`);
32
38
  }
33
- return ctx.succeed({ host: env.host, cleared: true, revoked }, `logout: signed out of ${env.host}${revoked ? ' and revoked the session' : ''}.`);
39
+ return ctx.succeed({ host: env.host, cleared: true, revoked }, ctx.ui.result({
40
+ title: 'Signed out',
41
+ tone: revoked ? 'ok' : 'warn',
42
+ rows: [
43
+ ['Host', shortHost(env.host)],
44
+ ['Session', revoked ? 'revoked at Auth0' : 'removed locally only'],
45
+ ],
46
+ }));
34
47
  }
35
48
  /**
36
49
  * Never throws. Every failure mode here — an unreachable host, a deployment
@@ -59,7 +72,11 @@ async function runLogoutAll(ctx, args) {
59
72
  const store = newStore(ctx, args.env);
60
73
  const hosts = store.hosts();
61
74
  if (hosts.length === 0) {
62
- return ctx.succeed({ hosts: [], cleared: 0 }, 'logout: no stored sessions.');
75
+ return ctx.succeed({ hosts: [], cleared: 0 }, ctx.ui.result({
76
+ title: 'Not signed in',
77
+ tone: 'warn',
78
+ notes: ['No stored sessions on this machine.'],
79
+ }));
63
80
  }
64
81
  const results = [];
65
82
  for (const host of hosts) {
@@ -72,6 +89,15 @@ async function runLogoutAll(ctx, args) {
72
89
  });
73
90
  }
74
91
  const cleared = store.clearAll();
75
- return ctx.succeed({ hosts: results, cleared }, `logout: signed out of ${cleared} host${cleared === 1 ? '' : 's'}.`);
92
+ return ctx.succeed({ hosts: results, cleared }, ctx.ui.result({
93
+ title: `Signed out of ${pluralize(cleared, 'host')}`,
94
+ tone: results.every((r) => r.revoked) ? 'ok' : 'warn',
95
+ rows: [
96
+ [
97
+ 'Hosts',
98
+ results.map((r) => `${shortHost(r.host)} · ${r.revoked ? 'revoked' : 'removed locally only'}`),
99
+ ],
100
+ ],
101
+ }));
76
102
  }
77
103
  //# sourceMappingURL=logout.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"logout.js","sourceRoot":"","sources":["../../src/commands/logout.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,EAAE,QAAQ,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AAanE;;;;;;;;;;GAUG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAC7B,GAAe,EACf,IAAgB;IAEhB,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC;QACb,OAAO,YAAY,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IACjC,CAAC;IAED,MAAM,GAAG,GAAG,sBAAsB,CAAC,GAAG,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,EAAE;QAC3D,UAAU,EAAE,IAAI,CAAC,GAAG;KACrB,CAAC,CAAC;IACH,MAAM,OAAO,GAAG,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAEzC,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,OAAO,GAAG,CAAC,OAAO,CAChB,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,EAClD,iCAAiC,GAAG,CAAC,IAAI,GAAG,CAC7C,CAAC;IACJ,CAAC;IAED,MAAM,OAAO,GAAG,MAAM,SAAS,CAAC,GAAG,EAAE,GAAG,CAAC,IAAI,EAAE,OAAO,CAAC,iBAAiB,CAAC,CAAC;IAE1E,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAE1B,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,GAAG,CAAC,IAAI,CACN,oEAAoE;YAClE,mEAAmE;YACnE,6DAA6D,CAChE,CAAC;IACJ,CAAC;IAED,OAAO,GAAG,CAAC,OAAO,CAChB,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,EAC1C,yBAAyB,GAAG,CAAC,IAAI,GAAG,OAAO,CAAC,CAAC,CAAC,0BAA0B,CAAC,CAAC,CAAC,EAAE,GAAG,CACjF,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,KAAK,UAAU,SAAS,CACtB,GAAe,EACf,IAAY,EACZ,YAAoB;IAEpB,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,kBAAkB,CAAC,IAAI,CAAC,CAAC;QAE9C,OAAO,MAAM,kBAAkB,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IACxD,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,yEAAyE;QACzE,4DAA4D;QAC5D,GAAG,CAAC,IAAI,CACN,2BAA2B,IAAI,yBAAyB;YACtD,IAAI,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CACjE,CAAC;QAEF,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,KAAK,UAAU,YAAY,CACzB,GAAe,EACf,IAAgB;IAEhB,MAAM,KAAK,GAAG,QAAQ,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC;IACtC,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,EAAE,CAAC;IAE5B,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,OAAO,GAAG,CAAC,OAAO,CAChB,EAAE,KAAK,EAAE,EAAE,EAAE,OAAO,EAAE,CAAC,EAAE,EACzB,6BAA6B,CAC9B,CAAC;IACJ,CAAC;IAED,MAAM,OAAO,GAAyC,EAAE,CAAC;IAEzD,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAEjC,OAAO,CAAC,IAAI,CAAC;YACX,IAAI;YACJ,OAAO,EAAE,OAAO;gBACd,CAAC,CAAC,MAAM,SAAS,CAAC,GAAG,EAAE,IAAI,EAAE,OAAO,CAAC,iBAAiB,CAAC;gBACvD,CAAC,CAAC,KAAK;SACV,CAAC,CAAC;IACL,CAAC;IAED,MAAM,OAAO,GAAG,KAAK,CAAC,QAAQ,EAAE,CAAC;IAEjC,OAAO,GAAG,CAAC,OAAO,CAChB,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,EAC3B,yBAAyB,OAAO,QAAQ,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,GAAG,CACpE,CAAC;AACJ,CAAC","sourcesContent":["import { revokeRefreshToken } from '../auth/device-flow';\nimport { fetchCliAuthConfig } from '../auth/ekanos-api';\nimport { newStore, resolveAuthEnvironment } from '../auth/session';\nimport type { CliContext } from '../context';\nimport type { ExitCode } from '../exit-codes';\n\nexport interface LogoutArgs {\n host?: string;\n /** Sign out of every host in the credentials file. */\n all: boolean;\n /** Where a project's ekanos.json may sit, for its optional `host` field. */\n cwd: string;\n env: Record<string, string | undefined>;\n}\n\n/**\n * `logout` — revoke the stored refresh token at Auth0, then delete it locally.\n *\n * Idempotent: logging out twice exits 0. `cleared: false` in the envelope says\n * there was nothing to remove; it is information, not a failure.\n *\n * Revocation is attempted first but never blocks the local delete. A logout\n * that only forgets the token would leave a rotating credential live for weeks;\n * a logout that REFUSES because the network is down would leave the token on\n * disk, which is worse. So: try to revoke, always delete, report both.\n */\nexport async function runLogout(\n ctx: CliContext,\n args: LogoutArgs,\n): Promise<ExitCode> {\n if (args.all) {\n return runLogoutAll(ctx, args);\n }\n\n const env = resolveAuthEnvironment(ctx, args.host, args.env, {\n projectDir: args.cwd,\n });\n const session = env.store.read(env.host);\n\n if (!session) {\n return ctx.succeed(\n { host: env.host, cleared: false, revoked: false },\n `logout: no stored session for ${env.host}.`,\n );\n }\n\n const revoked = await tryRevoke(ctx, env.host, session.auth0RefreshToken);\n\n env.store.clear(env.host);\n\n if (!revoked) {\n ctx.warn(\n `ekanos: the local credentials were removed, but the refresh token ` +\n `could not be revoked at Auth0. It stays valid until it expires — ` +\n `revoke the session in Auth0 if this machine is compromised.`,\n );\n }\n\n return ctx.succeed(\n { host: env.host, cleared: true, revoked },\n `logout: signed out of ${env.host}${revoked ? ' and revoked the session' : ''}.`,\n );\n}\n\n/**\n * Never throws. Every failure mode here — an unreachable host, a deployment\n * without the CLI routes, an Auth0 outage — must still end in the local token\n * being deleted.\n */\nasync function tryRevoke(\n ctx: CliContext,\n host: string,\n refreshToken: string,\n): Promise<boolean> {\n try {\n const config = await fetchCliAuthConfig(host);\n\n return await revokeRefreshToken(config, refreshToken);\n } catch (error) {\n // Diagnostic only — the message never carries the token, and the outcome\n // is reported in the envelope's `revoked` field either way.\n ctx.warn(\n `ekanos: could not reach ${host} to revoke the session ` +\n `(${error instanceof Error ? error.message : String(error)}).`,\n );\n\n return false;\n }\n}\n\n/**\n * `logout --all` — the \"I am leaving this machine\" verb. Every host is revoked\n * independently and the local file is emptied regardless, for the same reason\n * the single-host path does not let a failed revocation block the delete.\n */\nasync function runLogoutAll(\n ctx: CliContext,\n args: LogoutArgs,\n): Promise<ExitCode> {\n const store = newStore(ctx, args.env);\n const hosts = store.hosts();\n\n if (hosts.length === 0) {\n return ctx.succeed(\n { hosts: [], cleared: 0 },\n 'logout: no stored sessions.',\n );\n }\n\n const results: { host: string; revoked: boolean }[] = [];\n\n for (const host of hosts) {\n const session = store.read(host);\n\n results.push({\n host,\n revoked: session\n ? await tryRevoke(ctx, host, session.auth0RefreshToken)\n : false,\n });\n }\n\n const cleared = store.clearAll();\n\n return ctx.succeed(\n { hosts: results, cleared },\n `logout: signed out of ${cleared} host${cleared === 1 ? '' : 's'}.`,\n );\n}\n"]}
1
+ {"version":3,"file":"logout.js","sourceRoot":"","sources":["../../src/commands/logout.ts"],"names":[],"mappings":"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","sourcesContent":["import { revokeRefreshToken } from '../auth/device-flow';\nimport { fetchCliAuthConfig } from '../auth/ekanos-api';\nimport { newStore, resolveAuthEnvironment } from '../auth/session';\nimport type { CliContext } from '../context';\nimport type { ExitCode } from '../exit-codes';\nimport { pluralize, shortHost } from '../ui';\n\nexport interface LogoutArgs {\n host?: string;\n /** Sign out of every host in the credentials file. */\n all: boolean;\n /** Where a project's ekanos.json may sit, for its optional `host` field. */\n cwd: string;\n env: Record<string, string | undefined>;\n}\n\n/**\n * `logout` — revoke the stored refresh token at Auth0, then delete it locally.\n *\n * Idempotent: logging out twice exits 0. `cleared: false` in the envelope says\n * there was nothing to remove; it is information, not a failure.\n *\n * Revocation is attempted first but never blocks the local delete. A logout\n * that only forgets the token would leave a rotating credential live for weeks;\n * a logout that REFUSES because the network is down would leave the token on\n * disk, which is worse. So: try to revoke, always delete, report both.\n */\nexport async function runLogout(\n ctx: CliContext,\n args: LogoutArgs,\n): Promise<ExitCode> {\n if (args.all) {\n return runLogoutAll(ctx, args);\n }\n\n const env = resolveAuthEnvironment(ctx, args.host, args.env, {\n projectDir: args.cwd,\n });\n const session = env.store.read(env.host);\n\n if (!session) {\n return ctx.succeed(\n { host: env.host, cleared: false, revoked: false },\n ctx.ui.result({\n title: 'Not signed in',\n tone: 'warn',\n rows: [['Host', shortHost(env.host)]],\n notes: ['Nothing was stored for this host, so nothing was removed.'],\n }),\n );\n }\n\n const revoked = await tryRevoke(ctx, env.host, session.auth0RefreshToken);\n\n env.store.clear(env.host);\n\n if (!revoked) {\n ctx.warn(\n `ekanos: the local credentials were removed, but the refresh token ` +\n `could not be revoked at Auth0. It stays valid until it expires — ` +\n `revoke the session in Auth0 if this machine is compromised.`,\n );\n }\n\n return ctx.succeed(\n { host: env.host, cleared: true, revoked },\n ctx.ui.result({\n title: 'Signed out',\n tone: revoked ? 'ok' : 'warn',\n rows: [\n ['Host', shortHost(env.host)],\n ['Session', revoked ? 'revoked at Auth0' : 'removed locally only'],\n ],\n }),\n );\n}\n\n/**\n * Never throws. Every failure mode here — an unreachable host, a deployment\n * without the CLI routes, an Auth0 outage — must still end in the local token\n * being deleted.\n */\nasync function tryRevoke(\n ctx: CliContext,\n host: string,\n refreshToken: string,\n): Promise<boolean> {\n try {\n const config = await fetchCliAuthConfig(host);\n\n return await revokeRefreshToken(config, refreshToken);\n } catch (error) {\n // Diagnostic only — the message never carries the token, and the outcome\n // is reported in the envelope's `revoked` field either way.\n ctx.warn(\n `ekanos: could not reach ${host} to revoke the session ` +\n `(${error instanceof Error ? error.message : String(error)}).`,\n );\n\n return false;\n }\n}\n\n/**\n * `logout --all` — the \"I am leaving this machine\" verb. Every host is revoked\n * independently and the local file is emptied regardless, for the same reason\n * the single-host path does not let a failed revocation block the delete.\n */\nasync function runLogoutAll(\n ctx: CliContext,\n args: LogoutArgs,\n): Promise<ExitCode> {\n const store = newStore(ctx, args.env);\n const hosts = store.hosts();\n\n if (hosts.length === 0) {\n return ctx.succeed(\n { hosts: [], cleared: 0 },\n ctx.ui.result({\n title: 'Not signed in',\n tone: 'warn',\n notes: ['No stored sessions on this machine.'],\n }),\n );\n }\n\n const results: { host: string; revoked: boolean }[] = [];\n\n for (const host of hosts) {\n const session = store.read(host);\n\n results.push({\n host,\n revoked: session\n ? await tryRevoke(ctx, host, session.auth0RefreshToken)\n : false,\n });\n }\n\n const cleared = store.clearAll();\n\n return ctx.succeed(\n { hosts: results, cleared },\n ctx.ui.result({\n title: `Signed out of ${pluralize(cleared, 'host')}`,\n tone: results.every((r) => r.revoked) ? 'ok' : 'warn',\n rows: [\n [\n 'Hosts',\n results.map(\n (r) =>\n `${shortHost(r.host)} · ${r.revoked ? 'revoked' : 'removed locally only'}`,\n ),\n ],\n ],\n }),\n );\n}\n"]}
@@ -9,6 +9,7 @@ import { MAX_MANIFEST_BYTES, stillUnauthorized, submitArchive, } from '../publis
9
9
  import { assertSchemaNotSkewed } from '../schema-skew.js';
10
10
  import { fetchSeatsWithOneRefresh } from '../seats.js';
11
11
  import { stillUnauthorizedSourceSeats } from '../sources-api.js';
12
+ import { shortHost } from '../ui.js';
12
13
  import { announceUpdate, checkForUpdate } from '../update-notice.js';
13
14
  import { collectAllFindings } from '../validate-findings.js';
14
15
  /**
@@ -145,11 +146,6 @@ export async function runPublish(ctx, args) {
145
146
  ...(persisted.source ? [`"source": "${source}"`] : []),
146
147
  ...(persisted.host ? [`"host": "${env.host}"`] : []),
147
148
  ];
148
- const overrideNote = persisted.hostOverride
149
- ? ` NOTE: this publish went to ${env.host}, but ekanos.json keeps ` +
150
- `"host": "${persisted.hostOverride}" — a one-off override never ` +
151
- `replaces the saved host; edit ekanos.json to change it.`
152
- : '';
153
149
  return ctx.succeed(Object.assign(Object.assign({ host: env.host, source: {
154
150
  id: verified.sourceSeat.id,
155
151
  slug: verified.sourceSeat.slug,
@@ -162,13 +158,30 @@ export async function runPublish(ctx, args) {
162
158
  projectSchemaVersion: skew.projectSchemaVersion,
163
159
  },
164
160
  }
165
- : {})), (notice ? { toolchain: notice } : {})), `publish: submitted ${receipt.slug}@${receipt.version} to ${env.host} ` +
166
- `(source "${source}", state "${receipt.state}").` +
167
- (persistedNote.length > 0
168
- ? ` Saved ${persistedNote.join(' and ')} to ekanos.json — future ` +
169
- `publishes won't need the flag${persistedNote.length === 1 ? '' : 's'}.`
170
- : '') +
171
- overrideNote);
161
+ : {})), (notice ? { toolchain: notice } : {})), ctx.ui.result({
162
+ title: 'Submitted',
163
+ rows: [
164
+ ['Integration', `${receipt.slug}@${receipt.version}`],
165
+ ['State', receipt.state],
166
+ ['Source', source],
167
+ ['Host', shortHost(env.host)],
168
+ ...(persistedNote.length > 0
169
+ ? [
170
+ [
171
+ 'Saved',
172
+ `${persistedNote.join(' and ')} to ekanos.json`,
173
+ ],
174
+ ]
175
+ : []),
176
+ ],
177
+ notes: persisted.hostOverride
178
+ ? [
179
+ `This publish went to ${env.host}, but ekanos.json keeps "host": ` +
180
+ `"${persisted.hostOverride}" — a one-off override never replaces ` +
181
+ 'the saved host; edit ekanos.json to change it.',
182
+ ]
183
+ : [],
184
+ }));
172
185
  }
173
186
  /**
174
187
  * The publish-target verification gate.