local-bdk-cli 1.0.7

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/README.md ADDED
@@ -0,0 +1,3 @@
1
+ # BoldDesk App Template
2
+
3
+ Minimal scaffold for BoldDesk apps. Use `bdk apps:new my-app` to scaffold a new project.
@@ -0,0 +1,314 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.registerCreate = registerCreate;
7
+ const fs_1 = __importDefault(require("fs"));
8
+ const path_1 = __importDefault(require("path"));
9
+ // inquirer is ESM-only; import dynamically at runtime to avoid require() errors
10
+ const child_process_1 = require("child_process");
11
+ const package_1 = require("../lib/package");
12
+ const createApp_1 = require("../utils/createApp");
13
+ const uploadApp_1 = require("../utils/uploadApp");
14
+ const appConfig_1 = require("../utils/appConfig");
15
+ const getAppSettings_1 = require("../utils/getAppSettings");
16
+ function registerCreate(program) {
17
+ program
18
+ .command('apps:create [appDirectories...]')
19
+ .description('Scaffold one or more new app projects (usage: apps:create ./appName)')
20
+ .option('--template <template>', 'template name', 'default')
21
+ .option('--path <dir>', 'base path for created directories', '.')
22
+ .option('--force', 'overwrite if exists', false)
23
+ .option('--git', 'initialize a git repository', false)
24
+ .option('--npm', 'run npm init -y in the new project', false)
25
+ .option('--deploy', 'upload and deploy the app to BoldDesk', false)
26
+ .option('--api-url <url>', 'BoldDesk API base URL (env BOLD_API_URL)')
27
+ .option('--token <token>', 'BoldDesk API token (env BOLD_API_TOKEN)')
28
+ .action(async (...args) => {
29
+ // normalize action arguments: support both (appDirectories..., opts) and (opts) shapes
30
+ let opts = {};
31
+ let appDirectories = ['.'];
32
+ if (args.length === 0) {
33
+ opts = {};
34
+ appDirectories = ['.'];
35
+ }
36
+ else {
37
+ // pick the last plain object (non-array) as options if present
38
+ for (let i = args.length - 1; i >= 0; i--) {
39
+ const a = args[i];
40
+ if (typeof a === 'object' && !Array.isArray(a)) {
41
+ opts = a;
42
+ break;
43
+ }
44
+ }
45
+ // collect directory arguments: strings or arrays of strings
46
+ const collected = [];
47
+ for (const a of args) {
48
+ if (typeof a === 'string')
49
+ collected.push(a);
50
+ else if (Array.isArray(a)) {
51
+ for (const s of a)
52
+ if (typeof s === 'string')
53
+ collected.push(s);
54
+ }
55
+ }
56
+ appDirectories = collected.length ? collected : ['.'];
57
+ }
58
+ const template = opts.template || 'default';
59
+ const basePath = path_1.default.resolve(opts.path || '.');
60
+ // flatten nested array if commander wrapped variadic into single array
61
+ if (Array.isArray(appDirectories) && appDirectories.length === 1 && Array.isArray(appDirectories[0])) {
62
+ appDirectories = appDirectories[0];
63
+ }
64
+ const templatesDir = path_1.default.resolve(__dirname, '../../templates', template);
65
+ if (!fs_1.default.existsSync(templatesDir)) {
66
+ console.error(`Template not found: ${templatesDir}`);
67
+ process.exit(2);
68
+ }
69
+ for (const appDir of appDirectories) {
70
+ const targetDir = path_1.default.resolve(basePath, appDir);
71
+ const appName = path_1.default.basename(targetDir);
72
+ if (fs_1.default.existsSync(targetDir) && !opts.force) {
73
+ const dynamicImport = new Function('s', 'return import(s)');
74
+ const _inquirer = (await dynamicImport('inquirer')).default || (await dynamicImport('inquirer'));
75
+ const { overwrite } = await _inquirer.prompt([
76
+ {
77
+ type: 'confirm',
78
+ name: 'overwrite',
79
+ message: `Target directory ${targetDir} exists. Overwrite?`,
80
+ default: false
81
+ }
82
+ ]);
83
+ if (!overwrite) {
84
+ console.error('Aborted by user.');
85
+ continue;
86
+ }
87
+ }
88
+ fs_1.default.mkdirSync(targetDir, { recursive: true });
89
+ // Copy template directory. Use built-in cp if available, otherwise fallback to recursive copy.
90
+ if (fs_1.default.promises.cp) {
91
+ await fs_1.default.promises.cp(templatesDir, targetDir, { recursive: true });
92
+ }
93
+ else {
94
+ // recursive copy fallback
95
+ async function copyRecursive(src, dest) {
96
+ const entries = await fs_1.default.promises.readdir(src, { withFileTypes: true });
97
+ await fs_1.default.promises.mkdir(dest, { recursive: true });
98
+ for (const entry of entries) {
99
+ const srcPath = path_1.default.join(src, entry.name);
100
+ const destPath = path_1.default.join(dest, entry.name);
101
+ if (entry.isDirectory()) {
102
+ await copyRecursive(srcPath, destPath);
103
+ }
104
+ else if (entry.isSymbolicLink()) {
105
+ try {
106
+ const link = await fs_1.default.promises.readlink(srcPath);
107
+ await fs_1.default.promises.symlink(link, destPath);
108
+ }
109
+ catch (e) {
110
+ // fallback to copyFile if symlink fails
111
+ await fs_1.default.promises.copyFile(srcPath, destPath);
112
+ }
113
+ }
114
+ else {
115
+ await fs_1.default.promises.copyFile(srcPath, destPath);
116
+ }
117
+ }
118
+ }
119
+ await copyRecursive(templatesDir, targetDir);
120
+ }
121
+ // Ensure template contains basic assets and translations. If the template
122
+ // did not include them, create defaults (iframe, logos, translations).
123
+ try {
124
+ const assetsDir = path_1.default.join(targetDir, 'assets');
125
+ if (!fs_1.default.existsSync(assetsDir))
126
+ fs_1.default.mkdirSync(assetsDir, { recursive: true });
127
+ const iframePath = path_1.default.join(assetsDir, 'iframe.html');
128
+ if (!fs_1.default.existsSync(iframePath)) {
129
+ fs_1.default.writeFileSync(iframePath, `<!doctype html>\n<html lang="en">\n <head>\n <meta charset="utf-8" />\n <meta name="viewport" content="width=device-width,initial-scale=1" />\n <title>Sample Iframe</title>\n <style>body{font-family:Arial,Helvetica,sans-serif;padding:20px}</style>\n </head>\n <body>\n <h1>Hello from the app iframe</h1>\n <p>This is a simple placeholder iframe page generated by the CLI.</p>\n </body>\n</html>\n`, 'utf8');
130
+ }
131
+ const logoPath = path_1.default.join(assetsDir, 'logo.svg');
132
+ if (!fs_1.default.existsSync(logoPath)) {
133
+ fs_1.default.writeFileSync(logoPath, `<svg xmlns="http://www.w3.org/2000/svg" width="240" height="240" viewBox="0 0 240 240">\n <rect width="240" height="240" rx="24" fill="#0b6c6c" />\n <g fill="#fff" transform="translate(40,40)">\n <rect x="0" y="0" width="160" height="160" rx="12" />\n </g>\n</svg>\n`, 'utf8');
134
+ }
135
+ const logoSmall = path_1.default.join(assetsDir, 'logo-small.svg');
136
+ if (!fs_1.default.existsSync(logoSmall)) {
137
+ fs_1.default.writeFileSync(logoSmall, `<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64">\n <rect width="64" height="64" rx="8" fill="#0b6c6c" />\n <rect x="12" y="12" width="40" height="40" rx="6" fill="#fff" />\n</svg>\n`, 'utf8');
138
+ }
139
+ const translationsDir = path_1.default.join(targetDir, 'translations');
140
+ if (!fs_1.default.existsSync(translationsDir))
141
+ fs_1.default.mkdirSync(translationsDir, { recursive: true });
142
+ const enPath = path_1.default.join(translationsDir, 'en.json');
143
+ if (!fs_1.default.existsSync(enPath)) {
144
+ fs_1.default.writeFileSync(enPath, JSON.stringify({
145
+ app: {
146
+ name: 'Sample BoldDesk App',
147
+ short_description: 'A short description of your BoldDesk app.',
148
+ long_description: 'A longer description of your BoldDesk app and its features. Replace this text with real product details.',
149
+ installation_instructions: 'Follow the installation steps in the BoldDesk admin to install this app.'
150
+ }
151
+ }, null, 2), 'utf8');
152
+ }
153
+ }
154
+ catch (e) {
155
+ // ignore any template-creation errors
156
+ }
157
+ const manifestPath = path_1.default.resolve(targetDir, 'manifest.json');
158
+ // If template didn't include a manifest, create a sensible default so
159
+ // the CLI can prompt and the app can be packaged.
160
+ if (!fs_1.default.existsSync(manifestPath)) {
161
+ const defaultManifest = {
162
+ name: appName,
163
+ developer: {
164
+ name: '',
165
+ contactEmail: '',
166
+ supportEmail: '',
167
+ privacyUrl: '',
168
+ websiteUrl: '',
169
+ termsOfUseUrl: ''
170
+ },
171
+ widgets: [
172
+ {
173
+ url: 'assets/iframe.html',
174
+ name: 'Sample Widget',
175
+ location: 'desk.ticket.view.rightpanel'
176
+ }
177
+ ],
178
+ defaultLocale: 'en',
179
+ domainWhitelist: [],
180
+ private: true,
181
+ product: 'BoldDesk',
182
+ version: '1.0.0',
183
+ frameworkVersion: '2.0',
184
+ description: ''
185
+ };
186
+ try {
187
+ fs_1.default.writeFileSync(manifestPath, JSON.stringify(defaultManifest, null, 2), 'utf8');
188
+ }
189
+ catch (e) {
190
+ // ignore write errors
191
+ }
192
+ }
193
+ if (fs_1.default.existsSync(manifestPath)) {
194
+ const manifest = JSON.parse(fs_1.default.readFileSync(manifestPath, 'utf8'));
195
+ const dev = (manifest.developer && typeof manifest.developer === 'object') ? manifest.developer : {};
196
+ const dynamicImport = new Function('s', 'return import(s)');
197
+ const _inquirer = (await dynamicImport('inquirer')).default || (await dynamicImport('inquirer'));
198
+ // Placeholder sentinel values from the template — treat them as empty
199
+ const templatePlaceholders = new Set([
200
+ 'Your Name', 'you@example.com', 'https://example.com/privacy',
201
+ 'https://example.com', 'https://example.com/terms',
202
+ 'sample-app', 'A sample BoldDesk app scaffolded by the bdk CLI'
203
+ ]);
204
+ const clean = (v) => (v && typeof v === 'string' && !templatePlaceholders.has(v.trim())) ? v : '';
205
+ const answers = await _inquirer.prompt([
206
+ { name: 'name', message: 'App name', default: clean(manifest.name) || appName },
207
+ { name: 'version', message: 'Version', default: manifest.version || '1.0.0' },
208
+ { name: 'description', message: 'Description', default: clean(manifest.description) },
209
+ // developer fields — always default to '' so pressing Enter stores empty string
210
+ { name: 'devName', message: 'Developer name', default: clean(dev.name) },
211
+ { name: 'devContactEmail', message: 'Developer contact email', default: clean(dev.contactEmail) },
212
+ { name: 'devSupportEmail', message: 'Developer support email', default: clean(dev.supportEmail) },
213
+ { name: 'devPrivacyUrl', message: 'Developer privacy URL', default: clean(dev.privacyUrl) },
214
+ { name: 'devWebsiteUrl', message: 'Developer website URL', default: clean(dev.websiteUrl) },
215
+ { name: 'devTermsUrl', message: 'Developer terms of use URL', default: clean(dev.termsOfUseUrl) }
216
+ ]);
217
+ const newManifest = {
218
+ ...manifest,
219
+ name: answers.name,
220
+ version: answers.version,
221
+ description: answers.description,
222
+ developer: {
223
+ name: answers.devName,
224
+ contactEmail: answers.devContactEmail,
225
+ supportEmail: answers.devSupportEmail,
226
+ privacyUrl: answers.devPrivacyUrl,
227
+ websiteUrl: answers.devWebsiteUrl,
228
+ termsOfUseUrl: answers.devTermsUrl
229
+ }
230
+ };
231
+ // Remove legacy 'author' field if present
232
+ delete newManifest.author;
233
+ fs_1.default.writeFileSync(manifestPath, JSON.stringify(newManifest, null, 2), 'utf8');
234
+ console.log('Updated manifest.json with provided values.');
235
+ }
236
+ if (opts.npm) {
237
+ try {
238
+ console.log('Running `npm init -y`...');
239
+ (0, child_process_1.execSync)('npm init -y', { cwd: targetDir, stdio: 'inherit' });
240
+ }
241
+ catch (e) {
242
+ console.warn('npm init failed:', e.message);
243
+ }
244
+ }
245
+ if (opts.git) {
246
+ try {
247
+ console.log('Initializing git repository...');
248
+ (0, child_process_1.execSync)('git init', { cwd: targetDir, stdio: 'inherit' });
249
+ (0, child_process_1.execSync)('git add .', { cwd: targetDir, stdio: 'inherit' });
250
+ (0, child_process_1.execSync)('git commit -m "chore: scaffold initial project"', { cwd: targetDir, stdio: 'ignore' });
251
+ }
252
+ catch (e) {
253
+ console.warn('git init/commit failed (git may not be installed or user identity not set).');
254
+ }
255
+ }
256
+ console.log(`Created ${appName} at ${targetDir} from template ${template}`);
257
+ // If deploy flag is set, package and upload to BoldDesk
258
+ if (opts.deploy) {
259
+ const API_URL = opts.apiUrl || process.env.BOLD_API_URL;
260
+ const API_TOKEN = opts.token || process.env.BOLD_API_TOKEN;
261
+ if (!API_URL || !API_TOKEN) {
262
+ console.error('Missing BoldDesk API configuration. Set --api-url and --token or BOLD_API_URL and BOLD_API_TOKEN environment variables.');
263
+ process.exit(4);
264
+ }
265
+ try {
266
+ console.log('Creating app package...');
267
+ const pkgPath = await (0, package_1.createAppPkg)(targetDir, appName);
268
+ console.log('Uploading package to BoldDesk...');
269
+ const uploadRes = await (0, createApp_1.uploadAppPkg)(API_URL, API_TOKEN, pkgPath);
270
+ if (!uploadRes || !uploadRes.upload_id) {
271
+ console.error('Upload failed: no upload_id returned');
272
+ process.exit(5);
273
+ }
274
+ console.log('Deploying uploaded package...');
275
+ const deployRes = await (0, createApp_1.deployApp)(API_URL, API_TOKEN, uploadRes.upload_id, appName);
276
+ if (!deployRes || !deployRes.job_id) {
277
+ console.error('Deploy failed: no job_id returned');
278
+ process.exit(6);
279
+ }
280
+ console.log('Polling job status...');
281
+ const jobResult = await (0, uploadApp_1.getUploadJobStatus)(API_URL, API_TOKEN, deployRes.job_id);
282
+ if (!jobResult || !jobResult.app_id) {
283
+ console.error('Job did not produce an app_id');
284
+ process.exit(7);
285
+ }
286
+ console.log(`Deployed successfully with app_id: ${jobResult.app_id}`);
287
+ // Optionally create product installations if manifest specifies locations
288
+ const manifestPath2 = path_1.default.resolve(targetDir, 'manifest.json');
289
+ if (fs_1.default.existsSync(manifestPath2)) {
290
+ const manifest2 = JSON.parse(fs_1.default.readFileSync(manifestPath2, 'utf8'));
291
+ const allConfigs = (0, appConfig_1.getAllConfigs)(targetDir);
292
+ const configParams = (allConfigs === null || allConfigs === void 0 ? void 0 : allConfigs.parameters) || {};
293
+ const settings = manifest2.parameters ? await (0, getAppSettings_1.getAppSettings)(manifest2, configParams) : {};
294
+ if (!manifest2.requirementsOnly && manifest2.location) {
295
+ for (const product of Object.keys(manifest2.location)) {
296
+ const installed = await (0, createApp_1.createProductInstallation)(API_URL, API_TOKEN, settings, manifest2, jobResult.app_id, product);
297
+ if (!installed) {
298
+ console.error(`Failed to create installation for product ${product}`);
299
+ }
300
+ else {
301
+ console.log(`Installed on product ${product}`);
302
+ }
303
+ }
304
+ }
305
+ }
306
+ }
307
+ catch (e) {
308
+ console.error('Deploy flow failed:', e && e.message ? e.message : e);
309
+ process.exit(8);
310
+ }
311
+ }
312
+ }
313
+ });
314
+ }
@@ -0,0 +1,83 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.registerHelp = registerHelp;
4
+ const COMMANDS = [
5
+ {
6
+ command: 'apps:create',
7
+ alias: 'apps:create [appDirectories...]',
8
+ description: 'Scaffold one or more new app projects from a template.',
9
+ usage: 'bdk apps:create ./my-app [--template default] [--git] [--npm] [--deploy]',
10
+ },
11
+ {
12
+ command: 'apps:run',
13
+ alias: 'apps:run [source]',
14
+ description: 'Start a local dev server with live file serving for the app.',
15
+ usage: 'bdk apps:run [source] [--port 3000] [--env dev] [--open]',
16
+ },
17
+ {
18
+ command: 'apps:validate',
19
+ alias: 'apps:validate [appDirectory]',
20
+ description: 'Validate the app structure and manifest.json against the schema.',
21
+ usage: 'bdk apps:validate [appDirectory]',
22
+ },
23
+ {
24
+ command: 'apps:pack',
25
+ alias: 'apps:pack [source]',
26
+ description: 'Package the app into a zip/tar archive ready for publishing.',
27
+ usage: 'bdk apps:pack [source] [--output ./dist] [--format zip|tar]',
28
+ },
29
+ {
30
+ command: 'apps:test',
31
+ alias: 'apps:test [source]',
32
+ description: 'Run the app\'s unit and integration tests via npm test.',
33
+ usage: 'bdk apps:test [source] [--watch] [--coverage]',
34
+ },
35
+ {
36
+ command: 'apps:version',
37
+ alias: 'apps:version [source]',
38
+ description: 'Show or bump the app version in manifest.json.',
39
+ usage: 'bdk apps:version [source] [--bump patch|minor|major] [--set <version>]',
40
+ },
41
+ {
42
+ command: 'apps:help',
43
+ alias: 'apps:help [command]',
44
+ description: 'Show the list of available commands with descriptions and usage.',
45
+ usage: 'bdk apps:help [command]',
46
+ },
47
+ ];
48
+ function registerHelp(program) {
49
+ program
50
+ .command('help [commandName]')
51
+ .alias('apps:help')
52
+ .description('Show available commands and their descriptions')
53
+ .action((commandName) => {
54
+ if (commandName) {
55
+ // Show detailed info for a specific command
56
+ const found = COMMANDS.find((c) => c.command.toLowerCase() === commandName.toLowerCase() ||
57
+ c.command.toLowerCase() === `apps:${commandName.toLowerCase()}`);
58
+ if (!found) {
59
+ console.error(`\n Unknown command: "${commandName}"`);
60
+ console.error(` Run "bdk apps:help" to see all available commands.\n`);
61
+ process.exit(1);
62
+ return;
63
+ }
64
+ console.log('');
65
+ console.log(` Command : ${found.command}`);
66
+ console.log(` Synopsis : ${found.alias}`);
67
+ console.log(` Info : ${found.description}`);
68
+ console.log(` Usage : ${found.usage}`);
69
+ console.log('');
70
+ }
71
+ else {
72
+ // Show summary of all commands
73
+ console.log('');
74
+ console.log(' BoldDesk Developer Kit (bdk) — Available Commands');
75
+ console.log(' ' + '─'.repeat(55));
76
+ const maxCmd = Math.max(...COMMANDS.map((c) => c.command.length));
77
+ for (const cmd of COMMANDS) {
78
+ const padding = ' '.repeat(maxCmd - cmd.command.length + 2);
79
+ console.log(` ${cmd.command}${padding}${cmd.description}`);
80
+ }
81
+ }
82
+ });
83
+ }
@@ -0,0 +1,116 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.registerPack = registerPack;
7
+ const fs_1 = __importDefault(require("fs"));
8
+ const path_1 = __importDefault(require("path"));
9
+ const archiver_1 = __importDefault(require("archiver"));
10
+ const ajv_1 = __importDefault(require("ajv"));
11
+ const child_process_1 = require("child_process");
12
+ function registerPack(program) {
13
+ program
14
+ .command('pack [source]')
15
+ .alias('apps:pack')
16
+ .description('Package the app for publishing')
17
+ .option('--output <path>', 'output directory or filename', './dist')
18
+ .option('--format <fmt>', 'archive format (zip|tar)', 'zip')
19
+ .option('--no-validate', 'skip manifest validation')
20
+ .option('--source <dir>', 'source directory to package', '.')
21
+ .action(async (sourceArg, opts) => {
22
+ const sourceDir = path_1.default.resolve(process.cwd(), sourceArg || opts.source || '.');
23
+ const manifestPath = path_1.default.resolve(sourceDir, 'manifest.json');
24
+ if (!fs_1.default.existsSync(manifestPath)) {
25
+ console.error('manifest.json not found in source directory:', sourceDir);
26
+ process.exit(1);
27
+ }
28
+ if (opts.validate !== false) {
29
+ const schemaPath = path_1.default.resolve(__dirname, '../../schemas/manifest.schema.json');
30
+ if (fs_1.default.existsSync(schemaPath)) {
31
+ const manifest = JSON.parse(fs_1.default.readFileSync(manifestPath, 'utf8'));
32
+ const schema = JSON.parse(fs_1.default.readFileSync(schemaPath, 'utf8'));
33
+ const ajv = new ajv_1.default({ allowUnionTypes: true, allErrors: true });
34
+ const validate = ajv.compile(schema);
35
+ const valid = validate(manifest);
36
+ if (!valid) {
37
+ const errs = (validate.errors || []).map((e) => `${(e === null || e === void 0 ? void 0 : e.instancePath) || '/'}: ${e === null || e === void 0 ? void 0 : e.message}`).join('\n');
38
+ console.error(`Manifest validation failed for ${manifestPath}:\n${errs}`);
39
+ console.error(`Suggestions: ensure 'version' is a valid semver (e.g. 0.1.0), or run 'bdk apps:version <source> --set 0.1.0'. To skip validation: use '--no-validate'.`);
40
+ process.exit(2);
41
+ }
42
+ }
43
+ else {
44
+ console.warn('Schema not found; skipping schema validation.');
45
+ }
46
+ }
47
+ const pkgPath = path_1.default.resolve(sourceDir, 'package.json');
48
+ if (fs_1.default.existsSync(pkgPath)) {
49
+ try {
50
+ const pkg = JSON.parse(fs_1.default.readFileSync(pkgPath, 'utf8'));
51
+ if (pkg.scripts && pkg.scripts.build) {
52
+ try {
53
+ console.log('Running build script (npm run build)...');
54
+ (0, child_process_1.execSync)('npm run build', { cwd: sourceDir, stdio: 'inherit' });
55
+ }
56
+ catch (e) {
57
+ console.warn('Build failed or returned non-zero exit code; continuing to pack.');
58
+ }
59
+ }
60
+ }
61
+ catch (e) {
62
+ // ignore
63
+ }
64
+ }
65
+ const manifest = JSON.parse(fs_1.default.readFileSync(manifestPath, 'utf8'));
66
+ const name = manifest.name || path_1.default.basename(sourceDir);
67
+ const version = manifest.version || '0.0.0';
68
+ const fmt = (opts.format || 'zip').toLowerCase();
69
+ const outTarget = opts.output || './dist';
70
+ // Resolve output path relative to source directory, not current working directory
71
+ let outputPath = path_1.default.resolve(sourceDir, outTarget);
72
+ try {
73
+ const stat = fs_1.default.existsSync(outputPath) && fs_1.default.statSync(outputPath);
74
+ if (!stat || stat.isDirectory()) {
75
+ fs_1.default.mkdirSync(outputPath, { recursive: true });
76
+ const filename = `${name}-${version}.${fmt === 'zip' ? 'zip' : 'tar'}`;
77
+ outputPath = path_1.default.join(outputPath, filename);
78
+ }
79
+ }
80
+ catch (e) {
81
+ fs_1.default.mkdirSync(path_1.default.dirname(outputPath), { recursive: true });
82
+ }
83
+ const output = fs_1.default.createWriteStream(outputPath);
84
+ const archive = (0, archiver_1.default)(fmt === 'zip' ? 'zip' : 'tar', fmt === 'zip' ? {} : { gzip: true });
85
+ output.on('close', () => {
86
+ console.log(`Created archive: ${outputPath} (${archive.pointer()} bytes)`);
87
+ });
88
+ archive.on('warning', (err) => {
89
+ if (err.code === 'ENOENT')
90
+ console.warn(err.message);
91
+ else
92
+ throw err;
93
+ });
94
+ archive.on('error', (err) => {
95
+ throw err;
96
+ });
97
+ archive.pipe(output);
98
+ const preferDirs = ['dist', 'build', 'public'];
99
+ let addedAny = false;
100
+ for (const d of preferDirs) {
101
+ const p = path_1.default.join(sourceDir, d);
102
+ if (fs_1.default.existsSync(p) && fs_1.default.statSync(p).isDirectory()) {
103
+ archive.directory(p, d);
104
+ addedAny = true;
105
+ }
106
+ }
107
+ if (!addedAny) {
108
+ archive.glob('**/*', {
109
+ cwd: sourceDir,
110
+ ignore: ['node_modules/**', '**/node_modules/**']
111
+ });
112
+ }
113
+ archive.file(manifestPath, { name: 'manifest.json' });
114
+ await archive.finalize();
115
+ });
116
+ }
@@ -0,0 +1,97 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.registerPublish = registerPublish;
7
+ const fs_1 = __importDefault(require("fs"));
8
+ const path_1 = __importDefault(require("path"));
9
+ const child_process_1 = require("child_process");
10
+ function registerPublish(program) {
11
+ program
12
+ .command('publish [source]')
13
+ .alias('apps:publish')
14
+ .description('Publish app archive to BoldDesk marketplace')
15
+ .option('--file <path>', 'package file to upload (zip or tar)')
16
+ .option('--token <token>', 'API token for BoldDesk')
17
+ .option('--url <url>', 'publish endpoint URL', 'https://api.bolddesk.com/apps/upload')
18
+ .option('--dry-run', 'do not actually upload, show what would be sent', false)
19
+ .option('--private', 'mark uploaded app as private', false)
20
+ .option('--source <dir>', 'source directory to pack before publishing', '.')
21
+ .action(async (_sourceArg, _opts) => {
22
+ // Command is currently disabled — exit silently without any output
23
+ return;
24
+ /* ── disabled block ─────────────────────────────────────────────── */
25
+ // eslint-disable-next-line no-unreachable
26
+ try {
27
+ let filePath = _opts.file;
28
+ const sourceDir = path_1.default.resolve(process.cwd(), _sourceArg || _opts.source || '.');
29
+ if (!filePath) {
30
+ console.log('No package file given — running `bdk pack` to create archive...');
31
+ try {
32
+ const cliEntry = path_1.default.resolve(__dirname, '../index.js');
33
+ const sourceArg_str = _sourceArg ? `"${_sourceArg}"` : '.';
34
+ (0, child_process_1.execSync)(`node "${cliEntry}" pack ${sourceArg_str} --output ./dist --format zip`, { cwd: process.cwd(), stdio: 'inherit' });
35
+ }
36
+ catch (e) {
37
+ console.error('Automatic pack failed; please run `bdk pack` manually or provide --file');
38
+ process.exit(1);
39
+ }
40
+ const manifestPath = path_1.default.join(sourceDir, 'manifest.json');
41
+ if (!fs_1.default.existsSync(manifestPath)) {
42
+ console.error('manifest.json missing; cannot determine archive filename.');
43
+ process.exit(1);
44
+ }
45
+ const manifest = JSON.parse(fs_1.default.readFileSync(manifestPath, 'utf8'));
46
+ const name = manifest.name || path_1.default.basename(sourceDir);
47
+ const version = manifest.version || '0.0.0';
48
+ const candidate = path_1.default.resolve(sourceDir, 'dist', `${name}-${version}.zip`);
49
+ if (!fs_1.default.existsSync(candidate)) {
50
+ console.error('Expected archive not found:', candidate);
51
+ process.exit(1);
52
+ }
53
+ filePath = candidate;
54
+ }
55
+ filePath = path_1.default.resolve(process.cwd(), filePath);
56
+ if (!fs_1.default.existsSync(filePath)) {
57
+ console.error('Package file not found:', filePath);
58
+ process.exit(1);
59
+ }
60
+ if (_opts.dryRun) {
61
+ console.log('Dry-run: would upload', filePath, 'to', _opts.url);
62
+ process.exit(0);
63
+ }
64
+ const token = _opts.token || process.env.BOLD_API_TOKEN || process.env.BDK_TOKEN;
65
+ if (!token) {
66
+ console.error('No API token provided. Use --token or set BOLD_API_TOKEN/BDK_TOKEN env var.');
67
+ process.exit(1);
68
+ }
69
+ const FormData = require('form-data');
70
+ const fetch = require('node-fetch');
71
+ const form = new FormData();
72
+ form.append('file', fs_1.default.createReadStream(filePath));
73
+ form.append('private', _opts.private ? 'true' : 'false');
74
+ console.log(`Uploading ${path_1.default.basename(filePath)} to ${_opts.url} ...`);
75
+ const res = await fetch(_opts.url, {
76
+ method: 'POST',
77
+ headers: {
78
+ Authorization: `Bearer ${token}`,
79
+ ...form.getHeaders()
80
+ },
81
+ body: form
82
+ });
83
+ if (!res.ok) {
84
+ const text = await res.text();
85
+ console.error('Upload failed:', res.status, res.statusText, text);
86
+ process.exit(2);
87
+ }
88
+ const json = await res.json();
89
+ console.log('Publish succeeded:', JSON.stringify(json, null, 2));
90
+ }
91
+ catch (err) {
92
+ console.error('Publish failed:', err && err.message ? err.message : err);
93
+ process.exit(3);
94
+ }
95
+ /* ── end disabled block ─────────────────────────────────────────── */
96
+ });
97
+ }