@ekanos/cli 0.16.0 → 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (190) hide show
  1. package/README.md +62 -4
  2. package/dist/commands/add.d.ts +2 -0
  3. package/dist/commands/add.js +118 -0
  4. package/dist/commands/add.js.map +1 -0
  5. package/dist/commands/audiences.d.ts +31 -0
  6. package/dist/commands/audiences.js +239 -0
  7. package/dist/commands/audiences.js.map +1 -0
  8. package/dist/commands/build.d.ts +2 -0
  9. package/dist/commands/build.js +41 -0
  10. package/dist/commands/build.js.map +1 -0
  11. package/dist/commands/check.d.ts +2 -0
  12. package/dist/commands/check.js +43 -0
  13. package/dist/commands/check.js.map +1 -0
  14. package/dist/commands/codegen.d.ts +2 -0
  15. package/dist/commands/codegen.js +49 -0
  16. package/dist/commands/codegen.js.map +1 -0
  17. package/dist/commands/contract.d.ts +52 -0
  18. package/dist/commands/contract.js +300 -0
  19. package/dist/commands/contract.js.map +1 -0
  20. package/dist/commands/dev.d.ts +7 -0
  21. package/dist/commands/dev.js +186 -12
  22. package/dist/commands/dev.js.map +1 -1
  23. package/dist/commands/index-experience.d.ts +31 -0
  24. package/dist/commands/index-experience.js +21 -0
  25. package/dist/commands/index-experience.js.map +1 -0
  26. package/dist/commands/init.d.ts +7 -0
  27. package/dist/commands/init.js +12 -0
  28. package/dist/commands/init.js.map +1 -1
  29. package/dist/commands/new.d.ts +2 -0
  30. package/dist/commands/new.js +39 -0
  31. package/dist/commands/new.js.map +1 -0
  32. package/dist/commands/publish.d.ts +59 -0
  33. package/dist/commands/publish.js +118 -65
  34. package/dist/commands/publish.js.map +1 -1
  35. package/dist/commands/scope-notice.d.ts +6 -0
  36. package/dist/commands/scope-notice.js +15 -0
  37. package/dist/commands/scope-notice.js.map +1 -1
  38. package/dist/commands/sources.js +2 -0
  39. package/dist/commands/sources.js.map +1 -1
  40. package/dist/commands/sync.d.ts +2 -0
  41. package/dist/commands/sync.js +150 -0
  42. package/dist/commands/sync.js.map +1 -0
  43. package/dist/commands/teams-audience.d.ts +12 -0
  44. package/dist/commands/teams-audience.js +144 -0
  45. package/dist/commands/teams-audience.js.map +1 -0
  46. package/dist/commands/teams-scope.js +4 -0
  47. package/dist/commands/teams-scope.js.map +1 -1
  48. package/dist/commands/teams.d.ts +1 -1
  49. package/dist/commands/teams.js +6 -1
  50. package/dist/commands/teams.js.map +1 -1
  51. package/dist/compatibility.d.ts +10 -1
  52. package/dist/compatibility.js +10 -1
  53. package/dist/compatibility.js.map +1 -1
  54. package/dist/dev-experience.d.ts +125 -0
  55. package/dist/dev-experience.js +128 -0
  56. package/dist/dev-experience.js.map +1 -0
  57. package/dist/experience/boundary.d.ts +12 -0
  58. package/dist/experience/boundary.js +260 -0
  59. package/dist/experience/boundary.js.map +1 -0
  60. package/dist/experience/build.d.ts +16 -0
  61. package/dist/experience/build.js +90 -0
  62. package/dist/experience/build.js.map +1 -0
  63. package/dist/experience/checks.d.ts +29 -0
  64. package/dist/experience/checks.js +133 -0
  65. package/dist/experience/checks.js.map +1 -0
  66. package/dist/experience/contract-sources.d.ts +36 -0
  67. package/dist/experience/contract-sources.js +111 -0
  68. package/dist/experience/contract-sources.js.map +1 -0
  69. package/dist/experience/definition-checks.d.ts +26 -0
  70. package/dist/experience/definition-checks.js +201 -0
  71. package/dist/experience/definition-checks.js.map +1 -0
  72. package/dist/experience/definition-shape.d.ts +60 -0
  73. package/dist/experience/definition-shape.js +2 -0
  74. package/dist/experience/definition-shape.js.map +1 -0
  75. package/dist/experience/init.d.ts +10 -0
  76. package/dist/experience/init.js +64 -0
  77. package/dist/experience/init.js.map +1 -0
  78. package/dist/experience/integrations-codegen.d.ts +17 -0
  79. package/dist/experience/integrations-codegen.js +51 -0
  80. package/dist/experience/integrations-codegen.js.map +1 -0
  81. package/dist/experience/json-schema-to-ts.d.ts +1 -0
  82. package/dist/experience/json-schema-to-ts.js +90 -0
  83. package/dist/experience/json-schema-to-ts.js.map +1 -0
  84. package/dist/experience/load-experience.d.ts +23 -0
  85. package/dist/experience/load-experience.js +115 -0
  86. package/dist/experience/load-experience.js.map +1 -0
  87. package/dist/experience/overrides.d.ts +8 -0
  88. package/dist/experience/overrides.js +21 -0
  89. package/dist/experience/overrides.js.map +1 -0
  90. package/dist/experience/pin-integration.d.ts +8 -0
  91. package/dist/experience/pin-integration.js +17 -0
  92. package/dist/experience/pin-integration.js.map +1 -0
  93. package/dist/experience/pin-ops.d.ts +12 -0
  94. package/dist/experience/pin-ops.js +36 -0
  95. package/dist/experience/pin-ops.js.map +1 -0
  96. package/dist/experience/pins.d.ts +35 -0
  97. package/dist/experience/pins.js +91 -0
  98. package/dist/experience/pins.js.map +1 -0
  99. package/dist/experience/project.d.ts +52 -0
  100. package/dist/experience/project.js +149 -0
  101. package/dist/experience/project.js.map +1 -0
  102. package/dist/experience/publish.d.ts +51 -0
  103. package/dist/experience/publish.js +144 -0
  104. package/dist/experience/publish.js.map +1 -0
  105. package/dist/experience/regenerate.d.ts +10 -0
  106. package/dist/experience/regenerate.js +33 -0
  107. package/dist/experience/regenerate.js.map +1 -0
  108. package/dist/experience/report.d.ts +9 -0
  109. package/dist/experience/report.js +23 -0
  110. package/dist/experience/report.js.map +1 -0
  111. package/dist/harness-scaffold.d.ts +52 -18
  112. package/dist/harness-scaffold.js +189 -9
  113. package/dist/harness-scaffold.js.map +1 -1
  114. package/dist/index.js +139 -7
  115. package/dist/index.js.map +1 -1
  116. package/dist/project-checks.d.ts +12 -0
  117. package/dist/project-checks.js +46 -0
  118. package/dist/project-checks.js.map +1 -1
  119. package/dist/project.d.ts +14 -0
  120. package/dist/project.js +10 -0
  121. package/dist/project.js.map +1 -1
  122. package/dist/repo/publish.d.ts +42 -0
  123. package/dist/repo/publish.js +225 -0
  124. package/dist/repo/publish.js.map +1 -0
  125. package/dist/repo/scaffold.d.ts +30 -0
  126. package/dist/repo/scaffold.js +165 -0
  127. package/dist/repo/scaffold.js.map +1 -0
  128. package/dist/teams-api.d.ts +37 -1
  129. package/dist/teams-api.js +62 -1
  130. package/dist/teams-api.js.map +1 -1
  131. package/dist/validate-findings.js +2 -1
  132. package/dist/validate-findings.js.map +1 -1
  133. package/package.json +2 -2
  134. package/templates/ekanos.json.tmpl +1 -0
  135. package/templates/experience/AGENTS.md.tmpl +22 -0
  136. package/templates/experience/ekanos.json.tmpl +7 -0
  137. package/templates/experience/ekanos.lock.json.tmpl +4 -0
  138. package/templates/experience/eslint.config.mjs.tmpl +25 -0
  139. package/templates/experience/gitignore.tmpl +17 -0
  140. package/templates/experience/package.json.tmpl +41 -0
  141. package/templates/experience/src/.ekanos/integrations.d.ts.tmpl +3 -0
  142. package/templates/experience/src/experience.ts.tmpl +30 -0
  143. package/templates/experience/src/widgets/hello-widget.tsx.tmpl +40 -0
  144. package/templates/experience/tsconfig.json.tmpl +20 -0
  145. package/templates/harness/app/%5F%5Fekanos/api/[...path]/route.ts.tmpl +9 -0
  146. package/templates/harness/app/[slug]/activation/page.tsx.tmpl +1 -1
  147. package/templates/harness/app/[slug]/page.tsx.tmpl +1 -1
  148. package/templates/harness/app/[slug]/tile/page.tsx.tmpl +1 -1
  149. package/templates/harness/app/[slug]/triggers/page.tsx.tmpl +1 -1
  150. package/templates/harness/app/[slug]/widgets/[widgetId]/page.tsx.tmpl +1 -1
  151. package/templates/harness/app/[slug]/widgets/page.tsx.tmpl +1 -1
  152. package/templates/harness/app/experience/layout.tsx.tmpl +17 -0
  153. package/templates/harness/app/experience/page.tsx.tmpl +1 -0
  154. package/templates/harness/app/experience/pages/[id]/page.tsx.tmpl +1 -0
  155. package/templates/harness/app/experience/review-queue/page.tsx.tmpl +1 -0
  156. package/templates/harness/app/experience/runs/page.tsx.tmpl +1 -0
  157. package/templates/harness/app/experience/widgets/[id]/page.tsx.tmpl +1 -0
  158. package/templates/harness/app/experience/widgets/page.tsx.tmpl +1 -0
  159. package/templates/harness/app/experience/workflows/[id]/page.tsx.tmpl +1 -0
  160. package/templates/harness/app/experience/workflows/page.tsx.tmpl +1 -0
  161. package/templates/harness/app/integrations/[slug]/connect/page.tsx.tmpl +1 -0
  162. package/templates/harness/app/integrations/[slug]/events/page.tsx.tmpl +1 -0
  163. package/templates/harness/app/integrations/[slug]/health/page.tsx.tmpl +1 -0
  164. package/templates/harness/app/integrations/[slug]/operations/[kind]/[name]/page.tsx.tmpl +1 -0
  165. package/templates/harness/app/integrations/[slug]/operations/page.tsx.tmpl +1 -0
  166. package/templates/harness/app/integrations/[slug]/page.tsx.tmpl +1 -0
  167. package/templates/harness/app/integrations/[slug]/preview/activation/page.tsx.tmpl +1 -0
  168. package/templates/harness/app/integrations/[slug]/preview/layout.tsx.tmpl +1 -0
  169. package/templates/harness/app/integrations/[slug]/preview/page.tsx.tmpl +1 -0
  170. package/templates/harness/app/integrations/[slug]/preview/tile/page.tsx.tmpl +1 -0
  171. package/templates/harness/app/integrations/[slug]/preview/triggers/page.tsx.tmpl +1 -0
  172. package/templates/harness/app/integrations/[slug]/preview/widgets/[widgetId]/page.tsx.tmpl +1 -0
  173. package/templates/harness/app/integrations/[slug]/preview/widgets/page.tsx.tmpl +1 -0
  174. package/templates/harness/app/integrations/[slug]/runs/page.tsx.tmpl +1 -0
  175. package/templates/harness/app/integrations/layout.tsx.tmpl +1 -0
  176. package/templates/harness/app/integrations/page.tsx.tmpl +1 -0
  177. package/templates/harness/app/page.experience.tsx.tmpl +5 -0
  178. package/templates/harness/repo/app/%5F%5Fekanos/api/[...path]/route.ts.tmpl +11 -0
  179. package/templates/harness/repo/app/(repo)/connections/page.tsx.tmpl +1 -0
  180. package/templates/harness/repo/app/(repo)/layout.tsx.tmpl +1 -0
  181. package/templates/harness/repo/app/(repo)/page.tsx.tmpl +1 -0
  182. package/templates/harness/repo/app/experiences/[name]/connect/page.tsx.tmpl +1 -0
  183. package/templates/harness/repo/app/experiences/[name]/layout.tsx.tmpl +9 -0
  184. package/templates/harness/repo/app/experiences/page.tsx.tmpl +5 -0
  185. package/templates/harness/repo/app/harness-shell.tsx.tmpl +9 -0
  186. package/templates/harness/repo/app/integrations/[slug]/layout.tsx.tmpl +1 -0
  187. package/templates/harness/repo/app/integrations/page.tsx.tmpl +5 -0
  188. package/templates/harness/repo/app/repo-experiences.ts.tmpl +5 -0
  189. package/templates/harness/styles/globals.css.tmpl +15 -1
  190. package/templates/package.json.tmpl +4 -4
@@ -1,11 +1,16 @@
1
+ import { discoverRepo, isRepoRoot, } from '@ekanos/integration-schema/repo';
1
2
  import { spawn } from 'node:child_process';
2
3
  import * as path from 'node:path';
4
+ import { requireSession, resolveAuthEnvironment, refreshStoredSession, } from '../auth/session.js';
5
+ import { fetchSourceCatalog } from '../catalog-api.js';
3
6
  import { assertHarnessCompatible } from '../compatibility.js';
7
+ import { detectKind, loadHarnessResolver, readExperienceProject, writeSessionCatalog, } from '../dev-experience.js';
4
8
  import { preconditionError, usageError } from '../errors.js';
5
- import { BUILD_CACHE_DIR, HARNESS_DIR, HARNESS_PACKAGE, assertShellDirIsOurs, bootstrapHarnessConfig, ensureGitignoreEntry, missingDependencies, readInstalledHarness, scaffoldShell, } from '../harness-scaffold.js';
9
+ import { BUILD_CACHE_DIR, HARNESS_DIR, HARNESS_PACKAGE, SHELL_REGISTRY_FILENAME, assertShellDirIsOurs, bootstrapHarnessConfig, ensureGitignoreEntry, missingDependencies, readInstalledHarness, scaffoldShell, } from '../harness-scaffold.js';
6
10
  import { detectPackageManager, execArgs, formatCommand, installCommand, } from '../package-manager.js';
7
11
  import { createOutputTail, findIgnoredBuilds, ignoredBuildsHint, } from '../pnpm-builds.js';
8
12
  import { loadProject } from '../project.js';
13
+ import { resolveSourceTarget } from '../source-target.js';
9
14
  import { displayPath } from '../ui.js';
10
15
  import { announceUpdate, checkForUpdate } from '../update-notice.js';
11
16
  const DEFAULT_PORT = 3100;
@@ -50,7 +55,17 @@ const LOCAL_SECRETS_PATH = path.join('.ekanos', 'local-secrets.json');
50
55
  */
51
56
  export async function runDev(ctx, args) {
52
57
  var _a, _b, _c;
53
- const loaded = loadProject(args.dir);
58
+ // At a repo root one harness serves every package; anywhere else the
59
+ // project is a single integration or experience, exactly as before.
60
+ const repo = isRepoRoot(path.resolve(args.dir))
61
+ ? discoverRepo(path.resolve(args.dir))
62
+ : null;
63
+ const experience = repo === null && detectKind(path.resolve(args.dir)) === 'experience';
64
+ const loaded = repo
65
+ ? repoProject(repo)
66
+ : experience
67
+ ? experienceProject(path.resolve(args.dir))
68
+ : loadProject(args.dir);
54
69
  const port = parsePort(args.port);
55
70
  const host = parseHost(args.host);
56
71
  const exposed = !LOOPBACK_HOSTS.has(host);
@@ -86,14 +101,49 @@ export async function runDev(ctx, args) {
86
101
  // harness.config.ts first and only then discovering that .ekanos/harness is
87
102
  // a file or someone else's tree would leave the project half-scaffolded.
88
103
  assertShellDirIsOurs(path.join(loaded.projectDir, HARNESS_DIR), args.force);
89
- const config = bootstrapHarnessConfig(loaded.projectDir, loaded.integrations);
90
- const shell = scaffoldShell({
91
- projectDir: loaded.projectDir,
92
- slug: loaded.primary.slug,
93
- harnessVersion,
94
- force: args.force,
95
- sourceGlobs: loaded.project.sourceGlobs,
96
- });
104
+ // An experience has no integration to register, so it gets no
105
+ // project-root registry: the shell carries its own empty one.
106
+ if (repo) {
107
+ for (const problem of repo.problems) {
108
+ ctx.warn(`${problem.where}: ${problem.message}`);
109
+ }
110
+ }
111
+ // A repo's integration packages each keep their own harness.config.ts; the
112
+ // repo shell's registry imports them.
113
+ const repoConfigs = repo
114
+ ? repo.packages
115
+ .filter((pkg) => pkg.kind === 'integration')
116
+ .map((pkg) => bootstrapHarnessConfig(pkg.dir, loadProject(pkg.dir).integrations))
117
+ : [];
118
+ const config = repo
119
+ ? {
120
+ bootstrapped: repoConfigs.some((result) => result.bootstrapped),
121
+ configPath: path.join(loaded.projectDir, HARNESS_DIR, SHELL_REGISTRY_FILENAME),
122
+ }
123
+ : experience
124
+ ? {
125
+ bootstrapped: false,
126
+ configPath: path.join(loaded.projectDir, HARNESS_DIR, SHELL_REGISTRY_FILENAME),
127
+ }
128
+ : bootstrapHarnessConfig(loaded.projectDir, loaded.integrations);
129
+ const shell = scaffoldShell(Object.assign(Object.assign({ projectDir: loaded.projectDir, slug: loaded.primary.slug, harnessVersion, force: args.force, sourceGlobs: loaded.project.sourceGlobs }, (experience
130
+ ? { experienceEntry: readExperienceProject(loaded.projectDir).entry }
131
+ : {})), (repo
132
+ ? {
133
+ repo: {
134
+ integrations: repo.packages
135
+ .filter((pkg) => pkg.kind === 'integration')
136
+ .map((pkg) => ({ name: pkg.name, dir: pkg.relDir })),
137
+ experiences: repo.packages
138
+ .filter((pkg) => pkg.kind === 'experience')
139
+ .map((pkg) => ({
140
+ name: pkg.name,
141
+ dir: pkg.relDir,
142
+ entry: readExperienceProject(pkg.dir).entry,
143
+ })),
144
+ },
145
+ }
146
+ : {})));
97
147
  const gitignoreUpdated = ensureGitignoreEntry(loaded.projectDir);
98
148
  if (shell.stale) {
99
149
  ctx.log(`Regenerating ${HARNESS_DIR} — ${shell.staleReasons.join('; ')}. ` +
@@ -113,6 +163,18 @@ export async function runDev(ctx, args) {
113
163
  ctx.log(' updated .gitignore (.ekanos/)');
114
164
  ctx.log(` live-mode OAuth: the harness generates ${LOCAL_SECRETS_PATH} on first ` +
115
165
  'use — fill it in and follow "Local OAuth" on the Activation surface.');
166
+ let integrations = null;
167
+ if (repo) {
168
+ // Each experience package resolves its own integrations through the repo
169
+ // server; the session catalog is shared from the repo root.
170
+ writeSessionCatalog(loaded.projectDir, await tryFetchCatalog(ctx, args, loaded.projectDir));
171
+ for (const pkg of repo.packages) {
172
+ ctx.log(` ${pkg.kind} ${pkg.name} v${pkg.version} (${pkg.relDir})`);
173
+ }
174
+ }
175
+ else if (experience) {
176
+ integrations = await resolveExperienceIntegrations(ctx, args, loaded.projectDir);
177
+ }
116
178
  // `--hostname` is passed ALWAYS, never left to Next's default, so the URL
117
179
  // we print is the URL that is actually served.
118
180
  const serverArgs = ['dev', '--port', String(port), '--hostname', host];
@@ -122,7 +184,29 @@ export async function runDev(ctx, args) {
122
184
  const command = (exposed ? `${EXPOSED_ENV}=1 ` : '') +
123
185
  formatCommand(pm.bin, execArgs(pm, 'next', serverArgs));
124
186
  const url = `http://${displayHost(host)}:${port}`;
125
- const scaffold = Object.assign({ slug: loaded.primary.slug, integrations: loaded.integrations.map((i) => i.slug), projectDir: loaded.projectDir, harnessDir: shell.harnessDir, harnessVersion, previousVersion: shell.previousVersion, previousMarker: shell.previousMarker, stale: shell.stale, staleReasons: shell.staleReasons, buildCacheCleared: shell.buildCacheCleared, sourceGlobs: (_b = loaded.project.sourceGlobs) !== null && _b !== void 0 ? _b : [], created: shell.created, updated: shell.updated, unchanged: shell.unchanged, configBootstrapped: config.bootstrapped, configPath: config.configPath, gitignoreUpdated }, (notice ? { toolchain: notice } : {}));
187
+ const scaffold = Object.assign(Object.assign(Object.assign(Object.assign({ kind: repo ? 'repo' : experience ? 'experience' : 'integration' }, (repo
188
+ ? {
189
+ packages: repo.packages.map((pkg) => ({
190
+ name: pkg.name,
191
+ kind: pkg.kind,
192
+ dir: pkg.relDir,
193
+ })),
194
+ }
195
+ : {})), (integrations
196
+ ? {
197
+ overrides: integrations.overrides,
198
+ publishBlocked: integrations.overrides.length > 0,
199
+ }
200
+ : {})), { slug: loaded.primary.slug,
201
+ // An experience lists the integrations it depends on (provenance lines);
202
+ // an integration project lists its own slugs.
203
+ integrations: integrations
204
+ ? integrations.lines
205
+ : repo
206
+ ? repo.packages
207
+ .filter((p) => p.kind === 'integration')
208
+ .map((p) => p.name)
209
+ : loaded.integrations.map((i) => i.slug), projectDir: loaded.projectDir, harnessDir: shell.harnessDir, harnessVersion, previousVersion: shell.previousVersion, previousMarker: shell.previousMarker, stale: shell.stale, staleReasons: shell.staleReasons, buildCacheCleared: shell.buildCacheCleared, sourceGlobs: (_b = loaded.project.sourceGlobs) !== null && _b !== void 0 ? _b : [], created: shell.created, updated: shell.updated, unchanged: shell.unchanged, configBootstrapped: config.bootstrapped, configPath: config.configPath, gitignoreUpdated }), (notice ? { toolchain: notice } : {}));
126
210
  if (exposed) {
127
211
  ctx.warn(`WARNING: --host ${host} exposes the harness beyond this machine. It ` +
128
212
  'has no authentication, and it renders your fixtures and — in live ' +
@@ -138,11 +222,26 @@ export async function runDev(ctx, args) {
138
222
  // running it. `--start` overrides; `--no-start` opts out in a terminal.
139
223
  const shouldStart = (_c = args.start) !== null && _c !== void 0 ? _c : !ctx.jsonMode;
140
224
  if (!shouldStart) {
141
- return ctx.succeed(Object.assign(Object.assign({}, scaffold), { server: { started: false, command, url, port, host, exposed } }), ctx.ui.result({
225
+ const reason = args.start === false ? 'no-start' : 'non-interactive';
226
+ const note = reason === 'non-interactive'
227
+ ? `Scaffolded only: the server was NOT started because this run is non-interactive (JSON output, a pipe, or an agent). Pass --start to run it, or run: ${command}`
228
+ : `Scaffolded only (--no-start). Run: ${command}`;
229
+ ctx.log(note);
230
+ return ctx.succeed(Object.assign(Object.assign({}, scaffold), { server: {
231
+ started: false,
232
+ reason,
233
+ note,
234
+ command,
235
+ url,
236
+ port,
237
+ host,
238
+ exposed,
239
+ } }), ctx.ui.result({
142
240
  title: 'Harness ready',
143
241
  rows: [
144
242
  ['Directory', displayPath(shell.harnessDir, process.cwd())],
145
243
  ['URL', url],
244
+ ['Started', 'no (scaffold only)'],
146
245
  ['Start', command],
147
246
  ],
148
247
  }));
@@ -230,4 +329,79 @@ function parsePort(raw) {
230
329
  }
231
330
  return port;
232
331
  }
332
+ function experienceProject(projectDir) {
333
+ const { slug } = readExperienceProject(projectDir);
334
+ return {
335
+ projectDir,
336
+ // An experience registers no integration of its own with the harness; its
337
+ // integrations come from ekanos.json through /__ekanos/api.
338
+ integrations: [],
339
+ primary: { slug },
340
+ project: {},
341
+ };
342
+ }
343
+ function repoProject(repo) {
344
+ return {
345
+ projectDir: repo.root,
346
+ integrations: [],
347
+ primary: { slug: path.basename(repo.root) },
348
+ // The shell's stylesheet scans integrations/ already; experiences/ is the
349
+ // other half of a repo.
350
+ project: { sourceGlobs: ['experiences/**/*.{ts,tsx}'] },
351
+ };
352
+ }
353
+ /**
354
+ * Resolve every integration with the harness the project installed, print where each
355
+ * came from, and refuse to start when an integration cannot be served (a pin the
356
+ * resolver cannot satisfy is a startup failure, not a blank widget later).
357
+ */
358
+ async function resolveExperienceIntegrations(ctx, args, projectDir) {
359
+ var _a, _b, _c, _d;
360
+ const env = (_a = args.env) !== null && _a !== void 0 ? _a : process.env;
361
+ const resolver = await ((_b = args.loadResolver) !== null && _b !== void 0 ? _b : loadHarnessResolver)(projectDir);
362
+ const catalog = await ((_c = args.fetchCatalog) !== null && _c !== void 0 ? _c : (() => tryFetchCatalog(ctx, args, projectDir)))();
363
+ writeSessionCatalog(projectDir, catalog);
364
+ const contractsDir = (_d = args.contractsDir) !== null && _d !== void 0 ? _d : env.EKANOS_CONTRACTS_DIR;
365
+ const resolution = await resolver.resolveIntegrations(Object.assign(Object.assign({ projectRoot: projectDir, env }, (contractsDir ? { contractsDir: path.resolve(contractsDir) } : {})), { catalog }));
366
+ const lines = resolver.formatProvenanceLines(resolution);
367
+ for (const line of lines)
368
+ ctx.log(line);
369
+ for (const notice of resolution.notices)
370
+ ctx.warn(notice);
371
+ if (resolution.errors.length > 0) {
372
+ throw preconditionError(`${resolution.errors.length} integration${resolution.errors.length === 1 ? '' : 's'} could not be resolved: ${resolution.errors
373
+ .map((e) => e.message)
374
+ .join(' | ')}`, 'Fix the pin in ekanos.json (or run "ekanos sync"), or remove the bad override in ekanos.local.json, then re-run "ekanos dev".');
375
+ }
376
+ return { lines, overrides: resolution.overrides };
377
+ }
378
+ /** The dev's Source catalog when a session exists; null (offline) otherwise. Never throws. */
379
+ async function tryFetchCatalog(ctx, args, projectDir) {
380
+ var _a;
381
+ try {
382
+ const env = resolveAuthEnvironment(ctx, undefined, (_a = args.env) !== null && _a !== void 0 ? _a : process.env, {
383
+ projectDir,
384
+ });
385
+ let session = requireSession(env);
386
+ const resolved = await resolveSourceTarget(env, session, {
387
+ source: undefined,
388
+ dir: projectDir,
389
+ });
390
+ session = resolved.session;
391
+ let result = await fetchSourceCatalog(env.host, session.accessToken, {
392
+ source: resolved.source,
393
+ });
394
+ if (result.status === 'unauthorized') {
395
+ session = await refreshStoredSession(env, session);
396
+ result = await fetchSourceCatalog(env.host, session.accessToken, {
397
+ source: resolved.source,
398
+ });
399
+ }
400
+ return result.status === 'ok' ? result.catalog : null;
401
+ }
402
+ catch (_b) {
403
+ ctx.log(ctx.ui.dim('No CLI session: integrations resolve offline (registry integrations fixtures-only, overrides unverified). Run "ekanos login" to enable develop-aware live mode.'));
404
+ return null;
405
+ }
406
+ }
233
407
  //# sourceMappingURL=dev.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"dev.js","sourceRoot":"","sources":["../../src/commands/dev.ts"],"names":[],"mappings":"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","sourcesContent":["import { spawn } from 'node:child_process';\nimport * as path from 'node:path';\n\nimport { assertHarnessCompatible } from '../compatibility';\nimport type { CliContext } from '../context';\nimport { preconditionError, usageError } from '../errors';\nimport {\n BUILD_CACHE_DIR,\n HARNESS_DIR,\n HARNESS_PACKAGE,\n assertShellDirIsOurs,\n bootstrapHarnessConfig,\n ensureGitignoreEntry,\n missingDependencies,\n readInstalledHarness,\n scaffoldShell,\n} from '../harness-scaffold';\nimport {\n detectPackageManager,\n execArgs,\n formatCommand,\n installCommand,\n} from '../package-manager';\nimport {\n createOutputTail,\n findIgnoredBuilds,\n ignoredBuildsHint,\n} from '../pnpm-builds';\nimport { loadProject } from '../project';\nimport { displayPath } from '../ui';\nimport { announceUpdate, checkForUpdate } from '../update-notice';\n\nexport interface DevArgs {\n /** Project directory (contains ekanos.json). Defaults to cwd. */\n dir: string;\n /** Port for the dev server. */\n port?: string;\n /**\n * Interface to bind. Defaults to loopback — see `resolveHost`. Anything\n * else is an explicit, warned-about decision to expose the harness.\n *\n * NOTE: unrelated to the Ekanos HOST the update notice checks against —\n * this is the local bind address for `next dev`, never an Ekanos origin.\n */\n host?: string;\n /** Rewrite every generated file even when its bytes already match. */\n force: boolean;\n /**\n * Explicit `--start` / `--no-start`. Undefined means \"decide from the\n * output mode\": a JSON caller is an agent or a pipe and must not be handed\n * a process that never exits.\n */\n start?: boolean;\n env?: Record<string, string | undefined>;\n}\n\nconst DEFAULT_PORT = 3100;\n\n/**\n * Loopback, NOT Next's default.\n *\n * `next dev` binds 0.0.0.0 unless told otherwise, so the harness would be\n * reachable by anyone on the same network — a coffee-shop Wi-Fi, a shared\n * office VLAN — while we printed \"http://localhost:3100\" and implied\n * otherwise. It renders a partner's integration with their fixtures and, in\n * live mode, whatever their vendor API returns; there is no auth in front of\n * any of it. Binding loopback by default costs a partner nothing and makes\n * exposure a thing they ask for.\n */\nconst DEFAULT_HOST = '127.0.0.1';\n\n/** Addresses that are only reachable from this machine. */\nconst LOOPBACK_HOSTS = new Set(['127.0.0.1', '::1', 'localhost']);\n\n/**\n * Set for the dev server when `--host` binds beyond loopback. The shell's\n * API routes (`@ekanos/harness/api`) refuse non-loopback `Host` headers\n * unless this is `1`: they hold the partner's local OAuth credentials and\n * run handlers on request, so exposing them has to be a decision — this one,\n * made after the warning below — rather than a side effect of the bind.\n */\nconst EXPOSED_ENV = 'EKANOS_HARNESS_EXPOSED';\n\n/** Where the harness generates, and reads, local dev credentials. */\nconst LOCAL_SECRETS_PATH = path.join('.ekanos', 'local-secrets.json');\n\n/**\n * `dev` — scaffold the local harness shell into the partner's project, then\n * run it with their own Next.\n *\n * The shape of this verb follows from one rule: we must be able to ship a\n * harness bugfix as an `@ekanos/harness` version bump that a partner picks up\n * with `pnpm up`, with zero edits to any file they own. So everything under\n * `.ekanos/` is generated, gitignored and rewritten freely, `harness.config.ts`\n * at the project root is written once and never touched again, and all actual\n * harness behaviour lives in the installed package.\n *\n * Two idempotency properties matter and are both tested: a second run produces\n * byte-identical files and rewrites nothing (so Next's watcher stays quiet),\n * and `.gitignore` never grows a duplicate line.\n */\nexport async function runDev(ctx: CliContext, args: DevArgs): Promise<number> {\n const loaded = loadProject(args.dir);\n const port = parsePort(args.port);\n const host = parseHost(args.host);\n const exposed = !LOOPBACK_HOSTS.has(host);\n const pm = detectPackageManager(loaded.projectDir);\n\n const notice = await checkForUpdate({ env: args.env ?? process.env });\n announceUpdate(ctx, notice);\n\n // Fail before writing anything: the shell declares no dependencies of its\n // own, so a missing one would otherwise surface as an opaque Next\n // resolution error minutes later.\n const missing = missingDependencies(loaded.projectDir);\n if (missing.length > 0) {\n throw preconditionError(\n `${missing.length} package${missing.length === 1 ? '' : 's'} the ` +\n `harness needs ${missing.length === 1 ? 'is' : 'are'} not installed ` +\n `in ${loaded.projectDir}: ${missing.join(', ')}.`,\n `Run \"${installCommand(pm, missing)}\" in ${loaded.projectDir}, then ` +\n 're-run \"ekanos dev\".',\n );\n }\n\n const harness = readInstalledHarness(loaded.projectDir);\n const harnessVersion = harness?.version;\n if (!harnessVersion) {\n throw preconditionError(\n `${HARNESS_PACKAGE} is installed but declares no version.`,\n `Reinstall it with \"${installCommand(pm, [HARNESS_PACKAGE])}\" in ` +\n `${loaded.projectDir}, then re-run \"ekanos dev\".`,\n );\n }\n\n // The templates ship with the CLI and the code they import ships with the\n // harness, so a partner can upgrade one without the other. Name that\n // mismatch here rather than letting it surface later as a module-resolution\n // error inside a shell we cheerfully certified as current.\n assertHarnessCompatible({\n harnessVersion,\n harnessManifest: harness?.manifest,\n installHint: (spec) => installCommand(pm, [spec]),\n });\n\n // Validate the shell target BEFORE writing anything at all. Bootstrapping\n // harness.config.ts first and only then discovering that .ekanos/harness is\n // a file or someone else's tree would leave the project half-scaffolded.\n assertShellDirIsOurs(path.join(loaded.projectDir, HARNESS_DIR), args.force);\n\n const config = bootstrapHarnessConfig(loaded.projectDir, loaded.integrations);\n const shell = scaffoldShell({\n projectDir: loaded.projectDir,\n slug: loaded.primary.slug,\n harnessVersion,\n force: args.force,\n sourceGlobs: loaded.project.sourceGlobs,\n });\n const gitignoreUpdated = ensureGitignoreEntry(loaded.projectDir);\n\n if (shell.stale) {\n ctx.log(\n `Regenerating ${HARNESS_DIR} — ${shell.staleReasons.join('; ')}. ` +\n `Nothing you own changed; ${config.configPath} is untouched.`,\n );\n }\n if (shell.buildCacheCleared) {\n ctx.log(\n ` removed ${HARNESS_DIR}/${BUILD_CACHE_DIR} (Next's cache from the previous shell)`,\n );\n }\n\n if (config.bootstrapped) {\n ctx.log(` created harness.config.ts (yours — edit it, we never will)`);\n }\n for (const file of shell.created) ctx.log(` created ${HARNESS_DIR}/${file}`);\n for (const file of shell.updated) ctx.log(` updated ${HARNESS_DIR}/${file}`);\n if (gitignoreUpdated) ctx.log(' updated .gitignore (.ekanos/)');\n ctx.log(\n ` live-mode OAuth: the harness generates ${LOCAL_SECRETS_PATH} on first ` +\n 'use — fill it in and follow \"Local OAuth\" on the Activation surface.',\n );\n\n // `--hostname` is passed ALWAYS, never left to Next's default, so the URL\n // we print is the URL that is actually served.\n const serverArgs = ['dev', '--port', String(port), '--hostname', host];\n const serverEnv: Record<string, string> = exposed\n ? { [EXPOSED_ENV]: '1' }\n : {};\n const command =\n (exposed ? `${EXPOSED_ENV}=1 ` : '') +\n formatCommand(pm.bin, execArgs(pm, 'next', serverArgs));\n const url = `http://${displayHost(host)}:${port}`;\n\n const scaffold = {\n slug: loaded.primary.slug,\n integrations: loaded.integrations.map((i) => i.slug),\n projectDir: loaded.projectDir,\n harnessDir: shell.harnessDir,\n harnessVersion,\n previousVersion: shell.previousVersion,\n previousMarker: shell.previousMarker,\n stale: shell.stale,\n staleReasons: shell.staleReasons,\n buildCacheCleared: shell.buildCacheCleared,\n sourceGlobs: loaded.project.sourceGlobs ?? [],\n created: shell.created,\n updated: shell.updated,\n unchanged: shell.unchanged,\n configBootstrapped: config.bootstrapped,\n configPath: config.configPath,\n gitignoreUpdated,\n ...(notice ? { toolchain: notice } : {}),\n };\n\n if (exposed) {\n ctx.warn(\n `WARNING: --host ${host} exposes the harness beyond this machine. It ` +\n 'has no authentication, and it renders your fixtures and — in live ' +\n 'mode — real responses from your vendor API. Its API routes hold the ' +\n `client secret from ${LOCAL_SECRETS_PATH}, the tokens onTokens stored, ` +\n 'and run your handlers on request; they refuse non-loopback callers ' +\n `unless ${EXPOSED_ENV}=1, which this sets. Anyone who can reach ` +\n `${url} can read and run all of it. Omit --host to bind ${DEFAULT_HOST}.`,\n );\n }\n\n // A JSON caller is an agent or a pipe. Handing it a dev server that never\n // exits would hang its tool call forever and it would never see the\n // envelope, so JSON mode scaffolds and reports the command instead of\n // running it. `--start` overrides; `--no-start` opts out in a terminal.\n const shouldStart = args.start ?? !ctx.jsonMode;\n if (!shouldStart) {\n return ctx.succeed(\n {\n ...scaffold,\n server: { started: false, command, url, port, host, exposed },\n },\n ctx.ui.result({\n title: 'Harness ready',\n rows: [\n ['Directory', displayPath(shell.harnessDir, process.cwd())],\n ['URL', url],\n ['Start', command],\n ],\n }),\n );\n }\n\n ctx.log(ctx.ui.dim(`Starting the harness on ${url} (${command})…`));\n const tail = createOutputTail();\n const exitCode = await spawnServer({\n ctx,\n tail,\n bin: pm.bin,\n argv: execArgs(pm, 'next', serverArgs),\n cwd: shell.harnessDir,\n env: serverEnv,\n });\n\n const data = {\n ...scaffold,\n server: { started: true, command, url, port, host, exposed, exitCode },\n };\n\n if (exitCode === 0) {\n return ctx.succeed(\n data,\n ctx.ui.result({ title: 'Harness stopped', rows: [['URL', url]] }),\n );\n }\n\n const ignoredBuilds = findIgnoredBuilds(tail.text());\n\n return ctx.failWith({\n code: 'dev_server_failed',\n message: `\"${command}\" exited with code ${exitCode}.`,\n hint:\n ignoredBuilds.length > 0\n ? ignoredBuildsHint(ignoredBuilds)\n : 'Read the Next output on stderr. If it cannot resolve @ekanos/harness ' +\n `or next, reinstall dependencies in ${loaded.projectDir}; if a route ` +\n `fails to compile, delete ${shell.harnessDir} and re-run \"ekanos dev\".`,\n // Transparent passthrough of Next's own exit code.\n exitCode,\n data,\n });\n}\n\nfunction spawnServer(params: {\n ctx: CliContext;\n tail: ReturnType<typeof createOutputTail>;\n bin: string;\n argv: string[];\n cwd: string;\n env: Record<string, string>;\n}): Promise<number> {\n return new Promise<number>((resolve, reject) => {\n const child = spawn(params.bin, params.argv, {\n cwd: params.cwd,\n env: { ...process.env, ...params.env },\n // stdin inherited so Next's own key handling works; stdout/stderr piped\n // so we can route them and keep OUR stdout reserved for the envelope.\n stdio: ['inherit', 'pipe', 'pipe'],\n });\n\n child.stdout?.on('data', (chunk: Buffer) => {\n params.tail.push(chunk);\n (params.ctx.jsonMode ? process.stderr : process.stdout).write(chunk);\n });\n child.stderr?.on('data', (chunk: Buffer) => {\n params.tail.push(chunk);\n process.stderr.write(chunk);\n });\n\n child.on('error', (error) => reject(error));\n child.on('close', (code) => resolve(code ?? 1));\n });\n}\n\n/**\n * The hostname to show a human. `127.0.0.1` is spelled `localhost` because\n * that is what people type; a wildcard bind is spelled `localhost` too, but\n * only after `exposed` has already been warned about — printing `0.0.0.0` as\n * a URL is not useful.\n */\nfunction displayHost(host: string): string {\n if (host === '127.0.0.1' || host === '0.0.0.0' || host === '::') {\n return 'localhost';\n }\n if (host === '::1') return '[::1]';\n return host;\n}\n\nfunction parseHost(raw: string | undefined): string {\n if (raw === undefined) return DEFAULT_HOST;\n\n const host = raw.trim();\n if (host.length === 0) {\n throw usageError(\n 'Invalid --host \"\".',\n `Pass an interface to bind, e.g. \"--host 0.0.0.0\" to expose the ` +\n `harness on your network, or omit it to bind ${DEFAULT_HOST}.`,\n );\n }\n return host;\n}\n\nfunction parsePort(raw: string | undefined): number {\n if (raw === undefined) return DEFAULT_PORT;\n\n const port = Number(raw);\n if (!Number.isInteger(port) || port < 1 || port > 65535) {\n throw usageError(\n `Invalid --port \"${raw}\".`,\n 'Pass an integer between 1 and 65535, e.g. \"--port 3100\".',\n );\n }\n return port;\n}\n"]}
1
+ {"version":3,"file":"dev.js","sourceRoot":"","sources":["../../src/commands/dev.ts"],"names":[],"mappings":"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","sourcesContent":["import { type SourceCatalog } from '@ekanos/integration-schema';\nimport {\n type RepoDiscovery,\n discoverRepo,\n isRepoRoot,\n} from '@ekanos/integration-schema/repo';\nimport { spawn } from 'node:child_process';\nimport * as path from 'node:path';\n\nimport {\n requireSession,\n resolveAuthEnvironment,\n refreshStoredSession,\n} from '../auth/session';\nimport { fetchSourceCatalog } from '../catalog-api';\nimport { assertHarnessCompatible } from '../compatibility';\nimport type { CliContext } from '../context';\nimport {\n type HarnessResolver,\n detectKind,\n loadHarnessResolver,\n readExperienceProject,\n writeSessionCatalog,\n} from '../dev-experience';\nimport { preconditionError, usageError } from '../errors';\nimport {\n BUILD_CACHE_DIR,\n HARNESS_DIR,\n HARNESS_PACKAGE,\n SHELL_REGISTRY_FILENAME,\n assertShellDirIsOurs,\n bootstrapHarnessConfig,\n ensureGitignoreEntry,\n missingDependencies,\n readInstalledHarness,\n scaffoldShell,\n} from '../harness-scaffold';\nimport {\n detectPackageManager,\n execArgs,\n formatCommand,\n installCommand,\n} from '../package-manager';\nimport {\n createOutputTail,\n findIgnoredBuilds,\n ignoredBuildsHint,\n} from '../pnpm-builds';\nimport { loadProject } from '../project';\nimport { resolveSourceTarget } from '../source-target';\nimport { displayPath } from '../ui';\nimport { announceUpdate, checkForUpdate } from '../update-notice';\n\nexport interface DevArgs {\n /** Project directory (contains ekanos.json). Defaults to cwd. */\n dir: string;\n /** Port for the dev server. */\n port?: string;\n /**\n * Interface to bind. Defaults to loopback — see `resolveHost`. Anything\n * else is an explicit, warned-about decision to expose the harness.\n *\n * NOTE: unrelated to the Ekanos HOST the update notice checks against —\n * this is the local bind address for `next dev`, never an Ekanos origin.\n */\n host?: string;\n /** Rewrite every generated file even when its bytes already match. */\n force: boolean;\n /**\n * Explicit `--start` / `--no-start`. Undefined means \"decide from the\n * output mode\": a JSON caller is an agent or a pipe and must not be handed\n * a process that never exits.\n */\n start?: boolean;\n env?: Record<string, string | undefined>;\n /** Experience projects: an offline directory of contracts (`--contracts-dir`). */\n contractsDir?: string;\n /** Test seams. */\n loadResolver?: (projectDir: string) => Promise<HarnessResolver>;\n fetchCatalog?: () => Promise<SourceCatalog | null>;\n}\n\nconst DEFAULT_PORT = 3100;\n\n/**\n * Loopback, NOT Next's default.\n *\n * `next dev` binds 0.0.0.0 unless told otherwise, so the harness would be\n * reachable by anyone on the same network — a coffee-shop Wi-Fi, a shared\n * office VLAN — while we printed \"http://localhost:3100\" and implied\n * otherwise. It renders a partner's integration with their fixtures and, in\n * live mode, whatever their vendor API returns; there is no auth in front of\n * any of it. Binding loopback by default costs a partner nothing and makes\n * exposure a thing they ask for.\n */\nconst DEFAULT_HOST = '127.0.0.1';\n\n/** Addresses that are only reachable from this machine. */\nconst LOOPBACK_HOSTS = new Set(['127.0.0.1', '::1', 'localhost']);\n\n/**\n * Set for the dev server when `--host` binds beyond loopback. The shell's\n * API routes (`@ekanos/harness/api`) refuse non-loopback `Host` headers\n * unless this is `1`: they hold the partner's local OAuth credentials and\n * run handlers on request, so exposing them has to be a decision — this one,\n * made after the warning below — rather than a side effect of the bind.\n */\nconst EXPOSED_ENV = 'EKANOS_HARNESS_EXPOSED';\n\n/** Where the harness generates, and reads, local dev credentials. */\nconst LOCAL_SECRETS_PATH = path.join('.ekanos', 'local-secrets.json');\n\n/**\n * `dev` — scaffold the local harness shell into the partner's project, then\n * run it with their own Next.\n *\n * The shape of this verb follows from one rule: we must be able to ship a\n * harness bugfix as an `@ekanos/harness` version bump that a partner picks up\n * with `pnpm up`, with zero edits to any file they own. So everything under\n * `.ekanos/` is generated, gitignored and rewritten freely, `harness.config.ts`\n * at the project root is written once and never touched again, and all actual\n * harness behaviour lives in the installed package.\n *\n * Two idempotency properties matter and are both tested: a second run produces\n * byte-identical files and rewrites nothing (so Next's watcher stays quiet),\n * and `.gitignore` never grows a duplicate line.\n */\nexport async function runDev(ctx: CliContext, args: DevArgs): Promise<number> {\n // At a repo root one harness serves every package; anywhere else the\n // project is a single integration or experience, exactly as before.\n const repo = isRepoRoot(path.resolve(args.dir))\n ? discoverRepo(path.resolve(args.dir))\n : null;\n const experience =\n repo === null && detectKind(path.resolve(args.dir)) === 'experience';\n const loaded: DevProject = repo\n ? repoProject(repo)\n : experience\n ? experienceProject(path.resolve(args.dir))\n : loadProject(args.dir);\n const port = parsePort(args.port);\n const host = parseHost(args.host);\n const exposed = !LOOPBACK_HOSTS.has(host);\n const pm = detectPackageManager(loaded.projectDir);\n\n const notice = await checkForUpdate({ env: args.env ?? process.env });\n announceUpdate(ctx, notice);\n\n // Fail before writing anything: the shell declares no dependencies of its\n // own, so a missing one would otherwise surface as an opaque Next\n // resolution error minutes later.\n const missing = missingDependencies(loaded.projectDir);\n if (missing.length > 0) {\n throw preconditionError(\n `${missing.length} package${missing.length === 1 ? '' : 's'} the ` +\n `harness needs ${missing.length === 1 ? 'is' : 'are'} not installed ` +\n `in ${loaded.projectDir}: ${missing.join(', ')}.`,\n `Run \"${installCommand(pm, missing)}\" in ${loaded.projectDir}, then ` +\n 're-run \"ekanos dev\".',\n );\n }\n\n const harness = readInstalledHarness(loaded.projectDir);\n const harnessVersion = harness?.version;\n if (!harnessVersion) {\n throw preconditionError(\n `${HARNESS_PACKAGE} is installed but declares no version.`,\n `Reinstall it with \"${installCommand(pm, [HARNESS_PACKAGE])}\" in ` +\n `${loaded.projectDir}, then re-run \"ekanos dev\".`,\n );\n }\n\n // The templates ship with the CLI and the code they import ships with the\n // harness, so a partner can upgrade one without the other. Name that\n // mismatch here rather than letting it surface later as a module-resolution\n // error inside a shell we cheerfully certified as current.\n assertHarnessCompatible({\n harnessVersion,\n harnessManifest: harness?.manifest,\n installHint: (spec) => installCommand(pm, [spec]),\n });\n\n // Validate the shell target BEFORE writing anything at all. Bootstrapping\n // harness.config.ts first and only then discovering that .ekanos/harness is\n // a file or someone else's tree would leave the project half-scaffolded.\n assertShellDirIsOurs(path.join(loaded.projectDir, HARNESS_DIR), args.force);\n\n // An experience has no integration to register, so it gets no\n // project-root registry: the shell carries its own empty one.\n if (repo) {\n for (const problem of repo.problems) {\n ctx.warn(`${problem.where}: ${problem.message}`);\n }\n }\n\n // A repo's integration packages each keep their own harness.config.ts; the\n // repo shell's registry imports them.\n const repoConfigs = repo\n ? repo.packages\n .filter((pkg) => pkg.kind === 'integration')\n .map((pkg) =>\n bootstrapHarnessConfig(pkg.dir, loadProject(pkg.dir).integrations),\n )\n : [];\n const config = repo\n ? {\n bootstrapped: repoConfigs.some((result) => result.bootstrapped),\n configPath: path.join(\n loaded.projectDir,\n HARNESS_DIR,\n SHELL_REGISTRY_FILENAME,\n ),\n }\n : experience\n ? {\n bootstrapped: false,\n configPath: path.join(\n loaded.projectDir,\n HARNESS_DIR,\n SHELL_REGISTRY_FILENAME,\n ),\n }\n : bootstrapHarnessConfig(loaded.projectDir, loaded.integrations);\n const shell = scaffoldShell({\n projectDir: loaded.projectDir,\n slug: loaded.primary.slug,\n harnessVersion,\n force: args.force,\n sourceGlobs: loaded.project.sourceGlobs,\n ...(experience\n ? { experienceEntry: readExperienceProject(loaded.projectDir).entry }\n : {}),\n ...(repo\n ? {\n repo: {\n integrations: repo.packages\n .filter((pkg) => pkg.kind === 'integration')\n .map((pkg) => ({ name: pkg.name, dir: pkg.relDir })),\n experiences: repo.packages\n .filter((pkg) => pkg.kind === 'experience')\n .map((pkg) => ({\n name: pkg.name,\n dir: pkg.relDir,\n entry: readExperienceProject(pkg.dir).entry,\n })),\n },\n }\n : {}),\n });\n const gitignoreUpdated = ensureGitignoreEntry(loaded.projectDir);\n\n if (shell.stale) {\n ctx.log(\n `Regenerating ${HARNESS_DIR} — ${shell.staleReasons.join('; ')}. ` +\n `Nothing you own changed; ${config.configPath} is untouched.`,\n );\n }\n if (shell.buildCacheCleared) {\n ctx.log(\n ` removed ${HARNESS_DIR}/${BUILD_CACHE_DIR} (Next's cache from the previous shell)`,\n );\n }\n\n if (config.bootstrapped) {\n ctx.log(` created harness.config.ts (yours — edit it, we never will)`);\n }\n for (const file of shell.created) ctx.log(` created ${HARNESS_DIR}/${file}`);\n for (const file of shell.updated) ctx.log(` updated ${HARNESS_DIR}/${file}`);\n if (gitignoreUpdated) ctx.log(' updated .gitignore (.ekanos/)');\n ctx.log(\n ` live-mode OAuth: the harness generates ${LOCAL_SECRETS_PATH} on first ` +\n 'use — fill it in and follow \"Local OAuth\" on the Activation surface.',\n );\n\n let integrations: ExperienceStartup | null = null;\n if (repo) {\n // Each experience package resolves its own integrations through the repo\n // server; the session catalog is shared from the repo root.\n writeSessionCatalog(\n loaded.projectDir,\n await tryFetchCatalog(ctx, args, loaded.projectDir),\n );\n for (const pkg of repo.packages) {\n ctx.log(` ${pkg.kind} ${pkg.name} v${pkg.version} (${pkg.relDir})`);\n }\n } else if (experience) {\n integrations = await resolveExperienceIntegrations(\n ctx,\n args,\n loaded.projectDir,\n );\n }\n\n // `--hostname` is passed ALWAYS, never left to Next's default, so the URL\n // we print is the URL that is actually served.\n const serverArgs = ['dev', '--port', String(port), '--hostname', host];\n const serverEnv: Record<string, string> = exposed\n ? { [EXPOSED_ENV]: '1' }\n : {};\n const command =\n (exposed ? `${EXPOSED_ENV}=1 ` : '') +\n formatCommand(pm.bin, execArgs(pm, 'next', serverArgs));\n const url = `http://${displayHost(host)}:${port}`;\n\n const scaffold = {\n kind: repo ? 'repo' : experience ? 'experience' : 'integration',\n ...(repo\n ? {\n packages: repo.packages.map((pkg) => ({\n name: pkg.name,\n kind: pkg.kind,\n dir: pkg.relDir,\n })),\n }\n : {}),\n ...(integrations\n ? {\n overrides: integrations.overrides,\n publishBlocked: integrations.overrides.length > 0,\n }\n : {}),\n slug: loaded.primary.slug,\n // An experience lists the integrations it depends on (provenance lines);\n // an integration project lists its own slugs.\n integrations: integrations\n ? integrations.lines\n : repo\n ? repo.packages\n .filter((p) => p.kind === 'integration')\n .map((p) => p.name)\n : loaded.integrations.map((i) => i.slug),\n projectDir: loaded.projectDir,\n harnessDir: shell.harnessDir,\n harnessVersion,\n previousVersion: shell.previousVersion,\n previousMarker: shell.previousMarker,\n stale: shell.stale,\n staleReasons: shell.staleReasons,\n buildCacheCleared: shell.buildCacheCleared,\n sourceGlobs: loaded.project.sourceGlobs ?? [],\n created: shell.created,\n updated: shell.updated,\n unchanged: shell.unchanged,\n configBootstrapped: config.bootstrapped,\n configPath: config.configPath,\n gitignoreUpdated,\n ...(notice ? { toolchain: notice } : {}),\n };\n\n if (exposed) {\n ctx.warn(\n `WARNING: --host ${host} exposes the harness beyond this machine. It ` +\n 'has no authentication, and it renders your fixtures and — in live ' +\n 'mode — real responses from your vendor API. Its API routes hold the ' +\n `client secret from ${LOCAL_SECRETS_PATH}, the tokens onTokens stored, ` +\n 'and run your handlers on request; they refuse non-loopback callers ' +\n `unless ${EXPOSED_ENV}=1, which this sets. Anyone who can reach ` +\n `${url} can read and run all of it. Omit --host to bind ${DEFAULT_HOST}.`,\n );\n }\n\n // A JSON caller is an agent or a pipe. Handing it a dev server that never\n // exits would hang its tool call forever and it would never see the\n // envelope, so JSON mode scaffolds and reports the command instead of\n // running it. `--start` overrides; `--no-start` opts out in a terminal.\n const shouldStart = args.start ?? !ctx.jsonMode;\n if (!shouldStart) {\n const reason = args.start === false ? 'no-start' : 'non-interactive';\n const note =\n reason === 'non-interactive'\n ? `Scaffolded only: the server was NOT started because this run is non-interactive (JSON output, a pipe, or an agent). Pass --start to run it, or run: ${command}`\n : `Scaffolded only (--no-start). Run: ${command}`;\n ctx.log(note);\n return ctx.succeed(\n {\n ...scaffold,\n server: {\n started: false,\n reason,\n note,\n command,\n url,\n port,\n host,\n exposed,\n },\n },\n ctx.ui.result({\n title: 'Harness ready',\n rows: [\n ['Directory', displayPath(shell.harnessDir, process.cwd())],\n ['URL', url],\n ['Started', 'no (scaffold only)'],\n ['Start', command],\n ],\n }),\n );\n }\n\n ctx.log(ctx.ui.dim(`Starting the harness on ${url} (${command})…`));\n const tail = createOutputTail();\n const exitCode = await spawnServer({\n ctx,\n tail,\n bin: pm.bin,\n argv: execArgs(pm, 'next', serverArgs),\n cwd: shell.harnessDir,\n env: serverEnv,\n });\n\n const data = {\n ...scaffold,\n server: { started: true, command, url, port, host, exposed, exitCode },\n };\n\n if (exitCode === 0) {\n return ctx.succeed(\n data,\n ctx.ui.result({ title: 'Harness stopped', rows: [['URL', url]] }),\n );\n }\n\n const ignoredBuilds = findIgnoredBuilds(tail.text());\n\n return ctx.failWith({\n code: 'dev_server_failed',\n message: `\"${command}\" exited with code ${exitCode}.`,\n hint:\n ignoredBuilds.length > 0\n ? ignoredBuildsHint(ignoredBuilds)\n : 'Read the Next output on stderr. If it cannot resolve @ekanos/harness ' +\n `or next, reinstall dependencies in ${loaded.projectDir}; if a route ` +\n `fails to compile, delete ${shell.harnessDir} and re-run \"ekanos dev\".`,\n // Transparent passthrough of Next's own exit code.\n exitCode,\n data,\n });\n}\n\nfunction spawnServer(params: {\n ctx: CliContext;\n tail: ReturnType<typeof createOutputTail>;\n bin: string;\n argv: string[];\n cwd: string;\n env: Record<string, string>;\n}): Promise<number> {\n return new Promise<number>((resolve, reject) => {\n const child = spawn(params.bin, params.argv, {\n cwd: params.cwd,\n env: { ...process.env, ...params.env },\n // stdin inherited so Next's own key handling works; stdout/stderr piped\n // so we can route them and keep OUR stdout reserved for the envelope.\n stdio: ['inherit', 'pipe', 'pipe'],\n });\n\n child.stdout?.on('data', (chunk: Buffer) => {\n params.tail.push(chunk);\n (params.ctx.jsonMode ? process.stderr : process.stdout).write(chunk);\n });\n child.stderr?.on('data', (chunk: Buffer) => {\n params.tail.push(chunk);\n process.stderr.write(chunk);\n });\n\n child.on('error', (error) => reject(error));\n child.on('close', (code) => resolve(code ?? 1));\n });\n}\n\n/**\n * The hostname to show a human. `127.0.0.1` is spelled `localhost` because\n * that is what people type; a wildcard bind is spelled `localhost` too, but\n * only after `exposed` has already been warned about — printing `0.0.0.0` as\n * a URL is not useful.\n */\nfunction displayHost(host: string): string {\n if (host === '127.0.0.1' || host === '0.0.0.0' || host === '::') {\n return 'localhost';\n }\n if (host === '::1') return '[::1]';\n return host;\n}\n\nfunction parseHost(raw: string | undefined): string {\n if (raw === undefined) return DEFAULT_HOST;\n\n const host = raw.trim();\n if (host.length === 0) {\n throw usageError(\n 'Invalid --host \"\".',\n `Pass an interface to bind, e.g. \"--host 0.0.0.0\" to expose the ` +\n `harness on your network, or omit it to bind ${DEFAULT_HOST}.`,\n );\n }\n return host;\n}\n\nfunction parsePort(raw: string | undefined): number {\n if (raw === undefined) return DEFAULT_PORT;\n\n const port = Number(raw);\n if (!Number.isInteger(port) || port < 1 || port > 65535) {\n throw usageError(\n `Invalid --port \"${raw}\".`,\n 'Pass an integer between 1 and 65535, e.g. \"--port 3100\".',\n );\n }\n return port;\n}\n\ninterface DevProject {\n projectDir: string;\n integrations: Array<{ slug: string; entry: string }>;\n primary: { slug: string };\n project: { sourceGlobs?: string[] | undefined };\n}\n\nfunction experienceProject(projectDir: string): DevProject {\n const { slug } = readExperienceProject(projectDir);\n return {\n projectDir,\n // An experience registers no integration of its own with the harness; its\n // integrations come from ekanos.json through /__ekanos/api.\n integrations: [],\n primary: { slug },\n project: {},\n };\n}\n\nfunction repoProject(repo: RepoDiscovery): DevProject {\n return {\n projectDir: repo.root,\n integrations: [],\n primary: { slug: path.basename(repo.root) },\n // The shell's stylesheet scans integrations/ already; experiences/ is the\n // other half of a repo.\n project: { sourceGlobs: ['experiences/**/*.{ts,tsx}'] },\n };\n}\n\ninterface ExperienceStartup {\n lines: string[];\n overrides: Array<{ integration: string; path: string; via: 'file' | 'env' }>;\n}\n\n/**\n * Resolve every integration with the harness the project installed, print where each\n * came from, and refuse to start when an integration cannot be served (a pin the\n * resolver cannot satisfy is a startup failure, not a blank widget later).\n */\nasync function resolveExperienceIntegrations(\n ctx: CliContext,\n args: DevArgs,\n projectDir: string,\n): Promise<ExperienceStartup> {\n const env = args.env ?? process.env;\n const resolver = await (args.loadResolver ?? loadHarnessResolver)(projectDir);\n\n const catalog = await (\n args.fetchCatalog ?? (() => tryFetchCatalog(ctx, args, projectDir))\n )();\n writeSessionCatalog(projectDir, catalog);\n\n const contractsDir = args.contractsDir ?? env.EKANOS_CONTRACTS_DIR;\n const resolution = await resolver.resolveIntegrations({\n projectRoot: projectDir,\n env,\n ...(contractsDir ? { contractsDir: path.resolve(contractsDir) } : {}),\n catalog,\n });\n const lines = resolver.formatProvenanceLines(resolution as never);\n for (const line of lines) ctx.log(line);\n for (const notice of resolution.notices) ctx.warn(notice);\n\n if (resolution.errors.length > 0) {\n throw preconditionError(\n `${resolution.errors.length} integration${resolution.errors.length === 1 ? '' : 's'} could not be resolved: ${resolution.errors\n .map((e) => e.message)\n .join(' | ')}`,\n 'Fix the pin in ekanos.json (or run \"ekanos sync\"), or remove the bad override in ekanos.local.json, then re-run \"ekanos dev\".',\n );\n }\n return { lines, overrides: resolution.overrides };\n}\n\n/** The dev's Source catalog when a session exists; null (offline) otherwise. Never throws. */\nasync function tryFetchCatalog(\n ctx: CliContext,\n args: DevArgs,\n projectDir: string,\n): Promise<SourceCatalog | null> {\n try {\n const env = resolveAuthEnvironment(\n ctx,\n undefined,\n args.env ?? process.env,\n {\n projectDir,\n },\n );\n let session = requireSession(env);\n const resolved = await resolveSourceTarget(env, session, {\n source: undefined,\n dir: projectDir,\n });\n session = resolved.session;\n let result = await fetchSourceCatalog(env.host, session.accessToken, {\n source: resolved.source,\n });\n if (result.status === 'unauthorized') {\n session = await refreshStoredSession(env, session);\n result = await fetchSourceCatalog(env.host, session.accessToken, {\n source: resolved.source,\n });\n }\n return result.status === 'ok' ? result.catalog : null;\n } catch {\n ctx.log(\n ctx.ui.dim(\n 'No CLI session: integrations resolve offline (registry integrations fixtures-only, overrides unverified). Run \"ekanos login\" to enable develop-aware live mode.',\n ),\n );\n return null;\n }\n}\n"]}
@@ -0,0 +1,31 @@
1
+ import type { CliContext } from '../context.js';
2
+ import type { ExitCode } from '../exit-codes.js';
3
+ import type { FlagSpecs, ParsedArgs } from '../parse-argv.js';
4
+ /**
5
+ * The append-only registry of CLI commands added by the local
6
+ * integration/experience split. `index.ts` imports this file ONCE and merges
7
+ * every entry into its command table, flag specs, positionals and usage text,
8
+ * so lanes add commands here without touching `index.ts` again.
9
+ *
10
+ * Rules: one entry per line group, each under its own lane marker, never
11
+ * reordered. A lane edits only below its own marker.
12
+ */
13
+ export interface ExperienceCommandArgs {
14
+ flags: ParsedArgs['flags'];
15
+ positionals: readonly string[];
16
+ passthrough: readonly string[];
17
+ /** `--dir`, else the working directory. */
18
+ dir: string;
19
+ cwd: string;
20
+ env: Record<string, string | undefined>;
21
+ }
22
+ export interface ExperienceCommand {
23
+ name: string;
24
+ /** Usage lines for `ekanos --help`; continuation lines are indented 13 spaces. */
25
+ usage: readonly string[];
26
+ specs: FlagSpecs;
27
+ /** Names of positional arguments, in order. Default none. */
28
+ positionals?: readonly string[];
29
+ run(ctx: CliContext, args: ExperienceCommandArgs): Promise<ExitCode>;
30
+ }
31
+ export declare const EXPERIENCE_COMMANDS: readonly ExperienceCommand[];
@@ -0,0 +1,21 @@
1
+ import { addCommand } from './add.js';
2
+ import { buildCommand } from './build.js';
3
+ import { checkCommand } from './check.js';
4
+ import { codegenCommand } from './codegen.js';
5
+ import { contractCommand } from './contract.js';
6
+ import { newCommand } from './new.js';
7
+ import { syncCommand } from './sync.js';
8
+ export const EXPERIENCE_COMMANDS = [
9
+ // --- lane A (contract) ---
10
+ contractCommand,
11
+ // --- lane B (experience commands): append below this line ---
12
+ addCommand,
13
+ syncCommand,
14
+ codegenCommand,
15
+ checkCommand,
16
+ buildCommand,
17
+ // --- lane C (dev wiring): append below this line ---
18
+ // --- lane M1 (multi-package repo): append below this line ---
19
+ newCommand,
20
+ ];
21
+ //# sourceMappingURL=index-experience.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index-experience.js","sourceRoot":"","sources":["../../src/commands/index-experience.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC;AACnC,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC;AACnC,OAAO,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAC;AA+BrC,MAAM,CAAC,MAAM,mBAAmB,GAAiC;IAC/D,4BAA4B;IAC5B,eAAe;IAEf,+DAA+D;IAC/D,UAAU;IACV,WAAW;IACX,cAAc;IACd,YAAY;IACZ,YAAY;IAEZ,sDAAsD;IAEtD,+DAA+D;IAC/D,UAAU;CACX,CAAC","sourcesContent":["import type { CliContext } from '../context';\nimport type { ExitCode } from '../exit-codes';\nimport type { FlagSpecs, ParsedArgs } from '../parse-argv';\nimport { addCommand } from './add';\nimport { buildCommand } from './build';\nimport { checkCommand } from './check';\nimport { codegenCommand } from './codegen';\nimport { contractCommand } from './contract';\nimport { newCommand } from './new';\nimport { syncCommand } from './sync';\n\n/**\n * The append-only registry of CLI commands added by the local\n * integration/experience split. `index.ts` imports this file ONCE and merges\n * every entry into its command table, flag specs, positionals and usage text,\n * so lanes add commands here without touching `index.ts` again.\n *\n * Rules: one entry per line group, each under its own lane marker, never\n * reordered. A lane edits only below its own marker.\n */\nexport interface ExperienceCommandArgs {\n flags: ParsedArgs['flags'];\n positionals: readonly string[];\n passthrough: readonly string[];\n /** `--dir`, else the working directory. */\n dir: string;\n cwd: string;\n env: Record<string, string | undefined>;\n}\n\nexport interface ExperienceCommand {\n name: string;\n /** Usage lines for `ekanos --help`; continuation lines are indented 13 spaces. */\n usage: readonly string[];\n specs: FlagSpecs;\n /** Names of positional arguments, in order. Default none. */\n positionals?: readonly string[];\n run(ctx: CliContext, args: ExperienceCommandArgs): Promise<ExitCode>;\n}\n\nexport const EXPERIENCE_COMMANDS: readonly ExperienceCommand[] = [\n // --- lane A (contract) ---\n contractCommand,\n\n // --- lane B (experience commands): append below this line ---\n addCommand,\n syncCommand,\n codegenCommand,\n checkCommand,\n buildCommand,\n\n // --- lane C (dev wiring): append below this line ---\n\n // --- lane M1 (multi-package repo): append below this line ---\n newCommand,\n];\n"]}
@@ -32,6 +32,13 @@ export interface InitArgs {
32
32
  * prompts for it.
33
33
  */
34
34
  docs?: string[];
35
+ /** `integration` (default), `experience` or `repo`. */
36
+ kind?: string;
37
+ /**
38
+ * The project is a package of an Ekanos repo (`ekanos new`): the repo root
39
+ * owns the pnpm workspace file, so the package does not get its own.
40
+ */
41
+ inRepo?: boolean;
35
42
  /**
36
43
  * Test-only override for the clack wizard gate (`canWizard` in
37
44
  * `wizard.ts`) — never set from real CLI wiring. See `RunOptions` in
@@ -4,8 +4,10 @@ import { printBanner } from '../banner.js';
4
4
  import { copyTreeVerbatim } from '../copy-tree.js';
5
5
  import { SDK_DOCS_URL, docsPage } from '../docs-links.js';
6
6
  import { invalidStateError, usageError } from '../errors.js';
7
+ import { runInitExperience } from '../experience/init.js';
7
8
  import { mergeManifest, serializeManifest, } from '../merge-manifest.js';
8
9
  import { docEntryProblem, serializeProject, } from '../project.js';
10
+ import { runInitRepo } from '../repo/scaffold.js';
9
11
  import { classifyPath, readFileNoFollow, writeFileNoFollow } from '../safe-fs.js';
10
12
  import { readTemplate, templatesDir, toDisplayName } from '../templates.js';
11
13
  import { displayPath, pluralize } from '../ui.js';
@@ -101,6 +103,14 @@ const FILES = [
101
103
  */
102
104
  export async function runInit(ctx, args) {
103
105
  var _a;
106
+ if (args.kind !== undefined && args.kind !== 'integration') {
107
+ if (args.kind === 'repo')
108
+ return runInitRepo(ctx, args);
109
+ if (args.kind !== 'experience') {
110
+ throw usageError(`Unknown --kind "${args.kind}".`, 'Use "integration" (default), "experience" or "repo".');
111
+ }
112
+ return runInitExperience(ctx, args);
113
+ }
104
114
  const slug = args.slug;
105
115
  if (!slug) {
106
116
  throw usageError('init requires a slug.', 'Pass it explicitly: "ekanos init --slug <your-integration> --yes". ' +
@@ -159,6 +169,8 @@ export async function runInit(ctx, args) {
159
169
  const spinner = useWizard ? wizard.spinner() : null;
160
170
  spinner === null || spinner === void 0 ? void 0 : spinner.start(`Scaffolding "${slug}"…`);
161
171
  for (const { template, dest } of FILES) {
172
+ if (args.inRepo && dest === 'pnpm-workspace.yaml')
173
+ continue;
162
174
  const destPath = path.join(targetDir, dest);
163
175
  const kind = classifyPath(destPath);
164
176
  // A symlink is never "already exists": skipping would keep the plant, and