@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.
Files changed (92) hide show
  1. package/README.md +141 -0
  2. package/dist/analyzer.d.ts +96 -0
  3. package/dist/analyzer.d.ts.map +1 -0
  4. package/dist/analyzer.js +404 -0
  5. package/dist/auth.d.ts +14 -0
  6. package/dist/auth.d.ts.map +1 -0
  7. package/dist/auth.js +106 -0
  8. package/dist/cli.d.ts +3 -0
  9. package/dist/cli.d.ts.map +1 -0
  10. package/dist/cli.js +322875 -0
  11. package/dist/commands/build.d.ts +6 -0
  12. package/dist/commands/build.d.ts.map +1 -0
  13. package/dist/commands/build.js +36 -0
  14. package/dist/commands/config.d.ts +8 -0
  15. package/dist/commands/config.d.ts.map +1 -0
  16. package/dist/commands/config.js +23 -0
  17. package/dist/commands/data.d.ts +6 -0
  18. package/dist/commands/data.d.ts.map +1 -0
  19. package/dist/commands/data.js +300 -0
  20. package/dist/commands/deploy.d.ts +9 -0
  21. package/dist/commands/deploy.d.ts.map +1 -0
  22. package/dist/commands/deploy.js +59 -0
  23. package/dist/commands/dev.d.ts +10 -0
  24. package/dist/commands/dev.d.ts.map +1 -0
  25. package/dist/commands/dev.js +132 -0
  26. package/dist/commands/generate.d.ts +6 -0
  27. package/dist/commands/generate.d.ts.map +1 -0
  28. package/dist/commands/generate.js +100 -0
  29. package/dist/commands/init.d.ts +7 -0
  30. package/dist/commands/init.d.ts.map +1 -0
  31. package/dist/commands/logs.d.ts +10 -0
  32. package/dist/commands/logs.d.ts.map +1 -0
  33. package/dist/commands/logs.js +38 -0
  34. package/dist/commands/pull.d.ts +16 -0
  35. package/dist/commands/pull.d.ts.map +1 -0
  36. package/dist/commands/pull.js +415 -0
  37. package/dist/commands/restart.d.ts +11 -0
  38. package/dist/commands/restart.d.ts.map +1 -0
  39. package/dist/commands/restart.js +63 -0
  40. package/dist/commands/start.d.ts +11 -0
  41. package/dist/commands/start.d.ts.map +1 -0
  42. package/dist/commands/start.js +74 -0
  43. package/dist/commands/status.d.ts +8 -0
  44. package/dist/commands/status.d.ts.map +1 -0
  45. package/dist/commands/status.js +69 -0
  46. package/dist/commands/stop.d.ts +8 -0
  47. package/dist/commands/stop.d.ts.map +1 -0
  48. package/dist/commands/stop.js +23 -0
  49. package/dist/commands/upgrade.d.ts +12 -0
  50. package/dist/commands/upgrade.d.ts.map +1 -0
  51. package/dist/commands/upgrade.js +77 -0
  52. package/dist/docker/compose.d.ts +3 -0
  53. package/dist/docker/compose.d.ts.map +1 -0
  54. package/dist/docker/compose.js +47 -0
  55. package/dist/docker/config.d.ts +12 -0
  56. package/dist/docker/config.d.ts.map +1 -0
  57. package/dist/docker/config.js +183 -0
  58. package/dist/docker/manager.d.ts +19 -0
  59. package/dist/docker/manager.d.ts.map +1 -0
  60. package/dist/docker/manager.js +239 -0
  61. package/dist/docker/ports.d.ts +6 -0
  62. package/dist/docker/ports.d.ts.map +1 -0
  63. package/dist/docker/restart-on-deploy.d.ts +6 -0
  64. package/dist/docker/restart-on-deploy.d.ts.map +1 -0
  65. package/dist/docker/types.d.ts +36 -0
  66. package/dist/docker/types.d.ts.map +1 -0
  67. package/dist/docker/types.js +1 -0
  68. package/dist/events-listener.d.ts +19 -0
  69. package/dist/events-listener.d.ts.map +1 -0
  70. package/dist/events-listener.js +105 -0
  71. package/dist/generator.d.ts +44 -0
  72. package/dist/generator.d.ts.map +1 -0
  73. package/dist/generator.js +1816 -0
  74. package/dist/generators/di.d.ts +21 -0
  75. package/dist/generators/di.d.ts.map +1 -0
  76. package/dist/generators/di.js +100 -0
  77. package/dist/index.d.ts +7 -0
  78. package/dist/index.d.ts.map +1 -0
  79. package/dist/index.js +4 -0
  80. package/dist/project.d.ts +18 -0
  81. package/dist/project.d.ts.map +1 -0
  82. package/dist/protocol.d.ts +58 -0
  83. package/dist/protocol.d.ts.map +1 -0
  84. package/dist/protocol.js +5 -0
  85. package/dist/uploader.d.ts +63 -0
  86. package/dist/uploader.d.ts.map +1 -0
  87. package/dist/uploader.js +255 -0
  88. package/dist/watcher.d.ts +13 -0
  89. package/dist/watcher.d.ts.map +1 -0
  90. package/dist/watcher.js +38 -0
  91. package/package.json +58 -0
  92. package/scripts/postinstall.cjs +65 -0
@@ -0,0 +1,21 @@
1
+ /**
2
+ * DI Generator - Generates provider registration code
3
+ */
4
+ import type { InjectableDefinition } from "../analyzer.js";
5
+ export interface DIGeneratorOptions {
6
+ outputDir: string;
7
+ injectables: InjectableDefinition[];
8
+ }
9
+ export declare class DIGenerator {
10
+ private outputDir;
11
+ private injectables;
12
+ constructor(options: DIGeneratorOptions);
13
+ generate(): Promise<void>;
14
+ private generateContent;
15
+ private getImportPath;
16
+ }
17
+ /**
18
+ * Generate DI registration code
19
+ */
20
+ export declare function generateDI(outputDir: string, injectables: InjectableDefinition[]): Promise<void>;
21
+ //# sourceMappingURL=di.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"di.d.ts","sourceRoot":"","sources":["../../src/generators/di.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAE3D,MAAM,WAAW,kBAAkB;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,oBAAoB,EAAE,CAAC;CACvC;AAED,qBAAa,WAAW;IACpB,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,WAAW,CAAyB;gBAEhC,OAAO,EAAE,kBAAkB;IAKjC,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAY/B,OAAO,CAAC,eAAe;IA2EvB,OAAO,CAAC,aAAa;CAQxB;AAED;;GAEG;AACH,wBAAsB,UAAU,CAC5B,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,oBAAoB,EAAE,GACpC,OAAO,CAAC,IAAI,CAAC,CAOf"}
@@ -0,0 +1,100 @@
1
+ /**
2
+ * DI Generator - Generates provider registration code
3
+ */
4
+ import * as fs from "fs";
5
+ import * as path from "path";
6
+ export class DIGenerator {
7
+ outputDir;
8
+ injectables;
9
+ constructor(options) {
10
+ this.outputDir = options.outputDir;
11
+ this.injectables = options.injectables;
12
+ }
13
+ async generate() {
14
+ // Ensure output directory exists
15
+ if (!fs.existsSync(this.outputDir)) {
16
+ fs.mkdirSync(this.outputDir, { recursive: true });
17
+ }
18
+ const content = this.generateContent();
19
+ const outputPath = path.join(this.outputDir, "di.ts");
20
+ fs.writeFileSync(outputPath, content, "utf-8");
21
+ }
22
+ generateContent() {
23
+ const lines = [];
24
+ // Header comment
25
+ lines.push("/**");
26
+ lines.push(" * AUTO-GENERATED FILE - DO NOT EDIT");
27
+ lines.push(" * Generated by @archlast/cli");
28
+ lines.push(" * Contains provider registrations for dependency injection");
29
+ lines.push(" */");
30
+ lines.push("");
31
+ // Import Container
32
+ lines.push('import { Container } from "@archlast/server/di/container";');
33
+ lines.push("");
34
+ // Generate imports for each injectable
35
+ const importsByFile = new Map();
36
+ for (const injectable of this.injectables) {
37
+ const importPath = this.getImportPath(injectable.filePath);
38
+ if (!importsByFile.has(importPath)) {
39
+ importsByFile.set(importPath, []);
40
+ }
41
+ importsByFile.get(importPath).push(injectable.className);
42
+ }
43
+ for (const [importPath, classNames] of importsByFile) {
44
+ lines.push(`import { ${classNames.join(", ")} } from "${importPath}";`);
45
+ }
46
+ if (importsByFile.size > 0) {
47
+ lines.push("");
48
+ }
49
+ // Generate provider definitions
50
+ lines.push("/**");
51
+ lines.push(" * All detected injectable providers");
52
+ lines.push(" */");
53
+ lines.push("export const providers = [");
54
+ for (const injectable of this.injectables) {
55
+ lines.push(" {");
56
+ lines.push(` provide: "${injectable.provide}",`);
57
+ lines.push(` useClass: ${injectable.className},`);
58
+ if (injectable.implementation !== "default") {
59
+ lines.push(` implementation: "${injectable.implementation}",`);
60
+ }
61
+ lines.push(` scope: "transient",`);
62
+ lines.push(" },");
63
+ }
64
+ lines.push("];");
65
+ lines.push("");
66
+ // Generate registration function
67
+ lines.push("/**");
68
+ lines.push(" * Register all providers with the container");
69
+ lines.push(" */");
70
+ lines.push("export function registerProviders(container: Container): void {");
71
+ lines.push(" for (const provider of providers) {");
72
+ lines.push(' console.log(`[DI] Registering ${provider.provide} impl=${(provider as any).implementation ?? "default"} scope=${(provider as any).scope ?? "singleton"}`);');
73
+ lines.push(" container.register(provider);");
74
+ lines.push(" }");
75
+ lines.push("}");
76
+ lines.push("");
77
+ // Export provider count for debugging
78
+ lines.push(`export const providerCount = ${this.injectables.length}; `);
79
+ lines.push("");
80
+ return lines.join("\n");
81
+ }
82
+ getImportPath(filePath) {
83
+ // Convert file path to relative import path
84
+ // e.g., "services/email/resend.ts" -> "../src/services/email/resend"
85
+ const withoutExt = filePath.replace(/\.(ts|js)$/, "");
86
+ // Normalize path separators to forward slashes for imports
87
+ const normalized = withoutExt.replace(/\\/g, "/");
88
+ return `../src/${normalized}`;
89
+ }
90
+ }
91
+ /**
92
+ * Generate DI registration code
93
+ */
94
+ export async function generateDI(outputDir, injectables) {
95
+ const generator = new DIGenerator({
96
+ outputDir,
97
+ injectables,
98
+ });
99
+ await generator.generate();
100
+ }
@@ -0,0 +1,7 @@
1
+ export { CodeAnalyzer, CodeDiffer } from "./analyzer";
2
+ export { FileWatcher } from "./watcher";
3
+ export { TypeGenerator } from "./generator";
4
+ export { CodeUploader } from "./uploader";
5
+ export type { AnalysisResult, FunctionDefinition } from "./analyzer";
6
+ export type { UploadPayload } from "./uploader";
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACtD,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACxC,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,YAAY,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AACrE,YAAY,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,4 @@
1
+ export { CodeAnalyzer, CodeDiffer } from "./analyzer";
2
+ export { FileWatcher } from "./watcher";
3
+ export { TypeGenerator } from "./generator";
4
+ export { CodeUploader } from "./uploader";
@@ -0,0 +1,18 @@
1
+ export interface ProjectMetadata {
2
+ id: string;
3
+ name: string;
4
+ containerName: string;
5
+ dataVolumeName: string;
6
+ port: number;
7
+ createdAt: string;
8
+ projectRoot?: string;
9
+ }
10
+ export declare function getProjectDir(projectPath: string): string;
11
+ export declare function getProjectMetadataPath(projectPath: string): string;
12
+ export declare function readProjectMetadata(projectPath: string): ProjectMetadata | null;
13
+ export declare function writeProjectMetadata(projectPath: string, metadata: ProjectMetadata): void;
14
+ export declare function ensureProjectMetadata(projectPath: string, overrides?: Partial<ProjectMetadata>): ProjectMetadata;
15
+ export declare function updateProjectMetadata(projectPath: string, updates: Partial<ProjectMetadata>): ProjectMetadata;
16
+ export declare function deriveContainerName(projectSlug: string, projectId: string): string;
17
+ export declare function deriveVolumeName(projectSlug: string, projectId: string): string;
18
+ //# sourceMappingURL=project.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"project.d.ts","sourceRoot":"","sources":["../src/project.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,eAAe;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;CACxB;AAOD,wBAAgB,aAAa,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAEzD;AAED,wBAAgB,sBAAsB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAElE;AAED,wBAAgB,mBAAmB,CAAC,WAAW,EAAE,MAAM,GAAG,eAAe,GAAG,IAAI,CAS/E;AAED,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,eAAe,GAAG,IAAI,CAIzF;AAED,wBAAgB,qBAAqB,CACjC,WAAW,EAAE,MAAM,EACnB,SAAS,GAAE,OAAO,CAAC,eAAe,CAAM,GACzC,eAAe,CA4BjB;AAED,wBAAgB,qBAAqB,CACjC,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,OAAO,CAAC,eAAe,CAAC,GAClC,eAAe,CAMjB;AAED,wBAAgB,mBAAmB,CAAC,WAAW,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAIlF;AAED,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAI/E"}
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Delta Deployment Protocol
3
+ * Defines the contract between CLI and server for incremental deployments
4
+ */
5
+ /**
6
+ * Represents a single file change
7
+ */
8
+ export interface FileChange {
9
+ filePath: string;
10
+ code: string;
11
+ }
12
+ /**
13
+ * Full deployment payload (existing format)
14
+ */
15
+ export interface FullUploadPayload {
16
+ files: Array<{
17
+ filePath: string;
18
+ code: string;
19
+ }>;
20
+ timestamp: number;
21
+ }
22
+ /**
23
+ * Delta deployment payload (new format)
24
+ */
25
+ export interface DeltaUploadPayload {
26
+ timestamp: number;
27
+ changes: {
28
+ added: FileChange[];
29
+ modified: FileChange[];
30
+ removed: string[];
31
+ };
32
+ manifestHash?: string;
33
+ }
34
+ /**
35
+ * Result of computing differences between local and server state
36
+ */
37
+ export interface DeltaResult {
38
+ added: Array<{
39
+ filePath: string;
40
+ code: string;
41
+ }>;
42
+ modified: Array<{
43
+ filePath: string;
44
+ code: string;
45
+ }>;
46
+ removed: string[];
47
+ }
48
+ /**
49
+ * Deployment response from server
50
+ */
51
+ export interface DeploymentResponse {
52
+ success: boolean;
53
+ message: string;
54
+ functions: number;
55
+ schema: boolean;
56
+ deploymentType?: "full" | "delta";
57
+ }
58
+ //# sourceMappingURL=protocol.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;GAEG;AACH,MAAM,WAAW,UAAU;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAC9B,KAAK,EAAE,KAAK,CAAC;QACT,QAAQ,EAAE,MAAM,CAAC;QACjB,IAAI,EAAE,MAAM,CAAC;KAChB,CAAC,CAAC;IACH,SAAS,EAAE,MAAM,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE;QACL,KAAK,EAAE,UAAU,EAAE,CAAC;QACpB,QAAQ,EAAE,UAAU,EAAE,CAAC;QACvB,OAAO,EAAE,MAAM,EAAE,CAAC;KACrB,CAAC;IAEF,YAAY,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IACxB,KAAK,EAAE,KAAK,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACjD,QAAQ,EAAE,KAAK,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACpD,OAAO,EAAE,MAAM,EAAE,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IAC/B,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,OAAO,CAAC;IAChB,cAAc,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;CACrC"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Delta Deployment Protocol
3
+ * Defines the contract between CLI and server for incremental deployments
4
+ */
5
+ export {};
@@ -0,0 +1,63 @@
1
+ import type { DeltaResult } from "./protocol.js";
2
+ export interface UploadPayload {
3
+ files: Array<{
4
+ filePath: string;
5
+ code: string;
6
+ }>;
7
+ timestamp: number;
8
+ }
9
+ export declare class CodeUploader {
10
+ private serverUrl;
11
+ private archlastPath;
12
+ private projectRoot;
13
+ private includeNodeModules;
14
+ constructor(serverUrl: string, archlastPath: string, options?: {
15
+ includeNodeModules?: boolean;
16
+ });
17
+ /**
18
+ * Get auth headers for requests using Better-Auth API key
19
+ */
20
+ private getAuthHeaders;
21
+ /**
22
+ * Poll server until it's reachable
23
+ * @param maxRetries Maximum number of retries
24
+ * @param interval Milliseconds between retries
25
+ * @returns true if server is reachable, false if max retries exceeded
26
+ */
27
+ pollServer(maxRetries?: number, interval?: number): Promise<boolean>;
28
+ /**
29
+ * Check if local files differ from server manifest
30
+ * @returns Object with hasChanges flag and optional reason
31
+ * @deprecated Use computeDelta() instead for better change detection
32
+ */
33
+ checkHashDiff(): Promise<{
34
+ hasChanges: boolean;
35
+ reason?: string;
36
+ }>;
37
+ /**
38
+ * Compute delta between local files and server manifest
39
+ * @returns DeltaResult with added, modified, and removed files
40
+ */
41
+ computeDelta(): Promise<DeltaResult>;
42
+ upload(): Promise<{
43
+ success: boolean;
44
+ message?: string;
45
+ }>;
46
+ /**
47
+ * Upload only the changed files using delta deployment
48
+ * @returns Deployment result with deployment type indicator
49
+ */
50
+ uploadDelta(): Promise<{
51
+ success: boolean;
52
+ message?: string;
53
+ deploymentType?: "full" | "delta";
54
+ }>;
55
+ /**
56
+ * Compute hash of the full manifest for integrity verification
57
+ */
58
+ private computeManifestHash;
59
+ private preparePayload;
60
+ private collectAllFiles;
61
+ private hashString;
62
+ }
63
+ //# sourceMappingURL=uploader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"uploader.d.ts","sourceRoot":"","sources":["../src/uploader.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAGR,WAAW,EAEd,MAAM,eAAe,CAAC;AAIvB,MAAM,WAAW,aAAa;IAC1B,KAAK,EAAE,KAAK,CAAC;QACT,QAAQ,EAAE,MAAM,CAAC;QACjB,IAAI,EAAE,MAAM,CAAC;KAChB,CAAC,CAAC;IACH,SAAS,EAAE,MAAM,CAAC;CACrB;AAED,qBAAa,YAAY;IACrB,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,kBAAkB,CAAU;gBAExB,SAAS,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,kBAAkB,CAAC,EAAE,OAAO,CAAA;KAAE;IAS/F;;OAEG;IACH,OAAO,CAAC,cAAc;IAMtB;;;;;OAKG;IACG,UAAU,CAAC,UAAU,GAAE,MAAW,EAAE,QAAQ,GAAE,MAAa,GAAG,OAAO,CAAC,OAAO,CAAC;IAqBpF;;;;OAIG;IACG,aAAa,IAAI,OAAO,CAAC;QAAE,UAAU,EAAE,OAAO,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAiBxE;;;OAGG;IACG,YAAY,IAAI,OAAO,CAAC,WAAW,CAAC;IAiEpC,MAAM,IAAI,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IA4B/D;;;OAGG;IACG,WAAW,IAAI,OAAO,CAAC;QACzB,OAAO,EAAE,OAAO,CAAC;QACjB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,cAAc,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;KACrC,CAAC;IA0DF;;OAEG;IACH,OAAO,CAAC,mBAAmB;YAQb,cAAc;YASd,eAAe;IAwE7B,OAAO,CAAC,UAAU;CASrB"}
@@ -0,0 +1,255 @@
1
+ import * as fs from "fs";
2
+ import * as path from "path";
3
+ import { glob } from "glob";
4
+ import { readAdminToken } from "./auth.js";
5
+ export class CodeUploader {
6
+ serverUrl;
7
+ archlastPath;
8
+ constructor(serverUrl, archlastPath) {
9
+ this.serverUrl = serverUrl.replace(/\/$/, "");
10
+ this.archlastPath = path.resolve(archlastPath);
11
+ }
12
+ /**
13
+ * Get auth headers for requests using Better-Auth API key
14
+ */
15
+ getAuthHeaders() {
16
+ const apiKey = readAdminToken(this.archlastPath);
17
+ console.log(`[CLI] Using Better-Auth API key for deployment`);
18
+ return { "x-api-key": apiKey };
19
+ }
20
+ /**
21
+ * Poll server until it's reachable
22
+ * @param maxRetries Maximum number of retries
23
+ * @param interval Milliseconds between retries
24
+ * @returns true if server is reachable, false if max retries exceeded
25
+ */
26
+ async pollServer(maxRetries = 30, interval = 1000) {
27
+ for (let i = 0; i < maxRetries; i++) {
28
+ try {
29
+ const response = await fetch(`${this.serverUrl}/`, {
30
+ method: "GET",
31
+ signal: AbortSignal.timeout(2000),
32
+ });
33
+ if (response.ok || response.status < 500) {
34
+ return true;
35
+ }
36
+ }
37
+ catch (error) {
38
+ // Server not ready, continue polling
39
+ }
40
+ if (i < maxRetries - 1) {
41
+ await new Promise((resolve) => setTimeout(resolve, interval));
42
+ }
43
+ }
44
+ return false;
45
+ }
46
+ /**
47
+ * Check if local files differ from server manifest
48
+ * @returns Object with hasChanges flag and optional reason
49
+ * @deprecated Use computeDelta() instead for better change detection
50
+ */
51
+ async checkHashDiff() {
52
+ const delta = await this.computeDelta();
53
+ const hasChanges = delta.added.length > 0 || delta.modified.length > 0 || delta.removed.length > 0;
54
+ if (!hasChanges) {
55
+ return { hasChanges: false };
56
+ }
57
+ const reasons = [];
58
+ if (delta.added.length > 0)
59
+ reasons.push(`${delta.added.length} added`);
60
+ if (delta.modified.length > 0)
61
+ reasons.push(`${delta.modified.length} modified`);
62
+ if (delta.removed.length > 0)
63
+ reasons.push(`${delta.removed.length} removed`);
64
+ return { hasChanges: true, reason: reasons.join(", ") };
65
+ }
66
+ /**
67
+ * Compute delta between local files and server manifest
68
+ * @returns DeltaResult with added, modified, and removed files
69
+ */
70
+ async computeDelta() {
71
+ try {
72
+ const response = await fetch(`${this.serverUrl}/_archlast/deploy/manifest`, {
73
+ method: "GET",
74
+ headers: this.getAuthHeaders(),
75
+ signal: AbortSignal.timeout(5000),
76
+ });
77
+ if (!response.ok) {
78
+ // Check for auth error
79
+ if (response.status === 401) {
80
+ const error = await response.text();
81
+ throw new Error(`Authentication failed: ${error || "Invalid admin token"}\n\nPlease create a token in the dashboard (Settings → API Tokens) and update ARCHLAST_ADMIN_TOKEN in your .env file.`);
82
+ }
83
+ // Server not ready or no manifest - treat all files as added
84
+ const localFiles = await this.collectAllFiles();
85
+ return {
86
+ added: localFiles,
87
+ modified: [],
88
+ removed: [],
89
+ };
90
+ }
91
+ const serverManifest = (await response.json());
92
+ const localFiles = await this.collectAllFiles();
93
+ const localManifest = {};
94
+ for (const file of localFiles) {
95
+ localManifest[file.filePath] = this.hashString(file.code.trim());
96
+ }
97
+ const added = [];
98
+ const modified = [];
99
+ const removed = [];
100
+ // Check for added or modified files
101
+ for (const [filePath, hash] of Object.entries(localManifest)) {
102
+ if (!serverManifest[filePath]) {
103
+ const file = localFiles.find((f) => f.filePath === filePath);
104
+ if (file)
105
+ added.push(file);
106
+ }
107
+ else if (serverManifest[filePath] !== hash) {
108
+ const file = localFiles.find((f) => f.filePath === filePath);
109
+ if (file)
110
+ modified.push(file);
111
+ }
112
+ }
113
+ // Check for removed files
114
+ for (const filePath of Object.keys(serverManifest)) {
115
+ if (!localManifest[filePath]) {
116
+ removed.push(filePath);
117
+ }
118
+ }
119
+ return { added, modified, removed };
120
+ }
121
+ catch (error) {
122
+ // If we can't check, assume all files need to be uploaded
123
+ const localFiles = await this.collectAllFiles();
124
+ return {
125
+ added: localFiles,
126
+ modified: [],
127
+ removed: [],
128
+ };
129
+ }
130
+ }
131
+ async upload() {
132
+ const payload = await this.preparePayload();
133
+ try {
134
+ const response = await fetch(`${this.serverUrl}/_archlast/deploy`, {
135
+ method: "POST",
136
+ headers: {
137
+ "Content-Type": "application/json",
138
+ ...this.getAuthHeaders(),
139
+ },
140
+ body: JSON.stringify(payload),
141
+ });
142
+ if (!response.ok) {
143
+ const error = await response.text();
144
+ return { success: false, message: error };
145
+ }
146
+ const result = (await response.json());
147
+ return { success: true, message: result.message || "Deployed successfully" };
148
+ }
149
+ catch (error) {
150
+ return {
151
+ success: false,
152
+ message: error instanceof Error ? error.message : "Upload failed",
153
+ };
154
+ }
155
+ }
156
+ /**
157
+ * Upload only the changed files using delta deployment
158
+ * @returns Deployment result with deployment type indicator
159
+ */
160
+ async uploadDelta() {
161
+ const delta = await this.computeDelta();
162
+ const hasChanges = delta.added.length > 0 || delta.modified.length > 0 || delta.removed.length > 0;
163
+ if (!hasChanges) {
164
+ return { success: true, message: "No changes detected", deploymentType: "delta" };
165
+ }
166
+ const payload = {
167
+ timestamp: Date.now(),
168
+ changes: delta,
169
+ };
170
+ // Calculate manifest hash for integrity verification
171
+ const localFiles = await this.collectAllFiles();
172
+ const manifestHash = this.computeManifestHash(localFiles);
173
+ payload.manifestHash = manifestHash;
174
+ try {
175
+ const response = await fetch(`${this.serverUrl}/_archlast/deploy/delta`, {
176
+ method: "POST",
177
+ headers: {
178
+ "Content-Type": "application/json",
179
+ ...this.getAuthHeaders(),
180
+ },
181
+ body: JSON.stringify(payload),
182
+ });
183
+ if (!response.ok) {
184
+ const errorText = await response.text();
185
+ // Check if server doesn't support delta deployment, fall back to full upload
186
+ if (response.status === 404) {
187
+ return this.upload();
188
+ }
189
+ // Check for auth error
190
+ if (response.status === 401) {
191
+ return {
192
+ success: false,
193
+ message: `Authentication failed: ${errorText || "Invalid admin token"}\n\nPlease create a token in the dashboard (Settings → API Tokens) and update ARCHLAST_ADMIN_TOKEN in your .env file.`,
194
+ };
195
+ }
196
+ return { success: false, message: errorText };
197
+ }
198
+ const result = (await response.json());
199
+ return {
200
+ success: result.success,
201
+ message: result.message || "Delta deployment successful",
202
+ deploymentType: result.deploymentType || "delta",
203
+ };
204
+ }
205
+ catch (error) {
206
+ // If delta deployment fails, fall back to full upload
207
+ const fallbackResult = await this.upload();
208
+ return { ...fallbackResult, deploymentType: "full" };
209
+ }
210
+ }
211
+ /**
212
+ * Compute hash of the full manifest for integrity verification
213
+ */
214
+ computeManifestHash(files) {
215
+ const filesHashStr = files
216
+ .sort((a, b) => a.filePath.localeCompare(b.filePath))
217
+ .map((f) => `${f.filePath}:${this.hashString(f.code.trim())}`)
218
+ .join(";");
219
+ return this.hashString(filesHashStr);
220
+ }
221
+ async preparePayload() {
222
+ const files = await this.collectAllFiles();
223
+ return {
224
+ files,
225
+ timestamp: Date.now(),
226
+ };
227
+ }
228
+ async collectAllFiles() {
229
+ // Scan entire archlast directory, excluding node_modules
230
+ // Include _generated/server.ts for type definitions
231
+ const pattern = path.join(this.archlastPath, "**/*.ts").replace(/\\/g, "/");
232
+ const allFiles = await glob(pattern, {
233
+ ignore: ["**/node_modules/**", "**/_generated/api.ts", "**/_generated/index.ts"],
234
+ });
235
+ const files = [];
236
+ for (const file of allFiles) {
237
+ const content = fs.readFileSync(file, "utf-8");
238
+ const relativePath = path.relative(this.archlastPath, file).replace(/\\/g, "/");
239
+ files.push({
240
+ filePath: relativePath,
241
+ code: content,
242
+ });
243
+ }
244
+ return files;
245
+ }
246
+ hashString(str) {
247
+ let hash = 0;
248
+ for (let i = 0; i < str.length; i++) {
249
+ const char = str.charCodeAt(i);
250
+ hash = (hash << 5) - hash + char;
251
+ hash = hash & hash;
252
+ }
253
+ return Math.abs(hash).toString(36);
254
+ }
255
+ }
@@ -0,0 +1,13 @@
1
+ import { EventEmitter } from "events";
2
+ export interface WatchEvent {
3
+ type: "add" | "change" | "unlink" | "unlinkDir";
4
+ path: string;
5
+ }
6
+ export declare class FileWatcher extends EventEmitter {
7
+ private watcher;
8
+ private archlastPath;
9
+ constructor(archlastPath: string);
10
+ start(): void;
11
+ stop(): void;
12
+ }
13
+ //# sourceMappingURL=watcher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"watcher.d.ts","sourceRoot":"","sources":["../src/watcher.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAGtC,MAAM,WAAW,UAAU;IACvB,IAAI,EAAE,KAAK,GAAG,QAAQ,GAAG,QAAQ,GAAG,WAAW,CAAC;IAChD,IAAI,EAAE,MAAM,CAAC;CAChB;AAED,qBAAa,WAAY,SAAQ,YAAY;IACzC,OAAO,CAAC,OAAO,CAAmC;IAClD,OAAO,CAAC,YAAY,CAAS;gBAEjB,YAAY,EAAE,MAAM;IAOhC,KAAK,IAAI,IAAI;IAwBb,IAAI,IAAI,IAAI;CAMf"}
@@ -0,0 +1,38 @@
1
+ import * as chokidar from "chokidar";
2
+ import * as path from "path";
3
+ import { EventEmitter } from "events";
4
+ export class FileWatcher extends EventEmitter {
5
+ watcher = null;
6
+ archlastPath;
7
+ constructor(archlastPath) {
8
+ super();
9
+ this.archlastPath = path.resolve(archlastPath);
10
+ }
11
+ start() {
12
+ const pattern = path.join(this.archlastPath, "src", "**/*.ts");
13
+ this.watcher = chokidar.watch(pattern, {
14
+ ignored: ["**/node_modules/**", "**/_generated/**"],
15
+ persistent: true,
16
+ ignoreInitial: true,
17
+ });
18
+ this.watcher
19
+ .on("add", (filePath) => {
20
+ this.emit("change", { type: "add", path: filePath });
21
+ })
22
+ .on("change", (filePath) => {
23
+ this.emit("change", { type: "change", path: filePath });
24
+ })
25
+ .on("unlink", (filePath) => {
26
+ this.emit("change", { type: "unlink", path: filePath });
27
+ })
28
+ .on("unlinkDir", (dirPath) => {
29
+ this.emit("change", { type: "unlinkDir", path: dirPath });
30
+ });
31
+ }
32
+ stop() {
33
+ if (this.watcher) {
34
+ this.watcher.close();
35
+ this.watcher = null;
36
+ }
37
+ }
38
+ }
package/package.json ADDED
@@ -0,0 +1,58 @@
1
+ {
2
+ "name": "@archlast/cli",
3
+ "version": "0.0.1",
4
+ "description": "Archlast CLI for development and deployment",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "bin": {
8
+ "archlast": "./dist/cli.js"
9
+ },
10
+ "files": [
11
+ "dist",
12
+ "scripts/postinstall.cjs",
13
+ "README.md",
14
+ "LICENSE"
15
+ ],
16
+ "engines": {
17
+ "node": ">=18.0.0"
18
+ },
19
+ "scripts": {
20
+ "build": "bun build src/cli.ts --outdir dist --target node --format esm -e cpu-features && bun run build:types",
21
+ "build:types": "tsc --emitDeclarationOnly",
22
+ "dev": "bun --watch src/cli.ts",
23
+ "test": "bun test tests",
24
+ "format": "prettier --write \"**/*.{ts,tsx,js,jsx,json,md}\"",
25
+ "format:check": "prettier --check \"**/*.{ts,tsx,js,jsx,json,md}\"",
26
+ "prepublishOnly": "bun run build",
27
+ "postinstall": "node scripts/postinstall.cjs",
28
+ "clean": "rm -rf dist"
29
+ },
30
+ "keywords": [
31
+ "archlast",
32
+ "cli",
33
+ "reactive",
34
+ "backend"
35
+ ],
36
+ "author": "",
37
+ "license": "MIT",
38
+ "dependencies": {
39
+ "chalk": "^5.6.2",
40
+ "chokidar": "^3.5.3",
41
+ "commander": "^11.1.0",
42
+ "diff": "^8.0.2",
43
+ "dockerode": "^4.0.9",
44
+ "glob": "^10.3.10",
45
+ "inquirer": "^13.1.0",
46
+ "ora": "^5.4.1",
47
+ "ts-morph": "^21.0.1",
48
+ "typescript": "^5.3.3",
49
+ "yaml": "^2.3.4"
50
+ },
51
+ "devDependencies": {
52
+ "@types/dockerode": "^3.3.25",
53
+ "@types/node": "^20.10.5",
54
+ "bun-types": "latest",
55
+ "memfs": "^4.51.1",
56
+ "vitest": "^4.0.16"
57
+ }
58
+ }