@mondaydotcomorg/z2h-cli 0.25.9 → 0.26.0
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/commands/backend.js +2 -2
- package/dist/commands/deploy.d.ts +1 -0
- package/dist/commands/deploy.d.ts.map +1 -1
- package/dist/commands/deploy.js +19 -3
- package/dist/commands/generate.d.ts +12 -0
- package/dist/commands/generate.d.ts.map +1 -0
- package/dist/commands/generate.js +190 -0
- package/dist/commands/templates.d.ts +7 -0
- package/dist/commands/templates.d.ts.map +1 -0
- package/dist/commands/templates.js +23 -0
- package/dist/esm/commands/deploy.d.ts +1 -0
- package/dist/esm/commands/deploy.d.ts.map +1 -1
- package/dist/esm/commands/deploy.mjs +19 -4
- package/dist/esm/commands/generate.d.ts +12 -0
- package/dist/esm/commands/generate.d.ts.map +1 -0
- package/dist/esm/commands/generate.mjs +182 -0
- package/dist/esm/commands/templates.d.ts +7 -0
- package/dist/esm/commands/templates.d.ts.map +1 -0
- package/dist/esm/commands/templates.mjs +21 -0
- package/dist/esm/index.mjs +16 -1
- package/dist/esm/util/dashboard-templates/load-catalog.d.ts +22 -0
- package/dist/esm/util/dashboard-templates/load-catalog.d.ts.map +1 -0
- package/dist/esm/util/dashboard-templates/load-catalog.mjs +55 -0
- package/dist/esm/util/dashboard-templates/patch-bundle.d.ts +12 -0
- package/dist/esm/util/dashboard-templates/patch-bundle.d.ts.map +1 -0
- package/dist/esm/util/dashboard-templates/patch-bundle.mjs +108 -0
- package/dist/index.js +16 -1
- package/dist/util/app-name.js +2 -2
- package/dist/util/dashboard-templates/load-catalog.d.ts +22 -0
- package/dist/util/dashboard-templates/load-catalog.d.ts.map +1 -0
- package/dist/util/dashboard-templates/load-catalog.js +64 -0
- package/dist/util/dashboard-templates/patch-bundle.d.ts +12 -0
- package/dist/util/dashboard-templates/patch-bundle.d.ts.map +1 -0
- package/dist/util/dashboard-templates/patch-bundle.js +116 -0
- package/package.json +4 -2
- package/src/commands/deploy.ts +24 -5
- package/src/commands/generate.ts +225 -0
- package/src/commands/templates.ts +20 -0
- package/src/index.ts +36 -1
- package/src/util/dashboard-templates/load-catalog.ts +80 -0
- package/src/util/dashboard-templates/patch-bundle.ts +127 -0
package/dist/commands/backend.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
2
|
|
|
3
3
|
const path = require('node:path');
|
|
4
|
-
const
|
|
4
|
+
const fs = require('node:fs');
|
|
5
5
|
const clientS3 = require('@aws-sdk/client-s3');
|
|
6
6
|
const util_logger = require('../util/logger.js');
|
|
7
7
|
const util_appName = require('../util/app-name.js');
|
|
@@ -29,7 +29,7 @@ function currentAppName() {
|
|
|
29
29
|
* play.
|
|
30
30
|
*/
|
|
31
31
|
function hasHandlers(appDir) {
|
|
32
|
-
return
|
|
32
|
+
return fs.existsSync(path__default.default.join(appDir, 'backend', 'handlers'));
|
|
33
33
|
}
|
|
34
34
|
/**
|
|
35
35
|
* Read handlers, detect declared integrations, and upload to `prefix` when there
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deploy.d.ts","sourceRoot":"","sources":["../../src/commands/deploy.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"deploy.d.ts","sourceRoot":"","sources":["../../src/commands/deploy.ts"],"names":[],"mappings":"AAmBA,OAAO,EAAE,KAAK,UAAU,EAA2C,MAAM,4BAA4B,CAAC;AAkBtG,MAAM,WAAW,aAAa;IAC5B,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,UAAU,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IAIrB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAWD,wBAAsB,aAAa,CAAC,IAAI,GAAE,aAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAyB3E"}
|
package/dist/commands/deploy.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
2
|
|
|
3
3
|
const os = require('node:os');
|
|
4
|
+
const path = require('node:path');
|
|
4
5
|
const fs = require('fs-extra');
|
|
5
6
|
const util_logger = require('../util/logger.js');
|
|
6
7
|
const shadow_paths = require('../shadow/paths.js');
|
|
@@ -26,6 +27,7 @@ const commands_tag = require('./tag.js');
|
|
|
26
27
|
const _interopDefault = e => e && e.__esModule ? e : { default: e };
|
|
27
28
|
|
|
28
29
|
const os__default = /*#__PURE__*/_interopDefault(os);
|
|
30
|
+
const path__default = /*#__PURE__*/_interopDefault(path);
|
|
29
31
|
|
|
30
32
|
/** Who is publishing this version — the machine identity, else the OS user. */
|
|
31
33
|
function whoDeployed() {
|
|
@@ -64,7 +66,15 @@ async function deployCommand(opts = {}) {
|
|
|
64
66
|
* asset manifest Trident emitted.
|
|
65
67
|
*/
|
|
66
68
|
async function buildAndStamp(opts) {
|
|
67
|
-
|
|
69
|
+
let assetManifest;
|
|
70
|
+
if (opts.prebuiltDir) {
|
|
71
|
+
util_logger.info(`skipping build — reusing prebuilt bundle from ${opts.prebuiltDir}`);
|
|
72
|
+
await fs.copy(opts.prebuiltDir, opts.paths.shadowBuild, { overwrite: true });
|
|
73
|
+
assetManifest = await fs.readJson(path__default.default.join(opts.paths.shadowBuild, 'asset-manifest.json'));
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
({ assetManifest } = await commands_build.buildCommand({ publicUrl: opts.publicUrl }));
|
|
77
|
+
}
|
|
68
78
|
const deployedBy = whoDeployed();
|
|
69
79
|
return {
|
|
70
80
|
...assetManifest,
|
|
@@ -109,7 +119,13 @@ async function deployPreview({ paths, consumerName, appName, token, opts }) {
|
|
|
109
119
|
// and anything unclassified reports as `unknown` via reasonOf() in runCommand.
|
|
110
120
|
const nextVersion = (await util_auth_brokerCredentialProvider.fetchDeployVersion(previewAppName)) + 1;
|
|
111
121
|
util_logger.info('building');
|
|
112
|
-
const entry = await buildAndStamp({
|
|
122
|
+
const entry = await buildAndStamp({
|
|
123
|
+
paths,
|
|
124
|
+
publicUrl: url,
|
|
125
|
+
nextVersion,
|
|
126
|
+
gitRemote: '',
|
|
127
|
+
prebuiltDir: opts.prebuiltDir,
|
|
128
|
+
});
|
|
113
129
|
util_logger.info('getting sts credentials');
|
|
114
130
|
const creds = await util_auth_brokerCredentialProvider.brokerCredentialProvider({ appName: previewAppName, previewId })();
|
|
115
131
|
util_s3_client.setupS3Client(creds);
|
|
@@ -182,7 +198,7 @@ async function deployLive({ paths, consumerName, appName, token, opts }) {
|
|
|
182
198
|
const keyPrefix = `${appName}/${nextVersion}`;
|
|
183
199
|
const url = `${constants.BUCKET_READ_DNS_URL}/${keyPrefix}`;
|
|
184
200
|
util_logger.info('building');
|
|
185
|
-
const entry = await buildAndStamp({ paths, publicUrl: url, nextVersion, gitRemote });
|
|
201
|
+
const entry = await buildAndStamp({ paths, publicUrl: url, nextVersion, gitRemote, prebuiltDir: opts.prebuiltDir });
|
|
186
202
|
if (opts.dryRun) {
|
|
187
203
|
util_logger.output(`[z2h-cli] dry-run: would upload to s3://${constants.BUCKET_WRITE_NAME}/${keyPrefix}/ as v${version}`, {
|
|
188
204
|
ok: true,
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export interface GenerateOptions {
|
|
2
|
+
template: string;
|
|
3
|
+
input: string;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Copies a warm @mondaydotcomorg/z2h-dashboard-templates entry, patches its already-built bundle with real
|
|
7
|
+
* config data, and previews it — no `z2h-cli build`, no `yarn install`, no live deploy. See
|
|
8
|
+
* systems/owned/z2h/specs/agent-dashboard-generation.md ("Separating Z2H from its consumers")
|
|
9
|
+
* in bb-foundations for why this lives here instead of as agent-side MCP tools.
|
|
10
|
+
*/
|
|
11
|
+
export declare function generateCommand(appName: string, opts: GenerateOptions): Promise<void>;
|
|
12
|
+
//# sourceMappingURL=generate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate.d.ts","sourceRoot":"","sources":["../../src/commands/generate.ts"],"names":[],"mappings":"AAkBA,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;CACf;AAwHD;;;;;GAKG;AACH,wBAAsB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CA6E3F"}
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
|
+
|
|
3
|
+
const path = require('node:path');
|
|
4
|
+
const fs = require('node:fs');
|
|
5
|
+
const node_module = require('node:module');
|
|
6
|
+
const execa = require('execa');
|
|
7
|
+
const fs$1 = require('fs-extra');
|
|
8
|
+
const util_workspace = require('../util/workspace.js');
|
|
9
|
+
const util_appName = require('../util/app-name.js');
|
|
10
|
+
const util_broker_app = require('../util/broker/app.js');
|
|
11
|
+
const util_dashboardTemplates_patchBundle = require('../util/dashboard-templates/patch-bundle.js');
|
|
12
|
+
const util_dashboardTemplates_loadCatalog = require('../util/dashboard-templates/load-catalog.js');
|
|
13
|
+
const util_git_repo = require('../util/git/repo.js');
|
|
14
|
+
const util_logger = require('../util/logger.js');
|
|
15
|
+
|
|
16
|
+
var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
|
|
17
|
+
const _interopDefault = e => e && e.__esModule ? e : { default: e };
|
|
18
|
+
|
|
19
|
+
const path__default = /*#__PURE__*/_interopDefault(path);
|
|
20
|
+
const fs__default = /*#__PURE__*/_interopDefault(fs);
|
|
21
|
+
|
|
22
|
+
// Must match zth-backend-runner's route pattern — fail at generate time, not at first invocation.
|
|
23
|
+
const HANDLER_NAME_RE = /^[a-z][a-z0-9_]{0,63}$/;
|
|
24
|
+
function createSetupAPI(handlersDir) {
|
|
25
|
+
return {
|
|
26
|
+
createBackend(handlerName, content) {
|
|
27
|
+
if (!HANDLER_NAME_RE.test(handlerName)) {
|
|
28
|
+
throw new Error(`setupBackend: invalid handler name "${handlerName}" — must be lowercase snake_case, matching ${HANDLER_NAME_RE}`);
|
|
29
|
+
}
|
|
30
|
+
fs__default.default.writeFileSync(path__default.default.join(handlersDir, `${handlerName}.js`), content);
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
// Shape is entirely template-defined — validated wholesale against the template's own zod schema,
|
|
35
|
+
// not against any fields z2h-cli itself knows about.
|
|
36
|
+
function parseInput(raw, template) {
|
|
37
|
+
let parsed;
|
|
38
|
+
try {
|
|
39
|
+
parsed = JSON.parse(raw);
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
throw new Error('--input must be valid JSON');
|
|
43
|
+
}
|
|
44
|
+
const result = template.validateConfig(parsed);
|
|
45
|
+
if (!result.success) {
|
|
46
|
+
throw new Error(`--input failed validation against template "${template.id}": ${result.error}`);
|
|
47
|
+
}
|
|
48
|
+
return result.data;
|
|
49
|
+
}
|
|
50
|
+
async function isDirEmpty(dir) {
|
|
51
|
+
if (!(await fs$1.pathExists(dir))) {
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
return (await fs$1.readdir(dir)).length === 0;
|
|
55
|
+
}
|
|
56
|
+
const DEPENDENCY_FIELDS = ['dependencies', 'devDependencies', 'peerDependencies', 'optionalDependencies'];
|
|
57
|
+
// A real npm publish already resolves a template's workspace:* deps to real versions before
|
|
58
|
+
// packaging (see trident-monorepo's replaceWorkspaceProtocolInPackage) — this is a no-op there.
|
|
59
|
+
// In this repo's own dev setup (reached via yarn link, unpublished), that never ran, so we resolve
|
|
60
|
+
// it ourselves via require.resolve, same as node would, anchored to this file not to cwd.
|
|
61
|
+
function resolveWorkspaceProtocolDeps(appDir) {
|
|
62
|
+
const pkgPath = path__default.default.join(appDir, 'package.json');
|
|
63
|
+
const pkg = JSON.parse(fs__default.default.readFileSync(pkgPath, 'utf8'));
|
|
64
|
+
const nodeRequire = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('commands/generate.js', document.baseURI).href)));
|
|
65
|
+
let changed = false;
|
|
66
|
+
for (const field of DEPENDENCY_FIELDS) {
|
|
67
|
+
const deps = pkg[field];
|
|
68
|
+
if (!deps) {
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
for (const [depName, range] of Object.entries(deps)) {
|
|
72
|
+
if (!range.startsWith('workspace:')) {
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
const protocol = range.slice('workspace:'.length);
|
|
76
|
+
const depPkgPath = nodeRequire.resolve(`${depName}/package.json`);
|
|
77
|
+
const actualVersion = JSON.parse(fs__default.default.readFileSync(depPkgPath, 'utf8')).version;
|
|
78
|
+
deps[depName] = protocol === '*' ? actualVersion : `${protocol}${actualVersion}`;
|
|
79
|
+
changed = true;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
if (changed) {
|
|
83
|
+
fs__default.default.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n');
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
// Only the top-level package.json needs renaming — deployCommand() reads its "name" to derive
|
|
87
|
+
// the registered app name/URL slug. `.zth/package.json` is NOT patched here: `generate` never
|
|
88
|
+
// rebuilds (deploy runs with --prebuilt-dir), so it's never read; a real rebuild later would
|
|
89
|
+
// regenerate it from the top-level name anyway (see shadow/sync.ts's writeShadowPkg).
|
|
90
|
+
function patchPackageJson(appDir, appName) {
|
|
91
|
+
const pkgPath = path__default.default.join(appDir, 'package.json');
|
|
92
|
+
const pkg = JSON.parse(fs__default.default.readFileSync(pkgPath, 'utf8'));
|
|
93
|
+
pkg.name = appName;
|
|
94
|
+
delete pkg.devDependencies?.['@mondaydotcomorg/z2h-shared-utils'];
|
|
95
|
+
fs__default.default.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n');
|
|
96
|
+
resolveWorkspaceProtocolDeps(appDir);
|
|
97
|
+
}
|
|
98
|
+
// The copied template already ships src/templateConfig.ts as a placeholder — real, typed source
|
|
99
|
+
// kept buildable on its own so the template can be verified independently of the generator.
|
|
100
|
+
function patchConfigSource(appDir, frontendConfig) {
|
|
101
|
+
const configPath = path__default.default.join(appDir, 'src', 'templateConfig.ts');
|
|
102
|
+
const placeholder = fs__default.default.readFileSync(configPath, 'utf8');
|
|
103
|
+
const patched = util_dashboardTemplates_patchBundle.patchOneFile(placeholder, frontendConfig);
|
|
104
|
+
if (patched === null) {
|
|
105
|
+
throw new Error(`generate: ${configPath} has no Z2H_CONFIG_V1 anchor — template's placeholder is stale`);
|
|
106
|
+
}
|
|
107
|
+
fs__default.default.writeFileSync(configPath, patched);
|
|
108
|
+
}
|
|
109
|
+
// Optional per-template extension point, mirroring setup-backend.ts's contract: decides which
|
|
110
|
+
// fields of the raw, validated --input are safe to ship to the browser. Templates with nothing
|
|
111
|
+
// sensitive to strip (no sql, no secrets) can skip shipping this file entirely — the raw config
|
|
112
|
+
// is used as-is. Compiled from setup-frontend.ts into dist/setup-frontend.js by dashboard-templates'
|
|
113
|
+
// build script (scripts/build-templates.mjs) — a standalone tsc pass, separate from the browser
|
|
114
|
+
// MF build, since this hook is Node-side and never ships to the browser.
|
|
115
|
+
function toFrontendConfig(templateDir, rawConfig) {
|
|
116
|
+
const setupFrontendPath = path__default.default.join(templateDir, 'dist', 'setup-frontend.js');
|
|
117
|
+
if (!fs__default.default.existsSync(setupFrontendPath)) {
|
|
118
|
+
return rawConfig;
|
|
119
|
+
}
|
|
120
|
+
const templateRequire = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('commands/generate.js', document.baseURI).href)));
|
|
121
|
+
const { toFrontendConfig: transform } = templateRequire(setupFrontendPath);
|
|
122
|
+
return transform(rawConfig);
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Copies a warm @mondaydotcomorg/z2h-dashboard-templates entry, patches its already-built bundle with real
|
|
126
|
+
* config data, and previews it — no `z2h-cli build`, no `yarn install`, no live deploy. See
|
|
127
|
+
* systems/owned/z2h/specs/agent-dashboard-generation.md ("Separating Z2H from its consumers")
|
|
128
|
+
* in bb-foundations for why this lives here instead of as agent-side MCP tools.
|
|
129
|
+
*/
|
|
130
|
+
async function generateCommand(appName, opts) {
|
|
131
|
+
const workspace = await util_workspace.assertWorkspace();
|
|
132
|
+
util_appName.validateAppName(appName);
|
|
133
|
+
const template = util_dashboardTemplates_loadCatalog.findTemplate(opts.template);
|
|
134
|
+
const rawConfig = parseInput(opts.input, template);
|
|
135
|
+
const appDir = path__default.default.join(workspace, appName);
|
|
136
|
+
if (!(await isDirEmpty(appDir))) {
|
|
137
|
+
throw new Error(`${appDir} is non-empty. Pick a different name or wipe it first.`);
|
|
138
|
+
}
|
|
139
|
+
const existing = await util_broker_app.fetchApp(util_appName.mfAppName(appName));
|
|
140
|
+
if (existing) {
|
|
141
|
+
throw new Error(`An app named "${appName}" already exists in the Z2H registry. Pick a different name.`);
|
|
142
|
+
}
|
|
143
|
+
util_logger.info(`copying template "${template.id}" → ${appDir}`);
|
|
144
|
+
// Excludes node_modules (installed into templateDir itself by build-templates.mjs, never
|
|
145
|
+
// needed by the generated app), .build-shadow (a scratch dir that build-templates.mjs
|
|
146
|
+
// normally cleans up itself — only present here if a prior build crashed mid-way), and
|
|
147
|
+
// setup-backend/setup-frontend/template (source .ts at templateDir root, compiled .js in dist/ —
|
|
148
|
+
// generation-time-only files, required() straight from template.templateDir/dist above, never
|
|
149
|
+
// read again once the app exists, so neither form should end up in the generated app).
|
|
150
|
+
fs__default.default.cpSync(template.templateDir, appDir, {
|
|
151
|
+
recursive: true,
|
|
152
|
+
filter: src => !/[/\\](node_modules|\.build-shadow|setup-backend\.(ts|js)|setup-frontend\.(ts|js)|template\.(ts|js))([/\\]|$)/.test('/' + path__default.default.relative(template.templateDir, src)),
|
|
153
|
+
});
|
|
154
|
+
patchPackageJson(appDir, appName);
|
|
155
|
+
fs__default.default.appendFileSync(path__default.default.join(appDir, '.gitignore'), 'dist/\n');
|
|
156
|
+
const frontendConfig = toFrontendConfig(template.templateDir, rawConfig);
|
|
157
|
+
patchConfigSource(appDir, frontendConfig);
|
|
158
|
+
const patchedBuildDir = path__default.default.join(appDir, '.zth', 'build-patched');
|
|
159
|
+
util_logger.info('patching prebuilt bundle with real config data');
|
|
160
|
+
// Source is templateDir/dist/ — built by @mondaydotcomorg/z2h-dashboard-templates' own `build` script (see
|
|
161
|
+
// packages/dashboard-templates/scripts/build-templates.mjs), never committed to git.
|
|
162
|
+
util_dashboardTemplates_patchBundle.patchBuiltBundle(path__default.default.join(appDir, 'dist'), patchedBuildDir, frontendConfig);
|
|
163
|
+
const setupBackendPath = path__default.default.join(template.templateDir, 'dist', 'setup-backend.js');
|
|
164
|
+
if (fs__default.default.existsSync(setupBackendPath)) {
|
|
165
|
+
util_logger.info('generating backend handlers via template.setupBackend');
|
|
166
|
+
const templateRequire = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('commands/generate.js', document.baseURI).href)));
|
|
167
|
+
const { setupBackend } = templateRequire(setupBackendPath);
|
|
168
|
+
const handlersDir = path__default.default.join(appDir, 'backend', 'handlers');
|
|
169
|
+
fs__default.default.mkdirSync(handlersDir, { recursive: true });
|
|
170
|
+
setupBackend(rawConfig, createSetupAPI(handlersDir));
|
|
171
|
+
}
|
|
172
|
+
util_logger.info(`initializing git repo for ${appName}...`);
|
|
173
|
+
await util_git_repo.initRepo(appDir, util_git_repo.defaultS3Remote(appName));
|
|
174
|
+
// generate only ever produces a preview — a disposable, shareable synthetic slot. It never
|
|
175
|
+
// registers a live version; that's a separate, explicit `z2h-cli deploy` the user runs
|
|
176
|
+
// themselves once they're happy with the preview.
|
|
177
|
+
util_logger.info('deploying preview');
|
|
178
|
+
const previewArgs = ['deploy', '--preview', '--prebuilt-dir', patchedBuildDir, '--json', '--open'];
|
|
179
|
+
const { stdout } = await execa.execa('z2h-cli', previewArgs, { cwd: appDir });
|
|
180
|
+
const previewResult = JSON.parse(stdout.trim().split('\n').filter(Boolean).at(-1) ?? '{}');
|
|
181
|
+
util_logger.output(`[z2h-cli] generated "${appName}" from template "${template.id}" ✅\n preview: ${previewResult.previewUrl}`, {
|
|
182
|
+
ok: true,
|
|
183
|
+
appName: util_appName.cleanAppName(appName),
|
|
184
|
+
templateId: template.id,
|
|
185
|
+
previewUrl: previewResult.previewUrl,
|
|
186
|
+
version: previewResult.version,
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
exports.generateCommand = generateCommand;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Prints the @mondaydotcomorg/z2h-dashboard-templates catalog — id, title, description, and the
|
|
3
|
+
* config schema as JSON Schema (each template defines its own whole-config shape;
|
|
4
|
+
* `generate-app-template` validates `--input` against whichever one is named).
|
|
5
|
+
*/
|
|
6
|
+
export declare function templatesCommand(): Promise<void>;
|
|
7
|
+
//# sourceMappingURL=templates.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"templates.d.ts","sourceRoot":"","sources":["../../src/commands/templates.ts"],"names":[],"mappings":"AAGA;;;;GAIG;AACH,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC,CAWtD"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
|
+
|
|
3
|
+
const util_logger = require('../util/logger.js');
|
|
4
|
+
const util_dashboardTemplates_loadCatalog = require('../util/dashboard-templates/load-catalog.js');
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Prints the @mondaydotcomorg/z2h-dashboard-templates catalog — id, title, description, and the
|
|
8
|
+
* config schema as JSON Schema (each template defines its own whole-config shape;
|
|
9
|
+
* `generate-app-template` validates `--input` against whichever one is named).
|
|
10
|
+
*/
|
|
11
|
+
async function templatesCommand() {
|
|
12
|
+
const templates = util_dashboardTemplates_loadCatalog.loadDashboardTemplatesCatalog().map(({ id, title, description, configSchema }) => ({
|
|
13
|
+
id,
|
|
14
|
+
title,
|
|
15
|
+
description,
|
|
16
|
+
configSchema,
|
|
17
|
+
}));
|
|
18
|
+
const templatesInfo = templates.map(t => `${t.id} — ${t.title}\n ${t.description}`);
|
|
19
|
+
const hasInfo = templatesInfo.length > 0;
|
|
20
|
+
util_logger.output(hasInfo ? templatesInfo.join('\n') : '(no templates in catalog)', { ok: true, templates });
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
exports.templatesCommand = templatesCommand;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deploy.d.ts","sourceRoot":"","sources":["../../../src/commands/deploy.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"deploy.d.ts","sourceRoot":"","sources":["../../../src/commands/deploy.ts"],"names":[],"mappings":"AAmBA,OAAO,EAAE,KAAK,UAAU,EAA2C,MAAM,4BAA4B,CAAC;AAkBtG,MAAM,WAAW,aAAa;IAC5B,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,UAAU,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IAIrB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAWD,wBAAsB,aAAa,CAAC,IAAI,GAAE,aAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAyB3E"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import os from 'node:os';
|
|
2
|
-
import
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { readJson, ensureDir, copy } from 'fs-extra';
|
|
3
4
|
import { info, output, getOutputMode, warn } from '../util/logger.mjs';
|
|
4
5
|
import { resolveConsumerPaths } from '../shadow/paths.mjs';
|
|
5
6
|
import { fetchAppTags, fetchTagCatalog, setAppTags } from '../util/tags.mjs';
|
|
@@ -58,7 +59,15 @@ async function deployCommand(opts = {}) {
|
|
|
58
59
|
* asset manifest Trident emitted.
|
|
59
60
|
*/
|
|
60
61
|
async function buildAndStamp(opts) {
|
|
61
|
-
|
|
62
|
+
let assetManifest;
|
|
63
|
+
if (opts.prebuiltDir) {
|
|
64
|
+
info(`skipping build — reusing prebuilt bundle from ${opts.prebuiltDir}`);
|
|
65
|
+
await copy(opts.prebuiltDir, opts.paths.shadowBuild, { overwrite: true });
|
|
66
|
+
assetManifest = await readJson(path.join(opts.paths.shadowBuild, 'asset-manifest.json'));
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
({ assetManifest } = await buildCommand({ publicUrl: opts.publicUrl }));
|
|
70
|
+
}
|
|
62
71
|
const deployedBy = whoDeployed();
|
|
63
72
|
return {
|
|
64
73
|
...assetManifest,
|
|
@@ -103,7 +112,13 @@ async function deployPreview({ paths, consumerName, appName, token, opts }) {
|
|
|
103
112
|
// and anything unclassified reports as `unknown` via reasonOf() in runCommand.
|
|
104
113
|
const nextVersion = (await fetchDeployVersion(previewAppName)) + 1;
|
|
105
114
|
info('building');
|
|
106
|
-
const entry = await buildAndStamp({
|
|
115
|
+
const entry = await buildAndStamp({
|
|
116
|
+
paths,
|
|
117
|
+
publicUrl: url,
|
|
118
|
+
nextVersion,
|
|
119
|
+
gitRemote: '',
|
|
120
|
+
prebuiltDir: opts.prebuiltDir,
|
|
121
|
+
});
|
|
107
122
|
info('getting sts credentials');
|
|
108
123
|
const creds = await brokerCredentialProvider({ appName: previewAppName, previewId })();
|
|
109
124
|
setupS3Client(creds);
|
|
@@ -176,7 +191,7 @@ async function deployLive({ paths, consumerName, appName, token, opts }) {
|
|
|
176
191
|
const keyPrefix = `${appName}/${nextVersion}`;
|
|
177
192
|
const url = `${BUCKET_READ_DNS_URL}/${keyPrefix}`;
|
|
178
193
|
info('building');
|
|
179
|
-
const entry = await buildAndStamp({ paths, publicUrl: url, nextVersion, gitRemote });
|
|
194
|
+
const entry = await buildAndStamp({ paths, publicUrl: url, nextVersion, gitRemote, prebuiltDir: opts.prebuiltDir });
|
|
180
195
|
if (opts.dryRun) {
|
|
181
196
|
output(`[z2h-cli] dry-run: would upload to s3://${BUCKET_WRITE_NAME}/${keyPrefix}/ as v${version}`, {
|
|
182
197
|
ok: true,
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export interface GenerateOptions {
|
|
2
|
+
template: string;
|
|
3
|
+
input: string;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Copies a warm @mondaydotcomorg/z2h-dashboard-templates entry, patches its already-built bundle with real
|
|
7
|
+
* config data, and previews it — no `z2h-cli build`, no `yarn install`, no live deploy. See
|
|
8
|
+
* systems/owned/z2h/specs/agent-dashboard-generation.md ("Separating Z2H from its consumers")
|
|
9
|
+
* in bb-foundations for why this lives here instead of as agent-side MCP tools.
|
|
10
|
+
*/
|
|
11
|
+
export declare function generateCommand(appName: string, opts: GenerateOptions): Promise<void>;
|
|
12
|
+
//# sourceMappingURL=generate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate.d.ts","sourceRoot":"","sources":["../../../src/commands/generate.ts"],"names":[],"mappings":"AAkBA,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;CACf;AAwHD;;;;;GAKG;AACH,wBAAsB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CA6E3F"}
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import { createRequire } from 'node:module';
|
|
4
|
+
import { execa } from 'execa';
|
|
5
|
+
import { pathExists, readdir } from 'fs-extra';
|
|
6
|
+
import { assertWorkspace } from '../util/workspace.mjs';
|
|
7
|
+
import { validateAppName, mfAppName, cleanAppName } from '../util/app-name.mjs';
|
|
8
|
+
import { fetchApp } from '../util/broker/app.mjs';
|
|
9
|
+
import { patchBuiltBundle, patchOneFile } from '../util/dashboard-templates/patch-bundle.mjs';
|
|
10
|
+
import { findTemplate } from '../util/dashboard-templates/load-catalog.mjs';
|
|
11
|
+
import { initRepo, defaultS3Remote } from '../util/git/repo.mjs';
|
|
12
|
+
import { info, output } from '../util/logger.mjs';
|
|
13
|
+
|
|
14
|
+
// Must match zth-backend-runner's route pattern — fail at generate time, not at first invocation.
|
|
15
|
+
const HANDLER_NAME_RE = /^[a-z][a-z0-9_]{0,63}$/;
|
|
16
|
+
function createSetupAPI(handlersDir) {
|
|
17
|
+
return {
|
|
18
|
+
createBackend(handlerName, content) {
|
|
19
|
+
if (!HANDLER_NAME_RE.test(handlerName)) {
|
|
20
|
+
throw new Error(`setupBackend: invalid handler name "${handlerName}" — must be lowercase snake_case, matching ${HANDLER_NAME_RE}`);
|
|
21
|
+
}
|
|
22
|
+
fs.writeFileSync(path.join(handlersDir, `${handlerName}.js`), content);
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
// Shape is entirely template-defined — validated wholesale against the template's own zod schema,
|
|
27
|
+
// not against any fields z2h-cli itself knows about.
|
|
28
|
+
function parseInput(raw, template) {
|
|
29
|
+
let parsed;
|
|
30
|
+
try {
|
|
31
|
+
parsed = JSON.parse(raw);
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
throw new Error('--input must be valid JSON');
|
|
35
|
+
}
|
|
36
|
+
const result = template.validateConfig(parsed);
|
|
37
|
+
if (!result.success) {
|
|
38
|
+
throw new Error(`--input failed validation against template "${template.id}": ${result.error}`);
|
|
39
|
+
}
|
|
40
|
+
return result.data;
|
|
41
|
+
}
|
|
42
|
+
async function isDirEmpty(dir) {
|
|
43
|
+
if (!(await pathExists(dir))) {
|
|
44
|
+
return true;
|
|
45
|
+
}
|
|
46
|
+
return (await readdir(dir)).length === 0;
|
|
47
|
+
}
|
|
48
|
+
const DEPENDENCY_FIELDS = ['dependencies', 'devDependencies', 'peerDependencies', 'optionalDependencies'];
|
|
49
|
+
// A real npm publish already resolves a template's workspace:* deps to real versions before
|
|
50
|
+
// packaging (see trident-monorepo's replaceWorkspaceProtocolInPackage) — this is a no-op there.
|
|
51
|
+
// In this repo's own dev setup (reached via yarn link, unpublished), that never ran, so we resolve
|
|
52
|
+
// it ourselves via require.resolve, same as node would, anchored to this file not to cwd.
|
|
53
|
+
function resolveWorkspaceProtocolDeps(appDir) {
|
|
54
|
+
const pkgPath = path.join(appDir, 'package.json');
|
|
55
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
|
|
56
|
+
const nodeRequire = createRequire(import.meta.url);
|
|
57
|
+
let changed = false;
|
|
58
|
+
for (const field of DEPENDENCY_FIELDS) {
|
|
59
|
+
const deps = pkg[field];
|
|
60
|
+
if (!deps) {
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
for (const [depName, range] of Object.entries(deps)) {
|
|
64
|
+
if (!range.startsWith('workspace:')) {
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
const protocol = range.slice('workspace:'.length);
|
|
68
|
+
const depPkgPath = nodeRequire.resolve(`${depName}/package.json`);
|
|
69
|
+
const actualVersion = JSON.parse(fs.readFileSync(depPkgPath, 'utf8')).version;
|
|
70
|
+
deps[depName] = protocol === '*' ? actualVersion : `${protocol}${actualVersion}`;
|
|
71
|
+
changed = true;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
if (changed) {
|
|
75
|
+
fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n');
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
// Only the top-level package.json needs renaming — deployCommand() reads its "name" to derive
|
|
79
|
+
// the registered app name/URL slug. `.zth/package.json` is NOT patched here: `generate` never
|
|
80
|
+
// rebuilds (deploy runs with --prebuilt-dir), so it's never read; a real rebuild later would
|
|
81
|
+
// regenerate it from the top-level name anyway (see shadow/sync.ts's writeShadowPkg).
|
|
82
|
+
function patchPackageJson(appDir, appName) {
|
|
83
|
+
const pkgPath = path.join(appDir, 'package.json');
|
|
84
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
|
|
85
|
+
pkg.name = appName;
|
|
86
|
+
delete pkg.devDependencies?.['@mondaydotcomorg/z2h-shared-utils'];
|
|
87
|
+
fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n');
|
|
88
|
+
resolveWorkspaceProtocolDeps(appDir);
|
|
89
|
+
}
|
|
90
|
+
// The copied template already ships src/templateConfig.ts as a placeholder — real, typed source
|
|
91
|
+
// kept buildable on its own so the template can be verified independently of the generator.
|
|
92
|
+
function patchConfigSource(appDir, frontendConfig) {
|
|
93
|
+
const configPath = path.join(appDir, 'src', 'templateConfig.ts');
|
|
94
|
+
const placeholder = fs.readFileSync(configPath, 'utf8');
|
|
95
|
+
const patched = patchOneFile(placeholder, frontendConfig);
|
|
96
|
+
if (patched === null) {
|
|
97
|
+
throw new Error(`generate: ${configPath} has no Z2H_CONFIG_V1 anchor — template's placeholder is stale`);
|
|
98
|
+
}
|
|
99
|
+
fs.writeFileSync(configPath, patched);
|
|
100
|
+
}
|
|
101
|
+
// Optional per-template extension point, mirroring setup-backend.ts's contract: decides which
|
|
102
|
+
// fields of the raw, validated --input are safe to ship to the browser. Templates with nothing
|
|
103
|
+
// sensitive to strip (no sql, no secrets) can skip shipping this file entirely — the raw config
|
|
104
|
+
// is used as-is. Compiled from setup-frontend.ts into dist/setup-frontend.js by dashboard-templates'
|
|
105
|
+
// build script (scripts/build-templates.mjs) — a standalone tsc pass, separate from the browser
|
|
106
|
+
// MF build, since this hook is Node-side and never ships to the browser.
|
|
107
|
+
function toFrontendConfig(templateDir, rawConfig) {
|
|
108
|
+
const setupFrontendPath = path.join(templateDir, 'dist', 'setup-frontend.js');
|
|
109
|
+
if (!fs.existsSync(setupFrontendPath)) {
|
|
110
|
+
return rawConfig;
|
|
111
|
+
}
|
|
112
|
+
const templateRequire = createRequire(import.meta.url);
|
|
113
|
+
const { toFrontendConfig: transform } = templateRequire(setupFrontendPath);
|
|
114
|
+
return transform(rawConfig);
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Copies a warm @mondaydotcomorg/z2h-dashboard-templates entry, patches its already-built bundle with real
|
|
118
|
+
* config data, and previews it — no `z2h-cli build`, no `yarn install`, no live deploy. See
|
|
119
|
+
* systems/owned/z2h/specs/agent-dashboard-generation.md ("Separating Z2H from its consumers")
|
|
120
|
+
* in bb-foundations for why this lives here instead of as agent-side MCP tools.
|
|
121
|
+
*/
|
|
122
|
+
async function generateCommand(appName, opts) {
|
|
123
|
+
const workspace = await assertWorkspace();
|
|
124
|
+
validateAppName(appName);
|
|
125
|
+
const template = findTemplate(opts.template);
|
|
126
|
+
const rawConfig = parseInput(opts.input, template);
|
|
127
|
+
const appDir = path.join(workspace, appName);
|
|
128
|
+
if (!(await isDirEmpty(appDir))) {
|
|
129
|
+
throw new Error(`${appDir} is non-empty. Pick a different name or wipe it first.`);
|
|
130
|
+
}
|
|
131
|
+
const existing = await fetchApp(mfAppName(appName));
|
|
132
|
+
if (existing) {
|
|
133
|
+
throw new Error(`An app named "${appName}" already exists in the Z2H registry. Pick a different name.`);
|
|
134
|
+
}
|
|
135
|
+
info(`copying template "${template.id}" → ${appDir}`);
|
|
136
|
+
// Excludes node_modules (installed into templateDir itself by build-templates.mjs, never
|
|
137
|
+
// needed by the generated app), .build-shadow (a scratch dir that build-templates.mjs
|
|
138
|
+
// normally cleans up itself — only present here if a prior build crashed mid-way), and
|
|
139
|
+
// setup-backend/setup-frontend/template (source .ts at templateDir root, compiled .js in dist/ —
|
|
140
|
+
// generation-time-only files, required() straight from template.templateDir/dist above, never
|
|
141
|
+
// read again once the app exists, so neither form should end up in the generated app).
|
|
142
|
+
fs.cpSync(template.templateDir, appDir, {
|
|
143
|
+
recursive: true,
|
|
144
|
+
filter: src => !/[/\\](node_modules|\.build-shadow|setup-backend\.(ts|js)|setup-frontend\.(ts|js)|template\.(ts|js))([/\\]|$)/.test('/' + path.relative(template.templateDir, src)),
|
|
145
|
+
});
|
|
146
|
+
patchPackageJson(appDir, appName);
|
|
147
|
+
fs.appendFileSync(path.join(appDir, '.gitignore'), 'dist/\n');
|
|
148
|
+
const frontendConfig = toFrontendConfig(template.templateDir, rawConfig);
|
|
149
|
+
patchConfigSource(appDir, frontendConfig);
|
|
150
|
+
const patchedBuildDir = path.join(appDir, '.zth', 'build-patched');
|
|
151
|
+
info('patching prebuilt bundle with real config data');
|
|
152
|
+
// Source is templateDir/dist/ — built by @mondaydotcomorg/z2h-dashboard-templates' own `build` script (see
|
|
153
|
+
// packages/dashboard-templates/scripts/build-templates.mjs), never committed to git.
|
|
154
|
+
patchBuiltBundle(path.join(appDir, 'dist'), patchedBuildDir, frontendConfig);
|
|
155
|
+
const setupBackendPath = path.join(template.templateDir, 'dist', 'setup-backend.js');
|
|
156
|
+
if (fs.existsSync(setupBackendPath)) {
|
|
157
|
+
info('generating backend handlers via template.setupBackend');
|
|
158
|
+
const templateRequire = createRequire(import.meta.url);
|
|
159
|
+
const { setupBackend } = templateRequire(setupBackendPath);
|
|
160
|
+
const handlersDir = path.join(appDir, 'backend', 'handlers');
|
|
161
|
+
fs.mkdirSync(handlersDir, { recursive: true });
|
|
162
|
+
setupBackend(rawConfig, createSetupAPI(handlersDir));
|
|
163
|
+
}
|
|
164
|
+
info(`initializing git repo for ${appName}...`);
|
|
165
|
+
await initRepo(appDir, defaultS3Remote(appName));
|
|
166
|
+
// generate only ever produces a preview — a disposable, shareable synthetic slot. It never
|
|
167
|
+
// registers a live version; that's a separate, explicit `z2h-cli deploy` the user runs
|
|
168
|
+
// themselves once they're happy with the preview.
|
|
169
|
+
info('deploying preview');
|
|
170
|
+
const previewArgs = ['deploy', '--preview', '--prebuilt-dir', patchedBuildDir, '--json', '--open'];
|
|
171
|
+
const { stdout } = await execa('z2h-cli', previewArgs, { cwd: appDir });
|
|
172
|
+
const previewResult = JSON.parse(stdout.trim().split('\n').filter(Boolean).at(-1) ?? '{}');
|
|
173
|
+
output(`[z2h-cli] generated "${appName}" from template "${template.id}" ✅\n preview: ${previewResult.previewUrl}`, {
|
|
174
|
+
ok: true,
|
|
175
|
+
appName: cleanAppName(appName),
|
|
176
|
+
templateId: template.id,
|
|
177
|
+
previewUrl: previewResult.previewUrl,
|
|
178
|
+
version: previewResult.version,
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
export { generateCommand };
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Prints the @mondaydotcomorg/z2h-dashboard-templates catalog — id, title, description, and the
|
|
3
|
+
* config schema as JSON Schema (each template defines its own whole-config shape;
|
|
4
|
+
* `generate-app-template` validates `--input` against whichever one is named).
|
|
5
|
+
*/
|
|
6
|
+
export declare function templatesCommand(): Promise<void>;
|
|
7
|
+
//# sourceMappingURL=templates.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"templates.d.ts","sourceRoot":"","sources":["../../../src/commands/templates.ts"],"names":[],"mappings":"AAGA;;;;GAIG;AACH,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC,CAWtD"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { output } from '../util/logger.mjs';
|
|
2
|
+
import { loadDashboardTemplatesCatalog } from '../util/dashboard-templates/load-catalog.mjs';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Prints the @mondaydotcomorg/z2h-dashboard-templates catalog — id, title, description, and the
|
|
6
|
+
* config schema as JSON Schema (each template defines its own whole-config shape;
|
|
7
|
+
* `generate-app-template` validates `--input` against whichever one is named).
|
|
8
|
+
*/
|
|
9
|
+
async function templatesCommand() {
|
|
10
|
+
const templates = loadDashboardTemplatesCatalog().map(({ id, title, description, configSchema }) => ({
|
|
11
|
+
id,
|
|
12
|
+
title,
|
|
13
|
+
description,
|
|
14
|
+
configSchema,
|
|
15
|
+
}));
|
|
16
|
+
const templatesInfo = templates.map(t => `${t.id} — ${t.title}\n ${t.description}`);
|
|
17
|
+
const hasInfo = templatesInfo.length > 0;
|
|
18
|
+
output(hasInfo ? templatesInfo.join('\n') : '(no templates in catalog)', { ok: true, templates });
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export { templatesCommand };
|