@mesh-tech/mesh-cli 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (140) hide show
  1. package/dist/bin/mesh.d.ts +16 -0
  2. package/dist/bin/mesh.d.ts.map +1 -0
  3. package/dist/bin/mesh.js +40 -0
  4. package/dist/bin/mesh.js.map +1 -0
  5. package/dist/src/commands/create-app.d.ts +10 -0
  6. package/dist/src/commands/create-app.d.ts.map +1 -0
  7. package/dist/src/commands/create-app.js +176 -0
  8. package/dist/src/commands/create-app.js.map +1 -0
  9. package/dist/src/commands/db/connect.d.ts +13 -0
  10. package/dist/src/commands/db/connect.d.ts.map +1 -0
  11. package/dist/src/commands/db/connect.js +68 -0
  12. package/dist/src/commands/db/connect.js.map +1 -0
  13. package/dist/src/commands/db/credentials.d.ts +16 -0
  14. package/dist/src/commands/db/credentials.d.ts.map +1 -0
  15. package/dist/src/commands/db/credentials.js +56 -0
  16. package/dist/src/commands/db/credentials.js.map +1 -0
  17. package/dist/src/commands/db/env.d.ts +15 -0
  18. package/dist/src/commands/db/env.d.ts.map +1 -0
  19. package/dist/src/commands/db/env.js +50 -0
  20. package/dist/src/commands/db/env.js.map +1 -0
  21. package/dist/src/commands/db/exec.d.ts +54 -0
  22. package/dist/src/commands/db/exec.d.ts.map +1 -0
  23. package/dist/src/commands/db/exec.js +331 -0
  24. package/dist/src/commands/db/exec.js.map +1 -0
  25. package/dist/src/commands/db/index.d.ts +12 -0
  26. package/dist/src/commands/db/index.d.ts.map +1 -0
  27. package/dist/src/commands/db/index.js +62 -0
  28. package/dist/src/commands/db/index.js.map +1 -0
  29. package/dist/src/commands/db/psql.d.ts +16 -0
  30. package/dist/src/commands/db/psql.d.ts.map +1 -0
  31. package/dist/src/commands/db/psql.js +133 -0
  32. package/dist/src/commands/db/psql.js.map +1 -0
  33. package/dist/src/commands/deploy.d.ts +24 -0
  34. package/dist/src/commands/deploy.d.ts.map +1 -0
  35. package/dist/src/commands/deploy.js +228 -0
  36. package/dist/src/commands/deploy.js.map +1 -0
  37. package/dist/src/commands/dev.d.ts +17 -0
  38. package/dist/src/commands/dev.d.ts.map +1 -0
  39. package/dist/src/commands/dev.js +1248 -0
  40. package/dist/src/commands/dev.js.map +1 -0
  41. package/dist/src/commands/devbox.d.ts +22 -0
  42. package/dist/src/commands/devbox.d.ts.map +1 -0
  43. package/dist/src/commands/devbox.js +463 -0
  44. package/dist/src/commands/devbox.js.map +1 -0
  45. package/dist/src/commands/login.d.ts +47 -0
  46. package/dist/src/commands/login.d.ts.map +1 -0
  47. package/dist/src/commands/login.js +344 -0
  48. package/dist/src/commands/login.js.map +1 -0
  49. package/dist/src/commands/registry.d.ts +20 -0
  50. package/dist/src/commands/registry.d.ts.map +1 -0
  51. package/dist/src/commands/registry.js +336 -0
  52. package/dist/src/commands/registry.js.map +1 -0
  53. package/dist/src/commands/secrets/exec.d.ts +17 -0
  54. package/dist/src/commands/secrets/exec.d.ts.map +1 -0
  55. package/dist/src/commands/secrets/exec.js +132 -0
  56. package/dist/src/commands/secrets/exec.js.map +1 -0
  57. package/dist/src/commands/secrets/index.d.ts +10 -0
  58. package/dist/src/commands/secrets/index.d.ts.map +1 -0
  59. package/dist/src/commands/secrets/index.js +35 -0
  60. package/dist/src/commands/secrets/index.js.map +1 -0
  61. package/dist/src/commands/secrets/set.d.ts +22 -0
  62. package/dist/src/commands/secrets/set.d.ts.map +1 -0
  63. package/dist/src/commands/secrets/set.js +234 -0
  64. package/dist/src/commands/secrets/set.js.map +1 -0
  65. package/dist/src/commands/stack.d.ts +17 -0
  66. package/dist/src/commands/stack.d.ts.map +1 -0
  67. package/dist/src/commands/stack.js +175 -0
  68. package/dist/src/commands/stack.js.map +1 -0
  69. package/dist/src/commands/tunnel/index.d.ts +14 -0
  70. package/dist/src/commands/tunnel/index.d.ts.map +1 -0
  71. package/dist/src/commands/tunnel/index.js +217 -0
  72. package/dist/src/commands/tunnel/index.js.map +1 -0
  73. package/dist/src/index.d.ts +7 -0
  74. package/dist/src/index.d.ts.map +1 -0
  75. package/dist/src/index.js +7 -0
  76. package/dist/src/index.js.map +1 -0
  77. package/dist/src/utils/bastion.d.ts +43 -0
  78. package/dist/src/utils/bastion.d.ts.map +1 -0
  79. package/dist/src/utils/bastion.js +77 -0
  80. package/dist/src/utils/bastion.js.map +1 -0
  81. package/dist/src/utils/context.d.ts +29 -0
  82. package/dist/src/utils/context.d.ts.map +1 -0
  83. package/dist/src/utils/context.js +132 -0
  84. package/dist/src/utils/context.js.map +1 -0
  85. package/dist/src/utils/credentials.d.ts +61 -0
  86. package/dist/src/utils/credentials.d.ts.map +1 -0
  87. package/dist/src/utils/credentials.js +145 -0
  88. package/dist/src/utils/credentials.js.map +1 -0
  89. package/dist/src/utils/index.d.ts +9 -0
  90. package/dist/src/utils/index.d.ts.map +1 -0
  91. package/dist/src/utils/index.js +9 -0
  92. package/dist/src/utils/index.js.map +1 -0
  93. package/dist/src/utils/log.d.ts +8 -0
  94. package/dist/src/utils/log.d.ts.map +1 -0
  95. package/dist/src/utils/log.js +17 -0
  96. package/dist/src/utils/log.js.map +1 -0
  97. package/dist/src/utils/pulumi.d.ts +24 -0
  98. package/dist/src/utils/pulumi.d.ts.map +1 -0
  99. package/dist/src/utils/pulumi.js +63 -0
  100. package/dist/src/utils/pulumi.js.map +1 -0
  101. package/package.json +50 -0
  102. package/templates/api-web-db/api/.dockerignore +2 -0
  103. package/templates/api-web-db/api/Dockerfile +16 -0
  104. package/templates/api-web-db/api/package.json.hbs +21 -0
  105. package/templates/api-web-db/api/src/index.ts.hbs +123 -0
  106. package/templates/api-web-db/api/tsconfig.json +13 -0
  107. package/templates/api-web-db/package.json.hbs +27 -0
  108. package/templates/api-web-db/sst.config.ts.hbs +151 -0
  109. package/templates/api-web-db/tsconfig.json +11 -0
  110. package/templates/api-web-db/web/.dockerignore +3 -0
  111. package/templates/api-web-db/web/Dockerfile +17 -0
  112. package/templates/api-web-db/web/app/lib/db.server.ts +36 -0
  113. package/templates/api-web-db/web/app/root.tsx +29 -0
  114. package/templates/api-web-db/web/app/routes/home.tsx.hbs +114 -0
  115. package/templates/api-web-db/web/app/routes.ts +3 -0
  116. package/templates/api-web-db/web/package.json.hbs +30 -0
  117. package/templates/api-web-db/web/prisma/schema.prisma +25 -0
  118. package/templates/api-web-db/web/prisma.config.ts +18 -0
  119. package/templates/api-web-db/web/react-router.config.ts +5 -0
  120. package/templates/api-web-db/web/tsconfig.json +19 -0
  121. package/templates/api-web-db/web/vite.config.ts +9 -0
  122. package/templates/temporal-api-worker/README.md.hbs +133 -0
  123. package/templates/temporal-api-worker/api/.dockerignore +4 -0
  124. package/templates/temporal-api-worker/api/Dockerfile +13 -0
  125. package/templates/temporal-api-worker/api/package.json.hbs +23 -0
  126. package/templates/temporal-api-worker/api/src/encryption-codec.ts +133 -0
  127. package/templates/temporal-api-worker/api/src/index.ts.hbs +145 -0
  128. package/templates/temporal-api-worker/api/tsconfig.json +14 -0
  129. package/templates/temporal-api-worker/package.json.hbs +29 -0
  130. package/templates/temporal-api-worker/sst.config.ts.hbs +161 -0
  131. package/templates/temporal-api-worker/tsconfig.json +10 -0
  132. package/templates/temporal-api-worker/worker/.dockerignore +4 -0
  133. package/templates/temporal-api-worker/worker/Dockerfile +11 -0
  134. package/templates/temporal-api-worker/worker/package.json.hbs +25 -0
  135. package/templates/temporal-api-worker/worker/src/activities.ts.hbs +49 -0
  136. package/templates/temporal-api-worker/worker/src/bundle-workflows.ts +38 -0
  137. package/templates/temporal-api-worker/worker/src/encryption-codec.ts +156 -0
  138. package/templates/temporal-api-worker/worker/src/worker.ts.hbs +93 -0
  139. package/templates/temporal-api-worker/worker/src/workflows.ts.hbs +60 -0
  140. package/templates/temporal-api-worker/worker/tsconfig.json +14 -0
@@ -0,0 +1,145 @@
1
+ /**
2
+ * Database credential utilities
3
+ */
4
+ import { SecretsManagerClient, GetSecretValueCommand, } from "@aws-sdk/client-secrets-manager";
5
+ import { logInfo, logSuccess } from "./log.js";
6
+ /**
7
+ * Get DATABASE_URL from Secrets Manager.
8
+ *
9
+ * Credential path patterns:
10
+ * - With app: mesh/{tenant}/{stage}/{app}/db-credentials
11
+ * - Without app: mesh/{tenant}/{stage}/db-credentials
12
+ *
13
+ * Returns the DATABASE_URL string directly. Use rewriteDatabaseUrl() to
14
+ * modify host/port for tunnel usage.
15
+ */
16
+ export async function getDatabaseUrl(tenant, stage, options) {
17
+ const secretsManager = new SecretsManagerClient({});
18
+ // Build secret path - with or without app name
19
+ const secretName = options?.app
20
+ ? `mesh/${tenant}/${stage}/${options.app}/db-credentials`
21
+ : `mesh/${tenant}/${stage}/db-credentials`;
22
+ logInfo(`Looking up credentials at ${secretName}...`);
23
+ try {
24
+ const response = await secretsManager.send(new GetSecretValueCommand({ SecretId: secretName }));
25
+ if (response.SecretString) {
26
+ const secret = JSON.parse(response.SecretString);
27
+ // Handle DATABASE_URL format (preferred)
28
+ if (secret.DATABASE_URL) {
29
+ const parsed = new URL(secret.DATABASE_URL);
30
+ logSuccess(`Got credentials for user: ${parsed.username}`);
31
+ return secret.DATABASE_URL;
32
+ }
33
+ // Handle individual fields format (legacy)
34
+ if (secret.username && secret.password) {
35
+ logSuccess(`Got credentials for user: ${secret.username}`);
36
+ const database = secret.dbname ?? secret.database ?? "postgres";
37
+ return `postgresql://${secret.username}:${encodeURIComponent(secret.password)}@${secret.host ?? "localhost"}:${secret.port ?? 5432}/${database}`;
38
+ }
39
+ throw new Error(`Secret ${secretName} has unexpected format (needs DATABASE_URL or username/password)`);
40
+ }
41
+ }
42
+ catch (error) {
43
+ if (error.message?.includes("unexpected format")) {
44
+ throw error;
45
+ }
46
+ // Secret not found - will be caught by caller
47
+ }
48
+ throw new Error(`Could not find credentials at ${secretName}`);
49
+ }
50
+ /**
51
+ * @deprecated Use getDatabaseUrl instead - returns DATABASE_URL directly
52
+ */
53
+ export async function getDbCredentials(tenant, stage, _platformEnv, rdsHost, rdsPort, options) {
54
+ const databaseUrl = await getDatabaseUrl(tenant, stage, options);
55
+ const parsed = new URL(databaseUrl);
56
+ return {
57
+ username: parsed.username,
58
+ password: decodeURIComponent(parsed.password),
59
+ host: rdsHost,
60
+ port: rdsPort,
61
+ database: parsed.pathname.slice(1),
62
+ };
63
+ }
64
+ /**
65
+ * Rewrite DATABASE_URL with new endpoint and/or SSL mode.
66
+ * Preserves username, password, database name, and other query params.
67
+ *
68
+ * Used by both mesh-env-exec (for sst dev) and mesh db exec (for ad-hoc commands).
69
+ */
70
+ export function rewriteDatabaseUrl(url, options) {
71
+ try {
72
+ const parsed = new URL(url);
73
+ // Rewrite endpoint if provided
74
+ if (options.endpoint) {
75
+ if (options.endpoint.includes(":")) {
76
+ const colonIndex = options.endpoint.lastIndexOf(":");
77
+ parsed.hostname = options.endpoint.slice(0, colonIndex);
78
+ parsed.port = options.endpoint.slice(colonIndex + 1);
79
+ }
80
+ else {
81
+ parsed.hostname = options.endpoint;
82
+ parsed.port = parsed.port || "5432";
83
+ }
84
+ }
85
+ // Set SSL mode if provided
86
+ if (options.sslMode) {
87
+ parsed.searchParams.set("sslmode", options.sslMode);
88
+ // For Prisma: accept invalid/self-signed certificates through tunnels
89
+ if (options.sslMode === "require" || options.sslMode === "no-verify") {
90
+ parsed.searchParams.set("sslaccept", "accept_invalid_certs");
91
+ }
92
+ }
93
+ return parsed.toString();
94
+ }
95
+ catch {
96
+ // If URL parsing fails, return original
97
+ return url;
98
+ }
99
+ }
100
+ /**
101
+ * Build a DATABASE_URL from credentials.
102
+ */
103
+ export function buildDatabaseUrl(creds, options) {
104
+ const host = options?.endpoint?.split(":")[0] ?? creds.host;
105
+ const port = options?.endpoint?.split(":")[1] ?? String(creds.port);
106
+ let url = `postgresql://${creds.username}:${encodeURIComponent(creds.password)}@${host}:${port}/${creds.database}`;
107
+ if (options?.sslMode) {
108
+ url += `?sslmode=${options.sslMode}`;
109
+ if (options.sslMode === "require" || options.sslMode === "no-verify") {
110
+ url += "&sslaccept=accept_invalid_certs";
111
+ }
112
+ }
113
+ return url;
114
+ }
115
+ /**
116
+ * Try to read database info from SST outputs (when sst dev has run).
117
+ */
118
+ export async function readSstOutputs() {
119
+ const fs = await import("fs");
120
+ if (!fs.existsSync(".sst/outputs.json")) {
121
+ return null;
122
+ }
123
+ try {
124
+ const content = fs.readFileSync(".sst/outputs.json", "utf-8");
125
+ const outputs = JSON.parse(content);
126
+ if (outputs.databaseUrl) {
127
+ logInfo("Found SST outputs with database config");
128
+ // Extract secret ARN from URL if present
129
+ const secretArnMatch = outputs.databaseUrl.match(/secretArn=([^&]+)/);
130
+ const secretArn = secretArnMatch
131
+ ? decodeURIComponent(secretArnMatch[1])
132
+ : undefined;
133
+ return {
134
+ databaseUrl: outputs.databaseUrl,
135
+ databaseName: outputs.databaseName,
136
+ secretArn,
137
+ };
138
+ }
139
+ }
140
+ catch {
141
+ // Ignore parse errors
142
+ }
143
+ return null;
144
+ }
145
+ //# sourceMappingURL=credentials.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"credentials.js","sourceRoot":"","sources":["../../../src/utils/credentials.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Utility exports
3
+ */
4
+ export * from "./log.js";
5
+ export * from "./context.js";
6
+ export * from "./bastion.js";
7
+ export * from "./credentials.js";
8
+ export * from "./pulumi.js";
9
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/utils/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,cAAc,UAAU,CAAC;AACzB,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC;AAC7B,cAAc,kBAAkB,CAAC;AACjC,cAAc,aAAa,CAAC"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Utility exports
3
+ */
4
+ export * from "./log.js";
5
+ export * from "./context.js";
6
+ export * from "./bastion.js";
7
+ export * from "./credentials.js";
8
+ export * from "./pulumi.js";
9
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/utils/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,cAAc,UAAU,CAAC;AACzB,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC;AAC7B,cAAc,kBAAkB,CAAC;AACjC,cAAc,aAAa,CAAC"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Logging utilities with colored output
3
+ */
4
+ export declare function logInfo(message: string): void;
5
+ export declare function logSuccess(message: string): void;
6
+ export declare function logWarn(message: string): void;
7
+ export declare function logError(message: string): void;
8
+ //# sourceMappingURL=log.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"log.d.ts","sourceRoot":"","sources":["../../../src/utils/log.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,wBAAgB,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAE7C;AAED,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAEhD;AAED,wBAAgB,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAE7C;AAED,wBAAgB,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAE9C"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Logging utilities with colored output
3
+ */
4
+ import chalk from "chalk";
5
+ export function logInfo(message) {
6
+ console.error(chalk.blue("ℹ"), message);
7
+ }
8
+ export function logSuccess(message) {
9
+ console.error(chalk.green("✓"), message);
10
+ }
11
+ export function logWarn(message) {
12
+ console.error(chalk.yellow("⚠"), message);
13
+ }
14
+ export function logError(message) {
15
+ console.error(chalk.red("✗"), message);
16
+ }
17
+ //# sourceMappingURL=log.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"log.js","sourceRoot":"","sources":["../../../src/utils/log.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,MAAM,UAAU,OAAO,CAAC,OAAe;IACrC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC,CAAC;AAC1C,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,OAAe;IACxC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC,CAAC;AAC3C,CAAC;AAED,MAAM,UAAU,OAAO,CAAC,OAAe;IACrC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC,CAAC;AAC5C,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,OAAe;IACtC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC,CAAC;AACzC,CAAC"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Pulumi-related utilities shared across CLI commands.
3
+ */
4
+ /**
5
+ * Find the Pulumi app root by walking up from a starting directory.
6
+ * Returns the first directory containing `Pulumi.yaml`, or null.
7
+ */
8
+ export declare function findAppRoot(startDir: string): string | null;
9
+ /**
10
+ * Find Pulumi.<stack>.yaml files in a directory.
11
+ * Returns an array of stack names (without the Pulumi. prefix and .yaml suffix).
12
+ */
13
+ export declare function findStackConfigs(appRoot: string): string[];
14
+ /**
15
+ * Get the currently selected Pulumi stack name, or null.
16
+ */
17
+ export declare function getCurrentStack(appRoot: string): string | null;
18
+ /**
19
+ * Read a config value from Pulumi.<stack>.yaml.
20
+ * Simple regex-based parser — handles single-line string values.
21
+ * For complex values, use `pulumi config` CLI instead.
22
+ */
23
+ export declare function readStackConfig(appRoot: string, stack: string, key: string): string | null;
24
+ //# sourceMappingURL=pulumi.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pulumi.d.ts","sourceRoot":"","sources":["../../../src/utils/pulumi.ts"],"names":[],"mappings":"AAAA;;GAEG;AAMH;;;GAGG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAQ3D;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CAI1D;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAW9D;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAU1F"}
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Pulumi-related utilities shared across CLI commands.
3
+ */
4
+ import { execFileSync } from "child_process";
5
+ import * as path from "path";
6
+ import * as fs from "fs";
7
+ /**
8
+ * Find the Pulumi app root by walking up from a starting directory.
9
+ * Returns the first directory containing `Pulumi.yaml`, or null.
10
+ */
11
+ export function findAppRoot(startDir) {
12
+ let dir = startDir;
13
+ while (true) {
14
+ if (fs.existsSync(path.join(dir, "Pulumi.yaml")))
15
+ return dir;
16
+ const parent = path.dirname(dir);
17
+ if (parent === dir)
18
+ return null;
19
+ dir = parent;
20
+ }
21
+ }
22
+ /**
23
+ * Find Pulumi.<stack>.yaml files in a directory.
24
+ * Returns an array of stack names (without the Pulumi. prefix and .yaml suffix).
25
+ */
26
+ export function findStackConfigs(appRoot) {
27
+ return fs.readdirSync(appRoot)
28
+ .filter((f) => /^Pulumi\..+\.yaml$/.test(f) && f !== "Pulumi.yaml")
29
+ .map((f) => f.replace(/^Pulumi\./, "").replace(/\.yaml$/, ""));
30
+ }
31
+ /**
32
+ * Get the currently selected Pulumi stack name, or null.
33
+ */
34
+ export function getCurrentStack(appRoot) {
35
+ try {
36
+ const result = execFileSync("pulumi", ["stack", "--show-name"], {
37
+ encoding: "utf-8",
38
+ cwd: appRoot,
39
+ stdio: ["pipe", "pipe", "pipe"],
40
+ });
41
+ return result.trim() || null;
42
+ }
43
+ catch {
44
+ return null;
45
+ }
46
+ }
47
+ /**
48
+ * Read a config value from Pulumi.<stack>.yaml.
49
+ * Simple regex-based parser — handles single-line string values.
50
+ * For complex values, use `pulumi config` CLI instead.
51
+ */
52
+ export function readStackConfig(appRoot, stack, key) {
53
+ const configFile = path.join(appRoot, `Pulumi.${stack}.yaml`);
54
+ if (!fs.existsSync(configFile))
55
+ return null;
56
+ const content = fs.readFileSync(configFile, "utf-8");
57
+ const pattern = new RegExp(`^\\s+${key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}:\\s*(.+)$`, "m");
58
+ const match = content.match(pattern);
59
+ if (!match)
60
+ return null;
61
+ return match[1].trim().replace(/^["']|["']$/g, "");
62
+ }
63
+ //# sourceMappingURL=pulumi.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pulumi.js","sourceRoot":"","sources":["../../../src/utils/pulumi.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAC7B,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AAEzB;;;GAGG;AACH,MAAM,UAAU,WAAW,CAAC,QAAgB;IAC1C,IAAI,GAAG,GAAG,QAAQ,CAAC;IACnB,OAAO,IAAI,EAAE,CAAC;QACZ,IAAI,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,aAAa,CAAC,CAAC;YAAE,OAAO,GAAG,CAAC;QAC7D,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QACjC,IAAI,MAAM,KAAK,GAAG;YAAE,OAAO,IAAI,CAAC;QAChC,GAAG,GAAG,MAAM,CAAC;IACf,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAAC,OAAe;IAC9C,OAAO,EAAE,CAAC,WAAW,CAAC,OAAO,CAAC;SAC3B,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,oBAAoB,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,aAAa,CAAC;SAClE,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC,CAAC;AACnE,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,eAAe,CAAC,OAAe;IAC7C,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,YAAY,CAAC,QAAQ,EAAE,CAAC,OAAO,EAAE,aAAa,CAAC,EAAE;YAC9D,QAAQ,EAAE,OAAO;YACjB,GAAG,EAAE,OAAO;YACZ,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC;SAChC,CAAC,CAAC;QACH,OAAO,MAAM,CAAC,IAAI,EAAE,IAAI,IAAI,CAAC;IAC/B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAAC,OAAe,EAAE,KAAa,EAAE,GAAW;IACzE,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,UAAU,KAAK,OAAO,CAAC,CAAC;IAC9D,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC;QAAE,OAAO,IAAI,CAAC;IAE5C,MAAM,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;IACrD,MAAM,OAAO,GAAG,IAAI,MAAM,CAAC,QAAQ,GAAG,CAAC,OAAO,CAAC,qBAAqB,EAAE,MAAM,CAAC,YAAY,EAAE,GAAG,CAAC,CAAC;IAChG,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IACrC,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IAExB,OAAO,KAAK,CAAC,CAAC,CAAE,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC;AACtD,CAAC"}
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "@mesh-tech/mesh-cli",
3
+ "version": "0.1.0",
4
+ "description": "CLI for Mesh platform development — auth, deploy, registry, tunnels, and more",
5
+ "type": "module",
6
+ "license": "UNLICENSED",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/mesh-tech/mesh-platform.git",
10
+ "directory": "libs/mesh-cli"
11
+ },
12
+ "bin": {
13
+ "mesh": "./dist/bin/mesh.js"
14
+ },
15
+ "files": [
16
+ "dist",
17
+ "templates"
18
+ ],
19
+ "publishConfig": {
20
+ "access": "public",
21
+ "registry": "https://registry.npmjs.org"
22
+ },
23
+ "scripts": {
24
+ "build": "tsc",
25
+ "dev": "tsc --watch",
26
+ "typecheck": "tsc --noEmit",
27
+ "prepublishOnly": "tsc"
28
+ },
29
+ "exports": {
30
+ ".": {
31
+ "types": "./dist/src/index.d.ts",
32
+ "default": "./dist/src/index.js"
33
+ }
34
+ },
35
+ "devDependencies": {
36
+ "@types/node": "^20.0.0"
37
+ },
38
+ "dependencies": {
39
+ "@aws-sdk/client-secrets-manager": "^3.700.0",
40
+ "@aws-sdk/client-ssm": "^3.700.0",
41
+ "@inquirer/input": "^5.0.8",
42
+ "@inquirer/password": "^5.0.8",
43
+ "chalk": "^5.3.0",
44
+ "commander": "^12.1.0",
45
+ "handlebars": "^4.7.8"
46
+ },
47
+ "engines": {
48
+ "node": ">=20.0.0"
49
+ }
50
+ }
@@ -0,0 +1,2 @@
1
+ node_modules
2
+ dist
@@ -0,0 +1,16 @@
1
+ FROM node:22-alpine AS builder
2
+ WORKDIR /app
3
+ COPY package.json ./
4
+ RUN npm install
5
+ COPY tsconfig.json ./
6
+ COPY src ./src
7
+ RUN npm run build
8
+
9
+ FROM node:22-alpine
10
+ WORKDIR /app
11
+ COPY package.json ./
12
+ RUN npm install --omit=dev
13
+ COPY --from=builder /app/dist ./dist
14
+ ENV NODE_ENV=production PORT=3001
15
+ EXPOSE 3001
16
+ CMD ["node", "dist/index.js"]
@@ -0,0 +1,21 @@
1
+ {
2
+ "name": "{{name}}-api",
3
+ "type": "module",
4
+ "scripts": {
5
+ "dev": "tsx watch src/index.ts",
6
+ "build": "tsc",
7
+ "start": "node dist/index.js"
8
+ },
9
+ "dependencies": {
10
+ "@aws-sdk/client-secrets-manager": "^3.964.0",
11
+ "@hono/node-server": "^1.13.7",
12
+ "hono": "^4.6.14",
13
+ "pg": "^8.16.3"
14
+ },
15
+ "devDependencies": {
16
+ "@types/node": "^22.10.5",
17
+ "@types/pg": "^8.16.0",
18
+ "tsx": "^4.19.2",
19
+ "typescript": "^5.7.2"
20
+ }
21
+ }
@@ -0,0 +1,123 @@
1
+ import { serve } from "@hono/node-server";
2
+ import { Hono } from "hono";
3
+ import { cors } from "hono/cors";
4
+ import { logger } from "hono/logger";
5
+ import { Pool } from "pg";
6
+ import {
7
+ SecretsManagerClient,
8
+ GetSecretValueCommand,
9
+ } from "@aws-sdk/client-secrets-manager";
10
+
11
+ const app = new Hono();
12
+
13
+ // Middleware
14
+ app.use("*", logger());
15
+ app.use("*", cors());
16
+
17
+ // =============================================================================
18
+ // Database Connection
19
+ // =============================================================================
20
+
21
+ let dbPool: Pool | null = null;
22
+
23
+ async function getDbPassword(): Promise<string> {
24
+ const secretArn = process.env.DB_SECRET_ARN;
25
+ if (!secretArn) {
26
+ throw new Error("DB_SECRET_ARN not configured");
27
+ }
28
+
29
+ const client = new SecretsManagerClient({ region: "us-east-1" });
30
+ const response = await client.send(
31
+ new GetSecretValueCommand({ SecretId: secretArn })
32
+ );
33
+
34
+ if (!response.SecretString) {
35
+ throw new Error("No secret value found");
36
+ }
37
+
38
+ const secret = JSON.parse(response.SecretString);
39
+ return secret.password;
40
+ }
41
+
42
+ async function getDbPool(): Promise<Pool> {
43
+ if (dbPool) return dbPool;
44
+
45
+ const password = await getDbPassword();
46
+
47
+ dbPool = new Pool({
48
+ host: process.env.DB_HOST || "localhost",
49
+ port: parseInt(process.env.DB_PORT || "5432", 10),
50
+ database: process.env.DB_NAME || "postgres",
51
+ user: process.env.DB_USER || "postgres",
52
+ password,
53
+ ssl: { rejectUnauthorized: false },
54
+ max: 10,
55
+ idleTimeoutMillis: 30000,
56
+ connectionTimeoutMillis: 5000,
57
+ });
58
+
59
+ return dbPool;
60
+ }
61
+
62
+ // =============================================================================
63
+ // Routes
64
+ // =============================================================================
65
+
66
+ // Root - API info
67
+ app.get("/", (c) =>
68
+ c.json({
69
+ name: "{{name}}-api",
70
+ version: "1.0.0",
71
+ endpoints: ["/health", "/hello", "/db"],
72
+ })
73
+ );
74
+
75
+ // Health check (required for ALB)
76
+ app.get("/health", (c) => c.json({ status: "ok" }));
77
+
78
+ // Hello endpoint
79
+ app.get("/hello", (c) => {
80
+ return c.json({
81
+ message: "Hello from {{name}} API!",
82
+ stage: process.env.STAGE || "unknown",
83
+ timestamp: new Date().toISOString(),
84
+ });
85
+ });
86
+
87
+ // Database endpoint
88
+ app.get("/db", async (c) => {
89
+ try {
90
+ const pool = await getDbPool();
91
+ const result = await pool.query(
92
+ "SELECT version(), current_database(), current_user"
93
+ );
94
+ const row = result.rows[0];
95
+
96
+ return c.json({
97
+ status: "connected",
98
+ database: row.current_database,
99
+ user: row.current_user,
100
+ version: row.version,
101
+ timestamp: new Date().toISOString(),
102
+ });
103
+ } catch (error) {
104
+ return c.json(
105
+ {
106
+ status: "error",
107
+ message: error instanceof Error ? error.message : "Unknown error",
108
+ },
109
+ 500
110
+ );
111
+ }
112
+ });
113
+
114
+ // =============================================================================
115
+ // Start Server
116
+ // =============================================================================
117
+
118
+ const port = parseInt(process.env.PORT || "3001", 10);
119
+ console.log(`API server starting on port ${port}...`);
120
+
121
+ serve({ fetch: app.fetch, port });
122
+
123
+ console.log(`API server running at http://localhost:${port}`);
@@ -0,0 +1,13 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "ESNext",
5
+ "moduleResolution": "bundler",
6
+ "esModuleInterop": true,
7
+ "strict": true,
8
+ "skipLibCheck": true,
9
+ "outDir": "./dist",
10
+ "rootDir": "./src"
11
+ },
12
+ "include": ["src/**/*"]
13
+ }
@@ -0,0 +1,27 @@
1
+ {
2
+ "name": "{{name}}",
3
+ "private": true,
4
+ "type": "module",
5
+ "scripts": {
6
+ "typecheck": "tsc --noEmit",
7
+ "sst:patch": "patch-sst-pulumi .",
8
+ "dev": "pnpm run sst:patch && sst dev",
9
+ "deploy": "pnpm run sst:patch && sst deploy",
10
+ "deploy:dev": "pnpm run sst:patch && sst deploy --stage dev",
11
+ "deploy:prod": "pnpm run sst:patch && sst deploy --stage prod",
12
+ "remove": "sst remove"
13
+ },
14
+ "dependencies": {
15
+ "@mesh-tech/infra-components": "workspace:*",
16
+ "@pulumi/aws": "6.66.2",
17
+ "@pulumi/docker-build": "^0.0.15",
18
+ "@pulumi/eks": "4.2.0",
19
+ "@pulumi/kubernetes": "4.24.1",
20
+ "@pulumi/pulumi": "3.214.1",
21
+ "sst": "^3.3.61"
22
+ },
23
+ "devDependencies": {
24
+ "@aws-sdk/credential-providers": "^3.964.0",
25
+ "typescript": "^5.7.2"
26
+ }
27
+ }