@mediamonks/canopy-cli 0.7.12 → 0.7.13

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.
Files changed (119) hide show
  1. package/dist/{db-8Etjmzxh.mjs → db-DKnJQPm5.mjs} +2 -2
  2. package/dist/{deploy-B1WrPq5W.mjs → deploy-CnxJ4TE6.mjs} +2 -2
  3. package/dist/dev-BqLvttKc.mjs +2 -0
  4. package/dist/{dev-BnUYGQQm.mjs → dev-CyJT6EfQ.mjs} +4 -4
  5. package/dist/docs/ci.md +50 -0
  6. package/dist/docs/cli-create.md +46 -0
  7. package/dist/docs/cli-db.md +86 -0
  8. package/dist/docs/cli-delete.md +44 -0
  9. package/dist/docs/cli-deploy.md +58 -0
  10. package/dist/docs/cli-dev.md +82 -0
  11. package/dist/docs/cli-docs.md +47 -0
  12. package/dist/docs/cli-download.md +42 -0
  13. package/dist/docs/cli-egress.md +37 -0
  14. package/dist/docs/cli-feedback.md +75 -0
  15. package/dist/docs/cli-index.md +61 -0
  16. package/dist/docs/cli-init.md +46 -0
  17. package/dist/docs/cli-login.md +43 -0
  18. package/dist/docs/cli-logout.md +32 -0
  19. package/dist/docs/cli-logs.md +57 -0
  20. package/dist/docs/cli-open.md +41 -0
  21. package/dist/docs/cli-projects.md +43 -0
  22. package/dist/docs/cli-promote.md +44 -0
  23. package/dist/docs/cli-rollback.md +45 -0
  24. package/dist/docs/cli-skill.md +39 -0
  25. package/dist/docs/cli-staging.md +54 -0
  26. package/dist/docs/cli-token.md +47 -0
  27. package/dist/docs/cli-upgrade.md +43 -0
  28. package/dist/docs/cli-usage.md +43 -0
  29. package/dist/docs/cli-whoami.md +46 -0
  30. package/dist/docs/cli.md +86 -0
  31. package/dist/docs/data.md +85 -0
  32. package/dist/docs/deploy.md +81 -0
  33. package/dist/docs/dev.md +72 -0
  34. package/dist/docs/developers-agents-and-ai.md +95 -0
  35. package/dist/docs/developers-architecture.md +106 -0
  36. package/dist/docs/getting-started.md +108 -0
  37. package/dist/docs/index.json +336 -0
  38. package/dist/docs/infrastructure.md +406 -0
  39. package/dist/docs/migrations.md +183 -0
  40. package/dist/docs/overview-faq.md +105 -0
  41. package/dist/docs/overview-how-it-works.md +82 -0
  42. package/dist/docs/overview-use-cases.md +76 -0
  43. package/dist/docs/overview-what-is-canopy.md +75 -0
  44. package/dist/docs/platform-ai-usage.md +65 -0
  45. package/dist/docs/platform-client-access.md +54 -0
  46. package/dist/docs/platform-deploys-and-rollbacks.md +93 -0
  47. package/dist/docs/platform-egress.md +75 -0
  48. package/dist/docs/platform-feedback.md +114 -0
  49. package/dist/docs/platform-google.md +213 -0
  50. package/dist/docs/platform-members-and-access.md +71 -0
  51. package/dist/docs/platform-project-data.md +76 -0
  52. package/dist/docs/platform-schedules.md +90 -0
  53. package/dist/docs/platform-sites-and-profiles.md +61 -0
  54. package/dist/docs/porting-nextjs.md +233 -0
  55. package/dist/docs/sdk-ai.md +103 -0
  56. package/dist/docs/sdk-auth.md +57 -0
  57. package/dist/docs/sdk-database.md +126 -0
  58. package/dist/docs/sdk-files.md +82 -0
  59. package/dist/docs/sdk-services.md +58 -0
  60. package/dist/docs/sdk.md +126 -0
  61. package/dist/docs/staging.md +106 -0
  62. package/dist/docs/static-serving.md +323 -0
  63. package/dist/docs/troubleshooting.md +102 -0
  64. package/dist/{docs-DxB7NhZs.mjs → docs-BBMVxVta.mjs} +2 -2
  65. package/dist/{egress-CN_axsAN.mjs → egress-BFSwa8Uu.mjs} +1 -1
  66. package/dist/{feedback-B-GXUBPs.mjs → feedback-BMX_d4KZ.mjs} +2 -2
  67. package/dist/index.mjs +25 -24
  68. package/dist/{local-BGRd_FMZ.mjs → local-BQxsp-ZQ.mjs} +2 -2
  69. package/dist/{login-D7dI8D9N.mjs → login-BDTO3nVw.mjs} +1 -1
  70. package/dist/{logs-DNiZquki.mjs → logs-C53y2eAS.mjs} +1 -1
  71. package/dist/{migrations-C-5x6SYf.mjs → migrations-C6EcKD6a.mjs} +1 -1
  72. package/dist/{open-DRPp4QJg.mjs → open-C5x3tX6I.mjs} +1 -1
  73. package/dist/{package-CDLufecy.mjs → package-vJXq4RNN.mjs} +1 -1
  74. package/dist/{projects-C6JQjVqg.mjs → projects-MMbv7MmK.mjs} +1 -1
  75. package/dist/schedule-Cq9K0rnE.mjs +106 -0
  76. package/dist/skill/canopy/SKILL.md +160 -0
  77. package/dist/{skill-BOWfqwr2.mjs → skill-Zq9aIPKf.mjs} +2 -2
  78. package/dist/{staging-Bw1D_J8M.mjs → staging-D8G4amjO.mjs} +1 -1
  79. package/dist/templates/spa/.github/workflows/canopy-deploy.yml +34 -0
  80. package/dist/templates/spa/AGENTS.md +47 -0
  81. package/dist/templates/spa/README.md +14 -0
  82. package/dist/templates/spa/_gitignore +4 -0
  83. package/dist/templates/spa/canopy.json +5 -0
  84. package/dist/templates/spa/index.html +12 -0
  85. package/dist/templates/spa/package.json +21 -0
  86. package/dist/templates/spa/src/app.tsx +25 -0
  87. package/dist/templates/spa/src/canopy.ts +5 -0
  88. package/dist/templates/spa/src/main.tsx +10 -0
  89. package/dist/templates/spa/src/style.css +29 -0
  90. package/dist/templates/spa/tsconfig.json +16 -0
  91. package/dist/templates/spa/vite.config.ts +7 -0
  92. package/dist/templates/static/AGENTS.md +45 -0
  93. package/dist/templates/static/README.md +9 -0
  94. package/dist/templates/static/_gitignore +2 -0
  95. package/dist/templates/static/canopy.json +4 -0
  96. package/dist/templates/static/index.html +21 -0
  97. package/dist/templates/static/main.js +9 -0
  98. package/dist/templates/static/style.css +29 -0
  99. package/dist/templates/worker/.dev.vars.example +9 -0
  100. package/dist/templates/worker/.github/workflows/canopy-deploy.yml +34 -0
  101. package/dist/templates/worker/AGENTS.md +63 -0
  102. package/dist/templates/worker/README.md +17 -0
  103. package/dist/templates/worker/_gitignore +5 -0
  104. package/dist/templates/worker/canopy.json +6 -0
  105. package/dist/templates/worker/index.html +12 -0
  106. package/dist/templates/worker/package.json +22 -0
  107. package/dist/templates/worker/src/app.tsx +25 -0
  108. package/dist/templates/worker/src/canopy.ts +5 -0
  109. package/dist/templates/worker/src/main.tsx +10 -0
  110. package/dist/templates/worker/src/style.css +29 -0
  111. package/dist/templates/worker/tsconfig.json +16 -0
  112. package/dist/templates/worker/vite.config.ts +7 -0
  113. package/dist/templates/worker/worker/index.ts +52 -0
  114. package/dist/{token-D7zrIF8G.mjs → token-BUxgRJVA.mjs} +1 -1
  115. package/dist/{ui-2mrJcjYC.mjs → ui-B1Px5y7q.mjs} +17 -0
  116. package/dist/{upgrade-fhfrid_O.mjs → upgrade-ES8u8_Pn.mjs} +2 -2
  117. package/dist/{usage-CX1Izj_l.mjs → usage-DjNngyvy.mjs} +1 -1
  118. package/package.json +1 -1
  119. 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-2mrJcjYC.mjs";
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-CDLufecy.mjs";
1
+ import { t as version } from "./package-vJXq4RNN.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-2mrJcjYC.mjs";
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";
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { t as version } from "./package-CDLufecy.mjs";
2
+ import { t as version } from "./package-vJXq4RNN.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-D7dI8D9N.mjs").then((m) => m.loginCommand),
64
- whoami: () => import("./login-D7dI8D9N.mjs").then((m) => m.whoamiCommand),
65
- logout: () => import("./login-D7dI8D9N.mjs").then((m) => m.logoutCommand),
66
- create: () => import("./projects-C6JQjVqg.mjs").then((m) => m.createCommand),
67
- init: () => import("./projects-C6JQjVqg.mjs").then((m) => m.initCommand),
68
- projects: () => import("./projects-C6JQjVqg.mjs").then((m) => m.projectsCommand),
69
- delete: () => import("./projects-C6JQjVqg.mjs").then((m) => m.deleteCommand),
70
- dev: () => import("./dev-CySBxW7I.mjs").then((m) => m.devCommand),
71
- deploy: () => import("./deploy-B1WrPq5W.mjs").then((m) => m.deployCommand),
72
- promote: () => import("./deploy-B1WrPq5W.mjs").then((m) => m.promoteCommand),
73
- staging: () => import("./staging-Bw1D_J8M.mjs").then((m) => m.stagingCommand),
74
- rollback: () => import("./deploy-B1WrPq5W.mjs").then((m) => m.rollbackCommand),
75
- download: () => import("./deploy-B1WrPq5W.mjs").then((m) => m.downloadCommand),
76
- token: () => import("./token-D7zrIF8G.mjs").then((m) => m.tokenCommand),
77
- egress: () => import("./egress-CN_axsAN.mjs").then((m) => m.egressCommand),
78
- feedback: () => import("./feedback-B-GXUBPs.mjs").then((m) => m.feedbackCommand),
79
- db: () => import("./db-8Etjmzxh.mjs").then((m) => m.dbCommand),
80
- usage: () => import("./usage-CX1Izj_l.mjs").then((m) => m.usageCommand),
81
- open: () => import("./open-DRPp4QJg.mjs").then((m) => m.openCommand),
82
- docs: () => import("./docs-DxB7NhZs.mjs").then((m) => m.docsCommand),
83
- skill: () => import("./skill-BOWfqwr2.mjs").then((m) => m.skillCommand),
84
- upgrade: () => import("./upgrade-fhfrid_O.mjs").then((m) => m.upgradeCommand),
85
- logs: () => import("./logs-DNiZquki.mjs").then((m) => m.logsCommand)
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-BMX_d4KZ.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-BBMVxVta.mjs").then((m) => m.docsCommand),
84
+ skill: () => import("./skill-Zq9aIPKf.mjs").then((m) => m.skillCommand),
85
+ upgrade: () => import("./upgrade-ES8u8_Pn.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-2mrJcjYC.mjs";
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-BnUYGQQm.mjs";
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-2mrJcjYC.mjs";
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-2mrJcjYC.mjs";
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-2mrJcjYC.mjs";
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-2mrJcjYC.mjs";
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,4 +1,4 @@
1
1
  //#region package.json
2
- var version = "0.7.12";
2
+ var version = "0.7.13";
3
3
  //#endregion
4
4
  export { version as t };
@@ -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-2mrJcjYC.mjs";
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-CDLufecy.mjs";
2
- import { n as reportErrors } from "./ui-2mrJcjYC.mjs";
1
+ import { t as version } from "./package-vJXq4RNN.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-2mrJcjYC.mjs";
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,4 @@
1
+ node_modules/
2
+ dist/
3
+ .canopy/
4
+ .DS_Store
@@ -0,0 +1,5 @@
1
+ {
2
+ "slug": "{{slug}}",
3
+ "build": "npm run build",
4
+ "dist": "dist"
5
+ }
@@ -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
+ }
@@ -0,0 +1,25 @@
1
+ import { useEffect, useState } from "react";
2
+ import { canopy } from "./canopy.ts";
3
+
4
+ export function App() {
5
+ const [status, setStatus] = useState("connecting to Canopy…");
6
+
7
+ useEffect(() => {
8
+ canopy.auth
9
+ .user()
10
+ .then((user) => setStatus(`signed in as ${user.email}`))
11
+ .catch(() => setStatus("could not reach Canopy — is `canopy dev` running?"));
12
+ }, []);
13
+
14
+ return (
15
+ <main>
16
+ <h1>{"{{name}}"}</h1>
17
+ <p className="status">{status}</p>
18
+ <section>
19
+ <p className="placeholder">
20
+ Start building — edit <code>src/app.tsx</code>, then run <code>canopy deploy</code>.
21
+ </p>
22
+ </section>
23
+ </main>
24
+ );
25
+ }
@@ -0,0 +1,5 @@
1
+ // One SDK instance for the whole app. Auth (auth), data (db), files (blob)
2
+ // and AI (ai) are all live — see AGENTS.md for how to get the full reference.
3
+ import { createCanopy } from "@mediamonks/canopy";
4
+
5
+ export const canopy = createCanopy();
@@ -0,0 +1,10 @@
1
+ import { StrictMode } from "react";
2
+ import { createRoot } from "react-dom/client";
3
+ import { App } from "./app.tsx";
4
+ import "./style.css";
5
+
6
+ createRoot(document.getElementById("root") as HTMLElement).render(
7
+ <StrictMode>
8
+ <App />
9
+ </StrictMode>,
10
+ );
@@ -0,0 +1,29 @@
1
+ :root {
2
+ font-family: system-ui, sans-serif;
3
+ color-scheme: light dark;
4
+ }
5
+
6
+ body {
7
+ margin: 0;
8
+ display: grid;
9
+ place-items: center;
10
+ min-height: 100vh;
11
+ }
12
+
13
+ main {
14
+ max-width: 40rem;
15
+ padding: 2rem;
16
+ text-align: center;
17
+ }
18
+
19
+ .status {
20
+ font-size: 0.9rem;
21
+ opacity: 0.7;
22
+ }
23
+
24
+ .placeholder {
25
+ border: 1px dashed color-mix(in srgb, currentColor 30%, transparent);
26
+ border-radius: 8px;
27
+ padding: 1.5rem;
28
+ opacity: 0.6;
29
+ }