@ekanos/cli 0.1.3 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -29,6 +29,7 @@ export const integration = defineIntegration<MyStorage>({
29
29
  tools: [ /* assistant-callable functions */ ],
30
30
  webhooks: [ /* inbound deliveries */ ],
31
31
  schedules: [ /* cron runs */ ],
32
+ onActivate: async (ctx) => { /* seed caches, sanity-check credentials */ },
32
33
  storage: { account: { /* key → zod schema */ } },
33
34
  egress: ['https://api.example.com'],
34
35
  });
@@ -45,7 +46,7 @@ lives in both files; `ekanos validate` reports a finding if they disagree.
45
46
  ## The capability context (`ctx`) — the ONLY platform surface
46
47
 
47
48
  Every server-side handler (tool `run`, webhook `handler`, schedule `handler`,
48
- `oauth.onTokens`) receives one `ctx` object, already scoped to
49
+ `oauth.onTokens`, `onActivate`) receives one `ctx` object, already scoped to
49
50
  `{account, integration}` before your code runs.
50
51
 
51
52
  **Hard rule: NEVER use global `fetch`, `process.env`, or `node:fs` in
@@ -148,6 +149,37 @@ failure). Schedule results: `{ status: 'completed' }` / `{ status: 'skipped' }`
148
149
  (`invocation.trigger` distinguishes them). Cron is 5-field numeric only — no
149
150
  names, no `@daily`, no seconds.
150
151
 
152
+ ### `onActivate`
153
+
154
+ Runs once after an activation is first persisted, and again after
155
+ activationData is updated — **seed the cache in `onActivate`; the schedule
156
+ keeps it fresh.** Without it, a cache-backed widget is empty until the first
157
+ schedule tick. v1 errors are non-fatal: a throw is logged as a warning and the
158
+ activation stays connected, so this is for cache seeding and eager
159
+ validation, never a connect gate. Seed BEST-EFFORT — swallow what you can
160
+ name, log loudly what you can't: catch the upstream error classes you can
161
+ reason about (timeouts, 5xx, provider errors) and warn; log anything
162
+ unrecognized as an error and return, so a genuine bug stays visible in logs
163
+ without telling a user their brand-new connection failed. Reserve the throw
164
+ for misconfiguration the user can actually fix. Treat onActivate as an
165
+ OPTIMIZATION, never a correctness dependency: the schedule remains the
166
+ freshness guarantee, and a config fingerprint keeps the stale window safe
167
+ whether or not the hook fires.
168
+
169
+ Troubleshooting `ekanos dev`: adding/removing/re-adding handler modules can
170
+ leave the harness's Turbopack cache resolving a deleted path ("Module not
171
+ found" for a file that exists). Clear it with `rm -rf .ekanos/harness/.next`
172
+ and restart.
173
+
174
+ A cache invalidated only by age still serves the *previous* activation's data
175
+ for a while even with `onActivate` wired up. Close that gap with a
176
+ fingerprint: store a hash of the activation fields a cached value's **values**
177
+ depended on (not just the ones that pick which data to fetch — currency, units
178
+ and locale change the numbers without changing the cache key) alongside the
179
+ value, and treat a mismatch or an absent fingerprint on read as a miss.
180
+ Declare that field `.optional()` on the storage schema, or every pre-existing
181
+ row throws `StorageValidationError` on read.
182
+
151
183
  ## Widgets
152
184
 
153
185
  Dashboard widgets are client components (`'use client'`) declared at
@@ -168,8 +200,17 @@ system. Available modules (import individually, e.g. `@ekanos/ui/button`):
168
200
  `switch`, `textarea`, `tooltip`, `trans`, `utils` (the `cn()` helper),
169
201
  `ai-prompt-input`, plus the stylesheets `styles.css` / `tokens.css` /
170
202
  `theme.css` / `base.css`. Icons are Font Awesome glyph names via
171
- `@ekanos/ui/icon`; outside a host that loads Font Awesome they render as
172
- nothingexpected, not a bug.
203
+ `@ekanos/ui/icon`. This generated shell loads Font Awesome **Free**
204
+ (`@fortawesome/fontawesome-free`)a much smaller set than the Pro kit Fusion
205
+ itself runs — so a name that's Pro-only (or just mistyped) renders a
206
+ circle-question disc instead of vanishing, and dev builds `console.warn` once
207
+ per name to catch it before a screenshot review does. `fa-light` / `fa-duotone`
208
+ / `fa-thin` are rewritten to `fa-solid`, the one weight Free ships, so author
209
+ `fa-light` anyway — it degrades harmlessly today, and the intended weight
210
+ survives if this host ever loads Pro instead. Verify a glyph name against the
211
+ Free set (`pnpm --filter @ekanos/ui check:fa-free-names`, or
212
+ https://fontawesome.com/search?o=r&s=solid,regular,brands) before relying on
213
+ it looking like anything in particular.
173
214
 
174
215
  ## Testing
175
216
 
@@ -215,20 +256,40 @@ carries an imperative `hint`; treat hints as remediation instructions.
215
256
  | `ekanos test` | Run this project's test script through its package manager |
216
257
  | `ekanos login --host <url>` | Device-flow login to a Fusion deployment (stores the host for later verbs) |
217
258
  | `ekanos status` | Login state for the resolved host + this project's submissions |
218
- | `ekanos publish` | Validate, pack (whitelist: ekanos.json, package.json, README.md, src/), and submit. Refuses on any error-severity finding |
219
-
220
- Host resolution for `logout`/`whoami`/`status`/`publish`: `--host` flag
221
- `EKANOS_HOST` → the `host` field in ekanos.json → the sole stored login. The
222
- first successful `publish` saves `host` and `source` into ekanos.json.
223
- `login` is the exception it never reads ekanos.json for a host (`--host` →
224
- `EKANOS_HOST` the sole stored login only), since it is the verb that
225
- creates credentials and a committed file must not be able to redirect it.
259
+ | `ekanos sources` | List every Fusion source you hold a developer/admin seat on, marking this project's current target |
260
+ | `ekanos use <source-slug>` | Set this project's publish target — refuses (`forbidden`) a slug you hold no seat on |
261
+ | `ekanos publish` | Validate, pack (whitelist: ekanos.json, package.json, README.md, src/), verify the target, and submit. Refuses on any error-severity finding |
262
+
263
+ Host resolution for `logout`/`whoami`/`status`/`sources`/`use`/`publish`:
264
+ `--host` flag → `EKANOS_HOST` the `host` field in ekanos.json the sole
265
+ stored login. The first successful `publish` saves `host` and `source` into
266
+ ekanos.json. `login` is the exception it never reads ekanos.json for a host
267
+ (`--host` → `EKANOS_HOST` → the sole stored login only), since it is the verb
268
+ that creates credentials and a committed file must not be able to redirect it.
269
+
270
+ ### Publish targeting — do not skip this if you hold more than one seat
271
+
272
+ `publish` verifies its target BEFORE packing or submitting anything: it fetches
273
+ your seat list and refuses (`forbidden`, exit 5) a source you hold no seat on,
274
+ naming your actual seats in the hint. If the target came from ekanos.json
275
+ (not `--source` this run) and you hold more than one seat, a scripted/agent
276
+ run (JSON mode, which is what running under an agent means) is refused with
277
+ `validation` (exit 3) and the seat list **unless `--yes` is passed** — publish
278
+ never guesses and never prompts in this mode.
279
+
280
+ **As an agent: never pass `--yes` to paper over that ambiguity.** Run
281
+ `ekanos sources` first and pass `--source <slug>` explicitly, or run
282
+ `ekanos use <slug>` once to set the project's target deliberately. `--yes` is
283
+ for a human who already confirmed the target, or a pipeline pinned to one
284
+ source — not a way to silence the check when you are unsure.
226
285
 
227
286
  Exit codes (frozen contract — branch on these): `0` ok, `1` internal, `2`
228
- usage, `3` validation, `4` auth required (run `ekanos login`), `5` forbidden,
229
- `6` not found, `7` invalid state (e.g. version already submitted bump
230
- `package.json#version`), `8` network (retry, do NOT re-login), `9`
231
- precondition failed, `10` publish gate failed (fix `data.findings`).
287
+ usage, `3` validation (also: ambiguous publish target pass `--source` or
288
+ `--yes`), `4` auth required (run `ekanos login`), `5` forbidden (also: no seat
289
+ on the target source — check `ekanos sources`), `6` not found, `7` invalid
290
+ state (e.g. version already submitted bump `package.json#version`), `8`
291
+ network (retry, do NOT re-login), `9` precondition failed, `10` publish gate
292
+ failed (fix `data.findings`).
232
293
 
233
294
  The loop: `ekanos dev` → edit → `npx tsc --noEmit` → `ekanos validate` →
234
295
  `ekanos test` → `ekanos publish`.
@@ -18,8 +18,14 @@ root. Read it before writing integration code. The definition is one
18
18
  3. `npx tsc --noEmit` — types clean.
19
19
  4. `npx ekanos validate --json` — findings clean.
20
20
  5. `npm test` — green.
21
- 6. `npx ekanos publish` submits to the Fusion host (refuses while any
22
- error-severity finding remains).
21
+ 6. **Before publishing, know your target.** If you hold more than one
22
+ developer seat, run `npx ekanos sources --json` and pass the intended
23
+ `--source <slug>` explicitly to `publish`. Never pass `--yes` to get past
24
+ an ambiguous-target error you have not actually resolved — `--yes` is
25
+ consent to a target you already chose, not a way to silence the check.
26
+ 7. `npx ekanos publish --source <slug>` — submits to the Fusion host (refuses
27
+ while any error-severity finding remains, and refuses `forbidden` if you
28
+ hold no seat on that source).
23
29
 
24
30
  ## Reading CLI output
25
31
 
@@ -32,22 +38,37 @@ remediation instructions and follow them verbatim.** `ekanos validate` puts
32
38
  findings in `data.findings`; `ekanos publish` refuses with exit 10 and the
33
39
  same findings in `data.findings`.
34
40
 
35
- Exit codes to branch on: `0` ok · `2` usage · `3` validation · `4` run
36
- `ekanos login` · `7` version already submitted (bump `package.json#version`) ·
41
+ Exit codes to branch on: `0` ok · `2` usage · `3` validation (also: ambiguous
42
+ publish target pass `--source` or `--yes`, never guess) · `4` run
43
+ `ekanos login` · `5` forbidden (also: no seat on the target source — run
44
+ `ekanos sources`) · `7` version already submitted (bump `package.json#version`) ·
37
45
  `8` network (retry — do NOT re-login) · `9` precondition · `10` fix findings,
38
46
  publish again.
39
47
 
40
48
  ## Hosts and sessions
41
49
 
42
50
  `ekanos login --host <url>` stores a session; after that, `logout`/`whoami`/
43
- `status`/`publish` resolve the host from `--host` → `EKANOS_HOST` →
44
- ekanos.json's `host` field → the sole stored login. `login` itself never reads
45
- ekanos.json for a host — only `--host`, `EKANOS_HOST`, or the sole stored
46
- login — since it is the verb that creates credentials. `ekanos status` shows
47
- who you are logged in as and what this project has submitted. The first
48
- successful publish saves `host` and `source` into ekanos.json.
51
+ `status`/`sources`/`use`/`publish` resolve the host from `--host` →
52
+ `EKANOS_HOST` → ekanos.json's `host` field → the sole stored login. `login`
53
+ itself never reads ekanos.json for a host — only `--host`, `EKANOS_HOST`, or
54
+ the sole stored login — since it is the verb that creates credentials.
55
+ `ekanos status` shows who you are logged in as and what this project has
56
+ submitted. The first successful publish saves `host` and `source` into
57
+ ekanos.json.
58
+
59
+ ## Publish targets, when you hold more than one seat
60
+
61
+ `ekanos sources` lists every source you hold a seat on; `ekanos use <slug>`
62
+ sets this project's saved target (seat-checked — refuses a slug you don't
63
+ hold). `publish` verifies its resolved target against your seats before
64
+ packing anything, and — running as an agent, which is JSON/non-interactive
65
+ mode — never prompts: an ambiguous implicit target (ekanos.json, more than one
66
+ seat) is refused with the seat list unless you pass `--yes`. **Always resolve
67
+ the ambiguity yourself** (pick the right `--source`) rather than reaching for
68
+ `--yes` on a target you have not verified.
49
69
 
50
70
  ## Before claiming done
51
71
 
52
72
  Run steps 3-5 above and confirm all three are clean — a publish is only as
53
- good as the validate that precedes it.
73
+ good as the validate that precedes it. If publishing, confirm you named the
74
+ right `--source` rather than relying on `--yes`.