genbox 1.0.117 → 1.0.119
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/list.js +16 -1
- package/dist/commands/template.js +372 -0
- package/dist/index.js +3 -1
- package/package.json +1 -1
package/dist/commands/list.js
CHANGED
|
@@ -96,7 +96,22 @@ exports.listCommand = new commander_1.Command('list')
|
|
|
96
96
|
// Show auto-destroy status (not applicable for stopped/terminated)
|
|
97
97
|
let protectedInfo = '';
|
|
98
98
|
if (genbox.status === 'stopped') {
|
|
99
|
-
|
|
99
|
+
// Show snapshot status if available
|
|
100
|
+
if (genbox.snapshot) {
|
|
101
|
+
if (genbox.snapshot.status === 'creating') {
|
|
102
|
+
const progress = genbox.snapshot.progress || 0;
|
|
103
|
+
protectedInfo = chalk_1.default.yellow(` (snapshot: ${progress}%)`);
|
|
104
|
+
}
|
|
105
|
+
else if (genbox.snapshot.status === 'ready') {
|
|
106
|
+
protectedInfo = chalk_1.default.dim(' → gb start to resume (instant)');
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
protectedInfo = chalk_1.default.dim(' → gb start to resume');
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
protectedInfo = chalk_1.default.dim(' → gb start to resume');
|
|
114
|
+
}
|
|
100
115
|
}
|
|
101
116
|
else if (genbox.status === 'terminated') {
|
|
102
117
|
// No extra info for terminated
|
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.templateCommand = void 0;
|
|
40
|
+
const commander_1 = require("commander");
|
|
41
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
42
|
+
const ora_1 = __importDefault(require("ora"));
|
|
43
|
+
const fs = __importStar(require("fs"));
|
|
44
|
+
const child_process_1 = require("child_process");
|
|
45
|
+
const api_1 = require("../api");
|
|
46
|
+
const config_loader_1 = require("../config-loader");
|
|
47
|
+
function formatDate(dateStr) {
|
|
48
|
+
const date = new Date(dateStr);
|
|
49
|
+
const now = new Date();
|
|
50
|
+
const diffMs = now.getTime() - date.getTime();
|
|
51
|
+
const diffMins = Math.floor(diffMs / 60000);
|
|
52
|
+
const diffHours = Math.floor(diffMs / 3600000);
|
|
53
|
+
const diffDays = Math.floor(diffMs / 86400000);
|
|
54
|
+
if (diffMins < 1)
|
|
55
|
+
return 'just now';
|
|
56
|
+
if (diffMins < 60)
|
|
57
|
+
return `${diffMins}m ago`;
|
|
58
|
+
if (diffHours < 24)
|
|
59
|
+
return `${diffHours}h ago`;
|
|
60
|
+
if (diffDays < 7)
|
|
61
|
+
return `${diffDays}d ago`;
|
|
62
|
+
return date.toLocaleDateString('en-US', {
|
|
63
|
+
month: 'short',
|
|
64
|
+
day: 'numeric',
|
|
65
|
+
year: date.getFullYear() !== now.getFullYear() ? 'numeric' : undefined,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
function getStatusColor(status) {
|
|
69
|
+
switch (status) {
|
|
70
|
+
case 'ready': return chalk_1.default.green;
|
|
71
|
+
case 'building': return chalk_1.default.yellow;
|
|
72
|
+
case 'failed': return chalk_1.default.red;
|
|
73
|
+
case 'deprecated': return chalk_1.default.dim;
|
|
74
|
+
default: return chalk_1.default.white;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
function getStatusIcon(status) {
|
|
78
|
+
switch (status) {
|
|
79
|
+
case 'ready': return '✓';
|
|
80
|
+
case 'building': return '○';
|
|
81
|
+
case 'failed': return '✗';
|
|
82
|
+
case 'deprecated': return '–';
|
|
83
|
+
default: return '?';
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
exports.templateCommand = new commander_1.Command('template')
|
|
87
|
+
.description('Manage project templates (golden images) for fast boot')
|
|
88
|
+
.action(() => {
|
|
89
|
+
// Default action - show help
|
|
90
|
+
exports.templateCommand.outputHelp();
|
|
91
|
+
});
|
|
92
|
+
// Subcommand: build
|
|
93
|
+
exports.templateCommand
|
|
94
|
+
.command('build')
|
|
95
|
+
.description('Build a project template from genbox.yaml')
|
|
96
|
+
.option('-r, --repo <url>', 'Git repository URL')
|
|
97
|
+
.option('-b, --branch <name>', 'Git branch', 'main')
|
|
98
|
+
.option('-p, --project <id>', 'Project ID to associate with')
|
|
99
|
+
.option('-w, --wait', 'Wait for build to complete')
|
|
100
|
+
.action(async (options) => {
|
|
101
|
+
try {
|
|
102
|
+
// Try to find genbox.yaml in current directory or specified path
|
|
103
|
+
let repoUrl = options.repo;
|
|
104
|
+
let branch = options.branch;
|
|
105
|
+
let genboxYamlContent;
|
|
106
|
+
const cwd = process.cwd();
|
|
107
|
+
if (!repoUrl) {
|
|
108
|
+
// Try to get repo URL from git
|
|
109
|
+
try {
|
|
110
|
+
repoUrl = (0, child_process_1.execSync)('git config --get remote.origin.url', {
|
|
111
|
+
cwd,
|
|
112
|
+
encoding: 'utf-8'
|
|
113
|
+
}).trim();
|
|
114
|
+
// Get current branch
|
|
115
|
+
const currentBranch = (0, child_process_1.execSync)('git rev-parse --abbrev-ref HEAD', {
|
|
116
|
+
cwd,
|
|
117
|
+
encoding: 'utf-8'
|
|
118
|
+
}).trim();
|
|
119
|
+
if (!options.branch) {
|
|
120
|
+
branch = currentBranch;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
catch {
|
|
124
|
+
console.error(chalk_1.default.red('Could not detect git repository. Please specify --repo'));
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
if (!repoUrl) {
|
|
129
|
+
console.error(chalk_1.default.red('Repository URL required. Use --repo or run from a git directory.'));
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
// Check for genbox.yaml
|
|
133
|
+
if (!config_loader_1.configLoader.hasConfig(cwd)) {
|
|
134
|
+
console.error(chalk_1.default.red('No genbox.yaml found. Create one first with: gb init'));
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
// Read the raw content for hashing
|
|
138
|
+
const yamlPath = config_loader_1.configLoader.getConfigPath(cwd);
|
|
139
|
+
genboxYamlContent = fs.readFileSync(yamlPath, 'utf-8');
|
|
140
|
+
console.log(chalk_1.default.blue('Building template:'));
|
|
141
|
+
console.log(chalk_1.default.dim(` Repo: ${repoUrl}`));
|
|
142
|
+
console.log(chalk_1.default.dim(` Branch: ${branch}`));
|
|
143
|
+
console.log('');
|
|
144
|
+
const spinner = (0, ora_1.default)('Initiating template build...').start();
|
|
145
|
+
const result = await (0, api_1.fetchApi)('/genboxes/templates/build', {
|
|
146
|
+
method: 'POST',
|
|
147
|
+
body: JSON.stringify({
|
|
148
|
+
repoUrl,
|
|
149
|
+
branch,
|
|
150
|
+
genboxYamlContent,
|
|
151
|
+
projectId: options.project,
|
|
152
|
+
}),
|
|
153
|
+
});
|
|
154
|
+
if (result.action === 'exists') {
|
|
155
|
+
spinner.succeed(chalk_1.default.green('Template already exists with same configuration'));
|
|
156
|
+
if (result.template) {
|
|
157
|
+
console.log('');
|
|
158
|
+
console.log(chalk_1.default.dim(` Name: ${result.template.name}`));
|
|
159
|
+
console.log(chalk_1.default.dim(` Version: v${result.template.version}`));
|
|
160
|
+
console.log(chalk_1.default.dim(` Status: ${result.template.status}`));
|
|
161
|
+
}
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
if (result.action === 'building' && result.template) {
|
|
165
|
+
spinner.succeed(chalk_1.default.green('Template build started'));
|
|
166
|
+
console.log('');
|
|
167
|
+
console.log(chalk_1.default.dim(` Name: ${result.template.name}`));
|
|
168
|
+
console.log(chalk_1.default.dim(` Version: v${result.template.version}`));
|
|
169
|
+
console.log('');
|
|
170
|
+
console.log(chalk_1.default.yellow(' Build typically takes 10-20 minutes.'));
|
|
171
|
+
console.log(chalk_1.default.dim(` Check progress: gb template status ${result.template.id}`));
|
|
172
|
+
if (options.wait) {
|
|
173
|
+
console.log('');
|
|
174
|
+
await waitForBuild(result.template.id);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
catch (error) {
|
|
179
|
+
if (error instanceof api_1.AuthenticationError) {
|
|
180
|
+
(0, api_1.handleApiError)(error);
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
console.error(chalk_1.default.red(`Error: ${error.message}`));
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
// Subcommand: list
|
|
187
|
+
exports.templateCommand
|
|
188
|
+
.command('list')
|
|
189
|
+
.description('List all project templates')
|
|
190
|
+
.option('-p, --project <id>', 'Filter by project ID')
|
|
191
|
+
.option('--all', 'Show all templates including deprecated')
|
|
192
|
+
.option('--json', 'Output as JSON')
|
|
193
|
+
.action(async (options) => {
|
|
194
|
+
try {
|
|
195
|
+
const params = new URLSearchParams();
|
|
196
|
+
if (options.project)
|
|
197
|
+
params.set('projectId', options.project);
|
|
198
|
+
if (!options.all)
|
|
199
|
+
params.set('status', 'ready');
|
|
200
|
+
const result = await (0, api_1.fetchApi)(`/genboxes/templates?${params.toString()}`);
|
|
201
|
+
if (options.json) {
|
|
202
|
+
console.log(JSON.stringify(result, null, 2));
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
if (!result || result.length === 0) {
|
|
206
|
+
console.log(chalk_1.default.dim('No templates found.'));
|
|
207
|
+
console.log('');
|
|
208
|
+
console.log(chalk_1.default.dim('Build a template with: gb template build'));
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
console.log(chalk_1.default.blue(`Found ${result.length} template${result.length !== 1 ? 's' : ''}:`));
|
|
212
|
+
console.log('');
|
|
213
|
+
for (const template of result) {
|
|
214
|
+
const statusColor = getStatusColor(template.status);
|
|
215
|
+
const statusIcon = getStatusIcon(template.status);
|
|
216
|
+
const statusBadge = statusColor(statusIcon);
|
|
217
|
+
const sizeStr = template.sizeGb ? `${template.sizeGb.toFixed(1)}GB` : '-';
|
|
218
|
+
const dateStr = formatDate(template.createdAt);
|
|
219
|
+
const usageStr = template.usageCount > 0 ? chalk_1.default.dim(`(${template.usageCount} uses)`) : '';
|
|
220
|
+
console.log(` ${statusBadge} ${chalk_1.default.cyan(template.name)} ${sizeStr} ${dateStr} ${usageStr}`);
|
|
221
|
+
console.log(chalk_1.default.dim(` ${template.repoUrl}@${template.branch}`));
|
|
222
|
+
}
|
|
223
|
+
console.log('');
|
|
224
|
+
console.log(chalk_1.default.dim('─'.repeat(50)));
|
|
225
|
+
console.log(chalk_1.default.dim('Build new: gb template build'));
|
|
226
|
+
}
|
|
227
|
+
catch (error) {
|
|
228
|
+
if (error instanceof api_1.AuthenticationError) {
|
|
229
|
+
(0, api_1.handleApiError)(error);
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
console.error(chalk_1.default.red(`Error: ${error.message}`));
|
|
233
|
+
}
|
|
234
|
+
});
|
|
235
|
+
// Subcommand: status
|
|
236
|
+
exports.templateCommand
|
|
237
|
+
.command('status [templateId]')
|
|
238
|
+
.description('Check template build status')
|
|
239
|
+
.option('-w, --watch', 'Watch build progress')
|
|
240
|
+
.action(async (templateId, options) => {
|
|
241
|
+
try {
|
|
242
|
+
// If no templateId, show status of most recent building template
|
|
243
|
+
if (!templateId) {
|
|
244
|
+
const result = await (0, api_1.fetchApi)('/genboxes/templates?limit=5');
|
|
245
|
+
const building = result.find(t => t.status === 'building');
|
|
246
|
+
if (building) {
|
|
247
|
+
templateId = building._id;
|
|
248
|
+
}
|
|
249
|
+
else if (result.length > 0) {
|
|
250
|
+
templateId = result[0]._id;
|
|
251
|
+
}
|
|
252
|
+
else {
|
|
253
|
+
console.log(chalk_1.default.dim('No templates found.'));
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
const showStatus = async () => {
|
|
258
|
+
const template = await (0, api_1.fetchApi)(`/genboxes/templates/${templateId}`);
|
|
259
|
+
console.clear();
|
|
260
|
+
console.log(chalk_1.default.blue(`Template: ${template.name}`));
|
|
261
|
+
console.log('');
|
|
262
|
+
console.log(`Status: ${getStatusColor(template.status)(template.status)}`);
|
|
263
|
+
console.log(`Version: v${template.version}`);
|
|
264
|
+
console.log(`Repo: ${template.repoUrl}`);
|
|
265
|
+
console.log(`Branch: ${template.branch}`);
|
|
266
|
+
console.log('');
|
|
267
|
+
if (template.status === 'building') {
|
|
268
|
+
const elapsed = template.buildStartedAt
|
|
269
|
+
? Math.floor((Date.now() - new Date(template.buildStartedAt).getTime()) / 1000)
|
|
270
|
+
: 0;
|
|
271
|
+
console.log(chalk_1.default.yellow(`Building... (${Math.floor(elapsed / 60)}m ${elapsed % 60}s)`));
|
|
272
|
+
console.log('');
|
|
273
|
+
console.log(chalk_1.default.dim('Build steps:'));
|
|
274
|
+
console.log(chalk_1.default.dim(' 1. Create temporary server'));
|
|
275
|
+
console.log(chalk_1.default.dim(' 2. Clone repository'));
|
|
276
|
+
console.log(chalk_1.default.dim(' 3. Install dependencies'));
|
|
277
|
+
console.log(chalk_1.default.dim(' 4. Pull Docker images'));
|
|
278
|
+
console.log(chalk_1.default.dim(' 5. Create snapshot'));
|
|
279
|
+
console.log(chalk_1.default.dim(' 6. Cleanup'));
|
|
280
|
+
}
|
|
281
|
+
else if (template.status === 'ready') {
|
|
282
|
+
console.log(chalk_1.default.green('Ready to use!'));
|
|
283
|
+
console.log('');
|
|
284
|
+
if (template.sizeGb)
|
|
285
|
+
console.log(`Size: ${template.sizeGb.toFixed(1)} GB`);
|
|
286
|
+
if (template.buildDurationSeconds) {
|
|
287
|
+
const mins = Math.floor(template.buildDurationSeconds / 60);
|
|
288
|
+
const secs = template.buildDurationSeconds % 60;
|
|
289
|
+
console.log(`Duration: ${mins}m ${secs}s`);
|
|
290
|
+
}
|
|
291
|
+
console.log(`Uses: ${template.usageCount}`);
|
|
292
|
+
}
|
|
293
|
+
else if (template.status === 'failed') {
|
|
294
|
+
console.log(chalk_1.default.red('Build failed!'));
|
|
295
|
+
if (template.errorMessage) {
|
|
296
|
+
console.log('');
|
|
297
|
+
console.log(chalk_1.default.red(`Error: ${template.errorMessage}`));
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
return template.status;
|
|
301
|
+
};
|
|
302
|
+
let status = await showStatus();
|
|
303
|
+
if (options.watch && status === 'building') {
|
|
304
|
+
console.log('');
|
|
305
|
+
console.log(chalk_1.default.dim('Watching build progress... (Ctrl+C to exit)'));
|
|
306
|
+
while (status === 'building') {
|
|
307
|
+
await new Promise(resolve => setTimeout(resolve, 10000));
|
|
308
|
+
status = await showStatus();
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
catch (error) {
|
|
313
|
+
if (error instanceof api_1.AuthenticationError) {
|
|
314
|
+
(0, api_1.handleApiError)(error);
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
317
|
+
console.error(chalk_1.default.red(`Error: ${error.message}`));
|
|
318
|
+
}
|
|
319
|
+
});
|
|
320
|
+
// Subcommand: delete
|
|
321
|
+
exports.templateCommand
|
|
322
|
+
.command('delete <templateId>')
|
|
323
|
+
.description('Delete a template')
|
|
324
|
+
.action(async (templateId) => {
|
|
325
|
+
try {
|
|
326
|
+
await (0, api_1.fetchApi)(`/genboxes/templates/${templateId}`, { method: 'DELETE' });
|
|
327
|
+
console.log(chalk_1.default.green('Template deleted.'));
|
|
328
|
+
}
|
|
329
|
+
catch (error) {
|
|
330
|
+
if (error instanceof api_1.AuthenticationError) {
|
|
331
|
+
(0, api_1.handleApiError)(error);
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
334
|
+
console.error(chalk_1.default.red(`Error: ${error.message}`));
|
|
335
|
+
}
|
|
336
|
+
});
|
|
337
|
+
async function waitForBuild(templateId) {
|
|
338
|
+
const spinner = (0, ora_1.default)('Building template...').start();
|
|
339
|
+
while (true) {
|
|
340
|
+
await new Promise(resolve => setTimeout(resolve, 15000));
|
|
341
|
+
try {
|
|
342
|
+
const template = await (0, api_1.fetchApi)(`/genboxes/templates/${templateId}`);
|
|
343
|
+
if (template.status === 'ready') {
|
|
344
|
+
spinner.succeed(chalk_1.default.green('Template build complete!'));
|
|
345
|
+
console.log('');
|
|
346
|
+
if (template.sizeGb)
|
|
347
|
+
console.log(chalk_1.default.dim(` Size: ${template.sizeGb.toFixed(1)} GB`));
|
|
348
|
+
if (template.buildDurationSeconds) {
|
|
349
|
+
const mins = Math.floor(template.buildDurationSeconds / 60);
|
|
350
|
+
console.log(chalk_1.default.dim(` Duration: ${mins}m ${template.buildDurationSeconds % 60}s`));
|
|
351
|
+
}
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
354
|
+
if (template.status === 'failed') {
|
|
355
|
+
spinner.fail(chalk_1.default.red('Template build failed'));
|
|
356
|
+
if (template.errorMessage) {
|
|
357
|
+
console.log(chalk_1.default.red(` ${template.errorMessage}`));
|
|
358
|
+
}
|
|
359
|
+
return;
|
|
360
|
+
}
|
|
361
|
+
// Still building
|
|
362
|
+
const elapsed = template.buildStartedAt
|
|
363
|
+
? Math.floor((Date.now() - new Date(template.buildStartedAt).getTime()) / 1000)
|
|
364
|
+
: 0;
|
|
365
|
+
spinner.text = `Building template... (${Math.floor(elapsed / 60)}m ${elapsed % 60}s)`;
|
|
366
|
+
}
|
|
367
|
+
catch (error) {
|
|
368
|
+
spinner.fail(chalk_1.default.red(`Error checking status: ${error.message}`));
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -37,6 +37,7 @@ const backup_1 = require("./commands/backup");
|
|
|
37
37
|
const backups_1 = require("./commands/backups");
|
|
38
38
|
const stop_1 = require("./commands/stop");
|
|
39
39
|
const start_1 = require("./commands/start");
|
|
40
|
+
const template_1 = require("./commands/template");
|
|
40
41
|
program
|
|
41
42
|
.addCommand(init_1.initCommand)
|
|
42
43
|
.addCommand(create_1.createCommand)
|
|
@@ -66,5 +67,6 @@ program
|
|
|
66
67
|
.addCommand(backup_1.backupCommand)
|
|
67
68
|
.addCommand(backups_1.backupsCommand)
|
|
68
69
|
.addCommand(stop_1.stopCommand)
|
|
69
|
-
.addCommand(start_1.startCommand)
|
|
70
|
+
.addCommand(start_1.startCommand)
|
|
71
|
+
.addCommand(template_1.templateCommand);
|
|
70
72
|
program.parse(process.argv);
|