@archlast/cli 0.0.1
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 +141 -0
- package/dist/analyzer.d.ts +96 -0
- package/dist/analyzer.d.ts.map +1 -0
- package/dist/analyzer.js +404 -0
- package/dist/auth.d.ts +14 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +106 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +322875 -0
- package/dist/commands/build.d.ts +6 -0
- package/dist/commands/build.d.ts.map +1 -0
- package/dist/commands/build.js +36 -0
- package/dist/commands/config.d.ts +8 -0
- package/dist/commands/config.d.ts.map +1 -0
- package/dist/commands/config.js +23 -0
- package/dist/commands/data.d.ts +6 -0
- package/dist/commands/data.d.ts.map +1 -0
- package/dist/commands/data.js +300 -0
- package/dist/commands/deploy.d.ts +9 -0
- package/dist/commands/deploy.d.ts.map +1 -0
- package/dist/commands/deploy.js +59 -0
- package/dist/commands/dev.d.ts +10 -0
- package/dist/commands/dev.d.ts.map +1 -0
- package/dist/commands/dev.js +132 -0
- package/dist/commands/generate.d.ts +6 -0
- package/dist/commands/generate.d.ts.map +1 -0
- package/dist/commands/generate.js +100 -0
- package/dist/commands/init.d.ts +7 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/logs.d.ts +10 -0
- package/dist/commands/logs.d.ts.map +1 -0
- package/dist/commands/logs.js +38 -0
- package/dist/commands/pull.d.ts +16 -0
- package/dist/commands/pull.d.ts.map +1 -0
- package/dist/commands/pull.js +415 -0
- package/dist/commands/restart.d.ts +11 -0
- package/dist/commands/restart.d.ts.map +1 -0
- package/dist/commands/restart.js +63 -0
- package/dist/commands/start.d.ts +11 -0
- package/dist/commands/start.d.ts.map +1 -0
- package/dist/commands/start.js +74 -0
- package/dist/commands/status.d.ts +8 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +69 -0
- package/dist/commands/stop.d.ts +8 -0
- package/dist/commands/stop.d.ts.map +1 -0
- package/dist/commands/stop.js +23 -0
- package/dist/commands/upgrade.d.ts +12 -0
- package/dist/commands/upgrade.d.ts.map +1 -0
- package/dist/commands/upgrade.js +77 -0
- package/dist/docker/compose.d.ts +3 -0
- package/dist/docker/compose.d.ts.map +1 -0
- package/dist/docker/compose.js +47 -0
- package/dist/docker/config.d.ts +12 -0
- package/dist/docker/config.d.ts.map +1 -0
- package/dist/docker/config.js +183 -0
- package/dist/docker/manager.d.ts +19 -0
- package/dist/docker/manager.d.ts.map +1 -0
- package/dist/docker/manager.js +239 -0
- package/dist/docker/ports.d.ts +6 -0
- package/dist/docker/ports.d.ts.map +1 -0
- package/dist/docker/restart-on-deploy.d.ts +6 -0
- package/dist/docker/restart-on-deploy.d.ts.map +1 -0
- package/dist/docker/types.d.ts +36 -0
- package/dist/docker/types.d.ts.map +1 -0
- package/dist/docker/types.js +1 -0
- package/dist/events-listener.d.ts +19 -0
- package/dist/events-listener.d.ts.map +1 -0
- package/dist/events-listener.js +105 -0
- package/dist/generator.d.ts +44 -0
- package/dist/generator.d.ts.map +1 -0
- package/dist/generator.js +1816 -0
- package/dist/generators/di.d.ts +21 -0
- package/dist/generators/di.d.ts.map +1 -0
- package/dist/generators/di.js +100 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4 -0
- package/dist/project.d.ts +18 -0
- package/dist/project.d.ts.map +1 -0
- package/dist/protocol.d.ts +58 -0
- package/dist/protocol.d.ts.map +1 -0
- package/dist/protocol.js +5 -0
- package/dist/uploader.d.ts +63 -0
- package/dist/uploader.d.ts.map +1 -0
- package/dist/uploader.js +255 -0
- package/dist/watcher.d.ts +13 -0
- package/dist/watcher.d.ts.map +1 -0
- package/dist/watcher.js +38 -0
- package/package.json +58 -0
- package/scripts/postinstall.cjs +65 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../../src/commands/build.ts"],"names":[],"mappings":"AAMA,UAAU,YAAY;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,wBAAsB,YAAY,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CA6CvE"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import ora from "ora";
|
|
3
|
+
import { CodeAnalyzer } from "../analyzer";
|
|
4
|
+
import { TypeGenerator } from "../generator";
|
|
5
|
+
export async function buildCommand(options) {
|
|
6
|
+
const archlastPath = options.path || ".";
|
|
7
|
+
console.log(chalk.blue.bold("šØ Archlast Build\n"));
|
|
8
|
+
const analyzer = new CodeAnalyzer(archlastPath);
|
|
9
|
+
const generator = new TypeGenerator(archlastPath);
|
|
10
|
+
const spinner = ora("Analyzing code...").start();
|
|
11
|
+
try {
|
|
12
|
+
const analysis = await analyzer.analyze();
|
|
13
|
+
await generator.generate(analysis);
|
|
14
|
+
spinner.succeed(chalk.green(`ā
Generated types for ${analysis.functions.length} function(s)`));
|
|
15
|
+
// Show function breakdown
|
|
16
|
+
if (analysis.functions.length > 0) {
|
|
17
|
+
const byType = analysis.functions.reduce((acc, fn) => {
|
|
18
|
+
acc[fn.type] = (acc[fn.type] || 0) + 1;
|
|
19
|
+
return acc;
|
|
20
|
+
}, {});
|
|
21
|
+
const breakdown = Object.entries(byType)
|
|
22
|
+
.map(([type, count]) => `${count} ${type}(s)`)
|
|
23
|
+
.join(", ");
|
|
24
|
+
console.log(chalk.gray(` (${breakdown})`));
|
|
25
|
+
}
|
|
26
|
+
console.log(chalk.gray("\nGenerated files:"));
|
|
27
|
+
console.log(chalk.gray(" ⢠_generated/api.ts"));
|
|
28
|
+
console.log(chalk.gray(" ⢠_generated/server.ts"));
|
|
29
|
+
console.log(chalk.gray(" ⢠_generated/index.ts"));
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
spinner.fail(chalk.red("Build failed"));
|
|
33
|
+
console.error(error);
|
|
34
|
+
process.exit(1);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/commands/config.ts"],"names":[],"mappings":"AAGA,UAAU,aAAa;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,wBAAsB,aAAa,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAuBzE"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import { loadDockerConfig } from "../docker/config";
|
|
3
|
+
export async function configCommand(options) {
|
|
4
|
+
const config = loadDockerConfig({
|
|
5
|
+
path: options.path,
|
|
6
|
+
configFile: options.config,
|
|
7
|
+
});
|
|
8
|
+
if (options.json) {
|
|
9
|
+
console.log(JSON.stringify(config, null, 2));
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
console.log(chalk.blue.bold("Archlast Docker Config\n"));
|
|
13
|
+
console.log(`Project: ${config.projectPath}`);
|
|
14
|
+
console.log(`Config: ${config.configFilePath ?? "none"}`);
|
|
15
|
+
console.log(`Env file: ${config.envFilePath ?? "none"}`);
|
|
16
|
+
console.log(`Image: ${config.image}:${config.tag}`);
|
|
17
|
+
console.log(`Container: ${config.containerName}`);
|
|
18
|
+
console.log(`Port: ${config.port}`);
|
|
19
|
+
console.log(`Volume: ${config.dataVolumeName}`);
|
|
20
|
+
console.log(`Config dir: ${config.configDir}`);
|
|
21
|
+
console.log(`Deploy dir: ${config.deployDir}`);
|
|
22
|
+
console.log(`Compose: ${config.composePath}`);
|
|
23
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"data.d.ts","sourceRoot":"","sources":["../../src/commands/data.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA4DpC;;GAEG;AACH,eAAO,MAAM,WAAW,SAE4C,CAAC"}
|
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import ora from "ora";
|
|
3
|
+
import { Command } from "commander";
|
|
4
|
+
import * as fs from "fs";
|
|
5
|
+
import * as path from "path";
|
|
6
|
+
const BETTER_AUTH_API_KEY_PREFIX = "arch_";
|
|
7
|
+
/**
|
|
8
|
+
* Data command group for backup, export, import operations
|
|
9
|
+
*/
|
|
10
|
+
export const dataCommand = new Command("data")
|
|
11
|
+
.description("Data management commands (backup, export, import)")
|
|
12
|
+
.option("--server <url>", "Server URL", "http://localhost:4000");
|
|
13
|
+
/**
|
|
14
|
+
* Get API key from environment
|
|
15
|
+
*/
|
|
16
|
+
async function getAuthCredentials(server) {
|
|
17
|
+
// Check API key first
|
|
18
|
+
const apiKey = process.env.ARCHLAST_API_KEY;
|
|
19
|
+
if (apiKey) {
|
|
20
|
+
return apiKey;
|
|
21
|
+
}
|
|
22
|
+
// If no token, try to read from .env file
|
|
23
|
+
const envPath = path.join(process.cwd(), ".env");
|
|
24
|
+
if (fs.existsSync(envPath)) {
|
|
25
|
+
const envContent = fs.readFileSync(envPath, "utf-8");
|
|
26
|
+
const keyMatch = envContent.match(/ARCHLAST_API_KEY=(.+)/);
|
|
27
|
+
if (keyMatch && keyMatch[1]) {
|
|
28
|
+
return keyMatch[1].trim();
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
throw new Error("API key not found. Set ARCHLAST_API_KEY environment variable, or add it to .env file");
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Make authenticated request to server
|
|
35
|
+
*/
|
|
36
|
+
async function apiRequest(url, options = {}, token) {
|
|
37
|
+
const headers = {
|
|
38
|
+
...options.headers,
|
|
39
|
+
"Content-Type": "application/json",
|
|
40
|
+
"x-api-key": token,
|
|
41
|
+
};
|
|
42
|
+
return fetch(url, {
|
|
43
|
+
...options,
|
|
44
|
+
headers,
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Create a snapshot
|
|
49
|
+
*/
|
|
50
|
+
dataCommand
|
|
51
|
+
.command("snapshot")
|
|
52
|
+
.description("Create a database snapshot")
|
|
53
|
+
.option("-n, --name <name>", "Snapshot name")
|
|
54
|
+
.option("--include-sqlite", "Include SQLite export (default: false)", false)
|
|
55
|
+
.action(async function (options) {
|
|
56
|
+
const parent = this.parent;
|
|
57
|
+
const serverOpts = parent?.opts() || {};
|
|
58
|
+
const server = serverOpts.server || "http://localhost:4000";
|
|
59
|
+
const spinner = ora("Creating snapshot...").start();
|
|
60
|
+
try {
|
|
61
|
+
const token = await getAuthCredentials(server);
|
|
62
|
+
const response = await apiRequest(`${server}/_archlast/admin/backup/snapshot`, {
|
|
63
|
+
method: "POST",
|
|
64
|
+
body: JSON.stringify({ name: options.name }),
|
|
65
|
+
}, token);
|
|
66
|
+
const result = (await response.json());
|
|
67
|
+
if (!response.ok || !result.success) {
|
|
68
|
+
spinner.fail(chalk.red(`Failed to create snapshot: ${result.error || "Unknown error"}`));
|
|
69
|
+
process.exit(1);
|
|
70
|
+
}
|
|
71
|
+
spinner.succeed(chalk.green(`ā
Snapshot created: ${result.snapshotFile}`));
|
|
72
|
+
console.log(chalk.gray(` Records: ${result.recordCount}`));
|
|
73
|
+
console.log(chalk.gray(` Collections: ${result.collections.length}`));
|
|
74
|
+
}
|
|
75
|
+
catch (error) {
|
|
76
|
+
spinner.fail(chalk.red("Failed to create snapshot"));
|
|
77
|
+
console.error(error);
|
|
78
|
+
process.exit(1);
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
/**
|
|
82
|
+
* Export data to a file
|
|
83
|
+
*/
|
|
84
|
+
dataCommand
|
|
85
|
+
.command("export <outputFile>")
|
|
86
|
+
.description("Export all data to a ZIP file")
|
|
87
|
+
.option("-f, --format <format>", "Export format: zip or json", "zip")
|
|
88
|
+
.action(async function (outputFile, options) {
|
|
89
|
+
const parent = this.parent;
|
|
90
|
+
const serverOpts = parent?.opts() || {};
|
|
91
|
+
const server = serverOpts.server || "http://localhost:4000";
|
|
92
|
+
const spinner = ora("Exporting data...").start();
|
|
93
|
+
try {
|
|
94
|
+
const token = await getAuthCredentials(server);
|
|
95
|
+
const response = await apiRequest(`${server}/_archlast/admin/backup/export`, {}, token);
|
|
96
|
+
if (!response.ok) {
|
|
97
|
+
const result = (await response.json());
|
|
98
|
+
spinner.fail(chalk.red(`Failed to export: ${result.error || "Unknown error"}`));
|
|
99
|
+
process.exit(1);
|
|
100
|
+
}
|
|
101
|
+
const outputPath = path.resolve(outputFile);
|
|
102
|
+
if (options.format === "json") {
|
|
103
|
+
const data = (await response.json());
|
|
104
|
+
await fs.promises.writeFile(outputPath, JSON.stringify(data, null, 2), "utf-8");
|
|
105
|
+
const collectionCount = Object.keys(data.collections || {}).length;
|
|
106
|
+
const totalRecords = Object.values(data.collections || {}).reduce((sum, col) => sum + (col?.count || 0), 0);
|
|
107
|
+
spinner.succeed(chalk.green(`ā
Exported to ${outputFile}`));
|
|
108
|
+
console.log(chalk.gray(` Collections: ${collectionCount}`));
|
|
109
|
+
console.log(chalk.gray(` Records: ${totalRecords}`));
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
const arrayBuffer = await response.arrayBuffer();
|
|
113
|
+
await fs.promises.writeFile(outputPath, Buffer.from(arrayBuffer));
|
|
114
|
+
spinner.succeed(chalk.green(`ā
Exported to ${outputFile}`));
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
catch (error) {
|
|
118
|
+
spinner.fail(chalk.red("Failed to export data"));
|
|
119
|
+
console.error(error);
|
|
120
|
+
process.exit(1);
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
/**
|
|
124
|
+
* Restore from a snapshot
|
|
125
|
+
*/
|
|
126
|
+
dataCommand
|
|
127
|
+
.command("restore <snapshotFile>")
|
|
128
|
+
.description("Restore database from a snapshot")
|
|
129
|
+
.action(async function (snapshotFile) {
|
|
130
|
+
const parent = this.parent;
|
|
131
|
+
const serverOpts = parent?.opts() || {};
|
|
132
|
+
const server = serverOpts.server || "http://localhost:4000";
|
|
133
|
+
const spinner = ora("Restoring snapshot...").start();
|
|
134
|
+
try {
|
|
135
|
+
const token = await getAuthCredentials(server);
|
|
136
|
+
const response = await apiRequest(`${server}/_archlast/admin/backup/restore`, {
|
|
137
|
+
method: "POST",
|
|
138
|
+
body: JSON.stringify({ snapshotFile }),
|
|
139
|
+
}, token);
|
|
140
|
+
const result = (await response.json());
|
|
141
|
+
if (!response.ok || !result.success) {
|
|
142
|
+
spinner.fail(chalk.red(`Failed to restore: ${result.error || "Unknown error"}`));
|
|
143
|
+
process.exit(1);
|
|
144
|
+
}
|
|
145
|
+
spinner.succeed(chalk.green(`ā
Restored ${result.recordsImported} records`));
|
|
146
|
+
console.log(chalk.gray(` Collections: ${result.collections.length}`));
|
|
147
|
+
}
|
|
148
|
+
catch (error) {
|
|
149
|
+
spinner.fail(chalk.red("Failed to restore snapshot"));
|
|
150
|
+
console.error(error);
|
|
151
|
+
process.exit(1);
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
/**
|
|
155
|
+
* Export data to a file
|
|
156
|
+
*/
|
|
157
|
+
dataCommand
|
|
158
|
+
.command("export <outputFile>")
|
|
159
|
+
.description("Export all data to a JSON file")
|
|
160
|
+
.action(async function (outputFile) {
|
|
161
|
+
const parent = this.parent;
|
|
162
|
+
const serverOpts = parent?.opts() || {};
|
|
163
|
+
const server = serverOpts.server || "http://localhost:4000";
|
|
164
|
+
const spinner = ora("Exporting data...").start();
|
|
165
|
+
try {
|
|
166
|
+
const token = await getAuthCredentials(server);
|
|
167
|
+
const response = await apiRequest(`${server}/_archlast/admin/backup/export`, {}, token);
|
|
168
|
+
if (!response.ok) {
|
|
169
|
+
const result = (await response.json());
|
|
170
|
+
spinner.fail(chalk.red(`Failed to export: ${result.error || "Unknown error"}`));
|
|
171
|
+
process.exit(1);
|
|
172
|
+
}
|
|
173
|
+
const data = (await response.json());
|
|
174
|
+
// Write to file
|
|
175
|
+
const outputPath = path.resolve(outputFile);
|
|
176
|
+
await fs.promises.writeFile(outputPath, JSON.stringify(data, null, 2), "utf-8");
|
|
177
|
+
const collectionCount = Object.keys(data.collections || {}).length;
|
|
178
|
+
const totalRecords = Object.values(data.collections || {}).reduce((sum, col) => sum + (col?.count || 0), 0);
|
|
179
|
+
spinner.succeed(chalk.green(`ā
Exported to ${outputFile}`));
|
|
180
|
+
console.log(chalk.gray(` Collections: ${collectionCount}`));
|
|
181
|
+
console.log(chalk.gray(` Records: ${totalRecords}`));
|
|
182
|
+
}
|
|
183
|
+
catch (error) {
|
|
184
|
+
spinner.fail(chalk.red("Failed to export data"));
|
|
185
|
+
console.error(error);
|
|
186
|
+
process.exit(1);
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
/**
|
|
190
|
+
* Import data from a file
|
|
191
|
+
*/
|
|
192
|
+
dataCommand
|
|
193
|
+
.command("import <inputFile>")
|
|
194
|
+
.description("Import data from a JSON or ZIP file (auto-detects format)")
|
|
195
|
+
.option("-s, --strategy <strategy>", "Import strategy: replace or merge", "merge")
|
|
196
|
+
.action(async function (inputFile, options) {
|
|
197
|
+
const parent = this.parent;
|
|
198
|
+
const serverOpts = parent?.opts() || {};
|
|
199
|
+
const server = serverOpts.server || "http://localhost:4000";
|
|
200
|
+
const spinner = ora("Importing data...").start();
|
|
201
|
+
try {
|
|
202
|
+
const inputPath = path.resolve(inputFile);
|
|
203
|
+
if (!fs.existsSync(inputPath)) {
|
|
204
|
+
spinner.fail(chalk.red(`File not found: ${inputFile}`));
|
|
205
|
+
process.exit(1);
|
|
206
|
+
}
|
|
207
|
+
const buffer = await fs.promises.readFile(inputPath);
|
|
208
|
+
const isZip = isZipFile(buffer);
|
|
209
|
+
if (isZip) {
|
|
210
|
+
const formData = new FormData();
|
|
211
|
+
formData.append("file", new Blob([buffer]), path.basename(inputFile));
|
|
212
|
+
formData.append("strategy", options.strategy);
|
|
213
|
+
formData.append("verifyChecksums", "true");
|
|
214
|
+
const token = await getAuthCredentials(server);
|
|
215
|
+
const response = await fetch(`${server}/_archlast/admin/backup/restore`, {
|
|
216
|
+
method: "POST",
|
|
217
|
+
headers: {
|
|
218
|
+
"x-api-key": token,
|
|
219
|
+
},
|
|
220
|
+
body: formData,
|
|
221
|
+
});
|
|
222
|
+
const result = (await response.json());
|
|
223
|
+
if (!response.ok || !result.success) {
|
|
224
|
+
spinner.fail(chalk.red(`Failed to import: ${result.error || "Unknown error"}`));
|
|
225
|
+
process.exit(1);
|
|
226
|
+
}
|
|
227
|
+
spinner.succeed(chalk.green(`ā
Imported ${result.recordsImported} records`));
|
|
228
|
+
console.log(chalk.gray(` Format: ZIP`));
|
|
229
|
+
console.log(chalk.gray(` Collections: ${result.collections.length}`));
|
|
230
|
+
}
|
|
231
|
+
else {
|
|
232
|
+
const content = buffer.toString("utf-8");
|
|
233
|
+
const data = JSON.parse(content);
|
|
234
|
+
const token = await getAuthCredentials(server);
|
|
235
|
+
const response = await apiRequest(`${server}/_archlast/admin/backup/import`, {
|
|
236
|
+
method: "POST",
|
|
237
|
+
body: JSON.stringify({
|
|
238
|
+
data,
|
|
239
|
+
strategy: options.strategy,
|
|
240
|
+
}),
|
|
241
|
+
}, token);
|
|
242
|
+
const result = (await response.json());
|
|
243
|
+
if (!response.ok || !result.success) {
|
|
244
|
+
spinner.fail(chalk.red(`Failed to import: ${result.error || "Unknown error"}`));
|
|
245
|
+
process.exit(1);
|
|
246
|
+
}
|
|
247
|
+
spinner.succeed(chalk.green(`ā
Imported ${result.recordsImported} records`));
|
|
248
|
+
console.log(chalk.gray(` Format: JSON`));
|
|
249
|
+
console.log(chalk.gray(` Strategy: ${options.strategy}`));
|
|
250
|
+
console.log(chalk.gray(` Collections: ${result.collections.length}`));
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
catch (error) {
|
|
254
|
+
spinner.fail(chalk.red("Failed to import data"));
|
|
255
|
+
console.error(error);
|
|
256
|
+
process.exit(1);
|
|
257
|
+
}
|
|
258
|
+
});
|
|
259
|
+
/**
|
|
260
|
+
* Check if a buffer is a ZIP file by checking magic bytes
|
|
261
|
+
*/
|
|
262
|
+
function isZipFile(buffer) {
|
|
263
|
+
return (buffer.length >= 4 &&
|
|
264
|
+
buffer[0] === 0x50 &&
|
|
265
|
+
buffer[1] === 0x4b &&
|
|
266
|
+
buffer[2] === 0x03 &&
|
|
267
|
+
buffer[3] === 0x04);
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Delete a snapshot
|
|
271
|
+
*/
|
|
272
|
+
dataCommand
|
|
273
|
+
.command("delete <snapshotFile>")
|
|
274
|
+
.description("Delete a snapshot file (supports .json and .zip)")
|
|
275
|
+
.action(async function (snapshotFile) {
|
|
276
|
+
const parent = this.parent;
|
|
277
|
+
const serverOpts = parent?.opts() || {};
|
|
278
|
+
const server = serverOpts.server || "http://localhost:4000";
|
|
279
|
+
const spinner = ora("Deleting snapshot...").start();
|
|
280
|
+
try {
|
|
281
|
+
const token = await getAuthCredentials(server);
|
|
282
|
+
const filename = snapshotFile.endsWith(".json") || snapshotFile.endsWith(".zip")
|
|
283
|
+
? snapshotFile
|
|
284
|
+
: `${snapshotFile}.zip`;
|
|
285
|
+
const response = await apiRequest(`${server}/_archlast/admin/backup/snapshot/${filename}`, {
|
|
286
|
+
method: "DELETE",
|
|
287
|
+
}, token);
|
|
288
|
+
const result = (await response.json());
|
|
289
|
+
if (!response.ok || !result.success) {
|
|
290
|
+
spinner.fail(chalk.red(`Failed to delete: ${result.error || "Unknown error"}`));
|
|
291
|
+
process.exit(1);
|
|
292
|
+
}
|
|
293
|
+
spinner.succeed(chalk.green(`ā
Deleted snapshot: ${filename}`));
|
|
294
|
+
}
|
|
295
|
+
catch (error) {
|
|
296
|
+
spinner.fail(chalk.red("Failed to delete snapshot"));
|
|
297
|
+
console.error(error);
|
|
298
|
+
process.exit(1);
|
|
299
|
+
}
|
|
300
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deploy.d.ts","sourceRoot":"","sources":["../../src/commands/deploy.ts"],"names":[],"mappings":"AAQA,UAAU,aAAa;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAChC;AAED,wBAAsB,aAAa,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAwFzE"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import ora from "ora";
|
|
3
|
+
import { CodeAnalyzer } from "../analyzer";
|
|
4
|
+
import { TypeGenerator } from "../generator";
|
|
5
|
+
import { CodeUploader } from "../uploader";
|
|
6
|
+
export async function deployCommand(options) {
|
|
7
|
+
const archlastPath = options.path || ".";
|
|
8
|
+
const maxPoll = parseInt(String(options.maxPoll || "30"), 10);
|
|
9
|
+
console.log(chalk.blue.bold("š Archlast Deploy\n"));
|
|
10
|
+
const analyzer = new CodeAnalyzer(archlastPath);
|
|
11
|
+
const generator = new TypeGenerator(archlastPath);
|
|
12
|
+
const uploader = new CodeUploader(options.server || "http://localhost:4000", archlastPath);
|
|
13
|
+
const spinner = ora("Checking server availability...").start();
|
|
14
|
+
try {
|
|
15
|
+
// Poll server to ensure it's reachable
|
|
16
|
+
const serverReady = await uploader.pollServer(maxPoll, 1000);
|
|
17
|
+
if (!serverReady) {
|
|
18
|
+
spinner.fail(chalk.red(`Server not reachable after ${maxPoll} retries`));
|
|
19
|
+
process.exit(1);
|
|
20
|
+
}
|
|
21
|
+
spinner.text = "Analyzing code...";
|
|
22
|
+
const analysis = await analyzer.analyze();
|
|
23
|
+
spinner.text = "Generating types...";
|
|
24
|
+
await generator.generate(analysis);
|
|
25
|
+
spinner.text = "Checking for changes...";
|
|
26
|
+
const diffCheck = await uploader.checkHashDiff();
|
|
27
|
+
if (!diffCheck.hasChanges) {
|
|
28
|
+
spinner.succeed(chalk.green("ā
No changes detected, skipping upload"));
|
|
29
|
+
console.log(chalk.gray(`\nš Server: ${options.server || "http://localhost:4000"}`));
|
|
30
|
+
console.log(chalk.green("\n⨠Already up to date!\n"));
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
spinner.text = "Uploading to server...";
|
|
34
|
+
const uploadResult = await uploader.uploadDelta();
|
|
35
|
+
if (!uploadResult.success) {
|
|
36
|
+
spinner.fail(chalk.red(`Upload failed: ${uploadResult.message}`));
|
|
37
|
+
process.exit(1);
|
|
38
|
+
}
|
|
39
|
+
const deploymentType = uploadResult.deploymentType === "delta" ? "Ī (Delta)" : "Full";
|
|
40
|
+
spinner.succeed(chalk.green(`ā
Deployed ${analysis.functions.length} function(s) to ${options.server} [${deploymentType}]`));
|
|
41
|
+
console.log(chalk.gray("\nFunctions:"));
|
|
42
|
+
analysis.functions.forEach((f) => {
|
|
43
|
+
console.log(chalk.gray(` - ${f.name} (${f.type})`));
|
|
44
|
+
});
|
|
45
|
+
if (analysis.schema) {
|
|
46
|
+
console.log(chalk.gray("\nā Schema included"));
|
|
47
|
+
}
|
|
48
|
+
if (diffCheck.reason) {
|
|
49
|
+
console.log(chalk.gray(`\nš Change reason: ${diffCheck.reason}`));
|
|
50
|
+
}
|
|
51
|
+
console.log(chalk.green(`\nš Server: ${options.server || "http://localhost:4000"}`));
|
|
52
|
+
console.log(chalk.green("\n⨠Deployment complete!\n"));
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
spinner.fail(chalk.red("Deployment failed"));
|
|
56
|
+
console.error(error);
|
|
57
|
+
process.exit(1);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dev.d.ts","sourceRoot":"","sources":["../../src/commands/dev.ts"],"names":[],"mappings":"AAWA,UAAU,UAAU;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAChC;AAED,wBAAsB,UAAU,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAmMnE"}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import * as path from "path";
|
|
2
|
+
import chalk from "chalk";
|
|
3
|
+
import ora from "ora";
|
|
4
|
+
import { FileWatcher } from "../watcher";
|
|
5
|
+
import { CodeAnalyzer, CodeDiffer } from "../analyzer";
|
|
6
|
+
import { TypeGenerator } from "../generator";
|
|
7
|
+
import { CodeUploader } from "../uploader";
|
|
8
|
+
import { listenToServerEvents } from "../events-listener";
|
|
9
|
+
export async function devCommand(options) {
|
|
10
|
+
const archlastPath = options.path || ".";
|
|
11
|
+
const maxPoll = parseInt(String(options.maxPoll || "30"), 10);
|
|
12
|
+
console.log(chalk.blue.bold("š Archlast Dev Mode\n"));
|
|
13
|
+
const analyzer = new CodeAnalyzer(archlastPath);
|
|
14
|
+
const differ = new CodeDiffer();
|
|
15
|
+
const generator = new TypeGenerator(archlastPath);
|
|
16
|
+
const watcher = new FileWatcher(archlastPath);
|
|
17
|
+
const uploader = new CodeUploader(options.server || "http://localhost:4000", archlastPath);
|
|
18
|
+
let previousAnalysis = null;
|
|
19
|
+
// Initial analysis
|
|
20
|
+
const spinner = ora("Checking server availability...").start();
|
|
21
|
+
try {
|
|
22
|
+
// Poll server to ensure it's reachable
|
|
23
|
+
const serverReady = await uploader.pollServer(maxPoll, 1000);
|
|
24
|
+
if (!serverReady) {
|
|
25
|
+
spinner.fail(chalk.red(`Server not reachable after ${maxPoll} retries`));
|
|
26
|
+
console.log(chalk.yellow("\nš” Tip: Make sure the Archlast server is running before starting dev mode"));
|
|
27
|
+
process.exit(1);
|
|
28
|
+
}
|
|
29
|
+
spinner.text = "Analyzing code...";
|
|
30
|
+
const analysis = await analyzer.analyze();
|
|
31
|
+
await generator.generate(analysis);
|
|
32
|
+
spinner.text = "Checking for changes...";
|
|
33
|
+
const diffCheck = await uploader.checkHashDiff();
|
|
34
|
+
if (!diffCheck.hasChanges) {
|
|
35
|
+
spinner.succeed(chalk.green(`Found ${analysis.functions.length} function(s) | Generated types | No changes detected`));
|
|
36
|
+
console.log(chalk.gray("\nWatching for changes...\n"));
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
spinner.text = "Uploading to server...";
|
|
40
|
+
const uploadResult = await uploader.uploadDelta();
|
|
41
|
+
if (uploadResult.success) {
|
|
42
|
+
const deploymentType = uploadResult.deploymentType === "delta" ? "Ī" : "Full";
|
|
43
|
+
spinner.succeed(chalk.green(`Found ${analysis.functions.length} function(s) | Generated types | Uploaded (${deploymentType}) to ${options.server || "http://localhost:4000"}`));
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
spinner.warn(chalk.yellow(`Found ${analysis.functions.length} function(s) | Generated types | Upload failed: ${uploadResult.message}`));
|
|
47
|
+
}
|
|
48
|
+
console.log(chalk.gray("\nWatching for changes...\n"));
|
|
49
|
+
}
|
|
50
|
+
previousAnalysis = analysis;
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
spinner.fail(chalk.red("Analysis failed"));
|
|
54
|
+
console.error(error);
|
|
55
|
+
process.exit(1);
|
|
56
|
+
}
|
|
57
|
+
// Watch for changes
|
|
58
|
+
watcher.on("change", async (event) => {
|
|
59
|
+
const relativePath = path.relative(process.cwd(), event.path);
|
|
60
|
+
console.log(chalk.yellow(`\nš ${event.type}: ${relativePath}`));
|
|
61
|
+
const analyzeSpinner = ora("Re-analyzing...").start();
|
|
62
|
+
try {
|
|
63
|
+
const newAnalysis = await analyzer.analyze();
|
|
64
|
+
if (previousAnalysis) {
|
|
65
|
+
const diff = differ.diff(previousAnalysis, newAnalysis);
|
|
66
|
+
if (diff.added.length > 0) {
|
|
67
|
+
analyzeSpinner.info(chalk.green(`⨠Added ${diff.added.length} function(s)`));
|
|
68
|
+
diff.added.forEach((f) => console.log(chalk.green(` + ${f.name} (${f.type})`)));
|
|
69
|
+
}
|
|
70
|
+
if (diff.modified.length > 0) {
|
|
71
|
+
analyzeSpinner.info(chalk.blue(`š Modified ${diff.modified.length} function(s)`));
|
|
72
|
+
diff.modified.forEach((f) => console.log(chalk.blue(` ~ ${f.name} (${f.type})`)));
|
|
73
|
+
}
|
|
74
|
+
if (diff.removed.length > 0) {
|
|
75
|
+
analyzeSpinner.info(chalk.red(`šļø Removed ${diff.removed.length} function(s)`));
|
|
76
|
+
diff.removed.forEach((f) => console.log(chalk.red(` - ${f.name} (${f.type})`)));
|
|
77
|
+
}
|
|
78
|
+
if (diff.schemaChanged) {
|
|
79
|
+
analyzeSpinner.info(chalk.magenta("š Schema changed"));
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
await generator.generate(newAnalysis);
|
|
83
|
+
analyzeSpinner.text = "Uploading changes...";
|
|
84
|
+
const uploadResult = await uploader.uploadDelta();
|
|
85
|
+
previousAnalysis = newAnalysis;
|
|
86
|
+
if (uploadResult.success) {
|
|
87
|
+
const deploymentType = uploadResult.deploymentType === "delta" ? "Ī" : "Full";
|
|
88
|
+
analyzeSpinner.succeed(chalk.green(`ā
Types regenerated | Uploaded (${deploymentType})`));
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
analyzeSpinner.warn(chalk.yellow(`ā
Types regenerated | Upload failed: ${uploadResult.message}`));
|
|
92
|
+
}
|
|
93
|
+
console.log(chalk.gray("\nWatching for changes...\n"));
|
|
94
|
+
}
|
|
95
|
+
catch (error) {
|
|
96
|
+
analyzeSpinner.fail(chalk.red("Analysis failed"));
|
|
97
|
+
console.error(error);
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
watcher.start();
|
|
101
|
+
// Start listening to server events for reactive deployments
|
|
102
|
+
const serverUrl = options.server || "http://localhost:4000";
|
|
103
|
+
console.log(chalk.gray(`\nš Connected to server events stream (${serverUrl})`));
|
|
104
|
+
const stopListening = listenToServerEvents({
|
|
105
|
+
serverUrl,
|
|
106
|
+
onSchemaUpdated: async () => {
|
|
107
|
+
// Regenerate types when schema is updated on server
|
|
108
|
+
try {
|
|
109
|
+
const analysis = await analyzer.analyze();
|
|
110
|
+
await generator.generate(analysis);
|
|
111
|
+
previousAnalysis = analysis;
|
|
112
|
+
console.log(chalk.green("ā
Types regenerated from server schema"));
|
|
113
|
+
}
|
|
114
|
+
catch (error) {
|
|
115
|
+
console.error(chalk.red("Failed to regenerate types:"), error);
|
|
116
|
+
}
|
|
117
|
+
},
|
|
118
|
+
onDeploymentComplete: (type) => {
|
|
119
|
+
console.log(chalk.blue(`\n⨠Server deployment complete (${type})`));
|
|
120
|
+
},
|
|
121
|
+
onError: (error) => {
|
|
122
|
+
// Silently ignore connection errors, will retry
|
|
123
|
+
},
|
|
124
|
+
});
|
|
125
|
+
// Handle graceful shutdown
|
|
126
|
+
process.on("SIGINT", () => {
|
|
127
|
+
console.log(chalk.yellow("\n\nš Shutting down..."));
|
|
128
|
+
watcher.stop();
|
|
129
|
+
stopListening();
|
|
130
|
+
process.exit(0);
|
|
131
|
+
});
|
|
132
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate.d.ts","sourceRoot":"","sources":["../../src/commands/generate.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA8GpC;;GAEG;AACH,eAAO,MAAM,eAAe,SASU,CAAC"}
|