@primitivedotdev/cli 1.1.0 → 1.2.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/oclif/index.js +192 -20
- package/package.json +1 -1
package/dist/oclif/index.js
CHANGED
|
@@ -17171,6 +17171,108 @@ async function runSourceDeploy(api, params) {
|
|
|
17171
17171
|
result: data
|
|
17172
17172
|
};
|
|
17173
17173
|
}
|
|
17174
|
+
async function runSourceDeployWithSecrets(api, params) {
|
|
17175
|
+
const listed = await api.listFunctions();
|
|
17176
|
+
if (listed.error) return {
|
|
17177
|
+
kind: "error",
|
|
17178
|
+
payload: extractErrorPayload(listed.error),
|
|
17179
|
+
stage: "lookup"
|
|
17180
|
+
};
|
|
17181
|
+
const foundId = (listed.data?.data ?? []).find((f) => f.name === params.name)?.id ?? null;
|
|
17182
|
+
let functionId;
|
|
17183
|
+
let createPayload;
|
|
17184
|
+
if (foundId === null) {
|
|
17185
|
+
const created = await api.createFunction({
|
|
17186
|
+
files: params.files,
|
|
17187
|
+
name: params.name
|
|
17188
|
+
});
|
|
17189
|
+
if (created.error) return {
|
|
17190
|
+
kind: "error",
|
|
17191
|
+
payload: extractErrorPayload(created.error),
|
|
17192
|
+
stage: "create"
|
|
17193
|
+
};
|
|
17194
|
+
const data = created.data?.data;
|
|
17195
|
+
if (!data) return {
|
|
17196
|
+
kind: "error",
|
|
17197
|
+
payload: {
|
|
17198
|
+
code: "client_error",
|
|
17199
|
+
message: "Create returned no data"
|
|
17200
|
+
},
|
|
17201
|
+
stage: "create"
|
|
17202
|
+
};
|
|
17203
|
+
functionId = data.id;
|
|
17204
|
+
createPayload = data;
|
|
17205
|
+
} else functionId = foundId;
|
|
17206
|
+
const writtenSecrets = [];
|
|
17207
|
+
const succeededKeys = [];
|
|
17208
|
+
for (let i = 0; i < params.secrets.length; i++) {
|
|
17209
|
+
const pair = params.secrets[i];
|
|
17210
|
+
const pendingKeys = params.secrets.slice(i + 1).map((p) => p.key);
|
|
17211
|
+
const setResult = await api.setSecret({
|
|
17212
|
+
id: functionId,
|
|
17213
|
+
key: pair.key,
|
|
17214
|
+
value: pair.value
|
|
17215
|
+
});
|
|
17216
|
+
if (setResult.error) return {
|
|
17217
|
+
...createPayload ? { created: createPayload } : {},
|
|
17218
|
+
failedKey: pair.key,
|
|
17219
|
+
functionId,
|
|
17220
|
+
kind: "error",
|
|
17221
|
+
payload: extractErrorPayload(setResult.error),
|
|
17222
|
+
pendingKeys,
|
|
17223
|
+
stage: "set-secret",
|
|
17224
|
+
succeededKeys
|
|
17225
|
+
};
|
|
17226
|
+
const secret = setResult.data?.data;
|
|
17227
|
+
if (!secret) return {
|
|
17228
|
+
...createPayload ? { created: createPayload } : {},
|
|
17229
|
+
failedKey: pair.key,
|
|
17230
|
+
functionId,
|
|
17231
|
+
kind: "error",
|
|
17232
|
+
payload: {
|
|
17233
|
+
code: "client_error",
|
|
17234
|
+
message: "Secret write returned no data"
|
|
17235
|
+
},
|
|
17236
|
+
pendingKeys,
|
|
17237
|
+
stage: "set-secret",
|
|
17238
|
+
succeededKeys
|
|
17239
|
+
};
|
|
17240
|
+
writtenSecrets.push(secret);
|
|
17241
|
+
succeededKeys.push(pair.key);
|
|
17242
|
+
}
|
|
17243
|
+
const updated = await api.updateFunction({
|
|
17244
|
+
files: params.files,
|
|
17245
|
+
id: functionId
|
|
17246
|
+
});
|
|
17247
|
+
if (updated.error) return {
|
|
17248
|
+
...createPayload ? { created: createPayload } : {},
|
|
17249
|
+
functionId,
|
|
17250
|
+
kind: "error",
|
|
17251
|
+
payload: extractErrorPayload(updated.error),
|
|
17252
|
+
stage: "secret-redeploy",
|
|
17253
|
+
succeededKeys
|
|
17254
|
+
};
|
|
17255
|
+
const redeployed = updated.data?.data;
|
|
17256
|
+
if (!redeployed) return {
|
|
17257
|
+
...createPayload ? { created: createPayload } : {},
|
|
17258
|
+
functionId,
|
|
17259
|
+
kind: "error",
|
|
17260
|
+
payload: {
|
|
17261
|
+
code: "client_error",
|
|
17262
|
+
message: "Redeploy returned no data"
|
|
17263
|
+
},
|
|
17264
|
+
stage: "secret-redeploy",
|
|
17265
|
+
succeededKeys
|
|
17266
|
+
};
|
|
17267
|
+
return {
|
|
17268
|
+
kind: "ok",
|
|
17269
|
+
result: {
|
|
17270
|
+
action: foundId === null ? "created" : "redeployed",
|
|
17271
|
+
redeploy: redeployed,
|
|
17272
|
+
secrets: writtenSecrets
|
|
17273
|
+
}
|
|
17274
|
+
};
|
|
17275
|
+
}
|
|
17174
17276
|
function renderBuildFailure(payload, write) {
|
|
17175
17277
|
if (typeof payload !== "object" || payload === null) return false;
|
|
17176
17278
|
const error = payload.error ?? payload;
|
|
@@ -17657,14 +17759,23 @@ var FunctionsDeployCommand = class FunctionsDeployCommand extends Command {
|
|
|
17657
17759
|
|
|
17658
17760
|
Pass secret source flags to seed bindings in the same command. Keys
|
|
17659
17761
|
must match \`^[A-Z_][A-Z0-9_]*$\` (uppercase letters, digits,
|
|
17660
|
-
underscores; first character is a letter or underscore).
|
|
17661
|
-
|
|
17662
|
-
|
|
17663
|
-
|
|
17664
|
-
|
|
17665
|
-
|
|
17666
|
-
|
|
17667
|
-
|
|
17762
|
+
underscores; first character is a letter or underscore).
|
|
17763
|
+
|
|
17764
|
+
With one or more secrets the deploy fans out to multiple API calls.
|
|
17765
|
+
For --file (and for --source when no function with the given name
|
|
17766
|
+
exists yet): create-function, set-secret per pair, then a final
|
|
17767
|
+
update-function so the running handler picks up the bindings. For
|
|
17768
|
+
--source against an existing function name: the create-function step
|
|
17769
|
+
is replaced by an id lookup, then set-secret per pair, then a single
|
|
17770
|
+
update-function that binds the new code and the new secret env in
|
|
17771
|
+
one step (avoiding an intermediate redeploy that would briefly run
|
|
17772
|
+
the new code with the previous secret bindings).
|
|
17773
|
+
|
|
17774
|
+
If a secret write fails before the final redeploy, the function row
|
|
17775
|
+
carries whatever bindings landed but the running handler has NOT yet
|
|
17776
|
+
picked them up. Re-run \`primitive functions set-secret\` for the
|
|
17777
|
+
missing keys, then re-run \`primitive functions deploy\` (or
|
|
17778
|
+
\`functions redeploy\`) to push them live. ${SECRET_SOURCE_FLAGS_DESCRIPTION}`;
|
|
17668
17779
|
static summary = "Deploy a new function from a bundled handler file";
|
|
17669
17780
|
static examples = [
|
|
17670
17781
|
"<%= config.bin %> functions deploy --name forwarder --file ./bundle.js",
|
|
@@ -17674,6 +17785,7 @@ var FunctionsDeployCommand = class FunctionsDeployCommand extends Command {
|
|
|
17674
17785
|
"<%= config.bin %> functions deploy --name forwarder --file ./bundle.js --source-map-file ./bundle.js.map",
|
|
17675
17786
|
"<%= config.bin %> functions deploy --name forwarder --file ./bundle.js --secret OPENAI_KEY=sk-... --secret OWNER_EMAIL=me@example.com",
|
|
17676
17787
|
"<%= config.bin %> functions deploy --name forwarder --file ./bundle.js --secret-from-env OPENAI_KEY --secret-from-env-file .env.local:OWNER_EMAIL",
|
|
17788
|
+
"<%= config.bin %> functions deploy --name triage --source . --secret-from-env ANTHROPIC_API_KEY",
|
|
17677
17789
|
"printf '%s' \"$OPENAI_KEY\" | <%= config.bin %> functions deploy --name forwarder --file ./bundle.js --secret-from-stdin OPENAI_KEY"
|
|
17678
17790
|
];
|
|
17679
17791
|
static flags = {
|
|
@@ -17863,8 +17975,15 @@ var FunctionsDeployCommand = class FunctionsDeployCommand extends Command {
|
|
|
17863
17975
|
});
|
|
17864
17976
|
}
|
|
17865
17977
|
async runSourceMode(flags, sourceDir) {
|
|
17866
|
-
|
|
17867
|
-
|
|
17978
|
+
const parsedSecrets = resolveSecretFlags({
|
|
17979
|
+
fromEnv: flags["secret-from-env"] ?? [],
|
|
17980
|
+
fromEnvFile: flags["secret-from-env-file"] ?? [],
|
|
17981
|
+
fromFile: flags["secret-from-file"] ?? [],
|
|
17982
|
+
fromStdin: flags["secret-from-stdin"],
|
|
17983
|
+
inline: flags.secret ?? []
|
|
17984
|
+
});
|
|
17985
|
+
if (parsedSecrets.kind === "error") {
|
|
17986
|
+
process.stderr.write(`${parsedSecrets.message}\n`);
|
|
17868
17987
|
process.exitCode = 1;
|
|
17869
17988
|
return;
|
|
17870
17989
|
}
|
|
@@ -17884,7 +18003,7 @@ var FunctionsDeployCommand = class FunctionsDeployCommand extends Command {
|
|
|
17884
18003
|
baseUrlOverridden,
|
|
17885
18004
|
configDir: this.config.configDir
|
|
17886
18005
|
};
|
|
17887
|
-
const
|
|
18006
|
+
const apiSurface = {
|
|
17888
18007
|
createFunction: (p) => createFunction({
|
|
17889
18008
|
body: {
|
|
17890
18009
|
files: p.files,
|
|
@@ -17897,27 +18016,80 @@ var FunctionsDeployCommand = class FunctionsDeployCommand extends Command {
|
|
|
17897
18016
|
client: apiClient.client,
|
|
17898
18017
|
responseStyle: "fields"
|
|
17899
18018
|
}),
|
|
18019
|
+
setSecret: (p) => setFunctionSecret({
|
|
18020
|
+
body: { value: p.value },
|
|
18021
|
+
client: apiClient.client,
|
|
18022
|
+
path: {
|
|
18023
|
+
id: p.id,
|
|
18024
|
+
key: p.key
|
|
18025
|
+
},
|
|
18026
|
+
responseStyle: "fields"
|
|
18027
|
+
}),
|
|
17900
18028
|
updateFunction: (p) => updateFunction({
|
|
17901
18029
|
body: { files: p.files },
|
|
17902
18030
|
client: apiClient.client,
|
|
17903
18031
|
path: { id: p.id },
|
|
17904
18032
|
responseStyle: "fields"
|
|
17905
18033
|
})
|
|
17906
|
-
}
|
|
18034
|
+
};
|
|
18035
|
+
if (parsedSecrets.secrets.length === 0) {
|
|
18036
|
+
const outcome = await runSourceDeploy(apiSurface, {
|
|
18037
|
+
files: collected.files,
|
|
18038
|
+
name: flags.name
|
|
18039
|
+
});
|
|
18040
|
+
if (outcome.kind === "error") {
|
|
18041
|
+
renderBuildFailure(outcome.payload, (chunk) => process.stderr.write(chunk));
|
|
18042
|
+
writeErrorWithHints(outcome.payload);
|
|
18043
|
+
surfaceUnauthorizedHint({
|
|
18044
|
+
...authFailureContext,
|
|
18045
|
+
payload: outcome.payload
|
|
18046
|
+
});
|
|
18047
|
+
process.exitCode = 1;
|
|
18048
|
+
return;
|
|
18049
|
+
}
|
|
18050
|
+
await this.finishSourceDeploy({
|
|
18051
|
+
apiClient,
|
|
18052
|
+
authFailureContext,
|
|
18053
|
+
flags,
|
|
18054
|
+
payload: outcome.result
|
|
18055
|
+
});
|
|
18056
|
+
return;
|
|
18057
|
+
}
|
|
18058
|
+
const secretsOutcome = await runSourceDeployWithSecrets(apiSurface, {
|
|
17907
18059
|
files: collected.files,
|
|
17908
|
-
name: flags.name
|
|
18060
|
+
name: flags.name,
|
|
18061
|
+
secrets: parsedSecrets.secrets
|
|
17909
18062
|
});
|
|
17910
|
-
if (
|
|
17911
|
-
|
|
17912
|
-
|
|
18063
|
+
if (secretsOutcome.kind === "error") {
|
|
18064
|
+
if (secretsOutcome.stage === "set-secret") {
|
|
18065
|
+
const succeeded = secretsOutcome.succeededKeys.length > 0 ? secretsOutcome.succeededKeys.join(", ") : "(none)";
|
|
18066
|
+
const pending = secretsOutcome.pendingKeys.length > 0 ? secretsOutcome.pendingKeys.join(", ") : "(none)";
|
|
18067
|
+
const allMissing = [secretsOutcome.failedKey, ...secretsOutcome.pendingKeys].join(", ");
|
|
18068
|
+
const createdClause = secretsOutcome.created ? `Function ${secretsOutcome.created.name} (${secretsOutcome.functionId}) was created` : `Function ${flags.name} (${secretsOutcome.functionId}) already existed`;
|
|
18069
|
+
const stagingWarning = secretsOutcome.succeededKeys.length > 0 ? ` Note: [${succeeded}] are now staged on the function row and will bind on the next deploy of this function (including one that does not pass --secret).` : "";
|
|
18070
|
+
process.stderr.write(`${createdClause}, but writing secret ${secretsOutcome.failedKey} failed; succeeded keys so far: ${succeeded}; keys not yet attempted: ${pending}. The redeploy is NOT yet live. Re-run \`primitive functions set-secret\` for each of [${allMissing}], then \`primitive functions deploy --source ${sourceDir} --name ${flags.name}\` to push them live.${stagingWarning}\n`);
|
|
18071
|
+
} else if (secretsOutcome.stage === "secret-redeploy") {
|
|
18072
|
+
const succeeded = secretsOutcome.succeededKeys.length > 0 ? secretsOutcome.succeededKeys.join(", ") : "(none)";
|
|
18073
|
+
const createdClause = secretsOutcome.created ? `Function ${secretsOutcome.created.name} (${secretsOutcome.functionId}) was created and` : `Function ${flags.name} (${secretsOutcome.functionId}) already existed and`;
|
|
18074
|
+
process.stderr.write(`${createdClause} secrets [${succeeded}] were written, but the final redeploy failed; the new bindings are NOT yet live. Re-run \`primitive functions deploy --source ${sourceDir} --name ${flags.name}\` once the cause is fixed.\n`);
|
|
18075
|
+
} else renderBuildFailure(secretsOutcome.payload, (chunk) => process.stderr.write(chunk));
|
|
18076
|
+
writeErrorWithHints(secretsOutcome.payload);
|
|
17913
18077
|
surfaceUnauthorizedHint({
|
|
17914
18078
|
...authFailureContext,
|
|
17915
|
-
payload:
|
|
18079
|
+
payload: secretsOutcome.payload
|
|
17916
18080
|
});
|
|
17917
18081
|
process.exitCode = 1;
|
|
17918
18082
|
return;
|
|
17919
18083
|
}
|
|
17920
|
-
|
|
18084
|
+
await this.finishSourceDeploy({
|
|
18085
|
+
apiClient,
|
|
18086
|
+
authFailureContext,
|
|
18087
|
+
flags,
|
|
18088
|
+
payload: secretsOutcome.result.redeploy
|
|
18089
|
+
});
|
|
18090
|
+
}
|
|
18091
|
+
async finishSourceDeploy(args) {
|
|
18092
|
+
const { apiClient, authFailureContext, flags, payload } = args;
|
|
17921
18093
|
if (flags.wait) {
|
|
17922
18094
|
const waitResult = await waitForFunctionDeploy({
|
|
17923
18095
|
getFunction: (p) => getFunction({
|
|
@@ -17966,8 +18138,8 @@ const PRIMITIVE_TEAM_AUTHOR = {
|
|
|
17966
18138
|
name: "Primitive Team",
|
|
17967
18139
|
url: "https://primitive.dev"
|
|
17968
18140
|
};
|
|
17969
|
-
const SDK_VERSION_RANGE = "^1.
|
|
17970
|
-
const CLI_VERSION_RANGE = "^1.
|
|
18141
|
+
const SDK_VERSION_RANGE = "^1.2.0";
|
|
18142
|
+
const CLI_VERSION_RANGE = "^1.2.0";
|
|
17971
18143
|
const ESBUILD_VERSION_RANGE = "^0.27.0";
|
|
17972
18144
|
function renderHandler() {
|
|
17973
18145
|
return `// env.PRIMITIVE_API_KEY, env.PRIMITIVE_WEBHOOK_SECRET, and
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@primitivedotdev/cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "Official Primitive CLI: deploy Primitive Functions, send and inspect mail, manage endpoints, all from the terminal. Wraps the @primitivedotdev/sdk runtime client with one-shot commands.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|