@mediamonks/canopy-cli 0.5.0 → 0.6.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/{db-BW1ZolWn.mjs → db-BpnjQUkF.mjs} +42 -3
- package/dist/{deploy-alC2--pZ.mjs → deploy-DdSSL227.mjs} +29 -2
- package/dist/dev-DV5ud7AJ.mjs +2 -0
- package/dist/{dev-CQQhMOoD.mjs → dev-OxeYndd1.mjs} +24 -9
- package/dist/docs/ci.md +1 -3
- package/dist/docs/cli.md +3 -4
- package/dist/docs/data.md +5 -4
- package/dist/docs/deploy.md +1 -3
- package/dist/docs/dev.md +13 -11
- package/dist/docs/getting-started.md +1 -3
- package/dist/docs/infrastructure.md +27 -15
- package/dist/docs/migrations.md +166 -0
- package/dist/docs/porting-nextjs.md +232 -0
- package/dist/docs/sdk-ai.md +103 -0
- package/dist/docs/sdk-auth.md +57 -0
- package/dist/docs/sdk-database.md +126 -0
- package/dist/docs/sdk-files.md +82 -0
- package/dist/docs/sdk-services.md +58 -0
- package/dist/docs/sdk.md +113 -0
- package/dist/docs/static-serving.md +4 -1
- package/dist/docs/troubleshooting.md +1 -3
- package/dist/{docs-DvSPpSIR.mjs → docs-Cept8t_3.mjs} +23 -10
- package/dist/index.mjs +20 -20
- package/dist/{local-C5-GWJcx.mjs → local-D9OVEHDg.mjs} +363 -39
- package/dist/{login-Bz-rwlFF.mjs → login-Cb8D8a1Z.mjs} +125 -4
- package/dist/migrations-B0kJarjr.mjs +50 -0
- package/dist/{open-DnlYDqrU.mjs → open-BmgYBSdR.mjs} +2 -2
- package/dist/{package-Bi5FfVSP.mjs → package-D1wDsLSU.mjs} +1 -1
- package/dist/{projects-ijfe7Ffx.mjs → projects-CoGh8m9A.mjs} +17 -9
- package/dist/{site-BJjoePQS.mjs → site-DQnysZ3W.mjs} +8 -1
- package/dist/skill/canopy/SKILL.md +18 -1
- package/dist/{skill-IAAa2EkK.mjs → skill-192EdixC.mjs} +2 -2
- package/dist/templates/worker/AGENTS.md +3 -0
- package/dist/{token-BeF9zczg.mjs → token-CWgp_Lqe.mjs} +2 -2
- package/dist/{ui-DThlyHgo.mjs → ui-BhzMeKQl.mjs} +1 -1
- package/dist/{upgrade-DNxR-zh7.mjs → upgrade-BrHSSg2e.mjs} +2 -2
- package/dist/{usage-DQpH7A3X.mjs → usage-CYQRLXwf.mjs} +2 -2
- package/package.json +3 -3
- package/dist/dev-BJ5xEwqW.mjs +0 -2
package/dist/docs/sdk.md
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Using the SDK
|
|
3
|
+
description: One typed client for auth, data, files, AI, and services — no keys, no connection strings, the platform authenticates for you.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
`@mediamonks/canopy` is a thin, typed client over the platform's project API.
|
|
7
|
+
One `createCanopy()` call gives you five modules — `auth`, `db`, `blob`, `ai`,
|
|
8
|
+
and `services` — and that is the entire surface. There are no API keys,
|
|
9
|
+
connection strings, or environment variables to manage: every call rides the
|
|
10
|
+
session the platform already established before your code ran.
|
|
11
|
+
|
|
12
|
+
The same code works everywhere your project runs — in the browser during
|
|
13
|
+
`canopy dev`, in the deployed site, and in a project's backend worker.
|
|
14
|
+
|
|
15
|
+
## Getting the SDK
|
|
16
|
+
|
|
17
|
+
You usually don't install anything. The `spa` and `worker` templates ship with
|
|
18
|
+
`@mediamonks/canopy` in `package.json`; the `static` template imports the same
|
|
19
|
+
bundle straight from the platform, untyped but always version-matched to the
|
|
20
|
+
API:
|
|
21
|
+
|
|
22
|
+
```ts
|
|
23
|
+
// spa / worker templates (typed, from npm)
|
|
24
|
+
import { createCanopy } from "@mediamonks/canopy";
|
|
25
|
+
|
|
26
|
+
// static template (untyped, served by the platform)
|
|
27
|
+
import { createCanopy } from "/_api/data/v1/sdk.js";
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## createCanopy(config)
|
|
31
|
+
|
|
32
|
+
`createCanopy` takes an optional config object and returns the client:
|
|
33
|
+
|
|
34
|
+
```ts
|
|
35
|
+
import { createCanopy } from "@mediamonks/canopy";
|
|
36
|
+
|
|
37
|
+
const canopy = createCanopy();
|
|
38
|
+
// canopy.auth, canopy.db, canopy.blob, canopy.ai, canopy.services
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
| Option | Type | Default | What it does |
|
|
42
|
+
| --------- | -------------- | -------------- | ---------------------------------------------------------------------------------------------------- |
|
|
43
|
+
| `baseUrl` | `string` | `"/_api"` | Base URL of the platform data API. The default is same-origin, so it works untouched in the browser. |
|
|
44
|
+
| `fetch` | `typeof fetch` | global `fetch` | Override how requests are sent — a backend worker passes its platform service binding here. |
|
|
45
|
+
|
|
46
|
+
In the browser no config is needed. In a backend worker (the `worker`
|
|
47
|
+
template), the platform is reachable through the `CANOPY` service binding, so
|
|
48
|
+
create the client per request with that binding's fetch:
|
|
49
|
+
|
|
50
|
+
```ts
|
|
51
|
+
app.get("/api/hello", async (c) => {
|
|
52
|
+
const canopy = createCanopy({ fetch: c.env.CANOPY.fetch.bind(c.env.CANOPY) });
|
|
53
|
+
const user = await canopy.auth.user();
|
|
54
|
+
return c.json({ message: `Hello from the backend, ${user.name}!` });
|
|
55
|
+
});
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## One instance, shared
|
|
59
|
+
|
|
60
|
+
In the browser, create the client once and import it everywhere. The scaffolds
|
|
61
|
+
do this in `src/canopy.ts`:
|
|
62
|
+
|
|
63
|
+
```ts
|
|
64
|
+
// src/canopy.ts
|
|
65
|
+
import { createCanopy } from "@mediamonks/canopy";
|
|
66
|
+
|
|
67
|
+
export const canopy = createCanopy();
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Error handling
|
|
71
|
+
|
|
72
|
+
Any call that fails throws a `CanopyError` (exported from the package). It
|
|
73
|
+
extends `Error` with the HTTP `status` of the failed request; `message` carries
|
|
74
|
+
the response body when the platform sent one:
|
|
75
|
+
|
|
76
|
+
```ts
|
|
77
|
+
import { CanopyError } from "@mediamonks/canopy";
|
|
78
|
+
|
|
79
|
+
try {
|
|
80
|
+
await canopy.db.collection("todos").insert({ title: "hi", done: false });
|
|
81
|
+
} catch (err) {
|
|
82
|
+
if (err instanceof CanopyError && err.status === 403) {
|
|
83
|
+
// e.g. a viewer hitting a write — show a friendly message
|
|
84
|
+
}
|
|
85
|
+
throw err;
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Two read calls are deliberately soft instead: `collection.get(id)` and
|
|
90
|
+
`blob.get(key)` resolve to `null` for a missing document or object rather than
|
|
91
|
+
throwing.
|
|
92
|
+
|
|
93
|
+
## Where it runs
|
|
94
|
+
|
|
95
|
+
The client is plain `fetch` underneath, so behavior is identical across
|
|
96
|
+
environments:
|
|
97
|
+
|
|
98
|
+
- **Browser, deployed** — calls go to same-origin `/_api`; the platform
|
|
99
|
+
authenticated the visitor before your page loaded, and the session cookie
|
|
100
|
+
rides along automatically.
|
|
101
|
+
- **Browser, `canopy dev`** — the dev server proxies `/_api` to the platform
|
|
102
|
+
with your CLI login, so local code talks to your project's real data.
|
|
103
|
+
- **Backend worker** — pass the `CANOPY` service binding's fetch (shown
|
|
104
|
+
above); the incoming request's identity carries through, so `auth.user()`
|
|
105
|
+
and per-project budgets behave exactly as in the browser.
|
|
106
|
+
|
|
107
|
+
## Modules
|
|
108
|
+
|
|
109
|
+
- [Auth](https://docs.canopy.website/sdk/auth) — who is signed in, and ending sessions.
|
|
110
|
+
- [Database](https://docs.canopy.website/sdk/database) — document collections, plus a raw SQL escape hatch.
|
|
111
|
+
- [Files](https://docs.canopy.website/sdk/files) — private blob storage under the project's prefix.
|
|
112
|
+
- [AI](https://docs.canopy.website/sdk/ai) — metered, budget-capped LLM access.
|
|
113
|
+
- [Services](https://docs.canopy.website/sdk/services) — curated internal data services.
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
|
|
1
|
+
---
|
|
2
|
+
title: How static files are served
|
|
3
|
+
description: The full path a browser request takes to a deployed file.
|
|
4
|
+
---
|
|
2
5
|
|
|
3
6
|
This explains the full path a browser request takes to receive a deployed
|
|
4
7
|
project's `index.html` and its assets (JS/CSS/images/fonts), and reviews that
|
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: Troubleshooting
|
|
3
|
-
|
|
3
|
+
description: Common errors and how to resolve them.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
# Troubleshooting
|
|
7
|
-
|
|
8
6
|
## `command not found: canopy`
|
|
9
7
|
|
|
10
8
|
The CLI isn't installed or isn't on your PATH. Install it:
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { n as siteConfig } from "./site-
|
|
2
|
-
import { n as reportErrors } from "./ui-
|
|
1
|
+
import { n as siteConfig } from "./site-DQnysZ3W.mjs";
|
|
2
|
+
import { n as reportErrors } from "./ui-BhzMeKQl.mjs";
|
|
3
3
|
import { defineCommand } from "citty";
|
|
4
4
|
import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from "node:fs";
|
|
5
5
|
import { homedir } from "node:os";
|
|
@@ -24,12 +24,20 @@ const cacheDir = join(homedir(), ".canopy", "docs-cache");
|
|
|
24
24
|
const indexPath = join(cacheDir, "index.json");
|
|
25
25
|
const CACHE_TTL_MS = 3600 * 1e3;
|
|
26
26
|
const FETCH_TIMEOUT_MS = 4e3;
|
|
27
|
-
/**
|
|
27
|
+
/**
|
|
28
|
+
* The bundled snapshot dir: dist/docs when published (generated by
|
|
29
|
+
* sync-templates from the terminal-docs corpus), with raw guide sources as a
|
|
30
|
+
* dev last resort — those are MDX, which agents read fine untransformed.
|
|
31
|
+
*/
|
|
28
32
|
function bundledDocsDir() {
|
|
29
33
|
const base = fileURLToPath(new URL(".", import.meta.url));
|
|
30
|
-
for (const candidate of [
|
|
34
|
+
for (const candidate of [
|
|
35
|
+
join(base, "docs"),
|
|
36
|
+
join(base, "..", "dist", "docs"),
|
|
37
|
+
join(base, "..", "..", "..", "apps", "docs", "content", "docs", "guides")
|
|
38
|
+
]) if (existsSync(candidate)) return candidate;
|
|
31
39
|
}
|
|
32
|
-
/** Parse `---\ntitle: …\
|
|
40
|
+
/** Parse `---\ntitle: …\ndescription: …\n---\n<body>` frontmatter. */
|
|
33
41
|
function parseFrontmatter(raw) {
|
|
34
42
|
const match = /^---\n([\s\S]*?)\n---\n?([\s\S]*)$/.exec(raw);
|
|
35
43
|
if (!match?.[1] || match[2] === void 0) return { body: raw.trim() };
|
|
@@ -40,7 +48,7 @@ function parseFrontmatter(raw) {
|
|
|
40
48
|
}
|
|
41
49
|
return {
|
|
42
50
|
title: meta["title"],
|
|
43
|
-
summary: meta["summary"],
|
|
51
|
+
summary: meta["description"] ?? meta["summary"],
|
|
44
52
|
body: match[2].trim()
|
|
45
53
|
};
|
|
46
54
|
}
|
|
@@ -96,8 +104,8 @@ function topicsFromFallback() {
|
|
|
96
104
|
} catch {}
|
|
97
105
|
const dir = bundledDocsDir();
|
|
98
106
|
if (!dir) return [];
|
|
99
|
-
return readdirSync(dir).filter((f) => f
|
|
100
|
-
const slug = f.replace(/\.
|
|
107
|
+
return readdirSync(dir).filter((f) => /\.mdx?$/.test(f)).map((f) => {
|
|
108
|
+
const slug = f.replace(/\.mdx?$/, "");
|
|
101
109
|
const { title, summary } = parseFrontmatter(readFileSync(join(dir, f), "utf8"));
|
|
102
110
|
return {
|
|
103
111
|
slug,
|
|
@@ -111,8 +119,13 @@ function bodyFromFallback(slug) {
|
|
|
111
119
|
const cached = join(cacheDir, `${slug}.md`);
|
|
112
120
|
if (existsSync(cached)) return readFileSync(cached, "utf8");
|
|
113
121
|
const dir = bundledDocsDir();
|
|
114
|
-
const
|
|
115
|
-
|
|
122
|
+
for (const ext of [".md", ".mdx"]) {
|
|
123
|
+
const file = dir && join(dir, `${slug}${ext}`);
|
|
124
|
+
if (file && existsSync(file)) {
|
|
125
|
+
const { title, body } = parseFrontmatter(readFileSync(file, "utf8"));
|
|
126
|
+
return title ? `# ${title}\n\n${body}` : body;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
116
129
|
}
|
|
117
130
|
async function listTopics(refresh) {
|
|
118
131
|
const { apiUrl } = siteConfig();
|
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { t as version } from "./package-
|
|
3
|
-
import { r as siteHeaderLine, t as setInvocationProfile } from "./site-
|
|
2
|
+
import { t as version } from "./package-D1wDsLSU.mjs";
|
|
3
|
+
import { r as siteHeaderLine, t as setInvocationProfile } from "./site-DQnysZ3W.mjs";
|
|
4
4
|
import { i as runRefresh, r as notifyOfUpdate } from "./version-check-Cuh6HaLX.mjs";
|
|
5
5
|
import { defineCommand, runMain } from "citty";
|
|
6
6
|
//#region src/index.ts
|
|
@@ -73,24 +73,24 @@ const main = defineCommand({
|
|
|
73
73
|
description: "canopy — create and deploy vibecoded projects"
|
|
74
74
|
},
|
|
75
75
|
subCommands: {
|
|
76
|
-
login: () => import("./login-
|
|
77
|
-
whoami: () => import("./login-
|
|
78
|
-
logout: () => import("./login-
|
|
79
|
-
create: () => import("./projects-
|
|
80
|
-
init: () => import("./projects-
|
|
81
|
-
projects: () => import("./projects-
|
|
82
|
-
delete: () => import("./projects-
|
|
83
|
-
dev: () => import("./dev-
|
|
84
|
-
deploy: () => import("./deploy-
|
|
85
|
-
rollback: () => import("./deploy-
|
|
86
|
-
download: () => import("./deploy-
|
|
87
|
-
token: () => import("./token-
|
|
88
|
-
db: () => import("./db-
|
|
89
|
-
usage: () => import("./usage-
|
|
90
|
-
open: () => import("./open-
|
|
91
|
-
docs: () => import("./docs-
|
|
92
|
-
skill: () => import("./skill-
|
|
93
|
-
upgrade: () => import("./upgrade-
|
|
76
|
+
login: () => import("./login-Cb8D8a1Z.mjs").then((m) => m.loginCommand),
|
|
77
|
+
whoami: () => import("./login-Cb8D8a1Z.mjs").then((m) => m.whoamiCommand),
|
|
78
|
+
logout: () => import("./login-Cb8D8a1Z.mjs").then((m) => m.logoutCommand),
|
|
79
|
+
create: () => import("./projects-CoGh8m9A.mjs").then((m) => m.createCommand),
|
|
80
|
+
init: () => import("./projects-CoGh8m9A.mjs").then((m) => m.initCommand),
|
|
81
|
+
projects: () => import("./projects-CoGh8m9A.mjs").then((m) => m.projectsCommand),
|
|
82
|
+
delete: () => import("./projects-CoGh8m9A.mjs").then((m) => m.deleteCommand),
|
|
83
|
+
dev: () => import("./dev-DV5ud7AJ.mjs").then((m) => m.devCommand),
|
|
84
|
+
deploy: () => import("./deploy-DdSSL227.mjs").then((m) => m.deployCommand),
|
|
85
|
+
rollback: () => import("./deploy-DdSSL227.mjs").then((m) => m.rollbackCommand),
|
|
86
|
+
download: () => import("./deploy-DdSSL227.mjs").then((m) => m.downloadCommand),
|
|
87
|
+
token: () => import("./token-CWgp_Lqe.mjs").then((m) => m.tokenCommand),
|
|
88
|
+
db: () => import("./db-BpnjQUkF.mjs").then((m) => m.dbCommand),
|
|
89
|
+
usage: () => import("./usage-CYQRLXwf.mjs").then((m) => m.usageCommand),
|
|
90
|
+
open: () => import("./open-BmgYBSdR.mjs").then((m) => m.openCommand),
|
|
91
|
+
docs: () => import("./docs-Cept8t_3.mjs").then((m) => m.docsCommand),
|
|
92
|
+
skill: () => import("./skill-192EdixC.mjs").then((m) => m.skillCommand),
|
|
93
|
+
upgrade: () => import("./upgrade-BrHSSg2e.mjs").then((m) => m.upgradeCommand),
|
|
94
94
|
logs: planned("logs", "Tail worker logs", "2")
|
|
95
95
|
}
|
|
96
96
|
});
|