@mediamonks/canopy-cli 0.5.1 → 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.
Files changed (39) hide show
  1. package/dist/{db-BW1ZolWn.mjs → db-BpnjQUkF.mjs} +42 -3
  2. package/dist/{deploy-alC2--pZ.mjs → deploy-DdSSL227.mjs} +29 -2
  3. package/dist/dev-DV5ud7AJ.mjs +2 -0
  4. package/dist/{dev-CQQhMOoD.mjs → dev-OxeYndd1.mjs} +24 -9
  5. package/dist/docs/ci.md +1 -3
  6. package/dist/docs/cli.md +3 -4
  7. package/dist/docs/data.md +5 -4
  8. package/dist/docs/deploy.md +1 -3
  9. package/dist/docs/dev.md +13 -11
  10. package/dist/docs/getting-started.md +1 -3
  11. package/dist/docs/infrastructure.md +27 -15
  12. package/dist/docs/migrations.md +166 -0
  13. package/dist/docs/porting-nextjs.md +232 -0
  14. package/dist/docs/sdk-ai.md +103 -0
  15. package/dist/docs/sdk-auth.md +57 -0
  16. package/dist/docs/sdk-database.md +126 -0
  17. package/dist/docs/sdk-files.md +82 -0
  18. package/dist/docs/sdk-services.md +58 -0
  19. package/dist/docs/sdk.md +113 -0
  20. package/dist/docs/static-serving.md +4 -1
  21. package/dist/docs/troubleshooting.md +1 -3
  22. package/dist/{docs-DvSPpSIR.mjs → docs-Cept8t_3.mjs} +23 -10
  23. package/dist/index.mjs +20 -20
  24. package/dist/{local-C5-GWJcx.mjs → local-D9OVEHDg.mjs} +363 -39
  25. package/dist/{login-BO6abf40.mjs → login-Cb8D8a1Z.mjs} +5 -3
  26. package/dist/migrations-B0kJarjr.mjs +50 -0
  27. package/dist/{open-DnlYDqrU.mjs → open-BmgYBSdR.mjs} +2 -2
  28. package/dist/{package-CHoZKzaw.mjs → package-D1wDsLSU.mjs} +1 -1
  29. package/dist/{projects-ijfe7Ffx.mjs → projects-CoGh8m9A.mjs} +17 -9
  30. package/dist/{site-BJjoePQS.mjs → site-DQnysZ3W.mjs} +8 -1
  31. package/dist/skill/canopy/SKILL.md +18 -1
  32. package/dist/{skill-CW0QSbAJ.mjs → skill-192EdixC.mjs} +2 -2
  33. package/dist/templates/worker/AGENTS.md +3 -0
  34. package/dist/{token-BeF9zczg.mjs → token-CWgp_Lqe.mjs} +2 -2
  35. package/dist/{ui-DThlyHgo.mjs → ui-BhzMeKQl.mjs} +1 -1
  36. package/dist/{upgrade-CGz1yzsW.mjs → upgrade-BrHSSg2e.mjs} +2 -2
  37. package/dist/{usage-DQpH7A3X.mjs → usage-CYQRLXwf.mjs} +2 -2
  38. package/package.json +3 -3
  39. package/dist/dev-BJ5xEwqW.mjs +0 -2
@@ -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
- # How static files are served
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
- summary: Common errors and how to resolve them.
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-BJjoePQS.mjs";
2
- import { n as reportErrors } from "./ui-DThlyHgo.mjs";
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
- /** The bundled snapshot dir: dist/docs when published, repo docs/guides in dev. */
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 [join(base, "docs"), join(base, "..", "..", "..", "docs", "guides")]) if (existsSync(candidate)) return candidate;
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: …\nsummary: …\n---\n<body>` frontmatter. */
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.endsWith(".md")).map((f) => {
100
- const slug = f.replace(/\.md$/, "");
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 file = dir && join(dir, `${slug}.md`);
115
- if (file && existsSync(file)) return parseFrontmatter(readFileSync(file, "utf8")).body;
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-CHoZKzaw.mjs";
3
- import { r as siteHeaderLine, t as setInvocationProfile } from "./site-BJjoePQS.mjs";
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-BO6abf40.mjs").then((m) => m.loginCommand),
77
- whoami: () => import("./login-BO6abf40.mjs").then((m) => m.whoamiCommand),
78
- logout: () => import("./login-BO6abf40.mjs").then((m) => m.logoutCommand),
79
- create: () => import("./projects-ijfe7Ffx.mjs").then((m) => m.createCommand),
80
- init: () => import("./projects-ijfe7Ffx.mjs").then((m) => m.initCommand),
81
- projects: () => import("./projects-ijfe7Ffx.mjs").then((m) => m.projectsCommand),
82
- delete: () => import("./projects-ijfe7Ffx.mjs").then((m) => m.deleteCommand),
83
- dev: () => import("./dev-BJ5xEwqW.mjs").then((m) => m.devCommand),
84
- deploy: () => import("./deploy-alC2--pZ.mjs").then((m) => m.deployCommand),
85
- rollback: () => import("./deploy-alC2--pZ.mjs").then((m) => m.rollbackCommand),
86
- download: () => import("./deploy-alC2--pZ.mjs").then((m) => m.downloadCommand),
87
- token: () => import("./token-BeF9zczg.mjs").then((m) => m.tokenCommand),
88
- db: () => import("./db-BW1ZolWn.mjs").then((m) => m.dbCommand),
89
- usage: () => import("./usage-DQpH7A3X.mjs").then((m) => m.usageCommand),
90
- open: () => import("./open-DnlYDqrU.mjs").then((m) => m.openCommand),
91
- docs: () => import("./docs-DvSPpSIR.mjs").then((m) => m.docsCommand),
92
- skill: () => import("./skill-CW0QSbAJ.mjs").then((m) => m.skillCommand),
93
- upgrade: () => import("./upgrade-CGz1yzsW.mjs").then((m) => m.upgradeCommand),
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
  });