@mediamonks/canopy-cli 0.7.28 → 0.7.30
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/ai-fAvjdjVX.mjs +54 -0
- package/dist/{db-DVMwCLlU.mjs → db-KTLn8J1u.mjs} +2 -2
- package/dist/{deploy-CTMcwHhI.mjs → deploy-CBf44bvG.mjs} +2 -2
- package/dist/dev-DGCu9MjB.mjs +2 -0
- package/dist/{dev-BvjbJcse.mjs → dev-JlzMZJow.mjs} +50 -21
- package/dist/{dev-worker-BerdPJ_U.mjs → dev-worker-DjJGSUGj.mjs} +1 -1
- package/dist/docs/cli-ai.md +43 -0
- package/dist/docs/cli-db.md +2 -0
- package/dist/docs/cli.md +2 -0
- package/dist/docs/index.json +17 -2
- package/dist/docs/platform-ai-usage.md +6 -4
- package/dist/docs/platform-members-and-access.md +13 -4
- package/dist/docs/platform-slack.md +6 -0
- package/dist/docs/sdk-ai.md +41 -10
- package/dist/docs/sdk-notify.md +140 -0
- package/dist/docs/sdk-realtime.md +149 -0
- package/dist/docs/sdk-slack.md +3 -0
- package/dist/docs/sdk.md +5 -4
- package/dist/docs/static-serving.md +19 -0
- package/dist/{docs-DMFMPnxG.mjs → docs-Dg0UJugG.mjs} +2 -2
- package/dist/{egress-Cn-P46O3.mjs → egress-Cn9zDfDT.mjs} +1 -1
- package/dist/{feedback-OMAkwHpR.mjs → feedback-Dvb6CdlP.mjs} +2 -2
- package/dist/index.mjs +28 -26
- package/dist/{local-laAZEDVJ.mjs → local-BA_TPWpN.mjs} +56 -4
- package/dist/{login-C4MDP5NM.mjs → login-MX0xq8UE.mjs} +1 -1
- package/dist/{logs-CfDINw7U.mjs → logs-D5RWqsWl.mjs} +1 -1
- package/dist/{migrations-B9fXxMDj.mjs → migrations-C8yRPJIX.mjs} +1 -1
- package/dist/{open-rS1ARzOy.mjs → open-BJgd0R61.mjs} +1 -1
- package/dist/{package-BL6hRqtr.mjs → package-CnS450Yc.mjs} +1 -1
- package/dist/{projects-Dmm_976k.mjs → projects-BG7Injpb.mjs} +1 -1
- package/dist/{schedule-Buu71RWQ.mjs → schedule-CLuksxSM.mjs} +1 -1
- package/dist/{secrets-BD3DaTih.mjs → secrets-Bl3uOx4l.mjs} +1 -1
- package/dist/skill/canopy/SKILL.md +3 -0
- package/dist/{skill-C83Za5BU.mjs → skill-CuES7kPj.mjs} +2 -2
- package/dist/{src-CwSKZwNK.mjs → src-C7zPhbxi.mjs} +238 -1
- package/dist/{staging-CKEoDnac.mjs → staging-D06zobnf.mjs} +1 -1
- package/dist/{token-Dp2CoIYj.mjs → token-C_PFNqcv.mjs} +1 -1
- package/dist/{ui-DR-siu2S.mjs → ui-CeupNHNa.mjs} +4 -0
- package/dist/{upgrade-BjQguwG6.mjs → upgrade-C8YdSKKd.mjs} +2 -2
- package/dist/{usage-81JSiWIx.mjs → usage-CV7TjTO8.mjs} +1 -1
- package/package.json +2 -2
- package/dist/dev-BOvMCaML.mjs +0 -2
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Realtime
|
|
3
|
+
description: Live updates between the people using your app — subscribe to a topic, publish to it, no backend needed.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
`canopy.realtime` sends small messages to everyone who has your app open,
|
|
7
|
+
the moment something happens. A teammate adds a card and everyone's board
|
|
8
|
+
refreshes; someone starts editing and the others see a "being edited" badge.
|
|
9
|
+
It works from the browser alone, with nothing to set up.
|
|
10
|
+
|
|
11
|
+
## Messages are hints, not a log
|
|
12
|
+
|
|
13
|
+
Nothing is stored. A message reaches the pages that are connected when it's
|
|
14
|
+
sent, and nobody else. A laptop that went to sleep, a tab that was in the
|
|
15
|
+
background, a page that reconnected: each of them missed whatever was sent
|
|
16
|
+
meanwhile.
|
|
17
|
+
|
|
18
|
+
So build it like this: keep your data in [`canopy.db`](https://docs.canopy.website/sdk/database),
|
|
19
|
+
send a message to say _what changed_, and reload from the database whenever
|
|
20
|
+
the connection opens.
|
|
21
|
+
|
|
22
|
+
```ts
|
|
23
|
+
const off = canopy.realtime.subscribe(
|
|
24
|
+
"board",
|
|
25
|
+
(message) => refreshCard(message.data.cardId),
|
|
26
|
+
{ onOpen: () => reloadBoard() }, // first connect and every reconnect
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
// …somewhere else, after saving the change:
|
|
30
|
+
await canopy.db.collection("cards").update(card.id, { column: "done" });
|
|
31
|
+
await canopy.realtime.publish("board", { cardId: card.id });
|
|
32
|
+
|
|
33
|
+
off(); // stop listening
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## realtime.subscribe(topic, onMessage, options?)
|
|
37
|
+
|
|
38
|
+
Starts listening to `topic` and returns a function that stops. Every
|
|
39
|
+
subscription on a page shares one connection, so subscribe to as many topics
|
|
40
|
+
as you need (up to 20 per page).
|
|
41
|
+
|
|
42
|
+
- `onMessage(message)` runs for each message on the topic, including the ones
|
|
43
|
+
this page published itself. Ignore your own by comparing `message.id` with
|
|
44
|
+
what `publish` returned.
|
|
45
|
+
- `onOpen()` runs whenever the connection opens: the first time, and after
|
|
46
|
+
every reconnect. Reload what you show from the database here.
|
|
47
|
+
- `onError(error)` reports connection problems. `error.code` says which. The
|
|
48
|
+
SDK keeps retrying on its own unless retrying can't help (see
|
|
49
|
+
[When it's off](#when-its-off)).
|
|
50
|
+
|
|
51
|
+
A message looks like this:
|
|
52
|
+
|
|
53
|
+
```ts
|
|
54
|
+
{
|
|
55
|
+
id: "0muz…", // the id publish() returned to the sender
|
|
56
|
+
topic: "board",
|
|
57
|
+
data: { cardId: "c-7" }, // whatever was published
|
|
58
|
+
from: { kind: "monk", email: "ada@monks.com" },
|
|
59
|
+
ts: "2026-10-08T12:00:00.000Z",
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
`from` is filled in by Canopy, never by the sender, so you can trust it:
|
|
64
|
+
`monk` (with their sign-in email), `external` for a visitor who came in with
|
|
65
|
+
the [client password](https://docs.canopy.website/platform/client-access), or `worker` for your
|
|
66
|
+
project's backend. Client visitors never see a monk's email; for them the
|
|
67
|
+
sender is just `monk`.
|
|
68
|
+
|
|
69
|
+
## realtime.publish(topic, data)
|
|
70
|
+
|
|
71
|
+
Sends `data` (any JSON value, up to 4 KB) to everyone subscribed to `topic`,
|
|
72
|
+
and resolves with `{ id }`. Send an id and let pages load the rest from the
|
|
73
|
+
database; a message over 4 KB is refused with `message_too_large`.
|
|
74
|
+
|
|
75
|
+
Topic names are 1 to 64 letters, digits, `-`, `_` or `.`. They belong to your
|
|
76
|
+
project only: another project's `board` is a different topic, and so is your
|
|
77
|
+
own staging site's.
|
|
78
|
+
|
|
79
|
+
## Topics only your backend can publish to
|
|
80
|
+
|
|
81
|
+
Topics that start with `worker:` take messages from your project's backend
|
|
82
|
+
only. Pages can listen to them, but anything else that tries to publish gets
|
|
83
|
+
`topic_worker_only`: a page, the dashboard, or a personal token like the one
|
|
84
|
+
`canopy dev` uses. Use them for news that has to come from the server: a
|
|
85
|
+
job finished, a payment cleared, an import is done.
|
|
86
|
+
|
|
87
|
+
```ts
|
|
88
|
+
// In your worker (worker/index.ts):
|
|
89
|
+
const canopy = createCanopy({ fetch: c.env.CANOPY.fetch.bind(c.env.CANOPY) });
|
|
90
|
+
await canopy.realtime.publish("worker:imports", { importId, status: "done" });
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Messages from the backend arrive with `from.kind: "worker"`, also when the
|
|
94
|
+
backend is handling a signed-in visitor's request. A scheduled job can
|
|
95
|
+
publish too.
|
|
96
|
+
|
|
97
|
+
## Limits
|
|
98
|
+
|
|
99
|
+
- **4 KB** of data per message.
|
|
100
|
+
- **20 topics** per page.
|
|
101
|
+
- **100 open connections** per project at a time, and **10 per person**. One
|
|
102
|
+
more gets `too_many_subscribers` through `onError` and keeps retrying until
|
|
103
|
+
a connection closes.
|
|
104
|
+
- **A daily quota** per project: 100,000 messages by default, and a share of
|
|
105
|
+
data delivered (big messages to many people use it up faster). When either
|
|
106
|
+
is used up, `publish` is refused with `realtime_quota_exceeded` until
|
|
107
|
+
midnight UTC; subscriptions stay open. The Usage tab shows today's count. A
|
|
108
|
+
Canopy admin can raise the message quota if you need more.
|
|
109
|
+
- If many projects are busy at once, the whole site can reach its own daily
|
|
110
|
+
limit. Publishes then fail with `realtime_platform_limit`; that one isn't
|
|
111
|
+
about your project.
|
|
112
|
+
|
|
113
|
+
For cursors or presence, throttle updates to at most a few per second per
|
|
114
|
+
person. That's smooth to look at and keeps you well inside the quota.
|
|
115
|
+
|
|
116
|
+
## Background tabs
|
|
117
|
+
|
|
118
|
+
A tab that has been in the background for two minutes stops receiving
|
|
119
|
+
messages and lets its connection go. When you switch back, it reconnects
|
|
120
|
+
right away and `onOpen` runs, so the page catches up from the database. A
|
|
121
|
+
quick switch away and back keeps the connection.
|
|
122
|
+
|
|
123
|
+
Every connection also renews itself about every quarter of an hour, and
|
|
124
|
+
whenever access to the project changes (the client password, collaborators
|
|
125
|
+
only, someone removed). Each renewal runs `onOpen` again, which is what
|
|
126
|
+
keeps the reload-on-open pattern correct.
|
|
127
|
+
|
|
128
|
+
## When it's off
|
|
129
|
+
|
|
130
|
+
Realtime is a per-project setting that Canopy admins manage. If it's off for
|
|
131
|
+
your project, `subscribe` reports `realtime_not_enabled` through `onError`
|
|
132
|
+
once and `publish` is refused with the same code. Ask a Canopy admin to turn
|
|
133
|
+
it on. The Usage tab shows whether it's on.
|
|
134
|
+
|
|
135
|
+
Some Canopy sites don't run realtime at all; there the error is
|
|
136
|
+
`not_supported_on_target`. In both cases the SDK doesn't keep retrying.
|
|
137
|
+
|
|
138
|
+
## Topics are not secrets
|
|
139
|
+
|
|
140
|
+
Anyone who can open your app can subscribe to any of its topics. Don't put
|
|
141
|
+
anything in a message that the people using the app shouldn't see, and don't
|
|
142
|
+
treat a hard-to-guess topic name as a password.
|
|
143
|
+
|
|
144
|
+
## In local development
|
|
145
|
+
|
|
146
|
+
`canopy dev` connects to the real topics of your live project, so a local tab
|
|
147
|
+
and the deployed site see each other's messages. `canopy dev --local` keeps
|
|
148
|
+
everything on your laptop: open two tabs and they talk to each other, with no
|
|
149
|
+
quota and no `worker:` rule (like the local database, nothing is guarded).
|
package/dist/docs/sdk-slack.md
CHANGED
|
@@ -3,6 +3,9 @@ title: Slack
|
|
|
3
3
|
description: Handle your bot's events and slash commands in your worker, and post to Slack as the bot — no tokens in project code.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
+
> Only want to post a message? [`canopy.notify.slack`](https://docs.canopy.website/sdk/notify) does it
|
|
7
|
+
> through the Canopy bot, no app needed. This page is for a bot of your own.
|
|
8
|
+
|
|
6
9
|
Once your project has a Slack app ([set up on the Services
|
|
7
10
|
tab](https://docs.canopy.website/platform/slack)), the integration is two halves: inbound events arrive
|
|
8
11
|
at a `slack()` export in your worker, and outbound calls go through
|
package/dist/docs/sdk.md
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: Using the SDK
|
|
3
|
-
description: One typed client for auth, data, files, AI, email, and services — no keys, no connection strings, the platform authenticates for you.
|
|
3
|
+
description: One typed client for auth, data, files, AI, email, notify, realtime, and services — no keys, no connection strings, the platform authenticates for you.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
`@mediamonks/canopy` is a thin, typed client over the platform's project API.
|
|
7
|
-
One `createCanopy()` call gives you
|
|
8
|
-
`email`, `google`, and `services` — and that is the entire surface. There are no API keys,
|
|
7
|
+
One `createCanopy()` call gives you its modules — `auth`, `db`, `blob`, `ai`,
|
|
8
|
+
`email`, `notify`, `realtime`, `google`, `slack`, and `services` — and that is the entire surface. There are no API keys,
|
|
9
9
|
connection strings, or environment variables to manage: every call rides the
|
|
10
10
|
session the platform already established before your code ran.
|
|
11
11
|
|
|
@@ -35,7 +35,8 @@ import { createCanopy } from "/_api/data/v1/sdk.js";
|
|
|
35
35
|
import { createCanopy } from "@mediamonks/canopy";
|
|
36
36
|
|
|
37
37
|
const canopy = createCanopy();
|
|
38
|
-
// canopy.auth, canopy.db, canopy.blob, canopy.ai, canopy.email, canopy.
|
|
38
|
+
// canopy.auth, canopy.db, canopy.blob, canopy.ai, canopy.email, canopy.notify,
|
|
39
|
+
// canopy.realtime, canopy.google, canopy.slack, canopy.services
|
|
39
40
|
```
|
|
40
41
|
|
|
41
42
|
| Option | Type | Default | What it does |
|
|
@@ -77,5 +77,24 @@ belong in [file storage](https://docs.canopy.website/sdk/files) instead of the b
|
|
|
77
77
|
reachable, deploy it, or put it in [file storage](https://docs.canopy.website/sdk/files) and hand out a
|
|
78
78
|
signed link.
|
|
79
79
|
|
|
80
|
+
## Moving a project to a new address
|
|
81
|
+
|
|
82
|
+
When a project lives somewhere else now — it was rebuilt under another name,
|
|
83
|
+
or it graduated to its own domain — you can point the old address at the new
|
|
84
|
+
one. On the project's **Settings** page, the **Move project** card takes an
|
|
85
|
+
`https://` URL. From then on every request to the old address, including deep
|
|
86
|
+
links with their path and query, lands on the new URL.
|
|
87
|
+
|
|
88
|
+
A few things to know:
|
|
89
|
+
|
|
90
|
+
- **Visitors still sign in on the old address first.** The redirect happens
|
|
91
|
+
behind the same sign-in wall as everything else, so a colleague signs in once
|
|
92
|
+
and is sent on; a client-password visitor types the old password once.
|
|
93
|
+
- **The old project goes quiet.** Deploys are refused, the backend and
|
|
94
|
+
schedules stop. Its database and files stay put, and `canopy db pull` still
|
|
95
|
+
works, so you can take the data with you after the move.
|
|
96
|
+
- **It's reversible.** Undo the move on the same card and the last deployed
|
|
97
|
+
build is served again right away.
|
|
98
|
+
|
|
80
99
|
Porting an app that assumed a server? [Porting a Next.js app](https://docs.canopy.website/guides/porting-nextjs)
|
|
81
100
|
walks through the restructure.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { t as version } from "./package-
|
|
1
|
+
import { t as version } from "./package-CnS450Yc.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 { r as reportErrors } from "./ui-
|
|
4
|
+
import { r as reportErrors } from "./ui-CeupNHNa.mjs";
|
|
5
5
|
import { defineCommand } from "citty";
|
|
6
6
|
import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from "node:fs";
|
|
7
7
|
import { homedir } from "node:os";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { a as projectDirArgs, c as resolveProject } from "./site-DM2J10CB.mjs";
|
|
2
|
-
import { a as unwrap, d as withContext, i as requireTty, l as apiRequest, r as reportErrors, t as formatDateTime } from "./ui-
|
|
2
|
+
import { a as unwrap, d as withContext, i as requireTty, l as apiRequest, r as reportErrors, t as formatDateTime } from "./ui-CeupNHNa.mjs";
|
|
3
3
|
import { defineCommand } from "citty";
|
|
4
4
|
import * as p from "@clack/prompts";
|
|
5
5
|
//#region src/egress.ts
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { t as version } from "./package-
|
|
1
|
+
import { t as version } from "./package-CnS450Yc.mjs";
|
|
2
2
|
import { a as projectDirArgs, c as resolveProject, d as tryResolveProject, n as siteConfig, u as tryReadCanopyJson } from "./site-DM2J10CB.mjs";
|
|
3
|
-
import { c as apiDownload, l as apiRequest, r as reportErrors, t as formatDateTime, u as apiUpload } from "./ui-
|
|
3
|
+
import { c as apiDownload, l as apiRequest, r as reportErrors, t as formatDateTime, u as apiUpload } from "./ui-CeupNHNa.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-
|
|
2
|
+
import { t as version } from "./package-CnS450Yc.mjs";
|
|
3
3
|
import { r as siteHeaderLine, t as setInvocationProfile } from "./site-DM2J10CB.mjs";
|
|
4
4
|
import { a as notifyOfUpdate, o as runRefresh } from "./version-check-CcsA1QjI.mjs";
|
|
5
5
|
import { defineCommand, runMain } from "citty";
|
|
@@ -17,6 +17,7 @@ import { defineCommand, runMain } from "citty";
|
|
|
17
17
|
* db pull|snapshot|restore [phase 2]
|
|
18
18
|
* logs [--follow] read the project's worker logs [phase 2]
|
|
19
19
|
* usage AI spend + storage for a project [phase 1]
|
|
20
|
+
* ai models the site's AI models and auto/* aliases
|
|
20
21
|
* open open the deployed project / dashboard [phase 1]
|
|
21
22
|
*/
|
|
22
23
|
if (process.env["CANOPY_UPDATE_REFRESH"] === "1") {
|
|
@@ -60,31 +61,32 @@ const main = defineCommand({
|
|
|
60
61
|
description: "canopy — create and deploy vibecoded projects"
|
|
61
62
|
},
|
|
62
63
|
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
|
-
secrets: () => import("./secrets-
|
|
79
|
-
schedule: () => import("./schedule-
|
|
80
|
-
feedback: () => import("./feedback-
|
|
81
|
-
db: () => import("./db-
|
|
82
|
-
usage: () => import("./usage-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
64
|
+
login: () => import("./login-MX0xq8UE.mjs").then((m) => m.loginCommand),
|
|
65
|
+
whoami: () => import("./login-MX0xq8UE.mjs").then((m) => m.whoamiCommand),
|
|
66
|
+
logout: () => import("./login-MX0xq8UE.mjs").then((m) => m.logoutCommand),
|
|
67
|
+
create: () => import("./projects-BG7Injpb.mjs").then((m) => m.createCommand),
|
|
68
|
+
init: () => import("./projects-BG7Injpb.mjs").then((m) => m.initCommand),
|
|
69
|
+
projects: () => import("./projects-BG7Injpb.mjs").then((m) => m.projectsCommand),
|
|
70
|
+
delete: () => import("./projects-BG7Injpb.mjs").then((m) => m.deleteCommand),
|
|
71
|
+
dev: () => import("./dev-DGCu9MjB.mjs").then((m) => m.devCommand),
|
|
72
|
+
deploy: () => import("./deploy-CBf44bvG.mjs").then((m) => m.deployCommand),
|
|
73
|
+
promote: () => import("./deploy-CBf44bvG.mjs").then((m) => m.promoteCommand),
|
|
74
|
+
staging: () => import("./staging-D06zobnf.mjs").then((m) => m.stagingCommand),
|
|
75
|
+
rollback: () => import("./deploy-CBf44bvG.mjs").then((m) => m.rollbackCommand),
|
|
76
|
+
download: () => import("./deploy-CBf44bvG.mjs").then((m) => m.downloadCommand),
|
|
77
|
+
token: () => import("./token-C_PFNqcv.mjs").then((m) => m.tokenCommand),
|
|
78
|
+
egress: () => import("./egress-Cn9zDfDT.mjs").then((m) => m.egressCommand),
|
|
79
|
+
secrets: () => import("./secrets-Bl3uOx4l.mjs").then((m) => m.secretsCommand),
|
|
80
|
+
schedule: () => import("./schedule-CLuksxSM.mjs").then((m) => m.scheduleCommand),
|
|
81
|
+
feedback: () => import("./feedback-Dvb6CdlP.mjs").then((m) => m.feedbackCommand),
|
|
82
|
+
db: () => import("./db-KTLn8J1u.mjs").then((m) => m.dbCommand),
|
|
83
|
+
usage: () => import("./usage-CV7TjTO8.mjs").then((m) => m.usageCommand),
|
|
84
|
+
ai: () => import("./ai-fAvjdjVX.mjs").then((m) => m.aiCommand),
|
|
85
|
+
open: () => import("./open-BJgd0R61.mjs").then((m) => m.openCommand),
|
|
86
|
+
docs: () => import("./docs-Dg0UJugG.mjs").then((m) => m.docsCommand),
|
|
87
|
+
skill: () => import("./skill-CuES7kPj.mjs").then((m) => m.skillCommand),
|
|
88
|
+
upgrade: () => import("./upgrade-C8YdSKKd.mjs").then((m) => m.upgradeCommand),
|
|
89
|
+
logs: () => import("./logs-D5RWqsWl.mjs").then((m) => m.logsCommand)
|
|
88
90
|
}
|
|
89
91
|
});
|
|
90
92
|
if (process.argv[2] !== "upgrade") notifyOfUpdate(version);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { s as apiDataRequest } from "./ui-
|
|
2
|
-
import { a as
|
|
3
|
-
import {
|
|
1
|
+
import { s as apiDataRequest } from "./ui-CeupNHNa.mjs";
|
|
2
|
+
import { a as META_COLUMNS, i as dbRouter, o as blobRouter, r as realtimeRouter } from "./src-C7zPhbxi.mjs";
|
|
3
|
+
import { i as rewriteApiPath } from "./dev-JlzMZJow.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";
|
|
@@ -532,6 +532,39 @@ async function applyMigrations(store, dialect, files) {
|
|
|
532
532
|
};
|
|
533
533
|
}
|
|
534
534
|
//#endregion
|
|
535
|
+
//#region ../data-plane/src/realtime-memory.ts
|
|
536
|
+
const sameScope = (a, b) => a.projectId === b.projectId && a.channel === b.channel;
|
|
537
|
+
function memoryRealtimeBroker() {
|
|
538
|
+
const subscriptions = /* @__PURE__ */ new Set();
|
|
539
|
+
const dropProject = (projectId) => {
|
|
540
|
+
for (const sub of subscriptions) {
|
|
541
|
+
if (sub.scope.projectId !== projectId) continue;
|
|
542
|
+
subscriptions.delete(sub);
|
|
543
|
+
sub.sink.close();
|
|
544
|
+
}
|
|
545
|
+
};
|
|
546
|
+
return {
|
|
547
|
+
async subscribe(scope, topics, sink) {
|
|
548
|
+
const sub = {
|
|
549
|
+
scope,
|
|
550
|
+
topics: new Set(topics),
|
|
551
|
+
sink
|
|
552
|
+
};
|
|
553
|
+
subscriptions.add(sub);
|
|
554
|
+
return { close: () => void subscriptions.delete(sub) };
|
|
555
|
+
},
|
|
556
|
+
async publish(scope, envelope) {
|
|
557
|
+
for (const sub of subscriptions) if (sameScope(sub.scope, scope) && sub.topics.has(envelope.topic)) sub.sink.deliver(envelope);
|
|
558
|
+
},
|
|
559
|
+
async disconnect(projectId) {
|
|
560
|
+
dropProject(projectId);
|
|
561
|
+
},
|
|
562
|
+
async purge(projectId) {
|
|
563
|
+
dropProject(projectId);
|
|
564
|
+
}
|
|
565
|
+
};
|
|
566
|
+
}
|
|
567
|
+
//#endregion
|
|
535
568
|
//#region src/fs-blob.ts
|
|
536
569
|
/**
|
|
537
570
|
* Filesystem BlobBucket for `canopy dev --local` — same structural interface
|
|
@@ -808,6 +841,22 @@ async function createSandbox(options) {
|
|
|
808
841
|
},
|
|
809
842
|
signDownload: async () => "local"
|
|
810
843
|
}));
|
|
844
|
+
const realtimeHub = memoryRealtimeBroker();
|
|
845
|
+
app.route("/data/v1", realtimeRouter({ context: async (c) => ownProject(c) ?? {
|
|
846
|
+
broker: realtimeHub,
|
|
847
|
+
enabled: true,
|
|
848
|
+
scope: {
|
|
849
|
+
projectId: options.slug,
|
|
850
|
+
channel: "live"
|
|
851
|
+
},
|
|
852
|
+
sender: {
|
|
853
|
+
kind: "monk",
|
|
854
|
+
email: options.email ?? "dev@localhost"
|
|
855
|
+
},
|
|
856
|
+
redactEmails: false,
|
|
857
|
+
isRuntime: true,
|
|
858
|
+
owner: "local"
|
|
859
|
+
} }));
|
|
811
860
|
app.get("/data/v1/sdk.js", () => {
|
|
812
861
|
return new Response(readFileSync(resolveSdkBundle(), "utf8"), { headers: {
|
|
813
862
|
"content-type": "text/javascript",
|
|
@@ -816,7 +865,10 @@ async function createSandbox(options) {
|
|
|
816
865
|
});
|
|
817
866
|
app.get("/data/v1/platform", (c) => c.json({
|
|
818
867
|
dialect: dialect.name,
|
|
819
|
-
capabilities: {
|
|
868
|
+
capabilities: {
|
|
869
|
+
fts: dialect.fts,
|
|
870
|
+
realtime: true
|
|
871
|
+
}
|
|
820
872
|
}));
|
|
821
873
|
app.get("/data/v1/auth/me", (c) => c.json({
|
|
822
874
|
id: "local-dev",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { _ as writeConfigFile, f as clearAllProfileTokens, g as upsertProfileAuth, h as setActiveProfile, m as readConfigFile, n as siteConfig, p as listBakedProfiles, v as getBakedProfile, y as isProfileId } from "./site-DM2J10CB.mjs";
|
|
2
|
-
import { n as openBrowser, r as reportErrors } from "./ui-
|
|
2
|
+
import { n as openBrowser, r as reportErrors } from "./ui-CeupNHNa.mjs";
|
|
3
3
|
import { defineCommand } from "citty";
|
|
4
4
|
import { hostname } from "node:os";
|
|
5
5
|
import * as p from "@clack/prompts";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { c as resolveProject, o as projectFlagArgs } from "./site-DM2J10CB.mjs";
|
|
2
|
-
import { d as withContext, l as apiRequest, r as reportErrors } from "./ui-
|
|
2
|
+
import { d as withContext, l as apiRequest, r as reportErrors } from "./ui-CeupNHNa.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-DM2J10CB.mjs";
|
|
2
|
-
import { s as apiDataRequest } from "./ui-
|
|
2
|
+
import { s as apiDataRequest } from "./ui-CeupNHNa.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-DM2J10CB.mjs";
|
|
2
|
-
import { n as openBrowser, r as reportErrors } from "./ui-
|
|
2
|
+
import { n as openBrowser, r as reportErrors } from "./ui-CeupNHNa.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-DM2J10CB.mjs";
|
|
2
|
-
import { a as unwrap, i as requireTty, l as apiRequest, r as reportErrors } from "./ui-
|
|
2
|
+
import { a as unwrap, i as requireTty, l as apiRequest, r as reportErrors } from "./ui-CeupNHNa.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";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { c as resolveProject, o as projectFlagArgs } from "./site-DM2J10CB.mjs";
|
|
2
|
-
import { d as withContext, l as apiRequest, r as reportErrors, t as formatDateTime } from "./ui-
|
|
2
|
+
import { d as withContext, l as apiRequest, r as reportErrors, t as formatDateTime } from "./ui-CeupNHNa.mjs";
|
|
3
3
|
import { defineCommand } from "citty";
|
|
4
4
|
//#region src/schedule.ts
|
|
5
5
|
/**
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { a as projectDirArgs, c as resolveProject } from "./site-DM2J10CB.mjs";
|
|
2
|
-
import { a as unwrap, d as withContext, l as apiRequest, r as reportErrors, t as formatDateTime } from "./ui-
|
|
2
|
+
import { a as unwrap, d as withContext, l as apiRequest, r as reportErrors, t as formatDateTime } from "./ui-CeupNHNa.mjs";
|
|
3
3
|
import { defineCommand } from "citty";
|
|
4
4
|
import * as p from "@clack/prompts";
|
|
5
5
|
//#region src/secrets.ts
|
|
@@ -98,6 +98,9 @@ and never go stale.
|
|
|
98
98
|
- Roll back: `canopy rollback`
|
|
99
99
|
- Open the live site: `canopy open`
|
|
100
100
|
- Manage data: `canopy db pull | snapshot | snapshots | restore | migrate`
|
|
101
|
+
- Use AI (`canopy.ai`): run `canopy ai models` before naming a model in code.
|
|
102
|
+
Prefer the aliases `auto/low` (default) / `auto/mid` / `auto/high` over
|
|
103
|
+
hard-coded ids — model ids outside that list are refused.
|
|
101
104
|
- Work bug reports & feedback: `canopy feedback list | show | comment | status | report`
|
|
102
105
|
— see [Working feedback reports](#working-feedback-reports-issues) below
|
|
103
106
|
- CI credentials: `canopy token create | list | revoke`
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { t as version } from "./package-
|
|
2
|
-
import { r as reportErrors } from "./ui-
|
|
1
|
+
import { t as version } from "./package-CnS450Yc.mjs";
|
|
2
|
+
import { r as reportErrors } from "./ui-CeupNHNa.mjs";
|
|
3
3
|
import { defineCommand } from "citty";
|
|
4
4
|
import { cpSync, existsSync, mkdirSync, rmSync, symlinkSync, writeFileSync } from "node:fs";
|
|
5
5
|
import { homedir } from "node:os";
|