@orchestrator-claude/cli 3.1.12 → 3.2.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 (99) hide show
  1. package/dist/api-client/OrchestratorAPIClient.d.ts +50 -1
  2. package/dist/api-client/OrchestratorAPIClient.d.ts.map +1 -1
  3. package/dist/api-client/OrchestratorAPIClient.js +73 -1
  4. package/dist/api-client/OrchestratorAPIClient.js.map +1 -1
  5. package/dist/api-client/types.d.ts +95 -2
  6. package/dist/api-client/types.d.ts.map +1 -1
  7. package/dist/commands/InitCommand.d.ts +22 -3
  8. package/dist/commands/InitCommand.d.ts.map +1 -1
  9. package/dist/commands/InitCommand.js +147 -13
  10. package/dist/commands/InitCommand.js.map +1 -1
  11. package/dist/index.d.ts +1 -1
  12. package/dist/index.d.ts.map +1 -1
  13. package/dist/index.js +1 -1
  14. package/dist/index.js.map +1 -1
  15. package/dist/services/BootstrapService.d.ts +100 -0
  16. package/dist/services/BootstrapService.d.ts.map +1 -0
  17. package/dist/services/BootstrapService.js +231 -0
  18. package/dist/services/BootstrapService.js.map +1 -0
  19. package/dist/services/CleanupManager.d.ts +49 -0
  20. package/dist/services/CleanupManager.d.ts.map +1 -0
  21. package/dist/services/CleanupManager.js +87 -0
  22. package/dist/services/CleanupManager.js.map +1 -0
  23. package/dist/services/CredentialGenerator.d.ts +11 -0
  24. package/dist/services/CredentialGenerator.d.ts.map +1 -0
  25. package/dist/services/CredentialGenerator.js +25 -0
  26. package/dist/services/CredentialGenerator.js.map +1 -0
  27. package/dist/services/VerificationService.d.ts +103 -0
  28. package/dist/services/VerificationService.d.ts.map +1 -0
  29. package/dist/services/VerificationService.js +350 -0
  30. package/dist/services/VerificationService.js.map +1 -0
  31. package/dist/templates/base/claude/skills/release/SKILL.md +236 -0
  32. package/dist/templates/base/claude/skills/release/release.sh +460 -0
  33. package/dist/templates/base/docker-compose.yml.hbs +189 -8
  34. package/dist/templates/base/mcp.json.hbs +16 -9
  35. package/dist/templates/default/docker-compose.yml +190 -10
  36. package/package.json +1 -1
  37. package/templates/base/claude/skills/release/SKILL.md +236 -0
  38. package/templates/base/claude/skills/release/release.sh +460 -0
  39. package/templates/base/docker-compose.yml.hbs +189 -8
  40. package/templates/base/mcp.json.hbs +16 -9
  41. package/templates/default/docker-compose.yml +190 -10
  42. package/dist/templates/base/orchestrator-index.json.hbs +0 -37
  43. package/dist/templates/projects/api/files/index.ts.hbs +0 -30
  44. package/dist/templates/projects/api/files/server.ts.hbs +0 -63
  45. package/dist/templates/projects/api/files/tsconfig.json.hbs +0 -27
  46. package/dist/templates/projects/api/files/vitest.config.ts.hbs +0 -28
  47. package/dist/templates/projects/api/template.config.json +0 -238
  48. package/dist/templates/projects/api/template.config.ts +0 -149
  49. package/dist/templates/projects/cli/files/cli.ts.hbs +0 -50
  50. package/dist/templates/projects/cli/files/index.ts.hbs +0 -8
  51. package/dist/templates/projects/cli/files/tsconfig.json.hbs +0 -27
  52. package/dist/templates/projects/cli/files/vitest.config.ts.hbs +0 -28
  53. package/dist/templates/projects/cli/template.config.json +0 -213
  54. package/dist/templates/projects/cli/template.config.ts +0 -126
  55. package/dist/templates/projects/frontend/files/App.tsx.hbs +0 -31
  56. package/dist/templates/projects/frontend/files/index.html.hbs +0 -13
  57. package/dist/templates/projects/frontend/files/main.tsx.hbs +0 -22
  58. package/dist/templates/projects/frontend/files/tsconfig.json.hbs +0 -34
  59. package/dist/templates/projects/frontend/files/tsconfig.node.json.hbs +0 -10
  60. package/dist/templates/projects/frontend/files/vite.config.ts.hbs +0 -19
  61. package/dist/templates/projects/frontend/files/vitest.config.ts.hbs +0 -36
  62. package/dist/templates/projects/frontend/template.config.json +0 -241
  63. package/dist/templates/projects/frontend/template.config.ts +0 -153
  64. package/dist/templates/projects/minimal/files/claude-settings.json.hbs +0 -20
  65. package/dist/templates/projects/minimal/files/env.example.hbs +0 -17
  66. package/dist/templates/projects/minimal/files/gitignore.hbs +0 -41
  67. package/dist/templates/projects/minimal/files/index.ts.hbs +0 -13
  68. package/dist/templates/projects/minimal/files/tsconfig.json.hbs +0 -27
  69. package/dist/templates/projects/minimal/template.config.json +0 -185
  70. package/dist/templates/projects/minimal/template.config.ts +0 -88
  71. package/templates/base/orchestrator-index.json.hbs +0 -37
  72. package/templates/projects/api/files/index.ts.hbs +0 -30
  73. package/templates/projects/api/files/server.ts.hbs +0 -63
  74. package/templates/projects/api/files/tsconfig.json.hbs +0 -27
  75. package/templates/projects/api/files/vitest.config.ts.hbs +0 -28
  76. package/templates/projects/api/template.config.json +0 -238
  77. package/templates/projects/api/template.config.ts +0 -149
  78. package/templates/projects/cli/files/cli.ts.hbs +0 -50
  79. package/templates/projects/cli/files/index.ts.hbs +0 -8
  80. package/templates/projects/cli/files/tsconfig.json.hbs +0 -27
  81. package/templates/projects/cli/files/vitest.config.ts.hbs +0 -28
  82. package/templates/projects/cli/template.config.json +0 -213
  83. package/templates/projects/cli/template.config.ts +0 -126
  84. package/templates/projects/frontend/files/App.tsx.hbs +0 -31
  85. package/templates/projects/frontend/files/index.html.hbs +0 -13
  86. package/templates/projects/frontend/files/main.tsx.hbs +0 -22
  87. package/templates/projects/frontend/files/tsconfig.json.hbs +0 -34
  88. package/templates/projects/frontend/files/tsconfig.node.json.hbs +0 -10
  89. package/templates/projects/frontend/files/vite.config.ts.hbs +0 -19
  90. package/templates/projects/frontend/files/vitest.config.ts.hbs +0 -36
  91. package/templates/projects/frontend/template.config.json +0 -241
  92. package/templates/projects/frontend/template.config.ts +0 -153
  93. package/templates/projects/minimal/files/claude-settings.json.hbs +0 -20
  94. package/templates/projects/minimal/files/env.example.hbs +0 -17
  95. package/templates/projects/minimal/files/gitignore.hbs +0 -41
  96. package/templates/projects/minimal/files/index.ts.hbs +0 -13
  97. package/templates/projects/minimal/files/tsconfig.json.hbs +0 -27
  98. package/templates/projects/minimal/template.config.json +0 -185
  99. package/templates/projects/minimal/template.config.ts +0 -88
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CleanupManager.js","sourceRoot":"","sources":["../../src/services/CleanupManager.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAC7C,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAS9C,MAAM,OAAO,cAAc;IACR,KAAK,GAAG,IAAI,GAAG,EAAgC,CAAC;IACzD,WAAW,GAAG,KAAK,CAAC;IACpB,uBAAuB,GAAG,KAAK,CAAC;IAExC;;;;;OAKG;IACH,IAAI,CAAC,KAAmB,EAAE,UAA0B,EAAE;QACpD,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;IACjC,CAAC;IAED;;;;;;OAMG;IACH,KAAK,CAAC,OAAO;QACX,IAAI,IAAI,CAAC,WAAW;YAAE,OAAO;QAC7B,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;QAExB,iFAAiF;QACjF,IAAI,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,eAAe,CAAC,EAAE,CAAC;YACpC,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,eAAe,CAAE,CAAC;YAC7C,IAAI,CAAC;gBACH,QAAQ,CAAC,qBAAqB,EAAE,EAAE,GAAG,EAAE,GAAG,CAAC,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC;YACpE,CAAC;YAAC,MAAM,CAAC;gBACP,2CAA2C;gBAC3C,IAAI,CAAC;oBACH,QAAQ,CAAC,qBAAqB,EAAE,EAAE,GAAG,EAAE,GAAG,CAAC,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC;gBACpE,CAAC;gBAAC,MAAM,CAAC;oBACP,4DAA4D;gBAC9D,CAAC;YACH,CAAC;QACH,CAAC;QAED,kCAAkC;QAClC,IAAI,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,kBAAkB,CAAC,EAAE,CAAC;YACvC,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,kBAAkB,CAAE,CAAC;YAChD,IAAI,GAAG,CAAC,IAAI,IAAI,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;gBACrC,IAAI,CAAC;oBACH,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;gBACrD,CAAC;gBAAC,MAAM,CAAC;oBACP,qCAAqC;gBACvC,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAED;;;;;;OAMG;IACH,qBAAqB;QACnB,IAAI,IAAI,CAAC,uBAAuB;YAAE,OAAO;QACzC,IAAI,CAAC,uBAAuB,GAAG,IAAI,CAAC;QACpC,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,KAAK,IAAI,EAAE;YAC9B,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC;YACrB,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,oCAAoC;QACzD,CAAC,CAAC,CAAC;IACL,CAAC;CACF"}
@@ -0,0 +1,11 @@
1
+ import type { GeneratedCredentials } from '../api-client/types.js';
2
+ export declare class CredentialGenerator {
3
+ /**
4
+ * Generate secure credentials for a project.
5
+ *
6
+ * @param projectName - Project name used for email local domain
7
+ * @returns Generated credentials (email, password, jwtSecret)
8
+ */
9
+ generate(projectName: string): GeneratedCredentials;
10
+ }
11
+ //# sourceMappingURL=CredentialGenerator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CredentialGenerator.d.ts","sourceRoot":"","sources":["../../src/services/CredentialGenerator.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAEnE,qBAAa,mBAAmB;IAC9B;;;;;OAKG;IACH,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,oBAAoB;CAOpD"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Credential Generator
3
+ *
4
+ * Generates cryptographically secure credentials for project bootstrap.
5
+ * Pure class with no side effects — uses only Node.js built-in crypto.
6
+ *
7
+ * @module services/CredentialGenerator
8
+ */
9
+ import { randomBytes } from 'node:crypto';
10
+ export class CredentialGenerator {
11
+ /**
12
+ * Generate secure credentials for a project.
13
+ *
14
+ * @param projectName - Project name used for email local domain
15
+ * @returns Generated credentials (email, password, jwtSecret)
16
+ */
17
+ generate(projectName) {
18
+ return {
19
+ email: `admin@${projectName}.local`,
20
+ password: randomBytes(24).toString('base64url'),
21
+ jwtSecret: randomBytes(32).toString('hex'),
22
+ };
23
+ }
24
+ }
25
+ //# sourceMappingURL=CredentialGenerator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CredentialGenerator.js","sourceRoot":"","sources":["../../src/services/CredentialGenerator.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAG1C,MAAM,OAAO,mBAAmB;IAC9B;;;;;OAKG;IACH,QAAQ,CAAC,WAAmB;QAC1B,OAAO;YACL,KAAK,EAAE,SAAS,WAAW,QAAQ;YACnC,QAAQ,EAAE,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC;YAC/C,SAAS,EAAE,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC;SAC3C,CAAC;IACJ,CAAC;CACF"}
@@ -0,0 +1,103 @@
1
+ import type { VerificationCheck, VerificationResult } from '../api-client/types.js';
2
+ import type { OrchestratorAPIClient } from '../api-client/OrchestratorAPIClient.js';
3
+ /**
4
+ * Options for the top-level verify() method.
5
+ */
6
+ export interface VerifyOptions {
7
+ /** Absolute path to the project root (where .env and docker-compose.yml live). */
8
+ projectPath: string;
9
+ /** Admin email to use for the authentication check. */
10
+ email?: string;
11
+ /** Admin password to use for the authentication check. */
12
+ password?: string;
13
+ /** Project ID to verify registration (from bootstrap result). */
14
+ projectId?: string;
15
+ }
16
+ /**
17
+ * Runs structured post-init verification checks against the orchestrator stack.
18
+ *
19
+ * Each individual check method is public so they can be called in isolation
20
+ * during testing or from custom tooling.
21
+ */
22
+ export declare class VerificationService {
23
+ private readonly apiClient;
24
+ constructor(apiClient: OrchestratorAPIClient);
25
+ /**
26
+ * Verify that the Orchestrator API is reachable and reports a healthy status.
27
+ *
28
+ * @returns VerificationCheck with passed:true when API returns status === 'ok'
29
+ */
30
+ checkApiHealth(): Promise<VerificationCheck>;
31
+ /**
32
+ * Verify that the provided credentials can authenticate successfully.
33
+ *
34
+ * Returns passed:false with a descriptive message when no credentials are given,
35
+ * so callers always receive a structured result rather than a thrown error.
36
+ *
37
+ * @param email - Admin email address
38
+ * @param password - Admin password
39
+ * @returns VerificationCheck with passed:true when login succeeds
40
+ */
41
+ checkAuth(email?: string, password?: string): Promise<VerificationCheck>;
42
+ /**
43
+ * Verify that the .env file at the given project path contains all required
44
+ * critical environment variables with non-placeholder values.
45
+ *
46
+ * Parsing is done manually so no dotenv package is required at runtime.
47
+ *
48
+ * @param projectPath - Absolute path to the project root
49
+ * @returns VerificationCheck with passed:true when all CRITICAL_ENV_VARS are set
50
+ */
51
+ checkEnvCompleteness(projectPath: string): Promise<VerificationCheck>;
52
+ /**
53
+ * Verify that Docker Compose services are running in the given project directory.
54
+ *
55
+ * Runs `docker compose ps` synchronously and counts lines that contain
56
+ * 'running' or 'up' (case-insensitive) to determine service health.
57
+ *
58
+ * @param projectPath - Absolute path to the directory containing docker-compose.yml
59
+ * @returns VerificationCheck with passed:true when at least one service is running
60
+ */
61
+ checkDockerServices(projectPath: string): Promise<VerificationCheck>;
62
+ /**
63
+ * Verify that the registered project exists in the Orchestrator API.
64
+ *
65
+ * Returns passed:false gracefully when no projectId is provided or
66
+ * when the API returns 404.
67
+ *
68
+ * @param projectId - The project ID to look up (optional)
69
+ * @returns VerificationCheck with passed:true when project is found
70
+ */
71
+ checkProjectExists(projectId?: string): Promise<VerificationCheck>;
72
+ /**
73
+ * Verify that the MCP tool chain is reachable by exercising the
74
+ * detectWorkflow endpoint (a lightweight MCP-backed operation).
75
+ *
76
+ * @returns VerificationCheck with passed:true when detectWorkflow succeeds
77
+ */
78
+ checkMcpConnectivity(): Promise<VerificationCheck>;
79
+ /**
80
+ * Run all verification checks in parallel and aggregate the results.
81
+ *
82
+ * All four checks (API health, auth, env completeness, Docker services)
83
+ * are executed concurrently via Promise.all to minimise total wall-clock time.
84
+ *
85
+ * @param options - Verify options (projectPath, email, password)
86
+ * @returns Aggregated VerificationResult with allPassed flag and duration
87
+ */
88
+ verify(options: VerifyOptions): Promise<VerificationResult>;
89
+ /**
90
+ * Parse a .env file string into a key-value Map.
91
+ *
92
+ * Rules:
93
+ * - Lines starting with '#' are comments and are ignored
94
+ * - Empty lines are ignored
95
+ * - Lines without '=' are ignored
96
+ * - Values are trimmed of surrounding whitespace
97
+ *
98
+ * @param content - Raw .env file content
99
+ * @returns Map from variable name to value
100
+ */
101
+ private parseEnvFile;
102
+ }
103
+ //# sourceMappingURL=VerificationService.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"VerificationService.d.ts","sourceRoot":"","sources":["../../src/services/VerificationService.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AACpF,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,wCAAwC,CAAC;AA2BpF;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,kFAAkF;IAClF,WAAW,EAAE,MAAM,CAAC;IACpB,uDAAuD;IACvD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,0DAA0D;IAC1D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,iEAAiE;IACjE,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAMD;;;;;GAKG;AACH,qBAAa,mBAAmB;IAClB,OAAO,CAAC,QAAQ,CAAC,SAAS;gBAAT,SAAS,EAAE,qBAAqB;IAM7D;;;;OAIG;IACG,cAAc,IAAI,OAAO,CAAC,iBAAiB,CAAC;IAyBlD;;;;;;;;;OASG;IACG,SAAS,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC;IA8B9E;;;;;;;;OAQG;IACG,oBAAoB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAyD3E;;;;;;;;OAQG;IACG,mBAAmB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAyC1E;;;;;;;;OAQG;IACG,kBAAkB,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAiCxE;;;;;OAKG;IACG,oBAAoB,IAAI,OAAO,CAAC,iBAAiB,CAAC;IAyBxD;;;;;;;;OAQG;IACG,MAAM,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAuBjE;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,YAAY;CAiBrB"}
@@ -0,0 +1,350 @@
1
+ /**
2
+ * Verification Service
3
+ *
4
+ * Runs post-init verification checks to confirm the orchestrator stack
5
+ * is fully operational: API health, authentication, environment completeness,
6
+ * and Docker service status.
7
+ *
8
+ * Design principles:
9
+ * - All check methods catch errors internally and return passed:false (never throw)
10
+ * - Checks are run in parallel via Promise.all for speed
11
+ * - No dotenv dependency: .env is parsed manually
12
+ *
13
+ * @module services/VerificationService
14
+ */
15
+ import { readFileSync, existsSync } from 'node:fs';
16
+ import { execSync } from 'node:child_process';
17
+ import { join } from 'node:path';
18
+ // ---------------------------------------------------------------------------
19
+ // Constants
20
+ // ---------------------------------------------------------------------------
21
+ /**
22
+ * Environment variables that must be present and non-empty in .env
23
+ * for the project to be considered fully configured.
24
+ */
25
+ const CRITICAL_ENV_VARS = [
26
+ 'ORCHESTRATOR_API_URL',
27
+ 'ORCHESTRATOR_ADMIN_EMAIL',
28
+ 'ORCHESTRATOR_ADMIN_PASSWORD',
29
+ 'ORCHESTRATOR_PROJECT_ID',
30
+ 'JWT_SECRET',
31
+ ];
32
+ /**
33
+ * Regex to detect unresolved template placeholder tokens, e.g. {{SOME_VAR}}.
34
+ */
35
+ const PLACEHOLDER_REGEX = /\{\{[^}]+\}\}/;
36
+ // ---------------------------------------------------------------------------
37
+ // VerificationService
38
+ // ---------------------------------------------------------------------------
39
+ /**
40
+ * Runs structured post-init verification checks against the orchestrator stack.
41
+ *
42
+ * Each individual check method is public so they can be called in isolation
43
+ * during testing or from custom tooling.
44
+ */
45
+ export class VerificationService {
46
+ apiClient;
47
+ constructor(apiClient) {
48
+ this.apiClient = apiClient;
49
+ }
50
+ // -------------------------------------------------------------------------
51
+ // checkApiHealth
52
+ // -------------------------------------------------------------------------
53
+ /**
54
+ * Verify that the Orchestrator API is reachable and reports a healthy status.
55
+ *
56
+ * @returns VerificationCheck with passed:true when API returns status === 'ok'
57
+ */
58
+ async checkApiHealth() {
59
+ try {
60
+ const response = await this.apiClient.healthCheck();
61
+ const passed = response?.status === 'ok';
62
+ return {
63
+ name: 'API Health',
64
+ passed,
65
+ message: passed
66
+ ? 'API is healthy'
67
+ : `API returned status: ${response?.status}`,
68
+ };
69
+ }
70
+ catch (error) {
71
+ const msg = error instanceof Error ? error.message : String(error);
72
+ return {
73
+ name: 'API Health',
74
+ passed: false,
75
+ message: `API health check failed: ${msg}`,
76
+ };
77
+ }
78
+ }
79
+ // -------------------------------------------------------------------------
80
+ // checkAuth
81
+ // -------------------------------------------------------------------------
82
+ /**
83
+ * Verify that the provided credentials can authenticate successfully.
84
+ *
85
+ * Returns passed:false with a descriptive message when no credentials are given,
86
+ * so callers always receive a structured result rather than a thrown error.
87
+ *
88
+ * @param email - Admin email address
89
+ * @param password - Admin password
90
+ * @returns VerificationCheck with passed:true when login succeeds
91
+ */
92
+ async checkAuth(email, password) {
93
+ if (!email || !password) {
94
+ return {
95
+ name: 'Authentication',
96
+ passed: false,
97
+ message: 'No credentials provided for authentication check',
98
+ };
99
+ }
100
+ try {
101
+ await this.apiClient.login({ email, password });
102
+ return {
103
+ name: 'Authentication',
104
+ passed: true,
105
+ message: 'Authentication successful',
106
+ };
107
+ }
108
+ catch (error) {
109
+ const msg = error instanceof Error ? error.message : String(error);
110
+ return {
111
+ name: 'Authentication',
112
+ passed: false,
113
+ message: `Authentication failed: ${msg}`,
114
+ };
115
+ }
116
+ }
117
+ // -------------------------------------------------------------------------
118
+ // checkEnvCompleteness
119
+ // -------------------------------------------------------------------------
120
+ /**
121
+ * Verify that the .env file at the given project path contains all required
122
+ * critical environment variables with non-placeholder values.
123
+ *
124
+ * Parsing is done manually so no dotenv package is required at runtime.
125
+ *
126
+ * @param projectPath - Absolute path to the project root
127
+ * @returns VerificationCheck with passed:true when all CRITICAL_ENV_VARS are set
128
+ */
129
+ async checkEnvCompleteness(projectPath) {
130
+ const envPath = join(projectPath, '.env');
131
+ if (!existsSync(envPath)) {
132
+ return {
133
+ name: 'Environment Completeness',
134
+ passed: false,
135
+ message: `.env file not found at ${envPath}`,
136
+ };
137
+ }
138
+ try {
139
+ const content = readFileSync(envPath, 'utf-8');
140
+ const envVars = this.parseEnvFile(content);
141
+ // Check for missing or empty CRITICAL vars
142
+ const missing = CRITICAL_ENV_VARS.filter(v => !envVars.has(v) || !envVars.get(v));
143
+ if (missing.length > 0) {
144
+ return {
145
+ name: 'Environment Completeness',
146
+ passed: false,
147
+ message: `Required environment variables missing: ${missing.join(', ')}`,
148
+ };
149
+ }
150
+ // Check for unresolved placeholder tokens
151
+ const withPlaceholders = CRITICAL_ENV_VARS.filter(v => {
152
+ const val = envVars.get(v) ?? '';
153
+ return PLACEHOLDER_REGEX.test(val);
154
+ });
155
+ if (withPlaceholders.length > 0) {
156
+ return {
157
+ name: 'Environment Completeness',
158
+ passed: false,
159
+ message: `Environment variables contain unresolved placeholder tokens: ${withPlaceholders.join(', ')}`,
160
+ };
161
+ }
162
+ return {
163
+ name: 'Environment Completeness',
164
+ passed: true,
165
+ message: 'All required environment variables are set',
166
+ };
167
+ }
168
+ catch (error) {
169
+ const msg = error instanceof Error ? error.message : String(error);
170
+ return {
171
+ name: 'Environment Completeness',
172
+ passed: false,
173
+ message: `Failed to read .env: ${msg}`,
174
+ };
175
+ }
176
+ }
177
+ // -------------------------------------------------------------------------
178
+ // checkDockerServices
179
+ // -------------------------------------------------------------------------
180
+ /**
181
+ * Verify that Docker Compose services are running in the given project directory.
182
+ *
183
+ * Runs `docker compose ps` synchronously and counts lines that contain
184
+ * 'running' or 'up' (case-insensitive) to determine service health.
185
+ *
186
+ * @param projectPath - Absolute path to the directory containing docker-compose.yml
187
+ * @returns VerificationCheck with passed:true when at least one service is running
188
+ */
189
+ async checkDockerServices(projectPath) {
190
+ try {
191
+ const output = execSync('docker compose ps', {
192
+ cwd: projectPath,
193
+ stdio: 'pipe',
194
+ }).toString();
195
+ const lines = output.split('\n').filter(l => l.trim());
196
+ // First line is the header row — skip it
197
+ const serviceLines = lines.slice(1);
198
+ const runningServices = serviceLines.filter(l => l.toLowerCase().includes('running') || l.toLowerCase().includes('up'));
199
+ if (runningServices.length === 0) {
200
+ return {
201
+ name: 'Docker Services',
202
+ passed: false,
203
+ message: 'No running Docker services found',
204
+ };
205
+ }
206
+ return {
207
+ name: 'Docker Services',
208
+ passed: true,
209
+ message: `${runningServices.length} Docker service(s) running`,
210
+ };
211
+ }
212
+ catch (error) {
213
+ const msg = error instanceof Error ? error.message : String(error);
214
+ return {
215
+ name: 'Docker Services',
216
+ passed: false,
217
+ message: `Docker check failed: ${msg}`,
218
+ };
219
+ }
220
+ }
221
+ // -------------------------------------------------------------------------
222
+ // checkProjectExists
223
+ // -------------------------------------------------------------------------
224
+ /**
225
+ * Verify that the registered project exists in the Orchestrator API.
226
+ *
227
+ * Returns passed:false gracefully when no projectId is provided or
228
+ * when the API returns 404.
229
+ *
230
+ * @param projectId - The project ID to look up (optional)
231
+ * @returns VerificationCheck with passed:true when project is found
232
+ */
233
+ async checkProjectExists(projectId) {
234
+ if (!projectId) {
235
+ return {
236
+ name: 'Project Registration',
237
+ passed: false,
238
+ message: 'No projectId provided for project existence check',
239
+ };
240
+ }
241
+ try {
242
+ const project = await this.apiClient.getProject(projectId);
243
+ const passed = !!project?.id;
244
+ return {
245
+ name: 'Project Registration',
246
+ passed,
247
+ message: passed
248
+ ? `Project "${project.name}" is registered`
249
+ : 'Project not found in API',
250
+ };
251
+ }
252
+ catch (error) {
253
+ const msg = error instanceof Error ? error.message : String(error);
254
+ return {
255
+ name: 'Project Registration',
256
+ passed: false,
257
+ message: `Project existence check failed: ${msg}`,
258
+ };
259
+ }
260
+ }
261
+ // -------------------------------------------------------------------------
262
+ // checkMcpConnectivity
263
+ // -------------------------------------------------------------------------
264
+ /**
265
+ * Verify that the MCP tool chain is reachable by exercising the
266
+ * detectWorkflow endpoint (a lightweight MCP-backed operation).
267
+ *
268
+ * @returns VerificationCheck with passed:true when detectWorkflow succeeds
269
+ */
270
+ async checkMcpConnectivity() {
271
+ try {
272
+ const result = await this.apiClient.detectWorkflow('test connectivity');
273
+ const passed = !!result?.workflowType;
274
+ return {
275
+ name: 'MCP Connectivity',
276
+ passed,
277
+ message: passed
278
+ ? 'MCP tool chain is reachable'
279
+ : 'MCP detectWorkflow returned empty response',
280
+ };
281
+ }
282
+ catch (error) {
283
+ const msg = error instanceof Error ? error.message : String(error);
284
+ return {
285
+ name: 'MCP Connectivity',
286
+ passed: false,
287
+ message: `MCP connectivity check failed: ${msg}`,
288
+ };
289
+ }
290
+ }
291
+ // -------------------------------------------------------------------------
292
+ // verify
293
+ // -------------------------------------------------------------------------
294
+ /**
295
+ * Run all verification checks in parallel and aggregate the results.
296
+ *
297
+ * All four checks (API health, auth, env completeness, Docker services)
298
+ * are executed concurrently via Promise.all to minimise total wall-clock time.
299
+ *
300
+ * @param options - Verify options (projectPath, email, password)
301
+ * @returns Aggregated VerificationResult with allPassed flag and duration
302
+ */
303
+ async verify(options) {
304
+ const start = Date.now();
305
+ const checks = await Promise.all([
306
+ this.checkApiHealth(),
307
+ this.checkAuth(options.email, options.password),
308
+ this.checkEnvCompleteness(options.projectPath),
309
+ this.checkDockerServices(options.projectPath),
310
+ this.checkProjectExists(options.projectId),
311
+ this.checkMcpConnectivity(),
312
+ ]);
313
+ return {
314
+ allPassed: checks.every(c => c.passed),
315
+ checks,
316
+ duration: Date.now() - start,
317
+ };
318
+ }
319
+ // -------------------------------------------------------------------------
320
+ // Private helpers
321
+ // -------------------------------------------------------------------------
322
+ /**
323
+ * Parse a .env file string into a key-value Map.
324
+ *
325
+ * Rules:
326
+ * - Lines starting with '#' are comments and are ignored
327
+ * - Empty lines are ignored
328
+ * - Lines without '=' are ignored
329
+ * - Values are trimmed of surrounding whitespace
330
+ *
331
+ * @param content - Raw .env file content
332
+ * @returns Map from variable name to value
333
+ */
334
+ parseEnvFile(content) {
335
+ const envVars = new Map();
336
+ for (const line of content.split('\n')) {
337
+ const trimmed = line.trim();
338
+ if (!trimmed || trimmed.startsWith('#'))
339
+ continue;
340
+ const eqIndex = trimmed.indexOf('=');
341
+ if (eqIndex === -1)
342
+ continue;
343
+ const key = trimmed.substring(0, eqIndex).trim();
344
+ const value = trimmed.substring(eqIndex + 1).trim();
345
+ envVars.set(key, value);
346
+ }
347
+ return envVars;
348
+ }
349
+ }
350
+ //# sourceMappingURL=VerificationService.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"VerificationService.js","sourceRoot":"","sources":["../../src/services/VerificationService.ts"],"names":[],"mappings":"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"}