@mediamonks/canopy-cli 0.4.0 → 0.4.2
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/api-BcX_w6Xf.mjs +58 -0
- package/dist/{db-CtUG4vfG.mjs → db-Bkq-_qkd.mjs} +1 -1
- package/dist/{deploy-N88l4Kt3.mjs → deploy-BO_-gK32.mjs} +55 -9
- package/dist/dev-DBoGaChx.mjs +2 -0
- package/dist/{dev-DTGW0lm0.mjs → dev-DdUJbbfA.mjs} +4 -4
- package/dist/docs/cli.md +3 -0
- package/dist/docs/data.md +17 -0
- package/dist/docs/deploy.md +15 -0
- package/dist/docs/getting-started.md +6 -1
- package/dist/index.mjs +13 -12
- package/dist/{local-951zc9qJ.mjs → local-Ba-pZf2O.mjs} +2 -2
- package/dist/{package-BNWFYk8y.mjs → package-CUjYxZem.mjs} +1 -1
- package/dist/{projects-DbKcG5kx.mjs → projects-oyjC4ZGb.mjs} +1 -1
- package/dist/skill/canopy/SKILL.md +20 -0
- package/dist/{skill-esz0u6PO.mjs → skill-CObIJMP5.mjs} +1 -1
- package/dist/{token-rTD9jxPZ.mjs → token-BXc2klNm.mjs} +1 -1
- package/dist/{upgrade-D_fgGLxu.mjs → upgrade-DxUNN79Q.mjs} +1 -1
- package/package.json +4 -4
- package/dist/api-CyJ-wBZh.mjs +0 -40
- package/dist/dev-D_GlUULk.mjs +0 -2
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { t as readConfig } from "./config-C-z7st1X.mjs";
|
|
2
|
+
//#region src/api.ts
|
|
3
|
+
/**
|
|
4
|
+
* Authenticated requests to the Canopy control plane. Credentials, in order:
|
|
5
|
+
* CANOPY_DEPLOY_TOKEN (CI — a project-scoped deploy token, docs/02-decisions.md
|
|
6
|
+
* D11), then the CLI token from `canopy login` (~/.canopy/config.json).
|
|
7
|
+
*/
|
|
8
|
+
var ApiError = class extends Error {
|
|
9
|
+
status;
|
|
10
|
+
code;
|
|
11
|
+
constructor(status, code, detail) {
|
|
12
|
+
super(detail ? `${code}: ${detail}` : code);
|
|
13
|
+
this.status = status;
|
|
14
|
+
this.code = code;
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
/** Resolve the bearer token: CI deploy token first (D11), then the login token. */
|
|
18
|
+
function credentials() {
|
|
19
|
+
const config = readConfig();
|
|
20
|
+
const deployToken = process.env["CANOPY_DEPLOY_TOKEN"];
|
|
21
|
+
const token = deployToken || config.token;
|
|
22
|
+
if (!token) throw new Error("Not logged in — run `canopy login` first.");
|
|
23
|
+
return {
|
|
24
|
+
apiUrl: config.apiUrl,
|
|
25
|
+
token,
|
|
26
|
+
isDeployToken: Boolean(deployToken)
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
/** Turn a non-2xx response into a thrown error (friendlier copy on 401). */
|
|
30
|
+
async function fail(response, isDeployToken) {
|
|
31
|
+
const payload = await response.json().catch(() => ({}));
|
|
32
|
+
if (response.status === 401) throw new Error(isDeployToken ? "Deploy token rejected — it may have been revoked. Create a new one in the dashboard (Settings → Deploy tokens) or with `canopy token create`." : "Session expired — run `canopy login` again.");
|
|
33
|
+
throw new ApiError(response.status, payload.error ?? `http_${response.status}`, payload.detail);
|
|
34
|
+
}
|
|
35
|
+
async function apiRequest(method, path, body, rawContentType, headers) {
|
|
36
|
+
const { apiUrl, token, isDeployToken } = credentials();
|
|
37
|
+
const isForm = body instanceof FormData;
|
|
38
|
+
const response = await fetch(`${apiUrl}/v1${path}`, {
|
|
39
|
+
method,
|
|
40
|
+
headers: {
|
|
41
|
+
authorization: `Bearer ${token}`,
|
|
42
|
+
...body === void 0 || isForm ? {} : { "content-type": rawContentType ?? "application/json" },
|
|
43
|
+
...headers
|
|
44
|
+
},
|
|
45
|
+
body: body === void 0 ? void 0 : isForm ? body : rawContentType !== void 0 ? body : JSON.stringify(body)
|
|
46
|
+
});
|
|
47
|
+
if (!response.ok) return fail(response, isDeployToken);
|
|
48
|
+
return await response.json();
|
|
49
|
+
}
|
|
50
|
+
/** Authenticated GET returning raw bytes (e.g. a deploy zip). */
|
|
51
|
+
async function apiDownload(path) {
|
|
52
|
+
const { apiUrl, token, isDeployToken } = credentials();
|
|
53
|
+
const response = await fetch(`${apiUrl}/v1${path}`, { headers: { authorization: `Bearer ${token}` } });
|
|
54
|
+
if (!response.ok) return fail(response, isDeployToken);
|
|
55
|
+
return Buffer.from(await response.arrayBuffer());
|
|
56
|
+
}
|
|
57
|
+
//#endregion
|
|
58
|
+
export { apiRequest as n, apiDownload as t };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { n as apiRequest } from "./api-BcX_w6Xf.mjs";
|
|
2
2
|
import { i as resolveProject, n as projectFlagArgs } from "./project-CpiPLNby.mjs";
|
|
3
3
|
import { i as unwrap, n as reportErrors, r as requireTty } from "./ui-C8VuPzGp.mjs";
|
|
4
4
|
import { defineCommand } from "citty";
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { n as apiRequest, t as apiDownload } from "./api-BcX_w6Xf.mjs";
|
|
2
2
|
import { a as tryReadCanopyJson, i as resolveProject, n as projectFlagArgs, r as projectUrl, t as projectDirArgs } from "./project-CpiPLNby.mjs";
|
|
3
3
|
import { i as unwrap, n as reportErrors } from "./ui-C8VuPzGp.mjs";
|
|
4
4
|
import { defineCommand } from "citty";
|
|
5
5
|
import { spawn } from "node:child_process";
|
|
6
|
-
import { readFileSync, readdirSync, statSync } from "node:fs";
|
|
6
|
+
import { readFileSync, readdirSync, statSync, writeFileSync } from "node:fs";
|
|
7
7
|
import { join, relative, resolve } from "node:path";
|
|
8
8
|
import * as p from "@clack/prompts";
|
|
9
9
|
//#region src/deploy.ts
|
|
@@ -97,17 +97,16 @@ const deployCommand = defineCommand({
|
|
|
97
97
|
spin.stop(`Live: ${projectUrl(slug)} (v${result.version})`);
|
|
98
98
|
})
|
|
99
99
|
});
|
|
100
|
-
/** Show the deploy history and ask which version to
|
|
101
|
-
async function pickVersion(deploys, live) {
|
|
102
|
-
const fallback = live !== null && live > 1 ? live - 1 : void 0;
|
|
100
|
+
/** Show the deploy history and ask which version to act on. */
|
|
101
|
+
async function pickVersion(deploys, live, message, initial) {
|
|
103
102
|
return unwrap(await p.select({
|
|
104
|
-
message
|
|
103
|
+
message,
|
|
105
104
|
options: deploys.map((deploy) => ({
|
|
106
105
|
value: deploy.version,
|
|
107
106
|
label: `v${deploy.version} ${deploy.created_at} (${deploy.source})`,
|
|
108
107
|
hint: deploy.version === live ? "live" : void 0
|
|
109
108
|
})),
|
|
110
|
-
initialValue:
|
|
109
|
+
initialValue: initial
|
|
111
110
|
}));
|
|
112
111
|
}
|
|
113
112
|
const rollbackCommand = defineCommand({
|
|
@@ -137,7 +136,7 @@ const rollbackCommand = defineCommand({
|
|
|
137
136
|
const { deploys, live } = await apiRequest("GET", `/projects/${slug}/deploys`);
|
|
138
137
|
if (deploys.length < 2) throw new Error("Nothing to roll back to — this project has fewer than two deploys.");
|
|
139
138
|
if (process.stdin.isTTY) {
|
|
140
|
-
target = await pickVersion(deploys, live);
|
|
139
|
+
target = await pickVersion(deploys, live, "Version to make live", live !== null && live > 1 ? live - 1 : void 0);
|
|
141
140
|
if (target === live) throw new Error(`v${target} is already live.`);
|
|
142
141
|
} else {
|
|
143
142
|
if (live === null || live < 2) throw new Error("Nothing to roll back to — no deploy exists before the live one.");
|
|
@@ -149,5 +148,52 @@ const rollbackCommand = defineCommand({
|
|
|
149
148
|
console.log(`Live: ${projectUrl(slug)} (v${result.version}, was v${result.previous ?? "?"})`);
|
|
150
149
|
})
|
|
151
150
|
});
|
|
151
|
+
const downloadCommand = defineCommand({
|
|
152
|
+
meta: {
|
|
153
|
+
name: "download",
|
|
154
|
+
description: "Download a deploy's assets as a zip (defaults to the live version)"
|
|
155
|
+
},
|
|
156
|
+
args: {
|
|
157
|
+
version: {
|
|
158
|
+
type: "positional",
|
|
159
|
+
description: "Deploy version to download, e.g. v3 (the live version when omitted)",
|
|
160
|
+
required: false
|
|
161
|
+
},
|
|
162
|
+
...projectFlagArgs,
|
|
163
|
+
dir: {
|
|
164
|
+
type: "string",
|
|
165
|
+
description: "Project directory (defaults to the current directory)"
|
|
166
|
+
},
|
|
167
|
+
out: {
|
|
168
|
+
type: "string",
|
|
169
|
+
description: "Output file (defaults to <slug>-v<version>.zip)"
|
|
170
|
+
}
|
|
171
|
+
},
|
|
172
|
+
run: reportErrors("download", async ({ args }) => {
|
|
173
|
+
const { slug } = resolveProject(args);
|
|
174
|
+
let target;
|
|
175
|
+
if (args.version !== void 0) {
|
|
176
|
+
if (!/^v?\d+$/.test(args.version)) throw new Error(`Invalid version "${args.version}" — expected a number like 3 or v3.`);
|
|
177
|
+
target = Number(args.version.replace(/^v/, ""));
|
|
178
|
+
} else {
|
|
179
|
+
const { deploys, live } = await apiRequest("GET", `/projects/${slug}/deploys`);
|
|
180
|
+
if (deploys.length === 0) throw new Error("Nothing to download — this project has no deploys yet.");
|
|
181
|
+
if (process.stdin.isTTY) target = await pickVersion(deploys, live, "Version to download", live ?? void 0);
|
|
182
|
+
else if (live !== null) target = live;
|
|
183
|
+
else throw new Error("No live version to download — pass a version, e.g. v1.");
|
|
184
|
+
}
|
|
185
|
+
const out = args.out ?? `${slug}-v${target}.zip`;
|
|
186
|
+
const spin = p.spinner();
|
|
187
|
+
spin.start(`Downloading ${slug} v${target}…`);
|
|
188
|
+
try {
|
|
189
|
+
const zip = await apiDownload(`/projects/${slug}/deploys/${target}/download`);
|
|
190
|
+
writeFileSync(out, zip);
|
|
191
|
+
spin.stop(`Wrote ${out} (${(zip.byteLength / 1024).toFixed(0)} kB).`);
|
|
192
|
+
} catch (err) {
|
|
193
|
+
spin.error("Download failed.");
|
|
194
|
+
throw err;
|
|
195
|
+
}
|
|
196
|
+
})
|
|
197
|
+
});
|
|
152
198
|
//#endregion
|
|
153
|
-
export { deployCommand, rollbackCommand };
|
|
199
|
+
export { deployCommand, downloadCommand, rollbackCommand };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { t as readConfig } from "./config-C-z7st1X.mjs";
|
|
2
|
-
import {
|
|
2
|
+
import { n as apiRequest } from "./api-BcX_w6Xf.mjs";
|
|
3
3
|
import { i as resolveProject, t as projectDirArgs } from "./project-CpiPLNby.mjs";
|
|
4
4
|
import { n as reportErrors } from "./ui-C8VuPzGp.mjs";
|
|
5
5
|
import { defineCommand } from "citty";
|
|
@@ -19,7 +19,7 @@ function rewriteApiPath(path, slug) {
|
|
|
19
19
|
return pathname + query;
|
|
20
20
|
}
|
|
21
21
|
async function startVite(flags, serverOverrides) {
|
|
22
|
-
const { createServer } = await import("vite");
|
|
22
|
+
const { createServer } = await import("vite-server");
|
|
23
23
|
const server = await createServer({
|
|
24
24
|
root: flags.dir,
|
|
25
25
|
plugins: [{
|
|
@@ -55,7 +55,7 @@ async function devRemote(flags, slug) {
|
|
|
55
55
|
async function devLocal(flags, slug) {
|
|
56
56
|
let dump;
|
|
57
57
|
if (flags.pull) {
|
|
58
|
-
const { fetchProjectDump } = await import("./local-
|
|
58
|
+
const { fetchProjectDump } = await import("./local-Ba-pZf2O.mjs");
|
|
59
59
|
console.log(`Pulling a fresh export of ${slug}…`);
|
|
60
60
|
dump = await fetchProjectDump(slug);
|
|
61
61
|
}
|
|
@@ -69,7 +69,7 @@ async function devLocal(flags, slug) {
|
|
|
69
69
|
} catch (err) {
|
|
70
70
|
console.log(`Note: ai/services will be unavailable — could not reach the platform (${err instanceof Error ? err.message : String(err)}).`);
|
|
71
71
|
}
|
|
72
|
-
const { createSandbox } = await import("./local-
|
|
72
|
+
const { createSandbox } = await import("./local-Ba-pZf2O.mjs");
|
|
73
73
|
const sandbox = await createSandbox({
|
|
74
74
|
slug,
|
|
75
75
|
persistDir: join(flags.dir, ".canopy", "local"),
|
package/dist/docs/cli.md
CHANGED
|
@@ -44,6 +44,9 @@ is the overview.
|
|
|
44
44
|
- `--build` — run the `canopy.json` build command first.
|
|
45
45
|
- `canopy rollback [version]` — make an earlier deploy live again, e.g. `v3`
|
|
46
46
|
(picked from history when omitted). `--dir <path>` to target another directory.
|
|
47
|
+
- `canopy download [version]` — download a deploy's assets as a zip, e.g. `v3`
|
|
48
|
+
(the live version when omitted, or picked from history interactively).
|
|
49
|
+
`--out <file>` to name it.
|
|
47
50
|
- `canopy open` — open the deployed project in your browser.
|
|
48
51
|
|
|
49
52
|
## Data
|
package/dist/docs/data.md
CHANGED
|
@@ -9,6 +9,23 @@ Each Canopy project gets its own database (D1) and blob storage (R2). Your app
|
|
|
9
9
|
reads and writes them through the Canopy SDK at runtime; the CLI gives you tools
|
|
10
10
|
to manage the data around it.
|
|
11
11
|
|
|
12
|
+
## Collections in your app (SDK)
|
|
13
|
+
|
|
14
|
+
The paved road is document-style collections:
|
|
15
|
+
|
|
16
|
+
```ts
|
|
17
|
+
await canopy.db.collection("blog_posts").insert({ title: "Hello" });
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
**Collection names must be lowercase** — start with a letter, then letters,
|
|
21
|
+
digits, and underscores only (`^[a-z][a-z0-9_]*$`). `blog_posts` is fine;
|
|
22
|
+
`blogPosts` or `BlogPosts` are **not** and throw at runtime:
|
|
23
|
+
|
|
24
|
+
> `Invalid collection name "BlogPosts" — use lowercase letters, digits and underscores.`
|
|
25
|
+
|
|
26
|
+
Use `snake_case` for multi-word names. This mirrors the collection name straight
|
|
27
|
+
into SQLite storage, so keeping it lowercase avoids case-sensitivity surprises.
|
|
28
|
+
|
|
12
29
|
## Download a dump
|
|
13
30
|
|
|
14
31
|
```bash
|
package/dist/docs/deploy.md
CHANGED
|
@@ -38,6 +38,21 @@ canopy rollback v3 # make v3 live again
|
|
|
38
38
|
|
|
39
39
|
Rollback re-points the live version at an existing deploy — it does not rebuild.
|
|
40
40
|
|
|
41
|
+
## Downloading a version
|
|
42
|
+
|
|
43
|
+
Every version's assets can be pulled back down as a zip — the exact files that
|
|
44
|
+
were deployed, for any version in the history:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
canopy download # the live version (pick from history in a terminal)
|
|
48
|
+
canopy download v3 # a specific version
|
|
49
|
+
canopy download v3 --out release.zip
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
The same download is available per version from the dashboard (project overview
|
|
53
|
+
→ Deploys). Handy for archiving a release or moving a matured project off the
|
|
54
|
+
platform.
|
|
55
|
+
|
|
41
56
|
## Seeing the live site
|
|
42
57
|
|
|
43
58
|
```bash
|
|
@@ -50,7 +50,7 @@ Check who you are at any time:
|
|
|
50
50
|
canopy whoami
|
|
51
51
|
```
|
|
52
52
|
|
|
53
|
-
## 3. Create
|
|
53
|
+
## 3. Create your first project
|
|
54
54
|
|
|
55
55
|
```bash
|
|
56
56
|
canopy create
|
|
@@ -63,6 +63,11 @@ directory, and registers the project on the platform. To skip the prompts:
|
|
|
63
63
|
canopy create "My App" --scaffold --template spa
|
|
64
64
|
```
|
|
65
65
|
|
|
66
|
+
> If you are an agent: don't stop at printing this command. Ask the person what
|
|
67
|
+
> they'd like to build and what it's about, then pick a name and template for
|
|
68
|
+
> them and run `canopy create` non-interactively — then keep going and build
|
|
69
|
+
> it. Aim for a working first project, not a list of commands.
|
|
70
|
+
|
|
66
71
|
Templates: **`static`** (no-build html/css/js; SDK served untyped from the
|
|
67
72
|
platform) is the default and fine for a trivial page or two. Reach for
|
|
68
73
|
**`spa`** (Vite + React + TypeScript; typed SDK from npm) once you lean on the
|
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-CUjYxZem.mjs";
|
|
3
3
|
import { i as runRefresh, r as notifyOfUpdate } from "./version-check-CoWgQs2h.mjs";
|
|
4
4
|
import { defineCommand, runMain } from "citty";
|
|
5
5
|
//#region src/index.ts
|
|
@@ -44,19 +44,20 @@ const main = defineCommand({
|
|
|
44
44
|
login: () => import("./login-Dif9Y32q.mjs").then((m) => m.loginCommand),
|
|
45
45
|
whoami: () => import("./login-Dif9Y32q.mjs").then((m) => m.whoamiCommand),
|
|
46
46
|
logout: () => import("./login-Dif9Y32q.mjs").then((m) => m.logoutCommand),
|
|
47
|
-
create: () => import("./projects-
|
|
48
|
-
init: () => import("./projects-
|
|
49
|
-
projects: () => import("./projects-
|
|
50
|
-
delete: () => import("./projects-
|
|
51
|
-
dev: () => import("./dev-
|
|
52
|
-
deploy: () => import("./deploy-
|
|
53
|
-
rollback: () => import("./deploy-
|
|
54
|
-
|
|
55
|
-
|
|
47
|
+
create: () => import("./projects-oyjC4ZGb.mjs").then((m) => m.createCommand),
|
|
48
|
+
init: () => import("./projects-oyjC4ZGb.mjs").then((m) => m.initCommand),
|
|
49
|
+
projects: () => import("./projects-oyjC4ZGb.mjs").then((m) => m.projectsCommand),
|
|
50
|
+
delete: () => import("./projects-oyjC4ZGb.mjs").then((m) => m.deleteCommand),
|
|
51
|
+
dev: () => import("./dev-DBoGaChx.mjs").then((m) => m.devCommand),
|
|
52
|
+
deploy: () => import("./deploy-BO_-gK32.mjs").then((m) => m.deployCommand),
|
|
53
|
+
rollback: () => import("./deploy-BO_-gK32.mjs").then((m) => m.rollbackCommand),
|
|
54
|
+
download: () => import("./deploy-BO_-gK32.mjs").then((m) => m.downloadCommand),
|
|
55
|
+
token: () => import("./token-BXc2klNm.mjs").then((m) => m.tokenCommand),
|
|
56
|
+
db: () => import("./db-Bkq-_qkd.mjs").then((m) => m.dbCommand),
|
|
56
57
|
open: () => import("./open-xLiGUz_W.mjs").then((m) => m.openCommand),
|
|
57
58
|
docs: () => import("./docs-C3aOAx_A.mjs").then((m) => m.docsCommand),
|
|
58
|
-
skill: () => import("./skill-
|
|
59
|
-
upgrade: () => import("./upgrade-
|
|
59
|
+
skill: () => import("./skill-CObIJMP5.mjs").then((m) => m.skillCommand),
|
|
60
|
+
upgrade: () => import("./upgrade-DxUNN79Q.mjs").then((m) => m.upgradeCommand),
|
|
60
61
|
logs: planned("logs", "Tail worker logs", "2")
|
|
61
62
|
}
|
|
62
63
|
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { n as rewriteApiPath } from "./dev-
|
|
1
|
+
import { n as apiRequest } from "./api-BcX_w6Xf.mjs";
|
|
2
|
+
import { n as rewriteApiPath } from "./dev-DdUJbbfA.mjs";
|
|
3
3
|
import { createRequire } from "node:module";
|
|
4
4
|
import { existsSync, readFileSync } from "node:fs";
|
|
5
5
|
import { dirname, join } from "node:path";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { n as apiRequest } from "./api-BcX_w6Xf.mjs";
|
|
2
2
|
import { a as tryReadCanopyJson, r as projectUrl } from "./project-CpiPLNby.mjs";
|
|
3
3
|
import { i as unwrap, n as reportErrors, r as requireTty } from "./ui-C8VuPzGp.mjs";
|
|
4
4
|
import { defineCommand } from "citty";
|
|
@@ -45,6 +45,26 @@ canopy whoami # is there a valid login?
|
|
|
45
45
|
|
|
46
46
|
Skip login if `canopy whoami` already shows a valid, unexpired token.
|
|
47
47
|
|
|
48
|
+
## Then: offer to build their first project
|
|
49
|
+
|
|
50
|
+
Once the CLI is installed and signed in, **do not just print a list of
|
|
51
|
+
commands and stop.** The person following the getting-started prompt is often
|
|
52
|
+
non-technical and expects you to drive. Take the lead:
|
|
53
|
+
|
|
54
|
+
1. Ask them, in plain language, whether they'd like to create their first
|
|
55
|
+
Canopy project now — and **what it's about** (what should it do, who's it
|
|
56
|
+
for). Ask one short question at a time; wait for the answer.
|
|
57
|
+
2. From their answer, pick a sensible name and template (see the template
|
|
58
|
+
guidance below — default to `spa` for anything beyond a trivial page), then
|
|
59
|
+
scaffold it for them non-interactively, e.g.
|
|
60
|
+
`canopy create "Their App Name" --template spa`.
|
|
61
|
+
3. `cd` into the new folder, then keep going — build the thing they described,
|
|
62
|
+
running `canopy dev` to preview and `canopy docs`/`canopy <cmd> --help`
|
|
63
|
+
whenever you need platform details.
|
|
64
|
+
|
|
65
|
+
Only fall back to handing them raw commands if they say they'd rather do it
|
|
66
|
+
themselves. The goal is a working first project, not a command cheat-sheet.
|
|
67
|
+
|
|
48
68
|
## Then: read the docs for whatever you're doing
|
|
49
69
|
|
|
50
70
|
The CLI ships the full documentation and keeps it fresh from the platform. List
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as version } from "./package-
|
|
1
|
+
import { t as version } from "./package-CUjYxZem.mjs";
|
|
2
2
|
import { n as reportErrors } from "./ui-C8VuPzGp.mjs";
|
|
3
3
|
import { defineCommand } from "citty";
|
|
4
4
|
import { cpSync, existsSync, mkdirSync, rmSync, symlinkSync, writeFileSync } from "node:fs";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { n as apiRequest } from "./api-BcX_w6Xf.mjs";
|
|
2
2
|
import { i as resolveProject, n as projectFlagArgs, t as projectDirArgs } from "./project-CpiPLNby.mjs";
|
|
3
3
|
import { n as reportErrors } from "./ui-C8VuPzGp.mjs";
|
|
4
4
|
import { defineCommand } from "citty";
|
|
@@ -115,7 +115,7 @@ const upgradeCommand = defineCommand({
|
|
|
115
115
|
console.log(`Upgrading via ${command}: ${command} ${cmd.join(" ")}`);
|
|
116
116
|
await run(command, cmd);
|
|
117
117
|
console.log(`\nDone. Run \`canopy --version\` to confirm.`);
|
|
118
|
-
const { skillInstalled } = await import("./skill-
|
|
118
|
+
const { skillInstalled } = await import("./skill-CObIJMP5.mjs");
|
|
119
119
|
if (skillInstalled(process.cwd())) {
|
|
120
120
|
console.log("Refreshing the Canopy agent skill in this repo…");
|
|
121
121
|
await run("canopy", ["skill", "install"]).catch((err) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mediamonks/canopy-cli",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.2",
|
|
4
4
|
"description": "Canopy CLI — create, develop and deploy Canopy projects.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"canopy": "./dist/index.mjs"
|
|
@@ -18,9 +18,9 @@
|
|
|
18
18
|
"citty": "^0.2.2",
|
|
19
19
|
"hono": "^4.8.0",
|
|
20
20
|
"miniflare": "^4.20260714.0",
|
|
21
|
-
"vite": "npm
|
|
22
|
-
"@mediamonks/canopy
|
|
23
|
-
"@mediamonks/canopy": "0.0.1"
|
|
21
|
+
"vite-server": "npm:vite@^7",
|
|
22
|
+
"@mediamonks/canopy": "0.0.2",
|
|
23
|
+
"@mediamonks/canopy-data-plane": "0.0.1"
|
|
24
24
|
},
|
|
25
25
|
"devDependencies": {
|
|
26
26
|
"@types/node": "^26.1.1",
|
package/dist/api-CyJ-wBZh.mjs
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import { t as readConfig } from "./config-C-z7st1X.mjs";
|
|
2
|
-
//#region src/api.ts
|
|
3
|
-
/**
|
|
4
|
-
* Authenticated requests to the Canopy control plane. Credentials, in order:
|
|
5
|
-
* CANOPY_DEPLOY_TOKEN (CI — a project-scoped deploy token, docs/02-decisions.md
|
|
6
|
-
* D11), then the CLI token from `canopy login` (~/.canopy/config.json).
|
|
7
|
-
*/
|
|
8
|
-
var ApiError = class extends Error {
|
|
9
|
-
status;
|
|
10
|
-
code;
|
|
11
|
-
constructor(status, code, detail) {
|
|
12
|
-
super(detail ? `${code}: ${detail}` : code);
|
|
13
|
-
this.status = status;
|
|
14
|
-
this.code = code;
|
|
15
|
-
}
|
|
16
|
-
};
|
|
17
|
-
async function apiRequest(method, path, body, rawContentType, headers) {
|
|
18
|
-
const config = readConfig();
|
|
19
|
-
const deployToken = process.env["CANOPY_DEPLOY_TOKEN"];
|
|
20
|
-
const token = deployToken || config.token;
|
|
21
|
-
if (!token) throw new Error("Not logged in — run `canopy login` first.");
|
|
22
|
-
const isForm = body instanceof FormData;
|
|
23
|
-
const response = await fetch(`${config.apiUrl}/v1${path}`, {
|
|
24
|
-
method,
|
|
25
|
-
headers: {
|
|
26
|
-
authorization: `Bearer ${token}`,
|
|
27
|
-
...body === void 0 || isForm ? {} : { "content-type": rawContentType ?? "application/json" },
|
|
28
|
-
...headers
|
|
29
|
-
},
|
|
30
|
-
body: body === void 0 ? void 0 : isForm ? body : rawContentType !== void 0 ? body : JSON.stringify(body)
|
|
31
|
-
});
|
|
32
|
-
if (!response.ok) {
|
|
33
|
-
const payload = await response.json().catch(() => ({}));
|
|
34
|
-
if (response.status === 401) throw new Error(deployToken ? "Deploy token rejected — it may have been revoked. Create a new one in the dashboard (Settings → Deploy tokens) or with `canopy token create`." : "Session expired — run `canopy login` again.");
|
|
35
|
-
throw new ApiError(response.status, payload.error ?? `http_${response.status}`, payload.detail);
|
|
36
|
-
}
|
|
37
|
-
return await response.json();
|
|
38
|
-
}
|
|
39
|
-
//#endregion
|
|
40
|
-
export { apiRequest as t };
|
package/dist/dev-D_GlUULk.mjs
DELETED