@mesh-tech/mesh-cli 0.25.0 → 0.26.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/README.md +74 -246
- package/dist/bin/mesh.js +1415 -842
- package/dist/bin/mesh.js.map +3 -3
- package/dist/build-info.json +2 -2
- package/dist/src/commands/db/axes.d.ts +19 -0
- package/dist/src/commands/db/axes.js +35 -0
- package/dist/src/commands/db/connect.d.ts +2 -4
- package/dist/src/commands/db/connect.js +4 -19
- package/dist/src/commands/db/credentials.d.ts +2 -5
- package/dist/src/commands/db/credentials.js +7 -23
- package/dist/src/commands/db/env.d.ts +2 -5
- package/dist/src/commands/db/env.js +7 -23
- package/dist/src/commands/db/exec.js +7 -24
- package/dist/src/commands/db/psql.d.ts +2 -5
- package/dist/src/commands/db/psql.js +8 -23
- package/dist/src/commands/init/platform-setup.d.ts +68 -0
- package/dist/src/commands/init/platform-setup.js +203 -0
- package/dist/src/commands/init/wizard.d.ts +20 -0
- package/dist/src/commands/init/wizard.js +89 -7
- package/dist/src/commands/init.js +42 -36
- package/dist/src/commands/login.d.ts +4 -0
- package/dist/src/commands/login.js +16 -2
- package/dist/src/commands/registry.d.ts +2 -2
- package/dist/src/commands/registry.js +11 -6
- package/dist/src/commands/secrets/context.d.ts +5 -0
- package/dist/src/commands/secrets/context.js +5 -0
- package/dist/src/commands/secrets/index.js +3 -0
- package/dist/src/commands/secrets/migrate-config.d.ts +1 -0
- package/dist/src/commands/secrets/migrate-config.js +2 -3
- package/dist/src/commands/secrets/reindex.d.ts +1 -0
- package/dist/src/commands/secrets/reindex.js +2 -2
- package/dist/src/commands/secrets/set.d.ts +1 -0
- package/dist/src/commands/secrets/set.js +2 -2
- package/dist/src/commands/temporal.js +25 -8
- package/dist/src/commands/temporal.js.map +2 -2
- package/dist/src/commands/tunnel/index.d.ts +8 -0
- package/dist/src/commands/tunnel/index.js +3 -3
- package/dist/src/index.js +324 -62
- package/dist/src/index.js.map +3 -3
- package/dist/src/utils/context.d.ts +6 -1
- package/dist/src/utils/context.js +302 -39
- package/dist/src/utils/registry-identity.d.ts +5 -0
- package/dist/src/utils/registry-identity.js +16 -0
- package/fragments/agents/AGENTS.md.hbs +10 -2
- package/fragments/base/index.ts.hbs +15 -5
- package/package.json +2 -2
- package/skills/core/SKILL.md +73 -6
- package/templates/api-auth/index.ts.hbs +5 -4
- package/templates/api-role-gating/index.ts.hbs +5 -4
- package/templates/external-service/index.ts.hbs +5 -4
- package/templates/platform-repo/README.md.hbs +17 -3
- package/templates/platform-repo/core/Pulumi.{{tenant}}-{{env}}.yaml.hbs +7 -0
- package/templates/workflow/index.ts.hbs +5 -4
package/dist/bin/mesh.js
CHANGED
|
@@ -126,99 +126,373 @@ var init_log = __esm({
|
|
|
126
126
|
}
|
|
127
127
|
});
|
|
128
128
|
|
|
129
|
-
// libs/mesh-cli/src/utils/
|
|
129
|
+
// libs/mesh-cli/src/utils/mesh-json.ts
|
|
130
130
|
import * as fs from "fs";
|
|
131
131
|
import * as path from "path";
|
|
132
|
-
function
|
|
132
|
+
function isValidTenantName(value) {
|
|
133
|
+
return /^[a-z][a-z0-9-]*$/.test(value);
|
|
134
|
+
}
|
|
135
|
+
function parseMeshJson(content) {
|
|
136
|
+
try {
|
|
137
|
+
const data = JSON.parse(content);
|
|
138
|
+
if (typeof data?.tenant !== "string" || !isValidTenantName(data.tenant)) return null;
|
|
139
|
+
const platform = typeof data.platform === "string" && data.platform ? data.platform : "local";
|
|
140
|
+
return { tenant: data.tenant, platform };
|
|
141
|
+
} catch {
|
|
142
|
+
return null;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
function findMeshJson(startDir) {
|
|
146
|
+
let dir = path.resolve(startDir);
|
|
147
|
+
while (true) {
|
|
148
|
+
const candidate = path.join(dir, MESH_JSON);
|
|
149
|
+
if (fs.existsSync(candidate)) {
|
|
150
|
+
let content = "";
|
|
151
|
+
try {
|
|
152
|
+
content = fs.readFileSync(candidate, "utf-8");
|
|
153
|
+
} catch {
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
const data = parseMeshJson(content);
|
|
157
|
+
return data ? { path: candidate, data } : null;
|
|
158
|
+
}
|
|
159
|
+
if (fs.existsSync(path.join(dir, ".git"))) return null;
|
|
160
|
+
const parent = path.dirname(dir);
|
|
161
|
+
if (parent === dir) return null;
|
|
162
|
+
dir = parent;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
function writeMeshJson(rootDir, data) {
|
|
166
|
+
const target = path.join(rootDir, MESH_JSON);
|
|
167
|
+
fs.writeFileSync(target, `${JSON.stringify({ tenant: data.tenant, platform: data.platform }, null, 2)}
|
|
168
|
+
`);
|
|
169
|
+
return target;
|
|
170
|
+
}
|
|
171
|
+
var MESH_JSON;
|
|
172
|
+
var init_mesh_json = __esm({
|
|
173
|
+
"libs/mesh-cli/src/utils/mesh-json.ts"() {
|
|
174
|
+
"use strict";
|
|
175
|
+
MESH_JSON = "mesh.json";
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
// libs/mesh-cli/src/utils/context.ts
|
|
180
|
+
import * as fs2 from "fs";
|
|
181
|
+
import * as path2 from "path";
|
|
182
|
+
function findFileUpwardInRepo(filename, startDir = process.cwd()) {
|
|
133
183
|
let currentDir = startDir;
|
|
134
|
-
const root =
|
|
135
|
-
|
|
136
|
-
const filePath =
|
|
137
|
-
if (
|
|
184
|
+
const root = path2.parse(currentDir).root;
|
|
185
|
+
for (; ; ) {
|
|
186
|
+
const filePath = path2.join(currentDir, filename);
|
|
187
|
+
if (fs2.existsSync(filePath)) {
|
|
138
188
|
return filePath;
|
|
139
189
|
}
|
|
140
|
-
currentDir
|
|
190
|
+
if (fs2.existsSync(path2.join(currentDir, ".git")) || fs2.existsSync(path2.join(currentDir, "pnpm-workspace.yaml")) || currentDir === root) {
|
|
191
|
+
return void 0;
|
|
192
|
+
}
|
|
193
|
+
currentDir = path2.dirname(currentDir);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
function findRepoRoot(startDir) {
|
|
197
|
+
let currentDir = startDir;
|
|
198
|
+
const root = path2.parse(currentDir).root;
|
|
199
|
+
for (; ; ) {
|
|
200
|
+
if (fs2.existsSync(path2.join(currentDir, ".git")) || fs2.existsSync(path2.join(currentDir, "pnpm-workspace.yaml"))) {
|
|
201
|
+
return currentDir;
|
|
202
|
+
}
|
|
203
|
+
if (currentDir === root) {
|
|
204
|
+
return void 0;
|
|
205
|
+
}
|
|
206
|
+
currentDir = path2.dirname(currentDir);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
function stripComments(source) {
|
|
210
|
+
let out = "";
|
|
211
|
+
for (let i = 0; i < source.length; i++) {
|
|
212
|
+
const ch = source[i];
|
|
213
|
+
if (ch === '"' || ch === "'" || ch === "`") {
|
|
214
|
+
const start = i;
|
|
215
|
+
i++;
|
|
216
|
+
while (i < source.length && source[i] !== ch) {
|
|
217
|
+
if (source[i] === "\\") i++;
|
|
218
|
+
i++;
|
|
219
|
+
}
|
|
220
|
+
out += source.slice(start, i + 1);
|
|
221
|
+
} else if (ch === "/" && source[i + 1] === "/") {
|
|
222
|
+
while (i < source.length && source[i] !== "\n") i++;
|
|
223
|
+
i--;
|
|
224
|
+
} else if (ch === "/" && source[i + 1] === "*") {
|
|
225
|
+
i += 2;
|
|
226
|
+
while (i < source.length && !(source[i] === "*" && source[i + 1] === "/")) i++;
|
|
227
|
+
i++;
|
|
228
|
+
} else {
|
|
229
|
+
out += ch;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
return out;
|
|
233
|
+
}
|
|
234
|
+
function findExportedConfigStart(source) {
|
|
235
|
+
const declMatch = /export\s+(const\s+config\b|default\b)/.exec(source);
|
|
236
|
+
if (!declMatch) return void 0;
|
|
237
|
+
const start = source.indexOf("{", declMatch.index);
|
|
238
|
+
if (start === -1) return void 0;
|
|
239
|
+
const gap = source.slice(declMatch.index + declMatch[0].length, start);
|
|
240
|
+
if (!/^[\s=]*(:[^;{]*)?$/.test(gap)) return void 0;
|
|
241
|
+
return start;
|
|
242
|
+
}
|
|
243
|
+
function scanDepth1(source, start, visit) {
|
|
244
|
+
let depth = 0;
|
|
245
|
+
for (let i = start; i < source.length; i++) {
|
|
246
|
+
const ch = source[i];
|
|
247
|
+
if (ch === '"' || ch === "'" || ch === "`") {
|
|
248
|
+
i++;
|
|
249
|
+
while (i < source.length && source[i] !== ch) {
|
|
250
|
+
if (source[i] === "\\") i++;
|
|
251
|
+
i++;
|
|
252
|
+
}
|
|
253
|
+
} else if (ch === "{") {
|
|
254
|
+
depth++;
|
|
255
|
+
} else if (ch === "}") {
|
|
256
|
+
depth--;
|
|
257
|
+
if (depth === 0) return void 0;
|
|
258
|
+
} else if (depth === 1 && !/[\w$.]/.test(source[i - 1] ?? "")) {
|
|
259
|
+
const found = visit(i);
|
|
260
|
+
if (found !== void 0) return found;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
return void 0;
|
|
264
|
+
}
|
|
265
|
+
function extractConfigTenant(rawSource) {
|
|
266
|
+
const source = stripComments(rawSource);
|
|
267
|
+
const start = findExportedConfigStart(source);
|
|
268
|
+
if (start === void 0) return void 0;
|
|
269
|
+
const tenantRe = /tenant\s*:\s*(["'])([^"'\n]+)\1/y;
|
|
270
|
+
return scanDepth1(source, start, (i) => {
|
|
271
|
+
tenantRe.lastIndex = i;
|
|
272
|
+
return tenantRe.exec(source)?.[2];
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
function matchBlock(source, open) {
|
|
276
|
+
let depth = 0;
|
|
277
|
+
for (let i = open; i < source.length; i++) {
|
|
278
|
+
const ch = source[i];
|
|
279
|
+
if (ch === '"' || ch === "'" || ch === "`") {
|
|
280
|
+
i++;
|
|
281
|
+
while (i < source.length && source[i] !== ch) {
|
|
282
|
+
if (source[i] === "\\") i++;
|
|
283
|
+
i++;
|
|
284
|
+
}
|
|
285
|
+
} else if (ch === "{") {
|
|
286
|
+
depth++;
|
|
287
|
+
} else if (ch === "}") {
|
|
288
|
+
depth--;
|
|
289
|
+
if (depth === 0) return source.slice(open, i + 1);
|
|
290
|
+
}
|
|
141
291
|
}
|
|
142
292
|
return void 0;
|
|
143
293
|
}
|
|
144
|
-
function
|
|
294
|
+
function extractExportedConfigSource(cleanedSource) {
|
|
295
|
+
const start = findExportedConfigStart(cleanedSource);
|
|
296
|
+
return start === void 0 ? void 0 : matchBlock(cleanedSource, start);
|
|
297
|
+
}
|
|
298
|
+
function extractPlatformBlock(cleanedSource) {
|
|
299
|
+
const start = findExportedConfigStart(cleanedSource);
|
|
300
|
+
if (start === void 0) return void 0;
|
|
301
|
+
const platformRe = /platform\s*:\s*\{/y;
|
|
302
|
+
let open;
|
|
303
|
+
scanDepth1(cleanedSource, start, (i) => {
|
|
304
|
+
platformRe.lastIndex = i;
|
|
305
|
+
if (platformRe.exec(cleanedSource)) {
|
|
306
|
+
open = platformRe.lastIndex - 1;
|
|
307
|
+
return true;
|
|
308
|
+
}
|
|
309
|
+
return void 0;
|
|
310
|
+
});
|
|
311
|
+
if (open === void 0) return void 0;
|
|
312
|
+
return matchBlock(cleanedSource, open);
|
|
313
|
+
}
|
|
314
|
+
function pickStackFile(files, hint, explicitStack) {
|
|
315
|
+
if (explicitStack !== void 0) {
|
|
316
|
+
const wanted = `Pulumi.${explicitStack}.yaml`;
|
|
317
|
+
return { file: files.includes(wanted) ? wanted : void 0, qualified: true };
|
|
318
|
+
}
|
|
319
|
+
if (hint) {
|
|
320
|
+
const wanted = `Pulumi.${hint}.yaml`;
|
|
321
|
+
if (files.includes(wanted)) return { file: wanted, qualified: true };
|
|
322
|
+
}
|
|
323
|
+
const stageOf = (f) => f.slice("Pulumi.".length, -".yaml".length);
|
|
324
|
+
const byBaseFirst = [...files].sort(
|
|
325
|
+
(a, b) => stageOf(a).length - stageOf(b).length || a.localeCompare(b)
|
|
326
|
+
);
|
|
327
|
+
return { file: byBaseFirst[0], qualified: false };
|
|
328
|
+
}
|
|
329
|
+
function tenantNotDeclared(cwd, found) {
|
|
330
|
+
const looked = found.stackFile ? `${found.stackFile} in ${cwd} does not declare mesh:tenant` : found.hasProjectFile ? `${cwd} has a Pulumi.yaml but no Pulumi.<stack>.yaml to read mesh:tenant from` : `${cwd} is not a Pulumi project directory (no Pulumi.yaml)`;
|
|
331
|
+
return new MeshCliError(
|
|
332
|
+
`No tenant declared: ${looked}, and MESH_TENANT is not set. There is no default tenant \u2014 "mesh" is the live hub and is never assumed. Run from a directory whose Pulumi.<stack>.yaml declares mesh:tenant, set MESH_TENANT, or pass --tenant where the command accepts one.`,
|
|
333
|
+
{
|
|
334
|
+
remediation: {
|
|
335
|
+
command: "MESH_TENANT=<tenant> mesh \u2026 # or --tenant <tenant> where accepted, or run from a directory whose Pulumi.<stack>.yaml declares mesh:tenant"
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
);
|
|
339
|
+
}
|
|
340
|
+
function detectContext(stageArg, options = {}) {
|
|
145
341
|
let tenant;
|
|
146
342
|
let stage;
|
|
147
343
|
const cwd = process.cwd();
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
344
|
+
const stageHint = stageArg || process.env.MESH_STAGE || process.env.SST_STAGE;
|
|
345
|
+
let hasProjectFile = false;
|
|
346
|
+
let stackFile;
|
|
347
|
+
let stackFilesHere = [];
|
|
348
|
+
let namedStackMissing = false;
|
|
349
|
+
if (fs2.existsSync("Pulumi.yaml")) {
|
|
350
|
+
hasProjectFile = true;
|
|
351
|
+
const files = fs2.readdirSync(".").filter((f) => f.startsWith("Pulumi.") && f.endsWith(".yaml") && f !== "Pulumi.yaml").sort();
|
|
352
|
+
const picked = pickStackFile(files, stageHint, options.stack);
|
|
353
|
+
stackFile = picked.file;
|
|
354
|
+
namedStackMissing = options.stack !== void 0 && picked.file === void 0;
|
|
355
|
+
stackFilesHere = files;
|
|
356
|
+
if (!picked.qualified && files.length > 1) {
|
|
357
|
+
const lengths = files.map((f) => f.length - "Pulumi..yaml".length).sort((a, b) => a - b);
|
|
358
|
+
if (lengths[0] === lengths[1]) {
|
|
359
|
+
logWarn(
|
|
360
|
+
`Several Pulumi stack configs here (${files.join(", ")}); using ${stackFile}. Pass --stack to choose one.`
|
|
361
|
+
);
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
if (stackFile) {
|
|
365
|
+
const content = fs2.readFileSync(stackFile, "utf-8");
|
|
366
|
+
const tenantMatch = content.match(/^\s*mesh:tenant:\s*["']?([^"'#\n]+)["']?/m);
|
|
156
367
|
if (tenantMatch?.[1]) {
|
|
157
368
|
tenant = tenantMatch[1].trim();
|
|
158
369
|
}
|
|
159
|
-
const stageMatch =
|
|
370
|
+
const stageMatch = stackFile.match(/Pulumi\.(.+)\.yaml/);
|
|
160
371
|
if (stageMatch?.[1]) {
|
|
161
372
|
stage = stageMatch[1];
|
|
162
373
|
}
|
|
163
374
|
}
|
|
164
375
|
}
|
|
165
|
-
const sstConfigPath =
|
|
376
|
+
const sstConfigPath = findFileUpwardInRepo("sst.config.ts");
|
|
166
377
|
let sstDir;
|
|
167
378
|
if (sstConfigPath) {
|
|
168
|
-
sstDir =
|
|
169
|
-
const
|
|
170
|
-
if (
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
379
|
+
sstDir = path2.dirname(sstConfigPath);
|
|
380
|
+
const sstStagePath = path2.join(sstDir, ".sst", "stage");
|
|
381
|
+
if (fs2.existsSync(sstStagePath)) {
|
|
382
|
+
stage = fs2.readFileSync(sstStagePath, "utf-8").trim();
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
const repoRoot2 = findRepoRoot(cwd);
|
|
386
|
+
const parentConfigPath = (sstDir ? [path2.join(sstDir, "config.ts"), path2.join(sstDir, "..", "config.ts")] : hasProjectFile ? [path2.resolve(cwd, "..", "config.ts")] : []).filter(
|
|
387
|
+
// Fail closed when no checkout root is found — same stance as the
|
|
388
|
+
// no-tenant throw: an arbitrary config.ts is not a context source.
|
|
389
|
+
(p) => repoRoot2 !== void 0 && !path2.relative(repoRoot2, p).startsWith("..")
|
|
390
|
+
).find((p) => fs2.existsSync(p));
|
|
391
|
+
const parentConfig = parentConfigPath ? fs2.readFileSync(parentConfigPath, "utf-8") : void 0;
|
|
392
|
+
if (parentConfig && !tenant) {
|
|
393
|
+
tenant = extractConfigTenant(parentConfig);
|
|
394
|
+
}
|
|
395
|
+
if (!tenant && repoRoot2) {
|
|
396
|
+
const pathMatch = path2.relative(repoRoot2, cwd).match(/(?:^|\/)tenants\/([^/]+)/);
|
|
184
397
|
if (pathMatch) {
|
|
185
398
|
tenant = pathMatch[1];
|
|
186
399
|
}
|
|
187
400
|
}
|
|
188
|
-
|
|
189
|
-
|
|
401
|
+
if (!tenant) {
|
|
402
|
+
tenant = findMeshJson(cwd)?.data.tenant;
|
|
403
|
+
}
|
|
404
|
+
stage = stageHint || stage || "dev";
|
|
405
|
+
const detectedTenant = tenant;
|
|
406
|
+
tenant = options.tenant || process.env.MESH_TENANT || tenant;
|
|
407
|
+
if (!tenant) {
|
|
408
|
+
throw tenantNotDeclared(cwd, { stackFile, hasProjectFile });
|
|
409
|
+
}
|
|
410
|
+
if (!/^[a-z0-9][a-z0-9-]*$/.test(tenant)) {
|
|
411
|
+
throw new MeshCliError(
|
|
412
|
+
`Invalid tenant "${tenant}": expected lowercase alphanumerics and hyphens. This usually means a config file was misparsed.`,
|
|
413
|
+
{ remediation: { command: "mesh <command> --tenant <tenant>" } }
|
|
414
|
+
);
|
|
415
|
+
}
|
|
416
|
+
const overrideSource = options.tenant ? "--tenant" : "MESH_TENANT";
|
|
417
|
+
const tenantOverridden = detectedTenant !== void 0 && detectedTenant !== tenant;
|
|
418
|
+
if (tenantOverridden) {
|
|
419
|
+
logWarn(
|
|
420
|
+
`${overrideSource}=${tenant} overrides the tenant detected here (${detectedTenant}); paths will use ${tenant} while implicit AWS credentials still come from the ${detectedTenant} app's deployer role`
|
|
421
|
+
);
|
|
422
|
+
}
|
|
190
423
|
let platformEnv = stage;
|
|
191
|
-
|
|
424
|
+
const platformEnvMap = /* @__PURE__ */ Object.create(null);
|
|
192
425
|
let defaultPlatformEnv;
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
const
|
|
196
|
-
const
|
|
197
|
-
|
|
198
|
-
|
|
426
|
+
if (parentConfig) {
|
|
427
|
+
const cleaned = stripComments(parentConfig);
|
|
428
|
+
const platformScope = extractPlatformBlock(cleaned);
|
|
429
|
+
const legacyScope = extractExportedConfigSource(cleaned);
|
|
430
|
+
const mapSource = (scope, key) => {
|
|
431
|
+
if (!scope) return void 0;
|
|
432
|
+
const at = scope.search(new RegExp(`${key}\\s*:\\s*\\{`));
|
|
433
|
+
if (at === -1) return void 0;
|
|
434
|
+
return matchBlock(scope, scope.indexOf("{", at));
|
|
435
|
+
};
|
|
436
|
+
const mapBody = mapSource(platformScope, "envMap") ?? mapSource(legacyScope, "platformEnvMap");
|
|
437
|
+
if (mapBody) {
|
|
438
|
+
const entries = mapBody.matchAll(/(?:"([^"]+)"|'([^']+)'|([\w-]+))\s*:\s*["']([^"']+)["']/g);
|
|
199
439
|
for (const entry of entries) {
|
|
200
|
-
const key = entry[1];
|
|
201
|
-
const value = entry[
|
|
440
|
+
const key = entry[1] ?? entry[2] ?? entry[3];
|
|
441
|
+
const value = entry[4];
|
|
202
442
|
if (key && value) {
|
|
203
443
|
platformEnvMap[key] = value;
|
|
204
444
|
}
|
|
205
445
|
}
|
|
206
446
|
}
|
|
207
|
-
const defaultMatch =
|
|
447
|
+
const defaultMatch = platformScope?.match(/defaultEnv:\s*["']([^"']+)["']/) ?? legacyScope?.match(/defaultPlatformEnv:\s*["']([^"']+)["']/);
|
|
208
448
|
if (defaultMatch?.[1]) {
|
|
209
449
|
defaultPlatformEnv = defaultMatch[1];
|
|
210
450
|
}
|
|
211
451
|
}
|
|
212
|
-
const tenantPrefix = `${
|
|
213
|
-
const parsedEnv =
|
|
214
|
-
|
|
452
|
+
const tenantPrefix = [detectedTenant, tenant].filter((t) => Boolean(t)).map((t) => `${t}-`).sort((a, b) => b.length - a.length).find((p) => stage.startsWith(p));
|
|
453
|
+
const parsedEnv = tenantPrefix ? stage.slice(tenantPrefix.length) : stage;
|
|
454
|
+
const mapApplies = !tenantOverridden;
|
|
455
|
+
if (tenantOverridden && (Object.keys(platformEnvMap).length > 0 || defaultPlatformEnv)) {
|
|
456
|
+
logWarn(
|
|
457
|
+
`${overrideSource}=${tenant} also leaves the ${detectedTenant} config's envMap unapplied \u2014 it maps ${detectedTenant}'s stages, not ${tenant}'s \u2014 so the platform env is the stage itself ("${parsedEnv}"). Pass -e/--env where the command takes one, or run from the ${tenant} app's own directory.`
|
|
458
|
+
);
|
|
459
|
+
}
|
|
460
|
+
const mapped = mapApplies ? platformEnvMap[stage] ?? platformEnvMap[parsedEnv] : void 0;
|
|
461
|
+
if (mapped) {
|
|
462
|
+
platformEnv = mapped;
|
|
463
|
+
} else if (mapApplies && defaultPlatformEnv) {
|
|
464
|
+
if (namedStackMissing) {
|
|
465
|
+
const known = [
|
|
466
|
+
...stackFilesHere.map((f) => f.slice("Pulumi.".length, -".yaml".length)),
|
|
467
|
+
...Object.keys(platformEnvMap)
|
|
468
|
+
].filter((v, i, a) => a.indexOf(v) === i);
|
|
469
|
+
throw new MeshCliError(
|
|
470
|
+
`No such stack "${options.stack}": ${cwd} has no Pulumi.${options.stack}.yaml, and the app's config.ts does not map it to a platform env \u2014 it would address defaultEnv "${defaultPlatformEnv}", not the stack you asked for.`,
|
|
471
|
+
{
|
|
472
|
+
remediation: {
|
|
473
|
+
command: `mesh <command> --stack <${known.join("|") || "stack"}>`
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
);
|
|
477
|
+
}
|
|
478
|
+
platformEnv = defaultPlatformEnv;
|
|
479
|
+
if (Object.keys(platformEnvMap).length > 0) {
|
|
480
|
+
logWarn(
|
|
481
|
+
`Stage "${stage}" is not in the config's envMap (${Object.keys(platformEnvMap).join(", ")}); using defaultEnv "${defaultPlatformEnv}".`
|
|
482
|
+
);
|
|
483
|
+
}
|
|
484
|
+
} else {
|
|
485
|
+
platformEnv = parsedEnv;
|
|
486
|
+
}
|
|
215
487
|
logInfo(`Stage: ${stage}, Platform: ${platformEnv}, Tenant: ${tenant}`);
|
|
216
|
-
return { tenant, stage, platformEnv };
|
|
488
|
+
return { tenant, stage, platformEnv, detectedTenant };
|
|
217
489
|
}
|
|
218
490
|
var init_context = __esm({
|
|
219
491
|
"libs/mesh-cli/src/utils/context.ts"() {
|
|
220
492
|
"use strict";
|
|
493
|
+
init_errors();
|
|
221
494
|
init_log();
|
|
495
|
+
init_mesh_json();
|
|
222
496
|
}
|
|
223
497
|
});
|
|
224
498
|
|
|
@@ -367,19 +641,19 @@ var init_credentials = __esm({
|
|
|
367
641
|
|
|
368
642
|
// libs/mesh-cli/src/utils/pulumi.ts
|
|
369
643
|
import { execFileSync } from "child_process";
|
|
370
|
-
import * as
|
|
371
|
-
import * as
|
|
644
|
+
import * as path3 from "path";
|
|
645
|
+
import * as fs3 from "fs";
|
|
372
646
|
function findAppRoot(startDir) {
|
|
373
647
|
let dir = startDir;
|
|
374
648
|
while (true) {
|
|
375
|
-
if (
|
|
376
|
-
const parent =
|
|
649
|
+
if (fs3.existsSync(path3.join(dir, "Pulumi.yaml"))) return dir;
|
|
650
|
+
const parent = path3.dirname(dir);
|
|
377
651
|
if (parent === dir) return null;
|
|
378
652
|
dir = parent;
|
|
379
653
|
}
|
|
380
654
|
}
|
|
381
655
|
function findStackConfigs(appRoot) {
|
|
382
|
-
return
|
|
656
|
+
return fs3.readdirSync(appRoot).filter((f) => /^Pulumi\..+\.yaml$/.test(f) && f !== "Pulumi.yaml").map((f) => f.replace(/^Pulumi\./, "").replace(/\.yaml$/, ""));
|
|
383
657
|
}
|
|
384
658
|
function getCurrentStack(appRoot) {
|
|
385
659
|
try {
|
|
@@ -394,9 +668,9 @@ function getCurrentStack(appRoot) {
|
|
|
394
668
|
}
|
|
395
669
|
}
|
|
396
670
|
function readStackConfig(appRoot, stack, key) {
|
|
397
|
-
const configFile =
|
|
398
|
-
if (!
|
|
399
|
-
const content =
|
|
671
|
+
const configFile = path3.join(appRoot, `Pulumi.${stack}.yaml`);
|
|
672
|
+
if (!fs3.existsSync(configFile)) return null;
|
|
673
|
+
const content = fs3.readFileSync(configFile, "utf-8");
|
|
400
674
|
const pattern = new RegExp(`^\\s+${key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}:\\s*(.+)$`, "m");
|
|
401
675
|
const match = content.match(pattern);
|
|
402
676
|
if (!match) return null;
|
|
@@ -438,7 +712,7 @@ var init_pulumi = __esm({
|
|
|
438
712
|
|
|
439
713
|
// libs/mesh-cli/src/utils/vpn.ts
|
|
440
714
|
import { execFile, execFileSync as execFileSync2 } from "node:child_process";
|
|
441
|
-
import { existsSync as
|
|
715
|
+
import { existsSync as existsSync4 } from "node:fs";
|
|
442
716
|
async function isVpnConnected() {
|
|
443
717
|
try {
|
|
444
718
|
const stdout = await execTailscaleCmd(["status", "--json"]);
|
|
@@ -458,7 +732,7 @@ function headscaleDnsConfig(tenant = "mesh", env = "dev", overrides) {
|
|
|
458
732
|
};
|
|
459
733
|
}
|
|
460
734
|
function execTailscaleCmd(args) {
|
|
461
|
-
const socketPath2 = TAILSCALE_SOCKET_PATHS.find(
|
|
735
|
+
const socketPath2 = TAILSCALE_SOCKET_PATHS.find(existsSync4);
|
|
462
736
|
const attempts = [];
|
|
463
737
|
for (const binary of TAILSCALE_PATHS) {
|
|
464
738
|
if (socketPath2) {
|
|
@@ -466,7 +740,7 @@ function execTailscaleCmd(args) {
|
|
|
466
740
|
}
|
|
467
741
|
attempts.push({ binary, args });
|
|
468
742
|
}
|
|
469
|
-
return new Promise((
|
|
743
|
+
return new Promise((resolve20, reject) => {
|
|
470
744
|
let index = 0;
|
|
471
745
|
function tryNext() {
|
|
472
746
|
if (index >= attempts.length) {
|
|
@@ -478,7 +752,7 @@ function execTailscaleCmd(args) {
|
|
|
478
752
|
if (error) {
|
|
479
753
|
tryNext();
|
|
480
754
|
} else {
|
|
481
|
-
|
|
755
|
+
resolve20(stdout);
|
|
482
756
|
}
|
|
483
757
|
});
|
|
484
758
|
}
|
|
@@ -652,7 +926,7 @@ async function ensureTemporalNamespace(namespace, description) {
|
|
|
652
926
|
});
|
|
653
927
|
break;
|
|
654
928
|
} catch {
|
|
655
|
-
await new Promise((
|
|
929
|
+
await new Promise((resolve20) => setTimeout(resolve20, 1e3));
|
|
656
930
|
}
|
|
657
931
|
}
|
|
658
932
|
logSuccess(`Temporal namespace '${namespace}' is active`);
|
|
@@ -855,16 +1129,16 @@ async function upsertLocalSecret(secretId, value, client) {
|
|
|
855
1129
|
}
|
|
856
1130
|
function probeTcp(port, opts = {}) {
|
|
857
1131
|
const { host = "127.0.0.1", timeoutMs = 2e3 } = opts;
|
|
858
|
-
return new Promise((
|
|
1132
|
+
return new Promise((resolve20) => {
|
|
859
1133
|
const socket = net.connect({ host, port, timeout: timeoutMs });
|
|
860
1134
|
socket.once("connect", () => {
|
|
861
1135
|
socket.destroy();
|
|
862
|
-
|
|
1136
|
+
resolve20(true);
|
|
863
1137
|
});
|
|
864
|
-
socket.once("error", () =>
|
|
1138
|
+
socket.once("error", () => resolve20(false));
|
|
865
1139
|
socket.once("timeout", () => {
|
|
866
1140
|
socket.destroy();
|
|
867
|
-
|
|
1141
|
+
resolve20(false);
|
|
868
1142
|
});
|
|
869
1143
|
});
|
|
870
1144
|
}
|
|
@@ -957,15 +1231,15 @@ var init_helpers = __esm({
|
|
|
957
1231
|
});
|
|
958
1232
|
|
|
959
1233
|
// libs/mesh-cli/src/utils/cache-home.ts
|
|
960
|
-
import
|
|
1234
|
+
import fs4 from "node:fs";
|
|
961
1235
|
import os from "node:os";
|
|
962
|
-
import
|
|
1236
|
+
import path4 from "node:path";
|
|
963
1237
|
function meshCacheHome() {
|
|
964
|
-
return process.env.MESH_CACHE_HOME ||
|
|
1238
|
+
return process.env.MESH_CACHE_HOME || path4.join(os.homedir(), ".cache", "mesh");
|
|
965
1239
|
}
|
|
966
1240
|
function meshCacheDir(...segments) {
|
|
967
|
-
const dir =
|
|
968
|
-
|
|
1241
|
+
const dir = path4.join(meshCacheHome(), ...segments);
|
|
1242
|
+
fs4.mkdirSync(dir, { recursive: true });
|
|
969
1243
|
return dir;
|
|
970
1244
|
}
|
|
971
1245
|
var init_cache_home = __esm({
|
|
@@ -1005,8 +1279,8 @@ __export(stack_exports, {
|
|
|
1005
1279
|
writeAppServiceProbes: () => writeAppServiceProbes
|
|
1006
1280
|
});
|
|
1007
1281
|
import { execFileSync as execFileSync4, spawn, spawnSync } from "child_process";
|
|
1008
|
-
import * as
|
|
1009
|
-
import * as
|
|
1282
|
+
import * as fs5 from "fs";
|
|
1283
|
+
import * as path5 from "path";
|
|
1010
1284
|
import { fileURLToPath } from "url";
|
|
1011
1285
|
import { parse as parseYaml } from "yaml";
|
|
1012
1286
|
function localLogsDir() {
|
|
@@ -1030,8 +1304,8 @@ function writeAppServiceProbes(args) {
|
|
|
1030
1304
|
target: name
|
|
1031
1305
|
}
|
|
1032
1306
|
}));
|
|
1033
|
-
const file =
|
|
1034
|
-
|
|
1307
|
+
const file = path5.join(localProbesDir(), `${args.tenant}-${args.app}-services.json`);
|
|
1308
|
+
fs5.writeFileSync(file, `${JSON.stringify(targets, null, 2)}
|
|
1035
1309
|
`);
|
|
1036
1310
|
return file;
|
|
1037
1311
|
}
|
|
@@ -1063,17 +1337,17 @@ function hubEndpoints() {
|
|
|
1063
1337
|
];
|
|
1064
1338
|
}
|
|
1065
1339
|
function findPackageRoot(startDir) {
|
|
1066
|
-
let dir = startDir ??
|
|
1340
|
+
let dir = startDir ?? path5.dirname(fileURLToPath(import.meta.url));
|
|
1067
1341
|
for (let i = 0; i < 8; i++) {
|
|
1068
|
-
const pkgPath =
|
|
1069
|
-
if (
|
|
1342
|
+
const pkgPath = path5.join(dir, "package.json");
|
|
1343
|
+
if (fs5.existsSync(pkgPath)) {
|
|
1070
1344
|
try {
|
|
1071
|
-
const pkg = JSON.parse(
|
|
1345
|
+
const pkg = JSON.parse(fs5.readFileSync(pkgPath, "utf-8"));
|
|
1072
1346
|
if (pkg.name === "@mesh-tech/mesh-cli") return dir;
|
|
1073
1347
|
} catch {
|
|
1074
1348
|
}
|
|
1075
1349
|
}
|
|
1076
|
-
const parent =
|
|
1350
|
+
const parent = path5.dirname(dir);
|
|
1077
1351
|
if (parent === dir) break;
|
|
1078
1352
|
dir = parent;
|
|
1079
1353
|
}
|
|
@@ -1082,8 +1356,8 @@ function findPackageRoot(startDir) {
|
|
|
1082
1356
|
});
|
|
1083
1357
|
}
|
|
1084
1358
|
function stackDir() {
|
|
1085
|
-
const dir =
|
|
1086
|
-
if (!
|
|
1359
|
+
const dir = path5.join(findPackageRoot(), "stack");
|
|
1360
|
+
if (!fs5.existsSync(path5.join(dir, "docker-compose.yml"))) {
|
|
1087
1361
|
throw new MeshCliError(`Local stack assets not found at ${dir}.`, {
|
|
1088
1362
|
remediation: { command: "npm install -g @mesh-tech/mesh-cli" }
|
|
1089
1363
|
});
|
|
@@ -1096,13 +1370,13 @@ function parseComposeServiceNames(yamlText) {
|
|
|
1096
1370
|
}
|
|
1097
1371
|
function hubOverlayServices() {
|
|
1098
1372
|
if (!hubOverlayServicesCache) {
|
|
1099
|
-
const file =
|
|
1100
|
-
if (!
|
|
1373
|
+
const file = path5.join(stackDir(), HUB_OVERLAY_FILE);
|
|
1374
|
+
if (!fs5.existsSync(file)) {
|
|
1101
1375
|
throw new MeshCliError(`Local stack assets are incomplete: ${file} is missing.`, {
|
|
1102
1376
|
remediation: { command: "npm install -g @mesh-tech/mesh-cli" }
|
|
1103
1377
|
});
|
|
1104
1378
|
}
|
|
1105
|
-
hubOverlayServicesCache = new Set(parseComposeServiceNames(
|
|
1379
|
+
hubOverlayServicesCache = new Set(parseComposeServiceNames(fs5.readFileSync(file, "utf-8")));
|
|
1106
1380
|
}
|
|
1107
1381
|
return hubOverlayServicesCache;
|
|
1108
1382
|
}
|
|
@@ -1118,9 +1392,9 @@ function stackOwnedElsewhere() {
|
|
|
1118
1392
|
if (!label) return void 0;
|
|
1119
1393
|
const real = (p) => {
|
|
1120
1394
|
try {
|
|
1121
|
-
return
|
|
1395
|
+
return fs5.realpathSync(p);
|
|
1122
1396
|
} catch {
|
|
1123
|
-
return
|
|
1397
|
+
return path5.resolve(p);
|
|
1124
1398
|
}
|
|
1125
1399
|
};
|
|
1126
1400
|
return real(label) === real(stackDir()) ? void 0 : label;
|
|
@@ -1140,8 +1414,8 @@ function ensureDockerAvailable() {
|
|
|
1140
1414
|
}
|
|
1141
1415
|
function compose(args, opts = {}) {
|
|
1142
1416
|
const dir = stackDir();
|
|
1143
|
-
const files = ["-f",
|
|
1144
|
-
if (opts.hub) files.push("-f",
|
|
1417
|
+
const files = ["-f", path5.join(dir, "docker-compose.yml")];
|
|
1418
|
+
if (opts.hub) files.push("-f", path5.join(dir, "docker-compose.hub.yml"));
|
|
1145
1419
|
const fullArgs = ["compose", "-p", COMPOSE_PROJECT, ...files, ...args];
|
|
1146
1420
|
return execFileSync4("docker", fullArgs, {
|
|
1147
1421
|
cwd: dir,
|
|
@@ -1171,12 +1445,12 @@ function summarizeComposeFailure(output) {
|
|
|
1171
1445
|
}
|
|
1172
1446
|
function composeLogPath(op) {
|
|
1173
1447
|
const dir = meshCacheDir("mesh-local");
|
|
1174
|
-
return
|
|
1448
|
+
return path5.join(dir, `compose-${op.replace(/[^a-z0-9-]/gi, "_")}.log`);
|
|
1175
1449
|
}
|
|
1176
1450
|
async function composeStreamed(args, opts = {}) {
|
|
1177
1451
|
const dir = stackDir();
|
|
1178
|
-
const files = ["-f",
|
|
1179
|
-
if (opts.hub) files.push("-f",
|
|
1452
|
+
const files = ["-f", path5.join(dir, "docker-compose.yml")];
|
|
1453
|
+
if (opts.hub) files.push("-f", path5.join(dir, "docker-compose.hub.yml"));
|
|
1180
1454
|
const fullArgs = ["compose", "-p", COMPOSE_PROJECT, ...files, ...args];
|
|
1181
1455
|
const env = {
|
|
1182
1456
|
...process.env,
|
|
@@ -1205,12 +1479,12 @@ async function composeStreamed(args, opts = {}) {
|
|
|
1205
1479
|
return;
|
|
1206
1480
|
}
|
|
1207
1481
|
const logPath2 = composeLogPath(op);
|
|
1208
|
-
const logStream =
|
|
1482
|
+
const logStream = fs5.createWriteStream(logPath2);
|
|
1209
1483
|
const heartbeat = startHeartbeat(`docker compose ${args.join(" ")}`);
|
|
1210
1484
|
let captured = "";
|
|
1211
1485
|
let exitCode;
|
|
1212
1486
|
try {
|
|
1213
|
-
exitCode = await new Promise((
|
|
1487
|
+
exitCode = await new Promise((resolve20, reject) => {
|
|
1214
1488
|
const child = spawn("docker", fullArgs, {
|
|
1215
1489
|
cwd: dir,
|
|
1216
1490
|
env,
|
|
@@ -1226,11 +1500,11 @@ async function composeStreamed(args, opts = {}) {
|
|
|
1226
1500
|
child.stdout.on("data", consume);
|
|
1227
1501
|
child.stderr.on("data", consume);
|
|
1228
1502
|
child.on("error", reject);
|
|
1229
|
-
child.on("close", (code) =>
|
|
1503
|
+
child.on("close", (code) => resolve20(code ?? 1));
|
|
1230
1504
|
});
|
|
1231
1505
|
} finally {
|
|
1232
1506
|
heartbeat.stop();
|
|
1233
|
-
await new Promise((
|
|
1507
|
+
await new Promise((resolve20) => logStream.end(resolve20));
|
|
1234
1508
|
}
|
|
1235
1509
|
if (exitCode !== 0) {
|
|
1236
1510
|
throw new MeshCliError(
|
|
@@ -1400,8 +1674,17 @@ var init_stack = __esm({
|
|
|
1400
1674
|
});
|
|
1401
1675
|
|
|
1402
1676
|
// libs/mesh-cli/src/commands/skills.ts
|
|
1403
|
-
|
|
1404
|
-
|
|
1677
|
+
var skills_exports = {};
|
|
1678
|
+
__export(skills_exports, {
|
|
1679
|
+
INTENT_RANGE: () => INTENT_RANGE,
|
|
1680
|
+
listPackageSkills: () => listPackageSkills,
|
|
1681
|
+
registerSkillsCommands: () => registerSkillsCommands,
|
|
1682
|
+
resolveTargetRoot: () => resolveTargetRoot,
|
|
1683
|
+
seedAppSkill: () => seedAppSkill,
|
|
1684
|
+
syncSkills: () => syncSkills
|
|
1685
|
+
});
|
|
1686
|
+
import * as fs6 from "fs";
|
|
1687
|
+
import * as path6 from "path";
|
|
1405
1688
|
import { execFileSync as execFileSync5 } from "child_process";
|
|
1406
1689
|
function resolveTargetRoot(startDir = process.cwd()) {
|
|
1407
1690
|
try {
|
|
@@ -1415,15 +1698,15 @@ function resolveTargetRoot(startDir = process.cwd()) {
|
|
|
1415
1698
|
}
|
|
1416
1699
|
}
|
|
1417
1700
|
function cliAsset(...segments) {
|
|
1418
|
-
return
|
|
1701
|
+
return path6.join(findPackageRoot(), ...segments);
|
|
1419
1702
|
}
|
|
1420
1703
|
function listBaseSkills() {
|
|
1421
1704
|
const dir = cliAsset("skills");
|
|
1422
|
-
if (!
|
|
1423
|
-
return
|
|
1705
|
+
if (!fs6.existsSync(dir)) return [];
|
|
1706
|
+
return fs6.readdirSync(dir, { withFileTypes: true }).filter((entry) => entry.isDirectory() && fs6.existsSync(path6.join(dir, entry.name, "SKILL.md"))).map((entry) => ({ name: entry.name, source: path6.join(dir, entry.name, "SKILL.md") }));
|
|
1424
1707
|
}
|
|
1425
1708
|
function renderManagedSkill(source) {
|
|
1426
|
-
const raw =
|
|
1709
|
+
const raw = fs6.readFileSync(source, "utf-8");
|
|
1427
1710
|
const frontmatter = raw.match(/^(---\n[\s\S]*?\n---\n)/)?.[1];
|
|
1428
1711
|
if (!frontmatter) return `${MANAGED_MARKER}
|
|
1429
1712
|
|
|
@@ -1438,19 +1721,19 @@ function meshTechScopeDirs(root, maxDepth = 6) {
|
|
|
1438
1721
|
if (depth > maxDepth) return;
|
|
1439
1722
|
let entries;
|
|
1440
1723
|
try {
|
|
1441
|
-
entries =
|
|
1724
|
+
entries = fs6.readdirSync(dir, { withFileTypes: true });
|
|
1442
1725
|
} catch {
|
|
1443
1726
|
return;
|
|
1444
1727
|
}
|
|
1445
1728
|
for (const entry of entries) {
|
|
1446
1729
|
if (!entry.isDirectory()) continue;
|
|
1447
1730
|
if (entry.name === "node_modules") {
|
|
1448
|
-
const scope =
|
|
1449
|
-
if (
|
|
1731
|
+
const scope = path6.join(dir, entry.name, "@mesh-tech");
|
|
1732
|
+
if (fs6.existsSync(scope)) scopes.push(scope);
|
|
1450
1733
|
continue;
|
|
1451
1734
|
}
|
|
1452
1735
|
if (entry.name.startsWith(".")) continue;
|
|
1453
|
-
walk2(
|
|
1736
|
+
walk2(path6.join(dir, entry.name), depth + 1);
|
|
1454
1737
|
}
|
|
1455
1738
|
};
|
|
1456
1739
|
walk2(root, 0);
|
|
@@ -1459,57 +1742,57 @@ function meshTechScopeDirs(root, maxDepth = 6) {
|
|
|
1459
1742
|
function listPackageSkills(root) {
|
|
1460
1743
|
const byName = /* @__PURE__ */ new Map();
|
|
1461
1744
|
for (const scopeDir of meshTechScopeDirs(root)) {
|
|
1462
|
-
for (const pkg of
|
|
1745
|
+
for (const pkg of fs6.readdirSync(scopeDir, { withFileTypes: true })) {
|
|
1463
1746
|
if (!pkg.isDirectory() && !pkg.isSymbolicLink()) continue;
|
|
1464
1747
|
if (pkg.name === BASE_SKILL_PACKAGE) continue;
|
|
1465
|
-
const skillsDir =
|
|
1466
|
-
if (!
|
|
1467
|
-
for (const domain of
|
|
1468
|
-
const source =
|
|
1469
|
-
if (!
|
|
1748
|
+
const skillsDir = path6.join(scopeDir, pkg.name, "skills");
|
|
1749
|
+
if (!fs6.existsSync(skillsDir)) continue;
|
|
1750
|
+
for (const domain of fs6.readdirSync(skillsDir, { withFileTypes: true })) {
|
|
1751
|
+
const source = path6.join(skillsDir, domain.name, "SKILL.md");
|
|
1752
|
+
if (!fs6.existsSync(source)) continue;
|
|
1470
1753
|
const name = `mesh-${pkg.name}-${domain.name}`;
|
|
1471
|
-
if (!byName.has(name)) byName.set(name, { name, dir:
|
|
1754
|
+
if (!byName.has(name)) byName.set(name, { name, dir: path6.join(skillsDir, domain.name), source });
|
|
1472
1755
|
}
|
|
1473
1756
|
}
|
|
1474
1757
|
}
|
|
1475
1758
|
return [...byName.values()].sort((a, b) => a.name.localeCompare(b.name));
|
|
1476
1759
|
}
|
|
1477
1760
|
function copySkillExtras(sourceDir, targetDir) {
|
|
1478
|
-
for (const entry of
|
|
1761
|
+
for (const entry of fs6.readdirSync(sourceDir, { withFileTypes: true })) {
|
|
1479
1762
|
if (entry.name === "SKILL.md") continue;
|
|
1480
|
-
|
|
1763
|
+
fs6.cpSync(path6.join(sourceDir, entry.name), path6.join(targetDir, entry.name), { recursive: true });
|
|
1481
1764
|
}
|
|
1482
1765
|
}
|
|
1483
1766
|
function seedAppSkill(root, appName, appRelPath) {
|
|
1484
|
-
const target =
|
|
1485
|
-
if (
|
|
1486
|
-
const template =
|
|
1767
|
+
const target = path6.join(root, ".claude", "skills", appName, "SKILL.md");
|
|
1768
|
+
if (fs6.existsSync(target)) return null;
|
|
1769
|
+
const template = fs6.readFileSync(cliAsset("assets", "app-skill", "SKILL.md"), "utf-8");
|
|
1487
1770
|
const body = template.replaceAll("__APP_NAME__", appName).replaceAll("__APP_PATH__", appRelPath || ".");
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
return
|
|
1771
|
+
fs6.mkdirSync(path6.dirname(target), { recursive: true });
|
|
1772
|
+
fs6.writeFileSync(target, body);
|
|
1773
|
+
return path6.relative(root, target);
|
|
1491
1774
|
}
|
|
1492
1775
|
function planSync(root) {
|
|
1493
1776
|
const items = [];
|
|
1494
1777
|
for (const skill of listBaseSkills()) {
|
|
1495
|
-
const target =
|
|
1778
|
+
const target = path6.join(root, ".claude", "skills", `mesh-${skill.name}`, "SKILL.md");
|
|
1496
1779
|
const desired = renderManagedSkill(skill.source);
|
|
1497
1780
|
const label = `.claude/skills/mesh-${skill.name}/SKILL.md (base skill)`;
|
|
1498
|
-
if (!
|
|
1781
|
+
if (!fs6.existsSync(target)) {
|
|
1499
1782
|
items.push({
|
|
1500
1783
|
label,
|
|
1501
1784
|
state: "write",
|
|
1502
1785
|
apply: () => {
|
|
1503
|
-
|
|
1504
|
-
|
|
1786
|
+
fs6.mkdirSync(path6.dirname(target), { recursive: true });
|
|
1787
|
+
fs6.writeFileSync(target, desired);
|
|
1505
1788
|
}
|
|
1506
1789
|
});
|
|
1507
1790
|
} else {
|
|
1508
|
-
const current =
|
|
1791
|
+
const current = fs6.readFileSync(target, "utf-8");
|
|
1509
1792
|
if (current === desired) {
|
|
1510
1793
|
items.push({ label, state: "ok" });
|
|
1511
1794
|
} else if (current.includes(MANAGED_MARKER)) {
|
|
1512
|
-
items.push({ label, state: "write", apply: () =>
|
|
1795
|
+
items.push({ label, state: "write", apply: () => fs6.writeFileSync(target, desired) });
|
|
1513
1796
|
} else {
|
|
1514
1797
|
items.push({ label: `${label} \u2014 exists without the managed marker, leaving as-is`, state: "skip" });
|
|
1515
1798
|
}
|
|
@@ -1517,22 +1800,22 @@ function planSync(root) {
|
|
|
1517
1800
|
}
|
|
1518
1801
|
const packageSkills = listPackageSkills(root);
|
|
1519
1802
|
for (const skill of packageSkills) {
|
|
1520
|
-
const targetDir =
|
|
1521
|
-
const target =
|
|
1803
|
+
const targetDir = path6.join(root, ".claude", "skills", skill.name);
|
|
1804
|
+
const target = path6.join(targetDir, "SKILL.md");
|
|
1522
1805
|
const desired = renderManagedSkill(skill.source);
|
|
1523
1806
|
const label = `.claude/skills/${skill.name}/SKILL.md (platform skill)`;
|
|
1524
|
-
if (!
|
|
1807
|
+
if (!fs6.existsSync(target)) {
|
|
1525
1808
|
items.push({
|
|
1526
1809
|
label,
|
|
1527
1810
|
state: "write",
|
|
1528
1811
|
apply: () => {
|
|
1529
|
-
|
|
1530
|
-
|
|
1812
|
+
fs6.mkdirSync(targetDir, { recursive: true });
|
|
1813
|
+
fs6.writeFileSync(target, desired);
|
|
1531
1814
|
copySkillExtras(skill.dir, targetDir);
|
|
1532
1815
|
}
|
|
1533
1816
|
});
|
|
1534
1817
|
} else {
|
|
1535
|
-
const current =
|
|
1818
|
+
const current = fs6.readFileSync(target, "utf-8");
|
|
1536
1819
|
if (current === desired) {
|
|
1537
1820
|
items.push({ label, state: "ok" });
|
|
1538
1821
|
} else if (current.includes(MANAGED_MARKER)) {
|
|
@@ -1540,7 +1823,7 @@ function planSync(root) {
|
|
|
1540
1823
|
label,
|
|
1541
1824
|
state: "write",
|
|
1542
1825
|
apply: () => {
|
|
1543
|
-
|
|
1826
|
+
fs6.writeFileSync(target, desired);
|
|
1544
1827
|
copySkillExtras(skill.dir, targetDir);
|
|
1545
1828
|
}
|
|
1546
1829
|
});
|
|
@@ -1553,24 +1836,24 @@ function planSync(root) {
|
|
|
1553
1836
|
logInfo("No @mesh-tech packages installed yet \u2014 run `pnpm install`, then `mesh skills sync` again for the platform skills.");
|
|
1554
1837
|
}
|
|
1555
1838
|
const hookSource = cliAsset("assets", "intent", "intent-claude-gate.mjs");
|
|
1556
|
-
const hookTarget =
|
|
1557
|
-
const hookDesired =
|
|
1558
|
-
const hookCurrent =
|
|
1839
|
+
const hookTarget = path6.join(root, HOOK_RELATIVE);
|
|
1840
|
+
const hookDesired = fs6.readFileSync(hookSource, "utf-8");
|
|
1841
|
+
const hookCurrent = fs6.existsSync(hookTarget) ? fs6.readFileSync(hookTarget, "utf-8") : null;
|
|
1559
1842
|
items.push(
|
|
1560
1843
|
hookCurrent === hookDesired ? { label: `${HOOK_RELATIVE} (Intent gate)`, state: "ok" } : {
|
|
1561
1844
|
label: `${HOOK_RELATIVE} (Intent gate)`,
|
|
1562
1845
|
state: "write",
|
|
1563
1846
|
apply: () => {
|
|
1564
|
-
|
|
1565
|
-
|
|
1847
|
+
fs6.mkdirSync(path6.dirname(hookTarget), { recursive: true });
|
|
1848
|
+
fs6.writeFileSync(hookTarget, hookDesired, { mode: 493 });
|
|
1566
1849
|
}
|
|
1567
1850
|
}
|
|
1568
1851
|
);
|
|
1569
|
-
const rootPkgPath =
|
|
1570
|
-
if (
|
|
1852
|
+
const rootPkgPath = path6.join(root, "package.json");
|
|
1853
|
+
if (fs6.existsSync(rootPkgPath)) {
|
|
1571
1854
|
let rootPkg = null;
|
|
1572
1855
|
try {
|
|
1573
|
-
rootPkg = JSON.parse(
|
|
1856
|
+
rootPkg = JSON.parse(fs6.readFileSync(rootPkgPath, "utf-8"));
|
|
1574
1857
|
} catch {
|
|
1575
1858
|
rootPkg = null;
|
|
1576
1859
|
}
|
|
@@ -1593,15 +1876,15 @@ function planSync(root) {
|
|
|
1593
1876
|
rootPkg.devDependencies = Object.fromEntries(
|
|
1594
1877
|
Object.entries(rootPkg.devDependencies).sort(([a], [b]) => a.localeCompare(b))
|
|
1595
1878
|
);
|
|
1596
|
-
|
|
1879
|
+
fs6.writeFileSync(rootPkgPath, JSON.stringify(rootPkg, null, 2) + "\n");
|
|
1597
1880
|
}
|
|
1598
1881
|
});
|
|
1599
1882
|
}
|
|
1600
1883
|
}
|
|
1601
|
-
const settingsPath =
|
|
1884
|
+
const settingsPath = path6.join(root, ".claude", "settings.json");
|
|
1602
1885
|
let settings = {};
|
|
1603
1886
|
try {
|
|
1604
|
-
settings = JSON.parse(
|
|
1887
|
+
settings = JSON.parse(fs6.readFileSync(settingsPath, "utf-8"));
|
|
1605
1888
|
} catch {
|
|
1606
1889
|
settings = {};
|
|
1607
1890
|
}
|
|
@@ -1628,14 +1911,14 @@ function planSync(root) {
|
|
|
1628
1911
|
}
|
|
1629
1912
|
]
|
|
1630
1913
|
});
|
|
1631
|
-
|
|
1632
|
-
|
|
1914
|
+
fs6.mkdirSync(path6.dirname(settingsPath), { recursive: true });
|
|
1915
|
+
fs6.writeFileSync(settingsPath, JSON.stringify(settings, null, 1) + "\n");
|
|
1633
1916
|
}
|
|
1634
1917
|
}
|
|
1635
1918
|
);
|
|
1636
|
-
const agentsPath =
|
|
1637
|
-
const fence2 =
|
|
1638
|
-
const agentsCurrent =
|
|
1919
|
+
const agentsPath = path6.join(root, "AGENTS.md");
|
|
1920
|
+
const fence2 = fs6.readFileSync(cliAsset("assets", "intent", "agents-fence.md"), "utf-8");
|
|
1921
|
+
const agentsCurrent = fs6.existsSync(agentsPath) ? fs6.readFileSync(agentsPath, "utf-8") : null;
|
|
1639
1922
|
items.push(
|
|
1640
1923
|
agentsCurrent?.includes(FENCE_START) ? { label: "AGENTS.md (intent-skills fence)", state: "ok" } : {
|
|
1641
1924
|
label: "AGENTS.md (intent-skills fence)",
|
|
@@ -1645,7 +1928,7 @@ function planSync(root) {
|
|
|
1645
1928
|
${agentsCurrent}` : `${fence2}
|
|
1646
1929
|
# Agent Instructions
|
|
1647
1930
|
`;
|
|
1648
|
-
|
|
1931
|
+
fs6.writeFileSync(agentsPath, next);
|
|
1649
1932
|
}
|
|
1650
1933
|
}
|
|
1651
1934
|
);
|
|
@@ -1679,7 +1962,7 @@ function registerSkillsCommands(program2) {
|
|
|
1679
1962
|
skills.command("sync").description(
|
|
1680
1963
|
"Install the base building-with-Mesh skills into .claude/skills/, fetch the platform skills shipped by the repo's installed @mesh-tech/* packages, and wire TanStack-Intent discovery (intent devDependency + hook + settings + AGENTS.md fence)"
|
|
1681
1964
|
).option("--check", "verify only (CI/doctor): exit 1 when anything is missing or stale", false).option("--root <path>", "target repo root (default: enclosing git root)").action((opts) => {
|
|
1682
|
-
const root =
|
|
1965
|
+
const root = path6.resolve(opts.root ?? resolveTargetRoot());
|
|
1683
1966
|
const ok = syncSkills(root, { check: opts.check });
|
|
1684
1967
|
if (opts.check && !ok) {
|
|
1685
1968
|
throw new MeshCliError("Agent skills are missing or stale.", {
|
|
@@ -1697,7 +1980,7 @@ var init_skills = __esm({
|
|
|
1697
1980
|
init_stack();
|
|
1698
1981
|
MANAGED_MARKER = "<!-- managed-by: mesh skills sync \u2014 edits are overwritten; copy content elsewhere to customize -->";
|
|
1699
1982
|
FENCE_START = "<!-- intent-skills:start -->";
|
|
1700
|
-
HOOK_RELATIVE =
|
|
1983
|
+
HOOK_RELATIVE = path6.join(".intent", "hooks", "intent-claude-gate.mjs");
|
|
1701
1984
|
INTENT_RANGE = "^0.3.6";
|
|
1702
1985
|
BASE_SKILL_PACKAGE = "mesh-cli";
|
|
1703
1986
|
}
|
|
@@ -1769,20 +2052,20 @@ var init_pid = __esm({
|
|
|
1769
2052
|
|
|
1770
2053
|
// libs/mesh-cli/src/utils/tailscale-targets.ts
|
|
1771
2054
|
import { execFileSync as execFileSync6 } from "node:child_process";
|
|
1772
|
-
import * as
|
|
1773
|
-
import * as
|
|
2055
|
+
import * as fs7 from "node:fs";
|
|
2056
|
+
import * as path7 from "node:path";
|
|
1774
2057
|
function tenantPortIndex(tenant, registryPath) {
|
|
1775
|
-
const p = registryPath ??
|
|
2058
|
+
const p = registryPath ?? path7.join(CONFIG_DIR, "tailscale", "port-registry.json");
|
|
1776
2059
|
let reg;
|
|
1777
2060
|
try {
|
|
1778
|
-
reg = JSON.parse(
|
|
2061
|
+
reg = JSON.parse(fs7.readFileSync(p, "utf8"));
|
|
1779
2062
|
} catch {
|
|
1780
2063
|
reg = { mesh: 0 };
|
|
1781
2064
|
}
|
|
1782
2065
|
if (tenant in reg) return reg[tenant];
|
|
1783
2066
|
const idx = Math.max(-1, ...Object.values(reg)) + 1;
|
|
1784
2067
|
reg[tenant] = idx;
|
|
1785
|
-
|
|
2068
|
+
fs7.mkdirSync(path7.dirname(p), { recursive: true });
|
|
1786
2069
|
atomicWriteFileSync(p, JSON.stringify(reg, null, 2), 384);
|
|
1787
2070
|
return idx;
|
|
1788
2071
|
}
|
|
@@ -1909,29 +2192,29 @@ var init_socks_forward = __esm({
|
|
|
1909
2192
|
|
|
1910
2193
|
// libs/mesh-cli/src/utils/tunnel-ownership.ts
|
|
1911
2194
|
import * as crypto from "node:crypto";
|
|
1912
|
-
import * as
|
|
2195
|
+
import * as fs8 from "node:fs";
|
|
1913
2196
|
import * as net3 from "node:net";
|
|
1914
|
-
import * as
|
|
2197
|
+
import * as path8 from "node:path";
|
|
1915
2198
|
import { execFileSync as execFileSync7 } from "node:child_process";
|
|
1916
2199
|
function mintRunnerToken() {
|
|
1917
2200
|
return crypto.randomBytes(16).toString("hex");
|
|
1918
2201
|
}
|
|
1919
2202
|
function readRunnerManifest(file) {
|
|
1920
|
-
if (!
|
|
2203
|
+
if (!fs8.existsSync(file)) return null;
|
|
1921
2204
|
try {
|
|
1922
|
-
return JSON.parse(
|
|
2205
|
+
return JSON.parse(fs8.readFileSync(file, "utf8"));
|
|
1923
2206
|
} catch {
|
|
1924
2207
|
return null;
|
|
1925
2208
|
}
|
|
1926
2209
|
}
|
|
1927
2210
|
function writeRunnerManifest(file, manifest) {
|
|
1928
|
-
|
|
1929
|
-
|
|
2211
|
+
fs8.mkdirSync(path8.dirname(file), { recursive: true });
|
|
2212
|
+
fs8.writeFileSync(file, JSON.stringify(manifest, null, 2), { mode: 384 });
|
|
1930
2213
|
}
|
|
1931
2214
|
function clearRunnerManifest(file, token) {
|
|
1932
2215
|
if (token !== void 0 && readRunnerManifest(file)?.token !== token) return;
|
|
1933
2216
|
try {
|
|
1934
|
-
|
|
2217
|
+
fs8.unlinkSync(file);
|
|
1935
2218
|
} catch {
|
|
1936
2219
|
}
|
|
1937
2220
|
}
|
|
@@ -1973,7 +2256,7 @@ function discoverRunnerPids(tenant, exclude) {
|
|
|
1973
2256
|
}
|
|
1974
2257
|
}
|
|
1975
2258
|
function startControlServer(payload) {
|
|
1976
|
-
return new Promise((
|
|
2259
|
+
return new Promise((resolve20, reject) => {
|
|
1977
2260
|
const server = net3.createServer((socket) => {
|
|
1978
2261
|
socket.on("error", () => {
|
|
1979
2262
|
});
|
|
@@ -1986,12 +2269,12 @@ function startControlServer(payload) {
|
|
|
1986
2269
|
});
|
|
1987
2270
|
server.on("error", reject);
|
|
1988
2271
|
server.listen(0, "127.0.0.1", () => {
|
|
1989
|
-
|
|
2272
|
+
resolve20({ server, port: server.address().port });
|
|
1990
2273
|
});
|
|
1991
2274
|
});
|
|
1992
2275
|
}
|
|
1993
2276
|
function queryControl(port, timeoutMs = 700) {
|
|
1994
|
-
return new Promise((
|
|
2277
|
+
return new Promise((resolve20) => {
|
|
1995
2278
|
const s = new net3.Socket();
|
|
1996
2279
|
let buf = "";
|
|
1997
2280
|
let done = false;
|
|
@@ -1999,7 +2282,7 @@ function queryControl(port, timeoutMs = 700) {
|
|
|
1999
2282
|
if (done) return;
|
|
2000
2283
|
done = true;
|
|
2001
2284
|
s.destroy();
|
|
2002
|
-
|
|
2285
|
+
resolve20(v);
|
|
2003
2286
|
};
|
|
2004
2287
|
s.setTimeout(timeoutMs);
|
|
2005
2288
|
s.once("timeout", () => fin(null));
|
|
@@ -2100,64 +2383,64 @@ var init_tunnel_ownership = __esm({
|
|
|
2100
2383
|
});
|
|
2101
2384
|
|
|
2102
2385
|
// libs/mesh-cli/src/utils/tailscale.ts
|
|
2103
|
-
import * as
|
|
2386
|
+
import * as fs9 from "node:fs";
|
|
2104
2387
|
import * as net4 from "node:net";
|
|
2105
2388
|
import * as os2 from "node:os";
|
|
2106
|
-
import * as
|
|
2389
|
+
import * as path9 from "node:path";
|
|
2107
2390
|
import { spawn as spawn2, execFileSync as execFileSync8 } from "node:child_process";
|
|
2108
2391
|
function tenantStateDir(tenant) {
|
|
2109
|
-
return
|
|
2392
|
+
return path9.join(CONFIG_DIR, "tailscale", tenant);
|
|
2110
2393
|
}
|
|
2111
2394
|
function socketPath(tenant) {
|
|
2112
|
-
return
|
|
2395
|
+
return path9.join(tenantStateDir(tenant), "tailscaled.sock");
|
|
2113
2396
|
}
|
|
2114
2397
|
function logPath(tenant) {
|
|
2115
|
-
return
|
|
2398
|
+
return path9.join(tenantStateDir(tenant), "tailscaled.log");
|
|
2116
2399
|
}
|
|
2117
2400
|
function statePath(tenant) {
|
|
2118
|
-
return
|
|
2401
|
+
return path9.join(tenantStateDir(tenant), "tunnel-state.json");
|
|
2119
2402
|
}
|
|
2120
2403
|
function daemonMetaPath(tenant) {
|
|
2121
|
-
return
|
|
2404
|
+
return path9.join(tenantStateDir(tenant), "daemon.json");
|
|
2122
2405
|
}
|
|
2123
2406
|
function runnerManifestPath(tenant) {
|
|
2124
|
-
return
|
|
2407
|
+
return path9.join(tenantStateDir(tenant), "runner.json");
|
|
2125
2408
|
}
|
|
2126
2409
|
function readTunnelState(tenant) {
|
|
2127
2410
|
const p = statePath(tenant);
|
|
2128
|
-
if (!
|
|
2411
|
+
if (!fs9.existsSync(p)) return null;
|
|
2129
2412
|
try {
|
|
2130
|
-
return JSON.parse(
|
|
2413
|
+
return JSON.parse(fs9.readFileSync(p, "utf8"));
|
|
2131
2414
|
} catch {
|
|
2132
2415
|
return null;
|
|
2133
2416
|
}
|
|
2134
2417
|
}
|
|
2135
2418
|
function writeTunnelState(tenant, state) {
|
|
2136
|
-
|
|
2419
|
+
fs9.mkdirSync(tenantStateDir(tenant), { recursive: true });
|
|
2137
2420
|
atomicWriteFileSync(statePath(tenant), JSON.stringify(state, null, 2), 384);
|
|
2138
2421
|
}
|
|
2139
2422
|
function clearTunnelState(tenant) {
|
|
2140
2423
|
try {
|
|
2141
|
-
|
|
2424
|
+
fs9.unlinkSync(statePath(tenant));
|
|
2142
2425
|
} catch {
|
|
2143
2426
|
}
|
|
2144
2427
|
}
|
|
2145
2428
|
function readDaemonMeta(tenant) {
|
|
2146
2429
|
const p = daemonMetaPath(tenant);
|
|
2147
|
-
if (!
|
|
2430
|
+
if (!fs9.existsSync(p)) return null;
|
|
2148
2431
|
try {
|
|
2149
|
-
return JSON.parse(
|
|
2432
|
+
return JSON.parse(fs9.readFileSync(p, "utf8"));
|
|
2150
2433
|
} catch {
|
|
2151
2434
|
return null;
|
|
2152
2435
|
}
|
|
2153
2436
|
}
|
|
2154
2437
|
function writeDaemonMeta(tenant, meta) {
|
|
2155
|
-
|
|
2438
|
+
fs9.mkdirSync(tenantStateDir(tenant), { recursive: true });
|
|
2156
2439
|
atomicWriteFileSync(daemonMetaPath(tenant), JSON.stringify(meta, null, 2), 384);
|
|
2157
2440
|
}
|
|
2158
2441
|
function clearDaemonMeta(tenant) {
|
|
2159
2442
|
try {
|
|
2160
|
-
|
|
2443
|
+
fs9.unlinkSync(daemonMetaPath(tenant));
|
|
2161
2444
|
} catch {
|
|
2162
2445
|
}
|
|
2163
2446
|
}
|
|
@@ -2177,7 +2460,7 @@ function tailscaleBinPath() {
|
|
|
2177
2460
|
}
|
|
2178
2461
|
function resolveBrewBin(name) {
|
|
2179
2462
|
const brew = `/opt/homebrew/bin/${name}`;
|
|
2180
|
-
if (
|
|
2463
|
+
if (fs9.existsSync(brew)) return brew;
|
|
2181
2464
|
try {
|
|
2182
2465
|
const p = execFileSync8("which", [name], { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
|
|
2183
2466
|
if (p) return p;
|
|
@@ -2186,7 +2469,7 @@ function resolveBrewBin(name) {
|
|
|
2186
2469
|
throw new Error(`${name} not found \u2014 install the standalone binary: brew install tailscale`);
|
|
2187
2470
|
}
|
|
2188
2471
|
function daemonState(tenant) {
|
|
2189
|
-
if (!
|
|
2472
|
+
if (!fs9.existsSync(socketPath(tenant))) return { backendState: "Down" };
|
|
2190
2473
|
try {
|
|
2191
2474
|
const out = execFileSync8(
|
|
2192
2475
|
tailscaleBinPath(),
|
|
@@ -2224,14 +2507,14 @@ function findRunningDaemon(tenant) {
|
|
|
2224
2507
|
return null;
|
|
2225
2508
|
}
|
|
2226
2509
|
function portAccepts(port, timeoutMs = 400) {
|
|
2227
|
-
return new Promise((
|
|
2510
|
+
return new Promise((resolve20) => {
|
|
2228
2511
|
const s = new net4.Socket();
|
|
2229
2512
|
let done = false;
|
|
2230
2513
|
const fin = (v) => {
|
|
2231
2514
|
if (done) return;
|
|
2232
2515
|
done = true;
|
|
2233
2516
|
s.destroy();
|
|
2234
|
-
|
|
2517
|
+
resolve20(v);
|
|
2235
2518
|
};
|
|
2236
2519
|
s.setTimeout(timeoutMs);
|
|
2237
2520
|
s.once("connect", () => fin(true));
|
|
@@ -2260,8 +2543,8 @@ async function ensureDaemon(tenant, opts) {
|
|
|
2260
2543
|
}
|
|
2261
2544
|
}
|
|
2262
2545
|
const dir = tenantStateDir(tenant);
|
|
2263
|
-
|
|
2264
|
-
const log =
|
|
2546
|
+
fs9.mkdirSync(dir, { recursive: true });
|
|
2547
|
+
const log = fs9.openSync(logPath(tenant), "a");
|
|
2265
2548
|
try {
|
|
2266
2549
|
const child = spawn2(
|
|
2267
2550
|
tailscaledBinPath(),
|
|
@@ -2281,7 +2564,7 @@ async function ensureDaemon(tenant, opts) {
|
|
|
2281
2564
|
child.unref();
|
|
2282
2565
|
if (child.pid) writeDaemonMeta(tenant, { socksPort: opts.socksPort, pid: child.pid });
|
|
2283
2566
|
} finally {
|
|
2284
|
-
|
|
2567
|
+
fs9.closeSync(log);
|
|
2285
2568
|
}
|
|
2286
2569
|
for (let i = 0; i < 40; i++) {
|
|
2287
2570
|
await sleep(250);
|
|
@@ -2307,7 +2590,7 @@ function stopDaemon(tenant) {
|
|
|
2307
2590
|
}
|
|
2308
2591
|
for (const p of [socketPath(tenant)]) {
|
|
2309
2592
|
try {
|
|
2310
|
-
|
|
2593
|
+
fs9.unlinkSync(p);
|
|
2311
2594
|
} catch {
|
|
2312
2595
|
}
|
|
2313
2596
|
}
|
|
@@ -2452,7 +2735,7 @@ async function assertTunnelPortsFree(tenant, ports) {
|
|
|
2452
2735
|
async function ensureForwarders(state) {
|
|
2453
2736
|
const prior = readTunnelState(state.tenant);
|
|
2454
2737
|
const expected = { socksPort: state.socksPort, ports: state.tunnels.map((t) => t.localPort) };
|
|
2455
|
-
const runnerAlive = prior?.managed === "launchd" ?
|
|
2738
|
+
const runnerAlive = prior?.managed === "launchd" ? fs9.existsSync(launchAgentPath(state.tenant)) : pidAlive(prior?.supervisorPid);
|
|
2456
2739
|
if (prior && runnerAlive && tunnelTargetsEqual(prior.tunnels, state.tunnels) && // A prior supervisor spawned for a DIFFERENT SOCKS port still binds the same
|
|
2457
2740
|
// local ports but dials a dead upstream — reusing it silently strands every
|
|
2458
2741
|
// tunnel. The port must match, and the runner must PROVE it is
|
|
@@ -2477,7 +2760,7 @@ async function ensureForwarders(state) {
|
|
|
2477
2760
|
if (launchdAvailable()) {
|
|
2478
2761
|
state.managed = "launchd";
|
|
2479
2762
|
writeTunnelState(state.tenant, state);
|
|
2480
|
-
if (
|
|
2763
|
+
if (path9.isAbsolute(meshBin) && installLaunchAgent(state.tenant, meshBin)) {
|
|
2481
2764
|
if ((await awaitOwnership(state.tenant, expected, 8e3)).ok) return 0;
|
|
2482
2765
|
uninstallLaunchAgent(state.tenant);
|
|
2483
2766
|
await assertTunnelPortsFree(state.tenant, expected.ports);
|
|
@@ -2605,7 +2888,7 @@ function forwardersNeedingRebind(targets, listening) {
|
|
|
2605
2888
|
return targets.filter((t) => !listening.has(t.localPort));
|
|
2606
2889
|
}
|
|
2607
2890
|
function launchAgentPath(tenant) {
|
|
2608
|
-
return
|
|
2891
|
+
return path9.join(os2.homedir(), "Library", "LaunchAgents", `${launchAgentLabel(tenant)}.plist`);
|
|
2609
2892
|
}
|
|
2610
2893
|
function guiDomain() {
|
|
2611
2894
|
return `gui/${process.getuid?.() ?? 0}`;
|
|
@@ -2626,22 +2909,22 @@ function installLaunchAgent(tenant, meshBin) {
|
|
|
2626
2909
|
try {
|
|
2627
2910
|
let realMeshBin = meshBin;
|
|
2628
2911
|
try {
|
|
2629
|
-
realMeshBin =
|
|
2912
|
+
realMeshBin = fs9.realpathSync(meshBin);
|
|
2630
2913
|
} catch {
|
|
2631
2914
|
}
|
|
2632
2915
|
let pathEnv = "/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin";
|
|
2633
2916
|
try {
|
|
2634
|
-
pathEnv = `${
|
|
2917
|
+
pathEnv = `${path9.dirname(tailscaledBinPath())}:${pathEnv}`;
|
|
2635
2918
|
} catch {
|
|
2636
2919
|
}
|
|
2637
|
-
|
|
2638
|
-
|
|
2920
|
+
fs9.mkdirSync(path9.dirname(plistPath), { recursive: true });
|
|
2921
|
+
fs9.writeFileSync(
|
|
2639
2922
|
plistPath,
|
|
2640
2923
|
renderLaunchAgentPlist({
|
|
2641
2924
|
tenant,
|
|
2642
2925
|
meshBin: realMeshBin,
|
|
2643
2926
|
nodeBin: process.execPath,
|
|
2644
|
-
logPath:
|
|
2927
|
+
logPath: path9.join(tenantStateDir(tenant), "runner.log"),
|
|
2645
2928
|
pathEnv
|
|
2646
2929
|
})
|
|
2647
2930
|
);
|
|
@@ -2662,7 +2945,7 @@ function uninstallLaunchAgent(tenant) {
|
|
|
2662
2945
|
} catch {
|
|
2663
2946
|
}
|
|
2664
2947
|
try {
|
|
2665
|
-
|
|
2948
|
+
fs9.unlinkSync(plistPath);
|
|
2666
2949
|
} catch {
|
|
2667
2950
|
}
|
|
2668
2951
|
}
|
|
@@ -2769,24 +3052,24 @@ function deriveLoginServer(context) {
|
|
|
2769
3052
|
return `https://${parts.join(".")}`;
|
|
2770
3053
|
}
|
|
2771
3054
|
function findFreePort() {
|
|
2772
|
-
return new Promise((
|
|
3055
|
+
return new Promise((resolve20, reject) => {
|
|
2773
3056
|
const s = net5.createServer();
|
|
2774
3057
|
s.on("error", reject);
|
|
2775
3058
|
s.listen(0, "127.0.0.1", () => {
|
|
2776
3059
|
const port = s.address().port;
|
|
2777
|
-
s.close(() =>
|
|
3060
|
+
s.close(() => resolve20(port));
|
|
2778
3061
|
});
|
|
2779
3062
|
});
|
|
2780
3063
|
}
|
|
2781
3064
|
function canConnect(port) {
|
|
2782
|
-
return new Promise((
|
|
3065
|
+
return new Promise((resolve20) => {
|
|
2783
3066
|
const s = new net5.Socket();
|
|
2784
3067
|
let settled = false;
|
|
2785
3068
|
const done = (ok) => {
|
|
2786
3069
|
if (settled) return;
|
|
2787
3070
|
settled = true;
|
|
2788
3071
|
s.destroy();
|
|
2789
|
-
|
|
3072
|
+
resolve20(ok);
|
|
2790
3073
|
};
|
|
2791
3074
|
s.setTimeout(500);
|
|
2792
3075
|
s.once("connect", () => done(true));
|
|
@@ -3145,13 +3428,13 @@ function parseContextTenantEnv(context) {
|
|
|
3145
3428
|
return { tenant: tenant || "mesh", env: env || "dev" };
|
|
3146
3429
|
}
|
|
3147
3430
|
function findFreePort2() {
|
|
3148
|
-
return new Promise((
|
|
3431
|
+
return new Promise((resolve20, reject) => {
|
|
3149
3432
|
const srv = net6.createServer();
|
|
3150
3433
|
srv.on("error", reject);
|
|
3151
3434
|
srv.listen(0, "127.0.0.1", () => {
|
|
3152
3435
|
const addr = srv.address();
|
|
3153
3436
|
const port = typeof addr === "object" && addr ? addr.port : 0;
|
|
3154
|
-
srv.close(() =>
|
|
3437
|
+
srv.close(() => resolve20(port));
|
|
3155
3438
|
});
|
|
3156
3439
|
});
|
|
3157
3440
|
}
|
|
@@ -3374,18 +3657,19 @@ __export(login_exports, {
|
|
|
3374
3657
|
resolveRoleOrExplain: () => resolveRoleOrExplain,
|
|
3375
3658
|
resolveUserAwsConfigPath: () => resolveUserAwsConfigPath,
|
|
3376
3659
|
runLoginFlow: () => runLoginFlow,
|
|
3660
|
+
shorthandContext: () => shorthandContext,
|
|
3377
3661
|
tokenStillValid: () => tokenStillValid,
|
|
3378
3662
|
writeContextConfig: () => writeContextConfig
|
|
3379
3663
|
});
|
|
3380
3664
|
import * as http from "http";
|
|
3381
3665
|
import * as crypto2 from "crypto";
|
|
3382
|
-
import * as
|
|
3383
|
-
import * as
|
|
3666
|
+
import * as fs10 from "fs";
|
|
3667
|
+
import * as path10 from "path";
|
|
3384
3668
|
import { execFileSync as execFileSync10 } from "child_process";
|
|
3385
3669
|
function readConfig() {
|
|
3386
|
-
if (!
|
|
3670
|
+
if (!fs10.existsSync(CONFIG_FILE)) return {};
|
|
3387
3671
|
try {
|
|
3388
|
-
return JSON.parse(
|
|
3672
|
+
return JSON.parse(fs10.readFileSync(CONFIG_FILE, "utf-8"));
|
|
3389
3673
|
} catch {
|
|
3390
3674
|
return {};
|
|
3391
3675
|
}
|
|
@@ -3393,15 +3677,15 @@ function readConfig() {
|
|
|
3393
3677
|
function writeContextConfig(context, config) {
|
|
3394
3678
|
const existing = readConfig();
|
|
3395
3679
|
existing[context] = config;
|
|
3396
|
-
|
|
3397
|
-
|
|
3680
|
+
fs10.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
3681
|
+
fs10.writeFileSync(CONFIG_FILE, JSON.stringify(existing, null, 2));
|
|
3398
3682
|
}
|
|
3399
3683
|
function clearContextConfig(context) {
|
|
3400
3684
|
const existing = readConfig();
|
|
3401
3685
|
if (!(context in existing)) return;
|
|
3402
3686
|
delete existing[context];
|
|
3403
|
-
|
|
3404
|
-
|
|
3687
|
+
fs10.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
3688
|
+
fs10.writeFileSync(CONFIG_FILE, JSON.stringify(existing, null, 2));
|
|
3405
3689
|
}
|
|
3406
3690
|
function getContextConfig(context) {
|
|
3407
3691
|
const config = readConfig();
|
|
@@ -3509,9 +3793,9 @@ function discoverConfigFromWellKnown(domain, contextKey = domain, opts = {}) {
|
|
|
3509
3793
|
return discoverConfig(domain, contextKey, opts);
|
|
3510
3794
|
}
|
|
3511
3795
|
function readAllCredentials() {
|
|
3512
|
-
if (!
|
|
3796
|
+
if (!fs10.existsSync(CREDENTIALS_FILE)) return {};
|
|
3513
3797
|
try {
|
|
3514
|
-
return JSON.parse(
|
|
3798
|
+
return JSON.parse(fs10.readFileSync(CREDENTIALS_FILE, "utf-8"));
|
|
3515
3799
|
} catch {
|
|
3516
3800
|
return {};
|
|
3517
3801
|
}
|
|
@@ -3519,21 +3803,21 @@ function readAllCredentials() {
|
|
|
3519
3803
|
function readCredentials(context) {
|
|
3520
3804
|
return readAllCredentials()[context] ?? null;
|
|
3521
3805
|
}
|
|
3522
|
-
function atomicWriteFileSync(
|
|
3523
|
-
const tmpPath = `${
|
|
3806
|
+
function atomicWriteFileSync(path47, data, mode) {
|
|
3807
|
+
const tmpPath = `${path47}.${process.pid}.${atomicWriteCounter++}.tmp`;
|
|
3524
3808
|
try {
|
|
3525
|
-
|
|
3526
|
-
|
|
3809
|
+
fs10.writeFileSync(tmpPath, data, { mode });
|
|
3810
|
+
fs10.renameSync(tmpPath, path47);
|
|
3527
3811
|
} catch (err) {
|
|
3528
3812
|
try {
|
|
3529
|
-
|
|
3813
|
+
fs10.unlinkSync(tmpPath);
|
|
3530
3814
|
} catch {
|
|
3531
3815
|
}
|
|
3532
3816
|
throw err;
|
|
3533
3817
|
}
|
|
3534
3818
|
}
|
|
3535
3819
|
function writeCredentials(context, creds) {
|
|
3536
|
-
|
|
3820
|
+
fs10.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
3537
3821
|
const all = readAllCredentials();
|
|
3538
3822
|
all[context] = creds;
|
|
3539
3823
|
atomicWriteFileSync(CREDENTIALS_FILE, JSON.stringify(all, null, 2), 384);
|
|
@@ -3541,8 +3825,8 @@ function writeCredentials(context, creds) {
|
|
|
3541
3825
|
function clearCredentials(context) {
|
|
3542
3826
|
const all = readAllCredentials();
|
|
3543
3827
|
delete all[context];
|
|
3544
|
-
|
|
3545
|
-
|
|
3828
|
+
fs10.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
3829
|
+
fs10.writeFileSync(CREDENTIALS_FILE, JSON.stringify(all, null, 2), {
|
|
3546
3830
|
mode: 384
|
|
3547
3831
|
});
|
|
3548
3832
|
}
|
|
@@ -3596,7 +3880,7 @@ async function refreshTokens(issuer, clientId, refreshToken) {
|
|
|
3596
3880
|
return resp.json();
|
|
3597
3881
|
}
|
|
3598
3882
|
function login(context, config) {
|
|
3599
|
-
return new Promise((
|
|
3883
|
+
return new Promise((resolve20, reject) => {
|
|
3600
3884
|
const codeVerifier = generateCodeVerifier();
|
|
3601
3885
|
const codeChallenge = generateCodeChallenge(codeVerifier);
|
|
3602
3886
|
const state = base64url(crypto2.randomBytes(16));
|
|
@@ -3680,7 +3964,7 @@ function login(context, config) {
|
|
|
3680
3964
|
}
|
|
3681
3965
|
hintVpnIfDisconnected(context);
|
|
3682
3966
|
teardown();
|
|
3683
|
-
|
|
3967
|
+
resolve20();
|
|
3684
3968
|
} catch (err) {
|
|
3685
3969
|
teardown();
|
|
3686
3970
|
reject(err);
|
|
@@ -3821,7 +4105,7 @@ function loginTimeoutMessage(context) {
|
|
|
3821
4105
|
}
|
|
3822
4106
|
function isRemoteEnvironment() {
|
|
3823
4107
|
if (process.env.REMOTE_CONTAINERS || process.env.CODESPACES) return true;
|
|
3824
|
-
if (
|
|
4108
|
+
if (fs10.existsSync("/.dockerenv")) return true;
|
|
3825
4109
|
if (process.env.SSH_CLIENT || process.env.SSH_TTY) return true;
|
|
3826
4110
|
if (!process.stdout.isTTY || !process.stdin.isTTY) return true;
|
|
3827
4111
|
return false;
|
|
@@ -3967,7 +4251,9 @@ async function showStatus(context, config) {
|
|
|
3967
4251
|
}
|
|
3968
4252
|
}
|
|
3969
4253
|
function registerLoginCommand(program2) {
|
|
3970
|
-
program2.command("login").description(
|
|
4254
|
+
program2.command("login").description(
|
|
4255
|
+
"PLATFORM access: sign in to a deployed Mesh platform (`<tenant>.<env>`) \u2014 what `mesh deploy`, `mesh stack`, the Hub and the VPN use. It never touches your ~/.npmrc, and you do not need it to install packages: that is `mesh registry login`. OIDC PKCE, or device code with --device."
|
|
4256
|
+
).argument("<context>", 'Platform context (e.g., "mesh.dev")').option(
|
|
3971
4257
|
"--status",
|
|
3972
4258
|
"Show current authentication status \u2014 the Zitadel session if one is cached, otherwise the AWS identity the credential chain resolves (SSO profile or key pair). Exits non-zero only when neither works."
|
|
3973
4259
|
).option("--device", "Force device code flow (no callback server needed)").option(
|
|
@@ -4021,7 +4307,7 @@ function registerLoginCommand(program2) {
|
|
|
4021
4307
|
}
|
|
4022
4308
|
await hintRegistryIfMissing();
|
|
4023
4309
|
});
|
|
4024
|
-
program2.command("logout").description("Clear cached
|
|
4310
|
+
program2.command("logout").description("Clear a platform context's cached credentials (the package-registry session under `mesh registry` is untouched)").argument("<context>", 'Platform context (e.g., "mesh.dev")').action((context) => {
|
|
4025
4311
|
refuseReservedRegistryContext(context, "logout");
|
|
4026
4312
|
clearCredentials(context);
|
|
4027
4313
|
logSuccess(`Logged out of ${context}. Credentials cleared.`);
|
|
@@ -4046,8 +4332,17 @@ async function hintRegistryIfMissing() {
|
|
|
4046
4332
|
} catch {
|
|
4047
4333
|
}
|
|
4048
4334
|
}
|
|
4335
|
+
function shorthandContext(context) {
|
|
4336
|
+
const parts = context.split(".");
|
|
4337
|
+
if (parts.length !== 2) return null;
|
|
4338
|
+
const [tenant, env] = parts;
|
|
4339
|
+
return tenant && env ? { tenant, env } : null;
|
|
4340
|
+
}
|
|
4049
4341
|
function renderNoConfigHelp(context) {
|
|
4050
4342
|
const pinned = firstPartyDomainFor(context);
|
|
4343
|
+
const shorthand = shorthandContext(context);
|
|
4344
|
+
const createOne = shorthand ? `mesh init --tenant ${shorthand.tenant} --new-platform ${shorthand.env}` : "mesh init --tenant <tenant> --new-platform <env>";
|
|
4345
|
+
const scaffoldOne = `mesh init platform ${shorthand?.tenant ?? "<tenant>"} --domain <a domain you control>`;
|
|
4051
4346
|
return `No configuration found for "${context}".
|
|
4052
4347
|
|
|
4053
4348
|
Tried:
|
|
@@ -4059,6 +4354,8 @@ Tried:
|
|
|
4059
4354
|
(only attempted for a full domain, or a known first-party context)
|
|
4060
4355
|
|
|
4061
4356
|
`) + `To fix:
|
|
4357
|
+
- No platform deployed yet? Create one: ${createOne}
|
|
4358
|
+
(or the scaffold on its own: ${scaffoldOne})
|
|
4062
4359
|
- Use the platform's full domain: mesh login <env>.<tenant>.meshtech.io
|
|
4063
4360
|
- Or, if you have AWS access, ensure SSO is active: aws sso login --profile <profile>
|
|
4064
4361
|
- Or add config manually to ${CONFIG_FILE}:
|
|
@@ -4159,12 +4456,12 @@ export AWS_REGION=${shellSingleQuote(args.region)}
|
|
|
4159
4456
|
`;
|
|
4160
4457
|
}
|
|
4161
4458
|
function resolveUserAwsConfigPath(env = process.env) {
|
|
4162
|
-
return env.AWS_CONFIG_FILE ??
|
|
4459
|
+
return env.AWS_CONFIG_FILE ?? path10.join(env.HOME ?? "~", ".aws", "config");
|
|
4163
4460
|
}
|
|
4164
4461
|
function canWriteDir(dir) {
|
|
4165
4462
|
try {
|
|
4166
|
-
|
|
4167
|
-
|
|
4463
|
+
fs10.mkdirSync(dir, { recursive: true });
|
|
4464
|
+
fs10.accessSync(dir, fs10.constants.W_OK);
|
|
4168
4465
|
return true;
|
|
4169
4466
|
} catch {
|
|
4170
4467
|
return false;
|
|
@@ -4172,9 +4469,9 @@ function canWriteDir(dir) {
|
|
|
4172
4469
|
}
|
|
4173
4470
|
function resolveAwsConfigTarget(env = process.env) {
|
|
4174
4471
|
if (env.AWS_CONFIG_FILE) return { configPath: env.AWS_CONFIG_FILE, redirected: false };
|
|
4175
|
-
const real =
|
|
4176
|
-
if (canWriteDir(
|
|
4177
|
-
return { configPath:
|
|
4472
|
+
const real = path10.join(env.HOME ?? "~", ".aws", "config");
|
|
4473
|
+
if (canWriteDir(path10.dirname(real))) return { configPath: real, redirected: false };
|
|
4474
|
+
return { configPath: path10.join(CONFIG_DIR, "aws-config"), redirected: true };
|
|
4178
4475
|
}
|
|
4179
4476
|
function resolveRoleOrExplain(opts, config) {
|
|
4180
4477
|
const role = opts.role ?? process.env.MESH_AWS_ROLE ?? config.defaultRole;
|
|
@@ -4294,10 +4591,10 @@ async function exportAwsCredentials(context, config, opts) {
|
|
|
4294
4591
|
const sanitized = context.replace(/[^A-Za-z0-9_-]/g, "-");
|
|
4295
4592
|
const profileName = `mesh-${sanitized}`;
|
|
4296
4593
|
const { configPath, redirected } = resolveAwsConfigTarget();
|
|
4297
|
-
|
|
4298
|
-
const existedBefore =
|
|
4299
|
-
const existing = existedBefore ?
|
|
4300
|
-
const mode = existedBefore ?
|
|
4594
|
+
fs10.mkdirSync(path10.dirname(configPath), { recursive: true });
|
|
4595
|
+
const existedBefore = fs10.existsSync(configPath);
|
|
4596
|
+
const existing = existedBefore ? fs10.readFileSync(configPath, "utf-8") : "";
|
|
4597
|
+
const mode = existedBefore ? fs10.statSync(configPath).mode & 511 : 384;
|
|
4301
4598
|
atomicWriteFileSync(
|
|
4302
4599
|
configPath,
|
|
4303
4600
|
upsertManagedAwsConfigSection(
|
|
@@ -4365,12 +4662,12 @@ var init_login = __esm({
|
|
|
4365
4662
|
init_aws_auth();
|
|
4366
4663
|
init_first_party_contexts();
|
|
4367
4664
|
init_vpn2();
|
|
4368
|
-
CONFIG_DIR =
|
|
4369
|
-
process.env.XDG_CONFIG_HOME ??
|
|
4665
|
+
CONFIG_DIR = path10.join(
|
|
4666
|
+
process.env.XDG_CONFIG_HOME ?? path10.join(process.env.HOME ?? "~", ".config"),
|
|
4370
4667
|
"mesh"
|
|
4371
4668
|
);
|
|
4372
|
-
CONFIG_FILE =
|
|
4373
|
-
CREDENTIALS_FILE =
|
|
4669
|
+
CONFIG_FILE = path10.join(CONFIG_DIR, "config.json");
|
|
4670
|
+
CREDENTIALS_FILE = path10.join(CONFIG_DIR, "credentials.json");
|
|
4374
4671
|
REDIRECT_PORT = 9876;
|
|
4375
4672
|
REDIRECT_URI = `http://localhost:${REDIRECT_PORT}/callback`;
|
|
4376
4673
|
SCOPES = "openid email profile offline_access urn:zitadel:iam:org:project:id:zitadel:aud";
|
|
@@ -4487,6 +4784,7 @@ __export(registry_identity_exports, {
|
|
|
4487
4784
|
logRegistryIdentity: () => logRegistryIdentity,
|
|
4488
4785
|
readRegistrySession: () => readRegistrySession,
|
|
4489
4786
|
refreshRegistrySession: () => refreshRegistrySession,
|
|
4787
|
+
registryAccountState: () => registryAccountState,
|
|
4490
4788
|
registrySessionCandidates: () => registrySessionCandidates,
|
|
4491
4789
|
registryTokenRoles: () => registryTokenRoles,
|
|
4492
4790
|
resolveRegistryIdentity: () => resolveRegistryIdentity
|
|
@@ -4600,6 +4898,19 @@ function registryTokenRoles(idToken) {
|
|
|
4600
4898
|
return [];
|
|
4601
4899
|
}
|
|
4602
4900
|
}
|
|
4901
|
+
function registryAccountState(idToken) {
|
|
4902
|
+
if (!idToken) return {};
|
|
4903
|
+
let claims = null;
|
|
4904
|
+
try {
|
|
4905
|
+
claims = decodeJwtPayload(idToken);
|
|
4906
|
+
} catch {
|
|
4907
|
+
return {};
|
|
4908
|
+
}
|
|
4909
|
+
if (!claims) return {};
|
|
4910
|
+
const email = typeof claims.email === "string" ? claims.email : void 0;
|
|
4911
|
+
const verified = claims.email_verified;
|
|
4912
|
+
return { email, emailVerified: typeof verified === "boolean" ? verified : void 0 };
|
|
4913
|
+
}
|
|
4603
4914
|
async function refreshRegistrySession(context) {
|
|
4604
4915
|
const before = registryTokenRoles(readCredentials(context)?.idToken);
|
|
4605
4916
|
const idToken = await forceRefreshToken(context);
|
|
@@ -4689,14 +5000,14 @@ __export(auth_preflight_exports, {
|
|
|
4689
5000
|
stripUnscopedCodeArtifactRegistry: () => stripUnscopedCodeArtifactRegistry,
|
|
4690
5001
|
upsertNpmrcLines: () => upsertNpmrcLines
|
|
4691
5002
|
});
|
|
4692
|
-
import * as
|
|
5003
|
+
import * as fs11 from "fs";
|
|
4693
5004
|
import * as os3 from "os";
|
|
4694
|
-
import * as
|
|
5005
|
+
import * as path11 from "path";
|
|
4695
5006
|
function registryLoginFix() {
|
|
4696
5007
|
return REGISTRY_LOGIN_FIX;
|
|
4697
5008
|
}
|
|
4698
5009
|
function homeNpmrcPath() {
|
|
4699
|
-
return
|
|
5010
|
+
return path11.join(os3.homedir(), ".npmrc");
|
|
4700
5011
|
}
|
|
4701
5012
|
function parseNpmrcRegistryAuth(content) {
|
|
4702
5013
|
for (const line of content.split("\n")) {
|
|
@@ -4761,7 +5072,7 @@ async function probeRegistryToken(opts) {
|
|
|
4761
5072
|
const fetchFn = opts?.fetchFn ?? fetch;
|
|
4762
5073
|
let auth = null;
|
|
4763
5074
|
try {
|
|
4764
|
-
auth = parseNpmrcRegistryAuth(
|
|
5075
|
+
auth = parseNpmrcRegistryAuth(fs11.readFileSync(npmrcPath2, "utf-8"));
|
|
4765
5076
|
} catch {
|
|
4766
5077
|
}
|
|
4767
5078
|
if (!auth) return { state: "missing" };
|
|
@@ -4826,16 +5137,16 @@ function awsConfigHasProfile(content, profile) {
|
|
|
4826
5137
|
return content.split("\n").some((line) => pattern.test(line.trim()));
|
|
4827
5138
|
}
|
|
4828
5139
|
function readAwsConfig() {
|
|
4829
|
-
const file = process.env.AWS_CONFIG_FILE ??
|
|
5140
|
+
const file = process.env.AWS_CONFIG_FILE ?? path11.join(os3.homedir(), ".aws", "config");
|
|
4830
5141
|
try {
|
|
4831
|
-
return
|
|
5142
|
+
return fs11.readFileSync(file, "utf-8");
|
|
4832
5143
|
} catch {
|
|
4833
5144
|
return "";
|
|
4834
5145
|
}
|
|
4835
5146
|
}
|
|
4836
5147
|
function appUsesMeshPackages(appRoot) {
|
|
4837
5148
|
try {
|
|
4838
|
-
const pkg = JSON.parse(
|
|
5149
|
+
const pkg = JSON.parse(fs11.readFileSync(path11.join(appRoot, "package.json"), "utf-8"));
|
|
4839
5150
|
return [pkg.dependencies, pkg.devDependencies, pkg.optionalDependencies].some(
|
|
4840
5151
|
(deps) => deps && Object.keys(deps).some((name) => name.startsWith("@mesh-tech/"))
|
|
4841
5152
|
);
|
|
@@ -4858,8 +5169,8 @@ sso_registration_scopes = sso:account:access`;
|
|
|
4858
5169
|
});
|
|
4859
5170
|
|
|
4860
5171
|
// libs/mesh-cli/src/utils/aws-auth.ts
|
|
4861
|
-
import * as
|
|
4862
|
-
import * as
|
|
5172
|
+
import * as fs12 from "fs";
|
|
5173
|
+
import * as path12 from "path";
|
|
4863
5174
|
import {
|
|
4864
5175
|
AssumeRoleCommand,
|
|
4865
5176
|
AssumeRoleWithWebIdentityCommand,
|
|
@@ -4867,9 +5178,9 @@ import {
|
|
|
4867
5178
|
STSClient
|
|
4868
5179
|
} from "@aws-sdk/client-sts";
|
|
4869
5180
|
function derivePlatformContext(appRoot, stack) {
|
|
4870
|
-
const configFile =
|
|
4871
|
-
if (!
|
|
4872
|
-
const content =
|
|
5181
|
+
const configFile = path12.join(appRoot, `Pulumi.${stack}.yaml`);
|
|
5182
|
+
if (!fs12.existsSync(configFile)) return null;
|
|
5183
|
+
const content = fs12.readFileSync(configFile, "utf-8");
|
|
4873
5184
|
const jsonMatch = content.match(/mesh:platform:\s*'(\{[^']+\})'/);
|
|
4874
5185
|
if (jsonMatch) {
|
|
4875
5186
|
try {
|
|
@@ -4889,11 +5200,11 @@ function quoteCredProcessToken(token) {
|
|
|
4889
5200
|
return /\s/.test(token) ? `"${token}"` : token;
|
|
4890
5201
|
}
|
|
4891
5202
|
function resolveStableMeshBin(argv1, deps = {}) {
|
|
4892
|
-
const exists = deps.exists ??
|
|
5203
|
+
const exists = deps.exists ?? fs12.existsSync;
|
|
4893
5204
|
const warn = deps.warn ?? logWarn;
|
|
4894
|
-
let resolved = argv1 ?
|
|
5205
|
+
let resolved = argv1 ? path12.resolve(argv1) : void 0;
|
|
4895
5206
|
if (resolved && /\.ts$/.test(resolved)) {
|
|
4896
|
-
resolved =
|
|
5207
|
+
resolved = path12.join(path12.dirname(resolved), path12.basename(resolved, ".ts") + ".mjs");
|
|
4897
5208
|
}
|
|
4898
5209
|
const transient = resolved !== void 0 && (/[\\/]_npx[\\/]/.test(resolved) || /^\/(?:private\/)?(?:tmp|var\/folders)\//.test(resolved));
|
|
4899
5210
|
if (!resolved || !exists(resolved) || transient) {
|
|
@@ -5315,9 +5626,9 @@ var init_pulumi_run = __esm({
|
|
|
5315
5626
|
});
|
|
5316
5627
|
|
|
5317
5628
|
// libs/mesh-cli/src/utils/kubeconfig.ts
|
|
5318
|
-
import * as
|
|
5629
|
+
import * as fs13 from "fs";
|
|
5319
5630
|
import * as os4 from "os";
|
|
5320
|
-
import * as
|
|
5631
|
+
import * as path13 from "path";
|
|
5321
5632
|
function buildKubeconfig(cluster, region) {
|
|
5322
5633
|
return `apiVersion: v1
|
|
5323
5634
|
kind: Config
|
|
@@ -5348,7 +5659,7 @@ users:
|
|
|
5348
5659
|
`;
|
|
5349
5660
|
}
|
|
5350
5661
|
function sessionKubeconfigPath(sessionName) {
|
|
5351
|
-
return
|
|
5662
|
+
return path13.join(os4.tmpdir(), "mesh-dev-sessions", `${sessionName}.kubeconfig`);
|
|
5352
5663
|
}
|
|
5353
5664
|
function resolveHubPlatformName(platform) {
|
|
5354
5665
|
return platform?.name ?? "mesh";
|
|
@@ -5381,10 +5692,10 @@ async function ensureKubeconfig(platformName, env, sessionName, deps = {}) {
|
|
|
5381
5692
|
}
|
|
5382
5693
|
const region = process.env.AWS_REGION || "us-east-2";
|
|
5383
5694
|
const kubeconfigPath = sessionKubeconfigPath(sessionName);
|
|
5384
|
-
if (!
|
|
5385
|
-
|
|
5695
|
+
if (!fs13.existsSync(path13.dirname(kubeconfigPath))) {
|
|
5696
|
+
fs13.mkdirSync(path13.dirname(kubeconfigPath), { recursive: true });
|
|
5386
5697
|
}
|
|
5387
|
-
|
|
5698
|
+
fs13.writeFileSync(kubeconfigPath, buildKubeconfig(cluster, region), { mode: 384 });
|
|
5388
5699
|
return kubeconfigPath;
|
|
5389
5700
|
}
|
|
5390
5701
|
var init_kubeconfig = __esm({
|
|
@@ -5529,14 +5840,14 @@ var init_temporal_auth = __esm({
|
|
|
5529
5840
|
// libs/mesh-cli/src/utils/reachability.ts
|
|
5530
5841
|
import { createConnection } from "node:net";
|
|
5531
5842
|
function probeTcpReachable(host, port, timeoutMs = 1500) {
|
|
5532
|
-
return new Promise((
|
|
5843
|
+
return new Promise((resolve20) => {
|
|
5533
5844
|
let settled = false;
|
|
5534
5845
|
const socket = createConnection({ host, port });
|
|
5535
5846
|
const finish = (ok) => {
|
|
5536
5847
|
if (settled) return;
|
|
5537
5848
|
settled = true;
|
|
5538
5849
|
socket.destroy();
|
|
5539
|
-
|
|
5850
|
+
resolve20(ok);
|
|
5540
5851
|
};
|
|
5541
5852
|
socket.setTimeout(timeoutMs);
|
|
5542
5853
|
socket.once("connect", () => finish(true));
|
|
@@ -5545,7 +5856,7 @@ function probeTcpReachable(host, port, timeoutMs = 1500) {
|
|
|
5545
5856
|
});
|
|
5546
5857
|
}
|
|
5547
5858
|
function probeConnectionHolds(host, port, timeoutMs = 800, holdMs = 25) {
|
|
5548
|
-
return new Promise((
|
|
5859
|
+
return new Promise((resolve20) => {
|
|
5549
5860
|
let settled = false;
|
|
5550
5861
|
let holdTimer;
|
|
5551
5862
|
const socket = createConnection({ host, port });
|
|
@@ -5554,7 +5865,7 @@ function probeConnectionHolds(host, port, timeoutMs = 800, holdMs = 25) {
|
|
|
5554
5865
|
settled = true;
|
|
5555
5866
|
if (holdTimer) clearTimeout(holdTimer);
|
|
5556
5867
|
socket.destroy();
|
|
5557
|
-
|
|
5868
|
+
resolve20(ok);
|
|
5558
5869
|
};
|
|
5559
5870
|
socket.setTimeout(timeoutMs);
|
|
5560
5871
|
socket.once("connect", () => {
|
|
@@ -5573,27 +5884,27 @@ var init_reachability = __esm({
|
|
|
5573
5884
|
|
|
5574
5885
|
// libs/mesh-cli/src/utils/workflow-fingerprint.ts
|
|
5575
5886
|
import { createHash as createHash2 } from "node:crypto";
|
|
5576
|
-
import { existsSync as
|
|
5577
|
-
import { dirname as
|
|
5578
|
-
function
|
|
5887
|
+
import { existsSync as existsSync12, readFileSync as readFileSync12, readdirSync as readdirSync4 } from "node:fs";
|
|
5888
|
+
import { dirname as dirname12, isAbsolute as isAbsolute2, join as join12, relative as relative3 } from "node:path";
|
|
5889
|
+
function findRepoRoot2(startDir) {
|
|
5579
5890
|
let dir = startDir;
|
|
5580
5891
|
for (; ; ) {
|
|
5581
|
-
if (
|
|
5582
|
-
const parent =
|
|
5892
|
+
if (existsSync12(join12(dir, "pnpm-workspace.yaml"))) return dir;
|
|
5893
|
+
const parent = dirname12(dir);
|
|
5583
5894
|
if (parent === dir) return startDir;
|
|
5584
5895
|
dir = parent;
|
|
5585
5896
|
}
|
|
5586
5897
|
}
|
|
5587
5898
|
function workflowSourceDirs(repoRoot2, extraDirs = []) {
|
|
5588
|
-
const pkgDirs = WORKFLOW_PACKAGE_DIRS.map((d) =>
|
|
5899
|
+
const pkgDirs = WORKFLOW_PACKAGE_DIRS.map((d) => join12(repoRoot2, d)).filter((d) => existsSync12(d));
|
|
5589
5900
|
if (pkgDirs.length > 0) return pkgDirs;
|
|
5590
|
-
return extraDirs.filter((d) =>
|
|
5901
|
+
return extraDirs.filter((d) => existsSync12(d));
|
|
5591
5902
|
}
|
|
5592
5903
|
function resolveWorkerSourceDirs(repoRoot2, services = {}) {
|
|
5593
5904
|
const dirs = [];
|
|
5594
5905
|
for (const [name, svc] of Object.entries(services)) {
|
|
5595
5906
|
if (/worker/i.test(name) && svc?.src) {
|
|
5596
|
-
dirs.push(isAbsolute2(svc.src) ? svc.src :
|
|
5907
|
+
dirs.push(isAbsolute2(svc.src) ? svc.src : join12(repoRoot2, svc.src));
|
|
5597
5908
|
}
|
|
5598
5909
|
}
|
|
5599
5910
|
return dirs;
|
|
@@ -5609,7 +5920,7 @@ function computeWorkflowFingerprint(dirs, relativeTo) {
|
|
|
5609
5920
|
}
|
|
5610
5921
|
for (const rel2 of rels) {
|
|
5611
5922
|
const p = String(rel2);
|
|
5612
|
-
if (/\.(ts|tsx)$/.test(p) && !EXCLUDE.test(p)) files.push(
|
|
5923
|
+
if (/\.(ts|tsx)$/.test(p) && !EXCLUDE.test(p)) files.push(join12(dir, p));
|
|
5613
5924
|
}
|
|
5614
5925
|
}
|
|
5615
5926
|
files.sort();
|
|
@@ -5617,8 +5928,8 @@ function computeWorkflowFingerprint(dirs, relativeTo) {
|
|
|
5617
5928
|
const h = createHash2("sha256");
|
|
5618
5929
|
for (const f of files) {
|
|
5619
5930
|
try {
|
|
5620
|
-
const content =
|
|
5621
|
-
h.update(relativeTo ?
|
|
5931
|
+
const content = readFileSync12(f);
|
|
5932
|
+
h.update(relativeTo ? relative3(relativeTo, f) : f);
|
|
5622
5933
|
h.update("\0");
|
|
5623
5934
|
h.update(content);
|
|
5624
5935
|
h.update("\0");
|
|
@@ -5628,7 +5939,7 @@ function computeWorkflowFingerprint(dirs, relativeTo) {
|
|
|
5628
5939
|
return h.digest("hex").slice(0, 16);
|
|
5629
5940
|
}
|
|
5630
5941
|
function fingerprintWorkflowSource(appRoot, services = {}) {
|
|
5631
|
-
const repoRoot2 =
|
|
5942
|
+
const repoRoot2 = findRepoRoot2(appRoot);
|
|
5632
5943
|
const workerDirs = resolveWorkerSourceDirs(repoRoot2, services);
|
|
5633
5944
|
return computeWorkflowFingerprint(workflowSourceDirs(repoRoot2, workerDirs), repoRoot2);
|
|
5634
5945
|
}
|
|
@@ -5646,9 +5957,9 @@ var init_workflow_fingerprint = __esm({
|
|
|
5646
5957
|
});
|
|
5647
5958
|
|
|
5648
5959
|
// libs/mesh-cli/src/commands/dev-launch.ts
|
|
5649
|
-
import * as
|
|
5960
|
+
import * as fs14 from "fs";
|
|
5650
5961
|
import * as net7 from "net";
|
|
5651
|
-
import * as
|
|
5962
|
+
import * as path14 from "path";
|
|
5652
5963
|
function quoteShellValue(value) {
|
|
5653
5964
|
return `'${value.replace(/'/g, "'\\''")}'`;
|
|
5654
5965
|
}
|
|
@@ -5670,9 +5981,9 @@ function envFileName(serviceName) {
|
|
|
5670
5981
|
return `${serviceName.replace(/[^A-Za-z0-9._-]/g, "-")}.env.sh`;
|
|
5671
5982
|
}
|
|
5672
5983
|
function writeEnvFile(filePath, env) {
|
|
5673
|
-
|
|
5674
|
-
|
|
5675
|
-
|
|
5984
|
+
fs14.mkdirSync(path14.dirname(filePath), { recursive: true });
|
|
5985
|
+
fs14.writeFileSync(filePath, renderEnvFile(env), { mode: ENV_FILE_MODE });
|
|
5986
|
+
fs14.chmodSync(filePath, ENV_FILE_MODE);
|
|
5676
5987
|
}
|
|
5677
5988
|
function buildLaunchCommand(envFilePath, dir, command, logShipper) {
|
|
5678
5989
|
const base = `cd ${quoteShellValue(dir)} && source ${quoteShellValue(envFilePath)} && `;
|
|
@@ -5686,16 +5997,16 @@ async function waitForPort(host, port, timeoutMs, intervalMs = 500) {
|
|
|
5686
5997
|
const attemptTimeout = Math.max(250, Math.min(1e3, remaining));
|
|
5687
5998
|
if (await tryConnect(host, port, attemptTimeout)) return true;
|
|
5688
5999
|
if (Date.now() + intervalMs >= deadline) return false;
|
|
5689
|
-
await new Promise((
|
|
6000
|
+
await new Promise((resolve20) => setTimeout(resolve20, intervalMs));
|
|
5690
6001
|
}
|
|
5691
6002
|
}
|
|
5692
6003
|
function tryConnect(host, port, timeoutMs) {
|
|
5693
|
-
return new Promise((
|
|
6004
|
+
return new Promise((resolve20) => {
|
|
5694
6005
|
const socket = net7.connect({ host, port });
|
|
5695
6006
|
const done = (ok) => {
|
|
5696
6007
|
socket.removeAllListeners();
|
|
5697
6008
|
socket.destroy();
|
|
5698
|
-
|
|
6009
|
+
resolve20(ok);
|
|
5699
6010
|
};
|
|
5700
6011
|
socket.setTimeout(timeoutMs);
|
|
5701
6012
|
socket.once("connect", () => done(true));
|
|
@@ -5714,8 +6025,8 @@ var init_dev_launch = __esm({
|
|
|
5714
6025
|
|
|
5715
6026
|
// libs/mesh-cli/src/commands/local/mocks.ts
|
|
5716
6027
|
import { execFileSync as execFileSync12 } from "child_process";
|
|
5717
|
-
import * as
|
|
5718
|
-
import * as
|
|
6028
|
+
import * as fs15 from "fs";
|
|
6029
|
+
import * as path15 from "path";
|
|
5719
6030
|
function externalMode(name, decl) {
|
|
5720
6031
|
const mode = decl.mode ?? (decl.compose ? "local" : decl.openapi || decl.src ? "mock" : decl.sandbox || decl.credentials || decl.remote ? "remote" : void 0);
|
|
5721
6032
|
if (!mode || !EXTERNAL_MODES.includes(mode)) {
|
|
@@ -5839,8 +6150,8 @@ async function planComposeExternal(composeFile, project, port, probes) {
|
|
|
5839
6150
|
return { action: "up" };
|
|
5840
6151
|
}
|
|
5841
6152
|
async function composeExternalUp(appRoot, sessionName, name, decl, probes = dockerProbes) {
|
|
5842
|
-
const composeFile =
|
|
5843
|
-
if (!
|
|
6153
|
+
const composeFile = path15.resolve(appRoot, decl.compose);
|
|
6154
|
+
if (!fs15.existsSync(composeFile)) {
|
|
5844
6155
|
throw new MeshCliError(`External '${name}': compose file not found at ${composeFile}.`, {
|
|
5845
6156
|
remediation: { docs: 'package.json \u2192 "mesh": { "externals": { \u2026 } }' }
|
|
5846
6157
|
});
|
|
@@ -5980,7 +6291,7 @@ function composeExternalsDown(refs) {
|
|
|
5980
6291
|
}
|
|
5981
6292
|
function readLocalMocks(appRoot) {
|
|
5982
6293
|
try {
|
|
5983
|
-
const pkg = JSON.parse(
|
|
6294
|
+
const pkg = JSON.parse(fs15.readFileSync(path15.join(appRoot, "package.json"), "utf-8"));
|
|
5984
6295
|
const mocks = pkg?.mesh?.mocks;
|
|
5985
6296
|
const externals = pkg?.mesh?.externals;
|
|
5986
6297
|
return {
|
|
@@ -6075,12 +6386,12 @@ function externalDocs(decl) {
|
|
|
6075
6386
|
return typeof decl.docs === "string" ? { url: decl.docs } : decl.docs;
|
|
6076
6387
|
}
|
|
6077
6388
|
function localProbeFile(tenant, app, external) {
|
|
6078
|
-
return
|
|
6389
|
+
return path15.join(localProbesDir(), `${tenant}-${app}-${external}.json`);
|
|
6079
6390
|
}
|
|
6080
6391
|
function localProbesRemove(files) {
|
|
6081
6392
|
for (const file of files ?? []) {
|
|
6082
6393
|
try {
|
|
6083
|
-
|
|
6394
|
+
fs15.rmSync(file, { force: true });
|
|
6084
6395
|
} catch {
|
|
6085
6396
|
}
|
|
6086
6397
|
}
|
|
@@ -6169,7 +6480,7 @@ async function seedLocalMock(args) {
|
|
|
6169
6480
|
);
|
|
6170
6481
|
const probeFile = localProbeFile(tenant, app, decl.external);
|
|
6171
6482
|
if (probe) {
|
|
6172
|
-
|
|
6483
|
+
fs15.writeFileSync(
|
|
6173
6484
|
probeFile,
|
|
6174
6485
|
JSON.stringify(
|
|
6175
6486
|
[
|
|
@@ -6278,7 +6589,7 @@ var init_hub_roles = __esm({
|
|
|
6278
6589
|
|
|
6279
6590
|
// libs/api-registry/src/index.ts
|
|
6280
6591
|
import { z } from "zod";
|
|
6281
|
-
var rateLimitSpecSchema, rateLimitDefaultsSchema, integrationHealthSchema, apiSurfaceKindSchema, apiDocsSiteAudienceSchema, apiSurfaceSchema, apiRegistryEntrySchema, integrationStatusSchema;
|
|
6592
|
+
var rateLimitSpecSchema, rateLimitDefaultsSchema, integrationHealthSchema, apiSurfaceKindSchema, apiDocsSiteAudienceSchema, apiSurfaceSchema, apiRegistryEntrySchema, integrationStatusSchema, hubActAsExportsSchema;
|
|
6282
6593
|
var init_src = __esm({
|
|
6283
6594
|
"libs/api-registry/src/index.ts"() {
|
|
6284
6595
|
"use strict";
|
|
@@ -6314,6 +6625,7 @@ var init_src = __esm({
|
|
|
6314
6625
|
docs: z.object({
|
|
6315
6626
|
url: z.string().min(1),
|
|
6316
6627
|
contentHash: z.string().min(1),
|
|
6628
|
+
trimmed: z.object({ url: z.string().min(1), contentHash: z.string().min(1) }).optional(),
|
|
6317
6629
|
siteUrl: z.string().min(1).optional(),
|
|
6318
6630
|
audience: apiDocsSiteAudienceSchema.optional()
|
|
6319
6631
|
}),
|
|
@@ -6321,6 +6633,7 @@ var init_src = __esm({
|
|
|
6321
6633
|
// The consumer schema strips unknown keys, so a producer-side field that is
|
|
6322
6634
|
// not mirrored here never reaches a consumer — mirror every addition.
|
|
6323
6635
|
status: z.object({ url: z.string().min(1) }).optional(),
|
|
6636
|
+
actAs: z.object({ url: z.string().min(1) }).optional(),
|
|
6324
6637
|
appVersion: z.string().min(1).optional(),
|
|
6325
6638
|
rateLimits: rateLimitDefaultsSchema.optional(),
|
|
6326
6639
|
health: integrationHealthSchema.optional(),
|
|
@@ -6345,13 +6658,26 @@ var init_src = __esm({
|
|
|
6345
6658
|
health: integrationHealthSchema.optional(),
|
|
6346
6659
|
startedAt: z.string().min(1)
|
|
6347
6660
|
});
|
|
6661
|
+
hubActAsExportsSchema = z.object({
|
|
6662
|
+
issuer: z.string().min(1),
|
|
6663
|
+
keys: z.array(
|
|
6664
|
+
z.object({
|
|
6665
|
+
kid: z.string().min(1),
|
|
6666
|
+
kty: z.literal("RSA"),
|
|
6667
|
+
n: z.string().min(1),
|
|
6668
|
+
e: z.string().min(1),
|
|
6669
|
+
alg: z.string().optional(),
|
|
6670
|
+
use: z.string().optional()
|
|
6671
|
+
})
|
|
6672
|
+
).min(1)
|
|
6673
|
+
});
|
|
6348
6674
|
}
|
|
6349
6675
|
});
|
|
6350
6676
|
|
|
6351
6677
|
// libs/mesh-cli/src/commands/local/seed-zitadel.ts
|
|
6352
|
-
import * as
|
|
6678
|
+
import * as fs16 from "fs";
|
|
6353
6679
|
import * as os5 from "os";
|
|
6354
|
-
import * as
|
|
6680
|
+
import * as path16 from "path";
|
|
6355
6681
|
function buildCliAppPayload() {
|
|
6356
6682
|
return {
|
|
6357
6683
|
name: CLI_APP_NAME,
|
|
@@ -6368,10 +6694,10 @@ function buildCliAppPayload() {
|
|
|
6368
6694
|
};
|
|
6369
6695
|
}
|
|
6370
6696
|
function readSeederPat() {
|
|
6371
|
-
const tmp =
|
|
6697
|
+
const tmp = path16.join(fs16.mkdtempSync(path16.join(os5.tmpdir(), "mesh-zitadel-")), "pat.txt");
|
|
6372
6698
|
try {
|
|
6373
6699
|
compose(["cp", "zitadel:/machinekey/pat.txt", tmp]);
|
|
6374
|
-
const pat =
|
|
6700
|
+
const pat = fs16.readFileSync(tmp, "utf-8").trim();
|
|
6375
6701
|
if (!pat) throw new Error("empty PAT file");
|
|
6376
6702
|
return pat;
|
|
6377
6703
|
} catch {
|
|
@@ -6380,7 +6706,7 @@ function readSeederPat() {
|
|
|
6380
6706
|
{ remediation: { command: "mesh stop --destroy && mesh start" } }
|
|
6381
6707
|
);
|
|
6382
6708
|
} finally {
|
|
6383
|
-
|
|
6709
|
+
fs16.rmSync(path16.dirname(tmp), { recursive: true, force: true });
|
|
6384
6710
|
}
|
|
6385
6711
|
}
|
|
6386
6712
|
async function api(pat, method, apiPath, body, orgId) {
|
|
@@ -6479,11 +6805,11 @@ async function ensureTestUsers(pat) {
|
|
|
6479
6805
|
}
|
|
6480
6806
|
}
|
|
6481
6807
|
function hubAuthPath() {
|
|
6482
|
-
return
|
|
6808
|
+
return path16.join(meshCacheDir("mesh-local"), "hub-auth.json");
|
|
6483
6809
|
}
|
|
6484
6810
|
function readHubAuth() {
|
|
6485
6811
|
try {
|
|
6486
|
-
const parsed = JSON.parse(
|
|
6812
|
+
const parsed = JSON.parse(fs16.readFileSync(hubAuthPath(), "utf-8"));
|
|
6487
6813
|
if (parsed?.clientId && parsed?.clientSecret && parsed?.cookieSecret) return parsed;
|
|
6488
6814
|
return null;
|
|
6489
6815
|
} catch {
|
|
@@ -6622,7 +6948,7 @@ async function seedHubAuth(pat) {
|
|
|
6622
6948
|
clientSecret: app.clientSecret ?? persisted?.clientSecret ?? "",
|
|
6623
6949
|
cookieSecret: persistedCookie ?? (await import("crypto")).randomBytes(16).toString("hex")
|
|
6624
6950
|
};
|
|
6625
|
-
|
|
6951
|
+
fs16.writeFileSync(hubAuthPath(), JSON.stringify(config, null, 2), { mode: 384 });
|
|
6626
6952
|
return config;
|
|
6627
6953
|
}
|
|
6628
6954
|
async function publishHubAuthzPointer(projectId, orgId, awsConfig) {
|
|
@@ -7206,56 +7532,6 @@ var init_auth_provision = __esm({
|
|
|
7206
7532
|
}
|
|
7207
7533
|
});
|
|
7208
7534
|
|
|
7209
|
-
// libs/mesh-cli/src/utils/mesh-json.ts
|
|
7210
|
-
import * as fs16 from "fs";
|
|
7211
|
-
import * as path16 from "path";
|
|
7212
|
-
function isValidTenantName(value) {
|
|
7213
|
-
return /^[a-z][a-z0-9-]*$/.test(value);
|
|
7214
|
-
}
|
|
7215
|
-
function parseMeshJson(content) {
|
|
7216
|
-
try {
|
|
7217
|
-
const data = JSON.parse(content);
|
|
7218
|
-
if (typeof data?.tenant !== "string" || !isValidTenantName(data.tenant)) return null;
|
|
7219
|
-
const platform = typeof data.platform === "string" && data.platform ? data.platform : "local";
|
|
7220
|
-
return { tenant: data.tenant, platform };
|
|
7221
|
-
} catch {
|
|
7222
|
-
return null;
|
|
7223
|
-
}
|
|
7224
|
-
}
|
|
7225
|
-
function findMeshJson(startDir) {
|
|
7226
|
-
let dir = path16.resolve(startDir);
|
|
7227
|
-
while (true) {
|
|
7228
|
-
const candidate = path16.join(dir, MESH_JSON);
|
|
7229
|
-
if (fs16.existsSync(candidate)) {
|
|
7230
|
-
let content = "";
|
|
7231
|
-
try {
|
|
7232
|
-
content = fs16.readFileSync(candidate, "utf-8");
|
|
7233
|
-
} catch {
|
|
7234
|
-
return null;
|
|
7235
|
-
}
|
|
7236
|
-
const data = parseMeshJson(content);
|
|
7237
|
-
return data ? { path: candidate, data } : null;
|
|
7238
|
-
}
|
|
7239
|
-
if (fs16.existsSync(path16.join(dir, ".git"))) return null;
|
|
7240
|
-
const parent = path16.dirname(dir);
|
|
7241
|
-
if (parent === dir) return null;
|
|
7242
|
-
dir = parent;
|
|
7243
|
-
}
|
|
7244
|
-
}
|
|
7245
|
-
function writeMeshJson(rootDir, data) {
|
|
7246
|
-
const target = path16.join(rootDir, MESH_JSON);
|
|
7247
|
-
fs16.writeFileSync(target, `${JSON.stringify({ tenant: data.tenant, platform: data.platform }, null, 2)}
|
|
7248
|
-
`);
|
|
7249
|
-
return target;
|
|
7250
|
-
}
|
|
7251
|
-
var MESH_JSON;
|
|
7252
|
-
var init_mesh_json = __esm({
|
|
7253
|
-
"libs/mesh-cli/src/utils/mesh-json.ts"() {
|
|
7254
|
-
"use strict";
|
|
7255
|
-
MESH_JSON = "mesh.json";
|
|
7256
|
-
}
|
|
7257
|
-
});
|
|
7258
|
-
|
|
7259
7535
|
// libs/mesh-cli/src/commands/local/dev-local.ts
|
|
7260
7536
|
import * as fs17 from "fs";
|
|
7261
7537
|
import * as path17 from "path";
|
|
@@ -7780,7 +8056,7 @@ async function startTokenServer(port, context) {
|
|
|
7780
8056
|
})();
|
|
7781
8057
|
});
|
|
7782
8058
|
server.on("error", (e) => logWarn(`token-server error: ${e.message}`));
|
|
7783
|
-
await new Promise((
|
|
8059
|
+
await new Promise((resolve20) => server.listen(port, "127.0.0.1", resolve20));
|
|
7784
8060
|
logInfo(`dev-user token-server on http://127.0.0.1:${port} (context ${context})`);
|
|
7785
8061
|
await new Promise(() => {
|
|
7786
8062
|
});
|
|
@@ -7910,10 +8186,10 @@ function removeSessionState(sessionName) {
|
|
|
7910
8186
|
}
|
|
7911
8187
|
}
|
|
7912
8188
|
async function isPortFree(port) {
|
|
7913
|
-
const bindSucceeds = (host) => new Promise((
|
|
8189
|
+
const bindSucceeds = (host) => new Promise((resolve20) => {
|
|
7914
8190
|
const server = net8.createServer();
|
|
7915
|
-
server.once("error", () =>
|
|
7916
|
-
const onListening = () => server.close(() =>
|
|
8191
|
+
server.once("error", () => resolve20(false));
|
|
8192
|
+
const onListening = () => server.close(() => resolve20(true));
|
|
7917
8193
|
if (host === void 0) server.listen(port, onListening);
|
|
7918
8194
|
else server.listen(port, host, onListening);
|
|
7919
8195
|
});
|
|
@@ -7921,24 +8197,24 @@ async function isPortFree(port) {
|
|
|
7921
8197
|
return bindSucceeds("127.0.0.1");
|
|
7922
8198
|
}
|
|
7923
8199
|
async function findFreePort3() {
|
|
7924
|
-
return new Promise((
|
|
8200
|
+
return new Promise((resolve20, reject) => {
|
|
7925
8201
|
const server = net8.createServer();
|
|
7926
8202
|
server.once("error", reject);
|
|
7927
8203
|
server.listen(0, "127.0.0.1", () => {
|
|
7928
8204
|
const { port } = server.address();
|
|
7929
|
-
server.close(() =>
|
|
8205
|
+
server.close(() => resolve20(port));
|
|
7930
8206
|
});
|
|
7931
8207
|
});
|
|
7932
8208
|
}
|
|
7933
8209
|
function isPortListening(port) {
|
|
7934
|
-
return new Promise((
|
|
8210
|
+
return new Promise((resolve20) => {
|
|
7935
8211
|
const s = new net8.Socket();
|
|
7936
8212
|
let done = false;
|
|
7937
8213
|
const fin = (ok) => {
|
|
7938
8214
|
if (done) return;
|
|
7939
8215
|
done = true;
|
|
7940
8216
|
s.destroy();
|
|
7941
|
-
|
|
8217
|
+
resolve20(ok);
|
|
7942
8218
|
};
|
|
7943
8219
|
s.setTimeout(300);
|
|
7944
8220
|
s.once("connect", () => fin(true));
|
|
@@ -10372,14 +10648,14 @@ function isConfigStale(configMtimeMs, startedAtIso) {
|
|
|
10372
10648
|
return configMtimeMs > Date.parse(startedAtIso);
|
|
10373
10649
|
}
|
|
10374
10650
|
function canConnect2(host, port, timeoutMs = 800) {
|
|
10375
|
-
return new Promise((
|
|
10651
|
+
return new Promise((resolve20) => {
|
|
10376
10652
|
const socket = new net9.Socket();
|
|
10377
10653
|
let settled = false;
|
|
10378
10654
|
const done = (ok) => {
|
|
10379
10655
|
if (settled) return;
|
|
10380
10656
|
settled = true;
|
|
10381
10657
|
socket.destroy();
|
|
10382
|
-
|
|
10658
|
+
resolve20(ok);
|
|
10383
10659
|
};
|
|
10384
10660
|
socket.setTimeout(timeoutMs);
|
|
10385
10661
|
socket.once("connect", () => done(true));
|
|
@@ -11982,11 +12258,11 @@ function createVcsFolderReader(opts) {
|
|
|
11982
12258
|
}
|
|
11983
12259
|
return p;
|
|
11984
12260
|
};
|
|
11985
|
-
const readPath = async (repo,
|
|
12261
|
+
const readPath = async (repo, path47) => {
|
|
11986
12262
|
const dir = await cloneRepo(repo);
|
|
11987
|
-
const ref = await resolveFolderRef(dir,
|
|
11988
|
-
if (!ref) throw new Error(`path "${
|
|
11989
|
-
const files = await readFolderAtRef(dir, ref,
|
|
12263
|
+
const ref = await resolveFolderRef(dir, path47);
|
|
12264
|
+
if (!ref) throw new Error(`path "${path47}" not found on any ref of ${repo}`);
|
|
12265
|
+
const files = await readFolderAtRef(dir, ref, path47);
|
|
11990
12266
|
return { ref, files };
|
|
11991
12267
|
};
|
|
11992
12268
|
const cleanup = async () => {
|
|
@@ -13280,25 +13556,50 @@ var init_create_app = __esm({
|
|
|
13280
13556
|
}
|
|
13281
13557
|
});
|
|
13282
13558
|
|
|
13559
|
+
// libs/mesh-cli/src/commands/db/axes.ts
|
|
13560
|
+
function platformAxis(options, usage, detect = detectContext) {
|
|
13561
|
+
if (options.tenant && !options.env || !options.tenant && options.env) {
|
|
13562
|
+
throw new MeshCliError("Both --tenant and --env are required", {
|
|
13563
|
+
remediation: { command: usage }
|
|
13564
|
+
});
|
|
13565
|
+
}
|
|
13566
|
+
let resolved;
|
|
13567
|
+
return () => {
|
|
13568
|
+
if (resolved) return resolved;
|
|
13569
|
+
if (options.tenant && options.env) {
|
|
13570
|
+
resolved = { tenant: options.tenant, env: options.env };
|
|
13571
|
+
logInfo(`Platform: Tenant=${resolved.tenant}, Env=${resolved.env}`);
|
|
13572
|
+
} else {
|
|
13573
|
+
const ctx = detect();
|
|
13574
|
+
resolved = { tenant: ctx.tenant, env: ctx.platformEnv };
|
|
13575
|
+
logInfo(`Auto-detected: Tenant=${resolved.tenant}, Env=${resolved.env}`);
|
|
13576
|
+
}
|
|
13577
|
+
return resolved;
|
|
13578
|
+
};
|
|
13579
|
+
}
|
|
13580
|
+
function appAxis(options, platform) {
|
|
13581
|
+
if (options.appTenant && options.appStage) {
|
|
13582
|
+
return { tenant: options.appTenant, stage: options.appStage };
|
|
13583
|
+
}
|
|
13584
|
+
const resolved = platform();
|
|
13585
|
+
return {
|
|
13586
|
+
tenant: options.appTenant ?? resolved.tenant,
|
|
13587
|
+
stage: options.appStage ?? resolved.env
|
|
13588
|
+
};
|
|
13589
|
+
}
|
|
13590
|
+
var init_axes = __esm({
|
|
13591
|
+
"libs/mesh-cli/src/commands/db/axes.ts"() {
|
|
13592
|
+
"use strict";
|
|
13593
|
+
init_context();
|
|
13594
|
+
init_errors();
|
|
13595
|
+
init_log();
|
|
13596
|
+
}
|
|
13597
|
+
});
|
|
13598
|
+
|
|
13283
13599
|
// libs/mesh-cli/src/commands/db/connect.ts
|
|
13284
13600
|
import { spawn as spawn4 } from "child_process";
|
|
13285
13601
|
async function connectCommand(options) {
|
|
13286
|
-
|
|
13287
|
-
let platformEnv;
|
|
13288
|
-
if (options.tenant && options.env) {
|
|
13289
|
-
tenant = options.tenant;
|
|
13290
|
-
platformEnv = options.env;
|
|
13291
|
-
logInfo(`Explicit mode: Tenant=${tenant}, Env=${platformEnv}`);
|
|
13292
|
-
} else if (options.tenant || options.env) {
|
|
13293
|
-
logError("Both --tenant and --env are required");
|
|
13294
|
-
logInfo("Usage: mesh db connect --tenant mesh --env dev-temporal");
|
|
13295
|
-
process.exit(1);
|
|
13296
|
-
} else {
|
|
13297
|
-
const ctx = detectContext();
|
|
13298
|
-
tenant = ctx.tenant;
|
|
13299
|
-
platformEnv = ctx.platformEnv;
|
|
13300
|
-
logInfo(`Auto-detected: Tenant=${tenant}, Env=${platformEnv}`);
|
|
13301
|
-
}
|
|
13602
|
+
const { tenant, env: platformEnv } = platformAxis(options, USAGE)();
|
|
13302
13603
|
const bastion = await getPlatformBastionInfo(tenant, platformEnv);
|
|
13303
13604
|
const rdsService = bastion.services.rds;
|
|
13304
13605
|
if (!rdsService) {
|
|
@@ -13341,38 +13642,25 @@ async function connectCommand(options) {
|
|
|
13341
13642
|
process.exit(code ?? 0);
|
|
13342
13643
|
});
|
|
13343
13644
|
}
|
|
13645
|
+
var USAGE;
|
|
13344
13646
|
var init_connect = __esm({
|
|
13345
13647
|
"libs/mesh-cli/src/commands/db/connect.ts"() {
|
|
13346
13648
|
"use strict";
|
|
13347
13649
|
init_utils();
|
|
13650
|
+
init_axes();
|
|
13651
|
+
USAGE = "mesh db connect --tenant mesh --env dev-temporal";
|
|
13348
13652
|
}
|
|
13349
13653
|
});
|
|
13350
13654
|
|
|
13351
13655
|
// libs/mesh-cli/src/commands/db/credentials.ts
|
|
13352
13656
|
async function credentialsCommand(options) {
|
|
13353
|
-
|
|
13354
|
-
|
|
13355
|
-
if (options.tenant && options.env) {
|
|
13356
|
-
platformTenant = options.tenant;
|
|
13357
|
-
platformEnv = options.env;
|
|
13358
|
-
logInfo(`Platform: Tenant=${platformTenant}, Env=${platformEnv}`);
|
|
13359
|
-
} else if (options.tenant || options.env) {
|
|
13360
|
-
logError("Both --tenant and --env are required");
|
|
13361
|
-
logInfo("Usage: mesh db credentials --tenant mesh --env dev-temporal --app rdc --app-tenant encore --app-stage dev");
|
|
13362
|
-
process.exit(1);
|
|
13363
|
-
} else {
|
|
13364
|
-
const ctx = detectContext();
|
|
13365
|
-
platformTenant = ctx.tenant;
|
|
13366
|
-
platformEnv = ctx.platformEnv;
|
|
13367
|
-
logInfo(`Auto-detected: Tenant=${platformTenant}, Env=${platformEnv}`);
|
|
13368
|
-
}
|
|
13369
|
-
const appTenant = options.appTenant ?? platformTenant;
|
|
13370
|
-
const appStage = options.appStage ?? platformEnv;
|
|
13657
|
+
const platform = platformAxis(options, USAGE2);
|
|
13658
|
+
const app = appAxis(options, platform);
|
|
13371
13659
|
const appName = options.app;
|
|
13372
13660
|
if (appName) {
|
|
13373
|
-
logInfo(`App credentials: tenant=${
|
|
13661
|
+
logInfo(`App credentials: tenant=${app.tenant}, stage=${app.stage}, app=${appName}`);
|
|
13374
13662
|
}
|
|
13375
|
-
const databaseUrl = await getDatabaseUrl(
|
|
13663
|
+
const databaseUrl = await getDatabaseUrl(app.tenant, app.stage, { app: appName });
|
|
13376
13664
|
const parsed = new URL(databaseUrl);
|
|
13377
13665
|
console.log("");
|
|
13378
13666
|
console.log("Database Credentials:");
|
|
@@ -13392,38 +13680,25 @@ async function credentialsCommand(options) {
|
|
|
13392
13680
|
console.log(` ${tunnelUrl.toString()}`);
|
|
13393
13681
|
console.log("");
|
|
13394
13682
|
}
|
|
13683
|
+
var USAGE2;
|
|
13395
13684
|
var init_credentials2 = __esm({
|
|
13396
13685
|
"libs/mesh-cli/src/commands/db/credentials.ts"() {
|
|
13397
13686
|
"use strict";
|
|
13398
13687
|
init_utils();
|
|
13688
|
+
init_axes();
|
|
13689
|
+
USAGE2 = "mesh db credentials --tenant mesh --env dev-temporal --app rdc --app-tenant encore --app-stage dev";
|
|
13399
13690
|
}
|
|
13400
13691
|
});
|
|
13401
13692
|
|
|
13402
13693
|
// libs/mesh-cli/src/commands/db/env.ts
|
|
13403
13694
|
async function envCommand(options) {
|
|
13404
|
-
|
|
13405
|
-
|
|
13406
|
-
if (options.tenant && options.env) {
|
|
13407
|
-
platformTenant = options.tenant;
|
|
13408
|
-
platformEnv = options.env;
|
|
13409
|
-
console.error(`\u2139 Platform: Tenant=${platformTenant}, Env=${platformEnv}`);
|
|
13410
|
-
} else if (options.tenant || options.env) {
|
|
13411
|
-
logError("Both --tenant and --env are required");
|
|
13412
|
-
logInfo("Usage: eval $(mesh db env --tenant mesh --env dev-temporal --app rdc --app-tenant encore --app-stage dev)");
|
|
13413
|
-
process.exit(1);
|
|
13414
|
-
} else {
|
|
13415
|
-
const ctx = detectContext();
|
|
13416
|
-
platformTenant = ctx.tenant;
|
|
13417
|
-
platformEnv = ctx.platformEnv;
|
|
13418
|
-
console.error(`\u2139 Auto-detected: Tenant=${platformTenant}, Env=${platformEnv}`);
|
|
13419
|
-
}
|
|
13420
|
-
const appTenant = options.appTenant ?? platformTenant;
|
|
13421
|
-
const appStage = options.appStage ?? platformEnv;
|
|
13695
|
+
const platform = platformAxis(options, USAGE3);
|
|
13696
|
+
const app = appAxis(options, platform);
|
|
13422
13697
|
const appName = options.app;
|
|
13423
13698
|
if (appName) {
|
|
13424
|
-
console.error(`\u2139 App credentials: tenant=${
|
|
13699
|
+
console.error(`\u2139 App credentials: tenant=${app.tenant}, stage=${app.stage}, app=${appName}`);
|
|
13425
13700
|
}
|
|
13426
|
-
const rawUrl = await getDatabaseUrl(
|
|
13701
|
+
const rawUrl = await getDatabaseUrl(app.tenant, app.stage, { app: appName });
|
|
13427
13702
|
const tunnelUrl = rewriteDatabaseUrl(rawUrl, { endpoint: "localhost:5432" });
|
|
13428
13703
|
const parsed = new URL(tunnelUrl);
|
|
13429
13704
|
console.log(`export DATABASE_URL="${tunnelUrl}"`);
|
|
@@ -13433,10 +13708,13 @@ async function envCommand(options) {
|
|
|
13433
13708
|
console.log(`export DB_USERNAME="${parsed.username}"`);
|
|
13434
13709
|
console.log(`export DB_PASSWORD="${decodeURIComponent(parsed.password)}"`);
|
|
13435
13710
|
}
|
|
13711
|
+
var USAGE3;
|
|
13436
13712
|
var init_env = __esm({
|
|
13437
13713
|
"libs/mesh-cli/src/commands/db/env.ts"() {
|
|
13438
13714
|
"use strict";
|
|
13439
13715
|
init_utils();
|
|
13716
|
+
init_axes();
|
|
13717
|
+
USAGE3 = "eval $(mesh db env --tenant mesh --env dev-temporal --app rdc --app-tenant encore --app-stage dev)";
|
|
13440
13718
|
}
|
|
13441
13719
|
});
|
|
13442
13720
|
|
|
@@ -13479,58 +13757,43 @@ async function startTunnelBackground(instanceId, rdsHost, rdsPort, localPort) {
|
|
|
13479
13757
|
throw new Error(`Tunnel failed to start after ${maxAttempts} seconds`);
|
|
13480
13758
|
}
|
|
13481
13759
|
function checkPort(port) {
|
|
13482
|
-
return new Promise((
|
|
13760
|
+
return new Promise((resolve20) => {
|
|
13483
13761
|
const socket = new net10.Socket();
|
|
13484
13762
|
socket.setTimeout(500);
|
|
13485
13763
|
socket.on("connect", () => {
|
|
13486
13764
|
socket.destroy();
|
|
13487
|
-
|
|
13765
|
+
resolve20(true);
|
|
13488
13766
|
});
|
|
13489
13767
|
socket.on("timeout", () => {
|
|
13490
13768
|
socket.destroy();
|
|
13491
|
-
|
|
13769
|
+
resolve20(false);
|
|
13492
13770
|
});
|
|
13493
13771
|
socket.on("error", () => {
|
|
13494
13772
|
socket.destroy();
|
|
13495
|
-
|
|
13773
|
+
resolve20(false);
|
|
13496
13774
|
});
|
|
13497
13775
|
socket.connect(port, "localhost");
|
|
13498
13776
|
});
|
|
13499
13777
|
}
|
|
13500
13778
|
function sleep2(ms) {
|
|
13501
|
-
return new Promise((
|
|
13779
|
+
return new Promise((resolve20) => setTimeout(resolve20, ms));
|
|
13502
13780
|
}
|
|
13503
13781
|
async function psqlCommand(options) {
|
|
13504
|
-
|
|
13505
|
-
|
|
13506
|
-
|
|
13507
|
-
platformTenant = options.tenant;
|
|
13508
|
-
platformEnv = options.env;
|
|
13509
|
-
logInfo(`Platform: Tenant=${platformTenant}, Env=${platformEnv}`);
|
|
13510
|
-
} else if (options.tenant || options.env) {
|
|
13511
|
-
logError("Both --tenant and --env are required");
|
|
13512
|
-
logInfo("Usage: mesh db psql --tenant mesh --env dev-temporal --app rdc --app-tenant encore --app-stage dev");
|
|
13513
|
-
process.exit(1);
|
|
13514
|
-
} else {
|
|
13515
|
-
const ctx = detectContext();
|
|
13516
|
-
platformTenant = ctx.tenant;
|
|
13517
|
-
platformEnv = ctx.platformEnv;
|
|
13518
|
-
logInfo(`Auto-detected: Tenant=${platformTenant}, Env=${platformEnv}`);
|
|
13519
|
-
}
|
|
13520
|
-
const appTenant = options.appTenant ?? platformTenant;
|
|
13521
|
-
const appStage = options.appStage ?? platformEnv;
|
|
13782
|
+
const platform = platformAxis(options, USAGE4);
|
|
13783
|
+
const { tenant: platformTenant, env: platformEnv } = platform();
|
|
13784
|
+
const app = appAxis(options, platform);
|
|
13522
13785
|
const appName = options.app;
|
|
13523
13786
|
if (appName) {
|
|
13524
|
-
logInfo(`App credentials: tenant=${
|
|
13787
|
+
logInfo(`App credentials: tenant=${app.tenant}, stage=${app.stage}, app=${appName}`);
|
|
13525
13788
|
}
|
|
13526
13789
|
const psqlCheck = spawn5("which", ["psql"]);
|
|
13527
|
-
await new Promise((
|
|
13790
|
+
await new Promise((resolve20, reject) => {
|
|
13528
13791
|
psqlCheck.on("exit", (code) => {
|
|
13529
13792
|
if (code !== 0) {
|
|
13530
13793
|
logError("psql not found. Install with: brew install postgresql");
|
|
13531
13794
|
reject(new Error("psql not found"));
|
|
13532
13795
|
} else {
|
|
13533
|
-
|
|
13796
|
+
resolve20();
|
|
13534
13797
|
}
|
|
13535
13798
|
});
|
|
13536
13799
|
});
|
|
@@ -13553,7 +13816,7 @@ async function psqlCommand(options) {
|
|
|
13553
13816
|
process.on("exit", cleanup);
|
|
13554
13817
|
process.on("SIGINT", cleanup);
|
|
13555
13818
|
process.on("SIGTERM", cleanup);
|
|
13556
|
-
const databaseUrl = await getDatabaseUrl(
|
|
13819
|
+
const databaseUrl = await getDatabaseUrl(app.tenant, app.stage, { app: appName });
|
|
13557
13820
|
const parsed = new URL(databaseUrl);
|
|
13558
13821
|
const username = parsed.username;
|
|
13559
13822
|
const password2 = decodeURIComponent(parsed.password);
|
|
@@ -13574,10 +13837,13 @@ async function psqlCommand(options) {
|
|
|
13574
13837
|
process.exit(code ?? 0);
|
|
13575
13838
|
});
|
|
13576
13839
|
}
|
|
13840
|
+
var USAGE4;
|
|
13577
13841
|
var init_psql = __esm({
|
|
13578
13842
|
"libs/mesh-cli/src/commands/db/psql.ts"() {
|
|
13579
13843
|
"use strict";
|
|
13580
13844
|
init_utils();
|
|
13845
|
+
init_axes();
|
|
13846
|
+
USAGE4 = "mesh db psql --tenant mesh --env dev-temporal --app rdc --app-tenant encore --app-stage dev";
|
|
13581
13847
|
}
|
|
13582
13848
|
});
|
|
13583
13849
|
|
|
@@ -13617,26 +13883,26 @@ function findSstOutputs() {
|
|
|
13617
13883
|
return null;
|
|
13618
13884
|
}
|
|
13619
13885
|
function checkPort2(port) {
|
|
13620
|
-
return new Promise((
|
|
13886
|
+
return new Promise((resolve20) => {
|
|
13621
13887
|
const socket = new net11.Socket();
|
|
13622
13888
|
socket.setTimeout(500);
|
|
13623
13889
|
socket.on("connect", () => {
|
|
13624
13890
|
socket.destroy();
|
|
13625
|
-
|
|
13891
|
+
resolve20(true);
|
|
13626
13892
|
});
|
|
13627
13893
|
socket.on("timeout", () => {
|
|
13628
13894
|
socket.destroy();
|
|
13629
|
-
|
|
13895
|
+
resolve20(false);
|
|
13630
13896
|
});
|
|
13631
13897
|
socket.on("error", () => {
|
|
13632
13898
|
socket.destroy();
|
|
13633
|
-
|
|
13899
|
+
resolve20(false);
|
|
13634
13900
|
});
|
|
13635
13901
|
socket.connect(port, "localhost");
|
|
13636
13902
|
});
|
|
13637
13903
|
}
|
|
13638
13904
|
function sleep3(ms) {
|
|
13639
|
-
return new Promise((
|
|
13905
|
+
return new Promise((resolve20) => setTimeout(resolve20, ms));
|
|
13640
13906
|
}
|
|
13641
13907
|
async function startTunnelBackground2(instanceId, rdsEndpoint, rdsPort, localPort) {
|
|
13642
13908
|
logInfo("Starting database tunnel...");
|
|
@@ -13760,28 +14026,14 @@ async function execCommand(commandArgs, options) {
|
|
|
13760
14026
|
logWarn("Could not parse DATABASE_URL for rewriting");
|
|
13761
14027
|
}
|
|
13762
14028
|
} else {
|
|
13763
|
-
|
|
13764
|
-
|
|
13765
|
-
|
|
13766
|
-
|
|
13767
|
-
|
|
13768
|
-
logInfo(`Explicit mode: Platform Tenant=${platformTenant}, Platform Env=${platformEnv}`);
|
|
13769
|
-
} else if (options.tenant || options.env) {
|
|
13770
|
-
logError("Both --tenant and --env are required when using explicit mode");
|
|
13771
|
-
logInfo("Usage: mesh db exec --tenant mesh --env dev-temporal --app rdc --app-tenant encore --app-stage dev -- <command>");
|
|
13772
|
-
logInfo("Or omit both to use auto-detection");
|
|
13773
|
-
process.exit(1);
|
|
13774
|
-
} else {
|
|
13775
|
-
const ctx = detectContext();
|
|
13776
|
-
platformTenant = ctx.tenant;
|
|
13777
|
-
platformEnv = ctx.platformEnv;
|
|
13778
|
-
logInfo(`Discovery mode: Platform=${platformEnv}, Tenant=${platformTenant}`);
|
|
13779
|
-
}
|
|
13780
|
-
const appTenant = options.appTenant ?? platformTenant;
|
|
13781
|
-
const appStage = options.appStage ?? platformEnv;
|
|
14029
|
+
const platform = platformAxis(
|
|
14030
|
+
options,
|
|
14031
|
+
"mesh db exec --tenant mesh --env dev-temporal --app rdc --app-tenant encore --app-stage dev -- <command>"
|
|
14032
|
+
);
|
|
14033
|
+
const app = appAxis(options, platform);
|
|
13782
14034
|
const appName = options.app;
|
|
13783
14035
|
if (appName) {
|
|
13784
|
-
logInfo(`App credentials: tenant=${
|
|
14036
|
+
logInfo(`App credentials: tenant=${app.tenant}, stage=${app.stage}, app=${appName}`);
|
|
13785
14037
|
}
|
|
13786
14038
|
const tunnelExists = await checkPort2(localPort);
|
|
13787
14039
|
const sstOutputs = findSstOutputs();
|
|
@@ -13802,6 +14054,7 @@ async function execCommand(commandArgs, options) {
|
|
|
13802
14054
|
}
|
|
13803
14055
|
if (!databaseUrl) {
|
|
13804
14056
|
if (!tunnelExists) {
|
|
14057
|
+
const { tenant: platformTenant, env: platformEnv } = platform();
|
|
13805
14058
|
const bastion = await getPlatformBastionInfo(platformTenant, platformEnv);
|
|
13806
14059
|
const rdsService = bastion.services.rds;
|
|
13807
14060
|
if (!rdsService) {
|
|
@@ -13816,7 +14069,7 @@ async function execCommand(commandArgs, options) {
|
|
|
13816
14069
|
} else {
|
|
13817
14070
|
logSuccess(`Using existing tunnel on port ${localPort}`);
|
|
13818
14071
|
}
|
|
13819
|
-
const rawUrl = await getDatabaseUrl(
|
|
14072
|
+
const rawUrl = await getDatabaseUrl(app.tenant, app.stage, { app: appName });
|
|
13820
14073
|
databaseUrl = rewriteDatabaseUrl(rawUrl, {
|
|
13821
14074
|
endpoint: `localhost:${localPort}`,
|
|
13822
14075
|
sslMode: options.ssl
|
|
@@ -13856,6 +14109,7 @@ var init_exec = __esm({
|
|
|
13856
14109
|
"libs/mesh-cli/src/commands/db/exec.ts"() {
|
|
13857
14110
|
"use strict";
|
|
13858
14111
|
init_utils();
|
|
14112
|
+
init_axes();
|
|
13859
14113
|
DEFAULT_LOCAL_PORT = 5432;
|
|
13860
14114
|
MAX_RETRIES = 12;
|
|
13861
14115
|
RETRY_DELAY_MS = 5e3;
|
|
@@ -13884,7 +14138,7 @@ var init_db = __esm({
|
|
|
13884
14138
|
|
|
13885
14139
|
// libs/mesh-cli/src/utils/deploy-preflight.ts
|
|
13886
14140
|
import { existsSync as existsSync20, readFileSync as readFileSync21 } from "node:fs";
|
|
13887
|
-
import { dirname as dirname22, join as join25, relative as
|
|
14141
|
+
import { dirname as dirname22, join as join25, relative as relative7 } from "node:path";
|
|
13888
14142
|
import { parse as parseYaml3 } from "yaml";
|
|
13889
14143
|
function isGated(name) {
|
|
13890
14144
|
return GATED_PREFIXES.some((p) => name.startsWith(p));
|
|
@@ -13950,7 +14204,7 @@ function checkDeployDepsFresh(appRoot) {
|
|
|
13950
14204
|
);
|
|
13951
14205
|
return null;
|
|
13952
14206
|
}
|
|
13953
|
-
const importerRel =
|
|
14207
|
+
const importerRel = relative7(dirname22(lockPath), appRoot) || ".";
|
|
13954
14208
|
const expected = extractImporterMeshVersions(lockDoc, importerRel);
|
|
13955
14209
|
const names = Object.keys(expected);
|
|
13956
14210
|
if (names.length === 0) return [];
|
|
@@ -14976,8 +15230,8 @@ function formatDefault(value, paths = currentMachinePaths()) {
|
|
|
14976
15230
|
if (typeof value === "boolean" || typeof value === "number") return String(value);
|
|
14977
15231
|
return normalizeMachinePaths(JSON.stringify(value), paths);
|
|
14978
15232
|
}
|
|
14979
|
-
function slugifyCommandPath(
|
|
14980
|
-
return
|
|
15233
|
+
function slugifyCommandPath(path47) {
|
|
15234
|
+
return path47.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
14981
15235
|
}
|
|
14982
15236
|
function extractOptions(command) {
|
|
14983
15237
|
const options = command.options ?? [];
|
|
@@ -15011,17 +15265,17 @@ function isUndocumented(command) {
|
|
|
15011
15265
|
function extractCommand(command, parentPath, depth) {
|
|
15012
15266
|
const internals = command;
|
|
15013
15267
|
const name = internals._name ?? "";
|
|
15014
|
-
const
|
|
15268
|
+
const path47 = parentPath ? `${parentPath} ${name}` : name;
|
|
15015
15269
|
return {
|
|
15016
|
-
path:
|
|
15270
|
+
path: path47,
|
|
15017
15271
|
name,
|
|
15018
15272
|
aliases: [...internals._aliases ?? []],
|
|
15019
15273
|
description: internals._description ?? "",
|
|
15020
15274
|
args: extractArgs(command),
|
|
15021
15275
|
options: extractOptions(command),
|
|
15022
|
-
slug: slugifyCommandPath(
|
|
15276
|
+
slug: slugifyCommandPath(path47),
|
|
15023
15277
|
depth,
|
|
15024
|
-
subcommands: (internals.commands ?? []).filter((child) => !isUndocumented(child)).map((child) => extractCommand(child,
|
|
15278
|
+
subcommands: (internals.commands ?? []).filter((child) => !isUndocumented(child)).map((child) => extractCommand(child, path47, depth + 1))
|
|
15025
15279
|
};
|
|
15026
15280
|
}
|
|
15027
15281
|
function extractCliReference(program2) {
|
|
@@ -15249,14 +15503,14 @@ ${summary}`);
|
|
|
15249
15503
|
function runZudoku(args) {
|
|
15250
15504
|
const cliArgs = args.mode === "dev" ? ["dev", "--port", String(args.port ?? 3e3)] : ["build"];
|
|
15251
15505
|
logInfo(`Running \`zudoku ${cliArgs.join(" ")}\` in ${args.appDir}`);
|
|
15252
|
-
return new Promise((
|
|
15506
|
+
return new Promise((resolve20, reject) => {
|
|
15253
15507
|
const child = spawn7("pnpm", ["exec", "zudoku", ...cliArgs], {
|
|
15254
15508
|
cwd: args.appDir,
|
|
15255
15509
|
stdio: "inherit",
|
|
15256
15510
|
env: process.env
|
|
15257
15511
|
});
|
|
15258
15512
|
child.on("error", reject);
|
|
15259
|
-
child.on("close", (code) =>
|
|
15513
|
+
child.on("close", (code) => resolve20(code ?? 1));
|
|
15260
15514
|
});
|
|
15261
15515
|
}
|
|
15262
15516
|
async function runPortalCommand(opts, deps) {
|
|
@@ -15467,17 +15721,17 @@ async function serveDocsSite(args) {
|
|
|
15467
15721
|
res.writeHead(500).end("internal error");
|
|
15468
15722
|
});
|
|
15469
15723
|
});
|
|
15470
|
-
await new Promise((
|
|
15724
|
+
await new Promise((resolve20, reject) => {
|
|
15471
15725
|
server.once("error", reject);
|
|
15472
|
-
server.listen(args.port, "127.0.0.1", () =>
|
|
15726
|
+
server.listen(args.port, "127.0.0.1", () => resolve20());
|
|
15473
15727
|
});
|
|
15474
15728
|
const address = server.address();
|
|
15475
15729
|
const port = typeof address === "object" && address ? address.port : args.port;
|
|
15476
15730
|
return {
|
|
15477
15731
|
url: `http://127.0.0.1:${port}`,
|
|
15478
15732
|
port,
|
|
15479
|
-
close: () => new Promise((
|
|
15480
|
-
server.close((error) => error ? reject(error) :
|
|
15733
|
+
close: () => new Promise((resolve20, reject) => {
|
|
15734
|
+
server.close((error) => error ? reject(error) : resolve20());
|
|
15481
15735
|
})
|
|
15482
15736
|
};
|
|
15483
15737
|
}
|
|
@@ -15730,7 +15984,7 @@ Or run in the foreground instead: mesh docs start --foreground`
|
|
|
15730
15984
|
} catch (error) {
|
|
15731
15985
|
lastError = error instanceof Error ? error.message : String(error);
|
|
15732
15986
|
}
|
|
15733
|
-
await new Promise((
|
|
15987
|
+
await new Promise((resolve20) => setTimeout(resolve20, 500));
|
|
15734
15988
|
}
|
|
15735
15989
|
throw new Error(
|
|
15736
15990
|
`the docs server did not answer at ${url} within 60s (${lastError || "no response"}).
|
|
@@ -15767,8 +16021,8 @@ async function runDocsServeStatic(args) {
|
|
|
15767
16021
|
}
|
|
15768
16022
|
const server = await serveDocsSite({ root: args.root, port: requestedPort });
|
|
15769
16023
|
logSuccess(`Mesh docs serving at ${server.url} (from ${args.root})`);
|
|
15770
|
-
await new Promise((
|
|
15771
|
-
const stop = () => void server.close().finally(() =>
|
|
16024
|
+
await new Promise((resolve20) => {
|
|
16025
|
+
const stop = () => void server.close().finally(() => resolve20());
|
|
15772
16026
|
process.once("SIGINT", stop);
|
|
15773
16027
|
process.once("SIGTERM", stop);
|
|
15774
16028
|
});
|
|
@@ -15822,8 +16076,8 @@ async function runDocsStart(args) {
|
|
|
15822
16076
|
stdio: "inherit",
|
|
15823
16077
|
env: process.env
|
|
15824
16078
|
});
|
|
15825
|
-
await new Promise((
|
|
15826
|
-
child.on("close", () =>
|
|
16079
|
+
await new Promise((resolve20) => {
|
|
16080
|
+
child.on("close", () => resolve20());
|
|
15827
16081
|
process.once("SIGINT", () => child.kill("SIGINT"));
|
|
15828
16082
|
process.once("SIGTERM", () => child.kill("SIGTERM"));
|
|
15829
16083
|
});
|
|
@@ -15839,7 +16093,7 @@ async function runDocsStart(args) {
|
|
|
15839
16093
|
reportAssembly(result);
|
|
15840
16094
|
const { spawn: spawn10 } = await import("node:child_process");
|
|
15841
16095
|
writeFileSync18(buildLog, "");
|
|
15842
|
-
const code = await new Promise((
|
|
16096
|
+
const code = await new Promise((resolve20) => {
|
|
15843
16097
|
const child = spawn10("pnpm", ["exec", "zudoku", "build"], {
|
|
15844
16098
|
cwd: appDir,
|
|
15845
16099
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -15847,8 +16101,8 @@ async function runDocsStart(args) {
|
|
|
15847
16101
|
});
|
|
15848
16102
|
child.stdout?.on("data", (chunk) => appendFileSync2(buildLog, chunk));
|
|
15849
16103
|
child.stderr?.on("data", (chunk) => appendFileSync2(buildLog, chunk));
|
|
15850
|
-
child.on("error", () =>
|
|
15851
|
-
child.on("close", (exitCode) =>
|
|
16104
|
+
child.on("error", () => resolve20(1));
|
|
16105
|
+
child.on("close", (exitCode) => resolve20(exitCode ?? 1));
|
|
15852
16106
|
});
|
|
15853
16107
|
if (code !== 0) throw new Error(`zudoku build exited with code ${code} \u2014 see ${buildLog}`);
|
|
15854
16108
|
serveRoot = path32.join(appDir, "dist");
|
|
@@ -15868,9 +16122,9 @@ async function runDocsStart(args) {
|
|
|
15868
16122
|
const server = await serveDocsSite({ root: serveRoot, port: requestedPort });
|
|
15869
16123
|
logSuccess(`Mesh docs (${label}) serving at ${server.url}`);
|
|
15870
16124
|
logInfo("Press Ctrl-C to stop");
|
|
15871
|
-
await new Promise((
|
|
16125
|
+
await new Promise((resolve20) => {
|
|
15872
16126
|
const stop = () => {
|
|
15873
|
-
void server.close().finally(() =>
|
|
16127
|
+
void server.close().finally(() => resolve20());
|
|
15874
16128
|
};
|
|
15875
16129
|
process.once("SIGINT", stop);
|
|
15876
16130
|
process.once("SIGTERM", stop);
|
|
@@ -16025,7 +16279,7 @@ function guarded(action) {
|
|
|
16025
16279
|
};
|
|
16026
16280
|
}
|
|
16027
16281
|
async function runCliReference(opts) {
|
|
16028
|
-
const repoRoot2 =
|
|
16282
|
+
const repoRoot2 = findRepoRoot2(process.cwd());
|
|
16029
16283
|
const outPath = resolvePath(repoRoot2, opts.out);
|
|
16030
16284
|
const markdown = await renderCliReference();
|
|
16031
16285
|
if (opts.check) {
|
|
@@ -16045,7 +16299,7 @@ async function runCliReference(opts) {
|
|
|
16045
16299
|
logSuccess(`CLI reference \u2192 ${path33.relative(repoRoot2, outPath)}`);
|
|
16046
16300
|
}
|
|
16047
16301
|
async function runPortal(opts) {
|
|
16048
|
-
const repoRoot2 =
|
|
16302
|
+
const repoRoot2 = findRepoRoot2(process.cwd());
|
|
16049
16303
|
await runPortalCommand(
|
|
16050
16304
|
{
|
|
16051
16305
|
out: opts.out,
|
|
@@ -16083,7 +16337,7 @@ function registerDocsCommand(program2) {
|
|
|
16083
16337
|
(opts) => guarded(async () => {
|
|
16084
16338
|
const { runDocsStart: runDocsStart2 } = await Promise.resolve().then(() => (init_start(), start_exports));
|
|
16085
16339
|
await runDocsStart2({
|
|
16086
|
-
repoRoot:
|
|
16340
|
+
repoRoot: findRepoRoot2(process.cwd()),
|
|
16087
16341
|
version: opts.version,
|
|
16088
16342
|
port: opts.port,
|
|
16089
16343
|
dev: opts.dev,
|
|
@@ -16320,24 +16574,24 @@ function resolvePlatformDir(explicit, cwd, env = process.env) {
|
|
|
16320
16574
|
);
|
|
16321
16575
|
}
|
|
16322
16576
|
async function findFreePort4() {
|
|
16323
|
-
return new Promise((
|
|
16577
|
+
return new Promise((resolve20, reject) => {
|
|
16324
16578
|
const server = net12.createServer();
|
|
16325
16579
|
server.listen(0, "127.0.0.1", () => {
|
|
16326
16580
|
const address = server.address();
|
|
16327
16581
|
server.close(
|
|
16328
|
-
() => typeof address === "object" && address ?
|
|
16582
|
+
() => typeof address === "object" && address ? resolve20(address.port) : reject(new Error("no port"))
|
|
16329
16583
|
);
|
|
16330
16584
|
});
|
|
16331
16585
|
server.on("error", reject);
|
|
16332
16586
|
});
|
|
16333
16587
|
}
|
|
16334
16588
|
function isPortListening2(port) {
|
|
16335
|
-
return new Promise((
|
|
16589
|
+
return new Promise((resolve20) => {
|
|
16336
16590
|
const socket = net12.connect({ host: "127.0.0.1", port, timeout: 400 });
|
|
16337
16591
|
const done = (ok) => {
|
|
16338
16592
|
socket.removeAllListeners();
|
|
16339
16593
|
socket.destroy();
|
|
16340
|
-
|
|
16594
|
+
resolve20(ok);
|
|
16341
16595
|
};
|
|
16342
16596
|
socket.once("connect", () => done(true));
|
|
16343
16597
|
socket.once("timeout", () => done(false));
|
|
@@ -16533,10 +16787,231 @@ var init_hub = __esm({
|
|
|
16533
16787
|
}
|
|
16534
16788
|
});
|
|
16535
16789
|
|
|
16536
|
-
// libs/mesh-cli/src/commands/
|
|
16537
|
-
|
|
16790
|
+
// libs/mesh-cli/src/commands/init/platform-setup.ts
|
|
16791
|
+
var platform_setup_exports = {};
|
|
16792
|
+
__export(platform_setup_exports, {
|
|
16793
|
+
DOMAIN_REQUIRED_MESSAGE: () => DOMAIN_REQUIRED_MESSAGE,
|
|
16794
|
+
PLATFORM_SETUP_DEFAULTS: () => PLATFORM_SETUP_DEFAULTS,
|
|
16795
|
+
defaultPlatformDir: () => defaultPlatformDir,
|
|
16796
|
+
defaultStateBucket: () => defaultStateBucket,
|
|
16797
|
+
ensureStateBucket: () => ensureStateBucket,
|
|
16798
|
+
exportCredentialsCommand: () => exportCredentialsCommand,
|
|
16799
|
+
realStateBucketDeps: () => realStateBucketDeps,
|
|
16800
|
+
renderPlatformNextSteps: () => renderPlatformNextSteps,
|
|
16801
|
+
resolvePlatformSetup: () => resolvePlatformSetup,
|
|
16802
|
+
resolveTargetDir: () => resolveTargetDir,
|
|
16803
|
+
scaffoldPlatformRepo: () => scaffoldPlatformRepo,
|
|
16804
|
+
stateBucketCreateCommand: () => stateBucketCreateCommand,
|
|
16805
|
+
validateDomain: () => validateDomain,
|
|
16806
|
+
validateEnv: () => validateEnv,
|
|
16807
|
+
validateRegion: () => validateRegion,
|
|
16808
|
+
validateStateBucket: () => validateStateBucket
|
|
16809
|
+
});
|
|
16538
16810
|
import * as fs28 from "fs";
|
|
16539
16811
|
import * as path35 from "path";
|
|
16812
|
+
import { execFileSync as execFileSync24 } from "child_process";
|
|
16813
|
+
function defaultStateBucket(tenant) {
|
|
16814
|
+
return `${tenant}-mesh-platform-pulumi-state`;
|
|
16815
|
+
}
|
|
16816
|
+
function defaultPlatformDir(tenant) {
|
|
16817
|
+
return `${tenant}-mesh-platform`;
|
|
16818
|
+
}
|
|
16819
|
+
function validateEnv(value) {
|
|
16820
|
+
return /^[a-z][a-z0-9-]*$/.test(value.trim()) ? true : "lowercase letters, digits and dashes, starting with a letter";
|
|
16821
|
+
}
|
|
16822
|
+
function validateRegion(value) {
|
|
16823
|
+
return /^[a-z]{2}(-[a-z]+)+-\d$/.test(value.trim()) ? true : "an AWS region, e.g. us-east-2 or eu-west-1";
|
|
16824
|
+
}
|
|
16825
|
+
function validateDomain(value) {
|
|
16826
|
+
const domain = value.trim().toLowerCase().replace(/\.$/, "");
|
|
16827
|
+
if (!domain) return "a public domain you control, e.g. q2poc.pub";
|
|
16828
|
+
if (!/^(?=.{1,253}$)([a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z]{2,}$/.test(domain)) {
|
|
16829
|
+
return "a public domain you control, e.g. q2poc.pub";
|
|
16830
|
+
}
|
|
16831
|
+
if (/(^|\.)(example\.(com|org|net)|invalid|localhost|test)$/.test(domain)) {
|
|
16832
|
+
return `${domain} is a placeholder \u2014 the deploy stalls on an ACM certificate for a zone nobody can delegate. Use a domain you control.`;
|
|
16833
|
+
}
|
|
16834
|
+
return true;
|
|
16835
|
+
}
|
|
16836
|
+
function validateStateBucket(value) {
|
|
16837
|
+
const bucket = value.trim();
|
|
16838
|
+
return /^[a-z0-9][a-z0-9.-]{1,61}[a-z0-9]$/.test(bucket) && !bucket.includes("..") ? true : "3\u201363 characters: lowercase letters, digits, dots and dashes, starting and ending alphanumeric";
|
|
16839
|
+
}
|
|
16840
|
+
function resolveTargetDir(tenant, flags, cwd) {
|
|
16841
|
+
const dir = path35.resolve(cwd, flags.dir ?? defaultPlatformDir(tenant));
|
|
16842
|
+
if (fs28.existsSync(dir) && fs28.readdirSync(dir).length > 0) {
|
|
16843
|
+
throw new MeshCliError(`Target directory is not empty: ${dir}`, {
|
|
16844
|
+
remediation: { command: `mesh init platform ${tenant} --dir <empty-dir>` }
|
|
16845
|
+
});
|
|
16846
|
+
}
|
|
16847
|
+
return dir;
|
|
16848
|
+
}
|
|
16849
|
+
async function resolvePlatformSetup(tenant, flags, deps) {
|
|
16850
|
+
const cwd = deps.cwd ?? process.cwd();
|
|
16851
|
+
const dir = resolveTargetDir(tenant, flags, cwd);
|
|
16852
|
+
const ask = deps.interactive && !flags.yes ? deps.prompts : void 0;
|
|
16853
|
+
const env = flags.env ?? (ask ? await ask.input({ message: "Environment", default: PLATFORM_SETUP_DEFAULTS.env, validate: validateEnv }) : PLATFORM_SETUP_DEFAULTS.env);
|
|
16854
|
+
if (validateEnv(env) !== true) throw new MeshCliError(`Invalid environment '${env}' \u2014 ${validateEnv(env)}.`);
|
|
16855
|
+
const region = flags.region ?? (ask ? await ask.input({ message: "AWS region", default: PLATFORM_SETUP_DEFAULTS.region, validate: validateRegion }) : PLATFORM_SETUP_DEFAULTS.region);
|
|
16856
|
+
if (validateRegion(region) !== true) throw new MeshCliError(`Invalid region '${region}' \u2014 ${validateRegion(region)}.`);
|
|
16857
|
+
let domain = flags.domain;
|
|
16858
|
+
if (domain === void 0) {
|
|
16859
|
+
if (!ask) throw new MeshCliError(DOMAIN_REQUIRED_MESSAGE, { remediation: { command: `mesh init platform ${tenant} --domain <your-domain>` } });
|
|
16860
|
+
domain = await ask.input({
|
|
16861
|
+
message: `Public base domain (the core stack creates the Route 53 zone ${env}.<domain>; its NS records must be delegated from the parent before the deploy can finish)`,
|
|
16862
|
+
validate: validateDomain
|
|
16863
|
+
});
|
|
16864
|
+
}
|
|
16865
|
+
const domainCheck = validateDomain(domain);
|
|
16866
|
+
if (domainCheck !== true) throw new MeshCliError(`Invalid --domain '${domain}' \u2014 ${domainCheck}`);
|
|
16867
|
+
domain = domain.trim().toLowerCase().replace(/\.$/, "");
|
|
16868
|
+
const profile = flags.profile ?? (ask ? await ask.input({ message: "AWS profile the stack config references", default: PLATFORM_SETUP_DEFAULTS.profile }) : PLATFORM_SETUP_DEFAULTS.profile);
|
|
16869
|
+
const bucketDefault = defaultStateBucket(tenant);
|
|
16870
|
+
const stateBucket = flags.stateBucket ?? (ask ? await ask.input({ message: "Pulumi state S3 bucket (holds this platform's state \u2014 created below if it does not exist)", default: bucketDefault, validate: validateStateBucket }) : bucketDefault);
|
|
16871
|
+
const bucketCheck = validateStateBucket(stateBucket);
|
|
16872
|
+
if (bucketCheck !== true) throw new MeshCliError(`Invalid --state-bucket '${stateBucket}' \u2014 ${bucketCheck}`);
|
|
16873
|
+
let createStateBucket = flags.createStateBucket ?? false;
|
|
16874
|
+
if (flags.createStateBucket === void 0 && ask) {
|
|
16875
|
+
createStateBucket = await ask.confirm({
|
|
16876
|
+
message: `Create s3://${stateBucket} in ${region} now (versioned), if it does not exist?`,
|
|
16877
|
+
default: true
|
|
16878
|
+
});
|
|
16879
|
+
}
|
|
16880
|
+
return {
|
|
16881
|
+
tenant,
|
|
16882
|
+
env,
|
|
16883
|
+
region,
|
|
16884
|
+
domain,
|
|
16885
|
+
profile,
|
|
16886
|
+
stateBucket: stateBucket.trim(),
|
|
16887
|
+
dir,
|
|
16888
|
+
createStateBucket
|
|
16889
|
+
};
|
|
16890
|
+
}
|
|
16891
|
+
function stateBucketCreateCommand(setup) {
|
|
16892
|
+
const location = setup.region === "us-east-1" ? "" : ` --create-bucket-configuration LocationConstraint=${setup.region}`;
|
|
16893
|
+
return `aws s3api create-bucket --bucket ${setup.stateBucket} --region ${setup.region}${location} --profile ${setup.profile}`;
|
|
16894
|
+
}
|
|
16895
|
+
function awsJson(args, profile) {
|
|
16896
|
+
return execFileSync24("aws", [...args, "--profile", profile], {
|
|
16897
|
+
encoding: "utf8",
|
|
16898
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
16899
|
+
});
|
|
16900
|
+
}
|
|
16901
|
+
async function ensureStateBucket(setup, deps = realStateBucketDeps) {
|
|
16902
|
+
if (!setup.createStateBucket) {
|
|
16903
|
+
return { created: false, present: false, detail: "not requested" };
|
|
16904
|
+
}
|
|
16905
|
+
try {
|
|
16906
|
+
if (deps.headBucket(setup)) {
|
|
16907
|
+
return { created: false, present: true, detail: `s3://${setup.stateBucket} already exists` };
|
|
16908
|
+
}
|
|
16909
|
+
} catch {
|
|
16910
|
+
}
|
|
16911
|
+
try {
|
|
16912
|
+
deps.createBucket(setup);
|
|
16913
|
+
} catch (err) {
|
|
16914
|
+
const why = errorDetail(err);
|
|
16915
|
+
return { created: false, present: false, detail: `could not create s3://${setup.stateBucket} \u2014 ${why}` };
|
|
16916
|
+
}
|
|
16917
|
+
try {
|
|
16918
|
+
deps.enableVersioning(setup);
|
|
16919
|
+
return { created: true, present: true, detail: `created s3://${setup.stateBucket} in ${setup.region} (versioning on)` };
|
|
16920
|
+
} catch (err) {
|
|
16921
|
+
return { created: true, present: true, detail: `created s3://${setup.stateBucket} in ${setup.region}, but versioning could not be enabled \u2014 ${errorDetail(err)}` };
|
|
16922
|
+
}
|
|
16923
|
+
}
|
|
16924
|
+
function errorDetail(err) {
|
|
16925
|
+
const stderr = err && typeof err === "object" && "stderr" in err ? String(err.stderr ?? "") : "";
|
|
16926
|
+
const text = stderr.trim() || (err instanceof Error ? err.message : String(err));
|
|
16927
|
+
return text.split("\n").filter(Boolean).slice(-1)[0] ?? "unknown error";
|
|
16928
|
+
}
|
|
16929
|
+
function exportCredentialsCommand(profile) {
|
|
16930
|
+
return `eval "$(aws configure export-credentials --profile ${profile} --format env)" # mesh deploy reads AWS_ACCESS_KEY_ID; a platform stack carries no mesh:deployerRole`;
|
|
16931
|
+
}
|
|
16932
|
+
function renderPlatformNextSteps(setup, opts = {}) {
|
|
16933
|
+
const dir = opts.relativeDir ?? setup.dir;
|
|
16934
|
+
const steps = [];
|
|
16935
|
+
if (!opts.bucket?.present) {
|
|
16936
|
+
steps.push(`${stateBucketCreateCommand(setup)} # the Pulumi state bucket, once`);
|
|
16937
|
+
}
|
|
16938
|
+
steps.push(
|
|
16939
|
+
`cd ${dir}`,
|
|
16940
|
+
"pnpm install",
|
|
16941
|
+
`aws sso login --profile ${setup.profile} # if this profile is SSO`,
|
|
16942
|
+
exportCredentialsCommand(setup.profile),
|
|
16943
|
+
`cd core && mesh deploy up # the VPC, EKS, RDS and the public zone ${setup.env}.${setup.domain}`,
|
|
16944
|
+
`# \u2192 then delegate the NS records of ${setup.env}.${setup.domain} from ${setup.domain}; the deploy waits on the ACM certificate until you do`,
|
|
16945
|
+
"cd ../platform && mesh deploy up # Temporal, Zitadel, the Hub (needs Docker \u2014 it builds service images)",
|
|
16946
|
+
`mesh login ${setup.tenant}.${setup.env} # your platform sign-in, once it is up`,
|
|
16947
|
+
"",
|
|
16948
|
+
"Then, in your apps repo, for the hello-world app:",
|
|
16949
|
+
` mesh init --tenant ${setup.tenant} --platform ${setup.env}`,
|
|
16950
|
+
" mesh create-app --name hello --primitives service",
|
|
16951
|
+
" mesh deploy up # from the app directory"
|
|
16952
|
+
);
|
|
16953
|
+
return steps;
|
|
16954
|
+
}
|
|
16955
|
+
async function scaffoldPlatformRepo(setup) {
|
|
16956
|
+
if (fs28.existsSync(setup.dir) && fs28.readdirSync(setup.dir).length > 0) {
|
|
16957
|
+
throw new MeshCliError(`Target directory is not empty: ${setup.dir}`, {
|
|
16958
|
+
remediation: { command: `mesh init platform ${setup.tenant} --dir <empty-dir>` }
|
|
16959
|
+
});
|
|
16960
|
+
}
|
|
16961
|
+
const { copyTemplate: copyTemplate2 } = await Promise.resolve().then(() => (init_create_app(), create_app_exports));
|
|
16962
|
+
const { findPackageRoot: findPackageRoot2 } = await Promise.resolve().then(() => (init_stack(), stack_exports));
|
|
16963
|
+
const { syncSkills: syncSkills2 } = await Promise.resolve().then(() => (init_skills(), skills_exports));
|
|
16964
|
+
const templateDir = path35.join(findPackageRoot2(), "templates", "platform-repo");
|
|
16965
|
+
fs28.mkdirSync(setup.dir, { recursive: true });
|
|
16966
|
+
copyTemplate2(templateDir, setup.dir, {
|
|
16967
|
+
tenant: setup.tenant,
|
|
16968
|
+
env: setup.env,
|
|
16969
|
+
region: setup.region,
|
|
16970
|
+
domain: setup.domain,
|
|
16971
|
+
profile: setup.profile,
|
|
16972
|
+
stateBucket: setup.stateBucket
|
|
16973
|
+
});
|
|
16974
|
+
syncSkills2(setup.dir);
|
|
16975
|
+
}
|
|
16976
|
+
var PLATFORM_SETUP_DEFAULTS, DOMAIN_REQUIRED_MESSAGE, realStateBucketDeps;
|
|
16977
|
+
var init_platform_setup = __esm({
|
|
16978
|
+
"libs/mesh-cli/src/commands/init/platform-setup.ts"() {
|
|
16979
|
+
"use strict";
|
|
16980
|
+
init_errors();
|
|
16981
|
+
PLATFORM_SETUP_DEFAULTS = {
|
|
16982
|
+
env: "dev",
|
|
16983
|
+
region: "us-east-2",
|
|
16984
|
+
profile: "default"
|
|
16985
|
+
};
|
|
16986
|
+
DOMAIN_REQUIRED_MESSAGE = "--domain is required: the core stack creates the public Route 53 zone <env>.<domain>, and a placeholder domain scaffolds a zone that can never validate (the deploy stalls on the ACM certificate).";
|
|
16987
|
+
realStateBucketDeps = {
|
|
16988
|
+
headBucket(setup) {
|
|
16989
|
+
try {
|
|
16990
|
+
awsJson(["s3api", "head-bucket", "--bucket", setup.stateBucket, "--region", setup.region], setup.profile);
|
|
16991
|
+
return true;
|
|
16992
|
+
} catch {
|
|
16993
|
+
return false;
|
|
16994
|
+
}
|
|
16995
|
+
},
|
|
16996
|
+
createBucket(setup) {
|
|
16997
|
+
const args = ["s3api", "create-bucket", "--bucket", setup.stateBucket, "--region", setup.region];
|
|
16998
|
+
if (setup.region !== "us-east-1") args.push("--create-bucket-configuration", `LocationConstraint=${setup.region}`);
|
|
16999
|
+
awsJson(args, setup.profile);
|
|
17000
|
+
},
|
|
17001
|
+
enableVersioning(setup) {
|
|
17002
|
+
awsJson(
|
|
17003
|
+
["s3api", "put-bucket-versioning", "--bucket", setup.stateBucket, "--region", setup.region, "--versioning-configuration", "Status=Enabled"],
|
|
17004
|
+
setup.profile
|
|
17005
|
+
);
|
|
17006
|
+
}
|
|
17007
|
+
};
|
|
17008
|
+
}
|
|
17009
|
+
});
|
|
17010
|
+
|
|
17011
|
+
// libs/mesh-cli/src/commands/registry-publish.ts
|
|
17012
|
+
import { execFileSync as execFileSync25, execSync } from "child_process";
|
|
17013
|
+
import * as fs29 from "fs";
|
|
17014
|
+
import * as path36 from "path";
|
|
16540
17015
|
function repoRoot() {
|
|
16541
17016
|
return execSync("git rev-parse --show-toplevel", { encoding: "utf-8" }).trim();
|
|
16542
17017
|
}
|
|
@@ -16546,9 +17021,9 @@ function computeSnapshotVersion(root) {
|
|
|
16546
17021
|
return `0.0.0-dev.${stamp}.${sha}`;
|
|
16547
17022
|
}
|
|
16548
17023
|
function setPackageVersion(file, version) {
|
|
16549
|
-
const content =
|
|
17024
|
+
const content = fs29.readFileSync(file, "utf-8");
|
|
16550
17025
|
const next = content.replace(/"version"(\s*:\s*)"[^"]*"/, `"version"$1"${version}"`);
|
|
16551
|
-
|
|
17026
|
+
fs29.writeFileSync(file, next);
|
|
16552
17027
|
}
|
|
16553
17028
|
function dirtyPackageJsons(root) {
|
|
16554
17029
|
const out = execSync("git diff --name-only", { cwd: root, encoding: "utf-8" });
|
|
@@ -16581,8 +17056,8 @@ function registerRegistryPublish(registry) {
|
|
|
16581
17056
|
logError("CodeArtifact login failed. Run: mesh registry login <context>");
|
|
16582
17057
|
process.exit(1);
|
|
16583
17058
|
}
|
|
16584
|
-
const listPath =
|
|
16585
|
-
const all = JSON.parse(
|
|
17059
|
+
const listPath = path36.join(root, "scripts/lib/publishable-packages.json");
|
|
17060
|
+
const all = JSON.parse(fs29.readFileSync(listPath, "utf-8"));
|
|
16586
17061
|
const patterns = (opts.only ?? "").split(",").map((s) => s.trim()).filter(Boolean);
|
|
16587
17062
|
const selected = patterns.length ? all.filter((p) => patterns.some((pat) => p.dir.includes(pat) || p.name.includes(pat))) : all;
|
|
16588
17063
|
if (selected.length === 0) {
|
|
@@ -16594,16 +17069,16 @@ function registerRegistryPublish(registry) {
|
|
|
16594
17069
|
const preDirty = dirtyPackageJsons(root);
|
|
16595
17070
|
const backups = [];
|
|
16596
17071
|
const stamp = (dir) => {
|
|
16597
|
-
const file =
|
|
16598
|
-
if (!
|
|
16599
|
-
backups.push({ file, content:
|
|
17072
|
+
const file = path36.join(root, dir, "package.json");
|
|
17073
|
+
if (!fs29.existsSync(file)) return;
|
|
17074
|
+
backups.push({ file, content: fs29.readFileSync(file, "utf-8") });
|
|
16600
17075
|
setPackageVersion(file, version);
|
|
16601
17076
|
};
|
|
16602
17077
|
try {
|
|
16603
17078
|
for (const p of selected) {
|
|
16604
17079
|
stamp(p.dir);
|
|
16605
17080
|
for (const sub of p.subPackages ?? []) {
|
|
16606
|
-
stamp(
|
|
17081
|
+
stamp(path36.join(p.dir, typeof sub === "string" ? sub : sub.dir));
|
|
16607
17082
|
}
|
|
16608
17083
|
}
|
|
16609
17084
|
const args = [
|
|
@@ -16618,14 +17093,14 @@ function registerRegistryPublish(registry) {
|
|
|
16618
17093
|
...opts.dryRun ? ["--dry-run"] : []
|
|
16619
17094
|
];
|
|
16620
17095
|
logInfo(`Publishing ${selected.length} package(s)...`);
|
|
16621
|
-
|
|
17096
|
+
execFileSync25("bash", args, { cwd: root, stdio: "inherit", env: env ?? process.env });
|
|
16622
17097
|
} finally {
|
|
16623
|
-
for (const b of backups)
|
|
17098
|
+
for (const b of backups) fs29.writeFileSync(b.file, b.content);
|
|
16624
17099
|
logInfo("Restored source package.json versions.");
|
|
16625
17100
|
const collateral = [...dirtyPackageJsons(root)].filter((f) => !preDirty.has(f));
|
|
16626
17101
|
if (collateral.length) {
|
|
16627
17102
|
try {
|
|
16628
|
-
|
|
17103
|
+
execFileSync25("git", ["checkout", "--", ...collateral], { cwd: root, stdio: "inherit" });
|
|
16629
17104
|
logInfo(`Reverted ${collateral.length} package.json file(s) re-vendored by build:publish.`);
|
|
16630
17105
|
} catch {
|
|
16631
17106
|
logError(
|
|
@@ -16677,12 +17152,12 @@ __export(registry_exports, {
|
|
|
16677
17152
|
shouldRemintAfter: () => shouldRemintAfter,
|
|
16678
17153
|
shouldRepairUserNpmrc: () => shouldRepairUserNpmrc
|
|
16679
17154
|
});
|
|
16680
|
-
import { execFileSync as
|
|
16681
|
-
import * as
|
|
16682
|
-
import * as
|
|
17155
|
+
import { execFileSync as execFileSync26 } from "child_process";
|
|
17156
|
+
import * as fs30 from "fs";
|
|
17157
|
+
import * as path37 from "path";
|
|
16683
17158
|
function getEndpoint(env) {
|
|
16684
17159
|
try {
|
|
16685
|
-
const result =
|
|
17160
|
+
const result = execFileSync26("aws", [
|
|
16686
17161
|
"codeartifact",
|
|
16687
17162
|
"get-repository-endpoint",
|
|
16688
17163
|
"--domain",
|
|
@@ -16709,7 +17184,7 @@ function getEndpoint(env) {
|
|
|
16709
17184
|
}
|
|
16710
17185
|
function codeartifactLogin(env) {
|
|
16711
17186
|
try {
|
|
16712
|
-
|
|
17187
|
+
execFileSync26("aws", [
|
|
16713
17188
|
"codeartifact",
|
|
16714
17189
|
"login",
|
|
16715
17190
|
"--tool",
|
|
@@ -16764,7 +17239,7 @@ function resolvePublisherRoleArn({
|
|
|
16764
17239
|
}
|
|
16765
17240
|
function assumeRole2(roleArn) {
|
|
16766
17241
|
try {
|
|
16767
|
-
const result =
|
|
17242
|
+
const result = execFileSync26("aws", [
|
|
16768
17243
|
"sts",
|
|
16769
17244
|
"assume-role",
|
|
16770
17245
|
"--role-arn",
|
|
@@ -16786,7 +17261,7 @@ function assumeRole2(roleArn) {
|
|
|
16786
17261
|
}
|
|
16787
17262
|
function assumeRoleWithWebIdentity2(roleArn, idToken, sessionName) {
|
|
16788
17263
|
try {
|
|
16789
|
-
const result =
|
|
17264
|
+
const result = execFileSync26("aws", [
|
|
16790
17265
|
"sts",
|
|
16791
17266
|
"assume-role-with-web-identity",
|
|
16792
17267
|
"--role-arn",
|
|
@@ -16850,55 +17325,58 @@ function resolveCredentials(context, roleArn) {
|
|
|
16850
17325
|
function findProjectRoot2() {
|
|
16851
17326
|
let dir = process.cwd();
|
|
16852
17327
|
while (true) {
|
|
16853
|
-
if (
|
|
17328
|
+
if (fs30.existsSync(path37.join(dir, "pnpm-workspace.yaml")) || fs30.existsSync(path37.join(dir, "pnpm-lock.yaml")) || fs30.existsSync(path37.join(dir, "Pulumi.yaml")) || fs30.existsSync(path37.join(dir, ".npmrc"))) {
|
|
16854
17329
|
return dir;
|
|
16855
17330
|
}
|
|
16856
|
-
const parent =
|
|
17331
|
+
const parent = path37.dirname(dir);
|
|
16857
17332
|
if (parent === dir) return null;
|
|
16858
17333
|
dir = parent;
|
|
16859
17334
|
}
|
|
16860
17335
|
}
|
|
16861
17336
|
function ensureProjectNpmrc(endpoint) {
|
|
16862
17337
|
const projectRoot = findProjectRoot2() ?? process.cwd();
|
|
16863
|
-
const npmrcPath2 =
|
|
17338
|
+
const npmrcPath2 = path37.join(projectRoot, ".npmrc");
|
|
16864
17339
|
const scopeLine = `${CA_NAMESPACE}:registry=${endpoint}`;
|
|
16865
|
-
if (!
|
|
16866
|
-
|
|
17340
|
+
if (!fs30.existsSync(npmrcPath2)) {
|
|
17341
|
+
fs30.writeFileSync(npmrcPath2, scopeLine + "\n", "utf-8");
|
|
16867
17342
|
return { created: true, updated: false, path: npmrcPath2 };
|
|
16868
17343
|
}
|
|
16869
|
-
const content =
|
|
17344
|
+
const content = fs30.readFileSync(npmrcPath2, "utf-8");
|
|
16870
17345
|
const scopeRegex = new RegExp(`^${CA_NAMESPACE.replace("/", "\\/")}:registry=.+$`, "m");
|
|
16871
17346
|
if (scopeRegex.test(content)) {
|
|
16872
17347
|
if (content.includes(scopeLine)) {
|
|
16873
17348
|
return { created: false, updated: false, path: npmrcPath2 };
|
|
16874
17349
|
}
|
|
16875
17350
|
const updated = content.replace(scopeRegex, scopeLine);
|
|
16876
|
-
|
|
17351
|
+
fs30.writeFileSync(npmrcPath2, updated, "utf-8");
|
|
16877
17352
|
return { created: false, updated: true, path: npmrcPath2 };
|
|
16878
17353
|
}
|
|
16879
17354
|
const separator = content.endsWith("\n") ? "" : "\n";
|
|
16880
|
-
|
|
17355
|
+
fs30.writeFileSync(npmrcPath2, content + separator + scopeLine + "\n", "utf-8");
|
|
16881
17356
|
return { created: false, updated: true, path: npmrcPath2 };
|
|
16882
17357
|
}
|
|
16883
17358
|
function applyGrantToUserNpmrc(grant, npmrcPath2 = homeNpmrcPath()) {
|
|
16884
17359
|
let existing = "";
|
|
16885
17360
|
try {
|
|
16886
|
-
existing =
|
|
17361
|
+
existing = fs30.readFileSync(npmrcPath2, "utf-8");
|
|
16887
17362
|
} catch {
|
|
16888
17363
|
}
|
|
16889
17364
|
const content = upsertNpmrcLines(existing, [
|
|
16890
17365
|
{ key: `${grant.scope}:registry`, value: grant.endpoint },
|
|
16891
17366
|
{ key: `${npmrcAuthKeyForEndpoint(grant.endpoint)}:_authToken`, value: grant.authorizationToken }
|
|
16892
17367
|
]);
|
|
16893
|
-
|
|
16894
|
-
|
|
17368
|
+
fs30.mkdirSync(path37.dirname(npmrcPath2), { recursive: true });
|
|
17369
|
+
fs30.writeFileSync(npmrcPath2, content, { mode: 384 });
|
|
16895
17370
|
try {
|
|
16896
|
-
|
|
17371
|
+
fs30.chmodSync(npmrcPath2, 384);
|
|
16897
17372
|
} catch {
|
|
16898
17373
|
}
|
|
16899
17374
|
return { path: npmrcPath2 };
|
|
16900
17375
|
}
|
|
16901
|
-
function describeBrokerFailure(failure) {
|
|
17376
|
+
function describeBrokerFailure(failure, account = {}) {
|
|
17377
|
+
const unverified = account.emailVerified === false ? `
|
|
17378
|
+
Your Mesh account${account.email ? ` (${account.email})` : ""} has never confirmed its email address, and an unconfirmed account cannot hold a registry grant.
|
|
17379
|
+
This is not something a role fixes: ask your Mesh contact to verify the address on your account (a self-registered, passkey-only sign-up lands here), then run: mesh registry logout && mesh registry login` : "";
|
|
16902
17380
|
switch (failure.kind) {
|
|
16903
17381
|
case "no-broker":
|
|
16904
17382
|
return {
|
|
@@ -16913,7 +17391,7 @@ function describeBrokerFailure(failure) {
|
|
|
16913
17391
|
};
|
|
16914
17392
|
case "unauthenticated":
|
|
16915
17393
|
return {
|
|
16916
|
-
message: "The registry broker rejected your session (expired or wrong audience).",
|
|
17394
|
+
message: "The registry broker rejected your session (expired or wrong audience)." + unverified,
|
|
16917
17395
|
hint: "mesh registry logout && mesh registry login",
|
|
16918
17396
|
fatal: false
|
|
16919
17397
|
};
|
|
@@ -16925,7 +17403,7 @@ function describeBrokerFailure(failure) {
|
|
|
16925
17403
|
message: `Your Mesh account${failure.email ? ` (${failure.email})` : ""} is not authorized to read ${CA_NAMESPACE} packages.` + (failure.requiredRoles?.length ? `
|
|
16926
17404
|
Needs one of these Zitadel roles: ${failure.requiredRoles.join(", ")}` : "") + (failure.requestUrl ? `
|
|
16927
17405
|
Request access: ${failure.requestUrl}` : "\n Ask a Mesh platform admin to grant your account registry access.") + `
|
|
16928
|
-
${NOT_AUTHORIZED_RERUN}
|
|
17406
|
+
${NOT_AUTHORIZED_RERUN}` + unverified,
|
|
16929
17407
|
fatal: true
|
|
16930
17408
|
};
|
|
16931
17409
|
case "unavailable":
|
|
@@ -16988,7 +17466,10 @@ async function tryBrokerLogin(opts) {
|
|
|
16988
17466
|
}
|
|
16989
17467
|
}
|
|
16990
17468
|
if (result.ok) return { grant: result.grant };
|
|
16991
|
-
const { message, hint, fatal } = describeBrokerFailure(
|
|
17469
|
+
const { message, hint, fatal } = describeBrokerFailure(
|
|
17470
|
+
result.failure,
|
|
17471
|
+
registryAccountState(readCredentials(session.context)?.idToken)
|
|
17472
|
+
);
|
|
16992
17473
|
if (fatal) {
|
|
16993
17474
|
throw new RegistryNotAuthorizedError(message);
|
|
16994
17475
|
}
|
|
@@ -17010,7 +17491,7 @@ function shouldRepairUserNpmrc(opts) {
|
|
|
17010
17491
|
function repairUnscopedRegistry(npmrcPath2 = homeNpmrcPath()) {
|
|
17011
17492
|
let original;
|
|
17012
17493
|
try {
|
|
17013
|
-
original =
|
|
17494
|
+
original = fs30.readFileSync(npmrcPath2, "utf-8");
|
|
17014
17495
|
} catch {
|
|
17015
17496
|
return { removed: null };
|
|
17016
17497
|
}
|
|
@@ -17018,8 +17499,8 @@ function repairUnscopedRegistry(npmrcPath2 = homeNpmrcPath()) {
|
|
|
17018
17499
|
if (!removed) return { removed: null };
|
|
17019
17500
|
const backupPath = `${npmrcPath2}.bak-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}`;
|
|
17020
17501
|
try {
|
|
17021
|
-
|
|
17022
|
-
|
|
17502
|
+
fs30.writeFileSync(backupPath, original, "utf-8");
|
|
17503
|
+
fs30.writeFileSync(npmrcPath2, content, "utf-8");
|
|
17023
17504
|
} catch (err) {
|
|
17024
17505
|
return { removed: null, error: err instanceof Error ? err.message : String(err) };
|
|
17025
17506
|
}
|
|
@@ -17031,9 +17512,9 @@ function renderRegistrySessionLine(session, now = Date.now()) {
|
|
|
17031
17512
|
}
|
|
17032
17513
|
async function checkExistingToken() {
|
|
17033
17514
|
const npmrcPath2 = homeNpmrcPath();
|
|
17034
|
-
if (!
|
|
17515
|
+
if (!fs30.existsSync(npmrcPath2)) return { valid: false, state: "missing", reason: "no ~/.npmrc" };
|
|
17035
17516
|
const hijackLine = findUnscopedCodeArtifactRegistry(
|
|
17036
|
-
|
|
17517
|
+
fs30.readFileSync(npmrcPath2, "utf-8")
|
|
17037
17518
|
) ?? void 0;
|
|
17038
17519
|
const probe = await probeRegistryToken({ npmrcPath: npmrcPath2 });
|
|
17039
17520
|
if (probe.state === "missing") {
|
|
@@ -17086,9 +17567,9 @@ async function runRegistryStatus(opts) {
|
|
|
17086
17567
|
logInfo(renderRegistrySessionLine(opts?.session ?? readRegistrySession()));
|
|
17087
17568
|
const projectRoot = findProjectRoot2();
|
|
17088
17569
|
if (projectRoot) {
|
|
17089
|
-
const npmrcPath2 =
|
|
17090
|
-
if (
|
|
17091
|
-
const content =
|
|
17570
|
+
const npmrcPath2 = path37.join(projectRoot, ".npmrc");
|
|
17571
|
+
if (fs30.existsSync(npmrcPath2)) {
|
|
17572
|
+
const content = fs30.readFileSync(npmrcPath2, "utf-8");
|
|
17092
17573
|
if (content.includes(`${CA_NAMESPACE}:registry=`)) {
|
|
17093
17574
|
logSuccess(`Project .npmrc configured (${npmrcPath2})`);
|
|
17094
17575
|
} else {
|
|
@@ -17122,7 +17603,7 @@ region = us-east-2`,
|
|
|
17122
17603
|
{ remediation: { command: `aws configure sso --profile ${profile}` } }
|
|
17123
17604
|
);
|
|
17124
17605
|
}
|
|
17125
|
-
const probe = () =>
|
|
17606
|
+
const probe = () => execFileSync26("aws", ["sts", "get-caller-identity", "--profile", profile], {
|
|
17126
17607
|
stdio: ["ignore", "pipe", "pipe"],
|
|
17127
17608
|
timeout: 2e4
|
|
17128
17609
|
});
|
|
@@ -17133,7 +17614,7 @@ region = us-east-2`,
|
|
|
17133
17614
|
} catch {
|
|
17134
17615
|
logInfo(`AWS SSO session for profile '${profile}' is stale \u2014 opening browser login\u2026`);
|
|
17135
17616
|
}
|
|
17136
|
-
|
|
17617
|
+
execFileSync26("aws", ["sso", "login", "--profile", profile], { stdio: "inherit" });
|
|
17137
17618
|
try {
|
|
17138
17619
|
probe();
|
|
17139
17620
|
} catch (err) {
|
|
@@ -17289,7 +17770,9 @@ To install packages you do not need AWS at all \u2014 drop the flag:
|
|
|
17289
17770
|
}
|
|
17290
17771
|
}
|
|
17291
17772
|
function registerRegistryCommands(program2) {
|
|
17292
|
-
const registry = program2.command("registry").description(
|
|
17773
|
+
const registry = program2.command("registry").description(
|
|
17774
|
+
"PACKAGE access: the sign-in that makes @mesh-tech/* installable. One global identity \u2014 no AWS account, no platform, no `<tenant>.<env>` context. (`mesh login <tenant>.<env>` is the other one; that is for deploying to or operating a platform.)"
|
|
17775
|
+
);
|
|
17293
17776
|
registry.command("login").description(
|
|
17294
17777
|
"Sign in to the Mesh package registry (browser, or device code with --device) and configure ~/.npmrc + the project .npmrc. Needs no platform context and no AWS account."
|
|
17295
17778
|
).argument(
|
|
@@ -17347,28 +17830,39 @@ __export(wizard_exports, {
|
|
|
17347
17830
|
INIT_REPO_FIX: () => INIT_REPO_FIX,
|
|
17348
17831
|
INTERRUPTED_EXIT_CODE: () => INTERRUPTED_EXIT_CODE,
|
|
17349
17832
|
NO_SESSION_NO_TTY_MESSAGE: () => NO_SESSION_NO_TTY_MESSAGE,
|
|
17833
|
+
PLATFORM_SETUP_LABEL: () => PLATFORM_SETUP_LABEL,
|
|
17834
|
+
REGISTRY_WHY: () => REGISTRY_WHY,
|
|
17350
17835
|
TENANT_REQUIRED_MESSAGE: () => TENANT_REQUIRED_MESSAGE,
|
|
17351
17836
|
WIZARD_STEPS: () => WIZARD_STEPS,
|
|
17352
17837
|
classifyRepo: () => classifyRepo,
|
|
17353
17838
|
describeMode: () => describeMode,
|
|
17839
|
+
isNewPlatform: () => isNewPlatform,
|
|
17840
|
+
newPlatformStep: () => newPlatformStep,
|
|
17841
|
+
platformSetupFlags: () => platformSetupFlags,
|
|
17354
17842
|
registryStep: () => registryStep,
|
|
17355
17843
|
renderWizardSummary: () => renderWizardSummary,
|
|
17356
17844
|
repoStep: () => repoStep,
|
|
17357
17845
|
resolveAnswers: () => resolveAnswers,
|
|
17358
17846
|
runInitWizard: () => runInitWizard,
|
|
17359
17847
|
runWizardSteps: () => runWizardSteps,
|
|
17848
|
+
skillsStep: () => skillsStep,
|
|
17360
17849
|
stepBlocks: () => stepBlocks,
|
|
17361
17850
|
wizardExitCode: () => wizardExitCode,
|
|
17362
17851
|
wizardNextCommands: () => wizardNextCommands
|
|
17363
17852
|
});
|
|
17364
|
-
import * as
|
|
17365
|
-
import * as
|
|
17366
|
-
import { execFileSync as
|
|
17853
|
+
import * as fs31 from "fs";
|
|
17854
|
+
import * as path38 from "path";
|
|
17855
|
+
import { execFileSync as execFileSync27 } from "child_process";
|
|
17367
17856
|
import chalk4 from "chalk";
|
|
17857
|
+
function isNewPlatform(mode) {
|
|
17858
|
+
return typeof mode === "object" && "newPlatform" in mode;
|
|
17859
|
+
}
|
|
17368
17860
|
function wizardExitCode(steps) {
|
|
17369
17861
|
return steps.some((s) => s.status === "fail") ? 1 : 0;
|
|
17370
17862
|
}
|
|
17371
17863
|
function wizardNextCommands(mode, steps = []) {
|
|
17864
|
+
const owned = steps.find((s) => s.next?.length)?.next;
|
|
17865
|
+
if (owned) return owned;
|
|
17372
17866
|
const repo = steps.find((s) => s.name === STEP_LABELS.repo);
|
|
17373
17867
|
const first = repo && repo.status !== "pass" && repo.fix ? repo.fix : "mesh create-app";
|
|
17374
17868
|
return mode === "local" ? [first] : [first, "mesh deploy up # from the app directory, once it exists"];
|
|
@@ -17377,7 +17871,9 @@ function stepBlocks(result) {
|
|
|
17377
17871
|
return result.status === "fail" && result.blocking !== false;
|
|
17378
17872
|
}
|
|
17379
17873
|
function describeMode(mode) {
|
|
17380
|
-
|
|
17874
|
+
if (mode === "local") return "local only";
|
|
17875
|
+
if (isNewPlatform(mode)) return `a new platform (${mode.newPlatform.env ?? PLATFORM_SETUP_DEFAULTS.env})`;
|
|
17876
|
+
return `deployed platform (${mode.platform})`;
|
|
17381
17877
|
}
|
|
17382
17878
|
function renderWizardSummary(outcome, opts = {}) {
|
|
17383
17879
|
const paint = opts.color === false ? { green: (s) => s, red: (s) => s, yellow: (s) => s, dim: (s) => s, cyan: (s) => s } : chalk4;
|
|
@@ -17403,10 +17899,12 @@ function resolveAnswers(opts, recorded, interactive) {
|
|
|
17403
17899
|
open.push("tenant");
|
|
17404
17900
|
}
|
|
17405
17901
|
let mode;
|
|
17406
|
-
|
|
17407
|
-
|
|
17902
|
+
const chosen = [opts.local && "--local", opts.platform && "--platform", opts.newPlatform && "--new-platform"].filter(Boolean);
|
|
17903
|
+
if (chosen.length > 1) {
|
|
17904
|
+
throw new MeshCliError(`${chosen.join(", ")} are mutually exclusive \u2014 a tenant runs in exactly one of those three places.`);
|
|
17408
17905
|
}
|
|
17409
|
-
if (opts.
|
|
17906
|
+
if (opts.newPlatform) mode = { newPlatform: platformSetupFlags(opts) };
|
|
17907
|
+
else if (opts.platform) mode = { platform: opts.platform };
|
|
17410
17908
|
else if (opts.local) mode = "local";
|
|
17411
17909
|
else if (opts.yes || !interactive) {
|
|
17412
17910
|
mode = recorded && recorded.platform !== "local" ? { platform: recorded.platform } : "local";
|
|
@@ -17415,22 +17913,34 @@ function resolveAnswers(opts, recorded, interactive) {
|
|
|
17415
17913
|
}
|
|
17416
17914
|
return { tenant, mode, open };
|
|
17417
17915
|
}
|
|
17916
|
+
function platformSetupFlags(opts) {
|
|
17917
|
+
return {
|
|
17918
|
+
env: typeof opts.newPlatform === "string" ? opts.newPlatform : void 0,
|
|
17919
|
+
region: opts.region,
|
|
17920
|
+
domain: opts.domain,
|
|
17921
|
+
profile: opts.profile,
|
|
17922
|
+
stateBucket: opts.stateBucket,
|
|
17923
|
+
createStateBucket: opts.createStateBucket,
|
|
17924
|
+
dir: opts.dir,
|
|
17925
|
+
yes: opts.yes
|
|
17926
|
+
};
|
|
17927
|
+
}
|
|
17418
17928
|
function classifyRepo(cwd) {
|
|
17419
17929
|
if (isInsidePlatformMonorepo(cwd)) return { kind: "platform-monorepo", root: resolveTargetRoot(cwd) };
|
|
17420
17930
|
if (shouldBootstrapAppsRepo(cwd)) return { kind: "empty-apps-repo", root: cwd };
|
|
17421
17931
|
const root = resolveTargetRoot(cwd);
|
|
17422
|
-
const inRepo =
|
|
17932
|
+
const inRepo = fs31.existsSync(path38.join(root, ".git"));
|
|
17423
17933
|
if (!inRepo) {
|
|
17424
|
-
const hasContent =
|
|
17934
|
+
const hasContent = fs31.readdirSync(cwd).some((entry) => !entry.startsWith("."));
|
|
17425
17935
|
return { kind: hasContent ? "no-repo" : "empty-dir", root: cwd };
|
|
17426
17936
|
}
|
|
17427
|
-
const hasWorkspace =
|
|
17428
|
-
const hasApps =
|
|
17937
|
+
const hasWorkspace = fs31.existsSync(path38.join(root, "pnpm-workspace.yaml")) || fs31.existsSync(path38.join(root, "pnpm-lock.yaml"));
|
|
17938
|
+
const hasApps = fs31.existsSync(path38.join(root, "apps")) || fs31.existsSync(path38.join(root, "tenants"));
|
|
17429
17939
|
if (hasWorkspace || hasApps) return { kind: "apps-repo", root };
|
|
17430
17940
|
return { kind: "other-repo", root };
|
|
17431
17941
|
}
|
|
17432
17942
|
function gitInit(dir) {
|
|
17433
|
-
|
|
17943
|
+
execFileSync27("git", ["init", "-q"], { cwd: dir, stdio: ["ignore", "ignore", "pipe"] });
|
|
17434
17944
|
}
|
|
17435
17945
|
async function runWizardSteps(ctx, steps) {
|
|
17436
17946
|
const results = [];
|
|
@@ -17488,12 +17998,15 @@ async function runInitWizard(opts, deps = {}) {
|
|
|
17488
17998
|
message: "Where does this tenant run?",
|
|
17489
17999
|
choices: [
|
|
17490
18000
|
{ name: "Local only (mesh start / mesh dev \u2014 no deployed platform yet)", value: "local" },
|
|
17491
|
-
{ name: `A deployed Mesh platform (I have a ${tenant}.<env> context)`, value: "deployed" }
|
|
18001
|
+
{ name: `A deployed Mesh platform (I have a ${tenant}.<env> context)`, value: "deployed" },
|
|
18002
|
+
{ name: "Nothing yet \u2014 set up a new Mesh platform in my AWS account", value: "new" }
|
|
17492
18003
|
],
|
|
17493
18004
|
default: recorded && recorded.platform !== "local" ? "deployed" : "local"
|
|
17494
18005
|
});
|
|
17495
18006
|
if (choice === "local") mode = "local";
|
|
17496
|
-
else {
|
|
18007
|
+
else if (choice === "new") {
|
|
18008
|
+
mode = { newPlatform: platformSetupFlags({ ...opts, newPlatform: true }) };
|
|
18009
|
+
} else {
|
|
17497
18010
|
const env = await prompts.input({
|
|
17498
18011
|
message: "Environment",
|
|
17499
18012
|
default: recorded && recorded.platform !== "local" ? recorded.platform : "dev",
|
|
@@ -17506,7 +18019,7 @@ async function runInitWizard(opts, deps = {}) {
|
|
|
17506
18019
|
cwd,
|
|
17507
18020
|
tenant,
|
|
17508
18021
|
mode,
|
|
17509
|
-
context: mode === "local" ? null : `${tenant}.${mode.platform}`,
|
|
18022
|
+
context: mode === "local" || isNewPlatform(mode) ? null : `${tenant}.${mode.platform}`,
|
|
17510
18023
|
device: opts.device,
|
|
17511
18024
|
profile: opts.profile,
|
|
17512
18025
|
interactive,
|
|
@@ -17519,7 +18032,7 @@ async function runInitWizard(opts, deps = {}) {
|
|
|
17519
18032
|
logInfo(`Setting up tenant '${tenant}' (${describeMode(mode)})\u2026`);
|
|
17520
18033
|
const steps = await runWizardSteps(ctx, deps.steps ?? WIZARD_STEPS);
|
|
17521
18034
|
const { kind, root } = classifyRepo(cwd);
|
|
17522
|
-
if (!opts.skipRepo && (kind === "apps-repo" || kind === "empty-apps-repo")) {
|
|
18035
|
+
if (!opts.skipRepo && !isNewPlatform(mode) && (kind === "apps-repo" || kind === "empty-apps-repo")) {
|
|
17523
18036
|
const written = writeMeshJson(root, { tenant, platform: mode === "local" ? "local" : mode.platform });
|
|
17524
18037
|
logInfo(`Recorded tenant + mode in ${written}`);
|
|
17525
18038
|
}
|
|
@@ -17545,7 +18058,7 @@ async function runInitWizard(opts, deps = {}) {
|
|
|
17545
18058
|
console.log = origLog;
|
|
17546
18059
|
}
|
|
17547
18060
|
}
|
|
17548
|
-
var NO_SESSION_NO_TTY_MESSAGE, TENANT_REQUIRED_MESSAGE, INTERRUPTED_EXIT_CODE, STEP_LABELS, registryStep, platformStep, INIT_REPO_FIX, repoStep, skillsStep, WIZARD_STEPS;
|
|
18061
|
+
var NO_SESSION_NO_TTY_MESSAGE, TENANT_REQUIRED_MESSAGE, REGISTRY_WHY, INTERRUPTED_EXIT_CODE, STEP_LABELS, PLATFORM_SETUP_LABEL, registryStep, newPlatformStep, platformStep, INIT_REPO_FIX, repoStep, skillsStep, WIZARD_STEPS;
|
|
17549
18062
|
var init_wizard = __esm({
|
|
17550
18063
|
"libs/mesh-cli/src/commands/init/wizard.ts"() {
|
|
17551
18064
|
"use strict";
|
|
@@ -17559,8 +18072,10 @@ var init_wizard = __esm({
|
|
|
17559
18072
|
init_create_app();
|
|
17560
18073
|
init_skills();
|
|
17561
18074
|
init_dev_local();
|
|
18075
|
+
init_platform_setup();
|
|
17562
18076
|
NO_SESSION_NO_TTY_MESSAGE = "No registry session and no terminal to sign in with.\n On a headless box: mesh registry login --device (one-time, prints a code)\n In CI with AWS creds: mesh registry login --ci";
|
|
17563
18077
|
TENANT_REQUIRED_MESSAGE = "--tenant is required (no TTY for interactive mode)";
|
|
18078
|
+
REGISTRY_WHY = "Package access \u2014 a sign-in to the Mesh package REGISTRY, which is what makes `@mesh-tech/*` installable.\n It is one global identity: no AWS account, no platform, no <tenant>.<env> context.\n (`mesh login <tenant>.<env>` is the other sign-in \u2014 that one is only for deploying to, or operating, a platform.)";
|
|
17564
18079
|
INTERRUPTED_EXIT_CODE = 130;
|
|
17565
18080
|
STEP_LABELS = {
|
|
17566
18081
|
registry: "Registry access",
|
|
@@ -17568,6 +18083,7 @@ var init_wizard = __esm({
|
|
|
17568
18083
|
repo: "Repo",
|
|
17569
18084
|
skills: "Agent skills"
|
|
17570
18085
|
};
|
|
18086
|
+
PLATFORM_SETUP_LABEL = "Platform setup";
|
|
17571
18087
|
registryStep = {
|
|
17572
18088
|
name: "registry",
|
|
17573
18089
|
async run(ctx) {
|
|
@@ -17590,6 +18106,7 @@ var init_wizard = __esm({
|
|
|
17590
18106
|
return { name, status: "fail", detail: NO_SESSION_NO_TTY_MESSAGE.split("\n")[0], fix: "mesh registry login --device", blocking: true };
|
|
17591
18107
|
}
|
|
17592
18108
|
try {
|
|
18109
|
+
logInfo(REGISTRY_WHY);
|
|
17593
18110
|
const login2 = ctx.seams?.runRegistryLogin ?? (async (opts) => {
|
|
17594
18111
|
const { runRegistryLogin: runRegistryLogin2 } = await Promise.resolve().then(() => (init_registry(), registry_exports));
|
|
17595
18112
|
await runRegistryLogin2(void 0, opts);
|
|
@@ -17618,10 +18135,46 @@ var init_wizard = __esm({
|
|
|
17618
18135
|
return { name, status: "pass", detail: `Registry access OK (${session ? `signed in as ${session.email}` : "token written"})` };
|
|
17619
18136
|
}
|
|
17620
18137
|
};
|
|
18138
|
+
newPlatformStep = {
|
|
18139
|
+
name: "platform",
|
|
18140
|
+
async run(ctx) {
|
|
18141
|
+
const name = PLATFORM_SETUP_LABEL;
|
|
18142
|
+
if (!isNewPlatform(ctx.mode)) return { name, status: "skip", detail: "no new platform requested" };
|
|
18143
|
+
let setup;
|
|
18144
|
+
try {
|
|
18145
|
+
setup = await resolvePlatformSetup(ctx.tenant, { ...ctx.mode.newPlatform, yes: ctx.yes }, {
|
|
18146
|
+
prompts: ctx.prompts,
|
|
18147
|
+
interactive: ctx.interactive,
|
|
18148
|
+
cwd: ctx.cwd
|
|
18149
|
+
});
|
|
18150
|
+
} catch (err) {
|
|
18151
|
+
return {
|
|
18152
|
+
name,
|
|
18153
|
+
status: "fail",
|
|
18154
|
+
detail: err.message.split("\n")[0],
|
|
18155
|
+
fix: `mesh init --tenant ${ctx.tenant} --new-platform --domain <your-domain>`
|
|
18156
|
+
};
|
|
18157
|
+
}
|
|
18158
|
+
try {
|
|
18159
|
+
await (ctx.seams?.scaffoldPlatform ?? scaffoldPlatformRepo)(setup);
|
|
18160
|
+
} catch (err) {
|
|
18161
|
+
return { name, status: "fail", detail: `platform repo not scaffolded: ${err.message.split("\n")[0]}`, fix: `mesh init platform ${ctx.tenant} --dir <empty-dir>` };
|
|
18162
|
+
}
|
|
18163
|
+
const bucket = await ensureStateBucket(setup, ctx.seams?.stateBucket);
|
|
18164
|
+
const next = renderPlatformNextSteps(setup, { bucket, relativeDir: path38.relative(ctx.cwd, setup.dir) || "." });
|
|
18165
|
+
return {
|
|
18166
|
+
name,
|
|
18167
|
+
status: setup.createStateBucket && !bucket.present ? "warn" : "pass",
|
|
18168
|
+
detail: `platform repo scaffolded at ${setup.dir} (${setup.tenant}.${setup.env}, ${setup.region}, zone ${setup.env}.${setup.domain})` + (setup.createStateBucket ? ` \u2014 state bucket: ${bucket.detail}` : ` \u2014 state bucket ${setup.stateBucket} not created (see Next)`),
|
|
18169
|
+
next
|
|
18170
|
+
};
|
|
18171
|
+
}
|
|
18172
|
+
};
|
|
17621
18173
|
platformStep = {
|
|
17622
18174
|
name: "platform",
|
|
17623
18175
|
async run(ctx) {
|
|
17624
18176
|
const name = STEP_LABELS.platform;
|
|
18177
|
+
if (isNewPlatform(ctx.mode)) return newPlatformStep.run(ctx);
|
|
17625
18178
|
if (!ctx.context) return { name, status: "skip", detail: "local only \u2014 no platform to sign in to" };
|
|
17626
18179
|
const context = ctx.context;
|
|
17627
18180
|
const cached = readCredentials(context);
|
|
@@ -17655,6 +18208,9 @@ var init_wizard = __esm({
|
|
|
17655
18208
|
async run(ctx) {
|
|
17656
18209
|
const name = STEP_LABELS.repo;
|
|
17657
18210
|
if (ctx.skipRepo) return { name, status: "skip", detail: "--skip-repo" };
|
|
18211
|
+
if (isNewPlatform(ctx.mode)) {
|
|
18212
|
+
return { name, status: "skip", detail: "setting up a platform \u2014 the apps repo comes after the first deploy (see Next)" };
|
|
18213
|
+
}
|
|
17658
18214
|
const { kind, root } = classifyRepo(ctx.cwd);
|
|
17659
18215
|
switch (kind) {
|
|
17660
18216
|
case "platform-monorepo":
|
|
@@ -17703,8 +18259,8 @@ var init_wizard = __esm({
|
|
|
17703
18259
|
}
|
|
17704
18260
|
case "apps-repo": {
|
|
17705
18261
|
const missing = [];
|
|
17706
|
-
if (!
|
|
17707
|
-
if (!
|
|
18262
|
+
if (!fs31.existsSync(path38.join(root, "package.json"))) missing.push("package.json");
|
|
18263
|
+
if (!fs31.existsSync(path38.join(root, "pnpm-workspace.yaml")) && !fs31.existsSync(path38.join(root, "pnpm-lock.yaml"))) {
|
|
17708
18264
|
missing.push("pnpm-workspace.yaml");
|
|
17709
18265
|
}
|
|
17710
18266
|
if (missing.length) {
|
|
@@ -17720,6 +18276,9 @@ var init_wizard = __esm({
|
|
|
17720
18276
|
async run(ctx) {
|
|
17721
18277
|
const name = STEP_LABELS.skills;
|
|
17722
18278
|
if (ctx.skipRepo) return { name, status: "skip", detail: "--skip-repo" };
|
|
18279
|
+
if (isNewPlatform(ctx.mode)) {
|
|
18280
|
+
return { name, status: "skip", detail: "synced into the new platform repo by the scaffold" };
|
|
18281
|
+
}
|
|
17723
18282
|
const { kind, root } = classifyRepo(ctx.cwd);
|
|
17724
18283
|
if (kind !== "apps-repo" && kind !== "empty-apps-repo") {
|
|
17725
18284
|
return { name, status: "skip", detail: "no apps repo to install skills into" };
|
|
@@ -17738,8 +18297,15 @@ var init_wizard = __esm({
|
|
|
17738
18297
|
|
|
17739
18298
|
// libs/mesh-cli/src/commands/init.ts
|
|
17740
18299
|
import chalk5 from "chalk";
|
|
17741
|
-
import * as
|
|
17742
|
-
import * as
|
|
18300
|
+
import * as fs32 from "fs";
|
|
18301
|
+
import * as path39 from "path";
|
|
18302
|
+
async function setupPrompts() {
|
|
18303
|
+
const [{ default: input2 }, { default: confirm }] = await Promise.all([
|
|
18304
|
+
import("@inquirer/input"),
|
|
18305
|
+
import("@inquirer/confirm")
|
|
18306
|
+
]);
|
|
18307
|
+
return { input: (args) => input2(args), confirm: (args) => confirm(args) };
|
|
18308
|
+
}
|
|
17743
18309
|
function isLocalContext(context) {
|
|
17744
18310
|
return context === LOGIN_CONTEXT;
|
|
17745
18311
|
}
|
|
@@ -17758,7 +18324,7 @@ async function probeHubTenants(hubUrl, accessToken) {
|
|
|
17758
18324
|
}
|
|
17759
18325
|
function readHomeNpmrc() {
|
|
17760
18326
|
try {
|
|
17761
|
-
return
|
|
18327
|
+
return fs32.readFileSync(homeNpmrcPath(), "utf-8");
|
|
17762
18328
|
} catch {
|
|
17763
18329
|
return "";
|
|
17764
18330
|
}
|
|
@@ -17882,9 +18448,9 @@ async function runChecks2(args) {
|
|
|
17882
18448
|
if (local) {
|
|
17883
18449
|
checks.push({ name: "Deployer role", status: "pass", detail: "local context \u2014 no deployer role required" });
|
|
17884
18450
|
} else {
|
|
17885
|
-
const stackFiles =
|
|
18451
|
+
const stackFiles = fs32.existsSync(root) ? fs32.readdirSync(root).filter((f) => /^Pulumi\..*\.yaml$/.test(f)) : [];
|
|
17886
18452
|
const hasRole = stackFiles.some(
|
|
17887
|
-
(f) => /mesh:deployerRole/.test(
|
|
18453
|
+
(f) => /mesh:deployerRole/.test(fs32.readFileSync(path39.join(root, f), "utf-8"))
|
|
17888
18454
|
);
|
|
17889
18455
|
checks.push(
|
|
17890
18456
|
hasRole ? { name: "Deployer role", status: "pass", detail: "mesh:deployerRole configured in stack config" } : {
|
|
@@ -17896,8 +18462,8 @@ async function runChecks2(args) {
|
|
|
17896
18462
|
);
|
|
17897
18463
|
}
|
|
17898
18464
|
const shapeProblems = [];
|
|
17899
|
-
if (!
|
|
17900
|
-
const hasWorkspace =
|
|
18465
|
+
if (!fs32.existsSync(path39.join(root, "package.json"))) shapeProblems.push("package.json");
|
|
18466
|
+
const hasWorkspace = fs32.existsSync(path39.join(root, "pnpm-workspace.yaml")) || fs32.existsSync(path39.join(root, "pnpm-lock.yaml"));
|
|
17901
18467
|
if (!hasWorkspace) shapeProblems.push("pnpm-workspace.yaml");
|
|
17902
18468
|
checks.push(
|
|
17903
18469
|
shapeProblems.length === 0 ? { name: "Repo shape", status: "pass", detail: "pnpm workspace layout present" } : {
|
|
@@ -17920,8 +18486,11 @@ async function runChecks2(args) {
|
|
|
17920
18486
|
}
|
|
17921
18487
|
function registerInitCommands(program2) {
|
|
17922
18488
|
const init = program2.command("init").enablePositionalOptions().description(
|
|
17923
|
-
"Set up this machine and repo for a tenant \u2014 a guided wizard with no subcommand
|
|
17924
|
-
).option("--tenant <name>", "app tenant this repo belongs to (prompted when omitted on a TTY)").option("--local", "local only \u2014 mesh start / mesh dev, no deployed platform (the default)").option("--platform <env>", "a deployed platform's environment; signs in to
|
|
18489
|
+
"Set up this machine and repo for a tenant \u2014 a guided wizard with no subcommand. It starts from nothing: it asks where the tenant runs, and 'nowhere yet' is one of the answers (it scaffolds you a platform). Then package-registry sign-in, repo, skills. `init platform` is the same scaffold on its own; `init app-tenant` is the apps-repo doctor"
|
|
18490
|
+
).option("--tenant <name>", "app tenant this repo belongs to (prompted when omitted on a TTY)").option("--local", "local only \u2014 mesh start / mesh dev, no deployed platform (the default)").option("--platform <env>", "a deployed platform's environment; signs in to `<tenant>.<env>`").option(
|
|
18491
|
+
"--new-platform [env]",
|
|
18492
|
+
`nothing deployed yet \u2014 scaffold a tenant platform repo for \`<tenant>.<env>\` (default env: ${PLATFORM_SETUP_DEFAULTS.env}) and print its deploy sequence`
|
|
18493
|
+
).option("--region <region>", `--new-platform: AWS region (default: ${PLATFORM_SETUP_DEFAULTS.region})`).option("--domain <domain>", "--new-platform: base public domain you control; the core stack creates the zone {env}.{domain}. No default \u2014 a placeholder scaffolds a zone that can never validate").option("--state-bucket <bucket>", "--new-platform: Pulumi state S3 bucket (default: {tenant}-mesh-platform-pulumi-state)").option("--create-state-bucket", "--new-platform: create the state bucket (versioned) if it does not exist").option("--no-create-state-bucket", "--new-platform: never create the state bucket; print the aws command instead").option("--dir <path>", "--new-platform: where to scaffold the platform repo (default: ./{tenant}-mesh-platform)").option("--device", "device-code sign-in for the registry and platform steps (headless / SSH)").option("--profile <aws-profile>", "AWS profile this machine uses: read the registry without the broker (deployers only), and the profile a --new-platform stack config references").option("--skip-repo", "do not bootstrap, check or record anything in the current repo", false).option("--yes", "accept every default and never prompt", false).option("--json", "one JSON document on stdout ({tenant, mode, steps, next}); human output on stderr", false).action(
|
|
17925
18494
|
async (opts) => {
|
|
17926
18495
|
const { runInitWizard: runInitWizard2 } = await Promise.resolve().then(() => (init_wizard(), wizard_exports));
|
|
17927
18496
|
const code = await runInitWizard2(opts);
|
|
@@ -17929,42 +18498,34 @@ function registerInitCommands(program2) {
|
|
|
17929
18498
|
}
|
|
17930
18499
|
);
|
|
17931
18500
|
init.command("platform <tenant>").description(
|
|
17932
|
-
"Scaffold a tenant PLATFORM repo (core + platform Pulumi layers on @mesh-tech/infra-components) that deploys with `mesh deploy` unmodified \u2014 the proven mesh-sandbox shape, annotated."
|
|
17933
|
-
).option("--env <env>",
|
|
17934
|
-
|
|
17935
|
-
|
|
17936
|
-
|
|
17937
|
-
|
|
17938
|
-
|
|
17939
|
-
|
|
17940
|
-
|
|
18501
|
+
"Scaffold a tenant PLATFORM repo (core + platform Pulumi layers on @mesh-tech/infra-components) that deploys with `mesh deploy` unmodified \u2014 the proven mesh-sandbox shape, annotated. On a terminal it ASKS for the values that differ per install (env, region, domain, state bucket, AWS profile), each pre-filled with its default; every answer is also a flag."
|
|
18502
|
+
).option("--env <env>", `environment the first stack targets (default: ${PLATFORM_SETUP_DEFAULTS.env})`).option("--region <region>", `AWS region (default: ${PLATFORM_SETUP_DEFAULTS.region})`).option(
|
|
18503
|
+
"--domain <domain>",
|
|
18504
|
+
"base public domain you control \u2014 the core stack creates the Route 53 zone {env}.{domain} and waits on its NS delegation. Required: it has no default, because a placeholder scaffolds a zone that can never validate"
|
|
18505
|
+
).option("--profile <profile>", `AWS profile the stack config references (default: ${PLATFORM_SETUP_DEFAULTS.profile})`).option("--state-bucket <bucket>", "Pulumi state S3 bucket (default: {tenant}-mesh-platform-pulumi-state)").option("--create-state-bucket", "create the state bucket (versioned) if it does not exist \u2014 prompted on a terminal").option("--no-create-state-bucket", "never create the state bucket; print the aws command instead").option("--dir <path>", "target directory (default: ./{tenant}-mesh-platform)").option("--yes", "accept every default and never prompt", false).action(
|
|
18506
|
+
async (tenant, opts) => {
|
|
18507
|
+
if (!/^[a-z][a-z0-9-]*$/.test(tenant)) {
|
|
18508
|
+
throw new MeshCliError(`Invalid tenant name '${tenant}' (lowercase alphanumeric + dashes).`);
|
|
18509
|
+
}
|
|
18510
|
+
const interactive = Boolean(process.stdin.isTTY && process.stdout.isTTY);
|
|
18511
|
+
const { resolvePlatformSetup: resolvePlatformSetup2, scaffoldPlatformRepo: scaffoldPlatformRepo2, ensureStateBucket: ensureStateBucket2, renderPlatformNextSteps: renderPlatformNextSteps2 } = await Promise.resolve().then(() => (init_platform_setup(), platform_setup_exports));
|
|
18512
|
+
const setup = await resolvePlatformSetup2(tenant, opts, {
|
|
18513
|
+
prompts: interactive && !opts.yes ? await setupPrompts() : void 0,
|
|
18514
|
+
interactive
|
|
17941
18515
|
});
|
|
18516
|
+
await scaffoldPlatformRepo2(setup);
|
|
18517
|
+
logInfo(`Platform repo scaffolded at ${setup.dir}`);
|
|
18518
|
+
const bucket = await ensureStateBucket2(setup);
|
|
18519
|
+
if (bucket.detail !== "not requested") logInfo(`Pulumi state bucket: ${bucket.detail}`);
|
|
18520
|
+
console.log("");
|
|
18521
|
+
console.log("Next steps:");
|
|
18522
|
+
for (const line of renderPlatformNextSteps2(setup, { bucket, relativeDir: path39.relative(process.cwd(), setup.dir) || "." })) {
|
|
18523
|
+
console.log(line ? ` ${line}` : "");
|
|
18524
|
+
}
|
|
18525
|
+
console.log("");
|
|
18526
|
+
logInfo(`Review the annotated stack configs (Pulumi.${tenant}-${setup.env}.yaml) before the first deploy.`);
|
|
17942
18527
|
}
|
|
17943
|
-
|
|
17944
|
-
const { findPackageRoot: findPackageRoot2 } = await Promise.resolve().then(() => (init_stack(), stack_exports));
|
|
17945
|
-
const templateDir = path38.join(findPackageRoot2(), "templates", "platform-repo");
|
|
17946
|
-
const context = {
|
|
17947
|
-
tenant,
|
|
17948
|
-
env: opts.env,
|
|
17949
|
-
region: opts.region,
|
|
17950
|
-
domain: opts.domain,
|
|
17951
|
-
profile: opts.profile,
|
|
17952
|
-
stateBucket: opts.stateBucket ?? `${tenant}-mesh-platform-pulumi-state`
|
|
17953
|
-
};
|
|
17954
|
-
fs31.mkdirSync(targetDir, { recursive: true });
|
|
17955
|
-
copyTemplate2(templateDir, targetDir, context);
|
|
17956
|
-
syncSkills(targetDir);
|
|
17957
|
-
logInfo(`Platform repo scaffolded at ${targetDir}`);
|
|
17958
|
-
console.log("");
|
|
17959
|
-
console.log("Next steps:");
|
|
17960
|
-
console.log(` cd ${path38.relative(process.cwd(), targetDir) || "."}`);
|
|
17961
|
-
console.log(` mesh login ${tenant}.${opts.env} # or an operator context with access`);
|
|
17962
|
-
console.log(" pnpm install");
|
|
17963
|
-
console.log(" cd core && mesh deploy up");
|
|
17964
|
-
console.log(" cd ../platform && mesh deploy up");
|
|
17965
|
-
console.log("");
|
|
17966
|
-
logInfo(`Review the annotated stack configs (Pulumi.${tenant}-${opts.env}.yaml) before the first deploy.`);
|
|
17967
|
-
});
|
|
18528
|
+
);
|
|
17968
18529
|
init.command("app-tenant").description(
|
|
17969
18530
|
"Check (and with --fix, repair) everything an app-tenant repo needs: auth, registry, tenant registration, repo shape, agent skills. Re-run any time \u2014 it is the doctor."
|
|
17970
18531
|
).option("--tenant <name>", "app tenant (default: mesh:tenant from Pulumi config, else 'local')").option("--context <ctx>", "login context to check against (default: 'local' when the local platform is targeted)", LOGIN_CONTEXT).option("--hub-url <url>", "platform Hub API base URL", LOCAL_HUB_API).option("--fix", "apply developer-scope fixes (registry token, skills sync)", false).option("--profile <profile>", "AWS SSO profile --fix logs into CodeArtifact with (e.g. mesh-dev)").option("--json", "machine-readable output", false).action(async (opts) => {
|
|
@@ -18020,15 +18581,16 @@ var init_init = __esm({
|
|
|
18020
18581
|
init_skills();
|
|
18021
18582
|
init_dev_local();
|
|
18022
18583
|
init_seed_zitadel();
|
|
18584
|
+
init_platform_setup();
|
|
18023
18585
|
LOCAL_HUB_API = "http://localhost:4568";
|
|
18024
18586
|
}
|
|
18025
18587
|
});
|
|
18026
18588
|
|
|
18027
18589
|
// libs/mesh-cli/src/commands/install-shim.ts
|
|
18028
|
-
import { execFileSync as
|
|
18029
|
-
import * as
|
|
18590
|
+
import { execFileSync as execFileSync28 } from "child_process";
|
|
18591
|
+
import * as fs33 from "fs";
|
|
18030
18592
|
import * as os10 from "os";
|
|
18031
|
-
import * as
|
|
18593
|
+
import * as path40 from "path";
|
|
18032
18594
|
function shimScript() {
|
|
18033
18595
|
return `#!/bin/sh
|
|
18034
18596
|
# mesh \u2014 canonical launcher shim (installed by \`mesh install-shim\`).
|
|
@@ -18101,25 +18663,25 @@ exit 1
|
|
|
18101
18663
|
`;
|
|
18102
18664
|
}
|
|
18103
18665
|
function defaultShimDir() {
|
|
18104
|
-
return
|
|
18666
|
+
return path40.join(os10.homedir(), ".local", "bin");
|
|
18105
18667
|
}
|
|
18106
18668
|
function dirOnPath(dir, pathEnv) {
|
|
18107
18669
|
if (!pathEnv) return false;
|
|
18108
|
-
return pathEnv.split(
|
|
18670
|
+
return pathEnv.split(path40.delimiter).some((p) => p === dir);
|
|
18109
18671
|
}
|
|
18110
18672
|
function registerInstallShimCommand(program2) {
|
|
18111
18673
|
program2.command("install-shim").description("Install a build-free `mesh` shim on your PATH so bare `mesh` runs the local source").option("--dir <dir>", "Directory to install into (must be on your PATH)", defaultShimDir()).option("-f, --force", "Overwrite an existing file at the target path", false).action((opts) => {
|
|
18112
|
-
const dir =
|
|
18113
|
-
const target =
|
|
18114
|
-
if (
|
|
18674
|
+
const dir = path40.resolve(opts.dir);
|
|
18675
|
+
const target = path40.join(dir, "mesh");
|
|
18676
|
+
if (fs33.existsSync(target) && !opts.force) {
|
|
18115
18677
|
logWarn(`A file already exists at ${target}.`);
|
|
18116
18678
|
logInfo("Re-run with --force to overwrite it, or pass --dir <other-path-on-PATH>.");
|
|
18117
18679
|
return;
|
|
18118
18680
|
}
|
|
18119
18681
|
try {
|
|
18120
|
-
|
|
18121
|
-
|
|
18122
|
-
|
|
18682
|
+
fs33.mkdirSync(dir, { recursive: true });
|
|
18683
|
+
fs33.writeFileSync(target, shimScript(), { mode: 493 });
|
|
18684
|
+
fs33.chmodSync(target, 493);
|
|
18123
18685
|
} catch (err) {
|
|
18124
18686
|
logError(`Could not write the shim to ${target}: ${err.message}`);
|
|
18125
18687
|
process.exitCode = 1;
|
|
@@ -18132,13 +18694,13 @@ function registerInstallShimCommand(program2) {
|
|
|
18132
18694
|
} else {
|
|
18133
18695
|
logInfo("Bare `mesh <cmd>` now runs the local source of whatever workspace you're in.");
|
|
18134
18696
|
}
|
|
18135
|
-
const diagPath =
|
|
18697
|
+
const diagPath = path40.join(os10.tmpdir(), "mesh-shim-selftest.log");
|
|
18136
18698
|
let stdout = "";
|
|
18137
18699
|
let stderr = "";
|
|
18138
18700
|
let status = 0;
|
|
18139
18701
|
let ok = true;
|
|
18140
18702
|
try {
|
|
18141
|
-
stdout =
|
|
18703
|
+
stdout = execFileSync28(target, ["--help"], {
|
|
18142
18704
|
cwd: process.cwd(),
|
|
18143
18705
|
encoding: "utf8",
|
|
18144
18706
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -18165,7 +18727,7 @@ function registerInstallShimCommand(program2) {
|
|
|
18165
18727
|
""
|
|
18166
18728
|
].join("\n");
|
|
18167
18729
|
try {
|
|
18168
|
-
|
|
18730
|
+
fs33.writeFileSync(diagPath, diag);
|
|
18169
18731
|
} catch {
|
|
18170
18732
|
}
|
|
18171
18733
|
if (ok && /Commands:|Usage:/.test(stdout)) {
|
|
@@ -18185,17 +18747,17 @@ var init_install_shim = __esm({
|
|
|
18185
18747
|
});
|
|
18186
18748
|
|
|
18187
18749
|
// libs/mesh-cli/src/commands/local/hub-local.ts
|
|
18188
|
-
import { execFile as execFile3, execFileSync as
|
|
18189
|
-
import * as
|
|
18750
|
+
import { execFile as execFile3, execFileSync as execFileSync29 } from "child_process";
|
|
18751
|
+
import * as fs34 from "fs";
|
|
18190
18752
|
import * as os11 from "os";
|
|
18191
|
-
import * as
|
|
18753
|
+
import * as path41 from "path";
|
|
18192
18754
|
import { promisify as promisify2 } from "util";
|
|
18193
18755
|
function cacheDir() {
|
|
18194
18756
|
return meshCacheDir("hub-local");
|
|
18195
18757
|
}
|
|
18196
18758
|
function npmrcPath() {
|
|
18197
|
-
const p =
|
|
18198
|
-
if (!
|
|
18759
|
+
const p = path41.join(os11.homedir(), ".npmrc");
|
|
18760
|
+
if (!fs34.existsSync(p) || !fs34.readFileSync(p, "utf-8").includes("codeartifact")) {
|
|
18199
18761
|
throw new MeshCliError(
|
|
18200
18762
|
"The local Hub builds from the published @mesh-tech/hub tarball, which needs registry auth.",
|
|
18201
18763
|
{ remediation: { command: REGISTRY_LOGIN_FIX } }
|
|
@@ -18205,7 +18767,7 @@ function npmrcPath() {
|
|
|
18205
18767
|
}
|
|
18206
18768
|
function imageExists(tag) {
|
|
18207
18769
|
try {
|
|
18208
|
-
|
|
18770
|
+
execFileSync29("docker", ["image", "inspect", tag], { stdio: ["ignore", "pipe", "pipe"] });
|
|
18209
18771
|
return true;
|
|
18210
18772
|
} catch {
|
|
18211
18773
|
return false;
|
|
@@ -18214,22 +18776,22 @@ function imageExists(tag) {
|
|
|
18214
18776
|
function ensureHubAuthImage() {
|
|
18215
18777
|
if (imageExists(HUB_AUTH_IMAGE)) return;
|
|
18216
18778
|
logInfo(`Building ${HUB_AUTH_IMAGE} (Hub auth proxy)\u2026`);
|
|
18217
|
-
const hubStackDir =
|
|
18218
|
-
|
|
18779
|
+
const hubStackDir = path41.join(findPackageRoot(), "stack", "hub");
|
|
18780
|
+
execFileSync29(
|
|
18219
18781
|
"docker",
|
|
18220
|
-
["build", "-f",
|
|
18782
|
+
["build", "-f", path41.join(hubStackDir, "Dockerfile.auth"), "-t", HUB_AUTH_IMAGE, hubStackDir],
|
|
18221
18783
|
{ stdio: ["ignore", "inherit", "inherit"], env: { ...process.env, DOCKER_BUILDKIT: "1" } }
|
|
18222
18784
|
);
|
|
18223
18785
|
logSuccess(`Built ${HUB_AUTH_IMAGE}`);
|
|
18224
18786
|
}
|
|
18225
18787
|
function hasRegistryAuth() {
|
|
18226
|
-
const p =
|
|
18227
|
-
return
|
|
18788
|
+
const p = path41.join(os11.homedir(), ".npmrc");
|
|
18789
|
+
return fs34.existsSync(p) && fs34.readFileSync(p, "utf-8").includes("codeartifact");
|
|
18228
18790
|
}
|
|
18229
18791
|
function localHubVersion() {
|
|
18230
18792
|
const versions = HUB_IMAGES.map((name) => {
|
|
18231
18793
|
try {
|
|
18232
|
-
const out =
|
|
18794
|
+
const out = execFileSync29("docker", ["images", name, "--format", "{{.Tag}}"], {
|
|
18233
18795
|
encoding: "utf-8",
|
|
18234
18796
|
stdio: ["ignore", "pipe", "pipe"]
|
|
18235
18797
|
});
|
|
@@ -18255,10 +18817,10 @@ async function fetchTarball() {
|
|
|
18255
18817
|
{ encoding: "utf-8" }
|
|
18256
18818
|
);
|
|
18257
18819
|
const info = JSON.parse(stdout)[0];
|
|
18258
|
-
return { tarball:
|
|
18820
|
+
return { tarball: path41.join(dir, info.filename), version: info.version };
|
|
18259
18821
|
} catch (err) {
|
|
18260
18822
|
const authFailure = isNpmAuthError(err);
|
|
18261
|
-
const cached =
|
|
18823
|
+
const cached = fs34.readdirSync(dir).filter((f) => f.startsWith("mesh-tech-hub-") && f.endsWith(".tgz")).sort((a, b) => a.localeCompare(b, void 0, { numeric: true })).at(-1);
|
|
18262
18824
|
if (!cached) {
|
|
18263
18825
|
if (authFailure) {
|
|
18264
18826
|
throw new MeshCliError(
|
|
@@ -18276,7 +18838,7 @@ async function fetchTarball() {
|
|
|
18276
18838
|
} else {
|
|
18277
18839
|
logInfo(`Registry unavailable \u2014 using the cached hub tarball v${version}`);
|
|
18278
18840
|
}
|
|
18279
|
-
return { tarball:
|
|
18841
|
+
return { tarball: path41.join(dir, cached), version };
|
|
18280
18842
|
} finally {
|
|
18281
18843
|
heartbeat.stop();
|
|
18282
18844
|
}
|
|
@@ -18305,13 +18867,13 @@ async function ensureHubImages() {
|
|
|
18305
18867
|
logInfo(`Local Hub images ready (v${version})`);
|
|
18306
18868
|
return version;
|
|
18307
18869
|
}
|
|
18308
|
-
const context =
|
|
18309
|
-
|
|
18310
|
-
|
|
18311
|
-
|
|
18870
|
+
const context = path41.join(cacheDir(), `context-${version}`);
|
|
18871
|
+
fs34.rmSync(context, { recursive: true, force: true });
|
|
18872
|
+
fs34.mkdirSync(context, { recursive: true });
|
|
18873
|
+
execFileSync29("tar", ["-xzf", tarball, "-C", context, "--strip-components", "1"], {
|
|
18312
18874
|
stdio: ["ignore", "pipe", "pipe"]
|
|
18313
18875
|
});
|
|
18314
|
-
const hubStackDir =
|
|
18876
|
+
const hubStackDir = path41.join(findPackageRoot(), "stack", "hub");
|
|
18315
18877
|
for (const [name, dockerfile] of [
|
|
18316
18878
|
["mesh-local-hub-api", "Dockerfile.api"],
|
|
18317
18879
|
["mesh-local-hub-ui", "Dockerfile.ui"]
|
|
@@ -18319,12 +18881,12 @@ async function ensureHubImages() {
|
|
|
18319
18881
|
const tag = `${name}:${version}`;
|
|
18320
18882
|
if (imageExists(tag)) continue;
|
|
18321
18883
|
logInfo(`Building ${tag} from the published tarball\u2026`);
|
|
18322
|
-
|
|
18884
|
+
execFileSync29(
|
|
18323
18885
|
"docker",
|
|
18324
18886
|
[
|
|
18325
18887
|
"build",
|
|
18326
18888
|
"-f",
|
|
18327
|
-
|
|
18889
|
+
path41.join(hubStackDir, dockerfile),
|
|
18328
18890
|
"-t",
|
|
18329
18891
|
tag,
|
|
18330
18892
|
"--secret",
|
|
@@ -18342,29 +18904,29 @@ function tarballNameForImageVersion(version) {
|
|
|
18342
18904
|
return `mesh-tech-hub-${version.replace(/-r\d+$/, "")}.tgz`;
|
|
18343
18905
|
}
|
|
18344
18906
|
function readHubCompiledAuthz(version) {
|
|
18345
|
-
const context =
|
|
18346
|
-
const file =
|
|
18347
|
-
if (!
|
|
18907
|
+
const context = path41.join(cacheDir(), `context-${version}`);
|
|
18908
|
+
const file = path41.join(context, HUB_COMPILED_AUTHZ_REL);
|
|
18909
|
+
if (!fs34.existsSync(file)) {
|
|
18348
18910
|
const tarball = tarballNameForImageVersion(version);
|
|
18349
|
-
if (!tarball || !
|
|
18350
|
-
|
|
18911
|
+
if (!tarball || !fs34.existsSync(path41.join(cacheDir(), tarball))) return null;
|
|
18912
|
+
fs34.mkdirSync(context, { recursive: true });
|
|
18351
18913
|
try {
|
|
18352
|
-
|
|
18914
|
+
execFileSync29(
|
|
18353
18915
|
"tar",
|
|
18354
|
-
["-xzf",
|
|
18916
|
+
["-xzf", path41.join(cacheDir(), tarball), "-C", context, "--strip-components", "1", `package/${HUB_COMPILED_AUTHZ_REL}`],
|
|
18355
18917
|
{ stdio: ["ignore", "pipe", "pipe"] }
|
|
18356
18918
|
);
|
|
18357
18919
|
} catch {
|
|
18358
18920
|
return null;
|
|
18359
18921
|
}
|
|
18360
|
-
if (!
|
|
18922
|
+
if (!fs34.existsSync(file)) return null;
|
|
18361
18923
|
}
|
|
18362
|
-
const parsed = JSON.parse(
|
|
18924
|
+
const parsed = JSON.parse(fs34.readFileSync(file, "utf-8"));
|
|
18363
18925
|
if (typeof parsed.zed !== "string" || !parsed.metadata || typeof parsed.metadata !== "object") return null;
|
|
18364
18926
|
return { zed: parsed.zed, metadata: parsed.metadata };
|
|
18365
18927
|
}
|
|
18366
18928
|
function readWorkspaceCatalog(repoRoot2) {
|
|
18367
|
-
const text =
|
|
18929
|
+
const text = fs34.readFileSync(path41.join(repoRoot2, "pnpm-workspace.yaml"), "utf-8");
|
|
18368
18930
|
const marker = "\ncatalog:\n";
|
|
18369
18931
|
const at = text.indexOf(marker);
|
|
18370
18932
|
if (at === -1) return {};
|
|
@@ -18379,11 +18941,11 @@ function readWorkspaceCatalog(repoRoot2) {
|
|
|
18379
18941
|
function normalizeHubManifests(contextDir, catalog) {
|
|
18380
18942
|
const DEP_FIELDS = ["dependencies", "optionalDependencies", "peerDependencies"];
|
|
18381
18943
|
const touched = [];
|
|
18382
|
-
for (const entry of
|
|
18944
|
+
for (const entry of fs34.readdirSync(contextDir, { withFileTypes: true })) {
|
|
18383
18945
|
if (!entry.isDirectory()) continue;
|
|
18384
|
-
const manifest =
|
|
18385
|
-
if (!
|
|
18386
|
-
const pkg = JSON.parse(
|
|
18946
|
+
const manifest = path41.join(contextDir, entry.name, "package.json");
|
|
18947
|
+
if (!fs34.existsSync(manifest)) continue;
|
|
18948
|
+
const pkg = JSON.parse(fs34.readFileSync(manifest, "utf-8"));
|
|
18387
18949
|
let changed = false;
|
|
18388
18950
|
if (pkg.devDependencies) {
|
|
18389
18951
|
delete pkg.devDependencies;
|
|
@@ -18412,7 +18974,7 @@ function normalizeHubManifests(contextDir, catalog) {
|
|
|
18412
18974
|
}
|
|
18413
18975
|
}
|
|
18414
18976
|
if (changed) {
|
|
18415
|
-
|
|
18977
|
+
fs34.writeFileSync(manifest, `${JSON.stringify(pkg, null, 2)}
|
|
18416
18978
|
`);
|
|
18417
18979
|
touched.push(`${entry.name}/package.json`);
|
|
18418
18980
|
}
|
|
@@ -18420,15 +18982,15 @@ function normalizeHubManifests(contextDir, catalog) {
|
|
|
18420
18982
|
return touched;
|
|
18421
18983
|
}
|
|
18422
18984
|
async function buildHubImagesFromSource(repoRoot2) {
|
|
18423
|
-
const hubDir =
|
|
18424
|
-
if (!
|
|
18985
|
+
const hubDir = path41.join(repoRoot2, "apps", "hub");
|
|
18986
|
+
if (!fs34.existsSync(path41.join(hubDir, "package.json"))) {
|
|
18425
18987
|
throw new MeshCliError(
|
|
18426
18988
|
`--hub-from-source needs a mesh-platform checkout; no apps/hub under ${repoRoot2}.`,
|
|
18427
18989
|
{ remediation: { command: "Run mesh start from a mesh-platform checkout, or drop --hub-from-source" } }
|
|
18428
18990
|
);
|
|
18429
18991
|
}
|
|
18430
18992
|
const npmrc = npmrcPath();
|
|
18431
|
-
const version = `${JSON.parse(
|
|
18993
|
+
const version = `${JSON.parse(fs34.readFileSync(path41.join(hubDir, "package.json"), "utf-8")).version}-src`;
|
|
18432
18994
|
const build = startHeartbeat("building apps/hub (api + ui) from source");
|
|
18433
18995
|
try {
|
|
18434
18996
|
for (const pkg of ["@mesh-tech/hub-api", "@mesh-tech/hub-ui"]) {
|
|
@@ -18437,27 +18999,27 @@ async function buildHubImagesFromSource(repoRoot2) {
|
|
|
18437
18999
|
} finally {
|
|
18438
19000
|
build.stop();
|
|
18439
19001
|
}
|
|
18440
|
-
const context =
|
|
18441
|
-
|
|
18442
|
-
|
|
19002
|
+
const context = path41.join(cacheDir(), `context-${version}`);
|
|
19003
|
+
fs34.rmSync(context, { recursive: true, force: true });
|
|
19004
|
+
fs34.mkdirSync(context, { recursive: true });
|
|
18443
19005
|
await execFileAsync2("pnpm", ["pack", "--pack-destination", context], { cwd: hubDir, maxBuffer: 64 * 1024 * 1024 });
|
|
18444
|
-
const tarball =
|
|
19006
|
+
const tarball = fs34.readdirSync(context).find((f) => f.endsWith(".tgz"));
|
|
18445
19007
|
if (!tarball) throw new MeshCliError("pnpm pack produced no tarball for apps/hub");
|
|
18446
|
-
|
|
19008
|
+
execFileSync29("tar", ["-xzf", path41.join(context, tarball), "-C", context, "--strip-components", "1"], {
|
|
18447
19009
|
stdio: ["ignore", "pipe", "pipe"]
|
|
18448
19010
|
});
|
|
18449
19011
|
const rewritten = normalizeHubManifests(context, readWorkspaceCatalog(repoRoot2));
|
|
18450
19012
|
if (rewritten.length > 0) logInfo(`Normalized ${rewritten.length} manifest(s) for npm: ${rewritten.join(", ")}`);
|
|
18451
|
-
const hubStackDir =
|
|
19013
|
+
const hubStackDir = path41.join(findPackageRoot(), "stack", "hub");
|
|
18452
19014
|
for (const [name, dockerfile] of [
|
|
18453
19015
|
["mesh-local-hub-api", "Dockerfile.api"],
|
|
18454
19016
|
["mesh-local-hub-ui", "Dockerfile.ui"]
|
|
18455
19017
|
]) {
|
|
18456
19018
|
const tag = `${name}:${version}`;
|
|
18457
19019
|
logInfo(`Building ${tag} from source\u2026`);
|
|
18458
|
-
|
|
19020
|
+
execFileSync29(
|
|
18459
19021
|
"docker",
|
|
18460
|
-
["build", "-f",
|
|
19022
|
+
["build", "-f", path41.join(hubStackDir, dockerfile), "-t", tag, "--secret", `id=npmrc,src=${npmrc}`, context],
|
|
18461
19023
|
{ stdio: ["ignore", "inherit", "inherit"], env: { ...process.env, DOCKER_BUILDKIT: "1" } }
|
|
18462
19024
|
);
|
|
18463
19025
|
logSuccess(`Built ${tag}`);
|
|
@@ -18542,8 +19104,8 @@ var init_seed_hub_catalog = __esm({
|
|
|
18542
19104
|
});
|
|
18543
19105
|
|
|
18544
19106
|
// libs/mesh-cli/src/commands/local/index.ts
|
|
18545
|
-
import * as
|
|
18546
|
-
import * as
|
|
19107
|
+
import * as fs35 from "fs";
|
|
19108
|
+
import * as path42 from "path";
|
|
18547
19109
|
import chalk6 from "chalk";
|
|
18548
19110
|
function crashError(crashed) {
|
|
18549
19111
|
return new MeshCliError(
|
|
@@ -18586,7 +19148,7 @@ async function waitForStack(io = {
|
|
|
18586
19148
|
);
|
|
18587
19149
|
}
|
|
18588
19150
|
logInfo(`Waiting for ${pending.map((e) => e.label).join(", ")} \u2026`);
|
|
18589
|
-
await new Promise((
|
|
19151
|
+
await new Promise((resolve20) => setTimeout(resolve20, WAIT_POLL_MS));
|
|
18590
19152
|
}
|
|
18591
19153
|
}
|
|
18592
19154
|
function printEndpoints(hubRunning) {
|
|
@@ -18665,12 +19227,12 @@ function portMoveHints() {
|
|
|
18665
19227
|
}
|
|
18666
19228
|
function repoRootForHubSource() {
|
|
18667
19229
|
for (let dir = process.cwd(); ; ) {
|
|
18668
|
-
if (
|
|
18669
|
-
const parent =
|
|
19230
|
+
if (fs35.existsSync(path42.join(dir, "apps", "hub", "package.json"))) return dir;
|
|
19231
|
+
const parent = path42.dirname(dir);
|
|
18670
19232
|
if (parent === dir) break;
|
|
18671
19233
|
dir = parent;
|
|
18672
19234
|
}
|
|
18673
|
-
return
|
|
19235
|
+
return path42.resolve(findPackageRoot(), "..", "..");
|
|
18674
19236
|
}
|
|
18675
19237
|
function registerLocalCommands(program2) {
|
|
18676
19238
|
program2.command("start").description("Start the full-local Mesh platform (Docker only \u2014 no AWS, no VPN)").option("--no-seed", "skip first-boot seeding (tenant registry, artifacts bucket)").option("--no-hub", "start without the Hub (API + UI)").option(
|
|
@@ -18817,7 +19379,7 @@ Re-running start from here may recreate shared containers with this checkout's c
|
|
|
18817
19379
|
overlayStarted: true
|
|
18818
19380
|
});
|
|
18819
19381
|
if (crashed.length > 0) throw crashError(crashed);
|
|
18820
|
-
await new Promise((
|
|
19382
|
+
await new Promise((resolve20) => setTimeout(resolve20, 2e3));
|
|
18821
19383
|
up = await probeEndpoint(endpoint);
|
|
18822
19384
|
}
|
|
18823
19385
|
if (!up) {
|
|
@@ -19112,6 +19674,18 @@ var init_src3 = __esm({
|
|
|
19112
19674
|
}
|
|
19113
19675
|
});
|
|
19114
19676
|
|
|
19677
|
+
// libs/mesh-cli/src/commands/secrets/context.ts
|
|
19678
|
+
function resolveSecretsContext(opts, stackOpt = resolveStackOption(opts)) {
|
|
19679
|
+
return detectContext(stackOpt, { tenant: opts.tenant, stack: stackOpt });
|
|
19680
|
+
}
|
|
19681
|
+
var init_context2 = __esm({
|
|
19682
|
+
"libs/mesh-cli/src/commands/secrets/context.ts"() {
|
|
19683
|
+
"use strict";
|
|
19684
|
+
init_context();
|
|
19685
|
+
init_stack_flag();
|
|
19686
|
+
}
|
|
19687
|
+
});
|
|
19688
|
+
|
|
19115
19689
|
// libs/mesh-cli/src/commands/secrets/set.ts
|
|
19116
19690
|
import {
|
|
19117
19691
|
SSMClient as SSMClient2,
|
|
@@ -19260,7 +19834,7 @@ function displaySummary(fields, values, depth = 0) {
|
|
|
19260
19834
|
}
|
|
19261
19835
|
async function setCommand(servicePath, opts) {
|
|
19262
19836
|
const stackOpt = resolveStackOption(opts);
|
|
19263
|
-
const context =
|
|
19837
|
+
const context = resolveSecretsContext(opts, stackOpt);
|
|
19264
19838
|
const region = opts.region ?? process.env.AWS_REGION ?? "us-east-2";
|
|
19265
19839
|
await ensureAwsCredentialsForStack(stackOpt ?? context.stage);
|
|
19266
19840
|
const ssmClient = new SSMClient2({ region });
|
|
@@ -19498,9 +20072,9 @@ var init_set = __esm({
|
|
|
19498
20072
|
"libs/mesh-cli/src/commands/secrets/set.ts"() {
|
|
19499
20073
|
"use strict";
|
|
19500
20074
|
init_src3();
|
|
19501
|
-
init_context();
|
|
19502
20075
|
init_log();
|
|
19503
20076
|
init_stack_flag();
|
|
20077
|
+
init_context2();
|
|
19504
20078
|
init_pulumi();
|
|
19505
20079
|
init_aws_auth();
|
|
19506
20080
|
}
|
|
@@ -19538,7 +20112,7 @@ function indexAlreadyMatches(missing, stale, indexExists, malformed) {
|
|
|
19538
20112
|
}
|
|
19539
20113
|
async function reindexCommand(servicePath, opts) {
|
|
19540
20114
|
const stackOpt = resolveStackOption(opts);
|
|
19541
|
-
const context =
|
|
20115
|
+
const context = resolveSecretsContext(opts, stackOpt);
|
|
19542
20116
|
const region = opts.region ?? process.env.AWS_REGION ?? "us-east-2";
|
|
19543
20117
|
await ensureAwsCredentialsForStack(stackOpt ?? context.stage);
|
|
19544
20118
|
const ssmClient = new SSMClient3({ region });
|
|
@@ -19634,9 +20208,9 @@ var init_reindex = __esm({
|
|
|
19634
20208
|
"libs/mesh-cli/src/commands/secrets/reindex.ts"() {
|
|
19635
20209
|
"use strict";
|
|
19636
20210
|
init_src3();
|
|
19637
|
-
init_context();
|
|
19638
20211
|
init_log();
|
|
19639
20212
|
init_stack_flag();
|
|
20213
|
+
init_context2();
|
|
19640
20214
|
init_set();
|
|
19641
20215
|
}
|
|
19642
20216
|
});
|
|
@@ -19840,7 +20414,7 @@ async function migrateService(smClient, meta, schema, opts) {
|
|
|
19840
20414
|
return { migrated, skipped, errors };
|
|
19841
20415
|
}
|
|
19842
20416
|
async function migrateConfigCommand(opts) {
|
|
19843
|
-
const context =
|
|
20417
|
+
const context = resolveSecretsContext(opts);
|
|
19844
20418
|
const region = opts.region ?? process.env.AWS_REGION ?? "us-east-2";
|
|
19845
20419
|
const ssmClient = new SSMClient4({ region });
|
|
19846
20420
|
const smClient = new SecretsManagerClient8({ region });
|
|
@@ -19879,9 +20453,8 @@ Migrating: ${name}`);
|
|
|
19879
20453
|
var init_migrate_config = __esm({
|
|
19880
20454
|
"libs/mesh-cli/src/commands/secrets/migrate-config.ts"() {
|
|
19881
20455
|
"use strict";
|
|
19882
|
-
init_context();
|
|
19883
20456
|
init_log();
|
|
19884
|
-
|
|
20457
|
+
init_context2();
|
|
19885
20458
|
}
|
|
19886
20459
|
});
|
|
19887
20460
|
|
|
@@ -19893,15 +20466,15 @@ function registerSecretsCommands(program2) {
|
|
|
19893
20466
|
const args = cmd.args;
|
|
19894
20467
|
await execCommand2(args);
|
|
19895
20468
|
});
|
|
19896
|
-
secrets.command("set [service]").description("Set credentials for an external service (e.g., mesh secrets set external/symitar)").option("--key <key>", "Instance key for multi-instance services (e.g., FI ID)").option("--all", "Prompt for all fields including optional ones").option("--json <json>", "Non-interactive: provide values as JSON").option("--stack <stack>", "Pulumi stack name (override detection)").addOption(new Option2("--stage <stack>", "Deprecated alias for --stack").hideHelp()).option("--region <region>", "AWS region (default: us-east-2)").action(async (service, opts) => {
|
|
20469
|
+
secrets.command("set [service]").description("Set credentials for an external service (e.g., mesh secrets set external/symitar)").option("--key <key>", "Instance key for multi-instance services (e.g., FI ID)").option("--all", "Prompt for all fields including optional ones").option("--json <json>", "Non-interactive: provide values as JSON").option("-t, --tenant <tenant>", "Tenant to address (override detection)").option("--stack <stack>", "Pulumi stack name (override detection)").addOption(new Option2("--stage <stack>", "Deprecated alias for --stack").hideHelp()).option("--region <region>", "AWS region (default: us-east-2)").action(async (service, opts) => {
|
|
19897
20470
|
await setCommand(service, opts);
|
|
19898
20471
|
});
|
|
19899
20472
|
secrets.command("reindex [service]").description(
|
|
19900
20473
|
"Rebuild a multi-instance external service's instance index (the {prefix}/.index secret listInstances() reads) from the per-key secrets actually in Secrets Manager \u2014 the repair for an instance secret created or deleted out of band"
|
|
19901
|
-
).option("--dry-run", "Show what would change without writing").option("--stack <stack>", "Pulumi stack name (override detection)").addOption(new Option2("--stage <stack>", "Deprecated alias for --stack").hideHelp()).option("--region <region>", "AWS region (default: us-east-2)").action(async (service, opts) => {
|
|
20474
|
+
).option("--dry-run", "Show what would change without writing").option("-t, --tenant <tenant>", "Tenant to address (override detection)").option("--stack <stack>", "Pulumi stack name (override detection)").addOption(new Option2("--stage <stack>", "Deprecated alias for --stack").hideHelp()).option("--region <region>", "AWS region (default: us-east-2)").action(async (service, opts) => {
|
|
19902
20475
|
await reindexCommand(service, opts);
|
|
19903
20476
|
});
|
|
19904
|
-
secrets.command("migrate-config").description("Backfill .config mirror secrets for all external services").option("--force", "Overwrite existing .config mirrors").option("--dry-run", "Show what would be written without writing").option("--stack <stack>", "Pulumi stack name (override detection)").addOption(new Option2("--stage <stack>", "Deprecated alias for --stack").hideHelp()).option("--region <region>", "AWS region (default: us-east-2)").action(async (opts) => {
|
|
20477
|
+
secrets.command("migrate-config").description("Backfill .config mirror secrets for all external services").option("--force", "Overwrite existing .config mirrors").option("--dry-run", "Show what would be written without writing").option("-t, --tenant <tenant>", "Tenant to address (override detection)").option("--stack <stack>", "Pulumi stack name (override detection)").addOption(new Option2("--stage <stack>", "Deprecated alias for --stack").hideHelp()).option("--region <region>", "AWS region (default: us-east-2)").action(async (opts) => {
|
|
19905
20478
|
await migrateConfigCommand(opts);
|
|
19906
20479
|
});
|
|
19907
20480
|
}
|
|
@@ -19919,7 +20492,7 @@ var init_secrets = __esm({
|
|
|
19919
20492
|
import { createHash as createHash4 } from "node:crypto";
|
|
19920
20493
|
import { createReadStream } from "node:fs";
|
|
19921
20494
|
import { readdir, readFile as readFile2, stat } from "node:fs/promises";
|
|
19922
|
-
import { join as
|
|
20495
|
+
import { join as join38, relative as relative11, resolve as resolve17, sep as sep6 } from "node:path";
|
|
19923
20496
|
async function describeSiteHttpError(res, target, site) {
|
|
19924
20497
|
if (res.status === 403) {
|
|
19925
20498
|
return `not allowed to publish "${site}" \u2014 you are authenticated but lack the grant. Need one of: sites:${site}:publish, sites:*:publish, or studio-admin.`;
|
|
@@ -19941,9 +20514,9 @@ async function walk(root) {
|
|
|
19941
20514
|
async function visit(dir) {
|
|
19942
20515
|
for (const entry of await readdir(dir, { withFileTypes: true })) {
|
|
19943
20516
|
if (SKIP_ENTRIES.has(entry.name)) continue;
|
|
19944
|
-
const absolute =
|
|
20517
|
+
const absolute = join38(dir, entry.name);
|
|
19945
20518
|
if (entry.isSymbolicLink()) {
|
|
19946
|
-
logWarn(`skipping symlink ${
|
|
20519
|
+
logWarn(`skipping symlink ${relative11(root, absolute)}`);
|
|
19947
20520
|
continue;
|
|
19948
20521
|
}
|
|
19949
20522
|
if (entry.isDirectory()) {
|
|
@@ -19955,7 +20528,7 @@ async function walk(root) {
|
|
|
19955
20528
|
out.push({
|
|
19956
20529
|
// Manifest paths are always forward-slashed, whatever the host OS uses,
|
|
19957
20530
|
// so a site published from Windows resolves identically to one from a Mac.
|
|
19958
|
-
path:
|
|
20531
|
+
path: relative11(root, absolute).split(sep6).join("/"),
|
|
19959
20532
|
absolute,
|
|
19960
20533
|
hash: await hashFile(absolute),
|
|
19961
20534
|
size: info.size
|
|
@@ -19984,7 +20557,7 @@ async function pooled(items, limit, worker) {
|
|
|
19984
20557
|
await Promise.all(runners);
|
|
19985
20558
|
}
|
|
19986
20559
|
async function publish(name, dir, opts, target) {
|
|
19987
|
-
const root =
|
|
20560
|
+
const root = resolve17(dir);
|
|
19988
20561
|
const info = await stat(root).catch(() => null);
|
|
19989
20562
|
if (!info?.isDirectory()) throw new Error(`${root} is not a directory`);
|
|
19990
20563
|
logInfo(`Hashing ${root}\u2026`);
|
|
@@ -20136,23 +20709,23 @@ var init_site = __esm({
|
|
|
20136
20709
|
});
|
|
20137
20710
|
|
|
20138
20711
|
// libs/mesh-cli/src/commands/stack.ts
|
|
20139
|
-
import { execFileSync as
|
|
20140
|
-
import * as
|
|
20141
|
-
import * as
|
|
20712
|
+
import { execFileSync as execFileSync30 } from "child_process";
|
|
20713
|
+
import * as path43 from "path";
|
|
20714
|
+
import * as fs36 from "fs";
|
|
20142
20715
|
import { parse as parseYaml5 } from "yaml";
|
|
20143
20716
|
function readTopLevelYamlKey(appRoot, stack, key) {
|
|
20144
|
-
const configFile =
|
|
20145
|
-
if (!
|
|
20146
|
-
const content =
|
|
20717
|
+
const configFile = path43.join(appRoot, `Pulumi.${stack}.yaml`);
|
|
20718
|
+
if (!fs36.existsSync(configFile)) return null;
|
|
20719
|
+
const content = fs36.readFileSync(configFile, "utf-8");
|
|
20147
20720
|
const pattern = new RegExp(`^${key}:\\s*(.+)$`, "m");
|
|
20148
20721
|
const match = content.match(pattern);
|
|
20149
20722
|
if (!match) return null;
|
|
20150
20723
|
return match[1].trim().replace(/^["']|["']$/g, "");
|
|
20151
20724
|
}
|
|
20152
20725
|
function readConfigBlockKey(appRoot, stack, key) {
|
|
20153
|
-
const configFile =
|
|
20154
|
-
if (!
|
|
20155
|
-
const content =
|
|
20726
|
+
const configFile = path43.join(appRoot, `Pulumi.${stack}.yaml`);
|
|
20727
|
+
if (!fs36.existsSync(configFile)) return null;
|
|
20728
|
+
const content = fs36.readFileSync(configFile, "utf-8");
|
|
20156
20729
|
const pattern = new RegExp(`^\\s{2}${key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}:\\s*(.+)$`, "m");
|
|
20157
20730
|
const match = content.match(pattern);
|
|
20158
20731
|
if (!match) return null;
|
|
@@ -20212,11 +20785,11 @@ ${records}
|
|
|
20212
20785
|
}
|
|
20213
20786
|
}
|
|
20214
20787
|
function readBaseConfigFromYaml(appRoot, stack) {
|
|
20215
|
-
const file =
|
|
20216
|
-
if (!
|
|
20788
|
+
const file = path43.join(appRoot, `Pulumi.${stack}.yaml`);
|
|
20789
|
+
if (!fs36.existsSync(file)) return {};
|
|
20217
20790
|
let doc;
|
|
20218
20791
|
try {
|
|
20219
|
-
doc = parseYaml5(
|
|
20792
|
+
doc = parseYaml5(fs36.readFileSync(file, "utf-8"));
|
|
20220
20793
|
} catch {
|
|
20221
20794
|
return {};
|
|
20222
20795
|
}
|
|
@@ -20229,7 +20802,7 @@ function readBaseConfigFromYaml(appRoot, stack) {
|
|
|
20229
20802
|
}
|
|
20230
20803
|
function getGitHubUsername() {
|
|
20231
20804
|
try {
|
|
20232
|
-
const result =
|
|
20805
|
+
const result = execFileSync30("gh", ["api", "user", "--jq", ".login"], {
|
|
20233
20806
|
encoding: "utf-8",
|
|
20234
20807
|
stdio: ["pipe", "pipe", "pipe"]
|
|
20235
20808
|
});
|
|
@@ -20238,7 +20811,7 @@ function getGitHubUsername() {
|
|
|
20238
20811
|
} catch {
|
|
20239
20812
|
}
|
|
20240
20813
|
try {
|
|
20241
|
-
const result =
|
|
20814
|
+
const result = execFileSync30("git", ["config", "user.email"], {
|
|
20242
20815
|
encoding: "utf-8",
|
|
20243
20816
|
stdio: ["pipe", "pipe", "pipe"]
|
|
20244
20817
|
});
|
|
@@ -20293,7 +20866,7 @@ Specify which to base on: mesh stack init --from <stack>`
|
|
|
20293
20866
|
}
|
|
20294
20867
|
}
|
|
20295
20868
|
const baseConfigPath = `${appRoot}/Pulumi.${baseStack}.yaml`;
|
|
20296
|
-
if (!
|
|
20869
|
+
if (!fs36.existsSync(baseConfigPath)) {
|
|
20297
20870
|
logError(`Stack config not found: Pulumi.${baseStack}.yaml`);
|
|
20298
20871
|
process.exit(1);
|
|
20299
20872
|
}
|
|
@@ -20315,8 +20888,8 @@ Specify which to base on: mesh stack init --from <stack>`
|
|
|
20315
20888
|
);
|
|
20316
20889
|
}
|
|
20317
20890
|
}
|
|
20318
|
-
const newConfigPath =
|
|
20319
|
-
const configExists =
|
|
20891
|
+
const newConfigPath = path43.join(appRoot, `Pulumi.${newStack}.yaml`);
|
|
20892
|
+
const configExists = fs36.existsSync(newConfigPath);
|
|
20320
20893
|
const secretsProvider = readTopLevelYamlKey(appRoot, baseStack, "secretsprovider");
|
|
20321
20894
|
const credEnv = await resolvePulumiEnv({ appRoot, stack: baseStack });
|
|
20322
20895
|
const pulumiEnv = { ...process.env, ...credEnv };
|
|
@@ -20324,7 +20897,7 @@ Specify which to base on: mesh stack init --from <stack>`
|
|
|
20324
20897
|
if (!opts.adopt) {
|
|
20325
20898
|
let existing = [];
|
|
20326
20899
|
try {
|
|
20327
|
-
const raw =
|
|
20900
|
+
const raw = execFileSync30("pulumi", ["stack", "ls", "--json"], {
|
|
20328
20901
|
cwd: appRoot,
|
|
20329
20902
|
encoding: "utf-8",
|
|
20330
20903
|
env: pulumiEnv,
|
|
@@ -20348,7 +20921,7 @@ Specify which to base on: mesh stack init --from <stack>`
|
|
|
20348
20921
|
logInfo(`Using KMS secrets provider: ${secretsProvider}`);
|
|
20349
20922
|
}
|
|
20350
20923
|
try {
|
|
20351
|
-
|
|
20924
|
+
execFileSync30("pulumi", initArgs, {
|
|
20352
20925
|
cwd: appRoot,
|
|
20353
20926
|
env: pulumiEnv,
|
|
20354
20927
|
stdio: "inherit"
|
|
@@ -20365,7 +20938,7 @@ Specify which to base on: mesh stack init --from <stack>`
|
|
|
20365
20938
|
}
|
|
20366
20939
|
}
|
|
20367
20940
|
try {
|
|
20368
|
-
|
|
20941
|
+
execFileSync30("pulumi", ["stack", "select", newStack], {
|
|
20369
20942
|
cwd: appRoot,
|
|
20370
20943
|
env: pulumiEnv,
|
|
20371
20944
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -20375,7 +20948,7 @@ Specify which to base on: mesh stack init --from <stack>`
|
|
|
20375
20948
|
if (!configExists) {
|
|
20376
20949
|
let baseConfig = {};
|
|
20377
20950
|
try {
|
|
20378
|
-
const raw =
|
|
20951
|
+
const raw = execFileSync30(
|
|
20379
20952
|
"pulumi",
|
|
20380
20953
|
["config", "--json", "--stack", baseStack],
|
|
20381
20954
|
{ cwd: appRoot, env: pulumiEnv, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
|
|
@@ -20393,19 +20966,19 @@ Specify which to base on: mesh stack init --from <stack>`
|
|
|
20393
20966
|
if (key === "mesh:deploy") continue;
|
|
20394
20967
|
try {
|
|
20395
20968
|
if (entry.objectValue !== void 0) {
|
|
20396
|
-
|
|
20969
|
+
execFileSync30(
|
|
20397
20970
|
"pulumi",
|
|
20398
20971
|
["config", "set", key, JSON.stringify(entry.objectValue)],
|
|
20399
20972
|
{ cwd: appRoot, env: pulumiEnv, stdio: ["pipe", "pipe", "pipe"] }
|
|
20400
20973
|
);
|
|
20401
20974
|
} else if (entry.value === "true" || entry.value === "false") {
|
|
20402
|
-
|
|
20975
|
+
execFileSync30(
|
|
20403
20976
|
"pulumi",
|
|
20404
20977
|
["config", "set", "--type", "bool", key, entry.value],
|
|
20405
20978
|
{ cwd: appRoot, env: pulumiEnv, stdio: ["pipe", "pipe", "pipe"] }
|
|
20406
20979
|
);
|
|
20407
20980
|
} else {
|
|
20408
|
-
|
|
20981
|
+
execFileSync30(
|
|
20409
20982
|
"pulumi",
|
|
20410
20983
|
["config", "set", key, entry.value],
|
|
20411
20984
|
{ cwd: appRoot, env: pulumiEnv, stdio: ["pipe", "pipe", "pipe"] }
|
|
@@ -20418,7 +20991,7 @@ Specify which to base on: mesh stack init --from <stack>`
|
|
|
20418
20991
|
const baseEnv = readConfigBlockKey(appRoot, baseStack, "mesh:coreEnv") ?? (baseTenant && baseStack.startsWith(`${baseTenant}-`) ? baseStack.slice(baseTenant.length + 1) : baseStack);
|
|
20419
20992
|
const setCfg = (args) => {
|
|
20420
20993
|
try {
|
|
20421
|
-
|
|
20994
|
+
execFileSync30("pulumi", ["config", "set", ...args], {
|
|
20422
20995
|
cwd: appRoot,
|
|
20423
20996
|
env: pulumiEnv,
|
|
20424
20997
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -20467,7 +21040,7 @@ Specify which to base on: mesh stack init --from <stack>`
|
|
|
20467
21040
|
`Configured Pulumi.${newStack}.yaml (personal platform env on core '${baseEnv}'${opts.parentZone ? `, DNS zone ${newStack.startsWith(`${baseTenant}-`) ? newStack.slice(baseTenant.length + 1) : newStack}.${opts.parentZone}` : ""})`
|
|
20468
21041
|
);
|
|
20469
21042
|
} else {
|
|
20470
|
-
|
|
21043
|
+
execFileSync30("pulumi", ["config", "set", "--type", "bool", "mesh:deploy", "false"], {
|
|
20471
21044
|
cwd: appRoot,
|
|
20472
21045
|
env: pulumiEnv,
|
|
20473
21046
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -20497,7 +21070,7 @@ Specify which to base on: mesh stack init --from <stack>`
|
|
|
20497
21070
|
const args = ["stack", "rm", name];
|
|
20498
21071
|
if (opts.yes) args.push("--yes");
|
|
20499
21072
|
try {
|
|
20500
|
-
|
|
21073
|
+
execFileSync30("pulumi", args, { cwd: appRoot, env: pulumiEnv, stdio: "inherit" });
|
|
20501
21074
|
logSuccess(`Removed stack ${name}`);
|
|
20502
21075
|
} catch (err) {
|
|
20503
21076
|
process.exit(err.status ?? 1);
|
|
@@ -20709,12 +21282,12 @@ var init_recover_conversation = __esm({
|
|
|
20709
21282
|
});
|
|
20710
21283
|
|
|
20711
21284
|
// libs/mesh-cli/src/utils/temporal-codec.ts
|
|
20712
|
-
import { execFileSync as
|
|
21285
|
+
import { execFileSync as execFileSync31 } from "child_process";
|
|
20713
21286
|
import { webcrypto as crypto4 } from "node:crypto";
|
|
20714
21287
|
function resolveTemporalEncodingKeyFromK8s(namespace) {
|
|
20715
21288
|
const secretName = `${namespace}-temporal-encoding-key`;
|
|
20716
21289
|
try {
|
|
20717
|
-
const b64 =
|
|
21290
|
+
const b64 = execFileSync31(
|
|
20718
21291
|
"kubectl",
|
|
20719
21292
|
[
|
|
20720
21293
|
"get",
|
|
@@ -20965,7 +21538,7 @@ var init_capture_history = __esm({
|
|
|
20965
21538
|
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
20966
21539
|
import { writeFileSync as writeFileSync24, mkdirSync as mkdirSync19 } from "node:fs";
|
|
20967
21540
|
import { homedir as homedir6 } from "node:os";
|
|
20968
|
-
import { dirname as dirname28, join as
|
|
21541
|
+
import { dirname as dirname28, join as join40, resolve as resolve18 } from "node:path";
|
|
20969
21542
|
async function resolveConnection(options) {
|
|
20970
21543
|
if (options.address && options.namespace) {
|
|
20971
21544
|
return { address: options.address, namespace: options.namespace };
|
|
@@ -21393,7 +21966,7 @@ async function captureHistory(workflowId, runId, options) {
|
|
|
21393
21966
|
}
|
|
21394
21967
|
const { serializeHistoryToFixture: serializeHistoryToFixture2 } = await Promise.resolve().then(() => (init_capture_history(), capture_history_exports));
|
|
21395
21968
|
const { fixture, eventCount, decryptedPayloads } = await serializeHistoryToFixture2(events, decrypter);
|
|
21396
|
-
const outPath = options.out ??
|
|
21969
|
+
const outPath = options.out ?? join40(homedir6(), ".mesh", "replay-histories", `${sanitizeFileId(workflowId)}.json`);
|
|
21397
21970
|
mkdirSync19(dirname28(outPath), { recursive: true });
|
|
21398
21971
|
if (options.out) warnIfNotGitIgnored(options.out);
|
|
21399
21972
|
writeFileSync24(outPath, JSON.stringify(fixture, null, 2) + "\n");
|
|
@@ -21411,7 +21984,7 @@ function sanitizeFileId(id) {
|
|
|
21411
21984
|
return id.replace(/[^A-Za-z0-9._-]/g, "_");
|
|
21412
21985
|
}
|
|
21413
21986
|
function warnIfNotGitIgnored(outPath) {
|
|
21414
|
-
const abs =
|
|
21987
|
+
const abs = resolve18(outPath);
|
|
21415
21988
|
try {
|
|
21416
21989
|
const res = spawnSync4("git", ["-C", dirname28(abs), "check-ignore", "-q", abs], {
|
|
21417
21990
|
stdio: "ignore"
|
|
@@ -21585,8 +22158,8 @@ var init_temporal = __esm({
|
|
|
21585
22158
|
|
|
21586
22159
|
// libs/mesh-cli/src/commands/tenant.ts
|
|
21587
22160
|
import chalk7 from "chalk";
|
|
21588
|
-
import * as
|
|
21589
|
-
import * as
|
|
22161
|
+
import * as fs37 from "fs";
|
|
22162
|
+
import * as path44 from "path";
|
|
21590
22163
|
import { parseDocument, YAMLMap, isMap } from "yaml";
|
|
21591
22164
|
function validateTenantName(name) {
|
|
21592
22165
|
return /^[a-z][a-z0-9-]*$/.test(name) && !name.endsWith("-");
|
|
@@ -21654,16 +22227,16 @@ function resolveStackConfig(explicitStack) {
|
|
|
21654
22227
|
{ remediation: { command: "mesh tenant add <name> --stack <stack>" } }
|
|
21655
22228
|
);
|
|
21656
22229
|
}
|
|
21657
|
-
const file =
|
|
21658
|
-
if (!
|
|
22230
|
+
const file = path44.join(appRoot, `Pulumi.${stack}.yaml`);
|
|
22231
|
+
if (!fs37.existsSync(file)) {
|
|
21659
22232
|
throw new MeshCliError(`Stack config not found: ${file}`, {
|
|
21660
22233
|
remediation: { command: "mesh tenant add <name> --stack <stack>" }
|
|
21661
22234
|
});
|
|
21662
22235
|
}
|
|
21663
|
-
return { appRoot, stack, file, content:
|
|
22236
|
+
return { appRoot, stack, file, content: fs37.readFileSync(file, "utf-8") };
|
|
21664
22237
|
}
|
|
21665
22238
|
function readProjectName(appRoot) {
|
|
21666
|
-
const projectDoc = parseDocument(
|
|
22239
|
+
const projectDoc = parseDocument(fs37.readFileSync(path44.join(appRoot, "Pulumi.yaml"), "utf-8"));
|
|
21667
22240
|
return String(projectDoc.get("name") ?? "");
|
|
21668
22241
|
}
|
|
21669
22242
|
function looksLikePlatformLayer(projectName) {
|
|
@@ -21706,12 +22279,12 @@ function registerTenantCommands(program2) {
|
|
|
21706
22279
|
if (!result.ok) {
|
|
21707
22280
|
if (result.reason === "exists") {
|
|
21708
22281
|
throw new MeshCliError(
|
|
21709
|
-
`${result.detail} (${
|
|
22282
|
+
`${result.detail} (${path44.basename(resolved.file)}). Edit the existing entry instead of re-adding it.`
|
|
21710
22283
|
);
|
|
21711
22284
|
}
|
|
21712
22285
|
throw new MeshCliError(`${resolved.file}: ${result.detail}`);
|
|
21713
22286
|
}
|
|
21714
|
-
atomicWriteFileSync(resolved.file, result.yaml,
|
|
22287
|
+
atomicWriteFileSync(resolved.file, result.yaml, fs37.statSync(resolved.file).mode & 511);
|
|
21715
22288
|
if (opts.json) {
|
|
21716
22289
|
emitJsonPayload({
|
|
21717
22290
|
ok: true,
|
|
@@ -21724,7 +22297,7 @@ function registerTenantCommands(program2) {
|
|
|
21724
22297
|
return;
|
|
21725
22298
|
}
|
|
21726
22299
|
logInfo(
|
|
21727
|
-
`Tenant '${name}' registered in ${
|
|
22300
|
+
`Tenant '${name}' registered in ${path44.basename(resolved.file)} (mesh:tenants, subdomain '${subdomain}')${result.createdTenantsBlock ? " \u2014 created the mesh:tenants block" : ""}`
|
|
21728
22301
|
);
|
|
21729
22302
|
console.log("");
|
|
21730
22303
|
console.log("Next steps:");
|
|
@@ -21750,7 +22323,7 @@ function registerTenantCommands(program2) {
|
|
|
21750
22323
|
`Project '${projectName}' does not look like a platform layer \u2014 tenants are declared on the tenant platform repo's PLATFORM stack (e.g. mesh-sandbox/platform).`
|
|
21751
22324
|
);
|
|
21752
22325
|
}
|
|
21753
|
-
logInfo(`No tenants declared in ${
|
|
22326
|
+
logInfo(`No tenants declared in ${path44.basename(resolved.file)}.`);
|
|
21754
22327
|
return;
|
|
21755
22328
|
}
|
|
21756
22329
|
logInfo(`Tenants on stack '${resolved.stack}':`);
|
|
@@ -21772,9 +22345,9 @@ var init_tenant = __esm({
|
|
|
21772
22345
|
|
|
21773
22346
|
// libs/mesh-cli/src/commands/tunnel/index.ts
|
|
21774
22347
|
import { spawn as spawn9 } from "child_process";
|
|
21775
|
-
import * as
|
|
22348
|
+
import * as fs38 from "fs";
|
|
21776
22349
|
import * as os12 from "os";
|
|
21777
|
-
import * as
|
|
22350
|
+
import * as path45 from "path";
|
|
21778
22351
|
import {
|
|
21779
22352
|
SecretsManagerClient as SecretsManagerClient9,
|
|
21780
22353
|
GetSecretValueCommand as GetSecretValueCommand9
|
|
@@ -21783,7 +22356,7 @@ function resolveTenantEnv(options) {
|
|
|
21783
22356
|
if (options.tenant && options.env) {
|
|
21784
22357
|
return { tenant: options.tenant, env: options.env };
|
|
21785
22358
|
}
|
|
21786
|
-
const ctx = detectContext(options.
|
|
22359
|
+
const ctx = detectContext(void 0, { tenant: options.tenant });
|
|
21787
22360
|
return {
|
|
21788
22361
|
tenant: options.tenant ?? ctx.tenant,
|
|
21789
22362
|
env: options.env ?? ctx.platformEnv,
|
|
@@ -21794,9 +22367,9 @@ function readPulumiAwsRegion(stage) {
|
|
|
21794
22367
|
const preferred = stage ? `Pulumi.${stage}.yaml` : void 0;
|
|
21795
22368
|
const rank = (f) => f === preferred ? 0 : f === "Pulumi.yaml" ? 2 : 1;
|
|
21796
22369
|
try {
|
|
21797
|
-
const files =
|
|
22370
|
+
const files = fs38.readdirSync(".").filter((f) => f.startsWith("Pulumi.") && f.endsWith(".yaml")).sort((a, b) => rank(a) - rank(b));
|
|
21798
22371
|
for (const f of files) {
|
|
21799
|
-
const m =
|
|
22372
|
+
const m = fs38.readFileSync(f, "utf-8").match(/^\s*aws:region:\s*["']?([^"'\n]+)["']?/m);
|
|
21800
22373
|
if (m?.[1]) return m[1].trim();
|
|
21801
22374
|
}
|
|
21802
22375
|
} catch {
|
|
@@ -21805,14 +22378,14 @@ function readPulumiAwsRegion(stage) {
|
|
|
21805
22378
|
}
|
|
21806
22379
|
function awsProfileSections(name) {
|
|
21807
22380
|
return [
|
|
21808
|
-
[
|
|
21809
|
-
[
|
|
22381
|
+
[path45.join(os12.homedir(), ".aws", "config"), `[profile ${name}]`],
|
|
22382
|
+
[path45.join(os12.homedir(), ".aws", "credentials"), `[${name}]`]
|
|
21810
22383
|
];
|
|
21811
22384
|
}
|
|
21812
22385
|
function awsProfileExists(name) {
|
|
21813
22386
|
for (const [file, header] of awsProfileSections(name)) {
|
|
21814
22387
|
try {
|
|
21815
|
-
const lines =
|
|
22388
|
+
const lines = fs38.readFileSync(file, "utf-8").split("\n");
|
|
21816
22389
|
if (lines.some((l) => l.trim() === header)) return true;
|
|
21817
22390
|
} catch {
|
|
21818
22391
|
}
|
|
@@ -21838,7 +22411,7 @@ function awsProfileRegion(profile) {
|
|
|
21838
22411
|
if (!profile) return void 0;
|
|
21839
22412
|
for (const [file, header] of awsProfileSections(profile)) {
|
|
21840
22413
|
try {
|
|
21841
|
-
const region = parseProfileRegion(
|
|
22414
|
+
const region = parseProfileRegion(fs38.readFileSync(file, "utf-8"), header);
|
|
21842
22415
|
if (region) return region;
|
|
21843
22416
|
} catch {
|
|
21844
22417
|
}
|
|
@@ -21847,14 +22420,14 @@ function awsProfileRegion(profile) {
|
|
|
21847
22420
|
}
|
|
21848
22421
|
function awsDefaultProfileSections() {
|
|
21849
22422
|
return [
|
|
21850
|
-
[
|
|
21851
|
-
[
|
|
22423
|
+
[path45.join(os12.homedir(), ".aws", "config"), "[default]"],
|
|
22424
|
+
[path45.join(os12.homedir(), ".aws", "credentials"), "[default]"]
|
|
21852
22425
|
];
|
|
21853
22426
|
}
|
|
21854
22427
|
function awsDefaultProfileRegion() {
|
|
21855
22428
|
for (const [file, header] of awsDefaultProfileSections()) {
|
|
21856
22429
|
try {
|
|
21857
|
-
const region = parseProfileRegion(
|
|
22430
|
+
const region = parseProfileRegion(fs38.readFileSync(file, "utf-8"), header);
|
|
21858
22431
|
if (region) return region;
|
|
21859
22432
|
} catch {
|
|
21860
22433
|
}
|
|
@@ -21995,9 +22568,9 @@ async function tunnelServices(serviceNames, options) {
|
|
|
21995
22568
|
process.on("SIGTERM", cleanup);
|
|
21996
22569
|
await Promise.race(
|
|
21997
22570
|
processes.map(
|
|
21998
|
-
(proc) => new Promise((
|
|
21999
|
-
proc.on("exit", () =>
|
|
22000
|
-
proc.on("error", () =>
|
|
22571
|
+
(proc) => new Promise((resolve20) => {
|
|
22572
|
+
proc.on("exit", () => resolve20());
|
|
22573
|
+
proc.on("error", () => resolve20());
|
|
22001
22574
|
})
|
|
22002
22575
|
)
|
|
22003
22576
|
);
|
|
@@ -22094,9 +22667,9 @@ async function tunnelExternal(name, options) {
|
|
|
22094
22667
|
};
|
|
22095
22668
|
process.on("SIGINT", cleanup);
|
|
22096
22669
|
process.on("SIGTERM", cleanup);
|
|
22097
|
-
await new Promise((
|
|
22098
|
-
proc.on("exit", () =>
|
|
22099
|
-
proc.on("error", () =>
|
|
22670
|
+
await new Promise((resolve20) => {
|
|
22671
|
+
proc.on("exit", () => resolve20());
|
|
22672
|
+
proc.on("error", () => resolve20());
|
|
22100
22673
|
});
|
|
22101
22674
|
cleanup();
|
|
22102
22675
|
}
|
|
@@ -22141,7 +22714,7 @@ function registerTunnelCommands(program2) {
|
|
|
22141
22714
|
"Tenant for the external service's credentials (default: --tenant)"
|
|
22142
22715
|
).option(
|
|
22143
22716
|
"--app-stage <stage>",
|
|
22144
|
-
"Stage for the external service's credentials (default: --env)"
|
|
22717
|
+
"Stage for the external service's credentials (default: the detected app stage, else --env)"
|
|
22145
22718
|
).option(
|
|
22146
22719
|
"-k, --key <key>",
|
|
22147
22720
|
"Instance key for a multi-instance external (credentials.keyedBy)"
|
|
@@ -22241,8 +22814,8 @@ async function resolveToken(opts) {
|
|
|
22241
22814
|
"no token: pass --token, set VCS_TOKEN, or pass --context <platform-context> (after mesh login)"
|
|
22242
22815
|
);
|
|
22243
22816
|
}
|
|
22244
|
-
async function vcsApi(baseUrl, token,
|
|
22245
|
-
const res = await fetch(`${baseUrl}${
|
|
22817
|
+
async function vcsApi(baseUrl, token, path47, method = "GET", body) {
|
|
22818
|
+
const res = await fetch(`${baseUrl}${path47}`, {
|
|
22246
22819
|
method,
|
|
22247
22820
|
headers: {
|
|
22248
22821
|
authorization: `Bearer ${token}`,
|
|
@@ -22253,7 +22826,7 @@ async function vcsApi(baseUrl, token, path46, method = "GET", body) {
|
|
|
22253
22826
|
const data = await res.json().catch(() => ({}));
|
|
22254
22827
|
if (!res.ok) {
|
|
22255
22828
|
throw new Error(
|
|
22256
|
-
`${method} ${
|
|
22829
|
+
`${method} ${path47} failed (${res.status}): ${String(data.error ?? "unknown error")}`
|
|
22257
22830
|
);
|
|
22258
22831
|
}
|
|
22259
22832
|
return data;
|
|
@@ -22299,8 +22872,8 @@ var init_clone = __esm({
|
|
|
22299
22872
|
|
|
22300
22873
|
// libs/mesh-cli/src/commands/vcs/get.ts
|
|
22301
22874
|
import { writeFile as writeFile2, mkdir as mkdir2 } from "node:fs/promises";
|
|
22302
|
-
import { join as
|
|
22303
|
-
async function getCommand(repo,
|
|
22875
|
+
import { join as join43, dirname as dirname29 } from "node:path";
|
|
22876
|
+
async function getCommand(repo, path47, opts) {
|
|
22304
22877
|
let vcsBaseUrl;
|
|
22305
22878
|
let token;
|
|
22306
22879
|
if (opts.target) {
|
|
@@ -22315,18 +22888,18 @@ async function getCommand(repo, path46, opts) {
|
|
|
22315
22888
|
if (!token) throw new Error("no token: pass --target (after mesh login), or --token / --context");
|
|
22316
22889
|
const reader = createVcsFolderReader({ vcsBaseUrl, token });
|
|
22317
22890
|
try {
|
|
22318
|
-
const { ref, files } = await reader.readPath(repo,
|
|
22891
|
+
const { ref, files } = await reader.readPath(repo, path47);
|
|
22319
22892
|
if (files.length === 0) {
|
|
22320
|
-
logInfo(`no files at ${repo}:${
|
|
22893
|
+
logInfo(`no files at ${repo}:${path47}`);
|
|
22321
22894
|
return;
|
|
22322
22895
|
}
|
|
22323
22896
|
if (opts.output) {
|
|
22324
22897
|
for (const f of files) {
|
|
22325
|
-
const abs =
|
|
22898
|
+
const abs = join43(opts.output, f.path);
|
|
22326
22899
|
await mkdir2(dirname29(abs), { recursive: true });
|
|
22327
22900
|
await writeFile2(abs, f.contents);
|
|
22328
22901
|
}
|
|
22329
|
-
logInfo(`wrote ${files.length} file(s) from ${repo}:${
|
|
22902
|
+
logInfo(`wrote ${files.length} file(s) from ${repo}:${path47} (${ref}) \u2192 ${opts.output}`);
|
|
22330
22903
|
} else {
|
|
22331
22904
|
for (const f of files) {
|
|
22332
22905
|
if (files.length > 1) process.stdout.write(`
|
|
@@ -22351,10 +22924,10 @@ var init_get = __esm({
|
|
|
22351
22924
|
});
|
|
22352
22925
|
|
|
22353
22926
|
// libs/mesh-cli/src/commands/vcs/drafts.ts
|
|
22354
|
-
async function call(opts, repo,
|
|
22927
|
+
async function call(opts, repo, path47, method = "GET", body) {
|
|
22355
22928
|
const target = await resolveTarget2({ ...opts, repo });
|
|
22356
22929
|
const token = await resolveToken(opts);
|
|
22357
|
-
return vcsApi(target.baseUrl, token, `/v1/repos/${repo}${
|
|
22930
|
+
return vcsApi(target.baseUrl, token, `/v1/repos/${repo}${path47}`, method, body);
|
|
22358
22931
|
}
|
|
22359
22932
|
async function draftsListCommand(repo, opts) {
|
|
22360
22933
|
const data = await call(opts, repo, `/drafts${opts.all ? "?all=true" : ""}`);
|
|
@@ -22409,7 +22982,7 @@ var init_drafts = __esm({
|
|
|
22409
22982
|
|
|
22410
22983
|
// libs/mesh-cli/src/commands/vcs/propose.ts
|
|
22411
22984
|
import { readFile as readFile3 } from "node:fs/promises";
|
|
22412
|
-
import { join as
|
|
22985
|
+
import { join as join44 } from "node:path";
|
|
22413
22986
|
function parseStatus(out) {
|
|
22414
22987
|
const tokens = out.split("\0");
|
|
22415
22988
|
const changes = [];
|
|
@@ -22442,13 +23015,13 @@ function parseStatus(out) {
|
|
|
22442
23015
|
}
|
|
22443
23016
|
return changes;
|
|
22444
23017
|
}
|
|
22445
|
-
async function writeOp(cwd,
|
|
22446
|
-
const buf = await readFile3(
|
|
23018
|
+
async function writeOp(cwd, path47) {
|
|
23019
|
+
const buf = await readFile3(join44(cwd, path47));
|
|
22447
23020
|
const asUtf8 = buf.toString("utf8");
|
|
22448
23021
|
if (Buffer.from(asUtf8, "utf8").equals(buf)) {
|
|
22449
|
-
return { op: "write", path:
|
|
23022
|
+
return { op: "write", path: path47, content: asUtf8 };
|
|
22450
23023
|
}
|
|
22451
|
-
return { op: "write", path:
|
|
23024
|
+
return { op: "write", path: path47, content: buf.toString("base64"), encoding: "base64" };
|
|
22452
23025
|
}
|
|
22453
23026
|
async function proposeCommand(opts) {
|
|
22454
23027
|
const cwd = process.cwd();
|
|
@@ -22492,8 +23065,8 @@ async function proposeCommand(opts) {
|
|
|
22492
23065
|
operations,
|
|
22493
23066
|
...opts.mergeParent ? { mergeParent: opts.mergeParent } : {}
|
|
22494
23067
|
};
|
|
22495
|
-
const
|
|
22496
|
-
const data = await vcsApi(target.baseUrl, token,
|
|
23068
|
+
const path47 = opts.revise ? `/v1/repos/${target.repo}/proposals/${opts.revise}/revisions` : `/v1/repos/${target.repo}/proposals`;
|
|
23069
|
+
const data = await vcsApi(target.baseUrl, token, path47, "POST", body);
|
|
22497
23070
|
console.log(
|
|
22498
23071
|
`proposal ${data.proposalId} @ ${data.sha.slice(0, 8)} \u2014 review: mesh vcs show ${target.repo} ${data.proposalId} --url ${target.baseUrl}`
|
|
22499
23072
|
);
|
|
@@ -22508,28 +23081,28 @@ var init_propose = __esm({
|
|
|
22508
23081
|
// libs/mesh-cli/src/commands/vcs/rm.ts
|
|
22509
23082
|
import { mkdtemp as mkdtemp2, rm as rmrf } from "node:fs/promises";
|
|
22510
23083
|
import { tmpdir as tmpdir10 } from "node:os";
|
|
22511
|
-
import { join as
|
|
23084
|
+
import { join as join45 } from "node:path";
|
|
22512
23085
|
function normalize2(p) {
|
|
22513
23086
|
return p.trim().replace(/^\.\//, "").replace(/\/+$/, "");
|
|
22514
23087
|
}
|
|
22515
23088
|
function expandPathsToDeletions(repoFiles, requested) {
|
|
22516
23089
|
const out = /* @__PURE__ */ new Set();
|
|
22517
23090
|
for (const raw of requested) {
|
|
22518
|
-
const
|
|
22519
|
-
if (
|
|
23091
|
+
const path47 = normalize2(raw);
|
|
23092
|
+
if (path47.startsWith("/") || path47.split("/").includes("..")) {
|
|
22520
23093
|
throw new Error(
|
|
22521
23094
|
`"${raw}" is not a repo-relative path \u2014 vcs paths never start with "/" or traverse with "..".`
|
|
22522
23095
|
);
|
|
22523
23096
|
}
|
|
22524
|
-
const exact = repoFiles.filter((f) => f ===
|
|
23097
|
+
const exact = repoFiles.filter((f) => f === path47);
|
|
22525
23098
|
if (exact.length > 0) {
|
|
22526
23099
|
for (const f of exact) out.add(f);
|
|
22527
23100
|
continue;
|
|
22528
23101
|
}
|
|
22529
|
-
const under = repoFiles.filter((f) => f.startsWith(`${
|
|
23102
|
+
const under = repoFiles.filter((f) => f.startsWith(`${path47}/`));
|
|
22530
23103
|
if (under.length === 0) {
|
|
22531
23104
|
throw new Error(
|
|
22532
|
-
`"${raw}" matched no files in the repo \u2014 nothing was proposed. Check the path with: mesh vcs get <repo> ${
|
|
23105
|
+
`"${raw}" matched no files in the repo \u2014 nothing was proposed. Check the path with: mesh vcs get <repo> ${path47}`
|
|
22533
23106
|
);
|
|
22534
23107
|
}
|
|
22535
23108
|
for (const f of under) out.add(f);
|
|
@@ -22540,7 +23113,7 @@ async function rmCommand(repo, paths, opts) {
|
|
|
22540
23113
|
const baseUrl = (opts.url ?? process.env.VCS_URL)?.replace(/\/$/, "");
|
|
22541
23114
|
if (!baseUrl) throw new Error("rm requires --url or VCS_URL");
|
|
22542
23115
|
const token = await resolveToken(opts);
|
|
22543
|
-
const dir = await mkdtemp2(
|
|
23116
|
+
const dir = await mkdtemp2(join45(tmpdir10(), "mesh-vcs-rm-"));
|
|
22544
23117
|
try {
|
|
22545
23118
|
const git = (args, cwd) => gitWithAuth(args, token, cwd);
|
|
22546
23119
|
await git([
|
|
@@ -22565,7 +23138,7 @@ async function rmCommand(repo, paths, opts) {
|
|
|
22565
23138
|
const body = {
|
|
22566
23139
|
baseCommit: head.trim(),
|
|
22567
23140
|
message: opts.message ?? `Delete ${doomed.length} file(s)`,
|
|
22568
|
-
operations: doomed.map((
|
|
23141
|
+
operations: doomed.map((path47) => ({ op: "delete", path: path47 }))
|
|
22569
23142
|
};
|
|
22570
23143
|
const data = await vcsApi(
|
|
22571
23144
|
baseUrl,
|
|
@@ -22591,10 +23164,10 @@ var init_rm = __esm({
|
|
|
22591
23164
|
});
|
|
22592
23165
|
|
|
22593
23166
|
// libs/mesh-cli/src/commands/vcs/review.ts
|
|
22594
|
-
async function call2(opts, repo,
|
|
23167
|
+
async function call2(opts, repo, path47, method = "GET", body) {
|
|
22595
23168
|
const target = await resolveTarget2({ ...opts, repo });
|
|
22596
23169
|
const token = await resolveToken(opts);
|
|
22597
|
-
return vcsApi(target.baseUrl, token, `/v1/repos/${repo}${
|
|
23170
|
+
return vcsApi(target.baseUrl, token, `/v1/repos/${repo}${path47}`, method, body);
|
|
22598
23171
|
}
|
|
22599
23172
|
async function proposalsCommand(repo, opts) {
|
|
22600
23173
|
console.log(JSON.stringify(await call2(opts, repo, "/proposals"), null, 2));
|
|
@@ -22741,8 +23314,8 @@ function computeHappyPath(nodes, edges) {
|
|
|
22741
23314
|
];
|
|
22742
23315
|
const visited = /* @__PURE__ */ new Set([startNode.id]);
|
|
22743
23316
|
while (queue.length > 0) {
|
|
22744
|
-
const { id, path:
|
|
22745
|
-
if (targetIds.has(id)) return
|
|
23317
|
+
const { id, path: path47 } = queue.shift();
|
|
23318
|
+
if (targetIds.has(id)) return path47;
|
|
22746
23319
|
const neighbors = adjacency.get(id) ?? [];
|
|
22747
23320
|
const hasMainEdge = neighbors.some((e) => e.isMainPath);
|
|
22748
23321
|
const candidates = hasMainEdge ? neighbors.filter((e) => e.isMainPath) : neighbors;
|
|
@@ -22762,7 +23335,7 @@ function computeHappyPath(nodes, edges) {
|
|
|
22762
23335
|
if (!allowExceptional && (neighbor.isExceptional || neighbor.isTimeout || failureEndIds.has(neighbor.to)))
|
|
22763
23336
|
continue;
|
|
22764
23337
|
visited.add(neighbor.to);
|
|
22765
|
-
queue.push({ id: neighbor.to, path: [...
|
|
23338
|
+
queue.push({ id: neighbor.to, path: [...path47, neighbor.to] });
|
|
22766
23339
|
}
|
|
22767
23340
|
}
|
|
22768
23341
|
return null;
|
|
@@ -23047,11 +23620,11 @@ function wrapIntoGroup(workflow, nodeIds, groupId, label, groupType) {
|
|
|
23047
23620
|
}
|
|
23048
23621
|
}
|
|
23049
23622
|
if (found) {
|
|
23050
|
-
const
|
|
23051
|
-
for (let c = found; c !== void 0; c = prev.get(c))
|
|
23052
|
-
for (let i = 0; i <
|
|
23623
|
+
const path47 = [];
|
|
23624
|
+
for (let c = found; c !== void 0; c = prev.get(c)) path47.unshift(c);
|
|
23625
|
+
for (let i = 0; i < path47.length - 1; i++) mainEdgeKeys.add(edgeKey(path47[i], path47[i + 1]));
|
|
23053
23626
|
mainEdgeKeys.add(edgeKey(found, successExit.id));
|
|
23054
|
-
mainBody =
|
|
23627
|
+
mainBody = path47;
|
|
23055
23628
|
mainTail = [successExit.id];
|
|
23056
23629
|
}
|
|
23057
23630
|
}
|
|
@@ -23587,11 +24160,11 @@ function buildProcessManifest(artifact, outcomeNodeIds) {
|
|
|
23587
24160
|
...pointOfNoReturnStage ? { pointOfNoReturnStageId: pointOfNoReturnStage.id } : {}
|
|
23588
24161
|
};
|
|
23589
24162
|
}
|
|
23590
|
-
function formatPath(
|
|
23591
|
-
return
|
|
24163
|
+
function formatPath(path47) {
|
|
24164
|
+
return path47.length > 0 ? z4.core.toDotPath(path47) : "(root)";
|
|
23592
24165
|
}
|
|
23593
24166
|
function issueToDiagnostic(issue) {
|
|
23594
|
-
const
|
|
24167
|
+
const path47 = formatPath(issue.path);
|
|
23595
24168
|
let code = "SCHEMA_INVALID";
|
|
23596
24169
|
if (issue.code === "custom") {
|
|
23597
24170
|
const paramCode = issue.params?.code;
|
|
@@ -23602,8 +24175,8 @@ function issueToDiagnostic(issue) {
|
|
|
23602
24175
|
return {
|
|
23603
24176
|
severity: "error",
|
|
23604
24177
|
code,
|
|
23605
|
-
message: `${
|
|
23606
|
-
path:
|
|
24178
|
+
message: `${path47}: ${issue.message}`,
|
|
24179
|
+
path: path47
|
|
23607
24180
|
};
|
|
23608
24181
|
}
|
|
23609
24182
|
function upgradeV1(artifact) {
|
|
@@ -23770,24 +24343,24 @@ var init_process_artifact = __esm({
|
|
|
23770
24343
|
});
|
|
23771
24344
|
}
|
|
23772
24345
|
const declaredSelectors = new Set(Object.keys(process2.selectors ?? {}));
|
|
23773
|
-
const checkActor = (actorId,
|
|
24346
|
+
const checkActor = (actorId, path47) => {
|
|
23774
24347
|
if (actorId === void 0) return;
|
|
23775
24348
|
if (!Object.prototype.hasOwnProperty.call(process2.actors, actorId)) {
|
|
23776
24349
|
ctx.addIssue({
|
|
23777
24350
|
code: "custom",
|
|
23778
|
-
path:
|
|
23779
|
-
message: `Actor "${actorId}" is referenced at ${z4.core.toDotPath(
|
|
24351
|
+
path: path47,
|
|
24352
|
+
message: `Actor "${actorId}" is referenced at ${z4.core.toDotPath(path47)} but not declared in process.actors. Declare the actor (label, and optionally category/selector) or fix the reference.`,
|
|
23780
24353
|
params: { code: "UNDECLARED_ACTOR" }
|
|
23781
24354
|
});
|
|
23782
24355
|
}
|
|
23783
24356
|
};
|
|
23784
|
-
const checkSelector = (selector,
|
|
24357
|
+
const checkSelector = (selector, path47) => {
|
|
23785
24358
|
if (selector === void 0) return;
|
|
23786
24359
|
if (!declaredSelectors.has(selector)) {
|
|
23787
24360
|
ctx.addIssue({
|
|
23788
24361
|
code: "custom",
|
|
23789
|
-
path:
|
|
23790
|
-
message: `Selector "${selector}" is referenced at ${z4.core.toDotPath(
|
|
24362
|
+
path: path47,
|
|
24363
|
+
message: `Selector "${selector}" is referenced at ${z4.core.toDotPath(path47)} but not declared in process.selectors. Declare the selector or fix the reference.`,
|
|
23791
24364
|
params: { code: "UNDECLARED_SELECTOR" }
|
|
23792
24365
|
});
|
|
23793
24366
|
}
|
|
@@ -23801,20 +24374,20 @@ var init_process_artifact = __esm({
|
|
|
23801
24374
|
outcome: "DUPLICATE_OUTCOME_ID"
|
|
23802
24375
|
};
|
|
23803
24376
|
const idFirstSeenAt = /* @__PURE__ */ new Map();
|
|
23804
|
-
const checkId = (id, kind,
|
|
24377
|
+
const checkId = (id, kind, path47) => {
|
|
23805
24378
|
const first = idFirstSeenAt.get(id);
|
|
23806
24379
|
if (first) {
|
|
23807
24380
|
const kindLabel = kind === first.kind ? kind : "process";
|
|
23808
24381
|
ctx.addIssue({
|
|
23809
24382
|
code: "custom",
|
|
23810
|
-
path:
|
|
23811
|
-
message: `Duplicate ${kindLabel} id "${id}": ${kind} at ${z4.core.toDotPath(
|
|
24383
|
+
path: path47,
|
|
24384
|
+
message: `Duplicate ${kindLabel} id "${id}": ${kind} at ${z4.core.toDotPath(path47)} collides with ${first.kind} at ${z4.core.toDotPath(first.path)}.`,
|
|
23812
24385
|
params: {
|
|
23813
24386
|
code: kind === first.kind ? SAME_KIND_CODE[kind] : "DUPLICATE_PROCESS_ID"
|
|
23814
24387
|
}
|
|
23815
24388
|
});
|
|
23816
24389
|
} else {
|
|
23817
|
-
idFirstSeenAt.set(id, { kind, path:
|
|
24390
|
+
idFirstSeenAt.set(id, { kind, path: path47 });
|
|
23818
24391
|
}
|
|
23819
24392
|
};
|
|
23820
24393
|
process2.stages.forEach((stage, stageIndex) => {
|
|
@@ -23861,13 +24434,13 @@ function lintProcess(artifact, inventory, predicateNames, options = {}) {
|
|
|
23861
24434
|
const internalCommands = process2.internalCommands ?? [];
|
|
23862
24435
|
const internalSet = new Set(internalCommands);
|
|
23863
24436
|
const boundCommandPaths = /* @__PURE__ */ new Map();
|
|
23864
|
-
const checkPredicate = (predicate,
|
|
24437
|
+
const checkPredicate = (predicate, path47) => {
|
|
23865
24438
|
if (!predicateSet.has(predicate)) {
|
|
23866
24439
|
diagnostics.push({
|
|
23867
24440
|
severity: "error",
|
|
23868
24441
|
code: "UNKNOWN_PREDICATE",
|
|
23869
|
-
message: `Predicate "${predicate}" referenced at ${
|
|
23870
|
-
path:
|
|
24442
|
+
message: `Predicate "${predicate}" referenced at ${path47} is not in the predicate registry.`,
|
|
24443
|
+
path: path47
|
|
23871
24444
|
});
|
|
23872
24445
|
}
|
|
23873
24446
|
};
|
|
@@ -23927,19 +24500,19 @@ function lintProcess(artifact, inventory, predicateNames, options = {}) {
|
|
|
23927
24500
|
process2.outcomes.forEach((outcome, outcomeIndex) => {
|
|
23928
24501
|
const whenPath = `process.outcomes[${outcomeIndex}].when`;
|
|
23929
24502
|
outcomeTriggers(outcome).forEach((trigger, triggerIndex) => {
|
|
23930
|
-
const
|
|
24503
|
+
const path47 = Array.isArray(outcome.when) ? `${whenPath}[${triggerIndex}]` : whenPath;
|
|
23931
24504
|
if (trigger.kind === "predicate") {
|
|
23932
|
-
checkPredicate(trigger.name, `${
|
|
24505
|
+
checkPredicate(trigger.name, `${path47}.predicate`);
|
|
23933
24506
|
} else if (trigger.kind === "command") {
|
|
23934
24507
|
if (!boundCommandPaths.has(trigger.name)) {
|
|
23935
|
-
boundCommandPaths.set(trigger.name, `${
|
|
24508
|
+
boundCommandPaths.set(trigger.name, `${path47}.command`);
|
|
23936
24509
|
}
|
|
23937
24510
|
if (!commandNames.has(trigger.name)) {
|
|
23938
24511
|
diagnostics.push({
|
|
23939
24512
|
severity: "error",
|
|
23940
24513
|
code: "BOUND_COMMAND_NOT_IN_INVENTORY",
|
|
23941
|
-
message: `Command "${trigger.name}" triggering outcome "${outcome.id}" (${
|
|
23942
|
-
path: `${
|
|
24514
|
+
message: `Command "${trigger.name}" triggering outcome "${outcome.id}" (${path47}.command) does not exist in the inventory.`,
|
|
24515
|
+
path: `${path47}.command`
|
|
23943
24516
|
});
|
|
23944
24517
|
}
|
|
23945
24518
|
}
|
|
@@ -24051,10 +24624,10 @@ var init_src4 = __esm({
|
|
|
24051
24624
|
});
|
|
24052
24625
|
|
|
24053
24626
|
// libs/mesh-cli/src/commands/workflow.ts
|
|
24054
|
-
import * as
|
|
24055
|
-
import * as
|
|
24627
|
+
import * as fs39 from "fs";
|
|
24628
|
+
import * as path46 from "path";
|
|
24056
24629
|
import { createRequire as createRequire2 } from "module";
|
|
24057
|
-
import { execFileSync as
|
|
24630
|
+
import { execFileSync as execFileSync32 } from "child_process";
|
|
24058
24631
|
function resolveExtractorPath() {
|
|
24059
24632
|
try {
|
|
24060
24633
|
const require2 = createRequire2(import.meta.url);
|
|
@@ -24068,7 +24641,7 @@ function runExtraction(targetPath, extractorPath, explicitProcessPath) {
|
|
|
24068
24641
|
let explicitProcessArtifact;
|
|
24069
24642
|
if (explicitProcessPath) {
|
|
24070
24643
|
try {
|
|
24071
|
-
explicitProcessArtifact = JSON.parse(
|
|
24644
|
+
explicitProcessArtifact = JSON.parse(fs39.readFileSync(explicitProcessPath, "utf-8"));
|
|
24072
24645
|
} catch (err) {
|
|
24073
24646
|
logError(
|
|
24074
24647
|
`Could not read/parse --process ${explicitProcessPath}: ${err instanceof Error ? err.message : String(err)}`
|
|
@@ -24109,7 +24682,7 @@ for (const file of files) {
|
|
|
24109
24682
|
process.stdout.write(JSON.stringify(results));
|
|
24110
24683
|
`;
|
|
24111
24684
|
try {
|
|
24112
|
-
const result =
|
|
24685
|
+
const result = execFileSync32("npx", ["tsx", "--eval", script], {
|
|
24113
24686
|
encoding: "utf-8",
|
|
24114
24687
|
stdio: ["pipe", "pipe", "inherit"],
|
|
24115
24688
|
maxBuffer: 10 * 1024 * 1024
|
|
@@ -24151,7 +24724,7 @@ for (const file of files) {
|
|
|
24151
24724
|
process.stdout.write(JSON.stringify(results));
|
|
24152
24725
|
`;
|
|
24153
24726
|
try {
|
|
24154
|
-
const result =
|
|
24727
|
+
const result = execFileSync32("npx", ["tsx", "--eval", script], {
|
|
24155
24728
|
encoding: "utf-8",
|
|
24156
24729
|
stdio: ["pipe", "pipe", "inherit"],
|
|
24157
24730
|
maxBuffer: 10 * 1024 * 1024
|
|
@@ -24168,7 +24741,7 @@ function runLintExtraction(targetPath, extractorPath, explicitProcessPath) {
|
|
|
24168
24741
|
let explicitProcessArtifact;
|
|
24169
24742
|
if (explicitProcessPath) {
|
|
24170
24743
|
try {
|
|
24171
|
-
explicitProcessArtifact = JSON.parse(
|
|
24744
|
+
explicitProcessArtifact = JSON.parse(fs39.readFileSync(explicitProcessPath, "utf-8"));
|
|
24172
24745
|
} catch (err) {
|
|
24173
24746
|
logError(
|
|
24174
24747
|
`Could not read/parse --process ${explicitProcessPath}: ${err instanceof Error ? err.message : String(err)}`
|
|
@@ -24206,7 +24779,7 @@ for (const file of files) {
|
|
|
24206
24779
|
process.stdout.write(JSON.stringify(results));
|
|
24207
24780
|
`;
|
|
24208
24781
|
try {
|
|
24209
|
-
const result =
|
|
24782
|
+
const result = execFileSync32("npx", ["tsx", "--eval", script], {
|
|
24210
24783
|
encoding: "utf-8",
|
|
24211
24784
|
stdio: ["pipe", "pipe", "inherit"],
|
|
24212
24785
|
maxBuffer: 10 * 1024 * 1024
|
|
@@ -24276,16 +24849,16 @@ function registerWorkflowCommands(program2) {
|
|
|
24276
24849
|
).action(
|
|
24277
24850
|
async (targetPath, opts) => {
|
|
24278
24851
|
try {
|
|
24279
|
-
const resolvedPath =
|
|
24280
|
-
if (!
|
|
24852
|
+
const resolvedPath = path46.resolve(targetPath);
|
|
24853
|
+
if (!fs39.existsSync(resolvedPath)) {
|
|
24281
24854
|
logError(`Path does not exist: ${resolvedPath}`);
|
|
24282
24855
|
process.exitCode = 1;
|
|
24283
24856
|
return;
|
|
24284
24857
|
}
|
|
24285
24858
|
let resolvedProcessPath;
|
|
24286
24859
|
if (opts.process) {
|
|
24287
|
-
resolvedProcessPath =
|
|
24288
|
-
if (!
|
|
24860
|
+
resolvedProcessPath = path46.resolve(opts.process);
|
|
24861
|
+
if (!fs39.existsSync(resolvedProcessPath)) {
|
|
24289
24862
|
logError(`--process path does not exist: ${resolvedProcessPath}`);
|
|
24290
24863
|
process.exitCode = 1;
|
|
24291
24864
|
return;
|
|
@@ -24334,8 +24907,8 @@ function registerWorkflowCommands(program2) {
|
|
|
24334
24907
|
"Extract the as-built command/query/activity inventory from Temporal workflow source\n\nThe inventory ({ workflowType, commands, queries, activities }) is the process-\nconformance lint's code-side input \u2014 it is never hand-edited. Supports single\nfiles or directories."
|
|
24335
24908
|
).action(async (targetPath) => {
|
|
24336
24909
|
try {
|
|
24337
|
-
const resolvedPath =
|
|
24338
|
-
if (!
|
|
24910
|
+
const resolvedPath = path46.resolve(targetPath);
|
|
24911
|
+
if (!fs39.existsSync(resolvedPath)) {
|
|
24339
24912
|
logError(`Path does not exist: ${resolvedPath}`);
|
|
24340
24913
|
process.exitCode = 1;
|
|
24341
24914
|
return;
|
|
@@ -24381,16 +24954,16 @@ function registerWorkflowCommands(program2) {
|
|
|
24381
24954
|
"Comma-separated named-selector registry (enables UNKNOWN_SELECTOR checks)"
|
|
24382
24955
|
).action(async (targetPath, opts) => {
|
|
24383
24956
|
try {
|
|
24384
|
-
const resolvedPath =
|
|
24385
|
-
if (!
|
|
24957
|
+
const resolvedPath = path46.resolve(targetPath);
|
|
24958
|
+
if (!fs39.existsSync(resolvedPath)) {
|
|
24386
24959
|
logError(`Path does not exist: ${resolvedPath}`);
|
|
24387
24960
|
process.exitCode = 1;
|
|
24388
24961
|
return;
|
|
24389
24962
|
}
|
|
24390
24963
|
let resolvedProcessPath;
|
|
24391
24964
|
if (opts.process) {
|
|
24392
|
-
resolvedProcessPath =
|
|
24393
|
-
if (!
|
|
24965
|
+
resolvedProcessPath = path46.resolve(opts.process);
|
|
24966
|
+
if (!fs39.existsSync(resolvedProcessPath)) {
|
|
24394
24967
|
logError(`--process path does not exist: ${resolvedProcessPath}`);
|
|
24395
24968
|
process.exitCode = 1;
|
|
24396
24969
|
return;
|