@keystrokehq/cli 0.0.1
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/AGENTS-blurb.md +123 -0
- package/LICENSE +42 -0
- package/README.md +177 -0
- package/THIRD_PARTY_NOTICES.md +16 -0
- package/bin/keystroke.mjs +107 -0
- package/dist/_manifest-JSRE3H8k.mjs +385 -0
- package/dist/agent-bundle-package-DWV6B_5q-BtV7Xycc.mjs +2344 -0
- package/dist/agent-manifest-CDnbkR2f.mjs +245 -0
- package/dist/agents-CZJGxVqV.mjs +228 -0
- package/dist/api-keys-D2lgguuY.mjs +40 -0
- package/dist/auth-DN2VusyU.mjs +59 -0
- package/dist/auth.handler-CT1BQUvu.mjs +340 -0
- package/dist/browser-qwFrUH82.mjs +24 -0
- package/dist/build-agents-BmM_AsSd-BGi9wtzt.mjs +514 -0
- package/dist/build-metadata-BWS7uhd_-DR8gJjTX.mjs +1422 -0
- package/dist/build-progress-DgYKb4hB.mjs +183 -0
- package/dist/build-tasks-CdihpudT-D5r5HUHe.mjs +91 -0
- package/dist/build-workflows-CfxBnIWh-CdYPv8w2.mjs +370 -0
- package/dist/build.handler-4799CjWH.mjs +36 -0
- package/dist/chunk-CH6r78ws.mjs +37 -0
- package/dist/clear-cache.handler-B9tqSoSM.mjs +11 -0
- package/dist/clear.handler-BTIXXPTJ.mjs +42 -0
- package/dist/clear.handler-BydlX-zE.mjs +11 -0
- package/dist/commander-DfTVqQ-3.mjs +133 -0
- package/dist/concurrency-gXn9Rw8x-DNl2YtrS.mjs +20 -0
- package/dist/connect-BUXkeH0F.mjs +43 -0
- package/dist/connect.handler-CYel9cy6.mjs +430 -0
- package/dist/constants-CPpPdSNg.mjs +8 -0
- package/dist/context-T7HZuB97.mjs +138 -0
- package/dist/credential-env-map-CI8yWHVy.mjs +28 -0
- package/dist/credential-schema-mismatch-BKo5PjcQ.mjs +76 -0
- package/dist/credentials-CvmjU0lK.mjs +171 -0
- package/dist/credentials-OfVHOtG3.mjs +151216 -0
- package/dist/current-deployment-workflow-poHt27i3.mjs +94 -0
- package/dist/current.handler-B8zKzfPp.mjs +21 -0
- package/dist/delete.handler-bAu1iXVQ.mjs +17 -0
- package/dist/deploy-7Jjls436.mjs +26 -0
- package/dist/deploy-BOPIpRWm.mjs +74 -0
- package/dist/deploy-progress-BmGUNFKg.mjs +70 -0
- package/dist/deploy.handler-BAzgiNhd.mjs +370 -0
- package/dist/detect-env-access-CwkOYeYM-D_BCZqV6.mjs +209 -0
- package/dist/diff-utils-NEfcjqxt.mjs +185 -0
- package/dist/diff.handler-Du7SY8K4.mjs +47 -0
- package/dist/dist-BkJUoBiG.mjs +1116 -0
- package/dist/dist-CUK7yBM0.mjs +308 -0
- package/dist/env-91KwMKov.mjs +140 -0
- package/dist/env.handler-BAzBuMzQ.mjs +277 -0
- package/dist/error-boundary-VL-JLfIa.mjs +34 -0
- package/dist/file-metadata-D1vm-XY2.mjs +191 -0
- package/dist/get-intrinsic-zLxwtrLK.mjs +658 -0
- package/dist/import-module-CV84H5fZ-B_CBCmb4.mjs +1747 -0
- package/dist/init-DpMCotSK.mjs +45 -0
- package/dist/init.handler-CPRnif52.mjs +585 -0
- package/dist/inspect.handler-DT_cD036.mjs +146 -0
- package/dist/integration-catalog-Bt-L3GjF.mjs +104 -0
- package/dist/integrations-DlatPK4W.mjs +79 -0
- package/dist/keystroke.d.mts +3 -0
- package/dist/keystroke.mjs +707 -0
- package/dist/layout-CbMtQ2tm.mjs +67 -0
- package/dist/list-enrichment-y-cwizLr.mjs +189 -0
- package/dist/list.handler-BTWvCyjA.mjs +52 -0
- package/dist/list.handler-CWF_Dj15.mjs +24 -0
- package/dist/list.handler-CZ6G2x_G.mjs +75 -0
- package/dist/list.handler-DWaQkJaR.mjs +51 -0
- package/dist/list.handler-DqbFcBW7.mjs +180 -0
- package/dist/list.handler-lq3ZGAn4.mjs +104 -0
- package/dist/logs-BEg9L5l8.mjs +28 -0
- package/dist/logs.handler-6hoMBzqw.mjs +35 -0
- package/dist/logs.handler-BD_dXiL1.mjs +231 -0
- package/dist/metadata-layout-GUYIUo0i-_aG2zjue.mjs +5877 -0
- package/dist/normalize-path-CojS-CgQ-DLCOvnD1.mjs +20 -0
- package/dist/options-CeaTcFxP.mjs +43 -0
- package/dist/org-xLzBtt2_.mjs +41 -0
- package/dist/output-DM4b7KgY.mjs +72 -0
- package/dist/oxc-B3KI3rf_-n9d1hKNq.mjs +119 -0
- package/dist/paused.handler-BMFm9Cff.mjs +94 -0
- package/dist/project-config-D1qsQlO7.mjs +107 -0
- package/dist/projects-CHkRE9rS.mjs +1574 -0
- package/dist/projects-Cjb7sovS.mjs +30 -0
- package/dist/read-credential-keys-77a91T8M-KA0Iw0Z1.mjs +9 -0
- package/dist/register.handler-BPCdor1_.mjs +86 -0
- package/dist/requirements.handler-DPXdSks3.mjs +201 -0
- package/dist/resolve-project-DDJ29sCF.mjs +35 -0
- package/dist/rolldown-runtime-twds-ZHy-BWWzu8VG.mjs +15 -0
- package/dist/run-polling-CAgFRdK3.mjs +20 -0
- package/dist/runs-D9hNLb9A.mjs +259 -0
- package/dist/schedule-BXx3uXwr.mjs +1142 -0
- package/dist/schema-17qMfNyI.mjs +18 -0
- package/dist/schema-display-CgmeKigW.mjs +130 -0
- package/dist/schemas-CDib1RhE.mjs +125 -0
- package/dist/skills-sync.handler-DIy8GR16.mjs +34 -0
- package/dist/skills.command-CrjI2dN9.mjs +35 -0
- package/dist/skills.handler-Bz8bJKql.mjs +9 -0
- package/dist/source-analysis-Cj-ADyu--BJQcFPCG.mjs +144 -0
- package/dist/spinner-progress-DMVwgqO9.mjs +173 -0
- package/dist/src-C0X6u_Mw.mjs +1340 -0
- package/dist/src-eHwu-Gfw.mjs +369 -0
- package/dist/status.handler-BO4nwvWn.mjs +101 -0
- package/dist/switch.handler-D_9213Vf.mjs +51 -0
- package/dist/sync-BL_Mo5st.mjs +39 -0
- package/dist/sync-keystroke-agent-skills-Kx_H7UTd.mjs +70 -0
- package/dist/sync.handler-BUFPdzWz.mjs +82 -0
- package/dist/task-B2sZMaZu.mjs +8 -0
- package/dist/task-target-build-CBeCKbu2.mjs +432 -0
- package/dist/task-target-deploy-C5X-USeR.mjs +4 -0
- package/dist/task-target-deploy-CA6elFpF-BEr4gkol.mjs +271 -0
- package/dist/task-target-deploy-runner.d.mts +3 -0
- package/dist/task-target-deploy-runner.mjs +202 -0
- package/dist/test-BHTgR3UA.mjs +698 -0
- package/dist/test.handler-BcPQ8b74.mjs +13 -0
- package/dist/trigger-artifacts-DQPbQNqC-B4yeeFBY.mjs +239 -0
- package/dist/trigger-manifest-CY7brZeg.mjs +30 -0
- package/dist/try-deploy.handler-DqybNhXx.mjs +490 -0
- package/dist/upload-CkU--iDC.mjs +207 -0
- package/dist/upload.handler-DCtiznQp.mjs +441 -0
- package/dist/utils-CywxCDM7.mjs +14 -0
- package/dist/validate.handler-DOcTaJL0.mjs +280 -0
- package/dist/workflow-build-DBQaBfnn.mjs +1819 -0
- package/dist/workflow-bundler-BPiqVscj-X1PFFAuP.mjs +167 -0
- package/dist/workflows-g9z87AJJ.mjs +799 -0
- package/dist/writer-BG8poUm3-BbXlU2kI.mjs +426 -0
- package/package.json +87 -0
|
@@ -0,0 +1,441 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { D as throwReportedCliExit, h as toErrorMessage, t as ui } from "./keystroke.mjs";
|
|
4
|
+
import { t as assertWorkflowProjectRoot } from "./project-config-D1qsQlO7.mjs";
|
|
5
|
+
import { a as writeJsonError, i as writeJson } from "./output-DM4b7KgY.mjs";
|
|
6
|
+
import { a as readManifestsFromOutDir, t as collectCredentialFingerprintMapFromProjectDist } from "./dist-BkJUoBiG.mjs";
|
|
7
|
+
import { t as requireWorkflowsDir } from "./resolve-project-DDJ29sCF.mjs";
|
|
8
|
+
import { n as getProcessEnv } from "./env-91KwMKov.mjs";
|
|
9
|
+
import { i as requireClient } from "./context-T7HZuB97.mjs";
|
|
10
|
+
import { t as getIntegrationCatalog } from "./integration-catalog-Bt-L3GjF.mjs";
|
|
11
|
+
import { t as readCredentialEnvMap } from "./credential-env-map-CI8yWHVy.mjs";
|
|
12
|
+
import { a as verifyCredentialResolvable, i as validateManualCredentialWithHook, n as resolveCredentialValuesFromEnv, r as uploadCredential, t as groupCredentialRequirements } from "./credentials-OfVHOtG3.mjs";
|
|
13
|
+
import { n as renderCredentialSchemaMismatchText, r as writeCredentialSchemaMismatchJson, t as isCredentialSchemaMismatchErrorLike } from "./credential-schema-mismatch-BKo5PjcQ.mjs";
|
|
14
|
+
import * as path$1 from "node:path";
|
|
15
|
+
import { confirm, isCancel } from "@clack/prompts";
|
|
16
|
+
//#region src/commands/credentials/upload/upload.handler.ts
|
|
17
|
+
function resolveEnvVarName(credentialSetId, key, envMap) {
|
|
18
|
+
return envMap?.[credentialSetId]?.[key] ?? `KEYSTROKE_${key}`;
|
|
19
|
+
}
|
|
20
|
+
function resolveExpectedEnvVarNames(credentialSetId, keys, envMap) {
|
|
21
|
+
return keys.map((key) => resolveEnvVarName(credentialSetId, key, envMap));
|
|
22
|
+
}
|
|
23
|
+
function quoteCliArg(value) {
|
|
24
|
+
return /\s/.test(value) ? `"${value.replace(/"/g, "\\\"")}"` : value;
|
|
25
|
+
}
|
|
26
|
+
function getCredentialDisplayName(catalog, credentialSetId) {
|
|
27
|
+
const info = catalog.byResolvedCredentialSetId.get(credentialSetId);
|
|
28
|
+
if (info?.role === "connection" && info.integrationPublicId) return info.integrationPublicId;
|
|
29
|
+
return credentialSetId;
|
|
30
|
+
}
|
|
31
|
+
function isCatalogCredentialSet(catalog, credentialSetId) {
|
|
32
|
+
return catalog.byResolvedCredentialSetId.has(credentialSetId);
|
|
33
|
+
}
|
|
34
|
+
function buildMissingSchemaFingerprintMessage(params) {
|
|
35
|
+
return `Cannot upload non-official credential set "${params.credentialSetId}" because the CLI could not discover its schema fingerprint from local build artifacts. Run \`keystroke workflows build\` in "${params.projectRoot}" and retry. Use --path to point at the owning Keystroke project if needed.`;
|
|
36
|
+
}
|
|
37
|
+
function buildRerunCommand(options) {
|
|
38
|
+
const parts = ["keystroke credentials upload"];
|
|
39
|
+
if (options.credentialSet && options.keys) {
|
|
40
|
+
parts.push(`--credential-set ${quoteCliArg(options.credentialSet)}`);
|
|
41
|
+
parts.push(`--keys ${quoteCliArg(options.keys)}`);
|
|
42
|
+
} else if (options.integration) parts.push(`--integration ${quoteCliArg(options.integration)}`);
|
|
43
|
+
if (options.update) parts.push("--update");
|
|
44
|
+
if (options.scope !== "user") parts.push(`--scope ${options.scope}`);
|
|
45
|
+
if (options.path) parts.push(`--path ${quoteCliArg(options.path)}`);
|
|
46
|
+
return parts.join(" ");
|
|
47
|
+
}
|
|
48
|
+
function formatUploadOutcomeMessage(params) {
|
|
49
|
+
const { credentialSetId, uploadedCredentialSetId, action, scope, keyCount } = params;
|
|
50
|
+
const scopeLabel = `scope: ${scope}`;
|
|
51
|
+
if (action === "created") return {
|
|
52
|
+
level: "success",
|
|
53
|
+
message: `${credentialSetId}: created credential set ${uploadedCredentialSetId} (${scopeLabel}) and uploaded ${keyCount} key(s).`
|
|
54
|
+
};
|
|
55
|
+
if (action === "updated-values") return {
|
|
56
|
+
level: "success",
|
|
57
|
+
message: `${credentialSetId}: updated values for existing credential set ${uploadedCredentialSetId} (${scopeLabel}) using ${keyCount} key(s) from your current env vars.`
|
|
58
|
+
};
|
|
59
|
+
if (action === "marked-default") return {
|
|
60
|
+
level: "warn",
|
|
61
|
+
message: `${credentialSetId}: already configured (${uploadedCredentialSetId}, ${scopeLabel}). Marked as default; values unchanged — use --update to refresh from env vars.`
|
|
62
|
+
};
|
|
63
|
+
return {
|
|
64
|
+
level: "warn",
|
|
65
|
+
message: `${credentialSetId}: already configured (${uploadedCredentialSetId}, ${scopeLabel}). Values unchanged — use --update to refresh from env vars.`
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
function renderSkippedCredentials(skipped) {
|
|
69
|
+
if (skipped.length === 0) return;
|
|
70
|
+
ui.br();
|
|
71
|
+
ui.warn(`Skipped ${skipped.length} credential set(s) — env vars not found:`);
|
|
72
|
+
for (const item of skipped) ui.text(` - ${item.displayName} (missing: ${item.missingEnvVars.join(", ")})`);
|
|
73
|
+
}
|
|
74
|
+
function renderSummary(results) {
|
|
75
|
+
const created = results.filter((r) => r.action === "created").length;
|
|
76
|
+
const updated = results.filter((r) => r.action === "updated-values").length;
|
|
77
|
+
const unchanged = results.filter((r) => r.action === "unchanged" || r.action === "marked-default").length;
|
|
78
|
+
const parts = [];
|
|
79
|
+
if (created > 0) parts.push(`${created} created`);
|
|
80
|
+
if (updated > 0) parts.push(`${updated} updated`);
|
|
81
|
+
if (unchanged > 0) parts.push(`${unchanged} unchanged`);
|
|
82
|
+
if (parts.length > 0) {
|
|
83
|
+
ui.br();
|
|
84
|
+
ui.text(`Done: ${parts.join(", ")}.`);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
async function resolveProjectIdForUpload(params) {
|
|
88
|
+
const { client, projectRoot } = params;
|
|
89
|
+
const projectConfig = await assertWorkflowProjectRoot(projectRoot);
|
|
90
|
+
const auth = await client.public.auth.validate();
|
|
91
|
+
if (projectConfig.organizationId !== auth.organizationId) {
|
|
92
|
+
const message = `This checkout is configured for organization "${projectConfig.organizationId}", but the current credentials are scoped to "${auth.organizationId}".`;
|
|
93
|
+
ui.error(message);
|
|
94
|
+
ui.hint("Switch organizations or update your local project config before running this command.");
|
|
95
|
+
throwReportedCliExit(message);
|
|
96
|
+
}
|
|
97
|
+
return projectConfig.projectId;
|
|
98
|
+
}
|
|
99
|
+
async function resolveUploadScopeContext(params) {
|
|
100
|
+
const { client, scope, projectRoot } = params;
|
|
101
|
+
if (scope === "user") return {};
|
|
102
|
+
if (scope === "organization") return { organizationId: (await client.public.auth.validate()).organizationId };
|
|
103
|
+
return { projectId: await resolveProjectIdForUpload({
|
|
104
|
+
client,
|
|
105
|
+
projectRoot
|
|
106
|
+
}) };
|
|
107
|
+
}
|
|
108
|
+
function resolveEnvVarStatus(keys, credentialSetId, envMap, env) {
|
|
109
|
+
return keys.map((key) => {
|
|
110
|
+
const envVar = resolveEnvVarName(credentialSetId, key, envMap);
|
|
111
|
+
const value = env[envVar];
|
|
112
|
+
return {
|
|
113
|
+
key,
|
|
114
|
+
envVar,
|
|
115
|
+
present: !!value && value.trim().length > 0
|
|
116
|
+
};
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
async function handleCredentialsUpload(options, ctx) {
|
|
120
|
+
const client = requireClient(ctx);
|
|
121
|
+
const isExplicitCredentialSetMode = !!options.credentialSet;
|
|
122
|
+
const isExplicitIntegrationMode = !!options.integration;
|
|
123
|
+
const isExplicitMode = isExplicitCredentialSetMode || isExplicitIntegrationMode;
|
|
124
|
+
const hasKeys = !!options.keys;
|
|
125
|
+
if (isExplicitCredentialSetMode && isExplicitIntegrationMode) {
|
|
126
|
+
ui.error("--integration cannot be combined with --credential-set");
|
|
127
|
+
throwReportedCliExit("--integration cannot be combined with --credential-set");
|
|
128
|
+
}
|
|
129
|
+
if (isExplicitCredentialSetMode && !hasKeys) {
|
|
130
|
+
ui.error("--credential-set requires --keys (comma-separated credential keys)");
|
|
131
|
+
throwReportedCliExit("--credential-set requires --keys (comma-separated credential keys)");
|
|
132
|
+
}
|
|
133
|
+
if (hasKeys && !isExplicitCredentialSetMode) {
|
|
134
|
+
ui.error("--keys requires --credential-set");
|
|
135
|
+
throwReportedCliExit("--keys requires --credential-set");
|
|
136
|
+
}
|
|
137
|
+
if (!isExplicitMode && options.name) {
|
|
138
|
+
ui.error("--name can only be used when specifying --integration or --credential-set");
|
|
139
|
+
throwReportedCliExit("--name can only be used when specifying --integration or --credential-set");
|
|
140
|
+
}
|
|
141
|
+
const scope = options.scope;
|
|
142
|
+
const projectRoot = isExplicitMode && hasKeys ? options.path ? path$1.resolve(options.path) : process.cwd() : await requireWorkflowsDir(options.path);
|
|
143
|
+
const { organizationId, projectId } = await resolveUploadScopeContext({
|
|
144
|
+
client,
|
|
145
|
+
scope,
|
|
146
|
+
projectRoot
|
|
147
|
+
});
|
|
148
|
+
const envMap = await readCredentialEnvMap(projectRoot);
|
|
149
|
+
const catalog = await getIntegrationCatalog(ctx);
|
|
150
|
+
const fingerprintByCredentialSetId = isExplicitIntegrationMode ? /* @__PURE__ */ new Map() : await collectCredentialFingerprintMapFromProjectDist(projectRoot);
|
|
151
|
+
let toUpload = [];
|
|
152
|
+
const skipped = [];
|
|
153
|
+
if (isExplicitCredentialSetMode && hasKeys) {
|
|
154
|
+
const credentialSetId = options.credentialSet;
|
|
155
|
+
const keysArg = options.keys;
|
|
156
|
+
if (!credentialSetId || !keysArg) {
|
|
157
|
+
ui.error("--credential-set and --keys are required for explicit upload");
|
|
158
|
+
throwReportedCliExit("--credential-set and --keys are required for explicit upload");
|
|
159
|
+
}
|
|
160
|
+
const keys = keysArg.split(",").map((k) => k.trim()).filter(Boolean);
|
|
161
|
+
const keyToEnv = envMap?.[credentialSetId];
|
|
162
|
+
const values = resolveCredentialValuesFromEnv(keys, getProcessEnv(), keyToEnv);
|
|
163
|
+
if (!values) {
|
|
164
|
+
const expectedEnvVars = resolveExpectedEnvVarNames(credentialSetId, keys, envMap);
|
|
165
|
+
ui.error(`Missing env vars for ${credentialSetId}. Set: ${expectedEnvVars.join(", ")}`);
|
|
166
|
+
throwReportedCliExit(`Missing env vars for ${credentialSetId}. Set: ${expectedEnvVars.join(", ")}`);
|
|
167
|
+
}
|
|
168
|
+
const info = catalog.byResolvedCredentialSetId.get(credentialSetId);
|
|
169
|
+
toUpload = [{
|
|
170
|
+
credentialSetId,
|
|
171
|
+
displayName: getCredentialDisplayName(catalog, credentialSetId),
|
|
172
|
+
groupScope: null,
|
|
173
|
+
keys,
|
|
174
|
+
values,
|
|
175
|
+
...fingerprintByCredentialSetId.get(credentialSetId) ? { schemaFingerprint: fingerprintByCredentialSetId.get(credentialSetId) } : {},
|
|
176
|
+
...info?.connectionKind ? { connectionKind: info.connectionKind } : {}
|
|
177
|
+
}];
|
|
178
|
+
} else if (isExplicitIntegrationMode) {
|
|
179
|
+
const integrationId = options.integration?.trim().toLowerCase();
|
|
180
|
+
const entry = catalog.lookupByPublicId(integrationId);
|
|
181
|
+
if (!integrationId || !entry) {
|
|
182
|
+
ui.error(integrationId ? `Official integration "${integrationId}" is not supported for direct upload.` : "--integration is required for official integration upload");
|
|
183
|
+
throwReportedCliExit(integrationId ? `Official integration "${integrationId}" is not supported for direct upload.` : "--integration is required for official integration upload");
|
|
184
|
+
}
|
|
185
|
+
const publicId = entry.publicId;
|
|
186
|
+
const credentialSetId = entry.credentialSet.resolvedCredentialSetId;
|
|
187
|
+
const storedKeys = entry.credentialSet.storedKeys;
|
|
188
|
+
const authKeys = entry.credentialSet.authKeys;
|
|
189
|
+
const keyToEnv = envMap?.[credentialSetId];
|
|
190
|
+
const values = resolveCredentialValuesFromEnv(storedKeys, getProcessEnv(), keyToEnv);
|
|
191
|
+
if (!values) {
|
|
192
|
+
const expectedEnvVars = resolveExpectedEnvVarNames(credentialSetId, storedKeys, envMap);
|
|
193
|
+
ui.error(`Missing env vars for ${publicId}. Set: ${expectedEnvVars.join(", ")}`);
|
|
194
|
+
throwReportedCliExit(`Missing env vars for ${publicId}. Set: ${expectedEnvVars.join(", ")}`);
|
|
195
|
+
}
|
|
196
|
+
toUpload = [{
|
|
197
|
+
credentialSetId,
|
|
198
|
+
displayName: publicId,
|
|
199
|
+
groupScope: null,
|
|
200
|
+
keys: [...storedKeys],
|
|
201
|
+
verifyKeys: [...authKeys],
|
|
202
|
+
values,
|
|
203
|
+
connectionKind: entry.connectionKind
|
|
204
|
+
}];
|
|
205
|
+
} else {
|
|
206
|
+
const manifests = await readManifestsFromOutDir(projectRoot);
|
|
207
|
+
if (manifests.length === 0) {
|
|
208
|
+
ui.warn("No built manifests found. Run `keystroke workflows build` first.");
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
const seen = /* @__PURE__ */ new Set();
|
|
212
|
+
const env = getProcessEnv();
|
|
213
|
+
for (const { manifest } of manifests) {
|
|
214
|
+
const groups = groupCredentialRequirements(manifest);
|
|
215
|
+
for (const group of groups) {
|
|
216
|
+
const groupKey = `${group.credentialSetId}:${group.scope ?? "__default__"}`;
|
|
217
|
+
if (seen.has(groupKey)) continue;
|
|
218
|
+
seen.add(groupKey);
|
|
219
|
+
const keyToEnv = envMap?.[group.credentialSetId];
|
|
220
|
+
const values = resolveCredentialValuesFromEnv(group.keys, env, keyToEnv);
|
|
221
|
+
if (values) {
|
|
222
|
+
const info = catalog.byResolvedCredentialSetId.get(group.credentialSetId);
|
|
223
|
+
toUpload.push({
|
|
224
|
+
credentialSetId: group.credentialSetId,
|
|
225
|
+
displayName: getCredentialDisplayName(catalog, group.credentialSetId),
|
|
226
|
+
groupScope: group.scope,
|
|
227
|
+
keys: group.keys,
|
|
228
|
+
values,
|
|
229
|
+
...fingerprintByCredentialSetId.get(group.credentialSetId) ? { schemaFingerprint: fingerprintByCredentialSetId.get(group.credentialSetId) } : {},
|
|
230
|
+
...info?.connectionKind ? { connectionKind: info.connectionKind } : {}
|
|
231
|
+
});
|
|
232
|
+
} else {
|
|
233
|
+
const missingEnvVars = resolveExpectedEnvVarNames(group.credentialSetId, group.keys, envMap);
|
|
234
|
+
skipped.push({
|
|
235
|
+
credentialSetId: group.credentialSetId,
|
|
236
|
+
displayName: getCredentialDisplayName(catalog, group.credentialSetId),
|
|
237
|
+
missingEnvVars
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
if (toUpload.length === 0) {
|
|
243
|
+
ui.warn("No credential requirements had matching env vars.");
|
|
244
|
+
renderSkippedCredentials(skipped);
|
|
245
|
+
ui.hint("Set the missing env vars, then rerun: keystroke credentials upload");
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
const jsonOut = ctx.jsonMode;
|
|
250
|
+
if (options.update && !jsonOut && !options.dryRun && toUpload.length > 0) {
|
|
251
|
+
const shouldContinue = await confirm({ message: `Update ${toUpload.length} existing credential set(s) from current env vars?` });
|
|
252
|
+
if (isCancel(shouldContinue) || !shouldContinue) {
|
|
253
|
+
ui.text("Cancelled.");
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
if (options.dryRun) {
|
|
258
|
+
const env = getProcessEnv();
|
|
259
|
+
if (jsonOut) {
|
|
260
|
+
writeJson({
|
|
261
|
+
dryRun: true,
|
|
262
|
+
update: options.update,
|
|
263
|
+
items: toUpload.map((item) => ({
|
|
264
|
+
credentialSetId: item.credentialSetId,
|
|
265
|
+
displayName: item.displayName,
|
|
266
|
+
scope,
|
|
267
|
+
keys: item.keys,
|
|
268
|
+
envVars: Object.fromEntries(resolveEnvVarStatus(item.keys, item.credentialSetId, envMap, env).map((s) => [s.key, {
|
|
269
|
+
envVar: s.envVar,
|
|
270
|
+
present: s.present
|
|
271
|
+
}]))
|
|
272
|
+
})),
|
|
273
|
+
...skipped.length > 0 ? { skipped } : {}
|
|
274
|
+
});
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
ui.text(options.update ? `Would create or update ${toUpload.length} credential set(s) with scope ${scope}:` : `Would ensure ${toUpload.length} credential set(s) exist with scope ${scope}:`);
|
|
278
|
+
for (const item of toUpload) {
|
|
279
|
+
const envDetails = resolveEnvVarStatus(item.keys, item.credentialSetId, envMap, env).map((s) => `${s.envVar} (${s.present ? "set" : "missing"})`).join(", ");
|
|
280
|
+
ui.text(` - ${item.displayName} (${item.keys.length} key(s)): ${envDetails}`);
|
|
281
|
+
}
|
|
282
|
+
renderSkippedCredentials(skipped);
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
const results = [];
|
|
286
|
+
let unchangedCount = 0;
|
|
287
|
+
for (const item of toUpload) {
|
|
288
|
+
const credName = options.name ?? `[CLI] ${item.displayName}`;
|
|
289
|
+
if (item.connectionKind === "credentials-exchange") {
|
|
290
|
+
try {
|
|
291
|
+
const exchangeResponse = await client.credentials.exchange({
|
|
292
|
+
credentialSetId: item.credentialSetId,
|
|
293
|
+
input: item.values,
|
|
294
|
+
scope,
|
|
295
|
+
...projectId ? { projectId } : {},
|
|
296
|
+
name: credName,
|
|
297
|
+
isDefault: true
|
|
298
|
+
});
|
|
299
|
+
if (exchangeResponse.status === "needs-reinput") {
|
|
300
|
+
const msg = exchangeResponse.message ? `Exchange rejected for ${item.displayName}: ${exchangeResponse.message}` : `Exchange for ${item.displayName} returned needs-reinput; re-run after correcting your input.`;
|
|
301
|
+
if (jsonOut) writeJsonError(msg, { code: "EXCHANGE_NEEDS_REINPUT" });
|
|
302
|
+
else {
|
|
303
|
+
ui.warn(msg);
|
|
304
|
+
unchangedCount += 1;
|
|
305
|
+
}
|
|
306
|
+
continue;
|
|
307
|
+
}
|
|
308
|
+
results.push({
|
|
309
|
+
requestedCredentialSetId: item.displayName,
|
|
310
|
+
uploadedCredentialSetId: exchangeResponse.credentialSetId,
|
|
311
|
+
action: "created",
|
|
312
|
+
scope,
|
|
313
|
+
verified: true,
|
|
314
|
+
valuesUpdated: true
|
|
315
|
+
});
|
|
316
|
+
if (!jsonOut) ui.success(`${item.displayName}: exchanged credentials and persisted ${exchangeResponse.keys.length} key(s) (scope: ${scope}).`);
|
|
317
|
+
} catch (e) {
|
|
318
|
+
const msg = `Exchange failed for ${item.displayName}: ${toErrorMessage(e)}`;
|
|
319
|
+
if (jsonOut) writeJsonError(msg, { code: "EXCHANGE_FAILED" });
|
|
320
|
+
else {
|
|
321
|
+
ui.error(msg);
|
|
322
|
+
throwReportedCliExit(msg, { cause: e });
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
continue;
|
|
326
|
+
}
|
|
327
|
+
const validation = await validateManualCredentialWithHook({
|
|
328
|
+
credentialSetId: item.credentialSetId,
|
|
329
|
+
values: item.values
|
|
330
|
+
});
|
|
331
|
+
if (validation.status === "failed") {
|
|
332
|
+
const msg = `Validation failed for ${item.displayName}: ${validation.error}`;
|
|
333
|
+
if (jsonOut) {
|
|
334
|
+
writeJsonError(msg, { code: "VALIDATE_FAILED" });
|
|
335
|
+
continue;
|
|
336
|
+
}
|
|
337
|
+
ui.error(msg);
|
|
338
|
+
throwReportedCliExit(msg);
|
|
339
|
+
}
|
|
340
|
+
if (validation.status === "ok" && !jsonOut) ui.text(`Validated credentials against ${item.displayName}.`);
|
|
341
|
+
try {
|
|
342
|
+
if (!item.schemaFingerprint && !isCatalogCredentialSet(catalog, item.credentialSetId)) {
|
|
343
|
+
const message = buildMissingSchemaFingerprintMessage({
|
|
344
|
+
credentialSetId: item.credentialSetId,
|
|
345
|
+
projectRoot
|
|
346
|
+
});
|
|
347
|
+
if (jsonOut) writeJsonError(message, { code: "SCHEMA_FINGERPRINT_REQUIRED" });
|
|
348
|
+
else {
|
|
349
|
+
ui.error(message);
|
|
350
|
+
throwReportedCliExit(message);
|
|
351
|
+
}
|
|
352
|
+
continue;
|
|
353
|
+
}
|
|
354
|
+
const result = await uploadCredential({
|
|
355
|
+
client,
|
|
356
|
+
scope,
|
|
357
|
+
organizationId,
|
|
358
|
+
projectId,
|
|
359
|
+
credentialSetId: item.credentialSetId,
|
|
360
|
+
name: credName,
|
|
361
|
+
values: item.values,
|
|
362
|
+
updateExisting: options.update,
|
|
363
|
+
...item.schemaFingerprint ? { schemaFingerprint: item.schemaFingerprint } : {}
|
|
364
|
+
});
|
|
365
|
+
let verified = true;
|
|
366
|
+
const verifyKeys = item.verifyKeys ?? item.keys;
|
|
367
|
+
const verify = await verifyCredentialResolvable({
|
|
368
|
+
client,
|
|
369
|
+
credentialSetId: item.credentialSetId,
|
|
370
|
+
keys: verifyKeys,
|
|
371
|
+
scope,
|
|
372
|
+
projectId
|
|
373
|
+
});
|
|
374
|
+
if (!verify.success) {
|
|
375
|
+
verified = false;
|
|
376
|
+
const missing = verifyKeys.filter((k) => !verify.resolvedKeys.includes(k));
|
|
377
|
+
const msg = `Verify failed for ${item.displayName}: missing keys [${missing.join(", ")}]`;
|
|
378
|
+
if (jsonOut) writeJsonError(msg, { code: "VERIFY_FAILED" });
|
|
379
|
+
else {
|
|
380
|
+
ui.error(msg);
|
|
381
|
+
throwReportedCliExit(msg);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
results.push({
|
|
385
|
+
requestedCredentialSetId: item.displayName,
|
|
386
|
+
uploadedCredentialSetId: result.credentialSetId,
|
|
387
|
+
action: result.action,
|
|
388
|
+
scope,
|
|
389
|
+
verified,
|
|
390
|
+
valuesUpdated: result.action === "created" || result.action === "updated-values"
|
|
391
|
+
});
|
|
392
|
+
if (!jsonOut) {
|
|
393
|
+
const outcome = formatUploadOutcomeMessage({
|
|
394
|
+
credentialSetId: item.displayName,
|
|
395
|
+
uploadedCredentialSetId: result.credentialSetId,
|
|
396
|
+
action: result.action,
|
|
397
|
+
scope,
|
|
398
|
+
keyCount: item.keys.length
|
|
399
|
+
});
|
|
400
|
+
if (outcome.level === "success") ui.success(outcome.message);
|
|
401
|
+
else {
|
|
402
|
+
unchangedCount += 1;
|
|
403
|
+
ui.warn(outcome.message);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
} catch (e) {
|
|
407
|
+
if (isCredentialSchemaMismatchErrorLike(e)) {
|
|
408
|
+
if (jsonOut) {
|
|
409
|
+
writeCredentialSchemaMismatchJson(e);
|
|
410
|
+
throwReportedCliExit(e.message ?? `Credential schema mismatch for "${e.manifestId}"`, { cause: e });
|
|
411
|
+
}
|
|
412
|
+
renderCredentialSchemaMismatchText(ui, e);
|
|
413
|
+
throwReportedCliExit(e.message ?? `Credential schema mismatch for "${e.manifestId}"`, { cause: e });
|
|
414
|
+
}
|
|
415
|
+
const msg = `Failed for ${item.displayName}: ${toErrorMessage(e)}`;
|
|
416
|
+
if (jsonOut) writeJsonError(msg, { code: "UPLOAD_FAILED" });
|
|
417
|
+
else {
|
|
418
|
+
ui.error(msg);
|
|
419
|
+
throwReportedCliExit(msg, { cause: e });
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
if (jsonOut) {
|
|
424
|
+
writeJson({
|
|
425
|
+
results,
|
|
426
|
+
...skipped.length > 0 ? { skipped } : {}
|
|
427
|
+
});
|
|
428
|
+
return;
|
|
429
|
+
}
|
|
430
|
+
renderSkippedCredentials(skipped);
|
|
431
|
+
renderSummary(results);
|
|
432
|
+
if (unchangedCount > 0) {
|
|
433
|
+
const updateCommand = buildRerunCommand({
|
|
434
|
+
...options,
|
|
435
|
+
update: true
|
|
436
|
+
});
|
|
437
|
+
ui.hint(`To update values from your current env vars, rerun with --update: \`${updateCommand}\`.`);
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
//#endregion
|
|
441
|
+
export { handleCredentialsUpload };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { i as readProjectConfig } from "./project-config-D1qsQlO7.mjs";
|
|
4
|
+
import { h as resolveBuildOutputDir } from "./layout-CbMtQ2tm.mjs";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
//#region ../../packages/project-config/src/utils.ts
|
|
7
|
+
function resolveConfiguredBuildOutputDir(projectRoot, buildConfig) {
|
|
8
|
+
return buildConfig?.outDir ? path.resolve(projectRoot, buildConfig.outDir) : resolveBuildOutputDir(projectRoot);
|
|
9
|
+
}
|
|
10
|
+
async function resolveProjectBuildOutputDir(projectRoot) {
|
|
11
|
+
return resolveConfiguredBuildOutputDir(projectRoot, (await readProjectConfig(projectRoot))?.build);
|
|
12
|
+
}
|
|
13
|
+
//#endregion
|
|
14
|
+
export { resolveProjectBuildOutputDir as n, resolveConfiguredBuildOutputDir as t };
|