@hasna/microservices 0.0.18 → 0.0.20
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/README.md +19 -8
- package/bin/index.js +553 -113
- package/bin/mcp.js +776 -247
- package/dist/index.js +270 -20
- package/package.json +22 -4
package/bin/index.js
CHANGED
|
@@ -33,7 +33,7 @@ var __toESM = (mod, isNodeMode, target) => {
|
|
|
33
33
|
var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
|
34
34
|
var __require = import.meta.require;
|
|
35
35
|
|
|
36
|
-
// node_modules/commander/lib/error.js
|
|
36
|
+
// node_modules/.bun/commander@12.1.0/node_modules/commander/lib/error.js
|
|
37
37
|
var require_error = __commonJS((exports) => {
|
|
38
38
|
class CommanderError extends Error {
|
|
39
39
|
constructor(exitCode, code, message) {
|
|
@@ -57,7 +57,7 @@ var require_error = __commonJS((exports) => {
|
|
|
57
57
|
exports.InvalidArgumentError = InvalidArgumentError;
|
|
58
58
|
});
|
|
59
59
|
|
|
60
|
-
// node_modules/commander/lib/argument.js
|
|
60
|
+
// node_modules/.bun/commander@12.1.0/node_modules/commander/lib/argument.js
|
|
61
61
|
var require_argument = __commonJS((exports) => {
|
|
62
62
|
var { InvalidArgumentError } = require_error();
|
|
63
63
|
|
|
@@ -136,7 +136,7 @@ var require_argument = __commonJS((exports) => {
|
|
|
136
136
|
exports.humanReadableArgName = humanReadableArgName;
|
|
137
137
|
});
|
|
138
138
|
|
|
139
|
-
// node_modules/commander/lib/help.js
|
|
139
|
+
// node_modules/.bun/commander@12.1.0/node_modules/commander/lib/help.js
|
|
140
140
|
var require_help = __commonJS((exports) => {
|
|
141
141
|
var { humanReadableArgName } = require_argument();
|
|
142
142
|
|
|
@@ -385,7 +385,7 @@ var require_help = __commonJS((exports) => {
|
|
|
385
385
|
exports.Help = Help;
|
|
386
386
|
});
|
|
387
387
|
|
|
388
|
-
// node_modules/commander/lib/option.js
|
|
388
|
+
// node_modules/.bun/commander@12.1.0/node_modules/commander/lib/option.js
|
|
389
389
|
var require_option = __commonJS((exports) => {
|
|
390
390
|
var { InvalidArgumentError } = require_error();
|
|
391
391
|
|
|
@@ -536,7 +536,7 @@ var require_option = __commonJS((exports) => {
|
|
|
536
536
|
exports.DualOptions = DualOptions;
|
|
537
537
|
});
|
|
538
538
|
|
|
539
|
-
// node_modules/commander/lib/suggestSimilar.js
|
|
539
|
+
// node_modules/.bun/commander@12.1.0/node_modules/commander/lib/suggestSimilar.js
|
|
540
540
|
var require_suggestSimilar = __commonJS((exports) => {
|
|
541
541
|
var maxDistance = 3;
|
|
542
542
|
function editDistance(a, b) {
|
|
@@ -609,13 +609,13 @@ var require_suggestSimilar = __commonJS((exports) => {
|
|
|
609
609
|
exports.suggestSimilar = suggestSimilar;
|
|
610
610
|
});
|
|
611
611
|
|
|
612
|
-
// node_modules/commander/lib/command.js
|
|
612
|
+
// node_modules/.bun/commander@12.1.0/node_modules/commander/lib/command.js
|
|
613
613
|
var require_command = __commonJS((exports) => {
|
|
614
614
|
var EventEmitter = __require("events").EventEmitter;
|
|
615
615
|
var childProcess = __require("child_process");
|
|
616
616
|
var path = __require("path");
|
|
617
617
|
var fs = __require("fs");
|
|
618
|
-
var
|
|
618
|
+
var process3 = __require("process");
|
|
619
619
|
var { Argument, humanReadableArgName } = require_argument();
|
|
620
620
|
var { CommanderError } = require_error();
|
|
621
621
|
var { Help } = require_help();
|
|
@@ -657,10 +657,10 @@ var require_command = __commonJS((exports) => {
|
|
|
657
657
|
this._showHelpAfterError = false;
|
|
658
658
|
this._showSuggestionAfterError = true;
|
|
659
659
|
this._outputConfiguration = {
|
|
660
|
-
writeOut: (str) =>
|
|
661
|
-
writeErr: (str) =>
|
|
662
|
-
getOutHelpWidth: () =>
|
|
663
|
-
getErrHelpWidth: () =>
|
|
660
|
+
writeOut: (str) => process3.stdout.write(str),
|
|
661
|
+
writeErr: (str) => process3.stderr.write(str),
|
|
662
|
+
getOutHelpWidth: () => process3.stdout.isTTY ? process3.stdout.columns : undefined,
|
|
663
|
+
getErrHelpWidth: () => process3.stderr.isTTY ? process3.stderr.columns : undefined,
|
|
664
664
|
outputError: (str, write) => write(str)
|
|
665
665
|
};
|
|
666
666
|
this._hidden = false;
|
|
@@ -856,7 +856,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
856
856
|
if (this._exitCallback) {
|
|
857
857
|
this._exitCallback(new CommanderError(exitCode, code, message));
|
|
858
858
|
}
|
|
859
|
-
|
|
859
|
+
process3.exit(exitCode);
|
|
860
860
|
}
|
|
861
861
|
action(fn) {
|
|
862
862
|
const listener = (args) => {
|
|
@@ -1051,16 +1051,16 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
1051
1051
|
}
|
|
1052
1052
|
parseOptions = parseOptions || {};
|
|
1053
1053
|
if (argv === undefined && parseOptions.from === undefined) {
|
|
1054
|
-
if (
|
|
1054
|
+
if (process3.versions?.electron) {
|
|
1055
1055
|
parseOptions.from = "electron";
|
|
1056
1056
|
}
|
|
1057
|
-
const execArgv =
|
|
1057
|
+
const execArgv = process3.execArgv ?? [];
|
|
1058
1058
|
if (execArgv.includes("-e") || execArgv.includes("--eval") || execArgv.includes("-p") || execArgv.includes("--print")) {
|
|
1059
1059
|
parseOptions.from = "eval";
|
|
1060
1060
|
}
|
|
1061
1061
|
}
|
|
1062
1062
|
if (argv === undefined) {
|
|
1063
|
-
argv =
|
|
1063
|
+
argv = process3.argv;
|
|
1064
1064
|
}
|
|
1065
1065
|
this.rawArgs = argv.slice();
|
|
1066
1066
|
let userArgs;
|
|
@@ -1071,7 +1071,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
1071
1071
|
userArgs = argv.slice(2);
|
|
1072
1072
|
break;
|
|
1073
1073
|
case "electron":
|
|
1074
|
-
if (
|
|
1074
|
+
if (process3.defaultApp) {
|
|
1075
1075
|
this._scriptPath = argv[1];
|
|
1076
1076
|
userArgs = argv.slice(2);
|
|
1077
1077
|
} else {
|
|
@@ -1142,23 +1142,23 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
1142
1142
|
}
|
|
1143
1143
|
launchWithNode = sourceExt.includes(path.extname(executableFile));
|
|
1144
1144
|
let proc;
|
|
1145
|
-
if (
|
|
1145
|
+
if (process3.platform !== "win32") {
|
|
1146
1146
|
if (launchWithNode) {
|
|
1147
1147
|
args.unshift(executableFile);
|
|
1148
|
-
args = incrementNodeInspectorPort(
|
|
1149
|
-
proc = childProcess.spawn(
|
|
1148
|
+
args = incrementNodeInspectorPort(process3.execArgv).concat(args);
|
|
1149
|
+
proc = childProcess.spawn(process3.argv[0], args, { stdio: "inherit" });
|
|
1150
1150
|
} else {
|
|
1151
1151
|
proc = childProcess.spawn(executableFile, args, { stdio: "inherit" });
|
|
1152
1152
|
}
|
|
1153
1153
|
} else {
|
|
1154
1154
|
args.unshift(executableFile);
|
|
1155
|
-
args = incrementNodeInspectorPort(
|
|
1156
|
-
proc = childProcess.spawn(
|
|
1155
|
+
args = incrementNodeInspectorPort(process3.execArgv).concat(args);
|
|
1156
|
+
proc = childProcess.spawn(process3.execPath, args, { stdio: "inherit" });
|
|
1157
1157
|
}
|
|
1158
1158
|
if (!proc.killed) {
|
|
1159
1159
|
const signals = ["SIGUSR1", "SIGUSR2", "SIGTERM", "SIGINT", "SIGHUP"];
|
|
1160
1160
|
signals.forEach((signal) => {
|
|
1161
|
-
|
|
1161
|
+
process3.on(signal, () => {
|
|
1162
1162
|
if (proc.killed === false && proc.exitCode === null) {
|
|
1163
1163
|
proc.kill(signal);
|
|
1164
1164
|
}
|
|
@@ -1169,7 +1169,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
1169
1169
|
proc.on("close", (code) => {
|
|
1170
1170
|
code = code ?? 1;
|
|
1171
1171
|
if (!exitCallback) {
|
|
1172
|
-
|
|
1172
|
+
process3.exit(code);
|
|
1173
1173
|
} else {
|
|
1174
1174
|
exitCallback(new CommanderError(code, "commander.executeSubCommandAsync", "(close)"));
|
|
1175
1175
|
}
|
|
@@ -1186,7 +1186,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
1186
1186
|
throw new Error(`'${executableFile}' not executable`);
|
|
1187
1187
|
}
|
|
1188
1188
|
if (!exitCallback) {
|
|
1189
|
-
|
|
1189
|
+
process3.exit(1);
|
|
1190
1190
|
} else {
|
|
1191
1191
|
const wrappedError = new CommanderError(1, "commander.executeSubCommandAsync", "(error)");
|
|
1192
1192
|
wrappedError.nestedError = err;
|
|
@@ -1534,11 +1534,11 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
1534
1534
|
}
|
|
1535
1535
|
_parseOptionsEnv() {
|
|
1536
1536
|
this.options.forEach((option) => {
|
|
1537
|
-
if (option.envVar && option.envVar in
|
|
1537
|
+
if (option.envVar && option.envVar in process3.env) {
|
|
1538
1538
|
const optionKey = option.attributeName();
|
|
1539
1539
|
if (this.getOptionValue(optionKey) === undefined || ["default", "config", "env"].includes(this.getOptionValueSource(optionKey))) {
|
|
1540
1540
|
if (option.required || option.optional) {
|
|
1541
|
-
this.emit(`optionEnv:${option.name()}`,
|
|
1541
|
+
this.emit(`optionEnv:${option.name()}`, process3.env[option.envVar]);
|
|
1542
1542
|
} else {
|
|
1543
1543
|
this.emit(`optionEnv:${option.name()}`);
|
|
1544
1544
|
}
|
|
@@ -1784,7 +1784,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
1784
1784
|
}
|
|
1785
1785
|
help(contextOptions) {
|
|
1786
1786
|
this.outputHelp(contextOptions);
|
|
1787
|
-
let exitCode =
|
|
1787
|
+
let exitCode = process3.exitCode || 0;
|
|
1788
1788
|
if (exitCode === 0 && contextOptions && typeof contextOptions !== "function" && contextOptions.error) {
|
|
1789
1789
|
exitCode = 1;
|
|
1790
1790
|
}
|
|
@@ -1852,7 +1852,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
1852
1852
|
exports.Command = Command;
|
|
1853
1853
|
});
|
|
1854
1854
|
|
|
1855
|
-
// node_modules/commander/index.js
|
|
1855
|
+
// node_modules/.bun/commander@12.1.0/node_modules/commander/index.js
|
|
1856
1856
|
var require_commander = __commonJS((exports) => {
|
|
1857
1857
|
var { Argument } = require_argument();
|
|
1858
1858
|
var { Command } = require_command();
|
|
@@ -1872,23 +1872,11 @@ var require_commander = __commonJS((exports) => {
|
|
|
1872
1872
|
exports.InvalidOptionArgumentError = InvalidArgumentError;
|
|
1873
1873
|
});
|
|
1874
1874
|
|
|
1875
|
-
//
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
program,
|
|
1879
|
-
createCommand,
|
|
1880
|
-
createArgument,
|
|
1881
|
-
createOption,
|
|
1882
|
-
CommanderError,
|
|
1883
|
-
InvalidArgumentError,
|
|
1884
|
-
InvalidOptionArgumentError,
|
|
1885
|
-
Command,
|
|
1886
|
-
Argument,
|
|
1887
|
-
Option,
|
|
1888
|
-
Help
|
|
1889
|
-
} = import__.default;
|
|
1875
|
+
// src/cli/index.tsx
|
|
1876
|
+
import fs from "fs";
|
|
1877
|
+
import path from "path";
|
|
1890
1878
|
|
|
1891
|
-
// node_modules/chalk/source/vendor/ansi-styles/index.js
|
|
1879
|
+
// node_modules/.bun/chalk@5.6.2/node_modules/chalk/source/vendor/ansi-styles/index.js
|
|
1892
1880
|
var ANSI_BACKGROUND_OFFSET = 10;
|
|
1893
1881
|
var wrapAnsi16 = (offset = 0) => (code) => `\x1B[${code + offset}m`;
|
|
1894
1882
|
var wrapAnsi256 = (offset = 0) => (code) => `\x1B[${38 + offset};5;${code}m`;
|
|
@@ -2065,7 +2053,7 @@ function assembleStyles() {
|
|
|
2065
2053
|
var ansiStyles = assembleStyles();
|
|
2066
2054
|
var ansi_styles_default = ansiStyles;
|
|
2067
2055
|
|
|
2068
|
-
// node_modules/chalk/source/vendor/supports-color/index.js
|
|
2056
|
+
// node_modules/.bun/chalk@5.6.2/node_modules/chalk/source/vendor/supports-color/index.js
|
|
2069
2057
|
import process2 from "process";
|
|
2070
2058
|
import os from "os";
|
|
2071
2059
|
import tty from "tty";
|
|
@@ -2197,7 +2185,7 @@ var supportsColor = {
|
|
|
2197
2185
|
};
|
|
2198
2186
|
var supports_color_default = supportsColor;
|
|
2199
2187
|
|
|
2200
|
-
// node_modules/chalk/source/utilities.js
|
|
2188
|
+
// node_modules/.bun/chalk@5.6.2/node_modules/chalk/source/utilities.js
|
|
2201
2189
|
function stringReplaceAll(string, substring, replacer) {
|
|
2202
2190
|
let index = string.indexOf(substring);
|
|
2203
2191
|
if (index === -1) {
|
|
@@ -2230,7 +2218,7 @@ function stringEncaseCRLFWithFirstIndex(string, prefix, postfix, index) {
|
|
|
2230
2218
|
return returnValue;
|
|
2231
2219
|
}
|
|
2232
2220
|
|
|
2233
|
-
// node_modules/chalk/source/index.js
|
|
2221
|
+
// node_modules/.bun/chalk@5.6.2/node_modules/chalk/source/index.js
|
|
2234
2222
|
var { stdout: stdoutColor, stderr: stderrColor } = supports_color_default;
|
|
2235
2223
|
var GENERATOR = Symbol("GENERATOR");
|
|
2236
2224
|
var STYLER = Symbol("STYLER");
|
|
@@ -2377,6 +2365,25 @@ var chalk = createChalk();
|
|
|
2377
2365
|
var chalkStderr = createChalk({ level: stderrColor ? stderrColor.level : 0 });
|
|
2378
2366
|
var source_default = chalk;
|
|
2379
2367
|
|
|
2368
|
+
// node_modules/.bun/commander@12.1.0/node_modules/commander/esm.mjs
|
|
2369
|
+
var import__ = __toESM(require_commander(), 1);
|
|
2370
|
+
var {
|
|
2371
|
+
program,
|
|
2372
|
+
createCommand,
|
|
2373
|
+
createArgument,
|
|
2374
|
+
createOption,
|
|
2375
|
+
CommanderError,
|
|
2376
|
+
InvalidArgumentError,
|
|
2377
|
+
InvalidOptionArgumentError,
|
|
2378
|
+
Command,
|
|
2379
|
+
Argument,
|
|
2380
|
+
Option,
|
|
2381
|
+
Help
|
|
2382
|
+
} = import__.default;
|
|
2383
|
+
|
|
2384
|
+
// src/lib/installer.ts
|
|
2385
|
+
import { execFileSync, execSync } from "child_process";
|
|
2386
|
+
|
|
2380
2387
|
// src/lib/registry.ts
|
|
2381
2388
|
var MICROSERVICES = [
|
|
2382
2389
|
{
|
|
@@ -2387,9 +2394,24 @@ var MICROSERVICES = [
|
|
|
2387
2394
|
package: "@hasna/microservice-auth",
|
|
2388
2395
|
binary: "microservice-auth",
|
|
2389
2396
|
schemaPrefix: "auth",
|
|
2390
|
-
tags: [
|
|
2397
|
+
tags: [
|
|
2398
|
+
"auth",
|
|
2399
|
+
"users",
|
|
2400
|
+
"sessions",
|
|
2401
|
+
"jwt",
|
|
2402
|
+
"oauth",
|
|
2403
|
+
"2fa",
|
|
2404
|
+
"api-keys",
|
|
2405
|
+
"magic-links"
|
|
2406
|
+
],
|
|
2391
2407
|
requiredEnv: ["DATABASE_URL", "JWT_SECRET"],
|
|
2392
|
-
optionalEnv: [
|
|
2408
|
+
optionalEnv: [
|
|
2409
|
+
"GITHUB_CLIENT_ID",
|
|
2410
|
+
"GITHUB_CLIENT_SECRET",
|
|
2411
|
+
"GOOGLE_CLIENT_ID",
|
|
2412
|
+
"GOOGLE_CLIENT_SECRET",
|
|
2413
|
+
"AUTH_PORT"
|
|
2414
|
+
]
|
|
2393
2415
|
},
|
|
2394
2416
|
{
|
|
2395
2417
|
name: "teams",
|
|
@@ -2399,7 +2421,14 @@ var MICROSERVICES = [
|
|
|
2399
2421
|
package: "@hasna/microservice-teams",
|
|
2400
2422
|
binary: "microservice-teams",
|
|
2401
2423
|
schemaPrefix: "teams",
|
|
2402
|
-
tags: [
|
|
2424
|
+
tags: [
|
|
2425
|
+
"teams",
|
|
2426
|
+
"workspaces",
|
|
2427
|
+
"rbac",
|
|
2428
|
+
"invites",
|
|
2429
|
+
"permissions",
|
|
2430
|
+
"multi-tenancy"
|
|
2431
|
+
],
|
|
2403
2432
|
requiredEnv: ["DATABASE_URL"],
|
|
2404
2433
|
optionalEnv: ["TEAMS_PORT"]
|
|
2405
2434
|
},
|
|
@@ -2411,7 +2440,15 @@ var MICROSERVICES = [
|
|
|
2411
2440
|
package: "@hasna/microservice-billing",
|
|
2412
2441
|
binary: "microservice-billing",
|
|
2413
2442
|
schemaPrefix: "billing",
|
|
2414
|
-
tags: [
|
|
2443
|
+
tags: [
|
|
2444
|
+
"billing",
|
|
2445
|
+
"stripe",
|
|
2446
|
+
"subscriptions",
|
|
2447
|
+
"plans",
|
|
2448
|
+
"invoices",
|
|
2449
|
+
"usage",
|
|
2450
|
+
"webhooks"
|
|
2451
|
+
],
|
|
2415
2452
|
requiredEnv: ["DATABASE_URL", "STRIPE_SECRET_KEY", "STRIPE_WEBHOOK_SECRET"],
|
|
2416
2453
|
optionalEnv: ["BILLING_PORT"]
|
|
2417
2454
|
},
|
|
@@ -2423,9 +2460,26 @@ var MICROSERVICES = [
|
|
|
2423
2460
|
package: "@hasna/microservice-notify",
|
|
2424
2461
|
binary: "microservice-notify",
|
|
2425
2462
|
schemaPrefix: "notify",
|
|
2426
|
-
tags: [
|
|
2463
|
+
tags: [
|
|
2464
|
+
"notifications",
|
|
2465
|
+
"email",
|
|
2466
|
+
"sms",
|
|
2467
|
+
"in-app",
|
|
2468
|
+
"webhooks",
|
|
2469
|
+
"templates",
|
|
2470
|
+
"sse"
|
|
2471
|
+
],
|
|
2427
2472
|
requiredEnv: ["DATABASE_URL"],
|
|
2428
|
-
optionalEnv: [
|
|
2473
|
+
optionalEnv: [
|
|
2474
|
+
"RESEND_API_KEY",
|
|
2475
|
+
"SMTP_HOST",
|
|
2476
|
+
"SMTP_PORT",
|
|
2477
|
+
"SMTP_USER",
|
|
2478
|
+
"SMTP_PASS",
|
|
2479
|
+
"TWILIO_ACCOUNT_SID",
|
|
2480
|
+
"TWILIO_AUTH_TOKEN",
|
|
2481
|
+
"NOTIFY_PORT"
|
|
2482
|
+
]
|
|
2429
2483
|
},
|
|
2430
2484
|
{
|
|
2431
2485
|
name: "files",
|
|
@@ -2435,9 +2489,24 @@ var MICROSERVICES = [
|
|
|
2435
2489
|
package: "@hasna/microservice-files",
|
|
2436
2490
|
binary: "microservice-files",
|
|
2437
2491
|
schemaPrefix: "files",
|
|
2438
|
-
tags: [
|
|
2492
|
+
tags: [
|
|
2493
|
+
"files",
|
|
2494
|
+
"uploads",
|
|
2495
|
+
"s3",
|
|
2496
|
+
"storage",
|
|
2497
|
+
"images",
|
|
2498
|
+
"presigned-urls",
|
|
2499
|
+
"cdn"
|
|
2500
|
+
],
|
|
2439
2501
|
requiredEnv: ["DATABASE_URL"],
|
|
2440
|
-
optionalEnv: [
|
|
2502
|
+
optionalEnv: [
|
|
2503
|
+
"S3_BUCKET",
|
|
2504
|
+
"S3_REGION",
|
|
2505
|
+
"AWS_ACCESS_KEY_ID",
|
|
2506
|
+
"AWS_SECRET_ACCESS_KEY",
|
|
2507
|
+
"FILES_STORAGE",
|
|
2508
|
+
"FILES_PORT"
|
|
2509
|
+
]
|
|
2441
2510
|
},
|
|
2442
2511
|
{
|
|
2443
2512
|
name: "audit",
|
|
@@ -2459,7 +2528,13 @@ var MICROSERVICES = [
|
|
|
2459
2528
|
package: "@hasna/microservice-flags",
|
|
2460
2529
|
binary: "microservice-flags",
|
|
2461
2530
|
schemaPrefix: "flags",
|
|
2462
|
-
tags: [
|
|
2531
|
+
tags: [
|
|
2532
|
+
"feature-flags",
|
|
2533
|
+
"experiments",
|
|
2534
|
+
"rollouts",
|
|
2535
|
+
"ab-testing",
|
|
2536
|
+
"targeting"
|
|
2537
|
+
],
|
|
2463
2538
|
requiredEnv: ["DATABASE_URL"],
|
|
2464
2539
|
optionalEnv: ["FLAGS_PORT"]
|
|
2465
2540
|
},
|
|
@@ -2471,7 +2546,15 @@ var MICROSERVICES = [
|
|
|
2471
2546
|
package: "@hasna/microservice-jobs",
|
|
2472
2547
|
binary: "microservice-jobs",
|
|
2473
2548
|
schemaPrefix: "jobs",
|
|
2474
|
-
tags: [
|
|
2549
|
+
tags: [
|
|
2550
|
+
"jobs",
|
|
2551
|
+
"queues",
|
|
2552
|
+
"background",
|
|
2553
|
+
"cron",
|
|
2554
|
+
"scheduling",
|
|
2555
|
+
"workers",
|
|
2556
|
+
"retry"
|
|
2557
|
+
],
|
|
2475
2558
|
requiredEnv: ["DATABASE_URL"],
|
|
2476
2559
|
optionalEnv: ["JOBS_PORT", "JOBS_WORKER_CONCURRENCY"]
|
|
2477
2560
|
},
|
|
@@ -2483,9 +2566,23 @@ var MICROSERVICES = [
|
|
|
2483
2566
|
package: "@hasna/microservice-llm",
|
|
2484
2567
|
binary: "microservice-llm",
|
|
2485
2568
|
schemaPrefix: "llm",
|
|
2486
|
-
tags: [
|
|
2569
|
+
tags: [
|
|
2570
|
+
"llm",
|
|
2571
|
+
"openai",
|
|
2572
|
+
"anthropic",
|
|
2573
|
+
"groq",
|
|
2574
|
+
"ai",
|
|
2575
|
+
"gateway",
|
|
2576
|
+
"cost-tracking",
|
|
2577
|
+
"rate-limiting"
|
|
2578
|
+
],
|
|
2487
2579
|
requiredEnv: ["DATABASE_URL"],
|
|
2488
|
-
optionalEnv: [
|
|
2580
|
+
optionalEnv: [
|
|
2581
|
+
"OPENAI_API_KEY",
|
|
2582
|
+
"ANTHROPIC_API_KEY",
|
|
2583
|
+
"GROQ_API_KEY",
|
|
2584
|
+
"LLM_PORT"
|
|
2585
|
+
]
|
|
2489
2586
|
},
|
|
2490
2587
|
{
|
|
2491
2588
|
name: "memory",
|
|
@@ -2495,7 +2592,15 @@ var MICROSERVICES = [
|
|
|
2495
2592
|
package: "@hasna/microservice-memory",
|
|
2496
2593
|
binary: "microservice-memory",
|
|
2497
2594
|
schemaPrefix: "memory",
|
|
2498
|
-
tags: [
|
|
2595
|
+
tags: [
|
|
2596
|
+
"memory",
|
|
2597
|
+
"embeddings",
|
|
2598
|
+
"pgvector",
|
|
2599
|
+
"semantic-search",
|
|
2600
|
+
"rag",
|
|
2601
|
+
"ai-agents",
|
|
2602
|
+
"recall"
|
|
2603
|
+
],
|
|
2499
2604
|
requiredEnv: ["DATABASE_URL"],
|
|
2500
2605
|
optionalEnv: ["OPENAI_API_KEY", "MEMORY_PORT"]
|
|
2501
2606
|
},
|
|
@@ -2507,10 +2612,38 @@ var MICROSERVICES = [
|
|
|
2507
2612
|
package: "@hasna/microservice-search",
|
|
2508
2613
|
binary: "microservice-search",
|
|
2509
2614
|
schemaPrefix: "search",
|
|
2510
|
-
tags: [
|
|
2615
|
+
tags: [
|
|
2616
|
+
"search",
|
|
2617
|
+
"full-text",
|
|
2618
|
+
"semantic",
|
|
2619
|
+
"vector",
|
|
2620
|
+
"pgvector",
|
|
2621
|
+
"hybrid",
|
|
2622
|
+
"rag"
|
|
2623
|
+
],
|
|
2511
2624
|
requiredEnv: ["DATABASE_URL"],
|
|
2512
2625
|
optionalEnv: ["OPENAI_API_KEY", "SEARCH_PORT"]
|
|
2513
2626
|
},
|
|
2627
|
+
{
|
|
2628
|
+
name: "knowledge",
|
|
2629
|
+
displayName: "Knowledge",
|
|
2630
|
+
description: "RAG knowledge base: document ingestion, chunking (fixed/paragraph/sentence/recursive), embedding, and semantic/text/hybrid retrieval with source attribution.",
|
|
2631
|
+
category: "AI",
|
|
2632
|
+
package: "@hasna/microservice-knowledge",
|
|
2633
|
+
binary: "microservice-knowledge",
|
|
2634
|
+
schemaPrefix: "knowledge",
|
|
2635
|
+
tags: [
|
|
2636
|
+
"knowledge",
|
|
2637
|
+
"rag",
|
|
2638
|
+
"chunking",
|
|
2639
|
+
"embeddings",
|
|
2640
|
+
"retrieval",
|
|
2641
|
+
"pgvector",
|
|
2642
|
+
"documents"
|
|
2643
|
+
],
|
|
2644
|
+
requiredEnv: ["DATABASE_URL"],
|
|
2645
|
+
optionalEnv: ["OPENAI_API_KEY", "KNOWLEDGE_PORT"]
|
|
2646
|
+
},
|
|
2514
2647
|
{
|
|
2515
2648
|
name: "usage",
|
|
2516
2649
|
displayName: "Usage",
|
|
@@ -2531,7 +2664,14 @@ var MICROSERVICES = [
|
|
|
2531
2664
|
package: "@hasna/microservice-webhooks",
|
|
2532
2665
|
binary: "microservice-webhooks",
|
|
2533
2666
|
schemaPrefix: "webhooks",
|
|
2534
|
-
tags: [
|
|
2667
|
+
tags: [
|
|
2668
|
+
"webhooks",
|
|
2669
|
+
"outbound",
|
|
2670
|
+
"delivery",
|
|
2671
|
+
"retry",
|
|
2672
|
+
"signing",
|
|
2673
|
+
"integrations"
|
|
2674
|
+
],
|
|
2535
2675
|
requiredEnv: ["DATABASE_URL"],
|
|
2536
2676
|
optionalEnv: ["WEBHOOKS_PORT"]
|
|
2537
2677
|
},
|
|
@@ -2558,6 +2698,104 @@ var MICROSERVICES = [
|
|
|
2558
2698
|
tags: ["waitlist", "referral", "invites", "launch", "growth"],
|
|
2559
2699
|
requiredEnv: ["DATABASE_URL"],
|
|
2560
2700
|
optionalEnv: ["WAITLIST_PORT"]
|
|
2701
|
+
},
|
|
2702
|
+
{
|
|
2703
|
+
name: "sessions",
|
|
2704
|
+
displayName: "Sessions",
|
|
2705
|
+
description: "AI conversation history: message threads, context window management, multi-turn state, fork/pin, full-text search across messages, export as markdown/JSON.",
|
|
2706
|
+
category: "AI",
|
|
2707
|
+
package: "@hasna/microservice-sessions",
|
|
2708
|
+
binary: "microservice-sessions",
|
|
2709
|
+
schemaPrefix: "sessions",
|
|
2710
|
+
tags: [
|
|
2711
|
+
"sessions",
|
|
2712
|
+
"conversations",
|
|
2713
|
+
"chat",
|
|
2714
|
+
"messages",
|
|
2715
|
+
"context-window",
|
|
2716
|
+
"history"
|
|
2717
|
+
],
|
|
2718
|
+
requiredEnv: ["DATABASE_URL"],
|
|
2719
|
+
optionalEnv: ["SESSIONS_PORT"]
|
|
2720
|
+
},
|
|
2721
|
+
{
|
|
2722
|
+
name: "guardrails",
|
|
2723
|
+
displayName: "Guardrails",
|
|
2724
|
+
description: "AI safety layer: prompt injection detection, PII scanning (email/phone/SSN/CC), toxicity check, configurable per-workspace policies, violation logging.",
|
|
2725
|
+
category: "AI",
|
|
2726
|
+
package: "@hasna/microservice-guardrails",
|
|
2727
|
+
binary: "microservice-guardrails",
|
|
2728
|
+
schemaPrefix: "guardrails",
|
|
2729
|
+
tags: [
|
|
2730
|
+
"guardrails",
|
|
2731
|
+
"safety",
|
|
2732
|
+
"pii",
|
|
2733
|
+
"injection",
|
|
2734
|
+
"toxicity",
|
|
2735
|
+
"moderation",
|
|
2736
|
+
"policy"
|
|
2737
|
+
],
|
|
2738
|
+
requiredEnv: ["DATABASE_URL"],
|
|
2739
|
+
optionalEnv: ["GUARDRAILS_PORT"]
|
|
2740
|
+
},
|
|
2741
|
+
{
|
|
2742
|
+
name: "traces",
|
|
2743
|
+
displayName: "Traces",
|
|
2744
|
+
description: "Agent observability: span-based distributed tracing for LLM calls, tool calls, and decisions. Duration, token, and cost tracking per span. Stats with percentiles.",
|
|
2745
|
+
category: "Observability",
|
|
2746
|
+
package: "@hasna/microservice-traces",
|
|
2747
|
+
binary: "microservice-traces",
|
|
2748
|
+
schemaPrefix: "traces",
|
|
2749
|
+
tags: [
|
|
2750
|
+
"traces",
|
|
2751
|
+
"tracing",
|
|
2752
|
+
"spans",
|
|
2753
|
+
"observability",
|
|
2754
|
+
"latency",
|
|
2755
|
+
"debugging",
|
|
2756
|
+
"agent-ops"
|
|
2757
|
+
],
|
|
2758
|
+
requiredEnv: ["DATABASE_URL"],
|
|
2759
|
+
optionalEnv: ["TRACES_PORT"]
|
|
2760
|
+
},
|
|
2761
|
+
{
|
|
2762
|
+
name: "agents",
|
|
2763
|
+
displayName: "Agents",
|
|
2764
|
+
description: "Agent registry and orchestration: register agents with capabilities, health tracking via heartbeat, capability-based task routing, agent-to-agent messaging.",
|
|
2765
|
+
category: "AI",
|
|
2766
|
+
package: "@hasna/microservice-agents",
|
|
2767
|
+
binary: "microservice-agents",
|
|
2768
|
+
schemaPrefix: "agents",
|
|
2769
|
+
tags: [
|
|
2770
|
+
"agents",
|
|
2771
|
+
"registry",
|
|
2772
|
+
"orchestration",
|
|
2773
|
+
"routing",
|
|
2774
|
+
"multi-agent",
|
|
2775
|
+
"capabilities",
|
|
2776
|
+
"heartbeat"
|
|
2777
|
+
],
|
|
2778
|
+
requiredEnv: ["DATABASE_URL"],
|
|
2779
|
+
optionalEnv: ["AGENTS_PORT"]
|
|
2780
|
+
},
|
|
2781
|
+
{
|
|
2782
|
+
name: "prompts",
|
|
2783
|
+
displayName: "Prompts",
|
|
2784
|
+
description: "Versioned prompt management: auto-incrementing versions, per-workspace/user/agent overrides, A/B experiment variants, rollback, variable interpolation, diff.",
|
|
2785
|
+
category: "AI",
|
|
2786
|
+
package: "@hasna/microservice-prompts",
|
|
2787
|
+
binary: "microservice-prompts",
|
|
2788
|
+
schemaPrefix: "prompts",
|
|
2789
|
+
tags: [
|
|
2790
|
+
"prompts",
|
|
2791
|
+
"templates",
|
|
2792
|
+
"versioning",
|
|
2793
|
+
"ab-testing",
|
|
2794
|
+
"overrides",
|
|
2795
|
+
"rollback"
|
|
2796
|
+
],
|
|
2797
|
+
requiredEnv: ["DATABASE_URL"],
|
|
2798
|
+
optionalEnv: ["PROMPTS_PORT"]
|
|
2561
2799
|
}
|
|
2562
2800
|
];
|
|
2563
2801
|
function getMicroservice(name) {
|
|
@@ -2570,7 +2808,6 @@ function searchMicroservices(query) {
|
|
|
2570
2808
|
}
|
|
2571
2809
|
|
|
2572
2810
|
// src/lib/installer.ts
|
|
2573
|
-
import { execSync, execFileSync } from "child_process";
|
|
2574
2811
|
function microserviceExists(name) {
|
|
2575
2812
|
const meta = getMicroservice(name);
|
|
2576
2813
|
if (!meta)
|
|
@@ -2587,7 +2824,9 @@ function getMicroserviceVersion(name) {
|
|
|
2587
2824
|
if (!meta)
|
|
2588
2825
|
return null;
|
|
2589
2826
|
try {
|
|
2590
|
-
const out = execFileSync(meta.binary, ["--version"], {
|
|
2827
|
+
const out = execFileSync(meta.binary, ["--version"], {
|
|
2828
|
+
encoding: "utf8"
|
|
2829
|
+
}).trim();
|
|
2591
2830
|
return out || null;
|
|
2592
2831
|
} catch {
|
|
2593
2832
|
return null;
|
|
@@ -2596,7 +2835,11 @@ function getMicroserviceVersion(name) {
|
|
|
2596
2835
|
function installMicroservice(name, options = {}) {
|
|
2597
2836
|
const meta = getMicroservice(name);
|
|
2598
2837
|
if (!meta) {
|
|
2599
|
-
return {
|
|
2838
|
+
return {
|
|
2839
|
+
microservice: name,
|
|
2840
|
+
success: false,
|
|
2841
|
+
error: `Unknown microservice '${name}'`
|
|
2842
|
+
};
|
|
2600
2843
|
}
|
|
2601
2844
|
if (microserviceExists(name) && !options.force) {
|
|
2602
2845
|
const version = getMicroserviceVersion(name);
|
|
@@ -2617,9 +2860,6 @@ function installMicroservice(name, options = {}) {
|
|
|
2617
2860
|
function installMicroservices(names, options = {}) {
|
|
2618
2861
|
return names.map((name) => installMicroservice(name, options));
|
|
2619
2862
|
}
|
|
2620
|
-
function getInstalledMicroservices() {
|
|
2621
|
-
return MICROSERVICES.filter((m) => microserviceExists(m.name)).map((m) => m.name);
|
|
2622
|
-
}
|
|
2623
2863
|
function removeMicroservice(name) {
|
|
2624
2864
|
const meta = getMicroservice(name);
|
|
2625
2865
|
if (!meta)
|
|
@@ -2640,38 +2880,6 @@ function getMicroserviceStatus(name) {
|
|
|
2640
2880
|
};
|
|
2641
2881
|
}
|
|
2642
2882
|
|
|
2643
|
-
// src/lib/runner.ts
|
|
2644
|
-
import { execFile } from "child_process";
|
|
2645
|
-
async function runMicroserviceCommand(name, args, timeout = 30000) {
|
|
2646
|
-
const meta = getMicroservice(name);
|
|
2647
|
-
if (!meta) {
|
|
2648
|
-
return { success: false, stdout: "", stderr: `Unknown microservice '${name}'`, exitCode: 1 };
|
|
2649
|
-
}
|
|
2650
|
-
if (!microserviceExists(name)) {
|
|
2651
|
-
return {
|
|
2652
|
-
success: false,
|
|
2653
|
-
stdout: "",
|
|
2654
|
-
stderr: `microservice-${name} is not installed. Run: bun install -g ${meta.package}`,
|
|
2655
|
-
exitCode: 1
|
|
2656
|
-
};
|
|
2657
|
-
}
|
|
2658
|
-
return new Promise((resolve) => {
|
|
2659
|
-
execFile(meta.binary, args, { timeout, maxBuffer: 10 * 1024 * 1024 }, (error, stdout, stderr) => {
|
|
2660
|
-
if (error && "killed" in error && error.killed) {
|
|
2661
|
-
resolve({ success: false, stdout: "", stderr: "Command timed out", exitCode: 1 });
|
|
2662
|
-
return;
|
|
2663
|
-
}
|
|
2664
|
-
const exitCode = error?.code ? typeof error.code === "number" ? error.code : 1 : 0;
|
|
2665
|
-
resolve({
|
|
2666
|
-
success: exitCode === 0,
|
|
2667
|
-
stdout: (stdout || "").trim(),
|
|
2668
|
-
stderr: (stderr || "").trim(),
|
|
2669
|
-
exitCode
|
|
2670
|
-
});
|
|
2671
|
-
});
|
|
2672
|
-
});
|
|
2673
|
-
}
|
|
2674
|
-
|
|
2675
2883
|
// src/lib/package-info.ts
|
|
2676
2884
|
import { existsSync, readFileSync } from "fs";
|
|
2677
2885
|
import { dirname, join, resolve } from "path";
|
|
@@ -2704,6 +2912,48 @@ function getPackageVersion(startDir) {
|
|
|
2704
2912
|
return readPackageVersion(packageJsonPath);
|
|
2705
2913
|
}
|
|
2706
2914
|
|
|
2915
|
+
// src/lib/runner.ts
|
|
2916
|
+
import { execFile } from "child_process";
|
|
2917
|
+
async function runMicroserviceCommand(name, args, timeout = 30000) {
|
|
2918
|
+
const meta = getMicroservice(name);
|
|
2919
|
+
if (!meta) {
|
|
2920
|
+
return {
|
|
2921
|
+
success: false,
|
|
2922
|
+
stdout: "",
|
|
2923
|
+
stderr: `Unknown microservice '${name}'`,
|
|
2924
|
+
exitCode: 1
|
|
2925
|
+
};
|
|
2926
|
+
}
|
|
2927
|
+
if (!microserviceExists(name)) {
|
|
2928
|
+
return {
|
|
2929
|
+
success: false,
|
|
2930
|
+
stdout: "",
|
|
2931
|
+
stderr: `microservice-${name} is not installed. Run: bun install -g ${meta.package}`,
|
|
2932
|
+
exitCode: 1
|
|
2933
|
+
};
|
|
2934
|
+
}
|
|
2935
|
+
return new Promise((resolve2) => {
|
|
2936
|
+
execFile(meta.binary, args, { timeout, maxBuffer: 10 * 1024 * 1024 }, (error, stdout, stderr) => {
|
|
2937
|
+
if (error && "killed" in error && error.killed) {
|
|
2938
|
+
resolve2({
|
|
2939
|
+
success: false,
|
|
2940
|
+
stdout: "",
|
|
2941
|
+
stderr: "Command timed out",
|
|
2942
|
+
exitCode: 1
|
|
2943
|
+
});
|
|
2944
|
+
return;
|
|
2945
|
+
}
|
|
2946
|
+
const exitCode = error?.code ? typeof error.code === "number" ? error.code : 1 : 0;
|
|
2947
|
+
resolve2({
|
|
2948
|
+
success: exitCode === 0,
|
|
2949
|
+
stdout: (stdout || "").trim(),
|
|
2950
|
+
stderr: (stderr || "").trim(),
|
|
2951
|
+
exitCode
|
|
2952
|
+
});
|
|
2953
|
+
});
|
|
2954
|
+
});
|
|
2955
|
+
}
|
|
2956
|
+
|
|
2707
2957
|
// src/cli/index.tsx
|
|
2708
2958
|
var program2 = new Command;
|
|
2709
2959
|
program2.name("microservices").description("Production-grade microservice building blocks for SaaS apps").version(getPackageVersion());
|
|
@@ -2762,10 +3012,10 @@ Microservice status:
|
|
|
2762
3012
|
program2.command("run <name> [args...]").description("Run a command on an installed microservice").action(async (name, args) => {
|
|
2763
3013
|
const result = await runMicroserviceCommand(name, args);
|
|
2764
3014
|
if (result.stdout)
|
|
2765
|
-
process.stdout.write(result.stdout
|
|
3015
|
+
process.stdout.write(`${result.stdout}
|
|
2766
3016
|
`);
|
|
2767
3017
|
if (result.stderr)
|
|
2768
|
-
process.stderr.write(result.stderr
|
|
3018
|
+
process.stderr.write(`${result.stderr}
|
|
2769
3019
|
`);
|
|
2770
3020
|
process.exit(result.exitCode);
|
|
2771
3021
|
});
|
|
@@ -2779,25 +3029,130 @@ program2.command("search <query>").description("Search microservices by name or
|
|
|
2779
3029
|
console.log(` ${source_default.cyan(m.name.padEnd(20))} ${m.description}`);
|
|
2780
3030
|
}
|
|
2781
3031
|
});
|
|
2782
|
-
program2.command("migrate-all").description("Run migrations
|
|
3032
|
+
program2.command("migrate-all").description("Run database migrations for all installed microservices").option("--db <url>", "PostgreSQL connection URL (overrides DATABASE_URL)").action(async (opts) => {
|
|
2783
3033
|
if (opts.db)
|
|
2784
|
-
process.env
|
|
2785
|
-
const installed =
|
|
3034
|
+
process.env.DATABASE_URL = opts.db;
|
|
3035
|
+
const installed = MICROSERVICES.filter((m) => microserviceExists(m.name));
|
|
2786
3036
|
if (installed.length === 0) {
|
|
2787
|
-
console.log(source_default.yellow("No microservices installed."));
|
|
3037
|
+
console.log(source_default.yellow("No microservices installed to migrate."));
|
|
2788
3038
|
return;
|
|
2789
3039
|
}
|
|
2790
3040
|
console.log(source_default.bold(`
|
|
2791
|
-
Migrating ${installed.length}
|
|
3041
|
+
Migrating ${installed.length} installed microservices...
|
|
2792
3042
|
`));
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
|
|
2797
|
-
|
|
2798
|
-
console.log(` ${source_default.
|
|
3043
|
+
let hasErrors = false;
|
|
3044
|
+
for (const m of installed) {
|
|
3045
|
+
console.log(source_default.blue(`Migrating ${m.name}...`));
|
|
3046
|
+
const result = await runMicroserviceCommand(m.name, ["migrate"]);
|
|
3047
|
+
if (result.success) {
|
|
3048
|
+
console.log(` ${source_default.green("\u2713")} ${result.stdout.trim() || "Success"}`);
|
|
3049
|
+
} else {
|
|
3050
|
+
console.log(` ${source_default.red("\u2717")} ${result.stderr.trim() || result.stdout.trim() || "Failed"}`);
|
|
3051
|
+
hasErrors = true;
|
|
3052
|
+
}
|
|
2799
3053
|
}
|
|
2800
3054
|
console.log();
|
|
3055
|
+
if (hasErrors) {
|
|
3056
|
+
console.error(source_default.red("Some migrations failed. Please check the logs above."));
|
|
3057
|
+
process.exit(1);
|
|
3058
|
+
} else {
|
|
3059
|
+
console.log(source_default.green("\u2713 All migrations completed successfully."));
|
|
3060
|
+
}
|
|
3061
|
+
});
|
|
3062
|
+
program2.command("serve-all").description("Start HTTP servers for all installed microservices concurrently").option("--db <url>", "PostgreSQL connection URL (overrides DATABASE_URL)").action(async (opts) => {
|
|
3063
|
+
if (opts.db)
|
|
3064
|
+
process.env.DATABASE_URL = opts.db;
|
|
3065
|
+
const installed = MICROSERVICES.filter((m) => microserviceExists(m.name));
|
|
3066
|
+
if (installed.length === 0) {
|
|
3067
|
+
console.log(source_default.yellow("No microservices installed to serve."));
|
|
3068
|
+
return;
|
|
3069
|
+
}
|
|
3070
|
+
console.log(source_default.bold(`
|
|
3071
|
+
Starting ${installed.length} installed microservices...
|
|
3072
|
+
`));
|
|
3073
|
+
const { spawn } = await import("child_process");
|
|
3074
|
+
const procs = [];
|
|
3075
|
+
const colors = [
|
|
3076
|
+
source_default.cyan,
|
|
3077
|
+
source_default.magenta,
|
|
3078
|
+
source_default.green,
|
|
3079
|
+
source_default.yellow,
|
|
3080
|
+
source_default.blue
|
|
3081
|
+
];
|
|
3082
|
+
for (let i = 0;i < installed.length; i++) {
|
|
3083
|
+
const m = installed[i];
|
|
3084
|
+
const color = colors[i % colors.length];
|
|
3085
|
+
const prefix = color(`[${m.name.padEnd(10)}] `);
|
|
3086
|
+
const proc = spawn(m.binary, ["serve"], {
|
|
3087
|
+
env: process.env,
|
|
3088
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
3089
|
+
});
|
|
3090
|
+
proc.stdout.on("data", (data) => {
|
|
3091
|
+
const lines = data.toString().trim().split(`
|
|
3092
|
+
`);
|
|
3093
|
+
for (const line of lines)
|
|
3094
|
+
if (line)
|
|
3095
|
+
console.log(`${prefix}${line}`);
|
|
3096
|
+
});
|
|
3097
|
+
proc.stderr.on("data", (data) => {
|
|
3098
|
+
const lines = data.toString().trim().split(`
|
|
3099
|
+
`);
|
|
3100
|
+
for (const line of lines)
|
|
3101
|
+
if (line)
|
|
3102
|
+
console.error(`${prefix}${source_default.red(line)}`);
|
|
3103
|
+
});
|
|
3104
|
+
proc.on("error", (err) => {
|
|
3105
|
+
console.error(`${prefix}${source_default.red("Failed to start:")} ${err.message}`);
|
|
3106
|
+
});
|
|
3107
|
+
procs.push(proc);
|
|
3108
|
+
}
|
|
3109
|
+
process.on("SIGINT", () => {
|
|
3110
|
+
console.log(source_default.yellow(`
|
|
3111
|
+
Stopping all microservices...`));
|
|
3112
|
+
for (const p of procs)
|
|
3113
|
+
p.kill("SIGINT");
|
|
3114
|
+
process.exit(0);
|
|
3115
|
+
});
|
|
3116
|
+
});
|
|
3117
|
+
program2.command("init-all").description("Run init (migrations) for all installed microservices").option("--db <url>", "PostgreSQL connection URL").action(async (opts) => {
|
|
3118
|
+
const installed = MICROSERVICES.filter((m) => microserviceExists(m.name));
|
|
3119
|
+
if (installed.length === 0) {
|
|
3120
|
+
console.log(source_default.yellow("No microservices installed to init."));
|
|
3121
|
+
return;
|
|
3122
|
+
}
|
|
3123
|
+
if (!opts.db) {
|
|
3124
|
+
console.error(source_default.red("--db <url> is required. Example: microservices init-all --db postgres://localhost/myapp"));
|
|
3125
|
+
process.exit(1);
|
|
3126
|
+
}
|
|
3127
|
+
process.env.DATABASE_URL = opts.db;
|
|
3128
|
+
console.log(source_default.bold(`
|
|
3129
|
+
Initializing ${installed.length} installed microservices...
|
|
3130
|
+
`));
|
|
3131
|
+
let hasErrors = false;
|
|
3132
|
+
for (const m of installed) {
|
|
3133
|
+
console.log(source_default.blue(`Initializing ${m.name}...`));
|
|
3134
|
+
const result = await runMicroserviceCommand(m.name, [
|
|
3135
|
+
"init",
|
|
3136
|
+
"--db",
|
|
3137
|
+
opts.db
|
|
3138
|
+
]);
|
|
3139
|
+
if (result.success) {
|
|
3140
|
+
console.log(` ${source_default.green("\u2713")} ${result.stdout.trim().split(`
|
|
3141
|
+
`).join(`
|
|
3142
|
+
`) || "Success"}`);
|
|
3143
|
+
} else {
|
|
3144
|
+
console.log(` ${source_default.red("\u2717")} ${result.stderr.trim() || result.stdout.trim() || "Failed"}`);
|
|
3145
|
+
hasErrors = true;
|
|
3146
|
+
}
|
|
3147
|
+
}
|
|
3148
|
+
console.log();
|
|
3149
|
+
if (hasErrors) {
|
|
3150
|
+
console.error(source_default.red("Some inits failed. Please check the logs above."));
|
|
3151
|
+
process.exit(1);
|
|
3152
|
+
} else {
|
|
3153
|
+
console.log(source_default.green("\u2713 All services initialized successfully."));
|
|
3154
|
+
console.log(source_default.gray(" You can now run 'microservices serve-all' to start them."));
|
|
3155
|
+
}
|
|
2801
3156
|
});
|
|
2802
3157
|
program2.command("info <name>").description("Show detailed info about a microservice").action((name) => {
|
|
2803
3158
|
const m = getMicroservice(name);
|
|
@@ -2820,4 +3175,89 @@ ${m.displayName}`));
|
|
|
2820
3175
|
console.log(` Tags: ${m.tags.join(", ")}`);
|
|
2821
3176
|
console.log();
|
|
2822
3177
|
});
|
|
3178
|
+
program2.command("check-env").description("Verify environment variables for all installed microservices").action(() => {
|
|
3179
|
+
const installed = MICROSERVICES.filter((m) => microserviceExists(m.name));
|
|
3180
|
+
if (installed.length === 0) {
|
|
3181
|
+
console.log(source_default.yellow("No microservices installed to check."));
|
|
3182
|
+
return;
|
|
3183
|
+
}
|
|
3184
|
+
console.log(source_default.bold(`
|
|
3185
|
+
Checking environment for ${installed.length} microservices...
|
|
3186
|
+
`));
|
|
3187
|
+
let totalMissing = 0;
|
|
3188
|
+
for (const m of installed) {
|
|
3189
|
+
const missingRequired = m.requiredEnv.filter((env2) => !process.env[env2]);
|
|
3190
|
+
const missingOptional = (m.optionalEnv ?? []).filter((env2) => !process.env[env2]);
|
|
3191
|
+
const status = missingRequired.length > 0 ? source_default.red("\u2717 Critical Missing") : missingOptional.length > 0 ? source_default.yellow("\u26A0 Warning") : source_default.green("\u2713 OK");
|
|
3192
|
+
console.log(`${source_default.bold(m.name.padEnd(12))} [${status}]`);
|
|
3193
|
+
if (missingRequired.length > 0) {
|
|
3194
|
+
console.log(source_default.red(` Required missing: ${missingRequired.join(", ")}`));
|
|
3195
|
+
totalMissing += missingRequired.length;
|
|
3196
|
+
}
|
|
3197
|
+
if (missingOptional.length > 0) {
|
|
3198
|
+
console.log(source_default.gray(` Optional missing: ${missingOptional.join(", ")}`));
|
|
3199
|
+
}
|
|
3200
|
+
if (missingRequired.length === 0 && missingOptional.length === 0) {
|
|
3201
|
+
console.log(source_default.gray(" All variables set."));
|
|
3202
|
+
}
|
|
3203
|
+
console.log();
|
|
3204
|
+
}
|
|
3205
|
+
if (totalMissing > 0) {
|
|
3206
|
+
console.log(source_default.red(`Found ${totalMissing} missing required environment variables.`));
|
|
3207
|
+
console.log(source_default.gray(`Refer to .env.example for guidance.
|
|
3208
|
+
`));
|
|
3209
|
+
process.exit(1);
|
|
3210
|
+
} else {
|
|
3211
|
+
console.log(source_default.green(`\u2713 All required environment variables are set across all installed services.
|
|
3212
|
+
`));
|
|
3213
|
+
}
|
|
3214
|
+
});
|
|
3215
|
+
program2.command("scaffold <name>").description("Scaffold a new microservice from the _template directory").action((name) => {
|
|
3216
|
+
if (!/^[a-z0-9-]+$/.test(name)) {
|
|
3217
|
+
console.error(source_default.red("Error: Name can only contain lowercase letters, numbers, and dashes."));
|
|
3218
|
+
process.exit(1);
|
|
3219
|
+
}
|
|
3220
|
+
const cwd = process.cwd();
|
|
3221
|
+
const templateDir = path.join(cwd, "microservices", "_template");
|
|
3222
|
+
const targetDir = path.join(cwd, "microservices", `microservice-${name}`);
|
|
3223
|
+
if (!fs.existsSync(templateDir)) {
|
|
3224
|
+
console.error(source_default.red(`Template directory not found at ${templateDir}. Make sure you are running this from the monorepo root.`));
|
|
3225
|
+
process.exit(1);
|
|
3226
|
+
}
|
|
3227
|
+
if (fs.existsSync(targetDir)) {
|
|
3228
|
+
console.error(source_default.red(`Target directory already exists at ${targetDir}`));
|
|
3229
|
+
process.exit(1);
|
|
3230
|
+
}
|
|
3231
|
+
console.log(source_default.bold(`
|
|
3232
|
+
Scaffolding microservice-${name}...
|
|
3233
|
+
`));
|
|
3234
|
+
fs.cpSync(templateDir, targetDir, { recursive: true });
|
|
3235
|
+
const replaceInFile = (filePath) => {
|
|
3236
|
+
const content = fs.readFileSync(filePath, "utf-8");
|
|
3237
|
+
let updated = content.replace(/__NAME__/g, name.toUpperCase().replace(/-/g, "_"));
|
|
3238
|
+
updated = updated.replace(/__Name__/g, name.charAt(0).toUpperCase() + name.slice(1));
|
|
3239
|
+
updated = updated.replace(/__name__/g, name);
|
|
3240
|
+
fs.writeFileSync(filePath, updated, "utf-8");
|
|
3241
|
+
};
|
|
3242
|
+
const walkAndReplace = (dir) => {
|
|
3243
|
+
const files = fs.readdirSync(dir);
|
|
3244
|
+
for (const file of files) {
|
|
3245
|
+
const filePath = path.join(dir, file);
|
|
3246
|
+
if (fs.statSync(filePath).isDirectory()) {
|
|
3247
|
+
walkAndReplace(filePath);
|
|
3248
|
+
} else {
|
|
3249
|
+
replaceInFile(filePath);
|
|
3250
|
+
}
|
|
3251
|
+
}
|
|
3252
|
+
};
|
|
3253
|
+
walkAndReplace(targetDir);
|
|
3254
|
+
console.log(` ${source_default.green("\u2713")} Created microservices/microservice-${name}`);
|
|
3255
|
+
console.log(source_default.gray(`
|
|
3256
|
+
Next steps:
|
|
3257
|
+
1. Add entry to src/lib/registry.ts
|
|
3258
|
+
2. Implement schema, core logic, HTTP API, MCP, and CLI
|
|
3259
|
+
3. Run 'bun install' to link the workspace
|
|
3260
|
+
4. Run 'bun run build'
|
|
3261
|
+
`));
|
|
3262
|
+
});
|
|
2823
3263
|
program2.parse();
|