@packmind/cli 0.3.4 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/main.cjs +1415 -93
  2. package/package.json +4 -1
package/main.cjs CHANGED
@@ -13,6 +13,9 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
13
13
  var __getOwnPropNames = Object.getOwnPropertyNames;
14
14
  var __getProtoOf = Object.getPrototypeOf;
15
15
  var __hasOwnProp = Object.prototype.hasOwnProperty;
16
+ var __commonJS = (cb, mod) => function __require() {
17
+ return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
18
+ };
16
19
  var __copyProps = (to, from, except, desc) => {
17
20
  if (from && typeof from === "object" || typeof from === "function") {
18
21
  for (let key of __getOwnPropNames(from))
@@ -30,6 +33,48 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
30
33
  mod
31
34
  ));
32
35
 
36
+ // apps/cli/package.json
37
+ var require_package = __commonJS({
38
+ "apps/cli/package.json"(exports2, module2) {
39
+ module2.exports = {
40
+ name: "@packmind/cli",
41
+ version: "0.4.0",
42
+ description: "A command-line interface for Packmind linting and code quality checks",
43
+ private: false,
44
+ bin: {
45
+ "packmind-cli": "./main.cjs"
46
+ },
47
+ main: "./main.cjs",
48
+ files: [
49
+ "main.cjs",
50
+ "*.wasm",
51
+ "stubs",
52
+ "CHANGELOG.MD"
53
+ ],
54
+ keywords: [
55
+ "packmind",
56
+ "linting",
57
+ "code-quality",
58
+ "cli",
59
+ "static-analysis"
60
+ ],
61
+ author: "Packmind",
62
+ license: "UNLICENSED",
63
+ repository: {
64
+ type: "git",
65
+ url: "git+https://github.com/PackmindHub/packmind.git",
66
+ directory: "apps/cli"
67
+ },
68
+ publishConfig: {
69
+ access: "public"
70
+ },
71
+ scripts: {
72
+ "packmind-cli": "ts-node --project tsconfig.app.json src/main.ts"
73
+ }
74
+ };
75
+ }
76
+ });
77
+
33
78
  // apps/cli/src/main.ts
34
79
  var import_chalk2 = __toESM(require("chalk"));
35
80
  var import_cmd_ts3 = require("cmd-ts");
@@ -183,6 +228,12 @@ var createRecipesDeploymentId = brandedIdFactory();
183
228
  // packages/types/src/deployments/StandardsDeploymentId.ts
184
229
  var createStandardsDeploymentId = brandedIdFactory();
185
230
 
231
+ // packages/types/src/deployments/PackagesDeploymentId.ts
232
+ var createPackagesDeploymentId = brandedIdFactory();
233
+
234
+ // packages/types/src/deployments/Package.ts
235
+ var createPackageId = brandedIdFactory();
236
+
186
237
  // packages/types/src/git/GitRepoId.ts
187
238
  var createGitRepoId = brandedIdFactory();
188
239
 
@@ -407,6 +458,48 @@ var createDetectionProgramId = brandedIdFactory();
407
458
  // packages/types/src/linter/RuleDetectionAssessment.ts
408
459
  var createRuleDetectionAssessmentId = brandedIdFactory();
409
460
 
461
+ // packages/types/src/sse/SSEEvent.ts
462
+ function createProgramStatusChangeEvent(ruleId, language) {
463
+ return {
464
+ type: "PROGRAM_STATUS_CHANGE",
465
+ data: { ruleId, language },
466
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
467
+ };
468
+ }
469
+ function createAssessmentStatusChangeEvent(ruleId, language) {
470
+ return {
471
+ type: "ASSESSMENT_STATUS_CHANGE",
472
+ data: {
473
+ ruleId,
474
+ language
475
+ },
476
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
477
+ };
478
+ }
479
+ function createDetectionHeuristicsUpdatedEvent(ruleId, language, detectionHeuristicsId) {
480
+ return {
481
+ type: "DETECTION_HEURISTICS_UPDATED",
482
+ data: {
483
+ ruleId,
484
+ language,
485
+ detectionHeuristicsId
486
+ },
487
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
488
+ };
489
+ }
490
+ function createUserContextChangeEvent(userId, organizationId, changeType, role) {
491
+ return {
492
+ type: "USER_CONTEXT_CHANGE",
493
+ data: {
494
+ userId,
495
+ organizationId,
496
+ changeType,
497
+ role
498
+ },
499
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
500
+ };
501
+ }
502
+
410
503
  // apps/cli/src/application/useCases/ExecuteSingleFileAstUseCase.ts
411
504
  var ExecuteSingleFileAstUseCase = class _ExecuteSingleFileAstUseCase {
412
505
  constructor(linterExecutionUseCase) {
@@ -447,8 +540,8 @@ var import_util = require("util");
447
540
  var execAsync = (0, import_util.promisify)(import_child_process.exec);
448
541
  var origin = "GitService";
449
542
  var GitService = class {
450
- constructor(logger = new PackmindLogger(origin)) {
451
- this.logger = logger;
543
+ constructor(logger2 = new PackmindLogger(origin)) {
544
+ this.logger = logger2;
452
545
  }
453
546
  async getGitRepositoryRoot(path5) {
454
547
  try {
@@ -488,7 +581,7 @@ ${error.message}`
488
581
  throw new Error("Failed to get Git branches: Unknown error");
489
582
  }
490
583
  }
491
- async getGitRemoteUrl(repoPath, origin5) {
584
+ async getGitRemoteUrl(repoPath, origin10) {
492
585
  try {
493
586
  const { stdout } = await execAsync("git remote -v", {
494
587
  cwd: repoPath
@@ -498,10 +591,10 @@ ${error.message}`
498
591
  throw new Error("No Git remotes found in the repository");
499
592
  }
500
593
  let selectedRemote;
501
- if (origin5) {
502
- const foundRemote = remotes.find((remote) => remote.name === origin5);
594
+ if (origin10) {
595
+ const foundRemote = remotes.find((remote) => remote.name === origin10);
503
596
  if (!foundRemote) {
504
- throw new Error(`Remote '${origin5}' not found in repository`);
597
+ throw new Error(`Remote '${origin10}' not found in repository`);
505
598
  }
506
599
  selectedRemote = foundRemote.url;
507
600
  } else if (remotes.length === 1) {
@@ -591,8 +684,8 @@ var GetGitRemoteUrlUseCase = class {
591
684
  this.gitRemoteUrlService = gitRemoteUrlService;
592
685
  }
593
686
  async execute(command3) {
594
- const { path: repoPath, origin: origin5 } = command3;
595
- return this.gitRemoteUrlService.getGitRemoteUrl(repoPath, origin5);
687
+ const { path: repoPath, origin: origin10 } = command3;
688
+ return this.gitRemoteUrlService.getGitRemoteUrl(repoPath, origin10);
596
689
  }
597
690
  };
598
691
 
@@ -720,10 +813,10 @@ var path2 = __toESM(require("path"));
720
813
  var fs2 = __toESM(require("fs/promises"));
721
814
  var origin2 = "LintFilesInDirectoryUseCase";
722
815
  var LintFilesInDirectoryUseCase = class {
723
- constructor(services, repositories, logger = new PackmindLogger(origin2)) {
816
+ constructor(services, repositories, logger2 = new PackmindLogger(origin2)) {
724
817
  this.services = services;
725
818
  this.repositories = repositories;
726
- this.logger = logger;
819
+ this.logger = logger2;
727
820
  }
728
821
  fileMatchesScope(filePath, scopePatterns) {
729
822
  if (!scopePatterns || scopePatterns.length === 0) {
@@ -1113,7 +1206,7 @@ function decodeApiKey(apiKey) {
1113
1206
  var PackmindGateway = class {
1114
1207
  constructor(apiKey) {
1115
1208
  this.apiKey = apiKey;
1116
- this.getPullData = async () => {
1209
+ this.getPullData = async (command3) => {
1117
1210
  const decodedApiKey = decodeApiKey(this.apiKey);
1118
1211
  if (!decodedApiKey.isValid) {
1119
1212
  throw new Error(`Invalid API key: ${decodedApiKey.error}`);
@@ -1124,7 +1217,13 @@ var PackmindGateway = class {
1124
1217
  throw new Error("Invalid JWT: missing organizationId");
1125
1218
  }
1126
1219
  const organizationId = jwtPayload.organization.id;
1127
- const url = `${host}/api/v0/organizations/${organizationId}/pull`;
1220
+ const queryParams = new URLSearchParams();
1221
+ if (command3.packagesSlugs && command3.packagesSlugs.length > 0) {
1222
+ command3.packagesSlugs.forEach((slug) => {
1223
+ queryParams.append("packageSlug", slug);
1224
+ });
1225
+ }
1226
+ const url = `${host}/api/v0/organizations/${organizationId}/pull?${queryParams.toString()}`;
1128
1227
  try {
1129
1228
  const response = await fetch(url, {
1130
1229
  method: "GET",
@@ -1142,7 +1241,9 @@ var PackmindGateway = class {
1142
1241
  }
1143
1242
  } catch {
1144
1243
  }
1145
- throw new Error(errorMsg);
1244
+ const error = new Error(errorMsg);
1245
+ error.statusCode = response.status;
1246
+ throw error;
1146
1247
  }
1147
1248
  const result = await response.json();
1148
1249
  return result;
@@ -1159,6 +1260,54 @@ var PackmindGateway = class {
1159
1260
  );
1160
1261
  }
1161
1262
  };
1263
+ this.listPackages = async () => {
1264
+ const decodedApiKey = decodeApiKey(this.apiKey);
1265
+ if (!decodedApiKey.isValid) {
1266
+ throw new Error(`Invalid API key: ${decodedApiKey.error}`);
1267
+ }
1268
+ const { host, jwt } = decodedApiKey.payload;
1269
+ const jwtPayload = decodeJwt(jwt);
1270
+ if (!jwtPayload?.organization?.id) {
1271
+ throw new Error("Invalid JWT: missing organizationId");
1272
+ }
1273
+ const organizationId = jwtPayload.organization.id;
1274
+ const url = `${host}/api/v0/organizations/${organizationId}/packages`;
1275
+ try {
1276
+ const response = await fetch(url, {
1277
+ method: "GET",
1278
+ headers: {
1279
+ "Content-Type": "application/json",
1280
+ Authorization: `Bearer ${this.apiKey}`
1281
+ }
1282
+ });
1283
+ if (!response.ok) {
1284
+ let errorMsg = `API request failed: ${response.status} ${response.statusText}`;
1285
+ try {
1286
+ const errorBody = await response.json();
1287
+ if (errorBody && errorBody.message) {
1288
+ errorMsg = `${errorBody.message}`;
1289
+ }
1290
+ } catch {
1291
+ }
1292
+ const error = new Error(errorMsg);
1293
+ error.statusCode = response.status;
1294
+ throw error;
1295
+ }
1296
+ const result = await response.json();
1297
+ return result.packages;
1298
+ } catch (error) {
1299
+ const err = error;
1300
+ const code = err?.code || err?.cause?.code;
1301
+ if (code === "ECONNREFUSED" || code === "ENOTFOUND" || err?.name === "FetchError" || typeof err?.message === "string" && (err.message.includes("Failed to fetch") || err.message.includes("network") || err.message.includes("NetworkError"))) {
1302
+ throw new Error(
1303
+ `Packmind server is not accessible at ${host}. Please check your network connection or the server URL.`
1304
+ );
1305
+ }
1306
+ throw new Error(
1307
+ `Failed to list packages: Error: ${err?.message || JSON.stringify(error)}`
1308
+ );
1309
+ }
1310
+ };
1162
1311
  this.listExecutionPrograms = async (params) => {
1163
1312
  const decodedApiKey = decodeApiKey(this.apiKey);
1164
1313
  if (!decodedApiKey.isValid) {
@@ -1337,6 +1486,56 @@ var PackmindGateway = class {
1337
1486
  );
1338
1487
  }
1339
1488
  };
1489
+ this.getPackageSummary = async ({
1490
+ slug
1491
+ }) => {
1492
+ const decodedApiKey = decodeApiKey(this.apiKey);
1493
+ if (!decodedApiKey.isValid) {
1494
+ throw new Error(`Invalid API key: ${decodedApiKey.error}`);
1495
+ }
1496
+ const { host, jwt } = decodedApiKey.payload;
1497
+ const jwtPayload = decodeJwt(jwt);
1498
+ if (!jwtPayload?.organization?.id) {
1499
+ throw new Error("Invalid JWT: missing organizationId");
1500
+ }
1501
+ const organizationId = jwtPayload.organization.id;
1502
+ const url = `${host}/api/v0/organizations/${organizationId}/packages/${encodeURIComponent(slug)}`;
1503
+ try {
1504
+ const response = await fetch(url, {
1505
+ method: "GET",
1506
+ headers: {
1507
+ "Content-Type": "application/json",
1508
+ Authorization: `Bearer ${this.apiKey}`
1509
+ }
1510
+ });
1511
+ if (!response.ok) {
1512
+ let errorMsg = `API request failed: ${response.status} ${response.statusText}`;
1513
+ try {
1514
+ const errorBody = await response.json();
1515
+ if (errorBody && errorBody.message) {
1516
+ errorMsg = `${errorBody.message}`;
1517
+ }
1518
+ } catch {
1519
+ }
1520
+ const error = new Error(errorMsg);
1521
+ error.statusCode = response.status;
1522
+ throw error;
1523
+ }
1524
+ const result = await response.json();
1525
+ return result;
1526
+ } catch (error) {
1527
+ const err = error;
1528
+ const code = err?.code || err?.cause?.code;
1529
+ if (code === "ECONNREFUSED" || code === "ENOTFOUND" || err?.name === "FetchError" || typeof err?.message === "string" && (err.message.includes("Failed to fetch") || err.message.includes("network") || err.message.includes("NetworkError"))) {
1530
+ throw new Error(
1531
+ `Packmind server is not accessible at ${host}. Please check your network connection or the server URL.`
1532
+ );
1533
+ }
1534
+ throw new Error(
1535
+ `Failed to get package '${slug}': Error: ${err?.message || JSON.stringify(error)}`
1536
+ );
1537
+ }
1538
+ };
1340
1539
  }
1341
1540
  };
1342
1541
 
@@ -3011,9 +3210,9 @@ var TreeSitter17 = __toESM(require("web-tree-sitter"));
3011
3210
  // packages/linter-execution/src/application/useCases/ExecuteLinterProgramsUseCase.ts
3012
3211
  var origin3 = "ExecuteLinterProgramsUseCase";
3013
3212
  var ExecuteLinterProgramsUseCase = class {
3014
- constructor(linterAstAdapter = new LinterAstAdapter(), logger = new PackmindLogger(origin3)) {
3213
+ constructor(linterAstAdapter = new LinterAstAdapter(), logger2 = new PackmindLogger(origin3)) {
3015
3214
  this.linterAstAdapter = linterAstAdapter;
3016
- this.logger = logger;
3215
+ this.logger = logger2;
3017
3216
  }
3018
3217
  async execute(command3) {
3019
3218
  const { filePath, fileContent, language, programs } = command3;
@@ -3206,90 +3405,1078 @@ var ExecuteLinterProgramsUseCase = class {
3206
3405
  }
3207
3406
  };
3208
3407
 
3209
- // apps/cli/src/application/useCases/PullDataUseCase.ts
3210
- var fs3 = __toESM(require("fs/promises"));
3211
- var path3 = __toESM(require("path"));
3212
- var PullDataUseCase = class {
3213
- constructor(packmindGateway) {
3214
- this.packmindGateway = packmindGateway;
3408
+ // packages/node-utils/src/ai/prompts/OpenAIService.ts
3409
+ var import_openai = __toESM(require("openai"));
3410
+
3411
+ // packages/node-utils/src/config/infra/Infisical/InfisicalConfig.ts
3412
+ var import_sdk = require("@infisical/sdk");
3413
+ var origin4 = "InfisicalConfig";
3414
+ var InfisicalConfig = class {
3415
+ constructor(clientId, clientSecret, env, projectId, logger2 = new PackmindLogger(origin4)) {
3416
+ this.clientId = clientId;
3417
+ this.clientSecret = clientSecret;
3418
+ this.env = env;
3419
+ this.projectId = projectId;
3420
+ this.logger = logger2;
3421
+ this.logger.info("Initializing InfisicalConfig", { env, projectId });
3422
+ try {
3423
+ this.client = new import_sdk.InfisicalSDK({
3424
+ siteUrl: "https://eu.infisical.com"
3425
+ // Optional, defaults to https://app.infisical.com
3426
+ });
3427
+ this.logger.info("InfisicalSDK client created successfully", {
3428
+ siteUrl: "https://eu.infisical.com"
3429
+ });
3430
+ } catch (error) {
3431
+ this.logger.error("Failed to create InfisicalSDK client", {
3432
+ error: error instanceof Error ? error.message : String(error)
3433
+ });
3434
+ throw error;
3435
+ }
3215
3436
  }
3216
- async execute(command3) {
3217
- const baseDirectory = command3.baseDirectory || process.cwd();
3218
- const result = {
3219
- filesCreated: 0,
3220
- filesUpdated: 0,
3221
- filesDeleted: 0,
3222
- errors: []
3223
- };
3437
+ async initClient() {
3438
+ this.logger.info("Initializing Infisical client authentication");
3224
3439
  try {
3225
- const response = await this.packmindGateway.getPullData({});
3226
- for (const file of response.fileUpdates.createOrUpdate) {
3440
+ this.logger.debug("Authenticating with Infisical using universal auth");
3441
+ await this.client.auth().universalAuth.login({
3442
+ clientId: this.clientId,
3443
+ clientSecret: this.clientSecret
3444
+ });
3445
+ this.logger.info("Infisical client authenticated successfully");
3446
+ } catch (error) {
3447
+ this.logger.error("Failed to authenticate Infisical client", {
3448
+ error: error instanceof Error ? error.message : String(error)
3449
+ });
3450
+ throw error;
3451
+ }
3452
+ }
3453
+ async getValue(secretName) {
3454
+ this.logger.info("Retrieving secret from Infisical", {
3455
+ secretName,
3456
+ env: this.env,
3457
+ projectId: this.projectId
3458
+ });
3459
+ try {
3460
+ this.logger.debug("Fetching secret from Infisical API", { secretName });
3461
+ const nameSecret = await this.client.secrets().getSecret({
3462
+ projectId: this.projectId,
3463
+ environment: this.env,
3464
+ secretName
3465
+ });
3466
+ if (nameSecret?.secretValue) {
3467
+ this.logger.info("Secret retrieved from Infisical successfully", {
3468
+ secretName
3469
+ });
3470
+ return nameSecret.secretValue;
3471
+ } else {
3472
+ this.logger.warn("Secret not found or has no value in Infisical", {
3473
+ secretName
3474
+ });
3475
+ return null;
3476
+ }
3477
+ } catch (error) {
3478
+ this.logger.warn("Failed to retrieve secret from Infisical", {
3479
+ secretName,
3480
+ env: this.env,
3481
+ projectId: this.projectId,
3482
+ error: error instanceof Error ? error.message : String(error)
3483
+ });
3484
+ throw error;
3485
+ }
3486
+ }
3487
+ };
3488
+
3489
+ // packages/node-utils/src/config/config/Configuration.ts
3490
+ var origin5 = "Configuration";
3491
+ var Configuration = class _Configuration {
3492
+ constructor() {
3493
+ this.initialized = false;
3494
+ this.initializationPromise = null;
3495
+ }
3496
+ static {
3497
+ this.logger = new PackmindLogger(
3498
+ origin5,
3499
+ "info" /* INFO */
3500
+ );
3501
+ }
3502
+ static getInstance(logger2) {
3503
+ if (logger2) {
3504
+ _Configuration.logger = logger2;
3505
+ }
3506
+ _Configuration.logger.debug("Getting Configuration instance");
3507
+ if (!_Configuration.instance) {
3508
+ _Configuration.logger.info("Creating new Configuration instance");
3509
+ _Configuration.instance = new _Configuration();
3510
+ }
3511
+ return _Configuration.instance;
3512
+ }
3513
+ async initialize(env) {
3514
+ if (this.initialized) {
3515
+ _Configuration.logger.debug("Configuration already initialized, skipping");
3516
+ return;
3517
+ }
3518
+ if (this.initializationPromise) {
3519
+ _Configuration.logger.debug(
3520
+ "Configuration initialization already in progress, waiting for completion"
3521
+ );
3522
+ await this.initializationPromise;
3523
+ return;
3524
+ }
3525
+ this.initializationPromise = this.performInitialization(env);
3526
+ try {
3527
+ await this.initializationPromise;
3528
+ } finally {
3529
+ this.initializationPromise = null;
3530
+ }
3531
+ }
3532
+ async performInitialization(env) {
3533
+ _Configuration.logger.info("Initializing Configuration");
3534
+ const configurationMode = env["CONFIGURATION"]?.toLowerCase();
3535
+ _Configuration.logger.debug("Configuration mode detected", {
3536
+ mode: configurationMode
3537
+ });
3538
+ if (configurationMode === "infisical") {
3539
+ _Configuration.logger.info("Initializing Infisical configuration");
3540
+ const clientId = env["INFISICAL_CLIENT_ID"];
3541
+ const clientSecret = env["INFISICAL_CLIENT_SECRET"];
3542
+ const infisicalEnv = env["INFISICAL_ENV"];
3543
+ const projectId = env["INFISICAL_PROJECT_ID"];
3544
+ if (!clientId || !clientSecret || !infisicalEnv || !projectId) {
3545
+ _Configuration.logger.error(
3546
+ "Infisical configuration is incomplete, falling back to environment variables only",
3547
+ {
3548
+ hasClientId: !!clientId,
3549
+ hasClientSecret: !!clientSecret,
3550
+ hasInfisicalEnv: !!infisicalEnv,
3551
+ hasProjectId: !!projectId
3552
+ }
3553
+ );
3554
+ this.infisicalConfig = void 0;
3555
+ } else {
3227
3556
  try {
3228
- await this.createOrUpdateFile(
3229
- baseDirectory,
3230
- file.path,
3231
- file.content,
3232
- result
3557
+ this.infisicalConfig = new InfisicalConfig(
3558
+ clientId,
3559
+ clientSecret,
3560
+ infisicalEnv,
3561
+ projectId
3562
+ );
3563
+ _Configuration.logger.debug("Initializing Infisical client");
3564
+ await this.infisicalConfig.initClient();
3565
+ _Configuration.logger.info(
3566
+ "Infisical configuration initialized successfully"
3233
3567
  );
3234
3568
  } catch (error) {
3235
- const errorMsg = error instanceof Error ? error.message : String(error);
3236
- result.errors.push(
3237
- `Failed to create/update ${file.path}: ${errorMsg}`
3569
+ _Configuration.logger.error(
3570
+ "Failed to initialize Infisical, falling back to environment variables only",
3571
+ {
3572
+ error: error instanceof Error ? error.message : String(error)
3573
+ }
3238
3574
  );
3575
+ this.infisicalConfig = void 0;
3239
3576
  }
3240
3577
  }
3241
- for (const file of response.fileUpdates.delete) {
3578
+ } else {
3579
+ _Configuration.logger.info(
3580
+ "Using environment variables only (no Infisical)"
3581
+ );
3582
+ }
3583
+ this.initialized = true;
3584
+ _Configuration.logger.info("Configuration initialization completed");
3585
+ }
3586
+ static async getConfigWithDefault(key, defaultValue) {
3587
+ const value = await _Configuration.getConfig(key);
3588
+ return value ?? defaultValue;
3589
+ }
3590
+ static async getConfig(key, env = process.env, logger2) {
3591
+ if (logger2) {
3592
+ _Configuration.logger = logger2;
3593
+ }
3594
+ _Configuration.logger.info("Getting configuration value", { key });
3595
+ try {
3596
+ const instance = _Configuration.getInstance();
3597
+ await instance.initialize(env);
3598
+ const envValue = env[key];
3599
+ if (envValue) {
3600
+ _Configuration.logger.debug(
3601
+ "Configuration value found in environment variables",
3602
+ { key }
3603
+ );
3604
+ return envValue;
3605
+ }
3606
+ if (instance.infisicalConfig) {
3607
+ _Configuration.logger.debug(
3608
+ "Checking Infisical for configuration value",
3609
+ { key }
3610
+ );
3242
3611
  try {
3243
- await this.deleteFile(baseDirectory, file.path, result);
3612
+ const infisicalValue = await instance.infisicalConfig.getValue(key);
3613
+ if (infisicalValue) {
3614
+ _Configuration.logger.debug(
3615
+ "Configuration value found in Infisical",
3616
+ {
3617
+ key
3618
+ }
3619
+ );
3620
+ return infisicalValue;
3621
+ }
3244
3622
  } catch (error) {
3245
- const errorMsg = error instanceof Error ? error.message : String(error);
3246
- result.errors.push(`Failed to delete ${file.path}: ${errorMsg}`);
3623
+ _Configuration.logger.error(
3624
+ "Failed to retrieve value from Infisical, value not available",
3625
+ {
3626
+ key,
3627
+ error: error instanceof Error ? error.message : String(error)
3628
+ }
3629
+ );
3247
3630
  }
3248
3631
  }
3632
+ _Configuration.logger.warn("Configuration value not found", { key });
3633
+ return null;
3249
3634
  } catch (error) {
3250
- const errorMsg = error instanceof Error ? error.message : String(error);
3251
- result.errors.push(`Failed to pull data: ${errorMsg}`);
3635
+ _Configuration.logger.warn("Failed to get configuration value", {
3636
+ key,
3637
+ error: error instanceof Error ? error.message : String(error)
3638
+ });
3639
+ return null;
3252
3640
  }
3253
- return result;
3254
3641
  }
3255
- async createOrUpdateFile(baseDirectory, filePath, content, result) {
3256
- const fullPath = path3.join(baseDirectory, filePath);
3257
- const directory = path3.dirname(fullPath);
3258
- await fs3.mkdir(directory, { recursive: true });
3259
- const fileExists = await this.fileExists(fullPath);
3260
- if (fileExists) {
3261
- await fs3.appendFile(fullPath, content, "utf-8");
3262
- result.filesUpdated++;
3263
- } else {
3264
- await fs3.writeFile(fullPath, content, "utf-8");
3265
- result.filesCreated++;
3266
- }
3642
+ };
3643
+
3644
+ // packages/node-utils/src/cache/Cache.ts
3645
+ var import_ioredis = __toESM(require("ioredis"));
3646
+ var origin6 = "Cache";
3647
+ var Cache = class _Cache {
3648
+ constructor() {
3649
+ this.initialized = false;
3650
+ this.connectionConfig = {
3651
+ host: "redis",
3652
+ port: 6379,
3653
+ maxRetriesPerRequest: 3
3654
+ };
3267
3655
  }
3268
- async deleteFile(baseDirectory, filePath, result) {
3269
- const fullPath = path3.join(baseDirectory, filePath);
3270
- const fileExists = await this.fileExists(fullPath);
3271
- if (fileExists) {
3272
- await fs3.unlink(fullPath);
3273
- result.filesDeleted++;
3656
+ static {
3657
+ this.logger = new PackmindLogger(
3658
+ origin6,
3659
+ "info" /* INFO */
3660
+ );
3661
+ }
3662
+ static {
3663
+ // Default cache expiration time in seconds (5 minutes)
3664
+ this.DEFAULT_EXPIRATION_SECONDS = 300;
3665
+ }
3666
+ /**
3667
+ * Get the singleton instance of Cache
3668
+ */
3669
+ static getInstance() {
3670
+ _Cache.logger.debug("Getting Cache instance");
3671
+ if (!_Cache.instance) {
3672
+ _Cache.logger.info("Creating new Cache instance");
3673
+ _Cache.instance = new _Cache();
3274
3674
  }
3675
+ return _Cache.instance;
3275
3676
  }
3276
- async fileExists(filePath) {
3677
+ /**
3678
+ * Initialize the Redis client with configuration
3679
+ * This should be called during application startup
3680
+ */
3681
+ async initialize() {
3682
+ if (this.initialized) {
3683
+ return;
3684
+ }
3685
+ _Cache.logger.info("Initializing Redis cache client");
3277
3686
  try {
3278
- await fs3.access(filePath);
3279
- return true;
3280
- } catch {
3281
- return false;
3687
+ const redisUri = await Configuration.getConfig("REDIS_URI");
3688
+ if (!redisUri) {
3689
+ throw new Error("REDIS_URI configuration is required");
3690
+ }
3691
+ _Cache.logger.info("Using REDIS_URI configuration");
3692
+ this.client = new import_ioredis.default(redisUri, {
3693
+ maxRetriesPerRequest: 3
3694
+ });
3695
+ this.client.on("error", (error) => {
3696
+ _Cache.logger.error("Redis cache client error", {
3697
+ error: error instanceof Error ? error.message : String(error)
3698
+ });
3699
+ });
3700
+ this.client.on("connect", () => {
3701
+ _Cache.logger.info("Redis cache client connected successfully");
3702
+ });
3703
+ this.initialized = true;
3704
+ _Cache.logger.info("Cache initialization completed");
3705
+ } catch (error) {
3706
+ _Cache.logger.error("Failed to initialize cache", {
3707
+ error: error instanceof Error ? error.message : String(error)
3708
+ });
3709
+ throw new Error(
3710
+ `Cache initialization failed: ${error instanceof Error ? error.message : String(error)}`
3711
+ );
3282
3712
  }
3283
3713
  }
3284
- };
3285
-
3286
- // apps/cli/src/PackmindCliHexaFactory.ts
3287
- var PackmindCliHexaFactory = class {
3288
- constructor(logger) {
3289
- this.logger = logger;
3290
- this.repositories = {
3291
- packmindGateway: new PackmindGateway(
3292
- process.env.PACKMIND_API_KEY_V3 || ""
3714
+ /**
3715
+ * Set a value in the cache with optional expiration
3716
+ * @param key - The cache key
3717
+ * @param value - The value to cache (will be JSON serialized)
3718
+ * @param expirationSeconds - Expiration time in seconds (default: 300s)
3719
+ */
3720
+ async set(key, value, expirationSeconds = _Cache.DEFAULT_EXPIRATION_SECONDS) {
3721
+ if (!this.initialized || !this.client) {
3722
+ _Cache.logger.warn("Cache not initialized, skipping set operation", {
3723
+ key
3724
+ });
3725
+ return;
3726
+ }
3727
+ try {
3728
+ const serializedValue = JSON.stringify(value);
3729
+ await this.client.setex(key, expirationSeconds, serializedValue);
3730
+ } catch (error) {
3731
+ _Cache.logger.warn(
3732
+ "Failed to set cache value, continuing without caching",
3733
+ {
3734
+ key,
3735
+ error: error instanceof Error ? error.message : String(error)
3736
+ }
3737
+ );
3738
+ }
3739
+ }
3740
+ /**
3741
+ * Get a value from the cache
3742
+ * @param key - The cache key
3743
+ * @returns The cached value or null if not found, expired, or error occurred
3744
+ */
3745
+ async get(key) {
3746
+ if (!this.initialized || !this.client) {
3747
+ _Cache.logger.warn(
3748
+ "Cache not initialized, returning null for get operation",
3749
+ { key }
3750
+ );
3751
+ return null;
3752
+ }
3753
+ try {
3754
+ const serializedValue = await this.client.get(key);
3755
+ if (serializedValue === null) {
3756
+ return null;
3757
+ }
3758
+ const value = JSON.parse(serializedValue);
3759
+ return value;
3760
+ } catch (error) {
3761
+ _Cache.logger.warn("Failed to get cache value, returning null", {
3762
+ key,
3763
+ error: error instanceof Error ? error.message : String(error)
3764
+ });
3765
+ return null;
3766
+ }
3767
+ }
3768
+ /**
3769
+ * Invalidate (delete) a cache entry
3770
+ * @param key - The cache key to invalidate
3771
+ */
3772
+ async invalidate(key) {
3773
+ if (!this.initialized || !this.client) {
3774
+ _Cache.logger.warn(
3775
+ "Cache not initialized, skipping invalidate operation",
3776
+ { key }
3777
+ );
3778
+ return;
3779
+ }
3780
+ try {
3781
+ await this.client.del(key);
3782
+ } catch (error) {
3783
+ _Cache.logger.warn(
3784
+ "Failed to invalidate cache key, continuing without invalidation",
3785
+ {
3786
+ key,
3787
+ error: error instanceof Error ? error.message : String(error)
3788
+ }
3789
+ );
3790
+ }
3791
+ }
3792
+ /**
3793
+ * Disconnect the Redis client (for cleanup during shutdown)
3794
+ */
3795
+ async disconnect() {
3796
+ _Cache.logger.info("Disconnecting cache client");
3797
+ if (this.client) {
3798
+ try {
3799
+ await this.client.disconnect();
3800
+ _Cache.logger.info("Cache client disconnected successfully");
3801
+ } catch (error) {
3802
+ _Cache.logger.error("Error disconnecting cache client", {
3803
+ error: error instanceof Error ? error.message : String(error)
3804
+ });
3805
+ }
3806
+ }
3807
+ this.initialized = false;
3808
+ }
3809
+ /**
3810
+ * Get cache statistics (for monitoring/debugging)
3811
+ */
3812
+ async getStats() {
3813
+ return {
3814
+ connected: this.client?.status === "ready",
3815
+ initialized: this.initialized
3816
+ };
3817
+ }
3818
+ };
3819
+
3820
+ // packages/node-utils/src/jobs/infra/bullMQ/WorkerQueue.ts
3821
+ var import_bullmq2 = require("bullmq");
3822
+
3823
+ // packages/node-utils/src/jobs/infra/bullMQ/AbstractQueue.ts
3824
+ var import_bullmq = require("bullmq");
3825
+
3826
+ // packages/node-utils/src/jobs/infra/DelayedJobsFactory.ts
3827
+ var logger = new PackmindLogger("DelayedJobsFactory");
3828
+
3829
+ // packages/node-utils/src/mail/SmtpMailService.ts
3830
+ var import_nodemailer = __toESM(require("nodemailer"));
3831
+
3832
+ // packages/node-utils/src/nest/Public.ts
3833
+ var import_common = require("@nestjs/common");
3834
+
3835
+ // packages/node-utils/src/repositories/AbstractRepository.ts
3836
+ var import_common2 = require("@nestjs/common");
3837
+
3838
+ // packages/node-utils/src/sse/RedisSSEClient.ts
3839
+ var import_ioredis2 = __toESM(require("ioredis"));
3840
+ var origin7 = "RedisSSEClient";
3841
+ var RedisSSEClient = class _RedisSSEClient {
3842
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
3843
+ constructor() {
3844
+ this.initialized = false;
3845
+ }
3846
+ static {
3847
+ this.logger = new PackmindLogger(origin7);
3848
+ }
3849
+ static getInstance() {
3850
+ _RedisSSEClient.logger.debug("Getting RedisSSEClient instance");
3851
+ if (!_RedisSSEClient.instance) {
3852
+ _RedisSSEClient.logger.info("Creating new RedisSSEClient instance");
3853
+ _RedisSSEClient.instance = new _RedisSSEClient();
3854
+ }
3855
+ return _RedisSSEClient.instance;
3856
+ }
3857
+ /**
3858
+ * Initialize Redis clients using the same configuration as BullMQ
3859
+ */
3860
+ async initialize() {
3861
+ if (this.initialized) return;
3862
+ _RedisSSEClient.logger.info("Initializing Redis SSE clients");
3863
+ try {
3864
+ const redisURI = await Configuration.getConfig("REDIS_URI") || "redis";
3865
+ this.publisherClient = new import_ioredis2.default(redisURI);
3866
+ this.subscriberClient = new import_ioredis2.default(redisURI);
3867
+ this.publisherClient.on("error", (error) => {
3868
+ _RedisSSEClient.logger.error("Redis publisher client error", {
3869
+ error: error.message
3870
+ });
3871
+ });
3872
+ this.subscriberClient.on("error", (error) => {
3873
+ _RedisSSEClient.logger.error("Redis subscriber client error", {
3874
+ error: error.message
3875
+ });
3876
+ });
3877
+ await this.publisherClient.ping();
3878
+ await this.subscriberClient.ping();
3879
+ this.initialized = true;
3880
+ _RedisSSEClient.logger.info("Redis SSE clients initialized successfully");
3881
+ } catch (error) {
3882
+ _RedisSSEClient.logger.error("Failed to initialize Redis SSE clients", {
3883
+ error: error instanceof Error ? error.message : String(error)
3884
+ });
3885
+ throw error;
3886
+ }
3887
+ }
3888
+ /**
3889
+ * Publish a message to a Redis channel
3890
+ */
3891
+ async publish(channel, message) {
3892
+ await this.initialize();
3893
+ if (!this.publisherClient) {
3894
+ throw new Error("Publisher client not initialized");
3895
+ }
3896
+ _RedisSSEClient.logger.debug("Publishing message to Redis channel", {
3897
+ channel,
3898
+ messageLength: message.length
3899
+ });
3900
+ try {
3901
+ const subscriberCount = await this.publisherClient.publish(
3902
+ channel,
3903
+ message
3904
+ );
3905
+ _RedisSSEClient.logger.debug("Message published successfully", {
3906
+ channel,
3907
+ subscriberCount
3908
+ });
3909
+ return subscriberCount;
3910
+ } catch (error) {
3911
+ _RedisSSEClient.logger.error("Failed to publish message", {
3912
+ channel,
3913
+ error: error instanceof Error ? error.message : String(error)
3914
+ });
3915
+ throw error;
3916
+ }
3917
+ }
3918
+ /**
3919
+ * Subscribe to a Redis channel
3920
+ */
3921
+ async subscribe(channel, callback) {
3922
+ await this.initialize();
3923
+ if (!this.subscriberClient) {
3924
+ throw new Error("Subscriber client not initialized");
3925
+ }
3926
+ _RedisSSEClient.logger.info("Subscribing to Redis channel", { channel });
3927
+ try {
3928
+ this.subscriberClient.on("message", (receivedChannel, message) => {
3929
+ if (receivedChannel === channel) {
3930
+ _RedisSSEClient.logger.debug("Received message from Redis channel", {
3931
+ channel: receivedChannel,
3932
+ messageLength: message.length
3933
+ });
3934
+ callback(message);
3935
+ }
3936
+ });
3937
+ await this.subscriberClient.subscribe(channel);
3938
+ _RedisSSEClient.logger.info("Successfully subscribed to Redis channel", {
3939
+ channel
3940
+ });
3941
+ } catch (error) {
3942
+ _RedisSSEClient.logger.error("Failed to subscribe to Redis channel", {
3943
+ channel,
3944
+ error: error instanceof Error ? error.message : String(error)
3945
+ });
3946
+ throw error;
3947
+ }
3948
+ }
3949
+ /**
3950
+ * Unsubscribe from a Redis channel
3951
+ */
3952
+ async unsubscribe(channel) {
3953
+ if (!this.subscriberClient) {
3954
+ return;
3955
+ }
3956
+ _RedisSSEClient.logger.info("Unsubscribing from Redis channel", { channel });
3957
+ try {
3958
+ await this.subscriberClient.unsubscribe(channel);
3959
+ _RedisSSEClient.logger.info(
3960
+ "Successfully unsubscribed from Redis channel",
3961
+ { channel }
3962
+ );
3963
+ } catch (error) {
3964
+ _RedisSSEClient.logger.error("Failed to unsubscribe from Redis channel", {
3965
+ channel,
3966
+ error: error instanceof Error ? error.message : String(error)
3967
+ });
3968
+ }
3969
+ }
3970
+ /**
3971
+ * Clean up Redis connections
3972
+ */
3973
+ async disconnect() {
3974
+ _RedisSSEClient.logger.info("Disconnecting Redis SSE clients");
3975
+ try {
3976
+ if (this.publisherClient) {
3977
+ this.publisherClient.disconnect();
3978
+ }
3979
+ if (this.subscriberClient) {
3980
+ this.subscriberClient.disconnect();
3981
+ }
3982
+ this.initialized = false;
3983
+ _RedisSSEClient.logger.info("Redis SSE clients disconnected successfully");
3984
+ } catch (error) {
3985
+ _RedisSSEClient.logger.error("Error disconnecting Redis SSE clients", {
3986
+ error: error instanceof Error ? error.message : String(error)
3987
+ });
3988
+ }
3989
+ }
3990
+ /**
3991
+ * Get connection status
3992
+ */
3993
+ isConnected() {
3994
+ return this.initialized && this.publisherClient?.status === "ready" && this.subscriberClient?.status === "ready";
3995
+ }
3996
+ };
3997
+
3998
+ // packages/node-utils/src/sse/types.ts
3999
+ var SSE_REDIS_CHANNELS = {
4000
+ /**
4001
+ * Channel for subscription management messages
4002
+ * Used when clients subscribe/unsubscribe to specific event types
4003
+ */
4004
+ SUBSCRIPTIONS: "sse:subscriptions",
4005
+ /**
4006
+ * Channel for event notifications
4007
+ * Used to broadcast SSE events to all API instances
4008
+ */
4009
+ EVENTS: "sse:events"
4010
+ };
4011
+ function createSSEEventMessage(eventType, params, data, targetUserIds) {
4012
+ return {
4013
+ eventType,
4014
+ params,
4015
+ data,
4016
+ targetUserIds,
4017
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
4018
+ };
4019
+ }
4020
+ function serializeSSERedisMessage(message) {
4021
+ try {
4022
+ return JSON.stringify(message);
4023
+ } catch (error) {
4024
+ throw new Error(
4025
+ `Failed to serialize SSE Redis message: ${error instanceof Error ? error.message : String(error)}`
4026
+ );
4027
+ }
4028
+ }
4029
+
4030
+ // packages/node-utils/src/sse/SSEEventPublisher.ts
4031
+ var origin8 = "SSEEventPublisher";
4032
+ var SSEEventPublisher = class _SSEEventPublisher {
4033
+ static {
4034
+ this.logger = new PackmindLogger(origin8);
4035
+ }
4036
+ /**
4037
+ * Get the singleton instance
4038
+ */
4039
+ static getInstance() {
4040
+ _SSEEventPublisher.logger.debug("Getting SSEEventPublisher instance");
4041
+ if (!_SSEEventPublisher.instance) {
4042
+ _SSEEventPublisher.logger.info("Creating new SSEEventPublisher instance");
4043
+ _SSEEventPublisher.instance = new _SSEEventPublisher();
4044
+ _SSEEventPublisher.redisClient = RedisSSEClient.getInstance();
4045
+ }
4046
+ return _SSEEventPublisher.instance;
4047
+ }
4048
+ constructor() {
4049
+ }
4050
+ /**
4051
+ * Publish a program status change event for cache invalidation
4052
+ * This triggers React Query to refetch the program data
4053
+ */
4054
+ static async publishProgramStatusEvent(programId, ruleId, language, userId, organizationId) {
4055
+ _SSEEventPublisher.logger.info("Publishing program status change event", {
4056
+ programId,
4057
+ ruleId,
4058
+ language,
4059
+ userId,
4060
+ organizationId
4061
+ });
4062
+ try {
4063
+ const event = createProgramStatusChangeEvent(ruleId, language);
4064
+ await _SSEEventPublisher.publishEvent(
4065
+ "program_status_change",
4066
+ [ruleId, language],
4067
+ event,
4068
+ userId ? [userId] : void 0
4069
+ );
4070
+ _SSEEventPublisher.logger.debug(
4071
+ "Successfully published program status change event",
4072
+ { programId, ruleId, language }
4073
+ );
4074
+ } catch (error) {
4075
+ _SSEEventPublisher.logger.error(
4076
+ "Failed to publish program status change event",
4077
+ {
4078
+ programId,
4079
+ userId,
4080
+ error: error instanceof Error ? error.message : String(error)
4081
+ }
4082
+ );
4083
+ throw error;
4084
+ }
4085
+ }
4086
+ /**
4087
+ * Publish an assessment status change event for cache invalidation
4088
+ * This triggers React Query to refetch the assessment data
4089
+ */
4090
+ static async publishAssessmentStatusEvent(ruleId, language, userId, organizationId) {
4091
+ _SSEEventPublisher.logger.info("Publishing assessment status change event", {
4092
+ ruleId,
4093
+ language,
4094
+ userId,
4095
+ organizationId
4096
+ });
4097
+ try {
4098
+ const event = createAssessmentStatusChangeEvent(ruleId, language);
4099
+ await _SSEEventPublisher.publishEvent(
4100
+ "assessment_status_change",
4101
+ [ruleId, language],
4102
+ event,
4103
+ userId ? [userId] : void 0
4104
+ );
4105
+ _SSEEventPublisher.logger.debug(
4106
+ "Successfully published assessment status change event",
4107
+ { ruleId, language }
4108
+ );
4109
+ } catch (error) {
4110
+ _SSEEventPublisher.logger.error(
4111
+ "Failed to publish assessment status change event",
4112
+ {
4113
+ userId,
4114
+ error: error instanceof Error ? error.message : String(error)
4115
+ }
4116
+ );
4117
+ throw error;
4118
+ }
4119
+ }
4120
+ /**
4121
+ * Publish a detection heuristics updated event for cache invalidation
4122
+ * This triggers React Query to refetch the heuristics data
4123
+ */
4124
+ static async publishDetectionHeuristicsUpdatedEvent(ruleId, language, detectionHeuristicsId, userId, organizationId) {
4125
+ _SSEEventPublisher.logger.info(
4126
+ "Publishing detection heuristics updated event",
4127
+ {
4128
+ ruleId,
4129
+ language,
4130
+ detectionHeuristicsId,
4131
+ userId,
4132
+ organizationId
4133
+ }
4134
+ );
4135
+ try {
4136
+ const event = createDetectionHeuristicsUpdatedEvent(
4137
+ ruleId,
4138
+ language,
4139
+ detectionHeuristicsId
4140
+ );
4141
+ await _SSEEventPublisher.publishEvent(
4142
+ "detection_heuristics_updated",
4143
+ [ruleId, language, detectionHeuristicsId],
4144
+ event,
4145
+ userId ? [userId] : void 0
4146
+ );
4147
+ _SSEEventPublisher.logger.debug(
4148
+ "Successfully published detection heuristics updated event",
4149
+ { ruleId, language, detectionHeuristicsId }
4150
+ );
4151
+ } catch (error) {
4152
+ _SSEEventPublisher.logger.error(
4153
+ "Failed to publish detection heuristics updated event",
4154
+ {
4155
+ ruleId,
4156
+ language,
4157
+ detectionHeuristicsId,
4158
+ userId,
4159
+ error: error instanceof Error ? error.message : String(error)
4160
+ }
4161
+ );
4162
+ throw error;
4163
+ }
4164
+ }
4165
+ /**
4166
+ * Publish an event to notify a user that their context (role or membership) changed
4167
+ * This should trigger a refetch of the /me route on the frontend
4168
+ */
4169
+ static async publishUserContextChangeEvent(userId, organizationId, changeType, role) {
4170
+ _SSEEventPublisher.logger.info("Publishing user context change event", {
4171
+ userId,
4172
+ organizationId,
4173
+ changeType,
4174
+ role
4175
+ });
4176
+ try {
4177
+ const event = createUserContextChangeEvent(
4178
+ userId,
4179
+ organizationId,
4180
+ changeType,
4181
+ role
4182
+ );
4183
+ await _SSEEventPublisher.publishEvent("user_context_change", [], event, [
4184
+ userId
4185
+ ]);
4186
+ _SSEEventPublisher.logger.debug(
4187
+ "Successfully published user context change event",
4188
+ {
4189
+ userId,
4190
+ organizationId,
4191
+ changeType
4192
+ }
4193
+ );
4194
+ } catch (error) {
4195
+ _SSEEventPublisher.logger.error(
4196
+ "Failed to publish user context change event",
4197
+ {
4198
+ userId,
4199
+ organizationId,
4200
+ changeType,
4201
+ error: error instanceof Error ? error.message : String(error)
4202
+ }
4203
+ );
4204
+ throw error;
4205
+ }
4206
+ }
4207
+ /**
4208
+ * Generic method to publish any SSE event type to Redis pub/sub
4209
+ */
4210
+ static async publishEvent(eventType, params, data, targetUserIds) {
4211
+ _SSEEventPublisher.logger.info("Publishing SSE event", {
4212
+ eventType,
4213
+ params,
4214
+ targetUserIds: targetUserIds?.length || "all"
4215
+ });
4216
+ try {
4217
+ _SSEEventPublisher.getInstance();
4218
+ const redisClient = _SSEEventPublisher.redisClient;
4219
+ const message = createSSEEventMessage(
4220
+ eventType,
4221
+ params,
4222
+ data,
4223
+ targetUserIds
4224
+ );
4225
+ const serializedMessage = serializeSSERedisMessage(message);
4226
+ await redisClient.publish(SSE_REDIS_CHANNELS.EVENTS, serializedMessage);
4227
+ _SSEEventPublisher.logger.debug("Successfully published SSE event", {
4228
+ eventType,
4229
+ params
4230
+ });
4231
+ } catch (error) {
4232
+ _SSEEventPublisher.logger.error("Failed to publish SSE event", {
4233
+ eventType,
4234
+ params,
4235
+ error: error instanceof Error ? error.message : String(error)
4236
+ });
4237
+ throw error;
4238
+ }
4239
+ }
4240
+ };
4241
+
4242
+ // packages/node-utils/src/text/sectionMerge.ts
4243
+ function mergeSectionsIntoFileContent(existingContent, sections) {
4244
+ let result = existingContent;
4245
+ for (const section of sections) {
4246
+ const startMarker = `<!-- start: ${section.key} -->`;
4247
+ const endMarker = `<!-- end: ${section.key} -->`;
4248
+ const startIndex = result.indexOf(startMarker);
4249
+ const endIndex = result.indexOf(endMarker);
4250
+ if (startIndex !== -1 && endIndex !== -1 && endIndex > startIndex) {
4251
+ const before = result.substring(0, startIndex + startMarker.length);
4252
+ const after = result.substring(endIndex);
4253
+ result = `${before}
4254
+ ${section.content}
4255
+ ${after}`;
4256
+ } else {
4257
+ const sectionBlock = `${startMarker}
4258
+ ${section.content}
4259
+ ${endMarker}`;
4260
+ if (result.trim() === "") {
4261
+ result = sectionBlock;
4262
+ } else {
4263
+ result = result.endsWith("\n") ? `${result}${sectionBlock}
4264
+ ` : `${result}
4265
+ ${sectionBlock}
4266
+ `;
4267
+ }
4268
+ }
4269
+ }
4270
+ return result;
4271
+ }
4272
+
4273
+ // packages/node-utils/src/dataSources/local.ts
4274
+ var import_typeorm = require("typeorm");
4275
+ var dataSource = makeDatasource();
4276
+ function makeDatasource() {
4277
+ try {
4278
+ return new import_typeorm.DataSource({
4279
+ type: "postgres",
4280
+ url: process.env["DATABASE_URL"],
4281
+ entities: [],
4282
+ migrations: []
4283
+ });
4284
+ } catch {
4285
+ return {};
4286
+ }
4287
+ }
4288
+
4289
+ // apps/cli/src/application/useCases/PullDataUseCase.ts
4290
+ var fs3 = __toESM(require("fs/promises"));
4291
+ var path3 = __toESM(require("path"));
4292
+ var PullDataUseCase = class {
4293
+ constructor(packmindGateway) {
4294
+ this.packmindGateway = packmindGateway;
4295
+ }
4296
+ async execute(command3) {
4297
+ const baseDirectory = command3.baseDirectory || process.cwd();
4298
+ const result = {
4299
+ filesCreated: 0,
4300
+ filesUpdated: 0,
4301
+ filesDeleted: 0,
4302
+ errors: []
4303
+ };
4304
+ const response = await this.packmindGateway.getPullData({
4305
+ packagesSlugs: command3.packagesSlugs
4306
+ });
4307
+ try {
4308
+ for (const file of response.fileUpdates.createOrUpdate) {
4309
+ try {
4310
+ await this.createOrUpdateFile(baseDirectory, file, result);
4311
+ } catch (error) {
4312
+ const errorMsg = error instanceof Error ? error.message : String(error);
4313
+ result.errors.push(
4314
+ `Failed to create/update ${file.path}: ${errorMsg}`
4315
+ );
4316
+ }
4317
+ }
4318
+ for (const file of response.fileUpdates.delete) {
4319
+ try {
4320
+ await this.deleteFile(baseDirectory, file.path, result);
4321
+ } catch (error) {
4322
+ const errorMsg = error instanceof Error ? error.message : String(error);
4323
+ result.errors.push(`Failed to delete ${file.path}: ${errorMsg}`);
4324
+ }
4325
+ }
4326
+ } catch (error) {
4327
+ const errorMsg = error instanceof Error ? error.message : String(error);
4328
+ result.errors.push(`Failed to pull data: ${errorMsg}`);
4329
+ }
4330
+ return result;
4331
+ }
4332
+ async createOrUpdateFile(baseDirectory, file, result) {
4333
+ const fullPath = path3.join(baseDirectory, file.path);
4334
+ const directory = path3.dirname(fullPath);
4335
+ await fs3.mkdir(directory, { recursive: true });
4336
+ const fileExists = await this.fileExists(fullPath);
4337
+ if (file.content !== void 0) {
4338
+ await this.handleFullContentUpdate(
4339
+ fullPath,
4340
+ file.content,
4341
+ fileExists,
4342
+ result
4343
+ );
4344
+ } else if (file.sections !== void 0) {
4345
+ await this.handleSectionsUpdate(
4346
+ fullPath,
4347
+ file.sections,
4348
+ fileExists,
4349
+ result
4350
+ );
4351
+ }
4352
+ }
4353
+ async handleFullContentUpdate(fullPath, content, fileExists, result) {
4354
+ if (fileExists) {
4355
+ const existingContent = await fs3.readFile(fullPath, "utf-8");
4356
+ const commentMarker = this.extractCommentMarker(content);
4357
+ let finalContent;
4358
+ if (!commentMarker) {
4359
+ finalContent = content;
4360
+ } else {
4361
+ finalContent = this.mergeContentWithMarkers(
4362
+ existingContent,
4363
+ content,
4364
+ commentMarker
4365
+ );
4366
+ }
4367
+ await fs3.writeFile(fullPath, finalContent, "utf-8");
4368
+ result.filesUpdated++;
4369
+ } else {
4370
+ await fs3.writeFile(fullPath, content, "utf-8");
4371
+ result.filesCreated++;
4372
+ }
4373
+ }
4374
+ async handleSectionsUpdate(fullPath, sections, fileExists, result) {
4375
+ let currentContent = "";
4376
+ if (fileExists) {
4377
+ currentContent = await fs3.readFile(fullPath, "utf-8");
4378
+ }
4379
+ const mergedContent = mergeSectionsIntoFileContent(
4380
+ currentContent,
4381
+ sections
4382
+ );
4383
+ await fs3.writeFile(fullPath, mergedContent, "utf-8");
4384
+ if (fileExists) {
4385
+ result.filesUpdated++;
4386
+ } else {
4387
+ result.filesCreated++;
4388
+ }
4389
+ }
4390
+ async deleteFile(baseDirectory, filePath, result) {
4391
+ const fullPath = path3.join(baseDirectory, filePath);
4392
+ const fileExists = await this.fileExists(fullPath);
4393
+ if (fileExists) {
4394
+ await fs3.unlink(fullPath);
4395
+ result.filesDeleted++;
4396
+ }
4397
+ }
4398
+ async fileExists(filePath) {
4399
+ try {
4400
+ await fs3.access(filePath);
4401
+ return true;
4402
+ } catch {
4403
+ return false;
4404
+ }
4405
+ }
4406
+ /**
4407
+ * Extracts the comment marker from content if it's wrapped with HTML comments.
4408
+ * E.g., "<!-- start: Packmind recipes -->" returns "Packmind recipes"
4409
+ */
4410
+ extractCommentMarker(content) {
4411
+ const startMarkerPattern = /<!--\s*start:\s*([^-]+?)\s*-->/;
4412
+ const match = content.match(startMarkerPattern);
4413
+ return match ? match[1].trim() : null;
4414
+ }
4415
+ /**
4416
+ * Merges new content with existing content using comment markers.
4417
+ * If the section exists, replaces it; otherwise, appends it.
4418
+ */
4419
+ mergeContentWithMarkers(existingContent, newContent, commentMarker) {
4420
+ const startMarker = `<!-- start: ${commentMarker} -->`;
4421
+ const endMarker = `<!-- end: ${commentMarker} -->`;
4422
+ const newSectionPattern = new RegExp(
4423
+ `${this.escapeRegex(startMarker)}([\\s\\S]*?)${this.escapeRegex(endMarker)}`
4424
+ );
4425
+ const newSectionMatch = newContent.match(newSectionPattern);
4426
+ const newSectionContent = newSectionMatch ? newSectionMatch[1].trim() : newContent;
4427
+ const existingSectionPattern = new RegExp(
4428
+ `${this.escapeRegex(startMarker)}[\\s\\S]*?${this.escapeRegex(endMarker)}`,
4429
+ "g"
4430
+ );
4431
+ if (existingSectionPattern.test(existingContent)) {
4432
+ return existingContent.replace(
4433
+ existingSectionPattern,
4434
+ `${startMarker}
4435
+ ${newSectionContent}
4436
+ ${endMarker}`
4437
+ );
4438
+ } else {
4439
+ return `${existingContent}
4440
+ ${startMarker}
4441
+ ${newSectionContent}
4442
+ ${endMarker}`;
4443
+ }
4444
+ }
4445
+ /**
4446
+ * Escapes special regex characters in a string
4447
+ */
4448
+ escapeRegex(str) {
4449
+ return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
4450
+ }
4451
+ };
4452
+
4453
+ // apps/cli/src/application/useCases/ListPackagesUseCase.ts
4454
+ var ListPackagesUseCase = class {
4455
+ constructor(packmindGateway) {
4456
+ this.packmindGateway = packmindGateway;
4457
+ }
4458
+ async execute() {
4459
+ return this.packmindGateway.listPackages({});
4460
+ }
4461
+ };
4462
+
4463
+ // apps/cli/src/application/useCases/GetPackageSummaryUseCase.ts
4464
+ var GetPackageSummaryUseCase = class {
4465
+ constructor(gateway) {
4466
+ this.gateway = gateway;
4467
+ }
4468
+ async execute(command3) {
4469
+ return this.gateway.getPackageSummary(command3);
4470
+ }
4471
+ };
4472
+
4473
+ // apps/cli/src/PackmindCliHexaFactory.ts
4474
+ var PackmindCliHexaFactory = class {
4475
+ constructor(logger2) {
4476
+ this.logger = logger2;
4477
+ this.repositories = {
4478
+ packmindGateway: new PackmindGateway(
4479
+ process.env.PACKMIND_API_KEY_V3 || ""
3293
4480
  )
3294
4481
  };
3295
4482
  this.services = {
@@ -3308,16 +4495,20 @@ var PackmindCliHexaFactory = class {
3308
4495
  this.repositories,
3309
4496
  this.logger
3310
4497
  ),
3311
- pullData: new PullDataUseCase(this.repositories.packmindGateway)
4498
+ pullData: new PullDataUseCase(this.repositories.packmindGateway),
4499
+ listPackages: new ListPackagesUseCase(this.repositories.packmindGateway),
4500
+ getPackageBySlug: new GetPackageSummaryUseCase(
4501
+ this.repositories.packmindGateway
4502
+ )
3312
4503
  };
3313
4504
  }
3314
4505
  };
3315
4506
 
3316
4507
  // apps/cli/src/PackmindCliHexa.ts
3317
- var origin4 = "PackmindCliHexa";
4508
+ var origin9 = "PackmindCliHexa";
3318
4509
  var PackmindCliHexa = class {
3319
- constructor(logger = new PackmindLogger(origin4)) {
3320
- this.logger = logger;
4510
+ constructor(logger2 = new PackmindLogger(origin9)) {
4511
+ this.logger = logger2;
3321
4512
  try {
3322
4513
  this.hexa = new PackmindCliHexaFactory(this.logger);
3323
4514
  } catch (error) {
@@ -3349,6 +4540,12 @@ var PackmindCliHexa = class {
3349
4540
  async pullData(command3) {
3350
4541
  return this.hexa.useCases.pullData.execute(command3);
3351
4542
  }
4543
+ async listPackages(command3) {
4544
+ return this.hexa.useCases.listPackages.execute(command3);
4545
+ }
4546
+ async getPackageBySlug(command3) {
4547
+ return this.hexa.useCases.getPackageBySlug.execute(command3);
4548
+ }
3352
4549
  };
3353
4550
 
3354
4551
  // apps/cli/src/infra/repositories/IDELintLogger.ts
@@ -3465,7 +4662,7 @@ var lintCommand = (0, import_cmd_ts.command)({
3465
4662
  description: "Enable debug logging"
3466
4663
  })
3467
4664
  },
3468
- handler: async ({ path: path5, draft, rule, debug, language, logger }) => {
4665
+ handler: async ({ path: path5, draft, rule, debug, language, logger: logger2 }) => {
3469
4666
  if (draft && !rule) {
3470
4667
  throw new Error("option --rule is required to use --draft mode");
3471
4668
  }
@@ -3482,7 +4679,7 @@ var lintCommand = (0, import_cmd_ts.command)({
3482
4679
  ruleId: rule?.ruleId,
3483
4680
  language
3484
4681
  });
3485
- (logger === "ide" /* ide */ ? new IDELintLogger() : new HumanReadableLogger()).logViolations(violations);
4682
+ (logger2 === "ide" /* ide */ ? new IDELintLogger() : new HumanReadableLogger()).logViolations(violations);
3486
4683
  const durationSeconds = (Date.now() - startedAt) / 1e3;
3487
4684
  console.log(`Lint completed in ${durationSeconds.toFixed(2)}s`);
3488
4685
  if (violations.length > 0) {
@@ -3556,15 +4753,113 @@ var path4 = __toESM(require("path"));
3556
4753
  var import_cmd_ts2 = require("cmd-ts");
3557
4754
  var pullCommand = (0, import_cmd_ts2.command)({
3558
4755
  name: "pull",
3559
- description: "Pull recipes and standards from Packmind and save them to the current directory",
3560
- args: {},
3561
- handler: async () => {
3562
- console.log("Pulling content from Packmind...");
4756
+ description: "Pull recipes and standards from specified packages and save them to the current directory",
4757
+ args: {
4758
+ list: (0, import_cmd_ts2.flag)({
4759
+ long: "list",
4760
+ description: "List available packages"
4761
+ }),
4762
+ show: (0, import_cmd_ts2.option)({
4763
+ type: import_cmd_ts2.string,
4764
+ long: "show",
4765
+ description: "Show details of a specific package",
4766
+ defaultValue: () => ""
4767
+ }),
4768
+ packagesSlugs: (0, import_cmd_ts2.restPositionals)({
4769
+ type: import_cmd_ts2.string,
4770
+ displayName: "packages",
4771
+ description: "Package slugs to pull content from (e.g., backend frontend)"
4772
+ })
4773
+ },
4774
+ handler: async ({ list, show, packagesSlugs }) => {
3563
4775
  const packmindLogger = new PackmindLogger("PackmindCLI", "info" /* INFO */);
3564
4776
  const packmindCliHexa = new PackmindCliHexa(packmindLogger);
4777
+ if (list) {
4778
+ try {
4779
+ console.log("Fetching available packages...\n");
4780
+ const packages = await packmindCliHexa.listPackages({});
4781
+ if (packages.length === 0) {
4782
+ console.log("No packages found.");
4783
+ process.exit(0);
4784
+ }
4785
+ console.log("Available packages:");
4786
+ packages.forEach((pkg) => {
4787
+ console.log(` - ${pkg.slug}: ${pkg.description || pkg.name}`);
4788
+ });
4789
+ process.exit(0);
4790
+ } catch (error) {
4791
+ console.error("\n\u274C Failed to list packages:");
4792
+ if (error instanceof Error) {
4793
+ console.error(` ${error.message}`);
4794
+ } else {
4795
+ console.error(` ${String(error)}`);
4796
+ }
4797
+ process.exit(1);
4798
+ }
4799
+ }
4800
+ if (show) {
4801
+ try {
4802
+ console.log(`Fetching package details for '${show}'...
4803
+ `);
4804
+ const pkg = await packmindCliHexa.getPackageBySlug({ slug: show });
4805
+ console.log(`${pkg.name} (${pkg.slug}):
4806
+ `);
4807
+ if (pkg.description) {
4808
+ console.log(`${pkg.description}
4809
+ `);
4810
+ }
4811
+ if (pkg.standards && pkg.standards.length > 0) {
4812
+ console.log("Standards:");
4813
+ pkg.standards.forEach((standard) => {
4814
+ if (standard.summary) {
4815
+ console.log(` - ${standard.name}: ${standard.summary}`);
4816
+ } else {
4817
+ console.log(` - ${standard.name}`);
4818
+ }
4819
+ });
4820
+ console.log("");
4821
+ }
4822
+ if (pkg.recipes && pkg.recipes.length > 0) {
4823
+ console.log("Recipes:");
4824
+ pkg.recipes.forEach((recipe) => {
4825
+ if (recipe.summary) {
4826
+ console.log(` - ${recipe.name}: ${recipe.summary}`);
4827
+ } else {
4828
+ console.log(` - ${recipe.name}`);
4829
+ }
4830
+ });
4831
+ console.log("");
4832
+ }
4833
+ process.exit(0);
4834
+ } catch (error) {
4835
+ console.error("\n\u274C Failed to fetch package details:");
4836
+ if (error instanceof Error) {
4837
+ console.error(` ${error.message}`);
4838
+ } else {
4839
+ console.error(` ${String(error)}`);
4840
+ }
4841
+ process.exit(1);
4842
+ }
4843
+ }
4844
+ if (packagesSlugs.length === 0) {
4845
+ console.log("Usage: packmind-cli pull <package-slug> [package-slug...]");
4846
+ console.log(" packmind-cli pull --list");
4847
+ console.log("");
4848
+ console.log("Examples:");
4849
+ console.log(" packmind-cli pull backend");
4850
+ console.log(" packmind-cli pull backend frontend");
4851
+ console.log(" packmind-cli pull --list # Show available packages");
4852
+ console.log("");
4853
+ console.log("Pull recipes and standards from the specified packages.");
4854
+ process.exit(0);
4855
+ }
4856
+ console.log(
4857
+ `Pulling content from packages: ${packagesSlugs.join(", ")}...`
4858
+ );
3565
4859
  try {
3566
4860
  const result = await packmindCliHexa.pullData({
3567
- baseDirectory: process.cwd()
4861
+ baseDirectory: process.cwd(),
4862
+ packagesSlugs
3568
4863
  });
3569
4864
  console.log("\n\u2705 Pull completed successfully!");
3570
4865
  console.log(` Files created: ${result.filesCreated}`);
@@ -3579,15 +4874,37 @@ var pullCommand = (0, import_cmd_ts2.command)({
3579
4874
  }
3580
4875
  } catch (error) {
3581
4876
  console.error("\n\u274C Failed to pull content:");
3582
- console.error(
3583
- ` ${error instanceof Error ? error.message : String(error)}`
3584
- );
4877
+ if (error instanceof Error) {
4878
+ const errorObj = error;
4879
+ if (errorObj.statusCode === 404) {
4880
+ console.error(` ${errorObj.message}`);
4881
+ console.error(
4882
+ "\n\u{1F4A1} Use `packmind-cli pull --list` to show available packages"
4883
+ );
4884
+ } else {
4885
+ console.error(` ${errorObj.message}`);
4886
+ const apiErrorObj = error;
4887
+ if (apiErrorObj.response?.data?.message) {
4888
+ console.error(`
4889
+ Details: ${apiErrorObj.response.data.message}`);
4890
+ }
4891
+ console.error("\n\u{1F4A1} Troubleshooting tips:");
4892
+ console.error(" - Verify that the package slugs are correct");
4893
+ console.error(
4894
+ " - Check that the packages exist in your organization"
4895
+ );
4896
+ console.error(" - Ensure you have the correct API key configured");
4897
+ }
4898
+ } else {
4899
+ console.error(` ${String(error)}`);
4900
+ }
3585
4901
  process.exit(1);
3586
4902
  }
3587
4903
  }
3588
4904
  });
3589
4905
 
3590
4906
  // apps/cli/src/main.ts
4907
+ var { version: CLI_VERSION } = require_package();
3591
4908
  function findEnvFile() {
3592
4909
  let currentDir = process.cwd();
3593
4910
  const startDir = currentDir;
@@ -3626,6 +4943,11 @@ if (hasEmbeddedWasmFiles()) {
3626
4943
  } catch {
3627
4944
  }
3628
4945
  }
4946
+ var args = process.argv.slice(2);
4947
+ if (args.includes("--version") || args.includes("-v")) {
4948
+ console.log(`packmind-cli version ${CLI_VERSION}`);
4949
+ process.exit(0);
4950
+ }
3629
4951
  var app = (0, import_cmd_ts3.subcommands)({
3630
4952
  name: "packmind-cli",
3631
4953
  description: "Packmind CLI tool",
@@ -3634,7 +4956,7 @@ var app = (0, import_cmd_ts3.subcommands)({
3634
4956
  pull: pullCommand
3635
4957
  }
3636
4958
  });
3637
- (0, import_cmd_ts3.run)(app, process.argv.slice(2)).catch((error) => {
4959
+ (0, import_cmd_ts3.run)(app, args).catch((error) => {
3638
4960
  console.error(import_chalk2.default.bgRed.bold("packmind-cli"), import_chalk2.default.red(error.message));
3639
4961
  process.exit(1);
3640
4962
  });