@mediamonks/canopy-cli 0.7.12 → 0.7.14
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/{db-8Etjmzxh.mjs → db-DKnJQPm5.mjs} +2 -2
- package/dist/{deploy-B1WrPq5W.mjs → deploy-CnxJ4TE6.mjs} +2 -2
- package/dist/dev-BqLvttKc.mjs +2 -0
- package/dist/{dev-BnUYGQQm.mjs → dev-CyJT6EfQ.mjs} +4 -4
- package/dist/docs/ci.md +50 -0
- 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 +82 -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-feedback.md +85 -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/cli.md +86 -0
- package/dist/docs/data.md +85 -0
- package/dist/docs/deploy.md +81 -0
- package/dist/docs/dev.md +72 -0
- package/dist/docs/developers-agents-and-ai.md +95 -0
- package/dist/docs/developers-architecture.md +106 -0
- package/dist/docs/getting-started.md +108 -0
- package/dist/docs/index.json +336 -0
- package/dist/docs/infrastructure.md +406 -0
- package/dist/docs/migrations.md +183 -0
- package/dist/docs/overview-faq.md +105 -0
- package/dist/docs/overview-how-it-works.md +82 -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 +54 -0
- package/dist/docs/platform-deploys-and-rollbacks.md +93 -0
- package/dist/docs/platform-egress.md +75 -0
- package/dist/docs/platform-feedback.md +119 -0
- package/dist/docs/platform-google.md +213 -0
- package/dist/docs/platform-members-and-access.md +71 -0
- package/dist/docs/platform-project-data.md +76 -0
- package/dist/docs/platform-schedules.md +90 -0
- package/dist/docs/platform-sites-and-profiles.md +61 -0
- package/dist/docs/porting-nextjs.md +233 -0
- package/dist/docs/sdk-ai.md +103 -0
- package/dist/docs/sdk-auth.md +57 -0
- package/dist/docs/sdk-database.md +126 -0
- package/dist/docs/sdk-files.md +82 -0
- package/dist/docs/sdk-services.md +58 -0
- package/dist/docs/sdk.md +126 -0
- package/dist/docs/staging.md +106 -0
- package/dist/docs/static-serving.md +323 -0
- package/dist/docs/troubleshooting.md +102 -0
- package/dist/{docs-DxB7NhZs.mjs → docs-3pQHiuTn.mjs} +2 -2
- package/dist/{egress-CN_axsAN.mjs → egress-BFSwa8Uu.mjs} +1 -1
- package/dist/{feedback-B-GXUBPs.mjs → feedback-DReTDDzD.mjs} +26 -7
- package/dist/index.mjs +25 -24
- package/dist/{local-BGRd_FMZ.mjs → local-BQxsp-ZQ.mjs} +2 -2
- package/dist/{login-D7dI8D9N.mjs → login-BDTO3nVw.mjs} +1 -1
- package/dist/{logs-DNiZquki.mjs → logs-C53y2eAS.mjs} +1 -1
- package/dist/{migrations-C-5x6SYf.mjs → migrations-C6EcKD6a.mjs} +1 -1
- package/dist/{open-DRPp4QJg.mjs → open-C5x3tX6I.mjs} +1 -1
- package/dist/{package-CDLufecy.mjs → package-CuyFMXam.mjs} +1 -1
- package/dist/{projects-C6JQjVqg.mjs → projects-MMbv7MmK.mjs} +1 -1
- package/dist/schedule-Cq9K0rnE.mjs +106 -0
- package/dist/skill/canopy/SKILL.md +160 -0
- package/dist/{skill-BOWfqwr2.mjs → skill-M4RHvuUB.mjs} +2 -2
- package/dist/{staging-Bw1D_J8M.mjs → staging-D8G4amjO.mjs} +1 -1
- package/dist/templates/spa/.github/workflows/canopy-deploy.yml +34 -0
- package/dist/templates/spa/AGENTS.md +47 -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 +45 -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/.dev.vars.example +9 -0
- package/dist/templates/worker/.github/workflows/canopy-deploy.yml +34 -0
- package/dist/templates/worker/AGENTS.md +63 -0
- package/dist/templates/worker/README.md +17 -0
- package/dist/templates/worker/_gitignore +5 -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 +52 -0
- package/dist/{token-D7zrIF8G.mjs → token-BUxgRJVA.mjs} +1 -1
- package/dist/{ui-2mrJcjYC.mjs → ui-B1Px5y7q.mjs} +17 -0
- package/dist/{upgrade-fhfrid_O.mjs → upgrade-C8Xox-P-.mjs} +2 -2
- package/dist/{usage-CX1Izj_l.mjs → usage-DjNngyvy.mjs} +1 -1
- package/package.json +1 -1
- package/dist/dev-CySBxW7I.mjs +0 -2
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { a as projectDirArgs, c as resolveProject } from "./site-BUlQR117.mjs";
|
|
2
|
-
import { c as apiRequest, n as reportErrors } from "./ui-
|
|
2
|
+
import { c as apiRequest, n as reportErrors } from "./ui-B1Px5y7q.mjs";
|
|
3
3
|
import { defineCommand } from "citty";
|
|
4
4
|
//#region src/egress.ts
|
|
5
5
|
/**
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { t as version } from "./package-
|
|
1
|
+
import { t as version } from "./package-CuyFMXam.mjs";
|
|
2
2
|
import { a as projectDirArgs, c as resolveProject, n as siteConfig, u as tryReadCanopyJson } from "./site-BUlQR117.mjs";
|
|
3
|
-
import { c as apiRequest, l as apiUpload, n as reportErrors, s as apiDownload } from "./ui-
|
|
3
|
+
import { c as apiRequest, l as apiUpload, n as reportErrors, s as apiDownload } from "./ui-B1Px5y7q.mjs";
|
|
4
4
|
import { defineCommand } from "citty";
|
|
5
5
|
import { basename, extname, join } from "node:path";
|
|
6
6
|
import { readFile, writeFile } from "node:fs/promises";
|
|
@@ -165,7 +165,8 @@ const showCommand = defineCommand({
|
|
|
165
165
|
for (const entry of comments) {
|
|
166
166
|
const author = entry.author_name || entry.author_email || "unknown";
|
|
167
167
|
const move = entry.status_to ? ` [${entry.status_from} → ${entry.status_to}]` : "";
|
|
168
|
-
|
|
168
|
+
const internal = entry.internal ? " [internal]" : "";
|
|
169
|
+
console.log(`--- ${author} · ${entry.created_at}${move}${internal}`);
|
|
169
170
|
if (entry.body) console.log(entry.body);
|
|
170
171
|
}
|
|
171
172
|
if (args.download && shots.length > 0) {
|
|
@@ -196,12 +197,19 @@ const commentCommand = defineCommand({
|
|
|
196
197
|
description: "Comment text",
|
|
197
198
|
required: true
|
|
198
199
|
},
|
|
200
|
+
internal: {
|
|
201
|
+
type: "boolean",
|
|
202
|
+
description: "Triager-only note, hidden from the reporter — for tech detail (stack traces, table names) that would only confuse them (members only)"
|
|
203
|
+
},
|
|
199
204
|
...projectDirArgs
|
|
200
205
|
},
|
|
201
206
|
run: reportErrors("feedback comment", async ({ args }) => {
|
|
202
207
|
const { slug } = resolveProject(args);
|
|
203
|
-
await apiRequest("POST", `/projects/${slug}/feedback/${encodeURIComponent(args.id)}/comments`, {
|
|
204
|
-
|
|
208
|
+
await apiRequest("POST", `/projects/${slug}/feedback/${encodeURIComponent(args.id)}/comments`, {
|
|
209
|
+
body: args.text,
|
|
210
|
+
...args.internal ? { internal: true } : {}
|
|
211
|
+
});
|
|
212
|
+
console.log(args.internal ? "Internal note added (hidden from the reporter)." : "Comment added.");
|
|
205
213
|
})
|
|
206
214
|
});
|
|
207
215
|
const statusCommand = defineCommand({
|
|
@@ -366,17 +374,28 @@ const feedbackCommand = defineCommand({
|
|
|
366
374
|
type: "string",
|
|
367
375
|
description: "Post this comment first and hang the screenshots off it"
|
|
368
376
|
},
|
|
377
|
+
internal: {
|
|
378
|
+
type: "boolean",
|
|
379
|
+
description: "Make the --comment a triager-only note; its screenshots are hidden from the reporter too (members only)"
|
|
380
|
+
},
|
|
369
381
|
...projectDirArgs
|
|
370
382
|
},
|
|
371
383
|
run: reportErrors("feedback attach", async ({ args }) => {
|
|
372
384
|
const { slug } = resolveProject(args);
|
|
373
385
|
const paths = [args.file, ...attachedPaths(args.attach)];
|
|
374
386
|
for (const path of paths) await readFile(path);
|
|
387
|
+
if (args.internal && !args.comment) {
|
|
388
|
+
console.error("--internal needs a --comment to hang the screenshots off.");
|
|
389
|
+
process.exit(1);
|
|
390
|
+
}
|
|
375
391
|
let commentId;
|
|
376
392
|
if (args.comment) {
|
|
377
|
-
const { comment } = await apiRequest("POST", `/projects/${slug}/feedback/${encodeURIComponent(args.id)}/comments`, {
|
|
393
|
+
const { comment } = await apiRequest("POST", `/projects/${slug}/feedback/${encodeURIComponent(args.id)}/comments`, {
|
|
394
|
+
body: args.comment,
|
|
395
|
+
...args.internal ? { internal: true } : {}
|
|
396
|
+
});
|
|
378
397
|
commentId = comment.id;
|
|
379
|
-
console.log("Comment added.");
|
|
398
|
+
console.log(args.internal ? "Internal note added (hidden from the reporter)." : "Comment added.");
|
|
380
399
|
}
|
|
381
400
|
await attachFiles(slug, args.id, paths, args.caption, commentId);
|
|
382
401
|
})
|
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-CuyFMXam.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";
|
|
@@ -60,29 +60,30 @@ const main = defineCommand({
|
|
|
60
60
|
description: "canopy — create and deploy vibecoded projects"
|
|
61
61
|
},
|
|
62
62
|
subCommands: {
|
|
63
|
-
login: () => import("./login-
|
|
64
|
-
whoami: () => import("./login-
|
|
65
|
-
logout: () => import("./login-
|
|
66
|
-
create: () => import("./projects-
|
|
67
|
-
init: () => import("./projects-
|
|
68
|
-
projects: () => import("./projects-
|
|
69
|
-
delete: () => import("./projects-
|
|
70
|
-
dev: () => import("./dev-
|
|
71
|
-
deploy: () => import("./deploy-
|
|
72
|
-
promote: () => import("./deploy-
|
|
73
|
-
staging: () => import("./staging-
|
|
74
|
-
rollback: () => import("./deploy-
|
|
75
|
-
download: () => import("./deploy-
|
|
76
|
-
token: () => import("./token-
|
|
77
|
-
egress: () => import("./egress-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
63
|
+
login: () => import("./login-BDTO3nVw.mjs").then((m) => m.loginCommand),
|
|
64
|
+
whoami: () => import("./login-BDTO3nVw.mjs").then((m) => m.whoamiCommand),
|
|
65
|
+
logout: () => import("./login-BDTO3nVw.mjs").then((m) => m.logoutCommand),
|
|
66
|
+
create: () => import("./projects-MMbv7MmK.mjs").then((m) => m.createCommand),
|
|
67
|
+
init: () => import("./projects-MMbv7MmK.mjs").then((m) => m.initCommand),
|
|
68
|
+
projects: () => import("./projects-MMbv7MmK.mjs").then((m) => m.projectsCommand),
|
|
69
|
+
delete: () => import("./projects-MMbv7MmK.mjs").then((m) => m.deleteCommand),
|
|
70
|
+
dev: () => import("./dev-BqLvttKc.mjs").then((m) => m.devCommand),
|
|
71
|
+
deploy: () => import("./deploy-CnxJ4TE6.mjs").then((m) => m.deployCommand),
|
|
72
|
+
promote: () => import("./deploy-CnxJ4TE6.mjs").then((m) => m.promoteCommand),
|
|
73
|
+
staging: () => import("./staging-D8G4amjO.mjs").then((m) => m.stagingCommand),
|
|
74
|
+
rollback: () => import("./deploy-CnxJ4TE6.mjs").then((m) => m.rollbackCommand),
|
|
75
|
+
download: () => import("./deploy-CnxJ4TE6.mjs").then((m) => m.downloadCommand),
|
|
76
|
+
token: () => import("./token-BUxgRJVA.mjs").then((m) => m.tokenCommand),
|
|
77
|
+
egress: () => import("./egress-BFSwa8Uu.mjs").then((m) => m.egressCommand),
|
|
78
|
+
schedule: () => import("./schedule-Cq9K0rnE.mjs").then((m) => m.scheduleCommand),
|
|
79
|
+
feedback: () => import("./feedback-DReTDDzD.mjs").then((m) => m.feedbackCommand),
|
|
80
|
+
db: () => import("./db-DKnJQPm5.mjs").then((m) => m.dbCommand),
|
|
81
|
+
usage: () => import("./usage-DjNngyvy.mjs").then((m) => m.usageCommand),
|
|
82
|
+
open: () => import("./open-C5x3tX6I.mjs").then((m) => m.openCommand),
|
|
83
|
+
docs: () => import("./docs-3pQHiuTn.mjs").then((m) => m.docsCommand),
|
|
84
|
+
skill: () => import("./skill-M4RHvuUB.mjs").then((m) => m.skillCommand),
|
|
85
|
+
upgrade: () => import("./upgrade-C8Xox-P-.mjs").then((m) => m.upgradeCommand),
|
|
86
|
+
logs: () => import("./logs-C53y2eAS.mjs").then((m) => m.logsCommand)
|
|
86
87
|
}
|
|
87
88
|
});
|
|
88
89
|
if (process.argv[2] !== "upgrade") notifyOfUpdate(version);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { o as apiDataRequest } from "./ui-
|
|
1
|
+
import { o as apiDataRequest } from "./ui-B1Px5y7q.mjs";
|
|
2
2
|
import { n as META_COLUMNS, r as blobRouter, t as dbRouter } from "./src-Bu4Ze32I.mjs";
|
|
3
|
-
import { n as rewriteApiPath } from "./dev-
|
|
3
|
+
import { n as rewriteApiPath } from "./dev-CyJT6EfQ.mjs";
|
|
4
4
|
import { createRequire } from "node:module";
|
|
5
5
|
import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
6
6
|
import { dirname, join, relative, sep } from "node:path";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { _ as getBakedProfile, d as clearAllProfileTokens, f as listBakedProfiles, g as writeConfigFile, h as upsertProfileAuth, m as setActiveProfile, n as siteConfig, p as readConfigFile, v as isProfileId } from "./site-BUlQR117.mjs";
|
|
2
|
-
import { n as reportErrors, t as openBrowser } from "./ui-
|
|
2
|
+
import { n as reportErrors, t as openBrowser } from "./ui-B1Px5y7q.mjs";
|
|
3
3
|
import { defineCommand } from "citty";
|
|
4
4
|
import * as p from "@clack/prompts";
|
|
5
5
|
import { createHash, randomBytes } from "node:crypto";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { c as resolveProject, o as projectFlagArgs } from "./site-BUlQR117.mjs";
|
|
2
|
-
import { c as apiRequest, n as reportErrors, u as withContext } from "./ui-
|
|
2
|
+
import { c as apiRequest, n as reportErrors, u as withContext } from "./ui-B1Px5y7q.mjs";
|
|
3
3
|
import { defineCommand } from "citty";
|
|
4
4
|
//#region src/logs.ts
|
|
5
5
|
/**
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { u as tryReadCanopyJson } from "./site-BUlQR117.mjs";
|
|
2
|
-
import { o as apiDataRequest } from "./ui-
|
|
2
|
+
import { o as apiDataRequest } from "./ui-B1Px5y7q.mjs";
|
|
3
3
|
import { readFileSync, readdirSync } from "node:fs";
|
|
4
4
|
import { join, resolve } from "node:path";
|
|
5
5
|
//#region src/migrations.ts
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { a as projectDirArgs, c as resolveProject, l as stagingUrl, s as projectUrl } from "./site-BUlQR117.mjs";
|
|
2
|
-
import { n as reportErrors, t as openBrowser } from "./ui-
|
|
2
|
+
import { n as reportErrors, t as openBrowser } from "./ui-B1Px5y7q.mjs";
|
|
3
3
|
import { defineCommand } from "citty";
|
|
4
4
|
//#region src/open.ts
|
|
5
5
|
/**
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { n as siteConfig, s as projectUrl, u as tryReadCanopyJson } from "./site-BUlQR117.mjs";
|
|
2
|
-
import { c as apiRequest, i as unwrap, n as reportErrors, r as requireTty } from "./ui-
|
|
2
|
+
import { c as apiRequest, i as unwrap, n as reportErrors, r as requireTty } from "./ui-B1Px5y7q.mjs";
|
|
3
3
|
import { defineCommand } from "citty";
|
|
4
4
|
import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from "node:fs";
|
|
5
5
|
import { join, relative, resolve, sep } from "node:path";
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { c as resolveProject, o as projectFlagArgs } from "./site-BUlQR117.mjs";
|
|
2
|
+
import { c as apiRequest, n as reportErrors, u as withContext } from "./ui-B1Px5y7q.mjs";
|
|
3
|
+
import { defineCommand } from "citty";
|
|
4
|
+
//#region src/schedule.ts
|
|
5
|
+
/**
|
|
6
|
+
* canopy schedule — manage the project's cron schedules (docs/07-schedules.md).
|
|
7
|
+
* Each schedule ticks the backend worker's scheduled() handler on its cron.
|
|
8
|
+
* Same control-plane routes the dashboard's Schedules panel uses.
|
|
9
|
+
*/
|
|
10
|
+
/** One line per schedule: name, cron, state, last run. */
|
|
11
|
+
function renderSchedules(schedules) {
|
|
12
|
+
const nameWidth = Math.max(...schedules.map((s) => s.name.length));
|
|
13
|
+
const cronWidth = Math.max(...schedules.map((s) => s.cron.length));
|
|
14
|
+
return schedules.map((s) => {
|
|
15
|
+
const state = s.enabled ? "enabled " : "disabled";
|
|
16
|
+
const lastRun = s.last_run_at ? `last run ${s.last_run_at} ${s.last_status ?? ""}`.trimEnd() + (s.last_error ? ` (${s.last_error})` : "") : "never run";
|
|
17
|
+
return `${s.name.padEnd(nameWidth)} ${s.cron.padEnd(cronWidth)} ${state} ${lastRun}`;
|
|
18
|
+
}).join("\n");
|
|
19
|
+
}
|
|
20
|
+
const scheduleCommand = defineCommand({
|
|
21
|
+
meta: {
|
|
22
|
+
name: "schedule",
|
|
23
|
+
description: "Manage the project's cron schedules (they tick the backend worker)"
|
|
24
|
+
},
|
|
25
|
+
subCommands: {
|
|
26
|
+
list: defineCommand({
|
|
27
|
+
meta: {
|
|
28
|
+
name: "list",
|
|
29
|
+
description: "List the project's cron schedules"
|
|
30
|
+
},
|
|
31
|
+
args: {
|
|
32
|
+
...projectFlagArgs,
|
|
33
|
+
json: {
|
|
34
|
+
type: "boolean",
|
|
35
|
+
description: "Print raw JSON (same shape as the dashboard Schedules API)",
|
|
36
|
+
default: false
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
run: reportErrors("schedule list", async ({ args }) => {
|
|
40
|
+
const { slug } = resolveProject(args);
|
|
41
|
+
const { schedules, max } = await withContext(apiRequest("GET", `/projects/${slug}/schedules`), { action: `list schedules for "${slug}"` });
|
|
42
|
+
if (args.json) {
|
|
43
|
+
console.log(JSON.stringify({
|
|
44
|
+
slug,
|
|
45
|
+
schedules,
|
|
46
|
+
max
|
|
47
|
+
}, null, 2));
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
if (schedules.length === 0) {
|
|
51
|
+
console.log(`No schedules for ${slug} — add one with \`canopy schedule add <name> "<cron>"\`.`);
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
console.log(renderSchedules(schedules));
|
|
55
|
+
})
|
|
56
|
+
}),
|
|
57
|
+
add: defineCommand({
|
|
58
|
+
meta: {
|
|
59
|
+
name: "add",
|
|
60
|
+
description: "Add or update a schedule (e.g. canopy schedule add refresh-feeds \"0 5 * * *\")"
|
|
61
|
+
},
|
|
62
|
+
args: {
|
|
63
|
+
name: {
|
|
64
|
+
type: "positional",
|
|
65
|
+
description: "Schedule name (lowercase letters, digits, hyphens)",
|
|
66
|
+
required: true
|
|
67
|
+
},
|
|
68
|
+
cron: {
|
|
69
|
+
type: "positional",
|
|
70
|
+
description: "Five-field cron expression, quoted (e.g. \"0 5 * * *\")",
|
|
71
|
+
required: true
|
|
72
|
+
},
|
|
73
|
+
...projectFlagArgs
|
|
74
|
+
},
|
|
75
|
+
run: reportErrors("schedule add", async ({ args }) => {
|
|
76
|
+
const { slug } = resolveProject(args);
|
|
77
|
+
const { schedule } = await withContext(apiRequest("PUT", `/projects/${slug}/schedules/${encodeURIComponent(args.name)}`, { cron: args.cron }), { action: `add schedule "${args.name}" to "${slug}"` });
|
|
78
|
+
console.log(`Scheduled ${schedule.name} (${schedule.cron}) for ${slug} — it ticks the worker's scheduled() handler.`);
|
|
79
|
+
})
|
|
80
|
+
}),
|
|
81
|
+
remove: defineCommand({
|
|
82
|
+
meta: {
|
|
83
|
+
name: "remove",
|
|
84
|
+
description: "Remove a schedule"
|
|
85
|
+
},
|
|
86
|
+
args: {
|
|
87
|
+
name: {
|
|
88
|
+
type: "positional",
|
|
89
|
+
description: "Schedule name to remove",
|
|
90
|
+
required: true
|
|
91
|
+
},
|
|
92
|
+
...projectFlagArgs
|
|
93
|
+
},
|
|
94
|
+
run: reportErrors("schedule remove", async ({ args }) => {
|
|
95
|
+
const { slug } = resolveProject(args);
|
|
96
|
+
await withContext(apiRequest("DELETE", `/projects/${slug}/schedules/${encodeURIComponent(args.name)}`), {
|
|
97
|
+
action: `remove schedule "${args.name}" from "${slug}"`,
|
|
98
|
+
fixes: { not_found: `No schedule named "${args.name}" on ${slug} — see \`canopy schedule list\`.` }
|
|
99
|
+
});
|
|
100
|
+
console.log(`Removed ${args.name} from ${slug} — it will not run again.`);
|
|
101
|
+
})
|
|
102
|
+
})
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
//#endregion
|
|
106
|
+
export { scheduleCommand };
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: canopy
|
|
3
|
+
description: >-
|
|
4
|
+
Build, run, and deploy projects on Canopy — Monks' managed platform for
|
|
5
|
+
vibecoded apps (hosting + database + blob storage + AI). Use this whenever the
|
|
6
|
+
user wants to create a Canopy project, run/deploy one, manage its data or
|
|
7
|
+
deploy tokens, or asks how to use the `canopy` CLI. All platform docs are
|
|
8
|
+
available offline via `canopy docs`.
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Canopy
|
|
12
|
+
|
|
13
|
+
Canopy is Monks' managed platform for vibecoded projects. Everything is driven
|
|
14
|
+
by one CLI: **`canopy`**. This skill tells you how to drive it; the CLI itself
|
|
15
|
+
carries the full, always-current documentation.
|
|
16
|
+
|
|
17
|
+
## First: is the CLI installed and signed in?
|
|
18
|
+
|
|
19
|
+
Run these before anything else, in order:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
canopy --version # is the CLI installed?
|
|
23
|
+
canopy whoami # is there a valid login?
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
- **`canopy --version` fails / command not found** → install it:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
npm install -g @mediamonks/canopy-cli
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
(Needs Node.js 20+. If `npm` is missing, tell the user to install Node first.)
|
|
33
|
+
|
|
34
|
+
- **`canopy whoami` says not logged in / expired** → run:
|
|
35
|
+
```bash
|
|
36
|
+
canopy login
|
|
37
|
+
```
|
|
38
|
+
This opens the user's browser. **Then STOP and tell the user to complete the
|
|
39
|
+
sign-in in their browser.** The command waits for them and finishes on its own.
|
|
40
|
+
- Never ask for or type the user's password, OneLogin codes, or any
|
|
41
|
+
credentials yourself.
|
|
42
|
+
- Login only works on the user's own machine (it uses a `127.0.0.1` redirect).
|
|
43
|
+
If you're on a remote/headless box, tell the user — it can't complete there.
|
|
44
|
+
- `canopy login --json` gives machine-readable status if you need to parse it.
|
|
45
|
+
|
|
46
|
+
Skip login if `canopy whoami` already shows a valid, unexpired token.
|
|
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
|
+
|
|
68
|
+
## Then: read the docs for whatever you're doing
|
|
69
|
+
|
|
70
|
+
The CLI ships the full documentation and keeps it fresh from the platform. List
|
|
71
|
+
the topics and read the relevant one _before_ running unfamiliar commands:
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
canopy docs # list all topics
|
|
75
|
+
canopy docs deploy # print one topic
|
|
76
|
+
```
|
|
77
|
+
|
|
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.
|
|
84
|
+
|
|
85
|
+
## Command map (quick orientation — see `canopy docs cli` for detail)
|
|
86
|
+
|
|
87
|
+
- Create a **new** project: `canopy create`
|
|
88
|
+
- Set up a folder for an **existing** project (e.g. made in the dashboard):
|
|
89
|
+
`canopy init <slug>` — never use `canopy create` for a project that already
|
|
90
|
+
exists; that would register a duplicate. `canopy init <slug> .` targets the
|
|
91
|
+
current directory; add `--no-scaffold` to link an app you already have.
|
|
92
|
+
- Run locally: `canopy dev` (live data) or `canopy dev --local` (offline sandbox)
|
|
93
|
+
- Deploy: `canopy deploy` (add `--build` if the project has a build step)
|
|
94
|
+
- Test first on staging: `canopy deploy --staging` → verify → `canopy promote`
|
|
95
|
+
(own database cloned from live; `canopy staging status | sync | destroy`)
|
|
96
|
+
- Roll back: `canopy rollback`
|
|
97
|
+
- Open the live site: `canopy open`
|
|
98
|
+
- Manage data: `canopy db pull | snapshot | snapshots | restore | migrate`
|
|
99
|
+
- Work bug reports & feedback: `canopy feedback list | show | comment | status | report`
|
|
100
|
+
— see `canopy docs platform-feedback` for the triage and reporting loops
|
|
101
|
+
- CI credentials: `canopy token create | list | revoke`
|
|
102
|
+
|
|
103
|
+
## Custom tables & migrations (devs; Drizzle recommended)
|
|
104
|
+
|
|
105
|
+
The document API (`canopy.db.collection(…)`) needs no schema — prefer it. When
|
|
106
|
+
a project defines its **own tables** (raw `canopy.db.sql()` / Drizzle), use the
|
|
107
|
+
migrations flow — read `canopy docs migrations` first. The rules that prevent
|
|
108
|
+
confusing failures:
|
|
109
|
+
|
|
110
|
+
- Pin `"dialect"` and `"migrations"` in `canopy.json`; put ordered `.sql`
|
|
111
|
+
files in that directory (Drizzle: `npx drizzle-kit generate` writes them).
|
|
112
|
+
- **There is no `DATABASE_URL` and never will be.** Never run
|
|
113
|
+
`drizzle-kit push`/`migrate`/`studio` or Drizzle's runtime `migrate()` —
|
|
114
|
+
generating is local, applying is Canopy's job: `canopy deploy` applies
|
|
115
|
+
pending migrations automatically (`canopy db migrate` applies without a
|
|
116
|
+
deploy; `canopy dev --local` applies to the sandbox on boot).
|
|
117
|
+
- Never edit an applied migration (the platform refuses edited history) and
|
|
118
|
+
never reference `_canopy_*` tables.
|
|
119
|
+
- **Schema change? Rehearse it on staging.** `canopy deploy --staging` applies
|
|
120
|
+
pending migrations to the staging database (a clone of live) — verify the
|
|
121
|
+
app there, then `canopy promote` replays them against live and flips the
|
|
122
|
+
version. Never promote with failing checks.
|
|
123
|
+
|
|
124
|
+
## Choosing a starter template (`canopy create` / `canopy init`)
|
|
125
|
+
|
|
126
|
+
Both `canopy create` and `canopy init` scaffold from the same three templates.
|
|
127
|
+
**`static` is the default, but reach
|
|
128
|
+
for it only when the app is genuinely trivial** — a page or two with light SDK
|
|
129
|
+
use. Its SDK is served untyped from the platform (`/_api/data/v1/sdk.js`): no
|
|
130
|
+
autocomplete, no type-checking, mistakes only show up in the browser.
|
|
131
|
+
|
|
132
|
+
Prefer **`spa`** the moment either is true — which covers most real projects:
|
|
133
|
+
|
|
134
|
+
- **You lean on the SDK** (`db` queries, `blob`, `ai` — anything past a stray
|
|
135
|
+
`auth.user()`). `spa` installs the SDK from npm (`@mediamonks/canopy`), fully
|
|
136
|
+
typed, so the compiler catches mistakes as you write them.
|
|
137
|
+
- **The app will grow.** `spa` is Vite + React + TypeScript, giving components,
|
|
138
|
+
routing, and state a real home; `static` has no build step to organise into.
|
|
139
|
+
|
|
140
|
+
Use **`worker`** when you need server-side backend code — e.g. calling an
|
|
141
|
+
external API with a secret the browser can't hold, or running scheduled jobs.
|
|
142
|
+
It's `spa` plus Hono-style handlers in `worker/`; `canopy deploy` publishes
|
|
143
|
+
the frontend and the worker together.
|
|
144
|
+
|
|
145
|
+
Non-interactive: `canopy create "My App" --template spa`. **When unsure between
|
|
146
|
+
`static` and `spa`, pick `spa`.**
|
|
147
|
+
|
|
148
|
+
## Safety rules
|
|
149
|
+
|
|
150
|
+
- **Destructive commands** — `canopy db restore` and `canopy delete` wipe data.
|
|
151
|
+
Confirm intent with the user first, and take a `canopy db snapshot` before
|
|
152
|
+
restores.
|
|
153
|
+
- **Deploys are public.** `canopy deploy` makes a version live. Confirm before
|
|
154
|
+
deploying unless the user clearly asked you to.
|
|
155
|
+
- Keep the CLI current: `canopy upgrade` (this also refreshes this skill).
|
|
156
|
+
- **A `canopy` command itself failing?** First rule out user error (re-read
|
|
157
|
+
`canopy docs troubleshooting` and the command's `--help`). If the platform
|
|
158
|
+
really misbehaved, check `canopy feedback mine` for a duplicate, then file it:
|
|
159
|
+
`canopy feedback report "<what happened>" --command "<the command>" --error "<output>"`
|
|
160
|
+
— it reaches the platform team directly (no `--project` needed).
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { t as version } from "./package-
|
|
2
|
-
import { n as reportErrors } from "./ui-
|
|
1
|
+
import { t as version } from "./package-CuyFMXam.mjs";
|
|
2
|
+
import { n as reportErrors } from "./ui-B1Px5y7q.mjs";
|
|
3
3
|
import { defineCommand } from "citty";
|
|
4
4
|
import { cpSync, existsSync, mkdirSync, rmSync, symlinkSync, writeFileSync } from "node:fs";
|
|
5
5
|
import { join, relative } from "node:path";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { c as resolveProject, l as stagingUrl, o as projectFlagArgs, s as projectUrl } from "./site-BUlQR117.mjs";
|
|
2
|
-
import { c as apiRequest, i as unwrap, n as reportErrors, r as requireTty, u as withContext } from "./ui-
|
|
2
|
+
import { c as apiRequest, i as unwrap, n as reportErrors, r as requireTty, u as withContext } from "./ui-B1Px5y7q.mjs";
|
|
3
3
|
import { defineCommand } from "citty";
|
|
4
4
|
import * as p from "@clack/prompts";
|
|
5
5
|
//#region src/staging.ts
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Deploy to Canopy on every push to main. GitHub's runners are the build
|
|
2
|
+
# farm — the platform owns no build infrastructure.
|
|
3
|
+
#
|
|
4
|
+
# Needs a CANOPY_DEPLOY_TOKEN repo secret: create a deploy token in the
|
|
5
|
+
# dashboard (project → Settings → Deploy tokens) or with `canopy token
|
|
6
|
+
# create`, then add it via `gh secret set CANOPY_DEPLOY_TOKEN` or the repo's
|
|
7
|
+
# Settings → Secrets. Until the secret is set, this workflow skips itself
|
|
8
|
+
# instead of failing the build.
|
|
9
|
+
name: Deploy to Canopy
|
|
10
|
+
|
|
11
|
+
on:
|
|
12
|
+
push:
|
|
13
|
+
branches: [main]
|
|
14
|
+
|
|
15
|
+
jobs:
|
|
16
|
+
deploy:
|
|
17
|
+
runs-on: ubuntu-latest
|
|
18
|
+
env:
|
|
19
|
+
CANOPY_DEPLOY_TOKEN: ${{ secrets.CANOPY_DEPLOY_TOKEN }}
|
|
20
|
+
steps:
|
|
21
|
+
- uses: actions/checkout@v4
|
|
22
|
+
- uses: actions/setup-node@v4
|
|
23
|
+
with:
|
|
24
|
+
node-version: 22
|
|
25
|
+
- name: Install dependencies
|
|
26
|
+
if: env.CANOPY_DEPLOY_TOKEN != ''
|
|
27
|
+
run: |
|
|
28
|
+
if [ -f package-lock.json ]; then npm ci; else npm install; fi
|
|
29
|
+
- name: Deploy
|
|
30
|
+
if: env.CANOPY_DEPLOY_TOKEN != ''
|
|
31
|
+
run: npm exec --yes @mediamonks/canopy-cli deploy
|
|
32
|
+
- name: Skipped (no deploy token)
|
|
33
|
+
if: env.CANOPY_DEPLOY_TOKEN == ''
|
|
34
|
+
run: echo "CANOPY_DEPLOY_TOKEN secret not set — skipping deploy."
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# {{name}} — a Canopy React SPA
|
|
2
|
+
|
|
3
|
+
A Vite + React single-page app on [Canopy](https://dashboard.thanarie.dev),
|
|
4
|
+
Monks' managed platform for internal projects. Everything is served behind
|
|
5
|
+
company login; there is no infrastructure to manage and none should be added.
|
|
6
|
+
|
|
7
|
+
## Canopy skill
|
|
8
|
+
|
|
9
|
+
The full SDK reference (auth, db, blob, ai) and platform guardrails ship as an
|
|
10
|
+
agent skill, kept out of this scaffold so it stays current. Install it before
|
|
11
|
+
building features:
|
|
12
|
+
|
|
13
|
+
```
|
|
14
|
+
npx skills add mediamonks/canopy
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
If that fails the skill isn't published yet — the SDK surface is documented on
|
|
18
|
+
the [Canopy dashboard](https://dashboard.thanarie.dev).
|
|
19
|
+
|
|
20
|
+
## Working on this project
|
|
21
|
+
|
|
22
|
+
- A standard Vite + React + TypeScript app; `npm install` once, then develop
|
|
23
|
+
with `canopy dev` (it runs Vite with this project's `vite.config.ts` and
|
|
24
|
+
proxies `/_api` to the platform).
|
|
25
|
+
- The SDK comes from npm (`@mediamonks/canopy`) — the shared instance lives in
|
|
26
|
+
`src/canopy.ts`.
|
|
27
|
+
- `canopy dev` uses LIVE project data: read freely, but take a
|
|
28
|
+
`canopy db snapshot` before risky data changes.
|
|
29
|
+
- `canopy dev --local [--pull]` — offline sandbox with local data instead;
|
|
30
|
+
writes never reach the platform. Prefer it for destructive experiments.
|
|
31
|
+
- `canopy deploy` — builds (`npm run build`) and publishes `dist/`; live
|
|
32
|
+
(behind company login) at https://{{slug}}.thanarie.dev.
|
|
33
|
+
- Risky change (schema migration, backend rework)? `canopy deploy --staging`
|
|
34
|
+
first — a private copy with its own database cloned from live — verify, then
|
|
35
|
+
`canopy promote` ships the exact tested version. Never promote with failing
|
|
36
|
+
checks.
|
|
37
|
+
|
|
38
|
+
## Boundaries
|
|
39
|
+
|
|
40
|
+
- Do not add auth — the platform authenticates every request before it
|
|
41
|
+
reaches this app.
|
|
42
|
+
- Do not store secrets anywhere in this project, and never call external APIs
|
|
43
|
+
that need them from the browser — all backend needs go through the Canopy
|
|
44
|
+
SDK (`canopy.auth`, `canopy.db`, `canopy.blob`, `canopy.ai`).
|
|
45
|
+
- Frontend only: no SSR, no custom servers, no databases outside `canopy.db`.
|
|
46
|
+
(Need backend code? Recreate on the `worker` template, or ask the Canopy
|
|
47
|
+
team.)
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# {{name}}
|
|
2
|
+
|
|
3
|
+
A [Canopy](https://dashboard.thanarie.dev) project — Vite + React. Live
|
|
4
|
+
(behind company login) at https://{{slug}}.thanarie.dev after the first
|
|
5
|
+
`canopy deploy`.
|
|
6
|
+
|
|
7
|
+
- `npm install` — once, then:
|
|
8
|
+
- `canopy dev` — develop locally against the live platform (real data!)
|
|
9
|
+
- `canopy dev --local [--pull]` — offline sandbox with local data instead
|
|
10
|
+
- `canopy deploy` — build and publish `dist/`
|
|
11
|
+
- `AGENTS.md` — project guardrails and skill setup for coding agents
|
|
12
|
+
|
|
13
|
+
Push-to-main deploys are prepared in `.github/workflows/canopy-deploy.yml`
|
|
14
|
+
(needs a `CANOPY_DEPLOY_TOKEN` repo secret — see the workflow file).
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<title>{{name}}</title>
|
|
7
|
+
</head>
|
|
8
|
+
<body>
|
|
9
|
+
<div id="root"></div>
|
|
10
|
+
<script type="module" src="/src/main.tsx"></script>
|
|
11
|
+
</body>
|
|
12
|
+
</html>
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "{{slug}}",
|
|
3
|
+
"private": true,
|
|
4
|
+
"type": "module",
|
|
5
|
+
"scripts": {
|
|
6
|
+
"dev": "canopy dev",
|
|
7
|
+
"build": "tsc && vite build"
|
|
8
|
+
},
|
|
9
|
+
"dependencies": {
|
|
10
|
+
"@mediamonks/canopy": "^0.0.4",
|
|
11
|
+
"react": "^19.1.0",
|
|
12
|
+
"react-dom": "^19.1.0"
|
|
13
|
+
},
|
|
14
|
+
"devDependencies": {
|
|
15
|
+
"@types/react": "^19.1.0",
|
|
16
|
+
"@types/react-dom": "^19.1.0",
|
|
17
|
+
"@vitejs/plugin-react": "^5.0.0",
|
|
18
|
+
"typescript": "^5.8.0",
|
|
19
|
+
"vite": "^7.0.0"
|
|
20
|
+
}
|
|
21
|
+
}
|