@mediamonks/canopy-cli 0.7.41 → 0.7.42
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-PXXMv7kO.mjs → ai-DpQ1nLka.mjs} +2 -2
- package/dist/{api-BHzCvDcY.mjs → api-Do9c8tZ_.mjs} +61 -3
- package/dist/{browse-DRz1DowL.mjs → browse-Dp5HytqK.mjs} +2 -2
- package/dist/{db-5LfBG6N7.mjs → db-BNsX7sXo.mjs} +4 -4
- package/dist/{deploy-6rYq1Iq8.mjs → deploy-CXMLABvH.mjs} +8 -8
- package/dist/{dev-D_gyIeUq.mjs → dev-CPR-9slK.mjs} +6 -6
- package/dist/dev-Dj3wFKHe.mjs +2 -0
- package/dist/docs/ci.md +19 -1
- package/dist/docs/cli-rename.md +51 -0
- package/dist/docs/git.md +5 -0
- package/dist/docs/index.json +11 -1
- package/dist/docs/rename.md +65 -0
- package/dist/{docs-B9xGnLSR.mjs → docs-CkJGcCpR.mjs} +4 -4
- package/dist/{egress-CCpOsYpS.mjs → egress-D6qyaaAn.mjs} +3 -3
- package/dist/{feedback-CdjtPumG.mjs → feedback-DMgUUl0V.mjs} +4 -4
- package/dist/index.mjs +33 -32
- package/dist/{local-Dshy0NsJ.mjs → local-w2wXfE1d.mjs} +2 -2
- package/dist/{login-CKNFCp7c.mjs → login-CeNvHLUv.mjs} +3 -3
- package/dist/{logs-D3WakFME.mjs → logs-BzgJfWNP.mjs} +3 -3
- package/dist/{mcp-CoVS5Mj3.mjs → mcp-DFLaXiY_.mjs} +8 -6
- package/dist/{migrations-DDJ5JJqj.mjs → migrations-Bsu4quya.mjs} +2 -2
- package/dist/{open-BPYozPQo.mjs → open-D71Ms1nG.mjs} +2 -2
- package/dist/{package-SWgTmctH.mjs → package-CVDpzGgA.mjs} +1 -1
- package/dist/{project-template-DACdMdpn.mjs → project-template-BXfHpsI8.mjs} +3 -3
- package/dist/project-template-BbmSmraT.mjs +2 -0
- package/dist/{projects-naWQ89Qj.mjs → projects-BWTIk80a.mjs} +5 -5
- package/dist/rename-BdIFrqvG.mjs +81 -0
- package/dist/repo-JBKJqyoS.mjs +2 -0
- package/dist/{repo-BCua4L-I.mjs → repo-LSOxnmUk.mjs} +5 -3
- package/dist/{schedule-DjHcaxyt.mjs → schedule-DpMIpCGC.mjs} +3 -3
- package/dist/{secrets-COwRvCev.mjs → secrets-BVx8L0Gj.mjs} +3 -3
- package/dist/{site-DM2J10CB.mjs → site-0i7wAJqg.mjs} +24 -1
- package/dist/{skill-DSYQA4Ls.mjs → skill-D58yV6K8.mjs} +2 -2
- package/dist/{staging-CYMTZyyM.mjs → staging-ynPlMiHW.mjs} +3 -3
- package/dist/{token-CCpJ1ES4.mjs → token-CxiPrKn_.mjs} +3 -3
- package/dist/{ui-SBUdC9rX.mjs → ui-CDUJq-X-.mjs} +1 -1
- package/dist/{upgrade-B_tLCpc4.mjs → upgrade-Dll5rhOQ.mjs} +2 -2
- package/dist/{usage-BSPnsMXd.mjs → usage-BtPsb7Iu.mjs} +3 -3
- package/package.json +1 -1
- package/dist/dev-D38FQ2LD.mjs +0 -2
- package/dist/project-template-CMgD5hnV.mjs +0 -2
- package/dist/repo-Un_p617X.mjs +0 -2
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { a as apiRequest,
|
|
2
|
-
import { r as reportErrors } from "./ui-
|
|
1
|
+
import { a as apiRequest, f as withContext } from "./api-Do9c8tZ_.mjs";
|
|
2
|
+
import { r as reportErrors } from "./ui-CDUJq-X-.mjs";
|
|
3
3
|
import { defineCommand } from "citty";
|
|
4
4
|
//#region src/ai.ts
|
|
5
5
|
/**
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { r as siteConfig, t as projectDir, u as rewriteCanopyJsonSlug } from "./site-0i7wAJqg.mjs";
|
|
2
2
|
//#region src/api.ts
|
|
3
3
|
/**
|
|
4
4
|
* Authenticated requests to the Canopy control plane. Credentials, in order:
|
|
@@ -29,7 +29,7 @@ const ERROR_COPY = {
|
|
|
29
29
|
fix: "Choose a different slug."
|
|
30
30
|
},
|
|
31
31
|
invalid_slug: {
|
|
32
|
-
why: "the slug
|
|
32
|
+
why: "the slug breaks one of the naming rules",
|
|
33
33
|
fix: "Use lowercase letters, digits and hyphens only (e.g. my-project)."
|
|
34
34
|
},
|
|
35
35
|
project_paused: {
|
|
@@ -191,6 +191,26 @@ const ERROR_COPY = {
|
|
|
191
191
|
worker_secret_sync_failed: {
|
|
192
192
|
why: "the secret was saved but redeploying the running worker with it failed",
|
|
193
193
|
fix: "Retry, or redeploy the project (`canopy deploy`) to apply it."
|
|
194
|
+
},
|
|
195
|
+
slug_unchanged: {
|
|
196
|
+
why: "the project already has that slug",
|
|
197
|
+
fix: "Pass the new slug you want, e.g. `canopy rename my-new-name`."
|
|
198
|
+
},
|
|
199
|
+
rename_not_allowed: {
|
|
200
|
+
why: "Canopy's own projects (dashboard, www, docs) keep their names",
|
|
201
|
+
fix: "Nothing to do — those addresses are fixed."
|
|
202
|
+
},
|
|
203
|
+
rename_too_soon: {
|
|
204
|
+
why: "a project can be renamed once a day",
|
|
205
|
+
fix: "Try again after the time shown, or ask a Canopy admin if it can't wait."
|
|
206
|
+
},
|
|
207
|
+
too_many_old_slugs: {
|
|
208
|
+
why: "a project keeps at most 5 old slugs, and each one stays reserved for it",
|
|
209
|
+
fix: "Rename back to one of its old slugs instead (`canopy projects` shows them in the dashboard's Settings), or ask a Canopy admin."
|
|
210
|
+
},
|
|
211
|
+
rename_conflict: {
|
|
212
|
+
why: "the project changed while the rename ran",
|
|
213
|
+
fix: "Run the command again."
|
|
194
214
|
}
|
|
195
215
|
};
|
|
196
216
|
/** Generic explanation for a status code when we have no per-code copy. */
|
|
@@ -260,6 +280,40 @@ function commonHeaders() {
|
|
|
260
280
|
...agentHeaders()
|
|
261
281
|
};
|
|
262
282
|
}
|
|
283
|
+
/**
|
|
284
|
+
* Renamed projects (OpenSpec project-slug-rename). A request that names an
|
|
285
|
+
* old slug still reaches the project, and the answer carries its current
|
|
286
|
+
* slug in `Canopy-Project-Slug`. By default the CLI then points canopy.json
|
|
287
|
+
* at it (when that file named the old slug) and says so once; `canopy mcp
|
|
288
|
+
* serve` and the git credential helper only say so, because what names the
|
|
289
|
+
* slug there is the user's own configuration, not ours to edit.
|
|
290
|
+
*/
|
|
291
|
+
const RENAMED_HEADER = "canopy-project-slug";
|
|
292
|
+
const renames = /* @__PURE__ */ new Map();
|
|
293
|
+
let renameHandling = "rewrite";
|
|
294
|
+
/** Report renames without editing files (mcp serve, the git credential helper). */
|
|
295
|
+
function noticeRenamesOnly() {
|
|
296
|
+
renameHandling = "notice";
|
|
297
|
+
}
|
|
298
|
+
/** The current slug of a project this process saw renamed; `slug` when it saw none. */
|
|
299
|
+
function currentSlug(slug) {
|
|
300
|
+
return renames.get(slug) ?? slug;
|
|
301
|
+
}
|
|
302
|
+
/** Act on a response that says `named` is an old slug. Exported for the credential helper. */
|
|
303
|
+
function followRename(response, named) {
|
|
304
|
+
const current = response.headers.get(RENAMED_HEADER);
|
|
305
|
+
if (!named || !current || current === named || renames.has(named)) return;
|
|
306
|
+
renames.set(named, current);
|
|
307
|
+
if (renameHandling === "rewrite" && rewriteCanopyJsonSlug(projectDir(), named, current)) {
|
|
308
|
+
console.error(`canopy: project "${named}" is now "${current}" — updated canopy.json.`);
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
console.error(`canopy: project "${named}" is now "${current}" — use "${current}" where you name it (MCP client config, scripts); the old name keeps working for now.`);
|
|
312
|
+
}
|
|
313
|
+
/** The slug a control-plane path names (`/projects/<slug>/…`), if any. */
|
|
314
|
+
function slugInPath(path) {
|
|
315
|
+
return /^\/projects\/([^/?#]+)/.exec(path)?.[1];
|
|
316
|
+
}
|
|
263
317
|
var ApiError = class extends Error {
|
|
264
318
|
status;
|
|
265
319
|
code;
|
|
@@ -351,6 +405,7 @@ async function apiRequest(method, path, body, rawContentType, headers) {
|
|
|
351
405
|
},
|
|
352
406
|
body: body === void 0 ? void 0 : isForm ? body : rawContentType !== void 0 ? body : JSON.stringify(body)
|
|
353
407
|
});
|
|
408
|
+
followRename(response, slugInPath(path));
|
|
354
409
|
if (!response.ok) return fail(response, isDeployToken, method, path);
|
|
355
410
|
if (response.status === 204) return void 0;
|
|
356
411
|
return await response.json();
|
|
@@ -370,6 +425,7 @@ async function apiUpload(path, bytes, contentType) {
|
|
|
370
425
|
},
|
|
371
426
|
body: bytes
|
|
372
427
|
});
|
|
428
|
+
followRename(response, slugInPath(path));
|
|
373
429
|
if (!response.ok) return fail(response, isDeployToken, "POST", path);
|
|
374
430
|
return await response.json();
|
|
375
431
|
}
|
|
@@ -380,6 +436,7 @@ async function apiDownload(path) {
|
|
|
380
436
|
...commonHeaders(),
|
|
381
437
|
authorization: `Bearer ${token}`
|
|
382
438
|
} });
|
|
439
|
+
followRename(response, slugInPath(path));
|
|
383
440
|
if (!response.ok) return fail(response, isDeployToken, "GET", path);
|
|
384
441
|
return Buffer.from(await response.arrayBuffer());
|
|
385
442
|
}
|
|
@@ -401,9 +458,10 @@ async function apiDataRequest(method, path, projectSlug, body, rawContentType, c
|
|
|
401
458
|
},
|
|
402
459
|
body: body === void 0 ? void 0 : rawContentType !== void 0 ? body : JSON.stringify(body)
|
|
403
460
|
});
|
|
461
|
+
followRename(response, projectSlug);
|
|
404
462
|
if (!response.ok) return fail(response, isDeployToken, method, path);
|
|
405
463
|
if (response.status === 204) return void 0;
|
|
406
464
|
return await response.json();
|
|
407
465
|
}
|
|
408
466
|
//#endregion
|
|
409
|
-
export { apiRequest as a, credentials as c, apiDownload as i,
|
|
467
|
+
export { apiRequest as a, credentials as c, noticeRenamesOnly as d, withContext as f, apiDownload as i, currentSlug as l, agentHeaders as n, apiUpload as o, apiDataRequest as r, commonHeaders as s, ApiError as t, followRename as u };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { a as apiRequest, t as ApiError } from "./api-
|
|
1
|
+
import { p as tryResolveProject, r as siteConfig } from "./site-0i7wAJqg.mjs";
|
|
2
|
+
import { a as apiRequest, t as ApiError } from "./api-Do9c8tZ_.mjs";
|
|
3
3
|
import { defineCommand } from "citty";
|
|
4
4
|
//#region src/browse.ts
|
|
5
5
|
/**
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { a as apiRequest,
|
|
3
|
-
import { a as unwrap, i as requireTty, r as reportErrors } from "./ui-
|
|
4
|
-
import { n as migrateProject, r as readMigrationFiles, t as describeMigrateResult } from "./migrations-
|
|
1
|
+
import { l as resolveProject, s as projectFlagArgs } from "./site-0i7wAJqg.mjs";
|
|
2
|
+
import { a as apiRequest, f as withContext, r as apiDataRequest } from "./api-Do9c8tZ_.mjs";
|
|
3
|
+
import { a as unwrap, i as requireTty, r as reportErrors } from "./ui-CDUJq-X-.mjs";
|
|
4
|
+
import { n as migrateProject, r as readMigrationFiles, t as describeMigrateResult } from "./migrations-Bsu4quya.mjs";
|
|
5
5
|
import { defineCommand } from "citty";
|
|
6
6
|
import { readFileSync, writeFileSync } from "node:fs";
|
|
7
7
|
import * as p from "@clack/prompts";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { a as apiRequest, i as apiDownload, l as
|
|
3
|
-
import { a as unwrap, r as reportErrors, t as formatDateTime } from "./ui-
|
|
4
|
-
import { n as migrateProject, r as readMigrationFiles, t as describeMigrateResult } from "./migrations-
|
|
1
|
+
import { c as projectUrl, d as stagingUrl, f as tryReadCanopyJson, l as resolveProject, o as projectDirArgs, r as siteConfig, s as projectFlagArgs } from "./site-0i7wAJqg.mjs";
|
|
2
|
+
import { a as apiRequest, f as withContext, i as apiDownload, l as currentSlug, r as apiDataRequest } from "./api-Do9c8tZ_.mjs";
|
|
3
|
+
import { a as unwrap, r as reportErrors, t as formatDateTime } from "./ui-CDUJq-X-.mjs";
|
|
4
|
+
import { n as migrateProject, r as readMigrationFiles, t as describeMigrateResult } from "./migrations-Bsu4quya.mjs";
|
|
5
5
|
import { builtinModules } from "node:module";
|
|
6
6
|
import { defineCommand } from "citty";
|
|
7
7
|
import { mkdtempSync, readFileSync, readdirSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
@@ -314,10 +314,10 @@ const deployCommand = defineCommand({
|
|
|
314
314
|
}
|
|
315
315
|
const backend = result.worker?.serviceUrl ? ` · backend ${result.worker.serviceUrl}` : "";
|
|
316
316
|
if (channel === "staging") {
|
|
317
|
-
spin.stop(`Staging: ${stagingUrl(slug)} (v${result.version})${backend}`);
|
|
317
|
+
spin.stop(`Staging: ${stagingUrl(currentSlug(slug))} (v${result.version})${backend}`);
|
|
318
318
|
if (stagingCreated) console.log(" staging channel created — database cloned from live");
|
|
319
319
|
console.log(` happy with it? \`canopy promote\` makes v${result.version} live`);
|
|
320
|
-
} else spin.stop(`Live: ${projectUrl(slug)} (v${result.version})${backend}`);
|
|
320
|
+
} else spin.stop(`Live: ${projectUrl(currentSlug(slug))} (v${result.version})${backend}`);
|
|
321
321
|
if (migrateResult !== void 0) for (const line of describeMigrateResult(migrateResult)) console.log(` ${line}`);
|
|
322
322
|
})
|
|
323
323
|
});
|
|
@@ -371,7 +371,7 @@ const promoteCommand = defineCommand({
|
|
|
371
371
|
}
|
|
372
372
|
const backend = result.worker?.serviceUrl ? ` · backend ${result.worker.serviceUrl}` : "";
|
|
373
373
|
const was = result.previous === null ? "" : `, was v${result.previous}`;
|
|
374
|
-
spin.stop(`Live: ${projectUrl(slug)} (v${result.version}${was})${backend}`);
|
|
374
|
+
spin.stop(`Live: ${projectUrl(currentSlug(slug))} (v${result.version}${was})${backend}`);
|
|
375
375
|
for (const name of result.migrationsApplied) console.log(` applied ${name}`);
|
|
376
376
|
if (result.snapshot !== void 0) console.log(` pre-promote snapshot: ${result.snapshot} (canopy db restore --snapshot …)`);
|
|
377
377
|
})
|
|
@@ -445,7 +445,7 @@ const rollbackCommand = defineCommand({
|
|
|
445
445
|
console.log(`Rolling back ${slug} to v${target}`);
|
|
446
446
|
const { live: result } = await apiRequest("POST", `/projects/${slug}/deploys/${target}/rollback`);
|
|
447
447
|
const worker = result.worker ? ` · worker v${result.worker.version}` : "";
|
|
448
|
-
console.log(`Live: ${projectUrl(slug)} (v${result.version}, was v${result.previous ?? "?"})${worker}`);
|
|
448
|
+
console.log(`Live: ${projectUrl(currentSlug(slug))} (v${result.version}, was v${result.previous ?? "?"})${worker}`);
|
|
449
449
|
})
|
|
450
450
|
});
|
|
451
451
|
const downloadCommand = defineCommand({
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { a as
|
|
2
|
-
import { a as apiRequest,
|
|
3
|
-
import { r as reportErrors, t as formatDateTime } from "./ui-
|
|
4
|
-
import { r as readMigrationFiles } from "./migrations-
|
|
1
|
+
import { a as projectDialect, f as tryReadCanopyJson, l as resolveProject, o as projectDirArgs, r as siteConfig } from "./site-0i7wAJqg.mjs";
|
|
2
|
+
import { a as apiRequest, f as withContext, t as ApiError } from "./api-Do9c8tZ_.mjs";
|
|
3
|
+
import { r as reportErrors, t as formatDateTime } from "./ui-CDUJq-X-.mjs";
|
|
4
|
+
import { r as readMigrationFiles } from "./migrations-Bsu4quya.mjs";
|
|
5
5
|
import { i as stampWorkerHeaders, n as WORKER_ATTEST_HEADER } from "./src-RUf3Gfuh.mjs";
|
|
6
6
|
import { n as writeDevServerRecord } from "./dev-server-file-Dk9pk4AR.mjs";
|
|
7
7
|
import { createRequire } from "node:module";
|
|
@@ -251,7 +251,7 @@ async function devRemote(flags, slug) {
|
|
|
251
251
|
async function devLocal(flags, slug) {
|
|
252
252
|
let dump;
|
|
253
253
|
if (flags.pull) {
|
|
254
|
-
const { fetchProjectDump } = await import("./local-
|
|
254
|
+
const { fetchProjectDump } = await import("./local-w2wXfE1d.mjs");
|
|
255
255
|
console.log(`Pulling a fresh export of ${slug}${flags.staging ? " [staging]" : ""}…`);
|
|
256
256
|
dump = await fetchProjectDump(slug, flags.staging ? "staging" : "live");
|
|
257
257
|
}
|
|
@@ -267,7 +267,7 @@ async function devLocal(flags, slug) {
|
|
|
267
267
|
console.log(`Note: ai/services will be unavailable — could not reach the platform (${reason}).`);
|
|
268
268
|
}
|
|
269
269
|
const workerAttest = randomBytes(24).toString("base64url");
|
|
270
|
-
const { createSandbox } = await import("./local-
|
|
270
|
+
const { createSandbox } = await import("./local-w2wXfE1d.mjs");
|
|
271
271
|
const sandbox = await createSandbox({
|
|
272
272
|
slug,
|
|
273
273
|
persistDir: join(flags.dir, ".canopy", "local"),
|
package/dist/docs/ci.md
CHANGED
|
@@ -1,8 +1,26 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: CI and deploy tokens
|
|
3
|
-
description: Push-to-main deploys using a project-scoped deploy token.
|
|
3
|
+
description: Push-to-main deploys using a project-scoped deploy token, and why we recommend deploying from your own machine instead.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
+
## Our recommendation: deploy manually
|
|
7
|
+
|
|
8
|
+
Most Canopy users don't have a GitHub Enterprise seat, so they can't see or
|
|
9
|
+
interact with GitHub Actions. That is why Canopy repositories are created with
|
|
10
|
+
Actions turned **off**: pushing a workflow file works, but nothing runs, and
|
|
11
|
+
there is no Actions tab for you to look at. Turning Actions on for a repository
|
|
12
|
+
is something only a GitHub administrator can do, and you would still not see
|
|
13
|
+
the runs.
|
|
14
|
+
|
|
15
|
+
Deploy from your machine (or let your coding agent do it): run
|
|
16
|
+
`canopy deploy --staging`, check the result, then `canopy promote`. It is
|
|
17
|
+
usually quicker than waiting for a pipeline, and your agent can verify the
|
|
18
|
+
deploy right afterwards. Run your typecheck, lint and tests locally before you
|
|
19
|
+
deploy.
|
|
20
|
+
|
|
21
|
+
The rest of this page is for teams that run their own CI somewhere they can
|
|
22
|
+
see it.
|
|
23
|
+
|
|
6
24
|
Interactive `canopy login` is for humans at a terminal. For automated deploys
|
|
7
25
|
(GitHub Actions, etc.) use a **deploy token**: a project-scoped credential that
|
|
8
26
|
can deploy that one project and nothing else.
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: canopy rename
|
|
3
|
+
description: Change a project's slug — its address — and update canopy.json.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
`canopy rename` gives a project a new slug, and with it a new address. The old
|
|
7
|
+
slug stays with the project: old links redirect, and everything that still
|
|
8
|
+
names it keeps working. See [Rename a project](https://docs.canopy.website/guides/rename) for what changes
|
|
9
|
+
and what doesn't.
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
canopy rename my-new-name # in the project's folder
|
|
15
|
+
canopy rename my-new-name --project old-name # name the project yourself
|
|
16
|
+
canopy rename my-new-name --yes # skip the confirmation
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Arguments & flags
|
|
20
|
+
|
|
21
|
+
- `<slug>` — the new slug. Same rules as `canopy create`: 2–40 lowercase letters,
|
|
22
|
+
digits and dashes, starting with a letter.
|
|
23
|
+
- `--project <slug>` — the project to rename. Defaults to the `slug` in
|
|
24
|
+
`canopy.json`.
|
|
25
|
+
- `[dir]` — the project folder (defaults to the current directory). Its
|
|
26
|
+
`canopy.json` is updated after the rename.
|
|
27
|
+
- `--yes` — rename without asking. Required on non-interactive runs.
|
|
28
|
+
- `--json` — print the result as JSON: the project, its old slugs, and whether
|
|
29
|
+
the backend refresh and Slack app update are still to come.
|
|
30
|
+
|
|
31
|
+
## Details
|
|
32
|
+
|
|
33
|
+
Before renaming, the CLI lists what changes for people: the address, the email
|
|
34
|
+
sender, client-password visitors signing in again, and visitors' browser storage
|
|
35
|
+
starting empty. Only the owner or a Canopy admin can rename. A project can be
|
|
36
|
+
renamed once a day and keeps at most 5 old slugs; renaming back to one of them
|
|
37
|
+
is always allowed.
|
|
38
|
+
|
|
39
|
+
Other commands notice a rename too. When `canopy.json` still names the old
|
|
40
|
+
slug, the next command that talks to the project updates it and says so in one
|
|
41
|
+
line. `canopy mcp serve` and the git credential helper only print a notice,
|
|
42
|
+
since they're started from your own configuration.
|
|
43
|
+
|
|
44
|
+
## Examples
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
canopy rename q4-dashboard
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Shows what changes, asks to confirm, renames, and points `canopy.json` at
|
|
51
|
+
`q4-dashboard`.
|
package/dist/docs/git.md
CHANGED
|
@@ -79,6 +79,11 @@ sign in to a Canopy repository; use the terminal for those.
|
|
|
79
79
|
- **Deleting is for good.** `canopy repo delete` (owner only) removes the
|
|
80
80
|
repository and all its history. Deleting the project deletes its repository
|
|
81
81
|
too.
|
|
82
|
+
- **No CI on the repository.** GitHub Actions are turned off on Canopy
|
|
83
|
+
repositories, so workflow files you push don't run, and you can't see
|
|
84
|
+
Actions runs without a GitHub seat. Run your checks locally and deploy with
|
|
85
|
+
`canopy deploy --staging` then `canopy promote` (see
|
|
86
|
+
[CI and deploy tokens](https://docs.canopy.website/guides/ci)).
|
|
82
87
|
- **Moving to your own GitHub later** is ordinary git: create an empty
|
|
83
88
|
repository there, then `git remote add github <its url>` and
|
|
84
89
|
`git push github --all`.
|
package/dist/docs/index.json
CHANGED
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
{
|
|
66
66
|
"slug": "ci",
|
|
67
67
|
"title": "CI and deploy tokens",
|
|
68
|
-
"summary": "Push-to-main deploys using a project-scoped deploy token."
|
|
68
|
+
"summary": "Push-to-main deploys using a project-scoped deploy token, and why we recommend deploying from your own machine instead."
|
|
69
69
|
},
|
|
70
70
|
{
|
|
71
71
|
"slug": "git",
|
|
@@ -77,6 +77,11 @@
|
|
|
77
77
|
"title": "Start from a template",
|
|
78
78
|
"summary": "Begin a project from someone else's working one — a copy of their code and setup, never their data."
|
|
79
79
|
},
|
|
80
|
+
{
|
|
81
|
+
"slug": "rename",
|
|
82
|
+
"title": "Rename a project",
|
|
83
|
+
"summary": "Change a project's slug — its address — without breaking links, checkouts or integrations."
|
|
84
|
+
},
|
|
80
85
|
{
|
|
81
86
|
"slug": "agent-tools",
|
|
82
87
|
"title": "Agent tools (MCP)",
|
|
@@ -231,6 +236,11 @@
|
|
|
231
236
|
"title": "canopy delete",
|
|
232
237
|
"summary": "Permanently delete a Canopy project — its deploys, database, and files."
|
|
233
238
|
},
|
|
239
|
+
{
|
|
240
|
+
"slug": "cli-rename",
|
|
241
|
+
"title": "canopy rename",
|
|
242
|
+
"summary": "Change a project's slug — its address — and update canopy.json."
|
|
243
|
+
},
|
|
234
244
|
{
|
|
235
245
|
"slug": "cli-dev",
|
|
236
246
|
"title": "canopy dev",
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Rename a project
|
|
3
|
+
description: Change a project's slug — its address — without breaking links, checkouts or integrations.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
A project's **slug** is its address: `<slug>.canopy.website`. You can change it
|
|
7
|
+
from the dashboard or the CLI. Nothing needs to be redeployed, and everything
|
|
8
|
+
that still uses the old slug keeps reaching the project.
|
|
9
|
+
|
|
10
|
+
## How to rename
|
|
11
|
+
|
|
12
|
+
In the dashboard, open the project's **Settings** and use **Rename address**
|
|
13
|
+
on the _Project address_ card. Type the new slug, read what changes, and type
|
|
14
|
+
it again to confirm.
|
|
15
|
+
|
|
16
|
+
From the CLI, run it inside the project's folder:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
canopy rename my-new-name
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
The CLI shows what changes and asks before it renames. It also updates
|
|
23
|
+
`canopy.json` in that folder. See [`canopy rename`](https://docs.canopy.website/cli/rename) for the flags.
|
|
24
|
+
|
|
25
|
+
Only the project's **owner** (or a Canopy admin) can rename it.
|
|
26
|
+
|
|
27
|
+
## What changes
|
|
28
|
+
|
|
29
|
+
- **The address.** The project is now at `my-new-name.canopy.website`. The old
|
|
30
|
+
address sends visitors there, keeping the rest of the link
|
|
31
|
+
(`old-name.canopy.website/reports/42` opens `my-new-name.canopy.website/reports/42`).
|
|
32
|
+
The same goes for `staging-dot-` and pinned `v12-dot-` addresses.
|
|
33
|
+
- **The email sender.** Mail your project sends comes from `my-new-name@` instead
|
|
34
|
+
of `old-name@`.
|
|
35
|
+
- **Client-password visitors sign in again** on the new address. Their sign-in
|
|
36
|
+
belonged to the old one.
|
|
37
|
+
- **Browser storage starts empty** on the new address. Anything your app keeps
|
|
38
|
+
in the visitor's browser (localStorage, IndexedDB) stays with the old one.
|
|
39
|
+
- **New report keys** use the new slug: `my-new-name-18`.
|
|
40
|
+
|
|
41
|
+
## What keeps working
|
|
42
|
+
|
|
43
|
+
The old slug stays with your project. Nobody else can create a project with it.
|
|
44
|
+
|
|
45
|
+
- Old links and bookmarks redirect, as above. Pages that were already open keep
|
|
46
|
+
working until they're reloaded.
|
|
47
|
+
- `canopy.json` files, CI deploy tokens, scripts and MCP client configs that
|
|
48
|
+
still say the old slug keep working. The CLI updates a `canopy.json` it finds
|
|
49
|
+
with the old slug, and tells you when anything else should be updated.
|
|
50
|
+
- Your backend keeps running, and the rename updates the `PROJECT_SLUG` it
|
|
51
|
+
sees. If that update fails, Settings says so and has a retry button; Canopy
|
|
52
|
+
also retries on its own, and the next deploy updates it too.
|
|
53
|
+
- Report keys with the old slug (`old-name-17`) still open the report.
|
|
54
|
+
- Your Slack app keeps receiving events and slash commands.
|
|
55
|
+
- `git push` and `git pull` keep working. The repository itself keeps its name.
|
|
56
|
+
- Database, files, deploys, schedules, secrets and members don't change.
|
|
57
|
+
|
|
58
|
+
## Limits
|
|
59
|
+
|
|
60
|
+
- A project can be renamed **once a day**.
|
|
61
|
+
- A project keeps at most **5 old slugs**. Renaming back to one of them is always
|
|
62
|
+
allowed, and doesn't count as a new one.
|
|
63
|
+
- The new slug follows the same rules as a new project: 2–40 lowercase letters,
|
|
64
|
+
digits and dashes, starting with a letter.
|
|
65
|
+
- Canopy's own projects (`dashboard`, `www`, `docs`) can't be renamed.
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { t as version } from "./package-
|
|
2
|
-
import {
|
|
1
|
+
import { t as version } from "./package-CVDpzGgA.mjs";
|
|
2
|
+
import { r as siteConfig } from "./site-0i7wAJqg.mjs";
|
|
3
3
|
import { i as latestKnownVersion, r as compareSemver } from "./version-check-CcsA1QjI.mjs";
|
|
4
|
-
import { n as agentHeaders } from "./api-
|
|
5
|
-
import { r as reportErrors } from "./ui-
|
|
4
|
+
import { n as agentHeaders } from "./api-Do9c8tZ_.mjs";
|
|
5
|
+
import { r as reportErrors } from "./ui-CDUJq-X-.mjs";
|
|
6
6
|
import { defineCommand } from "citty";
|
|
7
7
|
import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from "node:fs";
|
|
8
8
|
import { homedir } from "node:os";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { a as apiRequest,
|
|
3
|
-
import { a as unwrap, i as requireTty, r as reportErrors, t as formatDateTime } from "./ui-
|
|
1
|
+
import { l as resolveProject, o as projectDirArgs } from "./site-0i7wAJqg.mjs";
|
|
2
|
+
import { a as apiRequest, f as withContext } from "./api-Do9c8tZ_.mjs";
|
|
3
|
+
import { a as unwrap, i as requireTty, r as reportErrors, t as formatDateTime } from "./ui-CDUJq-X-.mjs";
|
|
4
4
|
import { defineCommand } from "citty";
|
|
5
5
|
import * as p from "@clack/prompts";
|
|
6
6
|
//#region src/egress.ts
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { t as version } from "./package-
|
|
2
|
-
import {
|
|
3
|
-
import { a as apiRequest, c as credentials, i as apiDownload, o as apiUpload, s as commonHeaders } from "./api-
|
|
4
|
-
import { r as reportErrors, t as formatDateTime } from "./ui-
|
|
1
|
+
import { t as version } from "./package-CVDpzGgA.mjs";
|
|
2
|
+
import { f as tryReadCanopyJson, l as resolveProject, o as projectDirArgs, p as tryResolveProject, r as siteConfig } from "./site-0i7wAJqg.mjs";
|
|
3
|
+
import { a as apiRequest, c as credentials, i as apiDownload, o as apiUpload, s as commonHeaders } from "./api-Do9c8tZ_.mjs";
|
|
4
|
+
import { r as reportErrors, t as formatDateTime } from "./ui-CDUJq-X-.mjs";
|
|
5
5
|
import { defineCommand } from "citty";
|
|
6
6
|
import { basename, extname, join } from "node:path";
|
|
7
7
|
import { readFile, writeFile } from "node:fs/promises";
|
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { t as version } from "./package-
|
|
3
|
-
import {
|
|
2
|
+
import { t as version } from "./package-CVDpzGgA.mjs";
|
|
3
|
+
import { i as siteHeaderLine, n as setInvocationProfile } from "./site-0i7wAJqg.mjs";
|
|
4
4
|
import { a as notifyOfUpdate, o as runRefresh } from "./version-check-CcsA1QjI.mjs";
|
|
5
5
|
import { defineCommand, runMain } from "citty";
|
|
6
6
|
//#region src/index.ts
|
|
@@ -63,36 +63,37 @@ const main = defineCommand({
|
|
|
63
63
|
description: "canopy — create and deploy vibecoded projects"
|
|
64
64
|
},
|
|
65
65
|
subCommands: {
|
|
66
|
-
login: () => import("./login-
|
|
67
|
-
whoami: () => import("./login-
|
|
68
|
-
logout: () => import("./login-
|
|
69
|
-
create: () => import("./projects-
|
|
70
|
-
init: () => import("./projects-
|
|
71
|
-
projects: () => import("./projects-
|
|
72
|
-
delete: () => import("./projects-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
66
|
+
login: () => import("./login-CeNvHLUv.mjs").then((m) => m.loginCommand),
|
|
67
|
+
whoami: () => import("./login-CeNvHLUv.mjs").then((m) => m.whoamiCommand),
|
|
68
|
+
logout: () => import("./login-CeNvHLUv.mjs").then((m) => m.logoutCommand),
|
|
69
|
+
create: () => import("./projects-BWTIk80a.mjs").then((m) => m.createCommand),
|
|
70
|
+
init: () => import("./projects-BWTIk80a.mjs").then((m) => m.initCommand),
|
|
71
|
+
projects: () => import("./projects-BWTIk80a.mjs").then((m) => m.projectsCommand),
|
|
72
|
+
delete: () => import("./projects-BWTIk80a.mjs").then((m) => m.deleteCommand),
|
|
73
|
+
rename: () => import("./rename-BdIFrqvG.mjs").then((m) => m.renameCommand),
|
|
74
|
+
dev: () => import("./dev-Dj3wFKHe.mjs").then((m) => m.devCommand),
|
|
75
|
+
deploy: () => import("./deploy-CXMLABvH.mjs").then((m) => m.deployCommand),
|
|
76
|
+
promote: () => import("./deploy-CXMLABvH.mjs").then((m) => m.promoteCommand),
|
|
77
|
+
staging: () => import("./staging-ynPlMiHW.mjs").then((m) => m.stagingCommand),
|
|
78
|
+
rollback: () => import("./deploy-CXMLABvH.mjs").then((m) => m.rollbackCommand),
|
|
79
|
+
download: () => import("./deploy-CXMLABvH.mjs").then((m) => m.downloadCommand),
|
|
80
|
+
token: () => import("./token-CxiPrKn_.mjs").then((m) => m.tokenCommand),
|
|
81
|
+
egress: () => import("./egress-D6qyaaAn.mjs").then((m) => m.egressCommand),
|
|
82
|
+
secrets: () => import("./secrets-BVx8L0Gj.mjs").then((m) => m.secretsCommand),
|
|
83
|
+
schedule: () => import("./schedule-DpMIpCGC.mjs").then((m) => m.scheduleCommand),
|
|
84
|
+
feedback: () => import("./feedback-DMgUUl0V.mjs").then((m) => m.feedbackCommand),
|
|
85
|
+
db: () => import("./db-BNsX7sXo.mjs").then((m) => m.dbCommand),
|
|
86
|
+
usage: () => import("./usage-BtPsb7Iu.mjs").then((m) => m.usageCommand),
|
|
87
|
+
ai: () => import("./ai-DpQ1nLka.mjs").then((m) => m.aiCommand),
|
|
88
|
+
open: () => import("./open-D71Ms1nG.mjs").then((m) => m.openCommand),
|
|
89
|
+
browse: () => import("./browse-Dp5HytqK.mjs").then((m) => m.browseCommand),
|
|
90
|
+
docs: () => import("./docs-CkJGcCpR.mjs").then((m) => m.docsCommand),
|
|
91
|
+
skill: () => import("./skill-D58yV6K8.mjs").then((m) => m.skillCommand),
|
|
92
|
+
upgrade: () => import("./upgrade-Dll5rhOQ.mjs").then((m) => m.upgradeCommand),
|
|
93
|
+
logs: () => import("./logs-BzgJfWNP.mjs").then((m) => m.logsCommand),
|
|
94
|
+
mcp: () => import("./mcp-DFLaXiY_.mjs").then((m) => m.mcpCommand),
|
|
95
|
+
repo: () => import("./repo-JBKJqyoS.mjs").then((m) => m.repoCommand),
|
|
96
|
+
template: () => import("./project-template-BbmSmraT.mjs").then((m) => m.templateCommand)
|
|
96
97
|
}
|
|
97
98
|
});
|
|
98
99
|
const quiet = process.argv[2] === "mcp" && process.argv[3] === "serve" || process.argv[2] === "repo" && process.argv[3] === "credential";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { r as apiDataRequest } from "./api-
|
|
1
|
+
import { r as apiDataRequest } from "./api-Do9c8tZ_.mjs";
|
|
2
2
|
import { a as realtimeRouter, c as META_COLUMNS, l as blobRouter, n as WORKER_ATTEST_HEADER, o as dbRouter, s as hasPrivateNames } from "./src-RUf3Gfuh.mjs";
|
|
3
|
-
import { a as rewriteApiPath } from "./dev-
|
|
3
|
+
import { a as rewriteApiPath } from "./dev-CPR-9slK.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,6 +1,6 @@
|
|
|
1
|
-
import { _ as
|
|
2
|
-
import { n as agentHeaders } from "./api-
|
|
3
|
-
import { n as openBrowser, r as reportErrors } from "./ui-
|
|
1
|
+
import { _ as setActiveProfile, b as getBakedProfile, g as readConfigFile, h as listBakedProfiles, m as clearAllProfileTokens, r as siteConfig, v as upsertProfileAuth, x as isProfileId, y as writeConfigFile } from "./site-0i7wAJqg.mjs";
|
|
2
|
+
import { n as agentHeaders } from "./api-Do9c8tZ_.mjs";
|
|
3
|
+
import { n as openBrowser, r as reportErrors } from "./ui-CDUJq-X-.mjs";
|
|
4
4
|
import { n as clearRepoTokenCache } from "./repo-credential-CNDRM-tG.mjs";
|
|
5
5
|
import { defineCommand } from "citty";
|
|
6
6
|
import { hostname } from "node:os";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { a as apiRequest,
|
|
3
|
-
import { r as reportErrors } from "./ui-
|
|
1
|
+
import { l as resolveProject, s as projectFlagArgs } from "./site-0i7wAJqg.mjs";
|
|
2
|
+
import { a as apiRequest, f as withContext } from "./api-Do9c8tZ_.mjs";
|
|
3
|
+
import { r as reportErrors } from "./ui-CDUJq-X-.mjs";
|
|
4
4
|
import { defineCommand } from "citty";
|
|
5
5
|
//#region src/logs.ts
|
|
6
6
|
/**
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { t as version } from "./package-
|
|
2
|
-
import {
|
|
3
|
-
import { a as apiRequest, t as ApiError } from "./api-
|
|
1
|
+
import { t as version } from "./package-CVDpzGgA.mjs";
|
|
2
|
+
import { c as projectUrl, d as stagingUrl, p as tryResolveProject, r as siteConfig } from "./site-0i7wAJqg.mjs";
|
|
3
|
+
import { a as apiRequest, d as noticeRenamesOnly, l as currentSlug, t as ApiError } from "./api-Do9c8tZ_.mjs";
|
|
4
4
|
import { t as liveDevServer } from "./dev-server-file-Dk9pk4AR.mjs";
|
|
5
5
|
import { defineCommand } from "citty";
|
|
6
6
|
import { serveStdio } from "@modelcontextprotocol/server/stdio";
|
|
@@ -342,15 +342,16 @@ async function connect(args) {
|
|
|
342
342
|
requireSignIn();
|
|
343
343
|
const tokens = agentTokens(slug, channel);
|
|
344
344
|
await tokens.token();
|
|
345
|
+
const current = currentSlug(slug);
|
|
345
346
|
const upstream = await connectUpstream({
|
|
346
|
-
url: `${channel === "staging" ? stagingUrl(
|
|
347
|
-
project:
|
|
347
|
+
url: `${channel === "staging" ? stagingUrl(current) : projectUrl(current)}${MCP_PATH}`,
|
|
348
|
+
project: current,
|
|
348
349
|
tokens
|
|
349
350
|
});
|
|
350
351
|
const config = siteConfig();
|
|
351
352
|
return {
|
|
352
353
|
upstream,
|
|
353
|
-
slug,
|
|
354
|
+
slug: current,
|
|
354
355
|
channel,
|
|
355
356
|
where: `${config.profileId ?? config.apiUrl}, ${channel}`
|
|
356
357
|
};
|
|
@@ -375,6 +376,7 @@ const serveCommand = defineCommand({
|
|
|
375
376
|
async run({ args }) {
|
|
376
377
|
console.log = console.error;
|
|
377
378
|
console.info = console.error;
|
|
379
|
+
noticeRenamesOnly();
|
|
378
380
|
let connection;
|
|
379
381
|
try {
|
|
380
382
|
connection = await connect(args);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { r as apiDataRequest } from "./api-
|
|
1
|
+
import { f as tryReadCanopyJson } from "./site-0i7wAJqg.mjs";
|
|
2
|
+
import { r as apiDataRequest } from "./api-Do9c8tZ_.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
|
-
import {
|
|
2
|
-
import { n as openBrowser, r as reportErrors } from "./ui-
|
|
1
|
+
import { c as projectUrl, d as stagingUrl, l as resolveProject, o as projectDirArgs } from "./site-0i7wAJqg.mjs";
|
|
2
|
+
import { n as openBrowser, r as reportErrors } from "./ui-CDUJq-X-.mjs";
|
|
3
3
|
import { defineCommand } from "citty";
|
|
4
4
|
//#region src/open.ts
|
|
5
5
|
/**
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { a as apiRequest,
|
|
3
|
-
import { a as unwrap, i as requireTty, r as reportErrors } from "./ui-
|
|
1
|
+
import { l as resolveProject, o as projectDirArgs } from "./site-0i7wAJqg.mjs";
|
|
2
|
+
import { a as apiRequest, f as withContext } from "./api-Do9c8tZ_.mjs";
|
|
3
|
+
import { a as unwrap, i as requireTty, r as reportErrors } from "./ui-CDUJq-X-.mjs";
|
|
4
4
|
import { defineCommand } from "citty";
|
|
5
5
|
import * as p from "@clack/prompts";
|
|
6
6
|
/** Plain words for the API's template errors. */
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { a as apiRequest,
|
|
3
|
-
import { a as unwrap, i as requireTty, r as reportErrors } from "./ui-
|
|
4
|
-
import { i as nextSteps, n as describeCopy, r as fetchTemplate, t as TEMPLATE_FIXES } from "./project-template-
|
|
5
|
-
import { i as offerRepoOnCreate, n as cloneProjectRepo } from "./repo-
|
|
1
|
+
import { c as projectUrl, f as tryReadCanopyJson, r as siteConfig } from "./site-0i7wAJqg.mjs";
|
|
2
|
+
import { a as apiRequest, f as withContext } from "./api-Do9c8tZ_.mjs";
|
|
3
|
+
import { a as unwrap, i as requireTty, r as reportErrors } from "./ui-CDUJq-X-.mjs";
|
|
4
|
+
import { i as nextSteps, n as describeCopy, r as fetchTemplate, t as TEMPLATE_FIXES } from "./project-template-BXfHpsI8.mjs";
|
|
5
|
+
import { i as offerRepoOnCreate, n as cloneProjectRepo } from "./repo-LSOxnmUk.mjs";
|
|
6
6
|
import { defineCommand } from "citty";
|
|
7
7
|
import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from "node:fs";
|
|
8
8
|
import { join, relative, resolve, sep } from "node:path";
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { c as projectUrl, l as resolveProject, o as projectDirArgs, u as rewriteCanopyJsonSlug } from "./site-0i7wAJqg.mjs";
|
|
2
|
+
import { a as apiRequest, f as withContext } from "./api-Do9c8tZ_.mjs";
|
|
3
|
+
import { a as unwrap, i as requireTty, r as reportErrors } from "./ui-CDUJq-X-.mjs";
|
|
4
|
+
import { defineCommand } from "citty";
|
|
5
|
+
import * as p from "@clack/prompts";
|
|
6
|
+
//#region src/rename.ts
|
|
7
|
+
/**
|
|
8
|
+
* canopy rename <new-slug> — change a project's slug, its address on the
|
|
9
|
+
* site (OpenSpec project-slug-rename). The old slug stays the project's: its
|
|
10
|
+
* hostnames redirect, and everything that still names it (canopy.json
|
|
11
|
+
* elsewhere, CI, MCP configs, git remotes, report keys) keeps reaching the
|
|
12
|
+
* project. The canopy.json in this directory is pointed at the new slug.
|
|
13
|
+
*/
|
|
14
|
+
/** What changes for people outside the platform — said before anything happens. */
|
|
15
|
+
function renameConsequences(from, to) {
|
|
16
|
+
return [
|
|
17
|
+
`The address becomes ${projectUrl(to)}; ${projectUrl(from)} redirects there.`,
|
|
18
|
+
`Email your project sends comes from ${to}@ instead of ${from}@.`,
|
|
19
|
+
"Visitors who signed in with the client password sign in again on the new address.",
|
|
20
|
+
"Visitors' browser storage for the project (localStorage, IndexedDB) starts empty on the new address.",
|
|
21
|
+
`Scripts, CI, MCP client configs and git remotes that say "${from}" keep working; update them when convenient.`
|
|
22
|
+
];
|
|
23
|
+
}
|
|
24
|
+
const renameCommand = defineCommand({
|
|
25
|
+
meta: {
|
|
26
|
+
name: "rename",
|
|
27
|
+
description: "Change a project's slug (its address)"
|
|
28
|
+
},
|
|
29
|
+
args: {
|
|
30
|
+
slug: {
|
|
31
|
+
type: "positional",
|
|
32
|
+
description: "The new slug",
|
|
33
|
+
required: true
|
|
34
|
+
},
|
|
35
|
+
...projectDirArgs,
|
|
36
|
+
yes: {
|
|
37
|
+
type: "boolean",
|
|
38
|
+
default: false,
|
|
39
|
+
description: "Skip the confirmation"
|
|
40
|
+
},
|
|
41
|
+
json: {
|
|
42
|
+
type: "boolean",
|
|
43
|
+
default: false,
|
|
44
|
+
description: "Print the result as JSON"
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
run: reportErrors("rename", async ({ args }) => {
|
|
48
|
+
const { dir, slug: from } = resolveProject(args);
|
|
49
|
+
const to = String(args.slug).trim().toLowerCase();
|
|
50
|
+
if (!args.yes) {
|
|
51
|
+
requireTty("pass --yes to rename without confirmation.");
|
|
52
|
+
p.note(renameConsequences(from, to).join("\n"), `Rename ${from} → ${to}`);
|
|
53
|
+
if (!unwrap(await p.confirm({ message: `Rename "${from}" to "${to}"?` }))) {
|
|
54
|
+
p.cancel("Aborted.");
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
const result = await withContext(apiRequest("POST", `/projects/${from}/rename`, { slug: to }), {
|
|
59
|
+
action: `rename "${from}"`,
|
|
60
|
+
fixes: {
|
|
61
|
+
forbidden: "Only the project's owner or a Canopy admin can rename it — ask the owner.",
|
|
62
|
+
invalid_slug: "Use 2–40 lowercase letters, digits and dashes, starting with a letter — not a name Canopy reserves (www, docs, api, …).",
|
|
63
|
+
slug_taken: "Pick another slug: this one belongs to another project, now or in the past."
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
const updated = rewriteCanopyJsonSlug(dir, from, result.project.slug);
|
|
67
|
+
if (args.json) {
|
|
68
|
+
console.log(JSON.stringify({
|
|
69
|
+
...result,
|
|
70
|
+
canopy_json_updated: updated
|
|
71
|
+
}, null, 2));
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
p.log.success(`Renamed to ${result.project.slug}: ${projectUrl(result.project.slug)}`);
|
|
75
|
+
if (updated) p.log.info("Updated canopy.json.");
|
|
76
|
+
if (result.refresh.pending) p.log.warn("The backend still reports the old slug as PROJECT_SLUG (it keeps working). Canopy retries; `canopy deploy` or Settings → Retry refreshes it now.");
|
|
77
|
+
if (result.slack === "no_config_token" || result.slack === "rejected") p.log.info("The project's Slack app keeps its old URLs, which still work. They move on its next manifest update.");
|
|
78
|
+
})
|
|
79
|
+
});
|
|
80
|
+
//#endregion
|
|
81
|
+
export { renameCommand };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { a as apiRequest,
|
|
3
|
-
import { a as unwrap, i as requireTty, r as reportErrors, t as formatDateTime } from "./ui-
|
|
1
|
+
import { f as tryReadCanopyJson, l as resolveProject, o as projectDirArgs, r as siteConfig } from "./site-0i7wAJqg.mjs";
|
|
2
|
+
import { a as apiRequest, d as noticeRenamesOnly, f as withContext, n as agentHeaders, u as followRename } from "./api-Do9c8tZ_.mjs";
|
|
3
|
+
import { a as unwrap, i as requireTty, r as reportErrors, t as formatDateTime } from "./ui-CDUJq-X-.mjs";
|
|
4
4
|
import { a as parseCredentialInput, i as helperCommand, o as runCredentialHelper, r as fileTokenCache, t as RepoCredentialError } from "./repo-credential-CNDRM-tG.mjs";
|
|
5
5
|
import { defineCommand } from "citty";
|
|
6
6
|
import { existsSync, realpathSync } from "node:fs";
|
|
@@ -328,6 +328,8 @@ async function mintRepoToken(profile, slug) {
|
|
|
328
328
|
}
|
|
329
329
|
}).catch(() => null);
|
|
330
330
|
if (!res) throw new RepoCredentialError(`could not reach Canopy (${config.apiUrl}).`);
|
|
331
|
+
noticeRenamesOnly();
|
|
332
|
+
followRename(res, slug);
|
|
331
333
|
if (res.status === 201) return await res.json();
|
|
332
334
|
const body = await res.json().catch(() => ({}));
|
|
333
335
|
if (res.status === 401) throw new RepoCredentialError(`your ${profile} sign-in has expired — ${login}.`);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { a as apiRequest,
|
|
3
|
-
import { r as reportErrors, t as formatDateTime } from "./ui-
|
|
1
|
+
import { l as resolveProject, s as projectFlagArgs } from "./site-0i7wAJqg.mjs";
|
|
2
|
+
import { a as apiRequest, f as withContext } from "./api-Do9c8tZ_.mjs";
|
|
3
|
+
import { r as reportErrors, t as formatDateTime } from "./ui-CDUJq-X-.mjs";
|
|
4
4
|
import { defineCommand } from "citty";
|
|
5
5
|
//#region src/schedule.ts
|
|
6
6
|
/**
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { a as apiRequest,
|
|
3
|
-
import { a as unwrap, r as reportErrors, t as formatDateTime } from "./ui-
|
|
1
|
+
import { l as resolveProject, o as projectDirArgs } from "./site-0i7wAJqg.mjs";
|
|
2
|
+
import { a as apiRequest, f as withContext } from "./api-Do9c8tZ_.mjs";
|
|
3
|
+
import { a as unwrap, r as reportErrors, t as formatDateTime } from "./ui-CDUJq-X-.mjs";
|
|
4
4
|
import { defineCommand } from "citty";
|
|
5
5
|
import * as p from "@clack/prompts";
|
|
6
6
|
//#region src/secrets.ts
|
|
@@ -198,6 +198,25 @@ function projectDialect(dir) {
|
|
|
198
198
|
if (dialect !== "sqlite" && dialect !== "postgres") throw new Error(`canopy.json "dialect" must be "sqlite" or "postgres", not ${JSON.stringify(dialect)}.`);
|
|
199
199
|
return dialect;
|
|
200
200
|
}
|
|
201
|
+
/**
|
|
202
|
+
* Point canopy.json at a project's new slug after a rename (OpenSpec
|
|
203
|
+
* project-slug-rename) — only when it still names `from`, and changing
|
|
204
|
+
* nothing else in the file. True when it was rewritten.
|
|
205
|
+
*/
|
|
206
|
+
function rewriteCanopyJsonSlug(dir, from, to) {
|
|
207
|
+
let config;
|
|
208
|
+
try {
|
|
209
|
+
config = tryReadCanopyJson(dir);
|
|
210
|
+
} catch {
|
|
211
|
+
return false;
|
|
212
|
+
}
|
|
213
|
+
if (config?.slug !== from) return false;
|
|
214
|
+
writeFileSync(join(dir, "canopy.json"), `${JSON.stringify({
|
|
215
|
+
...config,
|
|
216
|
+
slug: to
|
|
217
|
+
}, null, 2)}\n`);
|
|
218
|
+
return true;
|
|
219
|
+
}
|
|
201
220
|
function readCanopyJson(dir) {
|
|
202
221
|
const config = tryReadCanopyJson(dir);
|
|
203
222
|
if (config === void 0) throw new Error(`No canopy.json in ${dir} — run from a project created by \`canopy create\`, add one ({"slug": "<slug>", "dist": "dist"}), or pass --project <slug>.`);
|
|
@@ -255,6 +274,10 @@ function setInvocationProfile(profile) {
|
|
|
255
274
|
function setProjectDirForPin(dir) {
|
|
256
275
|
projectDirForPin = dir;
|
|
257
276
|
}
|
|
277
|
+
/** The project directory this invocation resolved (cwd when none was named). */
|
|
278
|
+
function projectDir() {
|
|
279
|
+
return projectDirForPin ?? ".";
|
|
280
|
+
}
|
|
258
281
|
function projectProfileFromCwd() {
|
|
259
282
|
return tryReadCanopyJson(projectDirForPin ?? ".")?.profile;
|
|
260
283
|
}
|
|
@@ -277,4 +300,4 @@ function siteHeaderLine(cliVersion) {
|
|
|
277
300
|
return `canopy ${cliVersion} · ${c.envOverride || !c.profileId ? c.apiUrl : `${c.profileId} (${c.projectDomain ?? new URL(c.apiUrl).hostname})`}`;
|
|
278
301
|
}
|
|
279
302
|
//#endregion
|
|
280
|
-
export {
|
|
303
|
+
export { setActiveProfile as _, projectDialect as a, getBakedProfile as b, projectUrl as c, stagingUrl as d, tryReadCanopyJson as f, readConfigFile as g, listBakedProfiles as h, siteHeaderLine as i, resolveProject as l, clearAllProfileTokens as m, setInvocationProfile as n, projectDirArgs as o, tryResolveProject as p, siteConfig as r, projectFlagArgs as s, projectDir as t, rewriteCanopyJsonSlug as u, upsertProfileAuth as v, isProfileId as x, writeConfigFile as y };
|
|
@@ -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-CVDpzGgA.mjs";
|
|
2
|
+
import { r as reportErrors } from "./ui-CDUJq-X-.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";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { c as
|
|
2
|
-
import { a as apiRequest,
|
|
3
|
-
import { a as unwrap, i as requireTty, r as reportErrors, t as formatDateTime } from "./ui-
|
|
1
|
+
import { c as projectUrl, d as stagingUrl, l as resolveProject, s as projectFlagArgs } from "./site-0i7wAJqg.mjs";
|
|
2
|
+
import { a as apiRequest, f as withContext } from "./api-Do9c8tZ_.mjs";
|
|
3
|
+
import { a as unwrap, i as requireTty, r as reportErrors, t as formatDateTime } from "./ui-CDUJq-X-.mjs";
|
|
4
4
|
import { defineCommand } from "citty";
|
|
5
5
|
import * as p from "@clack/prompts";
|
|
6
6
|
//#region src/staging.ts
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { a as apiRequest } from "./api-
|
|
3
|
-
import { r as reportErrors, t as formatDateTime } from "./ui-
|
|
1
|
+
import { l as resolveProject, o as projectDirArgs, s as projectFlagArgs } from "./site-0i7wAJqg.mjs";
|
|
2
|
+
import { a as apiRequest } from "./api-Do9c8tZ_.mjs";
|
|
3
|
+
import { r as reportErrors, t as formatDateTime } from "./ui-CDUJq-X-.mjs";
|
|
4
4
|
import { defineCommand } from "citty";
|
|
5
5
|
const tokenCommand = defineCommand({
|
|
6
6
|
meta: {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { t as PACKAGE_NAME } from "./version-check-CcsA1QjI.mjs";
|
|
2
|
-
import { r as reportErrors } from "./ui-
|
|
2
|
+
import { r as reportErrors } from "./ui-CDUJq-X-.mjs";
|
|
3
3
|
import { defineCommand } from "citty";
|
|
4
4
|
import { spawn } from "node:child_process";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
@@ -112,7 +112,7 @@ const upgradeCommand = defineCommand({
|
|
|
112
112
|
const spec = `${PACKAGE_NAME}@${args.version ?? "latest"}`;
|
|
113
113
|
const command = pmCommand(pm);
|
|
114
114
|
const cmd = installArgs(pm, spec);
|
|
115
|
-
const { skillInstalled } = await import("./skill-
|
|
115
|
+
const { skillInstalled } = await import("./skill-D58yV6K8.mjs");
|
|
116
116
|
const { homedir } = await import("node:os");
|
|
117
117
|
const refreshGlobal = skillInstalled(homedir());
|
|
118
118
|
const refreshRepo = process.cwd() !== homedir() && skillInstalled(process.cwd());
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { a as apiRequest,
|
|
3
|
-
import { r as reportErrors } from "./ui-
|
|
1
|
+
import { l as resolveProject, s as projectFlagArgs } from "./site-0i7wAJqg.mjs";
|
|
2
|
+
import { a as apiRequest, f as withContext } from "./api-Do9c8tZ_.mjs";
|
|
3
|
+
import { r as reportErrors } from "./ui-CDUJq-X-.mjs";
|
|
4
4
|
import { defineCommand } from "citty";
|
|
5
5
|
//#region src/usage.ts
|
|
6
6
|
/**
|
package/package.json
CHANGED
package/dist/dev-D38FQ2LD.mjs
DELETED
package/dist/repo-Un_p617X.mjs
DELETED