@mesh-tech/mesh-cli 0.13.0 → 0.14.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/dist/bin/mesh.js +254 -51
- package/dist/bin/mesh.js.map +4 -4
- package/dist/build-info.json +2 -2
- package/dist/src/commands/secrets/index.d.ts +1 -0
- package/dist/src/commands/secrets/index.d.ts.map +1 -1
- package/dist/src/commands/secrets/index.js +12 -0
- package/dist/src/commands/secrets/index.js.map +1 -1
- package/dist/src/commands/secrets/reindex.d.ts +48 -0
- package/dist/src/commands/secrets/reindex.d.ts.map +1 -0
- package/dist/src/commands/secrets/reindex.js +157 -0
- package/dist/src/commands/secrets/reindex.js.map +1 -0
- package/dist/src/commands/secrets/set.d.ts +63 -0
- package/dist/src/commands/secrets/set.d.ts.map +1 -1
- package/dist/src/commands/secrets/set.js +78 -3
- package/dist/src/commands/secrets/set.js.map +1 -1
- package/package.json +3 -2
- package/skills/core/SKILL.md +3 -1
package/dist/bin/mesh.js
CHANGED
|
@@ -1859,8 +1859,8 @@ function resolveHubPlatformName(platform) {
|
|
|
1859
1859
|
return platform?.name ?? "mesh";
|
|
1860
1860
|
}
|
|
1861
1861
|
async function ssmGetParameter(name) {
|
|
1862
|
-
const { SSMClient:
|
|
1863
|
-
const ssm = new
|
|
1862
|
+
const { SSMClient: SSMClient5, GetParameterCommand: GetParameterCommand2 } = await import("@aws-sdk/client-ssm");
|
|
1863
|
+
const ssm = new SSMClient5({ region: process.env.AWS_REGION || "us-east-2" });
|
|
1864
1864
|
const resp = await ssm.send(new GetParameterCommand2({ Name: name, WithDecryption: true }));
|
|
1865
1865
|
return resp.Parameter?.Value;
|
|
1866
1866
|
}
|
|
@@ -1901,8 +1901,8 @@ var init_kubeconfig = __esm({
|
|
|
1901
1901
|
// libs/mesh-cli/src/utils/temporal-auth.ts
|
|
1902
1902
|
import { execFileSync as execFileSync3 } from "node:child_process";
|
|
1903
1903
|
async function resolveTemporalAuth(tenant, env, platformName = tenant) {
|
|
1904
|
-
const { SSMClient:
|
|
1905
|
-
const ssm = new
|
|
1904
|
+
const { SSMClient: SSMClient5, GetParameterCommand: GetParameterCommand2 } = await import("@aws-sdk/client-ssm");
|
|
1905
|
+
const ssm = new SSMClient5({ region: process.env.AWS_REGION || "us-east-2" });
|
|
1906
1906
|
const results = {};
|
|
1907
1907
|
async function trySSM(name) {
|
|
1908
1908
|
try {
|
|
@@ -2297,8 +2297,8 @@ function buildFabricCheckPayload(targetBytes = 8e3) {
|
|
|
2297
2297
|
return JSON.stringify({ ...base, pad: "x".repeat(Math.max(0, targetBytes - overhead)) });
|
|
2298
2298
|
}
|
|
2299
2299
|
async function verifyFabric() {
|
|
2300
|
-
const { SSMClient:
|
|
2301
|
-
const ssm = new
|
|
2300
|
+
const { SSMClient: SSMClient5, PutParameterCommand, GetParameterCommand: GetParameterCommand2, GetParametersByPathCommand: GetParametersByPathCommand3, DeleteParametersCommand } = await import("@aws-sdk/client-ssm");
|
|
2301
|
+
const ssm = new SSMClient5(AWS_CONFIG);
|
|
2302
2302
|
const payload = buildFabricCheckPayload();
|
|
2303
2303
|
const mainParam = FABRIC_CHECK_PATH;
|
|
2304
2304
|
const childParams = [`${FABRIC_CHECK_PATH}/vpc`, `${FABRIC_CHECK_PATH}/eks`];
|
|
@@ -2350,8 +2350,8 @@ async function verifyFabric() {
|
|
|
2350
2350
|
}
|
|
2351
2351
|
}
|
|
2352
2352
|
async function registerTenantEnv(tenant, opts = {}) {
|
|
2353
|
-
const { SSMClient:
|
|
2354
|
-
const ssm = new
|
|
2353
|
+
const { SSMClient: SSMClient5, PutParameterCommand } = await import("@aws-sdk/client-ssm");
|
|
2354
|
+
const ssm = new SSMClient5(AWS_CONFIG);
|
|
2355
2355
|
await ssm.send(
|
|
2356
2356
|
new PutParameterCommand({
|
|
2357
2357
|
Name: `/mesh-platform/${tenant}`,
|
|
@@ -2379,8 +2379,8 @@ async function registerTenantEnv(tenant, opts = {}) {
|
|
|
2379
2379
|
);
|
|
2380
2380
|
}
|
|
2381
2381
|
async function seedLocalPlatform() {
|
|
2382
|
-
const { SSMClient:
|
|
2383
|
-
const ssm = new
|
|
2382
|
+
const { SSMClient: SSMClient5, PutParameterCommand } = await import("@aws-sdk/client-ssm");
|
|
2383
|
+
const ssm = new SSMClient5(AWS_CONFIG);
|
|
2384
2384
|
await ssm.send(
|
|
2385
2385
|
new PutParameterCommand({
|
|
2386
2386
|
Name: APP_TENANTS_PARAM,
|
|
@@ -2434,14 +2434,14 @@ var init_seed = __esm({
|
|
|
2434
2434
|
// libs/mesh-cli/src/commands/local/helpers.ts
|
|
2435
2435
|
import * as net2 from "net";
|
|
2436
2436
|
async function upsertLocalSecret(secretId, value, client) {
|
|
2437
|
-
const { SecretsManagerClient:
|
|
2438
|
-
const sm = client ?? new
|
|
2437
|
+
const { SecretsManagerClient: SecretsManagerClient9, CreateSecretCommand: CreateSecretCommand4, PutSecretValueCommand: PutSecretValueCommand4 } = await import("@aws-sdk/client-secrets-manager");
|
|
2438
|
+
const sm = client ?? new SecretsManagerClient9(LOCAL_AWS_CONFIG);
|
|
2439
2439
|
const secretString = JSON.stringify(value);
|
|
2440
2440
|
try {
|
|
2441
|
-
await sm.send(new
|
|
2441
|
+
await sm.send(new CreateSecretCommand4({ Name: secretId, SecretString: secretString }));
|
|
2442
2442
|
} catch (err) {
|
|
2443
2443
|
if (err?.name === "ResourceExistsException") {
|
|
2444
|
-
await sm.send(new
|
|
2444
|
+
await sm.send(new PutSecretValueCommand4({ SecretId: secretId, SecretString: secretString }));
|
|
2445
2445
|
} else {
|
|
2446
2446
|
throw err;
|
|
2447
2447
|
}
|
|
@@ -3158,12 +3158,12 @@ async function fetchRemoteExternalCredentials(tenant, env, external, profile) {
|
|
|
3158
3158
|
const region = process.env.MESH_PLATFORM_REGION ?? process.env.AWS_REGION ?? "us-east-2";
|
|
3159
3159
|
const fallbackProfile = !process.env.AWS_ACCESS_KEY_ID && !process.env.AWS_PROFILE ? process.env.MESH_AWS_PROFILE ?? profile : void 0;
|
|
3160
3160
|
try {
|
|
3161
|
-
const { SecretsManagerClient:
|
|
3161
|
+
const { SecretsManagerClient: SecretsManagerClient9, GetSecretValueCommand: GetSecretValueCommand9 } = await import("@aws-sdk/client-secrets-manager");
|
|
3162
3162
|
if (fallbackProfile) process.env.AWS_PROFILE = fallbackProfile;
|
|
3163
|
-
const sm = new
|
|
3163
|
+
const sm = new SecretsManagerClient9({ region });
|
|
3164
3164
|
let res;
|
|
3165
3165
|
try {
|
|
3166
|
-
res = await sm.send(new
|
|
3166
|
+
res = await sm.send(new GetSecretValueCommand9({ SecretId: secretId }));
|
|
3167
3167
|
} finally {
|
|
3168
3168
|
if (fallbackProfile) delete process.env.AWS_PROFILE;
|
|
3169
3169
|
}
|
|
@@ -3277,8 +3277,8 @@ async function seedLocalMock(args) {
|
|
|
3277
3277
|
`${secretName}/.config`,
|
|
3278
3278
|
Object.fromEntries(Object.entries(value).filter(([key]) => !SECRET_KEY_RE.test(key)))
|
|
3279
3279
|
);
|
|
3280
|
-
const { SSMClient:
|
|
3281
|
-
const ssm = new
|
|
3280
|
+
const { SSMClient: SSMClient5, PutParameterCommand } = await import("@aws-sdk/client-ssm");
|
|
3281
|
+
const ssm = new SSMClient5(LOCAL_AWS_CONFIG);
|
|
3282
3282
|
const base = `/mesh-platform/${tenant}/${LOCAL_ENV}/apps/${app}/stacks/local/external-services/${decl.external}`;
|
|
3283
3283
|
await ssm.send(
|
|
3284
3284
|
new PutParameterCommand({
|
|
@@ -3720,8 +3720,8 @@ async function seedHubAuth(pat) {
|
|
|
3720
3720
|
return config;
|
|
3721
3721
|
}
|
|
3722
3722
|
async function writeRegistryParams(cliClientId, awsConfig) {
|
|
3723
|
-
const { SSMClient:
|
|
3724
|
-
const ssm = new
|
|
3723
|
+
const { SSMClient: SSMClient5, PutParameterCommand } = await import("@aws-sdk/client-ssm");
|
|
3724
|
+
const ssm = new SSMClient5(awsConfig);
|
|
3725
3725
|
await ssm.send(
|
|
3726
3726
|
new PutParameterCommand({
|
|
3727
3727
|
Name: ZITADEL_SSM_PARAM,
|
|
@@ -3911,9 +3911,9 @@ async function ensureM2mCaller(pat, tenant, app, orgId, projectId, roles) {
|
|
|
3911
3911
|
return false;
|
|
3912
3912
|
}
|
|
3913
3913
|
async function authSecretExists(tenant, app, service) {
|
|
3914
|
-
const { SecretsManagerClient:
|
|
3915
|
-
const sm = new
|
|
3916
|
-
return sm.send(new
|
|
3914
|
+
const { SecretsManagerClient: SecretsManagerClient9, GetSecretValueCommand: GetSecretValueCommand9 } = await import("@aws-sdk/client-secrets-manager");
|
|
3915
|
+
const sm = new SecretsManagerClient9(LOCAL_AWS_CONFIG);
|
|
3916
|
+
return sm.send(new GetSecretValueCommand9({ SecretId: authSecretPath(tenant, app, service) })).then(
|
|
3917
3917
|
() => true,
|
|
3918
3918
|
() => false
|
|
3919
3919
|
);
|
|
@@ -3924,8 +3924,8 @@ async function writeAuthSecret(tenant, app, service, value) {
|
|
|
3924
3924
|
logSuccess(`Stored service credentials \u2192 ${name}`);
|
|
3925
3925
|
}
|
|
3926
3926
|
async function registerLocalApp(args) {
|
|
3927
|
-
const { SSMClient:
|
|
3928
|
-
const ssm = new
|
|
3927
|
+
const { SSMClient: SSMClient5, PutParameterCommand } = await import("@aws-sdk/client-ssm");
|
|
3928
|
+
const ssm = new SSMClient5(LOCAL_AWS_CONFIG);
|
|
3929
3929
|
const put2 = (name, value, description) => ssm.send(
|
|
3930
3930
|
new PutParameterCommand({ Name: name, Type: "String", Overwrite: true, Value: JSON.stringify(value), Description: description })
|
|
3931
3931
|
);
|
|
@@ -3975,8 +3975,8 @@ async function registerLocalApp(args) {
|
|
|
3975
3975
|
}
|
|
3976
3976
|
async function reconcileRegistryFromZitadel() {
|
|
3977
3977
|
const pat = readSeederPat();
|
|
3978
|
-
const { SSMClient:
|
|
3979
|
-
const ssm = new
|
|
3978
|
+
const { SSMClient: SSMClient5, GetParameterCommand: GetParameterCommand2, PutParameterCommand } = await import("@aws-sdk/client-ssm");
|
|
3979
|
+
const ssm = new SSMClient5(LOCAL_AWS_CONFIG);
|
|
3980
3980
|
const orgs = await api(pat, "POST", "/admin/v1/orgs/_search", { query: { limit: 200 } });
|
|
3981
3981
|
const tenants = [];
|
|
3982
3982
|
let apps = 0;
|
|
@@ -7297,8 +7297,8 @@ function registerDevCommand(program2) {
|
|
|
7297
7297
|
});
|
|
7298
7298
|
dev.command("test-user [name]").description("Get Temporal test user credentials (from Pulumi-managed test users)").option("--tenant <tenant>", "Tenant name", "mesh").option("--env <env>", "Environment", "dev").option("--region <region>", "AWS region", "us-east-2").action(
|
|
7299
7299
|
async (name, opts) => {
|
|
7300
|
-
const { SSMClient:
|
|
7301
|
-
const ssm = new
|
|
7300
|
+
const { SSMClient: SSMClient5, GetParameterCommand: GetParameterCommand2, GetParametersByPathCommand: GetParametersByPathCommand3 } = await import("@aws-sdk/client-ssm");
|
|
7301
|
+
const ssm = new SSMClient5({ region: opts.region });
|
|
7302
7302
|
const basePath = `/mesh-platform/${opts.tenant}/${opts.env}/temporal/test-users`;
|
|
7303
7303
|
if (!name) {
|
|
7304
7304
|
try {
|
|
@@ -9291,9 +9291,9 @@ function parseTenantEnv(context) {
|
|
|
9291
9291
|
}
|
|
9292
9292
|
async function getRegistrySsmExport(tenant, env) {
|
|
9293
9293
|
try {
|
|
9294
|
-
const { SSMClient:
|
|
9294
|
+
const { SSMClient: SSMClient5, GetParameterCommand: GetParameterCommand2 } = await import("@aws-sdk/client-ssm");
|
|
9295
9295
|
const region = process.env.AWS_REGION ?? process.env.AWS_DEFAULT_REGION ?? CA_REGION;
|
|
9296
|
-
const ssm = new
|
|
9296
|
+
const ssm = new SSMClient5({ region });
|
|
9297
9297
|
const ssmPath = `/mesh-platform/${tenant}/${env}/registry`;
|
|
9298
9298
|
const response = await ssm.send(new GetParameterCommand2({ Name: ssmPath }));
|
|
9299
9299
|
if (!response.Parameter?.Value) return null;
|
|
@@ -9883,9 +9883,9 @@ async function discoverConfigFromSsm(context) {
|
|
|
9883
9883
|
const ssmPath = `/mesh-platform/${tenant}/${env}/platform/zitadel`;
|
|
9884
9884
|
logInfo(`Attempting SSM discovery from ${ssmPath}...`);
|
|
9885
9885
|
try {
|
|
9886
|
-
const { SSMClient:
|
|
9886
|
+
const { SSMClient: SSMClient5, GetParameterCommand: GetParameterCommand2 } = await import("@aws-sdk/client-ssm");
|
|
9887
9887
|
const region = process.env.AWS_REGION ?? process.env.AWS_DEFAULT_REGION ?? "us-east-2";
|
|
9888
|
-
const ssm = new
|
|
9888
|
+
const ssm = new SSMClient5({ region });
|
|
9889
9889
|
const resp = await ssm.send(new GetParameterCommand2({ Name: ssmPath }));
|
|
9890
9890
|
const raw = resp.Parameter?.Value;
|
|
9891
9891
|
if (!raw) {
|
|
@@ -16521,6 +16521,12 @@ import {
|
|
|
16521
16521
|
} from "@aws-sdk/client-secrets-manager";
|
|
16522
16522
|
import input from "@inquirer/input";
|
|
16523
16523
|
import password from "@inquirer/password";
|
|
16524
|
+
import {
|
|
16525
|
+
buildInstanceIndexValue,
|
|
16526
|
+
instanceIndexSecretId,
|
|
16527
|
+
isInstanceKey,
|
|
16528
|
+
parseInstanceIndex
|
|
16529
|
+
} from "@mesh-tech/secrets";
|
|
16524
16530
|
async function ensureAwsCredentialsForStack(stack) {
|
|
16525
16531
|
if (process.env.AWS_ACCESS_KEY_ID || process.env.AWS_SESSION_TOKEN) return;
|
|
16526
16532
|
if (!stack) return;
|
|
@@ -16705,6 +16711,12 @@ Usage: mesh secrets set external/<name>`);
|
|
|
16705
16711
|
);
|
|
16706
16712
|
process.exit(1);
|
|
16707
16713
|
}
|
|
16714
|
+
if (opts.key && !isInstanceKey(opts.key)) {
|
|
16715
|
+
logError(
|
|
16716
|
+
`Invalid instance key "${opts.key}": keys must not contain "/", start with ".", or end with ".config" \u2014 those names are reserved for the index and config mirrors.`
|
|
16717
|
+
);
|
|
16718
|
+
process.exit(1);
|
|
16719
|
+
}
|
|
16708
16720
|
let existing = {};
|
|
16709
16721
|
try {
|
|
16710
16722
|
const response = await smClient.send(
|
|
@@ -16729,6 +16741,9 @@ Usage: mesh secrets set external/<name>`);
|
|
|
16729
16741
|
const merged2 = deepMerge(existing, values2);
|
|
16730
16742
|
await writeSecret(smClient, secretId, merged2, svc.meta);
|
|
16731
16743
|
await writeConfigMirror(smClient, secretId, schema, merged2, svc.meta);
|
|
16744
|
+
if (opts.key) {
|
|
16745
|
+
await addKeyToInstanceIndex(smClient, secretPrefix, opts.key, serviceName);
|
|
16746
|
+
}
|
|
16732
16747
|
logSuccess(`Credentials written to ${secretId}`);
|
|
16733
16748
|
return;
|
|
16734
16749
|
}
|
|
@@ -16744,6 +16759,9 @@ Usage: mesh secrets set external/<name>`);
|
|
|
16744
16759
|
const merged = deepMerge(existing, values);
|
|
16745
16760
|
await writeSecret(smClient, secretId, merged, svc.meta);
|
|
16746
16761
|
await writeConfigMirror(smClient, secretId, schema, merged, svc.meta);
|
|
16762
|
+
if (opts.key) {
|
|
16763
|
+
await addKeyToInstanceIndex(smClient, secretPrefix, opts.key, serviceName);
|
|
16764
|
+
}
|
|
16747
16765
|
logSuccess(`Credentials written to ${secretId}`);
|
|
16748
16766
|
console.error("\nStored fields:");
|
|
16749
16767
|
displaySummary(schema.fields, merged);
|
|
@@ -16809,6 +16827,45 @@ function extractNonSecretFields(fields, values) {
|
|
|
16809
16827
|
}
|
|
16810
16828
|
return result;
|
|
16811
16829
|
}
|
|
16830
|
+
async function addKeyToInstanceIndex(client, secretPrefix, key, serviceName) {
|
|
16831
|
+
const indexId = instanceIndexSecretId(secretPrefix);
|
|
16832
|
+
try {
|
|
16833
|
+
let keys = [];
|
|
16834
|
+
try {
|
|
16835
|
+
const res = await client.send(new GetSecretValueCommand5({ SecretId: indexId }));
|
|
16836
|
+
keys = parseInstanceIndex(res.SecretString ?? "");
|
|
16837
|
+
} catch (err) {
|
|
16838
|
+
if (!isResourceNotFound(err)) throw err;
|
|
16839
|
+
}
|
|
16840
|
+
if (keys.includes(key)) return;
|
|
16841
|
+
const secretString = buildInstanceIndexValue([...keys, key]);
|
|
16842
|
+
try {
|
|
16843
|
+
await client.send(
|
|
16844
|
+
new PutSecretValueCommand({ SecretId: indexId, SecretString: secretString })
|
|
16845
|
+
);
|
|
16846
|
+
} catch (err) {
|
|
16847
|
+
if (!isResourceNotFound(err)) throw err;
|
|
16848
|
+
await client.send(
|
|
16849
|
+
new CreateSecretCommand({
|
|
16850
|
+
Name: indexId,
|
|
16851
|
+
Description: `Instance-key index for ${serviceName} (read by listInstances(); maintained by mesh secrets set/reindex and the Hub)`,
|
|
16852
|
+
SecretString: secretString,
|
|
16853
|
+
Tags: [
|
|
16854
|
+
{ Key: "mesh:type", Value: "external-service-index" },
|
|
16855
|
+
{ Key: "mesh:service", Value: serviceName }
|
|
16856
|
+
]
|
|
16857
|
+
})
|
|
16858
|
+
);
|
|
16859
|
+
logInfo(
|
|
16860
|
+
`Created ${indexId} OUT OF BAND \u2014 the declaring stack does not manage it. Deploy the declaring stack (it creates the index) before writing keys, or import this secret into the stack before its next deploy.`
|
|
16861
|
+
);
|
|
16862
|
+
}
|
|
16863
|
+
} catch (err) {
|
|
16864
|
+
logInfo(
|
|
16865
|
+
`Warning: failed to record instance "${key}" in ${indexId} \u2014 listInstances() will not see it until you run: mesh secrets reindex external/${serviceName} (${err})`
|
|
16866
|
+
);
|
|
16867
|
+
}
|
|
16868
|
+
}
|
|
16812
16869
|
async function writeConfigMirror(client, secretId, schema, merged, meta) {
|
|
16813
16870
|
const configId = `${secretId}/.config`;
|
|
16814
16871
|
const configValues = extractNonSecretFields(schema.fields, merged);
|
|
@@ -16850,17 +16907,157 @@ var init_set = __esm({
|
|
|
16850
16907
|
}
|
|
16851
16908
|
});
|
|
16852
16909
|
|
|
16910
|
+
// libs/mesh-cli/src/commands/secrets/reindex.ts
|
|
16911
|
+
import { SSMClient as SSMClient3 } from "@aws-sdk/client-ssm";
|
|
16912
|
+
import {
|
|
16913
|
+
SecretsManagerClient as SecretsManagerClient6,
|
|
16914
|
+
ListSecretsCommand,
|
|
16915
|
+
GetSecretValueCommand as GetSecretValueCommand6,
|
|
16916
|
+
PutSecretValueCommand as PutSecretValueCommand2,
|
|
16917
|
+
CreateSecretCommand as CreateSecretCommand2
|
|
16918
|
+
} from "@aws-sdk/client-secrets-manager";
|
|
16919
|
+
import {
|
|
16920
|
+
buildInstanceIndexValue as buildInstanceIndexValue2,
|
|
16921
|
+
instanceIndexSecretId as instanceIndexSecretId2,
|
|
16922
|
+
instanceKeyFromSecretName,
|
|
16923
|
+
parseInstanceIndex as parseInstanceIndex2
|
|
16924
|
+
} from "@mesh-tech/secrets";
|
|
16925
|
+
async function scanInstanceKeys(client, secretPrefix) {
|
|
16926
|
+
const keys = [];
|
|
16927
|
+
let nextToken;
|
|
16928
|
+
do {
|
|
16929
|
+
const response = await client.send(
|
|
16930
|
+
new ListSecretsCommand({
|
|
16931
|
+
Filters: [{ Key: "name", Values: [`${secretPrefix}/`] }],
|
|
16932
|
+
NextToken: nextToken
|
|
16933
|
+
})
|
|
16934
|
+
);
|
|
16935
|
+
for (const secret of response.SecretList ?? []) {
|
|
16936
|
+
const key = secret.Name ? instanceKeyFromSecretName(secretPrefix, secret.Name) : null;
|
|
16937
|
+
if (key) keys.push(key);
|
|
16938
|
+
}
|
|
16939
|
+
nextToken = response.NextToken;
|
|
16940
|
+
} while (nextToken);
|
|
16941
|
+
return keys;
|
|
16942
|
+
}
|
|
16943
|
+
function indexAlreadyMatches(missing, stale, indexExists, malformed) {
|
|
16944
|
+
return missing.length === 0 && stale.length === 0 && indexExists && !malformed;
|
|
16945
|
+
}
|
|
16946
|
+
async function reindexCommand(servicePath, opts) {
|
|
16947
|
+
const stackOpt = resolveStackOption(opts);
|
|
16948
|
+
const context = detectContext(stackOpt);
|
|
16949
|
+
const region = opts.region ?? process.env.AWS_REGION ?? "us-east-2";
|
|
16950
|
+
await ensureAwsCredentialsForStack(stackOpt ?? context.stage);
|
|
16951
|
+
const ssmClient = new SSMClient3({ region });
|
|
16952
|
+
const smClient = new SecretsManagerClient6({ region });
|
|
16953
|
+
const services = await discoverExternalServices(
|
|
16954
|
+
ssmClient,
|
|
16955
|
+
context.tenant,
|
|
16956
|
+
context.platformEnv
|
|
16957
|
+
);
|
|
16958
|
+
const serviceName = servicePath?.replace(/^external\//, "");
|
|
16959
|
+
if (!serviceName || !services.has(serviceName)) {
|
|
16960
|
+
if (serviceName) {
|
|
16961
|
+
logError(`External service "${serviceName}" not found.`);
|
|
16962
|
+
}
|
|
16963
|
+
const keyed = [...services.entries()].filter(([, s]) => s.schema?.keyedBy);
|
|
16964
|
+
console.error("\nMulti-instance external services:");
|
|
16965
|
+
for (const [name, svc2] of keyed) {
|
|
16966
|
+
console.error(` ${name} (keyed by ${svc2.schema.keyedBy})`);
|
|
16967
|
+
}
|
|
16968
|
+
if (keyed.length === 0) console.error(" (none)");
|
|
16969
|
+
console.error(`
|
|
16970
|
+
Usage: mesh secrets reindex external/<name>`);
|
|
16971
|
+
process.exit(serviceName ? 1 : 0);
|
|
16972
|
+
}
|
|
16973
|
+
const svc = services.get(serviceName);
|
|
16974
|
+
if (!svc.schema?.keyedBy) {
|
|
16975
|
+
logError(
|
|
16976
|
+
`"${serviceName}" is single-instance (no keyedBy) \u2014 it has no instance index.`
|
|
16977
|
+
);
|
|
16978
|
+
process.exit(1);
|
|
16979
|
+
}
|
|
16980
|
+
const secretPrefix = svc.meta.secretPrefix;
|
|
16981
|
+
const indexId = instanceIndexSecretId2(secretPrefix);
|
|
16982
|
+
const actual = [...new Set(await scanInstanceKeys(smClient, secretPrefix))].sort();
|
|
16983
|
+
let listed = [];
|
|
16984
|
+
let indexExists = true;
|
|
16985
|
+
let malformed = false;
|
|
16986
|
+
try {
|
|
16987
|
+
const res = await smClient.send(new GetSecretValueCommand6({ SecretId: indexId }));
|
|
16988
|
+
try {
|
|
16989
|
+
listed = parseInstanceIndex2(res.SecretString ?? "");
|
|
16990
|
+
} catch {
|
|
16991
|
+
malformed = true;
|
|
16992
|
+
logInfo(`Existing index at ${indexId} is malformed \u2014 rebuilding from scratch.`);
|
|
16993
|
+
}
|
|
16994
|
+
} catch (err) {
|
|
16995
|
+
if (!isResourceNotFound(err)) throw err;
|
|
16996
|
+
indexExists = false;
|
|
16997
|
+
logInfo(`No index at ${indexId} yet \u2014 it will be created.`);
|
|
16998
|
+
}
|
|
16999
|
+
const missing = actual.filter((k) => !listed.includes(k));
|
|
17000
|
+
const stale = listed.filter((k) => !actual.includes(k));
|
|
17001
|
+
logInfo(`Instances in Secrets Manager: ${actual.length ? actual.join(", ") : "(none)"}`);
|
|
17002
|
+
if (missing.length > 0) logInfo(`Missing from index: ${missing.join(", ")}`);
|
|
17003
|
+
if (stale.length > 0) logInfo(`Stale in index (secret gone): ${stale.join(", ")}`);
|
|
17004
|
+
if (indexAlreadyMatches(missing, stale, indexExists, malformed)) {
|
|
17005
|
+
logSuccess(`Index at ${indexId} already matches \u2014 nothing to do.`);
|
|
17006
|
+
return;
|
|
17007
|
+
}
|
|
17008
|
+
if (opts.dryRun) {
|
|
17009
|
+
logInfo(`Dry run \u2014 would write ${indexId} with ${actual.length} instance(s).`);
|
|
17010
|
+
return;
|
|
17011
|
+
}
|
|
17012
|
+
await writeInstanceIndex(smClient, secretPrefix, serviceName, actual);
|
|
17013
|
+
logSuccess(`Index rebuilt: ${indexId} now lists ${actual.length} instance(s).`);
|
|
17014
|
+
}
|
|
17015
|
+
async function writeInstanceIndex(client, secretPrefix, serviceName, keys) {
|
|
17016
|
+
const indexId = instanceIndexSecretId2(secretPrefix);
|
|
17017
|
+
const secretString = buildInstanceIndexValue2(keys);
|
|
17018
|
+
try {
|
|
17019
|
+
await client.send(
|
|
17020
|
+
new PutSecretValueCommand2({ SecretId: indexId, SecretString: secretString })
|
|
17021
|
+
);
|
|
17022
|
+
} catch (err) {
|
|
17023
|
+
if (!isResourceNotFound(err)) throw err;
|
|
17024
|
+
await client.send(
|
|
17025
|
+
new CreateSecretCommand2({
|
|
17026
|
+
Name: indexId,
|
|
17027
|
+
Description: `Instance-key index for ${serviceName} (read by listInstances(); maintained by mesh secrets set/reindex and the Hub)`,
|
|
17028
|
+
SecretString: secretString,
|
|
17029
|
+
Tags: [
|
|
17030
|
+
{ Key: "mesh:type", Value: "external-service-index" },
|
|
17031
|
+
{ Key: "mesh:service", Value: serviceName }
|
|
17032
|
+
]
|
|
17033
|
+
})
|
|
17034
|
+
);
|
|
17035
|
+
logInfo(
|
|
17036
|
+
`Created ${indexId} OUT OF BAND \u2014 the declaring stack does not manage it. Deploy the declaring stack BEFORE reindex next time, or import this secret into the stack before its next deploy.`
|
|
17037
|
+
);
|
|
17038
|
+
}
|
|
17039
|
+
}
|
|
17040
|
+
var init_reindex = __esm({
|
|
17041
|
+
"libs/mesh-cli/src/commands/secrets/reindex.ts"() {
|
|
17042
|
+
"use strict";
|
|
17043
|
+
init_context();
|
|
17044
|
+
init_log();
|
|
17045
|
+
init_stack_flag();
|
|
17046
|
+
init_set();
|
|
17047
|
+
}
|
|
17048
|
+
});
|
|
17049
|
+
|
|
16853
17050
|
// libs/mesh-cli/src/commands/secrets/migrate-config.ts
|
|
16854
17051
|
import {
|
|
16855
|
-
SSMClient as
|
|
17052
|
+
SSMClient as SSMClient4,
|
|
16856
17053
|
GetParametersByPathCommand as GetParametersByPathCommand2
|
|
16857
17054
|
} from "@aws-sdk/client-ssm";
|
|
16858
17055
|
import {
|
|
16859
|
-
SecretsManagerClient as
|
|
16860
|
-
GetSecretValueCommand as
|
|
16861
|
-
PutSecretValueCommand as
|
|
16862
|
-
CreateSecretCommand as
|
|
16863
|
-
ListSecretsCommand
|
|
17056
|
+
SecretsManagerClient as SecretsManagerClient7,
|
|
17057
|
+
GetSecretValueCommand as GetSecretValueCommand7,
|
|
17058
|
+
PutSecretValueCommand as PutSecretValueCommand3,
|
|
17059
|
+
CreateSecretCommand as CreateSecretCommand3,
|
|
17060
|
+
ListSecretsCommand as ListSecretsCommand2
|
|
16864
17061
|
} from "@aws-sdk/client-secrets-manager";
|
|
16865
17062
|
function normalizeEntry2(raw) {
|
|
16866
17063
|
if (raw.type === "group" && raw.fields) {
|
|
@@ -16946,7 +17143,7 @@ function isResourceNotFound2(err) {
|
|
|
16946
17143
|
}
|
|
16947
17144
|
async function readSecret(client, secretId) {
|
|
16948
17145
|
try {
|
|
16949
|
-
const res = await client.send(new
|
|
17146
|
+
const res = await client.send(new GetSecretValueCommand7({ SecretId: secretId }));
|
|
16950
17147
|
return res.SecretString ? JSON.parse(res.SecretString) : null;
|
|
16951
17148
|
} catch (err) {
|
|
16952
17149
|
if (isResourceNotFound2(err)) return null;
|
|
@@ -16956,10 +17153,10 @@ async function readSecret(client, secretId) {
|
|
|
16956
17153
|
async function writeSecret2(client, secretId, values, description) {
|
|
16957
17154
|
const secretString = JSON.stringify(values);
|
|
16958
17155
|
try {
|
|
16959
|
-
await client.send(new
|
|
17156
|
+
await client.send(new PutSecretValueCommand3({ SecretId: secretId, SecretString: secretString }));
|
|
16960
17157
|
} catch (err) {
|
|
16961
17158
|
if (isResourceNotFound2(err)) {
|
|
16962
|
-
await client.send(new
|
|
17159
|
+
await client.send(new CreateSecretCommand3({
|
|
16963
17160
|
Name: secretId,
|
|
16964
17161
|
Description: description,
|
|
16965
17162
|
SecretString: secretString,
|
|
@@ -16972,7 +17169,7 @@ async function writeSecret2(client, secretId, values, description) {
|
|
|
16972
17169
|
}
|
|
16973
17170
|
async function secretExists(client, secretId) {
|
|
16974
17171
|
try {
|
|
16975
|
-
await client.send(new
|
|
17172
|
+
await client.send(new GetSecretValueCommand7({ SecretId: secretId }));
|
|
16976
17173
|
return true;
|
|
16977
17174
|
} catch (err) {
|
|
16978
17175
|
if (isResourceNotFound2(err)) return false;
|
|
@@ -16985,7 +17182,7 @@ async function listInstanceKeys(client, secretPrefix) {
|
|
|
16985
17182
|
let nextToken;
|
|
16986
17183
|
do {
|
|
16987
17184
|
const response = await client.send(
|
|
16988
|
-
new
|
|
17185
|
+
new ListSecretsCommand2({
|
|
16989
17186
|
Filters: [{ Key: "name", Values: [prefix2] }],
|
|
16990
17187
|
NextToken: nextToken
|
|
16991
17188
|
})
|
|
@@ -17051,8 +17248,8 @@ async function migrateService(smClient, meta, schema, opts) {
|
|
|
17051
17248
|
async function migrateConfigCommand(opts) {
|
|
17052
17249
|
const context = detectContext(resolveStackOption(opts));
|
|
17053
17250
|
const region = opts.region ?? process.env.AWS_REGION ?? "us-east-2";
|
|
17054
|
-
const ssmClient = new
|
|
17055
|
-
const smClient = new
|
|
17251
|
+
const ssmClient = new SSMClient4({ region });
|
|
17252
|
+
const smClient = new SecretsManagerClient7({ region });
|
|
17056
17253
|
logInfo(`Migrating .config mirrors for ${context.tenant}/${context.platformEnv}`);
|
|
17057
17254
|
if (opts.dryRun) logInfo("DRY-RUN mode \u2014 no writes");
|
|
17058
17255
|
if (opts.force) logInfo("FORCE mode \u2014 overwrite existing .config");
|
|
@@ -17105,6 +17302,11 @@ function registerSecretsCommands(program2) {
|
|
|
17105
17302
|
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) => {
|
|
17106
17303
|
await setCommand(service, opts);
|
|
17107
17304
|
});
|
|
17305
|
+
secrets.command("reindex [service]").description(
|
|
17306
|
+
"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"
|
|
17307
|
+
).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) => {
|
|
17308
|
+
await reindexCommand(service, opts);
|
|
17309
|
+
});
|
|
17108
17310
|
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) => {
|
|
17109
17311
|
await migrateConfigCommand(opts);
|
|
17110
17312
|
});
|
|
@@ -17114,6 +17316,7 @@ var init_secrets = __esm({
|
|
|
17114
17316
|
"use strict";
|
|
17115
17317
|
init_exec2();
|
|
17116
17318
|
init_set();
|
|
17319
|
+
init_reindex();
|
|
17117
17320
|
init_migrate_config();
|
|
17118
17321
|
}
|
|
17119
17322
|
});
|
|
@@ -18755,8 +18958,8 @@ import * as fs32 from "fs";
|
|
|
18755
18958
|
import * as os13 from "os";
|
|
18756
18959
|
import * as path39 from "path";
|
|
18757
18960
|
import {
|
|
18758
|
-
SecretsManagerClient as
|
|
18759
|
-
GetSecretValueCommand as
|
|
18961
|
+
SecretsManagerClient as SecretsManagerClient8,
|
|
18962
|
+
GetSecretValueCommand as GetSecretValueCommand8
|
|
18760
18963
|
} from "@aws-sdk/client-secrets-manager";
|
|
18761
18964
|
function resolveTenantEnv(options) {
|
|
18762
18965
|
if (options.tenant && options.env) {
|
|
@@ -19019,10 +19222,10 @@ async function tunnelExternal(name, options) {
|
|
|
19019
19222
|
const { appTenant, appStage } = resolveCredentialAxis(options);
|
|
19020
19223
|
const secretId = externalSecretId(appTenant, appStage, name, key);
|
|
19021
19224
|
const setHint = `mesh secrets set external/${name}${key ? ` --key=${key}` : ""}`;
|
|
19022
|
-
const sm = new
|
|
19225
|
+
const sm = new SecretsManagerClient8({});
|
|
19023
19226
|
let creds;
|
|
19024
19227
|
try {
|
|
19025
|
-
const out = await sm.send(new
|
|
19228
|
+
const out = await sm.send(new GetSecretValueCommand8({ SecretId: secretId }));
|
|
19026
19229
|
creds = JSON.parse(out.SecretString ?? "{}");
|
|
19027
19230
|
} catch (err) {
|
|
19028
19231
|
logError(
|