@telemetryos/cli 1.16.1 → 1.17.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/CHANGELOG.md +11 -0
- package/package.json +2 -2
- package/dist/commands/archive.d.ts +0 -13
- package/dist/commands/archive.js +0 -49
- package/dist/commands/auth.d.ts +0 -2
- package/dist/commands/auth.js +0 -60
- package/dist/commands/claude-code.d.ts +0 -2
- package/dist/commands/claude-code.js +0 -29
- package/dist/commands/init.d.ts +0 -2
- package/dist/commands/init.js +0 -176
- package/dist/commands/publish.d.ts +0 -22
- package/dist/commands/publish.js +0 -238
- package/dist/commands/root.d.ts +0 -2
- package/dist/commands/root.js +0 -5
- package/dist/commands/serve.d.ts +0 -2
- package/dist/commands/serve.js +0 -7
- package/dist/index.d.ts +0 -2
- package/dist/index.js +0 -13
- package/dist/plugins/math-tools.d.ts +0 -2
- package/dist/plugins/math-tools.js +0 -18
- package/dist/services/api-client.d.ts +0 -18
- package/dist/services/api-client.js +0 -70
- package/dist/services/archiver.d.ts +0 -4
- package/dist/services/archiver.js +0 -65
- package/dist/services/build-poller.d.ts +0 -10
- package/dist/services/build-poller.js +0 -63
- package/dist/services/cli-config.d.ts +0 -10
- package/dist/services/cli-config.js +0 -45
- package/dist/services/config.d.ts +0 -5
- package/dist/services/config.js +0 -23
- package/dist/services/create-project.d.ts +0 -13
- package/dist/services/create-project.js +0 -188
- package/dist/services/generate-application.d.ts +0 -12
- package/dist/services/generate-application.js +0 -206
- package/dist/services/project-config.d.ts +0 -27
- package/dist/services/project-config.js +0 -54
- package/dist/services/run-server.d.ts +0 -5
- package/dist/services/run-server.js +0 -327
- package/dist/types/api.d.ts +0 -44
- package/dist/types/api.js +0 -1
- package/dist/types/applications.d.ts +0 -44
- package/dist/types/applications.js +0 -1
- package/dist/utils/ansi.d.ts +0 -11
- package/dist/utils/ansi.js +0 -11
- package/dist/utils/path-utils.d.ts +0 -55
- package/dist/utils/path-utils.js +0 -99
- package/dist/utils/template.d.ts +0 -2
- package/dist/utils/template.js +0 -30
- package/dist/utils/validate-project-name.d.ts +0 -19
- package/dist/utils/validate-project-name.js +0 -44
package/CHANGELOG.md
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@telemetryos/cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.17.0",
|
|
4
4
|
"description": "The official TelemetryOS application CLI package. Use it to build applications that run on the TelemetryOS platform",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"license": "",
|
|
26
26
|
"repository": "github:TelemetryTV/Application-API",
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@telemetryos/development-application-host-ui": "^1.
|
|
28
|
+
"@telemetryos/development-application-host-ui": "^1.17.0",
|
|
29
29
|
"@types/serve-handler": "^6.1.4",
|
|
30
30
|
"commander": "^14.0.0",
|
|
31
31
|
"ignore": "^6.0.2",
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { Command } from 'commander';
|
|
2
|
-
export type ArchiveCallbacks = {
|
|
3
|
-
onLog?: (line: string) => void;
|
|
4
|
-
onStateChange?: (state: string) => void;
|
|
5
|
-
onComplete?: (data: {
|
|
6
|
-
filename: string;
|
|
7
|
-
}) => void;
|
|
8
|
-
onError?: (error: Error) => void;
|
|
9
|
-
};
|
|
10
|
-
export declare const archiveCommand: Command;
|
|
11
|
-
export declare function handleArchiveCommand(projectPath: string, options: {
|
|
12
|
-
output?: string;
|
|
13
|
-
}, callbacks?: ArchiveCallbacks): Promise<void>;
|
package/dist/commands/archive.js
DELETED
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
import { Command } from 'commander';
|
|
2
|
-
import path from 'path';
|
|
3
|
-
import { loadProjectConfig } from '../services/project-config.js';
|
|
4
|
-
import { createArchive } from '../services/archiver.js';
|
|
5
|
-
import { ansi } from '../utils/ansi.js';
|
|
6
|
-
export const archiveCommand = new Command('archive')
|
|
7
|
-
.alias('zip')
|
|
8
|
-
.description('Create a project archive for manual upload')
|
|
9
|
-
.argument('[project-path]', 'Path to the project directory. Defaults to current working directory', process.cwd())
|
|
10
|
-
.option('-o, --output <path>', 'Output directory for the archive')
|
|
11
|
-
.action(handleArchiveCommand);
|
|
12
|
-
export async function handleArchiveCommand(projectPath, options, callbacks) {
|
|
13
|
-
var _a, _b, _c, _d, _e;
|
|
14
|
-
projectPath = path.resolve(process.cwd(), projectPath);
|
|
15
|
-
const logMessage = (callbacks === null || callbacks === void 0 ? void 0 : callbacks.onLog) || ((msg) => console.log(msg));
|
|
16
|
-
try {
|
|
17
|
-
// Step 1: Load project config
|
|
18
|
-
(_a = callbacks === null || callbacks === void 0 ? void 0 : callbacks.onStateChange) === null || _a === void 0 ? void 0 : _a.call(callbacks, 'loading');
|
|
19
|
-
logMessage(`\n${ansi.cyan}Loading project configuration...${ansi.reset}`);
|
|
20
|
-
const config = await loadProjectConfig(projectPath);
|
|
21
|
-
logMessage(` Project: ${ansi.bold}${config.name || path.basename(projectPath)}${ansi.reset}`);
|
|
22
|
-
if (config.version) {
|
|
23
|
-
logMessage(` Version: ${config.version}`);
|
|
24
|
-
}
|
|
25
|
-
// Step 2: Create archive directly in out/
|
|
26
|
-
(_b = callbacks === null || callbacks === void 0 ? void 0 : callbacks.onStateChange) === null || _b === void 0 ? void 0 : _b.call(callbacks, 'archiving');
|
|
27
|
-
logMessage(`\n${ansi.cyan}Archiving project...${ansi.reset}`);
|
|
28
|
-
const outDir = options.output
|
|
29
|
-
? path.resolve(process.cwd(), options.output)
|
|
30
|
-
: path.join(projectPath, 'out');
|
|
31
|
-
const { filename } = await createArchive(projectPath, outDir, (msg) => {
|
|
32
|
-
logMessage(` ${ansi.dim}${msg}${ansi.reset}`);
|
|
33
|
-
});
|
|
34
|
-
// Step 3: Report success
|
|
35
|
-
(_c = callbacks === null || callbacks === void 0 ? void 0 : callbacks.onStateChange) === null || _c === void 0 ? void 0 : _c.call(callbacks, 'success');
|
|
36
|
-
logMessage(`\n${ansi.green}${ansi.bold}Archive created!${ansi.reset}`);
|
|
37
|
-
logMessage(`\n ${ansi.dim}${outDir}/${filename}${ansi.reset}`);
|
|
38
|
-
logMessage('');
|
|
39
|
-
(_d = callbacks === null || callbacks === void 0 ? void 0 : callbacks.onComplete) === null || _d === void 0 ? void 0 : _d.call(callbacks, { filename });
|
|
40
|
-
}
|
|
41
|
-
catch (error) {
|
|
42
|
-
const err = error;
|
|
43
|
-
logMessage(`\n${ansi.red}Error: ${err.message}${ansi.reset}\n`);
|
|
44
|
-
(_e = callbacks === null || callbacks === void 0 ? void 0 : callbacks.onError) === null || _e === void 0 ? void 0 : _e.call(callbacks, err);
|
|
45
|
-
if (!callbacks) {
|
|
46
|
-
process.exit(1);
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
}
|
package/dist/commands/auth.d.ts
DELETED
package/dist/commands/auth.js
DELETED
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
import { Command } from 'commander';
|
|
2
|
-
import inquirer from 'inquirer';
|
|
3
|
-
import { apiTokenSchema, loadCliConfig, saveCliConfig } from '../services/cli-config.js';
|
|
4
|
-
export const authCommand = new Command('auth')
|
|
5
|
-
.description('Authenticate with TelemetryOS by saving your API token')
|
|
6
|
-
.option('-t, --token <string>', 'API token (skip interactive prompt)')
|
|
7
|
-
.option('-f, --force', 'Replace existing token without confirmation')
|
|
8
|
-
.action(handleAuthCommand);
|
|
9
|
-
function maskToken(token) {
|
|
10
|
-
if (token.length <= 8)
|
|
11
|
-
return '****';
|
|
12
|
-
return `${token.slice(0, 4)}...${token.slice(-4)}`;
|
|
13
|
-
}
|
|
14
|
-
function validateToken(input) {
|
|
15
|
-
const result = apiTokenSchema.safeParse(input.trim());
|
|
16
|
-
if (result.success)
|
|
17
|
-
return true;
|
|
18
|
-
return result.error.issues[0].message;
|
|
19
|
-
}
|
|
20
|
-
async function handleAuthCommand(options) {
|
|
21
|
-
let token = options.token;
|
|
22
|
-
const existingCliConfig = await loadCliConfig();
|
|
23
|
-
const existingToken = existingCliConfig.apiToken;
|
|
24
|
-
if (existingToken && !options.force) {
|
|
25
|
-
const { confirm } = await inquirer.prompt([
|
|
26
|
-
{
|
|
27
|
-
type: 'confirm',
|
|
28
|
-
name: 'confirm',
|
|
29
|
-
message: `A token already exists (${maskToken(existingToken)}). Do you want to replace it?`,
|
|
30
|
-
default: false,
|
|
31
|
-
},
|
|
32
|
-
]);
|
|
33
|
-
if (!confirm) {
|
|
34
|
-
console.log('Authentication cancelled.');
|
|
35
|
-
return;
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
if (token) {
|
|
39
|
-
const validation = validateToken(token);
|
|
40
|
-
if (validation !== true) {
|
|
41
|
-
console.error(validation);
|
|
42
|
-
process.exit(1);
|
|
43
|
-
}
|
|
44
|
-
token = token.trim();
|
|
45
|
-
}
|
|
46
|
-
else {
|
|
47
|
-
console.log('In Studio, go to Settings > API Tokens to get your API token.\n');
|
|
48
|
-
const answers = await inquirer.prompt([
|
|
49
|
-
{
|
|
50
|
-
type: 'input',
|
|
51
|
-
name: 'token',
|
|
52
|
-
message: 'Paste your API token:',
|
|
53
|
-
validate: validateToken,
|
|
54
|
-
},
|
|
55
|
-
]);
|
|
56
|
-
token = answers.token.trim();
|
|
57
|
-
}
|
|
58
|
-
await saveCliConfig({ ...existingCliConfig, apiToken: token });
|
|
59
|
-
console.log('API token saved successfully!');
|
|
60
|
-
}
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import { Command } from 'commander';
|
|
2
|
-
import path from 'path';
|
|
3
|
-
import { loadProjectConfig } from '../services/project-config.js';
|
|
4
|
-
import { copyDir, templatesDir } from '../utils/template.js';
|
|
5
|
-
import { ansi } from '../utils/ansi.js';
|
|
6
|
-
export const claudeCodeCommand = new Command('claude-code')
|
|
7
|
-
.description('Apply or update Claude Code skills and settings in a project')
|
|
8
|
-
.argument('[project-path]', 'Path to the telemetryOS project (defaults to current directory)', process.cwd())
|
|
9
|
-
.action(handleClaudeCodeCommand);
|
|
10
|
-
async function handleClaudeCodeCommand(projectPath) {
|
|
11
|
-
var _a, _b, _c;
|
|
12
|
-
const resolvedPath = path.resolve(projectPath);
|
|
13
|
-
let config;
|
|
14
|
-
try {
|
|
15
|
-
config = await loadProjectConfig(resolvedPath);
|
|
16
|
-
}
|
|
17
|
-
catch (error) {
|
|
18
|
-
console.error(`\n${ansi.red}Error:${ansi.reset} ${error instanceof Error ? error.message : error}`);
|
|
19
|
-
process.exit(1);
|
|
20
|
-
}
|
|
21
|
-
const name = (_a = config.name) !== null && _a !== void 0 ? _a : '';
|
|
22
|
-
const description = (_b = config.description) !== null && _b !== void 0 ? _b : '';
|
|
23
|
-
const version = (_c = config.version) !== null && _c !== void 0 ? _c : '';
|
|
24
|
-
console.log(`\nApplying Claude Code skills and settings...\n`);
|
|
25
|
-
await copyDir(path.join(templatesDir, 'claude-code'), resolvedPath, { name, version, description, author: '' }, (createdFilePath) => {
|
|
26
|
-
console.log(`.${path.sep}${path.relative(process.cwd(), createdFilePath)}`);
|
|
27
|
-
});
|
|
28
|
-
console.log(`\n${ansi.green}Done!${ansi.reset} Claude Code skills and settings have been applied.`);
|
|
29
|
-
}
|
package/dist/commands/init.d.ts
DELETED
package/dist/commands/init.js
DELETED
|
@@ -1,176 +0,0 @@
|
|
|
1
|
-
import { Command } from 'commander';
|
|
2
|
-
import { createProject, checkDirectoryConflicts, removeConflictingFiles, } from '../services/create-project.js';
|
|
3
|
-
import inquirer from 'inquirer';
|
|
4
|
-
import path from 'path';
|
|
5
|
-
import { resolveProjectPathAndName, validateProjectName } from '../utils/path-utils.js';
|
|
6
|
-
import { ansi } from '../utils/ansi.js';
|
|
7
|
-
async function promptConflictResolution() {
|
|
8
|
-
const { action } = await inquirer.prompt([
|
|
9
|
-
{
|
|
10
|
-
type: 'list',
|
|
11
|
-
name: 'action',
|
|
12
|
-
message: 'Target directory is not empty. Please choose how to proceed:',
|
|
13
|
-
choices: [
|
|
14
|
-
{ name: 'Cancel operation', value: 'cancel' },
|
|
15
|
-
{ name: 'Remove existing files and continue', value: 'remove' },
|
|
16
|
-
{ name: 'Ignore files and continue', value: 'ignore' },
|
|
17
|
-
],
|
|
18
|
-
default: 'cancel',
|
|
19
|
-
},
|
|
20
|
-
]);
|
|
21
|
-
return action;
|
|
22
|
-
}
|
|
23
|
-
export const initCommand = new Command('init')
|
|
24
|
-
.description('Initializes a new telemetryOS application')
|
|
25
|
-
.argument('[project-path]', 'Path to create the project (defaults to current directory)', '')
|
|
26
|
-
.option('-d, --description <string>', 'The description of the application', '')
|
|
27
|
-
.option('-a, --author <string>', 'The author of the application', '')
|
|
28
|
-
.option('-v, --version <string>', 'The version of the application', '0.1.0')
|
|
29
|
-
.option('-t, --template <string>', 'The template to use (vite-react-typescript, vite-react-typescript-web)', '')
|
|
30
|
-
.option('-y, --yes', 'Skip all prompts and use defaults', false)
|
|
31
|
-
.action(handleInitCommand);
|
|
32
|
-
async function handleInitCommand(projectPathArg, options) {
|
|
33
|
-
var _a, _b, _c, _d, _e, _f;
|
|
34
|
-
// Step 1: Resolve path and derive name
|
|
35
|
-
const cwd = process.cwd();
|
|
36
|
-
const inputPath = projectPathArg || '.';
|
|
37
|
-
const { resolvedPath, derivedName } = resolveProjectPathAndName(inputPath, cwd);
|
|
38
|
-
// Step 2: Validate the derived name BEFORE any destructive operations
|
|
39
|
-
const validation = validateProjectName(derivedName);
|
|
40
|
-
if (validation !== true) {
|
|
41
|
-
console.error(`\n${ansi.red}Error:${ansi.reset} Cannot initialize project - ${validation}`);
|
|
42
|
-
console.error(`\nDerived project name: ${ansi.yellow}${derivedName}${ansi.reset}`);
|
|
43
|
-
console.error(`From path: ${ansi.dim}${path.relative(cwd, resolvedPath)}${ansi.reset}`);
|
|
44
|
-
console.error(`\n${ansi.bold}Project names must:${ansi.reset}`);
|
|
45
|
-
console.error(' • Contain only lowercase letters, numbers, and hyphens');
|
|
46
|
-
console.error(' • Not start or end with a hyphen');
|
|
47
|
-
console.error(' • Not start with a dot (.) or underscore (_)');
|
|
48
|
-
console.error(' • Be between 1 and 214 characters');
|
|
49
|
-
console.error(' • Not be a reserved name (e.g., node_modules)');
|
|
50
|
-
console.error(`\n${ansi.yellow}Tip:${ansi.reset} Use a different directory name or specify a path`);
|
|
51
|
-
console.error(`Example: ${ansi.cyan}tos init my-app${ansi.reset}`);
|
|
52
|
-
process.exit(1);
|
|
53
|
-
}
|
|
54
|
-
// Step 3: Check for directory conflicts BEFORE prompting for details
|
|
55
|
-
const conflicts = await checkDirectoryConflicts(resolvedPath);
|
|
56
|
-
if (conflicts.length > 0) {
|
|
57
|
-
console.log(`\n${ansi.yellow}⚠${ansi.reset} Target directory contains files that could conflict:\n`);
|
|
58
|
-
conflicts.slice(0, 10).forEach((file) => console.log(` ${ansi.dim}${file}${ansi.reset}`));
|
|
59
|
-
if (conflicts.length > 10) {
|
|
60
|
-
console.log(` ${ansi.dim}... and ${conflicts.length - 10} more${ansi.reset}`);
|
|
61
|
-
}
|
|
62
|
-
console.log();
|
|
63
|
-
const action = await promptConflictResolution();
|
|
64
|
-
if (action === 'cancel') {
|
|
65
|
-
console.log('Operation cancelled');
|
|
66
|
-
process.exit(0);
|
|
67
|
-
}
|
|
68
|
-
else if (action === 'remove') {
|
|
69
|
-
console.log('\nRemoving existing files...');
|
|
70
|
-
await removeConflictingFiles(resolvedPath, conflicts);
|
|
71
|
-
console.log(`${ansi.green}✓${ansi.reset} Existing files removed\n`);
|
|
72
|
-
}
|
|
73
|
-
else {
|
|
74
|
-
// action === 'ignore' - continue with merge
|
|
75
|
-
console.log('\nContinuing with existing files (will overwrite conflicts)...\n');
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
// Step 4: Setup variables from options
|
|
79
|
-
let name = derivedName;
|
|
80
|
-
let description = options.description;
|
|
81
|
-
let author = options.author;
|
|
82
|
-
let version = options.version;
|
|
83
|
-
let template = options.template;
|
|
84
|
-
let claudeCode = true;
|
|
85
|
-
// Step 5: Build prompt questions (skipped with --yes)
|
|
86
|
-
if (options.yes) {
|
|
87
|
-
if (!name) {
|
|
88
|
-
console.error('Project name is required when using --yes');
|
|
89
|
-
process.exit(1);
|
|
90
|
-
}
|
|
91
|
-
if (!description)
|
|
92
|
-
description = 'A telemetryOS application';
|
|
93
|
-
if (!template)
|
|
94
|
-
template = 'vite-react-typescript';
|
|
95
|
-
if (!version)
|
|
96
|
-
version = '0.1.0';
|
|
97
|
-
}
|
|
98
|
-
else {
|
|
99
|
-
const questions = [];
|
|
100
|
-
// Always show the derived name with option to override
|
|
101
|
-
// This provides transparency and control
|
|
102
|
-
questions.push({
|
|
103
|
-
type: 'input',
|
|
104
|
-
name: 'name',
|
|
105
|
-
message: 'Project name:',
|
|
106
|
-
default: derivedName,
|
|
107
|
-
validate: (input) => {
|
|
108
|
-
const result = validateProjectName(input);
|
|
109
|
-
return result === true ? true : result;
|
|
110
|
-
},
|
|
111
|
-
});
|
|
112
|
-
if (!description)
|
|
113
|
-
questions.push({
|
|
114
|
-
type: 'input',
|
|
115
|
-
name: 'description',
|
|
116
|
-
message: 'What is the description of your application?',
|
|
117
|
-
default: 'A telemetryOS application',
|
|
118
|
-
});
|
|
119
|
-
if (!author)
|
|
120
|
-
questions.push({
|
|
121
|
-
type: 'input',
|
|
122
|
-
name: 'author',
|
|
123
|
-
message: 'Who is the author of your application?',
|
|
124
|
-
default: '',
|
|
125
|
-
});
|
|
126
|
-
if (!version)
|
|
127
|
-
questions.push({
|
|
128
|
-
type: 'input',
|
|
129
|
-
name: 'version',
|
|
130
|
-
message: 'What is the version of your application?',
|
|
131
|
-
default: '0.1.0',
|
|
132
|
-
validate: (input) => {
|
|
133
|
-
if (/^\d+\.\d+\.\d+(-.+)?$/.test(input))
|
|
134
|
-
return true;
|
|
135
|
-
return 'Version must be in semver format (e.g. 1.0.0)';
|
|
136
|
-
},
|
|
137
|
-
});
|
|
138
|
-
if (!template)
|
|
139
|
-
questions.push({
|
|
140
|
-
type: 'list',
|
|
141
|
-
name: 'template',
|
|
142
|
-
message: 'Which template would you like to use?',
|
|
143
|
-
choices: [
|
|
144
|
-
{ name: 'Vite + React + TypeScript', value: 'vite-react-typescript' },
|
|
145
|
-
{ name: 'Vite + React + TypeScript + Web', value: 'vite-react-typescript-web' },
|
|
146
|
-
],
|
|
147
|
-
});
|
|
148
|
-
questions.push({
|
|
149
|
-
type: 'confirm',
|
|
150
|
-
name: 'claudeCode',
|
|
151
|
-
message: 'Include Claude Code skills and settings?',
|
|
152
|
-
default: true,
|
|
153
|
-
});
|
|
154
|
-
// Step 6: Prompt user
|
|
155
|
-
const answers = await inquirer.prompt(questions);
|
|
156
|
-
name = (_a = answers.name) !== null && _a !== void 0 ? _a : name;
|
|
157
|
-
version = (_b = answers.version) !== null && _b !== void 0 ? _b : version;
|
|
158
|
-
description = (_c = answers.description) !== null && _c !== void 0 ? _c : description;
|
|
159
|
-
author = (_d = answers.author) !== null && _d !== void 0 ? _d : author;
|
|
160
|
-
template = (_e = answers.template) !== null && _e !== void 0 ? _e : template;
|
|
161
|
-
claudeCode = (_f = answers.claudeCode) !== null && _f !== void 0 ? _f : claudeCode;
|
|
162
|
-
}
|
|
163
|
-
// Step 7: Generate application
|
|
164
|
-
await createProject({
|
|
165
|
-
name,
|
|
166
|
-
description,
|
|
167
|
-
author,
|
|
168
|
-
version,
|
|
169
|
-
template,
|
|
170
|
-
claudeCode,
|
|
171
|
-
projectPath: resolvedPath,
|
|
172
|
-
progressFn: (createdFilePath) => {
|
|
173
|
-
console.log(`.${path.sep}${path.relative(cwd, createdFilePath)}`);
|
|
174
|
-
},
|
|
175
|
-
});
|
|
176
|
-
}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { Command } from 'commander';
|
|
2
|
-
export declare const publishCommand: Command;
|
|
3
|
-
type PublishOptions = {
|
|
4
|
-
interactive?: boolean;
|
|
5
|
-
name?: string;
|
|
6
|
-
baseImage?: string;
|
|
7
|
-
buildScript?: string;
|
|
8
|
-
buildOutput?: string;
|
|
9
|
-
workingPath?: string;
|
|
10
|
-
};
|
|
11
|
-
type PublishCallbacks = {
|
|
12
|
-
onLog?: (line: string) => void;
|
|
13
|
-
onStateChange?: (state: string) => void;
|
|
14
|
-
onComplete?: (data: {
|
|
15
|
-
success: boolean;
|
|
16
|
-
buildIndex?: number;
|
|
17
|
-
duration?: string;
|
|
18
|
-
}) => void;
|
|
19
|
-
onError?: (error: Error) => void;
|
|
20
|
-
};
|
|
21
|
-
declare function handlePublishCommand(projectPath: string, options: PublishOptions, callbacks?: PublishCallbacks): Promise<void>;
|
|
22
|
-
export { handlePublishCommand, type PublishCallbacks, type PublishOptions };
|
package/dist/commands/publish.js
DELETED
|
@@ -1,238 +0,0 @@
|
|
|
1
|
-
import { Command } from 'commander';
|
|
2
|
-
import inquirer from 'inquirer';
|
|
3
|
-
import path from 'path';
|
|
4
|
-
import { ApiClient } from '../services/api-client.js';
|
|
5
|
-
import { loadProjectConfig } from '../services/project-config.js';
|
|
6
|
-
import { createArchive } from '../services/archiver.js';
|
|
7
|
-
import { pollBuild } from '../services/build-poller.js';
|
|
8
|
-
import { ansi } from '../utils/ansi.js';
|
|
9
|
-
// Default values for new application builds
|
|
10
|
-
const APP_BUILD_DEFAULTS = {
|
|
11
|
-
baseImage: 'node:20',
|
|
12
|
-
buildScript: 'npm install\nnpm run build',
|
|
13
|
-
buildOutput: '/dist',
|
|
14
|
-
workingPath: '/',
|
|
15
|
-
};
|
|
16
|
-
export const publishCommand = new Command('publish')
|
|
17
|
-
.description('Publish an application to TelemetryOS')
|
|
18
|
-
.argument('[project-path]', 'Path to the project directory. Defaults to current working directory', process.cwd())
|
|
19
|
-
.option('-i, --interactive', 'Interactively prompt for all build configuration')
|
|
20
|
-
.option('--name <name>', 'Application name (overrides config)')
|
|
21
|
-
.option('--base-image <image>', 'Docker base image for build')
|
|
22
|
-
.option('--build-script <script>', 'Build script commands')
|
|
23
|
-
.option('--build-output <path>', 'Build output directory')
|
|
24
|
-
.option('--working-path <path>', 'Working directory in container')
|
|
25
|
-
.action(handlePublishCommand);
|
|
26
|
-
function getSpecifiedBuildOptions(options) {
|
|
27
|
-
const specified = [];
|
|
28
|
-
if (options.baseImage !== undefined)
|
|
29
|
-
specified.push('--base-image');
|
|
30
|
-
if (options.buildScript !== undefined)
|
|
31
|
-
specified.push('--build-script');
|
|
32
|
-
if (options.buildOutput !== undefined)
|
|
33
|
-
specified.push('--build-output');
|
|
34
|
-
if (options.workingPath !== undefined)
|
|
35
|
-
specified.push('--working-path');
|
|
36
|
-
return specified;
|
|
37
|
-
}
|
|
38
|
-
async function handlePublishCommand(projectPath, options, callbacks) {
|
|
39
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q;
|
|
40
|
-
projectPath = path.resolve(process.cwd(), projectPath);
|
|
41
|
-
const logMessage = (callbacks === null || callbacks === void 0 ? void 0 : callbacks.onLog) || ((msg) => console.log(msg));
|
|
42
|
-
try {
|
|
43
|
-
// Step 1: Load project config
|
|
44
|
-
(_a = callbacks === null || callbacks === void 0 ? void 0 : callbacks.onStateChange) === null || _a === void 0 ? void 0 : _a.call(callbacks, 'loading');
|
|
45
|
-
logMessage(`\n${ansi.cyan}Loading project configuration...${ansi.reset}`);
|
|
46
|
-
const config = await loadProjectConfig(projectPath);
|
|
47
|
-
// Step 2: Resolve build configuration
|
|
48
|
-
let buildConfig = {
|
|
49
|
-
name: options.name || config.name || '',
|
|
50
|
-
baseImage: options.baseImage,
|
|
51
|
-
buildScript: options.buildScript,
|
|
52
|
-
buildOutput: options.buildOutput,
|
|
53
|
-
workingPath: options.workingPath,
|
|
54
|
-
};
|
|
55
|
-
if (options.interactive) {
|
|
56
|
-
// Interactive mode: prompt for all fields
|
|
57
|
-
buildConfig = await promptBuildConfig(buildConfig);
|
|
58
|
-
}
|
|
59
|
-
else if (!buildConfig.name) {
|
|
60
|
-
// Non-interactive mode but no name: prompt just for name
|
|
61
|
-
const answers = await inquirer.prompt([
|
|
62
|
-
{
|
|
63
|
-
type: 'input',
|
|
64
|
-
name: 'name',
|
|
65
|
-
message: 'Application name:',
|
|
66
|
-
validate: (input) => (input.trim() ? true : 'Application name is required'),
|
|
67
|
-
},
|
|
68
|
-
]);
|
|
69
|
-
buildConfig.name = answers.name.trim();
|
|
70
|
-
}
|
|
71
|
-
if (!buildConfig.name) {
|
|
72
|
-
const error = new Error('Application name is required');
|
|
73
|
-
logMessage(`${ansi.red}Error: Application name is required${ansi.reset}`);
|
|
74
|
-
(_b = callbacks === null || callbacks === void 0 ? void 0 : callbacks.onError) === null || _b === void 0 ? void 0 : _b.call(callbacks, error);
|
|
75
|
-
if (!callbacks)
|
|
76
|
-
process.exit(1);
|
|
77
|
-
return;
|
|
78
|
-
}
|
|
79
|
-
logMessage(` Project: ${ansi.bold}${buildConfig.name}${ansi.reset}`);
|
|
80
|
-
if (config.version) {
|
|
81
|
-
logMessage(` Version: ${config.version}`);
|
|
82
|
-
}
|
|
83
|
-
// Step 3: Initialize API client
|
|
84
|
-
(_c = callbacks === null || callbacks === void 0 ? void 0 : callbacks.onStateChange) === null || _c === void 0 ? void 0 : _c.call(callbacks, 'authenticating');
|
|
85
|
-
logMessage(`\n${ansi.cyan}Authenticating...${ansi.reset}`);
|
|
86
|
-
const apiClient = await ApiClient.create();
|
|
87
|
-
logMessage(` ${ansi.green}Authenticated${ansi.reset}`);
|
|
88
|
-
// Step 4: Create archive
|
|
89
|
-
(_d = callbacks === null || callbacks === void 0 ? void 0 : callbacks.onStateChange) === null || _d === void 0 ? void 0 : _d.call(callbacks, 'archiving');
|
|
90
|
-
logMessage(`\n${ansi.cyan}Archiving project...${ansi.reset}`);
|
|
91
|
-
const { buffer, filename } = await createArchive(projectPath, (msg) => {
|
|
92
|
-
logMessage(` ${ansi.dim}${msg}${ansi.reset}`);
|
|
93
|
-
});
|
|
94
|
-
// Step 5: Find or create application
|
|
95
|
-
logMessage(`\n${ansi.cyan}Checking for existing application...${ansi.reset}`);
|
|
96
|
-
const appsRes = await apiClient.get('/application');
|
|
97
|
-
const apps = (await appsRes.json());
|
|
98
|
-
let app = apps.find((a) => a.title === buildConfig.name) || null;
|
|
99
|
-
if (app) {
|
|
100
|
-
logMessage(` Found existing application: ${ansi.dim}${app.id}${ansi.reset}`);
|
|
101
|
-
const specifiedOptions = getSpecifiedBuildOptions(options);
|
|
102
|
-
if (specifiedOptions.length > 0) {
|
|
103
|
-
logMessage(` ${ansi.yellow}Warning: ${specifiedOptions.join(', ')} ignored for existing application${ansi.reset}`);
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
else {
|
|
107
|
-
logMessage(` Creating new application...`);
|
|
108
|
-
const createRequest = {
|
|
109
|
-
kind: 'uploaded',
|
|
110
|
-
title: buildConfig.name,
|
|
111
|
-
baseImage: (_e = buildConfig.baseImage) !== null && _e !== void 0 ? _e : APP_BUILD_DEFAULTS.baseImage,
|
|
112
|
-
baseImageRegistryAuth: '',
|
|
113
|
-
buildWorkingPath: (_f = buildConfig.workingPath) !== null && _f !== void 0 ? _f : APP_BUILD_DEFAULTS.workingPath,
|
|
114
|
-
buildScript: (_g = buildConfig.buildScript) !== null && _g !== void 0 ? _g : APP_BUILD_DEFAULTS.buildScript,
|
|
115
|
-
buildOutputPath: (_h = buildConfig.buildOutput) !== null && _h !== void 0 ? _h : APP_BUILD_DEFAULTS.buildOutput,
|
|
116
|
-
};
|
|
117
|
-
const createRes = await apiClient.post('/application', { body: createRequest });
|
|
118
|
-
app = (await createRes.json());
|
|
119
|
-
logMessage(` ${ansi.green}Created application:${ansi.reset} ${ansi.dim}${app.id}${ansi.reset}`);
|
|
120
|
-
}
|
|
121
|
-
// Step 6: Upload archive
|
|
122
|
-
(_j = callbacks === null || callbacks === void 0 ? void 0 : callbacks.onStateChange) === null || _j === void 0 ? void 0 : _j.call(callbacks, 'uploading');
|
|
123
|
-
logMessage(`\n${ansi.cyan}Uploading archive...${ansi.reset}`);
|
|
124
|
-
const formData = new FormData();
|
|
125
|
-
const arrayBuffer = buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength);
|
|
126
|
-
const blob = new Blob([arrayBuffer], { type: 'application/gzip' });
|
|
127
|
-
formData.append('file', blob, filename);
|
|
128
|
-
await apiClient.post(`/application/${app.id}/archive`, { body: formData });
|
|
129
|
-
logMessage(` ${ansi.green}Upload complete${ansi.reset}`);
|
|
130
|
-
// Step 7: Poll for build status and stream logs
|
|
131
|
-
(_k = callbacks === null || callbacks === void 0 ? void 0 : callbacks.onStateChange) === null || _k === void 0 ? void 0 : _k.call(callbacks, 'building');
|
|
132
|
-
logMessage(`\n${ansi.cyan}Building...${ansi.reset}`);
|
|
133
|
-
logMessage(`${ansi.white}Build Logs${ansi.reset}`);
|
|
134
|
-
const build = await pollBuild(apiClient, app.id, {
|
|
135
|
-
onLog: (line) => {
|
|
136
|
-
logMessage(` ${ansi.dim}${line}${ansi.reset}`);
|
|
137
|
-
},
|
|
138
|
-
onStateChange: () => { },
|
|
139
|
-
onComplete: () => { },
|
|
140
|
-
onError: (error) => {
|
|
141
|
-
var _a;
|
|
142
|
-
logMessage(`${ansi.red}Polling error: ${error.message}${ansi.reset}`);
|
|
143
|
-
(_a = callbacks === null || callbacks === void 0 ? void 0 : callbacks.onError) === null || _a === void 0 ? void 0 : _a.call(callbacks, error);
|
|
144
|
-
},
|
|
145
|
-
});
|
|
146
|
-
// Step 8: Report result
|
|
147
|
-
if (build.state === 'success') {
|
|
148
|
-
(_l = callbacks === null || callbacks === void 0 ? void 0 : callbacks.onStateChange) === null || _l === void 0 ? void 0 : _l.call(callbacks, 'success');
|
|
149
|
-
logMessage(`\n${ansi.green}${ansi.bold}Build successful!${ansi.reset}`);
|
|
150
|
-
logMessage(`\n Application: ${buildConfig.name}`);
|
|
151
|
-
logMessage(` Build: #${build.index}`);
|
|
152
|
-
let duration = '';
|
|
153
|
-
if (build.finishedAt && build.startedAt) {
|
|
154
|
-
duration = calculateDuration(build.startedAt, build.finishedAt);
|
|
155
|
-
logMessage(` Duration: ${duration}`);
|
|
156
|
-
}
|
|
157
|
-
logMessage('');
|
|
158
|
-
(_m = callbacks === null || callbacks === void 0 ? void 0 : callbacks.onComplete) === null || _m === void 0 ? void 0 : _m.call(callbacks, {
|
|
159
|
-
success: true,
|
|
160
|
-
buildIndex: build.index,
|
|
161
|
-
duration,
|
|
162
|
-
});
|
|
163
|
-
}
|
|
164
|
-
else {
|
|
165
|
-
(_o = callbacks === null || callbacks === void 0 ? void 0 : callbacks.onStateChange) === null || _o === void 0 ? void 0 : _o.call(callbacks, 'failure');
|
|
166
|
-
logMessage(`\n${ansi.red}${ansi.bold}Build failed${ansi.reset}`);
|
|
167
|
-
logMessage(`\n State: ${build.state}`);
|
|
168
|
-
logMessage('');
|
|
169
|
-
const error = new Error(`Build failed with state: ${build.state}`);
|
|
170
|
-
(_p = callbacks === null || callbacks === void 0 ? void 0 : callbacks.onError) === null || _p === void 0 ? void 0 : _p.call(callbacks, error);
|
|
171
|
-
if (!callbacks) {
|
|
172
|
-
process.exit(1);
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
catch (error) {
|
|
177
|
-
const err = error;
|
|
178
|
-
logMessage(`\n${ansi.red}Error: ${err.message}${ansi.reset}\n`);
|
|
179
|
-
(_q = callbacks === null || callbacks === void 0 ? void 0 : callbacks.onError) === null || _q === void 0 ? void 0 : _q.call(callbacks, err);
|
|
180
|
-
if (!callbacks) {
|
|
181
|
-
process.exit(1);
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
// Export for use by server
|
|
186
|
-
export { handlePublishCommand };
|
|
187
|
-
async function promptBuildConfig(defaults) {
|
|
188
|
-
var _a, _b, _c, _d;
|
|
189
|
-
const answers = await inquirer.prompt([
|
|
190
|
-
{
|
|
191
|
-
type: 'input',
|
|
192
|
-
name: 'name',
|
|
193
|
-
message: 'Application name:',
|
|
194
|
-
default: defaults.name || undefined,
|
|
195
|
-
validate: (input) => (input.trim() ? true : 'Application name is required'),
|
|
196
|
-
},
|
|
197
|
-
{
|
|
198
|
-
type: 'input',
|
|
199
|
-
name: 'baseImage',
|
|
200
|
-
message: 'Base image:',
|
|
201
|
-
default: (_a = defaults.baseImage) !== null && _a !== void 0 ? _a : APP_BUILD_DEFAULTS.baseImage,
|
|
202
|
-
},
|
|
203
|
-
{
|
|
204
|
-
type: 'input',
|
|
205
|
-
name: 'buildScript',
|
|
206
|
-
message: 'Build script:',
|
|
207
|
-
default: (_b = defaults.buildScript) !== null && _b !== void 0 ? _b : APP_BUILD_DEFAULTS.buildScript,
|
|
208
|
-
},
|
|
209
|
-
{
|
|
210
|
-
type: 'input',
|
|
211
|
-
name: 'buildOutput',
|
|
212
|
-
message: 'Build output path:',
|
|
213
|
-
default: (_c = defaults.buildOutput) !== null && _c !== void 0 ? _c : APP_BUILD_DEFAULTS.buildOutput,
|
|
214
|
-
},
|
|
215
|
-
{
|
|
216
|
-
type: 'input',
|
|
217
|
-
name: 'workingPath',
|
|
218
|
-
message: 'Working path:',
|
|
219
|
-
default: (_d = defaults.workingPath) !== null && _d !== void 0 ? _d : APP_BUILD_DEFAULTS.workingPath,
|
|
220
|
-
},
|
|
221
|
-
]);
|
|
222
|
-
return {
|
|
223
|
-
name: answers.name.trim(),
|
|
224
|
-
baseImage: answers.baseImage.trim(),
|
|
225
|
-
buildScript: answers.buildScript.trim(),
|
|
226
|
-
buildOutput: answers.buildOutput.trim(),
|
|
227
|
-
workingPath: answers.workingPath.trim(),
|
|
228
|
-
};
|
|
229
|
-
}
|
|
230
|
-
function calculateDuration(start, end) {
|
|
231
|
-
const ms = new Date(end).getTime() - new Date(start).getTime();
|
|
232
|
-
const seconds = Math.floor(ms / 1000);
|
|
233
|
-
if (seconds < 60)
|
|
234
|
-
return `${seconds}s`;
|
|
235
|
-
const minutes = Math.floor(seconds / 60);
|
|
236
|
-
const remainingSeconds = seconds % 60;
|
|
237
|
-
return `${minutes}m ${remainingSeconds}s`;
|
|
238
|
-
}
|
package/dist/commands/root.d.ts
DELETED
package/dist/commands/root.js
DELETED
package/dist/commands/serve.d.ts
DELETED
package/dist/commands/serve.js
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import { Command } from 'commander';
|
|
2
|
-
import { runServer } from '../services/run-server.js';
|
|
3
|
-
export const serveCommand = new Command('serve')
|
|
4
|
-
.description('Serves a telemetryOS application locally for development')
|
|
5
|
-
.option('-p, --port <number>', 'Port to run the development ui on', '2026')
|
|
6
|
-
.argument('[project-path]', 'Path to the telemetry application project. Defaults to current working directory', process.cwd())
|
|
7
|
-
.action(runServer);
|