@mediamonks/canopy-cli 0.7.5 → 0.7.6
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/docs/cli-create.md +46 -0
- package/dist/docs/cli-db.md +86 -0
- package/dist/docs/cli-delete.md +44 -0
- package/dist/docs/cli-deploy.md +58 -0
- package/dist/docs/cli-dev.md +63 -0
- package/dist/docs/cli-docs.md +47 -0
- package/dist/docs/cli-download.md +42 -0
- package/dist/docs/cli-egress.md +37 -0
- package/dist/docs/cli-index.md +61 -0
- package/dist/docs/cli-init.md +46 -0
- package/dist/docs/cli-login.md +43 -0
- package/dist/docs/cli-logout.md +32 -0
- package/dist/docs/cli-logs.md +57 -0
- package/dist/docs/cli-open.md +41 -0
- package/dist/docs/cli-projects.md +43 -0
- package/dist/docs/cli-promote.md +44 -0
- package/dist/docs/cli-rollback.md +45 -0
- package/dist/docs/cli-skill.md +39 -0
- package/dist/docs/cli-staging.md +54 -0
- package/dist/docs/cli-token.md +47 -0
- package/dist/docs/cli-upgrade.md +43 -0
- package/dist/docs/cli-usage.md +43 -0
- package/dist/docs/cli-whoami.md +46 -0
- package/dist/docs/developers-agents-and-ai.md +95 -0
- package/dist/docs/developers-architecture.md +106 -0
- package/dist/docs/index.json +321 -0
- package/dist/docs/infrastructure.md +5 -8
- package/dist/docs/overview-faq.md +105 -0
- package/dist/docs/overview-how-it-works.md +81 -0
- package/dist/docs/overview-use-cases.md +76 -0
- package/dist/docs/overview-what-is-canopy.md +75 -0
- package/dist/docs/platform-ai-usage.md +65 -0
- package/dist/docs/platform-client-access.md +53 -0
- package/dist/docs/platform-deploys-and-rollbacks.md +93 -0
- package/dist/docs/platform-egress.md +75 -0
- package/dist/docs/platform-members-and-access.md +71 -0
- package/dist/docs/platform-project-data.md +66 -0
- package/dist/docs/platform-schedules.md +90 -0
- package/dist/docs/platform-sites-and-profiles.md +61 -0
- package/dist/docs/static-serving.md +5 -8
- package/dist/docs/troubleshooting.md +6 -8
- package/dist/{docs-DQmUReJR.mjs → docs-DhAz6QWS.mjs} +42 -20
- package/dist/index.mjs +9 -9
- package/dist/{logs-DBLDS3aQ.mjs → logs-DuzM-_Rn.mjs} +1 -1
- package/dist/{package-yVoO2pq4.mjs → package-CPXtNnfc.mjs} +1 -1
- package/dist/{projects-D2fDeWNU.mjs → projects-C9TBoiW7.mjs} +5 -8
- package/dist/skill/canopy/SKILL.md +10 -8
- package/dist/{skill-12Ga-2UT.mjs → skill-BCX4cUeP.mjs} +1 -1
- package/dist/templates/worker/AGENTS.md +0 -3
- package/dist/templates/worker/README.md +1 -5
- package/dist/{upgrade-Du6U0xjp.mjs → upgrade-jLu7jI0r.mjs} +1 -1
- package/package.json +3 -3
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Sites & profiles
|
|
3
|
+
description: Canopy runs as more than one installation. Most people only ever use main — here's what the others are and how the CLI keeps them straight.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Canopy isn't a single installation: the same platform runs as a few separate
|
|
7
|
+
**sites**, each with its own domain, its own projects, and its own data.
|
|
8
|
+
Unless someone told you otherwise, **you're on `main` and can stop reading
|
|
9
|
+
here** — the rest of this page explains what the other sites are for and how
|
|
10
|
+
the CLI targets them.
|
|
11
|
+
|
|
12
|
+
## The sites
|
|
13
|
+
|
|
14
|
+
| Site | Project URLs | What it's for |
|
|
15
|
+
| ----------- | --------------------- | ------------------------------------------------- |
|
|
16
|
+
| `main` | `*.canopy.website` | The real platform. Your projects live here. |
|
|
17
|
+
| `staging` | `*.thanarie.dev` | The platform team's testbed for platform changes. |
|
|
18
|
+
| `gcp-pilot` | `*.canopy.monks.zone` | A pilot running on Google Cloud. |
|
|
19
|
+
|
|
20
|
+
Sites are fully separate: a project on `main` doesn't exist on `staging`, and
|
|
21
|
+
signing in to one doesn't create anything on another. They can also differ in
|
|
22
|
+
what they support — the dashboard simply hides features a site can't honor,
|
|
23
|
+
and the API says `not_supported_on_target` if asked directly.
|
|
24
|
+
|
|
25
|
+
## What a profile is
|
|
26
|
+
|
|
27
|
+
A **profile** is the CLI's name for a site. `canopy login` signs you in once
|
|
28
|
+
via OneLogin and stores a credential for every site in one go; the profile
|
|
29
|
+
just picks which one a command talks to.
|
|
30
|
+
|
|
31
|
+
Three things decide the effective profile, in order:
|
|
32
|
+
|
|
33
|
+
1. `--profile <id>` on the command (or the `CANOPY_PROFILE` environment
|
|
34
|
+
variable) — one-off override.
|
|
35
|
+
2. The `"profile"` pinned in the project's `canopy.json` — set when the
|
|
36
|
+
project was created, so a checkout always targets the site it belongs to.
|
|
37
|
+
3. Your default active profile — `main` unless you chose otherwise at login.
|
|
38
|
+
|
|
39
|
+
Every command prints the site it's talking to in its header line, and
|
|
40
|
+
`canopy whoami` shows your email, active profile, and API host — so there's
|
|
41
|
+
never a mystery about where a deploy just went.
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
canopy whoami # who am I, on which site?
|
|
45
|
+
canopy --profile gcp-pilot deploy # one command against the pilot
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## When you'd actually care
|
|
49
|
+
|
|
50
|
+
- **Never, usually.** Day-to-day work on your own projects is all `main`,
|
|
51
|
+
and the `canopy.json` pin keeps it that way without any flags.
|
|
52
|
+
- **Trying backend features.** Custom backends and
|
|
53
|
+
[schedules](https://docs.canopy.website/platform/schedules) currently run on `gcp-pilot`, so
|
|
54
|
+
experiments with those happen there.
|
|
55
|
+
- **Helping test platform changes.** If the platform team asks you to check
|
|
56
|
+
something on `staging`, `--profile staging` points any command at it.
|
|
57
|
+
|
|
58
|
+
> **Note:** Deploying to a different site publishes a **separate copy** of the project there — it doesn't move
|
|
59
|
+
> or mirror the one on `main`. Each site keeps its own versions, data, and members.
|
|
60
|
+
|
|
61
|
+
- [CLI reference](https://docs.canopy.website/guides/cli) — Every canopy command, including login, whoami, and the --profile flag.
|
|
@@ -7,12 +7,10 @@ This explains the full path a browser request takes to receive a deployed
|
|
|
7
7
|
project's `index.html` and its assets (JS/CSS/images/fonts), and reviews that
|
|
8
8
|
path from a **security** and a **performance** angle.
|
|
9
9
|
|
|
10
|
-
Everything a visitor sees is a _deployed_ build stored in R2
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
Cloudflare, per-project workers remain a Phase 2 TODO and the capability is
|
|
15
|
-
gated off.)
|
|
10
|
+
Everything a visitor sees is a _deployed_ build stored in R2 — the platform
|
|
11
|
+
serves pre-built static files. (When a project has a backend worker, `/api/*`
|
|
12
|
+
requests dispatch to it — Workers for Platforms on Cloudflare, Cloud Run on
|
|
13
|
+
GCP; everything else is still static serving.)
|
|
16
14
|
|
|
17
15
|
Asset serving is the hottest **data-plane** path (see
|
|
18
16
|
[`docs/09-plane-split.md`](../09-plane-split.md)): it lives at
|
|
@@ -213,8 +211,7 @@ These are observations, not known incidents — worth a conscious decision:
|
|
|
213
211
|
runs on a `*.canopy.<domain>` origin that shares the session-cookie domain.
|
|
214
212
|
The session cookie's flags (HttpOnly/SameSite/Secure) in
|
|
215
213
|
[`session.ts`](../../apps/gateway/src/session.ts) are the mitigation to keep an
|
|
216
|
-
eye on;
|
|
217
|
-
stronger fix.
|
|
214
|
+
eye on; per-project origin isolation would be the stronger fix.
|
|
218
215
|
3. **SPA fallback returns `index.html` with 200 for unknown paths**, including
|
|
219
216
|
things that look like missing assets (`/assets/typo.js` → HTML). That's normal
|
|
220
217
|
SPA behavior but can mask 404s and, combined with #1, is another reason
|
|
@@ -47,11 +47,11 @@ The `CANOPY_DEPLOY_TOKEN` was revoked or is wrong. Create a new one with
|
|
|
47
47
|
|
|
48
48
|
## `not_supported_on_target` (501)
|
|
49
49
|
|
|
50
|
-
Some features are platform-target-specific:
|
|
51
|
-
per-project worker backends, dashboard
|
|
52
|
-
|
|
53
|
-
failure of your project — the feature doesn't exist
|
|
54
|
-
runs on.
|
|
50
|
+
Some features are platform-target-specific: a site whose platform target
|
|
51
|
+
doesn't support a feature (for example per-project worker backends, dashboard
|
|
52
|
+
schedules, or secret injection) answers this error, and the dashboard hides
|
|
53
|
+
those panels. It's not a failure of your project — the feature doesn't exist
|
|
54
|
+
on the target your platform runs on.
|
|
55
55
|
|
|
56
56
|
## `ai_budget_exceeded` (429)
|
|
57
57
|
|
|
@@ -73,9 +73,7 @@ canopy egress allow api.example.com
|
|
|
73
73
|
|
|
74
74
|
or use dashboard Settings → Outbound hosts. Details and entry rules:
|
|
75
75
|
[Outbound requests](https://docs.canopy.website/platform/egress). Calls made through the SDK /
|
|
76
|
-
`env.CANOPY.fetch` don't count as egress and are unaffected.
|
|
77
|
-
target outbound requests are currently unrestricted, so this error only
|
|
78
|
-
appears on GCP.
|
|
76
|
+
`env.CANOPY.fetch` don't count as egress and are unaffected.
|
|
79
77
|
|
|
80
78
|
## The worker returns errors and you can't see why
|
|
81
79
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as version } from "./package-
|
|
1
|
+
import { t as version } from "./package-CPXtNnfc.mjs";
|
|
2
2
|
import { n as siteConfig } from "./site-BUlQR117.mjs";
|
|
3
3
|
import { i as latestKnownVersion, r as compareSemver } from "./version-check-CcsA1QjI.mjs";
|
|
4
4
|
import { n as reportErrors } from "./ui-CpdtK7mt.mjs";
|
|
@@ -9,9 +9,10 @@ import { join } from "node:path";
|
|
|
9
9
|
import { fileURLToPath } from "node:url";
|
|
10
10
|
//#region src/docs.ts
|
|
11
11
|
/**
|
|
12
|
-
* canopy docs —
|
|
13
|
-
* (GET /control/v1/docs) so
|
|
14
|
-
* snapshot as offline
|
|
12
|
+
* canopy docs — the full docs site in the terminal (guides, platform, CLI and
|
|
13
|
+
* SDK references), served live by the platform (GET /control/v1/docs) so it
|
|
14
|
+
* never goes stale, with a local cache and a bundled snapshot as offline
|
|
15
|
+
* fallbacks. The listing is grouped per docs-site section.
|
|
15
16
|
*
|
|
16
17
|
* The platform requires a login for docs (internal platform), so fetches carry
|
|
17
18
|
* the CLI token when there is one; without it we simply fall back to the
|
|
@@ -98,15 +99,25 @@ function writeCache(file, content) {
|
|
|
98
99
|
writeFileSync(join(cacheDir, file), content);
|
|
99
100
|
} catch {}
|
|
100
101
|
}
|
|
101
|
-
/**
|
|
102
|
-
function
|
|
102
|
+
/** The index from cache, else from the bundled snapshot. */
|
|
103
|
+
function indexFromFallback() {
|
|
103
104
|
try {
|
|
104
105
|
const cached = JSON.parse(readFileSync(indexPath, "utf8"));
|
|
105
|
-
if (cached.topics?.length) return
|
|
106
|
+
if (cached.topics?.length) return {
|
|
107
|
+
topics: cached.topics,
|
|
108
|
+
sections: cached.sections
|
|
109
|
+
};
|
|
106
110
|
} catch {}
|
|
107
111
|
const dir = bundledDocsDir();
|
|
108
|
-
if (!dir) return [];
|
|
109
|
-
|
|
112
|
+
if (!dir) return { topics: [] };
|
|
113
|
+
try {
|
|
114
|
+
const { sections } = JSON.parse(readFileSync(join(dir, "index.json"), "utf8"));
|
|
115
|
+
if (sections?.length) return {
|
|
116
|
+
topics: sections.flatMap((s) => s.topics),
|
|
117
|
+
sections
|
|
118
|
+
};
|
|
119
|
+
} catch {}
|
|
120
|
+
return { topics: readdirSync(dir).filter((f) => /\.mdx?$/.test(f)).map((f) => {
|
|
110
121
|
const slug = f.replace(/\.mdx?$/, "");
|
|
111
122
|
const { title, summary } = parseFrontmatter(readFileSync(join(dir, f), "utf8"));
|
|
112
123
|
return {
|
|
@@ -114,7 +125,7 @@ function topicsFromFallback() {
|
|
|
114
125
|
title: title ?? slug,
|
|
115
126
|
summary: summary ?? ""
|
|
116
127
|
};
|
|
117
|
-
});
|
|
128
|
+
}) };
|
|
118
129
|
}
|
|
119
130
|
/** A single doc's body from cache, else from the bundled snapshot. */
|
|
120
131
|
function bodyFromFallback(slug) {
|
|
@@ -129,7 +140,7 @@ function bodyFromFallback(slug) {
|
|
|
129
140
|
}
|
|
130
141
|
}
|
|
131
142
|
}
|
|
132
|
-
async function
|
|
143
|
+
async function listIndex(refresh) {
|
|
133
144
|
const { apiUrl } = siteConfig();
|
|
134
145
|
if (refresh || !cacheFresh()) {
|
|
135
146
|
const data = await fetchJson(`${apiUrl}/control/v1/docs`);
|
|
@@ -138,10 +149,10 @@ async function listTopics(refresh) {
|
|
|
138
149
|
fetchedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
139
150
|
...data
|
|
140
151
|
}));
|
|
141
|
-
return data
|
|
152
|
+
return data;
|
|
142
153
|
}
|
|
143
154
|
}
|
|
144
|
-
return
|
|
155
|
+
return indexFromFallback();
|
|
145
156
|
}
|
|
146
157
|
async function readTopic(slug, refresh) {
|
|
147
158
|
const { apiUrl } = siteConfig();
|
|
@@ -157,7 +168,7 @@ async function readTopic(slug, refresh) {
|
|
|
157
168
|
const docsCommand = defineCommand({
|
|
158
169
|
meta: {
|
|
159
170
|
name: "docs",
|
|
160
|
-
description: "Read the Canopy
|
|
171
|
+
description: "Read the Canopy docs (offline-capable)"
|
|
161
172
|
},
|
|
162
173
|
args: {
|
|
163
174
|
topic: {
|
|
@@ -177,16 +188,27 @@ const docsCommand = defineCommand({
|
|
|
177
188
|
const latest = latestKnownVersion();
|
|
178
189
|
if (latest && compareSemver(version, latest) < 0) console.error(`Note: these docs describe CLI ${latest}; you have ${version} — commands or flags they mention may not exist here. Run \`canopy upgrade\`.\n`);
|
|
179
190
|
if (!topic) {
|
|
180
|
-
const
|
|
181
|
-
if (topics.length === 0) throw new Error("No docs available — check your connection, or reinstall the CLI.");
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
191
|
+
const index = await listIndex(refresh);
|
|
192
|
+
if (index.topics.length === 0) throw new Error("No docs available — check your connection, or reinstall the CLI.");
|
|
193
|
+
console.log("Canopy docs — run `canopy docs <topic>`:");
|
|
194
|
+
const groups = index.sections?.length ? index.sections : [{
|
|
195
|
+
key: "all",
|
|
196
|
+
title: "",
|
|
197
|
+
description: "",
|
|
198
|
+
topics: index.topics
|
|
199
|
+
}];
|
|
200
|
+
for (const section of groups) {
|
|
201
|
+
const heading = [section.title, section.description].filter(Boolean).join(" — ");
|
|
202
|
+
console.log(heading ? `\n${heading}` : "");
|
|
203
|
+
const width = Math.max(...section.topics.map((t) => t.slug.length));
|
|
204
|
+
for (const t of section.topics) console.log(` ${t.slug.padEnd(width)} ${t.summary}`);
|
|
205
|
+
}
|
|
185
206
|
return;
|
|
186
207
|
}
|
|
187
208
|
const body = await readTopic(topic, refresh);
|
|
188
209
|
if (body === void 0) {
|
|
189
|
-
const
|
|
210
|
+
const { topics } = await listIndex(false);
|
|
211
|
+
const known = topics.map((t) => t.slug).join(", ");
|
|
190
212
|
throw new Error(`No doc "${topic}".${known ? ` Available: ${known}.` : ""}`);
|
|
191
213
|
}
|
|
192
214
|
console.log(body);
|
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { t as version } from "./package-
|
|
2
|
+
import { t as version } from "./package-CPXtNnfc.mjs";
|
|
3
3
|
import { r as siteHeaderLine, t as setInvocationProfile } from "./site-BUlQR117.mjs";
|
|
4
4
|
import { a as notifyOfUpdate, o as runRefresh } from "./version-check-CcsA1QjI.mjs";
|
|
5
5
|
import { defineCommand, runMain } from "citty";
|
|
@@ -63,10 +63,10 @@ const main = defineCommand({
|
|
|
63
63
|
login: () => import("./login-C1x1l3qg.mjs").then((m) => m.loginCommand),
|
|
64
64
|
whoami: () => import("./login-C1x1l3qg.mjs").then((m) => m.whoamiCommand),
|
|
65
65
|
logout: () => import("./login-C1x1l3qg.mjs").then((m) => m.logoutCommand),
|
|
66
|
-
create: () => import("./projects-
|
|
67
|
-
init: () => import("./projects-
|
|
68
|
-
projects: () => import("./projects-
|
|
69
|
-
delete: () => import("./projects-
|
|
66
|
+
create: () => import("./projects-C9TBoiW7.mjs").then((m) => m.createCommand),
|
|
67
|
+
init: () => import("./projects-C9TBoiW7.mjs").then((m) => m.initCommand),
|
|
68
|
+
projects: () => import("./projects-C9TBoiW7.mjs").then((m) => m.projectsCommand),
|
|
69
|
+
delete: () => import("./projects-C9TBoiW7.mjs").then((m) => m.deleteCommand),
|
|
70
70
|
dev: () => import("./dev-D6Sy15Wv.mjs").then((m) => m.devCommand),
|
|
71
71
|
deploy: () => import("./deploy-ClioT9ZC.mjs").then((m) => m.deployCommand),
|
|
72
72
|
promote: () => import("./deploy-ClioT9ZC.mjs").then((m) => m.promoteCommand),
|
|
@@ -78,10 +78,10 @@ const main = defineCommand({
|
|
|
78
78
|
db: () => import("./db-D3RKaLVU.mjs").then((m) => m.dbCommand),
|
|
79
79
|
usage: () => import("./usage-wpew0a2M.mjs").then((m) => m.usageCommand),
|
|
80
80
|
open: () => import("./open-CLSh6oez.mjs").then((m) => m.openCommand),
|
|
81
|
-
docs: () => import("./docs-
|
|
82
|
-
skill: () => import("./skill-
|
|
83
|
-
upgrade: () => import("./upgrade-
|
|
84
|
-
logs: () => import("./logs-
|
|
81
|
+
docs: () => import("./docs-DhAz6QWS.mjs").then((m) => m.docsCommand),
|
|
82
|
+
skill: () => import("./skill-BCX4cUeP.mjs").then((m) => m.skillCommand),
|
|
83
|
+
upgrade: () => import("./upgrade-jLu7jI0r.mjs").then((m) => m.upgradeCommand),
|
|
84
|
+
logs: () => import("./logs-DuzM-_Rn.mjs").then((m) => m.logsCommand)
|
|
85
85
|
}
|
|
86
86
|
});
|
|
87
87
|
if (process.argv[2] !== "upgrade") notifyOfUpdate(version);
|
|
@@ -34,7 +34,7 @@ async function fetchLogs(slug, params) {
|
|
|
34
34
|
if (params.limit) query.set("limit", String(params.limit));
|
|
35
35
|
return withContext(apiRequest("GET", `/projects/${slug}/logs?${query}`), {
|
|
36
36
|
action: `read worker logs for "${slug}"`,
|
|
37
|
-
fixes: { not_supported_on_target: "Worker logs aren't available on this site yet
|
|
37
|
+
fixes: { not_supported_on_target: "Worker logs aren't available on this site yet." }
|
|
38
38
|
});
|
|
39
39
|
}
|
|
40
40
|
const logsCommand = defineCommand({
|
|
@@ -30,10 +30,9 @@ const TEMPLATES = [
|
|
|
30
30
|
},
|
|
31
31
|
{
|
|
32
32
|
id: "worker",
|
|
33
|
-
label: "React SPA + backend
|
|
34
|
-
hint: "
|
|
35
|
-
needsInstall: true
|
|
36
|
-
hidden: true
|
|
33
|
+
label: "React SPA + backend worker",
|
|
34
|
+
hint: "spa plus server-side handlers in worker/ — for secrets, schedules, external APIs",
|
|
35
|
+
needsInstall: true
|
|
37
36
|
}
|
|
38
37
|
];
|
|
39
38
|
function templatesRoot() {
|
|
@@ -134,7 +133,7 @@ const createCommand = defineCommand({
|
|
|
134
133
|
if (scaffold && !args.template && process.stdin.isTTY) {
|
|
135
134
|
const id = unwrap(await p.select({
|
|
136
135
|
message: "Template",
|
|
137
|
-
options: TEMPLATES.
|
|
136
|
+
options: TEMPLATES.map((t) => ({
|
|
138
137
|
value: t.id,
|
|
139
138
|
label: t.label,
|
|
140
139
|
hint: t.hint
|
|
@@ -142,7 +141,6 @@ const createCommand = defineCommand({
|
|
|
142
141
|
}));
|
|
143
142
|
template = TEMPLATES.find((t) => t.id === id) ?? template;
|
|
144
143
|
}
|
|
145
|
-
if (template.hidden) p.log.warn("Per-project Workers are not deployable yet (Phase 2) — `canopy deploy` publishes the frontend only for now.");
|
|
146
144
|
const targetDir = resolve(slug);
|
|
147
145
|
if (scaffold && existsSync(targetDir)) throw new Error(`Directory ./${slug} already exists — remove it, or use --no-scaffold to only register.`);
|
|
148
146
|
const spin = p.spinner();
|
|
@@ -241,7 +239,7 @@ const initCommand = defineCommand({
|
|
|
241
239
|
if (!args.template && process.stdin.isTTY) {
|
|
242
240
|
const id = unwrap(await p.select({
|
|
243
241
|
message: "Template",
|
|
244
|
-
options: TEMPLATES.
|
|
242
|
+
options: TEMPLATES.map((t) => ({
|
|
245
243
|
value: t.id,
|
|
246
244
|
label: t.label,
|
|
247
245
|
hint: t.hint
|
|
@@ -249,7 +247,6 @@ const initCommand = defineCommand({
|
|
|
249
247
|
}));
|
|
250
248
|
template = TEMPLATES.find((t) => t.id === id) ?? template;
|
|
251
249
|
}
|
|
252
|
-
if (template.hidden) p.log.warn("Per-project Workers are not deployable yet (Phase 2) — `canopy deploy` publishes the frontend only for now.");
|
|
253
250
|
scaffoldTemplate(template.id, targetDir, {
|
|
254
251
|
name: project.name,
|
|
255
252
|
slug
|
|
@@ -75,10 +75,12 @@ canopy docs # list all topics
|
|
|
75
75
|
canopy docs deploy # print one topic
|
|
76
76
|
```
|
|
77
77
|
|
|
78
|
-
|
|
79
|
-
`
|
|
80
|
-
|
|
81
|
-
|
|
78
|
+
The listing is grouped by section: guides (`getting-started`, `deploy`, `dev`,
|
|
79
|
+
`data`, `ci`, `troubleshooting`, …), platform feature pages (`platform-*`), a
|
|
80
|
+
per-command CLI reference (`cli-<command>`, plus `cli` for the one-page
|
|
81
|
+
overview), and the SDK reference (`sdk-*`). Always prefer `canopy docs` and
|
|
82
|
+
`canopy <command> --help` over guessing flags — they are the source of truth
|
|
83
|
+
and never go stale.
|
|
82
84
|
|
|
83
85
|
## Command map (quick orientation — see `canopy docs cli` for detail)
|
|
84
86
|
|
|
@@ -133,10 +135,10 @@ Prefer **`spa`** the moment either is true — which covers most real projects:
|
|
|
133
135
|
- **The app will grow.** `spa` is Vite + React + TypeScript, giving components,
|
|
134
136
|
routing, and state a real home; `static` has no build step to organise into.
|
|
135
137
|
|
|
136
|
-
Use **`worker`**
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
138
|
+
Use **`worker`** when you need server-side backend code — e.g. calling an
|
|
139
|
+
external API with a secret the browser can't hold, or running scheduled jobs.
|
|
140
|
+
It's `spa` plus Hono-style handlers in `worker/`; `canopy deploy` publishes
|
|
141
|
+
the frontend and the worker together.
|
|
140
142
|
|
|
141
143
|
Non-interactive: `canopy create "My App" --template spa`. **When unsure between
|
|
142
144
|
`static` and `spa`, pick `spa`.**
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as version } from "./package-
|
|
1
|
+
import { t as version } from "./package-CPXtNnfc.mjs";
|
|
2
2
|
import { n as reportErrors } from "./ui-CpdtK7mt.mjs";
|
|
3
3
|
import { defineCommand } from "citty";
|
|
4
4
|
import { cpSync, existsSync, mkdirSync, rmSync, symlinkSync, writeFileSync } from "node:fs";
|
|
@@ -5,9 +5,6 @@ A Vite + React single-page app plus a per-project backend Worker on
|
|
|
5
5
|
projects. Everything is served behind company login; there is no
|
|
6
6
|
infrastructure to manage and none should be added.
|
|
7
7
|
|
|
8
|
-
> On GCP, `canopy deploy` publishes the frontend **and** the `worker/`
|
|
9
|
-
> backend (Cloud Run). Cloudflare WfP upload is still Phase 2.
|
|
10
|
-
|
|
11
8
|
## Canopy skill
|
|
12
9
|
|
|
13
10
|
The full SDK reference (auth, db, blob, ai) and platform guardrails ship as an
|
|
@@ -4,14 +4,10 @@ A [Canopy](https://dashboard.thanarie.dev) project — Vite + React with a
|
|
|
4
4
|
backend Worker (`worker/`). Live (behind company login) at
|
|
5
5
|
https://{{slug}}.thanarie.dev after the first `canopy deploy`.
|
|
6
6
|
|
|
7
|
-
> Per-project Workers are a Canopy Phase 2 capability: `canopy deploy`
|
|
8
|
-
> publishes the frontend only for now — `worker/` deploys automatically once
|
|
9
|
-
> the platform supports it.
|
|
10
|
-
|
|
11
7
|
- `npm install` — once, then:
|
|
12
8
|
- `canopy dev` — develop locally against the live platform (real data!)
|
|
13
9
|
- `canopy dev --local [--pull]` — offline sandbox with local data instead
|
|
14
|
-
- `canopy deploy` — build and publish `
|
|
10
|
+
- `canopy deploy` — build and publish the frontend and the `worker/` backend
|
|
15
11
|
- `AGENTS.md` — project guardrails and skill setup for coding agents
|
|
16
12
|
|
|
17
13
|
Push-to-main deploys are prepared in `.github/workflows/canopy-deploy.yml`
|
|
@@ -112,7 +112,7 @@ const upgradeCommand = defineCommand({
|
|
|
112
112
|
const spec = `${PACKAGE_NAME}@${args.version ?? "latest"}`;
|
|
113
113
|
const command = pmCommand(pm);
|
|
114
114
|
const cmd = installArgs(pm, spec);
|
|
115
|
-
const { skillInstalled } = await import("./skill-
|
|
115
|
+
const { skillInstalled } = await import("./skill-BCX4cUeP.mjs");
|
|
116
116
|
const refreshSkill = skillInstalled(process.cwd());
|
|
117
117
|
console.log(`Upgrading via ${command}: ${command} ${cmd.join(" ")}`);
|
|
118
118
|
await run(command, cmd);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mediamonks/canopy-cli",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.6",
|
|
4
4
|
"description": "Canopy CLI — create, develop and deploy Canopy projects.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"canopy": "./dist/index.mjs"
|
|
@@ -20,8 +20,8 @@
|
|
|
20
20
|
"esbuild": "^0.28.1",
|
|
21
21
|
"hono": "^4.8.0",
|
|
22
22
|
"vite-server": "npm:vite@^7",
|
|
23
|
-
"@mediamonks/canopy": "0.0.
|
|
24
|
-
"@mediamonks/canopy
|
|
23
|
+
"@mediamonks/canopy-data-plane": "0.0.2",
|
|
24
|
+
"@mediamonks/canopy": "0.0.4"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
27
|
"@types/node": "^26.1.1",
|