@primitivedotdev/sdk 0.19.0 → 0.20.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/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { A as UnknownEvent, C as ParsedDataFailed, D as RawContentDownloadOnly, E as RawContent, M as WebhookAttachment, N as WebhookEvent, O as RawContentInline, S as ParsedDataComplete, T as ParsedStatus, _ as ForwardResultInline, a as DmarcPolicy, b as KnownWebhookEvent, c as EmailAnalysis, d as EventType, f as ForwardAnalysis, g as ForwardResultAttachmentSkipped, h as ForwardResultAttachmentAnalyzed, i as DkimSignature, j as ValidateEmailAuthResult, k as SpfResult, l as EmailAuth, m as ForwardResult, n as AuthVerdict, o as DmarcResult, p as ForwardOriginalSender, r as DkimResult, s as EmailAddress, t as AuthConfidence, u as EmailReceivedEvent, v as ForwardVerdict, w as ParsedError, x as ParsedData, y as ForwardVerification } from "./types-9vXGZjPd.js";
2
2
  import { a as buildReplySubject, c as parseHeaderAddress, i as buildForwardSubject, n as ReceivedEmailAddress, o as formatAddress, r as ReceivedEmailThread, s as normalizeReceivedEmail, t as ReceivedEmail } from "./received-email-DNjpq_Wt.js";
3
- import { a as PrimitiveApiError, c as PrimitiveClientOptions, d as SendInput, f as SendResult, g as createPrimitiveClient, l as ReplyInput, m as client, n as ForwardInput, p as SendThreadInput, s as PrimitiveClient } from "./index-oRkCqj6u.js";
3
+ import { a as PrimitiveApiError, c as PrimitiveClientOptions, d as SendInput, f as SendResult, g as createPrimitiveClient, l as ReplyInput, m as client, n as ForwardInput, p as SendThreadInput, s as PrimitiveClient } from "./index-C6ObsYjq.js";
4
4
  import { A as VerifyOptions, B as PAYLOAD_ERRORS, C as signStandardWebhooksPayload, D as PRIMITIVE_CONFIRMED_HEADER, E as LEGACY_SIGNATURE_HEADER, F as VerifyDownloadTokenResult, G as VERIFICATION_ERRORS, H as RAW_EMAIL_ERRORS, I as generateDownloadToken, J as WebhookPayloadErrorCode, K as WebhookErrorCode, L as verifyDownloadToken, M as verifyWebhookSignature, N as GenerateDownloadTokenOptions, O as PRIMITIVE_SIGNATURE_HEADER, P as VerifyDownloadTokenOptions, Q as WebhookVerificationErrorCode, R as safeValidateEmailReceivedEvent, S as StandardWebhooksVerifyOptions, T as LEGACY_CONFIRMED_HEADER, U as RawEmailDecodeError, V as PrimitiveWebhookError, W as RawEmailDecodeErrorCode, X as WebhookValidationErrorCode, Y as WebhookValidationError, Z as WebhookVerificationError, _ as emailReceivedEventJsonSchema, a as confirmedHeaders, b as STANDARD_WEBHOOK_TIMESTAMP_HEADER, c as handleWebhook, d as isRawIncluded, f as parseWebhookEvent, g as validateEmailAuth, h as WEBHOOK_VERSION, i as WebhookHeaders, j as signWebhookPayload, k as SignResult, l as isDownloadExpired, m as verifyRawEmailDownload, n as HandleWebhookOptions, o as decodeRawEmail, p as receive, q as WebhookPayloadError, r as ReceiveRequestOptions, s as getDownloadTimeRemaining, t as DecodeRawEmailOptions, u as isEmailReceivedEvent, v as STANDARD_WEBHOOK_ID_HEADER, w as verifyStandardWebhooksSignature, x as StandardWebhooksSignResult, y as STANDARD_WEBHOOK_SIGNATURE_HEADER, z as validateEmailReceivedEvent } from "./index-CDlwyxdp.js";
5
5
 
6
6
  //#region src/index.d.ts
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { a as parseHeaderAddress, i as normalizeReceivedEmail, n as buildReplySubject, r as formatAddress, t as buildForwardSubject } from "./received-email-D6tKtWwW.js";
2
- import { a as client, i as PrimitiveClient, r as PrimitiveApiError, s as createPrimitiveClient } from "./api-C5VR_Opg.js";
2
+ import { a as client, i as PrimitiveClient, r as PrimitiveApiError, s as createPrimitiveClient } from "./api-DNF21MDo.js";
3
3
  import { A as PRIMITIVE_CONFIRMED_HEADER, B as RAW_EMAIL_ERRORS, C as STANDARD_WEBHOOK_ID_HEADER, D as verifyStandardWebhooksSignature, E as signStandardWebhooksPayload, F as verifyDownloadToken, G as WebhookVerificationError, H as VERIFICATION_ERRORS, I as safeValidateEmailReceivedEvent, L as validateEmailReceivedEvent, M as signWebhookPayload, N as verifyWebhookSignature, O as LEGACY_CONFIRMED_HEADER, P as generateDownloadToken, R as PAYLOAD_ERRORS, S as emailReceivedEventJsonSchema, T as STANDARD_WEBHOOK_TIMESTAMP_HEADER, U as WebhookPayloadError, V as RawEmailDecodeError, W as WebhookValidationError, _ as DmarcResult, a as isDownloadExpired, b as ParsedStatus, c as parseWebhookEvent, d as WEBHOOK_VERSION, f as validateEmailAuth, g as DmarcPolicy, h as DkimResult, i as handleWebhook, j as PRIMITIVE_SIGNATURE_HEADER, k as LEGACY_SIGNATURE_HEADER, l as receive, m as AuthVerdict, n as decodeRawEmail, o as isEmailReceivedEvent, p as AuthConfidence, r as getDownloadTimeRemaining, s as isRawIncluded, t as confirmedHeaders, u as verifyRawEmailDownload, v as EventType, w as STANDARD_WEBHOOK_SIGNATURE_HEADER, x as SpfResult, y as ForwardVerdict, z as PrimitiveWebhookError } from "./webhook-rUjGV6Zu.js";
4
4
  //#region src/index.ts
5
5
  const primitive = {
@@ -224,6 +224,19 @@ export function readJsonBody(flags) {
224
224
  }
225
225
  return undefined;
226
226
  }
227
+ // Read a UTF-8 text file off disk, mapping any failure to a CLIError
228
+ // tagged with the originating flag so the user sees which path failed
229
+ // to open. Used by hand-rolled commands that take a file-input flag
230
+ // (e.g. functions:deploy --file).
231
+ export function readTextFileFlag(path, flagLabel) {
232
+ try {
233
+ return readFileSync(path, "utf8");
234
+ }
235
+ catch (error) {
236
+ const detail = error instanceof Error ? error.message : String(error);
237
+ throw cliError(`Could not read ${flagLabel} ${path}: ${detail}`);
238
+ }
239
+ }
227
240
  export function extractErrorPayload(raw) {
228
241
  if (raw &&
229
242
  typeof raw === "object" &&
@@ -0,0 +1,108 @@
1
+ import { Command, Flags } from "@oclif/core";
2
+ import { createFunction } from "../../api/generated/sdk.gen.js";
3
+ import { PrimitiveApiClient } from "../../api/index.js";
4
+ import { extractErrorPayload, readTextFileFlag, removeStaleSavedCredentialOnUnauthorized, runWithTiming, TIME_FLAG_DESCRIPTION, writeErrorWithHints, } from "../api-command.js";
5
+ import { resolveCliAuth } from "../auth.js";
6
+ // `primitive functions:deploy` is the agent-grade shortcut for
7
+ // `functions:create-function`. The underlying operation takes `code`
8
+ // as a string in the JSON body, which is awkward at the CLI for
9
+ // multi-line bundles: agents would otherwise have to shell-escape an
10
+ // entire ESM file or write a temp body.json. This command reads the
11
+ // bundle straight off disk via --file, so the natural workflow is:
12
+ //
13
+ // esbuild handler.ts --bundle --format=esm --outfile=bundle.js
14
+ // primitive functions:deploy --name myfn --file bundle.js
15
+ //
16
+ // Source maps follow the same shape via --source-map-file. They are
17
+ // stored only on the runtime side (not in our database) so dropping
18
+ // them later in the pipeline is fine; the CLI just hands them through.
19
+ //
20
+ // For full control (raw body, --raw-body JSON, etc.) the underlying
21
+ // `functions:create-function` operation stays available.
22
+ class FunctionsDeployCommand extends Command {
23
+ static description = `Deploy a new function from a bundled handler file. Agent-grade shortcut for functions:create-function.
24
+
25
+ Reads the bundle off disk (--file) instead of forcing the caller to
26
+ serialize the source into a JSON body. Use the underlying operation
27
+ \`functions:create-function\` if you need the full flag surface
28
+ (raw-body JSON, etc.).`;
29
+ static summary = "Deploy a new function from a bundled handler file";
30
+ static examples = [
31
+ "<%= config.bin %> functions:deploy --name forwarder --file ./bundle.js",
32
+ "<%= config.bin %> functions:deploy --name forwarder --file ./bundle.js --source-map-file ./bundle.js.map",
33
+ ];
34
+ static flags = {
35
+ "api-key": Flags.string({
36
+ description: "Primitive API key (defaults to PRIMITIVE_API_KEY or saved `primitive login` credentials)",
37
+ env: "PRIMITIVE_API_KEY",
38
+ }),
39
+ "base-url": Flags.string({
40
+ description: "API base URL (defaults to PRIMITIVE_API_URL or production)",
41
+ env: "PRIMITIVE_API_URL",
42
+ }),
43
+ name: Flags.string({
44
+ description: "Slug-style name. Lowercase letters, digits, hyphens, underscores. 1-64 chars. Must be unique within the org.",
45
+ required: true,
46
+ }),
47
+ file: Flags.string({
48
+ description: "Path to the bundled ESM handler file (single self-contained module). Loaded as the `code` body field.",
49
+ required: true,
50
+ }),
51
+ "source-map-file": Flags.string({
52
+ description: "Optional path to a source map for the bundle. Stored only on the runtime side and used to symbolicate stack traces.",
53
+ }),
54
+ time: Flags.boolean({
55
+ description: TIME_FLAG_DESCRIPTION,
56
+ }),
57
+ };
58
+ async run() {
59
+ const { flags } = await this.parse(FunctionsDeployCommand);
60
+ await runWithTiming(flags.time, async () => {
61
+ // Reads are inside the timed block so --time captures disk I/O
62
+ // alongside the API call. A pathological filesystem (NFS, slow
63
+ // FUSE mount) showing up here is exactly the kind of latency
64
+ // surprise --time is meant to surface.
65
+ const code = readTextFileFlag(flags.file, "--file");
66
+ const sourceMap = flags["source-map-file"]
67
+ ? readTextFileFlag(flags["source-map-file"], "--source-map-file")
68
+ : undefined;
69
+ const baseUrlOverridden = flags["base-url"] !== undefined;
70
+ const auth = resolveCliAuth({
71
+ apiKey: flags["api-key"],
72
+ baseUrl: flags["base-url"],
73
+ configDir: this.config.configDir,
74
+ });
75
+ const apiClient = new PrimitiveApiClient({
76
+ apiKey: auth.apiKey,
77
+ baseUrl: auth.baseUrl,
78
+ });
79
+ const authFailureContext = {
80
+ auth,
81
+ baseUrlOverridden,
82
+ configDir: this.config.configDir,
83
+ };
84
+ const result = await createFunction({
85
+ body: {
86
+ name: flags.name,
87
+ code,
88
+ ...(sourceMap !== undefined ? { sourceMap } : {}),
89
+ },
90
+ client: apiClient.client,
91
+ responseStyle: "fields",
92
+ });
93
+ if (result.error) {
94
+ const errorPayload = extractErrorPayload(result.error);
95
+ writeErrorWithHints(errorPayload);
96
+ removeStaleSavedCredentialOnUnauthorized({
97
+ ...authFailureContext,
98
+ payload: errorPayload,
99
+ });
100
+ process.exitCode = 1;
101
+ return;
102
+ }
103
+ const envelope = result.data;
104
+ this.log(JSON.stringify(envelope?.data ?? null, null, 2));
105
+ });
106
+ }
107
+ }
108
+ export default FunctionsDeployCommand;
@@ -0,0 +1,97 @@
1
+ import { Command, Flags } from "@oclif/core";
2
+ import { updateFunction } from "../../api/generated/sdk.gen.js";
3
+ import { PrimitiveApiClient } from "../../api/index.js";
4
+ import { extractErrorPayload, readTextFileFlag, removeStaleSavedCredentialOnUnauthorized, runWithTiming, TIME_FLAG_DESCRIPTION, writeErrorWithHints, } from "../api-command.js";
5
+ import { resolveCliAuth } from "../auth.js";
6
+ // `primitive functions:redeploy` is the agent-grade shortcut for
7
+ // `functions:update-function`. Same file-reading ergonomic as
8
+ // functions:deploy but for an existing function. Use this to push a
9
+ // new bundle, OR to refresh secret bindings: passing the
10
+ // previously-deployed bundle (or any equivalent file) re-runs the
11
+ // deploy and refreshes env from the secrets table, which is how
12
+ // secret writes go live.
13
+ class FunctionsRedeployCommand extends Command {
14
+ static description = `Update or redeploy a function from a bundled handler file. Agent-grade shortcut for functions:update-function.
15
+
16
+ Use to push a new bundle OR to refresh secret bindings into the
17
+ running handler. The same file is fine for both: the deploy reads
18
+ the bindings table fresh on every call, so passing the existing
19
+ bundle picks up any secret writes since the last deploy.`;
20
+ static summary = "Redeploy a function from a bundled handler file";
21
+ static examples = [
22
+ "<%= config.bin %> functions:redeploy --id <fn-id> --file ./bundle.js",
23
+ "<%= config.bin %> functions:redeploy --id <fn-id> --file ./bundle.js --source-map-file ./bundle.js.map",
24
+ ];
25
+ static flags = {
26
+ "api-key": Flags.string({
27
+ description: "Primitive API key (defaults to PRIMITIVE_API_KEY or saved `primitive login` credentials)",
28
+ env: "PRIMITIVE_API_KEY",
29
+ }),
30
+ "base-url": Flags.string({
31
+ description: "API base URL (defaults to PRIMITIVE_API_URL or production)",
32
+ env: "PRIMITIVE_API_URL",
33
+ }),
34
+ id: Flags.string({
35
+ description: "Function id (UUID). The function must already exist.",
36
+ required: true,
37
+ }),
38
+ file: Flags.string({
39
+ description: "Path to the bundled ESM handler file. Loaded as the `code` body field.",
40
+ required: true,
41
+ }),
42
+ "source-map-file": Flags.string({
43
+ description: "Optional path to a source map for the bundle. Used to symbolicate stack traces in the function's logs.",
44
+ }),
45
+ time: Flags.boolean({
46
+ description: TIME_FLAG_DESCRIPTION,
47
+ }),
48
+ };
49
+ async run() {
50
+ const { flags } = await this.parse(FunctionsRedeployCommand);
51
+ await runWithTiming(flags.time, async () => {
52
+ // Reads inside the timed block: --time captures disk I/O too,
53
+ // which is the latency the flag is meant to surface.
54
+ const code = readTextFileFlag(flags.file, "--file");
55
+ const sourceMap = flags["source-map-file"]
56
+ ? readTextFileFlag(flags["source-map-file"], "--source-map-file")
57
+ : undefined;
58
+ const baseUrlOverridden = flags["base-url"] !== undefined;
59
+ const auth = resolveCliAuth({
60
+ apiKey: flags["api-key"],
61
+ baseUrl: flags["base-url"],
62
+ configDir: this.config.configDir,
63
+ });
64
+ const apiClient = new PrimitiveApiClient({
65
+ apiKey: auth.apiKey,
66
+ baseUrl: auth.baseUrl,
67
+ });
68
+ const authFailureContext = {
69
+ auth,
70
+ baseUrlOverridden,
71
+ configDir: this.config.configDir,
72
+ };
73
+ const result = await updateFunction({
74
+ path: { id: flags.id },
75
+ body: {
76
+ code,
77
+ ...(sourceMap !== undefined ? { sourceMap } : {}),
78
+ },
79
+ client: apiClient.client,
80
+ responseStyle: "fields",
81
+ });
82
+ if (result.error) {
83
+ const errorPayload = extractErrorPayload(result.error);
84
+ writeErrorWithHints(errorPayload);
85
+ removeStaleSavedCredentialOnUnauthorized({
86
+ ...authFailureContext,
87
+ payload: errorPayload,
88
+ });
89
+ process.exitCode = 1;
90
+ return;
91
+ }
92
+ const envelope = result.data;
93
+ this.log(JSON.stringify(envelope?.data ?? null, null, 2));
94
+ });
95
+ }
96
+ }
97
+ export default FunctionsRedeployCommand;
@@ -2,6 +2,8 @@ import { Args, Command, Errors } from "@oclif/core";
2
2
  import { operationManifest, } from "../openapi/index.js";
3
3
  import { createOperationCommand } from "./api-command.js";
4
4
  import EmailsLatestCommand from "./commands/emails-latest.js";
5
+ import FunctionsDeployCommand from "./commands/functions-deploy.js";
6
+ import FunctionsRedeployCommand from "./commands/functions-redeploy.js";
5
7
  import LoginCommand from "./commands/login.js";
6
8
  import LogoutCommand from "./commands/logout.js";
7
9
  import SendCommand from "./commands/send.js";
@@ -131,5 +133,13 @@ export const COMMANDS = {
131
133
  // inbound emails as a compact text table. emails:list-emails stays
132
134
  // available for the full JSON envelope + cursor pagination.
133
135
  "emails:latest": EmailsLatestCommand,
136
+ // `functions:deploy` and `functions:redeploy` are file-input
137
+ // shortcuts for create-function / update-function. The underlying
138
+ // ops take `code` as a body string, which is awkward at the CLI
139
+ // for multi-line bundles; these read the bundle off disk and pass
140
+ // it through. The auto-generated functions:* operations stay
141
+ // available for callers that want the full surface.
142
+ "functions:deploy": FunctionsDeployCommand,
143
+ "functions:redeploy": FunctionsRedeployCommand,
134
144
  ...generatedCommands,
135
145
  };