@mediamonks/canopy-cli 0.0.1 → 0.2.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/api-CyJ-wBZh.mjs +40 -0
- package/dist/config-C-z7st1X.mjs +33 -0
- package/dist/{db-BBcgT1md.mjs → db-CtUG4vfG.mjs} +3 -3
- package/dist/{deploy-BfhAlb91.mjs → deploy-N88l4Kt3.mjs} +7 -6
- package/dist/{dev-BB7CwqoI.mjs → dev-DTGW0lm0.mjs} +6 -5
- package/dist/dev-D_GlUULk.mjs +2 -0
- package/dist/docs/ci.md +48 -0
- package/dist/docs/cli.md +67 -0
- package/dist/docs/data.md +49 -0
- package/dist/docs/deploy.md +50 -0
- package/dist/docs/dev.md +44 -0
- package/dist/docs/getting-started.md +100 -0
- package/dist/docs/troubleshooting.md +61 -0
- package/dist/docs-C3aOAx_A.mjs +179 -0
- package/dist/index.mjs +26 -16
- package/dist/{local-DhufX0eJ.mjs → local-951zc9qJ.mjs} +2 -2
- package/dist/{login-DrXKOEmr.mjs → login-Dif9Y32q.mjs} +38 -10
- package/dist/{open-DFWvrLFc.mjs → open-xLiGUz_W.mjs} +2 -2
- package/dist/package-OcWG7JAW.mjs +4 -0
- package/dist/{project-CrrpLEPs.mjs → project-CpiPLNby.mjs} +1 -1
- package/dist/projects-DJT_mJDR.mjs +228 -0
- package/dist/skill/canopy/SKILL.md +80 -0
- package/dist/skill-jbmERP1X.mjs +84 -0
- package/dist/templates/spa/.github/workflows/canopy-deploy.yml +34 -0
- package/dist/templates/spa/AGENTS.md +43 -0
- package/dist/templates/spa/README.md +14 -0
- package/dist/templates/spa/_gitignore +4 -0
- package/dist/templates/spa/canopy.json +5 -0
- package/dist/templates/spa/index.html +12 -0
- package/dist/templates/spa/package.json +21 -0
- package/dist/templates/spa/src/app.tsx +25 -0
- package/dist/templates/spa/src/canopy.ts +5 -0
- package/dist/templates/spa/src/main.tsx +10 -0
- package/dist/templates/spa/src/style.css +29 -0
- package/dist/templates/spa/tsconfig.json +16 -0
- package/dist/templates/spa/vite.config.ts +7 -0
- package/dist/templates/static/AGENTS.md +41 -0
- package/dist/templates/static/README.md +9 -0
- package/dist/templates/static/_gitignore +2 -0
- package/dist/templates/static/canopy.json +4 -0
- package/dist/templates/static/index.html +21 -0
- package/dist/templates/static/main.js +9 -0
- package/dist/templates/static/style.css +29 -0
- package/dist/templates/worker/.github/workflows/canopy-deploy.yml +34 -0
- package/dist/templates/worker/AGENTS.md +51 -0
- package/dist/templates/worker/README.md +18 -0
- package/dist/templates/worker/_gitignore +4 -0
- package/dist/templates/worker/canopy.json +6 -0
- package/dist/templates/worker/index.html +12 -0
- package/dist/templates/worker/package.json +22 -0
- package/dist/templates/worker/src/app.tsx +25 -0
- package/dist/templates/worker/src/canopy.ts +5 -0
- package/dist/templates/worker/src/main.tsx +10 -0
- package/dist/templates/worker/src/style.css +29 -0
- package/dist/templates/worker/tsconfig.json +16 -0
- package/dist/templates/worker/vite.config.ts +7 -0
- package/dist/templates/worker/worker/index.ts +29 -0
- package/dist/token-rTD9jxPZ.mjs +75 -0
- package/dist/ui-C8VuPzGp.mjs +40 -0
- package/dist/upgrade-Y8ejvoeg.mjs +99 -0
- package/dist/version-check-CoWgQs2h.mjs +128 -0
- package/package.json +2 -2
- package/dist/api-BEY4O4ap.mjs +0 -36
- package/dist/dev-Bvy21srH.mjs +0 -2
- package/dist/projects-f_KocDae.mjs +0 -295
- package/dist/ui-Cl5P_yQ6.mjs +0 -72
|
@@ -0,0 +1,40 @@
|
|
|
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 };
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { chmodSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
//#region src/config.ts
|
|
5
|
+
/**
|
|
6
|
+
* CLI config at ~/.canopy/config.json — platform endpoints and the current
|
|
7
|
+
* CLI token. Env overrides: CANOPY_API_URL, CANOPY_OIDC_ISSUER,
|
|
8
|
+
* CANOPY_OIDC_CLIENT_ID.
|
|
9
|
+
*/
|
|
10
|
+
const DEFAULT_API_URL = "https://canopy-api.arjan-van-wijk.workers.dev";
|
|
11
|
+
const DEFAULT_ISSUER = "https://s4capital.onelogin.com/oidc/2";
|
|
12
|
+
const DEFAULT_CLI_CLIENT_ID = "b69755d0-6678-013f-b5ce-09fe44a57aa0180501";
|
|
13
|
+
const configDir = join(homedir(), ".canopy");
|
|
14
|
+
const configPath = join(configDir, "config.json");
|
|
15
|
+
function readConfig() {
|
|
16
|
+
let stored = {};
|
|
17
|
+
try {
|
|
18
|
+
stored = JSON.parse(readFileSync(configPath, "utf8"));
|
|
19
|
+
} catch {}
|
|
20
|
+
return {
|
|
21
|
+
...stored,
|
|
22
|
+
apiUrl: process.env["CANOPY_API_URL"] ?? stored.apiUrl ?? DEFAULT_API_URL,
|
|
23
|
+
issuer: process.env["CANOPY_OIDC_ISSUER"] ?? stored.issuer ?? DEFAULT_ISSUER,
|
|
24
|
+
cliClientId: process.env["CANOPY_OIDC_CLIENT_ID"] ?? stored.cliClientId ?? DEFAULT_CLI_CLIENT_ID
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
function writeConfig(config) {
|
|
28
|
+
mkdirSync(configDir, { recursive: true });
|
|
29
|
+
writeFileSync(configPath, `${JSON.stringify(config, null, 2)}\n`);
|
|
30
|
+
chmodSync(configPath, 384);
|
|
31
|
+
}
|
|
32
|
+
//#endregion
|
|
33
|
+
export { writeConfig as n, readConfig as t };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { i as
|
|
1
|
+
import { t as apiRequest } from "./api-CyJ-wBZh.mjs";
|
|
2
|
+
import { i as resolveProject, n as projectFlagArgs } from "./project-CpiPLNby.mjs";
|
|
3
|
+
import { i as unwrap, n as reportErrors, r as requireTty } from "./ui-C8VuPzGp.mjs";
|
|
4
4
|
import { defineCommand } from "citty";
|
|
5
5
|
import { readFileSync, writeFileSync } from "node:fs";
|
|
6
6
|
import * as p from "@clack/prompts";
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { t as
|
|
3
|
-
import {
|
|
1
|
+
import { t as apiRequest } from "./api-CyJ-wBZh.mjs";
|
|
2
|
+
import { a as tryReadCanopyJson, i as resolveProject, n as projectFlagArgs, r as projectUrl, t as projectDirArgs } from "./project-CpiPLNby.mjs";
|
|
3
|
+
import { i as unwrap, n as reportErrors } from "./ui-C8VuPzGp.mjs";
|
|
4
4
|
import { defineCommand } from "citty";
|
|
5
|
+
import { spawn } from "node:child_process";
|
|
5
6
|
import { readFileSync, readdirSync, statSync } from "node:fs";
|
|
6
|
-
import * as p from "@clack/prompts";
|
|
7
7
|
import { join, relative, resolve } from "node:path";
|
|
8
|
-
import
|
|
8
|
+
import * as p from "@clack/prompts";
|
|
9
9
|
//#region src/deploy.ts
|
|
10
10
|
/**
|
|
11
11
|
* canopy deploy — build (optional), bundle the output directory as multipart
|
|
@@ -17,6 +17,7 @@ import { spawn } from "node:child_process";
|
|
|
17
17
|
*/
|
|
18
18
|
const IGNORED = /* @__PURE__ */ new Set([
|
|
19
19
|
"canopy.json",
|
|
20
|
+
"AGENTS.md",
|
|
20
21
|
"CLAUDE.md",
|
|
21
22
|
"README.md",
|
|
22
23
|
".gitignore",
|
|
@@ -88,7 +89,7 @@ const deployCommand = defineCommand({
|
|
|
88
89
|
spin.start(`Deploying ${paths.length} files (${(total / 1024).toFixed(0)} kB) → ${slug}`);
|
|
89
90
|
let result;
|
|
90
91
|
try {
|
|
91
|
-
({deploy: result} = await apiRequest("POST", `/projects/${slug}/deploys`, form));
|
|
92
|
+
({deploy: result} = await apiRequest("POST", `/projects/${slug}/deploys`, form, void 0, process.env["GITHUB_ACTIONS"] ? { "x-canopy-source": "github-action" } : void 0));
|
|
92
93
|
} catch (err) {
|
|
93
94
|
spin.error("Deploy failed.");
|
|
94
95
|
throw err;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { t as apiRequest } from "./api-
|
|
3
|
-
import { i as resolveProject, t as projectDirArgs } from "./project-
|
|
1
|
+
import { t as readConfig } from "./config-C-z7st1X.mjs";
|
|
2
|
+
import { t as apiRequest } from "./api-CyJ-wBZh.mjs";
|
|
3
|
+
import { i as resolveProject, t as projectDirArgs } from "./project-CpiPLNby.mjs";
|
|
4
|
+
import { n as reportErrors } from "./ui-C8VuPzGp.mjs";
|
|
4
5
|
import { defineCommand } from "citty";
|
|
5
6
|
import { join } from "node:path";
|
|
6
7
|
//#region src/dev.ts
|
|
@@ -54,7 +55,7 @@ async function devRemote(flags, slug) {
|
|
|
54
55
|
async function devLocal(flags, slug) {
|
|
55
56
|
let dump;
|
|
56
57
|
if (flags.pull) {
|
|
57
|
-
const { fetchProjectDump } = await import("./local-
|
|
58
|
+
const { fetchProjectDump } = await import("./local-951zc9qJ.mjs");
|
|
58
59
|
console.log(`Pulling a fresh export of ${slug}…`);
|
|
59
60
|
dump = await fetchProjectDump(slug);
|
|
60
61
|
}
|
|
@@ -68,7 +69,7 @@ async function devLocal(flags, slug) {
|
|
|
68
69
|
} catch (err) {
|
|
69
70
|
console.log(`Note: ai/services will be unavailable — could not reach the platform (${err instanceof Error ? err.message : String(err)}).`);
|
|
70
71
|
}
|
|
71
|
-
const { createSandbox } = await import("./local-
|
|
72
|
+
const { createSandbox } = await import("./local-951zc9qJ.mjs");
|
|
72
73
|
const sandbox = await createSandbox({
|
|
73
74
|
slug,
|
|
74
75
|
persistDir: join(flags.dir, ".canopy", "local"),
|
package/dist/docs/ci.md
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: CI and deploy tokens
|
|
3
|
+
summary: Push-to-main deploys using a project-scoped deploy token.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# CI and deploy tokens
|
|
7
|
+
|
|
8
|
+
Interactive `canopy login` is for humans at a terminal. For automated deploys
|
|
9
|
+
(GitHub Actions, etc.) use a **deploy token**: a project-scoped credential that
|
|
10
|
+
can deploy that one project and nothing else.
|
|
11
|
+
|
|
12
|
+
## Create a token
|
|
13
|
+
|
|
14
|
+
From inside the project directory:
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
canopy token create --name "GitHub Actions"
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
This prints the token **once** — copy it immediately and store it as a CI secret
|
|
21
|
+
(e.g. `CANOPY_DEPLOY_TOKEN`). It is not shown again.
|
|
22
|
+
|
|
23
|
+
Manage tokens:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
canopy token list
|
|
27
|
+
canopy token revoke <id>
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Use it in CI
|
|
31
|
+
|
|
32
|
+
The CLI picks up `CANOPY_DEPLOY_TOKEN` from the environment and uses it instead
|
|
33
|
+
of an interactive login. A minimal GitHub Actions step:
|
|
34
|
+
|
|
35
|
+
```yaml
|
|
36
|
+
- run: npm install -g @mediamonks/canopy-cli
|
|
37
|
+
- run: canopy deploy --build
|
|
38
|
+
env:
|
|
39
|
+
CANOPY_DEPLOY_TOKEN: ${{ secrets.CANOPY_DEPLOY_TOKEN }}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
No `canopy login` step is needed — the token is the credential.
|
|
43
|
+
|
|
44
|
+
## Notes
|
|
45
|
+
|
|
46
|
+
- A deploy token can only deploy its own project. If it strays to another
|
|
47
|
+
project's resources, the platform rejects it.
|
|
48
|
+
- Revoke a token the moment it might be exposed; create a fresh one to replace it.
|
package/dist/docs/cli.md
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: CLI command reference
|
|
3
|
+
summary: Every canopy command, its flags, and what it does.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# CLI command reference
|
|
7
|
+
|
|
8
|
+
Run `canopy <command> --help` for the authoritative flags of any command. This
|
|
9
|
+
is the overview.
|
|
10
|
+
|
|
11
|
+
## Auth
|
|
12
|
+
|
|
13
|
+
- `canopy login` — sign in via OneLogin (opens the browser; finish there).
|
|
14
|
+
Add `--json` for machine-readable status output (for agents/CI).
|
|
15
|
+
- `canopy whoami` — show the current login and token expiry.
|
|
16
|
+
- `canopy logout` — forget the local CLI token.
|
|
17
|
+
|
|
18
|
+
## Projects
|
|
19
|
+
|
|
20
|
+
- `canopy create [name]` — create and register a project.
|
|
21
|
+
- `--slug <slug>` — URL slug (defaults to a slugified name).
|
|
22
|
+
- `--template <id>` — `static` | `spa` | `worker` (picker when omitted).
|
|
23
|
+
- `--scaffold` — write `./<slug>/` from the starter template.
|
|
24
|
+
- `canopy projects` — list your projects. `--all` includes ones you can't edit.
|
|
25
|
+
- `canopy delete [slug]` — full teardown of a project. `--yes` skips the
|
|
26
|
+
type-the-slug confirmation.
|
|
27
|
+
|
|
28
|
+
## Develop
|
|
29
|
+
|
|
30
|
+
- `canopy dev` — run the project locally against live platform data.
|
|
31
|
+
- `--port <port>` — dev server port.
|
|
32
|
+
- `--local` — offline sandbox (Miniflare D1/R2 under `.canopy/local/`).
|
|
33
|
+
- `--pull` — hydrate the sandbox from a fresh prod export first (with `--local`).
|
|
34
|
+
|
|
35
|
+
## Deploy
|
|
36
|
+
|
|
37
|
+
- `canopy deploy` — build and deploy the project.
|
|
38
|
+
- `--build` — run the `canopy.json` build command first.
|
|
39
|
+
- `canopy rollback [version]` — make an earlier deploy live again, e.g. `v3`
|
|
40
|
+
(picked from history when omitted). `--dir <path>` to target another directory.
|
|
41
|
+
- `canopy open` — open the deployed project in your browser.
|
|
42
|
+
|
|
43
|
+
## Data
|
|
44
|
+
|
|
45
|
+
- `canopy db pull` — download a fresh SQL dump. `--out <file>` to name it.
|
|
46
|
+
- `canopy db snapshot` — store a dump server-side (restorable anytime).
|
|
47
|
+
- `canopy db snapshots` — list stored snapshots.
|
|
48
|
+
- `canopy db restore [file]` — replace ALL project data from a `.sql` file or
|
|
49
|
+
`--snapshot <key>`. `--yes` skips the confirmation. **Destructive.**
|
|
50
|
+
|
|
51
|
+
## CI / deploy tokens
|
|
52
|
+
|
|
53
|
+
- `canopy token create` — create a project-scoped deploy token for CI.
|
|
54
|
+
`--name "<label>"` to label it.
|
|
55
|
+
- `canopy token list` — list the project's deploy tokens.
|
|
56
|
+
- `canopy token revoke <id>` — revoke a token.
|
|
57
|
+
|
|
58
|
+
See `canopy docs ci` for wiring push-to-main deploys.
|
|
59
|
+
|
|
60
|
+
## Maintenance
|
|
61
|
+
|
|
62
|
+
- `canopy upgrade` — update the CLI to the latest version. `--version <v>` pins a
|
|
63
|
+
release; `--pm <npm|pnpm|yarn|bun>` overrides the package-manager guess.
|
|
64
|
+
Upgrading also refreshes the installed Canopy agent skill.
|
|
65
|
+
- `canopy docs [topic]` — list docs, or print one. `--refresh` re-fetches from
|
|
66
|
+
the platform.
|
|
67
|
+
- `canopy skill install` — (re)install the Canopy agent skill into this repo.
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Working with project data
|
|
3
|
+
summary: Pulling, snapshotting, and restoring your project database.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Working with project data
|
|
7
|
+
|
|
8
|
+
Each Canopy project gets its own database (D1) and blob storage (R2). Your app
|
|
9
|
+
reads and writes them through the Canopy SDK at runtime; the CLI gives you tools
|
|
10
|
+
to manage the data around it.
|
|
11
|
+
|
|
12
|
+
## Download a dump
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
canopy db pull # writes <slug>-<timestamp>.sql
|
|
16
|
+
canopy db pull --out data.sql # choose the filename
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
A plain SQL dump you can inspect, diff, or keep as a backup.
|
|
20
|
+
|
|
21
|
+
## Snapshots (server-side backups)
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
canopy db snapshot # store a dump on the platform
|
|
25
|
+
canopy db snapshots # list stored snapshots
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Snapshots live on the platform and can be restored at any time — handy before a
|
|
29
|
+
risky change.
|
|
30
|
+
|
|
31
|
+
## Restore — destructive
|
|
32
|
+
|
|
33
|
+
Restoring **replaces ALL project data**:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
canopy db restore backup.sql # from a local dump
|
|
37
|
+
canopy db restore --snapshot <key> # from a server-side snapshot
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
You'll be asked to type the project slug to confirm. `--yes` skips that prompt —
|
|
41
|
+
use it only in scripts you trust.
|
|
42
|
+
|
|
43
|
+
## Safe workflow for agents
|
|
44
|
+
|
|
45
|
+
Before any destructive data change:
|
|
46
|
+
|
|
47
|
+
1. `canopy db snapshot` — take a restore point.
|
|
48
|
+
2. Make the change (or test it first with `canopy dev --local --pull`).
|
|
49
|
+
3. If something goes wrong, `canopy db restore --snapshot <key>`.
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Deploying a project
|
|
3
|
+
summary: How canopy deploy builds, uploads, and makes a version live.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Deploying a project
|
|
7
|
+
|
|
8
|
+
From inside a project directory:
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
canopy deploy
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
This uploads your built assets to the platform, creates a new immutable deploy
|
|
15
|
+
version, and makes it live. Each deploy is versioned (`v1`, `v2`, …).
|
|
16
|
+
|
|
17
|
+
## Building first
|
|
18
|
+
|
|
19
|
+
If your project needs a build step (React SPA, anything with a bundler), declare
|
|
20
|
+
the build command in `canopy.json` and pass `--build`:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
canopy deploy --build
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
`--build` runs the project's configured build command before uploading. Without
|
|
27
|
+
it, `canopy deploy` uploads whatever is already built (or the static files as-is
|
|
28
|
+
for a `static` project).
|
|
29
|
+
|
|
30
|
+
## Rolling back
|
|
31
|
+
|
|
32
|
+
Every deploy is kept, so you can instantly revert:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
canopy rollback # pick a version from history
|
|
36
|
+
canopy rollback v3 # make v3 live again
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Rollback re-points the live version at an existing deploy — it does not rebuild.
|
|
40
|
+
|
|
41
|
+
## Seeing the live site
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
canopy open
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Deploying from CI
|
|
48
|
+
|
|
49
|
+
For automated push-to-main deploys, use a deploy token instead of an interactive
|
|
50
|
+
login — see `canopy docs ci`.
|
package/dist/docs/dev.md
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Local development
|
|
3
|
+
summary: Running a project locally against live or sandboxed data.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Local development
|
|
7
|
+
|
|
8
|
+
From inside a project directory:
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
canopy dev
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
By default `canopy dev` runs your project locally but talks to **live platform
|
|
15
|
+
data** (your project's real database, blobs, and AI). This is the fastest way to
|
|
16
|
+
develop against realistic state.
|
|
17
|
+
|
|
18
|
+
- `--port <port>` — choose the dev-server port.
|
|
19
|
+
|
|
20
|
+
## Offline sandbox
|
|
21
|
+
|
|
22
|
+
To develop without touching live data — for example on a plane, or before you
|
|
23
|
+
trust a destructive change — use a local sandbox backed by Miniflare:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
canopy dev --local
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
This creates local D1 and R2 under `.canopy/local/` in your project. The sandbox
|
|
30
|
+
starts empty.
|
|
31
|
+
|
|
32
|
+
To seed the sandbox from a fresh copy of production data first:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
canopy dev --local --pull
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
`.canopy/local/` is disposable — delete it to reset the sandbox.
|
|
39
|
+
|
|
40
|
+
## Which mode should an agent use?
|
|
41
|
+
|
|
42
|
+
- Iterating on UI or read-only features → `canopy dev` (live data, realistic).
|
|
43
|
+
- Testing writes, migrations, or anything destructive → `canopy dev --local`
|
|
44
|
+
(optionally `--pull` to start from real data).
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Getting started
|
|
3
|
+
summary: Install the CLI, sign in with OneLogin, and ship your first project.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Getting started with Canopy
|
|
7
|
+
|
|
8
|
+
Canopy is Monks' managed platform for vibecoded projects. You build a small web
|
|
9
|
+
app, and Canopy hosts it, gives it a database, blob storage, and AI — no
|
|
10
|
+
infrastructure to manage.
|
|
11
|
+
|
|
12
|
+
This guide is written so an AI coding agent (Claude, Cursor, etc.) can follow it
|
|
13
|
+
on your behalf. Everything runs through one command-line tool: `canopy`.
|
|
14
|
+
|
|
15
|
+
## 1. Install the CLI
|
|
16
|
+
|
|
17
|
+
The CLI is published on npm. Install it globally:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install -g @mediamonks/canopy-cli
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Confirm it's on your PATH:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
canopy --version
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
If `npm` isn't available, install Node.js 20+ first (https://nodejs.org), then
|
|
30
|
+
re-run the install.
|
|
31
|
+
|
|
32
|
+
## 2. Sign in
|
|
33
|
+
|
|
34
|
+
Canopy authenticates through Monks' OneLogin. Run:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
canopy login
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
This opens your browser. **Finish the sign-in there** — the CLI waits for the
|
|
41
|
+
browser to redirect back, then stores a token in `~/.canopy/config.json`.
|
|
42
|
+
|
|
43
|
+
> If you are an agent: run `canopy login`, then stop and tell the human to
|
|
44
|
+
> complete the sign-in in the browser window that just opened. Never ask for or
|
|
45
|
+
> type the user's password or any codes yourself.
|
|
46
|
+
|
|
47
|
+
Check who you are at any time:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
canopy whoami
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## 3. Create a project
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
canopy create
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
This prompts for a name and a starter template, scaffolds a runnable project
|
|
60
|
+
directory, and registers the project on the platform. To skip the prompts:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
canopy create "My App" --scaffold --template static
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## 4. Run it locally
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
cd my-app
|
|
70
|
+
canopy dev
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Your project runs against live platform data by default. For an offline sandbox
|
|
74
|
+
with local data, use `canopy dev --local`.
|
|
75
|
+
|
|
76
|
+
## 5. Deploy
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
canopy deploy
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Then open the live site:
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
canopy open
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Where to go next
|
|
89
|
+
|
|
90
|
+
Everything the CLI can do is documented and available offline. List the topics:
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
canopy docs
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Read one, e.g.:
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
canopy docs deploy
|
|
100
|
+
```
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Troubleshooting
|
|
3
|
+
summary: Common errors and how to resolve them.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Troubleshooting
|
|
7
|
+
|
|
8
|
+
## `command not found: canopy`
|
|
9
|
+
|
|
10
|
+
The CLI isn't installed or isn't on your PATH. Install it:
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
npm install -g @mediamonks/canopy-cli
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
If it installs but still isn't found, your npm global bin directory isn't on your
|
|
17
|
+
PATH. Run `npm bin -g` to find it and add that directory to your PATH.
|
|
18
|
+
|
|
19
|
+
## `Not logged in — run canopy login`
|
|
20
|
+
|
|
21
|
+
You have no valid CLI token. Run `canopy login` and finish the sign-in in the
|
|
22
|
+
browser. Check status with `canopy whoami`.
|
|
23
|
+
|
|
24
|
+
## `Session expired — run canopy login again`
|
|
25
|
+
|
|
26
|
+
Your token expired. Just run `canopy login` again.
|
|
27
|
+
|
|
28
|
+
## The browser didn't open during login
|
|
29
|
+
|
|
30
|
+
`canopy login` prints the sign-in URL as a fallback — open it manually in a
|
|
31
|
+
browser on the **same machine** as the CLI (login uses a local loopback
|
|
32
|
+
redirect, so it must be the same machine).
|
|
33
|
+
|
|
34
|
+
## `Port 8976 is in use`
|
|
35
|
+
|
|
36
|
+
A previous login attempt is still holding the callback port. Close it (or wait a
|
|
37
|
+
moment) and retry `canopy login`.
|
|
38
|
+
|
|
39
|
+
## Login hangs and never completes
|
|
40
|
+
|
|
41
|
+
Login only works when the CLI runs on the machine whose browser you're using
|
|
42
|
+
(the redirect goes to `127.0.0.1`). It cannot complete on a remote/headless box.
|
|
43
|
+
Run it on your own laptop.
|
|
44
|
+
|
|
45
|
+
## `Deploy token rejected`
|
|
46
|
+
|
|
47
|
+
The `CANOPY_DEPLOY_TOKEN` was revoked or is wrong. Create a new one with
|
|
48
|
+
`canopy token create` (or in the dashboard) and update your CI secret.
|
|
49
|
+
|
|
50
|
+
## Docs are stale
|
|
51
|
+
|
|
52
|
+
`canopy docs` caches the platform's docs. Force a refresh:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
canopy docs <topic> --refresh
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Still stuck?
|
|
59
|
+
|
|
60
|
+
Run the failing command again and share its full output. `canopy whoami` and
|
|
61
|
+
`canopy --version` are useful to include.
|