@porchestra/cli 1.0.0 → 1.0.2

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 (39) hide show
  1. package/bin/porchestra.js +1 -1
  2. package/dist/index.d.ts +1 -0
  3. package/dist/index.js +1666 -0
  4. package/dist/index.js.map +1 -0
  5. package/package.json +11 -1
  6. package/src/agents/testPrompt/ast.json +0 -71
  7. package/src/agents/testPrompt/config.ts +0 -18
  8. package/src/agents/testPrompt/index.ts +0 -64
  9. package/src/agents/testPrompt/schemas.ts +0 -45
  10. package/src/agents/testPrompt/tools.ts +0 -88
  11. package/src/commands/agents.ts +0 -173
  12. package/src/commands/config.ts +0 -97
  13. package/src/commands/explore.ts +0 -160
  14. package/src/commands/login.ts +0 -101
  15. package/src/commands/logout.ts +0 -52
  16. package/src/commands/pull.ts +0 -220
  17. package/src/commands/status.ts +0 -78
  18. package/src/commands/whoami.ts +0 -56
  19. package/src/core/api/client.ts +0 -133
  20. package/src/core/auth/auth-service.ts +0 -176
  21. package/src/core/auth/token-manager.ts +0 -47
  22. package/src/core/config/config-manager.ts +0 -107
  23. package/src/core/config/config-schema.ts +0 -56
  24. package/src/core/config/project-tracker.ts +0 -158
  25. package/src/core/generators/code-generator.ts +0 -329
  26. package/src/core/generators/schema-generator.ts +0 -59
  27. package/src/index.ts +0 -85
  28. package/src/types/index.ts +0 -214
  29. package/src/utils/date.ts +0 -23
  30. package/src/utils/errors.ts +0 -38
  31. package/src/utils/logger.ts +0 -11
  32. package/src/utils/path-utils.ts +0 -47
  33. package/tests/unit/config-manager.test.ts +0 -74
  34. package/tests/unit/config-schema.test.ts +0 -61
  35. package/tests/unit/path-utils.test.ts +0 -53
  36. package/tests/unit/schema-generator.test.ts +0 -82
  37. package/tsconfig.json +0 -30
  38. package/tsup.config.ts +0 -19
  39. package/vitest.config.ts +0 -20
package/dist/index.js ADDED
@@ -0,0 +1,1666 @@
1
+ #!/usr/bin/env node
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __esm = (fn, res) => function __init() {
5
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
6
+ };
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+
12
+ // src/core/config/project-tracker.ts
13
+ var ProjectTracker;
14
+ var init_project_tracker = __esm({
15
+ "src/core/config/project-tracker.ts"() {
16
+ "use strict";
17
+ ProjectTracker = class {
18
+ constructor(configManager) {
19
+ this.configManager = configManager;
20
+ }
21
+ async getTrackedProjects() {
22
+ const config = await this.configManager.get();
23
+ return config.trackedProjects || [];
24
+ }
25
+ async getTrackedAgents(projectId) {
26
+ const config = await this.configManager.get();
27
+ const project = config.trackedProjects?.find((p) => p.projectId === projectId);
28
+ return project?.agents || [];
29
+ }
30
+ async listTrackedAgents() {
31
+ const projects = await this.getTrackedProjects();
32
+ return projects.flatMap(
33
+ (project) => project.agents.map((agent) => ({ project, agent }))
34
+ );
35
+ }
36
+ async trackProject(project, agents) {
37
+ await this.configManager.update((config) => {
38
+ const existingIndex = config.trackedProjects.findIndex(
39
+ (p) => p.projectId === project.id
40
+ );
41
+ const trackedAgents = agents.map((agent) => ({
42
+ agentId: agent.id,
43
+ agentName: agent.name,
44
+ agentSlug: agent.slug,
45
+ folderPath: agent.folderPath,
46
+ selectedAt: (/* @__PURE__ */ new Date()).toISOString()
47
+ }));
48
+ const trackedProject = {
49
+ projectId: project.id,
50
+ projectName: project.name,
51
+ projectSlug: project.slug,
52
+ selectedAt: (/* @__PURE__ */ new Date()).toISOString(),
53
+ agents: trackedAgents
54
+ };
55
+ const newProjects = [...config.trackedProjects];
56
+ if (existingIndex >= 0) {
57
+ newProjects[existingIndex] = trackedProject;
58
+ } else {
59
+ newProjects.push(trackedProject);
60
+ }
61
+ return { ...config, trackedProjects: newProjects };
62
+ });
63
+ }
64
+ async untrackProject(projectId) {
65
+ await this.configManager.update((config) => ({
66
+ ...config,
67
+ trackedProjects: config.trackedProjects.filter(
68
+ (p) => p.projectId !== projectId
69
+ )
70
+ }));
71
+ }
72
+ async untrackAgent(projectId, agentId) {
73
+ let removed = false;
74
+ await this.configManager.update((config) => {
75
+ const projectIndex = config.trackedProjects.findIndex(
76
+ (p) => p.projectId === projectId
77
+ );
78
+ if (projectIndex === -1) return config;
79
+ const project = config.trackedProjects[projectIndex];
80
+ const remainingAgents = project.agents.filter(
81
+ (agent) => agent.agentId !== agentId
82
+ );
83
+ if (remainingAgents.length === project.agents.length) {
84
+ return config;
85
+ }
86
+ removed = true;
87
+ const newProjects = [...config.trackedProjects];
88
+ if (remainingAgents.length === 0) {
89
+ newProjects.splice(projectIndex, 1);
90
+ } else {
91
+ newProjects[projectIndex] = { ...project, agents: remainingAgents };
92
+ }
93
+ return { ...config, trackedProjects: newProjects };
94
+ });
95
+ return removed;
96
+ }
97
+ async updateLastPulled(projectId, agentId, version) {
98
+ await this.configManager.update((config) => {
99
+ const projectIndex = config.trackedProjects.findIndex(
100
+ (p) => p.projectId === projectId
101
+ );
102
+ if (projectIndex === -1) return config;
103
+ const project = config.trackedProjects[projectIndex];
104
+ const agentIndex = project.agents.findIndex(
105
+ (a) => a.agentId === agentId
106
+ );
107
+ if (agentIndex === -1) return config;
108
+ const now = (/* @__PURE__ */ new Date()).toISOString();
109
+ const newAgents = [...project.agents];
110
+ newAgents[agentIndex] = {
111
+ ...newAgents[agentIndex],
112
+ lastPulledAt: now,
113
+ lastPulledVersion: version
114
+ };
115
+ const newProjects = [...config.trackedProjects];
116
+ newProjects[projectIndex] = {
117
+ ...project,
118
+ agents: newAgents,
119
+ lastPulledAt: now
120
+ };
121
+ return { ...config, trackedProjects: newProjects };
122
+ });
123
+ }
124
+ async getSummary() {
125
+ const projects = await this.getTrackedProjects();
126
+ return {
127
+ projectCount: projects.length,
128
+ agentCount: projects.reduce((sum, p) => sum + p.agents.length, 0)
129
+ };
130
+ }
131
+ };
132
+ }
133
+ });
134
+
135
+ // src/utils/date.ts
136
+ function formatDistanceToNow(date) {
137
+ const now = /* @__PURE__ */ new Date();
138
+ const diffMs = now.getTime() - date.getTime();
139
+ const diffSecs = Math.floor(diffMs / 1e3);
140
+ const diffMins = Math.floor(diffSecs / 60);
141
+ const diffHours = Math.floor(diffMins / 60);
142
+ const diffDays = Math.floor(diffHours / 24);
143
+ if (diffSecs < 60) return "just now";
144
+ if (diffMins < 60) return `${diffMins}m ago`;
145
+ if (diffHours < 24) return `${diffHours}h ago`;
146
+ if (diffDays < 30) return `${diffDays}d ago`;
147
+ return date.toISOString().split("T")[0];
148
+ }
149
+ function formatDate(dateStr) {
150
+ const date = new Date(dateStr);
151
+ return date.toLocaleDateString("en-US", {
152
+ year: "numeric",
153
+ month: "short",
154
+ day: "numeric"
155
+ });
156
+ }
157
+ var init_date = __esm({
158
+ "src/utils/date.ts"() {
159
+ "use strict";
160
+ }
161
+ });
162
+
163
+ // src/commands/explore.ts
164
+ var explore_exports = {};
165
+ __export(explore_exports, {
166
+ createExploreCommand: () => createExploreCommand
167
+ });
168
+ import { Command as Command4 } from "commander";
169
+ import { select, checkbox, confirm as confirm2, Separator } from "@inquirer/prompts";
170
+ import pc4 from "picocolors";
171
+ import ora from "ora";
172
+ function createExploreCommand(configManager, apiClient) {
173
+ const projectTracker = new ProjectTracker(configManager);
174
+ return new Command4("explore").description("Explore and select projects/agents to track").option("-p, --project <id>", "Start with specific project").action(async (options) => {
175
+ const spinner = ora("Fetching projects...").start();
176
+ try {
177
+ const response = await apiClient.getProjectsBrief();
178
+ const projects = response.projects;
179
+ spinner.stop();
180
+ if (projects.length === 0) {
181
+ console.log(pc4.yellow("No projects found. Create one in the web app first."));
182
+ return;
183
+ }
184
+ let exploring = true;
185
+ while (exploring) {
186
+ const projectChoices = projects.map((p) => ({
187
+ name: `${p.name} ${pc4.gray(`(${p.agentCount} agents, last modified ${formatDate(p.lastModifiedAt)})`)}`,
188
+ value: p,
189
+ short: p.name
190
+ }));
191
+ let selectedProject;
192
+ if (options.project) {
193
+ selectedProject = projects.find((p) => p.id === options.project || p.slug === options.project);
194
+ } else {
195
+ const choices = [
196
+ ...projectChoices,
197
+ new Separator(),
198
+ { name: pc4.yellow("Done exploring"), value: "DONE" }
199
+ ];
200
+ selectedProject = await select({
201
+ message: "Select a project to explore:",
202
+ choices
203
+ });
204
+ }
205
+ if (selectedProject === "DONE") {
206
+ exploring = false;
207
+ break;
208
+ }
209
+ if (!selectedProject) {
210
+ console.log(pc4.red("Project not found"));
211
+ continue;
212
+ }
213
+ const agentSpinner = ora(`Fetching agents for ${selectedProject.name}...`).start();
214
+ const agentsResponse = await apiClient.getProjectAgents(selectedProject.id);
215
+ const agents = agentsResponse;
216
+ agentSpinner.succeed(`Found ${agents.agents.length} agents`);
217
+ console.log(pc4.bold(`
218
+ \u{1F4C1} ${selectedProject.name}
219
+ `));
220
+ agents.agents.forEach((agent) => {
221
+ const isPublished = agent.isPublished ? pc4.green("\u25CF") : pc4.gray("\u25CB");
222
+ const versionInfo = `${agent.version} ${pc4.gray(`(${agents.versionResolution.source})`)}`;
223
+ console.log(` ${isPublished} ${pc4.cyan(agent.name)}`);
224
+ console.log(` ${pc4.gray("Path:")} ${agent.folderPath}`);
225
+ console.log(` ${pc4.gray("Version:")} ${versionInfo}`);
226
+ console.log(` ${pc4.gray("Tools:")} ${agent.toolCount}`);
227
+ if (agents.versionResolution.source !== "PRODUCTION") {
228
+ console.log(` ${pc4.yellow("\u26A0")} ${agents.versionResolution.note}`);
229
+ }
230
+ console.log();
231
+ });
232
+ const currentlyTracked = await projectTracker.getTrackedAgents(selectedProject.id);
233
+ const trackedIds = new Set(currentlyTracked.map((a) => a.agentId));
234
+ const selectedAgentIds = await checkbox({
235
+ message: "Select agents to track (space to toggle, enter to confirm):",
236
+ choices: agents.agents.map((agent) => ({
237
+ name: `${agent.name} ${pc4.gray(`(${agent.folderPath})`)}`,
238
+ value: agent.id,
239
+ checked: trackedIds.has(agent.id)
240
+ }))
241
+ });
242
+ if (selectedAgentIds.length > 0) {
243
+ const selectedAgents = agents.agents.filter((a) => selectedAgentIds.includes(a.id));
244
+ try {
245
+ await projectTracker.trackProject(selectedProject, selectedAgents);
246
+ console.log(pc4.green(`
247
+ \u2713 Auto-saved: Tracking ${selectedAgents.length} agents from ${selectedProject.name}`));
248
+ } catch (error) {
249
+ console.error(pc4.red(`
250
+ \u2717 Failed to save tracking: ${error.message}`));
251
+ throw error;
252
+ }
253
+ } else {
254
+ try {
255
+ await projectTracker.untrackProject(selectedProject.id);
256
+ console.log(pc4.yellow(`
257
+ \u2713 Auto-saved: No longer tracking ${selectedProject.name}`));
258
+ } catch (error) {
259
+ console.error(pc4.red(`
260
+ \u2717 Failed to untrack: ${error.message}`));
261
+ throw error;
262
+ }
263
+ }
264
+ if (!options.project) {
265
+ const continueExploring = await confirm2({
266
+ message: "Explore another project?",
267
+ default: true
268
+ });
269
+ if (!continueExploring) exploring = false;
270
+ } else {
271
+ exploring = false;
272
+ }
273
+ }
274
+ const summary = await projectTracker.getSummary();
275
+ console.log(pc4.bold("\n\u{1F4CA} Tracking Summary\n"));
276
+ console.log(` Projects: ${summary.projectCount}`);
277
+ console.log(` Agents: ${summary.agentCount}`);
278
+ console.log(pc4.gray("\nRun `porchestra pull` to generate code for tracked agents\n"));
279
+ } catch (error) {
280
+ spinner.fail("Failed to fetch projects");
281
+ console.error(pc4.red(`
282
+ \u2717 ${error.message}`));
283
+ process.exit(1);
284
+ }
285
+ });
286
+ }
287
+ var init_explore = __esm({
288
+ "src/commands/explore.ts"() {
289
+ "use strict";
290
+ init_project_tracker();
291
+ init_date();
292
+ }
293
+ });
294
+
295
+ // src/index.ts
296
+ import { Command as Command9 } from "commander";
297
+ import pc9 from "picocolors";
298
+
299
+ // src/core/config/config-manager.ts
300
+ import { promises as fs } from "fs";
301
+ import path from "path";
302
+ import os from "os";
303
+ import { z as z2 } from "zod";
304
+
305
+ // src/core/config/config-schema.ts
306
+ import { z } from "zod";
307
+ var DateTimeStringSchema = z.string().optional();
308
+ var TrackedAgentSchema = z.object({
309
+ agentId: z.string().uuid(),
310
+ agentName: z.string(),
311
+ agentSlug: z.string(),
312
+ folderPath: z.string(),
313
+ selectedAt: DateTimeStringSchema,
314
+ lastPulledAt: DateTimeStringSchema.optional(),
315
+ lastPulledVersion: z.string().optional()
316
+ });
317
+ var TrackedProjectSchema = z.object({
318
+ projectId: z.string().uuid(),
319
+ projectName: z.string(),
320
+ projectSlug: z.string(),
321
+ selectedAt: DateTimeStringSchema,
322
+ agents: z.array(TrackedAgentSchema),
323
+ lastPulledAt: DateTimeStringSchema.optional()
324
+ });
325
+ var CliConfigSchema = z.object({
326
+ auth: z.object({
327
+ token: z.string().optional(),
328
+ tokenId: z.string().uuid().optional(),
329
+ expiresAt: DateTimeStringSchema.optional(),
330
+ deviceName: z.string().optional()
331
+ }).optional(),
332
+ api: z.object({
333
+ baseUrl: z.string().url().default("https://api.porchestra.io/v1"),
334
+ skipTlsVerify: z.boolean().default(false)
335
+ }).default({}),
336
+ trackedProjects: z.array(TrackedProjectSchema).default([]),
337
+ output: z.object({
338
+ baseDir: z.string().default("./src/agents"),
339
+ createIndexFiles: z.boolean().default(true)
340
+ }).default({}),
341
+ cli: z.object({
342
+ lastLoginAt: DateTimeStringSchema.optional(),
343
+ lastVersionCheck: DateTimeStringSchema.optional(),
344
+ latestKnownVersion: z.string().optional()
345
+ }).optional(),
346
+ version: z.literal("1.0.0")
347
+ });
348
+
349
+ // src/core/config/config-manager.ts
350
+ var DEFAULT_CONFIG_DIR = path.join(os.homedir(), ".porchestra");
351
+ var CONFIG_FILE = "config.json";
352
+ var ConfigManager = class {
353
+ configPath;
354
+ config = null;
355
+ constructor() {
356
+ const configDir = process.env.PORCHESTRA_CONFIG_DIR || DEFAULT_CONFIG_DIR;
357
+ this.configPath = path.join(configDir, CONFIG_FILE);
358
+ }
359
+ async load() {
360
+ try {
361
+ const data = await fs.readFile(this.configPath, "utf-8");
362
+ const parsed = JSON.parse(data);
363
+ const validated = CliConfigSchema.parse(parsed);
364
+ this.config = validated;
365
+ return validated;
366
+ } catch (error) {
367
+ if (error.code === "ENOENT") {
368
+ const defaultConfig = { version: "1.0.0" };
369
+ this.config = CliConfigSchema.parse(defaultConfig);
370
+ return this.config;
371
+ }
372
+ if (error instanceof z2.ZodError) {
373
+ console.error("\u274C Config validation failed:");
374
+ error.errors.forEach((err) => {
375
+ console.error(` - ${err.path.join(".")}: ${err.message}`);
376
+ });
377
+ throw new Error(`Invalid config file at ${this.configPath}. Please run 'porchestra config reset' to fix.`);
378
+ }
379
+ throw error;
380
+ }
381
+ }
382
+ async save(config) {
383
+ try {
384
+ const validated = CliConfigSchema.parse(config);
385
+ const configDir = path.dirname(this.configPath);
386
+ await fs.mkdir(configDir, { recursive: true });
387
+ const tempPath = `${this.configPath}.tmp`;
388
+ await fs.writeFile(
389
+ tempPath,
390
+ JSON.stringify(validated, null, 2),
391
+ "utf-8"
392
+ );
393
+ await fs.rename(tempPath, this.configPath);
394
+ this.config = validated;
395
+ } catch (error) {
396
+ if (error instanceof z2.ZodError) {
397
+ console.error("\u274C Config validation failed on save:");
398
+ error.errors.forEach((err) => {
399
+ console.error(` - ${err.path.join(".")}: ${err.message}`);
400
+ });
401
+ throw new Error("Failed to save config: validation error");
402
+ }
403
+ throw error;
404
+ }
405
+ }
406
+ async get() {
407
+ if (!this.config) {
408
+ return this.load();
409
+ }
410
+ return this.config;
411
+ }
412
+ async update(updater) {
413
+ const config = await this.get();
414
+ const updated = updater({ ...config });
415
+ await this.save(updated);
416
+ }
417
+ async clear() {
418
+ try {
419
+ await fs.unlink(this.configPath);
420
+ } catch (error) {
421
+ if (error.code !== "ENOENT") {
422
+ throw error;
423
+ }
424
+ }
425
+ this.config = null;
426
+ }
427
+ getConfigPath() {
428
+ return this.configPath;
429
+ }
430
+ };
431
+
432
+ // src/utils/errors.ts
433
+ var PorchestraError = class extends Error {
434
+ constructor(message, code) {
435
+ super(message);
436
+ this.code = code;
437
+ this.name = "PorchestraError";
438
+ }
439
+ };
440
+ var AuthenticationError = class extends PorchestraError {
441
+ constructor(message, retryable = false) {
442
+ super(message, "AUTH_ERROR");
443
+ this.retryable = retryable;
444
+ this.name = "AuthenticationError";
445
+ }
446
+ };
447
+ var NetworkError = class extends PorchestraError {
448
+ constructor(message, statusCode, retryable = true) {
449
+ super(message, "NETWORK_ERROR");
450
+ this.statusCode = statusCode;
451
+ this.retryable = retryable;
452
+ this.name = "NetworkError";
453
+ }
454
+ };
455
+
456
+ // src/core/auth/auth-service.ts
457
+ import got from "got";
458
+ import os2 from "os";
459
+ var AuthService = class {
460
+ configManager;
461
+ constructor(configManager) {
462
+ this.configManager = configManager;
463
+ }
464
+ async login(credentials) {
465
+ const config = await this.configManager.get();
466
+ const baseUrl = credentials.apiUrl || config.api?.baseUrl || "https://api.porchestra.io/v1";
467
+ const deviceInfo = {
468
+ os: os2.platform(),
469
+ version: os2.release(),
470
+ cliVersion: "1.0.0"
471
+ };
472
+ const deviceName = credentials.deviceName || `${os2.hostname()} - ${deviceInfo.os}`;
473
+ try {
474
+ const response = await got.post(`${baseUrl}/cli/login`, {
475
+ json: {
476
+ email: credentials.email,
477
+ password: credentials.password,
478
+ deviceName,
479
+ deviceInfo
480
+ },
481
+ headers: {
482
+ "Content-Type": "application/json"
483
+ },
484
+ https: {
485
+ rejectUnauthorized: !credentials.skipTlsVerify
486
+ }
487
+ }).json();
488
+ return response.data;
489
+ } catch (error) {
490
+ if (error.response?.statusCode) {
491
+ if (error.response.statusCode === 401) {
492
+ throw new AuthenticationError("Invalid email or password");
493
+ }
494
+ throw new AuthenticationError(`Login failed: ${error.message}`);
495
+ }
496
+ if (error.code === "ECONNREFUSED" || error.code === "ENOTFOUND" || error.code === "ETIMEDOUT") {
497
+ throw new NetworkError(`Network error: ${error.message}`);
498
+ }
499
+ throw error;
500
+ }
501
+ }
502
+ async refreshToken(tokenId, currentToken) {
503
+ const config = await this.configManager.get();
504
+ const baseUrl = config.api?.baseUrl || "https://api.porchestra.io/v1";
505
+ try {
506
+ const response = await got.post(`${baseUrl}/cli/token/refresh`, {
507
+ json: {
508
+ tokenId,
509
+ currentToken
510
+ },
511
+ headers: {
512
+ "Authorization": `Bearer ${currentToken}`,
513
+ "Content-Type": "application/json"
514
+ }
515
+ }).json();
516
+ return response.data;
517
+ } catch (error) {
518
+ if (error.response?.statusCode === 401) {
519
+ throw new AuthenticationError("Token refresh failed. Please login again.");
520
+ }
521
+ throw error;
522
+ }
523
+ }
524
+ async revokeToken(token, revokeAll = false) {
525
+ const config = await this.configManager.get();
526
+ const baseUrl = config.api?.baseUrl || "https://api.porchestra.io/v1";
527
+ try {
528
+ const response = await got.delete(`${baseUrl}/cli/token`, {
529
+ json: {
530
+ revokeAll
531
+ },
532
+ headers: {
533
+ "Authorization": `Bearer ${token}`,
534
+ "Content-Type": "application/json"
535
+ }
536
+ }).json();
537
+ return response.data;
538
+ } catch (error) {
539
+ if (error.response?.statusCode === 401) {
540
+ throw new AuthenticationError("Token already revoked or invalid");
541
+ }
542
+ throw error;
543
+ }
544
+ }
545
+ async getCurrentUser(token) {
546
+ const config = await this.configManager.get();
547
+ const baseUrl = config.api?.baseUrl || "https://api.porchestra.io/v1";
548
+ try {
549
+ const response = await got.get(`${baseUrl}/auth/me`, {
550
+ headers: {
551
+ "Authorization": `Bearer ${token}`
552
+ }
553
+ }).json();
554
+ return response.data;
555
+ } catch (error) {
556
+ if (error.response?.statusCode === 401) {
557
+ throw new AuthenticationError("Token is invalid or expired");
558
+ }
559
+ throw error;
560
+ }
561
+ }
562
+ async checkAndRefreshTokenIfNeeded() {
563
+ const config = await this.configManager.get();
564
+ if (!config.auth?.token || !config.auth?.tokenId || !config.auth?.expiresAt) {
565
+ return false;
566
+ }
567
+ const expiresAt = new Date(config.auth.expiresAt);
568
+ const now = /* @__PURE__ */ new Date();
569
+ const sevenDaysFromNow = new Date(now.getTime() + 7 * 24 * 60 * 60 * 1e3);
570
+ if (expiresAt < sevenDaysFromNow) {
571
+ try {
572
+ const refreshed = await this.refreshToken(
573
+ config.auth.tokenId,
574
+ config.auth.token
575
+ );
576
+ await this.configManager.update((cfg) => ({
577
+ ...cfg,
578
+ auth: {
579
+ ...cfg.auth,
580
+ token: refreshed.token,
581
+ expiresAt: refreshed.expiresAt
582
+ }
583
+ }));
584
+ return true;
585
+ } catch {
586
+ return false;
587
+ }
588
+ }
589
+ return true;
590
+ }
591
+ };
592
+
593
+ // src/core/auth/token-manager.ts
594
+ var TokenManager = class {
595
+ constructor(configManager) {
596
+ this.configManager = configManager;
597
+ }
598
+ async getToken() {
599
+ const config = await this.configManager.get();
600
+ return config.auth?.token;
601
+ }
602
+ async getTokenId() {
603
+ const config = await this.configManager.get();
604
+ return config.auth?.tokenId;
605
+ }
606
+ async getExpiresAt() {
607
+ const config = await this.configManager.get();
608
+ return config.auth?.expiresAt;
609
+ }
610
+ async isAuthenticated() {
611
+ const token = await this.getToken();
612
+ if (!token) return false;
613
+ const expiresAt = await this.getExpiresAt();
614
+ if (!expiresAt) return false;
615
+ const expiryDate = new Date(expiresAt);
616
+ return expiryDate > /* @__PURE__ */ new Date();
617
+ }
618
+ async getDaysUntilExpiry() {
619
+ const expiresAt = await this.getExpiresAt();
620
+ if (!expiresAt) return null;
621
+ const expiryDate = new Date(expiresAt);
622
+ const now = /* @__PURE__ */ new Date();
623
+ const diffMs = expiryDate.getTime() - now.getTime();
624
+ return Math.ceil(diffMs / (1e3 * 60 * 60 * 24));
625
+ }
626
+ async isExpiringSoon(days = 7) {
627
+ const daysUntilExpiry = await this.getDaysUntilExpiry();
628
+ if (daysUntilExpiry === null) return false;
629
+ return daysUntilExpiry <= days;
630
+ }
631
+ };
632
+
633
+ // src/core/api/client.ts
634
+ import got2 from "got";
635
+ var MAX_RETRIES = 3;
636
+ var RETRY_DELAY = 1e3;
637
+ var ApiClient = class {
638
+ configManager;
639
+ constructor(configManager) {
640
+ this.configManager = configManager;
641
+ }
642
+ async getBaseUrl() {
643
+ const config = await this.configManager.get();
644
+ return config.api?.baseUrl || "https://api.porchestra.io/v1";
645
+ }
646
+ async getToken() {
647
+ const config = await this.configManager.get();
648
+ return config.auth?.token;
649
+ }
650
+ async requestWithRetry(url, options, attempt = 1) {
651
+ try {
652
+ const token = await this.getToken();
653
+ const headers = {
654
+ "Content-Type": "application/json",
655
+ ...token && { Authorization: `Bearer ${token}` },
656
+ ...options.headers || {}
657
+ };
658
+ const response = await got2(url, {
659
+ method: options.method,
660
+ headers,
661
+ retry: { limit: 0 }
662
+ }).json();
663
+ return response.data;
664
+ } catch (error) {
665
+ if (error.response?.statusCode === 401) {
666
+ throw new AuthenticationError("Authentication failed. Please login again.");
667
+ }
668
+ const isRetryable = error.code === "ECONNRESET" || error.code === "ETIMEDOUT" || error.code === "ENOTFOUND" || error.response?.statusCode && error.response.statusCode >= 500;
669
+ if (isRetryable && attempt < MAX_RETRIES) {
670
+ await new Promise((resolve) => setTimeout(resolve, RETRY_DELAY * attempt));
671
+ return this.requestWithRetry(url, options, attempt + 1);
672
+ }
673
+ throw new NetworkError(
674
+ `API request failed: ${error.message}`,
675
+ error.response?.statusCode,
676
+ isRetryable
677
+ );
678
+ }
679
+ }
680
+ async getProjectsBrief() {
681
+ const baseUrl = await this.getBaseUrl();
682
+ return this.requestWithRetry(
683
+ `${baseUrl}/projects/brief`,
684
+ { method: "GET" }
685
+ );
686
+ }
687
+ async getProjectAgents(projectId, environment) {
688
+ const baseUrl = await this.getBaseUrl();
689
+ const query = environment ? `?environment=${environment}` : "";
690
+ return this.requestWithRetry(
691
+ `${baseUrl}/projects/${projectId}/agents${query}`,
692
+ { method: "GET" }
693
+ );
694
+ }
695
+ async getAgentTools(projectId, agentId, environment) {
696
+ const baseUrl = await this.getBaseUrl();
697
+ const query = environment ? `?environment=${environment}` : "";
698
+ return this.requestWithRetry(
699
+ `${baseUrl}/projects/${projectId}/agents/${agentId}/tools${query}`,
700
+ { method: "GET" }
701
+ );
702
+ }
703
+ async getCurrentUser(token) {
704
+ const baseUrl = await this.getBaseUrl();
705
+ return this.requestWithRetry(
706
+ `${baseUrl}/auth/me`,
707
+ {
708
+ method: "GET",
709
+ headers: { Authorization: `Bearer ${token}` }
710
+ }
711
+ );
712
+ }
713
+ async getCliConfig() {
714
+ const baseUrl = await this.getBaseUrl();
715
+ return this.requestWithRetry(
716
+ `${baseUrl}/cli/config`,
717
+ { method: "GET" }
718
+ );
719
+ }
720
+ };
721
+
722
+ // src/core/generators/code-generator.ts
723
+ import fs2 from "fs/promises";
724
+ import path3 from "path";
725
+ import Handlebars from "handlebars";
726
+
727
+ // src/utils/path-utils.ts
728
+ import path2 from "path";
729
+ function normalizeFolderPath(folderPath) {
730
+ let normalized = folderPath.replace(/^\//, "");
731
+ normalized = normalized.replace(/\/+/g, "/");
732
+ normalized = normalized.split("/").map((segment) => sanitizePathSegment(segment)).join("/");
733
+ normalized = normalized.replace(/\/$/, "");
734
+ return normalized;
735
+ }
736
+ function sanitizePathSegment(segment) {
737
+ return segment.replace(/[^a-zA-Z0-9-_]/g, "-").replace(/^-+|-+$/g, "").replace(/-+/g, "-");
738
+ }
739
+ function calculateAgentOutputPath(baseDir, folderPath, agentName, filename) {
740
+ const normalizedFolder = normalizeFolderPath(folderPath);
741
+ const normalizedAgent = sanitizePathSegment(agentName);
742
+ const fullPath = path2.join(baseDir, normalizedFolder, normalizedAgent);
743
+ return filename ? path2.join(fullPath, filename) : fullPath;
744
+ }
745
+
746
+ // src/core/generators/code-generator.ts
747
+ Handlebars.registerHelper("pascalCase", function(str) {
748
+ return str.replace(/[-_\s](.)/g, (_, char) => char.toUpperCase()).replace(/^(.)/, (_, char) => char.toUpperCase());
749
+ });
750
+ Handlebars.registerHelper("camelCase", function(str) {
751
+ return str.replace(/[-_\s](.)/g, (_, char) => char.toUpperCase()).replace(/^(.)/, (_, char) => char.toLowerCase());
752
+ });
753
+ Handlebars.registerHelper("zodType", function(schema) {
754
+ return jsonSchemaToZodType(schema);
755
+ });
756
+ Handlebars.registerHelper("json", function(value) {
757
+ return JSON.stringify(value ?? {}, null, 2);
758
+ });
759
+ function normalizeSchemaContent(content) {
760
+ if (typeof content === "string") {
761
+ try {
762
+ return JSON.parse(content);
763
+ } catch {
764
+ return void 0;
765
+ }
766
+ }
767
+ return content;
768
+ }
769
+ function buildVariableJsonSchemaFromDefinitions(defs) {
770
+ const schema = {
771
+ type: "object",
772
+ properties: {},
773
+ required: []
774
+ };
775
+ const ensureObjectSchema = (target, key) => {
776
+ if (!target.properties[key]) {
777
+ target.properties[key] = { type: "object", properties: {}, required: [] };
778
+ }
779
+ return target.properties[key];
780
+ };
781
+ const toJsonSchema = (def) => {
782
+ const type = def?.type;
783
+ if (type === "enum") {
784
+ return {
785
+ type: "string",
786
+ enum: def?.validation?.options ?? []
787
+ };
788
+ }
789
+ if (type === "string" || type === "number" || type === "boolean") {
790
+ return { type };
791
+ }
792
+ if (type === "array") {
793
+ return { type: "array", items: {} };
794
+ }
795
+ if (type === "object") {
796
+ return { type: "object", properties: {} };
797
+ }
798
+ return {};
799
+ };
800
+ for (const def of defs) {
801
+ if (!def?.path || typeof def.path !== "string") continue;
802
+ const pathParts = def.path.split(".").filter(Boolean);
803
+ if (pathParts.length === 0) continue;
804
+ let curr = schema;
805
+ for (let i = 0; i < pathParts.length; i++) {
806
+ const key = pathParts[i];
807
+ const isLeaf = i === pathParts.length - 1;
808
+ if (isLeaf) {
809
+ curr.properties[key] = toJsonSchema(def);
810
+ if (def?.validation?.required !== false) {
811
+ curr.required.push(key);
812
+ }
813
+ } else {
814
+ curr = ensureObjectSchema(curr, key);
815
+ }
816
+ }
817
+ }
818
+ if (schema.required.length === 0) delete schema.required;
819
+ return schema;
820
+ }
821
+ function jsonSchemaToZodType(schema) {
822
+ if (!schema) return "z.any()";
823
+ switch (schema.type) {
824
+ case "string": {
825
+ let zod = "z.string()";
826
+ if (schema.minLength) zod += `.min(${schema.minLength})`;
827
+ if (schema.maxLength) zod += `.max(${schema.maxLength})`;
828
+ if (schema.pattern) zod += `.regex(/${schema.pattern}/)`;
829
+ if (schema.enum) zod = `z.enum([${schema.enum.map((e) => `'${e}'`).join(", ")}])`;
830
+ if (schema.nullable) zod += ".nullable()";
831
+ return zod;
832
+ }
833
+ case "number":
834
+ case "integer": {
835
+ let zod = schema.type === "integer" ? "z.number().int()" : "z.number()";
836
+ if (schema.minimum !== void 0) zod += `.min(${schema.minimum})`;
837
+ if (schema.maximum !== void 0) zod += `.max(${schema.maximum})`;
838
+ if (schema.nullable) zod += ".nullable()";
839
+ return zod;
840
+ }
841
+ case "boolean": {
842
+ let zod = "z.boolean()";
843
+ if (schema.nullable) zod += ".nullable()";
844
+ return zod;
845
+ }
846
+ case "array": {
847
+ const itemType = jsonSchemaToZodType(schema.items);
848
+ let zod = `z.array(${itemType})`;
849
+ if (schema.minItems) zod += `.min(${schema.minItems})`;
850
+ if (schema.maxItems) zod += `.max(${schema.maxItems})`;
851
+ if (schema.nullable) zod += ".nullable()";
852
+ return zod;
853
+ }
854
+ case "object": {
855
+ if (!schema.properties) return "z.record(z.any())";
856
+ const required = new Set(schema.required || []);
857
+ const props = Object.entries(schema.properties).map(([key, prop]) => {
858
+ const propType = jsonSchemaToZodType(prop);
859
+ const isRequired = required.has(key);
860
+ const formattedKey = /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(key) ? key : JSON.stringify(key);
861
+ return ` ${formattedKey}: ${isRequired ? propType : `${propType}.optional()`}`;
862
+ });
863
+ let zod = `z.object({
864
+ ${props.join(",\n")}
865
+ })`;
866
+ if (schema.additionalProperties === false) zod += ".strict()";
867
+ if (schema.nullable) zod += ".nullable()";
868
+ return zod;
869
+ }
870
+ default:
871
+ return "z.any()";
872
+ }
873
+ }
874
+ var CodeGenerator = class {
875
+ schemasTemplate;
876
+ toolsTemplate;
877
+ indexTemplate;
878
+ constructor() {
879
+ this.schemasTemplate = Handlebars.compile(schemasTemplateSource);
880
+ this.toolsTemplate = Handlebars.compile(toolsTemplateSource);
881
+ this.indexTemplate = Handlebars.compile(indexTemplateSource);
882
+ }
883
+ async generate(options) {
884
+ const { agent, tools, components, outputDir } = options;
885
+ await fs2.mkdir(outputDir, { recursive: true });
886
+ const responseJsonSchema = components?.responseSchema?.content || {};
887
+ const rawVariableSchema = normalizeSchemaContent(components?.variableSchema?.content);
888
+ const resolvedVariableSchema = Array.isArray(rawVariableSchema) ? buildVariableJsonSchemaFromDefinitions(rawVariableSchema) : rawVariableSchema || null;
889
+ const templateData = {
890
+ agentName: agent?.name || "Unknown Agent",
891
+ version: agent?.version || "unknown",
892
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
893
+ tools: (tools || []).filter((t) => t && !t.isBuiltin && typeof t.name === "string" && t.name.length > 0).map((t) => ({
894
+ name: t.name,
895
+ description: t.description || "",
896
+ parameters: t.parameters || {},
897
+ returns: t.returns ?? null,
898
+ isBuiltin: t.isBuiltin,
899
+ builtinType: t.builtinType
900
+ })),
901
+ variableSchema: resolvedVariableSchema,
902
+ inputSchema: components?.inputSchema?.content || null,
903
+ responseSchema: components?.responseSchema?.content || null,
904
+ responseJsonSchema
905
+ };
906
+ const astPath = path3.join(outputDir, "ast.json");
907
+ const astContent = JSON.stringify(components?.ast?.content ?? {}, null, 2);
908
+ await fs2.writeFile(astPath, astContent, "utf-8");
909
+ const configPath = path3.join(outputDir, "config.ts");
910
+ const configContent = `/**
911
+ * Auto-generated model config for ${templateData.agentName}
912
+ * Generated: ${templateData.generatedAt}
913
+ * Version: ${templateData.version}
914
+ * DO NOT MODIFY - This file is auto-generated by Porchestra CLI
915
+ */
916
+
917
+ export const ModelConfig = ${JSON.stringify(components?.modelConfig?.content ?? {}, null, 2)} as const;
918
+ `;
919
+ await fs2.writeFile(configPath, configContent, "utf-8");
920
+ const schemasPath = path3.join(outputDir, "schemas.ts");
921
+ const schemasContent = this.schemasTemplate(templateData);
922
+ await fs2.writeFile(schemasPath, schemasContent, "utf-8");
923
+ const toolsPath = path3.join(outputDir, "tools.ts");
924
+ const toolsContent = this.toolsTemplate(templateData);
925
+ await fs2.writeFile(toolsPath, toolsContent, "utf-8");
926
+ const indexPath = path3.join(outputDir, "index.ts");
927
+ const indexContent = this.indexTemplate(templateData);
928
+ await fs2.writeFile(indexPath, indexContent, "utf-8");
929
+ }
930
+ calculateOutputPath(baseDir, folderPath, agentName) {
931
+ return calculateAgentOutputPath(baseDir, folderPath, agentName);
932
+ }
933
+ };
934
+ var schemasTemplateSource = `/**
935
+ * Auto-generated Zod schemas for {{agentName}}
936
+ * Generated: {{generatedAt}}
937
+ * Version: {{version}}
938
+ * DO NOT MODIFY - This file is auto-generated by Porchestra CLI
939
+ */
940
+
941
+ import { z } from 'zod';
942
+
943
+ // 1. System Prompt Variables
944
+ export const VariableSchema = {{{zodType variableSchema}}};
945
+ export type AgentVariables = z.infer<typeof VariableSchema>;
946
+
947
+ // 2. User Input
948
+ export const InputSchema = {{{zodType inputSchema}}};
949
+ export type AgentInput = z.infer<typeof InputSchema>;
950
+
951
+ // 3. Model Response
952
+ export const ResponseSchema = {{{zodType responseSchema}}};
953
+ export type AgentResponse = z.infer<typeof ResponseSchema>;
954
+
955
+ // 4. Raw JSON Schema (for LLM Provider 'response_format')
956
+ export const ResponseJsonSchema = {{{json responseJsonSchema}}} as const;
957
+ `;
958
+ var toolsTemplateSource = `/**
959
+ * Auto-generated tools for {{agentName}}
960
+ * Generated: {{generatedAt}}
961
+ * Version: {{version}}
962
+ * DO NOT MODIFY - This file is auto-generated by Porchestra CLI
963
+ */
964
+
965
+ import { z } from 'zod';
966
+
967
+ {{#each tools}}
968
+ // Schema for {{name}}
969
+ export const {{pascalCase name}}Params = {{{zodType parameters}}};
970
+ export type {{pascalCase name}}ParamsType = z.infer<typeof {{pascalCase name}}Params>;
971
+
972
+ {{/each}}
973
+ // The Interface the user MUST implement
974
+ export interface ToolImplementation {
975
+ {{#each tools}}
976
+ {{camelCase name}}: (args: {{pascalCase name}}ParamsType) => Promise<unknown>;
977
+ {{/each}}
978
+ }
979
+
980
+ // Raw Tool Definitions for provider SDKs
981
+ export const ToolDefinitions = [
982
+ {{#each tools}}
983
+ {
984
+ name: '{{name}}',
985
+ description: {{{json description}}},
986
+ parameters: {{{json parameters}}},
987
+ },
988
+ {{/each}}
989
+ ] as const;
990
+
991
+ // Dispatcher with validation
992
+ export async function dispatch(
993
+ name: string,
994
+ args: unknown,
995
+ impl: ToolImplementation
996
+ ): Promise<unknown> {
997
+ switch (name) {
998
+ {{#each tools}}
999
+ case '{{name}}':
1000
+ return await impl.{{camelCase name}}({{pascalCase name}}Params.parse(args));
1001
+ {{/each}}
1002
+ default:
1003
+ throw new Error(\`Unknown tool: \${name}\`);
1004
+ }
1005
+ }
1006
+ `;
1007
+ var indexTemplateSource = `/**
1008
+ * Auto-generated Agent class for {{agentName}}
1009
+ * Generated: {{generatedAt}}
1010
+ * Version: {{version}}
1011
+ * DO NOT MODIFY - This file is auto-generated by Porchestra CLI
1012
+ */
1013
+
1014
+ import { PorchestraRuntime } from '@porchestra/core';
1015
+ import rawAst from './ast.json';
1016
+ import { ModelConfig } from './config.js';
1017
+ import * as Schemas from './schemas.js';
1018
+ import * as Tools from './tools.js';
1019
+
1020
+ export class {{pascalCase agentName}}Agent {
1021
+ /**
1022
+ * @param tools - The implementation of the tool logic. Required if the agent uses tools.
1023
+ */
1024
+ constructor(private readonly tools?: Tools.ToolImplementation) {}
1025
+
1026
+ /**
1027
+ * Renders the System Prompt.
1028
+ * Throws ZodError if variables are invalid.
1029
+ */
1030
+ public renderPrompt(variables: Schemas.AgentVariables): string {
1031
+ const safeVars = Schemas.VariableSchema.parse(variables);
1032
+ return PorchestraRuntime.render(rawAst as any, safeVars);
1033
+ }
1034
+
1035
+ /**
1036
+ * Routes an LLM Tool Call to your implementation.
1037
+ * Validates arguments automatically.
1038
+ */
1039
+ public async handleTool(name: string, args: unknown): Promise<unknown> {
1040
+ if (!this.tools) {
1041
+ throw new Error('No tool implementation provided to Agent constructor.');
1042
+ }
1043
+ return Tools.dispatch(name, args, this.tools);
1044
+ }
1045
+
1046
+ /** Validates User Input (e.g. from API Request) */
1047
+ public static validateInput(data: unknown): Schemas.AgentInput {
1048
+ return Schemas.InputSchema.parse(data);
1049
+ }
1050
+
1051
+ /** Validates LLM Output (Post-generation check) */
1052
+ public static validateResponse(data: unknown): Schemas.AgentResponse {
1053
+ return Schemas.ResponseSchema.parse(data);
1054
+ }
1055
+
1056
+ /** Get Provider Configuration */
1057
+ public static get config() {
1058
+ return ModelConfig;
1059
+ }
1060
+
1061
+ /** Get Tool Definitions for the LLM Provider */
1062
+ public static get toolDefinitions() {
1063
+ return Tools.ToolDefinitions;
1064
+ }
1065
+
1066
+ /** Get JSON Schema for Structured Outputs */
1067
+ public static get responseFormat() {
1068
+ return Schemas.ResponseJsonSchema;
1069
+ }
1070
+ }
1071
+ `;
1072
+
1073
+ // src/commands/login.ts
1074
+ import { Command } from "commander";
1075
+ import pc from "picocolors";
1076
+ import { input, password } from "@inquirer/prompts";
1077
+ function createLoginCommand(configManager, authService) {
1078
+ return new Command("login").description("Authenticate with your Porchestra account").option("--api-url <url>", "Override API URL").option("--skip-tls-verify", "Skip TLS certificate verification (insecure)").action(async (options) => {
1079
+ console.log(pc.bold("\n \u{1F510} Porchestra CLI Login\n"));
1080
+ const apiUrl = options.apiUrl || process.env.PORCHESTRA_API_URL;
1081
+ if (options.skipTlsVerify) {
1082
+ console.log(pc.yellow("\n \u26A0\uFE0F WARNING: TLS verification disabled. Not recommended for production.\n"));
1083
+ }
1084
+ const email = await input({
1085
+ message: " Email:",
1086
+ validate: (value) => value.includes("@") || "Please enter a valid email"
1087
+ });
1088
+ const pwd = await password({
1089
+ message: " Password:",
1090
+ mask: "*"
1091
+ });
1092
+ try {
1093
+ const result = await authService.login({
1094
+ email,
1095
+ password: pwd,
1096
+ apiUrl,
1097
+ skipTlsVerify: options.skipTlsVerify
1098
+ });
1099
+ console.log(pc.yellow("\n [DEBUG] Raw API response:"));
1100
+ console.log(pc.yellow(` token: ${result.token?.substring(0, 20)}...`));
1101
+ console.log(pc.yellow(` tokenId: ${result.tokenId}`));
1102
+ console.log(pc.yellow(` expiresAt: ${result.expiresAt}`));
1103
+ console.log(pc.yellow(` issuedAt: ${result.issuedAt}`));
1104
+ console.log(pc.yellow(` deviceName: ${result.deviceName}`));
1105
+ try {
1106
+ await configManager.update((cfg) => ({
1107
+ ...cfg,
1108
+ auth: {
1109
+ token: result.token,
1110
+ tokenId: result.tokenId,
1111
+ expiresAt: result.expiresAt,
1112
+ deviceName: result.deviceName
1113
+ },
1114
+ api: {
1115
+ baseUrl: apiUrl || cfg.api?.baseUrl || "https://api.porchestra.io/v1",
1116
+ skipTlsVerify: options.skipTlsVerify || false
1117
+ },
1118
+ cli: {
1119
+ ...cfg.cli,
1120
+ lastLoginAt: (/* @__PURE__ */ new Date()).toISOString()
1121
+ }
1122
+ }));
1123
+ const savedConfig = await configManager.get();
1124
+ console.log(pc.yellow("\n [DEBUG] Saved config auth:"));
1125
+ console.log(pc.yellow(` ${JSON.stringify(savedConfig.auth, null, 2)}`));
1126
+ } catch (saveError) {
1127
+ console.error(pc.red("\n [DEBUG] Save error:"), saveError);
1128
+ throw saveError;
1129
+ }
1130
+ const expiresAt = new Date(result.expiresAt);
1131
+ const daysUntilExpiry = Math.ceil((expiresAt.getTime() - Date.now()) / (1e3 * 60 * 60 * 24));
1132
+ console.log(pc.green(`
1133
+ \u2713 Login successful
1134
+ `));
1135
+ console.log(pc.gray(` Token expires in ${daysUntilExpiry} days (${result.expiresAt})`));
1136
+ console.log(pc.gray(` Device: ${result.deviceName}`));
1137
+ if (apiUrl) {
1138
+ console.log(pc.gray(` API: ${apiUrl}`));
1139
+ }
1140
+ console.log(pc.gray(` Auto-refresh: 7 days before expiry`));
1141
+ console.log();
1142
+ } catch (error) {
1143
+ console.error(pc.red(`
1144
+ \u2717 Login failed: ${error.message}`));
1145
+ process.exit(1);
1146
+ }
1147
+ });
1148
+ }
1149
+
1150
+ // src/commands/logout.ts
1151
+ import { Command as Command2 } from "commander";
1152
+ import pc2 from "picocolors";
1153
+ import { confirm } from "@inquirer/prompts";
1154
+ function createLogoutCommand(configManager, authService) {
1155
+ return new Command2("logout").description("Logout and revoke CLI token").option("--all", "Revoke all device tokens (logout everywhere)").action(async (options) => {
1156
+ const config = await configManager.get();
1157
+ if (!config.auth?.token) {
1158
+ console.log(pc2.yellow("Not currently logged in"));
1159
+ return;
1160
+ }
1161
+ if (options.all) {
1162
+ const confirmed = await confirm({
1163
+ message: "This will revoke ALL your CLI tokens on ALL devices. Continue?",
1164
+ default: false
1165
+ });
1166
+ if (!confirmed) return;
1167
+ }
1168
+ try {
1169
+ await authService.revokeToken(config.auth.token, options.all);
1170
+ await configManager.clear();
1171
+ if (options.all) {
1172
+ console.log(pc2.green("\u2713 Revoked all tokens and logged out all devices"));
1173
+ } else {
1174
+ console.log(pc2.green("\u2713 Logged out successfully"));
1175
+ }
1176
+ } catch (error) {
1177
+ await configManager.clear();
1178
+ console.log(pc2.yellow("\u2713 Cleared local credentials (server revoke may have failed)"));
1179
+ }
1180
+ });
1181
+ }
1182
+
1183
+ // src/commands/whoami.ts
1184
+ init_project_tracker();
1185
+ import { Command as Command3 } from "commander";
1186
+ import pc3 from "picocolors";
1187
+ function createWhoamiCommand(configManager, authService) {
1188
+ const projectTracker = new ProjectTracker(configManager);
1189
+ return new Command3("whoami").description("Show current user and token information").action(async () => {
1190
+ const config = await configManager.get();
1191
+ if (!config.auth?.token) {
1192
+ console.log(pc3.yellow("Not logged in. Run `porchestra login`"));
1193
+ return;
1194
+ }
1195
+ try {
1196
+ const userInfo = await authService.getCurrentUser(config.auth.token);
1197
+ const expiresAt = new Date(config.auth.expiresAt);
1198
+ const isExpiringSoon = expiresAt.getTime() - Date.now() < 7 * 24 * 60 * 60 * 1e3;
1199
+ console.log(pc3.bold("\n\u{1F464} User Information\n"));
1200
+ console.log(` Email: ${pc3.cyan(userInfo.email)}`);
1201
+ console.log(` Name: ${userInfo.name || "N/A"}`);
1202
+ console.log(pc3.bold("\n\u{1F511} Token Information\n"));
1203
+ console.log(` Device: ${config.auth.deviceName || "N/A"}`);
1204
+ console.log(` Expires: ${isExpiringSoon ? pc3.yellow(expiresAt.toISOString()) : pc3.green(expiresAt.toISOString())}`);
1205
+ if (isExpiringSoon) {
1206
+ console.log(pc3.yellow(" \u26A0\uFE0F Expires within 7 days - will auto-refresh"));
1207
+ }
1208
+ if (config.api?.baseUrl !== "https://api.porchestra.io/v1") {
1209
+ console.log(pc3.bold("\n\u{1F517} API Configuration\n"));
1210
+ console.log(` URL: ${config.api?.baseUrl}`);
1211
+ }
1212
+ const summary = await projectTracker.getSummary();
1213
+ console.log(pc3.bold("\n\u{1F4C1} Tracked Projects\n"));
1214
+ console.log(` Projects: ${summary.projectCount}`);
1215
+ console.log(` Agents: ${summary.agentCount}`);
1216
+ } catch (error) {
1217
+ console.log(pc3.red("Failed to fetch user info. Token may be invalid."));
1218
+ console.log(pc3.gray("Run `porchestra login` to re-authenticate."));
1219
+ }
1220
+ });
1221
+ }
1222
+
1223
+ // src/index.ts
1224
+ init_explore();
1225
+
1226
+ // src/commands/pull.ts
1227
+ init_project_tracker();
1228
+ import { Command as Command5 } from "commander";
1229
+ import path4 from "path";
1230
+ import pc5 from "picocolors";
1231
+ import ora2 from "ora";
1232
+ function createPullCommand(configManager, apiClient, codeGenerator) {
1233
+ const projectTracker = new ProjectTracker(configManager);
1234
+ return new Command5("pull").description("Generate tool code for tracked agents").option("-p, --project <id>", "Pull specific project only").option("-a, --agent <id>", "Pull specific agent only").option("-e, --env <environment>", "Target environment (production|staging|development)").option("-o, --output <path>", "Override output directory").option("--force", "Overwrite implementation files (WARNING: may lose code)").action(async (options) => {
1235
+ const config = await configManager.get();
1236
+ if (!config.auth?.token) {
1237
+ console.log(pc5.red("Not logged in. Run `porchestra login` first."));
1238
+ process.exit(1);
1239
+ }
1240
+ let trackedProjects = await projectTracker.getTrackedProjects();
1241
+ if (trackedProjects.length === 0) {
1242
+ console.log(pc5.yellow("\n\u26A0 No projects selected for tracking.\n"));
1243
+ console.log(pc5.gray("Starting project explorer...\n"));
1244
+ const { createExploreCommand: createExploreCommand2 } = await Promise.resolve().then(() => (init_explore(), explore_exports));
1245
+ const exploreCmd = createExploreCommand2(configManager, apiClient);
1246
+ await exploreCmd.parseAsync(["node", "script", "explore"]);
1247
+ trackedProjects = await projectTracker.getTrackedProjects();
1248
+ if (trackedProjects.length === 0) {
1249
+ console.log(pc5.red("\n\u274C No projects selected. Run `porchestra explore` to select projects."));
1250
+ process.exit(1);
1251
+ }
1252
+ }
1253
+ if (options.project) {
1254
+ trackedProjects = trackedProjects.filter(
1255
+ (p) => p.projectId === options.project || p.projectSlug === options.project
1256
+ );
1257
+ if (trackedProjects.length === 0) {
1258
+ console.log(pc5.red(`Project "${options.project}" not found in tracked projects`));
1259
+ process.exit(1);
1260
+ }
1261
+ }
1262
+ const baseOutputDir = options.output || config.output?.baseDir || path4.resolve(process.cwd(), "src/porchestra/agents");
1263
+ let totalAgents = 0;
1264
+ let successCount = 0;
1265
+ for (const project of trackedProjects) {
1266
+ let agents = project.agents;
1267
+ if (options.agent) {
1268
+ agents = agents.filter(
1269
+ (a) => a.agentId === options.agent || a.agentSlug === options.agent
1270
+ );
1271
+ }
1272
+ if (agents.length === 0) continue;
1273
+ console.log(pc5.bold(`
1274
+ \u{1F4E6} ${project.projectName}
1275
+ `));
1276
+ totalAgents += agents.length;
1277
+ for (let i = 0; i < agents.length; i++) {
1278
+ const agent = agents[i];
1279
+ const progress = `[${i + 1}/${agents.length}]`;
1280
+ const spinner = ora2(`${progress} Fetching ${agent.agentName}...`).start();
1281
+ try {
1282
+ const toolsResponse = await apiClient.getAgentTools(
1283
+ project.projectId,
1284
+ agent.agentId,
1285
+ options.env
1286
+ );
1287
+ spinner.text = `${progress} Generating code...`;
1288
+ const normalizeToolset = (toolset = []) => toolset.map((t, idx) => ({
1289
+ id: t.id ?? `toolset-${idx}`,
1290
+ name: t.name,
1291
+ description: t.description ?? "",
1292
+ parameters: t.parameters ?? {},
1293
+ returns: t.returns ?? null,
1294
+ isBuiltin: t.isBuiltin ?? false,
1295
+ builtinType: t.builtinType
1296
+ }));
1297
+ const rawToolConfig = toolsResponse?.components?.toolConfig?.content;
1298
+ const parsedToolConfig = (() => {
1299
+ if (typeof rawToolConfig === "string") {
1300
+ try {
1301
+ return JSON.parse(rawToolConfig);
1302
+ } catch {
1303
+ return void 0;
1304
+ }
1305
+ }
1306
+ return rawToolConfig;
1307
+ })();
1308
+ const findToolArray = (value) => {
1309
+ if (Array.isArray(value)) {
1310
+ const hasNamedObjects = value.every((v) => v && typeof v === "object" && "name" in v);
1311
+ return hasNamedObjects ? value : void 0;
1312
+ }
1313
+ if (value && typeof value === "object") {
1314
+ if (Array.isArray(value.toolset)) return value.toolset;
1315
+ if (Array.isArray(value.tools)) return value.tools;
1316
+ for (const key of Object.keys(value)) {
1317
+ const found = findToolArray(value[key]);
1318
+ if (found) return found;
1319
+ }
1320
+ }
1321
+ return void 0;
1322
+ };
1323
+ const toolsetFromConfig = findToolArray(parsedToolConfig) ?? [];
1324
+ const resolvedTools = toolsResponse?.tools && toolsResponse.tools.length > 0 ? toolsResponse.tools : Array.isArray(toolsResponse?.toolset) ? normalizeToolset(toolsResponse.toolset) : normalizeToolset(toolsetFromConfig);
1325
+ if (!toolsResponse?.agent || !toolsResponse?.components) {
1326
+ throw new Error("Invalid tools response from API");
1327
+ }
1328
+ const outputDir = codeGenerator.calculateOutputPath(
1329
+ baseOutputDir,
1330
+ agent.folderPath,
1331
+ toolsResponse.agent.name
1332
+ );
1333
+ await codeGenerator.generate({
1334
+ agent: toolsResponse.agent,
1335
+ tools: resolvedTools,
1336
+ components: toolsResponse.components,
1337
+ outputDir,
1338
+ forceOverwrite: options.force || false
1339
+ });
1340
+ spinner.succeed(`${progress} ${agent.agentName} \u2192 ${pc5.gray(outputDir)}`);
1341
+ successCount++;
1342
+ await projectTracker.updateLastPulled(
1343
+ project.projectId,
1344
+ agent.agentId,
1345
+ toolsResponse.agent.version
1346
+ );
1347
+ } catch (error) {
1348
+ spinner.fail(`${progress} ${agent.agentName}`);
1349
+ console.error(pc5.red(` \u2717 ${error.message}`));
1350
+ }
1351
+ }
1352
+ }
1353
+ console.log(pc5.bold("\n\u2728 Pull Complete\n"));
1354
+ if (successCount === totalAgents) {
1355
+ console.log(` ${pc5.green("\u2713")} Generated: ${successCount} / ${totalAgents} agents`);
1356
+ } else if (successCount > 0) {
1357
+ console.log(` ${pc5.yellow("\u26A0")} Generated: ${successCount} / ${totalAgents} agents`);
1358
+ console.log(` ${pc5.red("\u2717")} Failed: ${totalAgents - successCount} agents`);
1359
+ } else {
1360
+ console.log(` ${pc5.red("\u2717")} Failed: All ${totalAgents} agents`);
1361
+ }
1362
+ console.log(` Output: ${pc5.gray(baseOutputDir)}`);
1363
+ if (successCount > 0) {
1364
+ console.log(pc5.gray("\nNext steps:"));
1365
+ console.log(pc5.gray(" 1. Implement tool functions in tool-impl.ts files"));
1366
+ console.log(pc5.gray(" 2. Import and use the tool dispatcher in your code"));
1367
+ }
1368
+ console.log();
1369
+ if (successCount < totalAgents) {
1370
+ process.exit(1);
1371
+ }
1372
+ });
1373
+ }
1374
+
1375
+ // src/commands/config.ts
1376
+ import { Command as Command6 } from "commander";
1377
+ import pc6 from "picocolors";
1378
+ import { confirm as confirm3 } from "@inquirer/prompts";
1379
+ function createConfigCommand(configManager) {
1380
+ const config = new Command6("config").description("Manage CLI configuration");
1381
+ config.command("get <key>").description("Get a configuration value").action(async (key) => {
1382
+ const cfg = await configManager.get();
1383
+ const value = getNestedValue(cfg, key);
1384
+ console.log(value !== void 0 ? value : pc6.gray("(not set)"));
1385
+ });
1386
+ config.command("set <key> <value>").description("Set a configuration value").action(async (key, value) => {
1387
+ await configManager.update((cfg) => {
1388
+ return setNestedValue(cfg, key, parseValue(value));
1389
+ });
1390
+ console.log(pc6.green(`\u2713 Set ${key} = ${value}`));
1391
+ });
1392
+ config.command("list").description("List all configuration values").action(async () => {
1393
+ const cfg = await configManager.get();
1394
+ console.log(pc6.bold("\nConfiguration:"));
1395
+ printConfig(cfg);
1396
+ });
1397
+ config.command("reset").description("Reset all configuration (WARNING: clears auth)").option("--force", "Skip confirmation").action(async (options) => {
1398
+ if (!options.force) {
1399
+ const confirmed = await confirm3({
1400
+ message: "This will clear all config including login. Continue?",
1401
+ default: false
1402
+ });
1403
+ if (!confirmed) return;
1404
+ }
1405
+ await configManager.clear();
1406
+ console.log(pc6.green("\u2713 Configuration reset"));
1407
+ });
1408
+ return config;
1409
+ }
1410
+ function getNestedValue(obj, key) {
1411
+ return key.split(".").reduce((o, k) => o?.[k], obj);
1412
+ }
1413
+ function setNestedValue(obj, key, value) {
1414
+ const keys = key.split(".");
1415
+ const last = keys.pop();
1416
+ const target = keys.reduce((o, k) => {
1417
+ if (!o[k]) o[k] = {};
1418
+ return o[k];
1419
+ }, obj);
1420
+ target[last] = value;
1421
+ return obj;
1422
+ }
1423
+ function parseValue(value) {
1424
+ if (value === "true") return true;
1425
+ if (value === "false") return false;
1426
+ if (value === "null") return null;
1427
+ if (!isNaN(Number(value))) return Number(value);
1428
+ try {
1429
+ return JSON.parse(value);
1430
+ } catch {
1431
+ return value;
1432
+ }
1433
+ }
1434
+ function printConfig(obj, prefix = "") {
1435
+ for (const [key, value] of Object.entries(obj)) {
1436
+ const fullKey = prefix ? `${prefix}.${key}` : key;
1437
+ if (value && typeof value === "object" && !Array.isArray(value)) {
1438
+ console.log(`
1439
+ ${pc6.cyan(fullKey)}:`);
1440
+ printConfig(value, fullKey);
1441
+ } else {
1442
+ const displayValue = key.includes("token") ? pc6.gray("***hidden***") : value;
1443
+ console.log(` ${fullKey} = ${displayValue}`);
1444
+ }
1445
+ }
1446
+ }
1447
+
1448
+ // src/commands/status.ts
1449
+ init_project_tracker();
1450
+ init_date();
1451
+ import { Command as Command7 } from "commander";
1452
+ import pc7 from "picocolors";
1453
+ function createStatusCommand(configManager) {
1454
+ const projectTracker = new ProjectTracker(configManager);
1455
+ return new Command7("status").description("Show CLI status and tracked projects").action(async () => {
1456
+ const config = await configManager.get();
1457
+ console.log(pc7.bold("\n\u{1F4CA} Porchestra CLI Status\n"));
1458
+ if (config.auth?.token) {
1459
+ const expiresAt = new Date(config.auth.expiresAt);
1460
+ const daysUntilExpiry = Math.ceil(
1461
+ (expiresAt.getTime() - Date.now()) / (1e3 * 60 * 60 * 24)
1462
+ );
1463
+ const expiryColor = daysUntilExpiry < 7 ? pc7.yellow : pc7.green;
1464
+ console.log(pc7.bold("\u{1F511} Authentication"));
1465
+ console.log(` Status: ${pc7.green("\u2713 Logged in")}`);
1466
+ console.log(` Device: ${config.auth.deviceName}`);
1467
+ console.log(` Expires: ${expiryColor(daysUntilExpiry + " days")} (${config.auth.expiresAt})`);
1468
+ } else {
1469
+ console.log(pc7.bold("\u{1F511} Authentication"));
1470
+ console.log(` Status: ${pc7.yellow("\u2717 Not logged in")}`);
1471
+ console.log(pc7.gray(" Run `porchestra login` to authenticate"));
1472
+ }
1473
+ console.log(pc7.bold("\n\u{1F517} API Configuration"));
1474
+ console.log(` URL: ${config.api?.baseUrl || pc7.gray("(default)")}`);
1475
+ if (config.api?.skipTlsVerify) {
1476
+ console.log(pc7.yellow(` \u26A0\uFE0F TLS verification disabled`));
1477
+ }
1478
+ const projects = await projectTracker.getTrackedProjects();
1479
+ console.log(pc7.bold("\n\u{1F4C1} Tracked Projects"));
1480
+ if (projects.length === 0) {
1481
+ console.log(pc7.gray(" No projects tracked"));
1482
+ console.log(pc7.gray(" Run `porchestra explore` to select projects"));
1483
+ } else {
1484
+ console.log(` Projects: ${projects.length}`);
1485
+ console.log(` Agents: ${projects.reduce((sum, p) => sum + p.agents.length, 0)}`);
1486
+ projects.forEach((project) => {
1487
+ const lastPulled = project.lastPulledAt ? formatDistanceToNow(new Date(project.lastPulledAt)) : pc7.gray("never");
1488
+ console.log(pc7.gray(`
1489
+ ${project.projectName}`));
1490
+ project.agents.forEach((agent) => {
1491
+ const agentPulled = agent.lastPulledAt ? formatDistanceToNow(new Date(agent.lastPulledAt)) : pc7.gray("pending");
1492
+ console.log(pc7.gray(` \u2514\u2500 ${agent.agentName} ${pc7.cyan(agent.folderPath)} (${agentPulled})`));
1493
+ });
1494
+ console.log(pc7.gray(` Last pulled: ${lastPulled}`));
1495
+ });
1496
+ }
1497
+ console.log(pc7.bold("\n\u{1F4C2} Output Configuration"));
1498
+ console.log(` Base dir: ${config.output?.baseDir || "./src/agents"}`);
1499
+ console.log(` Index files: ${config.output?.createIndexFiles ? "yes" : "no"}`);
1500
+ console.log();
1501
+ });
1502
+ }
1503
+
1504
+ // src/commands/agents.ts
1505
+ init_project_tracker();
1506
+ init_date();
1507
+ import { Command as Command8 } from "commander";
1508
+ import { select as select2, confirm as confirm4 } from "@inquirer/prompts";
1509
+ import pc8 from "picocolors";
1510
+ function createAgentsCommand(configManager) {
1511
+ const projectTracker = new ProjectTracker(configManager);
1512
+ const listAction = async (options = {}) => {
1513
+ let projects = await projectTracker.getTrackedProjects();
1514
+ if (options.project) {
1515
+ projects = projects.filter(
1516
+ (project) => project.projectId === options.project || project.projectSlug === options.project
1517
+ );
1518
+ if (projects.length === 0) {
1519
+ console.log(pc8.red(`No tracked project found for "${options.project}"`));
1520
+ return;
1521
+ }
1522
+ }
1523
+ if (options.json) {
1524
+ console.log(JSON.stringify(projects, null, 2));
1525
+ return;
1526
+ }
1527
+ console.log(pc8.bold("\n\u{1F4C1} Tracked Agents\n"));
1528
+ if (projects.length === 0) {
1529
+ console.log(pc8.gray(" No agents tracked. Run `porchestra explore` to add agents."));
1530
+ console.log();
1531
+ return;
1532
+ }
1533
+ let totalAgents = 0;
1534
+ projects.forEach((project) => {
1535
+ totalAgents += project.agents.length;
1536
+ console.log(pc8.bold(` ${project.projectName}`));
1537
+ project.agents.forEach((agent) => {
1538
+ const lastPulled = agent.lastPulledAt ? formatDistanceToNow(new Date(agent.lastPulledAt)) : "pending";
1539
+ const version = agent.lastPulledVersion ? ` @ ${agent.lastPulledVersion}` : "";
1540
+ console.log(` \u2514\u2500 ${pc8.cyan(agent.agentName)}${version ? pc8.gray(version) : ""}`);
1541
+ console.log(pc8.gray(` ${agent.folderPath} (${lastPulled})`));
1542
+ });
1543
+ console.log();
1544
+ });
1545
+ console.log(pc8.gray(`Total: ${projects.length} project(s), ${totalAgents} agent(s)
1546
+ `));
1547
+ };
1548
+ const command = new Command8("agents").description("Manage tracked agents").action(async () => listAction());
1549
+ command.command("list").description("List tracked agents").option("-p, --project <id>", "Filter by project id or slug").option("--json", "Output raw JSON for scripting").action(listAction);
1550
+ command.command("remove [agent]").description("Stop tracking a specific agent").option("-p, --project <id>", "Project id or slug (disambiguates agents with the same slug)").option("-f, --force", "Skip confirmation prompt").action(async (agentArg, options) => {
1551
+ let projects = await projectTracker.getTrackedProjects();
1552
+ if (projects.length === 0) {
1553
+ console.log(pc8.gray("No agents tracked. Run `porchestra explore` first."));
1554
+ return;
1555
+ }
1556
+ if (options.project) {
1557
+ projects = projects.filter(
1558
+ (project) => project.projectId === options.project || project.projectSlug === options.project
1559
+ );
1560
+ if (projects.length === 0) {
1561
+ console.log(pc8.red(`No tracked project found for "${options.project}"`));
1562
+ return;
1563
+ }
1564
+ }
1565
+ const candidates = projects.flatMap(
1566
+ (project) => project.agents.map((agent) => ({
1567
+ project,
1568
+ agent,
1569
+ value: `${project.projectId}:${agent.agentId}`
1570
+ }))
1571
+ );
1572
+ let filtered = candidates;
1573
+ if (agentArg) {
1574
+ filtered = candidates.filter(
1575
+ ({ agent }) => agent.agentId === agentArg || agent.agentSlug === agentArg
1576
+ );
1577
+ }
1578
+ if (filtered.length === 0) {
1579
+ const message = agentArg ? `No tracked agent found for "${agentArg}".` : "No tracked agents found.";
1580
+ console.log(pc8.red(message));
1581
+ return;
1582
+ }
1583
+ let target = filtered[0];
1584
+ if (filtered.length > 1) {
1585
+ const choice = await select2({
1586
+ message: "Select an agent to stop tracking:",
1587
+ choices: filtered.map(({ project, agent, value }) => ({
1588
+ name: `${agent.agentName} ${pc8.gray(`(${project.projectName}) ${agent.folderPath}`)}`,
1589
+ value
1590
+ }))
1591
+ });
1592
+ target = filtered.find((candidate) => candidate.value === choice);
1593
+ }
1594
+ if (!options.force) {
1595
+ const confirmed = await confirm4({
1596
+ message: `Remove ${target.agent.agentName} from ${target.project.projectName}?`,
1597
+ default: false
1598
+ });
1599
+ if (!confirmed) return;
1600
+ }
1601
+ const removed = await projectTracker.untrackAgent(
1602
+ target.project.projectId,
1603
+ target.agent.agentId
1604
+ );
1605
+ if (!removed) {
1606
+ console.log(pc8.red("Agent was not removed (not found)."));
1607
+ return;
1608
+ }
1609
+ const projectRemoved = target.project.agents.length === 1;
1610
+ console.log(pc8.green(`\u2713 Stopped tracking ${target.agent.agentName}`));
1611
+ console.log(pc8.gray(` Project: ${target.project.projectName}`));
1612
+ if (projectRemoved) {
1613
+ console.log(pc8.gray(" Project removed from tracking because it has no remaining agents."));
1614
+ }
1615
+ });
1616
+ return command;
1617
+ }
1618
+
1619
+ // src/index.ts
1620
+ var packageJson = { version: "1.0.0" };
1621
+ process.on("unhandledRejection", (error) => {
1622
+ console.error(pc9.red("\n\u2717 Unexpected error:"));
1623
+ console.error(error);
1624
+ process.exit(1);
1625
+ });
1626
+ process.on("uncaughtException", (error) => {
1627
+ console.error(pc9.red("\n\u2717 Fatal error:"));
1628
+ console.error(error);
1629
+ process.exit(1);
1630
+ });
1631
+ async function main() {
1632
+ const configManager = new ConfigManager();
1633
+ const authService = new AuthService(configManager);
1634
+ new TokenManager(configManager);
1635
+ const apiClient = new ApiClient(configManager);
1636
+ const codeGenerator = new CodeGenerator();
1637
+ await authService.checkAndRefreshTokenIfNeeded();
1638
+ const program = new Command9().name("porchestra").description("CLI for Porchestra - Generate LLM tool handlers").version(packageJson.version).configureOutput({
1639
+ writeErr: (str) => process.stderr.write(str),
1640
+ outputError: (str, write) => write(pc9.red(str))
1641
+ });
1642
+ program.option("--api-url <url>", "Override API URL").option("--config-dir <dir>", "Override config directory");
1643
+ program.addCommand(createLoginCommand(configManager, authService));
1644
+ program.addCommand(createLogoutCommand(configManager, authService));
1645
+ program.addCommand(createWhoamiCommand(configManager, authService));
1646
+ program.addCommand(createExploreCommand(configManager, apiClient));
1647
+ program.addCommand(createPullCommand(configManager, apiClient, codeGenerator));
1648
+ program.addCommand(createConfigCommand(configManager));
1649
+ program.addCommand(createStatusCommand(configManager));
1650
+ program.addCommand(createAgentsCommand(configManager));
1651
+ await program.parseAsync(process.argv);
1652
+ }
1653
+ main().catch((error) => {
1654
+ if (error instanceof PorchestraError) {
1655
+ console.error(pc9.red(`
1656
+ \u2717 ${error.message}`));
1657
+ if (process.env.DEBUG) {
1658
+ console.error(pc9.gray(error.stack));
1659
+ }
1660
+ } else {
1661
+ console.error(pc9.red(`
1662
+ \u2717 Unexpected error: ${error.message}`));
1663
+ }
1664
+ process.exit(1);
1665
+ });
1666
+ //# sourceMappingURL=index.js.map