@mesh-tech/mesh-cli 0.36.0 → 0.37.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/assets/docs-preview.css +1599 -0
- package/dist/bin/mesh.auth-preflight-JFKL4Y4X.js +48 -0
- package/dist/bin/mesh.auth-preflight-JFKL4Y4X.js.map +6 -0
- package/dist/bin/mesh.capture-history-A4IAVVIR.js +38 -0
- package/dist/bin/mesh.capture-history-A4IAVVIR.js.map +6 -0
- package/dist/bin/mesh.chunk-5A5OFSYR.js +187 -0
- package/dist/bin/mesh.chunk-5A5OFSYR.js.map +6 -0
- package/dist/bin/mesh.chunk-7DMAFUBQ.js +7715 -0
- package/dist/bin/mesh.chunk-7DMAFUBQ.js.map +6 -0
- package/dist/bin/mesh.chunk-A4DGMGHF.js +303 -0
- package/dist/bin/mesh.chunk-A4DGMGHF.js.map +6 -0
- package/dist/bin/mesh.chunk-CQ2IC4OP.js +217 -0
- package/dist/bin/mesh.chunk-CQ2IC4OP.js.map +6 -0
- package/dist/bin/mesh.chunk-F4654EPX.js +383 -0
- package/dist/bin/mesh.chunk-F4654EPX.js.map +6 -0
- package/dist/bin/mesh.chunk-IQOBTL42.js +60 -0
- package/dist/bin/mesh.chunk-IQOBTL42.js.map +6 -0
- package/dist/bin/mesh.chunk-J6DVWKRW.js +647 -0
- package/dist/bin/mesh.chunk-J6DVWKRW.js.map +6 -0
- package/dist/bin/mesh.chunk-JB7FWYSP.js +1751 -0
- package/dist/bin/mesh.chunk-JB7FWYSP.js.map +6 -0
- package/dist/bin/mesh.chunk-MCBMHWXP.js +837 -0
- package/dist/bin/mesh.chunk-MCBMHWXP.js.map +6 -0
- package/dist/bin/mesh.chunk-OW5EMVG5.js +507 -0
- package/dist/bin/mesh.chunk-OW5EMVG5.js.map +6 -0
- package/dist/bin/mesh.chunk-R5VVTXG4.js +10092 -0
- package/dist/bin/mesh.chunk-R5VVTXG4.js.map +6 -0
- package/dist/bin/mesh.chunk-VKV6Z3UI.js +1146 -0
- package/dist/bin/mesh.chunk-VKV6Z3UI.js.map +6 -0
- package/dist/bin/mesh.chunk-WFFDF346.js +59 -0
- package/dist/bin/mesh.chunk-WFFDF346.js.map +6 -0
- package/dist/bin/mesh.chunk-WOJOTUFZ.js +835 -0
- package/dist/bin/mesh.chunk-WOJOTUFZ.js.map +6 -0
- package/dist/bin/mesh.cli-reference-H3TOPIET.js +158 -0
- package/dist/bin/mesh.cli-reference-H3TOPIET.js.map +6 -0
- package/dist/bin/mesh.convert-KK6ZP256.js +274 -0
- package/dist/bin/mesh.convert-KK6ZP256.js.map +6 -0
- package/dist/bin/mesh.create-app-A5F45PWZ.js +39 -0
- package/dist/bin/mesh.create-app-A5F45PWZ.js.map +6 -0
- package/dist/bin/mesh.dev-token-server-GYDYPXYH.js +61 -0
- package/dist/bin/mesh.dev-token-server-GYDYPXYH.js.map +6 -0
- package/dist/bin/mesh.js +20 -26418
- package/dist/bin/mesh.js.map +3 -3
- package/dist/bin/mesh.login-JNHPVMCT.js +91 -0
- package/dist/bin/mesh.login-JNHPVMCT.js.map +6 -0
- package/dist/bin/mesh.platform-setup-EB64EFRN.js +37 -0
- package/dist/bin/mesh.platform-setup-EB64EFRN.js.map +6 -0
- package/dist/bin/mesh.preview-3IP34TDO.js +2410 -0
- package/dist/bin/mesh.preview-3IP34TDO.js.map +6 -0
- package/dist/bin/mesh.program-DWOKHNMQ.js +23 -0
- package/dist/bin/mesh.program-DWOKHNMQ.js.map +6 -0
- package/dist/bin/mesh.registry-OXF57OQW.js +47 -0
- package/dist/bin/mesh.registry-OXF57OQW.js.map +6 -0
- package/dist/bin/mesh.registry-identity-2HSGQGJC.js +42 -0
- package/dist/bin/mesh.registry-identity-2HSGQGJC.js.map +6 -0
- package/dist/bin/mesh.skills-MNDSMBIE.js +23 -0
- package/dist/bin/mesh.skills-MNDSMBIE.js.map +6 -0
- package/dist/bin/mesh.src-5D5DFSLX.js +1302 -0
- package/dist/bin/mesh.src-5D5DFSLX.js.map +6 -0
- package/dist/bin/mesh.src-G7TJBLNJ.js +40 -0
- package/dist/bin/mesh.src-G7TJBLNJ.js.map +6 -0
- package/dist/bin/mesh.stack-EFTFAOCV.js +64 -0
- package/dist/bin/mesh.stack-EFTFAOCV.js.map +6 -0
- package/dist/bin/mesh.start-ARIIY6CV.js +454 -0
- package/dist/bin/mesh.start-ARIIY6CV.js.map +6 -0
- package/dist/bin/mesh.wizard-ROAQAGSF.js +500 -0
- package/dist/bin/mesh.wizard-ROAQAGSF.js.map +6 -0
- package/dist/build-info.json +2 -2
- package/dist/src/commands/app-check.d.ts +2 -1
- package/dist/src/commands/app-check.js +32 -0
- package/dist/src/commands/dev.d.ts +6 -0
- package/dist/src/commands/dev.js +152 -3
- package/dist/src/commands/docs.js +33 -0
- package/dist/src/commands/local/act-as-local.d.ts +5 -0
- package/dist/src/commands/local/act-as-local.js +41 -0
- package/dist/src/commands/local/auth-provision.d.ts +1 -0
- package/dist/src/commands/local/auth-provision.js +1 -0
- package/dist/src/commands/local/index.js +4 -0
- package/dist/src/commands/local/local-api-key.d.ts +44 -0
- package/dist/src/commands/local/local-api-key.js +115 -0
- package/dist/src/commands/local/local-spicedb.d.ts +95 -0
- package/dist/src/commands/local/local-spicedb.js +304 -0
- package/dist/src/commands/local/publish-docs.d.ts +81 -0
- package/dist/src/commands/local/publish-docs.js +328 -0
- package/dist/src/commands/local/seed-zitadel.js +2 -0
- package/dist/src/commands/temporal.js +191 -61
- package/dist/src/commands/temporal.js.map +3 -3
- package/dist/src/docs/convert.d.ts +35 -0
- package/dist/src/docs/convert.js +276 -0
- package/dist/src/docs/preview-source.d.ts +13 -0
- package/dist/src/docs/preview-source.js +58 -0
- package/dist/src/docs/preview.d.ts +32 -0
- package/dist/src/docs/preview.js +279 -0
- package/fragments/agents/AGENTS.md.hbs +27 -27
- package/fragments/base/README.md.hbs +5 -2
- package/fragments/base/index.ts.hbs +17 -30
- package/fragments/base/package.json.hbs +4 -1
- package/fragments/service/api/openapi.json.hbs +1 -1
- package/fragments/service/docs/docs.yaml.hbs +10 -0
- package/fragments/service/docs/pages/getting-started.md.hbs +6 -9
- package/package.json +19 -3
- package/skills/mesh/SKILL.md +2 -2
- package/stack/docker-compose.hub.yml +6 -0
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import * as fs from "node:fs";
|
|
3
|
+
import * as path from "node:path";
|
|
4
|
+
import { execFileSync } from "node:child_process";
|
|
5
|
+
import { pathToFileURL } from "node:url";
|
|
6
|
+
import { API_REGISTRY_SCHEMA_VERSION, apiEntryPath, docsSourceKey, parseDocsConfig, sandboxModeSchema, } from "@mesh-tech/api-registry";
|
|
7
|
+
import { buildDocsSource } from "@mesh-tech/api-registry/docs-source-build";
|
|
8
|
+
import { ARTIFACTS_BUCKET, LOCAL_ENV } from "./seed.js";
|
|
9
|
+
import { localAppNamespace } from "./dev-local.js";
|
|
10
|
+
import { connectLocalSpicedb, localSpicedbIo } from "./local-spicedb.js";
|
|
11
|
+
export function readLocalPublishDeclaration(pkg, appRoot) {
|
|
12
|
+
const problems = [];
|
|
13
|
+
const mesh = pkg?.mesh;
|
|
14
|
+
const out = { problems };
|
|
15
|
+
const docs = mesh?.docs;
|
|
16
|
+
if (docs !== undefined) {
|
|
17
|
+
if (typeof docs?.config !== "string" || !docs.config)
|
|
18
|
+
problems.push("mesh.docs.config must name the docs.yaml");
|
|
19
|
+
const sandbox = docs?.sandbox === undefined ? undefined : sandboxModeSchema.safeParse(docs.sandbox);
|
|
20
|
+
if (sandbox && !sandbox.success)
|
|
21
|
+
problems.push("mesh.docs.sandbox must be live, mock or vendor-sandbox");
|
|
22
|
+
if (docs?.local !== undefined && (typeof docs.local !== "string" || !docs.local.includes("#"))) {
|
|
23
|
+
problems.push("mesh.docs.local must be module#export");
|
|
24
|
+
}
|
|
25
|
+
const integrationTryIt = Array.isArray(docs?.integrationTryIt)
|
|
26
|
+
? [...new Set(docs.integrationTryIt.filter((n) => typeof n === "string" && n.length > 0))].sort()
|
|
27
|
+
: [];
|
|
28
|
+
if (docs?.integrationTryIt !== undefined && !Array.isArray(docs.integrationTryIt)) {
|
|
29
|
+
problems.push("mesh.docs.integrationTryIt must list integration names");
|
|
30
|
+
}
|
|
31
|
+
if (integrationTryIt.length > 0 && docs?.actAs !== true) {
|
|
32
|
+
problems.push("mesh.docs.integrationTryIt needs mesh.docs.actAs: true");
|
|
33
|
+
}
|
|
34
|
+
if (typeof docs?.config === "string" && docs.config) {
|
|
35
|
+
out.docs = {
|
|
36
|
+
config: path.resolve(appRoot, docs.config),
|
|
37
|
+
actAs: docs.actAs === true,
|
|
38
|
+
...(sandbox?.success ? { sandbox: sandbox.data } : {}),
|
|
39
|
+
...(typeof docs.local === "string" && docs.local.includes("#") ? { local: docs.local } : {}),
|
|
40
|
+
...(typeof docs.service === "string" && docs.service ? { service: docs.service } : {}),
|
|
41
|
+
...(integrationTryIt.length > 0 ? { integrationTryIt } : {}),
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
const metadata = mesh?.authz?.metadata;
|
|
46
|
+
if (metadata !== undefined) {
|
|
47
|
+
if (typeof metadata !== "string" || !metadata.includes("#"))
|
|
48
|
+
problems.push("mesh.authz.metadata must be module#export");
|
|
49
|
+
else
|
|
50
|
+
out.authzMetadata = metadata;
|
|
51
|
+
}
|
|
52
|
+
const spicedbSchema = mesh?.authz?.spicedbSchema;
|
|
53
|
+
if (spicedbSchema !== undefined) {
|
|
54
|
+
if (typeof spicedbSchema !== "string" || !spicedbSchema.includes("#"))
|
|
55
|
+
problems.push("mesh.authz.spicedbSchema must be module#export");
|
|
56
|
+
else if (!out.authzMetadata)
|
|
57
|
+
problems.push("mesh.authz.spicedbSchema needs mesh.authz.metadata");
|
|
58
|
+
else
|
|
59
|
+
out.authzSpicedbSchema = spicedbSchema;
|
|
60
|
+
}
|
|
61
|
+
const spicedbSeed = mesh?.authz?.spicedbSeed;
|
|
62
|
+
if (spicedbSeed !== undefined) {
|
|
63
|
+
if (typeof spicedbSeed !== "string" || !spicedbSeed.includes("#"))
|
|
64
|
+
problems.push("mesh.authz.spicedbSeed must be module#export");
|
|
65
|
+
else if (!out.authzSpicedbSchema)
|
|
66
|
+
problems.push("mesh.authz.spicedbSeed needs mesh.authz.spicedbSchema");
|
|
67
|
+
else
|
|
68
|
+
out.authzSpicedbSeed = spicedbSeed;
|
|
69
|
+
}
|
|
70
|
+
return out;
|
|
71
|
+
}
|
|
72
|
+
export async function callExport(appRoot, spec, arg) {
|
|
73
|
+
const [modulePath, exportName] = spec.split("#");
|
|
74
|
+
const file = path.resolve(appRoot, modulePath);
|
|
75
|
+
let mod;
|
|
76
|
+
if (/\.m?js$/.test(file)) {
|
|
77
|
+
mod = (await import(pathToFileURL(file).href));
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
let tsImport;
|
|
81
|
+
try {
|
|
82
|
+
({ tsImport } = await import("tsx/esm/api"));
|
|
83
|
+
}
|
|
84
|
+
catch (err) {
|
|
85
|
+
throw new Error(`${spec}: loading a TypeScript module needs tsx, which the mesh CLI ships as a dependency and could not load (${err.message}). ` +
|
|
86
|
+
"Reinstall @mesh-tech/mesh-cli, or point the declaration at a built .js/.mjs file.");
|
|
87
|
+
}
|
|
88
|
+
mod = (await tsImport(file, import.meta.url));
|
|
89
|
+
}
|
|
90
|
+
const value = mod[exportName];
|
|
91
|
+
if (typeof value !== "function")
|
|
92
|
+
throw new Error(`${spec} is not an exported function`);
|
|
93
|
+
return value(arg);
|
|
94
|
+
}
|
|
95
|
+
export function localDocsEntry(input) {
|
|
96
|
+
return {
|
|
97
|
+
schemaVersion: API_REGISTRY_SCHEMA_VERSION,
|
|
98
|
+
name: input.name,
|
|
99
|
+
kind: "app",
|
|
100
|
+
title: input.title,
|
|
101
|
+
...(input.description ? { description: input.description } : {}),
|
|
102
|
+
surfaces: { http: { url: `http://localhost:${input.httpPort}` } },
|
|
103
|
+
docs: {
|
|
104
|
+
url: input.docs.url,
|
|
105
|
+
contentHash: input.docs.contentHash,
|
|
106
|
+
source: { url: input.docs.sourceUrl, contentHash: input.docs.sourceContentHash },
|
|
107
|
+
},
|
|
108
|
+
enabledOps: [],
|
|
109
|
+
...(input.actAs
|
|
110
|
+
? {
|
|
111
|
+
actAs: {
|
|
112
|
+
url: `http://${input.name}.${input.namespace}:${input.httpPort}`,
|
|
113
|
+
...(input.integrations?.length ? { integrations: [...input.integrations] } : {}),
|
|
114
|
+
},
|
|
115
|
+
}
|
|
116
|
+
: {}),
|
|
117
|
+
...(input.sandbox ? { sandbox: input.sandbox } : {}),
|
|
118
|
+
...(input.eventsEntry ? { events: { entry: input.eventsEntry } } : {}),
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
export const LOCAL_PLATFORM_NETWORK = "mesh-local_mesh_network";
|
|
122
|
+
export function aliasContainerArgs(input) {
|
|
123
|
+
return [
|
|
124
|
+
"run",
|
|
125
|
+
"-d",
|
|
126
|
+
"--rm",
|
|
127
|
+
"--name",
|
|
128
|
+
`mesh-local-alias-${input.namespace}`,
|
|
129
|
+
"--network",
|
|
130
|
+
LOCAL_PLATFORM_NETWORK,
|
|
131
|
+
"--network-alias",
|
|
132
|
+
`${input.name}.${input.namespace}`,
|
|
133
|
+
"--add-host",
|
|
134
|
+
"host.docker.internal:host-gateway",
|
|
135
|
+
"alpine/socat",
|
|
136
|
+
`TCP-LISTEN:${input.port},fork,reuseaddr`,
|
|
137
|
+
`TCP:host.docker.internal:${input.port}`,
|
|
138
|
+
];
|
|
139
|
+
}
|
|
140
|
+
export function aliasContainerName(namespace) {
|
|
141
|
+
return `mesh-local-alias-${namespace}`;
|
|
142
|
+
}
|
|
143
|
+
function docker(args) {
|
|
144
|
+
try {
|
|
145
|
+
return execFileSync("docker", args, { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] });
|
|
146
|
+
}
|
|
147
|
+
catch (err) {
|
|
148
|
+
const stderr = err.stderr?.trim();
|
|
149
|
+
throw new Error(`docker ${args[0]} failed${stderr ? `: ${stderr}` : ""}`);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
export function removeAllAliasContainers() {
|
|
153
|
+
let names = [];
|
|
154
|
+
try {
|
|
155
|
+
names = docker(["ps", "-aq", "--filter", "name=^mesh-local-alias-", "--format", "{{.Names}}"])
|
|
156
|
+
.split("\n")
|
|
157
|
+
.map((n) => n.trim())
|
|
158
|
+
.filter(Boolean);
|
|
159
|
+
}
|
|
160
|
+
catch {
|
|
161
|
+
return [];
|
|
162
|
+
}
|
|
163
|
+
for (const name of names) {
|
|
164
|
+
try {
|
|
165
|
+
docker(["rm", "-f", name]);
|
|
166
|
+
}
|
|
167
|
+
catch {
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return names;
|
|
171
|
+
}
|
|
172
|
+
function ensureAliasContainer(input) {
|
|
173
|
+
try {
|
|
174
|
+
docker(["rm", "-f", aliasContainerName(input.namespace)]);
|
|
175
|
+
}
|
|
176
|
+
catch {
|
|
177
|
+
}
|
|
178
|
+
docker(aliasContainerArgs(input));
|
|
179
|
+
}
|
|
180
|
+
export function resolveLocalSandbox(local, declared) {
|
|
181
|
+
const notes = [];
|
|
182
|
+
const parsed = local.sandbox === undefined ? undefined : sandboxModeSchema.safeParse(local.sandbox);
|
|
183
|
+
if (parsed && !parsed.success)
|
|
184
|
+
notes.push("the local export returned a sandbox that is not live, mock or vendor-sandbox");
|
|
185
|
+
const pins = {};
|
|
186
|
+
for (const [key, value] of Object.entries(local.env ?? {}))
|
|
187
|
+
if (typeof value === "string")
|
|
188
|
+
pins[key] = value;
|
|
189
|
+
const wanted = parsed?.success ? parsed.data : declared;
|
|
190
|
+
if (!wanted || wanted === "live")
|
|
191
|
+
return { sandbox: "live", pins, notes };
|
|
192
|
+
if (!parsed?.success || Object.keys(pins).length === 0) {
|
|
193
|
+
notes.push(`sandbox '${wanted}' is published as 'live': nothing pins the service env that would make it true, so try-it runs reads only`);
|
|
194
|
+
return { sandbox: "live", pins, notes };
|
|
195
|
+
}
|
|
196
|
+
return { sandbox: wanted, pins, notes };
|
|
197
|
+
}
|
|
198
|
+
export async function publishLocalDocs(input) {
|
|
199
|
+
const { declaration, appRoot, tenant, app, awsConfig } = input;
|
|
200
|
+
const docs = declaration.docs;
|
|
201
|
+
const result = {
|
|
202
|
+
problems: [...declaration.problems],
|
|
203
|
+
notes: [],
|
|
204
|
+
pins: {},
|
|
205
|
+
authzPublished: false,
|
|
206
|
+
authzEnv: {},
|
|
207
|
+
docsPublished: false,
|
|
208
|
+
actAs: false,
|
|
209
|
+
};
|
|
210
|
+
const { SSMClient, PutParameterCommand, GetParameterCommand } = await import("@aws-sdk/client-ssm");
|
|
211
|
+
const ssm = new SSMClient(awsConfig);
|
|
212
|
+
const namespace = localAppNamespace(tenant, app);
|
|
213
|
+
if (declaration.authzMetadata && input.zitadel) {
|
|
214
|
+
const metadata = await callExport(appRoot, declaration.authzMetadata);
|
|
215
|
+
const pointerPath = localAuthzPointerPath(tenant, app);
|
|
216
|
+
const metadataPath = `${pointerPath}/ops-hub-metadata`;
|
|
217
|
+
await ssm.send(new PutParameterCommand({ Name: metadataPath, Type: "String", Tier: "Advanced", Overwrite: true, Value: JSON.stringify(metadata) }));
|
|
218
|
+
let current = {};
|
|
219
|
+
try {
|
|
220
|
+
const existing = await ssm.send(new GetParameterCommand({ Name: pointerPath }));
|
|
221
|
+
current = JSON.parse(existing.Parameter?.Value ?? "{}");
|
|
222
|
+
}
|
|
223
|
+
catch {
|
|
224
|
+
}
|
|
225
|
+
let instanceRefs = [];
|
|
226
|
+
const { authzSpicedbSchema: schemaExport, authzSpicedbSeed: seedExport } = declaration;
|
|
227
|
+
if (schemaExport) {
|
|
228
|
+
const connection = await connectLocalSpicedb({
|
|
229
|
+
app,
|
|
230
|
+
tenant,
|
|
231
|
+
schema: () => callExport(appRoot, schemaExport),
|
|
232
|
+
...(seedExport ? { seed: () => callExport(appRoot, seedExport) } : {}),
|
|
233
|
+
io: localSpicedbIo(tenant, awsConfig),
|
|
234
|
+
});
|
|
235
|
+
instanceRefs = connection.instanceRefs;
|
|
236
|
+
result.authzEnv = connection.env;
|
|
237
|
+
result.notes.push(...connection.notes);
|
|
238
|
+
}
|
|
239
|
+
const pointer = {
|
|
240
|
+
...current,
|
|
241
|
+
spicedb: { instanceRefs },
|
|
242
|
+
zitadel: input.zitadel,
|
|
243
|
+
opsHubMetadataRef: metadataPath,
|
|
244
|
+
mode: typeof current.mode === "string" ? current.mode : "policy-engine",
|
|
245
|
+
};
|
|
246
|
+
await ssm.send(new PutParameterCommand({ Name: pointerPath, Type: "String", Overwrite: true, Value: JSON.stringify(pointer) }));
|
|
247
|
+
result.authzPublished = true;
|
|
248
|
+
}
|
|
249
|
+
else if (declaration.authzMetadata) {
|
|
250
|
+
result.notes.push("the app has no local Zitadel project yet, so its API-key metadata was not published");
|
|
251
|
+
}
|
|
252
|
+
if (!docs)
|
|
253
|
+
return result;
|
|
254
|
+
const local = docs.local ? (await callExport(appRoot, docs.local, { ...input.serviceEnv })) : {};
|
|
255
|
+
const sandbox = resolveLocalSandbox(local, docs.sandbox);
|
|
256
|
+
result.pins = sandbox.pins;
|
|
257
|
+
result.notes.push(...sandbox.notes);
|
|
258
|
+
result.sandbox = sandbox.sandbox;
|
|
259
|
+
const built = buildDocsSource({
|
|
260
|
+
configPath: docs.config,
|
|
261
|
+
...(local.availability ? { availability: local.availability } : {}),
|
|
262
|
+
...(local.events ? { events: local.events } : {}),
|
|
263
|
+
});
|
|
264
|
+
result.problems.push(...built.problems);
|
|
265
|
+
const config = parseDocsConfig(fs.readFileSync(docs.config, "utf8"), docs.config);
|
|
266
|
+
const { S3Client, PutObjectCommand } = await import("@aws-sdk/client-s3");
|
|
267
|
+
const s3 = new S3Client({ ...awsConfig, forcePathStyle: true });
|
|
268
|
+
const specKey = `api-docs/${app}/openapi.json`;
|
|
269
|
+
const sourceKey = docsSourceKey(app);
|
|
270
|
+
const specText = fs.readFileSync(built.specPath, "utf8");
|
|
271
|
+
await s3.send(new PutObjectCommand({ Bucket: ARTIFACTS_BUCKET, Key: specKey, Body: specText, ContentType: "application/json" }));
|
|
272
|
+
await s3.send(new PutObjectCommand({ Bucket: ARTIFACTS_BUCKET, Key: sourceKey, Body: built.json, ContentType: "application/json" }));
|
|
273
|
+
if (docs.actAs) {
|
|
274
|
+
try {
|
|
275
|
+
ensureAliasContainer({ name: app, namespace, port: input.httpPort });
|
|
276
|
+
result.actAs = true;
|
|
277
|
+
}
|
|
278
|
+
catch (err) {
|
|
279
|
+
result.notes.push(`try-it is off: ${err instanceof Error ? err.message : String(err)}`);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
const entry = localDocsEntry({
|
|
283
|
+
name: app,
|
|
284
|
+
title: config.title,
|
|
285
|
+
...(config.description ? { description: config.description } : {}),
|
|
286
|
+
docs: {
|
|
287
|
+
url: `s3://${ARTIFACTS_BUCKET}/${specKey}`,
|
|
288
|
+
contentHash: createHash("sha256").update(specText).digest("hex"),
|
|
289
|
+
sourceUrl: `s3://${ARTIFACTS_BUCKET}/${sourceKey}`,
|
|
290
|
+
sourceContentHash: built.contentHash,
|
|
291
|
+
},
|
|
292
|
+
httpPort: input.httpPort,
|
|
293
|
+
namespace,
|
|
294
|
+
actAs: result.actAs,
|
|
295
|
+
...(docs.integrationTryIt?.length ? { integrations: docs.integrationTryIt } : {}),
|
|
296
|
+
sandbox: sandbox.sandbox,
|
|
297
|
+
...(local.events?.service ? { eventsEntry: local.events.service } : {}),
|
|
298
|
+
});
|
|
299
|
+
await ssm.send(new PutParameterCommand({
|
|
300
|
+
Name: apiEntryPath(tenant, LOCAL_ENV, app),
|
|
301
|
+
Type: "String",
|
|
302
|
+
Tier: "Advanced",
|
|
303
|
+
Overwrite: true,
|
|
304
|
+
Value: JSON.stringify(entry),
|
|
305
|
+
}));
|
|
306
|
+
result.docsPublished = true;
|
|
307
|
+
return result;
|
|
308
|
+
}
|
|
309
|
+
export function localAuthzPointerPath(tenant, app) {
|
|
310
|
+
return `/mesh-platform/${tenant}/${LOCAL_ENV}/apps/${app}/stacks/local/authz`;
|
|
311
|
+
}
|
|
312
|
+
export async function unpublishLocalDocs(tenant, app, awsConfig) {
|
|
313
|
+
const removed = [];
|
|
314
|
+
try {
|
|
315
|
+
docker(["rm", "-f", aliasContainerName(localAppNamespace(tenant, app))]);
|
|
316
|
+
removed.push("alias container");
|
|
317
|
+
}
|
|
318
|
+
catch {
|
|
319
|
+
}
|
|
320
|
+
try {
|
|
321
|
+
const { SSMClient, DeleteParameterCommand } = await import("@aws-sdk/client-ssm");
|
|
322
|
+
await new SSMClient(awsConfig).send(new DeleteParameterCommand({ Name: apiEntryPath(tenant, LOCAL_ENV, app) }));
|
|
323
|
+
removed.push("registry entry");
|
|
324
|
+
}
|
|
325
|
+
catch {
|
|
326
|
+
}
|
|
327
|
+
return removed;
|
|
328
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as fs from "fs";
|
|
2
|
+
import { publishLocalHubActAsKey } from "./act-as-local.js";
|
|
2
3
|
import * as os from "os";
|
|
3
4
|
import * as path from "path";
|
|
4
5
|
import { hubOperatorRoleKeys, hubRoleKeyTenant, restrictedRoleBase } from "@mesh-tech/api-registry";
|
|
@@ -438,6 +439,7 @@ export async function seedZitadel(awsConfig) {
|
|
|
438
439
|
await publishHubAuthzPointer(hubAuth.projectId, org.result[0].id, awsConfig);
|
|
439
440
|
await writeRegistryParams(cliClientId, awsConfig);
|
|
440
441
|
await ensureOpsHubAdmin(pat, awsConfig);
|
|
442
|
+
await publishLocalHubActAsKey(awsConfig).catch((err) => logWarn(`Hub act-as key not published: ${err instanceof Error ? err.message : err} — local try-it will be refused`));
|
|
441
443
|
writeContextConfig(LOGIN_CONTEXT, { issuer: ZITADEL_ISSUER, clientId: cliClientId });
|
|
442
444
|
logSuccess(`Login context '${LOGIN_CONTEXT}' configured → try: mesh login ${LOGIN_CONTEXT}`);
|
|
443
445
|
return { projectId, cliClientId, hubAuth };
|
|
@@ -499,6 +499,90 @@ var init_mocks = __esm({
|
|
|
499
499
|
}
|
|
500
500
|
});
|
|
501
501
|
|
|
502
|
+
// packages/api-registry/src/docs-source.ts
|
|
503
|
+
import { parse as parseYaml2 } from "yaml";
|
|
504
|
+
import { z } from "zod";
|
|
505
|
+
function isReaderFacingServerUrl(value) {
|
|
506
|
+
let url;
|
|
507
|
+
try {
|
|
508
|
+
url = new URL(value);
|
|
509
|
+
} catch {
|
|
510
|
+
return false;
|
|
511
|
+
}
|
|
512
|
+
if (url.protocol !== "https:" && url.protocol !== "http:") return false;
|
|
513
|
+
const host = url.hostname.toLowerCase();
|
|
514
|
+
if (host.startsWith("[") || /^\d+(\.\d+){3}$/.test(host)) return false;
|
|
515
|
+
const labels = host.split(".");
|
|
516
|
+
if (labels.length < 2) return false;
|
|
517
|
+
const tld = labels[labels.length - 1];
|
|
518
|
+
if (["svc", "local", "internal", "localhost", "cluster"].includes(tld)) return false;
|
|
519
|
+
if (host.endsWith(".svc.cluster.local") || host.endsWith(".cluster.local")) return false;
|
|
520
|
+
return /^[a-z]{2,63}$/.test(tld) || /^xn--[a-z0-9-]{1,59}$/.test(tld);
|
|
521
|
+
}
|
|
522
|
+
var PAGE_ID_PATTERN, pageIdSchema, docsConfigSchema, OPERATION_AVAILABILITY_STATES, operationAvailabilitySchema, docsEventsCatalogSchema, SANDBOX_MODES, sandboxModeSchema, MAX_DOCS_IMAGE_BYTES, MAX_DOCS_IMAGES_TOTAL_BYTES, IMAGE_KEY_PATTERN, BASE64_PATTERN, docsSourceBundleSchema;
|
|
523
|
+
var init_docs_source = __esm({
|
|
524
|
+
"packages/api-registry/src/docs-source.ts"() {
|
|
525
|
+
"use strict";
|
|
526
|
+
PAGE_ID_PATTERN = /^[a-z0-9][a-z0-9_-]*(?:\/[a-z0-9][a-z0-9_-]*)*$/;
|
|
527
|
+
pageIdSchema = z.string().regex(PAGE_ID_PATTERN, "a page id is a lower-case path under pages/, without .md");
|
|
528
|
+
docsConfigSchema = z.object({
|
|
529
|
+
title: z.string().min(1),
|
|
530
|
+
description: z.string().min(1).optional(),
|
|
531
|
+
spec: z.string().min(1).optional(),
|
|
532
|
+
pages: z.string().min(1).optional(),
|
|
533
|
+
landing: pageIdSchema.optional(),
|
|
534
|
+
serverUrl: z.string().url().refine(isReaderFacingServerUrl, "serverUrl must be a reader-facing http(s) URL, never an in-cluster or local address").optional(),
|
|
535
|
+
nav: z.array(z.object({ group: z.string().min(1), pages: z.array(pageIdSchema).min(1) }).strict()).optional(),
|
|
536
|
+
reference: z.union([z.object({ label: z.string().min(1).optional() }).strict(), z.literal(false)]).optional(),
|
|
537
|
+
events: z.string().min(1).optional()
|
|
538
|
+
}).strict();
|
|
539
|
+
OPERATION_AVAILABILITY_STATES = ["available", "not_enabled", "not_implemented", "not_supported"];
|
|
540
|
+
operationAvailabilitySchema = z.object({
|
|
541
|
+
state: z.enum(OPERATION_AVAILABILITY_STATES),
|
|
542
|
+
reason: z.string().min(1).optional()
|
|
543
|
+
});
|
|
544
|
+
docsEventsCatalogSchema = z.object({
|
|
545
|
+
service: z.string().min(1),
|
|
546
|
+
types: z.array(
|
|
547
|
+
z.object({
|
|
548
|
+
name: z.string().min(1),
|
|
549
|
+
description: z.string().optional(),
|
|
550
|
+
schemaVersion: z.number().int().positive().optional(),
|
|
551
|
+
payloadSchema: z.record(z.string(), z.unknown()).optional()
|
|
552
|
+
})
|
|
553
|
+
),
|
|
554
|
+
delivery: z.object({
|
|
555
|
+
payload: z.enum(["thin", "full"]).optional(),
|
|
556
|
+
retry: z.object({ maxAttempts: z.number().int().positive().optional() }).optional(),
|
|
557
|
+
signing: z.array(z.string().min(1)).optional()
|
|
558
|
+
}).optional()
|
|
559
|
+
});
|
|
560
|
+
SANDBOX_MODES = ["live", "mock", "vendor-sandbox"];
|
|
561
|
+
sandboxModeSchema = z.enum(SANDBOX_MODES);
|
|
562
|
+
MAX_DOCS_IMAGE_BYTES = 1024 * 1024;
|
|
563
|
+
MAX_DOCS_IMAGES_TOTAL_BYTES = 4 * 1024 * 1024;
|
|
564
|
+
IMAGE_KEY_PATTERN = /^[A-Za-z0-9_-][A-Za-z0-9._-]*(?:\/[A-Za-z0-9_-][A-Za-z0-9._-]*)*$/;
|
|
565
|
+
BASE64_PATTERN = /^[A-Za-z0-9+/]*={0,2}$/;
|
|
566
|
+
docsSourceBundleSchema = z.object({
|
|
567
|
+
bundleVersion: z.number().int().positive(),
|
|
568
|
+
config: docsConfigSchema,
|
|
569
|
+
pages: z.record(
|
|
570
|
+
pageIdSchema,
|
|
571
|
+
z.object({ title: z.string().min(1), description: z.string().optional(), markdown: z.string() })
|
|
572
|
+
),
|
|
573
|
+
availability: z.record(z.string().min(1), operationAvailabilitySchema).optional(),
|
|
574
|
+
events: docsEventsCatalogSchema.optional(),
|
|
575
|
+
images: z.record(
|
|
576
|
+
z.string().regex(IMAGE_KEY_PATTERN, "an image key is a path under pages/"),
|
|
577
|
+
z.object({
|
|
578
|
+
contentType: z.enum(["image/png", "image/jpeg", "image/gif", "image/webp"]),
|
|
579
|
+
data: z.string().regex(BASE64_PATTERN, "image data is base64")
|
|
580
|
+
}).strict()
|
|
581
|
+
).optional()
|
|
582
|
+
});
|
|
583
|
+
}
|
|
584
|
+
});
|
|
585
|
+
|
|
502
586
|
// packages/api-registry/src/hub-roles.ts
|
|
503
587
|
function hubOperatorRoleKeys(tenants) {
|
|
504
588
|
const cleaned = [...new Set(tenants.filter((t) => t !== ""))].sort();
|
|
@@ -542,93 +626,106 @@ var init_hub_roles = __esm({
|
|
|
542
626
|
});
|
|
543
627
|
|
|
544
628
|
// packages/api-registry/src/index.ts
|
|
545
|
-
import { z } from "zod";
|
|
629
|
+
import { z as z2 } from "zod";
|
|
546
630
|
var rateLimitSpecSchema, rateLimitDefaultsSchema, integrationHealthSchema, apiSurfaceKindSchema, apiDocsSiteAudienceSchema, apiSurfaceSchema, apiRegistryEntrySchema, integrationStatusSchema, hubActAsExportsSchema;
|
|
547
631
|
var init_src = __esm({
|
|
548
632
|
"packages/api-registry/src/index.ts"() {
|
|
549
633
|
"use strict";
|
|
634
|
+
init_docs_source();
|
|
550
635
|
init_hub_roles();
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
636
|
+
init_docs_source();
|
|
637
|
+
rateLimitSpecSchema = z2.object({
|
|
638
|
+
rps: z2.number().int().min(1),
|
|
639
|
+
burst: z2.number().int().min(1)
|
|
554
640
|
});
|
|
555
|
-
rateLimitDefaultsSchema =
|
|
556
|
-
integrationHealthSchema =
|
|
557
|
-
|
|
558
|
-
|
|
641
|
+
rateLimitDefaultsSchema = z2.record(z2.string().min(1), rateLimitSpecSchema);
|
|
642
|
+
integrationHealthSchema = z2.union([
|
|
643
|
+
z2.object({ op: z2.string().trim().min(1) }),
|
|
644
|
+
z2.object({ unavailable: z2.string().trim().min(1) })
|
|
559
645
|
]);
|
|
560
|
-
apiSurfaceKindSchema =
|
|
561
|
-
apiDocsSiteAudienceSchema =
|
|
562
|
-
|
|
563
|
-
|
|
646
|
+
apiSurfaceKindSchema = z2.enum(["app", "vendor", "events"]);
|
|
647
|
+
apiDocsSiteAudienceSchema = z2.discriminatedUnion("readers", [
|
|
648
|
+
z2.object({ readers: z2.literal("partner"), roles: z2.array(z2.string().min(1)) }),
|
|
649
|
+
z2.object({ readers: z2.literal("operator") })
|
|
564
650
|
]);
|
|
565
|
-
apiSurfaceSchema =
|
|
566
|
-
http:
|
|
567
|
-
nexus:
|
|
651
|
+
apiSurfaceSchema = z2.object({
|
|
652
|
+
http: z2.object({ url: z2.string().min(1) }).optional(),
|
|
653
|
+
nexus: z2.object({ endpoint: z2.string().min(1), taskQueue: z2.string().min(1) }).optional()
|
|
568
654
|
});
|
|
569
|
-
apiRegistryEntrySchema =
|
|
570
|
-
schemaVersion:
|
|
571
|
-
name:
|
|
655
|
+
apiRegistryEntrySchema = z2.object({
|
|
656
|
+
schemaVersion: z2.number().int().positive().default(1),
|
|
657
|
+
name: z2.string().min(1),
|
|
572
658
|
kind: apiSurfaceKindSchema.optional(),
|
|
573
|
-
provider:
|
|
574
|
-
externalService:
|
|
575
|
-
version:
|
|
576
|
-
title:
|
|
577
|
-
description:
|
|
659
|
+
provider: z2.string().optional(),
|
|
660
|
+
externalService: z2.string().min(1).optional(),
|
|
661
|
+
version: z2.string().optional(),
|
|
662
|
+
title: z2.string().optional(),
|
|
663
|
+
description: z2.string().optional(),
|
|
578
664
|
surfaces: apiSurfaceSchema,
|
|
579
|
-
credentials:
|
|
580
|
-
docs:
|
|
581
|
-
url:
|
|
582
|
-
contentHash:
|
|
583
|
-
trimmed:
|
|
584
|
-
siteUrl:
|
|
585
|
-
audience: apiDocsSiteAudienceSchema.optional()
|
|
665
|
+
credentials: z2.object({ keyedBy: z2.string().nullish() }).optional(),
|
|
666
|
+
docs: z2.object({
|
|
667
|
+
url: z2.string().min(1),
|
|
668
|
+
contentHash: z2.string().min(1),
|
|
669
|
+
trimmed: z2.object({ url: z2.string().min(1), contentHash: z2.string().min(1) }).optional(),
|
|
670
|
+
siteUrl: z2.string().min(1).optional(),
|
|
671
|
+
audience: apiDocsSiteAudienceSchema.optional(),
|
|
672
|
+
source: z2.object({ url: z2.string().min(1), contentHash: z2.string().min(1) }).optional()
|
|
586
673
|
}),
|
|
587
|
-
enabledOps:
|
|
674
|
+
enabledOps: z2.array(z2.string()),
|
|
588
675
|
// The consumer schema strips unknown keys, so a producer-side field that is
|
|
589
676
|
// not mirrored here never reaches a consumer — mirror every addition.
|
|
590
|
-
status:
|
|
591
|
-
actAs:
|
|
592
|
-
appVersion:
|
|
677
|
+
status: z2.object({ url: z2.string().min(1) }).optional(),
|
|
678
|
+
actAs: z2.object({ url: z2.string().min(1), integrations: z2.array(z2.string().min(1)).optional() }).optional(),
|
|
679
|
+
appVersion: z2.string().min(1).optional(),
|
|
593
680
|
rateLimits: rateLimitDefaultsSchema.optional(),
|
|
594
681
|
health: integrationHealthSchema.optional(),
|
|
595
|
-
producedRateClasses:
|
|
682
|
+
producedRateClasses: z2.array(z2.string().min(1)).optional(),
|
|
683
|
+
sandbox: sandboxModeSchema.optional(),
|
|
684
|
+
events: z2.object({ entry: z2.string().min(1) }).optional()
|
|
596
685
|
});
|
|
597
|
-
integrationStatusSchema =
|
|
598
|
-
contract:
|
|
599
|
-
name:
|
|
600
|
-
provider:
|
|
601
|
-
type:
|
|
602
|
-
title:
|
|
603
|
-
definitionVersion:
|
|
604
|
-
packageVersion:
|
|
605
|
-
tenant:
|
|
606
|
-
instanceKey:
|
|
607
|
-
surfaces:
|
|
608
|
-
coreMode:
|
|
609
|
-
coreModeSource:
|
|
610
|
-
enabledOps:
|
|
611
|
-
credentialsWired:
|
|
686
|
+
integrationStatusSchema = z2.object({
|
|
687
|
+
contract: z2.literal("v1"),
|
|
688
|
+
name: z2.string().min(1),
|
|
689
|
+
provider: z2.string().optional(),
|
|
690
|
+
type: z2.string().optional(),
|
|
691
|
+
title: z2.string().optional(),
|
|
692
|
+
definitionVersion: z2.string().optional(),
|
|
693
|
+
packageVersion: z2.string().min(1).optional(),
|
|
694
|
+
tenant: z2.string().min(1),
|
|
695
|
+
instanceKey: z2.string().optional(),
|
|
696
|
+
surfaces: z2.array(z2.enum(["http", "nexus"])),
|
|
697
|
+
coreMode: z2.enum(["mock", "live"]),
|
|
698
|
+
coreModeSource: z2.string().optional(),
|
|
699
|
+
enabledOps: z2.array(z2.string()),
|
|
700
|
+
credentialsWired: z2.boolean(),
|
|
612
701
|
rateLimits: rateLimitDefaultsSchema.optional(),
|
|
613
702
|
health: integrationHealthSchema.optional(),
|
|
614
|
-
startedAt:
|
|
703
|
+
startedAt: z2.string().min(1)
|
|
615
704
|
});
|
|
616
|
-
hubActAsExportsSchema =
|
|
617
|
-
issuer:
|
|
618
|
-
keys:
|
|
619
|
-
|
|
620
|
-
kid:
|
|
621
|
-
kty:
|
|
622
|
-
n:
|
|
623
|
-
e:
|
|
624
|
-
alg:
|
|
625
|
-
use:
|
|
705
|
+
hubActAsExportsSchema = z2.object({
|
|
706
|
+
issuer: z2.string().min(1),
|
|
707
|
+
keys: z2.array(
|
|
708
|
+
z2.object({
|
|
709
|
+
kid: z2.string().min(1),
|
|
710
|
+
kty: z2.literal("RSA"),
|
|
711
|
+
n: z2.string().min(1),
|
|
712
|
+
e: z2.string().min(1),
|
|
713
|
+
alg: z2.string().optional(),
|
|
714
|
+
use: z2.string().optional()
|
|
626
715
|
})
|
|
627
716
|
).min(1)
|
|
628
717
|
});
|
|
629
718
|
}
|
|
630
719
|
});
|
|
631
720
|
|
|
721
|
+
// packages/mesh-cli/src/commands/local/act-as-local.ts
|
|
722
|
+
var init_act_as_local = __esm({
|
|
723
|
+
"packages/mesh-cli/src/commands/local/act-as-local.ts"() {
|
|
724
|
+
"use strict";
|
|
725
|
+
init_seed();
|
|
726
|
+
}
|
|
727
|
+
});
|
|
728
|
+
|
|
632
729
|
// packages/mesh-cli/src/commands/local/mock-idps.ts
|
|
633
730
|
var init_mock_idps = __esm({
|
|
634
731
|
"packages/mesh-cli/src/commands/local/mock-idps.ts"() {
|
|
@@ -643,6 +740,7 @@ var HUB_DEFAULT_REDIRECT_URI, HUB_ROLES, ZITADEL_SSM_PARAM, TEST_USERS_SSM_PREFI
|
|
|
643
740
|
var init_seed_zitadel = __esm({
|
|
644
741
|
"packages/mesh-cli/src/commands/local/seed-zitadel.ts"() {
|
|
645
742
|
"use strict";
|
|
743
|
+
init_act_as_local();
|
|
646
744
|
init_src();
|
|
647
745
|
init_log();
|
|
648
746
|
init_errors();
|
|
@@ -686,6 +784,34 @@ var init_dev_local = __esm({
|
|
|
686
784
|
}
|
|
687
785
|
});
|
|
688
786
|
|
|
787
|
+
// packages/mesh-cli/src/commands/local/local-spicedb.ts
|
|
788
|
+
var init_local_spicedb = __esm({
|
|
789
|
+
"packages/mesh-cli/src/commands/local/local-spicedb.ts"() {
|
|
790
|
+
"use strict";
|
|
791
|
+
init_seed();
|
|
792
|
+
init_aws_errors();
|
|
793
|
+
init_dev_local();
|
|
794
|
+
}
|
|
795
|
+
});
|
|
796
|
+
|
|
797
|
+
// packages/mesh-cli/src/commands/local/publish-docs.ts
|
|
798
|
+
var init_publish_docs = __esm({
|
|
799
|
+
"packages/mesh-cli/src/commands/local/publish-docs.ts"() {
|
|
800
|
+
"use strict";
|
|
801
|
+
init_seed();
|
|
802
|
+
init_dev_local();
|
|
803
|
+
init_local_spicedb();
|
|
804
|
+
}
|
|
805
|
+
});
|
|
806
|
+
|
|
807
|
+
// packages/mesh-cli/src/commands/local/local-api-key.ts
|
|
808
|
+
var init_local_api_key = __esm({
|
|
809
|
+
"packages/mesh-cli/src/commands/local/local-api-key.ts"() {
|
|
810
|
+
"use strict";
|
|
811
|
+
init_seed();
|
|
812
|
+
}
|
|
813
|
+
});
|
|
814
|
+
|
|
689
815
|
// packages/mesh-cli/src/commands/local/docker-runner.ts
|
|
690
816
|
var init_docker_runner = __esm({
|
|
691
817
|
"packages/mesh-cli/src/commands/local/docker-runner.ts"() {
|
|
@@ -748,6 +874,10 @@ var init_dev = __esm({
|
|
|
748
874
|
init_dev_local();
|
|
749
875
|
init_auth_provision();
|
|
750
876
|
init_seed();
|
|
877
|
+
init_publish_docs();
|
|
878
|
+
init_act_as_local();
|
|
879
|
+
init_local_api_key();
|
|
880
|
+
init_seed_zitadel();
|
|
751
881
|
init_mocks();
|
|
752
882
|
init_dev_local();
|
|
753
883
|
init_stack();
|