@ekanos/cli 0.4.2 → 0.5.1

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.
@@ -15,3 +15,11 @@ export interface SourcesArgs {
15
15
  * publish to?" before running `ekanos use` or passing `--source`.
16
16
  */
17
17
  export declare function runSources(ctx: CliContext, args: SourcesArgs): Promise<ExitCode>;
18
+ /**
19
+ * Best-effort, lenient read of ekanos.json's `source` field — mirrors
20
+ * `readProjectHost` in `auth/session.ts`. A missing, unparseable, or
21
+ * sourceless ekanos.json (or no project at all) simply means "no active
22
+ * source to mark", never an error: `sources` must answer outside a project
23
+ * too.
24
+ */
25
+ export declare function readActiveSource(dir: string): string | null;
@@ -32,7 +32,7 @@ export async function runSources(ctx, args) {
32
32
  * source to mark", never an error: `sources` must answer outside a project
33
33
  * too.
34
34
  */
35
- function readActiveSource(dir) {
35
+ export function readActiveSource(dir) {
36
36
  const configPath = path.join(dir, EKANOS_CONFIG_FILENAME);
37
37
  let raw;
38
38
  try {
@@ -1 +1 @@
1
- {"version":3,"file":"sources.js","sourceRoot":"","sources":["../../src/commands/sources.ts"],"names":[],"mappings":"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","sourcesContent":["import * as fs from 'node:fs';\nimport * as path from 'node:path';\n\nimport { requireSession, resolveAuthEnvironment } from '../auth/session';\nimport type { CliContext } from '../context';\nimport type { ExitCode } from '../exit-codes';\nimport { EKANOS_CONFIG_FILENAME } from '../project';\nimport { fetchSeatsWithOneRefresh } from '../seats';\nimport { type SourceSeat, stillUnauthorizedSourceSeats } from '../sources-api';\nimport { type Ui, pluralize, shortHost } from '../ui';\n\nexport interface SourcesArgs {\n host?: string;\n /** Where a project's ekanos.json may sit — its `source` field, if any, is the marker. */\n dir: string;\n env: Record<string, string | undefined>;\n}\n\n/**\n * `sources` — list every source the caller holds a developer or admin seat\n * on, the read half of the publish-target-verification story.\n *\n * The list itself is the mechanism that makes `publish` safe: a partner (or\n * an agent acting for one) can always answer \"which sources can I possibly\n * publish to?\" before running `ekanos use` or passing `--source`.\n */\nexport async function runSources(\n ctx: CliContext,\n args: SourcesArgs,\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 result = await fetchSeatsWithOneRefresh(env, session);\n\n if (result.status === 'unauthorized') {\n stillUnauthorizedSourceSeats(env.host);\n }\n\n const activeSource = readActiveSource(args.dir);\n\n return ctx.succeed(\n { host: env.host, sources: result.sources, activeSource },\n humanSummary(ctx.ui, env.host, result.sources, activeSource),\n );\n}\n\n/**\n * Best-effort, lenient read of ekanos.json's `source` field — mirrors\n * `readProjectHost` in `auth/session.ts`. A missing, unparseable, or\n * sourceless ekanos.json (or no project at all) simply means \"no active\n * source to mark\", never an error: `sources` must answer outside a project\n * too.\n */\nfunction readActiveSource(dir: string): string | null {\n const configPath = path.join(dir, EKANOS_CONFIG_FILENAME);\n\n let raw: string;\n try {\n raw = fs.readFileSync(configPath, 'utf8');\n } catch {\n return null;\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n return null;\n }\n\n const source =\n typeof parsed === 'object' && parsed !== null\n ? (parsed as { source?: unknown }).source\n : undefined;\n\n return typeof source === 'string' && source.trim().length > 0\n ? source.trim()\n : null;\n}\n\nfunction humanSummary(\n ui: Ui,\n host: string,\n sources: SourceSeat[],\n activeSource: string | null,\n): string {\n if (sources.length === 0) {\n return ui.result({\n title: 'No sources',\n tone: 'warn',\n rows: [['Host', shortHost(host)]],\n notes: ['This account holds no developer seats on this host.'],\n });\n }\n\n const orphaned =\n activeSource && !sources.some((s) => s.slug === activeSource);\n\n return ui.result({\n title: `${pluralize(sources.length, 'source')} on ${shortHost(host)}`,\n rows: sources.map((source): [string, string] => [\n source.slug,\n [\n source.name ?? '(unnamed)',\n source.roles.join(', '),\n ...(source.slug === activeSource ? [ui.accent('this project')] : []),\n ].join(' · '),\n ]),\n notes: orphaned\n ? [\n `This project's ekanos.json points at \"${activeSource}\", which is not in the list above.`,\n ]\n : [],\n });\n}\n"]}
1
+ {"version":3,"file":"sources.js","sourceRoot":"","sources":["../../src/commands/sources.ts"],"names":[],"mappings":"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","sourcesContent":["import * as fs from 'node:fs';\nimport * as path from 'node:path';\n\nimport { requireSession, resolveAuthEnvironment } from '../auth/session';\nimport type { CliContext } from '../context';\nimport type { ExitCode } from '../exit-codes';\nimport { EKANOS_CONFIG_FILENAME } from '../project';\nimport { fetchSeatsWithOneRefresh } from '../seats';\nimport { type SourceSeat, stillUnauthorizedSourceSeats } from '../sources-api';\nimport { type Ui, pluralize, shortHost } from '../ui';\n\nexport interface SourcesArgs {\n host?: string;\n /** Where a project's ekanos.json may sit — its `source` field, if any, is the marker. */\n dir: string;\n env: Record<string, string | undefined>;\n}\n\n/**\n * `sources` — list every source the caller holds a developer or admin seat\n * on, the read half of the publish-target-verification story.\n *\n * The list itself is the mechanism that makes `publish` safe: a partner (or\n * an agent acting for one) can always answer \"which sources can I possibly\n * publish to?\" before running `ekanos use` or passing `--source`.\n */\nexport async function runSources(\n ctx: CliContext,\n args: SourcesArgs,\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 result = await fetchSeatsWithOneRefresh(env, session);\n\n if (result.status === 'unauthorized') {\n stillUnauthorizedSourceSeats(env.host);\n }\n\n const activeSource = readActiveSource(args.dir);\n\n return ctx.succeed(\n { host: env.host, sources: result.sources, activeSource },\n humanSummary(ctx.ui, env.host, result.sources, activeSource),\n );\n}\n\n/**\n * Best-effort, lenient read of ekanos.json's `source` field — mirrors\n * `readProjectHost` in `auth/session.ts`. A missing, unparseable, or\n * sourceless ekanos.json (or no project at all) simply means \"no active\n * source to mark\", never an error: `sources` must answer outside a project\n * too.\n */\nexport function readActiveSource(dir: string): string | null {\n const configPath = path.join(dir, EKANOS_CONFIG_FILENAME);\n\n let raw: string;\n try {\n raw = fs.readFileSync(configPath, 'utf8');\n } catch {\n return null;\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n return null;\n }\n\n const source =\n typeof parsed === 'object' && parsed !== null\n ? (parsed as { source?: unknown }).source\n : undefined;\n\n return typeof source === 'string' && source.trim().length > 0\n ? source.trim()\n : null;\n}\n\nfunction humanSummary(\n ui: Ui,\n host: string,\n sources: SourceSeat[],\n activeSource: string | null,\n): string {\n if (sources.length === 0) {\n return ui.result({\n title: 'No sources',\n tone: 'warn',\n rows: [['Host', shortHost(host)]],\n notes: ['This account holds no developer seats on this host.'],\n });\n }\n\n const orphaned =\n activeSource && !sources.some((s) => s.slug === activeSource);\n\n return ui.result({\n title: `${pluralize(sources.length, 'source')} on ${shortHost(host)}`,\n rows: sources.map((source): [string, string] => [\n source.slug,\n [\n source.name ?? '(unnamed)',\n source.roles.join(', '),\n ...(source.slug === activeSource ? [ui.accent('this project')] : []),\n ].join(' · '),\n ]),\n notes: orphaned\n ? [\n `This project's ekanos.json points at \"${activeSource}\", which is not in the list above.`,\n ]\n : [],\n });\n}\n"]}
package/dist/index.js CHANGED
@@ -1,4 +1,6 @@
1
1
  import * as fs from 'node:fs';
2
+ import { runAgents } from './commands/agents.js';
3
+ import { runCatalog } from './commands/catalog.js';
2
4
  import { runDev } from './commands/dev.js';
3
5
  import { runInit } from './commands/init.js';
4
6
  import { runLogin } from './commands/login.js';
@@ -73,6 +75,22 @@ const COMMAND_SPECS = {
73
75
  host: { type: 'string' },
74
76
  dir: { type: 'string' },
75
77
  },
78
+ catalog: {
79
+ host: { type: 'string' },
80
+ source: { type: 'string' },
81
+ dir: { type: 'string' },
82
+ },
83
+ agents: {
84
+ host: { type: 'string' },
85
+ source: { type: 'string' },
86
+ dir: { type: 'string' },
87
+ // Not `--version`: that is global, and prints the CLI's own version.
88
+ to: { type: 'string' },
89
+ 'base-version': { type: 'string' },
90
+ overwrite: { type: 'boolean' },
91
+ out: { type: 'string' },
92
+ yes: { type: 'boolean' },
93
+ },
76
94
  use: {
77
95
  host: { type: 'string' },
78
96
  dir: { type: 'string' },
@@ -93,9 +111,11 @@ const COMMAND_SPECS = {
93
111
  verify: { type: 'boolean' },
94
112
  },
95
113
  };
96
- /** Commands that take a positional argument, and what it means. Every other command is positional-free. */
114
+ /** Commands that take positional arguments, and what each means, in order. Every other command is positional-free. */
97
115
  const POSITIONAL_COMMANDS = {
98
- use: 'source-slug',
116
+ use: ['source-slug'],
117
+ catalog: ['product-slug'],
118
+ agents: ['subcommand', 'key-or-path'],
99
119
  };
100
120
  const COMMANDS = Object.keys(COMMAND_SPECS);
101
121
  const USAGE = [
@@ -118,6 +138,12 @@ const USAGE = [
118
138
  ' integrations in the source, --watch to block until this',
119
139
  " project's current version reaches a gate verdict)",
120
140
  ' sources List every source you hold a developer/source-admin seat on',
141
+ ' catalog What a source can already use, and how to set it up',
142
+ ' ("ekanos catalog [<product-slug>] --source <slug>", --json)',
143
+ " agents The source's agent templates, held by Ekanos:",
144
+ ' list | show <key> | pull <key> [--out <dir>]',
145
+ ' apply <dir|file> [--base-version <n>] [--overwrite]',
146
+ ' publish <key> [--to <n>] --yes | rollback <key> --to <n> --yes',
121
147
  ' use Set this project\'s publish target ("ekanos use <source-slug>")',
122
148
  ' publish Validate, pack and submit the integration to an Ekanos host',
123
149
  ' upgrade Move this toolchain to a coherent target version, with',
@@ -132,7 +158,7 @@ const USAGE = [
132
158
  ' bare "ekanos upgrade" MUTATES the project (installs the target versions,',
133
159
  ' verifies with validate + the test script, rolls back on any failure).',
134
160
  '',
135
- 'Host resolution (logout, whoami, status, sources, use, publish, upgrade):',
161
+ 'Host resolution (logout, whoami, status, sources, catalog, agents, use, publish, upgrade):',
136
162
  ' --host, else EKANOS_HOST, else the "host" field in ekanos.json, else the',
137
163
  ' sole stored login. "ekanos publish" saves host and source to ekanos.json.',
138
164
  ' "ekanos login" never reads ekanos.json for a host (--host, else',
@@ -158,7 +184,7 @@ const USAGE = [
158
184
  * `process.exit`, so this stays fully testable.
159
185
  */
160
186
  export async function run(argv, options = {}) {
161
- var _a, _b, _c;
187
+ var _a, _b, _c, _d;
162
188
  const jsonFlag = scanJsonFlag(argv);
163
189
  const ctx = new CliContext({
164
190
  jsonFlag,
@@ -205,16 +231,17 @@ export async function run(argv, options = {}) {
205
231
  if (parsed.flags.help === true) {
206
232
  return emitCommandHelp(ctx, command);
207
233
  }
208
- const positionalName = POSITIONAL_COMMANDS[command];
209
- if (!positionalName && parsed.positionals.length > 0) {
234
+ const positionalNames = (_c = POSITIONAL_COMMANDS[command]) !== null && _c !== void 0 ? _c : [];
235
+ if (positionalNames.length === 0 && parsed.positionals.length > 0) {
210
236
  throw usageError(`"${command}" takes no positional arguments (got "${parsed.positionals[0]}").`, `Pass options as flags, e.g. "--dir <path>". See "ekanos ${command} --help".`);
211
237
  }
212
- if (positionalName && parsed.positionals.length > 1) {
213
- throw usageError(`"${command}" takes at most one positional argument (a ` +
214
- `${positionalName}), got ${parsed.positionals.length}.`, `Pass exactly one, e.g. "ekanos ${command} <${positionalName}>". ` +
215
- `See "ekanos ${command} --help".`);
238
+ if (parsed.positionals.length > positionalNames.length) {
239
+ const usage = positionalNames.map((name) => `<${name}>`).join(' ');
240
+ throw usageError(`"${command}" takes at most ${positionalNames.length} positional ` +
241
+ `argument${positionalNames.length === 1 ? '' : 's'} (${usage}), ` +
242
+ `got ${parsed.positionals.length}.`, `e.g. "ekanos ${command} ${usage}". See "ekanos ${command} --help".`);
216
243
  }
217
- const dir = (_c = parsed.flags.dir) !== null && _c !== void 0 ? _c : cwd;
244
+ const dir = (_d = parsed.flags.dir) !== null && _d !== void 0 ? _d : cwd;
218
245
  switch (command) {
219
246
  case 'init':
220
247
  return await runInit(ctx, {
@@ -281,6 +308,30 @@ export async function run(argv, options = {}) {
281
308
  dir,
282
309
  env,
283
310
  });
311
+ case 'catalog':
312
+ return await runCatalog(ctx, {
313
+ host: parsed.flags.host,
314
+ source: parsed.flags.source,
315
+ product: parsed.positionals[0],
316
+ dir,
317
+ env,
318
+ });
319
+ case 'agents':
320
+ return await runAgents(ctx, {
321
+ host: parsed.flags.host,
322
+ source: parsed.flags.source,
323
+ subcommand: parsed.positionals[0],
324
+ target: parsed.positionals[1],
325
+ to: parsed.flags.to,
326
+ baseVersion: parsed.flags['base-version'],
327
+ overwrite: parsed.flags.overwrite === true,
328
+ out: parsed.flags.out,
329
+ yes: parsed.flags.yes === true,
330
+ dir,
331
+ env,
332
+ stdinIsTTY: options.stdinIsTTY,
333
+ confirm: options.confirm,
334
+ });
284
335
  case 'use':
285
336
  return await runUse(ctx, {
286
337
  host: parsed.flags.host,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["import * as fs from 'node:fs';\n\nimport { runDev } from './commands/dev';\nimport { runInit } from './commands/init';\nimport { runLogin } from './commands/login';\nimport { runLogout } from './commands/logout';\nimport { runPublish } from './commands/publish';\nimport { runSources } from './commands/sources';\nimport { runStatus } from './commands/status';\nimport { runTest } from './commands/test';\nimport { runUpgrade } from './commands/upgrade';\nimport { runUse } from './commands/use';\nimport { runValidate } from './commands/validate';\nimport { runWhoami } from './commands/whoami';\nimport { CliContext, type WriteStream } from './context';\nimport { SDK_DOCS_URL, docsPage } from './docs-links';\nimport { toCliError, usageError } from './errors';\nimport { EXIT_CODES } from './exit-codes';\nimport { type FlagSpecs, parseArgv } from './parse-argv';\n\nexport { CliContext } from './context';\nexport { EXIT_CODES, ERROR_CODES } from './exit-codes';\nexport type { ExitCode, ErrorCode } from './exit-codes';\n\nconst GLOBAL_SPECS: FlagSpecs = {\n json: { type: 'boolean' },\n help: { type: 'boolean' },\n version: { type: 'boolean' },\n};\n\nconst COMMAND_SPECS: Record<string, FlagSpecs> = {\n init: {\n slug: { type: 'string' },\n dir: { type: 'string' },\n force: { type: 'boolean' },\n yes: { type: 'boolean' },\n // Repeatable: each occurrence appends one docs entry (URL or relative\n // path) to the scaffolded ekanos.json.\n docs: { type: 'string', repeatable: true },\n },\n validate: {\n dir: { type: 'string' },\n },\n dev: {\n dir: { type: 'string' },\n port: { type: 'string' },\n host: { type: 'string' },\n force: { type: 'boolean' },\n start: { type: 'boolean' },\n },\n test: {\n dir: { type: 'string' },\n // Skips the vitest JSON-reporter injection and the curated failures\n // appendix, reproducing the pre-appendix raw-passthrough behavior —\n // also the escape hatch if the vitest-detection heuristic ever guesses\n // wrong for a project. See `test-report.ts`.\n raw: { type: 'boolean' },\n },\n login: {\n host: { type: 'string' },\n force: { type: 'boolean' },\n },\n logout: {\n host: { type: 'string' },\n all: { type: 'boolean' },\n },\n whoami: {\n host: { type: 'string' },\n },\n status: {\n host: { type: 'string' },\n dir: { type: 'string' },\n findings: { type: 'boolean' },\n all: { type: 'boolean' },\n watch: { type: 'boolean' },\n },\n sources: {\n host: { type: 'string' },\n dir: { type: 'string' },\n },\n use: {\n host: { type: 'string' },\n dir: { type: 'string' },\n },\n publish: {\n host: { type: 'string' },\n source: { type: 'string' },\n dir: { type: 'string' },\n yes: { type: 'boolean' },\n },\n upgrade: {\n dir: { type: 'string' },\n host: { type: 'string' },\n check: { type: 'boolean' },\n yes: { type: 'boolean' },\n // `--no-verify` is the negation of this — parse-argv's `--no-<bool>`\n // convention (same as `--no-json`/`--no-start`), not a flag of its own.\n verify: { type: 'boolean' },\n },\n};\n\n/** Commands that take a positional argument, and what it means. Every other command is positional-free. */\nconst POSITIONAL_COMMANDS: Record<string, string> = {\n use: 'source-slug',\n};\n\nconst COMMANDS = Object.keys(COMMAND_SPECS);\n\nconst USAGE = [\n 'Usage: ekanos <command> [options]',\n '',\n 'Commands:',\n ' init Scaffold a new integration project into ./<slug> (--slug <slug> --yes)',\n ' validate Validate ekanos.json + the integration definition (--json)',\n ' dev Scaffold .ekanos/harness and run it with your own Next',\n ' test Run the project test script via its package manager (--json).',\n ' For a vitest test script, also renders a curated failures',\n ' appendix from a JSON reporter report; --raw skips that and',\n ' the reporter injection entirely.',\n ' login Authenticate against an Ekanos host in a browser (--host <url>)',\n ' logout Revoke and delete the stored session (--host <url> | --all)',\n ' whoami Print the identity the stored session authenticates as, plus',\n ' a compact list of the sources you hold a seat on',\n \" status Show login state and this project's submissions on the host\",\n ' (--findings for full gate findings, --all to expand other',\n ' integrations in the source, --watch to block until this',\n \" project's current version reaches a gate verdict)\",\n ' sources List every source you hold a developer/source-admin seat on',\n ' use Set this project\\'s publish target (\"ekanos use <source-slug>\")',\n ' publish Validate, pack and submit the integration to an Ekanos host',\n ' upgrade Move this toolchain to a coherent target version, with',\n ' verify-and-rollback (--check --yes --no-verify --host <url>)',\n '',\n 'Keeping the toolchain current:',\n ' validate, dev and publish all check for a newer @ekanos/cli in the',\n ' background (never blocking, silenced by EKANOS_NO_UPDATE_CHECK) and warn',\n ' on stderr when one exists. Run \"ekanos upgrade --check\" any time a',\n ' validate finding smells like version skew (an unrecognized-key error you',\n ' are confident is correct) — it is read-only and safe to run freely. A',\n ' bare \"ekanos upgrade\" MUTATES the project (installs the target versions,',\n ' verifies with validate + the test script, rolls back on any failure).',\n '',\n 'Host resolution (logout, whoami, status, sources, use, publish, upgrade):',\n ' --host, else EKANOS_HOST, else the \"host\" field in ekanos.json, else the',\n ' sole stored login. \"ekanos publish\" saves host and source to ekanos.json.',\n ' \"ekanos login\" never reads ekanos.json for a host (--host, else',\n ' EKANOS_HOST, else the sole stored login) — it is the verb that creates',\n ' credentials, so a committed file must never choose where they go.',\n '',\n 'Publish targeting (see \"ekanos sources\" / \"ekanos use\"):',\n ' publish refuses (FORBIDDEN) a source you hold no seat on. With multiple',\n ' seats and an implicit (ekanos.json) source, a human run asks to confirm',\n ' the target; a scripted/JSON run needs --source or --yes to proceed —',\n ' it never prompts and never guesses.',\n '',\n 'Global options:',\n ' --json Emit a single JSON envelope on stdout (auto-on when piped);',\n ' --no-json forces the human-readable form',\n ' --version Print the CLI version',\n ' --help Show this help',\n].join('\\n');\n\nexport interface RunOptions {\n stdout?: WriteStream;\n stderr?: WriteStream;\n env?: Record<string, string | undefined>;\n cwd?: string;\n /**\n * Test-only override for `publish`'s interactive-confirm gate and `init`\n * / `login`'s clack wizard gate (`canWizard` in `wizard.ts`) — see\n * `PublishArgs` in `commands/publish.ts`. Never set this from real CLI\n * wiring; it exists so a test can exercise the human y/N confirm branch or\n * the wizard flow without a real terminal.\n */\n stdinIsTTY?: boolean;\n confirm?: (question: string) => Promise<boolean>;\n /**\n * Test-only override for `status --watch`'s poll delay — see `StatusArgs`\n * in `commands/status.ts`. Never set this from real CLI wiring; it exists\n * so a test can exercise the poll loop without waiting 30s per iteration.\n */\n sleep?: (ms: number) => Promise<void>;\n /**\n * Test-only override for `status`'s AGE column (\"now\") — see `StatusArgs`\n * in `commands/status.ts`. Never set this from real CLI wiring; it exists\n * so a test can assert an exact `relativeTime` string deterministically.\n */\n now?: Date;\n}\n\n/**\n * The single entry point. Resolves the agent-native envelope once, dispatches\n * to a verb, and guarantees exactly one terminal emission (success or error).\n * Returns the process exit code — `bin.ts` is the only place that calls\n * `process.exit`, so this stays fully testable.\n */\nexport async function run(\n argv: readonly string[],\n options: RunOptions = {},\n): Promise<number> {\n const jsonFlag = scanJsonFlag(argv);\n const ctx = new CliContext({\n jsonFlag,\n stdout: options.stdout,\n stderr: options.stderr,\n env: options.env,\n });\n const cwd = options.cwd ?? process.cwd();\n const env = options.env ?? process.env;\n\n try {\n // `--version` is global and verb-independent, so it is answered before the\n // command is even resolved: `ekanos --version` and `ekanos validate\n // --version` both report the tool's version and exit 0.\n if (scanFlagPresence(argv, 'version')) {\n return emitVersion(ctx);\n }\n\n const { command, rest } = splitCommand(argv);\n\n // Top-level help / no command.\n if (!command) {\n if (scanFlagPresence(rest, 'help') || argv.length === 0) {\n return emitHelp(ctx);\n }\n // A leading token that is not a command (e.g. `--nonsense`).\n ctx.emitClaudeCodeHint({\n tool: 'ekanos',\n error: 'unknown-command',\n commands: COMMANDS,\n usage: USAGE,\n });\n throw usageError(\n `Unknown command or flag \"${argv[0]}\".`,\n `Run one of: ${COMMANDS.join(', ')}. See \"ekanos --help\".`,\n );\n }\n\n if (!COMMANDS.includes(command)) {\n ctx.emitClaudeCodeHint({\n tool: 'ekanos',\n error: 'unknown-command',\n given: command,\n commands: COMMANDS,\n usage: USAGE,\n });\n throw usageError(\n `Unknown command \"${command}\".`,\n `Run one of: ${COMMANDS.join(', ')}. See \"ekanos --help\".`,\n );\n }\n\n const specs: FlagSpecs = { ...GLOBAL_SPECS, ...COMMAND_SPECS[command] };\n const parsed = parseArgv(rest, specs);\n\n if (parsed.flags.help === true) {\n return emitCommandHelp(ctx, command);\n }\n\n const positionalName = POSITIONAL_COMMANDS[command];\n\n if (!positionalName && parsed.positionals.length > 0) {\n throw usageError(\n `\"${command}\" takes no positional arguments (got \"${parsed.positionals[0]}\").`,\n `Pass options as flags, e.g. \"--dir <path>\". See \"ekanos ${command} --help\".`,\n );\n }\n\n if (positionalName && parsed.positionals.length > 1) {\n throw usageError(\n `\"${command}\" takes at most one positional argument (a ` +\n `${positionalName}), got ${parsed.positionals.length}.`,\n `Pass exactly one, e.g. \"ekanos ${command} <${positionalName}>\". ` +\n `See \"ekanos ${command} --help\".`,\n );\n }\n\n const dir = (parsed.flags.dir as string | undefined) ?? cwd;\n\n switch (command) {\n case 'init':\n return await runInit(ctx, {\n slug: parsed.flags.slug as string | undefined,\n dir: parsed.flags.dir as string | undefined,\n cwd,\n force: parsed.flags.force === true,\n yes: parsed.flags.yes === true,\n docs: parsed.flags.docs as string[] | undefined,\n stdinIsTTY: options.stdinIsTTY,\n });\n case 'validate':\n return await runValidate(ctx, { dir, env });\n case 'dev':\n return await runDev(ctx, {\n dir,\n port: parsed.flags.port as string | undefined,\n host: parsed.flags.host as string | undefined,\n force: parsed.flags.force === true,\n start: parsed.flags.start as boolean | undefined,\n env,\n });\n case 'test':\n return await runTest(ctx, {\n dir,\n passthrough: parsed.passthrough,\n raw: parsed.flags.raw === true,\n });\n case 'login':\n return await runLogin(ctx, {\n host: parsed.flags.host as string | undefined,\n force: parsed.flags.force === true,\n cwd,\n env,\n stdinIsTTY: options.stdinIsTTY,\n });\n case 'logout':\n return await runLogout(ctx, {\n host: parsed.flags.host as string | undefined,\n all: parsed.flags.all === true,\n cwd,\n env,\n });\n case 'whoami':\n return await runWhoami(ctx, {\n host: parsed.flags.host as string | undefined,\n cwd,\n env,\n });\n case 'status':\n return await runStatus(ctx, {\n host: parsed.flags.host as string | undefined,\n dir,\n env,\n findings: parsed.flags.findings === true,\n all: parsed.flags.all === true,\n watch: parsed.flags.watch === true,\n sleep: options.sleep,\n now: options.now,\n });\n case 'sources':\n return await runSources(ctx, {\n host: parsed.flags.host as string | undefined,\n dir,\n env,\n });\n case 'use':\n return await runUse(ctx, {\n host: parsed.flags.host as string | undefined,\n dir,\n slug: parsed.positionals[0],\n env,\n });\n case 'publish':\n return await runPublish(ctx, {\n host: parsed.flags.host as string | undefined,\n source: parsed.flags.source as string | undefined,\n dir,\n yes: parsed.flags.yes === true,\n env,\n stdinIsTTY: options.stdinIsTTY,\n confirm: options.confirm,\n });\n case 'upgrade':\n return await runUpgrade(ctx, {\n dir,\n host: parsed.flags.host as string | undefined,\n env,\n check: parsed.flags.check === true,\n yes: parsed.flags.yes === true,\n noVerify: parsed.flags.verify === false,\n stdinIsTTY: options.stdinIsTTY,\n confirm: options.confirm,\n });\n default:\n // Unreachable — COMMANDS gate above — but keeps the switch total.\n throw usageError(\n `Unknown command \"${command}\".`,\n `Run one of: ${COMMANDS.join(', ')}.`,\n );\n }\n } catch (error) {\n return ctx.fail(toCliError(error));\n }\n}\n\n/**\n * Presence of `--json` (or `--no-json`) BEFORE the first `--` passthrough.\n *\n * Tri-state on purpose. `undefined` means \"the user expressed no preference\",\n * which is what lets the context fall back to inferring the mode from a\n * non-TTY stdout or CLAUDECODE. Collapsing `--no-json` to `false` here would\n * make it indistinguishable from absence, and the flag could then never turn\n * JSON mode OFF in exactly the cases it exists for.\n */\nfunction scanJsonFlag(argv: readonly string[]): boolean | undefined {\n for (const token of argv) {\n if (token === '--') break;\n if (token === '--json') return true;\n if (token === '--no-json') return false;\n }\n return undefined;\n}\n\nfunction scanFlagPresence(argv: readonly string[], name: string): boolean {\n for (const token of argv) {\n if (token === '--') break;\n if (token === `--${name}`) return true;\n }\n return false;\n}\n\n/**\n * Split argv into the command (first bare token before `--`) and the rest\n * (everything else, order preserved, including global flags and the `--`\n * passthrough). Global flags are all booleans, so any bare token is the\n * command — no value-consumption ambiguity.\n */\nfunction splitCommand(argv: readonly string[]): {\n command: string | null;\n rest: string[];\n} {\n for (let i = 0; i < argv.length; i++) {\n const token = argv[i]!;\n if (token === '--') break;\n if (!token.startsWith('-')) {\n return {\n command: token,\n rest: [...argv.slice(0, i), ...argv.slice(i + 1)],\n };\n }\n }\n return { command: null, rest: [...argv] };\n}\n\n/**\n * The CLI's own version, read from the package manifest at runtime rather than\n * baked in by the build — one source of truth, so a release bump cannot leave\n * `--version` reporting a stale number. The path resolves identically from\n * `src/index.ts` and from the emitted `dist/index.js`, both of which sit one\n * directory below the package root.\n */\nfunction readVersion(): string {\n const manifest = new URL('../package.json', import.meta.url);\n const parsed: unknown = JSON.parse(fs.readFileSync(manifest, 'utf8'));\n\n return typeof parsed === 'object' &&\n parsed !== null &&\n 'version' in parsed &&\n typeof parsed.version === 'string'\n ? parsed.version\n : '0.0.0';\n}\n\nfunction emitVersion(ctx: CliContext): number {\n const version = readVersion();\n\n return ctx.succeed({ name: 'ekanos', version }, version);\n}\n\nconst DOCS_FOOTER = `\\nDocs: ${SDK_DOCS_URL} (commands: ${docsPage('cli/commands')})`;\n\nfunction emitHelp(ctx: CliContext): number {\n ctx.emitClaudeCodeHint({\n tool: 'ekanos',\n commands: COMMANDS,\n usage: USAGE,\n exitCodes: EXIT_CODES,\n });\n return ctx.succeed(\n { usage: USAGE, commands: COMMANDS, docs: SDK_DOCS_URL },\n `${USAGE}${DOCS_FOOTER}`,\n );\n}\n\nfunction emitCommandHelp(ctx: CliContext, command: string): number {\n const specs = COMMAND_SPECS[command] ?? {};\n const flags = Object.keys(specs).map((name) => `--${name}`);\n const help = `Usage: ekanos ${command} [${flags.join('] [')}]`;\n ctx.emitClaudeCodeHint({ tool: 'ekanos', command, flags });\n return ctx.succeed({ command, flags }, help);\n}\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["import * as fs from 'node:fs';\n\nimport { runAgents } from './commands/agents';\nimport { runCatalog } from './commands/catalog';\nimport { runDev } from './commands/dev';\nimport { runInit } from './commands/init';\nimport { runLogin } from './commands/login';\nimport { runLogout } from './commands/logout';\nimport { runPublish } from './commands/publish';\nimport { runSources } from './commands/sources';\nimport { runStatus } from './commands/status';\nimport { runTest } from './commands/test';\nimport { runUpgrade } from './commands/upgrade';\nimport { runUse } from './commands/use';\nimport { runValidate } from './commands/validate';\nimport { runWhoami } from './commands/whoami';\nimport { CliContext, type WriteStream } from './context';\nimport { SDK_DOCS_URL, docsPage } from './docs-links';\nimport { toCliError, usageError } from './errors';\nimport { EXIT_CODES } from './exit-codes';\nimport { type FlagSpecs, parseArgv } from './parse-argv';\n\nexport { CliContext } from './context';\nexport { EXIT_CODES, ERROR_CODES } from './exit-codes';\nexport type { ExitCode, ErrorCode } from './exit-codes';\n\nconst GLOBAL_SPECS: FlagSpecs = {\n json: { type: 'boolean' },\n help: { type: 'boolean' },\n version: { type: 'boolean' },\n};\n\nconst COMMAND_SPECS: Record<string, FlagSpecs> = {\n init: {\n slug: { type: 'string' },\n dir: { type: 'string' },\n force: { type: 'boolean' },\n yes: { type: 'boolean' },\n // Repeatable: each occurrence appends one docs entry (URL or relative\n // path) to the scaffolded ekanos.json.\n docs: { type: 'string', repeatable: true },\n },\n validate: {\n dir: { type: 'string' },\n },\n dev: {\n dir: { type: 'string' },\n port: { type: 'string' },\n host: { type: 'string' },\n force: { type: 'boolean' },\n start: { type: 'boolean' },\n },\n test: {\n dir: { type: 'string' },\n // Skips the vitest JSON-reporter injection and the curated failures\n // appendix, reproducing the pre-appendix raw-passthrough behavior —\n // also the escape hatch if the vitest-detection heuristic ever guesses\n // wrong for a project. See `test-report.ts`.\n raw: { type: 'boolean' },\n },\n login: {\n host: { type: 'string' },\n force: { type: 'boolean' },\n },\n logout: {\n host: { type: 'string' },\n all: { type: 'boolean' },\n },\n whoami: {\n host: { type: 'string' },\n },\n status: {\n host: { type: 'string' },\n dir: { type: 'string' },\n findings: { type: 'boolean' },\n all: { type: 'boolean' },\n watch: { type: 'boolean' },\n },\n sources: {\n host: { type: 'string' },\n dir: { type: 'string' },\n },\n catalog: {\n host: { type: 'string' },\n source: { type: 'string' },\n dir: { type: 'string' },\n },\n agents: {\n host: { type: 'string' },\n source: { type: 'string' },\n dir: { type: 'string' },\n // Not `--version`: that is global, and prints the CLI's own version.\n to: { type: 'string' },\n 'base-version': { type: 'string' },\n overwrite: { type: 'boolean' },\n out: { type: 'string' },\n yes: { type: 'boolean' },\n },\n use: {\n host: { type: 'string' },\n dir: { type: 'string' },\n },\n publish: {\n host: { type: 'string' },\n source: { type: 'string' },\n dir: { type: 'string' },\n yes: { type: 'boolean' },\n },\n upgrade: {\n dir: { type: 'string' },\n host: { type: 'string' },\n check: { type: 'boolean' },\n yes: { type: 'boolean' },\n // `--no-verify` is the negation of this — parse-argv's `--no-<bool>`\n // convention (same as `--no-json`/`--no-start`), not a flag of its own.\n verify: { type: 'boolean' },\n },\n};\n\n/** Commands that take positional arguments, and what each means, in order. Every other command is positional-free. */\nconst POSITIONAL_COMMANDS: Record<string, readonly string[]> = {\n use: ['source-slug'],\n catalog: ['product-slug'],\n agents: ['subcommand', 'key-or-path'],\n};\n\nconst COMMANDS = Object.keys(COMMAND_SPECS);\n\nconst USAGE = [\n 'Usage: ekanos <command> [options]',\n '',\n 'Commands:',\n ' init Scaffold a new integration project into ./<slug> (--slug <slug> --yes)',\n ' validate Validate ekanos.json + the integration definition (--json)',\n ' dev Scaffold .ekanos/harness and run it with your own Next',\n ' test Run the project test script via its package manager (--json).',\n ' For a vitest test script, also renders a curated failures',\n ' appendix from a JSON reporter report; --raw skips that and',\n ' the reporter injection entirely.',\n ' login Authenticate against an Ekanos host in a browser (--host <url>)',\n ' logout Revoke and delete the stored session (--host <url> | --all)',\n ' whoami Print the identity the stored session authenticates as, plus',\n ' a compact list of the sources you hold a seat on',\n \" status Show login state and this project's submissions on the host\",\n ' (--findings for full gate findings, --all to expand other',\n ' integrations in the source, --watch to block until this',\n \" project's current version reaches a gate verdict)\",\n ' sources List every source you hold a developer/source-admin seat on',\n ' catalog What a source can already use, and how to set it up',\n ' (\"ekanos catalog [<product-slug>] --source <slug>\", --json)',\n \" agents The source's agent templates, held by Ekanos:\",\n ' list | show <key> | pull <key> [--out <dir>]',\n ' apply <dir|file> [--base-version <n>] [--overwrite]',\n ' publish <key> [--to <n>] --yes | rollback <key> --to <n> --yes',\n ' use Set this project\\'s publish target (\"ekanos use <source-slug>\")',\n ' publish Validate, pack and submit the integration to an Ekanos host',\n ' upgrade Move this toolchain to a coherent target version, with',\n ' verify-and-rollback (--check --yes --no-verify --host <url>)',\n '',\n 'Keeping the toolchain current:',\n ' validate, dev and publish all check for a newer @ekanos/cli in the',\n ' background (never blocking, silenced by EKANOS_NO_UPDATE_CHECK) and warn',\n ' on stderr when one exists. Run \"ekanos upgrade --check\" any time a',\n ' validate finding smells like version skew (an unrecognized-key error you',\n ' are confident is correct) — it is read-only and safe to run freely. A',\n ' bare \"ekanos upgrade\" MUTATES the project (installs the target versions,',\n ' verifies with validate + the test script, rolls back on any failure).',\n '',\n 'Host resolution (logout, whoami, status, sources, catalog, agents, use, publish, upgrade):',\n ' --host, else EKANOS_HOST, else the \"host\" field in ekanos.json, else the',\n ' sole stored login. \"ekanos publish\" saves host and source to ekanos.json.',\n ' \"ekanos login\" never reads ekanos.json for a host (--host, else',\n ' EKANOS_HOST, else the sole stored login) — it is the verb that creates',\n ' credentials, so a committed file must never choose where they go.',\n '',\n 'Publish targeting (see \"ekanos sources\" / \"ekanos use\"):',\n ' publish refuses (FORBIDDEN) a source you hold no seat on. With multiple',\n ' seats and an implicit (ekanos.json) source, a human run asks to confirm',\n ' the target; a scripted/JSON run needs --source or --yes to proceed —',\n ' it never prompts and never guesses.',\n '',\n 'Global options:',\n ' --json Emit a single JSON envelope on stdout (auto-on when piped);',\n ' --no-json forces the human-readable form',\n ' --version Print the CLI version',\n ' --help Show this help',\n].join('\\n');\n\nexport interface RunOptions {\n stdout?: WriteStream;\n stderr?: WriteStream;\n env?: Record<string, string | undefined>;\n cwd?: string;\n /**\n * Test-only override for `publish`'s interactive-confirm gate and `init`\n * / `login`'s clack wizard gate (`canWizard` in `wizard.ts`) — see\n * `PublishArgs` in `commands/publish.ts`. Never set this from real CLI\n * wiring; it exists so a test can exercise the human y/N confirm branch or\n * the wizard flow without a real terminal.\n */\n stdinIsTTY?: boolean;\n confirm?: (question: string) => Promise<boolean>;\n /**\n * Test-only override for `status --watch`'s poll delay — see `StatusArgs`\n * in `commands/status.ts`. Never set this from real CLI wiring; it exists\n * so a test can exercise the poll loop without waiting 30s per iteration.\n */\n sleep?: (ms: number) => Promise<void>;\n /**\n * Test-only override for `status`'s AGE column (\"now\") — see `StatusArgs`\n * in `commands/status.ts`. Never set this from real CLI wiring; it exists\n * so a test can assert an exact `relativeTime` string deterministically.\n */\n now?: Date;\n}\n\n/**\n * The single entry point. Resolves the agent-native envelope once, dispatches\n * to a verb, and guarantees exactly one terminal emission (success or error).\n * Returns the process exit code — `bin.ts` is the only place that calls\n * `process.exit`, so this stays fully testable.\n */\nexport async function run(\n argv: readonly string[],\n options: RunOptions = {},\n): Promise<number> {\n const jsonFlag = scanJsonFlag(argv);\n const ctx = new CliContext({\n jsonFlag,\n stdout: options.stdout,\n stderr: options.stderr,\n env: options.env,\n });\n const cwd = options.cwd ?? process.cwd();\n const env = options.env ?? process.env;\n\n try {\n // `--version` is global and verb-independent, so it is answered before the\n // command is even resolved: `ekanos --version` and `ekanos validate\n // --version` both report the tool's version and exit 0.\n if (scanFlagPresence(argv, 'version')) {\n return emitVersion(ctx);\n }\n\n const { command, rest } = splitCommand(argv);\n\n // Top-level help / no command.\n if (!command) {\n if (scanFlagPresence(rest, 'help') || argv.length === 0) {\n return emitHelp(ctx);\n }\n // A leading token that is not a command (e.g. `--nonsense`).\n ctx.emitClaudeCodeHint({\n tool: 'ekanos',\n error: 'unknown-command',\n commands: COMMANDS,\n usage: USAGE,\n });\n throw usageError(\n `Unknown command or flag \"${argv[0]}\".`,\n `Run one of: ${COMMANDS.join(', ')}. See \"ekanos --help\".`,\n );\n }\n\n if (!COMMANDS.includes(command)) {\n ctx.emitClaudeCodeHint({\n tool: 'ekanos',\n error: 'unknown-command',\n given: command,\n commands: COMMANDS,\n usage: USAGE,\n });\n throw usageError(\n `Unknown command \"${command}\".`,\n `Run one of: ${COMMANDS.join(', ')}. See \"ekanos --help\".`,\n );\n }\n\n const specs: FlagSpecs = { ...GLOBAL_SPECS, ...COMMAND_SPECS[command] };\n const parsed = parseArgv(rest, specs);\n\n if (parsed.flags.help === true) {\n return emitCommandHelp(ctx, command);\n }\n\n const positionalNames = POSITIONAL_COMMANDS[command] ?? [];\n\n if (positionalNames.length === 0 && parsed.positionals.length > 0) {\n throw usageError(\n `\"${command}\" takes no positional arguments (got \"${parsed.positionals[0]}\").`,\n `Pass options as flags, e.g. \"--dir <path>\". See \"ekanos ${command} --help\".`,\n );\n }\n\n if (parsed.positionals.length > positionalNames.length) {\n const usage = positionalNames.map((name) => `<${name}>`).join(' ');\n throw usageError(\n `\"${command}\" takes at most ${positionalNames.length} positional ` +\n `argument${positionalNames.length === 1 ? '' : 's'} (${usage}), ` +\n `got ${parsed.positionals.length}.`,\n `e.g. \"ekanos ${command} ${usage}\". See \"ekanos ${command} --help\".`,\n );\n }\n\n const dir = (parsed.flags.dir as string | undefined) ?? cwd;\n\n switch (command) {\n case 'init':\n return await runInit(ctx, {\n slug: parsed.flags.slug as string | undefined,\n dir: parsed.flags.dir as string | undefined,\n cwd,\n force: parsed.flags.force === true,\n yes: parsed.flags.yes === true,\n docs: parsed.flags.docs as string[] | undefined,\n stdinIsTTY: options.stdinIsTTY,\n });\n case 'validate':\n return await runValidate(ctx, { dir, env });\n case 'dev':\n return await runDev(ctx, {\n dir,\n port: parsed.flags.port as string | undefined,\n host: parsed.flags.host as string | undefined,\n force: parsed.flags.force === true,\n start: parsed.flags.start as boolean | undefined,\n env,\n });\n case 'test':\n return await runTest(ctx, {\n dir,\n passthrough: parsed.passthrough,\n raw: parsed.flags.raw === true,\n });\n case 'login':\n return await runLogin(ctx, {\n host: parsed.flags.host as string | undefined,\n force: parsed.flags.force === true,\n cwd,\n env,\n stdinIsTTY: options.stdinIsTTY,\n });\n case 'logout':\n return await runLogout(ctx, {\n host: parsed.flags.host as string | undefined,\n all: parsed.flags.all === true,\n cwd,\n env,\n });\n case 'whoami':\n return await runWhoami(ctx, {\n host: parsed.flags.host as string | undefined,\n cwd,\n env,\n });\n case 'status':\n return await runStatus(ctx, {\n host: parsed.flags.host as string | undefined,\n dir,\n env,\n findings: parsed.flags.findings === true,\n all: parsed.flags.all === true,\n watch: parsed.flags.watch === true,\n sleep: options.sleep,\n now: options.now,\n });\n case 'sources':\n return await runSources(ctx, {\n host: parsed.flags.host as string | undefined,\n dir,\n env,\n });\n case 'catalog':\n return await runCatalog(ctx, {\n host: parsed.flags.host as string | undefined,\n source: parsed.flags.source as string | undefined,\n product: parsed.positionals[0],\n dir,\n env,\n });\n case 'agents':\n return await runAgents(ctx, {\n host: parsed.flags.host as string | undefined,\n source: parsed.flags.source as string | undefined,\n subcommand: parsed.positionals[0],\n target: parsed.positionals[1],\n to: parsed.flags.to as string | undefined,\n baseVersion: parsed.flags['base-version'] as string | undefined,\n overwrite: parsed.flags.overwrite === true,\n out: parsed.flags.out as string | undefined,\n yes: parsed.flags.yes === true,\n dir,\n env,\n stdinIsTTY: options.stdinIsTTY,\n confirm: options.confirm,\n });\n case 'use':\n return await runUse(ctx, {\n host: parsed.flags.host as string | undefined,\n dir,\n slug: parsed.positionals[0],\n env,\n });\n case 'publish':\n return await runPublish(ctx, {\n host: parsed.flags.host as string | undefined,\n source: parsed.flags.source as string | undefined,\n dir,\n yes: parsed.flags.yes === true,\n env,\n stdinIsTTY: options.stdinIsTTY,\n confirm: options.confirm,\n });\n case 'upgrade':\n return await runUpgrade(ctx, {\n dir,\n host: parsed.flags.host as string | undefined,\n env,\n check: parsed.flags.check === true,\n yes: parsed.flags.yes === true,\n noVerify: parsed.flags.verify === false,\n stdinIsTTY: options.stdinIsTTY,\n confirm: options.confirm,\n });\n default:\n // Unreachable — COMMANDS gate above — but keeps the switch total.\n throw usageError(\n `Unknown command \"${command}\".`,\n `Run one of: ${COMMANDS.join(', ')}.`,\n );\n }\n } catch (error) {\n return ctx.fail(toCliError(error));\n }\n}\n\n/**\n * Presence of `--json` (or `--no-json`) BEFORE the first `--` passthrough.\n *\n * Tri-state on purpose. `undefined` means \"the user expressed no preference\",\n * which is what lets the context fall back to inferring the mode from a\n * non-TTY stdout or CLAUDECODE. Collapsing `--no-json` to `false` here would\n * make it indistinguishable from absence, and the flag could then never turn\n * JSON mode OFF in exactly the cases it exists for.\n */\nfunction scanJsonFlag(argv: readonly string[]): boolean | undefined {\n for (const token of argv) {\n if (token === '--') break;\n if (token === '--json') return true;\n if (token === '--no-json') return false;\n }\n return undefined;\n}\n\nfunction scanFlagPresence(argv: readonly string[], name: string): boolean {\n for (const token of argv) {\n if (token === '--') break;\n if (token === `--${name}`) return true;\n }\n return false;\n}\n\n/**\n * Split argv into the command (first bare token before `--`) and the rest\n * (everything else, order preserved, including global flags and the `--`\n * passthrough). Global flags are all booleans, so any bare token is the\n * command — no value-consumption ambiguity.\n */\nfunction splitCommand(argv: readonly string[]): {\n command: string | null;\n rest: string[];\n} {\n for (let i = 0; i < argv.length; i++) {\n const token = argv[i]!;\n if (token === '--') break;\n if (!token.startsWith('-')) {\n return {\n command: token,\n rest: [...argv.slice(0, i), ...argv.slice(i + 1)],\n };\n }\n }\n return { command: null, rest: [...argv] };\n}\n\n/**\n * The CLI's own version, read from the package manifest at runtime rather than\n * baked in by the build — one source of truth, so a release bump cannot leave\n * `--version` reporting a stale number. The path resolves identically from\n * `src/index.ts` and from the emitted `dist/index.js`, both of which sit one\n * directory below the package root.\n */\nfunction readVersion(): string {\n const manifest = new URL('../package.json', import.meta.url);\n const parsed: unknown = JSON.parse(fs.readFileSync(manifest, 'utf8'));\n\n return typeof parsed === 'object' &&\n parsed !== null &&\n 'version' in parsed &&\n typeof parsed.version === 'string'\n ? parsed.version\n : '0.0.0';\n}\n\nfunction emitVersion(ctx: CliContext): number {\n const version = readVersion();\n\n return ctx.succeed({ name: 'ekanos', version }, version);\n}\n\nconst DOCS_FOOTER = `\\nDocs: ${SDK_DOCS_URL} (commands: ${docsPage('cli/commands')})`;\n\nfunction emitHelp(ctx: CliContext): number {\n ctx.emitClaudeCodeHint({\n tool: 'ekanos',\n commands: COMMANDS,\n usage: USAGE,\n exitCodes: EXIT_CODES,\n });\n return ctx.succeed(\n { usage: USAGE, commands: COMMANDS, docs: SDK_DOCS_URL },\n `${USAGE}${DOCS_FOOTER}`,\n );\n}\n\nfunction emitCommandHelp(ctx: CliContext, command: string): number {\n const specs = COMMAND_SPECS[command] ?? {};\n const flags = Object.keys(specs).map((name) => `--${name}`);\n const help = `Usage: ekanos ${command} [${flags.join('] [')}]`;\n ctx.emitClaudeCodeHint({ tool: 'ekanos', command, flags });\n return ctx.succeed({ command, flags }, help);\n}\n"]}
package/dist/pack.js CHANGED
@@ -2,7 +2,7 @@ import * as fs from 'node:fs';
2
2
  import * as path from 'node:path';
3
3
  import * as zlib from 'node:zlib';
4
4
  import { preconditionError, validationError } from './errors.js';
5
- import { VITEST_CONFIG_NAMES } from './project-checks.js';
5
+ import { ESLINT_CONFIG_NAMES, VITEST_CONFIG_NAMES } from './project-checks.js';
6
6
  /**
7
7
  * Deterministic project packing for `ekanos publish`.
8
8
  *
@@ -17,6 +17,9 @@ import { VITEST_CONFIG_NAMES } from './project-checks.js';
17
17
  * - `tsconfig*.json` at the project root (when present)
18
18
  * - a vitest/vite config at the project root (when present — see
19
19
  * `VITEST_CONFIG_NAMES`)
20
+ * - an ESLint flat config at the project root (when present — see
21
+ * `ESLINT_CONFIG_NAMES`): the gate's `checks/capability-lint` runs the
22
+ * capability preset from it, and refuses an archive without one
20
23
  * - everything under `src/`, recursively
21
24
  *
22
25
  * The tsconfig/vitest entries exist because the hosted gate's
@@ -99,8 +102,8 @@ export function selectProjectFiles(projectDir) {
99
102
  * search. */
100
103
  const TSCONFIG_PATTERN = /^tsconfig(\.[^./]+)?\.json$/;
101
104
  /**
102
- * The project-root config files the hosted gate's `checks/typecheck` and
103
- * `checks/tests` stages need to run the project's OWN `tsc`/`vitest`
105
+ * The project-root config files the hosted gate's `checks/typecheck`,
106
+ * `checks/tests` and `checks/capability-lint` stages need to run the project's OWN `tsc`/`vitest`
104
107
  * meaningfully — see the module doc comment above. Each is included only
105
108
  * when present as a regular file (never a symlink, matching every other
106
109
  * entry point in this walk), so a project without one of these (unusual, but
@@ -115,7 +118,7 @@ function selectRootConfigFiles(projectDir) {
115
118
  continue;
116
119
  selected.push(entry.name);
117
120
  }
118
- for (const name of VITEST_CONFIG_NAMES) {
121
+ for (const name of [...VITEST_CONFIG_NAMES, ...ESLINT_CONFIG_NAMES]) {
119
122
  if (isRegularFile(path.join(projectDir, name)))
120
123
  selected.push(name);
121
124
  }
package/dist/pack.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"pack.js","sourceRoot":"","sources":["../src/pack.ts"],"names":[],"mappings":"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","sourcesContent":["import * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport * as zlib from 'node:zlib';\n\nimport { preconditionError, validationError } from './errors';\nimport { VITEST_CONFIG_NAMES } from './project-checks';\n\n/**\n * Deterministic project packing for `ekanos publish`.\n *\n * A minimal ustar writer over Node's built-ins — no `tar` dependency. The\n * selection is deliberately a WHITELIST, not \"everything minus a blocklist\":\n * the submission is reviewed by an operator and installed into Ekanos, so the\n * archive must contain exactly the files the toolchain knows about and\n * nothing a working directory happens to hold.\n *\n * - `ekanos.json`, `package.json` (required)\n * - `README.md` (when present)\n * - `tsconfig*.json` at the project root (when present)\n * - a vitest/vite config at the project root (when present — see\n * `VITEST_CONFIG_NAMES`)\n * - everything under `src/`, recursively\n *\n * The tsconfig/vitest entries exist because the hosted gate's\n * `checks/typecheck` and `checks/tests` stages run the project's OWN `tsc`\n * and `vitest` — over the ARCHIVE, not the partner's working directory — so\n * without them there is no `tsconfig.json` to compile against (`tsc --noEmit`\n * falls back to printing its help text and exiting 1, not a type error) and\n * no test config to load the SDK's server-inline block. `ekanos init`\n * scaffolds both at the project root, so a project published straight from\n * `init` always has them; this walk just stops silently dropping them.\n *\n * Excluded everywhere: dotfiles and dot-directories (which covers `.ekanos`,\n * `.git`, `.env`), `node_modules`, and anything that is not a regular file\n * (symlinks are skipped, not followed — a link pointing outside the project\n * must never pull foreign bytes into a submission).\n *\n * Determinism: entries are sorted by path and every header carries a fixed\n * mtime, so packing the same tree twice yields byte-identical archives and a\n * stable sha256.\n */\n\nconst BLOCK_SIZE = 512;\n\n/** Fixed header mtime — determinism over provenance (the VCS owns history). */\nconst FIXED_MTIME_SECONDS = 0;\n\n/** The server refuses archives over 5 MiB; fail fast on the client too. */\n// 4 MiB — mirrors the server route's cap, which itself sits under Vercel's\n// ~4.5 MB request-body ceiling so oversize is caught here with a clear error\n// instead of by an opaque platform 413.\nexport const MAX_ARCHIVE_BYTES = 4 * 1024 * 1024;\n\nexport interface PackedProject {\n /** The gzipped tarball. */\n archive: Buffer;\n /** Project-relative POSIX paths, in the order they were packed. */\n files: string[];\n}\n\nexport function packProject(projectDir: string): PackedProject {\n const files = selectProjectFiles(projectDir);\n\n const blocks: Buffer[] = [];\n\n for (const relative of files) {\n const absolute = path.join(projectDir, relative);\n const content = fs.readFileSync(absolute);\n\n blocks.push(tarHeader(relative, content.length));\n blocks.push(content);\n\n const remainder = content.length % BLOCK_SIZE;\n if (remainder !== 0) {\n blocks.push(Buffer.alloc(BLOCK_SIZE - remainder));\n }\n }\n\n // End-of-archive: two zero blocks.\n blocks.push(Buffer.alloc(BLOCK_SIZE * 2));\n\n const archive = zlib.gzipSync(Buffer.concat(blocks), { level: 9 });\n\n if (archive.length > MAX_ARCHIVE_BYTES) {\n throw validationError(\n `The packed archive is ${archive.length} bytes, over the ` +\n `${MAX_ARCHIVE_BYTES}-byte submission limit.`,\n 'Reduce the project to source only — large binary assets do not ' +\n 'belong in an integration submission.',\n );\n }\n\n return { archive, files };\n}\n\n/**\n * The whitelist walk. Exported for tests: the selection rules are the part\n * with security consequences, so they are asserted directly.\n */\nexport function selectProjectFiles(projectDir: string): string[] {\n for (const required of ['ekanos.json', 'package.json']) {\n if (!isRegularFile(path.join(projectDir, required))) {\n throw preconditionError(\n `Cannot pack ${projectDir}: ${required} is missing.`,\n `Run \"ekanos publish\" from an integration project (or pass --dir), ` +\n `and run \"ekanos init\" first if this is a new project.`,\n );\n }\n }\n\n const selected = ['ekanos.json', 'package.json'];\n\n if (isRegularFile(path.join(projectDir, 'README.md'))) {\n selected.push('README.md');\n }\n\n selected.push(...selectRootConfigFiles(projectDir));\n\n const srcDir = path.join(projectDir, 'src');\n // lstat, not stat: `src` itself as a symlink must not pull a foreign tree\n // into the archive — the same rule walk() applies to every entry inside.\n if (fs.existsSync(srcDir) && fs.lstatSync(srcDir).isDirectory()) {\n selected.push(...walk(projectDir, 'src'));\n }\n\n return selected.sort();\n}\n\n/** `tsconfig.json`, plus any sibling a root `tsconfig.json` might `extends`\n * (e.g. `tsconfig.build.json`) — matched by name, not content, since packing\n * runs before anything parses JSON with comments. Root-level only: this is\n * about the build/test config the gate's own stages need, not a recursive\n * search. */\nconst TSCONFIG_PATTERN = /^tsconfig(\\.[^./]+)?\\.json$/;\n\n/**\n * The project-root config files the hosted gate's `checks/typecheck` and\n * `checks/tests` stages need to run the project's OWN `tsc`/`vitest`\n * meaningfully — see the module doc comment above. Each is included only\n * when present as a regular file (never a symlink, matching every other\n * entry point in this walk), so a project without one of these (unusual, but\n * not invalid) packs exactly as before.\n */\nfunction selectRootConfigFiles(projectDir: string): string[] {\n const selected: string[] = [];\n\n for (const entry of fs.readdirSync(projectDir, { withFileTypes: true })) {\n if (!TSCONFIG_PATTERN.test(entry.name)) continue;\n if (!isRegularFile(path.join(projectDir, entry.name))) continue;\n selected.push(entry.name);\n }\n\n for (const name of VITEST_CONFIG_NAMES) {\n if (isRegularFile(path.join(projectDir, name))) selected.push(name);\n }\n\n return selected;\n}\n\nfunction walk(projectDir: string, relativeDir: string): string[] {\n const absoluteDir = path.join(projectDir, relativeDir);\n const collected: string[] = [];\n\n for (const entry of fs.readdirSync(absoluteDir, { withFileTypes: true })) {\n if (entry.name.startsWith('.')) continue;\n if (entry.name === 'node_modules') continue;\n\n const relative = `${relativeDir}/${entry.name}`;\n\n if (entry.isDirectory()) {\n collected.push(...walk(projectDir, relative));\n continue;\n }\n\n // Regular files only. `isFile()` is false for symlinks under\n // `withFileTypes`, so a link — wherever it points — is skipped here.\n if (entry.isFile()) {\n collected.push(relative);\n }\n }\n\n return collected;\n}\n\nfunction isRegularFile(absolute: string): boolean {\n try {\n return fs.lstatSync(absolute).isFile();\n } catch {\n return false;\n }\n}\n\n/**\n * One 512-byte ustar header. Long paths use the ustar `prefix` field\n * (155 + '/' + 100); a path that fits neither is refused with a clear error\n * rather than silently truncated.\n */\nfunction tarHeader(relativePath: string, size: number): Buffer {\n const { name, prefix } = splitUstarPath(relativePath);\n const header = Buffer.alloc(BLOCK_SIZE);\n\n header.write(name, 0, 100, 'utf8');\n writeOctal(header, 100, 8, 0o644); // mode\n writeOctal(header, 108, 8, 0); // uid\n writeOctal(header, 116, 8, 0); // gid\n writeOctal(header, 124, 12, size);\n writeOctal(header, 136, 12, FIXED_MTIME_SECONDS);\n header.fill(' ', 148, 156); // checksum placeholder: eight spaces\n header.write('0', 156, 1, 'utf8'); // typeflag: regular file\n header.write('ustar', 257, 5, 'utf8');\n header.write('00', 263, 2, 'utf8'); // version\n header.write(prefix, 345, 155, 'utf8');\n\n let checksum = 0;\n for (const byte of header) checksum += byte;\n\n // Six octal digits, NUL, space — the historical checksum encoding.\n header.write(`${checksum.toString(8).padStart(6, '0')}\\0 `, 148, 8, 'latin1');\n\n return header;\n}\n\nfunction splitUstarPath(relativePath: string): {\n name: string;\n prefix: string;\n} {\n if (Buffer.byteLength(relativePath, 'utf8') <= 100) {\n return { name: relativePath, prefix: '' };\n }\n\n // Split at a '/' so that name ≤ 100 bytes and prefix ≤ 155 bytes.\n for (let i = relativePath.length - 1; i > 0; i--) {\n if (relativePath[i] !== '/') continue;\n\n const prefix = relativePath.slice(0, i);\n const name = relativePath.slice(i + 1);\n\n if (\n Buffer.byteLength(name, 'utf8') <= 100 &&\n Buffer.byteLength(prefix, 'utf8') <= 155\n ) {\n return { name, prefix };\n }\n }\n\n throw validationError(\n `Cannot pack \"${relativePath}\": the path is too long for a tar entry.`,\n 'Shorten the file path (tar limits one segment to 100 bytes and the ' +\n 'directory prefix to 155).',\n );\n}\n\nfunction writeOctal(\n header: Buffer,\n offset: number,\n length: number,\n value: number,\n): void {\n header.write(\n `${value.toString(8).padStart(length - 1, '0')}\\0`,\n offset,\n length,\n 'latin1',\n );\n}\n"]}
1
+ {"version":3,"file":"pack.js","sourceRoot":"","sources":["../src/pack.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAElC,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAC9D,OAAO,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAE5E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AAEH,MAAM,UAAU,GAAG,GAAG,CAAC;AAEvB,+EAA+E;AAC/E,MAAM,mBAAmB,GAAG,CAAC,CAAC;AAE9B,2EAA2E;AAC3E,2EAA2E;AAC3E,6EAA6E;AAC7E,wCAAwC;AACxC,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,GAAG,IAAI,GAAG,IAAI,CAAC;AASjD,MAAM,UAAU,WAAW,CAAC,UAAkB;IAC5C,MAAM,KAAK,GAAG,kBAAkB,CAAC,UAAU,CAAC,CAAC;IAE7C,MAAM,MAAM,GAAa,EAAE,CAAC;IAE5B,KAAK,MAAM,QAAQ,IAAI,KAAK,EAAE,CAAC;QAC7B,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;QACjD,MAAM,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC;QAE1C,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;QACjD,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAErB,MAAM,SAAS,GAAG,OAAO,CAAC,MAAM,GAAG,UAAU,CAAC;QAC9C,IAAI,SAAS,KAAK,CAAC,EAAE,CAAC;YACpB,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,UAAU,GAAG,SAAS,CAAC,CAAC,CAAC;QACpD,CAAC;IACH,CAAC;IAED,mCAAmC;IACnC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,UAAU,GAAG,CAAC,CAAC,CAAC,CAAC;IAE1C,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC;IAEnE,IAAI,OAAO,CAAC,MAAM,GAAG,iBAAiB,EAAE,CAAC;QACvC,MAAM,eAAe,CACnB,yBAAyB,OAAO,CAAC,MAAM,mBAAmB;YACxD,GAAG,iBAAiB,yBAAyB,EAC/C,iEAAiE;YAC/D,sCAAsC,CACzC,CAAC;IACJ,CAAC;IAED,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;AAC5B,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,kBAAkB,CAAC,UAAkB;IACnD,KAAK,MAAM,QAAQ,IAAI,CAAC,aAAa,EAAE,cAAc,CAAC,EAAE,CAAC;QACvD,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC,EAAE,CAAC;YACpD,MAAM,iBAAiB,CACrB,eAAe,UAAU,KAAK,QAAQ,cAAc,EACpD,oEAAoE;gBAClE,uDAAuD,CAC1D,CAAC;QACJ,CAAC;IACH,CAAC;IAED,MAAM,QAAQ,GAAG,CAAC,aAAa,EAAE,cAAc,CAAC,CAAC;IAEjD,IAAI,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC,EAAE,CAAC;QACtD,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IAC7B,CAAC;IAED,QAAQ,CAAC,IAAI,CAAC,GAAG,qBAAqB,CAAC,UAAU,CAAC,CAAC,CAAC;IAEpD,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;IAC5C,0EAA0E;IAC1E,yEAAyE;IACzE,IAAI,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC;QAChE,QAAQ,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC,CAAC;IAC5C,CAAC;IAED,OAAO,QAAQ,CAAC,IAAI,EAAE,CAAC;AACzB,CAAC;AAED;;;;aAIa;AACb,MAAM,gBAAgB,GAAG,6BAA6B,CAAC;AAEvD;;;;;;;GAOG;AACH,SAAS,qBAAqB,CAAC,UAAkB;IAC/C,MAAM,QAAQ,GAAa,EAAE,CAAC;IAE9B,KAAK,MAAM,KAAK,IAAI,EAAE,CAAC,WAAW,CAAC,UAAU,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;QACxE,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC;YAAE,SAAS;QACjD,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;YAAE,SAAS;QAChE,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC5B,CAAC;IAED,KAAK,MAAM,IAAI,IAAI,CAAC,GAAG,mBAAmB,EAAE,GAAG,mBAAmB,CAAC,EAAE,CAAC;QACpE,IAAI,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;YAAE,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACtE,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,SAAS,IAAI,CAAC,UAAkB,EAAE,WAAmB;IACnD,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC;IACvD,MAAM,SAAS,GAAa,EAAE,CAAC;IAE/B,KAAK,MAAM,KAAK,IAAI,EAAE,CAAC,WAAW,CAAC,WAAW,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;QACzE,IAAI,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;YAAE,SAAS;QACzC,IAAI,KAAK,CAAC,IAAI,KAAK,cAAc;YAAE,SAAS;QAE5C,MAAM,QAAQ,GAAG,GAAG,WAAW,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC;QAEhD,IAAI,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;YACxB,SAAS,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC,CAAC;YAC9C,SAAS;QACX,CAAC;QAED,6DAA6D;QAC7D,qEAAqE;QACrE,IAAI,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC;YACnB,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC3B,CAAC;IACH,CAAC;IAED,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,SAAS,aAAa,CAAC,QAAgB;IACrC,IAAI,CAAC;QACH,OAAO,EAAE,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,MAAM,EAAE,CAAC;IACzC,CAAC;eAAO,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,SAAS,SAAS,CAAC,YAAoB,EAAE,IAAY;IACnD,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,cAAc,CAAC,YAAY,CAAC,CAAC;IACtD,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;IAExC,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,EAAE,GAAG,EAAE,MAAM,CAAC,CAAC;IACnC,UAAU,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,OAAO;IAC1C,UAAU,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM;IACrC,UAAU,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM;IACrC,UAAU,CAAC,MAAM,EAAE,GAAG,EAAE,EAAE,EAAE,IAAI,CAAC,CAAC;IAClC,UAAU,CAAC,MAAM,EAAE,GAAG,EAAE,EAAE,EAAE,mBAAmB,CAAC,CAAC;IACjD,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC,qCAAqC;IACjE,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,GAAG,EAAE,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,yBAAyB;IAC5D,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,EAAE,MAAM,CAAC,CAAC;IACtC,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,GAAG,EAAE,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,UAAU;IAC9C,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,CAAC,CAAC;IAEvC,IAAI,QAAQ,GAAG,CAAC,CAAC;IACjB,KAAK,MAAM,IAAI,IAAI,MAAM;QAAE,QAAQ,IAAI,IAAI,CAAC;IAE5C,mEAAmE;IACnE,MAAM,CAAC,KAAK,CAAC,GAAG,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC,EAAE,QAAQ,CAAC,CAAC;IAE9E,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAS,cAAc,CAAC,YAAoB;IAI1C,IAAI,MAAM,CAAC,UAAU,CAAC,YAAY,EAAE,MAAM,CAAC,IAAI,GAAG,EAAE,CAAC;QACnD,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;IAC5C,CAAC;IAED,kEAAkE;IAClE,KAAK,IAAI,CAAC,GAAG,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QACjD,IAAI,YAAY,CAAC,CAAC,CAAC,KAAK,GAAG;YAAE,SAAS;QAEtC,MAAM,MAAM,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QACxC,MAAM,IAAI,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QAEvC,IACE,MAAM,CAAC,UAAU,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,GAAG;YACtC,MAAM,CAAC,UAAU,CAAC,MAAM,EAAE,MAAM,CAAC,IAAI,GAAG,EACxC,CAAC;YACD,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;QAC1B,CAAC;IACH,CAAC;IAED,MAAM,eAAe,CACnB,gBAAgB,YAAY,0CAA0C,EACtE,qEAAqE;QACnE,2BAA2B,CAC9B,CAAC;AACJ,CAAC;AAED,SAAS,UAAU,CACjB,MAAc,EACd,MAAc,EACd,MAAc,EACd,KAAa;IAEb,MAAM,CAAC,KAAK,CACV,GAAG,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,GAAG,CAAC,IAAI,EAClD,MAAM,EACN,MAAM,EACN,QAAQ,CACT,CAAC;AACJ,CAAC","sourcesContent":["import * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport * as zlib from 'node:zlib';\n\nimport { preconditionError, validationError } from './errors';\nimport { ESLINT_CONFIG_NAMES, VITEST_CONFIG_NAMES } from './project-checks';\n\n/**\n * Deterministic project packing for `ekanos publish`.\n *\n * A minimal ustar writer over Node's built-ins — no `tar` dependency. The\n * selection is deliberately a WHITELIST, not \"everything minus a blocklist\":\n * the submission is reviewed by an operator and installed into Ekanos, so the\n * archive must contain exactly the files the toolchain knows about and\n * nothing a working directory happens to hold.\n *\n * - `ekanos.json`, `package.json` (required)\n * - `README.md` (when present)\n * - `tsconfig*.json` at the project root (when present)\n * - a vitest/vite config at the project root (when present — see\n * `VITEST_CONFIG_NAMES`)\n * - an ESLint flat config at the project root (when present — see\n * `ESLINT_CONFIG_NAMES`): the gate's `checks/capability-lint` runs the\n * capability preset from it, and refuses an archive without one\n * - everything under `src/`, recursively\n *\n * The tsconfig/vitest entries exist because the hosted gate's\n * `checks/typecheck` and `checks/tests` stages run the project's OWN `tsc`\n * and `vitest` — over the ARCHIVE, not the partner's working directory — so\n * without them there is no `tsconfig.json` to compile against (`tsc --noEmit`\n * falls back to printing its help text and exiting 1, not a type error) and\n * no test config to load the SDK's server-inline block. `ekanos init`\n * scaffolds both at the project root, so a project published straight from\n * `init` always has them; this walk just stops silently dropping them.\n *\n * Excluded everywhere: dotfiles and dot-directories (which covers `.ekanos`,\n * `.git`, `.env`), `node_modules`, and anything that is not a regular file\n * (symlinks are skipped, not followed — a link pointing outside the project\n * must never pull foreign bytes into a submission).\n *\n * Determinism: entries are sorted by path and every header carries a fixed\n * mtime, so packing the same tree twice yields byte-identical archives and a\n * stable sha256.\n */\n\nconst BLOCK_SIZE = 512;\n\n/** Fixed header mtime — determinism over provenance (the VCS owns history). */\nconst FIXED_MTIME_SECONDS = 0;\n\n/** The server refuses archives over 5 MiB; fail fast on the client too. */\n// 4 MiB — mirrors the server route's cap, which itself sits under Vercel's\n// ~4.5 MB request-body ceiling so oversize is caught here with a clear error\n// instead of by an opaque platform 413.\nexport const MAX_ARCHIVE_BYTES = 4 * 1024 * 1024;\n\nexport interface PackedProject {\n /** The gzipped tarball. */\n archive: Buffer;\n /** Project-relative POSIX paths, in the order they were packed. */\n files: string[];\n}\n\nexport function packProject(projectDir: string): PackedProject {\n const files = selectProjectFiles(projectDir);\n\n const blocks: Buffer[] = [];\n\n for (const relative of files) {\n const absolute = path.join(projectDir, relative);\n const content = fs.readFileSync(absolute);\n\n blocks.push(tarHeader(relative, content.length));\n blocks.push(content);\n\n const remainder = content.length % BLOCK_SIZE;\n if (remainder !== 0) {\n blocks.push(Buffer.alloc(BLOCK_SIZE - remainder));\n }\n }\n\n // End-of-archive: two zero blocks.\n blocks.push(Buffer.alloc(BLOCK_SIZE * 2));\n\n const archive = zlib.gzipSync(Buffer.concat(blocks), { level: 9 });\n\n if (archive.length > MAX_ARCHIVE_BYTES) {\n throw validationError(\n `The packed archive is ${archive.length} bytes, over the ` +\n `${MAX_ARCHIVE_BYTES}-byte submission limit.`,\n 'Reduce the project to source only — large binary assets do not ' +\n 'belong in an integration submission.',\n );\n }\n\n return { archive, files };\n}\n\n/**\n * The whitelist walk. Exported for tests: the selection rules are the part\n * with security consequences, so they are asserted directly.\n */\nexport function selectProjectFiles(projectDir: string): string[] {\n for (const required of ['ekanos.json', 'package.json']) {\n if (!isRegularFile(path.join(projectDir, required))) {\n throw preconditionError(\n `Cannot pack ${projectDir}: ${required} is missing.`,\n `Run \"ekanos publish\" from an integration project (or pass --dir), ` +\n `and run \"ekanos init\" first if this is a new project.`,\n );\n }\n }\n\n const selected = ['ekanos.json', 'package.json'];\n\n if (isRegularFile(path.join(projectDir, 'README.md'))) {\n selected.push('README.md');\n }\n\n selected.push(...selectRootConfigFiles(projectDir));\n\n const srcDir = path.join(projectDir, 'src');\n // lstat, not stat: `src` itself as a symlink must not pull a foreign tree\n // into the archive — the same rule walk() applies to every entry inside.\n if (fs.existsSync(srcDir) && fs.lstatSync(srcDir).isDirectory()) {\n selected.push(...walk(projectDir, 'src'));\n }\n\n return selected.sort();\n}\n\n/** `tsconfig.json`, plus any sibling a root `tsconfig.json` might `extends`\n * (e.g. `tsconfig.build.json`) — matched by name, not content, since packing\n * runs before anything parses JSON with comments. Root-level only: this is\n * about the build/test config the gate's own stages need, not a recursive\n * search. */\nconst TSCONFIG_PATTERN = /^tsconfig(\\.[^./]+)?\\.json$/;\n\n/**\n * The project-root config files the hosted gate's `checks/typecheck`,\n * `checks/tests` and `checks/capability-lint` stages need to run the project's OWN `tsc`/`vitest`\n * meaningfully — see the module doc comment above. Each is included only\n * when present as a regular file (never a symlink, matching every other\n * entry point in this walk), so a project without one of these (unusual, but\n * not invalid) packs exactly as before.\n */\nfunction selectRootConfigFiles(projectDir: string): string[] {\n const selected: string[] = [];\n\n for (const entry of fs.readdirSync(projectDir, { withFileTypes: true })) {\n if (!TSCONFIG_PATTERN.test(entry.name)) continue;\n if (!isRegularFile(path.join(projectDir, entry.name))) continue;\n selected.push(entry.name);\n }\n\n for (const name of [...VITEST_CONFIG_NAMES, ...ESLINT_CONFIG_NAMES]) {\n if (isRegularFile(path.join(projectDir, name))) selected.push(name);\n }\n\n return selected;\n}\n\nfunction walk(projectDir: string, relativeDir: string): string[] {\n const absoluteDir = path.join(projectDir, relativeDir);\n const collected: string[] = [];\n\n for (const entry of fs.readdirSync(absoluteDir, { withFileTypes: true })) {\n if (entry.name.startsWith('.')) continue;\n if (entry.name === 'node_modules') continue;\n\n const relative = `${relativeDir}/${entry.name}`;\n\n if (entry.isDirectory()) {\n collected.push(...walk(projectDir, relative));\n continue;\n }\n\n // Regular files only. `isFile()` is false for symlinks under\n // `withFileTypes`, so a link — wherever it points — is skipped here.\n if (entry.isFile()) {\n collected.push(relative);\n }\n }\n\n return collected;\n}\n\nfunction isRegularFile(absolute: string): boolean {\n try {\n return fs.lstatSync(absolute).isFile();\n } catch {\n return false;\n }\n}\n\n/**\n * One 512-byte ustar header. Long paths use the ustar `prefix` field\n * (155 + '/' + 100); a path that fits neither is refused with a clear error\n * rather than silently truncated.\n */\nfunction tarHeader(relativePath: string, size: number): Buffer {\n const { name, prefix } = splitUstarPath(relativePath);\n const header = Buffer.alloc(BLOCK_SIZE);\n\n header.write(name, 0, 100, 'utf8');\n writeOctal(header, 100, 8, 0o644); // mode\n writeOctal(header, 108, 8, 0); // uid\n writeOctal(header, 116, 8, 0); // gid\n writeOctal(header, 124, 12, size);\n writeOctal(header, 136, 12, FIXED_MTIME_SECONDS);\n header.fill(' ', 148, 156); // checksum placeholder: eight spaces\n header.write('0', 156, 1, 'utf8'); // typeflag: regular file\n header.write('ustar', 257, 5, 'utf8');\n header.write('00', 263, 2, 'utf8'); // version\n header.write(prefix, 345, 155, 'utf8');\n\n let checksum = 0;\n for (const byte of header) checksum += byte;\n\n // Six octal digits, NUL, space — the historical checksum encoding.\n header.write(`${checksum.toString(8).padStart(6, '0')}\\0 `, 148, 8, 'latin1');\n\n return header;\n}\n\nfunction splitUstarPath(relativePath: string): {\n name: string;\n prefix: string;\n} {\n if (Buffer.byteLength(relativePath, 'utf8') <= 100) {\n return { name: relativePath, prefix: '' };\n }\n\n // Split at a '/' so that name ≤ 100 bytes and prefix ≤ 155 bytes.\n for (let i = relativePath.length - 1; i > 0; i--) {\n if (relativePath[i] !== '/') continue;\n\n const prefix = relativePath.slice(0, i);\n const name = relativePath.slice(i + 1);\n\n if (\n Buffer.byteLength(name, 'utf8') <= 100 &&\n Buffer.byteLength(prefix, 'utf8') <= 155\n ) {\n return { name, prefix };\n }\n }\n\n throw validationError(\n `Cannot pack \"${relativePath}\": the path is too long for a tar entry.`,\n 'Shorten the file path (tar limits one segment to 100 bytes and the ' +\n 'directory prefix to 155).',\n );\n}\n\nfunction writeOctal(\n header: Buffer,\n offset: number,\n length: number,\n value: number,\n): void {\n header.write(\n `${value.toString(8).padStart(length - 1, '0')}\\0`,\n offset,\n length,\n 'latin1',\n );\n}\n"]}
@@ -7,6 +7,12 @@ import { type EkanosProject } from './project.js';
7
7
  * property can never disagree on what "the test config" even is.
8
8
  */
9
9
  export declare const VITEST_CONFIG_NAMES: string[];
10
+ /**
11
+ * The ESLint flat-config basenames the hosted gate's `checks/capability-lint`
12
+ * looks for in the ARCHIVE — the same four, so a project that lints locally
13
+ * is never refused for a config `ekanos publish` left behind.
14
+ */
15
+ export declare const ESLINT_CONFIG_NAMES: string[];
10
16
  /**
11
17
  * The host's own compiler-strictness overlay a project's tsconfig should
12
18
  * already satisfy — mirrors the exact flags (`HOST_TSC_FLAGS`) the gate's
@@ -19,6 +19,17 @@ export const VITEST_CONFIG_NAMES = [
19
19
  'vite.config.js',
20
20
  'vite.config.mjs',
21
21
  ];
22
+ /**
23
+ * The ESLint flat-config basenames the hosted gate's `checks/capability-lint`
24
+ * looks for in the ARCHIVE — the same four, so a project that lints locally
25
+ * is never refused for a config `ekanos publish` left behind.
26
+ */
27
+ export const ESLINT_CONFIG_NAMES = [
28
+ 'eslint.config.mjs',
29
+ 'eslint.config.js',
30
+ 'eslint.config.cjs',
31
+ 'eslint.config.ts',
32
+ ];
22
33
  /** The exact block the hint quotes — the one config that makes the SDK load. */
23
34
  const REQUIRED_INLINE_BLOCK = [
24
35
  'test: {',
@@ -1 +1 @@
1
- {"version":3,"file":"project-checks.js","sourceRoot":"","sources":["../src/project-checks.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Finding } from '@ekanos/integration-schema';\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\n\nimport { type EkanosProject, isDocUrl } from './project';\n\n/**\n * Every vitest/vite config basename `ekanos init` might have scaffolded (or a\n * partner might have renamed to). Exported so `pack.ts` can pack exactly the\n * same set it checks here — one list, so packing and validating this project\n * property can never disagree on what \"the test config\" even is.\n */\nexport const VITEST_CONFIG_NAMES = [\n 'vitest.config.ts',\n 'vitest.config.mts',\n 'vitest.config.cts',\n 'vitest.config.js',\n 'vitest.config.mjs',\n 'vitest.config.cjs',\n 'vite.config.ts',\n 'vite.config.mts',\n 'vite.config.js',\n 'vite.config.mjs',\n];\n\n/** The exact block the hint quotes — the one config that makes the SDK load. */\nconst REQUIRED_INLINE_BLOCK = [\n 'test: {',\n ' server: {',\n ' deps: {',\n \" inline: ['@ekanos/sdk'],\",\n ' },',\n ' },',\n '}',\n].join('\\n');\n\n/**\n * The host's own compiler-strictness overlay a project's tsconfig should\n * already satisfy — mirrors the exact flags (`HOST_TSC_FLAGS`) the gate's\n * own SECOND `tsc --noEmit` pass checks, read from the host's real tsconfig\n * (source of truth for the values themselves). Hardcoded here for the same\n * reason it is hardcoded there: this package ships to a partner's local\n * machine, so it cannot read a file inside the host's own repo at runtime.\n * Rules-as-data via `/api/partner/toolchain` supersedes this hardcoded copy\n * later — until then, keep both copies in sync by hand.\n */\nexport const HOST_COMPILER_RULES: Record<string, boolean> = {\n strict: true,\n noUncheckedIndexedAccess: true,\n};\n\n/** Matches `tsconfig.json`, `tsconfig.build.json`, etc. — mirrors the pattern\n * `pack.ts` and the gate's `typecheck.ts` use to find a project's tsconfig. */\nconst TSCONFIG_PATTERN = /^tsconfig(\\.[^./]+)?\\.json$/;\n\n/**\n * Project-level integrity checks that read files in the project root (next to\n * ekanos.json), NOT the integration definition. These are deliberately kept\n * out of the schema package's definition collectors: the definition is valid or\n * not on its own; whether the partner's TEST runner is configured to load the\n * published SDK is a property of THIS project.\n *\n * The highest-value check: a partner's vitest must inline `@ekanos/sdk`. The\n * published SDK dist imports extensionless `next/link`, so without\n * `test.server.deps.inline: ['@ekanos/sdk']` vitest fails to load the SDK, the\n * test file silently imports nothing, and vitest reports green over zero tests.\n *\n * The check is conditional: if the project declares NO vitest/vite config at\n * all, there is nothing to assert (the `test` verb would surface a missing\n * runner on its own). If a config IS present, it MUST inline the SDK.\n */\nexport function collectProjectFindings(projectDir: string): Finding[] {\n const configFile = VITEST_CONFIG_NAMES.map((name) =>\n path.join(projectDir, name),\n ).find((candidate) => fs.existsSync(candidate));\n\n if (!configFile) return [];\n\n const source = fs.readFileSync(configFile, 'utf8');\n if (vitestInlinesSdk(source)) return [];\n\n return [\n {\n check: 'project.vitest-inline-sdk',\n severity: 'error',\n file: configFile,\n message:\n `${path.basename(configFile)} does not inline @ekanos/sdk. The ` +\n `published SDK dist imports extensionless \"next/link\", so without ` +\n `test.server.deps.inline including \"@ekanos/sdk\", vitest cannot load ` +\n `the SDK — the test file imports nothing and vitest reports green ` +\n `over zero tests.`,\n hint:\n `Add this to your vitest config's defineConfig so the SDK is ` +\n `transformed for the test runner:\\n\\n${REQUIRED_INLINE_BLOCK}`,\n },\n ];\n}\n\n/**\n * Advisory check on the `docs` array: a relative entry that resolves to\n * nothing on disk gets a WARNING — never an error, because a missing doc file\n * must not block a publish (the gate refuses only error-severity findings).\n * URLs are never checked: `validate` does no network, ever, and an agent\n * dereferences them at authoring time instead.\n */\nexport function collectDocsFindings(\n project: EkanosProject,\n projectDir: string,\n): Finding[] {\n const findings: Finding[] = [];\n\n for (const entry of project.docs ?? []) {\n if (isDocUrl(entry)) continue;\n\n const resolved = path.resolve(projectDir, entry);\n if (fs.existsSync(resolved)) continue;\n\n findings.push({\n check: 'docs/missing-file',\n severity: 'warn',\n file: resolved,\n message:\n `ekanos.json lists \"${entry}\" under \"docs\", but no file exists ` +\n `there (resolved to ${resolved}).`,\n hint:\n `Create ${entry}, or fix the path in ekanos.json's \"docs\" array so ` +\n 'it points at real API documentation.',\n });\n }\n\n return findings;\n}\n\n/**\n * WARN when the project's tsconfig does not already set the host's\n * compiler-strictness overlay (`HOST_COMPILER_RULES`). This exists because a\n * project can pass its own `checks/typecheck` cleanly, publish, and only THEN\n * discover `checks/typecheck-host-rules` (the gate's second, stricter pass)\n * fails on the exact same source — e.g. `noUncheckedIndexedAccess` catching a\n * possibly-`undefined` array/record access `strict` alone allows. WARN, not\n * ERROR: this check reads a partner's local tsconfig, not the gate's own\n * verdict, so it must never block a publish that is actually fine — a\n * project whose tsconfig extends something odd, or omits `compilerOptions`\n * intentionally, still gets to publish and find out from the real gate.\n * `publish` escalates this specific check to ERROR locally (see its\n * severity-escalation list) so a submission that WOULD fail the gate never\n * gets uploaded in the first place.\n *\n * Absent tsconfig is not this check's concern — `checks/typecheck` (and its\n * host-rules sibling) already report that loudly on their own.\n */\nexport function collectHostCompilerRulesFindings(\n projectDir: string,\n): Finding[] {\n const tsconfigPath = findTsconfigPath(projectDir);\n if (!tsconfigPath) return [];\n\n const raw = fs.readFileSync(tsconfigPath, 'utf8');\n const tsconfigName = path.basename(tsconfigPath);\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(stripJsonComments(raw));\n } catch {\n // An unparseable tsconfig is a real defect, but not one THIS check can\n // reason about — `tsc` itself will refuse it far more usefully. info\n // severity: surfaced, but never blocks anything.\n return [\n {\n check: 'project.host-compiler-rules',\n severity: 'info',\n file: tsconfigPath,\n message:\n `${tsconfigName} could not be parsed as JSON (even tolerating ` +\n `comments and trailing commas), so this check could not verify it ` +\n `sets the host's compiler rules ` +\n `(${Object.keys(HOST_COMPILER_RULES).join(', ')}).`,\n hint: `Fix ${tsconfigName}'s syntax, then re-run validate.`,\n },\n ];\n }\n\n const compilerOptions =\n typeof parsed === 'object' &&\n parsed !== null &&\n typeof (parsed as { compilerOptions?: unknown }).compilerOptions ===\n 'object' &&\n (parsed as { compilerOptions?: unknown }).compilerOptions !== null\n ? ((parsed as { compilerOptions: Record<string, unknown> })\n .compilerOptions ?? {})\n : {};\n\n const missing = Object.entries(HOST_COMPILER_RULES)\n .filter(([name, required]) => compilerOptions[name] !== required)\n .map(([name]) => name);\n\n if (missing.length === 0) return [];\n\n return [\n {\n check: 'project.host-compiler-rules',\n severity: 'warn',\n file: tsconfigPath,\n message:\n `${tsconfigName} does not set ${missing.join(' and ')} in ` +\n `compilerOptions. The gate re-checks your submission under the ` +\n `host's own stricter compiler rules — a project that typechecks ` +\n `cleanly under its own laxer tsconfig can still fail that pass.`,\n hint:\n `Add ${missing.map((name) => `\"${name}\": true`).join(', ')} to ` +\n `compilerOptions in ${tsconfigName}.`,\n },\n ];\n}\n\n/** `tsconfig.json` if present (matching what a bare `tsc --noEmit` would\n * auto-discover), else the first `tsconfig.*.json` found. */\nfunction findTsconfigPath(projectDir: string): string | undefined {\n let names: string[];\n try {\n names = fs\n .readdirSync(projectDir, { withFileTypes: true })\n .filter((entry) => entry.isFile() && TSCONFIG_PATTERN.test(entry.name))\n .map((entry) => entry.name);\n } catch {\n return undefined;\n }\n if (names.length === 0) return undefined;\n const name = names.includes('tsconfig.json')\n ? 'tsconfig.json'\n : [...names].sort()[0];\n return name ? path.join(projectDir, name) : undefined;\n}\n\n/**\n * A minimal, string-literal-aware strip of `//` and `/* *\\/` comments —\n * tsconfig.json is conventionally JSONC, but nothing in this package's\n * dependency graph parses it as such. Not a full JSONC parser: it does not\n * touch trailing commas beyond the one cleanup pass below, and it makes no\n * attempt to validate the result — `JSON.parse` does that, and a project\n * with genuinely malformed JSON falls through to this check's `info`-severity\n * catch, never a crash.\n */\nfunction stripJsonComments(source: string): string {\n let out = '';\n let inString = false;\n let quote = '';\n\n for (let i = 0; i < source.length; i++) {\n const char = source[i] ?? '';\n\n if (inString) {\n out += char;\n if (char === '\\\\') {\n out += source[++i] ?? '';\n continue;\n }\n if (char === quote) inString = false;\n continue;\n }\n\n if (char === '\"' || char === \"'\") {\n inString = true;\n quote = char;\n out += char;\n continue;\n }\n\n if (char === '/' && source[i + 1] === '/') {\n while (i < source.length && source[i] !== '\\n') i++;\n out += '\\n';\n continue;\n }\n\n if (char === '/' && source[i + 1] === '*') {\n i += 2;\n while (\n i < source.length &&\n !(source[i] === '*' && source[i + 1] === '/')\n ) {\n i++;\n }\n i++; // consume the closing '/'\n continue;\n }\n\n out += char;\n }\n\n // Trailing commas before a closing brace/bracket.\n return out.replace(/,(\\s*[}\\]])/g, '$1');\n}\n\n/**\n * True when the config source contains an `inline: [ … ]` array that lists\n * `@ekanos/sdk`. Textual on purpose — evaluating a partner's config module\n * would run arbitrary code; a lint-style scan does not.\n */\nfunction vitestInlinesSdk(source: string): boolean {\n const inlineArrayPattern = /inline\\s*:\\s*\\[([\\s\\S]*?)\\]/g;\n for (\n let match = inlineArrayPattern.exec(source);\n match !== null;\n match = inlineArrayPattern.exec(source)\n ) {\n if (/['\"]@ekanos\\/sdk['\"]/.test(match[1] ?? '')) return true;\n }\n return false;\n}\n"]}
1
+ {"version":3,"file":"project-checks.js","sourceRoot":"","sources":["../src/project-checks.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Finding } from '@ekanos/integration-schema';\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\n\nimport { type EkanosProject, isDocUrl } from './project';\n\n/**\n * Every vitest/vite config basename `ekanos init` might have scaffolded (or a\n * partner might have renamed to). Exported so `pack.ts` can pack exactly the\n * same set it checks here — one list, so packing and validating this project\n * property can never disagree on what \"the test config\" even is.\n */\nexport const VITEST_CONFIG_NAMES = [\n 'vitest.config.ts',\n 'vitest.config.mts',\n 'vitest.config.cts',\n 'vitest.config.js',\n 'vitest.config.mjs',\n 'vitest.config.cjs',\n 'vite.config.ts',\n 'vite.config.mts',\n 'vite.config.js',\n 'vite.config.mjs',\n];\n\n/**\n * The ESLint flat-config basenames the hosted gate's `checks/capability-lint`\n * looks for in the ARCHIVE — the same four, so a project that lints locally\n * is never refused for a config `ekanos publish` left behind.\n */\nexport const ESLINT_CONFIG_NAMES = [\n 'eslint.config.mjs',\n 'eslint.config.js',\n 'eslint.config.cjs',\n 'eslint.config.ts',\n];\n\n/** The exact block the hint quotes — the one config that makes the SDK load. */\nconst REQUIRED_INLINE_BLOCK = [\n 'test: {',\n ' server: {',\n ' deps: {',\n \" inline: ['@ekanos/sdk'],\",\n ' },',\n ' },',\n '}',\n].join('\\n');\n\n/**\n * The host's own compiler-strictness overlay a project's tsconfig should\n * already satisfy — mirrors the exact flags (`HOST_TSC_FLAGS`) the gate's\n * own SECOND `tsc --noEmit` pass checks, read from the host's real tsconfig\n * (source of truth for the values themselves). Hardcoded here for the same\n * reason it is hardcoded there: this package ships to a partner's local\n * machine, so it cannot read a file inside the host's own repo at runtime.\n * Rules-as-data via `/api/partner/toolchain` supersedes this hardcoded copy\n * later — until then, keep both copies in sync by hand.\n */\nexport const HOST_COMPILER_RULES: Record<string, boolean> = {\n strict: true,\n noUncheckedIndexedAccess: true,\n};\n\n/** Matches `tsconfig.json`, `tsconfig.build.json`, etc. — mirrors the pattern\n * `pack.ts` and the gate's `typecheck.ts` use to find a project's tsconfig. */\nconst TSCONFIG_PATTERN = /^tsconfig(\\.[^./]+)?\\.json$/;\n\n/**\n * Project-level integrity checks that read files in the project root (next to\n * ekanos.json), NOT the integration definition. These are deliberately kept\n * out of the schema package's definition collectors: the definition is valid or\n * not on its own; whether the partner's TEST runner is configured to load the\n * published SDK is a property of THIS project.\n *\n * The highest-value check: a partner's vitest must inline `@ekanos/sdk`. The\n * published SDK dist imports extensionless `next/link`, so without\n * `test.server.deps.inline: ['@ekanos/sdk']` vitest fails to load the SDK, the\n * test file silently imports nothing, and vitest reports green over zero tests.\n *\n * The check is conditional: if the project declares NO vitest/vite config at\n * all, there is nothing to assert (the `test` verb would surface a missing\n * runner on its own). If a config IS present, it MUST inline the SDK.\n */\nexport function collectProjectFindings(projectDir: string): Finding[] {\n const configFile = VITEST_CONFIG_NAMES.map((name) =>\n path.join(projectDir, name),\n ).find((candidate) => fs.existsSync(candidate));\n\n if (!configFile) return [];\n\n const source = fs.readFileSync(configFile, 'utf8');\n if (vitestInlinesSdk(source)) return [];\n\n return [\n {\n check: 'project.vitest-inline-sdk',\n severity: 'error',\n file: configFile,\n message:\n `${path.basename(configFile)} does not inline @ekanos/sdk. The ` +\n `published SDK dist imports extensionless \"next/link\", so without ` +\n `test.server.deps.inline including \"@ekanos/sdk\", vitest cannot load ` +\n `the SDK — the test file imports nothing and vitest reports green ` +\n `over zero tests.`,\n hint:\n `Add this to your vitest config's defineConfig so the SDK is ` +\n `transformed for the test runner:\\n\\n${REQUIRED_INLINE_BLOCK}`,\n },\n ];\n}\n\n/**\n * Advisory check on the `docs` array: a relative entry that resolves to\n * nothing on disk gets a WARNING — never an error, because a missing doc file\n * must not block a publish (the gate refuses only error-severity findings).\n * URLs are never checked: `validate` does no network, ever, and an agent\n * dereferences them at authoring time instead.\n */\nexport function collectDocsFindings(\n project: EkanosProject,\n projectDir: string,\n): Finding[] {\n const findings: Finding[] = [];\n\n for (const entry of project.docs ?? []) {\n if (isDocUrl(entry)) continue;\n\n const resolved = path.resolve(projectDir, entry);\n if (fs.existsSync(resolved)) continue;\n\n findings.push({\n check: 'docs/missing-file',\n severity: 'warn',\n file: resolved,\n message:\n `ekanos.json lists \"${entry}\" under \"docs\", but no file exists ` +\n `there (resolved to ${resolved}).`,\n hint:\n `Create ${entry}, or fix the path in ekanos.json's \"docs\" array so ` +\n 'it points at real API documentation.',\n });\n }\n\n return findings;\n}\n\n/**\n * WARN when the project's tsconfig does not already set the host's\n * compiler-strictness overlay (`HOST_COMPILER_RULES`). This exists because a\n * project can pass its own `checks/typecheck` cleanly, publish, and only THEN\n * discover `checks/typecheck-host-rules` (the gate's second, stricter pass)\n * fails on the exact same source — e.g. `noUncheckedIndexedAccess` catching a\n * possibly-`undefined` array/record access `strict` alone allows. WARN, not\n * ERROR: this check reads a partner's local tsconfig, not the gate's own\n * verdict, so it must never block a publish that is actually fine — a\n * project whose tsconfig extends something odd, or omits `compilerOptions`\n * intentionally, still gets to publish and find out from the real gate.\n * `publish` escalates this specific check to ERROR locally (see its\n * severity-escalation list) so a submission that WOULD fail the gate never\n * gets uploaded in the first place.\n *\n * Absent tsconfig is not this check's concern — `checks/typecheck` (and its\n * host-rules sibling) already report that loudly on their own.\n */\nexport function collectHostCompilerRulesFindings(\n projectDir: string,\n): Finding[] {\n const tsconfigPath = findTsconfigPath(projectDir);\n if (!tsconfigPath) return [];\n\n const raw = fs.readFileSync(tsconfigPath, 'utf8');\n const tsconfigName = path.basename(tsconfigPath);\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(stripJsonComments(raw));\n } catch {\n // An unparseable tsconfig is a real defect, but not one THIS check can\n // reason about — `tsc` itself will refuse it far more usefully. info\n // severity: surfaced, but never blocks anything.\n return [\n {\n check: 'project.host-compiler-rules',\n severity: 'info',\n file: tsconfigPath,\n message:\n `${tsconfigName} could not be parsed as JSON (even tolerating ` +\n `comments and trailing commas), so this check could not verify it ` +\n `sets the host's compiler rules ` +\n `(${Object.keys(HOST_COMPILER_RULES).join(', ')}).`,\n hint: `Fix ${tsconfigName}'s syntax, then re-run validate.`,\n },\n ];\n }\n\n const compilerOptions =\n typeof parsed === 'object' &&\n parsed !== null &&\n typeof (parsed as { compilerOptions?: unknown }).compilerOptions ===\n 'object' &&\n (parsed as { compilerOptions?: unknown }).compilerOptions !== null\n ? ((parsed as { compilerOptions: Record<string, unknown> })\n .compilerOptions ?? {})\n : {};\n\n const missing = Object.entries(HOST_COMPILER_RULES)\n .filter(([name, required]) => compilerOptions[name] !== required)\n .map(([name]) => name);\n\n if (missing.length === 0) return [];\n\n return [\n {\n check: 'project.host-compiler-rules',\n severity: 'warn',\n file: tsconfigPath,\n message:\n `${tsconfigName} does not set ${missing.join(' and ')} in ` +\n `compilerOptions. The gate re-checks your submission under the ` +\n `host's own stricter compiler rules — a project that typechecks ` +\n `cleanly under its own laxer tsconfig can still fail that pass.`,\n hint:\n `Add ${missing.map((name) => `\"${name}\": true`).join(', ')} to ` +\n `compilerOptions in ${tsconfigName}.`,\n },\n ];\n}\n\n/** `tsconfig.json` if present (matching what a bare `tsc --noEmit` would\n * auto-discover), else the first `tsconfig.*.json` found. */\nfunction findTsconfigPath(projectDir: string): string | undefined {\n let names: string[];\n try {\n names = fs\n .readdirSync(projectDir, { withFileTypes: true })\n .filter((entry) => entry.isFile() && TSCONFIG_PATTERN.test(entry.name))\n .map((entry) => entry.name);\n } catch {\n return undefined;\n }\n if (names.length === 0) return undefined;\n const name = names.includes('tsconfig.json')\n ? 'tsconfig.json'\n : [...names].sort()[0];\n return name ? path.join(projectDir, name) : undefined;\n}\n\n/**\n * A minimal, string-literal-aware strip of `//` and `/* *\\/` comments —\n * tsconfig.json is conventionally JSONC, but nothing in this package's\n * dependency graph parses it as such. Not a full JSONC parser: it does not\n * touch trailing commas beyond the one cleanup pass below, and it makes no\n * attempt to validate the result — `JSON.parse` does that, and a project\n * with genuinely malformed JSON falls through to this check's `info`-severity\n * catch, never a crash.\n */\nfunction stripJsonComments(source: string): string {\n let out = '';\n let inString = false;\n let quote = '';\n\n for (let i = 0; i < source.length; i++) {\n const char = source[i] ?? '';\n\n if (inString) {\n out += char;\n if (char === '\\\\') {\n out += source[++i] ?? '';\n continue;\n }\n if (char === quote) inString = false;\n continue;\n }\n\n if (char === '\"' || char === \"'\") {\n inString = true;\n quote = char;\n out += char;\n continue;\n }\n\n if (char === '/' && source[i + 1] === '/') {\n while (i < source.length && source[i] !== '\\n') i++;\n out += '\\n';\n continue;\n }\n\n if (char === '/' && source[i + 1] === '*') {\n i += 2;\n while (\n i < source.length &&\n !(source[i] === '*' && source[i + 1] === '/')\n ) {\n i++;\n }\n i++; // consume the closing '/'\n continue;\n }\n\n out += char;\n }\n\n // Trailing commas before a closing brace/bracket.\n return out.replace(/,(\\s*[}\\]])/g, '$1');\n}\n\n/**\n * True when the config source contains an `inline: [ … ]` array that lists\n * `@ekanos/sdk`. Textual on purpose — evaluating a partner's config module\n * would run arbitrary code; a lint-style scan does not.\n */\nfunction vitestInlinesSdk(source: string): boolean {\n const inlineArrayPattern = /inline\\s*:\\s*\\[([\\s\\S]*?)\\]/g;\n for (\n let match = inlineArrayPattern.exec(source);\n match !== null;\n match = inlineArrayPattern.exec(source)\n ) {\n if (/['\"]@ekanos\\/sdk['\"]/.test(match[1] ?? '')) return true;\n }\n return false;\n}\n"]}
@@ -0,0 +1,15 @@
1
+ import type { StoredSession } from './auth/credential-store.js';
2
+ import type { AuthEnvironment } from './auth/session.js';
3
+ /**
4
+ * Which Source a Source-scoped read or write (`catalog`, `agents`) is about:
5
+ * `--source`, else `source` in ekanos.json, else the caller's only seat.
6
+ * Works outside a project. With several seats and nothing named, it refuses
7
+ * rather than guessing.
8
+ */
9
+ export declare function resolveSourceTarget(env: AuthEnvironment, session: StoredSession, args: {
10
+ source?: string;
11
+ dir: string;
12
+ }): Promise<{
13
+ source: string;
14
+ session: StoredSession;
15
+ }>;