@ekanos/cli 0.2.0 → 0.2.2

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.
package/package.json CHANGED
@@ -1,7 +1,6 @@
1
1
  {
2
2
  "name": "@ekanos/cli",
3
- "version": "0.2.0",
4
- "private": false,
3
+ "version": "0.2.2",
5
4
  "type": "module",
6
5
  "description": "The Ekanos partner toolchain CLI: scaffold, validate, and test a Fusion integration against the published @ekanos packages. Agent-native — every verb speaks JSON with a stable exit-code taxonomy.",
7
6
  "license": "MIT",
@@ -34,7 +33,7 @@
34
33
  },
35
34
  "dependencies": {
36
35
  "@clack/prompts": "1.7.0",
37
- "@ekanos/integration-schema": "0.1.5",
36
+ "@ekanos/integration-schema": "0.2.0",
38
37
  "esbuild": "0.28.1",
39
38
  "picocolors": "1.1.1"
40
39
  },
@@ -212,9 +212,15 @@ your slug (`__SLUG__-<name>`). `widgetState` (usually `'active'`), plus
212
212
 
213
213
  Render through the `Widget.*` compound API and `WidgetContext` from
214
214
  `@ekanos/sdk/components`; wrap in `WidgetPreviewProvider` to render outside
215
- the host. There is **no client-side `ctx.fetch`** — a widget reads declared
216
- `clientReadable` account-scope storage back with `fetchIntegrationStorage()`
217
- from `@ekanos/sdk/hooks`, and anything else needs your own seam.
215
+ the host. Widgets talk to a vendor with `useIntegrationFetch()` from
216
+ `@ekanos/sdk/hooks` — never global `fetch` — and reach for `useActivationData()`
217
+ (same module) when a widget needs a client-visible credential or activation
218
+ field. Both have a safe default when nothing has mounted a provider (a plain
219
+ fetch wrapper, and `null`, respectively), so calling them never crashes; see
220
+ "Network model" below for what that fetch is actually gated by, in the
221
+ harness and in production. A widget reads declared `clientReadable`
222
+ account-scope storage back with `fetchIntegrationStorage()` from
223
+ `@ekanos/sdk/hooks`.
218
224
 
219
225
  Build widget UI from `@ekanos/ui` — the published slice of the host design
220
226
  system. Available modules (import individually, e.g. `@ekanos/ui/button`):
@@ -240,11 +246,13 @@ it looking like anything in particular.
240
246
  The harness (`npx ekanos dev`) mocks FUSION, not your vendor. **You do not
241
247
  need mock data to develop against the real API**, and never claim otherwise:
242
248
  the dev toolbar has a fixtures ⇄ live switch per integration, and in live
243
- mode your widgets' real query functions run and their requests reach the
244
- real vendor API. The only gate is the `egress` allowlist from
245
- `defineIntegration()` — enforced locally by the same matcher production
246
- uses, so an origin you forgot to declare fails on this machine with
247
- `EgressDeniedError` instead of in review.
249
+ mode your widgets' real query functions — calling `useIntegrationFetch()` —
250
+ run and their requests reach the real vendor API. The gate is the `egress`
251
+ allowlist from `defineIntegration()`, checked with the same matcher
252
+ production's `ctx.fetch` uses, so an origin you forgot to declare fails on
253
+ this machine with `EgressDeniedError` instead of in review — see "Network
254
+ model" below for what that check does and does not mean once you're not in
255
+ the harness anymore.
248
256
 
249
257
  Live mode is opt-in per integration. Add a `live` block to the entry in
250
258
  `harness.config.ts`:
@@ -269,6 +277,30 @@ webhook/schedule/onActivate handlers against the mock context (its
269
277
  `ctx.fetch` never touches the network) — cover handler network behavior
270
278
  with fixtures in tests, then for real on the dev host after publishing.
271
279
 
280
+ ### Network model
281
+
282
+ Two different fetches, two different guarantees:
283
+
284
+ - **`ctx.fetch` (handlers — `onActivate`/webhooks/schedules/tools).** Real
285
+ enforcement, in the harness AND in production: your `egress` allowlist,
286
+ redirect re-vetting, IP pinning. Locally, handlers only ever run on
287
+ Triggers/Activate against the mock context, so their `ctx.fetch` never
288
+ leaves your machine regardless of the toolbar — real network only after
289
+ you publish to a dev host.
290
+ - **`useIntegrationFetch()` (widgets, browser-side).** In the harness, live
291
+ mode routes it through the same egress check as a convenience, enforced
292
+ centrally rather than something you configure per widget. **That check
293
+ does not exist in production** — a browser can't pin an address or
294
+ re-vet a redirect the way `ctx.fetch` does, and today's host mounts no
295
+ egress check on the client path at any layer. Do not read
296
+ `useIntegrationFetch()`'s live-mode behavior as "the same protection as
297
+ `ctx.fetch`, just in the browser" — that is the one thing this section
298
+ means to rule out.
299
+
300
+ In short: widgets get real network in Live API mode, gated by convenience in
301
+ the harness and by nothing in production; handlers get fixtures locally and
302
+ the real, enforced thing only after publish.
303
+
272
304
  ## Testing
273
305
 
274
306
  `@ekanos/sdk/testing` builds the same context production uses, in memory —
@@ -71,8 +71,11 @@ hierarchy, and no amount of padding will save it.
71
71
 
72
72
  Every widget: `'use client'`, `Widget.DataState` wrapping `Widget.Card` →
73
73
  `Widget.Header` + `Widget.Loading` + `Widget.Error` + `Widget.Active`. Gate
74
- fetching on `ctx?.state.state === 'active'` from `WidgetContext`. Copy the
75
- canonical example from the SDK README rather than improvising the tree.
74
+ fetching on `ctx?.state.state === 'active'` from `WidgetContext`, and fetch
75
+ with `useIntegrationFetch()` from `@ekanos/sdk/hooks` — never global
76
+ `fetch` — reaching for `useActivationData()` (same module) if the widget
77
+ needs a client-visible credential. Copy the canonical example from the SDK
78
+ README rather than improvising the tree.
76
79
 
77
80
  `Widget.Active` itself renders flush (no padding) so full-bleed charts and
78
81
  tables can run edge-to-edge. For ordinary content — text, rows, a KPI