@synap-core/cli 1.10.0 → 1.11.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.
- package/dist/commands/capability.js +79 -8
- package/dist/commands/capability.js.map +1 -1
- package/dist/commands/centrality.js +2 -1
- package/dist/commands/centrality.js.map +1 -1
- package/dist/commands/context.js +2 -1
- package/dist/commands/context.js.map +1 -1
- package/dist/commands/data-extra.js +7 -4
- package/dist/commands/data-extra.js.map +1 -1
- package/dist/commands/data.js +14 -4
- package/dist/commands/data.js.map +1 -1
- package/dist/commands/diagnose.js +44 -1
- package/dist/commands/diagnose.js.map +1 -1
- package/dist/commands/digest.js +2 -1
- package/dist/commands/digest.js.map +1 -1
- package/dist/commands/doc.d.ts +14 -0
- package/dist/commands/doc.js +61 -4
- package/dist/commands/doc.js.map +1 -1
- package/dist/commands/doctor.d.ts +21 -0
- package/dist/commands/doctor.js +189 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/events.js +3 -1
- package/dist/commands/events.js.map +1 -1
- package/dist/commands/graph.js +5 -3
- package/dist/commands/graph.js.map +1 -1
- package/dist/commands/import.js +40 -9
- package/dist/commands/import.js.map +1 -1
- package/dist/commands/knowledge.d.ts +6 -0
- package/dist/commands/knowledge.js +253 -30
- package/dist/commands/knowledge.js.map +1 -1
- package/dist/commands/launch.js +2 -2
- package/dist/commands/launch.js.map +1 -1
- package/dist/commands/lens.js +1 -1
- package/dist/commands/lens.js.map +1 -1
- package/dist/commands/market-authoring.d.ts +45 -0
- package/dist/commands/market-authoring.js +225 -0
- package/dist/commands/market-authoring.js.map +1 -0
- package/dist/commands/market.d.ts +22 -7
- package/dist/commands/market.js +335 -60
- package/dist/commands/market.js.map +1 -1
- package/dist/commands/mcp.d.ts +24 -2
- package/dist/commands/mcp.js +84 -3
- package/dist/commands/mcp.js.map +1 -1
- package/dist/commands/pod.js +1 -1
- package/dist/commands/pod.js.map +1 -1
- package/dist/commands/pods.d.ts +14 -0
- package/dist/commands/pods.js +156 -2
- package/dist/commands/pods.js.map +1 -1
- package/dist/commands/proposals-actions.js +3 -0
- package/dist/commands/proposals-actions.js.map +1 -1
- package/dist/commands/sessions.js +2 -1
- package/dist/commands/sessions.js.map +1 -1
- package/dist/commands/skills.js +3 -1
- package/dist/commands/skills.js.map +1 -1
- package/dist/commands/template.d.ts +42 -0
- package/dist/commands/template.js +191 -0
- package/dist/commands/template.js.map +1 -0
- package/dist/commands/upload.d.ts +30 -0
- package/dist/commands/upload.js +155 -0
- package/dist/commands/upload.js.map +1 -0
- package/dist/index.js +118 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/capture-lane.js +5 -3
- package/dist/lib/capture-lane.js.map +1 -1
- package/dist/lib/capture-structure.d.ts +123 -0
- package/dist/lib/capture-structure.js +57 -0
- package/dist/lib/capture-structure.js.map +1 -0
- package/dist/lib/cp-packages.d.ts +56 -0
- package/dist/lib/cp-packages.js +171 -0
- package/dist/lib/cp-packages.js.map +1 -1
- package/dist/lib/hub-client.d.ts +14 -1
- package/dist/lib/hub-client.js +110 -19
- package/dist/lib/hub-client.js.map +1 -1
- package/dist/lib/id.d.ts +23 -0
- package/dist/lib/id.js +35 -0
- package/dist/lib/id.js.map +1 -0
- package/dist/lib/pod.d.ts +53 -3
- package/dist/lib/pod.js +81 -9
- package/dist/lib/pod.js.map +1 -1
- package/dist/lib/template-file.d.ts +74 -0
- package/dist/lib/template-file.js +140 -0
- package/dist/lib/template-file.js.map +1 -0
- package/node_modules/@synap/hub-rest-client/dist/index.cjs +1261 -0
- package/node_modules/@synap/hub-rest-client/dist/index.d.cts +1647 -0
- package/node_modules/@synap/hub-rest-client/dist/index.d.ts +1647 -0
- package/node_modules/@synap/hub-rest-client/dist/index.js +1231 -0
- package/node_modules/@synap/hub-rest-client/package.json +45 -0
- package/node_modules/@synap/hub-rest-client/src/client.ts +1631 -0
- package/node_modules/@synap/hub-rest-client/src/errors.ts +30 -0
- package/node_modules/@synap/hub-rest-client/src/index.ts +153 -0
- package/node_modules/@synap/hub-rest-client/src/setup.ts +133 -0
- package/node_modules/@synap/hub-rest-client/src/types.ts +1299 -0
- package/package.json +3 -2
- package/skills/agent-os/SKILL.md +6 -0
- package/skills/manifest.json +1 -1
- package/skills/onboard/SKILL.md +7 -0
- package/skills/synap/SKILL.md +110 -29
- package/skills/synap/capture.md +50 -152
- package/skills/synap/common-mistakes.md +1 -0
- package/skills/synap/content-os.md +83 -0
- package/skills/synap/digesting-a-channel.md +25 -0
- package/skills/synap/mental-model.md +7 -7
- package/skills/synap/multi-entity-capture.md +6 -4
- package/skills/synap/worked-examples.md +21 -0
- package/skills/synap/writes.md +75 -18
- package/skills/synap-app/SKILL.md +319 -0
- package/skills/synap-app/_order.txt +5 -0
- package/skills/synap-app/_skill.yaml +20 -0
- package/skills/synap-app/creating-the-base-app.md +64 -0
- package/skills/synap-app/governance.md +42 -0
- package/skills/synap-app/install-commands.md +55 -0
- package/skills/synap-app/overview.md +50 -0
- package/skills/synap-app/using-the-sdk.md +73 -0
- package/skills/synap-market/SKILL.md +335 -0
- package/skills/synap-market/_order.txt +5 -0
- package/skills/synap-market/_skill.yaml +24 -0
- package/skills/synap-market/author-and-publish.md +75 -0
- package/skills/synap-market/discover-and-fetch.md +59 -0
- package/skills/synap-market/governance-and-catalog.md +56 -0
- package/skills/synap-market/install.md +55 -0
- package/skills/synap-market/overview.md +51 -0
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Governance — `"proposed"` is success, not an error
|
|
2
|
+
|
|
3
|
+
This is the single most important thing to get right when writing code that
|
|
4
|
+
mutates a Synap pod.
|
|
5
|
+
|
|
6
|
+
## The contract
|
|
7
|
+
|
|
8
|
+
Every mutation (`entities.create`, `entities.update`, `relations.create`, …)
|
|
9
|
+
resolves to one of three outcomes, decided **by the pod**, not by your app:
|
|
10
|
+
|
|
11
|
+
| Outcome | Meaning | What your code should do |
|
|
12
|
+
|---|---|---|
|
|
13
|
+
| `approved` | applied immediately | proceed |
|
|
14
|
+
| `proposed` | queued for the pod owner's review (like a PR) | **surface the review — do NOT treat as failure, do NOT retry** |
|
|
15
|
+
| `denied` | the principal may not do this | show why; do not retry blindly |
|
|
16
|
+
|
|
17
|
+
## Why it matters for your code
|
|
18
|
+
|
|
19
|
+
- **Never treat `proposed` as an error.** It is the normal, healthy path for any
|
|
20
|
+
write that governance decides a human should see first. Retrying a `proposed`
|
|
21
|
+
write creates duplicates.
|
|
22
|
+
- **Never `try/catch` a `proposed` as if it threw.** It's a successful response
|
|
23
|
+
with a status, not an exception.
|
|
24
|
+
- **Surface the review link/id** so the user can approve it. An unreviewed
|
|
25
|
+
proposal is not yet in the graph — it won't show up in reads until approved.
|
|
26
|
+
|
|
27
|
+
## Pattern
|
|
28
|
+
|
|
29
|
+
```ts
|
|
30
|
+
const res = await synap.entities.create.mutate(input);
|
|
31
|
+
switch (res.status) {
|
|
32
|
+
case "approved": /* it's live */ break;
|
|
33
|
+
case "proposed": /* tell the user: "queued for review" + show res.proposalId */ break;
|
|
34
|
+
case "denied": /* show res.reason; don't retry */ break;
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Idempotency
|
|
39
|
+
|
|
40
|
+
If you must retry a create after a network error, pass an idempotency key where
|
|
41
|
+
the API accepts one — a `proposed` write that you retry without one will
|
|
42
|
+
duplicate on approval. When unsure, read back before re-creating.
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Install & scaffold commands
|
|
2
|
+
|
|
3
|
+
## Scaffold a full app (recommended for a fresh start)
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npx create-synap-app my-app
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
Creates a Next.js + HeroUI project from the base template, with the SDK, auth,
|
|
10
|
+
app shell, command palette, realtime, and mock mode pre-wired. It also drops an
|
|
11
|
+
`AGENTS.md` into the project so your AI coding tool knows how to work with it.
|
|
12
|
+
|
|
13
|
+
## Add the SDK to an existing app
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
# npm
|
|
17
|
+
npm install @synap-core/sdk@^1.0.0
|
|
18
|
+
|
|
19
|
+
# with React bindings
|
|
20
|
+
npm install @synap-core/sdk@^1.0.0 @synap-core/react@^1.0.0
|
|
21
|
+
|
|
22
|
+
# realtime
|
|
23
|
+
npm install @synap-core/sdk-realtime
|
|
24
|
+
|
|
25
|
+
# auth (browser sign-in) or auth-bootstrap (server credential bootstrap)
|
|
26
|
+
npm install @synap-core/auth # or @synap-core/auth-bootstrap
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Pin `@^1.0.0` on `sdk`/`react`. **Do not** install unpinned `@synap-core/sdk` —
|
|
30
|
+
older `0.x` versions on the registry are a different, abandoned package.
|
|
31
|
+
|
|
32
|
+
## Get this knowledge into your AI coding tool
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
# add the app-creation skill (this skill) to a connected AI tool
|
|
36
|
+
synap skills get synap-app
|
|
37
|
+
|
|
38
|
+
# or wire a tool end-to-end (skills + MCP), any target
|
|
39
|
+
synap connect --target=cursor # or claude, openclaw, …
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Alternatively, the scaffolded project ships an `AGENTS.md` at its root — most AI
|
|
43
|
+
coding tools read it automatically, so a freshly-scaffolded app teaches the AI
|
|
44
|
+
how to build on it with zero extra setup.
|
|
45
|
+
|
|
46
|
+
## Verify an install actually works
|
|
47
|
+
|
|
48
|
+
Publishing bugs have shipped uninstallable Synap packages before. After adding a
|
|
49
|
+
package, confirm it resolves in a clean directory:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
mkdir /tmp/synap-check && cd /tmp/synap-check && npm init -y
|
|
53
|
+
npm install @synap-core/sdk@^1.0.0
|
|
54
|
+
node -e "console.log(typeof require('@synap-core/sdk').createSynapClient)" # → function
|
|
55
|
+
```
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# Building an app on Synap
|
|
2
|
+
|
|
3
|
+
Synap is a sovereign data pod with a complete, typed API. You build an app on it
|
|
4
|
+
the same way you'd build on any backend — except the backend is the user's own
|
|
5
|
+
pod, and **every write is governed** (see `governance.md`).
|
|
6
|
+
|
|
7
|
+
## Decide the path first
|
|
8
|
+
|
|
9
|
+
| The user has… | Do this | Read |
|
|
10
|
+
|---|---|---|
|
|
11
|
+
| an existing app (any framework) | add the SDK | `using-the-sdk.md`, `install-commands.md` |
|
|
12
|
+
| nothing yet, wants a full app fast | scaffold the base app | `creating-the-base-app.md`, `install-commands.md` |
|
|
13
|
+
| a server / script / CLI (no browser) | SDK with a session token or API key | `using-the-sdk.md` |
|
|
14
|
+
|
|
15
|
+
Both paths use the same underlying packages; only the amount of scaffolding differs.
|
|
16
|
+
|
|
17
|
+
## The packages (all published under `@synap-core/*`)
|
|
18
|
+
|
|
19
|
+
- `@synap-core/sdk` — typed tRPC client. `createSynapClient({ podUrl, ... })`. **The one you almost always want.**
|
|
20
|
+
- `@synap-core/react` — React/TanStack-Query bindings (`SynapProvider`, hooks).
|
|
21
|
+
- `@synap-core/sdk-realtime` — Socket.IO presence/live-updates (`createRealtimeClient`).
|
|
22
|
+
- `@synap-core/auth` — sign-in orchestration (Synap Cloud + pod handshake), framework-agnostic.
|
|
23
|
+
- `@synap-core/auth-bootstrap` — zero-dep server helpers (`setupAgent`, `handshake`) for machine clients.
|
|
24
|
+
|
|
25
|
+
Pin `@^1.0.0` on `sdk`/`react` so you never resolve an old, abandoned `0.x`.
|
|
26
|
+
|
|
27
|
+
## The three rules that trip people up
|
|
28
|
+
|
|
29
|
+
1. **A write can come back `"proposed"` — that is SUCCESS, not an error.** It means
|
|
30
|
+
the change is queued for the pod owner's review (like a PR). Surface the review,
|
|
31
|
+
don't retry. Full contract: `governance.md`.
|
|
32
|
+
2. **The pod URL has no `/trpc` suffix and no trailing slash.** The SDK appends `/trpc`.
|
|
33
|
+
3. **Non-browser clients (server/CLI/mobile) authenticate with a token or API key**,
|
|
34
|
+
never a cookie. Browser apps use the Synap Cloud sign-in flow (`@synap-core/auth`).
|
|
35
|
+
|
|
36
|
+
## Get a pod to build against
|
|
37
|
+
|
|
38
|
+
- **Self-host (free):** `git clone` the backend, `docker compose up -d` (run its
|
|
39
|
+
`install.sh` to generate secrets). Pod answers on `:4000`.
|
|
40
|
+
- **Managed (paid):** sign up at synap.live and provision a pod.
|
|
41
|
+
|
|
42
|
+
You can develop the base app with **no pod at all** using its mock mode
|
|
43
|
+
(`NEXT_PUBLIC_BASE_MOCK_MODE=true`).
|
|
44
|
+
|
|
45
|
+
## Honest scope
|
|
46
|
+
|
|
47
|
+
Building on the user's OWN pod is fully supported today. Building a **multi-tenant
|
|
48
|
+
commercial app** where *other* people connect *their* pods is **not yet supported**
|
|
49
|
+
— per-app data scoping is still being built. If the user asks for that, say so;
|
|
50
|
+
don't architect around a capability that isn't there.
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# Using the SDK
|
|
2
|
+
|
|
3
|
+
## Vanilla / TypeScript / server / CLI — `@synap-core/sdk`
|
|
4
|
+
|
|
5
|
+
```ts
|
|
6
|
+
import { createSynapClient } from "@synap-core/sdk";
|
|
7
|
+
|
|
8
|
+
const synap = createSynapClient({
|
|
9
|
+
podUrl: "https://pod.example.com", // NO trailing slash, NO /trpc suffix
|
|
10
|
+
apiKey: process.env.SYNAP_API_KEY, // or sessionToken (see auth below)
|
|
11
|
+
workspaceId: process.env.SYNAP_WORKSPACE_ID ?? "",
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
// It's a typed tRPC client — full autocomplete over every router.
|
|
15
|
+
const me = await synap.users.me.query();
|
|
16
|
+
const entities = await synap.entities.list.query({ /* filter */ });
|
|
17
|
+
const result = await synap.entities.create.mutate({ /* ... */ });
|
|
18
|
+
// result may be { status: "proposed", ... } — that is success. See governance.md.
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
### Auth modes (pick ONE)
|
|
22
|
+
- **`apiKey`** — a Bearer key generated in the workspace's developer settings.
|
|
23
|
+
Best for servers, scripts, CLIs.
|
|
24
|
+
- **`sessionToken`** — an Ory Kratos session token, sent as `X-Session-Token`.
|
|
25
|
+
Best for non-browser clients that completed a sign-in.
|
|
26
|
+
- **cookie fallback** — omit both; the client uses the ambient Kratos cookie.
|
|
27
|
+
Browser, same-origin only.
|
|
28
|
+
|
|
29
|
+
For machine clients that need to bootstrap a credential, use
|
|
30
|
+
`@synap-core/auth-bootstrap` (`setupAgent(provisioningToken)` → API key,
|
|
31
|
+
`handshake(jwt)` → session token). Zero-dependency, safe in edge/serverless.
|
|
32
|
+
|
|
33
|
+
## React — `@synap-core/react`
|
|
34
|
+
|
|
35
|
+
```tsx
|
|
36
|
+
import { SynapProvider, useSynapContext } from "@synap-core/react";
|
|
37
|
+
|
|
38
|
+
function App() {
|
|
39
|
+
return (
|
|
40
|
+
<SynapProvider podUrl={podUrl} sessionToken={token} workspaceId={ws}>
|
|
41
|
+
<Dashboard />
|
|
42
|
+
</SynapProvider>
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
`SynapProvider` honours every `createSynapClient` option, incl. `sessionToken`
|
|
48
|
+
and `onAuthError` (a 401 handler). `react` is a peer of your app's React — it
|
|
49
|
+
does not bundle its own.
|
|
50
|
+
|
|
51
|
+
## Realtime — `@synap-core/sdk-realtime`
|
|
52
|
+
|
|
53
|
+
```ts
|
|
54
|
+
import { createRealtimeClient } from "@synap-core/sdk-realtime";
|
|
55
|
+
|
|
56
|
+
const rt = createRealtimeClient({ podUrl, sessionToken });
|
|
57
|
+
const stop = rt.onInvalidate((target) => {
|
|
58
|
+
// e.g. refetch the affected query
|
|
59
|
+
});
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Websocket-only transport (credentials never ride a long-poll URL) and it refuses
|
|
63
|
+
to send credentials over non-TLS unless you explicitly pass `allowInsecure`.
|
|
64
|
+
|
|
65
|
+
## Two transports, on purpose
|
|
66
|
+
- `@synap-core/sdk` (tRPC) — for apps. Typed, hooks, realtime.
|
|
67
|
+
- `@synap-core/hub-rest-client` (REST, zero-dep) — for agents/CLIs/scripts. Carries
|
|
68
|
+
agent verbs the tRPC surface has no first-class path for (`ask`, `orient`,
|
|
69
|
+
`discover`). Use it when you want a dependency-free REST client, not a full app.
|
|
70
|
+
|
|
71
|
+
## Keep types fresh
|
|
72
|
+
The SDK is typed by `@synap-core/api-types`, generated from the pod's live router.
|
|
73
|
+
If the pod adds a procedure and you don't see it, bump `@synap-core/api-types`.
|
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: synap-market
|
|
3
|
+
description: >
|
|
4
|
+
Use this skill when the user (or an agent driving the CLI/MCP/HTTP) wants to
|
|
5
|
+
DISCOVER, INSTALL, or AUTHOR/PUBLISH marketplace & template data on Synap —
|
|
6
|
+
workspace templates, capabilities, automations, and cells. Triggers: "find a
|
|
7
|
+
template for X", "is there a marketplace package for Y", "install this
|
|
8
|
+
template/capability", "create a new template", "publish this as a
|
|
9
|
+
workspace/capability", "scaffold a template", "share my workspace as a
|
|
10
|
+
template", "make this private/public". NOT the `synap` skill (capture/recall
|
|
11
|
+
data INSIDE a workspace) nor `synap-schema` (extending the data model of an
|
|
12
|
+
existing workspace) nor `synap-ui` (building views/dashboards) — this skill is
|
|
13
|
+
about the PACKAGE layer: finding, installing, and publishing reusable
|
|
14
|
+
templates/capabilities/automations/cells to/from the Control Plane catalog.
|
|
15
|
+
metadata:
|
|
16
|
+
openclaw:
|
|
17
|
+
requires:
|
|
18
|
+
env: []
|
|
19
|
+
optional_env: [SYNAP_POD_URL, SYNAP_WORKSPACE_ID, SYNAP_HUB_API_KEY]
|
|
20
|
+
primaryEnv: null
|
|
21
|
+
homepage: https://synap.live
|
|
22
|
+
capabilities:
|
|
23
|
+
[market-search, market-install, template-author, template-publish]
|
|
24
|
+
os: [macos, linux, windows]
|
|
25
|
+
userInvocable: true
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
# Marketplace & templates on Synap
|
|
29
|
+
|
|
30
|
+
The marketplace is Synap's **package layer**: workspace templates, capabilities,
|
|
31
|
+
automations, and cells — anything that can be installed into a pod rather than
|
|
32
|
+
hand-built. It lives in the Control Plane (CP) catalog and is discoverable and
|
|
33
|
+
installable from any door: the `synap` CLI, MCP (`run_capability`), or the Hub
|
|
34
|
+
REST API (`/api/hub/capabilities/execute`).
|
|
35
|
+
|
|
36
|
+
## The two reflexes
|
|
37
|
+
|
|
38
|
+
1. **DISCOVER before you invent.** Before scaffolding a new template or
|
|
39
|
+
capability, search the catalog — `synap market --search "<query>"` or the
|
|
40
|
+
`market.search` verb. Someone may have already published what you need.
|
|
41
|
+
Reinventing a workspace that already exists as a template is wasted work
|
|
42
|
+
AND fragments the catalog.
|
|
43
|
+
2. **One catalog, never a second one.** Every package — public or private —
|
|
44
|
+
lives in the CP's `synapPackages` table, reached through
|
|
45
|
+
`GET/POST /api/packages`. There is no side channel: don't write a
|
|
46
|
+
template to a random file and call it "published," and don't stand up a
|
|
47
|
+
local list of "known templates" — search the real catalog every time.
|
|
48
|
+
|
|
49
|
+
## The four things this skill teaches
|
|
50
|
+
|
|
51
|
+
| Task | Doc |
|
|
52
|
+
| --------------------------------------------------------- | --------------------------- |
|
|
53
|
+
| Search/browse/fetch a package (any kind) | `discover-and-fetch.md` |
|
|
54
|
+
| Install a package into a pod | `install.md` |
|
|
55
|
+
| Author a new template and publish it (the ONE write door) | `author-and-publish.md` |
|
|
56
|
+
| Governance rules + the one-catalog principle | `governance-and-catalog.md` |
|
|
57
|
+
|
|
58
|
+
## Package kinds
|
|
59
|
+
|
|
60
|
+
- **workspace / template** — a full workspace definition (profiles, views,
|
|
61
|
+
entity links) you can spin up as a new workspace, or reconcile onto an
|
|
62
|
+
existing one.
|
|
63
|
+
- **capability** — a tool + skill bundle (an integration or verb pack).
|
|
64
|
+
- **automation** — a trigger + action definition.
|
|
65
|
+
- **cell** — a custom UI renderer.
|
|
66
|
+
|
|
67
|
+
The CLI's browse filter (`market --type`) uses
|
|
68
|
+
`workspace|capability|skill|workflow|view|cell`; the pod-native install verb
|
|
69
|
+
(`market.install`) and its catalog use the narrower
|
|
70
|
+
`capability|automation|template|cell` (workspace ≡ template there). Match
|
|
71
|
+
whichever surface you're calling.
|
|
72
|
+
|
|
73
|
+
## Visibility
|
|
74
|
+
|
|
75
|
+
Every package is **public** or **private** (owner-only). Default on publish is
|
|
76
|
+
**private** — you opt in to sharing with `--public`. A private package 404s
|
|
77
|
+
(not 403) to anyone who isn't its author or an active member of its owning pod
|
|
78
|
+
— see `governance-and-catalog.md`.
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
# Discover & fetch
|
|
83
|
+
|
|
84
|
+
Three doors reach the same catalog. Pick the one that matches where you're
|
|
85
|
+
running.
|
|
86
|
+
|
|
87
|
+
## CLI — `synap market`
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
synap market # browse/list (the default action)
|
|
91
|
+
synap market --search "crm" # filter by name/description/slug
|
|
92
|
+
synap market --type capability # filter by type: workspace|capability|skill|workflow|view|cell
|
|
93
|
+
synap market --search "crm" --type workspace --json
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
`market` with no subcommand lists; `--list` is accepted explicitly too. It
|
|
97
|
+
merges: your OWN packages (public + private, via `GET /api/packages/mine`)
|
|
98
|
+
with the PUBLIC catalog (`GET /api/packages`) — logged out, you only see the
|
|
99
|
+
public/bundled set.
|
|
100
|
+
|
|
101
|
+
Companion commands:
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
synap market installed # what's already installed on THIS pod (pure read)
|
|
105
|
+
synap market installed --tree # composition graph: package → templates → workspaces
|
|
106
|
+
synap market update # check installed packages for drift against the catalog
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## Pod verb — `market.search` (MCP / agent / automation)
|
|
110
|
+
|
|
111
|
+
The pod-native verb, run via MCP `run_capability` or
|
|
112
|
+
`POST /api/hub/capabilities/execute` with `{ verbId: "market.search", parameters }`:
|
|
113
|
+
|
|
114
|
+
```json
|
|
115
|
+
{ "query": "crm", "kind": "template", "limit": 20 }
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
- `kind` ∈ `capability | automation | template | cell` (omit to search all).
|
|
119
|
+
- Reads the pod-local `cp_catalog_cache` — fast, no live CP round-trip.
|
|
120
|
+
- Returns `{ entries: [{ slug, kind, name, description, version, tier, installed }], count }`.
|
|
121
|
+
- `installed` is `true`/`false` for capability/cell (cheap natural-key check);
|
|
122
|
+
`undefined` for automation/template — that's an HONEST unknown, not a bug,
|
|
123
|
+
never read it as "not installed."
|
|
124
|
+
- Empty result → say so plainly ("nothing matched") — never fabricate a
|
|
125
|
+
package that doesn't exist. Offer to capture the gap as a note for later.
|
|
126
|
+
|
|
127
|
+
This is the verb an agent should reach for first — it's read-only (auto-runs,
|
|
128
|
+
no approval needed) and mirrors exactly what `synap market --search` shows a
|
|
129
|
+
human.
|
|
130
|
+
|
|
131
|
+
## Fetching one package's full definition
|
|
132
|
+
|
|
133
|
+
- **CLI (authoring path):** `synap market publish --from-workspace <id>` reads
|
|
134
|
+
a LIVE workspace via the pod's own `to-template` serializer — see
|
|
135
|
+
`author-and-publish.md`.
|
|
136
|
+
- **CP directly:** `GET /api/packages/:slug` returns the full row **with**
|
|
137
|
+
`definition` — the install payload. Auth is required to see a private
|
|
138
|
+
package (author-only). No auth → public only, and a private slug 404s.
|
|
139
|
+
- **From an install:** you don't normally need the definition yourself —
|
|
140
|
+
`market.install` (see `install.md`) resolves and applies it for you.
|
|
141
|
+
|
|
142
|
+
---
|
|
143
|
+
|
|
144
|
+
# Install
|
|
145
|
+
|
|
146
|
+
## CLI — `synap market install <slug>`
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
synap market install crm # install as a new workspace
|
|
150
|
+
synap market install crm --dry-run # preview: would-create / reuse / conflicts, writes nothing
|
|
151
|
+
synap market install crm --onto <workspaceId> # reconcile ONTO an existing workspace (additive)
|
|
152
|
+
synap market install crm --project <id> # tag seeded entities to a project (install stays pod-wide)
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
Installs are **workspace-first**: a `workspace`/`template` package spins up
|
|
156
|
+
(or reconciles onto) a workspace directly. Other package types (capability,
|
|
157
|
+
automation, cell) route you to the right surface (`synap capability add`,
|
|
158
|
+
etc.) rather than being force-fit into a workspace install.
|
|
159
|
+
|
|
160
|
+
## Pod verb — `market.install` (MCP / agent / automation)
|
|
161
|
+
|
|
162
|
+
```json
|
|
163
|
+
{ "slug": "crm", "kind": "template", "version": "optional", "params": {} }
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
`kind` ∈ `capability | automation | template | cell` — required, unlike
|
|
167
|
+
`market.search`.
|
|
168
|
+
|
|
169
|
+
**This verb ALWAYS mutates**, so it goes through the full permission gate —
|
|
170
|
+
never treat a non-"installed" response as failure:
|
|
171
|
+
|
|
172
|
+
- **Operator call** (no agent identity — e.g. you're driving the CLI as the
|
|
173
|
+
pod owner): executes directly → `{ status: "installed", result }`.
|
|
174
|
+
- **Agent call** (any MCP/automation/agent-key caller): ALWAYS proposes,
|
|
175
|
+
regardless of any standing grant on `market.install` itself — a grant on
|
|
176
|
+
the verb governs _invoking_ it, not the provisioning it performs. Response:
|
|
177
|
+
`{ status: "proposed", proposalId, reviewUrl }`.
|
|
178
|
+
|
|
179
|
+
**`"proposed"` is success, not an error.** Surface `reviewUrl` to the user so
|
|
180
|
+
they can approve it — don't retry, don't report it as a failure. See
|
|
181
|
+
`governance-and-catalog.md`.
|
|
182
|
+
|
|
183
|
+
## Idempotency by kind
|
|
184
|
+
|
|
185
|
+
- **capability** — natural key is `(name, workspaceId)`; installing twice
|
|
186
|
+
converges, doesn't duplicate.
|
|
187
|
+
- **template/workspace** — keyed by `packageSlug`/`proposalId` (both set to
|
|
188
|
+
the catalog slug); re-installing the same template for the same user
|
|
189
|
+
converges to the existing workspace rather than creating a second one.
|
|
190
|
+
- **automation** — pre-checked by `(name, workspace)` before creating.
|
|
191
|
+
- **cell** — keyed by `(typeKey, workspaceId)`.
|
|
192
|
+
|
|
193
|
+
## Locked / tier-gated packages
|
|
194
|
+
|
|
195
|
+
A package can declare a `requiredTier`. Installing one your account's plan
|
|
196
|
+
doesn't cover fails a pre-check (`assertPackageTierAccess`) BEFORE any
|
|
197
|
+
proposal or provisioning — tell the user which tier is required rather than
|
|
198
|
+
retrying.
|
|
199
|
+
|
|
200
|
+
---
|
|
201
|
+
|
|
202
|
+
# Author & publish — the ONE write door
|
|
203
|
+
|
|
204
|
+
`synap market <verb>` is the only supported authoring path today (human or AI
|
|
205
|
+
driving the CLI). There is no agent-native "publish a template" MCP verb —
|
|
206
|
+
`market.install` only ever CONSUMES the catalog. Authoring is a human/CLI
|
|
207
|
+
action; if you're an agent helping the user author one, drive the CLI on
|
|
208
|
+
their behalf and always tell them what you ran.
|
|
209
|
+
|
|
210
|
+
## The loop
|
|
211
|
+
|
|
212
|
+
```bash
|
|
213
|
+
synap market scaffold my-thing # writes my-thing.template.yaml (refuses to overwrite)
|
|
214
|
+
# ...edit the file...
|
|
215
|
+
synap market validate my-thing.template.yaml
|
|
216
|
+
synap market publish my-thing.template.yaml # PRIVATE by default
|
|
217
|
+
synap market publish my-thing.template.yaml --public # opt in to sharing
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
### `scaffold <slug>`
|
|
221
|
+
|
|
222
|
+
Writes a minimal valid `<slug>.template.yaml` — `meta` (slug/name/description/
|
|
223
|
+
icon/color), `workspace` (name/description), and one starter profile. Refuses
|
|
224
|
+
to clobber an existing file — create-then-configure, never silently overwrite
|
|
225
|
+
an author's work.
|
|
226
|
+
|
|
227
|
+
### `validate <file>`
|
|
228
|
+
|
|
229
|
+
Runs the ONE shared validator (`validateTemplate` from
|
|
230
|
+
`@synap-core/workspace-templates`) locally — fast feedback before any network
|
|
231
|
+
call. Fix every reported error before publishing; the CP re-runs the same
|
|
232
|
+
class of check server-side and will reject an invalid definition (400).
|
|
233
|
+
|
|
234
|
+
### `publish [file]`
|
|
235
|
+
|
|
236
|
+
```bash
|
|
237
|
+
synap market publish [file] [--public|--private] [--from-workspace <id>] [--json]
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
- Validates first — a failing template is never published.
|
|
241
|
+
- Default is **private**; pass `--public` to share. `--private` is accepted
|
|
242
|
+
explicitly if a script wants to state intent.
|
|
243
|
+
- `--from-workspace <id>` serializes a LIVE, already-built workspace into a
|
|
244
|
+
template via the pod's own `POST /workspaces/:id/to-template` door, then
|
|
245
|
+
publishes that — the fastest way to turn something you already built into a
|
|
246
|
+
reusable package. It runs through the SAME validator as a hand-authored file.
|
|
247
|
+
- Writes go to `POST /api/packages` (upsert by `(slug, authorId)`; version is
|
|
248
|
+
a content hash, so republishing identical content is a safe no-op — you'll
|
|
249
|
+
see `outcome: "no-op"` vs `"created"`/`"updated"`).
|
|
250
|
+
- Slugs `foundation`, `crm`, `operations`, `content-os`, `enterprise-os`,
|
|
251
|
+
`research-base` are reserved bedrock templates — publishing under one of
|
|
252
|
+
those 403s; pick a different slug.
|
|
253
|
+
|
|
254
|
+
### `unpublish <slug>`
|
|
255
|
+
|
|
256
|
+
Flips a published package back to private (owner-only). ⚠️ Known gap: the CP's
|
|
257
|
+
PATCH validator doesn't yet accept `isPublic` on every deploy — if it 400s,
|
|
258
|
+
that's a server-side TODO, not something to work around; report it honestly.
|
|
259
|
+
|
|
260
|
+
## Server-side validation scope (important honesty note)
|
|
261
|
+
|
|
262
|
+
`POST /api/packages` validates **self-contained** templates (no `dependencies`
|
|
263
|
+
declared) with the full referential-integrity check (dangling profile refs in
|
|
264
|
+
views/entityLinks, missing view scope, entityLink predicates). A template that
|
|
265
|
+
**declares `dependencies`** (compose/require another template) passes through
|
|
266
|
+
this HTTP door **without** that validation — composition-aware server-side
|
|
267
|
+
validation is not yet wired for ad-hoc publishes (only the seeded flagship
|
|
268
|
+
templates get validated at that level). Don't claim a composed publish was
|
|
269
|
+
fully validated; say what actually ran.
|
|
270
|
+
|
|
271
|
+
## What you're producing
|
|
272
|
+
|
|
273
|
+
A `PackageDefinition`: `_meta` (slug/name/description/icon/color/tags),
|
|
274
|
+
`workspaceName`, `description`, `profiles`, `views`, `entityLinks`, optional
|
|
275
|
+
`dependencies`. The scaffolded YAML and `--from-workspace` both produce this
|
|
276
|
+
shape — never hand-write JSON against the raw DB row shape.
|
|
277
|
+
|
|
278
|
+
---
|
|
279
|
+
|
|
280
|
+
# Governance & the one-catalog principle
|
|
281
|
+
|
|
282
|
+
## Writes are governed — "proposed" is success
|
|
283
|
+
|
|
284
|
+
Every mutating marketplace action an AGENT takes (`market.install` above all)
|
|
285
|
+
can come back `{ status: "proposed", proposalId, reviewUrl }` instead of
|
|
286
|
+
`{ status: "installed" }`. That is the write queued for the pod owner's
|
|
287
|
+
review — like opening a PR, not a failure and not an error to retry. Always:
|
|
288
|
+
|
|
289
|
+
- Surface `reviewUrl` to the user.
|
|
290
|
+
- Don't retry the same install hoping for "installed" — it won't change the
|
|
291
|
+
outcome and may create a duplicate proposal.
|
|
292
|
+
- Remember an unreviewed proposal isn't live yet: `market.search`'s
|
|
293
|
+
`installed` flag and any downstream read won't reflect it until approved.
|
|
294
|
+
|
|
295
|
+
An OPERATOR-driven install (the pod owner themself, via CLI with their own
|
|
296
|
+
credentials, no agent identity) executes directly — no proposal. The
|
|
297
|
+
distinction is WHO is acting, not which door they used.
|
|
298
|
+
|
|
299
|
+
## One catalog — never fork it
|
|
300
|
+
|
|
301
|
+
- The catalog is `synapPackages` in the Control Plane, reached through
|
|
302
|
+
`GET/POST/PATCH /api/packages*`. `cp_catalog_cache` on the pod is a
|
|
303
|
+
**read-through cache** of it (populated by sync), not a second source of
|
|
304
|
+
truth — never write to it directly, and never treat a cache miss as "the
|
|
305
|
+
package doesn't exist" without falling back to a live CP fetch (which
|
|
306
|
+
`market.install` already does for automation/template kinds).
|
|
307
|
+
- Discovery happens on the CP (`market.search`, `synap market`); apply
|
|
308
|
+
happens on the pod (`market.install`, workspace creation). Don't invent a
|
|
309
|
+
parallel "known templates" list anywhere else in a workflow you're
|
|
310
|
+
building — always search the real catalog.
|
|
311
|
+
- Publishing to anywhere other than `POST /api/packages` (e.g. hand-writing a
|
|
312
|
+
file and calling it "published," or registering a template only in a local
|
|
313
|
+
config) is NOT publishing — it produces something invisible to
|
|
314
|
+
`market.search` and every other consumer.
|
|
315
|
+
|
|
316
|
+
## Visibility model
|
|
317
|
+
|
|
318
|
+
- **Public** (`isPublic: true`) — visible to everyone via `GET /api/packages`.
|
|
319
|
+
- **Private** (`isPublic: false`, the default) — visible only to its
|
|
320
|
+
`authorId` (and, for non-workspace categories, active members of an
|
|
321
|
+
`podId`-owned team). A private **workspace template** is stricter:
|
|
322
|
+
owner-only, full stop, even for pod teammates.
|
|
323
|
+
- A private package a caller can't see **404s, never 403s** — this is
|
|
324
|
+
deliberate (don't leak existence). If you get a 404 fetching a package by
|
|
325
|
+
slug, don't assume it never existed — it may just be private to someone
|
|
326
|
+
else.
|
|
327
|
+
|
|
328
|
+
## Tier gating
|
|
329
|
+
|
|
330
|
+
A public package can declare `requiredTier`. `GET /api/packages/available`
|
|
331
|
+
tells an authed caller which tiered slugs their subscription actually
|
|
332
|
+
unlocks; a slug missing from that list but present in the browse catalog is
|
|
333
|
+
locked for them — `market.install` pre-checks this and fails before touching
|
|
334
|
+
governance, so surface the tier requirement rather than treating it as a
|
|
335
|
+
generic install error.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
name: synap-market
|
|
2
|
+
description: >
|
|
3
|
+
Use this skill when the user (or an agent driving the CLI/MCP/HTTP) wants to
|
|
4
|
+
DISCOVER, INSTALL, or AUTHOR/PUBLISH marketplace & template data on Synap —
|
|
5
|
+
workspace templates, capabilities, automations, and cells. Triggers: "find a
|
|
6
|
+
template for X", "is there a marketplace package for Y", "install this
|
|
7
|
+
template/capability", "create a new template", "publish this as a
|
|
8
|
+
workspace/capability", "scaffold a template", "share my workspace as a
|
|
9
|
+
template", "make this private/public". NOT the `synap` skill (capture/recall
|
|
10
|
+
data INSIDE a workspace) nor `synap-schema` (extending the data model of an
|
|
11
|
+
existing workspace) nor `synap-ui` (building views/dashboards) — this skill is
|
|
12
|
+
about the PACKAGE layer: finding, installing, and publishing reusable
|
|
13
|
+
templates/capabilities/automations/cells to/from the Control Plane catalog.
|
|
14
|
+
metadata:
|
|
15
|
+
openclaw:
|
|
16
|
+
requires:
|
|
17
|
+
env: []
|
|
18
|
+
optional_env: [SYNAP_POD_URL, SYNAP_WORKSPACE_ID, SYNAP_HUB_API_KEY]
|
|
19
|
+
primaryEnv: null
|
|
20
|
+
homepage: https://synap.live
|
|
21
|
+
capabilities:
|
|
22
|
+
[market-search, market-install, template-author, template-publish]
|
|
23
|
+
os: [macos, linux, windows]
|
|
24
|
+
userInvocable: true
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# Author & publish — the ONE write door
|
|
2
|
+
|
|
3
|
+
`synap market <verb>` is the only supported authoring path today (human or AI
|
|
4
|
+
driving the CLI). There is no agent-native "publish a template" MCP verb —
|
|
5
|
+
`market.install` only ever CONSUMES the catalog. Authoring is a human/CLI
|
|
6
|
+
action; if you're an agent helping the user author one, drive the CLI on
|
|
7
|
+
their behalf and always tell them what you ran.
|
|
8
|
+
|
|
9
|
+
## The loop
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
synap market scaffold my-thing # writes my-thing.template.yaml (refuses to overwrite)
|
|
13
|
+
# ...edit the file...
|
|
14
|
+
synap market validate my-thing.template.yaml
|
|
15
|
+
synap market publish my-thing.template.yaml # PRIVATE by default
|
|
16
|
+
synap market publish my-thing.template.yaml --public # opt in to sharing
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
### `scaffold <slug>`
|
|
20
|
+
|
|
21
|
+
Writes a minimal valid `<slug>.template.yaml` — `meta` (slug/name/description/
|
|
22
|
+
icon/color), `workspace` (name/description), and one starter profile. Refuses
|
|
23
|
+
to clobber an existing file — create-then-configure, never silently overwrite
|
|
24
|
+
an author's work.
|
|
25
|
+
|
|
26
|
+
### `validate <file>`
|
|
27
|
+
|
|
28
|
+
Runs the ONE shared validator (`validateTemplate` from
|
|
29
|
+
`@synap-core/workspace-templates`) locally — fast feedback before any network
|
|
30
|
+
call. Fix every reported error before publishing; the CP re-runs the same
|
|
31
|
+
class of check server-side and will reject an invalid definition (400).
|
|
32
|
+
|
|
33
|
+
### `publish [file]`
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
synap market publish [file] [--public|--private] [--from-workspace <id>] [--json]
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
- Validates first — a failing template is never published.
|
|
40
|
+
- Default is **private**; pass `--public` to share. `--private` is accepted
|
|
41
|
+
explicitly if a script wants to state intent.
|
|
42
|
+
- `--from-workspace <id>` serializes a LIVE, already-built workspace into a
|
|
43
|
+
template via the pod's own `POST /workspaces/:id/to-template` door, then
|
|
44
|
+
publishes that — the fastest way to turn something you already built into a
|
|
45
|
+
reusable package. It runs through the SAME validator as a hand-authored file.
|
|
46
|
+
- Writes go to `POST /api/packages` (upsert by `(slug, authorId)`; version is
|
|
47
|
+
a content hash, so republishing identical content is a safe no-op — you'll
|
|
48
|
+
see `outcome: "no-op"` vs `"created"`/`"updated"`).
|
|
49
|
+
- Slugs `foundation`, `crm`, `operations`, `content-os`, `enterprise-os`,
|
|
50
|
+
`research-base` are reserved bedrock templates — publishing under one of
|
|
51
|
+
those 403s; pick a different slug.
|
|
52
|
+
|
|
53
|
+
### `unpublish <slug>`
|
|
54
|
+
|
|
55
|
+
Flips a published package back to private (owner-only). ⚠️ Known gap: the CP's
|
|
56
|
+
PATCH validator doesn't yet accept `isPublic` on every deploy — if it 400s,
|
|
57
|
+
that's a server-side TODO, not something to work around; report it honestly.
|
|
58
|
+
|
|
59
|
+
## Server-side validation scope (important honesty note)
|
|
60
|
+
|
|
61
|
+
`POST /api/packages` validates **self-contained** templates (no `dependencies`
|
|
62
|
+
declared) with the full referential-integrity check (dangling profile refs in
|
|
63
|
+
views/entityLinks, missing view scope, entityLink predicates). A template that
|
|
64
|
+
**declares `dependencies`** (compose/require another template) passes through
|
|
65
|
+
this HTTP door **without** that validation — composition-aware server-side
|
|
66
|
+
validation is not yet wired for ad-hoc publishes (only the seeded flagship
|
|
67
|
+
templates get validated at that level). Don't claim a composed publish was
|
|
68
|
+
fully validated; say what actually ran.
|
|
69
|
+
|
|
70
|
+
## What you're producing
|
|
71
|
+
|
|
72
|
+
A `PackageDefinition`: `_meta` (slug/name/description/icon/color/tags),
|
|
73
|
+
`workspaceName`, `description`, `profiles`, `views`, `entityLinks`, optional
|
|
74
|
+
`dependencies`. The scaffolded YAML and `--from-workspace` both produce this
|
|
75
|
+
shape — never hand-write JSON against the raw DB row shape.
|