@packmind/cli 0.3.4 → 0.5.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.
- package/main.cjs +2382 -228
- 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.5.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
|
|
|
@@ -208,38 +259,38 @@ var createRuleId = brandedIdFactory();
|
|
|
208
259
|
var createRuleExampleId = brandedIdFactory();
|
|
209
260
|
|
|
210
261
|
// packages/types/src/languages/ProgrammingLanguage.ts
|
|
211
|
-
var ProgrammingLanguage = /* @__PURE__ */ ((
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
return
|
|
262
|
+
var ProgrammingLanguage = /* @__PURE__ */ ((ProgrammingLanguage4) => {
|
|
263
|
+
ProgrammingLanguage4["JAVASCRIPT"] = "JAVASCRIPT";
|
|
264
|
+
ProgrammingLanguage4["JAVASCRIPT_JSX"] = "JAVASCRIPT_JSX";
|
|
265
|
+
ProgrammingLanguage4["TYPESCRIPT"] = "TYPESCRIPT";
|
|
266
|
+
ProgrammingLanguage4["TYPESCRIPT_TSX"] = "TYPESCRIPT_TSX";
|
|
267
|
+
ProgrammingLanguage4["PYTHON"] = "PYTHON";
|
|
268
|
+
ProgrammingLanguage4["PHP"] = "PHP";
|
|
269
|
+
ProgrammingLanguage4["JAVA"] = "JAVA";
|
|
270
|
+
ProgrammingLanguage4["SCSS"] = "SCSS";
|
|
271
|
+
ProgrammingLanguage4["HTML"] = "HTML";
|
|
272
|
+
ProgrammingLanguage4["CSHARP"] = "CSHARP";
|
|
273
|
+
ProgrammingLanguage4["GENERIC"] = "GENERIC";
|
|
274
|
+
ProgrammingLanguage4["GO"] = "GO";
|
|
275
|
+
ProgrammingLanguage4["C"] = "C";
|
|
276
|
+
ProgrammingLanguage4["CPP"] = "CPP";
|
|
277
|
+
ProgrammingLanguage4["SQL"] = "SQL";
|
|
278
|
+
ProgrammingLanguage4["KOTLIN"] = "KOTLIN";
|
|
279
|
+
ProgrammingLanguage4["VUE"] = "VUE";
|
|
280
|
+
ProgrammingLanguage4["CSS"] = "CSS";
|
|
281
|
+
ProgrammingLanguage4["YAML"] = "YAML";
|
|
282
|
+
ProgrammingLanguage4["JSON"] = "JSON";
|
|
283
|
+
ProgrammingLanguage4["XML"] = "XML";
|
|
284
|
+
ProgrammingLanguage4["BASH"] = "BASH";
|
|
285
|
+
ProgrammingLanguage4["MARKDOWN"] = "MARKDOWN";
|
|
286
|
+
ProgrammingLanguage4["RUBY"] = "RUBY";
|
|
287
|
+
ProgrammingLanguage4["RUST"] = "RUST";
|
|
288
|
+
ProgrammingLanguage4["SAP_ABAP"] = "SAP_ABAP";
|
|
289
|
+
ProgrammingLanguage4["SAP_CDS"] = "SAP_CDS";
|
|
290
|
+
ProgrammingLanguage4["SAP_HANA_SQL"] = "SAP_HANA_SQL";
|
|
291
|
+
ProgrammingLanguage4["SWIFT"] = "SWIFT";
|
|
292
|
+
ProgrammingLanguage4["PROPERTIES"] = "PROPERTIES";
|
|
293
|
+
return ProgrammingLanguage4;
|
|
243
294
|
})(ProgrammingLanguage || {});
|
|
244
295
|
var ProgrammingLanguageDetails = {
|
|
245
296
|
["GENERIC" /* GENERIC */]: {
|
|
@@ -407,6 +458,304 @@ var createDetectionProgramId = brandedIdFactory();
|
|
|
407
458
|
// packages/types/src/linter/RuleDetectionAssessment.ts
|
|
408
459
|
var createRuleDetectionAssessmentId = brandedIdFactory();
|
|
409
460
|
|
|
461
|
+
// packages/types/src/llm/LLMProviderMetadata.ts
|
|
462
|
+
var DEFAULT_OPENAI_MODELS = {
|
|
463
|
+
model: "gpt-5.1",
|
|
464
|
+
fastestModel: "gpt-4.1-mini"
|
|
465
|
+
};
|
|
466
|
+
var DEFAULT_ANTHROPIC_MODELS = {
|
|
467
|
+
model: "claude-sonnet-4-5-20250929",
|
|
468
|
+
fastestModel: "claude-haiku-4-5-20251001"
|
|
469
|
+
};
|
|
470
|
+
var DEFAULT_GEMINI_MODELS = {
|
|
471
|
+
model: "gemini-3-pro-preview",
|
|
472
|
+
fastestModel: "gemini-2.5-flash"
|
|
473
|
+
};
|
|
474
|
+
var DEFAULT_AZURE_OPENAI_API_VERSION = "2024-12-01-preview";
|
|
475
|
+
var LLM_PROVIDER_METADATA = {
|
|
476
|
+
["openai" /* OPENAI */]: {
|
|
477
|
+
id: "openai" /* OPENAI */,
|
|
478
|
+
displayName: "OpenAI",
|
|
479
|
+
description: "OpenAI GPT models including GPT-4 and GPT-5. Requires an API key from OpenAI.",
|
|
480
|
+
defaultModel: DEFAULT_OPENAI_MODELS.model,
|
|
481
|
+
defaultFastModel: DEFAULT_OPENAI_MODELS.fastestModel,
|
|
482
|
+
documentationUrl: "https://platform.openai.com/docs",
|
|
483
|
+
fields: [
|
|
484
|
+
{
|
|
485
|
+
name: "apiKey",
|
|
486
|
+
label: "API Key",
|
|
487
|
+
type: "password",
|
|
488
|
+
defaultValue: "",
|
|
489
|
+
helpMessage: "Your OpenAI API key from platform.openai.com.",
|
|
490
|
+
optional: false,
|
|
491
|
+
placeholder: "sk-...",
|
|
492
|
+
secret: true
|
|
493
|
+
},
|
|
494
|
+
{
|
|
495
|
+
name: "model",
|
|
496
|
+
label: "Model",
|
|
497
|
+
type: "text",
|
|
498
|
+
defaultValue: DEFAULT_OPENAI_MODELS.model,
|
|
499
|
+
helpMessage: "The primary model to use for standard operations. Defaults to the latest recommended model.",
|
|
500
|
+
optional: true,
|
|
501
|
+
placeholder: "gpt-5.1",
|
|
502
|
+
secret: false
|
|
503
|
+
},
|
|
504
|
+
{
|
|
505
|
+
name: "fastestModel",
|
|
506
|
+
label: "Fast Model",
|
|
507
|
+
type: "text",
|
|
508
|
+
defaultValue: DEFAULT_OPENAI_MODELS.fastestModel,
|
|
509
|
+
helpMessage: "A faster, more economical model for less complex operations.",
|
|
510
|
+
optional: true,
|
|
511
|
+
placeholder: "gpt-4.1-mini",
|
|
512
|
+
secret: false
|
|
513
|
+
}
|
|
514
|
+
]
|
|
515
|
+
},
|
|
516
|
+
["anthropic" /* ANTHROPIC */]: {
|
|
517
|
+
id: "anthropic" /* ANTHROPIC */,
|
|
518
|
+
displayName: "Anthropic Claude",
|
|
519
|
+
description: "Anthropic Claude models known for safety and helpfulness. Requires an API key from Anthropic.",
|
|
520
|
+
defaultModel: DEFAULT_ANTHROPIC_MODELS.model,
|
|
521
|
+
defaultFastModel: DEFAULT_ANTHROPIC_MODELS.fastestModel,
|
|
522
|
+
documentationUrl: "https://docs.anthropic.com",
|
|
523
|
+
fields: [
|
|
524
|
+
{
|
|
525
|
+
name: "apiKey",
|
|
526
|
+
label: "API Key",
|
|
527
|
+
type: "password",
|
|
528
|
+
defaultValue: "",
|
|
529
|
+
helpMessage: "Your Anthropic API key from console.anthropic.com.",
|
|
530
|
+
optional: false,
|
|
531
|
+
placeholder: "sk-ant-...",
|
|
532
|
+
secret: true
|
|
533
|
+
},
|
|
534
|
+
{
|
|
535
|
+
name: "model",
|
|
536
|
+
label: "Model",
|
|
537
|
+
type: "text",
|
|
538
|
+
defaultValue: DEFAULT_ANTHROPIC_MODELS.model,
|
|
539
|
+
helpMessage: "The primary Claude model to use. Defaults to the latest Sonnet model.",
|
|
540
|
+
optional: true,
|
|
541
|
+
placeholder: "claude-sonnet-4-5-20250929",
|
|
542
|
+
secret: false
|
|
543
|
+
},
|
|
544
|
+
{
|
|
545
|
+
name: "fastestModel",
|
|
546
|
+
label: "Fast Model",
|
|
547
|
+
type: "text",
|
|
548
|
+
defaultValue: DEFAULT_ANTHROPIC_MODELS.fastestModel,
|
|
549
|
+
helpMessage: "A faster Claude model for less complex operations. Defaults to Haiku.",
|
|
550
|
+
optional: true,
|
|
551
|
+
placeholder: "claude-haiku-4-5-20251001",
|
|
552
|
+
secret: false
|
|
553
|
+
}
|
|
554
|
+
]
|
|
555
|
+
},
|
|
556
|
+
["gemini" /* GEMINI */]: {
|
|
557
|
+
id: "gemini" /* GEMINI */,
|
|
558
|
+
displayName: "Google Gemini",
|
|
559
|
+
description: "Google's Gemini models with multimodal capabilities. Requires an API key from Google AI Studio.",
|
|
560
|
+
defaultModel: DEFAULT_GEMINI_MODELS.model,
|
|
561
|
+
defaultFastModel: DEFAULT_GEMINI_MODELS.fastestModel,
|
|
562
|
+
documentationUrl: "https://ai.google.dev/docs",
|
|
563
|
+
fields: [
|
|
564
|
+
{
|
|
565
|
+
name: "apiKey",
|
|
566
|
+
label: "API Key",
|
|
567
|
+
type: "password",
|
|
568
|
+
defaultValue: "",
|
|
569
|
+
helpMessage: "Your Google AI API key from aistudio.google.com.",
|
|
570
|
+
optional: false,
|
|
571
|
+
placeholder: "AIza...",
|
|
572
|
+
secret: true
|
|
573
|
+
},
|
|
574
|
+
{
|
|
575
|
+
name: "model",
|
|
576
|
+
label: "Model",
|
|
577
|
+
type: "text",
|
|
578
|
+
defaultValue: DEFAULT_GEMINI_MODELS.model,
|
|
579
|
+
helpMessage: "The primary Gemini model to use. Defaults to the latest Pro model.",
|
|
580
|
+
optional: true,
|
|
581
|
+
placeholder: "gemini-3-pro-preview",
|
|
582
|
+
secret: false
|
|
583
|
+
},
|
|
584
|
+
{
|
|
585
|
+
name: "fastestModel",
|
|
586
|
+
label: "Fast Model",
|
|
587
|
+
type: "text",
|
|
588
|
+
defaultValue: DEFAULT_GEMINI_MODELS.fastestModel,
|
|
589
|
+
helpMessage: "A faster Gemini model for less complex operations. Defaults to Flash.",
|
|
590
|
+
optional: true,
|
|
591
|
+
placeholder: "gemini-2.5-flash",
|
|
592
|
+
secret: false
|
|
593
|
+
}
|
|
594
|
+
]
|
|
595
|
+
},
|
|
596
|
+
["azure-openai" /* AZURE_OPENAI */]: {
|
|
597
|
+
id: "azure-openai" /* AZURE_OPENAI */,
|
|
598
|
+
displayName: "Azure OpenAI",
|
|
599
|
+
description: "Microsoft Azure-hosted OpenAI models. Requires Azure deployment names and credentials.",
|
|
600
|
+
defaultModel: "",
|
|
601
|
+
defaultFastModel: "",
|
|
602
|
+
documentationUrl: "https://learn.microsoft.com/en-us/azure/ai-services/openai/",
|
|
603
|
+
fields: [
|
|
604
|
+
{
|
|
605
|
+
name: "model",
|
|
606
|
+
label: "Model Deployment Name",
|
|
607
|
+
type: "text",
|
|
608
|
+
defaultValue: "",
|
|
609
|
+
helpMessage: "The Azure deployment name for the primary model. This is the name you gave your deployment in Azure Portal.",
|
|
610
|
+
optional: false,
|
|
611
|
+
placeholder: "my-gpt-4-deployment",
|
|
612
|
+
secret: false
|
|
613
|
+
},
|
|
614
|
+
{
|
|
615
|
+
name: "fastestModel",
|
|
616
|
+
label: "Fast Model Deployment Name",
|
|
617
|
+
type: "text",
|
|
618
|
+
defaultValue: "",
|
|
619
|
+
helpMessage: "The Azure deployment name for the fast/economical model.",
|
|
620
|
+
optional: false,
|
|
621
|
+
placeholder: "my-gpt-35-turbo-deployment",
|
|
622
|
+
secret: false
|
|
623
|
+
},
|
|
624
|
+
{
|
|
625
|
+
name: "endpoint",
|
|
626
|
+
label: "Endpoint URL",
|
|
627
|
+
type: "url",
|
|
628
|
+
defaultValue: "",
|
|
629
|
+
helpMessage: "Your Azure OpenAI endpoint URL.",
|
|
630
|
+
optional: false,
|
|
631
|
+
placeholder: "https://your-resource.openai.azure.com",
|
|
632
|
+
secret: false
|
|
633
|
+
},
|
|
634
|
+
{
|
|
635
|
+
name: "apiKey",
|
|
636
|
+
label: "API Key",
|
|
637
|
+
type: "password",
|
|
638
|
+
defaultValue: "",
|
|
639
|
+
helpMessage: "Your Azure OpenAI API key.",
|
|
640
|
+
optional: false,
|
|
641
|
+
placeholder: "",
|
|
642
|
+
secret: true
|
|
643
|
+
},
|
|
644
|
+
{
|
|
645
|
+
name: "apiVersion",
|
|
646
|
+
label: "API Version",
|
|
647
|
+
type: "text",
|
|
648
|
+
defaultValue: DEFAULT_AZURE_OPENAI_API_VERSION,
|
|
649
|
+
helpMessage: "The Azure OpenAI API version to use. Defaults to the latest stable version.",
|
|
650
|
+
optional: true,
|
|
651
|
+
placeholder: "2024-12-01-preview",
|
|
652
|
+
secret: false
|
|
653
|
+
}
|
|
654
|
+
]
|
|
655
|
+
},
|
|
656
|
+
["openai-compatible" /* OPENAI_COMPATIBLE */]: {
|
|
657
|
+
id: "openai-compatible" /* OPENAI_COMPATIBLE */,
|
|
658
|
+
displayName: "OpenAI-Compatible",
|
|
659
|
+
description: "Any OpenAI-compatible API endpoint. Use this for local models (Ollama, LM Studio) or other compatible providers.",
|
|
660
|
+
defaultModel: "",
|
|
661
|
+
defaultFastModel: "",
|
|
662
|
+
documentationUrl: void 0,
|
|
663
|
+
fields: [
|
|
664
|
+
{
|
|
665
|
+
name: "llmEndpoint",
|
|
666
|
+
label: "Endpoint URL",
|
|
667
|
+
type: "url",
|
|
668
|
+
defaultValue: "",
|
|
669
|
+
helpMessage: "The base URL of the OpenAI-compatible API endpoint (e.g., http://localhost:11434/v1 for Ollama).",
|
|
670
|
+
optional: false,
|
|
671
|
+
placeholder: "http://localhost:11434/v1",
|
|
672
|
+
secret: false
|
|
673
|
+
},
|
|
674
|
+
{
|
|
675
|
+
name: "llmApiKey",
|
|
676
|
+
label: "API Key",
|
|
677
|
+
type: "password",
|
|
678
|
+
defaultValue: "",
|
|
679
|
+
helpMessage: "API key for authentication. Some local providers may not require this.",
|
|
680
|
+
optional: false,
|
|
681
|
+
placeholder: "",
|
|
682
|
+
secret: true
|
|
683
|
+
},
|
|
684
|
+
{
|
|
685
|
+
name: "model",
|
|
686
|
+
label: "Model",
|
|
687
|
+
type: "text",
|
|
688
|
+
defaultValue: "",
|
|
689
|
+
helpMessage: "The model identifier to use for standard operations (e.g., llama3, mistral).",
|
|
690
|
+
optional: false,
|
|
691
|
+
placeholder: "llama3",
|
|
692
|
+
secret: false
|
|
693
|
+
},
|
|
694
|
+
{
|
|
695
|
+
name: "fastestModel",
|
|
696
|
+
label: "Fast Model",
|
|
697
|
+
type: "text",
|
|
698
|
+
defaultValue: "",
|
|
699
|
+
helpMessage: "A faster model for less complex operations. Can be the same as the primary model.",
|
|
700
|
+
optional: false,
|
|
701
|
+
placeholder: "llama3",
|
|
702
|
+
secret: false
|
|
703
|
+
}
|
|
704
|
+
]
|
|
705
|
+
},
|
|
706
|
+
["packmind" /* PACKMIND */]: {
|
|
707
|
+
id: "packmind" /* PACKMIND */,
|
|
708
|
+
displayName: "Packmind (SaaS)",
|
|
709
|
+
description: "Packmind managed LLM service. Uses the platform default provider configuration.",
|
|
710
|
+
defaultModel: DEFAULT_OPENAI_MODELS.model,
|
|
711
|
+
defaultFastModel: DEFAULT_OPENAI_MODELS.fastestModel,
|
|
712
|
+
documentationUrl: void 0,
|
|
713
|
+
fields: []
|
|
714
|
+
}
|
|
715
|
+
};
|
|
716
|
+
|
|
717
|
+
// packages/types/src/sse/SSEEvent.ts
|
|
718
|
+
function createProgramStatusChangeEvent(ruleId, language) {
|
|
719
|
+
return {
|
|
720
|
+
type: "PROGRAM_STATUS_CHANGE",
|
|
721
|
+
data: { ruleId, language },
|
|
722
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
723
|
+
};
|
|
724
|
+
}
|
|
725
|
+
function createAssessmentStatusChangeEvent(ruleId, language) {
|
|
726
|
+
return {
|
|
727
|
+
type: "ASSESSMENT_STATUS_CHANGE",
|
|
728
|
+
data: {
|
|
729
|
+
ruleId,
|
|
730
|
+
language
|
|
731
|
+
},
|
|
732
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
733
|
+
};
|
|
734
|
+
}
|
|
735
|
+
function createDetectionHeuristicsUpdatedEvent(ruleId, language, detectionHeuristicsId) {
|
|
736
|
+
return {
|
|
737
|
+
type: "DETECTION_HEURISTICS_UPDATED",
|
|
738
|
+
data: {
|
|
739
|
+
ruleId,
|
|
740
|
+
language,
|
|
741
|
+
detectionHeuristicsId
|
|
742
|
+
},
|
|
743
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
744
|
+
};
|
|
745
|
+
}
|
|
746
|
+
function createUserContextChangeEvent(userId, organizationId, changeType, role) {
|
|
747
|
+
return {
|
|
748
|
+
type: "USER_CONTEXT_CHANGE",
|
|
749
|
+
data: {
|
|
750
|
+
userId,
|
|
751
|
+
organizationId,
|
|
752
|
+
changeType,
|
|
753
|
+
role
|
|
754
|
+
},
|
|
755
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
756
|
+
};
|
|
757
|
+
}
|
|
758
|
+
|
|
410
759
|
// apps/cli/src/application/useCases/ExecuteSingleFileAstUseCase.ts
|
|
411
760
|
var ExecuteSingleFileAstUseCase = class _ExecuteSingleFileAstUseCase {
|
|
412
761
|
constructor(linterExecutionUseCase) {
|
|
@@ -447,30 +796,37 @@ var import_util = require("util");
|
|
|
447
796
|
var execAsync = (0, import_util.promisify)(import_child_process.exec);
|
|
448
797
|
var origin = "GitService";
|
|
449
798
|
var GitService = class {
|
|
450
|
-
constructor(
|
|
451
|
-
this.logger =
|
|
799
|
+
constructor(logger2 = new PackmindLogger(origin)) {
|
|
800
|
+
this.logger = logger2;
|
|
452
801
|
}
|
|
453
|
-
async getGitRepositoryRoot(
|
|
802
|
+
async getGitRepositoryRoot(path7) {
|
|
454
803
|
try {
|
|
455
804
|
const { stdout } = await execAsync("git rev-parse --show-toplevel", {
|
|
456
|
-
cwd:
|
|
805
|
+
cwd: path7
|
|
457
806
|
});
|
|
458
807
|
const gitRoot = stdout.trim();
|
|
459
808
|
this.logger.debug("Resolved git repository root", {
|
|
460
|
-
inputPath:
|
|
809
|
+
inputPath: path7,
|
|
461
810
|
gitRoot
|
|
462
811
|
});
|
|
463
812
|
return gitRoot;
|
|
464
813
|
} catch (error) {
|
|
465
814
|
if (error instanceof Error) {
|
|
466
815
|
throw new Error(
|
|
467
|
-
`Failed to get Git repository root. The path '${
|
|
816
|
+
`Failed to get Git repository root. The path '${path7}' does not appear to be inside a Git repository.
|
|
468
817
|
${error.message}`
|
|
469
818
|
);
|
|
470
819
|
}
|
|
471
820
|
throw new Error("Failed to get Git repository root: Unknown error");
|
|
472
821
|
}
|
|
473
822
|
}
|
|
823
|
+
async tryGetGitRepositoryRoot(path7) {
|
|
824
|
+
try {
|
|
825
|
+
return await this.getGitRepositoryRoot(path7);
|
|
826
|
+
} catch {
|
|
827
|
+
return null;
|
|
828
|
+
}
|
|
829
|
+
}
|
|
474
830
|
async getCurrentBranches(repoPath) {
|
|
475
831
|
try {
|
|
476
832
|
const { stdout } = await execAsync("git branch -a --contains HEAD", {
|
|
@@ -488,7 +844,7 @@ ${error.message}`
|
|
|
488
844
|
throw new Error("Failed to get Git branches: Unknown error");
|
|
489
845
|
}
|
|
490
846
|
}
|
|
491
|
-
async getGitRemoteUrl(repoPath,
|
|
847
|
+
async getGitRemoteUrl(repoPath, origin11) {
|
|
492
848
|
try {
|
|
493
849
|
const { stdout } = await execAsync("git remote -v", {
|
|
494
850
|
cwd: repoPath
|
|
@@ -498,10 +854,10 @@ ${error.message}`
|
|
|
498
854
|
throw new Error("No Git remotes found in the repository");
|
|
499
855
|
}
|
|
500
856
|
let selectedRemote;
|
|
501
|
-
if (
|
|
502
|
-
const foundRemote = remotes.find((remote) => remote.name ===
|
|
857
|
+
if (origin11) {
|
|
858
|
+
const foundRemote = remotes.find((remote) => remote.name === origin11);
|
|
503
859
|
if (!foundRemote) {
|
|
504
|
-
throw new Error(`Remote '${
|
|
860
|
+
throw new Error(`Remote '${origin11}' not found in repository`);
|
|
505
861
|
}
|
|
506
862
|
selectedRemote = foundRemote.url;
|
|
507
863
|
} else if (remotes.length === 1) {
|
|
@@ -571,14 +927,14 @@ ${error.message}`
|
|
|
571
927
|
normalizeGitUrl(url) {
|
|
572
928
|
const sshMatch = url.match(/^git@([^:]+):(.+)$/);
|
|
573
929
|
if (sshMatch) {
|
|
574
|
-
const [, host,
|
|
575
|
-
const cleanPath =
|
|
930
|
+
const [, host, path7] = sshMatch;
|
|
931
|
+
const cleanPath = path7.replace(/\.git$/, "");
|
|
576
932
|
return `${host}/${cleanPath}`;
|
|
577
933
|
}
|
|
578
934
|
const httpsMatch = url.match(/^https?:\/\/([^/]+)\/(.+)$/);
|
|
579
935
|
if (httpsMatch) {
|
|
580
|
-
const [, host,
|
|
581
|
-
const cleanPath =
|
|
936
|
+
const [, host, path7] = httpsMatch;
|
|
937
|
+
const cleanPath = path7.replace(/\.git$/, "");
|
|
582
938
|
return `${host}/${cleanPath}`;
|
|
583
939
|
}
|
|
584
940
|
return url;
|
|
@@ -591,8 +947,8 @@ var GetGitRemoteUrlUseCase = class {
|
|
|
591
947
|
this.gitRemoteUrlService = gitRemoteUrlService;
|
|
592
948
|
}
|
|
593
949
|
async execute(command3) {
|
|
594
|
-
const { path: repoPath, origin:
|
|
595
|
-
return this.gitRemoteUrlService.getGitRemoteUrl(repoPath,
|
|
950
|
+
const { path: repoPath, origin: origin11 } = command3;
|
|
951
|
+
return this.gitRemoteUrlService.getGitRemoteUrl(repoPath, origin11);
|
|
596
952
|
}
|
|
597
953
|
};
|
|
598
954
|
|
|
@@ -720,10 +1076,10 @@ var path2 = __toESM(require("path"));
|
|
|
720
1076
|
var fs2 = __toESM(require("fs/promises"));
|
|
721
1077
|
var origin2 = "LintFilesInDirectoryUseCase";
|
|
722
1078
|
var LintFilesInDirectoryUseCase = class {
|
|
723
|
-
constructor(services, repositories,
|
|
1079
|
+
constructor(services, repositories, logger2 = new PackmindLogger(origin2)) {
|
|
724
1080
|
this.services = services;
|
|
725
1081
|
this.repositories = repositories;
|
|
726
|
-
this.logger =
|
|
1082
|
+
this.logger = logger2;
|
|
727
1083
|
}
|
|
728
1084
|
fileMatchesScope(filePath, scopePatterns) {
|
|
729
1085
|
if (!scopePatterns || scopePatterns.length === 0) {
|
|
@@ -1057,6 +1413,224 @@ var LintFilesInDirectoryUseCase = class {
|
|
|
1057
1413
|
}
|
|
1058
1414
|
};
|
|
1059
1415
|
|
|
1416
|
+
// apps/cli/src/application/useCases/LintFilesLocallyUseCase.ts
|
|
1417
|
+
var import_minimatch2 = require("minimatch");
|
|
1418
|
+
var path3 = __toESM(require("path"));
|
|
1419
|
+
var fs3 = __toESM(require("fs/promises"));
|
|
1420
|
+
var origin3 = "LintFilesLocallyUseCase";
|
|
1421
|
+
var LintFilesLocallyUseCase = class {
|
|
1422
|
+
constructor(services, repositories, logger2 = new PackmindLogger(origin3)) {
|
|
1423
|
+
this.services = services;
|
|
1424
|
+
this.repositories = repositories;
|
|
1425
|
+
this.logger = logger2;
|
|
1426
|
+
}
|
|
1427
|
+
fileMatchesTargetAndScope(filePath, targetPath, scopePatterns) {
|
|
1428
|
+
if (!scopePatterns || scopePatterns.length === 0) {
|
|
1429
|
+
const effectivePattern = this.buildEffectivePattern(targetPath, null);
|
|
1430
|
+
return (0, import_minimatch2.minimatch)(filePath, effectivePattern, { matchBase: false });
|
|
1431
|
+
}
|
|
1432
|
+
return scopePatterns.some((scopePattern) => {
|
|
1433
|
+
const effectivePattern = this.buildEffectivePattern(
|
|
1434
|
+
targetPath,
|
|
1435
|
+
scopePattern
|
|
1436
|
+
);
|
|
1437
|
+
return (0, import_minimatch2.minimatch)(filePath, effectivePattern, { matchBase: false });
|
|
1438
|
+
});
|
|
1439
|
+
}
|
|
1440
|
+
buildEffectivePattern(targetPath, scope) {
|
|
1441
|
+
const normalizedTarget = targetPath === "/" ? "/" : targetPath.replace(/\/$/, "");
|
|
1442
|
+
if (!scope) {
|
|
1443
|
+
return normalizedTarget === "/" ? "/**" : normalizedTarget + "/**";
|
|
1444
|
+
}
|
|
1445
|
+
if (scope.startsWith(normalizedTarget + "/") || scope === normalizedTarget) {
|
|
1446
|
+
return scope.endsWith("/") ? scope + "**" : scope;
|
|
1447
|
+
}
|
|
1448
|
+
const cleanScope = scope.startsWith("/") ? scope.substring(1) : scope;
|
|
1449
|
+
let pattern;
|
|
1450
|
+
if (normalizedTarget === "/") {
|
|
1451
|
+
pattern = "/" + cleanScope;
|
|
1452
|
+
} else {
|
|
1453
|
+
pattern = normalizedTarget + "/" + cleanScope;
|
|
1454
|
+
}
|
|
1455
|
+
if (pattern.endsWith("/")) {
|
|
1456
|
+
pattern = pattern + "**";
|
|
1457
|
+
}
|
|
1458
|
+
return pattern;
|
|
1459
|
+
}
|
|
1460
|
+
async execute(command3) {
|
|
1461
|
+
const { path: userPath } = command3;
|
|
1462
|
+
this.logger.debug(`Starting local linting: path="${userPath}"`);
|
|
1463
|
+
const absoluteUserPath = path3.isAbsolute(userPath) ? userPath : path3.resolve(process.cwd(), userPath);
|
|
1464
|
+
let pathStats;
|
|
1465
|
+
try {
|
|
1466
|
+
pathStats = await fs3.stat(absoluteUserPath);
|
|
1467
|
+
} catch {
|
|
1468
|
+
throw new Error(
|
|
1469
|
+
`The path "${absoluteUserPath}" does not exist or cannot be accessed`
|
|
1470
|
+
);
|
|
1471
|
+
}
|
|
1472
|
+
const isFile = pathStats.isFile();
|
|
1473
|
+
const directoryForConfig = isFile ? path3.dirname(absoluteUserPath) : absoluteUserPath;
|
|
1474
|
+
const gitRepoRoot = await this.services.gitRemoteUrlService.tryGetGitRepositoryRoot(
|
|
1475
|
+
directoryForConfig
|
|
1476
|
+
);
|
|
1477
|
+
const hierarchicalConfig = await this.repositories.configFileRepository.readHierarchicalConfig(
|
|
1478
|
+
directoryForConfig,
|
|
1479
|
+
gitRepoRoot
|
|
1480
|
+
);
|
|
1481
|
+
if (!hierarchicalConfig.hasConfigs) {
|
|
1482
|
+
const boundary = gitRepoRoot ?? "filesystem root";
|
|
1483
|
+
throw new Error(
|
|
1484
|
+
`No packmind.json found between ${directoryForConfig} and ${boundary}. Cannot use local linting.`
|
|
1485
|
+
);
|
|
1486
|
+
}
|
|
1487
|
+
const basePath = gitRepoRoot ?? directoryForConfig;
|
|
1488
|
+
this.logger.debug(
|
|
1489
|
+
`Found ${hierarchicalConfig.configPaths.length} packmind.json file(s)`
|
|
1490
|
+
);
|
|
1491
|
+
for (const configPath of hierarchicalConfig.configPaths) {
|
|
1492
|
+
this.logger.debug(`Using config: ${configPath}`);
|
|
1493
|
+
}
|
|
1494
|
+
const packageSlugs = Object.keys(hierarchicalConfig.packages);
|
|
1495
|
+
this.logger.debug(
|
|
1496
|
+
`Merged ${packageSlugs.length} packages from configuration files`
|
|
1497
|
+
);
|
|
1498
|
+
const detectionPrograms = await this.repositories.packmindGateway.getDetectionProgramsForPackages({
|
|
1499
|
+
packagesSlugs: packageSlugs
|
|
1500
|
+
});
|
|
1501
|
+
this.logger.debug(
|
|
1502
|
+
`Retrieved detection programs: targetsCount=${detectionPrograms.targets.length}`
|
|
1503
|
+
);
|
|
1504
|
+
const files = isFile ? [{ path: absoluteUserPath }] : await this.services.listFiles.listFilesInDirectory(
|
|
1505
|
+
absoluteUserPath,
|
|
1506
|
+
[],
|
|
1507
|
+
["node_modules", "dist", ".min.", ".map.", ".git"]
|
|
1508
|
+
);
|
|
1509
|
+
this.logger.debug(`Found ${files.length} files to lint`);
|
|
1510
|
+
const violations = [];
|
|
1511
|
+
for (const file of files) {
|
|
1512
|
+
const fileViolations = [];
|
|
1513
|
+
const relativeFilePath = file.path.startsWith(basePath) ? file.path.substring(basePath.length) : file.path;
|
|
1514
|
+
const normalizedFilePath = relativeFilePath.startsWith("/") ? relativeFilePath : "/" + relativeFilePath;
|
|
1515
|
+
this.logger.debug(
|
|
1516
|
+
`Processing file: absolute="${file.path}", relative="${normalizedFilePath}"`
|
|
1517
|
+
);
|
|
1518
|
+
const fileExtension = this.extractExtensionFromFile(file.path);
|
|
1519
|
+
const fileLanguage = this.resolveProgrammingLanguage(fileExtension);
|
|
1520
|
+
if (!fileLanguage) {
|
|
1521
|
+
continue;
|
|
1522
|
+
}
|
|
1523
|
+
const programsByLanguage = /* @__PURE__ */ new Map();
|
|
1524
|
+
for (const target of detectionPrograms.targets) {
|
|
1525
|
+
for (const standard of target.standards) {
|
|
1526
|
+
if (!this.fileMatchesTargetAndScope(
|
|
1527
|
+
normalizedFilePath,
|
|
1528
|
+
target.path,
|
|
1529
|
+
standard.scope
|
|
1530
|
+
)) {
|
|
1531
|
+
continue;
|
|
1532
|
+
}
|
|
1533
|
+
for (const rule of standard.rules) {
|
|
1534
|
+
for (const activeProgram of rule.activeDetectionPrograms) {
|
|
1535
|
+
try {
|
|
1536
|
+
const programLanguage = this.resolveProgrammingLanguage(
|
|
1537
|
+
activeProgram.language
|
|
1538
|
+
);
|
|
1539
|
+
if (!programLanguage || programLanguage !== fileLanguage) {
|
|
1540
|
+
continue;
|
|
1541
|
+
}
|
|
1542
|
+
const programsForLanguage = programsByLanguage.get(programLanguage) ?? [];
|
|
1543
|
+
programsForLanguage.push({
|
|
1544
|
+
code: activeProgram.detectionProgram.code,
|
|
1545
|
+
ruleContent: rule.content,
|
|
1546
|
+
standardSlug: standard.slug,
|
|
1547
|
+
sourceCodeState: activeProgram.detectionProgram.sourceCodeState,
|
|
1548
|
+
language: fileLanguage
|
|
1549
|
+
});
|
|
1550
|
+
programsByLanguage.set(programLanguage, programsForLanguage);
|
|
1551
|
+
} catch (error) {
|
|
1552
|
+
console.error(
|
|
1553
|
+
`Error preparing program for file ${file.path}: ${error}`
|
|
1554
|
+
);
|
|
1555
|
+
}
|
|
1556
|
+
}
|
|
1557
|
+
}
|
|
1558
|
+
}
|
|
1559
|
+
}
|
|
1560
|
+
if (programsByLanguage.size > 0) {
|
|
1561
|
+
try {
|
|
1562
|
+
const fileContent = await this.services.listFiles.readFileContent(
|
|
1563
|
+
file.path
|
|
1564
|
+
);
|
|
1565
|
+
for (const [language, programs] of programsByLanguage.entries()) {
|
|
1566
|
+
try {
|
|
1567
|
+
const result = await this.executeProgramsForFile({
|
|
1568
|
+
filePath: file.path,
|
|
1569
|
+
fileContent,
|
|
1570
|
+
language,
|
|
1571
|
+
programs
|
|
1572
|
+
});
|
|
1573
|
+
fileViolations.push(...result);
|
|
1574
|
+
} catch (error) {
|
|
1575
|
+
console.error(
|
|
1576
|
+
`Error executing programs for file ${file.path} (${language}): ${error}`
|
|
1577
|
+
);
|
|
1578
|
+
}
|
|
1579
|
+
}
|
|
1580
|
+
} catch (error) {
|
|
1581
|
+
console.error(
|
|
1582
|
+
`Error reading file content for ${file.path}: ${error}`
|
|
1583
|
+
);
|
|
1584
|
+
}
|
|
1585
|
+
}
|
|
1586
|
+
if (fileViolations.length > 0) {
|
|
1587
|
+
violations.push({
|
|
1588
|
+
file: file.path,
|
|
1589
|
+
violations: fileViolations
|
|
1590
|
+
});
|
|
1591
|
+
}
|
|
1592
|
+
}
|
|
1593
|
+
const totalViolations = violations.reduce(
|
|
1594
|
+
(sum, violation) => sum + violation.violations.length,
|
|
1595
|
+
0
|
|
1596
|
+
);
|
|
1597
|
+
const standardsChecked = Array.from(
|
|
1598
|
+
new Set(
|
|
1599
|
+
detectionPrograms.targets.flatMap(
|
|
1600
|
+
(target) => target.standards.map((standard) => standard.slug)
|
|
1601
|
+
)
|
|
1602
|
+
)
|
|
1603
|
+
);
|
|
1604
|
+
return {
|
|
1605
|
+
violations,
|
|
1606
|
+
summary: {
|
|
1607
|
+
totalFiles: files.length,
|
|
1608
|
+
violatedFiles: violations.length,
|
|
1609
|
+
totalViolations,
|
|
1610
|
+
standardsChecked
|
|
1611
|
+
}
|
|
1612
|
+
};
|
|
1613
|
+
}
|
|
1614
|
+
resolveProgrammingLanguage(language) {
|
|
1615
|
+
try {
|
|
1616
|
+
return stringToProgrammingLanguage(language);
|
|
1617
|
+
} catch {
|
|
1618
|
+
return null;
|
|
1619
|
+
}
|
|
1620
|
+
}
|
|
1621
|
+
async executeProgramsForFile(command3) {
|
|
1622
|
+
const result = await this.services.linterExecutionUseCase.execute(command3);
|
|
1623
|
+
return result.violations;
|
|
1624
|
+
}
|
|
1625
|
+
extractExtensionFromFile(filePath) {
|
|
1626
|
+
const lastDotIndex = filePath.lastIndexOf(".");
|
|
1627
|
+
if (lastDotIndex === -1 || lastDotIndex === filePath.length - 1) {
|
|
1628
|
+
return "";
|
|
1629
|
+
}
|
|
1630
|
+
return filePath.substring(lastDotIndex + 1);
|
|
1631
|
+
}
|
|
1632
|
+
};
|
|
1633
|
+
|
|
1060
1634
|
// apps/cli/src/infra/repositories/PackmindGateway.ts
|
|
1061
1635
|
function decodeJwt(jwt) {
|
|
1062
1636
|
try {
|
|
@@ -1113,7 +1687,7 @@ function decodeApiKey(apiKey) {
|
|
|
1113
1687
|
var PackmindGateway = class {
|
|
1114
1688
|
constructor(apiKey) {
|
|
1115
1689
|
this.apiKey = apiKey;
|
|
1116
|
-
this.getPullData = async () => {
|
|
1690
|
+
this.getPullData = async (command3) => {
|
|
1117
1691
|
const decodedApiKey = decodeApiKey(this.apiKey);
|
|
1118
1692
|
if (!decodedApiKey.isValid) {
|
|
1119
1693
|
throw new Error(`Invalid API key: ${decodedApiKey.error}`);
|
|
@@ -1124,7 +1698,13 @@ var PackmindGateway = class {
|
|
|
1124
1698
|
throw new Error("Invalid JWT: missing organizationId");
|
|
1125
1699
|
}
|
|
1126
1700
|
const organizationId = jwtPayload.organization.id;
|
|
1127
|
-
const
|
|
1701
|
+
const queryParams = new URLSearchParams();
|
|
1702
|
+
if (command3.packagesSlugs && command3.packagesSlugs.length > 0) {
|
|
1703
|
+
command3.packagesSlugs.forEach((slug) => {
|
|
1704
|
+
queryParams.append("packageSlug", slug);
|
|
1705
|
+
});
|
|
1706
|
+
}
|
|
1707
|
+
const url = `${host}/api/v0/organizations/${organizationId}/pull?${queryParams.toString()}`;
|
|
1128
1708
|
try {
|
|
1129
1709
|
const response = await fetch(url, {
|
|
1130
1710
|
method: "GET",
|
|
@@ -1142,7 +1722,9 @@ var PackmindGateway = class {
|
|
|
1142
1722
|
}
|
|
1143
1723
|
} catch {
|
|
1144
1724
|
}
|
|
1145
|
-
|
|
1725
|
+
const error = new Error(errorMsg);
|
|
1726
|
+
error.statusCode = response.status;
|
|
1727
|
+
throw error;
|
|
1146
1728
|
}
|
|
1147
1729
|
const result = await response.json();
|
|
1148
1730
|
return result;
|
|
@@ -1155,29 +1737,29 @@ var PackmindGateway = class {
|
|
|
1155
1737
|
);
|
|
1156
1738
|
}
|
|
1157
1739
|
throw new Error(
|
|
1158
|
-
`Failed to
|
|
1740
|
+
`Failed to fetch content: Error: ${err?.message || JSON.stringify(error)}`
|
|
1159
1741
|
);
|
|
1160
1742
|
}
|
|
1161
1743
|
};
|
|
1162
|
-
this.
|
|
1744
|
+
this.listPackages = async () => {
|
|
1163
1745
|
const decodedApiKey = decodeApiKey(this.apiKey);
|
|
1164
1746
|
if (!decodedApiKey.isValid) {
|
|
1165
1747
|
throw new Error(`Invalid API key: ${decodedApiKey.error}`);
|
|
1166
1748
|
}
|
|
1167
|
-
const { host } = decodedApiKey.payload;
|
|
1168
|
-
const
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1749
|
+
const { host, jwt } = decodedApiKey.payload;
|
|
1750
|
+
const jwtPayload = decodeJwt(jwt);
|
|
1751
|
+
if (!jwtPayload?.organization?.id) {
|
|
1752
|
+
throw new Error("Invalid JWT: missing organizationId");
|
|
1753
|
+
}
|
|
1754
|
+
const organizationId = jwtPayload.organization.id;
|
|
1755
|
+
const url = `${host}/api/v0/organizations/${organizationId}/packages`;
|
|
1173
1756
|
try {
|
|
1174
1757
|
const response = await fetch(url, {
|
|
1175
|
-
method: "
|
|
1758
|
+
method: "GET",
|
|
1176
1759
|
headers: {
|
|
1177
1760
|
"Content-Type": "application/json",
|
|
1178
1761
|
Authorization: `Bearer ${this.apiKey}`
|
|
1179
|
-
}
|
|
1180
|
-
body: JSON.stringify(payload)
|
|
1762
|
+
}
|
|
1181
1763
|
});
|
|
1182
1764
|
if (!response.ok) {
|
|
1183
1765
|
let errorMsg = `API request failed: ${response.status} ${response.statusText}`;
|
|
@@ -1188,10 +1770,12 @@ var PackmindGateway = class {
|
|
|
1188
1770
|
}
|
|
1189
1771
|
} catch {
|
|
1190
1772
|
}
|
|
1191
|
-
|
|
1773
|
+
const error = new Error(errorMsg);
|
|
1774
|
+
error.statusCode = response.status;
|
|
1775
|
+
throw error;
|
|
1192
1776
|
}
|
|
1193
1777
|
const result = await response.json();
|
|
1194
|
-
return result;
|
|
1778
|
+
return result.packages;
|
|
1195
1779
|
} catch (error) {
|
|
1196
1780
|
const err = error;
|
|
1197
1781
|
const code = err?.code || err?.cause?.code;
|
|
@@ -1201,22 +1785,68 @@ var PackmindGateway = class {
|
|
|
1201
1785
|
);
|
|
1202
1786
|
}
|
|
1203
1787
|
throw new Error(
|
|
1204
|
-
`Failed to
|
|
1788
|
+
`Failed to list packages: Error: ${err?.message || JSON.stringify(error)}`
|
|
1205
1789
|
);
|
|
1206
1790
|
}
|
|
1207
1791
|
};
|
|
1208
|
-
this.
|
|
1792
|
+
this.listExecutionPrograms = async (params) => {
|
|
1209
1793
|
const decodedApiKey = decodeApiKey(this.apiKey);
|
|
1210
1794
|
if (!decodedApiKey.isValid) {
|
|
1211
1795
|
throw new Error(`Invalid API key: ${decodedApiKey.error}`);
|
|
1212
1796
|
}
|
|
1213
1797
|
const { host } = decodedApiKey.payload;
|
|
1214
|
-
const url = `${host}/api/v0/list-
|
|
1798
|
+
const url = `${host}/api/v0/list-detection-program`;
|
|
1215
1799
|
const payload = {
|
|
1216
|
-
|
|
1217
|
-
|
|
1800
|
+
gitRemoteUrl: params.gitRemoteUrl,
|
|
1801
|
+
branches: params.branches
|
|
1218
1802
|
};
|
|
1219
|
-
|
|
1803
|
+
try {
|
|
1804
|
+
const response = await fetch(url, {
|
|
1805
|
+
method: "POST",
|
|
1806
|
+
headers: {
|
|
1807
|
+
"Content-Type": "application/json",
|
|
1808
|
+
Authorization: `Bearer ${this.apiKey}`
|
|
1809
|
+
},
|
|
1810
|
+
body: JSON.stringify(payload)
|
|
1811
|
+
});
|
|
1812
|
+
if (!response.ok) {
|
|
1813
|
+
let errorMsg = `API request failed: ${response.status} ${response.statusText}`;
|
|
1814
|
+
try {
|
|
1815
|
+
const errorBody = await response.json();
|
|
1816
|
+
if (errorBody && errorBody.message) {
|
|
1817
|
+
errorMsg = `${errorBody.message}`;
|
|
1818
|
+
}
|
|
1819
|
+
} catch {
|
|
1820
|
+
}
|
|
1821
|
+
throw new Error(errorMsg);
|
|
1822
|
+
}
|
|
1823
|
+
const result = await response.json();
|
|
1824
|
+
return result;
|
|
1825
|
+
} catch (error) {
|
|
1826
|
+
const err = error;
|
|
1827
|
+
const code = err?.code || err?.cause?.code;
|
|
1828
|
+
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"))) {
|
|
1829
|
+
throw new Error(
|
|
1830
|
+
`Packmind server is not accessible at ${host}. Please check your network connection or the server URL.`
|
|
1831
|
+
);
|
|
1832
|
+
}
|
|
1833
|
+
throw new Error(
|
|
1834
|
+
`Failed to fetch detection programs: Error: ${err?.message || JSON.stringify(error)}`
|
|
1835
|
+
);
|
|
1836
|
+
}
|
|
1837
|
+
};
|
|
1838
|
+
this.getDraftDetectionProgramsForRule = async (params) => {
|
|
1839
|
+
const decodedApiKey = decodeApiKey(this.apiKey);
|
|
1840
|
+
if (!decodedApiKey.isValid) {
|
|
1841
|
+
throw new Error(`Invalid API key: ${decodedApiKey.error}`);
|
|
1842
|
+
}
|
|
1843
|
+
const { host } = decodedApiKey.payload;
|
|
1844
|
+
const url = `${host}/api/v0/list-draft-detection-program`;
|
|
1845
|
+
const payload = {
|
|
1846
|
+
standardSlug: params.standardSlug,
|
|
1847
|
+
ruleId: params.ruleId
|
|
1848
|
+
};
|
|
1849
|
+
if (params.language) {
|
|
1220
1850
|
payload.language = params.language;
|
|
1221
1851
|
}
|
|
1222
1852
|
try {
|
|
@@ -1337,6 +1967,101 @@ var PackmindGateway = class {
|
|
|
1337
1967
|
);
|
|
1338
1968
|
}
|
|
1339
1969
|
};
|
|
1970
|
+
this.getPackageSummary = async ({
|
|
1971
|
+
slug
|
|
1972
|
+
}) => {
|
|
1973
|
+
const decodedApiKey = decodeApiKey(this.apiKey);
|
|
1974
|
+
if (!decodedApiKey.isValid) {
|
|
1975
|
+
throw new Error(`Invalid API key: ${decodedApiKey.error}`);
|
|
1976
|
+
}
|
|
1977
|
+
const { host, jwt } = decodedApiKey.payload;
|
|
1978
|
+
const jwtPayload = decodeJwt(jwt);
|
|
1979
|
+
if (!jwtPayload?.organization?.id) {
|
|
1980
|
+
throw new Error("Invalid JWT: missing organizationId");
|
|
1981
|
+
}
|
|
1982
|
+
const organizationId = jwtPayload.organization.id;
|
|
1983
|
+
const url = `${host}/api/v0/organizations/${organizationId}/packages/${encodeURIComponent(slug)}`;
|
|
1984
|
+
try {
|
|
1985
|
+
const response = await fetch(url, {
|
|
1986
|
+
method: "GET",
|
|
1987
|
+
headers: {
|
|
1988
|
+
"Content-Type": "application/json",
|
|
1989
|
+
Authorization: `Bearer ${this.apiKey}`
|
|
1990
|
+
}
|
|
1991
|
+
});
|
|
1992
|
+
if (!response.ok) {
|
|
1993
|
+
let errorMsg = `API request failed: ${response.status} ${response.statusText}`;
|
|
1994
|
+
try {
|
|
1995
|
+
const errorBody = await response.json();
|
|
1996
|
+
if (errorBody && errorBody.message) {
|
|
1997
|
+
errorMsg = `${errorBody.message}`;
|
|
1998
|
+
}
|
|
1999
|
+
} catch {
|
|
2000
|
+
}
|
|
2001
|
+
const error = new Error(errorMsg);
|
|
2002
|
+
error.statusCode = response.status;
|
|
2003
|
+
throw error;
|
|
2004
|
+
}
|
|
2005
|
+
const result = await response.json();
|
|
2006
|
+
return result;
|
|
2007
|
+
} catch (error) {
|
|
2008
|
+
const err = error;
|
|
2009
|
+
const code = err?.code || err?.cause?.code;
|
|
2010
|
+
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"))) {
|
|
2011
|
+
throw new Error(
|
|
2012
|
+
`Packmind server is not accessible at ${host}. Please check your network connection or the server URL.`
|
|
2013
|
+
);
|
|
2014
|
+
}
|
|
2015
|
+
throw new Error(
|
|
2016
|
+
`Failed to get package '${slug}': Error: ${err?.message || JSON.stringify(error)}`
|
|
2017
|
+
);
|
|
2018
|
+
}
|
|
2019
|
+
};
|
|
2020
|
+
this.getDetectionProgramsForPackages = async (params) => {
|
|
2021
|
+
const decodedApiKey = decodeApiKey(this.apiKey);
|
|
2022
|
+
if (!decodedApiKey.isValid) {
|
|
2023
|
+
throw new Error(`Invalid API key: ${decodedApiKey.error}`);
|
|
2024
|
+
}
|
|
2025
|
+
const { host } = decodedApiKey.payload;
|
|
2026
|
+
const url = `${host}/api/v0/detection-programs-for-packages`;
|
|
2027
|
+
const payload = {
|
|
2028
|
+
packagesSlugs: params.packagesSlugs
|
|
2029
|
+
};
|
|
2030
|
+
try {
|
|
2031
|
+
const response = await fetch(url, {
|
|
2032
|
+
method: "POST",
|
|
2033
|
+
headers: {
|
|
2034
|
+
"Content-Type": "application/json",
|
|
2035
|
+
Authorization: `Bearer ${this.apiKey}`
|
|
2036
|
+
},
|
|
2037
|
+
body: JSON.stringify(payload)
|
|
2038
|
+
});
|
|
2039
|
+
if (!response.ok) {
|
|
2040
|
+
let errorMsg = `API request failed: ${response.status} ${response.statusText}`;
|
|
2041
|
+
try {
|
|
2042
|
+
const errorBody = await response.json();
|
|
2043
|
+
if (errorBody && errorBody.message) {
|
|
2044
|
+
errorMsg = `${errorBody.message}`;
|
|
2045
|
+
}
|
|
2046
|
+
} catch {
|
|
2047
|
+
}
|
|
2048
|
+
throw new Error(errorMsg);
|
|
2049
|
+
}
|
|
2050
|
+
const result = await response.json();
|
|
2051
|
+
return result;
|
|
2052
|
+
} catch (error) {
|
|
2053
|
+
const err = error;
|
|
2054
|
+
const code = err?.code || err?.cause?.code;
|
|
2055
|
+
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"))) {
|
|
2056
|
+
throw new Error(
|
|
2057
|
+
`Packmind server is not accessible at ${host}. Please check your network connection or the server URL.`
|
|
2058
|
+
);
|
|
2059
|
+
}
|
|
2060
|
+
throw new Error(
|
|
2061
|
+
`Failed to fetch detection programs for packages: Error: ${err?.message || JSON.stringify(error)}`
|
|
2062
|
+
);
|
|
2063
|
+
}
|
|
2064
|
+
};
|
|
1340
2065
|
}
|
|
1341
2066
|
};
|
|
1342
2067
|
|
|
@@ -3009,11 +3734,11 @@ var LinterAstAdapter = class {
|
|
|
3009
3734
|
var TreeSitter17 = __toESM(require("web-tree-sitter"));
|
|
3010
3735
|
|
|
3011
3736
|
// packages/linter-execution/src/application/useCases/ExecuteLinterProgramsUseCase.ts
|
|
3012
|
-
var
|
|
3737
|
+
var origin4 = "ExecuteLinterProgramsUseCase";
|
|
3013
3738
|
var ExecuteLinterProgramsUseCase = class {
|
|
3014
|
-
constructor(linterAstAdapter = new LinterAstAdapter(),
|
|
3739
|
+
constructor(linterAstAdapter = new LinterAstAdapter(), logger2 = new PackmindLogger(origin4)) {
|
|
3015
3740
|
this.linterAstAdapter = linterAstAdapter;
|
|
3016
|
-
this.logger =
|
|
3741
|
+
this.logger = logger2;
|
|
3017
3742
|
}
|
|
3018
3743
|
async execute(command3) {
|
|
3019
3744
|
const { filePath, fileContent, language, programs } = command3;
|
|
@@ -3206,149 +3931,1340 @@ var ExecuteLinterProgramsUseCase = class {
|
|
|
3206
3931
|
}
|
|
3207
3932
|
};
|
|
3208
3933
|
|
|
3209
|
-
//
|
|
3210
|
-
var
|
|
3211
|
-
var
|
|
3212
|
-
|
|
3213
|
-
|
|
3214
|
-
|
|
3934
|
+
// packages/node-utils/src/dataSources/local.ts
|
|
3935
|
+
var import_typeorm = require("typeorm");
|
|
3936
|
+
var dataSource = makeDatasource();
|
|
3937
|
+
function makeDatasource() {
|
|
3938
|
+
try {
|
|
3939
|
+
return new import_typeorm.DataSource({
|
|
3940
|
+
type: "postgres",
|
|
3941
|
+
url: process.env["DATABASE_URL"],
|
|
3942
|
+
entities: [],
|
|
3943
|
+
migrations: []
|
|
3944
|
+
});
|
|
3945
|
+
} catch {
|
|
3946
|
+
return {};
|
|
3215
3947
|
}
|
|
3216
|
-
|
|
3217
|
-
|
|
3218
|
-
|
|
3219
|
-
|
|
3220
|
-
|
|
3221
|
-
|
|
3222
|
-
|
|
3223
|
-
|
|
3948
|
+
}
|
|
3949
|
+
|
|
3950
|
+
// packages/node-utils/src/cache/Cache.ts
|
|
3951
|
+
var import_ioredis = __toESM(require("ioredis"));
|
|
3952
|
+
|
|
3953
|
+
// packages/node-utils/src/config/infra/Infisical/InfisicalConfig.ts
|
|
3954
|
+
var import_sdk = require("@infisical/sdk");
|
|
3955
|
+
var origin5 = "InfisicalConfig";
|
|
3956
|
+
var InfisicalConfig = class {
|
|
3957
|
+
constructor(clientId, clientSecret, env, projectId, logger2 = new PackmindLogger(origin5)) {
|
|
3958
|
+
this.clientId = clientId;
|
|
3959
|
+
this.clientSecret = clientSecret;
|
|
3960
|
+
this.env = env;
|
|
3961
|
+
this.projectId = projectId;
|
|
3962
|
+
this.logger = logger2;
|
|
3963
|
+
this.logger.info("Initializing InfisicalConfig", { env, projectId });
|
|
3224
3964
|
try {
|
|
3225
|
-
|
|
3226
|
-
|
|
3227
|
-
|
|
3228
|
-
|
|
3229
|
-
|
|
3230
|
-
|
|
3231
|
-
|
|
3232
|
-
result
|
|
3233
|
-
);
|
|
3234
|
-
} catch (error) {
|
|
3235
|
-
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
3236
|
-
result.errors.push(
|
|
3237
|
-
`Failed to create/update ${file.path}: ${errorMsg}`
|
|
3238
|
-
);
|
|
3239
|
-
}
|
|
3240
|
-
}
|
|
3241
|
-
for (const file of response.fileUpdates.delete) {
|
|
3242
|
-
try {
|
|
3243
|
-
await this.deleteFile(baseDirectory, file.path, result);
|
|
3244
|
-
} catch (error) {
|
|
3245
|
-
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
3246
|
-
result.errors.push(`Failed to delete ${file.path}: ${errorMsg}`);
|
|
3247
|
-
}
|
|
3248
|
-
}
|
|
3965
|
+
this.client = new import_sdk.InfisicalSDK({
|
|
3966
|
+
siteUrl: "https://eu.infisical.com"
|
|
3967
|
+
// Optional, defaults to https://app.infisical.com
|
|
3968
|
+
});
|
|
3969
|
+
this.logger.info("InfisicalSDK client created successfully", {
|
|
3970
|
+
siteUrl: "https://eu.infisical.com"
|
|
3971
|
+
});
|
|
3249
3972
|
} catch (error) {
|
|
3250
|
-
|
|
3251
|
-
|
|
3252
|
-
|
|
3253
|
-
|
|
3254
|
-
}
|
|
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
|
-
}
|
|
3267
|
-
}
|
|
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++;
|
|
3973
|
+
this.logger.error("Failed to create InfisicalSDK client", {
|
|
3974
|
+
error: error instanceof Error ? error.message : String(error)
|
|
3975
|
+
});
|
|
3976
|
+
throw error;
|
|
3274
3977
|
}
|
|
3275
3978
|
}
|
|
3276
|
-
async
|
|
3979
|
+
async initClient() {
|
|
3980
|
+
this.logger.info("Initializing Infisical client authentication");
|
|
3277
3981
|
try {
|
|
3278
|
-
|
|
3279
|
-
|
|
3280
|
-
|
|
3281
|
-
|
|
3982
|
+
this.logger.debug("Authenticating with Infisical using universal auth");
|
|
3983
|
+
await this.client.auth().universalAuth.login({
|
|
3984
|
+
clientId: this.clientId,
|
|
3985
|
+
clientSecret: this.clientSecret
|
|
3986
|
+
});
|
|
3987
|
+
this.logger.info("Infisical client authenticated successfully");
|
|
3988
|
+
} catch (error) {
|
|
3989
|
+
this.logger.error("Failed to authenticate Infisical client", {
|
|
3990
|
+
error: error instanceof Error ? error.message : String(error)
|
|
3991
|
+
});
|
|
3992
|
+
throw error;
|
|
3282
3993
|
}
|
|
3283
3994
|
}
|
|
3284
|
-
|
|
3285
|
-
|
|
3286
|
-
|
|
3287
|
-
|
|
3288
|
-
|
|
3289
|
-
|
|
3290
|
-
this.repositories = {
|
|
3291
|
-
packmindGateway: new PackmindGateway(
|
|
3292
|
-
process.env.PACKMIND_API_KEY_V3 || ""
|
|
3293
|
-
)
|
|
3294
|
-
};
|
|
3295
|
-
this.services = {
|
|
3296
|
-
listFiles: new ListFiles(),
|
|
3297
|
-
gitRemoteUrlService: new GitService(this.logger),
|
|
3298
|
-
linterExecutionUseCase: new ExecuteLinterProgramsUseCase()
|
|
3299
|
-
};
|
|
3300
|
-
this.useCases = {
|
|
3301
|
-
executeSingleFileAst: new ExecuteSingleFileAstUseCase(
|
|
3302
|
-
this.services.linterExecutionUseCase
|
|
3303
|
-
),
|
|
3304
|
-
getGitRemoteUrl: new GetGitRemoteUrlUseCase(),
|
|
3305
|
-
listFilesInDirectoryUseCase: new ListFilesInDirectoryUseCase(),
|
|
3306
|
-
lintFilesInDirectory: new LintFilesInDirectoryUseCase(
|
|
3307
|
-
this.services,
|
|
3308
|
-
this.repositories,
|
|
3309
|
-
this.logger
|
|
3310
|
-
),
|
|
3311
|
-
pullData: new PullDataUseCase(this.repositories.packmindGateway)
|
|
3312
|
-
};
|
|
3313
|
-
}
|
|
3314
|
-
};
|
|
3315
|
-
|
|
3316
|
-
// apps/cli/src/PackmindCliHexa.ts
|
|
3317
|
-
var origin4 = "PackmindCliHexa";
|
|
3318
|
-
var PackmindCliHexa = class {
|
|
3319
|
-
constructor(logger = new PackmindLogger(origin4)) {
|
|
3320
|
-
this.logger = logger;
|
|
3995
|
+
async getValue(secretName) {
|
|
3996
|
+
this.logger.info("Retrieving secret from Infisical", {
|
|
3997
|
+
secretName,
|
|
3998
|
+
env: this.env,
|
|
3999
|
+
projectId: this.projectId
|
|
4000
|
+
});
|
|
3321
4001
|
try {
|
|
3322
|
-
this.
|
|
4002
|
+
this.logger.debug("Fetching secret from Infisical API", { secretName });
|
|
4003
|
+
const nameSecret = await this.client.secrets().getSecret({
|
|
4004
|
+
projectId: this.projectId,
|
|
4005
|
+
environment: this.env,
|
|
4006
|
+
secretName
|
|
4007
|
+
});
|
|
4008
|
+
if (nameSecret?.secretValue) {
|
|
4009
|
+
this.logger.info("Secret retrieved from Infisical successfully", {
|
|
4010
|
+
secretName
|
|
4011
|
+
});
|
|
4012
|
+
return nameSecret.secretValue;
|
|
4013
|
+
} else {
|
|
4014
|
+
this.logger.warn("Secret not found or has no value in Infisical", {
|
|
4015
|
+
secretName
|
|
4016
|
+
});
|
|
4017
|
+
return null;
|
|
4018
|
+
}
|
|
3323
4019
|
} catch (error) {
|
|
3324
|
-
this.logger.
|
|
4020
|
+
this.logger.warn("Failed to retrieve secret from Infisical", {
|
|
4021
|
+
secretName,
|
|
4022
|
+
env: this.env,
|
|
4023
|
+
projectId: this.projectId,
|
|
3325
4024
|
error: error instanceof Error ? error.message : String(error)
|
|
3326
4025
|
});
|
|
3327
4026
|
throw error;
|
|
3328
4027
|
}
|
|
3329
4028
|
}
|
|
3330
|
-
|
|
3331
|
-
|
|
3332
|
-
|
|
3333
|
-
|
|
3334
|
-
|
|
3335
|
-
|
|
3336
|
-
|
|
3337
|
-
|
|
3338
|
-
return this.hexa.useCases.getGitRemoteUrl.execute(command3);
|
|
4029
|
+
};
|
|
4030
|
+
|
|
4031
|
+
// packages/node-utils/src/config/config/Configuration.ts
|
|
4032
|
+
var origin6 = "Configuration";
|
|
4033
|
+
var Configuration = class _Configuration {
|
|
4034
|
+
constructor() {
|
|
4035
|
+
this.initialized = false;
|
|
4036
|
+
this.initializationPromise = null;
|
|
3339
4037
|
}
|
|
3340
|
-
|
|
3341
|
-
|
|
4038
|
+
static {
|
|
4039
|
+
this.logger = new PackmindLogger(
|
|
4040
|
+
origin6,
|
|
4041
|
+
"info" /* INFO */
|
|
4042
|
+
);
|
|
3342
4043
|
}
|
|
3343
|
-
|
|
3344
|
-
|
|
4044
|
+
static getInstance(logger2) {
|
|
4045
|
+
if (logger2) {
|
|
4046
|
+
_Configuration.logger = logger2;
|
|
4047
|
+
}
|
|
4048
|
+
_Configuration.logger.debug("Getting Configuration instance");
|
|
4049
|
+
if (!_Configuration.instance) {
|
|
4050
|
+
_Configuration.logger.info("Creating new Configuration instance");
|
|
4051
|
+
_Configuration.instance = new _Configuration();
|
|
4052
|
+
}
|
|
4053
|
+
return _Configuration.instance;
|
|
4054
|
+
}
|
|
4055
|
+
async initialize(env) {
|
|
4056
|
+
if (this.initialized) {
|
|
4057
|
+
_Configuration.logger.debug("Configuration already initialized, skipping");
|
|
4058
|
+
return;
|
|
4059
|
+
}
|
|
4060
|
+
if (this.initializationPromise) {
|
|
4061
|
+
_Configuration.logger.debug(
|
|
4062
|
+
"Configuration initialization already in progress, waiting for completion"
|
|
4063
|
+
);
|
|
4064
|
+
await this.initializationPromise;
|
|
4065
|
+
return;
|
|
4066
|
+
}
|
|
4067
|
+
this.initializationPromise = this.performInitialization(env);
|
|
4068
|
+
try {
|
|
4069
|
+
await this.initializationPromise;
|
|
4070
|
+
} finally {
|
|
4071
|
+
this.initializationPromise = null;
|
|
4072
|
+
}
|
|
4073
|
+
}
|
|
4074
|
+
async performInitialization(env) {
|
|
4075
|
+
_Configuration.logger.info("Initializing Configuration");
|
|
4076
|
+
const configurationMode = env["CONFIGURATION"]?.toLowerCase();
|
|
4077
|
+
_Configuration.logger.debug("Configuration mode detected", {
|
|
4078
|
+
mode: configurationMode
|
|
4079
|
+
});
|
|
4080
|
+
if (configurationMode === "infisical") {
|
|
4081
|
+
_Configuration.logger.info("Initializing Infisical configuration");
|
|
4082
|
+
const clientId = env["INFISICAL_CLIENT_ID"];
|
|
4083
|
+
const clientSecret = env["INFISICAL_CLIENT_SECRET"];
|
|
4084
|
+
const infisicalEnv = env["INFISICAL_ENV"];
|
|
4085
|
+
const projectId = env["INFISICAL_PROJECT_ID"];
|
|
4086
|
+
if (!clientId || !clientSecret || !infisicalEnv || !projectId) {
|
|
4087
|
+
_Configuration.logger.error(
|
|
4088
|
+
"Infisical configuration is incomplete, falling back to environment variables only",
|
|
4089
|
+
{
|
|
4090
|
+
hasClientId: !!clientId,
|
|
4091
|
+
hasClientSecret: !!clientSecret,
|
|
4092
|
+
hasInfisicalEnv: !!infisicalEnv,
|
|
4093
|
+
hasProjectId: !!projectId
|
|
4094
|
+
}
|
|
4095
|
+
);
|
|
4096
|
+
this.infisicalConfig = void 0;
|
|
4097
|
+
} else {
|
|
4098
|
+
try {
|
|
4099
|
+
this.infisicalConfig = new InfisicalConfig(
|
|
4100
|
+
clientId,
|
|
4101
|
+
clientSecret,
|
|
4102
|
+
infisicalEnv,
|
|
4103
|
+
projectId
|
|
4104
|
+
);
|
|
4105
|
+
_Configuration.logger.debug("Initializing Infisical client");
|
|
4106
|
+
await this.infisicalConfig.initClient();
|
|
4107
|
+
_Configuration.logger.info(
|
|
4108
|
+
"Infisical configuration initialized successfully"
|
|
4109
|
+
);
|
|
4110
|
+
} catch (error) {
|
|
4111
|
+
_Configuration.logger.error(
|
|
4112
|
+
"Failed to initialize Infisical, falling back to environment variables only",
|
|
4113
|
+
{
|
|
4114
|
+
error: error instanceof Error ? error.message : String(error)
|
|
4115
|
+
}
|
|
4116
|
+
);
|
|
4117
|
+
this.infisicalConfig = void 0;
|
|
4118
|
+
}
|
|
4119
|
+
}
|
|
4120
|
+
} else {
|
|
4121
|
+
_Configuration.logger.info(
|
|
4122
|
+
"Using environment variables only (no Infisical)"
|
|
4123
|
+
);
|
|
4124
|
+
}
|
|
4125
|
+
this.initialized = true;
|
|
4126
|
+
_Configuration.logger.info("Configuration initialization completed");
|
|
4127
|
+
}
|
|
4128
|
+
static async getConfigWithDefault(key, defaultValue) {
|
|
4129
|
+
const value = await _Configuration.getConfig(key);
|
|
4130
|
+
return value ?? defaultValue;
|
|
4131
|
+
}
|
|
4132
|
+
static async getConfig(key, env = process.env, logger2) {
|
|
4133
|
+
if (logger2) {
|
|
4134
|
+
_Configuration.logger = logger2;
|
|
4135
|
+
}
|
|
4136
|
+
_Configuration.logger.info("Getting configuration value", { key });
|
|
4137
|
+
try {
|
|
4138
|
+
const instance = _Configuration.getInstance();
|
|
4139
|
+
await instance.initialize(env);
|
|
4140
|
+
const envValue = env[key];
|
|
4141
|
+
if (envValue) {
|
|
4142
|
+
_Configuration.logger.debug(
|
|
4143
|
+
"Configuration value found in environment variables",
|
|
4144
|
+
{ key }
|
|
4145
|
+
);
|
|
4146
|
+
return envValue;
|
|
4147
|
+
}
|
|
4148
|
+
if (instance.infisicalConfig) {
|
|
4149
|
+
_Configuration.logger.debug(
|
|
4150
|
+
"Checking Infisical for configuration value",
|
|
4151
|
+
{ key }
|
|
4152
|
+
);
|
|
4153
|
+
try {
|
|
4154
|
+
const infisicalValue = await instance.infisicalConfig.getValue(key);
|
|
4155
|
+
if (infisicalValue) {
|
|
4156
|
+
_Configuration.logger.debug(
|
|
4157
|
+
"Configuration value found in Infisical",
|
|
4158
|
+
{
|
|
4159
|
+
key
|
|
4160
|
+
}
|
|
4161
|
+
);
|
|
4162
|
+
return infisicalValue;
|
|
4163
|
+
}
|
|
4164
|
+
} catch (error) {
|
|
4165
|
+
_Configuration.logger.error(
|
|
4166
|
+
"Failed to retrieve value from Infisical, value not available",
|
|
4167
|
+
{
|
|
4168
|
+
key,
|
|
4169
|
+
error: error instanceof Error ? error.message : String(error)
|
|
4170
|
+
}
|
|
4171
|
+
);
|
|
4172
|
+
}
|
|
4173
|
+
}
|
|
4174
|
+
_Configuration.logger.warn("Configuration value not found", { key });
|
|
4175
|
+
return null;
|
|
4176
|
+
} catch (error) {
|
|
4177
|
+
_Configuration.logger.warn("Failed to get configuration value", {
|
|
4178
|
+
key,
|
|
4179
|
+
error: error instanceof Error ? error.message : String(error)
|
|
4180
|
+
});
|
|
4181
|
+
return null;
|
|
4182
|
+
}
|
|
4183
|
+
}
|
|
4184
|
+
};
|
|
4185
|
+
|
|
4186
|
+
// packages/node-utils/src/cache/Cache.ts
|
|
4187
|
+
var origin7 = "Cache";
|
|
4188
|
+
var Cache = class _Cache {
|
|
4189
|
+
constructor() {
|
|
4190
|
+
this.initialized = false;
|
|
4191
|
+
this.connectionConfig = {
|
|
4192
|
+
host: "redis",
|
|
4193
|
+
port: 6379,
|
|
4194
|
+
maxRetriesPerRequest: 3
|
|
4195
|
+
};
|
|
4196
|
+
}
|
|
4197
|
+
static {
|
|
4198
|
+
this.logger = new PackmindLogger(
|
|
4199
|
+
origin7,
|
|
4200
|
+
"info" /* INFO */
|
|
4201
|
+
);
|
|
4202
|
+
}
|
|
4203
|
+
static {
|
|
4204
|
+
// Default cache expiration time in seconds (5 minutes)
|
|
4205
|
+
this.DEFAULT_EXPIRATION_SECONDS = 300;
|
|
4206
|
+
}
|
|
4207
|
+
/**
|
|
4208
|
+
* Get the singleton instance of Cache
|
|
4209
|
+
*/
|
|
4210
|
+
static getInstance() {
|
|
4211
|
+
_Cache.logger.debug("Getting Cache instance");
|
|
4212
|
+
if (!_Cache.instance) {
|
|
4213
|
+
_Cache.logger.info("Creating new Cache instance");
|
|
4214
|
+
_Cache.instance = new _Cache();
|
|
4215
|
+
}
|
|
4216
|
+
return _Cache.instance;
|
|
4217
|
+
}
|
|
4218
|
+
/**
|
|
4219
|
+
* Initialize the Redis client with configuration
|
|
4220
|
+
* This should be called during application startup
|
|
4221
|
+
*/
|
|
4222
|
+
async initialize() {
|
|
4223
|
+
if (this.initialized) {
|
|
4224
|
+
return;
|
|
4225
|
+
}
|
|
4226
|
+
_Cache.logger.info("Initializing Redis cache client");
|
|
4227
|
+
try {
|
|
4228
|
+
const redisUri = await Configuration.getConfig("REDIS_URI");
|
|
4229
|
+
if (!redisUri) {
|
|
4230
|
+
throw new Error("REDIS_URI configuration is required");
|
|
4231
|
+
}
|
|
4232
|
+
_Cache.logger.info("Using REDIS_URI configuration");
|
|
4233
|
+
this.client = new import_ioredis.default(redisUri, {
|
|
4234
|
+
maxRetriesPerRequest: 3
|
|
4235
|
+
});
|
|
4236
|
+
this.client.on("error", (error) => {
|
|
4237
|
+
_Cache.logger.error("Redis cache client error", {
|
|
4238
|
+
error: error instanceof Error ? error.message : String(error)
|
|
4239
|
+
});
|
|
4240
|
+
});
|
|
4241
|
+
this.client.on("connect", () => {
|
|
4242
|
+
_Cache.logger.info("Redis cache client connected successfully");
|
|
4243
|
+
});
|
|
4244
|
+
this.initialized = true;
|
|
4245
|
+
_Cache.logger.info("Cache initialization completed");
|
|
4246
|
+
} catch (error) {
|
|
4247
|
+
_Cache.logger.error("Failed to initialize cache", {
|
|
4248
|
+
error: error instanceof Error ? error.message : String(error)
|
|
4249
|
+
});
|
|
4250
|
+
throw new Error(
|
|
4251
|
+
`Cache initialization failed: ${error instanceof Error ? error.message : String(error)}`
|
|
4252
|
+
);
|
|
4253
|
+
}
|
|
4254
|
+
}
|
|
4255
|
+
/**
|
|
4256
|
+
* Set a value in the cache with optional expiration
|
|
4257
|
+
* @param key - The cache key
|
|
4258
|
+
* @param value - The value to cache (will be JSON serialized)
|
|
4259
|
+
* @param expirationSeconds - Expiration time in seconds (default: 300s)
|
|
4260
|
+
*/
|
|
4261
|
+
async set(key, value, expirationSeconds = _Cache.DEFAULT_EXPIRATION_SECONDS) {
|
|
4262
|
+
if (!this.initialized || !this.client) {
|
|
4263
|
+
_Cache.logger.warn("Cache not initialized, skipping set operation", {
|
|
4264
|
+
key
|
|
4265
|
+
});
|
|
4266
|
+
return;
|
|
4267
|
+
}
|
|
4268
|
+
try {
|
|
4269
|
+
const serializedValue = JSON.stringify(value);
|
|
4270
|
+
await this.client.setex(key, expirationSeconds, serializedValue);
|
|
4271
|
+
} catch (error) {
|
|
4272
|
+
_Cache.logger.warn(
|
|
4273
|
+
"Failed to set cache value, continuing without caching",
|
|
4274
|
+
{
|
|
4275
|
+
key,
|
|
4276
|
+
error: error instanceof Error ? error.message : String(error)
|
|
4277
|
+
}
|
|
4278
|
+
);
|
|
4279
|
+
}
|
|
4280
|
+
}
|
|
4281
|
+
/**
|
|
4282
|
+
* Get a value from the cache
|
|
4283
|
+
* @param key - The cache key
|
|
4284
|
+
* @returns The cached value or null if not found, expired, or error occurred
|
|
4285
|
+
*/
|
|
4286
|
+
async get(key) {
|
|
4287
|
+
if (!this.initialized || !this.client) {
|
|
4288
|
+
_Cache.logger.warn(
|
|
4289
|
+
"Cache not initialized, returning null for get operation",
|
|
4290
|
+
{ key }
|
|
4291
|
+
);
|
|
4292
|
+
return null;
|
|
4293
|
+
}
|
|
4294
|
+
try {
|
|
4295
|
+
const serializedValue = await this.client.get(key);
|
|
4296
|
+
if (serializedValue === null) {
|
|
4297
|
+
return null;
|
|
4298
|
+
}
|
|
4299
|
+
const value = JSON.parse(serializedValue);
|
|
4300
|
+
return value;
|
|
4301
|
+
} catch (error) {
|
|
4302
|
+
_Cache.logger.warn("Failed to get cache value, returning null", {
|
|
4303
|
+
key,
|
|
4304
|
+
error: error instanceof Error ? error.message : String(error)
|
|
4305
|
+
});
|
|
4306
|
+
return null;
|
|
4307
|
+
}
|
|
4308
|
+
}
|
|
4309
|
+
/**
|
|
4310
|
+
* Invalidate (delete) a cache entry
|
|
4311
|
+
* @param key - The cache key to invalidate
|
|
4312
|
+
*/
|
|
4313
|
+
async invalidate(key) {
|
|
4314
|
+
if (!this.initialized || !this.client) {
|
|
4315
|
+
_Cache.logger.warn(
|
|
4316
|
+
"Cache not initialized, skipping invalidate operation",
|
|
4317
|
+
{ key }
|
|
4318
|
+
);
|
|
4319
|
+
return;
|
|
4320
|
+
}
|
|
4321
|
+
try {
|
|
4322
|
+
await this.client.del(key);
|
|
4323
|
+
} catch (error) {
|
|
4324
|
+
_Cache.logger.warn(
|
|
4325
|
+
"Failed to invalidate cache key, continuing without invalidation",
|
|
4326
|
+
{
|
|
4327
|
+
key,
|
|
4328
|
+
error: error instanceof Error ? error.message : String(error)
|
|
4329
|
+
}
|
|
4330
|
+
);
|
|
4331
|
+
}
|
|
4332
|
+
}
|
|
4333
|
+
/**
|
|
4334
|
+
* Disconnect the Redis client (for cleanup during shutdown)
|
|
4335
|
+
*/
|
|
4336
|
+
async disconnect() {
|
|
4337
|
+
_Cache.logger.info("Disconnecting cache client");
|
|
4338
|
+
if (this.client) {
|
|
4339
|
+
try {
|
|
4340
|
+
await this.client.disconnect();
|
|
4341
|
+
_Cache.logger.info("Cache client disconnected successfully");
|
|
4342
|
+
} catch (error) {
|
|
4343
|
+
_Cache.logger.error("Error disconnecting cache client", {
|
|
4344
|
+
error: error instanceof Error ? error.message : String(error)
|
|
4345
|
+
});
|
|
4346
|
+
}
|
|
4347
|
+
}
|
|
4348
|
+
this.initialized = false;
|
|
4349
|
+
}
|
|
4350
|
+
/**
|
|
4351
|
+
* Get cache statistics (for monitoring/debugging)
|
|
4352
|
+
*/
|
|
4353
|
+
async getStats() {
|
|
4354
|
+
return {
|
|
4355
|
+
connected: this.client?.status === "ready",
|
|
4356
|
+
initialized: this.initialized
|
|
4357
|
+
};
|
|
4358
|
+
}
|
|
4359
|
+
};
|
|
4360
|
+
|
|
4361
|
+
// packages/node-utils/src/jobs/infra/bullMQ/WorkerQueue.ts
|
|
4362
|
+
var import_bullmq2 = require("bullmq");
|
|
4363
|
+
|
|
4364
|
+
// packages/node-utils/src/jobs/infra/bullMQ/AbstractQueue.ts
|
|
4365
|
+
var import_bullmq = require("bullmq");
|
|
4366
|
+
|
|
4367
|
+
// packages/node-utils/src/jobs/infra/DelayedJobsFactory.ts
|
|
4368
|
+
var logger = new PackmindLogger("DelayedJobsFactory");
|
|
4369
|
+
|
|
4370
|
+
// packages/node-utils/src/mail/SmtpMailService.ts
|
|
4371
|
+
var import_nodemailer = __toESM(require("nodemailer"));
|
|
4372
|
+
|
|
4373
|
+
// packages/node-utils/src/nest/Public.ts
|
|
4374
|
+
var import_common = require("@nestjs/common");
|
|
4375
|
+
|
|
4376
|
+
// packages/node-utils/src/repositories/AbstractRepository.ts
|
|
4377
|
+
var import_common2 = require("@nestjs/common");
|
|
4378
|
+
|
|
4379
|
+
// packages/node-utils/src/sse/RedisSSEClient.ts
|
|
4380
|
+
var import_ioredis2 = __toESM(require("ioredis"));
|
|
4381
|
+
var origin8 = "RedisSSEClient";
|
|
4382
|
+
var RedisSSEClient = class _RedisSSEClient {
|
|
4383
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
4384
|
+
constructor() {
|
|
4385
|
+
this.initialized = false;
|
|
4386
|
+
}
|
|
4387
|
+
static {
|
|
4388
|
+
this.logger = new PackmindLogger(origin8);
|
|
4389
|
+
}
|
|
4390
|
+
static getInstance() {
|
|
4391
|
+
_RedisSSEClient.logger.debug("Getting RedisSSEClient instance");
|
|
4392
|
+
if (!_RedisSSEClient.instance) {
|
|
4393
|
+
_RedisSSEClient.logger.info("Creating new RedisSSEClient instance");
|
|
4394
|
+
_RedisSSEClient.instance = new _RedisSSEClient();
|
|
4395
|
+
}
|
|
4396
|
+
return _RedisSSEClient.instance;
|
|
4397
|
+
}
|
|
4398
|
+
/**
|
|
4399
|
+
* Initialize Redis clients using the same configuration as BullMQ
|
|
4400
|
+
*/
|
|
4401
|
+
async initialize() {
|
|
4402
|
+
if (this.initialized) return;
|
|
4403
|
+
_RedisSSEClient.logger.info("Initializing Redis SSE clients");
|
|
4404
|
+
try {
|
|
4405
|
+
const redisURI = await Configuration.getConfig("REDIS_URI") || "redis";
|
|
4406
|
+
this.publisherClient = new import_ioredis2.default(redisURI);
|
|
4407
|
+
this.subscriberClient = new import_ioredis2.default(redisURI);
|
|
4408
|
+
this.publisherClient.on("error", (error) => {
|
|
4409
|
+
_RedisSSEClient.logger.error("Redis publisher client error", {
|
|
4410
|
+
error: error.message
|
|
4411
|
+
});
|
|
4412
|
+
});
|
|
4413
|
+
this.subscriberClient.on("error", (error) => {
|
|
4414
|
+
_RedisSSEClient.logger.error("Redis subscriber client error", {
|
|
4415
|
+
error: error.message
|
|
4416
|
+
});
|
|
4417
|
+
});
|
|
4418
|
+
await this.publisherClient.ping();
|
|
4419
|
+
await this.subscriberClient.ping();
|
|
4420
|
+
this.initialized = true;
|
|
4421
|
+
_RedisSSEClient.logger.info("Redis SSE clients initialized successfully");
|
|
4422
|
+
} catch (error) {
|
|
4423
|
+
_RedisSSEClient.logger.error("Failed to initialize Redis SSE clients", {
|
|
4424
|
+
error: error instanceof Error ? error.message : String(error)
|
|
4425
|
+
});
|
|
4426
|
+
throw error;
|
|
4427
|
+
}
|
|
4428
|
+
}
|
|
4429
|
+
/**
|
|
4430
|
+
* Publish a message to a Redis channel
|
|
4431
|
+
*/
|
|
4432
|
+
async publish(channel, message) {
|
|
4433
|
+
await this.initialize();
|
|
4434
|
+
if (!this.publisherClient) {
|
|
4435
|
+
throw new Error("Publisher client not initialized");
|
|
4436
|
+
}
|
|
4437
|
+
_RedisSSEClient.logger.debug("Publishing message to Redis channel", {
|
|
4438
|
+
channel,
|
|
4439
|
+
messageLength: message.length
|
|
4440
|
+
});
|
|
4441
|
+
try {
|
|
4442
|
+
const subscriberCount = await this.publisherClient.publish(
|
|
4443
|
+
channel,
|
|
4444
|
+
message
|
|
4445
|
+
);
|
|
4446
|
+
_RedisSSEClient.logger.debug("Message published successfully", {
|
|
4447
|
+
channel,
|
|
4448
|
+
subscriberCount
|
|
4449
|
+
});
|
|
4450
|
+
return subscriberCount;
|
|
4451
|
+
} catch (error) {
|
|
4452
|
+
_RedisSSEClient.logger.error("Failed to publish message", {
|
|
4453
|
+
channel,
|
|
4454
|
+
error: error instanceof Error ? error.message : String(error)
|
|
4455
|
+
});
|
|
4456
|
+
throw error;
|
|
4457
|
+
}
|
|
4458
|
+
}
|
|
4459
|
+
/**
|
|
4460
|
+
* Subscribe to a Redis channel
|
|
4461
|
+
*/
|
|
4462
|
+
async subscribe(channel, callback) {
|
|
4463
|
+
await this.initialize();
|
|
4464
|
+
if (!this.subscriberClient) {
|
|
4465
|
+
throw new Error("Subscriber client not initialized");
|
|
4466
|
+
}
|
|
4467
|
+
_RedisSSEClient.logger.info("Subscribing to Redis channel", { channel });
|
|
4468
|
+
try {
|
|
4469
|
+
this.subscriberClient.on("message", (receivedChannel, message) => {
|
|
4470
|
+
if (receivedChannel === channel) {
|
|
4471
|
+
_RedisSSEClient.logger.debug("Received message from Redis channel", {
|
|
4472
|
+
channel: receivedChannel,
|
|
4473
|
+
messageLength: message.length
|
|
4474
|
+
});
|
|
4475
|
+
callback(message);
|
|
4476
|
+
}
|
|
4477
|
+
});
|
|
4478
|
+
await this.subscriberClient.subscribe(channel);
|
|
4479
|
+
_RedisSSEClient.logger.info("Successfully subscribed to Redis channel", {
|
|
4480
|
+
channel
|
|
4481
|
+
});
|
|
4482
|
+
} catch (error) {
|
|
4483
|
+
_RedisSSEClient.logger.error("Failed to subscribe to Redis channel", {
|
|
4484
|
+
channel,
|
|
4485
|
+
error: error instanceof Error ? error.message : String(error)
|
|
4486
|
+
});
|
|
4487
|
+
throw error;
|
|
4488
|
+
}
|
|
4489
|
+
}
|
|
4490
|
+
/**
|
|
4491
|
+
* Unsubscribe from a Redis channel
|
|
4492
|
+
*/
|
|
4493
|
+
async unsubscribe(channel) {
|
|
4494
|
+
if (!this.subscriberClient) {
|
|
4495
|
+
return;
|
|
4496
|
+
}
|
|
4497
|
+
_RedisSSEClient.logger.info("Unsubscribing from Redis channel", { channel });
|
|
4498
|
+
try {
|
|
4499
|
+
await this.subscriberClient.unsubscribe(channel);
|
|
4500
|
+
_RedisSSEClient.logger.info(
|
|
4501
|
+
"Successfully unsubscribed from Redis channel",
|
|
4502
|
+
{ channel }
|
|
4503
|
+
);
|
|
4504
|
+
} catch (error) {
|
|
4505
|
+
_RedisSSEClient.logger.error("Failed to unsubscribe from Redis channel", {
|
|
4506
|
+
channel,
|
|
4507
|
+
error: error instanceof Error ? error.message : String(error)
|
|
4508
|
+
});
|
|
4509
|
+
}
|
|
4510
|
+
}
|
|
4511
|
+
/**
|
|
4512
|
+
* Clean up Redis connections
|
|
4513
|
+
*/
|
|
4514
|
+
async disconnect() {
|
|
4515
|
+
_RedisSSEClient.logger.info("Disconnecting Redis SSE clients");
|
|
4516
|
+
try {
|
|
4517
|
+
if (this.publisherClient) {
|
|
4518
|
+
this.publisherClient.disconnect();
|
|
4519
|
+
}
|
|
4520
|
+
if (this.subscriberClient) {
|
|
4521
|
+
this.subscriberClient.disconnect();
|
|
4522
|
+
}
|
|
4523
|
+
this.initialized = false;
|
|
4524
|
+
_RedisSSEClient.logger.info("Redis SSE clients disconnected successfully");
|
|
4525
|
+
} catch (error) {
|
|
4526
|
+
_RedisSSEClient.logger.error("Error disconnecting Redis SSE clients", {
|
|
4527
|
+
error: error instanceof Error ? error.message : String(error)
|
|
4528
|
+
});
|
|
4529
|
+
}
|
|
4530
|
+
}
|
|
4531
|
+
/**
|
|
4532
|
+
* Get connection status
|
|
4533
|
+
*/
|
|
4534
|
+
isConnected() {
|
|
4535
|
+
return this.initialized && this.publisherClient?.status === "ready" && this.subscriberClient?.status === "ready";
|
|
4536
|
+
}
|
|
4537
|
+
};
|
|
4538
|
+
|
|
4539
|
+
// packages/node-utils/src/sse/types.ts
|
|
4540
|
+
var SSE_REDIS_CHANNELS = {
|
|
4541
|
+
/**
|
|
4542
|
+
* Channel for subscription management messages
|
|
4543
|
+
* Used when clients subscribe/unsubscribe to specific event types
|
|
4544
|
+
*/
|
|
4545
|
+
SUBSCRIPTIONS: "sse:subscriptions",
|
|
4546
|
+
/**
|
|
4547
|
+
* Channel for event notifications
|
|
4548
|
+
* Used to broadcast SSE events to all API instances
|
|
4549
|
+
*/
|
|
4550
|
+
EVENTS: "sse:events"
|
|
4551
|
+
};
|
|
4552
|
+
function createSSEEventMessage(eventType, params, data, targetUserIds) {
|
|
4553
|
+
return {
|
|
4554
|
+
eventType,
|
|
4555
|
+
params,
|
|
4556
|
+
data,
|
|
4557
|
+
targetUserIds,
|
|
4558
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
4559
|
+
};
|
|
4560
|
+
}
|
|
4561
|
+
function serializeSSERedisMessage(message) {
|
|
4562
|
+
try {
|
|
4563
|
+
return JSON.stringify(message);
|
|
4564
|
+
} catch (error) {
|
|
4565
|
+
throw new Error(
|
|
4566
|
+
`Failed to serialize SSE Redis message: ${error instanceof Error ? error.message : String(error)}`
|
|
4567
|
+
);
|
|
4568
|
+
}
|
|
4569
|
+
}
|
|
4570
|
+
|
|
4571
|
+
// packages/node-utils/src/sse/SSEEventPublisher.ts
|
|
4572
|
+
var origin9 = "SSEEventPublisher";
|
|
4573
|
+
var SSEEventPublisher = class _SSEEventPublisher {
|
|
4574
|
+
static {
|
|
4575
|
+
this.logger = new PackmindLogger(origin9);
|
|
4576
|
+
}
|
|
4577
|
+
/**
|
|
4578
|
+
* Get the singleton instance
|
|
4579
|
+
*/
|
|
4580
|
+
static getInstance() {
|
|
4581
|
+
_SSEEventPublisher.logger.debug("Getting SSEEventPublisher instance");
|
|
4582
|
+
if (!_SSEEventPublisher.instance) {
|
|
4583
|
+
_SSEEventPublisher.logger.info("Creating new SSEEventPublisher instance");
|
|
4584
|
+
_SSEEventPublisher.instance = new _SSEEventPublisher();
|
|
4585
|
+
_SSEEventPublisher.redisClient = RedisSSEClient.getInstance();
|
|
4586
|
+
}
|
|
4587
|
+
return _SSEEventPublisher.instance;
|
|
4588
|
+
}
|
|
4589
|
+
constructor() {
|
|
4590
|
+
}
|
|
4591
|
+
/**
|
|
4592
|
+
* Publish a program status change event for cache invalidation
|
|
4593
|
+
* This triggers React Query to refetch the program data
|
|
4594
|
+
*/
|
|
4595
|
+
static async publishProgramStatusEvent(programId, ruleId, language, userId, organizationId) {
|
|
4596
|
+
_SSEEventPublisher.logger.info("Publishing program status change event", {
|
|
4597
|
+
programId,
|
|
4598
|
+
ruleId,
|
|
4599
|
+
language,
|
|
4600
|
+
userId,
|
|
4601
|
+
organizationId
|
|
4602
|
+
});
|
|
4603
|
+
try {
|
|
4604
|
+
const event = createProgramStatusChangeEvent(ruleId, language);
|
|
4605
|
+
await _SSEEventPublisher.publishEvent(
|
|
4606
|
+
"program_status_change",
|
|
4607
|
+
[ruleId, language],
|
|
4608
|
+
event,
|
|
4609
|
+
userId ? [userId] : void 0
|
|
4610
|
+
);
|
|
4611
|
+
_SSEEventPublisher.logger.debug(
|
|
4612
|
+
"Successfully published program status change event",
|
|
4613
|
+
{ programId, ruleId, language }
|
|
4614
|
+
);
|
|
4615
|
+
} catch (error) {
|
|
4616
|
+
_SSEEventPublisher.logger.error(
|
|
4617
|
+
"Failed to publish program status change event",
|
|
4618
|
+
{
|
|
4619
|
+
programId,
|
|
4620
|
+
userId,
|
|
4621
|
+
error: error instanceof Error ? error.message : String(error)
|
|
4622
|
+
}
|
|
4623
|
+
);
|
|
4624
|
+
throw error;
|
|
4625
|
+
}
|
|
4626
|
+
}
|
|
4627
|
+
/**
|
|
4628
|
+
* Publish an assessment status change event for cache invalidation
|
|
4629
|
+
* This triggers React Query to refetch the assessment data
|
|
4630
|
+
*/
|
|
4631
|
+
static async publishAssessmentStatusEvent(ruleId, language, userId, organizationId) {
|
|
4632
|
+
_SSEEventPublisher.logger.info("Publishing assessment status change event", {
|
|
4633
|
+
ruleId,
|
|
4634
|
+
language,
|
|
4635
|
+
userId,
|
|
4636
|
+
organizationId
|
|
4637
|
+
});
|
|
4638
|
+
try {
|
|
4639
|
+
const event = createAssessmentStatusChangeEvent(ruleId, language);
|
|
4640
|
+
await _SSEEventPublisher.publishEvent(
|
|
4641
|
+
"assessment_status_change",
|
|
4642
|
+
[ruleId, language],
|
|
4643
|
+
event,
|
|
4644
|
+
userId ? [userId] : void 0
|
|
4645
|
+
);
|
|
4646
|
+
_SSEEventPublisher.logger.debug(
|
|
4647
|
+
"Successfully published assessment status change event",
|
|
4648
|
+
{ ruleId, language }
|
|
4649
|
+
);
|
|
4650
|
+
} catch (error) {
|
|
4651
|
+
_SSEEventPublisher.logger.error(
|
|
4652
|
+
"Failed to publish assessment status change event",
|
|
4653
|
+
{
|
|
4654
|
+
userId,
|
|
4655
|
+
error: error instanceof Error ? error.message : String(error)
|
|
4656
|
+
}
|
|
4657
|
+
);
|
|
4658
|
+
throw error;
|
|
4659
|
+
}
|
|
4660
|
+
}
|
|
4661
|
+
/**
|
|
4662
|
+
* Publish a detection heuristics updated event for cache invalidation
|
|
4663
|
+
* This triggers React Query to refetch the heuristics data
|
|
4664
|
+
*/
|
|
4665
|
+
static async publishDetectionHeuristicsUpdatedEvent(ruleId, language, detectionHeuristicsId, userId, organizationId) {
|
|
4666
|
+
_SSEEventPublisher.logger.info(
|
|
4667
|
+
"Publishing detection heuristics updated event",
|
|
4668
|
+
{
|
|
4669
|
+
ruleId,
|
|
4670
|
+
language,
|
|
4671
|
+
detectionHeuristicsId,
|
|
4672
|
+
userId,
|
|
4673
|
+
organizationId
|
|
4674
|
+
}
|
|
4675
|
+
);
|
|
4676
|
+
try {
|
|
4677
|
+
const event = createDetectionHeuristicsUpdatedEvent(
|
|
4678
|
+
ruleId,
|
|
4679
|
+
language,
|
|
4680
|
+
detectionHeuristicsId
|
|
4681
|
+
);
|
|
4682
|
+
await _SSEEventPublisher.publishEvent(
|
|
4683
|
+
"detection_heuristics_updated",
|
|
4684
|
+
[ruleId, language, detectionHeuristicsId],
|
|
4685
|
+
event,
|
|
4686
|
+
userId ? [userId] : void 0
|
|
4687
|
+
);
|
|
4688
|
+
_SSEEventPublisher.logger.debug(
|
|
4689
|
+
"Successfully published detection heuristics updated event",
|
|
4690
|
+
{ ruleId, language, detectionHeuristicsId }
|
|
4691
|
+
);
|
|
4692
|
+
} catch (error) {
|
|
4693
|
+
_SSEEventPublisher.logger.error(
|
|
4694
|
+
"Failed to publish detection heuristics updated event",
|
|
4695
|
+
{
|
|
4696
|
+
ruleId,
|
|
4697
|
+
language,
|
|
4698
|
+
detectionHeuristicsId,
|
|
4699
|
+
userId,
|
|
4700
|
+
error: error instanceof Error ? error.message : String(error)
|
|
4701
|
+
}
|
|
4702
|
+
);
|
|
4703
|
+
throw error;
|
|
4704
|
+
}
|
|
4705
|
+
}
|
|
4706
|
+
/**
|
|
4707
|
+
* Publish an event to notify a user that their context (role or membership) changed
|
|
4708
|
+
* This should trigger a refetch of the /me route on the frontend
|
|
4709
|
+
*/
|
|
4710
|
+
static async publishUserContextChangeEvent(userId, organizationId, changeType, role) {
|
|
4711
|
+
_SSEEventPublisher.logger.info("Publishing user context change event", {
|
|
4712
|
+
userId,
|
|
4713
|
+
organizationId,
|
|
4714
|
+
changeType,
|
|
4715
|
+
role
|
|
4716
|
+
});
|
|
4717
|
+
try {
|
|
4718
|
+
const event = createUserContextChangeEvent(
|
|
4719
|
+
userId,
|
|
4720
|
+
organizationId,
|
|
4721
|
+
changeType,
|
|
4722
|
+
role
|
|
4723
|
+
);
|
|
4724
|
+
await _SSEEventPublisher.publishEvent("user_context_change", [], event, [
|
|
4725
|
+
userId
|
|
4726
|
+
]);
|
|
4727
|
+
_SSEEventPublisher.logger.debug(
|
|
4728
|
+
"Successfully published user context change event",
|
|
4729
|
+
{
|
|
4730
|
+
userId,
|
|
4731
|
+
organizationId,
|
|
4732
|
+
changeType
|
|
4733
|
+
}
|
|
4734
|
+
);
|
|
4735
|
+
} catch (error) {
|
|
4736
|
+
_SSEEventPublisher.logger.error(
|
|
4737
|
+
"Failed to publish user context change event",
|
|
4738
|
+
{
|
|
4739
|
+
userId,
|
|
4740
|
+
organizationId,
|
|
4741
|
+
changeType,
|
|
4742
|
+
error: error instanceof Error ? error.message : String(error)
|
|
4743
|
+
}
|
|
4744
|
+
);
|
|
4745
|
+
throw error;
|
|
4746
|
+
}
|
|
4747
|
+
}
|
|
4748
|
+
/**
|
|
4749
|
+
* Generic method to publish any SSE event type to Redis pub/sub
|
|
4750
|
+
*/
|
|
4751
|
+
static async publishEvent(eventType, params, data, targetUserIds) {
|
|
4752
|
+
_SSEEventPublisher.logger.info("Publishing SSE event", {
|
|
4753
|
+
eventType,
|
|
4754
|
+
params,
|
|
4755
|
+
targetUserIds: targetUserIds?.length || "all"
|
|
4756
|
+
});
|
|
4757
|
+
try {
|
|
4758
|
+
_SSEEventPublisher.getInstance();
|
|
4759
|
+
const redisClient = _SSEEventPublisher.redisClient;
|
|
4760
|
+
const message = createSSEEventMessage(
|
|
4761
|
+
eventType,
|
|
4762
|
+
params,
|
|
4763
|
+
data,
|
|
4764
|
+
targetUserIds
|
|
4765
|
+
);
|
|
4766
|
+
const serializedMessage = serializeSSERedisMessage(message);
|
|
4767
|
+
await redisClient.publish(SSE_REDIS_CHANNELS.EVENTS, serializedMessage);
|
|
4768
|
+
_SSEEventPublisher.logger.debug("Successfully published SSE event", {
|
|
4769
|
+
eventType,
|
|
4770
|
+
params
|
|
4771
|
+
});
|
|
4772
|
+
} catch (error) {
|
|
4773
|
+
_SSEEventPublisher.logger.error("Failed to publish SSE event", {
|
|
4774
|
+
eventType,
|
|
4775
|
+
params,
|
|
4776
|
+
error: error instanceof Error ? error.message : String(error)
|
|
4777
|
+
});
|
|
4778
|
+
throw error;
|
|
4779
|
+
}
|
|
4780
|
+
}
|
|
4781
|
+
};
|
|
4782
|
+
|
|
4783
|
+
// packages/node-utils/src/text/sectionMerge.ts
|
|
4784
|
+
function mergeSectionsIntoFileContent(existingContent, sections) {
|
|
4785
|
+
let result = existingContent;
|
|
4786
|
+
for (const section of sections) {
|
|
4787
|
+
const startMarker = `<!-- start: ${section.key} -->`;
|
|
4788
|
+
const endMarker = `<!-- end: ${section.key} -->`;
|
|
4789
|
+
const startIndex = result.indexOf(startMarker);
|
|
4790
|
+
const endIndex = result.indexOf(endMarker);
|
|
4791
|
+
if (startIndex !== -1 && endIndex !== -1 && endIndex > startIndex) {
|
|
4792
|
+
const before = result.substring(0, startIndex + startMarker.length);
|
|
4793
|
+
const after = result.substring(endIndex);
|
|
4794
|
+
result = `${before}
|
|
4795
|
+
${section.content}
|
|
4796
|
+
${after}`;
|
|
4797
|
+
} else {
|
|
4798
|
+
const sectionBlock = `${startMarker}
|
|
4799
|
+
${section.content}
|
|
4800
|
+
${endMarker}`;
|
|
4801
|
+
if (result.trim() === "") {
|
|
4802
|
+
result = sectionBlock;
|
|
4803
|
+
} else {
|
|
4804
|
+
result = result.endsWith("\n") ? `${result}${sectionBlock}
|
|
4805
|
+
` : `${result}
|
|
4806
|
+
${sectionBlock}
|
|
4807
|
+
`;
|
|
4808
|
+
}
|
|
4809
|
+
}
|
|
4810
|
+
}
|
|
4811
|
+
return result;
|
|
4812
|
+
}
|
|
4813
|
+
|
|
4814
|
+
// apps/cli/src/application/useCases/PullDataUseCase.ts
|
|
4815
|
+
var fs4 = __toESM(require("fs/promises"));
|
|
4816
|
+
var path4 = __toESM(require("path"));
|
|
4817
|
+
var PullDataUseCase = class {
|
|
4818
|
+
constructor(packmindGateway) {
|
|
4819
|
+
this.packmindGateway = packmindGateway;
|
|
4820
|
+
}
|
|
4821
|
+
async execute(command3) {
|
|
4822
|
+
const baseDirectory = command3.baseDirectory || process.cwd();
|
|
4823
|
+
const result = {
|
|
4824
|
+
filesCreated: 0,
|
|
4825
|
+
filesUpdated: 0,
|
|
4826
|
+
filesDeleted: 0,
|
|
4827
|
+
errors: [],
|
|
4828
|
+
recipesCount: 0,
|
|
4829
|
+
standardsCount: 0
|
|
4830
|
+
};
|
|
4831
|
+
const response = await this.packmindGateway.getPullData({
|
|
4832
|
+
packagesSlugs: command3.packagesSlugs
|
|
4833
|
+
});
|
|
4834
|
+
const uniqueFilesMap = /* @__PURE__ */ new Map();
|
|
4835
|
+
for (const file of response.fileUpdates.createOrUpdate) {
|
|
4836
|
+
uniqueFilesMap.set(file.path, file);
|
|
4837
|
+
}
|
|
4838
|
+
const uniqueFiles = Array.from(uniqueFilesMap.values());
|
|
4839
|
+
for (const file of uniqueFiles) {
|
|
4840
|
+
if (file.path.includes(".packmind/recipes/") && file.path.endsWith(".md")) {
|
|
4841
|
+
result.recipesCount++;
|
|
4842
|
+
} else if (file.path.includes(".packmind/standards/") && file.path.endsWith(".md")) {
|
|
4843
|
+
result.standardsCount++;
|
|
4844
|
+
}
|
|
4845
|
+
}
|
|
4846
|
+
try {
|
|
4847
|
+
for (const file of uniqueFiles) {
|
|
4848
|
+
try {
|
|
4849
|
+
await this.createOrUpdateFile(baseDirectory, file, result);
|
|
4850
|
+
} catch (error) {
|
|
4851
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
4852
|
+
result.errors.push(
|
|
4853
|
+
`Failed to create/update ${file.path}: ${errorMsg}`
|
|
4854
|
+
);
|
|
4855
|
+
}
|
|
4856
|
+
}
|
|
4857
|
+
for (const file of response.fileUpdates.delete) {
|
|
4858
|
+
try {
|
|
4859
|
+
await this.deleteFile(baseDirectory, file.path, result);
|
|
4860
|
+
} catch (error) {
|
|
4861
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
4862
|
+
result.errors.push(`Failed to delete ${file.path}: ${errorMsg}`);
|
|
4863
|
+
}
|
|
4864
|
+
}
|
|
4865
|
+
} catch (error) {
|
|
4866
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
4867
|
+
result.errors.push(`Failed to pull data: ${errorMsg}`);
|
|
4868
|
+
}
|
|
4869
|
+
return result;
|
|
4870
|
+
}
|
|
4871
|
+
async createOrUpdateFile(baseDirectory, file, result) {
|
|
4872
|
+
const fullPath = path4.join(baseDirectory, file.path);
|
|
4873
|
+
const directory = path4.dirname(fullPath);
|
|
4874
|
+
await fs4.mkdir(directory, { recursive: true });
|
|
4875
|
+
const fileExists = await this.fileExists(fullPath);
|
|
4876
|
+
if (file.content !== void 0) {
|
|
4877
|
+
await this.handleFullContentUpdate(
|
|
4878
|
+
fullPath,
|
|
4879
|
+
file.content,
|
|
4880
|
+
fileExists,
|
|
4881
|
+
result
|
|
4882
|
+
);
|
|
4883
|
+
} else if (file.sections !== void 0) {
|
|
4884
|
+
await this.handleSectionsUpdate(
|
|
4885
|
+
fullPath,
|
|
4886
|
+
file.sections,
|
|
4887
|
+
fileExists,
|
|
4888
|
+
result
|
|
4889
|
+
);
|
|
4890
|
+
}
|
|
4891
|
+
}
|
|
4892
|
+
async handleFullContentUpdate(fullPath, content, fileExists, result) {
|
|
4893
|
+
if (fileExists) {
|
|
4894
|
+
const existingContent = await fs4.readFile(fullPath, "utf-8");
|
|
4895
|
+
const commentMarker = this.extractCommentMarker(content);
|
|
4896
|
+
let finalContent;
|
|
4897
|
+
if (!commentMarker) {
|
|
4898
|
+
finalContent = content;
|
|
4899
|
+
} else {
|
|
4900
|
+
finalContent = this.mergeContentWithMarkers(
|
|
4901
|
+
existingContent,
|
|
4902
|
+
content,
|
|
4903
|
+
commentMarker
|
|
4904
|
+
);
|
|
4905
|
+
}
|
|
4906
|
+
await fs4.writeFile(fullPath, finalContent, "utf-8");
|
|
4907
|
+
result.filesUpdated++;
|
|
4908
|
+
} else {
|
|
4909
|
+
await fs4.writeFile(fullPath, content, "utf-8");
|
|
4910
|
+
result.filesCreated++;
|
|
4911
|
+
}
|
|
4912
|
+
}
|
|
4913
|
+
async handleSectionsUpdate(fullPath, sections, fileExists, result) {
|
|
4914
|
+
let currentContent = "";
|
|
4915
|
+
if (fileExists) {
|
|
4916
|
+
currentContent = await fs4.readFile(fullPath, "utf-8");
|
|
4917
|
+
}
|
|
4918
|
+
const mergedContent = mergeSectionsIntoFileContent(
|
|
4919
|
+
currentContent,
|
|
4920
|
+
sections
|
|
4921
|
+
);
|
|
4922
|
+
await fs4.writeFile(fullPath, mergedContent, "utf-8");
|
|
4923
|
+
if (fileExists) {
|
|
4924
|
+
result.filesUpdated++;
|
|
4925
|
+
} else {
|
|
4926
|
+
result.filesCreated++;
|
|
4927
|
+
}
|
|
4928
|
+
}
|
|
4929
|
+
async deleteFile(baseDirectory, filePath, result) {
|
|
4930
|
+
const fullPath = path4.join(baseDirectory, filePath);
|
|
4931
|
+
const fileExists = await this.fileExists(fullPath);
|
|
4932
|
+
if (fileExists) {
|
|
4933
|
+
await fs4.unlink(fullPath);
|
|
4934
|
+
result.filesDeleted++;
|
|
4935
|
+
}
|
|
4936
|
+
}
|
|
4937
|
+
async fileExists(filePath) {
|
|
4938
|
+
try {
|
|
4939
|
+
await fs4.access(filePath);
|
|
4940
|
+
return true;
|
|
4941
|
+
} catch {
|
|
4942
|
+
return false;
|
|
4943
|
+
}
|
|
4944
|
+
}
|
|
4945
|
+
/**
|
|
4946
|
+
* Extracts the comment marker from content if it's wrapped with HTML comments.
|
|
4947
|
+
* E.g., "<!-- start: Packmind recipes -->" returns "Packmind recipes"
|
|
4948
|
+
*/
|
|
4949
|
+
extractCommentMarker(content) {
|
|
4950
|
+
const startMarkerPattern = /<!--\s*start:\s*([^-]+?)\s*-->/;
|
|
4951
|
+
const match = content.match(startMarkerPattern);
|
|
4952
|
+
return match ? match[1].trim() : null;
|
|
4953
|
+
}
|
|
4954
|
+
/**
|
|
4955
|
+
* Merges new content with existing content using comment markers.
|
|
4956
|
+
* If the section exists, replaces it; otherwise, appends it.
|
|
4957
|
+
*/
|
|
4958
|
+
mergeContentWithMarkers(existingContent, newContent, commentMarker) {
|
|
4959
|
+
const startMarker = `<!-- start: ${commentMarker} -->`;
|
|
4960
|
+
const endMarker = `<!-- end: ${commentMarker} -->`;
|
|
4961
|
+
const newSectionPattern = new RegExp(
|
|
4962
|
+
`${this.escapeRegex(startMarker)}([\\s\\S]*?)${this.escapeRegex(endMarker)}`
|
|
4963
|
+
);
|
|
4964
|
+
const newSectionMatch = newContent.match(newSectionPattern);
|
|
4965
|
+
const newSectionContent = newSectionMatch ? newSectionMatch[1].trim() : newContent;
|
|
4966
|
+
const existingSectionPattern = new RegExp(
|
|
4967
|
+
`${this.escapeRegex(startMarker)}[\\s\\S]*?${this.escapeRegex(endMarker)}`,
|
|
4968
|
+
"g"
|
|
4969
|
+
);
|
|
4970
|
+
if (existingSectionPattern.test(existingContent)) {
|
|
4971
|
+
return existingContent.replace(
|
|
4972
|
+
existingSectionPattern,
|
|
4973
|
+
`${startMarker}
|
|
4974
|
+
${newSectionContent}
|
|
4975
|
+
${endMarker}`
|
|
4976
|
+
);
|
|
4977
|
+
} else {
|
|
4978
|
+
return `${existingContent}
|
|
4979
|
+
${startMarker}
|
|
4980
|
+
${newSectionContent}
|
|
4981
|
+
${endMarker}`;
|
|
4982
|
+
}
|
|
4983
|
+
}
|
|
4984
|
+
/**
|
|
4985
|
+
* Escapes special regex characters in a string
|
|
4986
|
+
*/
|
|
4987
|
+
escapeRegex(str) {
|
|
4988
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
4989
|
+
}
|
|
4990
|
+
};
|
|
4991
|
+
|
|
4992
|
+
// apps/cli/src/application/useCases/ListPackagesUseCase.ts
|
|
4993
|
+
var ListPackagesUseCase = class {
|
|
4994
|
+
constructor(packmindGateway) {
|
|
4995
|
+
this.packmindGateway = packmindGateway;
|
|
4996
|
+
}
|
|
4997
|
+
async execute() {
|
|
4998
|
+
return this.packmindGateway.listPackages({});
|
|
4999
|
+
}
|
|
5000
|
+
};
|
|
5001
|
+
|
|
5002
|
+
// apps/cli/src/application/useCases/GetPackageSummaryUseCase.ts
|
|
5003
|
+
var GetPackageSummaryUseCase = class {
|
|
5004
|
+
constructor(gateway) {
|
|
5005
|
+
this.gateway = gateway;
|
|
5006
|
+
}
|
|
5007
|
+
async execute(command3) {
|
|
5008
|
+
return this.gateway.getPackageSummary(command3);
|
|
5009
|
+
}
|
|
5010
|
+
};
|
|
5011
|
+
|
|
5012
|
+
// apps/cli/src/infra/repositories/ConfigFileRepository.ts
|
|
5013
|
+
var fs5 = __toESM(require("fs/promises"));
|
|
5014
|
+
var path5 = __toESM(require("path"));
|
|
5015
|
+
var ConfigFileRepository = class {
|
|
5016
|
+
constructor() {
|
|
5017
|
+
this.CONFIG_FILENAME = "packmind.json";
|
|
5018
|
+
this.EXCLUDED_DIRECTORIES = [
|
|
5019
|
+
"node_modules",
|
|
5020
|
+
".git",
|
|
5021
|
+
"dist",
|
|
5022
|
+
"build",
|
|
5023
|
+
"coverage",
|
|
5024
|
+
".nx"
|
|
5025
|
+
];
|
|
5026
|
+
}
|
|
5027
|
+
async writeConfig(baseDirectory, config) {
|
|
5028
|
+
const configPath = path5.join(baseDirectory, this.CONFIG_FILENAME);
|
|
5029
|
+
const configContent = JSON.stringify(config, null, 2) + "\n";
|
|
5030
|
+
await fs5.writeFile(configPath, configContent, "utf-8");
|
|
5031
|
+
}
|
|
5032
|
+
async readConfig(baseDirectory) {
|
|
5033
|
+
const configPath = path5.join(baseDirectory, this.CONFIG_FILENAME);
|
|
5034
|
+
try {
|
|
5035
|
+
const configContent = await fs5.readFile(configPath, "utf-8");
|
|
5036
|
+
const config = JSON.parse(configContent);
|
|
5037
|
+
if (!config.packages || typeof config.packages !== "object") {
|
|
5038
|
+
throw new Error(
|
|
5039
|
+
"Invalid packmind.json structure. Expected { packages: { ... } }"
|
|
5040
|
+
);
|
|
5041
|
+
}
|
|
5042
|
+
return config;
|
|
5043
|
+
} catch (error) {
|
|
5044
|
+
if (error.code === "ENOENT") {
|
|
5045
|
+
return null;
|
|
5046
|
+
}
|
|
5047
|
+
throw new Error(
|
|
5048
|
+
`Failed to read packmind.json: ${error.message}`
|
|
5049
|
+
);
|
|
5050
|
+
}
|
|
5051
|
+
}
|
|
5052
|
+
/**
|
|
5053
|
+
* Recursively finds all directories containing packmind.json in descendant folders.
|
|
5054
|
+
* Excludes common build/dependency directories (node_modules, .git, dist, etc.)
|
|
5055
|
+
*
|
|
5056
|
+
* @param directory - The root directory to search from
|
|
5057
|
+
* @returns Array of directory paths that contain a packmind.json file
|
|
5058
|
+
*/
|
|
5059
|
+
async findDescendantConfigs(directory) {
|
|
5060
|
+
const normalizedDir = path5.resolve(directory);
|
|
5061
|
+
const results = [];
|
|
5062
|
+
const searchRecursively = async (currentDir) => {
|
|
5063
|
+
let entries;
|
|
5064
|
+
try {
|
|
5065
|
+
entries = await fs5.readdir(currentDir, { withFileTypes: true });
|
|
5066
|
+
} catch {
|
|
5067
|
+
return;
|
|
5068
|
+
}
|
|
5069
|
+
for (const entry of entries) {
|
|
5070
|
+
if (!entry.isDirectory()) {
|
|
5071
|
+
continue;
|
|
5072
|
+
}
|
|
5073
|
+
if (this.EXCLUDED_DIRECTORIES.includes(entry.name)) {
|
|
5074
|
+
continue;
|
|
5075
|
+
}
|
|
5076
|
+
const entryPath = path5.join(currentDir, entry.name);
|
|
5077
|
+
const config = await this.readConfig(entryPath);
|
|
5078
|
+
if (config) {
|
|
5079
|
+
results.push(entryPath);
|
|
5080
|
+
}
|
|
5081
|
+
await searchRecursively(entryPath);
|
|
5082
|
+
}
|
|
5083
|
+
};
|
|
5084
|
+
await searchRecursively(normalizedDir);
|
|
5085
|
+
return results;
|
|
5086
|
+
}
|
|
5087
|
+
/**
|
|
5088
|
+
* Reads all packmind.json files from startDirectory up to stopDirectory (inclusive)
|
|
5089
|
+
* and merges their package configurations.
|
|
5090
|
+
*
|
|
5091
|
+
* @param startDirectory - Directory to start searching from (typically the lint target)
|
|
5092
|
+
* @param stopDirectory - Directory to stop searching at (typically git repo root), or null to walk to filesystem root
|
|
5093
|
+
* @returns Merged configuration from all found packmind.json files
|
|
5094
|
+
*/
|
|
5095
|
+
async readHierarchicalConfig(startDirectory, stopDirectory) {
|
|
5096
|
+
const configs = [];
|
|
5097
|
+
const configPaths = [];
|
|
5098
|
+
const normalizedStart = path5.resolve(startDirectory);
|
|
5099
|
+
const normalizedStop = stopDirectory ? path5.resolve(stopDirectory) : null;
|
|
5100
|
+
let currentDir = normalizedStart;
|
|
5101
|
+
while (true) {
|
|
5102
|
+
const config = await this.readConfig(currentDir);
|
|
5103
|
+
if (config) {
|
|
5104
|
+
configs.push(config);
|
|
5105
|
+
configPaths.push(path5.join(currentDir, this.CONFIG_FILENAME));
|
|
5106
|
+
}
|
|
5107
|
+
if (normalizedStop !== null && currentDir === normalizedStop) {
|
|
5108
|
+
break;
|
|
5109
|
+
}
|
|
5110
|
+
const parentDir = path5.dirname(currentDir);
|
|
5111
|
+
if (parentDir === currentDir) {
|
|
5112
|
+
break;
|
|
5113
|
+
}
|
|
5114
|
+
currentDir = parentDir;
|
|
5115
|
+
}
|
|
5116
|
+
const mergedPackages = {};
|
|
5117
|
+
for (const config of configs) {
|
|
5118
|
+
for (const [slug, version] of Object.entries(config.packages)) {
|
|
5119
|
+
if (!(slug in mergedPackages)) {
|
|
5120
|
+
mergedPackages[slug] = version;
|
|
5121
|
+
}
|
|
5122
|
+
}
|
|
5123
|
+
}
|
|
5124
|
+
return {
|
|
5125
|
+
packages: mergedPackages,
|
|
5126
|
+
configPaths,
|
|
5127
|
+
hasConfigs: configs.length > 0
|
|
5128
|
+
};
|
|
5129
|
+
}
|
|
5130
|
+
};
|
|
5131
|
+
|
|
5132
|
+
// apps/cli/src/PackmindCliHexaFactory.ts
|
|
5133
|
+
var PackmindCliHexaFactory = class {
|
|
5134
|
+
constructor(logger2) {
|
|
5135
|
+
this.logger = logger2;
|
|
5136
|
+
this.repositories = {
|
|
5137
|
+
packmindGateway: new PackmindGateway(
|
|
5138
|
+
process.env.PACKMIND_API_KEY_V3 || ""
|
|
5139
|
+
),
|
|
5140
|
+
configFileRepository: new ConfigFileRepository()
|
|
5141
|
+
};
|
|
5142
|
+
this.services = {
|
|
5143
|
+
listFiles: new ListFiles(),
|
|
5144
|
+
gitRemoteUrlService: new GitService(this.logger),
|
|
5145
|
+
linterExecutionUseCase: new ExecuteLinterProgramsUseCase()
|
|
5146
|
+
};
|
|
5147
|
+
this.useCases = {
|
|
5148
|
+
executeSingleFileAst: new ExecuteSingleFileAstUseCase(
|
|
5149
|
+
this.services.linterExecutionUseCase
|
|
5150
|
+
),
|
|
5151
|
+
getGitRemoteUrl: new GetGitRemoteUrlUseCase(),
|
|
5152
|
+
listFilesInDirectoryUseCase: new ListFilesInDirectoryUseCase(),
|
|
5153
|
+
lintFilesInDirectory: new LintFilesInDirectoryUseCase(
|
|
5154
|
+
this.services,
|
|
5155
|
+
this.repositories,
|
|
5156
|
+
this.logger
|
|
5157
|
+
),
|
|
5158
|
+
lintFilesLocally: new LintFilesLocallyUseCase(
|
|
5159
|
+
this.services,
|
|
5160
|
+
this.repositories,
|
|
5161
|
+
this.logger
|
|
5162
|
+
),
|
|
5163
|
+
pullData: new PullDataUseCase(this.repositories.packmindGateway),
|
|
5164
|
+
listPackages: new ListPackagesUseCase(this.repositories.packmindGateway),
|
|
5165
|
+
getPackageBySlug: new GetPackageSummaryUseCase(
|
|
5166
|
+
this.repositories.packmindGateway
|
|
5167
|
+
)
|
|
5168
|
+
};
|
|
5169
|
+
}
|
|
5170
|
+
};
|
|
5171
|
+
|
|
5172
|
+
// apps/cli/src/PackmindCliHexa.ts
|
|
5173
|
+
var origin10 = "PackmindCliHexa";
|
|
5174
|
+
var PackmindCliHexa = class {
|
|
5175
|
+
constructor(logger2 = new PackmindLogger(origin10)) {
|
|
5176
|
+
this.logger = logger2;
|
|
5177
|
+
try {
|
|
5178
|
+
this.hexa = new PackmindCliHexaFactory(this.logger);
|
|
5179
|
+
} catch (error) {
|
|
5180
|
+
this.logger.error("Failed to initialize PackmindCliHexa", {
|
|
5181
|
+
error: error instanceof Error ? error.message : String(error)
|
|
5182
|
+
});
|
|
5183
|
+
throw error;
|
|
5184
|
+
}
|
|
5185
|
+
}
|
|
5186
|
+
/**
|
|
5187
|
+
* Destroys the DeploymentsHexa and cleans up resources
|
|
5188
|
+
*/
|
|
5189
|
+
destroy() {
|
|
5190
|
+
this.logger.info("Destroying PackmindCliHexa");
|
|
5191
|
+
this.logger.info("PackmindCliHexa destroyed");
|
|
5192
|
+
}
|
|
5193
|
+
async getGitRemoteUrl(command3) {
|
|
5194
|
+
return this.hexa.useCases.getGitRemoteUrl.execute(command3);
|
|
5195
|
+
}
|
|
5196
|
+
async executeSingleFileAst(command3) {
|
|
5197
|
+
return this.hexa.useCases.executeSingleFileAst.execute(command3);
|
|
5198
|
+
}
|
|
5199
|
+
async listFilesInDirectory(command3) {
|
|
5200
|
+
return this.hexa.useCases.listFilesInDirectoryUseCase.execute(command3);
|
|
3345
5201
|
}
|
|
3346
5202
|
async lintFilesInDirectory(command3) {
|
|
3347
5203
|
return this.hexa.useCases.lintFilesInDirectory.execute(command3);
|
|
3348
5204
|
}
|
|
5205
|
+
async lintFilesLocally(command3) {
|
|
5206
|
+
return this.hexa.useCases.lintFilesLocally.execute(command3);
|
|
5207
|
+
}
|
|
3349
5208
|
async pullData(command3) {
|
|
3350
5209
|
return this.hexa.useCases.pullData.execute(command3);
|
|
3351
5210
|
}
|
|
5211
|
+
async listPackages(command3) {
|
|
5212
|
+
return this.hexa.useCases.listPackages.execute(command3);
|
|
5213
|
+
}
|
|
5214
|
+
async getPackageBySlug(command3) {
|
|
5215
|
+
return this.hexa.useCases.getPackageBySlug.execute(command3);
|
|
5216
|
+
}
|
|
5217
|
+
async writeConfig(baseDirectory, packagesSlugs) {
|
|
5218
|
+
const config = {
|
|
5219
|
+
packages: packagesSlugs.reduce(
|
|
5220
|
+
(acc, slug) => {
|
|
5221
|
+
acc[slug] = "*";
|
|
5222
|
+
return acc;
|
|
5223
|
+
},
|
|
5224
|
+
{}
|
|
5225
|
+
)
|
|
5226
|
+
};
|
|
5227
|
+
await this.hexa.repositories.configFileRepository.writeConfig(
|
|
5228
|
+
baseDirectory,
|
|
5229
|
+
config
|
|
5230
|
+
);
|
|
5231
|
+
}
|
|
5232
|
+
async readConfig(baseDirectory) {
|
|
5233
|
+
const config = await this.hexa.repositories.configFileRepository.readConfig(
|
|
5234
|
+
baseDirectory
|
|
5235
|
+
);
|
|
5236
|
+
if (!config) return [];
|
|
5237
|
+
const hasNonWildcardVersions = Object.values(config.packages).some(
|
|
5238
|
+
(version) => version !== "*"
|
|
5239
|
+
);
|
|
5240
|
+
if (hasNonWildcardVersions) {
|
|
5241
|
+
console.log(
|
|
5242
|
+
"WARN: Package versions are not supported yet, getting the latest version"
|
|
5243
|
+
);
|
|
5244
|
+
}
|
|
5245
|
+
return Object.keys(config.packages);
|
|
5246
|
+
}
|
|
5247
|
+
async readHierarchicalConfig(startDirectory, stopDirectory) {
|
|
5248
|
+
return this.hexa.repositories.configFileRepository.readHierarchicalConfig(
|
|
5249
|
+
startDirectory,
|
|
5250
|
+
stopDirectory
|
|
5251
|
+
);
|
|
5252
|
+
}
|
|
5253
|
+
async findDescendantConfigs(directory) {
|
|
5254
|
+
return this.hexa.repositories.configFileRepository.findDescendantConfigs(
|
|
5255
|
+
directory
|
|
5256
|
+
);
|
|
5257
|
+
}
|
|
5258
|
+
async getGitRepositoryRoot(directory) {
|
|
5259
|
+
return this.hexa.services.gitRemoteUrlService.getGitRepositoryRoot(
|
|
5260
|
+
directory
|
|
5261
|
+
);
|
|
5262
|
+
}
|
|
5263
|
+
async tryGetGitRepositoryRoot(directory) {
|
|
5264
|
+
return this.hexa.services.gitRemoteUrlService.tryGetGitRepositoryRoot(
|
|
5265
|
+
directory
|
|
5266
|
+
);
|
|
5267
|
+
}
|
|
3352
5268
|
};
|
|
3353
5269
|
|
|
3354
5270
|
// apps/cli/src/infra/repositories/IDELintLogger.ts
|
|
@@ -3403,6 +5319,7 @@ var HumanReadableLogger = class {
|
|
|
3403
5319
|
};
|
|
3404
5320
|
|
|
3405
5321
|
// apps/cli/src/infra/commands/LinterCommand.ts
|
|
5322
|
+
var pathModule = __toESM(require("path"));
|
|
3406
5323
|
var Logger = {
|
|
3407
5324
|
from: async (input) => {
|
|
3408
5325
|
switch (input) {
|
|
@@ -3465,7 +5382,7 @@ var lintCommand = (0, import_cmd_ts.command)({
|
|
|
3465
5382
|
description: "Enable debug logging"
|
|
3466
5383
|
})
|
|
3467
5384
|
},
|
|
3468
|
-
handler: async ({ path:
|
|
5385
|
+
handler: async ({ path: path7, draft, rule, debug, language, logger: logger2 }) => {
|
|
3469
5386
|
if (draft && !rule) {
|
|
3470
5387
|
throw new Error("option --rule is required to use --draft mode");
|
|
3471
5388
|
}
|
|
@@ -3475,14 +5392,53 @@ var lintCommand = (0, import_cmd_ts.command)({
|
|
|
3475
5392
|
debug ? "debug" /* DEBUG */ : "info" /* INFO */
|
|
3476
5393
|
);
|
|
3477
5394
|
const packmindCliHexa = new PackmindCliHexa(packmindLogger);
|
|
3478
|
-
const
|
|
3479
|
-
|
|
3480
|
-
|
|
3481
|
-
|
|
3482
|
-
|
|
3483
|
-
|
|
3484
|
-
|
|
3485
|
-
|
|
5395
|
+
const targetPath = path7 ?? ".";
|
|
5396
|
+
const hasArguments = !!(draft || rule || language);
|
|
5397
|
+
const absolutePath = pathModule.isAbsolute(targetPath) ? targetPath : pathModule.resolve(process.cwd(), targetPath);
|
|
5398
|
+
let useLocalLinting = false;
|
|
5399
|
+
let lintTargets = [];
|
|
5400
|
+
if (!hasArguments) {
|
|
5401
|
+
const stopDirectory = await packmindCliHexa.tryGetGitRepositoryRoot(absolutePath);
|
|
5402
|
+
const hierarchicalConfig = await packmindCliHexa.readHierarchicalConfig(
|
|
5403
|
+
absolutePath,
|
|
5404
|
+
stopDirectory
|
|
5405
|
+
);
|
|
5406
|
+
if (hierarchicalConfig.hasConfigs) {
|
|
5407
|
+
useLocalLinting = true;
|
|
5408
|
+
const rootConfig = await packmindCliHexa.readHierarchicalConfig(
|
|
5409
|
+
absolutePath,
|
|
5410
|
+
absolutePath
|
|
5411
|
+
);
|
|
5412
|
+
if (rootConfig.hasConfigs) {
|
|
5413
|
+
lintTargets.push(absolutePath);
|
|
5414
|
+
}
|
|
5415
|
+
const descendantTargets = await packmindCliHexa.findDescendantConfigs(absolutePath);
|
|
5416
|
+
lintTargets = [...lintTargets, ...descendantTargets];
|
|
5417
|
+
if (lintTargets.length === 0) {
|
|
5418
|
+
lintTargets.push(absolutePath);
|
|
5419
|
+
}
|
|
5420
|
+
}
|
|
5421
|
+
}
|
|
5422
|
+
let violations = [];
|
|
5423
|
+
if (useLocalLinting && lintTargets.length > 0) {
|
|
5424
|
+
for (const target of lintTargets) {
|
|
5425
|
+
packmindLogger.debug(`Linting target: ${target}`);
|
|
5426
|
+
const result = await packmindCliHexa.lintFilesLocally({
|
|
5427
|
+
path: target
|
|
5428
|
+
});
|
|
5429
|
+
violations = [...violations, ...result.violations];
|
|
5430
|
+
}
|
|
5431
|
+
} else {
|
|
5432
|
+
const result = await packmindCliHexa.lintFilesInDirectory({
|
|
5433
|
+
path: targetPath,
|
|
5434
|
+
draftMode: draft,
|
|
5435
|
+
standardSlug: rule?.standardSlug,
|
|
5436
|
+
ruleId: rule?.ruleId,
|
|
5437
|
+
language
|
|
5438
|
+
});
|
|
5439
|
+
violations = result.violations;
|
|
5440
|
+
}
|
|
5441
|
+
(logger2 === "ide" /* ide */ ? new IDELintLogger() : new HumanReadableLogger()).logViolations(violations);
|
|
3486
5442
|
const durationSeconds = (Date.now() - startedAt) / 1e3;
|
|
3487
5443
|
console.log(`Lint completed in ${durationSeconds.toFixed(2)}s`);
|
|
3488
5444
|
if (violations.length > 0) {
|
|
@@ -3549,27 +5505,158 @@ function extractWasmFiles() {
|
|
|
3549
5505
|
|
|
3550
5506
|
// apps/cli/src/main.ts
|
|
3551
5507
|
var import_dotenv = require("dotenv");
|
|
3552
|
-
var
|
|
3553
|
-
var
|
|
5508
|
+
var fs6 = __toESM(require("fs"));
|
|
5509
|
+
var path6 = __toESM(require("path"));
|
|
3554
5510
|
|
|
3555
5511
|
// apps/cli/src/infra/commands/PullCommand.ts
|
|
3556
5512
|
var import_cmd_ts2 = require("cmd-ts");
|
|
3557
5513
|
var pullCommand = (0, import_cmd_ts2.command)({
|
|
3558
5514
|
name: "pull",
|
|
3559
|
-
description: "Pull recipes and standards from
|
|
3560
|
-
args: {
|
|
3561
|
-
|
|
3562
|
-
|
|
5515
|
+
description: "Pull recipes and standards from specified packages and save them to the current directory",
|
|
5516
|
+
args: {
|
|
5517
|
+
list: (0, import_cmd_ts2.flag)({
|
|
5518
|
+
long: "list",
|
|
5519
|
+
description: "List available packages"
|
|
5520
|
+
}),
|
|
5521
|
+
show: (0, import_cmd_ts2.option)({
|
|
5522
|
+
type: import_cmd_ts2.string,
|
|
5523
|
+
long: "show",
|
|
5524
|
+
description: "Show details of a specific package",
|
|
5525
|
+
defaultValue: () => ""
|
|
5526
|
+
}),
|
|
5527
|
+
packagesSlugs: (0, import_cmd_ts2.restPositionals)({
|
|
5528
|
+
type: import_cmd_ts2.string,
|
|
5529
|
+
displayName: "packages",
|
|
5530
|
+
description: "Package slugs to pull content from (e.g., backend frontend)"
|
|
5531
|
+
})
|
|
5532
|
+
},
|
|
5533
|
+
handler: async ({ list, show, packagesSlugs }) => {
|
|
3563
5534
|
const packmindLogger = new PackmindLogger("PackmindCLI", "info" /* INFO */);
|
|
3564
5535
|
const packmindCliHexa = new PackmindCliHexa(packmindLogger);
|
|
5536
|
+
if (list) {
|
|
5537
|
+
try {
|
|
5538
|
+
console.log("Fetching available packages...\n");
|
|
5539
|
+
const packages = await packmindCliHexa.listPackages({});
|
|
5540
|
+
if (packages.length === 0) {
|
|
5541
|
+
console.log("No packages found.");
|
|
5542
|
+
process.exit(0);
|
|
5543
|
+
}
|
|
5544
|
+
console.log("Available packages:");
|
|
5545
|
+
packages.forEach((pkg) => {
|
|
5546
|
+
console.log(`- ${pkg.name} (${pkg.slug})`);
|
|
5547
|
+
if (pkg.description) {
|
|
5548
|
+
console.log(` ${pkg.description}`);
|
|
5549
|
+
console.log("");
|
|
5550
|
+
}
|
|
5551
|
+
});
|
|
5552
|
+
process.exit(0);
|
|
5553
|
+
} catch (error) {
|
|
5554
|
+
console.error("\n\u274C Failed to list packages:");
|
|
5555
|
+
if (error instanceof Error) {
|
|
5556
|
+
console.error(` ${error.message}`);
|
|
5557
|
+
} else {
|
|
5558
|
+
console.error(` ${String(error)}`);
|
|
5559
|
+
}
|
|
5560
|
+
process.exit(1);
|
|
5561
|
+
}
|
|
5562
|
+
}
|
|
5563
|
+
if (show) {
|
|
5564
|
+
try {
|
|
5565
|
+
console.log(`Fetching package details for '${show}'...
|
|
5566
|
+
`);
|
|
5567
|
+
const pkg = await packmindCliHexa.getPackageBySlug({ slug: show });
|
|
5568
|
+
console.log(`${pkg.name} (${pkg.slug}):
|
|
5569
|
+
`);
|
|
5570
|
+
if (pkg.description) {
|
|
5571
|
+
console.log(`${pkg.description}
|
|
5572
|
+
`);
|
|
5573
|
+
}
|
|
5574
|
+
if (pkg.standards && pkg.standards.length > 0) {
|
|
5575
|
+
console.log("Standards:");
|
|
5576
|
+
pkg.standards.forEach((standard) => {
|
|
5577
|
+
if (standard.summary) {
|
|
5578
|
+
console.log(` - ${standard.name}: ${standard.summary}`);
|
|
5579
|
+
} else {
|
|
5580
|
+
console.log(` - ${standard.name}`);
|
|
5581
|
+
}
|
|
5582
|
+
});
|
|
5583
|
+
console.log("");
|
|
5584
|
+
}
|
|
5585
|
+
if (pkg.recipes && pkg.recipes.length > 0) {
|
|
5586
|
+
console.log("Recipes:");
|
|
5587
|
+
pkg.recipes.forEach((recipe) => {
|
|
5588
|
+
if (recipe.summary) {
|
|
5589
|
+
console.log(` - ${recipe.name}: ${recipe.summary}`);
|
|
5590
|
+
} else {
|
|
5591
|
+
console.log(` - ${recipe.name}`);
|
|
5592
|
+
}
|
|
5593
|
+
});
|
|
5594
|
+
console.log("");
|
|
5595
|
+
}
|
|
5596
|
+
process.exit(0);
|
|
5597
|
+
} catch (error) {
|
|
5598
|
+
console.error("\n\u274C Failed to fetch package details:");
|
|
5599
|
+
if (error instanceof Error) {
|
|
5600
|
+
console.error(` ${error.message}`);
|
|
5601
|
+
} else {
|
|
5602
|
+
console.error(` ${String(error)}`);
|
|
5603
|
+
}
|
|
5604
|
+
process.exit(1);
|
|
5605
|
+
}
|
|
5606
|
+
}
|
|
5607
|
+
let configPackages;
|
|
5608
|
+
let configExists = false;
|
|
3565
5609
|
try {
|
|
5610
|
+
configPackages = await packmindCliHexa.readConfig(process.cwd());
|
|
5611
|
+
configExists = configPackages.length > 0;
|
|
5612
|
+
} catch (error) {
|
|
5613
|
+
console.error("ERROR Failed to parse packmind.json");
|
|
5614
|
+
if (error instanceof Error) {
|
|
5615
|
+
console.error(`ERROR ${error.message}`);
|
|
5616
|
+
} else {
|
|
5617
|
+
console.error(`ERROR ${String(error)}`);
|
|
5618
|
+
}
|
|
5619
|
+
console.error(
|
|
5620
|
+
"\n\u{1F4A1} Please fix the packmind.json file or delete it to continue."
|
|
5621
|
+
);
|
|
5622
|
+
process.exit(1);
|
|
5623
|
+
}
|
|
5624
|
+
const allPackages = [.../* @__PURE__ */ new Set([...configPackages, ...packagesSlugs])];
|
|
5625
|
+
if (allPackages.length === 0) {
|
|
5626
|
+
console.log("WARN config packmind.json not found");
|
|
5627
|
+
console.log(
|
|
5628
|
+
"Usage: packmind-cli install <package-slug> [package-slug...]"
|
|
5629
|
+
);
|
|
5630
|
+
console.log(" packmind-cli install --list");
|
|
5631
|
+
console.log("");
|
|
5632
|
+
console.log("Examples:");
|
|
5633
|
+
console.log(" packmind-cli install backend");
|
|
5634
|
+
console.log(" packmind-cli install backend frontend");
|
|
5635
|
+
console.log(" packmind-cli install --list # Show available packages");
|
|
5636
|
+
console.log("");
|
|
5637
|
+
console.log("Install recipes and standards from the specified packages.");
|
|
5638
|
+
process.exit(0);
|
|
5639
|
+
}
|
|
5640
|
+
if (!configExists && packagesSlugs.length > 0) {
|
|
5641
|
+
console.log("INFO initializing packmind.json");
|
|
5642
|
+
}
|
|
5643
|
+
try {
|
|
5644
|
+
const packageCount = allPackages.length;
|
|
5645
|
+
const packageWord = packageCount === 1 ? "package" : "packages";
|
|
5646
|
+
console.log(
|
|
5647
|
+
`Fetching ${packageCount} ${packageWord}: ${allPackages.join(", ")}...`
|
|
5648
|
+
);
|
|
3566
5649
|
const result = await packmindCliHexa.pullData({
|
|
3567
|
-
baseDirectory: process.cwd()
|
|
5650
|
+
baseDirectory: process.cwd(),
|
|
5651
|
+
packagesSlugs: allPackages
|
|
3568
5652
|
});
|
|
3569
|
-
console.log(
|
|
3570
|
-
|
|
3571
|
-
|
|
3572
|
-
console.log(
|
|
5653
|
+
console.log(
|
|
5654
|
+
`Installing ${result.recipesCount} recipes and ${result.standardsCount} standards...`
|
|
5655
|
+
);
|
|
5656
|
+
console.log(
|
|
5657
|
+
`
|
|
5658
|
+
added ${result.filesCreated} files, changed ${result.filesUpdated} files, removed ${result.filesDeleted} files`
|
|
5659
|
+
);
|
|
3573
5660
|
if (result.errors.length > 0) {
|
|
3574
5661
|
console.log("\n\u26A0\uFE0F Errors encountered:");
|
|
3575
5662
|
result.errors.forEach((error) => {
|
|
@@ -3577,41 +5664,102 @@ var pullCommand = (0, import_cmd_ts2.command)({
|
|
|
3577
5664
|
});
|
|
3578
5665
|
process.exit(1);
|
|
3579
5666
|
}
|
|
5667
|
+
await packmindCliHexa.writeConfig(process.cwd(), allPackages);
|
|
3580
5668
|
} catch (error) {
|
|
3581
|
-
console.error("\n\u274C Failed to
|
|
3582
|
-
|
|
3583
|
-
|
|
3584
|
-
|
|
5669
|
+
console.error("\n\u274C Failed to install content:");
|
|
5670
|
+
if (error instanceof Error) {
|
|
5671
|
+
const errorObj = error;
|
|
5672
|
+
if (errorObj.statusCode === 404) {
|
|
5673
|
+
console.error(` ${errorObj.message}`);
|
|
5674
|
+
if (configExists && configPackages.length > 0) {
|
|
5675
|
+
const missingPackages = allPackages.filter(
|
|
5676
|
+
(pkg) => configPackages.includes(pkg)
|
|
5677
|
+
);
|
|
5678
|
+
if (missingPackages.length > 0) {
|
|
5679
|
+
console.error(
|
|
5680
|
+
"\n\u{1F4A1} Either remove the following package(s) from packmind.json:"
|
|
5681
|
+
);
|
|
5682
|
+
missingPackages.forEach((pkg) => {
|
|
5683
|
+
console.error(` "${pkg}"`);
|
|
5684
|
+
});
|
|
5685
|
+
console.error(" Or ensure that:");
|
|
5686
|
+
console.error(
|
|
5687
|
+
" - The package slug exists and is correctly spelled"
|
|
5688
|
+
);
|
|
5689
|
+
console.error(" - The package exists in your organization");
|
|
5690
|
+
console.error(" - You have the correct API key configured");
|
|
5691
|
+
} else {
|
|
5692
|
+
console.error("\n\u{1F4A1} Troubleshooting tips:");
|
|
5693
|
+
console.error(
|
|
5694
|
+
" - Check if the package slug exists and is correctly spelled"
|
|
5695
|
+
);
|
|
5696
|
+
console.error(
|
|
5697
|
+
" - Check that the package exists in your organization"
|
|
5698
|
+
);
|
|
5699
|
+
console.error(
|
|
5700
|
+
" - Ensure you have the correct API key configured"
|
|
5701
|
+
);
|
|
5702
|
+
}
|
|
5703
|
+
} else {
|
|
5704
|
+
console.error("\n\u{1F4A1} Troubleshooting tips:");
|
|
5705
|
+
console.error(
|
|
5706
|
+
" - Check if the package slug exists and is correctly spelled"
|
|
5707
|
+
);
|
|
5708
|
+
console.error(
|
|
5709
|
+
" - Check that the package exists in your organization"
|
|
5710
|
+
);
|
|
5711
|
+
console.error(
|
|
5712
|
+
" - Ensure you have the correct API key configured"
|
|
5713
|
+
);
|
|
5714
|
+
}
|
|
5715
|
+
} else {
|
|
5716
|
+
console.error(` ${errorObj.message}`);
|
|
5717
|
+
const apiErrorObj = error;
|
|
5718
|
+
if (apiErrorObj.response?.data?.message) {
|
|
5719
|
+
console.error(`
|
|
5720
|
+
Details: ${apiErrorObj.response.data.message}`);
|
|
5721
|
+
}
|
|
5722
|
+
console.error("\n\u{1F4A1} Troubleshooting tips:");
|
|
5723
|
+
console.error(" - Verify that the package slugs are correct");
|
|
5724
|
+
console.error(
|
|
5725
|
+
" - Check that the packages exist in your organization"
|
|
5726
|
+
);
|
|
5727
|
+
console.error(" - Ensure you have the correct API key configured");
|
|
5728
|
+
}
|
|
5729
|
+
} else {
|
|
5730
|
+
console.error(` ${String(error)}`);
|
|
5731
|
+
}
|
|
3585
5732
|
process.exit(1);
|
|
3586
5733
|
}
|
|
3587
5734
|
}
|
|
3588
5735
|
});
|
|
3589
5736
|
|
|
3590
5737
|
// apps/cli/src/main.ts
|
|
5738
|
+
var { version: CLI_VERSION } = require_package();
|
|
3591
5739
|
function findEnvFile() {
|
|
3592
5740
|
let currentDir = process.cwd();
|
|
3593
5741
|
const startDir = currentDir;
|
|
3594
5742
|
let gitRootFound = false;
|
|
3595
5743
|
let searchDir = currentDir;
|
|
3596
|
-
while (searchDir !==
|
|
3597
|
-
if (
|
|
5744
|
+
while (searchDir !== path6.parse(searchDir).root) {
|
|
5745
|
+
if (fs6.existsSync(path6.join(searchDir, ".git"))) {
|
|
3598
5746
|
gitRootFound = true;
|
|
3599
5747
|
break;
|
|
3600
5748
|
}
|
|
3601
|
-
searchDir =
|
|
5749
|
+
searchDir = path6.dirname(searchDir);
|
|
3602
5750
|
}
|
|
3603
|
-
while (currentDir !==
|
|
3604
|
-
const envPath2 =
|
|
3605
|
-
if (
|
|
5751
|
+
while (currentDir !== path6.parse(currentDir).root) {
|
|
5752
|
+
const envPath2 = path6.join(currentDir, ".env");
|
|
5753
|
+
if (fs6.existsSync(envPath2)) {
|
|
3606
5754
|
return envPath2;
|
|
3607
5755
|
}
|
|
3608
|
-
if (gitRootFound &&
|
|
5756
|
+
if (gitRootFound && fs6.existsSync(path6.join(currentDir, ".git"))) {
|
|
3609
5757
|
break;
|
|
3610
5758
|
}
|
|
3611
5759
|
if (!gitRootFound && currentDir !== startDir) {
|
|
3612
5760
|
break;
|
|
3613
5761
|
}
|
|
3614
|
-
currentDir =
|
|
5762
|
+
currentDir = path6.dirname(currentDir);
|
|
3615
5763
|
}
|
|
3616
5764
|
return null;
|
|
3617
5765
|
}
|
|
@@ -3626,15 +5774,21 @@ if (hasEmbeddedWasmFiles()) {
|
|
|
3626
5774
|
} catch {
|
|
3627
5775
|
}
|
|
3628
5776
|
}
|
|
5777
|
+
var args = process.argv.slice(2);
|
|
5778
|
+
if (args.includes("--version") || args.includes("-v")) {
|
|
5779
|
+
console.log(`packmind-cli version ${CLI_VERSION}`);
|
|
5780
|
+
process.exit(0);
|
|
5781
|
+
}
|
|
3629
5782
|
var app = (0, import_cmd_ts3.subcommands)({
|
|
3630
5783
|
name: "packmind-cli",
|
|
3631
5784
|
description: "Packmind CLI tool",
|
|
3632
5785
|
cmds: {
|
|
3633
5786
|
lint: lintCommand,
|
|
3634
|
-
pull: pullCommand
|
|
5787
|
+
pull: pullCommand,
|
|
5788
|
+
install: pullCommand
|
|
3635
5789
|
}
|
|
3636
5790
|
});
|
|
3637
|
-
(0, import_cmd_ts3.run)(app,
|
|
5791
|
+
(0, import_cmd_ts3.run)(app, args).catch((error) => {
|
|
3638
5792
|
console.error(import_chalk2.default.bgRed.bold("packmind-cli"), import_chalk2.default.red(error.message));
|
|
3639
5793
|
process.exit(1);
|
|
3640
5794
|
});
|