@ekanos/cli 0.1.7 → 0.1.9

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.
@@ -82,3 +82,8 @@ export declare function persistProjectFields(loaded: LoadedProject, source: stri
82
82
  host: boolean;
83
83
  hostOverride: string | null;
84
84
  };
85
+ /**
86
+ * The submitted version is the project's own `package.json#version` — one
87
+ * source of truth, the same field npm would publish.
88
+ */
89
+ export declare function readProjectVersion(projectDir: string): string;
@@ -358,7 +358,7 @@ const SEMVER_PATTERN = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?(?:
358
358
  * The submitted version is the project's own `package.json#version` — one
359
359
  * source of truth, the same field npm would publish.
360
360
  */
361
- function readProjectVersion(projectDir) {
361
+ export function readProjectVersion(projectDir) {
362
362
  const manifestPath = path.join(projectDir, 'package.json');
363
363
  let parsed;
364
364
  try {
@@ -1 +1 @@
1
- {"version":3,"file":"publish.js","sourceRoot":"","sources":["../../src/commands/publish.ts"],"names":[],"mappings":"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","sourcesContent":["import * as fs from 'node:fs';\nimport * as path from 'node:path';\n\nimport type { StoredSession } from '../auth/credential-store';\nimport {\n type AuthEnvironment,\n refreshStoredSession,\n requireSession,\n resolveAuthEnvironment,\n} from '../auth/session';\nimport type { CliContext } from '../context';\nimport {\n forbiddenError,\n gateFailedError,\n preconditionError,\n validationError,\n} from '../errors';\nimport type { ExitCode } from '../exit-codes';\nimport { packProject } from '../pack';\nimport { type LoadedProject, loadProject, serializeProject } from '../project';\nimport {\n MAX_MANIFEST_BYTES,\n stillUnauthorized,\n submitArchive,\n} from '../publish-api';\nimport { assertSchemaNotSkewed } from '../schema-skew';\nimport { fetchSeatsWithOneRefresh } from '../seats';\nimport { type SourceSeat, stillUnauthorizedSourceSeats } from '../sources-api';\nimport { announceUpdate, checkForUpdate } from '../update-notice';\nimport { collectAllFindings } from '../validate-findings';\n\nexport interface PublishArgs {\n host?: string;\n /** Fusion source slug. Falls back to ekanos.json's \"source\" field. */\n source?: string;\n /** Project directory (contains ekanos.json). Defaults to cwd. */\n dir: string;\n /**\n * Skips the interactive confirm when the source resolved IMPLICITLY (from\n * ekanos.json) and the caller holds more than one developer seat. Has no\n * effect when `--source` was passed this invocation (explicit is already\n * consent) or when the caller holds exactly one seat (nothing to\n * disambiguate).\n */\n yes: boolean;\n env: Record<string, string | undefined>;\n /**\n * Whether stdin is a real interactive terminal — the SECOND condition (with\n * human/non-JSON mode) required before publish will ever prompt. Defaults\n * to `process.stdin.isTTY === true`. Injectable so tests can exercise the\n * confirm branch without a real TTY.\n */\n stdinIsTTY?: boolean;\n /**\n * Injectable y/N prompt, for tests. Defaults to a real readline prompt over\n * stdin/stderr. Never invoked unless BOTH human mode and a real stdin TTY\n * are in play — see `canPrompt` in `verifyPublishTarget`.\n */\n confirm?: (question: string) => Promise<boolean>;\n}\n\n/** Where the resolved source slug came from — governs the confirmation rules below. */\ninterface SourceResolution {\n source: string;\n /** True iff `--source` was passed THIS invocation. */\n explicit: boolean;\n}\n\n/**\n * `publish` — pack the project and submit it to a Fusion deployment.\n *\n * The submission gate is the SAME findings pass `validate` runs (one\n * implementation, `validate-findings.ts`): any error-severity finding refuses\n * the publish with GATE_FAILED (exit 10) and the findings in `data`, so an\n * agent can branch on the code and fix-then-retry without re-running validate.\n *\n * Authorization is server-side — the dev seat on the target source — and the\n * stored session is refreshed once on a 401, mirroring `whoami`. On the first\n * successful publish the chosen source slug is persisted into `ekanos.json`\n * so later publishes need no `--source`.\n */\nexport async function runPublish(\n ctx: CliContext,\n args: PublishArgs,\n): Promise<ExitCode> {\n const env = resolveAuthEnvironment(ctx, args.host, args.env, {\n projectDir: args.dir,\n });\n const session = requireSession(env.store, env.host);\n\n const loaded = loadProject(args.dir);\n\n // Fail fast, before anything else: a project whose schema is newer than\n // this CLI bundles cannot be trusted to validate correctly, so publishing\n // it — the one verb whose findings gate is meant to be authoritative —\n // must refuse before packing or submitting anything. See `schema-skew.ts`.\n const skew = assertSchemaNotSkewed(ctx, loaded.projectDir);\n\n const notice = await checkForUpdate({\n env: args.env,\n host: env.host,\n session,\n });\n announceUpdate(ctx, notice);\n\n // The host would reject a build from a CLI it has declared unsupported\n // anyway (see the wire contract in `toolchain-api.ts`) — refuse here with a\n // hint naming the fix, rather than let the submission round-trip fail with\n // whatever generic error the host happens to answer with.\n if (notice?.status === 'unsupported') {\n throw preconditionError(\n `@ekanos/cli ${notice.current.cli} is below ${env.host}'s minimum ` +\n `supported version (needs at least ${notice.target.cli}).`,\n 'Run \"ekanos upgrade\" before publishing — the host will refuse this ' +\n 'submission from an unsupported CLI regardless.',\n );\n }\n\n const resolution = await resolveSourceSlug(env, session, args.source, loaded);\n const version = readProjectVersion(loaded.projectDir);\n const slug = loaded.primary.slug;\n\n ctx.log(`Validating ${slug} before publishing…`);\n\n const findings = await collectAllFindings(loaded);\n const errorCount = findings.filter((f) => f.severity === 'error').length;\n\n if (errorCount > 0) {\n return ctx.fail(\n gateFailedError(\n `Refusing to publish: ${errorCount} validation finding` +\n `${errorCount === 1 ? '' : 's'}.`,\n 'Resolve the error-severity findings in data.findings (they are ' +\n 'exactly what \"ekanos validate\" reports), then re-run ' +\n '\"ekanos publish\".',\n ),\n { slug, version, findings },\n );\n }\n\n // The 100%-certainty mechanism: confirm the resolved source is one the\n // caller actually holds a seat on — and, when it was resolved IMPLICITLY\n // from ekanos.json with more than one seat in play, that a human (or an\n // explicit --yes) actually intends this target — BEFORE anything is packed\n // or sent over the wire.\n const verified = await verifyPublishTarget(ctx, env, resolution.session, {\n source: resolution.source,\n explicit: resolution.explicit,\n slug,\n version,\n yes: args.yes,\n stdinIsTTY: args.stdinIsTTY ?? process.stdin.isTTY === true,\n confirm: args.confirm ?? promptYesNo,\n });\n\n const source = verified.sourceSeat.slug;\n\n ctx.log(`Packing ${slug}@${version}…`);\n\n const packed = packProject(loaded.projectDir);\n const manifest = buildManifest(loaded, packed.files);\n\n ctx.log(`Submitting to ${env.host} (source \"${source}\")…`);\n\n const receipt = await submitWithOneRefresh(env, verified.session, {\n source,\n slug,\n version,\n manifest,\n archive: packed.archive,\n });\n\n const persisted = persistProjectFields(loaded, source, env.host);\n\n const persistedNote = [\n ...(persisted.source ? [`\"source\": \"${source}\"`] : []),\n ...(persisted.host ? [`\"host\": \"${env.host}\"`] : []),\n ];\n\n const overrideNote = persisted.hostOverride\n ? ` NOTE: this publish went to ${env.host}, but ekanos.json keeps ` +\n `\"host\": \"${persisted.hostOverride}\" — a one-off override never ` +\n `replaces the saved host; edit ekanos.json to change it.`\n : '';\n\n return ctx.succeed(\n {\n host: env.host,\n source: {\n id: verified.sourceSeat.id,\n slug: verified.sourceSeat.slug,\n name: verified.sourceSeat.name,\n },\n slug: receipt.slug,\n version: receipt.version,\n submissionId: receipt.submissionId,\n state: receipt.state,\n archiveSha256: receipt.archiveSha256,\n files: packed.files,\n sourcePersisted: persisted.source,\n hostPersisted: persisted.host,\n hostOverride: persisted.hostOverride,\n ...(skew.status === 'project-older'\n ? {\n schemaSkew: {\n status: skew.status,\n cliSchemaVersion: skew.cliSchemaVersion,\n projectSchemaVersion: skew.projectSchemaVersion,\n },\n }\n : {}),\n ...(notice ? { toolchain: notice } : {}),\n },\n `publish: submitted ${receipt.slug}@${receipt.version} to ${env.host} ` +\n `(source \"${source}\", state \"${receipt.state}\").` +\n (persistedNote.length > 0\n ? ` Saved ${persistedNote.join(' and ')} to ekanos.json — future ` +\n `publishes won't need the flag${persistedNote.length === 1 ? '' : 's'}.`\n : '') +\n overrideNote,\n );\n}\n\n/**\n * The publish-target verification gate.\n *\n * FORBIDDEN (exit 5) whenever the resolved source is not among the caller's\n * seats — regardless of whether it came from `--source` or ekanos.json.\n * Beyond that, the confirmation rules branch on how the source was resolved:\n *\n * - `--source` THIS invocation: explicit is consent. Proceed.\n * - ekanos.json, exactly one seat held (which must be the match, or the\n * FORBIDDEN check above would have already fired): proceed, but say so —\n * there is nothing to disambiguate, but the target should never be silent.\n * - ekanos.json, multiple seats held: the ambiguous case this mechanism\n * exists for. Only when BOTH human (non-JSON) mode AND stdin is a real\n * TTY does it print the target and require an explicit y/N confirm\n * (`--yes` skips it) — see `canPrompt` below for why stdin matters, not\n * just stdout. Every other combination (JSON mode, or human mode with a\n * closed/non-interactive stdin) NEVER prompts — it proceeds only with\n * `--yes`, otherwise VALIDATION (exit 3) with the seat list, naming\n * `--source`, `--yes` and `ekanos use`.\n */\nasync function verifyPublishTarget(\n ctx: CliContext,\n env: AuthEnvironment,\n session: StoredSession,\n args: SourceResolution & {\n slug: string;\n version: string;\n yes: boolean;\n /** Real interactivity signal for stdin — see `canPrompt` below. */\n stdinIsTTY: boolean;\n confirm: (question: string) => Promise<boolean>;\n },\n): Promise<{ sourceSeat: SourceSeat; session: StoredSession }> {\n const result = await fetchSeatsWithOneRefresh(env, session);\n\n if (result.status === 'unauthorized') {\n stillUnauthorizedSourceSeats(env.host);\n }\n\n const seat = result.sources.find((s) => s.slug === args.source);\n\n if (!seat) {\n throw forbiddenError(\n `This account does not hold a seat on source \"${args.source}\" on ${env.host}.`,\n seatsHint(result.sources, env.host),\n );\n }\n\n if (args.explicit) {\n return { sourceSeat: seat, session: result.session };\n }\n\n if (result.sources.length === 1) {\n ctx.log(\n `Publishing to ${seat.name ?? seat.slug} (${seat.slug}) — your only ` +\n `developer seat.`,\n );\n return { sourceSeat: seat, session: result.session };\n }\n\n // Implicit source, multiple seats: the ambiguous case.\n //\n // `ctx.jsonMode` alone is NOT enough to decide whether an interactive\n // confirm is safe: `--no-json` forces human mode even when stdout is\n // piped, and neither `--no-json` nor a bare human run says anything about\n // stdin. Prompting `readline`'s `.question()` against a closed/non-TTY\n // stdin (a cron job, a wrapper script piping stdout but not attached to a\n // terminal) never resolves — the process hangs instead of exiting with a\n // clean code. So an actual prompt requires BOTH: human mode AND a real\n // stdin TTY. Anything else — JSON mode, or human mode with non-interactive\n // stdin — falls through to the same non-prompting `--yes`-or-refuse rule.\n const canPrompt = !ctx.jsonMode && args.stdinIsTTY;\n\n if (canPrompt) {\n ctx.log(\n `Publishing ${args.slug}@${args.version} to SOURCE ${seat.name ?? seat.slug} ` +\n `(${seat.slug}) on ${env.host}`,\n );\n\n if (!args.yes) {\n const confirmed = await args.confirm('Proceed? [y/N]');\n\n if (!confirmed) {\n throw validationError(\n `Publish to \"${seat.slug}\" was not confirmed.`,\n multiSeatHint(result.sources),\n );\n }\n }\n\n return { sourceSeat: seat, session: result.session };\n }\n\n if (args.yes) {\n return { sourceSeat: seat, session: result.session };\n }\n\n throw validationError(\n `Ambiguous publish target: this account holds ${result.sources.length} ` +\n `developer seats and the source was resolved from ekanos.json, not ` +\n `an explicit flag.`,\n multiSeatHint(result.sources),\n );\n}\n\nfunction seatsHint(sources: SourceSeat[], host: string): string {\n if (sources.length === 0) {\n return (\n `You hold no seats on any source on ${host}. Ask a Fusion super-admin ` +\n `to grant your account the developer seat on the intended source.`\n );\n }\n\n return (\n `You hold a seat on: ${describeSeats(sources)}. Pass \"--source <slug>\" ` +\n `naming one of these, or run \"ekanos use <slug>\" to set the project's ` +\n `default.`\n );\n}\n\nfunction multiSeatHint(sources: SourceSeat[]): string {\n return (\n `You hold seats on: ${describeSeats(sources)}. Pass \"--source <slug>\" ` +\n `to name the target explicitly, add \"--yes\" to confirm the target ` +\n `already in ekanos.json non-interactively, or run \"ekanos use <slug>\" ` +\n `to change the project's default.`\n );\n}\n\nfunction describeSeats(sources: SourceSeat[]): string {\n return sources.map((s) => `${s.slug} (${s.roles.join(', ')})`).join(', ');\n}\n\n/**\n * The real, non-test y/N prompt. Reads a line from stdin and writes the\n * question to stderr, so it never touches stdout — the one stream the JSON\n * envelope invariant depends on. Only reachable when `verifyPublishTarget`'s\n * `canPrompt` is true — human mode AND a real stdin TTY — so this never\n * blocks on a closed or non-interactive stdin.\n */\n/**\n * Exported so `upgrade` can reuse the exact same y/N prompt rather than\n * writing a second one — see `canPrompt` above for the stdin-TTY gating this\n * is only ever invoked behind.\n */\nexport async function promptYesNo(question: string): Promise<boolean> {\n const readline = await import('node:readline/promises');\n const rl = readline.createInterface({\n input: process.stdin,\n output: process.stderr,\n });\n\n try {\n const answer = await rl.question(`${question} `);\n return /^y(es)?$/i.test(answer.trim());\n } finally {\n rl.close();\n }\n}\n\nasync function submitWithOneRefresh(\n env: ReturnType<typeof resolveAuthEnvironment>,\n session: StoredSession,\n payload: Parameters<typeof submitArchive>[2],\n) {\n const first = await submitArchive(env.host, session.accessToken, payload);\n\n if (first.status === 'ok') return first.receipt;\n\n // The access token no longer authenticates — an ordinary event, not an\n // error. One refresh, one retry; a second 401 is authoritative.\n const refreshed = await refreshStoredSession(env, session);\n const second = await submitArchive(env.host, refreshed.accessToken, payload);\n\n if (second.status === 'ok') return second.receipt;\n\n return stillUnauthorized(env.host);\n}\n\n/**\n * Resolve the source slug (flag → ekanos.json) and whether it was given\n * EXPLICITLY this invocation — the fact `verifyPublishTarget` uses to decide\n * whether a confirmation is even in play. `flag !== undefined` (rather than\n * \"non-empty after trim\") is deliberate: an explicit `--source` that is\n * effectively empty is still a caller who typed the flag, not one who\n * fell through to ekanos.json.\n */\nasync function resolveSourceSlug(\n env: AuthEnvironment,\n session: StoredSession,\n flag: string | undefined,\n loaded: LoadedProject,\n): Promise<SourceResolution & { session: StoredSession }> {\n const explicit = flag !== undefined;\n const raw = flag ?? loaded.project.source;\n\n if (!raw || raw.trim().length === 0) {\n return noSourceError(env, session);\n }\n\n return { source: raw.trim(), explicit, session };\n}\n\n/**\n * The no-source validation error, enriched with the caller's seat list when\n * it can be fetched — best-effort, since a session is guaranteed to exist\n * here (publish already called `requireSession`) but the fetch itself can\n * still fail (network, MFA, a down deployment). A failure to enrich must\n * never mask or replace the primary \"no source given\" error.\n */\nasync function noSourceError(\n env: AuthEnvironment,\n session: StoredSession,\n): Promise<never> {\n let seatsNote = '';\n\n try {\n const result = await fetchSeatsWithOneRefresh(env, session);\n\n if (result.status === 'ok') {\n seatsNote =\n result.sources.length > 0\n ? ` You hold a seat on: ${describeSeats(result.sources)}.`\n : ' You hold no seats on any source on this host yet.';\n }\n } catch {\n // Best-effort — the primary error below stands on its own.\n }\n\n throw validationError(\n 'No Fusion source given for the submission.',\n 'Pass \"--source <slug>\" (your operator names the source), or set ' +\n '\"source\": \"<slug>\" in ekanos.json. A successful publish saves it ' +\n `there for you.${seatsNote}`,\n );\n}\n\n/**\n * Persist the chosen source AND host into ekanos.json after a SUCCESSFUL\n * publish, so the next publish — and every other host-taking verb — needs no\n * flag. Returns what was written. A write failure is reported but never fails\n * the command — the submission already landed.\n *\n * `publish` is the only verb that writes ekanos.json: it is the one verb that\n * proves the (host, source) pair actually works, and it always runs inside a\n * project. `login` deliberately never writes — it is routinely run outside\n * one, and the credential store's sole-host fallback covers it.\n *\n * `host` persists on FIRST WRITE ONLY. It decides where every future verb\n * sends the bearer token and the source archive, so a one-off `--host`\n * override (a staging test, say) must not silently become the project's\n * sticky default; when the override differs from the stored host, the stored\n * value is kept and the difference is called out in the result instead.\n * Changing it is an explicit edit of ekanos.json. `source` keeps the simpler\n * always-update semantics — it is scoped BY the host, and updating it is the\n * documented way to repoint a project.\n */\nexport function persistProjectFields(\n loaded: LoadedProject,\n source: string,\n host: string,\n): { source: boolean; host: boolean; hostOverride: string | null } {\n const wants = {\n source: loaded.project.source !== source,\n host: loaded.project.host === undefined,\n };\n\n const hostOverride =\n loaded.project.host !== undefined && loaded.project.host !== host\n ? loaded.project.host\n : null;\n\n if (!wants.source && !wants.host) {\n return { source: false, host: false, hostOverride };\n }\n\n try {\n fs.writeFileSync(\n loaded.configPath,\n serializeProject({\n ...loaded.project,\n source,\n host: wants.host ? host : loaded.project.host,\n }),\n );\n\n return { ...wants, hostOverride };\n } catch {\n // Diagnostics-only: the publish succeeded; a read-only ekanos.json just\n // means the next publish needs the flags again.\n return { source: false, host: false, hostOverride };\n }\n}\n\n/**\n * How many packed paths the manifest embeds before switching to a truncated\n * list plus a `filesTruncated` total. The manifest is metadata — the archive\n * itself is the payload — and the server caps it at MAX_MANIFEST_BYTES, so a\n * many-small-files project must not be able to blow the cap with its file\n * list alone.\n */\nconst MANIFEST_FILE_LIST_CAP = 200;\n\n/**\n * The submission manifest: the parsed ekanos.json, the integration entries,\n * and (a bounded prefix of) the packed file list. Refuses to build one over\n * the server's byte cap — a clear VALIDATION error here beats the server's\n * generic 400 after a 4 MiB upload.\n */\nfunction buildManifest(loaded: LoadedProject, files: string[]): string {\n const manifest = JSON.stringify({\n project: loaded.project,\n integrations: loaded.integrations.map((i) => ({\n slug: i.slug,\n entry: i.entry,\n })),\n files: files.slice(0, MANIFEST_FILE_LIST_CAP),\n ...(files.length > MANIFEST_FILE_LIST_CAP\n ? { filesTruncated: files.length }\n : {}),\n });\n\n const bytes = Buffer.byteLength(manifest, 'utf8');\n\n if (bytes > MAX_MANIFEST_BYTES) {\n throw validationError(\n `The submission manifest is ${bytes} bytes, over the ` +\n `${MAX_MANIFEST_BYTES}-byte server cap.`,\n 'The manifest embeds your ekanos.json — trim oversized fields there ' +\n '(sourceGlobs is the usual culprit), then re-run \"ekanos publish\".',\n );\n }\n\n return manifest;\n}\n\nconst SEMVER_PATTERN =\n /^\\d+\\.\\d+\\.\\d+(?:-[0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*)?(?:\\+[0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*)?$/;\n\n/**\n * The submitted version is the project's own `package.json#version` — one\n * source of truth, the same field npm would publish.\n */\nfunction readProjectVersion(projectDir: string): string {\n const manifestPath = path.join(projectDir, 'package.json');\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));\n } catch (error) {\n throw preconditionError(\n `Could not read ${manifestPath}: ${\n error instanceof Error ? error.message : String(error)\n }`,\n `Ensure the project has a valid package.json — its \"version\" field is ` +\n `what gets submitted.`,\n );\n }\n\n const version =\n typeof parsed === 'object' && parsed !== null\n ? (parsed as { version?: unknown }).version\n : undefined;\n\n if (typeof version !== 'string' || !SEMVER_PATTERN.test(version)) {\n throw validationError(\n `package.json \"version\" is ${\n typeof version === 'string' ? `\"${version}\"` : 'missing'\n }, which is not a semver version.`,\n `Set \"version\" in ${manifestPath} to a semver string like \"0.1.0\", ` +\n `then re-run \"ekanos publish\".`,\n );\n }\n\n return version;\n}\n"]}
1
+ {"version":3,"file":"publish.js","sourceRoot":"","sources":["../../src/commands/publish.ts"],"names":[],"mappings":"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","sourcesContent":["import * as fs from 'node:fs';\nimport * as path from 'node:path';\n\nimport type { StoredSession } from '../auth/credential-store';\nimport {\n type AuthEnvironment,\n refreshStoredSession,\n requireSession,\n resolveAuthEnvironment,\n} from '../auth/session';\nimport type { CliContext } from '../context';\nimport {\n forbiddenError,\n gateFailedError,\n preconditionError,\n validationError,\n} from '../errors';\nimport type { ExitCode } from '../exit-codes';\nimport { packProject } from '../pack';\nimport { type LoadedProject, loadProject, serializeProject } from '../project';\nimport {\n MAX_MANIFEST_BYTES,\n stillUnauthorized,\n submitArchive,\n} from '../publish-api';\nimport { assertSchemaNotSkewed } from '../schema-skew';\nimport { fetchSeatsWithOneRefresh } from '../seats';\nimport { type SourceSeat, stillUnauthorizedSourceSeats } from '../sources-api';\nimport { announceUpdate, checkForUpdate } from '../update-notice';\nimport { collectAllFindings } from '../validate-findings';\n\nexport interface PublishArgs {\n host?: string;\n /** Fusion source slug. Falls back to ekanos.json's \"source\" field. */\n source?: string;\n /** Project directory (contains ekanos.json). Defaults to cwd. */\n dir: string;\n /**\n * Skips the interactive confirm when the source resolved IMPLICITLY (from\n * ekanos.json) and the caller holds more than one developer seat. Has no\n * effect when `--source` was passed this invocation (explicit is already\n * consent) or when the caller holds exactly one seat (nothing to\n * disambiguate).\n */\n yes: boolean;\n env: Record<string, string | undefined>;\n /**\n * Whether stdin is a real interactive terminal — the SECOND condition (with\n * human/non-JSON mode) required before publish will ever prompt. Defaults\n * to `process.stdin.isTTY === true`. Injectable so tests can exercise the\n * confirm branch without a real TTY.\n */\n stdinIsTTY?: boolean;\n /**\n * Injectable y/N prompt, for tests. Defaults to a real readline prompt over\n * stdin/stderr. Never invoked unless BOTH human mode and a real stdin TTY\n * are in play — see `canPrompt` in `verifyPublishTarget`.\n */\n confirm?: (question: string) => Promise<boolean>;\n}\n\n/** Where the resolved source slug came from — governs the confirmation rules below. */\ninterface SourceResolution {\n source: string;\n /** True iff `--source` was passed THIS invocation. */\n explicit: boolean;\n}\n\n/**\n * `publish` — pack the project and submit it to a Fusion deployment.\n *\n * The submission gate is the SAME findings pass `validate` runs (one\n * implementation, `validate-findings.ts`): any error-severity finding refuses\n * the publish with GATE_FAILED (exit 10) and the findings in `data`, so an\n * agent can branch on the code and fix-then-retry without re-running validate.\n *\n * Authorization is server-side — the dev seat on the target source — and the\n * stored session is refreshed once on a 401, mirroring `whoami`. On the first\n * successful publish the chosen source slug is persisted into `ekanos.json`\n * so later publishes need no `--source`.\n */\nexport async function runPublish(\n ctx: CliContext,\n args: PublishArgs,\n): Promise<ExitCode> {\n const env = resolveAuthEnvironment(ctx, args.host, args.env, {\n projectDir: args.dir,\n });\n const session = requireSession(env.store, env.host);\n\n const loaded = loadProject(args.dir);\n\n // Fail fast, before anything else: a project whose schema is newer than\n // this CLI bundles cannot be trusted to validate correctly, so publishing\n // it — the one verb whose findings gate is meant to be authoritative —\n // must refuse before packing or submitting anything. See `schema-skew.ts`.\n const skew = assertSchemaNotSkewed(ctx, loaded.projectDir);\n\n const notice = await checkForUpdate({\n env: args.env,\n host: env.host,\n session,\n });\n announceUpdate(ctx, notice);\n\n // The host would reject a build from a CLI it has declared unsupported\n // anyway (see the wire contract in `toolchain-api.ts`) — refuse here with a\n // hint naming the fix, rather than let the submission round-trip fail with\n // whatever generic error the host happens to answer with.\n if (notice?.status === 'unsupported') {\n throw preconditionError(\n `@ekanos/cli ${notice.current.cli} is below ${env.host}'s minimum ` +\n `supported version (needs at least ${notice.target.cli}).`,\n 'Run \"ekanos upgrade\" before publishing — the host will refuse this ' +\n 'submission from an unsupported CLI regardless.',\n );\n }\n\n const resolution = await resolveSourceSlug(env, session, args.source, loaded);\n const version = readProjectVersion(loaded.projectDir);\n const slug = loaded.primary.slug;\n\n ctx.log(`Validating ${slug} before publishing…`);\n\n const findings = await collectAllFindings(loaded);\n const errorCount = findings.filter((f) => f.severity === 'error').length;\n\n if (errorCount > 0) {\n return ctx.fail(\n gateFailedError(\n `Refusing to publish: ${errorCount} validation finding` +\n `${errorCount === 1 ? '' : 's'}.`,\n 'Resolve the error-severity findings in data.findings (they are ' +\n 'exactly what \"ekanos validate\" reports), then re-run ' +\n '\"ekanos publish\".',\n ),\n { slug, version, findings },\n );\n }\n\n // The 100%-certainty mechanism: confirm the resolved source is one the\n // caller actually holds a seat on — and, when it was resolved IMPLICITLY\n // from ekanos.json with more than one seat in play, that a human (or an\n // explicit --yes) actually intends this target — BEFORE anything is packed\n // or sent over the wire.\n const verified = await verifyPublishTarget(ctx, env, resolution.session, {\n source: resolution.source,\n explicit: resolution.explicit,\n slug,\n version,\n yes: args.yes,\n stdinIsTTY: args.stdinIsTTY ?? process.stdin.isTTY === true,\n confirm: args.confirm ?? promptYesNo,\n });\n\n const source = verified.sourceSeat.slug;\n\n ctx.log(`Packing ${slug}@${version}…`);\n\n const packed = packProject(loaded.projectDir);\n const manifest = buildManifest(loaded, packed.files);\n\n ctx.log(`Submitting to ${env.host} (source \"${source}\")…`);\n\n const receipt = await submitWithOneRefresh(env, verified.session, {\n source,\n slug,\n version,\n manifest,\n archive: packed.archive,\n });\n\n const persisted = persistProjectFields(loaded, source, env.host);\n\n const persistedNote = [\n ...(persisted.source ? [`\"source\": \"${source}\"`] : []),\n ...(persisted.host ? [`\"host\": \"${env.host}\"`] : []),\n ];\n\n const overrideNote = persisted.hostOverride\n ? ` NOTE: this publish went to ${env.host}, but ekanos.json keeps ` +\n `\"host\": \"${persisted.hostOverride}\" — a one-off override never ` +\n `replaces the saved host; edit ekanos.json to change it.`\n : '';\n\n return ctx.succeed(\n {\n host: env.host,\n source: {\n id: verified.sourceSeat.id,\n slug: verified.sourceSeat.slug,\n name: verified.sourceSeat.name,\n },\n slug: receipt.slug,\n version: receipt.version,\n submissionId: receipt.submissionId,\n state: receipt.state,\n archiveSha256: receipt.archiveSha256,\n files: packed.files,\n sourcePersisted: persisted.source,\n hostPersisted: persisted.host,\n hostOverride: persisted.hostOverride,\n ...(skew.status === 'project-older'\n ? {\n schemaSkew: {\n status: skew.status,\n cliSchemaVersion: skew.cliSchemaVersion,\n projectSchemaVersion: skew.projectSchemaVersion,\n },\n }\n : {}),\n ...(notice ? { toolchain: notice } : {}),\n },\n `publish: submitted ${receipt.slug}@${receipt.version} to ${env.host} ` +\n `(source \"${source}\", state \"${receipt.state}\").` +\n (persistedNote.length > 0\n ? ` Saved ${persistedNote.join(' and ')} to ekanos.json — future ` +\n `publishes won't need the flag${persistedNote.length === 1 ? '' : 's'}.`\n : '') +\n overrideNote,\n );\n}\n\n/**\n * The publish-target verification gate.\n *\n * FORBIDDEN (exit 5) whenever the resolved source is not among the caller's\n * seats — regardless of whether it came from `--source` or ekanos.json.\n * Beyond that, the confirmation rules branch on how the source was resolved:\n *\n * - `--source` THIS invocation: explicit is consent. Proceed.\n * - ekanos.json, exactly one seat held (which must be the match, or the\n * FORBIDDEN check above would have already fired): proceed, but say so —\n * there is nothing to disambiguate, but the target should never be silent.\n * - ekanos.json, multiple seats held: the ambiguous case this mechanism\n * exists for. Only when BOTH human (non-JSON) mode AND stdin is a real\n * TTY does it print the target and require an explicit y/N confirm\n * (`--yes` skips it) — see `canPrompt` below for why stdin matters, not\n * just stdout. Every other combination (JSON mode, or human mode with a\n * closed/non-interactive stdin) NEVER prompts — it proceeds only with\n * `--yes`, otherwise VALIDATION (exit 3) with the seat list, naming\n * `--source`, `--yes` and `ekanos use`.\n */\nasync function verifyPublishTarget(\n ctx: CliContext,\n env: AuthEnvironment,\n session: StoredSession,\n args: SourceResolution & {\n slug: string;\n version: string;\n yes: boolean;\n /** Real interactivity signal for stdin — see `canPrompt` below. */\n stdinIsTTY: boolean;\n confirm: (question: string) => Promise<boolean>;\n },\n): Promise<{ sourceSeat: SourceSeat; session: StoredSession }> {\n const result = await fetchSeatsWithOneRefresh(env, session);\n\n if (result.status === 'unauthorized') {\n stillUnauthorizedSourceSeats(env.host);\n }\n\n const seat = result.sources.find((s) => s.slug === args.source);\n\n if (!seat) {\n throw forbiddenError(\n `This account does not hold a seat on source \"${args.source}\" on ${env.host}.`,\n seatsHint(result.sources, env.host),\n );\n }\n\n if (args.explicit) {\n return { sourceSeat: seat, session: result.session };\n }\n\n if (result.sources.length === 1) {\n ctx.log(\n `Publishing to ${seat.name ?? seat.slug} (${seat.slug}) — your only ` +\n `developer seat.`,\n );\n return { sourceSeat: seat, session: result.session };\n }\n\n // Implicit source, multiple seats: the ambiguous case.\n //\n // `ctx.jsonMode` alone is NOT enough to decide whether an interactive\n // confirm is safe: `--no-json` forces human mode even when stdout is\n // piped, and neither `--no-json` nor a bare human run says anything about\n // stdin. Prompting `readline`'s `.question()` against a closed/non-TTY\n // stdin (a cron job, a wrapper script piping stdout but not attached to a\n // terminal) never resolves — the process hangs instead of exiting with a\n // clean code. So an actual prompt requires BOTH: human mode AND a real\n // stdin TTY. Anything else — JSON mode, or human mode with non-interactive\n // stdin — falls through to the same non-prompting `--yes`-or-refuse rule.\n const canPrompt = !ctx.jsonMode && args.stdinIsTTY;\n\n if (canPrompt) {\n ctx.log(\n `Publishing ${args.slug}@${args.version} to SOURCE ${seat.name ?? seat.slug} ` +\n `(${seat.slug}) on ${env.host}`,\n );\n\n if (!args.yes) {\n const confirmed = await args.confirm('Proceed? [y/N]');\n\n if (!confirmed) {\n throw validationError(\n `Publish to \"${seat.slug}\" was not confirmed.`,\n multiSeatHint(result.sources),\n );\n }\n }\n\n return { sourceSeat: seat, session: result.session };\n }\n\n if (args.yes) {\n return { sourceSeat: seat, session: result.session };\n }\n\n throw validationError(\n `Ambiguous publish target: this account holds ${result.sources.length} ` +\n `developer seats and the source was resolved from ekanos.json, not ` +\n `an explicit flag.`,\n multiSeatHint(result.sources),\n );\n}\n\nfunction seatsHint(sources: SourceSeat[], host: string): string {\n if (sources.length === 0) {\n return (\n `You hold no seats on any source on ${host}. Ask a Fusion super-admin ` +\n `to grant your account the developer seat on the intended source.`\n );\n }\n\n return (\n `You hold a seat on: ${describeSeats(sources)}. Pass \"--source <slug>\" ` +\n `naming one of these, or run \"ekanos use <slug>\" to set the project's ` +\n `default.`\n );\n}\n\nfunction multiSeatHint(sources: SourceSeat[]): string {\n return (\n `You hold seats on: ${describeSeats(sources)}. Pass \"--source <slug>\" ` +\n `to name the target explicitly, add \"--yes\" to confirm the target ` +\n `already in ekanos.json non-interactively, or run \"ekanos use <slug>\" ` +\n `to change the project's default.`\n );\n}\n\nfunction describeSeats(sources: SourceSeat[]): string {\n return sources.map((s) => `${s.slug} (${s.roles.join(', ')})`).join(', ');\n}\n\n/**\n * The real, non-test y/N prompt. Reads a line from stdin and writes the\n * question to stderr, so it never touches stdout — the one stream the JSON\n * envelope invariant depends on. Only reachable when `verifyPublishTarget`'s\n * `canPrompt` is true — human mode AND a real stdin TTY — so this never\n * blocks on a closed or non-interactive stdin.\n */\n/**\n * Exported so `upgrade` can reuse the exact same y/N prompt rather than\n * writing a second one — see `canPrompt` above for the stdin-TTY gating this\n * is only ever invoked behind.\n */\nexport async function promptYesNo(question: string): Promise<boolean> {\n const readline = await import('node:readline/promises');\n const rl = readline.createInterface({\n input: process.stdin,\n output: process.stderr,\n });\n\n try {\n const answer = await rl.question(`${question} `);\n return /^y(es)?$/i.test(answer.trim());\n } finally {\n rl.close();\n }\n}\n\nasync function submitWithOneRefresh(\n env: ReturnType<typeof resolveAuthEnvironment>,\n session: StoredSession,\n payload: Parameters<typeof submitArchive>[2],\n) {\n const first = await submitArchive(env.host, session.accessToken, payload);\n\n if (first.status === 'ok') return first.receipt;\n\n // The access token no longer authenticates — an ordinary event, not an\n // error. One refresh, one retry; a second 401 is authoritative.\n const refreshed = await refreshStoredSession(env, session);\n const second = await submitArchive(env.host, refreshed.accessToken, payload);\n\n if (second.status === 'ok') return second.receipt;\n\n return stillUnauthorized(env.host);\n}\n\n/**\n * Resolve the source slug (flag → ekanos.json) and whether it was given\n * EXPLICITLY this invocation — the fact `verifyPublishTarget` uses to decide\n * whether a confirmation is even in play. `flag !== undefined` (rather than\n * \"non-empty after trim\") is deliberate: an explicit `--source` that is\n * effectively empty is still a caller who typed the flag, not one who\n * fell through to ekanos.json.\n */\nasync function resolveSourceSlug(\n env: AuthEnvironment,\n session: StoredSession,\n flag: string | undefined,\n loaded: LoadedProject,\n): Promise<SourceResolution & { session: StoredSession }> {\n const explicit = flag !== undefined;\n const raw = flag ?? loaded.project.source;\n\n if (!raw || raw.trim().length === 0) {\n return noSourceError(env, session);\n }\n\n return { source: raw.trim(), explicit, session };\n}\n\n/**\n * The no-source validation error, enriched with the caller's seat list when\n * it can be fetched — best-effort, since a session is guaranteed to exist\n * here (publish already called `requireSession`) but the fetch itself can\n * still fail (network, MFA, a down deployment). A failure to enrich must\n * never mask or replace the primary \"no source given\" error.\n */\nasync function noSourceError(\n env: AuthEnvironment,\n session: StoredSession,\n): Promise<never> {\n let seatsNote = '';\n\n try {\n const result = await fetchSeatsWithOneRefresh(env, session);\n\n if (result.status === 'ok') {\n seatsNote =\n result.sources.length > 0\n ? ` You hold a seat on: ${describeSeats(result.sources)}.`\n : ' You hold no seats on any source on this host yet.';\n }\n } catch {\n // Best-effort — the primary error below stands on its own.\n }\n\n throw validationError(\n 'No Fusion source given for the submission.',\n 'Pass \"--source <slug>\" (your operator names the source), or set ' +\n '\"source\": \"<slug>\" in ekanos.json. A successful publish saves it ' +\n `there for you.${seatsNote}`,\n );\n}\n\n/**\n * Persist the chosen source AND host into ekanos.json after a SUCCESSFUL\n * publish, so the next publish — and every other host-taking verb — needs no\n * flag. Returns what was written. A write failure is reported but never fails\n * the command — the submission already landed.\n *\n * `publish` is the only verb that writes ekanos.json: it is the one verb that\n * proves the (host, source) pair actually works, and it always runs inside a\n * project. `login` deliberately never writes — it is routinely run outside\n * one, and the credential store's sole-host fallback covers it.\n *\n * `host` persists on FIRST WRITE ONLY. It decides where every future verb\n * sends the bearer token and the source archive, so a one-off `--host`\n * override (a staging test, say) must not silently become the project's\n * sticky default; when the override differs from the stored host, the stored\n * value is kept and the difference is called out in the result instead.\n * Changing it is an explicit edit of ekanos.json. `source` keeps the simpler\n * always-update semantics — it is scoped BY the host, and updating it is the\n * documented way to repoint a project.\n */\nexport function persistProjectFields(\n loaded: LoadedProject,\n source: string,\n host: string,\n): { source: boolean; host: boolean; hostOverride: string | null } {\n const wants = {\n source: loaded.project.source !== source,\n host: loaded.project.host === undefined,\n };\n\n const hostOverride =\n loaded.project.host !== undefined && loaded.project.host !== host\n ? loaded.project.host\n : null;\n\n if (!wants.source && !wants.host) {\n return { source: false, host: false, hostOverride };\n }\n\n try {\n fs.writeFileSync(\n loaded.configPath,\n serializeProject({\n ...loaded.project,\n source,\n host: wants.host ? host : loaded.project.host,\n }),\n );\n\n return { ...wants, hostOverride };\n } catch {\n // Diagnostics-only: the publish succeeded; a read-only ekanos.json just\n // means the next publish needs the flags again.\n return { source: false, host: false, hostOverride };\n }\n}\n\n/**\n * How many packed paths the manifest embeds before switching to a truncated\n * list plus a `filesTruncated` total. The manifest is metadata — the archive\n * itself is the payload — and the server caps it at MAX_MANIFEST_BYTES, so a\n * many-small-files project must not be able to blow the cap with its file\n * list alone.\n */\nconst MANIFEST_FILE_LIST_CAP = 200;\n\n/**\n * The submission manifest: the parsed ekanos.json, the integration entries,\n * and (a bounded prefix of) the packed file list. Refuses to build one over\n * the server's byte cap — a clear VALIDATION error here beats the server's\n * generic 400 after a 4 MiB upload.\n */\nfunction buildManifest(loaded: LoadedProject, files: string[]): string {\n const manifest = JSON.stringify({\n project: loaded.project,\n integrations: loaded.integrations.map((i) => ({\n slug: i.slug,\n entry: i.entry,\n })),\n files: files.slice(0, MANIFEST_FILE_LIST_CAP),\n ...(files.length > MANIFEST_FILE_LIST_CAP\n ? { filesTruncated: files.length }\n : {}),\n });\n\n const bytes = Buffer.byteLength(manifest, 'utf8');\n\n if (bytes > MAX_MANIFEST_BYTES) {\n throw validationError(\n `The submission manifest is ${bytes} bytes, over the ` +\n `${MAX_MANIFEST_BYTES}-byte server cap.`,\n 'The manifest embeds your ekanos.json — trim oversized fields there ' +\n '(sourceGlobs is the usual culprit), then re-run \"ekanos publish\".',\n );\n }\n\n return manifest;\n}\n\nconst SEMVER_PATTERN =\n /^\\d+\\.\\d+\\.\\d+(?:-[0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*)?(?:\\+[0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*)?$/;\n\n/**\n * The submitted version is the project's own `package.json#version` — one\n * source of truth, the same field npm would publish.\n */\nexport function readProjectVersion(projectDir: string): string {\n const manifestPath = path.join(projectDir, 'package.json');\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));\n } catch (error) {\n throw preconditionError(\n `Could not read ${manifestPath}: ${\n error instanceof Error ? error.message : String(error)\n }`,\n `Ensure the project has a valid package.json — its \"version\" field is ` +\n `what gets submitted.`,\n );\n }\n\n const version =\n typeof parsed === 'object' && parsed !== null\n ? (parsed as { version?: unknown }).version\n : undefined;\n\n if (typeof version !== 'string' || !SEMVER_PATTERN.test(version)) {\n throw validationError(\n `package.json \"version\" is ${\n typeof version === 'string' ? `\"${version}\"` : 'missing'\n }, which is not a semver version.`,\n `Set \"version\" in ${manifestPath} to a semver string like \"0.1.0\", ` +\n `then re-run \"ekanos publish\".`,\n );\n }\n\n return version;\n}\n"]}
@@ -5,6 +5,16 @@ export interface StatusArgs {
5
5
  /** Project directory (may contain ekanos.json). Defaults to cwd. */
6
6
  dir: string;
7
7
  env: Record<string, string | undefined>;
8
+ /** Render each version's full finding list, not just error/warn counts. */
9
+ findings?: boolean;
10
+ /**
11
+ * Block until this project's current (slug, version) submission reaches a
12
+ * terminal-for-waiting state (gated, dev, rejected), polling every 30s.
13
+ * Changes the command's exit-code contract — see `runStatus`'s docstring.
14
+ */
15
+ watch?: boolean;
16
+ /** Injectable for tests — see `auth/device-flow.ts`'s identical pattern. */
17
+ sleep?: (ms: number) => Promise<void>;
8
18
  }
9
19
  /**
10
20
  * `status` — the read verb: where am I pointed, who am I, and what has this
@@ -23,6 +33,22 @@ export interface StatusArgs {
23
33
  * Server errors here DO map through the taxonomy: an unreachable host or a
24
34
  * missing source is a fault to surface, not a state to absorb.
25
35
  *
26
- * Exit 0 whenever the report itself could be assembled.
36
+ * Exit 0 whenever the report itself could be assembled — EXCEPT under
37
+ * `--watch` (see below), whose exit code carries the gate's verdict instead.
38
+ *
39
+ * `--findings` requests each version's full finding list (`?detail=findings`
40
+ * server-side) instead of just error/warn counts.
41
+ *
42
+ * `--watch` turns `status` into a blocking wait: it requires a project with
43
+ * a resolved source and a live session (both are PRECONDITIONs here, not
44
+ * degraded-to-false states — there is nothing to watch without them), then
45
+ * polls `GET /api/partner/submissions?detail=findings` every 30s for THIS
46
+ * project's current (slug, package.json version) submission until it reaches
47
+ * a terminal-for-waiting state: `gated`/`dev` exit 0, `rejected` exits 10
48
+ * (GATE_FAILED) with the findings carried in `data` — the same shape
49
+ * `publish`'s own gate refusal uses, so an agent's fix-then-retry loop
50
+ * branches on it identically whether the gate ran inline (future) or via
51
+ * this poll (today). `--json --watch` still emits exactly ONE envelope, at
52
+ * completion — the polling itself only ever writes to stderr via `ctx.log`.
27
53
  */
28
54
  export declare function runStatus(ctx: CliContext, args: StatusArgs): Promise<ExitCode>;
@@ -1,11 +1,20 @@
1
1
  import * as fs from 'node:fs';
2
2
  import * as path from 'node:path';
3
3
  import { refreshStoredSession, resolveAuthEnvironment, resolveIdentity, } from '../auth/session.js';
4
- import { CliError, authRequiredError } from '../errors.js';
4
+ import { CliError, authRequiredError, gateFailedError, preconditionError, } from '../errors.js';
5
5
  import { ERROR_CODES } from '../exit-codes.js';
6
6
  import { EKANOS_CONFIG_FILENAME, loadProject } from '../project.js';
7
- import { listSubmissions } from '../publish-api.js';
7
+ import { listSubmissions, } from '../publish-api.js';
8
8
  import { announceUpdate, checkForUpdate } from '../update-notice.js';
9
+ import { readProjectVersion } from './publish.js';
10
+ /** `--watch`'s poll interval — matches the mission brief exactly. */
11
+ const WATCH_POLL_INTERVAL_MS = 30000;
12
+ /**
13
+ * The states `--watch` stops on. `submitted` is the only non-terminal state
14
+ * a version can be in when watching starts (an already-`gated`/`dev`/
15
+ * `rejected` version resolves on the very first check, no polling needed).
16
+ */
17
+ const WATCH_TERMINAL_STATES = new Set(['gated', 'dev', 'rejected']);
9
18
  /**
10
19
  * `status` — the read verb: where am I pointed, who am I, and what has this
11
20
  * project submitted?
@@ -23,10 +32,26 @@ import { announceUpdate, checkForUpdate } from '../update-notice.js';
23
32
  * Server errors here DO map through the taxonomy: an unreachable host or a
24
33
  * missing source is a fault to surface, not a state to absorb.
25
34
  *
26
- * Exit 0 whenever the report itself could be assembled.
35
+ * Exit 0 whenever the report itself could be assembled — EXCEPT under
36
+ * `--watch` (see below), whose exit code carries the gate's verdict instead.
37
+ *
38
+ * `--findings` requests each version's full finding list (`?detail=findings`
39
+ * server-side) instead of just error/warn counts.
40
+ *
41
+ * `--watch` turns `status` into a blocking wait: it requires a project with
42
+ * a resolved source and a live session (both are PRECONDITIONs here, not
43
+ * degraded-to-false states — there is nothing to watch without them), then
44
+ * polls `GET /api/partner/submissions?detail=findings` every 30s for THIS
45
+ * project's current (slug, package.json version) submission until it reaches
46
+ * a terminal-for-waiting state: `gated`/`dev` exit 0, `rejected` exits 10
47
+ * (GATE_FAILED) with the findings carried in `data` — the same shape
48
+ * `publish`'s own gate refusal uses, so an agent's fix-then-retry loop
49
+ * branches on it identically whether the gate ran inline (future) or via
50
+ * this poll (today). `--json --watch` still emits exactly ONE envelope, at
51
+ * completion — the polling itself only ever writes to stderr via `ctx.log`.
27
52
  */
28
53
  export async function runStatus(ctx, args) {
29
- var _a, _b;
54
+ var _a, _b, _c;
30
55
  const env = resolveAuthEnvironment(ctx, args.host, args.env, {
31
56
  projectDir: args.dir,
32
57
  });
@@ -51,9 +76,23 @@ export async function runStatus(ctx, args) {
51
76
  // never to produce.
52
77
  const live = stored ? ((_a = env.store.read(env.host)) !== null && _a !== void 0 ? _a : stored) : null;
53
78
  const project = readProjectReport(args.dir);
79
+ const detail = args.findings || args.watch ? 'findings' : undefined;
80
+ if (args.watch) {
81
+ if (!session.authenticated || !live) {
82
+ throw authRequiredError(`"ekanos status --watch" needs a live session on ${env.host}.`, `Run "ekanos login --host ${env.host}" and retry.`);
83
+ }
84
+ if (!project) {
85
+ throw preconditionError(`No ekanos.json found in ${path.resolve(args.dir)}.`, 'Run "ekanos publish" at least once before watching its gate result.');
86
+ }
87
+ if (!project.source) {
88
+ throw preconditionError('This project has no "source" in ekanos.json yet.', 'Run "ekanos publish" at least once (it saves the source you ' +
89
+ 'publish to) before watching its gate result.');
90
+ }
91
+ return runWatch(ctx, env, project, args);
92
+ }
54
93
  let submissions = null;
55
94
  if ((project === null || project === void 0 ? void 0 : project.source) && session.authenticated && live) {
56
- submissions = await listWithOneRefresh(env, live, project.source);
95
+ submissions = await listWithOneRefresh(env, live, project.source, detail);
57
96
  }
58
97
  else if ((project === null || project === void 0 ? void 0 : project.source) && !session.authenticated) {
59
98
  ctx.log(`ekanos: not logged in to ${env.host} — skipping the submission list. ` +
@@ -65,7 +104,81 @@ export async function runStatus(ctx, args) {
65
104
  'submissions from then on.');
66
105
  }
67
106
  return ctx.succeed(Object.assign(Object.assign(Object.assign({ host: env.host }, session), { credentialsPath: env.store.filePath, project,
68
- submissions }), (notice ? { toolchain: notice } : {})), humanSummary(env.host, session, project, submissions));
107
+ submissions }), (notice ? { toolchain: notice } : {})), humanSummary(env.host, session, project, submissions, (_c = args.findings) !== null && _c !== void 0 ? _c : false));
108
+ }
109
+ /**
110
+ * The `--watch` poll loop. Targets THIS project's current submission —
111
+ * `project.slug` + the version presently in `package.json` (the same value
112
+ * `publish` would submit right now) — not "whatever's most recent," so
113
+ * watching after a publish can never resolve against a stale prior version.
114
+ */
115
+ async function runWatch(ctx, env, project, args) {
116
+ var _a;
117
+ const source = project.source;
118
+ if (!source) {
119
+ // Unreachable — the caller already checked — but keeps this function's
120
+ // own type honest without a non-null assertion.
121
+ throw preconditionError('This project has no "source" in ekanos.json yet.', 'Run "ekanos publish" at least once before watching its gate result.');
122
+ }
123
+ const targetVersion = readProjectVersion(project.dir);
124
+ const sleep = (_a = args.sleep) !== null && _a !== void 0 ? _a : defaultSleep;
125
+ ctx.log(`ekanos: watching ${project.slug}@${targetVersion} on ${env.host} ` +
126
+ `(polling every ${WATCH_POLL_INTERVAL_MS / 1000}s)…`);
127
+ for (;;) {
128
+ // Re-read the session from the credential store on EVERY iteration —
129
+ // never carry one over in a closure. Auth0 rotates the refresh token on
130
+ // every use (see `refreshStoredSession`'s docstring), so a token this
131
+ // loop already consumed is dead by the next iteration; the store is the
132
+ // persisted source of truth for whichever refresh most recently landed
133
+ // — this loop's own (via `listWithOneRefresh` below, which persists
134
+ // through `refreshStoredSession`), or a concurrent one from another
135
+ // process (another terminal running login/publish/status --watch
136
+ // against the same host). Threading the refreshed session back out of
137
+ // `listWithOneRefresh` instead would only cover the first case.
138
+ const current = env.store.read(env.host);
139
+ if (!current) {
140
+ throw authRequiredError(`The session for ${env.host} is gone — "ekanos status --watch" ` +
141
+ 'cannot continue without it.', `Run "ekanos login --host ${env.host}" and retry.`);
142
+ }
143
+ const integrations = await listWithOneRefresh(env, current, source, 'findings');
144
+ const version = findVersion(integrations, project.slug, targetVersion);
145
+ if (version && WATCH_TERMINAL_STATES.has(version.state)) {
146
+ return reportWatchOutcome(ctx, project.slug, version);
147
+ }
148
+ ctx.log(`ekanos: ${project.slug}@${targetVersion} is ` +
149
+ `${version ? version.state : 'not visible yet'} — waiting…`);
150
+ await sleep(WATCH_POLL_INTERVAL_MS);
151
+ }
152
+ }
153
+ function findVersion(integrations, slug, version) {
154
+ var _a;
155
+ const integration = integrations.find((i) => i.slug === slug);
156
+ return (_a = integration === null || integration === void 0 ? void 0 : integration.versions.find((v) => v.version === version)) !== null && _a !== void 0 ? _a : null;
157
+ }
158
+ function reportWatchOutcome(ctx, slug, version) {
159
+ var _a, _b, _c;
160
+ const data = {
161
+ slug,
162
+ version: version.version,
163
+ state: version.state,
164
+ gatedAt: version.gatedAt,
165
+ errorCount: version.errorCount,
166
+ warnCount: version.warnCount,
167
+ findings: (_a = version.findings) !== null && _a !== void 0 ? _a : [],
168
+ stages: (_b = version.stages) !== null && _b !== void 0 ? _b : [],
169
+ };
170
+ if (version.state === 'rejected') {
171
+ return ctx.fail(gateFailedError(`${slug}@${version.version} was rejected by the gate ` +
172
+ `(${(_c = data.errorCount) !== null && _c !== void 0 ? _c : 0} error${data.errorCount === 1 ? '' : 's'}).`, 'Resolve the error-severity findings in data.findings, bump the ' +
173
+ 'version, and re-run "ekanos publish".'), data);
174
+ }
175
+ return ctx.succeed(data, `status --watch: ${slug}@${version.version} is now "${version.state}"` +
176
+ `${data.warnCount ? ` (${data.warnCount} warning${data.warnCount === 1 ? '' : 's'})` : ''}.`);
177
+ }
178
+ function defaultSleep(ms) {
179
+ return new Promise((resolve) => {
180
+ setTimeout(resolve, ms);
181
+ });
69
182
  }
70
183
  /**
71
184
  * whoami's identity resolution, with AUTH_REQUIRED absorbed into
@@ -109,12 +222,16 @@ function readProjectReport(dir) {
109
222
  };
110
223
  }
111
224
  /** Mirrors publish's one-refresh-one-retry on a stale access token. */
112
- async function listWithOneRefresh(env, session, source) {
113
- const first = await listSubmissions(env.host, session.accessToken, source);
225
+ async function listWithOneRefresh(env, session, source, detail) {
226
+ const first = await listSubmissions(env.host, session.accessToken, source, {
227
+ detail,
228
+ });
114
229
  if (first.status === 'ok')
115
230
  return first.integrations;
116
231
  const refreshed = await refreshStoredSession(env, session);
117
- const second = await listSubmissions(env.host, refreshed.accessToken, source);
232
+ const second = await listSubmissions(env.host, refreshed.accessToken, source, {
233
+ detail,
234
+ });
118
235
  if (second.status === 'ok')
119
236
  return second.integrations;
120
237
  // Authenticated a moment ago, unauthorized twice here: report the list as
@@ -122,8 +239,8 @@ async function listWithOneRefresh(env, session, source) {
122
239
  // it. This is unreachable in practice; belt and braces for a racing revoke.
123
240
  throw authRequiredError(`The session for ${env.host} could not authorize the submission list.`, `Run "ekanos login --host ${env.host}" and retry.`);
124
241
  }
125
- function humanSummary(host, session, project, submissions) {
126
- var _a, _b, _c;
242
+ function humanSummary(host, session, project, submissions, showFindings) {
243
+ var _a, _b, _c, _d;
127
244
  const lines = [];
128
245
  lines.push(session.authenticated
129
246
  ? `status: logged in to ${host} as ${(_b = (_a = session.user) === null || _a === void 0 ? void 0 : _a.email) !== null && _b !== void 0 ? _b : (_c = session.user) === null || _c === void 0 ? void 0 : _c.id}${session.expiresAt ? ` (session expires ${session.expiresAt})` : ''}.`
@@ -144,10 +261,26 @@ function humanSummary(host, session, project, submissions) {
144
261
  for (const integration of submissions) {
145
262
  for (const version of integration.versions) {
146
263
  lines.push(` submissions: ${integration.slug}@${version.version} — ` +
147
- `${version.state}${version.submittedAt ? ` (submitted ${version.submittedAt})` : ''}`);
264
+ `${version.state}${version.submittedAt ? ` (submitted ${version.submittedAt})` : ''}${gateCountsSuffix(version)}`);
265
+ if (showFindings) {
266
+ for (const finding of (_d = version.findings) !== null && _d !== void 0 ? _d : []) {
267
+ lines.push(` [${finding.severity}] ${finding.check}: ${finding.message} ` +
268
+ `— ${finding.hint}`);
269
+ }
270
+ }
148
271
  }
149
272
  }
150
273
  }
151
274
  return lines.join('\n');
152
275
  }
276
+ /** ` (N errors, M warnings)`, omitted entirely until a gate has actually run. */
277
+ function gateCountsSuffix(version) {
278
+ var _a, _b;
279
+ if (version.errorCount === null && version.warnCount === null)
280
+ return '';
281
+ const errorCount = (_a = version.errorCount) !== null && _a !== void 0 ? _a : 0;
282
+ const warnCount = (_b = version.warnCount) !== null && _b !== void 0 ? _b : 0;
283
+ return (` (${errorCount} error${errorCount === 1 ? '' : 's'}, ` +
284
+ `${warnCount} warning${warnCount === 1 ? '' : 's'})`);
285
+ }
153
286
  //# sourceMappingURL=status.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"status.js","sourceRoot":"","sources":["../../src/commands/status.ts"],"names":[],"mappings":"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","sourcesContent":["import * as fs from 'node:fs';\nimport * as path from 'node:path';\n\nimport type { StoredSession } from '../auth/credential-store';\nimport {\n refreshStoredSession,\n resolveAuthEnvironment,\n resolveIdentity,\n} from '../auth/session';\nimport type { CliContext } from '../context';\nimport { CliError, authRequiredError } from '../errors';\nimport { ERROR_CODES, type ExitCode } from '../exit-codes';\nimport { EKANOS_CONFIG_FILENAME, loadProject } from '../project';\nimport { type SubmissionListing, listSubmissions } from '../publish-api';\nimport { announceUpdate, checkForUpdate } from '../update-notice';\n\nexport interface StatusArgs {\n host?: string;\n /** Project directory (may contain ekanos.json). Defaults to cwd. */\n dir: string;\n env: Record<string, string | undefined>;\n}\n\ninterface SessionReport {\n authenticated: boolean;\n user?: { id: string; email: string | null; name: string | null };\n expiresAt?: string;\n hint?: string;\n}\n\ninterface ProjectReport {\n dir: string;\n slug: string;\n source: string | null;\n}\n\n/**\n * `status` — the read verb: where am I pointed, who am I, and what has this\n * project submitted?\n *\n * Three sections, each degrading gracefully rather than failing:\n *\n * - **session** — whoami's server-side identity check, but an absent or dead\n * session reports `authenticated: false` with a hint instead of exit 4. A\n * partner asking \"what is my state?\" must get an answer, not an error whose\n * answer it is.\n * - **project** — the ekanos.json in `--dir` (default cwd), when there is\n * one. No project is an ordinary state for `status`, never an error.\n * - **submissions** — `GET /api/partner/submissions?source=…` for the\n * project's resolved source, only when both a session and a source exist.\n * Server errors here DO map through the taxonomy: an unreachable host or a\n * missing source is a fault to surface, not a state to absorb.\n *\n * Exit 0 whenever the report itself could be assembled.\n */\nexport async function runStatus(\n ctx: CliContext,\n args: StatusArgs,\n): Promise<ExitCode> {\n const env = resolveAuthEnvironment(ctx, args.host, args.env, {\n projectDir: args.dir,\n });\n\n const stored = env.store.read(env.host);\n\n const notice = await checkForUpdate({\n env: args.env,\n host: env.host,\n session: stored ?? undefined,\n });\n announceUpdate(ctx, notice);\n\n const session = stored\n ? await probeSession(env, stored)\n : {\n authenticated: false,\n hint: `Run \"ekanos login --host ${env.host}\" to sign in.`,\n };\n\n // probeSession may have refreshed the session — persisting a NEW access\n // token and ROTATING the refresh token on disk. Re-read before using it:\n // the pre-probe object now holds a dead access token and an already-spent\n // refresh token, and presenting those would turn \"logged in, token was\n // merely stale\" into a hard exit-4 — the one failure this command promises\n // never to produce.\n const live = stored ? (env.store.read(env.host) ?? stored) : null;\n\n const project = readProjectReport(args.dir);\n\n let submissions: SubmissionListing[] | null = null;\n\n if (project?.source && session.authenticated && live) {\n submissions = await listWithOneRefresh(env, live, project.source);\n } else if (project?.source && !session.authenticated) {\n ctx.log(\n `ekanos: not logged in to ${env.host} — skipping the submission list. ` +\n (session.hint ?? ''),\n );\n } else if (project && !project.source) {\n ctx.log(\n 'ekanos: this project has no \"source\" in ekanos.json yet — the first ' +\n '\"ekanos publish --source <slug>\" saves one, and status will list ' +\n 'submissions from then on.',\n );\n }\n\n return ctx.succeed(\n {\n host: env.host,\n ...session,\n credentialsPath: env.store.filePath,\n project,\n submissions,\n ...(notice ? { toolchain: notice } : {}),\n },\n humanSummary(env.host, session, project, submissions),\n );\n}\n\n/**\n * whoami's identity resolution, with AUTH_REQUIRED absorbed into\n * `authenticated: false`. Anything else — network, precondition, MFA mapped\n * to auth by `fetchIdentity` — still propagates: \"the host was unreachable\"\n * must never be reported as \"you are logged out\".\n */\nasync function probeSession(\n env: ReturnType<typeof resolveAuthEnvironment>,\n stored: StoredSession,\n): Promise<SessionReport> {\n try {\n const { identity, session } = await resolveIdentity(env, stored);\n\n return {\n authenticated: true,\n user: { id: identity.id, email: identity.email, name: identity.name },\n expiresAt: session.expiresAt,\n };\n } catch (error) {\n if (error instanceof CliError && error.code === ERROR_CODES.AUTH_REQUIRED) {\n return { authenticated: false, hint: error.hint };\n }\n\n throw error;\n }\n}\n\n/**\n * Lenient project read: no ekanos.json means \"not in a project\", which is an\n * answer, not an error. An ekanos.json that EXISTS but does not load keeps\n * `loadProject`'s strict behaviour — a broken project file is a fault the\n * partner needs named, whatever verb they ran.\n */\nfunction readProjectReport(dir: string): ProjectReport | null {\n const projectDir = path.resolve(dir);\n\n if (!fs.existsSync(path.join(projectDir, EKANOS_CONFIG_FILENAME))) {\n return null;\n }\n\n const loaded = loadProject(projectDir);\n\n return {\n dir: loaded.projectDir,\n slug: loaded.primary.slug,\n source: loaded.project.source ?? null,\n };\n}\n\n/** Mirrors publish's one-refresh-one-retry on a stale access token. */\nasync function listWithOneRefresh(\n env: ReturnType<typeof resolveAuthEnvironment>,\n session: StoredSession,\n source: string,\n): Promise<SubmissionListing[]> {\n const first = await listSubmissions(env.host, session.accessToken, source);\n\n if (first.status === 'ok') return first.integrations;\n\n const refreshed = await refreshStoredSession(env, session);\n const second = await listSubmissions(env.host, refreshed.accessToken, source);\n\n if (second.status === 'ok') return second.integrations;\n\n // Authenticated a moment ago, unauthorized twice here: report the list as\n // unavailable rather than the session as dead — probeSession already proved\n // it. This is unreachable in practice; belt and braces for a racing revoke.\n throw authRequiredError(\n `The session for ${env.host} could not authorize the submission list.`,\n `Run \"ekanos login --host ${env.host}\" and retry.`,\n );\n}\n\nfunction humanSummary(\n host: string,\n session: SessionReport,\n project: ProjectReport | null,\n submissions: SubmissionListing[] | null,\n): string {\n const lines: string[] = [];\n\n lines.push(\n session.authenticated\n ? `status: logged in to ${host} as ${\n session.user?.email ?? session.user?.id\n }${session.expiresAt ? ` (session expires ${session.expiresAt})` : ''}.`\n : `status: not logged in to ${host}.`,\n );\n\n if (project) {\n lines.push(\n ` project: ${project.slug} in ${project.dir}` +\n (project.source\n ? ` (source \"${project.source}\")`\n : ' (no source configured yet)'),\n );\n } else {\n lines.push(' project: none (no ekanos.json here).');\n }\n\n if (submissions) {\n if (submissions.length === 0) {\n lines.push(' submissions: none yet.');\n }\n\n for (const integration of submissions) {\n for (const version of integration.versions) {\n lines.push(\n ` submissions: ${integration.slug}@${version.version} — ` +\n `${version.state}${\n version.submittedAt ? ` (submitted ${version.submittedAt})` : ''\n }`,\n );\n }\n }\n }\n\n return lines.join('\\n');\n}\n"]}
1
+ {"version":3,"file":"status.js","sourceRoot":"","sources":["../../src/commands/status.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAGlC,OAAO,EACL,oBAAoB,EACpB,sBAAsB,EACtB,eAAe,GAChB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EACL,QAAQ,EACR,iBAAiB,EACjB,eAAe,EACf,iBAAiB,GAClB,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,WAAW,EAAiB,MAAM,eAAe,CAAC;AAC3D,OAAO,EAAE,sBAAsB,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACjE,OAAO,EAGL,eAAe,GAChB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClE,OAAO,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAE/C,qEAAqE;AACrE,MAAM,sBAAsB,GAAG,KAAM,CAAC;AAEtC;;;;GAIG;AACH,MAAM,qBAAqB,GAAG,IAAI,GAAG,CAAC,CAAC,OAAO,EAAE,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC;AAgCpE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAC7B,GAAe,EACf,IAAgB;;IAEhB,MAAM,GAAG,GAAG,sBAAsB,CAAC,GAAG,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,EAAE;QAC3D,UAAU,EAAE,IAAI,CAAC,GAAG;KACrB,CAAC,CAAC;IAEH,MAAM,MAAM,GAAG,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAExC,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC;QAClC,GAAG,EAAE,IAAI,CAAC,GAAG;QACb,IAAI,EAAE,GAAG,CAAC,IAAI;QACd,OAAO,EAAE,MAAM,aAAN,MAAM,cAAN,MAAM,GAAI,SAAS;KAC7B,CAAC,CAAC;IACH,cAAc,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;IAE5B,MAAM,OAAO,GAAG,MAAM;QACpB,CAAC,CAAC,MAAM,YAAY,CAAC,GAAG,EAAE,MAAM,CAAC;QACjC,CAAC,CAAC;YACE,aAAa,EAAE,KAAK;YACpB,IAAI,EAAE,4BAA4B,GAAG,CAAC,IAAI,eAAe;SAC1D,CAAC;IAEN,wEAAwE;IACxE,yEAAyE;IACzE,0EAA0E;IAC1E,uEAAuE;IACvE,2EAA2E;IAC3E,oBAAoB;IACpB,MAAM,IAAI,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,MAAA,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,mCAAI,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAElE,MAAM,OAAO,GAAG,iBAAiB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAE5C,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC;IAEpE,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;QACf,IAAI,CAAC,OAAO,CAAC,aAAa,IAAI,CAAC,IAAI,EAAE,CAAC;YACpC,MAAM,iBAAiB,CACrB,mDAAmD,GAAG,CAAC,IAAI,GAAG,EAC9D,4BAA4B,GAAG,CAAC,IAAI,cAAc,CACnD,CAAC;QACJ,CAAC;QAED,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,MAAM,iBAAiB,CACrB,2BAA2B,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,EACpD,qEAAqE,CACtE,CAAC;QACJ,CAAC;QAED,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;YACpB,MAAM,iBAAiB,CACrB,kDAAkD,EAClD,8DAA8D;gBAC5D,8CAA8C,CACjD,CAAC;QACJ,CAAC;QAED,OAAO,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;IAC3C,CAAC;IAED,IAAI,WAAW,GAA+B,IAAI,CAAC;IAEnD,IAAI,CAAA,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,MAAM,KAAI,OAAO,CAAC,aAAa,IAAI,IAAI,EAAE,CAAC;QACrD,WAAW,GAAG,MAAM,kBAAkB,CAAC,GAAG,EAAE,IAAI,EAAE,OAAO,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5E,CAAC;SAAM,IAAI,CAAA,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,MAAM,KAAI,CAAC,OAAO,CAAC,aAAa,EAAE,CAAC;QACrD,GAAG,CAAC,GAAG,CACL,4BAA4B,GAAG,CAAC,IAAI,mCAAmC;YACrE,CAAC,MAAA,OAAO,CAAC,IAAI,mCAAI,EAAE,CAAC,CACvB,CAAC;IACJ,CAAC;SAAM,IAAI,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;QACtC,GAAG,CAAC,GAAG,CACL,sEAAsE;YACpE,mEAAmE;YACnE,2BAA2B,CAC9B,CAAC;IACJ,CAAC;IAED,OAAO,GAAG,CAAC,OAAO,6CAEd,IAAI,EAAE,GAAG,CAAC,IAAI,IACX,OAAO,KACV,eAAe,EAAE,GAAG,CAAC,KAAK,CAAC,QAAQ,EACnC,OAAO;QACP,WAAW,KACR,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,GAE1C,YAAY,CACV,GAAG,CAAC,IAAI,EACR,OAAO,EACP,OAAO,EACP,WAAW,EACX,MAAA,IAAI,CAAC,QAAQ,mCAAI,KAAK,CACvB,CACF,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,KAAK,UAAU,QAAQ,CACrB,GAAe,EACf,GAA8C,EAC9C,OAAsB,EACtB,IAAgB;;IAEhB,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAC9B,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,uEAAuE;QACvE,gDAAgD;QAChD,MAAM,iBAAiB,CACrB,kDAAkD,EAClD,qEAAqE,CACtE,CAAC;IACJ,CAAC;IAED,MAAM,aAAa,GAAG,kBAAkB,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACtD,MAAM,KAAK,GAAG,MAAA,IAAI,CAAC,KAAK,mCAAI,YAAY,CAAC;IAEzC,GAAG,CAAC,GAAG,CACL,oBAAoB,OAAO,CAAC,IAAI,IAAI,aAAa,OAAO,GAAG,CAAC,IAAI,GAAG;QACjE,kBAAkB,sBAAsB,GAAG,IAAI,KAAK,CACvD,CAAC;IAEF,SAAS,CAAC;QACR,qEAAqE;QACrE,wEAAwE;QACxE,sEAAsE;QACtE,wEAAwE;QACxE,uEAAuE;QACvE,oEAAoE;QACpE,oEAAoE;QACpE,iEAAiE;QACjE,sEAAsE;QACtE,gEAAgE;QAChE,MAAM,OAAO,GAAG,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAEzC,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,MAAM,iBAAiB,CACrB,mBAAmB,GAAG,CAAC,IAAI,qCAAqC;gBAC9D,6BAA6B,EAC/B,4BAA4B,GAAG,CAAC,IAAI,cAAc,CACnD,CAAC;QACJ,CAAC;QAED,MAAM,YAAY,GAAG,MAAM,kBAAkB,CAC3C,GAAG,EACH,OAAO,EACP,MAAM,EACN,UAAU,CACX,CAAC;QACF,MAAM,OAAO,GAAG,WAAW,CAAC,YAAY,EAAE,OAAO,CAAC,IAAI,EAAE,aAAa,CAAC,CAAC;QAEvE,IAAI,OAAO,IAAI,qBAAqB,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;YACxD,OAAO,kBAAkB,CAAC,GAAG,EAAE,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QACxD,CAAC;QAED,GAAG,CAAC,GAAG,CACL,WAAW,OAAO,CAAC,IAAI,IAAI,aAAa,MAAM;YAC5C,GAAG,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,iBAAiB,aAAa,CAC9D,CAAC;QAEF,MAAM,KAAK,CAAC,sBAAsB,CAAC,CAAC;IACtC,CAAC;AACH,CAAC;AAED,SAAS,WAAW,CAClB,YAAiC,EACjC,IAAY,EACZ,OAAe;;IAEf,MAAM,WAAW,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC;IAC9D,OAAO,MAAA,WAAW,aAAX,WAAW,uBAAX,WAAW,CAAE,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,OAAO,CAAC,mCAAI,IAAI,CAAC;AAC1E,CAAC;AAED,SAAS,kBAAkB,CACzB,GAAe,EACf,IAAY,EACZ,OAA0B;;IAE1B,MAAM,IAAI,GAAG;QACX,IAAI;QACJ,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,KAAK,EAAE,OAAO,CAAC,KAAK;QACpB,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,UAAU,EAAE,OAAO,CAAC,UAAU;QAC9B,SAAS,EAAE,OAAO,CAAC,SAAS;QAC5B,QAAQ,EAAE,MAAA,OAAO,CAAC,QAAQ,mCAAI,EAAE;QAChC,MAAM,EAAE,MAAA,OAAO,CAAC,MAAM,mCAAI,EAAE;KAC7B,CAAC;IAEF,IAAI,OAAO,CAAC,KAAK,KAAK,UAAU,EAAE,CAAC;QACjC,OAAO,GAAG,CAAC,IAAI,CACb,eAAe,CACb,GAAG,IAAI,IAAI,OAAO,CAAC,OAAO,4BAA4B;YACpD,IAAI,MAAA,IAAI,CAAC,UAAU,mCAAI,CAAC,SAAS,IAAI,CAAC,UAAU,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,IAAI,EACvE,iEAAiE;YAC/D,uCAAuC,CAC1C,EACD,IAAI,CACL,CAAC;IACJ,CAAC;IAED,OAAO,GAAG,CAAC,OAAO,CAChB,IAAI,EACJ,mBAAmB,IAAI,IAAI,OAAO,CAAC,OAAO,YAAY,OAAO,CAAC,KAAK,GAAG;QACpE,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,SAAS,WAAW,IAAI,CAAC,SAAS,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,CAC/F,CAAC;AACJ,CAAC;AAED,SAAS,YAAY,CAAC,EAAU;IAC9B,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;IAC1B,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;;;GAKG;AACH,KAAK,UAAU,YAAY,CACzB,GAA8C,EAC9C,MAAqB;IAErB,IAAI,CAAC;QACH,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,MAAM,eAAe,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;QAEjE,OAAO;YACL,aAAa,EAAE,IAAI;YACnB,IAAI,EAAE,EAAE,EAAE,EAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,EAAE,QAAQ,CAAC,KAAK,EAAE,IAAI,EAAE,QAAQ,CAAC,IAAI,EAAE;YACrE,SAAS,EAAE,OAAO,CAAC,SAAS;SAC7B,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,KAAK,YAAY,QAAQ,IAAI,KAAK,CAAC,IAAI,KAAK,WAAW,CAAC,aAAa,EAAE,CAAC;YAC1E,OAAO,EAAE,aAAa,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,CAAC;QACpD,CAAC;QAED,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,SAAS,iBAAiB,CAAC,GAAW;;IACpC,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAErC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,sBAAsB,CAAC,CAAC,EAAE,CAAC;QAClE,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,MAAM,GAAG,WAAW,CAAC,UAAU,CAAC,CAAC;IAEvC,OAAO;QACL,GAAG,EAAE,MAAM,CAAC,UAAU;QACtB,IAAI,EAAE,MAAM,CAAC,OAAO,CAAC,IAAI;QACzB,MAAM,EAAE,MAAA,MAAM,CAAC,OAAO,CAAC,MAAM,mCAAI,IAAI;KACtC,CAAC;AACJ,CAAC;AAED,uEAAuE;AACvE,KAAK,UAAU,kBAAkB,CAC/B,GAA8C,EAC9C,OAAsB,EACtB,MAAc,EACd,MAAmB;IAEnB,MAAM,KAAK,GAAG,MAAM,eAAe,CAAC,GAAG,CAAC,IAAI,EAAE,OAAO,CAAC,WAAW,EAAE,MAAM,EAAE;QACzE,MAAM;KACP,CAAC,CAAC;IAEH,IAAI,KAAK,CAAC,MAAM,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC,YAAY,CAAC;IAErD,MAAM,SAAS,GAAG,MAAM,oBAAoB,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;IAC3D,MAAM,MAAM,GAAG,MAAM,eAAe,CAClC,GAAG,CAAC,IAAI,EACR,SAAS,CAAC,WAAW,EACrB,MAAM,EACN;QACE,MAAM;KACP,CACF,CAAC;IAEF,IAAI,MAAM,CAAC,MAAM,KAAK,IAAI;QAAE,OAAO,MAAM,CAAC,YAAY,CAAC;IAEvD,0EAA0E;IAC1E,4EAA4E;IAC5E,4EAA4E;IAC5E,MAAM,iBAAiB,CACrB,mBAAmB,GAAG,CAAC,IAAI,2CAA2C,EACtE,4BAA4B,GAAG,CAAC,IAAI,cAAc,CACnD,CAAC;AACJ,CAAC;AAED,SAAS,YAAY,CACnB,IAAY,EACZ,OAAsB,EACtB,OAA6B,EAC7B,WAAuC,EACvC,YAAqB;;IAErB,MAAM,KAAK,GAAa,EAAE,CAAC;IAE3B,KAAK,CAAC,IAAI,CACR,OAAO,CAAC,aAAa;QACnB,CAAC,CAAC,wBAAwB,IAAI,OAC1B,MAAA,MAAA,OAAO,CAAC,IAAI,0CAAE,KAAK,mCAAI,MAAA,OAAO,CAAC,IAAI,0CAAE,EACvC,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,qBAAqB,OAAO,CAAC,SAAS,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG;QAC1E,CAAC,CAAC,4BAA4B,IAAI,GAAG,CACxC,CAAC;IAEF,IAAI,OAAO,EAAE,CAAC;QACZ,KAAK,CAAC,IAAI,CACR,cAAc,OAAO,CAAC,IAAI,OAAO,OAAO,CAAC,GAAG,EAAE;YAC5C,CAAC,OAAO,CAAC,MAAM;gBACb,CAAC,CAAC,aAAa,OAAO,CAAC,MAAM,IAAI;gBACjC,CAAC,CAAC,6BAA6B,CAAC,CACrC,CAAC;IACJ,CAAC;SAAM,CAAC;QACN,KAAK,CAAC,IAAI,CAAC,wCAAwC,CAAC,CAAC;IACvD,CAAC;IAED,IAAI,WAAW,EAAE,CAAC;QAChB,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC7B,KAAK,CAAC,IAAI,CAAC,0BAA0B,CAAC,CAAC;QACzC,CAAC;QAED,KAAK,MAAM,WAAW,IAAI,WAAW,EAAE,CAAC;YACtC,KAAK,MAAM,OAAO,IAAI,WAAW,CAAC,QAAQ,EAAE,CAAC;gBAC3C,KAAK,CAAC,IAAI,CACR,kBAAkB,WAAW,CAAC,IAAI,IAAI,OAAO,CAAC,OAAO,KAAK;oBACxD,GAAG,OAAO,CAAC,KAAK,GACd,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,eAAe,OAAO,CAAC,WAAW,GAAG,CAAC,CAAC,CAAC,EAChE,GAAG,gBAAgB,CAAC,OAAO,CAAC,EAAE,CACjC,CAAC;gBAEF,IAAI,YAAY,EAAE,CAAC;oBACjB,KAAK,MAAM,OAAO,IAAI,MAAA,OAAO,CAAC,QAAQ,mCAAI,EAAE,EAAE,CAAC;wBAC7C,KAAK,CAAC,IAAI,CACR,QAAQ,OAAO,CAAC,QAAQ,KAAK,OAAO,CAAC,KAAK,KAAK,OAAO,CAAC,OAAO,GAAG;4BAC/D,KAAK,OAAO,CAAC,IAAI,EAAE,CACtB,CAAC;oBACJ,CAAC;gBACH,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED,iFAAiF;AACjF,SAAS,gBAAgB,CAAC,OAA0B;;IAClD,IAAI,OAAO,CAAC,UAAU,KAAK,IAAI,IAAI,OAAO,CAAC,SAAS,KAAK,IAAI;QAAE,OAAO,EAAE,CAAC;IAEzE,MAAM,UAAU,GAAG,MAAA,OAAO,CAAC,UAAU,mCAAI,CAAC,CAAC;IAC3C,MAAM,SAAS,GAAG,MAAA,OAAO,CAAC,SAAS,mCAAI,CAAC,CAAC;IAEzC,OAAO,CACL,KAAK,UAAU,SAAS,UAAU,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,IAAI;QACvD,GAAG,SAAS,WAAW,SAAS,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,GAAG,CACrD,CAAC;AACJ,CAAC","sourcesContent":["import * as fs from 'node:fs';\nimport * as path from 'node:path';\n\nimport type { StoredSession } from '../auth/credential-store';\nimport {\n refreshStoredSession,\n resolveAuthEnvironment,\n resolveIdentity,\n} from '../auth/session';\nimport type { CliContext } from '../context';\nimport {\n CliError,\n authRequiredError,\n gateFailedError,\n preconditionError,\n} from '../errors';\nimport { ERROR_CODES, type ExitCode } from '../exit-codes';\nimport { EKANOS_CONFIG_FILENAME, loadProject } from '../project';\nimport {\n type SubmissionListing,\n type SubmissionVersion,\n listSubmissions,\n} from '../publish-api';\nimport { announceUpdate, checkForUpdate } from '../update-notice';\nimport { readProjectVersion } from './publish';\n\n/** `--watch`'s poll interval — matches the mission brief exactly. */\nconst WATCH_POLL_INTERVAL_MS = 30_000;\n\n/**\n * The states `--watch` stops on. `submitted` is the only non-terminal state\n * a version can be in when watching starts (an already-`gated`/`dev`/\n * `rejected` version resolves on the very first check, no polling needed).\n */\nconst WATCH_TERMINAL_STATES = new Set(['gated', 'dev', 'rejected']);\n\nexport interface StatusArgs {\n host?: string;\n /** Project directory (may contain ekanos.json). Defaults to cwd. */\n dir: string;\n env: Record<string, string | undefined>;\n /** Render each version's full finding list, not just error/warn counts. */\n findings?: boolean;\n /**\n * Block until this project's current (slug, version) submission reaches a\n * terminal-for-waiting state (gated, dev, rejected), polling every 30s.\n * Changes the command's exit-code contract — see `runStatus`'s docstring.\n */\n watch?: boolean;\n /** Injectable for tests — see `auth/device-flow.ts`'s identical pattern. */\n sleep?: (ms: number) => Promise<void>;\n}\n\ninterface SessionReport {\n authenticated: boolean;\n user?: { id: string; email: string | null; name: string | null };\n expiresAt?: string;\n hint?: string;\n}\n\ninterface ProjectReport {\n dir: string;\n slug: string;\n source: string | null;\n}\n\n/**\n * `status` — the read verb: where am I pointed, who am I, and what has this\n * project submitted?\n *\n * Three sections, each degrading gracefully rather than failing:\n *\n * - **session** — whoami's server-side identity check, but an absent or dead\n * session reports `authenticated: false` with a hint instead of exit 4. A\n * partner asking \"what is my state?\" must get an answer, not an error whose\n * answer it is.\n * - **project** — the ekanos.json in `--dir` (default cwd), when there is\n * one. No project is an ordinary state for `status`, never an error.\n * - **submissions** — `GET /api/partner/submissions?source=…` for the\n * project's resolved source, only when both a session and a source exist.\n * Server errors here DO map through the taxonomy: an unreachable host or a\n * missing source is a fault to surface, not a state to absorb.\n *\n * Exit 0 whenever the report itself could be assembled — EXCEPT under\n * `--watch` (see below), whose exit code carries the gate's verdict instead.\n *\n * `--findings` requests each version's full finding list (`?detail=findings`\n * server-side) instead of just error/warn counts.\n *\n * `--watch` turns `status` into a blocking wait: it requires a project with\n * a resolved source and a live session (both are PRECONDITIONs here, not\n * degraded-to-false states — there is nothing to watch without them), then\n * polls `GET /api/partner/submissions?detail=findings` every 30s for THIS\n * project's current (slug, package.json version) submission until it reaches\n * a terminal-for-waiting state: `gated`/`dev` exit 0, `rejected` exits 10\n * (GATE_FAILED) with the findings carried in `data` — the same shape\n * `publish`'s own gate refusal uses, so an agent's fix-then-retry loop\n * branches on it identically whether the gate ran inline (future) or via\n * this poll (today). `--json --watch` still emits exactly ONE envelope, at\n * completion — the polling itself only ever writes to stderr via `ctx.log`.\n */\nexport async function runStatus(\n ctx: CliContext,\n args: StatusArgs,\n): Promise<ExitCode> {\n const env = resolveAuthEnvironment(ctx, args.host, args.env, {\n projectDir: args.dir,\n });\n\n const stored = env.store.read(env.host);\n\n const notice = await checkForUpdate({\n env: args.env,\n host: env.host,\n session: stored ?? undefined,\n });\n announceUpdate(ctx, notice);\n\n const session = stored\n ? await probeSession(env, stored)\n : {\n authenticated: false,\n hint: `Run \"ekanos login --host ${env.host}\" to sign in.`,\n };\n\n // probeSession may have refreshed the session — persisting a NEW access\n // token and ROTATING the refresh token on disk. Re-read before using it:\n // the pre-probe object now holds a dead access token and an already-spent\n // refresh token, and presenting those would turn \"logged in, token was\n // merely stale\" into a hard exit-4 — the one failure this command promises\n // never to produce.\n const live = stored ? (env.store.read(env.host) ?? stored) : null;\n\n const project = readProjectReport(args.dir);\n\n const detail = args.findings || args.watch ? 'findings' : undefined;\n\n if (args.watch) {\n if (!session.authenticated || !live) {\n throw authRequiredError(\n `\"ekanos status --watch\" needs a live session on ${env.host}.`,\n `Run \"ekanos login --host ${env.host}\" and retry.`,\n );\n }\n\n if (!project) {\n throw preconditionError(\n `No ekanos.json found in ${path.resolve(args.dir)}.`,\n 'Run \"ekanos publish\" at least once before watching its gate result.',\n );\n }\n\n if (!project.source) {\n throw preconditionError(\n 'This project has no \"source\" in ekanos.json yet.',\n 'Run \"ekanos publish\" at least once (it saves the source you ' +\n 'publish to) before watching its gate result.',\n );\n }\n\n return runWatch(ctx, env, project, args);\n }\n\n let submissions: SubmissionListing[] | null = null;\n\n if (project?.source && session.authenticated && live) {\n submissions = await listWithOneRefresh(env, live, project.source, detail);\n } else if (project?.source && !session.authenticated) {\n ctx.log(\n `ekanos: not logged in to ${env.host} — skipping the submission list. ` +\n (session.hint ?? ''),\n );\n } else if (project && !project.source) {\n ctx.log(\n 'ekanos: this project has no \"source\" in ekanos.json yet — the first ' +\n '\"ekanos publish --source <slug>\" saves one, and status will list ' +\n 'submissions from then on.',\n );\n }\n\n return ctx.succeed(\n {\n host: env.host,\n ...session,\n credentialsPath: env.store.filePath,\n project,\n submissions,\n ...(notice ? { toolchain: notice } : {}),\n },\n humanSummary(\n env.host,\n session,\n project,\n submissions,\n args.findings ?? false,\n ),\n );\n}\n\n/**\n * The `--watch` poll loop. Targets THIS project's current submission —\n * `project.slug` + the version presently in `package.json` (the same value\n * `publish` would submit right now) — not \"whatever's most recent,\" so\n * watching after a publish can never resolve against a stale prior version.\n */\nasync function runWatch(\n ctx: CliContext,\n env: ReturnType<typeof resolveAuthEnvironment>,\n project: ProjectReport,\n args: StatusArgs,\n): Promise<ExitCode> {\n const source = project.source;\n if (!source) {\n // Unreachable — the caller already checked — but keeps this function's\n // own type honest without a non-null assertion.\n throw preconditionError(\n 'This project has no \"source\" in ekanos.json yet.',\n 'Run \"ekanos publish\" at least once before watching its gate result.',\n );\n }\n\n const targetVersion = readProjectVersion(project.dir);\n const sleep = args.sleep ?? defaultSleep;\n\n ctx.log(\n `ekanos: watching ${project.slug}@${targetVersion} on ${env.host} ` +\n `(polling every ${WATCH_POLL_INTERVAL_MS / 1000}s)…`,\n );\n\n for (;;) {\n // Re-read the session from the credential store on EVERY iteration —\n // never carry one over in a closure. Auth0 rotates the refresh token on\n // every use (see `refreshStoredSession`'s docstring), so a token this\n // loop already consumed is dead by the next iteration; the store is the\n // persisted source of truth for whichever refresh most recently landed\n // — this loop's own (via `listWithOneRefresh` below, which persists\n // through `refreshStoredSession`), or a concurrent one from another\n // process (another terminal running login/publish/status --watch\n // against the same host). Threading the refreshed session back out of\n // `listWithOneRefresh` instead would only cover the first case.\n const current = env.store.read(env.host);\n\n if (!current) {\n throw authRequiredError(\n `The session for ${env.host} is gone — \"ekanos status --watch\" ` +\n 'cannot continue without it.',\n `Run \"ekanos login --host ${env.host}\" and retry.`,\n );\n }\n\n const integrations = await listWithOneRefresh(\n env,\n current,\n source,\n 'findings',\n );\n const version = findVersion(integrations, project.slug, targetVersion);\n\n if (version && WATCH_TERMINAL_STATES.has(version.state)) {\n return reportWatchOutcome(ctx, project.slug, version);\n }\n\n ctx.log(\n `ekanos: ${project.slug}@${targetVersion} is ` +\n `${version ? version.state : 'not visible yet'} — waiting…`,\n );\n\n await sleep(WATCH_POLL_INTERVAL_MS);\n }\n}\n\nfunction findVersion(\n integrations: SubmissionListing[],\n slug: string,\n version: string,\n): SubmissionVersion | null {\n const integration = integrations.find((i) => i.slug === slug);\n return integration?.versions.find((v) => v.version === version) ?? null;\n}\n\nfunction reportWatchOutcome(\n ctx: CliContext,\n slug: string,\n version: SubmissionVersion,\n): ExitCode {\n const data = {\n slug,\n version: version.version,\n state: version.state,\n gatedAt: version.gatedAt,\n errorCount: version.errorCount,\n warnCount: version.warnCount,\n findings: version.findings ?? [],\n stages: version.stages ?? [],\n };\n\n if (version.state === 'rejected') {\n return ctx.fail(\n gateFailedError(\n `${slug}@${version.version} was rejected by the gate ` +\n `(${data.errorCount ?? 0} error${data.errorCount === 1 ? '' : 's'}).`,\n 'Resolve the error-severity findings in data.findings, bump the ' +\n 'version, and re-run \"ekanos publish\".',\n ),\n data,\n );\n }\n\n return ctx.succeed(\n data,\n `status --watch: ${slug}@${version.version} is now \"${version.state}\"` +\n `${data.warnCount ? ` (${data.warnCount} warning${data.warnCount === 1 ? '' : 's'})` : ''}.`,\n );\n}\n\nfunction defaultSleep(ms: number): Promise<void> {\n return new Promise((resolve) => {\n setTimeout(resolve, ms);\n });\n}\n\n/**\n * whoami's identity resolution, with AUTH_REQUIRED absorbed into\n * `authenticated: false`. Anything else — network, precondition, MFA mapped\n * to auth by `fetchIdentity` — still propagates: \"the host was unreachable\"\n * must never be reported as \"you are logged out\".\n */\nasync function probeSession(\n env: ReturnType<typeof resolveAuthEnvironment>,\n stored: StoredSession,\n): Promise<SessionReport> {\n try {\n const { identity, session } = await resolveIdentity(env, stored);\n\n return {\n authenticated: true,\n user: { id: identity.id, email: identity.email, name: identity.name },\n expiresAt: session.expiresAt,\n };\n } catch (error) {\n if (error instanceof CliError && error.code === ERROR_CODES.AUTH_REQUIRED) {\n return { authenticated: false, hint: error.hint };\n }\n\n throw error;\n }\n}\n\n/**\n * Lenient project read: no ekanos.json means \"not in a project\", which is an\n * answer, not an error. An ekanos.json that EXISTS but does not load keeps\n * `loadProject`'s strict behaviour — a broken project file is a fault the\n * partner needs named, whatever verb they ran.\n */\nfunction readProjectReport(dir: string): ProjectReport | null {\n const projectDir = path.resolve(dir);\n\n if (!fs.existsSync(path.join(projectDir, EKANOS_CONFIG_FILENAME))) {\n return null;\n }\n\n const loaded = loadProject(projectDir);\n\n return {\n dir: loaded.projectDir,\n slug: loaded.primary.slug,\n source: loaded.project.source ?? null,\n };\n}\n\n/** Mirrors publish's one-refresh-one-retry on a stale access token. */\nasync function listWithOneRefresh(\n env: ReturnType<typeof resolveAuthEnvironment>,\n session: StoredSession,\n source: string,\n detail?: 'findings',\n): Promise<SubmissionListing[]> {\n const first = await listSubmissions(env.host, session.accessToken, source, {\n detail,\n });\n\n if (first.status === 'ok') return first.integrations;\n\n const refreshed = await refreshStoredSession(env, session);\n const second = await listSubmissions(\n env.host,\n refreshed.accessToken,\n source,\n {\n detail,\n },\n );\n\n if (second.status === 'ok') return second.integrations;\n\n // Authenticated a moment ago, unauthorized twice here: report the list as\n // unavailable rather than the session as dead — probeSession already proved\n // it. This is unreachable in practice; belt and braces for a racing revoke.\n throw authRequiredError(\n `The session for ${env.host} could not authorize the submission list.`,\n `Run \"ekanos login --host ${env.host}\" and retry.`,\n );\n}\n\nfunction humanSummary(\n host: string,\n session: SessionReport,\n project: ProjectReport | null,\n submissions: SubmissionListing[] | null,\n showFindings: boolean,\n): string {\n const lines: string[] = [];\n\n lines.push(\n session.authenticated\n ? `status: logged in to ${host} as ${\n session.user?.email ?? session.user?.id\n }${session.expiresAt ? ` (session expires ${session.expiresAt})` : ''}.`\n : `status: not logged in to ${host}.`,\n );\n\n if (project) {\n lines.push(\n ` project: ${project.slug} in ${project.dir}` +\n (project.source\n ? ` (source \"${project.source}\")`\n : ' (no source configured yet)'),\n );\n } else {\n lines.push(' project: none (no ekanos.json here).');\n }\n\n if (submissions) {\n if (submissions.length === 0) {\n lines.push(' submissions: none yet.');\n }\n\n for (const integration of submissions) {\n for (const version of integration.versions) {\n lines.push(\n ` submissions: ${integration.slug}@${version.version} — ` +\n `${version.state}${\n version.submittedAt ? ` (submitted ${version.submittedAt})` : ''\n }${gateCountsSuffix(version)}`,\n );\n\n if (showFindings) {\n for (const finding of version.findings ?? []) {\n lines.push(\n ` [${finding.severity}] ${finding.check}: ${finding.message} ` +\n `— ${finding.hint}`,\n );\n }\n }\n }\n }\n }\n\n return lines.join('\\n');\n}\n\n/** ` (N errors, M warnings)`, omitted entirely until a gate has actually run. */\nfunction gateCountsSuffix(version: SubmissionVersion): string {\n if (version.errorCount === null && version.warnCount === null) return '';\n\n const errorCount = version.errorCount ?? 0;\n const warnCount = version.warnCount ?? 0;\n\n return (\n ` (${errorCount} error${errorCount === 1 ? '' : 's'}, ` +\n `${warnCount} warning${warnCount === 1 ? '' : 's'})`\n );\n}\n"]}
@@ -0,0 +1,9 @@
1
+ export declare const FA_FREE_VERSION = "7.3.1";
2
+ /** Every icon name Font Awesome Free ships — NOT Fusion's set (Pro, a superset). */
3
+ export declare const FA_FREE_ICON_NAMES: ReadonlySet<string>;
4
+ /**
5
+ * Hand-curated Pro-only name → nearest Free alternative, sourced from
6
+ * @ekanos/harness's Pro→Free glyph shim. Not exhaustive — a Pro-only name
7
+ * with no shim entry has no known Free substitute here.
8
+ */
9
+ export declare const FA_PRO_TO_FREE_HINTS: ReadonlyMap<string, string>;