Skip to content

Commit 7db33ae

Browse files
committed
feat(skills): add interchange-embed-hub and rewrite client-apps around app-driven setup
1 parent 3cd4929 commit 7db33ae

6 files changed

Lines changed: 82 additions & 55 deletions

File tree

‎docs/PRODUCT.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ the file path and parse details.
107107

108108
The TUI has an extensible slash-command framework. Built-ins include `/help` (shortcut + command overlay), `/model` (models-only picker for connected accounts; **Alt+A** or `/connect` adds a provider), `/settings`, `/permissions`, `/plugins`, `/clear`, `/new`, `/compact` (fold conversation context now, optional trailing instructions to the summarizer; does not wait for the 60% occupancy governor; idle success shows the fold and does not start a new turn), `/mcp` (enable, disable, or remove servers), `/handoff [optional instructions]` (folds context through the shared operator pipeline, then immediately starts the next turn with the instructions as the inbound content — default copy when omitted; unlike `/compact`, which stops after the fold, handoff always re-infers, so the operator can pivot goals without `/clear`; a handoff issued mid-tool-batch queues behind the in-flight batch and whichever boundary fires first runs the single fold), and `/yolo` (`/yolo [on|off|toggle]`, bare `/yolo` toggles), plus a `/<name>` command per available workflow. `/yolo` persists skip-permissions to the active settings file. That file is the user-global `~/.corbits/settings.json` by default, making the setting machine-wide; explicit `--config <path>` selects a different active file, and `/yolo` writes that file. An ordinary TUI launch without the same `--config` returns to the user-global source and does not modify the custom file. `--dangerously-skip-permissions` and `--yolo` are process-only aliases. Secret-guard and authz still apply. When a session starts with its active persisted setting already on, the TUI and `corbits exec` warn that permission prompts are disabled by saved settings at the active settings path and direct the operator to edit that file to re-enable them. Plugins can register additional commands.
109109

110-
**Default skills** exist out of the gate as first-party slash **actions**, not director names: `/implement`, `/plan`, `/refactor`, `/review`, `/pull-request`, `/issue`, `/docs`, `/interview`. Each one is a how-to playbook — the slash sends the skill body to the primary, which follows the steps. Skills do not assign identity or route the fleet; that stays on director system prompts. Background reference skills (`typescript`, `git-worktrees`, and the `interchange` hub with its `interchange-agents`, `interchange-workflows`, `interchange-run-modes`, and `interchange-client-apps` references) have no slash; the model loads them with `skill_search` and `use_skill`, and each one ends with "need more, go here" pointers to the next skill in the chain. `/review` classifies the target first, then dispatches a selected fleet; `/pull-request` finds or opens the PR for the current branch, and `/review` takes a PR target and reviews it from a worktree; `/docs` is how to maintain PRODUCT / ARCHITECTURE / IMPLEMENTATION; `/implement` takes a plan and a ticket to a pushed branch (worktree, per-commit planner/build/reviewer loop, whole-branch review, push, hand off to `/pull-request`) — it does not steal planning from `/plan`. Substantial coder work consumes a planner / `/plan` plan first; tiny parent-DIY stays plan-optional. `/plan` authors an eng change plan (files, AC, non-goals, risks, ordered steps) and does not implement. `/issue` finds or creates the tracker issue: Linear MCP when available; otherwise it `ask_operator`s for the platform (GitHub etc.) and persists `Preferred issue tracker` in `.corbits/MEMORY.md` (GitHub via `gh issue create`). There is no first-party dispatch skill: the dispatch director orchestrates natively. `typescript` stays `use_skill` only (`user-invocable: false`); `git-worktrees` is a background library that is not listed for `use_skill`. Designer and the other specialists are not slashes; they remain closed directors via `spawn_agent(agent=…)`. There is no catch-all worker. Slash names are also available to the model via `skill_search` (descriptions) then `use_skill` (body). Disable the catalog in `/plugins` (`corbits-skills`) if you want them gone.
110+
**Default skills** exist out of the gate as first-party slash **actions**, not director names: `/implement`, `/plan`, `/refactor`, `/review`, `/pull-request`, `/issue`, `/docs`, `/interview`. Each one is a how-to playbook — the slash sends the skill body to the primary, which follows the steps. Skills do not assign identity or route the fleet; that stays on director system prompts. Background reference skills (`typescript`, `git-worktrees`, and the `interchange` hub with its `interchange-agents`, `interchange-workflows`, `interchange-run-modes`, `interchange-embed-hub`, and `interchange-client-apps` references) have no slash; the model loads them with `skill_search` and `use_skill`, and each one ends with "need more, go here" pointers to the next skill in the chain. `/review` classifies the target first, then dispatches a selected fleet; `/pull-request` finds or opens the PR for the current branch, and `/review` takes a PR target and reviews it from a worktree; `/docs` is how to maintain PRODUCT / ARCHITECTURE / IMPLEMENTATION; `/implement` takes a plan and a ticket to a pushed branch (worktree, per-commit planner/build/reviewer loop, whole-branch review, push, hand off to `/pull-request`) — it does not steal planning from `/plan`. Substantial coder work consumes a planner / `/plan` plan first; tiny parent-DIY stays plan-optional. `/plan` authors an eng change plan (files, AC, non-goals, risks, ordered steps) and does not implement. `/issue` finds or creates the tracker issue: Linear MCP when available; otherwise it `ask_operator`s for the platform (GitHub etc.) and persists `Preferred issue tracker` in `.corbits/MEMORY.md` (GitHub via `gh issue create`). There is no first-party dispatch skill: the dispatch director orchestrates natively. `typescript` stays `use_skill` only (`user-invocable: false`); `git-worktrees` is a background library that is not listed for `use_skill`. Designer and the other specialists are not slashes; they remain closed directors via `spawn_agent(agent=…)`. There is no catch-all worker. Slash names are also available to the model via `skill_search` (descriptions) then `use_skill` (body). Disable the catalog in `/plugins` (`corbits-skills`) if you want them gone.
111111

112112
Providers are **models-first**: there is no standalone `/login` command. `/model` opens a **models-only list** (Recent, Favorites, then connected provider/model rows) — type-to-filter owns printable keys, so Connect is never a bare letter. **Alt+A** or `/connect` opens a dedicated add-provider selector over every first-class kind (OpenAI dual-path ChatGPT OAuth or API key, xAI, OpenCode Zen, Anthropic, Google, OpenCode Go, Z.AI Coding Plan, Ollama, Custom), each annotated with its live account count and never filtered out for “already connected.” **Alt+F** toggles favorite on the highlighted model. **Alt+D** persists the highlighted pair as the default without switching the live session. Advanced provider drill-down (edit/delete/tiers) stays on the advanced surface, not a bare printable key while the model list is filtering. OAuth providers open their existing browser login with a named account step so multiple accounts per kind coexist (`codex/work`, …). API-key providers use the same named-instance step before the key (auth-only form: instance name + key + fixed catalog base URL), so personal and team keys land as distinct catalog rows (`openai/default`, `anthropic/work`, …); reusing a name re-keys that instance after confirm. Custom remains a free-form single endpoint (full manual form). Successful connect refreshes the catalog and reopens the model list focused on the new account’s default model. OpenCode Go lists models from the live `/zen/go/v1/models` catalog (packaged seed on fetch failure), routes each by its protocol metadata (chat completions, OpenAI responses, or Anthropic messages) and can show subscription usage in the status bar when active (rolling 5h / weekly / monthly windows when the usage API responds; omitted on auth or network failure). When Go returns a quota or rate-limit error — including some HTTP 400 responses that carry limit payloads — Corbits classifies them so quota aborts cleanly and short provider rate limits remain retryable. On a free-tier or subscription quota hit, wait for the window to reset or use OpenCode Zen free models.
113113

Lines changed: 27 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,69 +1,44 @@
11
---
22
name: interchange-client-apps
3-
description: Build a web or mobile client that deploys, triggers, and watches workflows on an Interchange hub with @intx/hub-client.
3+
description: Build a client-driven app that sets up its own Interchange hub (tenants, auth, grants, providers, installs, deploys, runs) using stock @intx and @corbits packages.
44
user-invocable: false
55
---
66

7-
# Interchange client apps
7+
# Client-driven apps on Interchange
88

9-
A client-driven app talks to a hub over HTTP. The hub owns tenants, grants, credentials, and deploys. The client never talks to a sidecar.
9+
A client-driven app drives the whole setup. The hub boots vanilla (migrate, mount, serve). The app signs in, creates its tenants, grants, providers, and assets, deploys its workflows, and runs them, all over the hub's HTTP API using stock `@intx/*` and `@corbits/*` packages. Nothing about the product is baked into the hub.
1010

11-
## Pieces
11+
The flagship example is Corbits Solution Builder (`corbitsdev/corbits-solution-builder`). Its architecture note says it directly: the client runs the installer over the hub on first launch and after every credential change. Read `packages/installer/src/*.ts` first. Workbench (`corbitsdev/workbench`) is the server-composition reference.
1212

13-
- `@intx/hub-client`: browser-side `Transport`, run session, and workflow helpers. It is side-effect free at import. `apps/admin-ui` upstream is the reference client.
14-
- `@intx/hub-api`: the hub's Hono app. Mount extra routes on the same auth stack with `createRequireGrant`.
15-
- `@intx/hub-agent` is sidecar-side, not a client SDK.
16-
- API reference: the hub serves `GET /openapi.json`, and upstream `docs/API.md` is generated.
13+
## The shape
1714

18-
## Auth and URLs
15+
- **Host** (once per launch): picks embedded or remote hub, opens the database, migrates, mints keys, mounts the hub, starts sidecar reconcilers. See `use_skill interchange-embed-hub`.
16+
- **Client** (browser or script): everything else, through one `Transport` (`fetch(method, path, body)` plus `subscribe`).
17+
- Keep an `install()` that is idempotent (list-then-create, tolerate 409). Run it on first launch and after changes. Solution Builder keeps it free of any database import.
1918

20-
Sign in with better-auth (cookie session, same origin). Tenant is always in the path: `/api/tenants/:tenantId/...`. Cross-tenant user views live under `/api/me/...`. Scripts that need git access use hub-issued `itx_pat_*` or `itx_svc_*` tokens.
19+
## Bootstrap recipe (client side)
2120

22-
## Deploy, trigger, watch
21+
1. **Auth.** better-auth, email and password. `POST /api/auth/sign-up/email {name,email,password}`, on 422 `POST /api/auth/sign-in/email`. Replay the `better-auth.session_token` cookie. The REST API has no bearer or service token, so an app that acts as "system" holds a user account and keeps its cookie. A desktop app can mint one fixed local owner with a keychain-held password.
22+
2. **Tenant.** `POST /api/tenants {name, slug, parentId?}`. The caller becomes the owner principal with `*`/`*` allow. Find existing ones via `GET /api/me` and `GET /api/me/principals`. A child tenant (`parentId`) per project inherits the parent's catalog, assets, and placement policy. Do not treat the hierarchy as a trust boundary yet: the create route does not appear to check the caller's rights on `parentId` (unverified).
23+
3. **Roles and grants.** `POST /roles`, `POST /grants {roleId|principalId, resource, action, effect, origin}`, `POST /principals/:p/roles/:r`. Model app authorities as grants such as `authority:<name>` / `hold`. An invited member must be PATCHed to `active` before the tenant accepts them.
24+
4. **Inference.** In order: `POST /providers` (set `apiBaseUrl`), `POST /credentials`, `POST /catalog/models`, `POST /catalog/providers` (bound to the credential), `POST /catalog/offerings`. A model provider's credential cannot be repointed, so rotate the same credential id. The offering ids are the failover chain you give each deploy.
25+
5. **Install code.** `POST /assets {kind:"workflow"|"skill"|"package-registry", name}`. Push source through a git token (`POST /git-tokens`, `can_read` and `can_push`, short expiry, revoke after). Poll until the push is visible before deploying.
26+
6. **Deploy.** `deployWorkflow(transport, tenantId, { source: { kind: "asset", assetId, package: { format: "source", commitSha } }, entry, sourceOfferingIds, defaultSourceOfferingId })`. The chain freezes at deploy, so a model switch is a new deployment. A deploy needs a non-empty chain and a sidecar provisioner.
27+
7. **Run.** `triggerWorkflowRun`, then fold `readWorkflowRunEvents` (or `createRunSession`, which polls every 2 s). Answer waits with `deliverWorkflowSignal(transport, tenantId, runId, { runId, signalName, signalId, payload })`. A repeated `signalId` with a different payload is a 409. Resolve tool approvals with `POST /approvals/:id/approve` or `/reject`, which needs `approval:*` / `resolve`.
2328

24-
```ts
25-
import {
26-
createBrowserTransport,
27-
createRunSession,
28-
deployWorkflow,
29-
triggerWorkflowRun,
30-
deliverWorkflowSignal,
31-
findAwaitingSignal,
32-
} from "@intx/hub-client";
29+
## Rules that save time
3330

34-
const transport = createBrowserTransport();
35-
const deployment = await deployWorkflow(transport, tenantId, {
36-
source,
37-
entry: "./src/workflow.ts",
38-
sourceOfferingIds: [offeringId],
39-
defaultSourceOfferingId: offeringId,
40-
});
41-
await triggerWorkflowRun(transport, tenantId, runId, { content: "start" });
42-
43-
const session = createRunSession({
44-
tenantId,
45-
runId,
46-
transport,
47-
onChange: () => render(session.events),
48-
});
49-
const stop = session.start(); // polls events every 2s until terminal
50-
// when findAwaitingSignal(session.events) returns a signal, answer it:
51-
// await deliverWorkflowSignal(transport, tenantId, runId, { runId, signalName, signalId, payload })
52-
```
53-
54-
- Sources are `asset-source`, `asset-tarball`, or `registry` (registry needs `pin: "name@range"`).
55-
- `signalId` must be stable so retries dedupe. A terminal run cannot be retriggered.
56-
- Errors throw `ApiError(status, code, message)`. Validate every response with arktype.
57-
- Only a browser transport ships. A non-browser client must supply its own `Transport` that adds credentials.
58-
- Live updates: `Transport.subscribe` uses SSE, but the shipped run session polls.
59-
- Approvals: `GET /api/tenants/:t/approvals` with approve and reject POSTs. The docs disagree on whether these are live, so verify in the code.
60-
61-
## UI
62-
63-
Chat, run, approval, and step components exist in `@corbits/react-ui`: `use_skill corbits-ui-apps`. Scheduled and webhook runs: `use_skill corbits-hub-libs`.
31+
- Only the browser transport ships. Outside a browser, supply your own: base URL, cookie jar, JSON content type, `ApiError` mapping, empty 202 and 204 bodies. In process you can call `hub.app.fetch(new Request(origin + path))` with the session cookie.
32+
- `@intx/hub-client` covers workflows only. Solution Builder's `installer/src/hub.ts` is the broader typed client for tenants, roles, grants, and the catalog. Validate every response with arktype.
33+
- List endpoints return inherited rows. Filter assets by `tenantId` or you will adopt the parent's same-named asset. Send signals to the tenant the deployment belongs to.
34+
- Deploy lazily, per project or stage, when the work first needs it. Do not provision everything at install.
35+
- Client-side checks (for example guarding which grants the app may mint) are not security. The hub authorizes.
36+
- Never put secrets in responses, logs, or prompts. Credentials are sealed by the hub's cipher.
6437

6538
## Need more
6639

67-
- Run topology and credentials: `use_skill interchange-run-modes`.
68-
- Workflow authoring: `use_skill interchange-workflows`.
40+
- Composing, embedding, and running the hub itself: `use_skill interchange-embed-hub`.
41+
- Sidecars, placement, and credential delivery: `use_skill interchange-run-modes`.
42+
- Workflow code you will deploy: `use_skill interchange-workflows`.
43+
- Corbits libraries to mount (mailbox, artifacts, OAuth login, cron, memory): `use_skill corbits-hub-libs`. UI and reference apps: `use_skill corbits-ui-apps`.
6944
- Overview: `use_skill interchange`.

0 commit comments

Comments
 (0)