@mediamonks/canopy-cli 0.7.37 → 0.7.39
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/README.md +5 -2
- package/dist/{ai-CjvrcBIz.mjs → ai-EyrXJC5z.mjs} +2 -2
- package/dist/{api-BPEYm9ts.mjs → api-ArfD6-t5.mjs} +40 -5
- package/dist/{browse-CBelCgnW.mjs → browse-DEhnJAPT.mjs} +1 -1
- package/dist/{db-CbqgjfYz.mjs → db-DGrCgj-R.mjs} +3 -3
- package/dist/{deploy-CqCyry4o.mjs → deploy-B7KItUnh.mjs} +3 -3
- package/dist/{dev-BtnEuAnS.mjs → dev-BMWj4NDZ.mjs} +5 -5
- package/dist/dev-BdSq6CGD.mjs +2 -0
- package/dist/docs/cli-create.md +2 -0
- package/dist/docs/cli-feedback.md +27 -1
- package/dist/docs/cli-projects.md +2 -0
- package/dist/docs/cli-secrets.md +2 -1
- package/dist/docs/cli-template.md +47 -0
- package/dist/docs/index.json +10 -0
- package/dist/docs/platform-schedules.md +21 -0
- package/dist/docs/templates.md +84 -0
- package/dist/{docs-DvCUaI1O.mjs → docs-Bplp4sOY.mjs} +7 -3
- package/dist/{egress-ButFsQ2N.mjs → egress-CzxRD6Tp.mjs} +2 -2
- package/dist/{feedback-CIjjiEaj.mjs → feedback-DFK0TXlt.mjs} +358 -14
- package/dist/index.mjs +31 -30
- package/dist/{local-BfqTheMP.mjs → local-BnfAEwso.mjs} +2 -2
- package/dist/{login-CtDWXICZ.mjs → login-B2FJGIrD.mjs} +24 -7
- package/dist/{logs-BltUi4Gp.mjs → logs-aJwWWaMx.mjs} +2 -2
- package/dist/{mcp-DTUJV4FT.mjs → mcp-DuT6_wei.mjs} +2 -2
- package/dist/{migrations-CF1iNZ7F.mjs → migrations-CG_O5Ghw.mjs} +1 -1
- package/dist/{open-DXo60Ikf.mjs → open-COFMburt.mjs} +1 -1
- package/dist/{package-BY69Tjq_.mjs → package-CJJpjVh_.mjs} +1 -1
- package/dist/project-template-DlzMx8Oj.mjs +2 -0
- package/dist/project-template-DtTfXyLI.mjs +125 -0
- package/dist/{projects-DPGRI-SI.mjs → projects-B3Wi01lw.mjs} +74 -8
- package/dist/{repo-CKIQrrHK.mjs → repo-4x-CJwCp.mjs} +22 -16
- package/dist/repo-CmbakFVi.mjs +2 -0
- package/dist/{schedule-DdMq5eXC.mjs → schedule-CbaDIJXr.mjs} +2 -2
- package/dist/{secrets-BQYaPSOT.mjs → secrets-CmY4dUHz.mjs} +6 -5
- package/dist/skill/canopy/SKILL.md +7 -4
- package/dist/{skill-B96cKy8K.mjs → skill-C2lGGz9n.mjs} +2 -2
- package/dist/{staging-CU4QrzFX.mjs → staging-ypR79MlD.mjs} +2 -2
- package/dist/{token-BWnLsWLg.mjs → token-CDrHQ5Xb.mjs} +2 -2
- package/dist/{ui-CHfKV6lG.mjs → ui-CaHgvvkJ.mjs} +1 -1
- package/dist/{upgrade-SfxmPh3F.mjs → upgrade-Caj1RsA7.mjs} +2 -2
- package/dist/{usage-Cqqvnzrf.mjs → usage-CP8rBVZi.mjs} +2 -2
- package/package.json +1 -1
- package/dist/dev-D_qzeLng.mjs +0 -2
- package/dist/repo-DvrIsubH.mjs +0 -2
package/README.md
CHANGED
|
@@ -11,12 +11,15 @@ canopy deploy [--no-build] # authorize → signed upload → complete
|
|
|
11
11
|
canopy rollback / deploys / download
|
|
12
12
|
canopy db pull | snapshot | snapshots | restore
|
|
13
13
|
canopy token create | list | revoke
|
|
14
|
-
canopy
|
|
14
|
+
canopy feedback list | show | comment | note | status | report | watch
|
|
15
|
+
canopy usage / docs / whoami [--json] / logout
|
|
15
16
|
```
|
|
16
17
|
|
|
17
18
|
Control-plane calls go through `src/api.ts` (`/control/v1` on the active
|
|
18
19
|
site's `api.` host, bearer token — `CANOPY_DEPLOY_TOKEN` in CI wins over the
|
|
19
|
-
login token
|
|
20
|
+
login token; every request says which AI agent drives the CLI, if any, as
|
|
21
|
+
`x-canopy-agent`: `CANOPY_AGENT=<name>`, `none` to opt out, else `claude-code`
|
|
22
|
+
under `CLAUDECODE=1`); database safety runs on the data plane (`apiDataRequest` with
|
|
20
23
|
`X-Canopy-Project`). `canopy deploy` streams bundle bytes straight to
|
|
21
24
|
`/data/v1/uploads/:grant` under a short-lived signed grant — they never
|
|
22
25
|
transit the control plane (docs/09-plane-split.md D-d). Multi-site profiles:
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { r as reportErrors } from "./ui-
|
|
1
|
+
import { a as apiRequest, l as withContext } from "./api-ArfD6-t5.mjs";
|
|
2
|
+
import { r as reportErrors } from "./ui-CaHgvvkJ.mjs";
|
|
3
3
|
import { defineCommand } from "citty";
|
|
4
4
|
//#region src/ai.ts
|
|
5
5
|
/**
|
|
@@ -213,6 +213,41 @@ function randomHex(bytes) {
|
|
|
213
213
|
function traceHeaders() {
|
|
214
214
|
return { traceparent: `00-${TRACE_ID}-${randomHex(8)}-01` };
|
|
215
215
|
}
|
|
216
|
+
/**
|
|
217
|
+
* Which AI agent drives this process, if any (report-agent). The platform
|
|
218
|
+
* records it next to what the caller writes — "Arjan's agent (claude-code)" —
|
|
219
|
+
* without changing who the author is or what they may do. `CANOPY_AGENT`
|
|
220
|
+
* names it (`none` opts out, e.g. a person running the CLI from a shell that
|
|
221
|
+
* inherited the variable); otherwise Claude Code is recognised by the
|
|
222
|
+
* `CLAUDECODE=1` it sets in every shell it spawns.
|
|
223
|
+
*/
|
|
224
|
+
const AGENT_RE = /^[a-z0-9-]{1,40}$/;
|
|
225
|
+
const warnedAgents = /* @__PURE__ */ new Set();
|
|
226
|
+
function agentName(env = process.env) {
|
|
227
|
+
const named = env["CANOPY_AGENT"];
|
|
228
|
+
if (named !== void 0 && named !== "") {
|
|
229
|
+
if (named === "none") return void 0;
|
|
230
|
+
if (AGENT_RE.test(named)) return named;
|
|
231
|
+
if (!warnedAgents.has(named)) {
|
|
232
|
+
warnedAgents.add(named);
|
|
233
|
+
console.error(`warning: ignoring CANOPY_AGENT=${JSON.stringify(named)} — use 1–40 characters from a-z, 0-9 and -, or \`none\` to send no agent name.`);
|
|
234
|
+
}
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
return env["CLAUDECODE"] === "1" ? "claude-code" : void 0;
|
|
238
|
+
}
|
|
239
|
+
/** The `x-canopy-agent` header for every platform request (empty when no agent drives us). */
|
|
240
|
+
function agentHeaders(env = process.env) {
|
|
241
|
+
const agent = agentName(env);
|
|
242
|
+
return agent ? { "x-canopy-agent": agent } : {};
|
|
243
|
+
}
|
|
244
|
+
/** Headers every control/data-plane request carries besides auth: trace + agent. */
|
|
245
|
+
function commonHeaders() {
|
|
246
|
+
return {
|
|
247
|
+
...traceHeaders(),
|
|
248
|
+
...agentHeaders()
|
|
249
|
+
};
|
|
250
|
+
}
|
|
216
251
|
var ApiError = class extends Error {
|
|
217
252
|
status;
|
|
218
253
|
code;
|
|
@@ -297,7 +332,7 @@ async function apiRequest(method, path, body, rawContentType, headers) {
|
|
|
297
332
|
const response = await fetch(`${apiUrl}/control/v1${path}`, {
|
|
298
333
|
method,
|
|
299
334
|
headers: {
|
|
300
|
-
...
|
|
335
|
+
...commonHeaders(),
|
|
301
336
|
authorization: `Bearer ${token}`,
|
|
302
337
|
...body === void 0 || isForm ? {} : { "content-type": rawContentType ?? "application/json" },
|
|
303
338
|
...headers
|
|
@@ -317,7 +352,7 @@ async function apiUpload(path, bytes, contentType) {
|
|
|
317
352
|
const response = await fetch(`${apiUrl}/control/v1${path}`, {
|
|
318
353
|
method: "POST",
|
|
319
354
|
headers: {
|
|
320
|
-
...
|
|
355
|
+
...commonHeaders(),
|
|
321
356
|
authorization: `Bearer ${token}`,
|
|
322
357
|
"content-type": contentType
|
|
323
358
|
},
|
|
@@ -330,7 +365,7 @@ async function apiUpload(path, bytes, contentType) {
|
|
|
330
365
|
async function apiDownload(path) {
|
|
331
366
|
const { apiUrl, token, isDeployToken } = credentials();
|
|
332
367
|
const response = await fetch(`${apiUrl}/control/v1${path}`, { headers: {
|
|
333
|
-
...
|
|
368
|
+
...commonHeaders(),
|
|
334
369
|
authorization: `Bearer ${token}`
|
|
335
370
|
} });
|
|
336
371
|
if (!response.ok) return fail(response, isDeployToken, "GET", path);
|
|
@@ -346,7 +381,7 @@ async function apiDataRequest(method, path, projectSlug, body, rawContentType, c
|
|
|
346
381
|
const response = await fetch(`${apiUrl}/data/v1${path}`, {
|
|
347
382
|
method,
|
|
348
383
|
headers: {
|
|
349
|
-
...
|
|
384
|
+
...commonHeaders(),
|
|
350
385
|
authorization: `Bearer ${token}`,
|
|
351
386
|
"x-canopy-project": projectSlug,
|
|
352
387
|
...channel === "staging" ? { "x-canopy-channel": "staging" } : {},
|
|
@@ -359,4 +394,4 @@ async function apiDataRequest(method, path, projectSlug, body, rawContentType, c
|
|
|
359
394
|
return await response.json();
|
|
360
395
|
}
|
|
361
396
|
//#endregion
|
|
362
|
-
export {
|
|
397
|
+
export { apiRequest as a, credentials as c, apiDownload as i, withContext as l, agentHeaders as n, apiUpload as o, apiDataRequest as r, commonHeaders as s, ApiError as t };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { d as tryResolveProject, n as siteConfig } from "./site-DM2J10CB.mjs";
|
|
2
|
-
import {
|
|
2
|
+
import { a as apiRequest, t as ApiError } from "./api-ArfD6-t5.mjs";
|
|
3
3
|
import { defineCommand } from "citty";
|
|
4
4
|
//#region src/browse.ts
|
|
5
5
|
/**
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { c as resolveProject, o as projectFlagArgs } from "./site-DM2J10CB.mjs";
|
|
2
|
-
import {
|
|
3
|
-
import { a as unwrap, i as requireTty, r as reportErrors } from "./ui-
|
|
4
|
-
import { n as migrateProject, r as readMigrationFiles, t as describeMigrateResult } from "./migrations-
|
|
2
|
+
import { a as apiRequest, l as withContext, r as apiDataRequest } from "./api-ArfD6-t5.mjs";
|
|
3
|
+
import { a as unwrap, i as requireTty, r as reportErrors } from "./ui-CaHgvvkJ.mjs";
|
|
4
|
+
import { n as migrateProject, r as readMigrationFiles, t as describeMigrateResult } from "./migrations-CG_O5Ghw.mjs";
|
|
5
5
|
import { defineCommand } from "citty";
|
|
6
6
|
import { readFileSync, writeFileSync } from "node:fs";
|
|
7
7
|
import * as p from "@clack/prompts";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { a as projectDirArgs, c as resolveProject, l as stagingUrl, n as siteConfig, o as projectFlagArgs, s as projectUrl, u as tryReadCanopyJson } from "./site-DM2J10CB.mjs";
|
|
2
|
-
import {
|
|
3
|
-
import { a as unwrap, r as reportErrors, t as formatDateTime } from "./ui-
|
|
4
|
-
import { n as migrateProject, r as readMigrationFiles, t as describeMigrateResult } from "./migrations-
|
|
2
|
+
import { a as apiRequest, i as apiDownload, l as withContext, r as apiDataRequest } from "./api-ArfD6-t5.mjs";
|
|
3
|
+
import { a as unwrap, r as reportErrors, t as formatDateTime } from "./ui-CaHgvvkJ.mjs";
|
|
4
|
+
import { n as migrateProject, r as readMigrationFiles, t as describeMigrateResult } from "./migrations-CG_O5Ghw.mjs";
|
|
5
5
|
import { builtinModules } from "node:module";
|
|
6
6
|
import { defineCommand } from "citty";
|
|
7
7
|
import { mkdtempSync, readFileSync, readdirSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { a as projectDirArgs, c as resolveProject, i as projectDialect, n as siteConfig, u as tryReadCanopyJson } from "./site-DM2J10CB.mjs";
|
|
2
|
-
import {
|
|
3
|
-
import { r as reportErrors, t as formatDateTime } from "./ui-
|
|
4
|
-
import { r as readMigrationFiles } from "./migrations-
|
|
2
|
+
import { a as apiRequest, l as withContext, t as ApiError } from "./api-ArfD6-t5.mjs";
|
|
3
|
+
import { r as reportErrors, t as formatDateTime } from "./ui-CaHgvvkJ.mjs";
|
|
4
|
+
import { r as readMigrationFiles } from "./migrations-CG_O5Ghw.mjs";
|
|
5
5
|
import { i as stampWorkerHeaders, n as WORKER_ATTEST_HEADER } from "./src-CxzGOEhP.mjs";
|
|
6
6
|
import { n as writeDevServerRecord } from "./dev-server-file-Dk9pk4AR.mjs";
|
|
7
7
|
import { createRequire } from "node:module";
|
|
@@ -251,7 +251,7 @@ async function devRemote(flags, slug) {
|
|
|
251
251
|
async function devLocal(flags, slug) {
|
|
252
252
|
let dump;
|
|
253
253
|
if (flags.pull) {
|
|
254
|
-
const { fetchProjectDump } = await import("./local-
|
|
254
|
+
const { fetchProjectDump } = await import("./local-BnfAEwso.mjs");
|
|
255
255
|
console.log(`Pulling a fresh export of ${slug}${flags.staging ? " [staging]" : ""}…`);
|
|
256
256
|
dump = await fetchProjectDump(slug, flags.staging ? "staging" : "live");
|
|
257
257
|
}
|
|
@@ -267,7 +267,7 @@ async function devLocal(flags, slug) {
|
|
|
267
267
|
console.log(`Note: ai/services will be unavailable — could not reach the platform (${reason}).`);
|
|
268
268
|
}
|
|
269
269
|
const workerAttest = randomBytes(24).toString("base64url");
|
|
270
|
-
const { createSandbox } = await import("./local-
|
|
270
|
+
const { createSandbox } = await import("./local-BnfAEwso.mjs");
|
|
271
271
|
const sandbox = await createSandbox({
|
|
272
272
|
slug,
|
|
273
273
|
persistDir: join(flags.dir, ".canopy", "local"),
|
package/dist/docs/cli-create.md
CHANGED
|
@@ -11,6 +11,7 @@ description: Register a new project on Canopy and scaffold it locally from a sta
|
|
|
11
11
|
canopy create # fully interactive
|
|
12
12
|
canopy create "My Portfolio" --slug my-portfolio --template spa
|
|
13
13
|
canopy create existing-app --no-scaffold # register only, no files written
|
|
14
|
+
canopy create "My Retro" --from retro-board # start from a template
|
|
14
15
|
```
|
|
15
16
|
|
|
16
17
|
## Arguments & flags
|
|
@@ -20,6 +21,7 @@ canopy create existing-app --no-scaffold # register only, no files written
|
|
|
20
21
|
- `--template <id>` — starter to scaffold: `static` (plain HTML/CSS/JS, no build step, untyped SDK from the platform — simplest apps), `spa` (Vite + React + TypeScript with the typed SDK from npm — best for real SDK use or growth), or `worker` (`spa` plus a backend worker in `worker/` — for server-side code: secrets, schedules, calling external APIs). A picker appears when omitted on a TTY; `static` is the default otherwise.
|
|
21
22
|
- `--scaffold` / `--no-scaffold` — whether to write `./<slug>/` from the template. When neither is given, the CLI scaffolds unless the current directory already has a `canopy.json` (in which case it assumes you're registering an existing app and only registers).
|
|
22
23
|
- `--repo` / `--no-repo` — also create a git repository for the project on Canopy and set up the new folder to push to it (see [`canopy repo`](https://docs.canopy.website/cli/repo)). When neither is given, an interactive run asks (default no) on sites that offer repositories; non-interactive runs create none.
|
|
24
|
+
- `--from <template>` — start from a template project instead of a starter: the new project gets a copy of the template's code in its own git repository, cloned into `./<slug>/`, plus the setup the code needs. The CLI shows what comes along and asks before creating. `--template`, `--scaffold` and `--repo` don't apply. See [Start from a template](https://docs.canopy.website/guides/templates).
|
|
23
25
|
- `--profile <site>` — global flag (or `CANOPY_PROFILE`): which site to create the project on. The chosen profile is pinned into the scaffolded `canopy.json`.
|
|
24
26
|
|
|
25
27
|
## How it works
|
|
@@ -13,10 +13,12 @@ ones.
|
|
|
13
13
|
canopy feedback list --status investigate
|
|
14
14
|
canopy feedback show <id>
|
|
15
15
|
canopy feedback comment <id> "root cause: the upload grant expires too early"
|
|
16
|
+
canopy feedback note <id> "stack trace and table names for the team"
|
|
16
17
|
canopy feedback status <id> investigated --comment "root cause: the grant expires too early"
|
|
17
18
|
canopy feedback list --status accepted
|
|
18
19
|
canopy feedback status <id> resolved --comment "fixed in v42"
|
|
19
20
|
canopy feedback mine
|
|
21
|
+
canopy feedback watch --project my-project
|
|
20
22
|
canopy feedback report "canopy deploy exits 1 after upload" --command "canopy deploy"
|
|
21
23
|
canopy feedback report "the header overlaps the logo" --attach ./shot.png
|
|
22
24
|
canopy feedback attach <id> ./after-fix.png --comment "fixed — see the shot"
|
|
@@ -46,6 +48,9 @@ machine-readable output.
|
|
|
46
48
|
updates). `--internal` (members only) keeps the note off the reporter's
|
|
47
49
|
view — for stack traces, table names, and other working detail that would
|
|
48
50
|
only confuse them.
|
|
51
|
+
- `note <id> <text>` — post an internal note: the same as
|
|
52
|
+
`comment <id> <text> --internal`, with no way to make it public. Handy
|
|
53
|
+
when you let an agent post notes but not replies the reporter sees.
|
|
49
54
|
- `attach <id> <file> [--attach <more>] [--caption <text>] [--comment <text>] [--internal]` —
|
|
50
55
|
add screenshots to an existing report. With `--comment`, the comment is
|
|
51
56
|
posted first and the screenshots hang off that trail entry — how you show
|
|
@@ -56,6 +61,12 @@ machine-readable output.
|
|
|
56
61
|
findings: pass `--comment`, or comment first.
|
|
57
62
|
- `mine [--project <slug>] [--json]` — your own reports across all projects,
|
|
58
63
|
with current statuses.
|
|
64
|
+
- `watch [--project <slug>]... [--since <time>] [--after <cursor>] [--json]` —
|
|
65
|
+
follow new reports, comments and status changes on projects you're a member
|
|
66
|
+
of, live, until you stop it. Repeat `--project` to follow several. With
|
|
67
|
+
`--json` each event is one line of JSON carrying an `id`; pass the last one
|
|
68
|
+
back as `--after` to pick up where you left off. Events say what happened
|
|
69
|
+
and who did it, not the text — use `show` for that.
|
|
59
70
|
- `report <text> [--kind bug|feedback] [--project <slug>] [--command <cmd>] [--error <output>] [--attach <file>] [--caption <text>]` —
|
|
60
71
|
file a new report. **Without `--project` it goes to the platform inbox**
|
|
61
72
|
(Canopy itself) — the right target when a `canopy` command misbehaves. The
|
|
@@ -65,6 +76,21 @@ machine-readable output.
|
|
|
65
76
|
itself is the write-up of what you found, `accepted` when the person you are
|
|
66
77
|
working with asked for it.
|
|
67
78
|
|
|
79
|
+
## Agents and people
|
|
80
|
+
|
|
81
|
+
Every report and comment remembers how it was made: from the dashboard or
|
|
82
|
+
widget, by a person at the CLI, or by an AI agent driving the CLI. Agent
|
|
83
|
+
entries read as "Ana's agent (claude-code)" in the dashboard, in Slack and
|
|
84
|
+
in `show`, so you can tell what you wrote from what your agent wrote.
|
|
85
|
+
|
|
86
|
+
The CLI notices on its own when Claude Code runs it. For another agent, set
|
|
87
|
+
`CANOPY_AGENT=<name>` (letters, digits and dashes) in its environment. To
|
|
88
|
+
mark a command as yours even inside an agent's shell, set
|
|
89
|
+
`CANOPY_AGENT=none`.
|
|
90
|
+
|
|
91
|
+
You get a Slack message when your own agent does something on a report you
|
|
92
|
+
follow, even though you never get one for your own actions.
|
|
93
|
+
|
|
68
94
|
## For agents
|
|
69
95
|
|
|
70
96
|
Two loops.
|
|
@@ -77,7 +103,7 @@ Stop there — say what it would take to fix and let the person decide.
|
|
|
77
103
|
Split your findings by audience: the status comment is what the **reporter**
|
|
78
104
|
reads, so keep it plain ("the upload fails for files over 5 MB — looking into
|
|
79
105
|
it"). The tech-loaded version — stack traces, table names, reproduction
|
|
80
|
-
queries — goes in `
|
|
106
|
+
queries — goes in `note <id> "<detail>"`, which only project
|
|
81
107
|
members see. An internal note counts as findings for `investigated`.
|
|
82
108
|
|
|
83
109
|
**Build:** `list --status accepted` is what a person has asked for. Do the
|
|
@@ -10,12 +10,14 @@ description: List your Canopy projects with their status, role, and live URLs.
|
|
|
10
10
|
```bash
|
|
11
11
|
canopy projects
|
|
12
12
|
canopy projects --all
|
|
13
|
+
canopy projects --templates
|
|
13
14
|
canopy --profile staging projects
|
|
14
15
|
```
|
|
15
16
|
|
|
16
17
|
## Arguments & flags
|
|
17
18
|
|
|
18
19
|
- `--all` — include projects you can't edit. By default the list shows only projects where you're an owner or editor (admins always see everything, since they can edit everything).
|
|
20
|
+
- `--templates` — list the templates anyone can start a project from, with their owners. See [Start from a template](https://docs.canopy.website/guides/templates).
|
|
19
21
|
- `--profile <site>` — global flag (or `CANOPY_PROFILE`): each site has its own project registry, so listing on `staging` shows a completely different set than `main`.
|
|
20
22
|
|
|
21
23
|
## Details
|
package/dist/docs/cli-secrets.md
CHANGED
|
@@ -25,7 +25,8 @@ target directory, or `--project <slug>` to override — and honor the global
|
|
|
25
25
|
`--profile <site>` / `CANOPY_PROFILE`.
|
|
26
26
|
|
|
27
27
|
- `list` — print the secret names and when each was last set. Values are
|
|
28
|
-
never shown.
|
|
28
|
+
never shown. For a project started from a template, names it still needs a
|
|
29
|
+
value for are listed as missing.
|
|
29
30
|
- `set <name>` — store a value. Names are `UPPER_SNAKE_CASE`, up to 64
|
|
30
31
|
characters. The value comes from **stdin** when piped (one trailing newline
|
|
31
32
|
is trimmed, so `echo` works too), from `--from-env <VAR>` to read an
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: canopy template
|
|
3
|
+
description: Offer a project as a template, or see what a template gives a new project.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
`canopy template` controls whether others can start a project from yours, and
|
|
7
|
+
shows what a template gives a new project. To start from one, use
|
|
8
|
+
[`canopy create --from`](https://docs.canopy.website/cli/create).
|
|
9
|
+
|
|
10
|
+
## Usage
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
canopy template mark [--yes]
|
|
14
|
+
canopy template unmark
|
|
15
|
+
canopy template show <slug>
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Arguments & flags
|
|
19
|
+
|
|
20
|
+
`mark` and `unmark` resolve the project the usual way: `canopy.json` in the
|
|
21
|
+
target folder, or `--project <slug>`. All of them honour the global
|
|
22
|
+
`--profile <site>` / `CANOPY_PROFILE`.
|
|
23
|
+
|
|
24
|
+
- `mark` — let anyone on the site start a project from this one. Owner only.
|
|
25
|
+
The project needs a Canopy git repository with code pushed to it. Asks for
|
|
26
|
+
confirmation; `--yes` skips it in scripts. Warns when `canopy.json` names no
|
|
27
|
+
migrations folder, because new projects then start without any tables.
|
|
28
|
+
- `unmark` — stop new projects starting from this one. Existing copies are not
|
|
29
|
+
affected.
|
|
30
|
+
- `show <slug>` — what a new project gets from a template: the code, outbound
|
|
31
|
+
hosts, page settings, Google access, schedules (turned off) and secret names.
|
|
32
|
+
|
|
33
|
+
## Examples
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
canopy template mark
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Offers the project in this folder as a template.
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
canopy template show retro-board
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Shows what `canopy create --from retro-board` would give you.
|
|
46
|
+
|
|
47
|
+
Read [Start from a template](https://docs.canopy.website/guides/templates) for the bigger picture.
|
package/dist/docs/index.json
CHANGED
|
@@ -72,6 +72,11 @@
|
|
|
72
72
|
"title": "Keep your code in git",
|
|
73
73
|
"summary": "Give your project a git repository on Canopy — history, backup and sharing, no GitHub account needed."
|
|
74
74
|
},
|
|
75
|
+
{
|
|
76
|
+
"slug": "templates",
|
|
77
|
+
"title": "Start from a template",
|
|
78
|
+
"summary": "Begin a project from someone else's working one — a copy of their code and setup, never their data."
|
|
79
|
+
},
|
|
75
80
|
{
|
|
76
81
|
"slug": "agent-tools",
|
|
77
82
|
"title": "Agent tools (MCP)",
|
|
@@ -266,6 +271,11 @@
|
|
|
266
271
|
"title": "canopy repo",
|
|
267
272
|
"summary": "Create, clone and set up the project's git repository on Canopy."
|
|
268
273
|
},
|
|
274
|
+
{
|
|
275
|
+
"slug": "cli-template",
|
|
276
|
+
"title": "canopy template",
|
|
277
|
+
"summary": "Offer a project as a template, or see what a template gives a new project."
|
|
278
|
+
},
|
|
269
279
|
{
|
|
270
280
|
"slug": "cli-egress",
|
|
271
281
|
"title": "canopy egress",
|
|
@@ -91,6 +91,27 @@ of truth for _when_ a job runs, so you can change `daily-digest` from 6:00 to
|
|
|
91
91
|
7:30 — or pause it — without touching code or redeploying. The name is the
|
|
92
92
|
stable contract between the dashboard and your handler.
|
|
93
93
|
|
|
94
|
+
## How long a run may take
|
|
95
|
+
|
|
96
|
+
Plan for **at most 5 minutes** per run. A run that takes longer can be cut
|
|
97
|
+
off before it finishes, and is then recorded as failed. If a job grows past
|
|
98
|
+
that, split it: handle a batch per run and remember where you stopped (in
|
|
99
|
+
the database), or run the schedule more often.
|
|
100
|
+
|
|
101
|
+
What happens around a run:
|
|
102
|
+
|
|
103
|
+
- **No retries.** A failed run is recorded on the schedule row, members are
|
|
104
|
+
notified the first time it fails, and the next attempt is simply the next
|
|
105
|
+
tick.
|
|
106
|
+
- **No overlap protection.** Every tick starts a fresh run, and **Run now**
|
|
107
|
+
can run alongside a scheduled one. Make the handler safe to run twice: write
|
|
108
|
+
idempotently, or take a lock row in the database and skip when it's held.
|
|
109
|
+
- **Deploys don't stop a run.** A run in progress finishes on the version it
|
|
110
|
+
started with, and keeps its data access while it does. New code takes over
|
|
111
|
+
from the next run. Database migrations in a deploy apply right away, so a
|
|
112
|
+
run on the old version may meet the new schema. Additive migrations keep
|
|
113
|
+
that safe.
|
|
114
|
+
|
|
94
115
|
## Things to know
|
|
95
116
|
|
|
96
117
|
- **Cron runs as the project, not as a person.** Data access works (see
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Start from a template
|
|
3
|
+
description: Begin a project from someone else's working one — a copy of their code and setup, never their data.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Some projects are worth starting from: a retro board, a feedback form, a small
|
|
7
|
+
dashboard on an internal API. When the owner offers one as a **template**,
|
|
8
|
+
anyone on Canopy can start a project of their own from it. You get a copy of
|
|
9
|
+
the code and the setup the code needs, then change it however you like.
|
|
10
|
+
|
|
11
|
+
Your copy is yours. Later changes to the template don't reach it, and nothing
|
|
12
|
+
you do reaches the template.
|
|
13
|
+
|
|
14
|
+
## Find one
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
canopy projects --templates
|
|
18
|
+
canopy template show retro-board
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
`show` lists exactly what a new project gets. In the dashboard, switch on the
|
|
22
|
+
**Templates** filter on the project list; each template has a **Use as
|
|
23
|
+
template** button that shows the same list.
|
|
24
|
+
|
|
25
|
+
## Start your project
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
canopy create "My Retro" --slug my-retro --from retro-board
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Canopy creates the project, gives it its own git repository with the
|
|
32
|
+
template's code in one fresh commit, and clones it into `./my-retro/`. It then
|
|
33
|
+
tells you what is left, usually:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
cd my-retro
|
|
37
|
+
canopy secrets set OPENAI_KEY
|
|
38
|
+
canopy deploy
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
The first deploy runs the template's migrations, so your database starts with
|
|
42
|
+
its tables and nothing else.
|
|
43
|
+
|
|
44
|
+
## What you get, and what you don't
|
|
45
|
+
|
|
46
|
+
You get:
|
|
47
|
+
|
|
48
|
+
- the code in the template's repository, as it is right now, without its
|
|
49
|
+
history;
|
|
50
|
+
- its allowed outbound hosts and page settings;
|
|
51
|
+
- the Google access it asks visitors for (restricted Google access stays off
|
|
52
|
+
until a Canopy admin unlocks it for your project);
|
|
53
|
+
- its schedules, **turned off** — turn each one on when you're ready;
|
|
54
|
+
- the **names** of its secrets, so you know what to fill in. `canopy secrets
|
|
55
|
+
list` shows the ones still missing.
|
|
56
|
+
|
|
57
|
+
You never get its data, stored files, secret values, members, client password,
|
|
58
|
+
budgets, feedback or deploy history. Your project starts with the usual
|
|
59
|
+
defaults for those.
|
|
60
|
+
|
|
61
|
+
## Offer your project as a template
|
|
62
|
+
|
|
63
|
+
Your project needs a Canopy git repository with your code pushed to it (see
|
|
64
|
+
[Keep your code in git](https://docs.canopy.website/guides/git)). Then:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
canopy template mark
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
or **Settings → Template** in the dashboard. Only the owner can do this.
|
|
71
|
+
|
|
72
|
+
Before you do:
|
|
73
|
+
|
|
74
|
+
- **Everyone on the site can copy the code.** Make sure the repository holds no
|
|
75
|
+
keys, passwords or client data.
|
|
76
|
+
- **Put sample data in migrations.** The database never comes along, so any
|
|
77
|
+
starting rows belong in your migrations. Without a migrations folder, new
|
|
78
|
+
projects start with an empty database.
|
|
79
|
+
- **Explain setup in the README.** It's the first thing people read in their
|
|
80
|
+
copy.
|
|
81
|
+
|
|
82
|
+
`canopy template unmark` stops new copies. Projects already started from it
|
|
83
|
+
are not affected. The template's Settings page shows how many projects started
|
|
84
|
+
from it.
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { t as version } from "./package-
|
|
1
|
+
import { t as version } from "./package-CJJpjVh_.mjs";
|
|
2
2
|
import { n as siteConfig } from "./site-DM2J10CB.mjs";
|
|
3
3
|
import { i as latestKnownVersion, r as compareSemver } from "./version-check-CcsA1QjI.mjs";
|
|
4
|
-
import {
|
|
4
|
+
import { n as agentHeaders } from "./api-ArfD6-t5.mjs";
|
|
5
|
+
import { r as reportErrors } from "./ui-CaHgvvkJ.mjs";
|
|
5
6
|
import { defineCommand } from "citty";
|
|
6
7
|
import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from "node:fs";
|
|
7
8
|
import { homedir } from "node:os";
|
|
@@ -62,7 +63,10 @@ function parseFrontmatter(raw) {
|
|
|
62
63
|
/** Bearer header from the deploy token (CI) or the CLI login, if either exists. */
|
|
63
64
|
function authHeaders() {
|
|
64
65
|
const token = process.env["CANOPY_DEPLOY_TOKEN"] || siteConfig().token;
|
|
65
|
-
return token ? {
|
|
66
|
+
return token ? {
|
|
67
|
+
authorization: `Bearer ${token}`,
|
|
68
|
+
...agentHeaders()
|
|
69
|
+
} : {};
|
|
66
70
|
}
|
|
67
71
|
async function fetchRes(url) {
|
|
68
72
|
try {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { a as projectDirArgs, c as resolveProject } from "./site-DM2J10CB.mjs";
|
|
2
|
-
import {
|
|
3
|
-
import { a as unwrap, i as requireTty, r as reportErrors, t as formatDateTime } from "./ui-
|
|
2
|
+
import { a as apiRequest, l as withContext } from "./api-ArfD6-t5.mjs";
|
|
3
|
+
import { a as unwrap, i as requireTty, r as reportErrors, t as formatDateTime } from "./ui-CaHgvvkJ.mjs";
|
|
4
4
|
import { defineCommand } from "citty";
|
|
5
5
|
import * as p from "@clack/prompts";
|
|
6
6
|
//#region src/egress.ts
|