@neta-art/cohub 8.19.0 → 8.20.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.
@@ -1,7 +1,7 @@
1
1
  # Cohub App Runtime Guide
2
2
 
3
- This guide explains the Cohub SDK in a published App iframe or an explicitly
4
- configured standalone broker. New Apps should use `auth.authorize()`; see the
3
+ This guide explains the Cohub SDK in a published App iframe or a registered
4
+ standalone origin. New Apps should use `auth.authorize()`; see the
5
5
  [authorization contract](../../../docs/app-authorization.md). Legacy APIs below
6
6
  retain their original return types. Read this before building any
7
7
  App that calls Cohub capabilities from browser-side JavaScript.
@@ -67,13 +67,13 @@ These runtime-only APIs form the foundation; everything else is standard SDK:
67
67
  | API | What it does | Returns |
68
68
  |---|---|---|
69
69
  | `client.context()` | Asks the host for the App's identity | `{ app, space, viewer?, invocation?, shell?, permissions, capabilities?, mode? }` or `null` |
70
- | `client.auth.authorize({ target, scopes, reason?, alwaysAsk?, fallback? })` | Requests consent for an account, a Space, or a viewer-picked Space; silent when a grant already covers the scopes | `{ status: "granted", requestedTarget, target, resolution, grant }` \| `{ status: "cancelled" }` \| `{ status: "denied", code }` |
70
+ | `client.auth.authorize({ target, scopes, reason?, alwaysAsk?, fallback? })` | Requests consent for an account, a Space, or a viewer-picked Space; also silently grants approved read-only scopes on the trusted Shell Space | `{ status: "granted", requestedTarget, target, resolution, grant }` \| `{ status: "cancelled" }` \| `{ status: "denied", code }` |
71
71
  | `client.auth.request()` / `requestSpace()` / `requestCreateSpace()` | Legacy entry points, kept for older Apps | original shapes (`boolean`, `{ granted, space }`) |
72
72
  | `client.context().permissions.viewerGrants` | Render the viewer's current per-space grants | `{ spaceId, scopes }[]` |
73
73
  | `client.app.commerce.*` / `client.app.realtime.*` | Commerce and realtime, bound to the app's runtime identity | (see below) |
74
74
 
75
- > **Runtime requirement.** Use a published App iframe or explicitly configure
76
- > a broker. A standalone page without broker configuration has no runtime.
75
+ > **Runtime requirement.** Use a published App iframe or a standalone origin
76
+ > registered with Cohub. The SDK discovers the transport automatically.
77
77
  > Initialize `context()` before using APIs to discover Host capabilities.
78
78
 
79
79
  `client.app.onContextChanged(listener)` pushes a fresh context whenever the
@@ -117,19 +117,18 @@ The App is accessed as a standalone page (`window.parent === window`), e.g.
117
117
  a direct static-asset URL not wrapped in the Cohub iframe. The SDK opens a
118
118
  **popup window** to a Cohub auth-broker page to obtain tokens.
119
119
 
120
- - `client.context()` is **answered locally** by the SDK: it reports
121
- `mode: "broker"`, `space.id` is an **empty string `""`**, and viewer grants
122
- are unavailable (empty). Broker tokens stay in memory and are never restored
123
- from or written to `localStorage`.
124
- - `client.auth.authorize()` opens a popup to
125
- `${brokerOrigin}/app-auth?app=${appId}`.
120
+ - `client.context()` is **answered locally** by the SDK after anonymously
121
+ resolving the page Origin. It has the same shape as bridge context, including
122
+ App identity, Home Space, capabilities and App scopes. `shell.surface` is
123
+ `"broker"`; there is no active shell Space, session or turn.
124
+ - `client.auth.authorize()` opens the environment's trusted Cohub broker.
125
+ - Broker tokens stay in memory and are never restored from or written to
126
+ `localStorage`.
126
127
 
127
- > **Broker mode requires configuration.** You must pass `app: { brokerOrigin,
128
- > appId }` — or `app: { brokerOrigin, ownerUsername, spaceSlug, appSlug }`
129
- > when the appId isn't known yet — to `createCohubClient` for broker mode to
130
- > activate. Without it, a standalone page gets `ParentBridgeTransport` which
131
- > has no parent to talk to, so `context()` returns `null`. See
132
- > [Initialization recipe](#4-initialization-recipe).
128
+ > **No mode configuration is required.** `createCohubClient({ env })` uses
129
+ > bridge inside the Cohub shell and broker on a registered standalone Origin.
130
+ > Unregistered pages receive `null` from `context()`. Explicit `app` transport
131
+ > options remain available for legacy integrations and local debugging.
133
132
 
134
133
  ### Detecting the mode at runtime
135
134
 
@@ -242,9 +241,17 @@ requested Space.
242
241
 
243
242
  ### Requesting viewer grants
244
243
 
245
- Consent must be requested **from a user gesture** (a button click). It is
246
- silent when an existing grant already covers the scopes — the dialog only
247
- opens when something new is needed:
244
+ Normal consent must be requested **from a user gesture** (a button click).
245
+ A request whose explicit target is the trusted `ctx.shell.space.id` is also
246
+ silent for the read-only Shell scopes `space.view`, `file.view`,
247
+ `file.view.filtered`, `session.view`, `taskrun.view`, and `checkpoint.view`.
248
+ The Host decides this by comparing the request against the Space it is actually
249
+ showing, and the server still checks the viewer's current permission on that
250
+ Space. The API keeps its single `authorize` endpoint and is not told whether
251
+ consent was interactive, so there is no flag to trust or forge. Broker surfaces
252
+ keep the interactive flow. A Shell hosting an embedded App applies the same rule
253
+ on its behalf. Existing grants are read without extending their expiry. Any other target, mixed scope set, `pick-space`,
254
+ account data, writes, or actions keeps the interactive flow:
248
255
 
249
256
  ```js
250
257
  // 1. Target a known Space. The result names the Space that was actually
@@ -432,54 +439,22 @@ const client = createCohubClient({
432
439
  });
433
440
  ```
434
441
 
435
- ### Broker mode configuration (standalone pages only)
442
+ ### Automatic standalone mode
436
443
 
437
- If the App may be accessed as a standalone page (not inside the Cohub
438
- iframe), pass the `app` option so the SDK can fall back to broker mode:
444
+ Published public `file` and `directory` Apps may also be opened at a standalone
445
+ URL: the deployment's standalone host template with the App id substituted, for
446
+ example `https://<app-id>.apps.example.com`. Standalone origins are opt-in
447
+ against deployment configuration; without a template, Apps have no standalone
448
+ URL.
439
449
 
440
- ```js
441
- const client = createCohubClient({
442
- env: isDevApp ? "dev" : "prod",
443
- app: {
444
- brokerOrigin: isDevApp ? "https://dev.cohub.live" : "https://cohub.live",
445
- appId: "<your-published-app-id>",
446
- },
447
- });
448
- ```
449
-
450
- When inside the Cohub iframe, the SDK auto-detects bridge mode and ignores
451
- broker config. When standalone, it uses broker mode. **One codebase, both
452
- deployments.**
453
-
454
- #### Broker mode without a pre-known appId
455
-
456
- The `appId` is only generated at publish time, so you often cannot hardcode
457
- it while writing the App. In standalone deployments you can omit `appId` and
458
- instead pass the App's public **slug triple**. The SDK resolves the appId at
459
- runtime via the public `apps.getBySlug` API (anonymous, no auth required),
460
- caches it, and starts broker mode with it.
461
-
462
- All three values are known before publishing:
463
-
464
- - `appSlug` — the slug you chose when creating the App.
465
- - `ownerUsername` — the space owner's username (`cohub auth whoami`).
466
- - `spaceSlug` — the space's slug (`cohub spaces get <spaceId>`).
467
-
468
- ```js
469
- const client = createCohubClient({
470
- env: isDevApp ? "dev" : "prod",
471
- app: {
472
- brokerOrigin: isDevApp ? "https://dev.cohub.live" : "https://cohub.live",
473
- ownerUsername,
474
- spaceSlug,
475
- appSlug,
476
- },
477
- });
478
- ```
450
+ The same client initialization works in the Cohub iframe and at this direct
451
+ URL. The SDK first uses the parent bridge when embedded; at a top-level page it
452
+ anonymously resolves `window.location.origin` and selects the trusted broker.
453
+ The resolver returns identity only and never returns a token.
479
454
 
480
- Either `appId` or the full slug triple is enough to activate broker mode. If
481
- you pass both, the explicit `appId` wins and no lookup is performed. Inside
482
- the Cohub iframe both are ignored (bridge mode).
455
+ Explicit `app: { brokerOrigin, appId }` and slug-triple configuration remains
456
+ supported for older externally hosted Apps, but new Apps should not declare a
457
+ mode or hardcode their App id.
483
458
 
484
459
  ### Standard initialization sequence
485
460
 
@@ -487,8 +462,8 @@ the Cohub iframe both are ignored (bridge mode).
487
462
  // 1. Create client (env is mandatory in the browser)
488
463
  const client = createCohubClient({ env: isDevApp ? "dev" : "prod" });
489
464
 
490
- // 2. Get runtime context (and keep it fresh). `app` is present in bridge and
491
- // broker mode; a Space id is only reported by the bridge.
465
+ // 2. Get runtime context (and keep it fresh). `app` and its Home Space are
466
+ // present in bridge and broker mode; shell location exists only when hosted.
492
467
  const ctx = await client.context();
493
468
  if (!ctx?.app?.id) {
494
469
  throw new Error("Not running inside a published app.");
@@ -516,10 +491,11 @@ const activeSpace = client.space(consent.target.spaceId);
516
491
  const result = await activeSpace.prompt({ content: [{ type: "text", text: "Hello" }] });
517
492
  ```
518
493
 
519
- > **`auth.authorize()` must be called from a user gesture** (click handler).
520
- > Browsers block popups (broker mode) and some consent flows (bridge mode)
521
- > when triggered programmatically without user activation. Do not call it on
522
- > page load. It is safe to call repeatedly: covered scopes renew silently.
494
+ > **Normal interactive `auth.authorize()` calls must use a user gesture** (click
495
+ > handler). The approved Shell read-only path may run during initialization in
496
+ > bridge mode. Broker mode has no trusted Shell Space, so it keeps the normal
497
+ > popup and gesture requirements. `alwaysAsk: true` always returns to the
498
+ > interactive flow.
523
499
 
524
500
  ---
525
501
 
@@ -1286,9 +1262,8 @@ Before publishing your App, verify each item:
1286
1262
  - [ ] **`subscribeGeneration` errors are not silently swallowed** — if the
1287
1263
  stream fails, surface it; a silent fallback to polling will also 403 if
1288
1264
  `session.view` is missing.
1289
- - [ ] **Broker mode**: if the App may be accessed standalone, pass
1290
- `app: { brokerOrigin, appId }` and call `auth.authorize()` before any other
1291
- API call (to avoid user-activation exhaustion).
1265
+ - [ ] **Broker mode**: initialize `context()` early so Origin discovery is warm,
1266
+ then call `auth.authorize()` from a user action before protected API calls.
1292
1267
  - [ ] **Space has a slug and owner has a username** before publishing — the
1293
1268
  API rejects Apps when either is missing.
1294
1269
  - [ ] **Model ids are not hardcoded** — use `client.models.listMultimodal()`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neta-art/cohub",
3
- "version": "8.19.0",
3
+ "version": "8.20.1",
4
4
  "description": "Cohub SDK for spaces, sessions, boards, and realtime agent collaboration.",
5
5
  "license": "Apache-2.0",
6
6
  "private": false,