@minamocms/framework 0.0.0-stage → 0.10.2
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/CHANGELOG.md +240 -0
- package/LICENSE +105 -0
- package/README.md +133 -2
- package/dist/boot.d.ts +19 -0
- package/dist/boot.js +22 -0
- package/dist/config.d.ts +18 -0
- package/dist/config.js +44 -0
- package/dist/create-site/cli.d.ts +2 -0
- package/dist/create-site/cli.js +31 -0
- package/dist/create-site/generate-site.d.ts +12 -0
- package/dist/create-site/generate-site.js +168 -0
- package/dist/create-site/mint-token-cli.d.ts +2 -0
- package/dist/create-site/mint-token-cli.js +26 -0
- package/dist/create-site/mint-token.d.ts +7 -0
- package/dist/create-site/mint-token.js +49 -0
- package/dist/create-site/template/AGENTS.md +473 -0
- package/dist/create-site/template/content/menus/footerCompany.json +9 -0
- package/dist/create-site/template/content/menus/footerProduct.json +9 -0
- package/dist/create-site/template/content/menus/footerResources.json +9 -0
- package/dist/create-site/template/content/menus/main.json +9 -0
- package/dist/create-site/template/content/pages/404.json +23 -0
- package/dist/create-site/template/content/pages/about/careers.json +22 -0
- package/dist/create-site/template/content/pages/about/team.json +22 -0
- package/dist/create-site/template/content/pages/about.json +22 -0
- package/dist/create-site/template/content/pages/docs/deployment.json +22 -0
- package/dist/create-site/template/content/pages/docs/getting-started/quickstart.json +22 -0
- package/dist/create-site/template/content/pages/docs/getting-started.json +24 -0
- package/dist/create-site/template/content/pages/docs.json +22 -0
- package/dist/create-site/template/content/pages/index.json +182 -0
- package/dist/create-site/template/content/redirects.json +4 -0
- package/dist/create-site/template/dockerignore +6 -0
- package/dist/create-site/template/gitignore +11 -0
- package/dist/create-site/template/theme/assets/site.js +49 -0
- package/dist/create-site/template/theme/assets/style.css +1223 -0
- package/dist/create-site/template/theme/blocks/button.liquid +21 -0
- package/dist/create-site/template/theme/blocks/comparison-row.liquid +19 -0
- package/dist/create-site/template/theme/blocks/faq-item.liquid +22 -0
- package/dist/create-site/template/theme/blocks/feature-card.liquid +23 -0
- package/dist/create-site/template/theme/blocks/logo-mark.liquid +13 -0
- package/dist/create-site/template/theme/blocks/pricing-tier.liquid +42 -0
- package/dist/create-site/template/theme/blocks/step-card.liquid +19 -0
- package/dist/create-site/template/theme/blocks/testimonial-card.liquid +25 -0
- package/dist/create-site/template/theme/config/site_settings.json +11 -0
- package/dist/create-site/template/theme/layouts/theme.liquid +102 -0
- package/dist/create-site/template/theme/sections/comparison.liquid +41 -0
- package/dist/create-site/template/theme/sections/cta-banner.liquid +36 -0
- package/dist/create-site/template/theme/sections/faq.liquid +27 -0
- package/dist/create-site/template/theme/sections/feature-grid.liquid +27 -0
- package/dist/create-site/template/theme/sections/hero.liquid +48 -0
- package/dist/create-site/template/theme/sections/how-it-works.liquid +27 -0
- package/dist/create-site/template/theme/sections/pricing.liquid +27 -0
- package/dist/create-site/template/theme/sections/social-proof.liquid +21 -0
- package/dist/create-site/template/theme/sections/testimonials.liquid +27 -0
- package/dist/create-site/template/theme/snippets/icon.liquid +24 -0
- package/dist/create-site/template/theme/snippets/logo.liquid +7 -0
- package/dist/create-site/template/theme/snippets/responsive-media.liquid +107 -0
- package/dist/create-site/template/theme/snippets/site-name.liquid +1 -0
- package/dist/create-site/template/vhost/Dockerfile +33 -0
- package/dist/create-site/template/vhost/docker-entrypoint.sh +58 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +4 -0
- package/dist/media/drivers/driver.d.ts +11 -0
- package/dist/media/drivers/driver.js +1 -0
- package/dist/media/drivers/local-fs-driver.d.ts +3 -0
- package/dist/media/drivers/local-fs-driver.js +94 -0
- package/dist/media/filename.d.ts +2 -0
- package/dist/media/filename.js +54 -0
- package/dist/media/manage-media.d.ts +19 -0
- package/dist/media/manage-media.js +45 -0
- package/dist/media/seed-media-cli.d.ts +2 -0
- package/dist/media/seed-media-cli.js +22 -0
- package/dist/media/seed-media.d.ts +11 -0
- package/dist/media/seed-media.js +76 -0
- package/dist/migrations/index.d.ts +3 -0
- package/dist/migrations/index.js +83 -0
- package/dist/pull-site/cli.d.ts +2 -0
- package/dist/pull-site/cli.js +78 -0
- package/dist/pull-site/prompt-token.d.ts +16 -0
- package/dist/pull-site/prompt-token.js +69 -0
- package/dist/pull-site/prompts.d.ts +22 -0
- package/dist/pull-site/prompts.js +127 -0
- package/dist/pull-site/pull-site.d.ts +23 -0
- package/dist/pull-site/pull-site.js +211 -0
- package/dist/renderer/engine.d.ts +2 -0
- package/dist/renderer/engine.js +48 -0
- package/dist/renderer/render-cache.d.ts +11 -0
- package/dist/renderer/render-cache.js +11 -0
- package/dist/renderer/render-page.d.ts +34 -0
- package/dist/renderer/render-page.js +209 -0
- package/dist/renderer/theme-templates.d.ts +7 -0
- package/dist/renderer/theme-templates.js +82 -0
- package/dist/routes/admin-redirect.d.ts +5 -0
- package/dist/routes/admin-redirect.js +26 -0
- package/dist/routes/assets.d.ts +6 -0
- package/dist/routes/assets.js +30 -0
- package/dist/routes/batch.d.ts +10 -0
- package/dist/routes/batch.js +148 -0
- package/dist/routes/capabilities.d.ts +6 -0
- package/dist/routes/capabilities.js +27 -0
- package/dist/routes/content.d.ts +8 -0
- package/dist/routes/content.js +169 -0
- package/dist/routes/deleted-pages.d.ts +8 -0
- package/dist/routes/deleted-pages.js +9 -0
- package/dist/routes/drafts.d.ts +10 -0
- package/dist/routes/drafts.js +73 -0
- package/dist/routes/git.d.ts +8 -0
- package/dist/routes/git.js +158 -0
- package/dist/routes/index.d.ts +12 -0
- package/dist/routes/index.js +86 -0
- package/dist/routes/links.d.ts +8 -0
- package/dist/routes/links.js +18 -0
- package/dist/routes/media-public.d.ts +11 -0
- package/dist/routes/media-public.js +125 -0
- package/dist/routes/media.d.ts +9 -0
- package/dist/routes/media.js +80 -0
- package/dist/routes/menus.d.ts +8 -0
- package/dist/routes/menus.js +148 -0
- package/dist/routes/preview-revision.d.ts +10 -0
- package/dist/routes/preview-revision.js +96 -0
- package/dist/routes/preview.d.ts +10 -0
- package/dist/routes/preview.js +56 -0
- package/dist/routes/public.d.ts +10 -0
- package/dist/routes/public.js +123 -0
- package/dist/routes/publish.d.ts +10 -0
- package/dist/routes/publish.js +139 -0
- package/dist/routes/redirects.d.ts +8 -0
- package/dist/routes/redirects.js +132 -0
- package/dist/routes/search-public.d.ts +6 -0
- package/dist/routes/search-public.js +104 -0
- package/dist/routes/search.d.ts +8 -0
- package/dist/routes/search.js +15 -0
- package/dist/routes/settings.d.ts +10 -0
- package/dist/routes/settings.js +68 -0
- package/dist/routes/sitemap.d.ts +7 -0
- package/dist/routes/sitemap.js +79 -0
- package/dist/routes/theme.d.ts +10 -0
- package/dist/routes/theme.js +116 -0
- package/dist/schemas/instance.schema.json +17 -0
- package/dist/schemas/menu.schema.json +24 -0
- package/dist/schemas/page.schema.json +26 -0
- package/dist/schemas/redirects.schema.json +24 -0
- package/dist/search/drivers/driver.d.ts +9 -0
- package/dist/search/drivers/driver.js +1 -0
- package/dist/search/drivers/node-sqlite-driver.d.ts +7 -0
- package/dist/search/drivers/node-sqlite-driver.js +19 -0
- package/dist/search/query-content.d.ts +32 -0
- package/dist/search/query-content.js +207 -0
- package/dist/search/query-index.d.ts +5 -0
- package/dist/search/query-index.js +21 -0
- package/dist/search/rebuild-index.d.ts +3 -0
- package/dist/search/rebuild-index.js +320 -0
- package/dist/server-config.d.ts +23 -0
- package/dist/server-config.js +237 -0
- package/dist/server.d.ts +13 -0
- package/dist/server.js +235 -0
- package/dist/services/batch.d.ts +35 -0
- package/dist/services/batch.js +145 -0
- package/dist/services/checkpoint.d.ts +4 -0
- package/dist/services/checkpoint.js +45 -0
- package/dist/services/content-read.d.ts +27 -0
- package/dist/services/content-read.js +151 -0
- package/dist/services/delete-content.d.ts +12 -0
- package/dist/services/delete-content.js +166 -0
- package/dist/services/deleted-pages.d.ts +11 -0
- package/dist/services/deleted-pages.js +83 -0
- package/dist/services/dev-tunnel.d.ts +5 -0
- package/dist/services/dev-tunnel.js +5 -0
- package/dist/services/drafts.d.ts +16 -0
- package/dist/services/drafts.js +164 -0
- package/dist/services/etag.d.ts +2 -0
- package/dist/services/etag.js +30 -0
- package/dist/services/fs-walk.d.ts +1 -0
- package/dist/services/fs-walk.js +26 -0
- package/dist/services/git-commit.d.ts +3 -0
- package/dist/services/git-commit.js +12 -0
- package/dist/services/git-history.d.ts +28 -0
- package/dist/services/git-history.js +131 -0
- package/dist/services/git-revert.d.ts +12 -0
- package/dist/services/git-revert.js +140 -0
- package/dist/services/git.d.ts +16 -0
- package/dist/services/git.js +127 -0
- package/dist/services/interval-job.d.ts +4 -0
- package/dist/services/interval-job.js +9 -0
- package/dist/services/ip-allowlist.d.ts +7 -0
- package/dist/services/ip-allowlist.js +31 -0
- package/dist/services/links.d.ts +22 -0
- package/dist/services/links.js +169 -0
- package/dist/services/manage-menus.d.ts +15 -0
- package/dist/services/manage-menus.js +138 -0
- package/dist/services/manage-redirects.d.ts +25 -0
- package/dist/services/manage-redirects.js +156 -0
- package/dist/services/menu-references.d.ts +3 -0
- package/dist/services/menu-references.js +38 -0
- package/dist/services/menus.d.ts +9 -0
- package/dist/services/menus.js +43 -0
- package/dist/services/migration-runner.d.ts +16 -0
- package/dist/services/migration-runner.js +169 -0
- package/dist/services/mime-types.d.ts +3 -0
- package/dist/services/mime-types.js +43 -0
- package/dist/services/move.d.ts +16 -0
- package/dist/services/move.js +236 -0
- package/dist/services/path-safety.d.ts +6 -0
- package/dist/services/path-safety.js +112 -0
- package/dist/services/pid-file.d.ts +13 -0
- package/dist/services/pid-file.js +54 -0
- package/dist/services/post-urls.d.ts +3 -0
- package/dist/services/post-urls.js +27 -0
- package/dist/services/prepared-operation.d.ts +4 -0
- package/dist/services/prepared-operation.js +1 -0
- package/dist/services/publish.d.ts +15 -0
- package/dist/services/publish.js +313 -0
- package/dist/services/rate-limit-config.d.ts +9 -0
- package/dist/services/rate-limit-config.js +22 -0
- package/dist/services/redirects.d.ts +27 -0
- package/dist/services/redirects.js +175 -0
- package/dist/services/reindex-on-write.d.ts +3 -0
- package/dist/services/reindex-on-write.js +30 -0
- package/dist/services/resolve-blog-url.d.ts +11 -0
- package/dist/services/resolve-blog-url.js +31 -0
- package/dist/services/resolve-url.d.ts +11 -0
- package/dist/services/resolve-url.js +20 -0
- package/dist/services/site-settings.d.ts +28 -0
- package/dist/services/site-settings.js +116 -0
- package/dist/services/startup-checks.d.ts +10 -0
- package/dist/services/startup-checks.js +81 -0
- package/dist/services/static-file.d.ts +7 -0
- package/dist/services/static-file.js +37 -0
- package/dist/services/theme-component-file.d.ts +6 -0
- package/dist/services/theme-component-file.js +39 -0
- package/dist/services/theme-files.d.ts +33 -0
- package/dist/services/theme-files.js +148 -0
- package/dist/services/theme-page-templates.d.ts +7 -0
- package/dist/services/theme-page-templates.js +53 -0
- package/dist/services/theme-schemas.d.ts +2 -0
- package/dist/services/theme-schemas.js +137 -0
- package/dist/services/token-auth.d.ts +10 -0
- package/dist/services/token-auth.js +64 -0
- package/dist/services/urls.d.ts +2 -0
- package/dist/services/urls.js +20 -0
- package/dist/services/validation.d.ts +26 -0
- package/dist/services/validation.js +195 -0
- package/dist/services/write-queue.d.ts +1 -0
- package/dist/services/write-queue.js +14 -0
- package/dist/site-check/cli.d.ts +2 -0
- package/dist/site-check/cli.js +48 -0
- package/dist/site-check/run-check.d.ts +12 -0
- package/dist/site-check/run-check.js +188 -0
- package/dist/site-sync/cli-helpers.d.ts +23 -0
- package/dist/site-sync/cli-helpers.js +75 -0
- package/dist/site-sync/deploy.d.ts +36 -0
- package/dist/site-sync/deploy.js +124 -0
- package/dist/site-sync/live-content.d.ts +30 -0
- package/dist/site-sync/live-content.js +68 -0
- package/dist/site-sync/prompts.d.ts +30 -0
- package/dist/site-sync/prompts.js +237 -0
- package/dist/site-sync/pull-cli.d.ts +2 -0
- package/dist/site-sync/pull-cli.js +75 -0
- package/dist/site-sync/pull-site.d.ts +28 -0
- package/dist/site-sync/pull-site.js +171 -0
- package/dist/site-sync/push-cli.d.ts +2 -0
- package/dist/site-sync/push-cli.js +268 -0
- package/dist/site-sync/push-site.d.ts +96 -0
- package/dist/site-sync/push-site.js +376 -0
- package/dist/site-sync/remote-site.d.ts +38 -0
- package/dist/site-sync/remote-site.js +122 -0
- package/dist/site-sync/sync-record.d.ts +26 -0
- package/dist/site-sync/sync-record.js +76 -0
- package/dist/site-sync/theme-sync.d.ts +34 -0
- package/dist/site-sync/theme-sync.js +128 -0
- package/dist/stop-site/cli.d.ts +2 -0
- package/dist/stop-site/cli.js +28 -0
- package/dist/stop-site/stop-site.d.ts +26 -0
- package/dist/stop-site/stop-site.js +56 -0
- package/dist/theme-state.d.ts +20 -0
- package/dist/theme-state.js +33 -0
- package/dist/upgrade/changelog.d.ts +8 -0
- package/dist/upgrade/changelog.js +46 -0
- package/dist/upgrade/finish.d.ts +9 -0
- package/dist/upgrade/finish.js +30 -0
- package/dist/upgrade/owned-files.d.ts +11 -0
- package/dist/upgrade/owned-files.js +42 -0
- package/dist/upgrade/package-names.d.ts +4 -0
- package/dist/upgrade/package-names.js +26 -0
- package/dist/upgrade/upgrade-cli.d.ts +2 -0
- package/dist/upgrade/upgrade-cli.js +170 -0
- package/package.json +64 -4
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { resolve } from 'node:path';
|
|
3
|
+
import { ScaffoldError, scaffoldSite } from "./generate-site.js";
|
|
4
|
+
const targetArg = process.argv[2];
|
|
5
|
+
if (!targetArg) {
|
|
6
|
+
console.error('Usage: create-site <directory>');
|
|
7
|
+
process.exit(1);
|
|
8
|
+
}
|
|
9
|
+
try {
|
|
10
|
+
const targetDir = resolve(process.cwd(), targetArg);
|
|
11
|
+
const { raw } = scaffoldSite(targetDir);
|
|
12
|
+
console.log(`Created a new site at ${targetDir}`);
|
|
13
|
+
console.log('');
|
|
14
|
+
console.log('Your access token (save this now - it will not be shown again):');
|
|
15
|
+
console.log(` ${raw}`);
|
|
16
|
+
console.log('');
|
|
17
|
+
console.log('Next steps:');
|
|
18
|
+
console.log(` cd ${targetArg}/vhost`);
|
|
19
|
+
console.log(' npm install');
|
|
20
|
+
console.log(' npm start');
|
|
21
|
+
console.log('');
|
|
22
|
+
console.log('Want to edit this site from a hosted admin while developing locally?');
|
|
23
|
+
console.log(' npm run tunnel');
|
|
24
|
+
}
|
|
25
|
+
catch (error) {
|
|
26
|
+
if (error instanceof ScaffoldError) {
|
|
27
|
+
console.error(error.message);
|
|
28
|
+
process.exit(1);
|
|
29
|
+
}
|
|
30
|
+
throw error;
|
|
31
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export declare const TEMPLATE_ROOT: string;
|
|
2
|
+
export declare function generateToken(): {
|
|
3
|
+
raw: string;
|
|
4
|
+
hash: string;
|
|
5
|
+
};
|
|
6
|
+
export declare const SERVER_JS = "import { startServer } from '@minamocms/framework';\nimport { join } from 'node:path';\n\nawait startServer(join(import.meta.dirname, '..'), { tunnel: process.argv.includes('--tunnel') });\n";
|
|
7
|
+
export declare const SCAFFOLD_SCRIPTS: Record<string, string>;
|
|
8
|
+
export declare class ScaffoldError extends Error {
|
|
9
|
+
}
|
|
10
|
+
export declare function scaffoldSite(targetDir: string): {
|
|
11
|
+
raw: string;
|
|
12
|
+
};
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import { createHash, randomBytes } from 'node:crypto';
|
|
2
|
+
import { cpSync, existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import { basename, join } from 'node:path';
|
|
4
|
+
import { execFileSync } from 'node:child_process';
|
|
5
|
+
import { CHECKPOINT_AUTHOR } from "../services/checkpoint.js";
|
|
6
|
+
import { commitPaths } from "../services/git.js";
|
|
7
|
+
import { PACKAGE_NAME } from "../upgrade/package-names.js";
|
|
8
|
+
// This file's own bundled template (dist/create-site/template, copied
|
|
9
|
+
// there by `npm run build`'s cp step) - the agent's own asset, not
|
|
10
|
+
// site data, matching validation.ts's/capabilities.ts's existing
|
|
11
|
+
// import.meta.dirname-relative pattern for the agent's own bundled
|
|
12
|
+
// files (see test/static/static-analysis.test.ts's B1 allowlist).
|
|
13
|
+
export const TEMPLATE_ROOT = join(import.meta.dirname, 'template');
|
|
14
|
+
// The agent's own bundled package.json, reused for the exact version
|
|
15
|
+
// to pin the scaffold's dependency to - never a second hardcoded
|
|
16
|
+
// literal, same reasoning as capabilities.ts's readAgentVersion.
|
|
17
|
+
function readAgentVersion() {
|
|
18
|
+
const packageJsonPath = join(import.meta.dirname, '..', '..', 'package.json');
|
|
19
|
+
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
|
|
20
|
+
return packageJson.version ?? '0.0.0';
|
|
21
|
+
}
|
|
22
|
+
function sanitisePackageName(dirName) {
|
|
23
|
+
const cleaned = dirName.toLowerCase().replace(/[^a-z0-9-]+/g, '-').replace(/^-+|-+$/g, '');
|
|
24
|
+
return cleaned.length > 0 ? cleaned : 'my-site';
|
|
25
|
+
}
|
|
26
|
+
export function generateToken() {
|
|
27
|
+
const raw = randomBytes(32).toString('hex');
|
|
28
|
+
const hash = createHash('sha256').update(raw).digest('hex');
|
|
29
|
+
return { raw, hash };
|
|
30
|
+
}
|
|
31
|
+
// server.js lives in vhost/, one level below the real site root - it
|
|
32
|
+
// states its own fixed, known relationship to its sibling directory
|
|
33
|
+
// rather than the agent inferring anything (constraint 2 stays intact:
|
|
34
|
+
// the site root is always explicit, never assumed from the agent's own
|
|
35
|
+
// location).
|
|
36
|
+
export const SERVER_JS = `import { startServer } from '${PACKAGE_NAME}';
|
|
37
|
+
import { join } from 'node:path';
|
|
38
|
+
|
|
39
|
+
await startServer(join(import.meta.dirname, '..'), { tunnel: process.argv.includes('--tunnel') });
|
|
40
|
+
`;
|
|
41
|
+
// The scripts every site's vhost/package.json gets. The CMS owns these:
|
|
42
|
+
// npm run upgrade puts them back as the new version defines them.
|
|
43
|
+
// "start" matters beyond convenience: several PaaS platforms
|
|
44
|
+
// auto-detect and run `npm start` by default for a Node app
|
|
45
|
+
// with no other deploy config - without this, a site pushed
|
|
46
|
+
// straight to one of those (no Dockerfile in the loop) simply
|
|
47
|
+
// wouldn't boot. "tunnel" mirrors the --tunnel flag create-site
|
|
48
|
+
// already tells the operator about in its own next-steps output.
|
|
49
|
+
// "dev" is Node's own --watch-path, not custom fs-watching code
|
|
50
|
+
// - confirmed empirically that it both picks up changes to a
|
|
51
|
+
// plain file under a watched directory that's never imported
|
|
52
|
+
// (theme/*.liquid is only ever read via readFileSync) and sends
|
|
53
|
+
// a real SIGTERM on restart, which server.js's own shutdown
|
|
54
|
+
// handling already listens for. "../theme" because npm run dev
|
|
55
|
+
// runs with cwd vhost/, a sibling of theme/. Deliberately not
|
|
56
|
+
// content/ - that's already read fresh on every request, so
|
|
57
|
+
// watching it would only cause pointless restarts on every
|
|
58
|
+
// content edit. "check" is check-site, the package's own
|
|
59
|
+
// installed CLI bin - runs from vhost/ (site-check/cli.ts's own
|
|
60
|
+
// assumed cwd relationship), same pattern as start/tunnel/dev.
|
|
61
|
+
export const SCAFFOLD_SCRIPTS = {
|
|
62
|
+
start: 'node server.js',
|
|
63
|
+
tunnel: 'node server.js --tunnel',
|
|
64
|
+
dev: 'node --watch-path=../theme server.js',
|
|
65
|
+
check: 'check-site',
|
|
66
|
+
// seed-media is the package's own installed CLI bin, exposed
|
|
67
|
+
// as a script for exactly the same reason check is: there is
|
|
68
|
+
// no package.json or node_modules at the SITE root, only here
|
|
69
|
+
// in vhost/, so the obvious-looking `npx seed-media` run from
|
|
70
|
+
// the site directory resolves nothing locally, goes to the
|
|
71
|
+
// public registry for a package by that name and dies with
|
|
72
|
+
// E404 - while still exiting 0. That is not hypothetical: it
|
|
73
|
+
// is what sent a generated site's images into theme/root/
|
|
74
|
+
// instead of media/. Run it from here as
|
|
75
|
+
// `npm run seed-media -- .. <file>`.
|
|
76
|
+
'seed-media': 'seed-media',
|
|
77
|
+
// Stops the site from any terminal, not only the one it was
|
|
78
|
+
// started in (stop-site, via vhost/data/server.pid).
|
|
79
|
+
stop: 'stop-site',
|
|
80
|
+
// Copies a running site's content and media into this one
|
|
81
|
+
// (pull-site): `npm run pull`, which asks for the address and token.
|
|
82
|
+
pull: 'pull-site',
|
|
83
|
+
// Sends changes made here since the last pull back to that
|
|
84
|
+
// live site (push-site), refusing to overwrite anything
|
|
85
|
+
// edited there since, and only after typing its address.
|
|
86
|
+
push: 'push-site',
|
|
87
|
+
// Upgrades the CMS itself (upgrade-site): shows what's new, installs
|
|
88
|
+
// it, and updates the files in vhost/ that belong to the CMS.
|
|
89
|
+
upgrade: 'upgrade-site',
|
|
90
|
+
};
|
|
91
|
+
export class ScaffoldError extends Error {
|
|
92
|
+
}
|
|
93
|
+
// A standalone, one-shot CLI operation - no running server exists yet
|
|
94
|
+
// for enqueue()'s serialisation to matter, and this whole function
|
|
95
|
+
// runs synchronously start to finish, so there's no concurrent-write
|
|
96
|
+
// hazard to guard against.
|
|
97
|
+
export function scaffoldSite(targetDir) {
|
|
98
|
+
if (existsSync(targetDir) && readdirSync(targetDir).length > 0) {
|
|
99
|
+
throw new ScaffoldError(`"${targetDir}" already exists and is not empty`);
|
|
100
|
+
}
|
|
101
|
+
mkdirSync(targetDir, { recursive: true });
|
|
102
|
+
// The static theme/content template, copied verbatim except its own
|
|
103
|
+
// "gitignore"/"dockerignore" (no leading dot - a literal .gitignore
|
|
104
|
+
// or .dockerignore inside the template tree would be silently
|
|
105
|
+
// dropped by npm pack's own packlist logic, verified empirically
|
|
106
|
+
// before this was written).
|
|
107
|
+
cpSync(TEMPLATE_ROOT, targetDir, {
|
|
108
|
+
recursive: true,
|
|
109
|
+
filter: (source) => basename(source) !== 'gitignore' && basename(source) !== 'dockerignore',
|
|
110
|
+
});
|
|
111
|
+
cpSync(join(TEMPLATE_ROOT, 'gitignore'), join(targetDir, '.gitignore'));
|
|
112
|
+
cpSync(join(TEMPLATE_ROOT, 'dockerignore'), join(targetDir, '.dockerignore'));
|
|
113
|
+
// content/drafts/ starts empty - not part of the static template
|
|
114
|
+
// (nothing to copy), created directly so a fresh site has somewhere
|
|
115
|
+
// to write its first draft immediately. Nested inside content/, not
|
|
116
|
+
// a sibling of it - see config.ts's own comment for why.
|
|
117
|
+
mkdirSync(join(targetDir, 'content', 'drafts'), { recursive: true });
|
|
118
|
+
// media/ also starts empty, also created directly (no template
|
|
119
|
+
// content to copy - see config.ts's own comment for why this is a
|
|
120
|
+
// sibling of content/theme/vhost, not nested under content/). Not
|
|
121
|
+
// passed to commitPaths below: media is never git-tracked
|
|
122
|
+
// (constraint 2), even at scaffold time - an empty directory has
|
|
123
|
+
// nothing to commit anyway (git doesn't track empty directories), so
|
|
124
|
+
// this omission is a non-issue mechanically, but it's deliberate,
|
|
125
|
+
// not an oversight, should anyone go looking for why it's absent
|
|
126
|
+
// from that list.
|
|
127
|
+
mkdirSync(join(targetDir, 'media'), { recursive: true });
|
|
128
|
+
// vhost/: the site's own serving configuration (package.json,
|
|
129
|
+
// server.js, site.config.json), matching a real vhost's own scope in
|
|
130
|
+
// hosting terms - never a sibling of content/theme.
|
|
131
|
+
const vhostDir = join(targetDir, 'vhost');
|
|
132
|
+
mkdirSync(vhostDir, { recursive: true });
|
|
133
|
+
// A zero-token site fails closed on every write route (401) - not
|
|
134
|
+
// usable for editing at all. Generated fresh per scaffold, printed
|
|
135
|
+
// once, never written to disk - matches every other write-capable
|
|
136
|
+
// identity in this codebase never depending on a value the operator
|
|
137
|
+
// could lose or that leaks into version control.
|
|
138
|
+
const token = generateToken();
|
|
139
|
+
writeFileSync(join(vhostDir, 'site.config.json'), JSON.stringify({
|
|
140
|
+
// Written explicitly, matching server-config.ts's own
|
|
141
|
+
// DEFAULT_PORT, rather than left absent - a real value here
|
|
142
|
+
// is a visible, editable config knob; an absent key is
|
|
143
|
+
// invisible until something else is already using the
|
|
144
|
+
// default port, at which point the failure is a raw
|
|
145
|
+
// EADDRINUSE crash instead of an obvious setting to change.
|
|
146
|
+
port: 3000,
|
|
147
|
+
tokens: [{ hash: token.hash, scopes: ['content', 'theme', 'media'] }],
|
|
148
|
+
}, null, 2));
|
|
149
|
+
const packageName = sanitisePackageName(basename(targetDir));
|
|
150
|
+
writeFileSync(join(vhostDir, 'package.json'), JSON.stringify({
|
|
151
|
+
name: packageName,
|
|
152
|
+
version: '0.0.0',
|
|
153
|
+
private: true,
|
|
154
|
+
type: 'module',
|
|
155
|
+
scripts: SCAFFOLD_SCRIPTS,
|
|
156
|
+
dependencies: {
|
|
157
|
+
// Pinned exact, never a ^range - at v0.x even a minor bump
|
|
158
|
+
// can be breaking (build plan's own word is "pinned").
|
|
159
|
+
[PACKAGE_NAME]: readAgentVersion(),
|
|
160
|
+
},
|
|
161
|
+
}, null, 2));
|
|
162
|
+
writeFileSync(join(vhostDir, 'server.js'), SERVER_JS);
|
|
163
|
+
// runStartupChecks hard-fails not-a-git-repo otherwise - a scaffold
|
|
164
|
+
// without a real git repo cannot boot at all.
|
|
165
|
+
execFileSync('git', ['init', '--quiet'], { cwd: targetDir });
|
|
166
|
+
commitPaths(targetDir, ['theme', 'content', 'vhost', 'AGENTS.md', '.gitignore', '.dockerignore'], 'chore: initial scaffold', CHECKPOINT_AUTHOR);
|
|
167
|
+
return { raw: token.raw };
|
|
168
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { resolve } from 'node:path';
|
|
3
|
+
import { MintTokenError, mintToken, parseScopes } from "./mint-token.js";
|
|
4
|
+
const targetArg = process.argv[2];
|
|
5
|
+
const scopesFlagIndex = process.argv.indexOf('--scopes');
|
|
6
|
+
const scopesArg = scopesFlagIndex !== -1 ? process.argv[scopesFlagIndex + 1] : undefined;
|
|
7
|
+
if (!targetArg) {
|
|
8
|
+
console.error('Usage: mint-token <site-directory> [--scopes content,theme,media]');
|
|
9
|
+
process.exit(1);
|
|
10
|
+
}
|
|
11
|
+
try {
|
|
12
|
+
const scopes = parseScopes(scopesArg);
|
|
13
|
+
const siteDir = resolve(process.cwd(), targetArg);
|
|
14
|
+
const { raw } = mintToken(siteDir, scopes);
|
|
15
|
+
console.log('New access token (save this now - it will not be shown again):');
|
|
16
|
+
console.log(` ${raw}`);
|
|
17
|
+
console.log('');
|
|
18
|
+
console.log(`Scopes: ${scopes.join(', ')}`);
|
|
19
|
+
}
|
|
20
|
+
catch (error) {
|
|
21
|
+
if (error instanceof MintTokenError) {
|
|
22
|
+
console.error(error.message);
|
|
23
|
+
process.exit(1);
|
|
24
|
+
}
|
|
25
|
+
throw error;
|
|
26
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Scope } from '../server-config.ts';
|
|
2
|
+
export declare class MintTokenError extends Error {
|
|
3
|
+
}
|
|
4
|
+
export declare function parseScopes(raw: string | undefined): Scope[];
|
|
5
|
+
export declare function mintToken(siteDir: string, scopes: Scope[]): {
|
|
6
|
+
raw: string;
|
|
7
|
+
};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { readFileSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { CHECKPOINT_AUTHOR } from "../services/checkpoint.js";
|
|
4
|
+
import { commitPaths } from "../services/git.js";
|
|
5
|
+
import { generateToken } from "./generate-site.js";
|
|
6
|
+
export class MintTokenError extends Error {
|
|
7
|
+
}
|
|
8
|
+
const VALID_SCOPES = new Set(['content', 'theme', 'media']);
|
|
9
|
+
export function parseScopes(raw) {
|
|
10
|
+
if (!raw) {
|
|
11
|
+
return ['content', 'theme', 'media'];
|
|
12
|
+
}
|
|
13
|
+
const scopes = raw.split(',').map((scope) => scope.trim());
|
|
14
|
+
for (const scope of scopes) {
|
|
15
|
+
if (!VALID_SCOPES.has(scope)) {
|
|
16
|
+
throw new MintTokenError(`Unknown scope "${scope}" - valid scopes are content, theme, media`);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
return scopes;
|
|
20
|
+
}
|
|
21
|
+
// A standalone CLI operation against an operator-supplied site
|
|
22
|
+
// directory, same category as scaffoldSite - not a web request's
|
|
23
|
+
// :path, so sanitisePath's traversal concern doesn't apply here.
|
|
24
|
+
export function mintToken(siteDir, scopes) {
|
|
25
|
+
const configPath = join(siteDir, 'vhost', 'site.config.json');
|
|
26
|
+
let raw;
|
|
27
|
+
try {
|
|
28
|
+
raw = readFileSync(configPath, 'utf-8');
|
|
29
|
+
}
|
|
30
|
+
catch (error) {
|
|
31
|
+
throw new MintTokenError(`Could not read ${configPath} - is "${siteDir}" a real site created by create-site?`, {
|
|
32
|
+
cause: error,
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
let config;
|
|
36
|
+
try {
|
|
37
|
+
config = JSON.parse(raw);
|
|
38
|
+
}
|
|
39
|
+
catch (error) {
|
|
40
|
+
throw new MintTokenError(`${configPath} is not valid JSON`, { cause: error });
|
|
41
|
+
}
|
|
42
|
+
const token = generateToken();
|
|
43
|
+
const tokens = Array.isArray(config.tokens) ? config.tokens : [];
|
|
44
|
+
tokens.push({ hash: token.hash, scopes });
|
|
45
|
+
config.tokens = tokens;
|
|
46
|
+
writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
47
|
+
commitPaths(siteDir, ['vhost/site.config.json'], 'chore: mint a new access token', CHECKPOINT_AUTHOR);
|
|
48
|
+
return { raw: token.raw };
|
|
49
|
+
}
|