@sendly/cli 3.27.1 → 3.32.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/README.md CHANGED
@@ -14,9 +14,19 @@ Official command-line interface for the [Sendly](https://sendly.live) SMS API.
14
14
  ## Installation
15
15
 
16
16
  ```bash
17
+ # npm
17
18
  npm install -g @sendly/cli
19
+
20
+ # or Homebrew (macOS / Linux)
21
+ brew install SendlyHQ/tap/sendly
18
22
  ```
19
23
 
24
+ ## Staying up to date
25
+
26
+ The CLI checks npm once a day and prints a one-line banner after your command if a newer version is out. Run `sendly upgrade` to update — it auto-detects your install path (Homebrew vs npm) and runs the right command. Use `sendly upgrade --check` to see what it would do without executing.
27
+
28
+ Banner is silent in CI (`CI=true`) and offline-safe.
29
+
20
30
  ## Quick Start
21
31
 
22
32
  ```bash
@@ -0,0 +1,12 @@
1
+ import { AuthenticatedCommand } from "../../lib/base-command.js";
2
+ export default class ContactsBulkMarkValid extends AuthenticatedCommand {
3
+ static description: string;
4
+ static examples: string[];
5
+ static flags: {
6
+ ids: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
7
+ list: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
8
+ json: import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
9
+ quiet: import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
10
+ };
11
+ run(): Promise<void>;
12
+ }
@@ -0,0 +1,46 @@
1
+ import { Flags } from "@oclif/core";
2
+ import { AuthenticatedCommand } from "../../lib/base-command.js";
3
+ import { apiClient } from "../../lib/api-client.js";
4
+ import { success, json, isJsonMode, error } from "../../lib/output.js";
5
+ export default class ContactsBulkMarkValid extends AuthenticatedCommand {
6
+ static description = "Clear the invalid flag on many contacts at once. Pass --ids (comma-separated, up to 10,000) OR --list <id>, not both. Escape hatch for when auto-mark misclassifies at scale.";
7
+ static examples = [
8
+ "<%= config.bin %> contacts bulk-mark-valid --ids cnt_abc,cnt_def,cnt_ghi",
9
+ "<%= config.bin %> contacts bulk-mark-valid --list lst_xxx",
10
+ ];
11
+ static flags = {
12
+ ...AuthenticatedCommand.baseFlags,
13
+ ids: Flags.string({
14
+ description: "Comma-separated contact IDs to clear (max 10,000 per call)",
15
+ exclusive: ["list"],
16
+ }),
17
+ list: Flags.string({
18
+ description: "Clear every flagged member of this list ID",
19
+ exclusive: ["ids"],
20
+ }),
21
+ };
22
+ async run() {
23
+ const { flags } = await this.parse(ContactsBulkMarkValid);
24
+ if (!flags.ids && !flags.list) {
25
+ error("Provide either --ids or --list");
26
+ this.exit(1);
27
+ }
28
+ const body = flags.ids
29
+ ? {
30
+ ids: flags.ids
31
+ .split(",")
32
+ .map((s) => s.trim())
33
+ .filter(Boolean),
34
+ }
35
+ : { listId: flags.list };
36
+ const result = await apiClient.post("/api/contacts/bulk-mark-valid", body);
37
+ if (isJsonMode()) {
38
+ json(result);
39
+ return;
40
+ }
41
+ success(result.cleared === 0
42
+ ? "No flagged contacts matched the selection"
43
+ : `${result.cleared} contact${result.cleared === 1 ? "" : "s"} marked as valid`, { Cleared: result.cleared });
44
+ }
45
+ }
46
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYnVsay1tYXJrLXZhbGlkLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL2NvbW1hbmRzL2NvbnRhY3RzL2J1bGstbWFyay12YWxpZC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsS0FBSyxFQUFFLE1BQU0sYUFBYSxDQUFDO0FBQ3BDLE9BQU8sRUFBRSxvQkFBb0IsRUFBRSxNQUFNLDJCQUEyQixDQUFDO0FBQ2pFLE9BQU8sRUFBRSxTQUFTLEVBQUUsTUFBTSx5QkFBeUIsQ0FBQztBQUNwRCxPQUFPLEVBQUUsT0FBTyxFQUFFLElBQUksRUFBRSxVQUFVLEVBQUUsS0FBSyxFQUFFLE1BQU0scUJBQXFCLENBQUM7QUFFdkUsTUFBTSxDQUFDLE9BQU8sT0FBTyxxQkFBc0IsU0FBUSxvQkFBb0I7SUFDckUsTUFBTSxDQUFDLFdBQVcsR0FDaEIsK0tBQStLLENBQUM7SUFFbEwsTUFBTSxDQUFDLFFBQVEsR0FBRztRQUNoQiwwRUFBMEU7UUFDMUUsMkRBQTJEO0tBQzVELENBQUM7SUFFRixNQUFNLENBQUMsS0FBSyxHQUFHO1FBQ2IsR0FBRyxvQkFBb0IsQ0FBQyxTQUFTO1FBQ2pDLEdBQUcsRUFBRSxLQUFLLENBQUMsTUFBTSxDQUFDO1lBQ2hCLFdBQVcsRUFDVCw0REFBNEQ7WUFDOUQsU0FBUyxFQUFFLENBQUMsTUFBTSxDQUFDO1NBQ3BCLENBQUM7UUFDRixJQUFJLEVBQUUsS0FBSyxDQUFDLE1BQU0sQ0FBQztZQUNqQixXQUFXLEVBQUUsNENBQTRDO1lBQ3pELFNBQVMsRUFBRSxDQUFDLEtBQUssQ0FBQztTQUNuQixDQUFDO0tBQ0gsQ0FBQztJQUVGLEtBQUssQ0FBQyxHQUFHO1FBQ1AsTUFBTSxFQUFFLEtBQUssRUFBRSxHQUFHLE1BQU0sSUFBSSxDQUFDLEtBQUssQ0FBQyxxQkFBcUIsQ0FBQyxDQUFDO1FBRTFELElBQUksQ0FBQyxLQUFLLENBQUMsR0FBRyxJQUFJLENBQUMsS0FBSyxDQUFDLElBQUksRUFBRSxDQUFDO1lBQzlCLEtBQUssQ0FBQyxnQ0FBZ0MsQ0FBQyxDQUFDO1lBQ3hDLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUM7UUFDZixDQUFDO1FBRUQsTUFBTSxJQUFJLEdBQXdDLEtBQUssQ0FBQyxHQUFHO1lBQ3pELENBQUMsQ0FBQztnQkFDRSxHQUFHLEVBQUUsS0FBSyxDQUFDLEdBQUc7cUJBQ1gsS0FBSyxDQUFDLEdBQUcsQ0FBQztxQkFDVixHQUFHLENBQUMsQ0FBQyxDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUMsQ0FBQyxJQUFJLEVBQUUsQ0FBQztxQkFDcEIsTUFBTSxDQUFDLE9BQU8sQ0FBQzthQUNuQjtZQUNILENBQUMsQ0FBQyxFQUFFLE1BQU0sRUFBRSxLQUFLLENBQUMsSUFBSSxFQUFFLENBQUM7UUFFM0IsTUFBTSxNQUFNLEdBQUcsTUFBTSxTQUFTLENBQUMsSUFBSSxDQUNqQywrQkFBK0IsRUFDL0IsSUFBSSxDQUNMLENBQUM7UUFFRixJQUFJLFVBQVUsRUFBRSxFQUFFLENBQUM7WUFDakIsSUFBSSxDQUFDLE1BQU0sQ0FBQyxDQUFDO1lBQ2IsT0FBTztRQUNULENBQUM7UUFFRCxPQUFPLENBQ0wsTUFBTSxDQUFDLE9BQU8sS0FBSyxDQUFDO1lBQ2xCLENBQUMsQ0FBQywyQ0FBMkM7WUFDN0MsQ0FBQyxDQUFDLEdBQUcsTUFBTSxDQUFDLE9BQU8sV0FBVyxNQUFNLENBQUMsT0FBTyxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQyxHQUFHLGtCQUFrQixFQUNqRixFQUFFLE9BQU8sRUFBRSxNQUFNLENBQUMsT0FBTyxFQUFFLENBQzVCLENBQUM7SUFDSixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgRmxhZ3MgfSBmcm9tIFwiQG9jbGlmL2NvcmVcIjtcbmltcG9ydCB7IEF1dGhlbnRpY2F0ZWRDb21tYW5kIH0gZnJvbSBcIi4uLy4uL2xpYi9iYXNlLWNvbW1hbmQuanNcIjtcbmltcG9ydCB7IGFwaUNsaWVudCB9IGZyb20gXCIuLi8uLi9saWIvYXBpLWNsaWVudC5qc1wiO1xuaW1wb3J0IHsgc3VjY2VzcywganNvbiwgaXNKc29uTW9kZSwgZXJyb3IgfSBmcm9tIFwiLi4vLi4vbGliL291dHB1dC5qc1wiO1xuXG5leHBvcnQgZGVmYXVsdCBjbGFzcyBDb250YWN0c0J1bGtNYXJrVmFsaWQgZXh0ZW5kcyBBdXRoZW50aWNhdGVkQ29tbWFuZCB7XG4gIHN0YXRpYyBkZXNjcmlwdGlvbiA9XG4gICAgXCJDbGVhciB0aGUgaW52YWxpZCBmbGFnIG9uIG1hbnkgY29udGFjdHMgYXQgb25jZS4gUGFzcyAtLWlkcyAoY29tbWEtc2VwYXJhdGVkLCB1cCB0byAxMCwwMDApIE9SIC0tbGlzdCA8aWQ+LCBub3QgYm90aC4gRXNjYXBlIGhhdGNoIGZvciB3aGVuIGF1dG8tbWFyayBtaXNjbGFzc2lmaWVzIGF0IHNjYWxlLlwiO1xuXG4gIHN0YXRpYyBleGFtcGxlcyA9IFtcbiAgICBcIjwlPSBjb25maWcuYmluICU+IGNvbnRhY3RzIGJ1bGstbWFyay12YWxpZCAtLWlkcyBjbnRfYWJjLGNudF9kZWYsY250X2doaVwiLFxuICAgIFwiPCU9IGNvbmZpZy5iaW4gJT4gY29udGFjdHMgYnVsay1tYXJrLXZhbGlkIC0tbGlzdCBsc3RfeHh4XCIsXG4gIF07XG5cbiAgc3RhdGljIGZsYWdzID0ge1xuICAgIC4uLkF1dGhlbnRpY2F0ZWRDb21tYW5kLmJhc2VGbGFncyxcbiAgICBpZHM6IEZsYWdzLnN0cmluZyh7XG4gICAgICBkZXNjcmlwdGlvbjpcbiAgICAgICAgXCJDb21tYS1zZXBhcmF0ZWQgY29udGFjdCBJRHMgdG8gY2xlYXIgKG1heCAxMCwwMDAgcGVyIGNhbGwpXCIsXG4gICAgICBleGNsdXNpdmU6IFtcImxpc3RcIl0sXG4gICAgfSksXG4gICAgbGlzdDogRmxhZ3Muc3RyaW5nKHtcbiAgICAgIGRlc2NyaXB0aW9uOiBcIkNsZWFyIGV2ZXJ5IGZsYWdnZWQgbWVtYmVyIG9mIHRoaXMgbGlzdCBJRFwiLFxuICAgICAgZXhjbHVzaXZlOiBbXCJpZHNcIl0sXG4gICAgfSksXG4gIH07XG5cbiAgYXN5bmMgcnVuKCk6IFByb21pc2U8dm9pZD4ge1xuICAgIGNvbnN0IHsgZmxhZ3MgfSA9IGF3YWl0IHRoaXMucGFyc2UoQ29udGFjdHNCdWxrTWFya1ZhbGlkKTtcblxuICAgIGlmICghZmxhZ3MuaWRzICYmICFmbGFncy5saXN0KSB7XG4gICAgICBlcnJvcihcIlByb3ZpZGUgZWl0aGVyIC0taWRzIG9yIC0tbGlzdFwiKTtcbiAgICAgIHRoaXMuZXhpdCgxKTtcbiAgICB9XG5cbiAgICBjb25zdCBib2R5OiB7IGlkcz86IHN0cmluZ1tdOyBsaXN0SWQ/OiBzdHJpbmcgfSA9IGZsYWdzLmlkc1xuICAgICAgPyB7XG4gICAgICAgICAgaWRzOiBmbGFncy5pZHNcbiAgICAgICAgICAgIC5zcGxpdChcIixcIilcbiAgICAgICAgICAgIC5tYXAoKHMpID0+IHMudHJpbSgpKVxuICAgICAgICAgICAgLmZpbHRlcihCb29sZWFuKSxcbiAgICAgICAgfVxuICAgICAgOiB7IGxpc3RJZDogZmxhZ3MubGlzdCB9O1xuXG4gICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgYXBpQ2xpZW50LnBvc3Q8eyBjbGVhcmVkOiBudW1iZXIgfT4oXG4gICAgICBcIi9hcGkvY29udGFjdHMvYnVsay1tYXJrLXZhbGlkXCIsXG4gICAgICBib2R5LFxuICAgICk7XG5cbiAgICBpZiAoaXNKc29uTW9kZSgpKSB7XG4gICAgICBqc29uKHJlc3VsdCk7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgc3VjY2VzcyhcbiAgICAgIHJlc3VsdC5jbGVhcmVkID09PSAwXG4gICAgICAgID8gXCJObyBmbGFnZ2VkIGNvbnRhY3RzIG1hdGNoZWQgdGhlIHNlbGVjdGlvblwiXG4gICAgICAgIDogYCR7cmVzdWx0LmNsZWFyZWR9IGNvbnRhY3Qke3Jlc3VsdC5jbGVhcmVkID09PSAxID8gXCJcIiA6IFwic1wifSBtYXJrZWQgYXMgdmFsaWRgLFxuICAgICAgeyBDbGVhcmVkOiByZXN1bHQuY2xlYXJlZCB9LFxuICAgICk7XG4gIH1cbn1cbiJdfQ==
@@ -0,0 +1,12 @@
1
+ import { AuthenticatedCommand } from "../../lib/base-command.js";
2
+ export default class ContactsCheckNumbers extends AuthenticatedCommand {
3
+ static description: string;
4
+ static examples: string[];
5
+ static flags: {
6
+ list: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
7
+ force: import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
8
+ json: import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
9
+ quiet: import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
10
+ };
11
+ run(): Promise<void>;
12
+ }
@@ -0,0 +1,41 @@
1
+ import { Flags } from "@oclif/core";
2
+ import { AuthenticatedCommand } from "../../lib/base-command.js";
3
+ import { apiClient } from "../../lib/api-client.js";
4
+ import { success, json, isJsonMode, info } from "../../lib/output.js";
5
+ export default class ContactsCheckNumbers extends AuthenticatedCommand {
6
+ static description = "Trigger a background carrier lookup across your contacts. Landlines and other non-SMS-capable numbers are auto-excluded from future campaigns. Runs asynchronously — results populate line_type / carrier_name / invalid_reason on affected contacts within a few minutes.";
7
+ static examples = [
8
+ "<%= config.bin %> contacts check-numbers",
9
+ "<%= config.bin %> contacts check-numbers --list lst_xxx",
10
+ "<%= config.bin %> contacts check-numbers --force",
11
+ ];
12
+ static flags = {
13
+ ...AuthenticatedCommand.baseFlags,
14
+ list: Flags.string({
15
+ char: "l",
16
+ description: "Scope the lookup to a single contact list",
17
+ }),
18
+ force: Flags.boolean({
19
+ description: "Re-check contacts even if already looked up",
20
+ default: false,
21
+ }),
22
+ };
23
+ async run() {
24
+ const { flags } = await this.parse(ContactsCheckNumbers);
25
+ const response = await apiClient.post("/api/contacts/lookup", {
26
+ listId: flags.list ?? null,
27
+ force: flags.force,
28
+ });
29
+ if (isJsonMode()) {
30
+ json(response);
31
+ return;
32
+ }
33
+ success("Carrier lookup started", {
34
+ Scope: flags.list ? `list ${flags.list}` : "all un-checked contacts",
35
+ Force: flags.force ? "yes" : "no",
36
+ });
37
+ info(response.message ??
38
+ "Results will appear on contacts within a few minutes. Run `sendly contacts list` to check progress.");
39
+ }
40
+ }
41
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2hlY2stbnVtYmVycy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9jb21tYW5kcy9jb250YWN0cy9jaGVjay1udW1iZXJzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxLQUFLLEVBQUUsTUFBTSxhQUFhLENBQUM7QUFDcEMsT0FBTyxFQUFFLG9CQUFvQixFQUFFLE1BQU0sMkJBQTJCLENBQUM7QUFDakUsT0FBTyxFQUFFLFNBQVMsRUFBRSxNQUFNLHlCQUF5QixDQUFDO0FBQ3BELE9BQU8sRUFBRSxPQUFPLEVBQUUsSUFBSSxFQUFFLFVBQVUsRUFBRSxJQUFJLEVBQUUsTUFBTSxxQkFBcUIsQ0FBQztBQUV0RSxNQUFNLENBQUMsT0FBTyxPQUFPLG9CQUFxQixTQUFRLG9CQUFvQjtJQUNwRSxNQUFNLENBQUMsV0FBVyxHQUNoQiw0UUFBNFEsQ0FBQztJQUUvUSxNQUFNLENBQUMsUUFBUSxHQUFHO1FBQ2hCLDBDQUEwQztRQUMxQyx5REFBeUQ7UUFDekQsa0RBQWtEO0tBQ25ELENBQUM7SUFFRixNQUFNLENBQUMsS0FBSyxHQUFHO1FBQ2IsR0FBRyxvQkFBb0IsQ0FBQyxTQUFTO1FBQ2pDLElBQUksRUFBRSxLQUFLLENBQUMsTUFBTSxDQUFDO1lBQ2pCLElBQUksRUFBRSxHQUFHO1lBQ1QsV0FBVyxFQUFFLDJDQUEyQztTQUN6RCxDQUFDO1FBQ0YsS0FBSyxFQUFFLEtBQUssQ0FBQyxPQUFPLENBQUM7WUFDbkIsV0FBVyxFQUFFLDZDQUE2QztZQUMxRCxPQUFPLEVBQUUsS0FBSztTQUNmLENBQUM7S0FDSCxDQUFDO0lBRUYsS0FBSyxDQUFDLEdBQUc7UUFDUCxNQUFNLEVBQUUsS0FBSyxFQUFFLEdBQUcsTUFBTSxJQUFJLENBQUMsS0FBSyxDQUFDLG9CQUFvQixDQUFDLENBQUM7UUFFekQsTUFBTSxRQUFRLEdBQUcsTUFBTSxTQUFTLENBQUMsSUFBSSxDQUdsQyxzQkFBc0IsRUFBRTtZQUN6QixNQUFNLEVBQUUsS0FBSyxDQUFDLElBQUksSUFBSSxJQUFJO1lBQzFCLEtBQUssRUFBRSxLQUFLLENBQUMsS0FBSztTQUNuQixDQUFDLENBQUM7UUFFSCxJQUFJLFVBQVUsRUFBRSxFQUFFLENBQUM7WUFDakIsSUFBSSxDQUFDLFFBQVEsQ0FBQyxDQUFDO1lBQ2YsT0FBTztRQUNULENBQUM7UUFFRCxPQUFPLENBQUMsd0JBQXdCLEVBQUU7WUFDaEMsS0FBSyxFQUFFLEtBQUssQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLFFBQVEsS0FBSyxDQUFDLElBQUksRUFBRSxDQUFDLENBQUMsQ0FBQyx5QkFBeUI7WUFDcEUsS0FBSyxFQUFFLEtBQUssQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsSUFBSTtTQUNsQyxDQUFDLENBQUM7UUFDSCxJQUFJLENBQ0YsUUFBUSxDQUFDLE9BQU87WUFDZCxxR0FBcUcsQ0FDeEcsQ0FBQztJQUNKLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBGbGFncyB9IGZyb20gXCJAb2NsaWYvY29yZVwiO1xuaW1wb3J0IHsgQXV0aGVudGljYXRlZENvbW1hbmQgfSBmcm9tIFwiLi4vLi4vbGliL2Jhc2UtY29tbWFuZC5qc1wiO1xuaW1wb3J0IHsgYXBpQ2xpZW50IH0gZnJvbSBcIi4uLy4uL2xpYi9hcGktY2xpZW50LmpzXCI7XG5pbXBvcnQgeyBzdWNjZXNzLCBqc29uLCBpc0pzb25Nb2RlLCBpbmZvIH0gZnJvbSBcIi4uLy4uL2xpYi9vdXRwdXQuanNcIjtcblxuZXhwb3J0IGRlZmF1bHQgY2xhc3MgQ29udGFjdHNDaGVja051bWJlcnMgZXh0ZW5kcyBBdXRoZW50aWNhdGVkQ29tbWFuZCB7XG4gIHN0YXRpYyBkZXNjcmlwdGlvbiA9XG4gICAgXCJUcmlnZ2VyIGEgYmFja2dyb3VuZCBjYXJyaWVyIGxvb2t1cCBhY3Jvc3MgeW91ciBjb250YWN0cy4gTGFuZGxpbmVzIGFuZCBvdGhlciBub24tU01TLWNhcGFibGUgbnVtYmVycyBhcmUgYXV0by1leGNsdWRlZCBmcm9tIGZ1dHVyZSBjYW1wYWlnbnMuIFJ1bnMgYXN5bmNocm9ub3VzbHkg4oCUIHJlc3VsdHMgcG9wdWxhdGUgbGluZV90eXBlIC8gY2Fycmllcl9uYW1lIC8gaW52YWxpZF9yZWFzb24gb24gYWZmZWN0ZWQgY29udGFjdHMgd2l0aGluIGEgZmV3IG1pbnV0ZXMuXCI7XG5cbiAgc3RhdGljIGV4YW1wbGVzID0gW1xuICAgIFwiPCU9IGNvbmZpZy5iaW4gJT4gY29udGFjdHMgY2hlY2stbnVtYmVyc1wiLFxuICAgIFwiPCU9IGNvbmZpZy5iaW4gJT4gY29udGFjdHMgY2hlY2stbnVtYmVycyAtLWxpc3QgbHN0X3h4eFwiLFxuICAgIFwiPCU9IGNvbmZpZy5iaW4gJT4gY29udGFjdHMgY2hlY2stbnVtYmVycyAtLWZvcmNlXCIsXG4gIF07XG5cbiAgc3RhdGljIGZsYWdzID0ge1xuICAgIC4uLkF1dGhlbnRpY2F0ZWRDb21tYW5kLmJhc2VGbGFncyxcbiAgICBsaXN0OiBGbGFncy5zdHJpbmcoe1xuICAgICAgY2hhcjogXCJsXCIsXG4gICAgICBkZXNjcmlwdGlvbjogXCJTY29wZSB0aGUgbG9va3VwIHRvIGEgc2luZ2xlIGNvbnRhY3QgbGlzdFwiLFxuICAgIH0pLFxuICAgIGZvcmNlOiBGbGFncy5ib29sZWFuKHtcbiAgICAgIGRlc2NyaXB0aW9uOiBcIlJlLWNoZWNrIGNvbnRhY3RzIGV2ZW4gaWYgYWxyZWFkeSBsb29rZWQgdXBcIixcbiAgICAgIGRlZmF1bHQ6IGZhbHNlLFxuICAgIH0pLFxuICB9O1xuXG4gIGFzeW5jIHJ1bigpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBjb25zdCB7IGZsYWdzIH0gPSBhd2FpdCB0aGlzLnBhcnNlKENvbnRhY3RzQ2hlY2tOdW1iZXJzKTtcblxuICAgIGNvbnN0IHJlc3BvbnNlID0gYXdhaXQgYXBpQ2xpZW50LnBvc3Q8e1xuICAgICAgc3VjY2VzczogYm9vbGVhbjtcbiAgICAgIG1lc3NhZ2U/OiBzdHJpbmc7XG4gICAgfT4oXCIvYXBpL2NvbnRhY3RzL2xvb2t1cFwiLCB7XG4gICAgICBsaXN0SWQ6IGZsYWdzLmxpc3QgPz8gbnVsbCxcbiAgICAgIGZvcmNlOiBmbGFncy5mb3JjZSxcbiAgICB9KTtcblxuICAgIGlmIChpc0pzb25Nb2RlKCkpIHtcbiAgICAgIGpzb24ocmVzcG9uc2UpO1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIHN1Y2Nlc3MoXCJDYXJyaWVyIGxvb2t1cCBzdGFydGVkXCIsIHtcbiAgICAgIFNjb3BlOiBmbGFncy5saXN0ID8gYGxpc3QgJHtmbGFncy5saXN0fWAgOiBcImFsbCB1bi1jaGVja2VkIGNvbnRhY3RzXCIsXG4gICAgICBGb3JjZTogZmxhZ3MuZm9yY2UgPyBcInllc1wiIDogXCJub1wiLFxuICAgIH0pO1xuICAgIGluZm8oXG4gICAgICByZXNwb25zZS5tZXNzYWdlID8/XG4gICAgICAgIFwiUmVzdWx0cyB3aWxsIGFwcGVhciBvbiBjb250YWN0cyB3aXRoaW4gYSBmZXcgbWludXRlcy4gUnVuIGBzZW5kbHkgY29udGFjdHMgbGlzdGAgdG8gY2hlY2sgcHJvZ3Jlc3MuXCIsXG4gICAgKTtcbiAgfVxufVxuIl19
@@ -0,0 +1,13 @@
1
+ import { AuthenticatedCommand } from "../../lib/base-command.js";
2
+ export default class ContactsMarkValid extends AuthenticatedCommand {
3
+ static description: string;
4
+ static examples: string[];
5
+ static args: {
6
+ id: import("@oclif/core/lib/interfaces/parser.js").Arg<string, Record<string, unknown>>;
7
+ };
8
+ static flags: {
9
+ json: import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
10
+ quiet: import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
11
+ };
12
+ run(): Promise<void>;
13
+ }
@@ -0,0 +1,32 @@
1
+ import { Args } from "@oclif/core";
2
+ import { AuthenticatedCommand } from "../../lib/base-command.js";
3
+ import { apiClient } from "../../lib/api-client.js";
4
+ import { success, json, isJsonMode } from "../../lib/output.js";
5
+ export default class ContactsMarkValid extends AuthenticatedCommand {
6
+ static description = "Clear the invalid flag on a contact so future campaigns include it again. Use when you disagree with an auto-exclusion (e.g. the recipient ported from a landline to mobile).";
7
+ static examples = [
8
+ "<%= config.bin %> contacts mark-valid cnt_xxx",
9
+ ];
10
+ static args = {
11
+ id: Args.string({
12
+ description: "Contact ID to clear the invalid flag on",
13
+ required: true,
14
+ }),
15
+ };
16
+ static flags = {
17
+ ...AuthenticatedCommand.baseFlags,
18
+ };
19
+ async run() {
20
+ const { args } = await this.parse(ContactsMarkValid);
21
+ const contact = await apiClient.post(`/api/contacts/${args.id}/mark-valid`);
22
+ if (isJsonMode()) {
23
+ json(contact);
24
+ return;
25
+ }
26
+ success("Contact marked as valid — future campaigns will include it", {
27
+ ID: contact.id,
28
+ Phone: contact.phoneNumber ?? contact.phone_number ?? "—",
29
+ });
30
+ }
31
+ }
32
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWFyay12YWxpZC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9jb21tYW5kcy9jb250YWN0cy9tYXJrLXZhbGlkLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxJQUFJLEVBQUUsTUFBTSxhQUFhLENBQUM7QUFDbkMsT0FBTyxFQUFFLG9CQUFvQixFQUFFLE1BQU0sMkJBQTJCLENBQUM7QUFDakUsT0FBTyxFQUFFLFNBQVMsRUFBRSxNQUFNLHlCQUF5QixDQUFDO0FBQ3BELE9BQU8sRUFBRSxPQUFPLEVBQUUsSUFBSSxFQUFFLFVBQVUsRUFBRSxNQUFNLHFCQUFxQixDQUFDO0FBRWhFLE1BQU0sQ0FBQyxPQUFPLE9BQU8saUJBQWtCLFNBQVEsb0JBQW9CO0lBQ2pFLE1BQU0sQ0FBQyxXQUFXLEdBQ2hCLCtLQUErSyxDQUFDO0lBRWxMLE1BQU0sQ0FBQyxRQUFRLEdBQUc7UUFDaEIsK0NBQStDO0tBQ2hELENBQUM7SUFFRixNQUFNLENBQUMsSUFBSSxHQUFHO1FBQ1osRUFBRSxFQUFFLElBQUksQ0FBQyxNQUFNLENBQUM7WUFDZCxXQUFXLEVBQUUseUNBQXlDO1lBQ3RELFFBQVEsRUFBRSxJQUFJO1NBQ2YsQ0FBQztLQUNILENBQUM7SUFFRixNQUFNLENBQUMsS0FBSyxHQUFHO1FBQ2IsR0FBRyxvQkFBb0IsQ0FBQyxTQUFTO0tBQ2xDLENBQUM7SUFFRixLQUFLLENBQUMsR0FBRztRQUNQLE1BQU0sRUFBRSxJQUFJLEVBQUUsR0FBRyxNQUFNLElBQUksQ0FBQyxLQUFLLENBQUMsaUJBQWlCLENBQUMsQ0FBQztRQUVyRCxNQUFNLE9BQU8sR0FBRyxNQUFNLFNBQVMsQ0FBQyxJQUFJLENBTWpDLGlCQUFpQixJQUFJLENBQUMsRUFBRSxhQUFhLENBQUMsQ0FBQztRQUUxQyxJQUFJLFVBQVUsRUFBRSxFQUFFLENBQUM7WUFDakIsSUFBSSxDQUFDLE9BQU8sQ0FBQyxDQUFDO1lBQ2QsT0FBTztRQUNULENBQUM7UUFFRCxPQUFPLENBQUMsNERBQTRELEVBQUU7WUFDcEUsRUFBRSxFQUFFLE9BQU8sQ0FBQyxFQUFFO1lBQ2QsS0FBSyxFQUFFLE9BQU8sQ0FBQyxXQUFXLElBQUksT0FBTyxDQUFDLFlBQVksSUFBSSxHQUFHO1NBQzFELENBQUMsQ0FBQztJQUNMLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBBcmdzIH0gZnJvbSBcIkBvY2xpZi9jb3JlXCI7XG5pbXBvcnQgeyBBdXRoZW50aWNhdGVkQ29tbWFuZCB9IGZyb20gXCIuLi8uLi9saWIvYmFzZS1jb21tYW5kLmpzXCI7XG5pbXBvcnQgeyBhcGlDbGllbnQgfSBmcm9tIFwiLi4vLi4vbGliL2FwaS1jbGllbnQuanNcIjtcbmltcG9ydCB7IHN1Y2Nlc3MsIGpzb24sIGlzSnNvbk1vZGUgfSBmcm9tIFwiLi4vLi4vbGliL291dHB1dC5qc1wiO1xuXG5leHBvcnQgZGVmYXVsdCBjbGFzcyBDb250YWN0c01hcmtWYWxpZCBleHRlbmRzIEF1dGhlbnRpY2F0ZWRDb21tYW5kIHtcbiAgc3RhdGljIGRlc2NyaXB0aW9uID1cbiAgICBcIkNsZWFyIHRoZSBpbnZhbGlkIGZsYWcgb24gYSBjb250YWN0IHNvIGZ1dHVyZSBjYW1wYWlnbnMgaW5jbHVkZSBpdCBhZ2Fpbi4gVXNlIHdoZW4geW91IGRpc2FncmVlIHdpdGggYW4gYXV0by1leGNsdXNpb24gKGUuZy4gdGhlIHJlY2lwaWVudCBwb3J0ZWQgZnJvbSBhIGxhbmRsaW5lIHRvIG1vYmlsZSkuXCI7XG5cbiAgc3RhdGljIGV4YW1wbGVzID0gW1xuICAgIFwiPCU9IGNvbmZpZy5iaW4gJT4gY29udGFjdHMgbWFyay12YWxpZCBjbnRfeHh4XCIsXG4gIF07XG5cbiAgc3RhdGljIGFyZ3MgPSB7XG4gICAgaWQ6IEFyZ3Muc3RyaW5nKHtcbiAgICAgIGRlc2NyaXB0aW9uOiBcIkNvbnRhY3QgSUQgdG8gY2xlYXIgdGhlIGludmFsaWQgZmxhZyBvblwiLFxuICAgICAgcmVxdWlyZWQ6IHRydWUsXG4gICAgfSksXG4gIH07XG5cbiAgc3RhdGljIGZsYWdzID0ge1xuICAgIC4uLkF1dGhlbnRpY2F0ZWRDb21tYW5kLmJhc2VGbGFncyxcbiAgfTtcblxuICBhc3luYyBydW4oKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgY29uc3QgeyBhcmdzIH0gPSBhd2FpdCB0aGlzLnBhcnNlKENvbnRhY3RzTWFya1ZhbGlkKTtcblxuICAgIGNvbnN0IGNvbnRhY3QgPSBhd2FpdCBhcGlDbGllbnQucG9zdDx7XG4gICAgICBpZDogc3RyaW5nO1xuICAgICAgcGhvbmVOdW1iZXI/OiBzdHJpbmc7XG4gICAgICBwaG9uZV9udW1iZXI/OiBzdHJpbmc7XG4gICAgICBpbnZhbGlkUmVhc29uPzogc3RyaW5nIHwgbnVsbDtcbiAgICAgIGludmFsaWRfcmVhc29uPzogc3RyaW5nIHwgbnVsbDtcbiAgICB9PihgL2FwaS9jb250YWN0cy8ke2FyZ3MuaWR9L21hcmstdmFsaWRgKTtcblxuICAgIGlmIChpc0pzb25Nb2RlKCkpIHtcbiAgICAgIGpzb24oY29udGFjdCk7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgc3VjY2VzcyhcIkNvbnRhY3QgbWFya2VkIGFzIHZhbGlkIOKAlCBmdXR1cmUgY2FtcGFpZ25zIHdpbGwgaW5jbHVkZSBpdFwiLCB7XG4gICAgICBJRDogY29udGFjdC5pZCxcbiAgICAgIFBob25lOiBjb250YWN0LnBob25lTnVtYmVyID8/IGNvbnRhY3QucGhvbmVfbnVtYmVyID8/IFwi4oCUXCIsXG4gICAgfSk7XG4gIH1cbn1cbiJdfQ==
@@ -0,0 +1,21 @@
1
+ import { BaseCommand } from "../lib/base-command.js";
2
+ /**
3
+ * `sendly countries` — list supported countries with pricing.
4
+ *
5
+ * Backs the public `GET /api/v1/countries` endpoint. No auth required so
6
+ * customers can browse before signing up. Useful for populating country
7
+ * pickers in sign-up forms, validating phone numbers client-side, and
8
+ * checking which tiers require registration (toll-free brand
9
+ * verification in US/CA, for instance).
10
+ */
11
+ export default class Countries extends BaseCommand {
12
+ static description: string;
13
+ static examples: string[];
14
+ static flags: {
15
+ search: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
16
+ tier: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
17
+ json: import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
18
+ quiet: import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
19
+ };
20
+ run(): Promise<void>;
21
+ }
@@ -0,0 +1,96 @@
1
+ import { Flags } from "@oclif/core";
2
+ import { BaseCommand } from "../lib/base-command.js";
3
+ import { apiClient } from "../lib/api-client.js";
4
+ import { json, isJsonMode, colors, header } from "../lib/output.js";
5
+ /**
6
+ * `sendly countries` — list supported countries with pricing.
7
+ *
8
+ * Backs the public `GET /api/v1/countries` endpoint. No auth required so
9
+ * customers can browse before signing up. Useful for populating country
10
+ * pickers in sign-up forms, validating phone numbers client-side, and
11
+ * checking which tiers require registration (toll-free brand
12
+ * verification in US/CA, for instance).
13
+ */
14
+ export default class Countries extends BaseCommand {
15
+ static description = "List supported countries with pricing (credits per SMS, USD price, tier). No auth required.";
16
+ static examples = [
17
+ "<%= config.bin %> countries",
18
+ "<%= config.bin %> countries --search UK",
19
+ "<%= config.bin %> countries --tier domestic",
20
+ "<%= config.bin %> countries --json",
21
+ ];
22
+ static flags = {
23
+ ...BaseCommand.baseFlags,
24
+ search: Flags.string({
25
+ description: "Filter by name, country code, or dial code",
26
+ }),
27
+ tier: Flags.string({
28
+ description: "Filter by pricing tier (domestic, international, etc.)",
29
+ }),
30
+ };
31
+ async run() {
32
+ const { flags } = await this.parse(Countries);
33
+ const resp = await apiClient.get("/api/v1/countries");
34
+ const all = Array.isArray(resp)
35
+ ? resp
36
+ : (resp.countries ?? []);
37
+ const filtered = all.filter((c) => {
38
+ if (flags.tier && c.tier !== flags.tier)
39
+ return false;
40
+ if (flags.search) {
41
+ const q = flags.search.toLowerCase();
42
+ return (c.code.toLowerCase().includes(q) ||
43
+ c.name.toLowerCase().includes(q) ||
44
+ c.dial_code.includes(q));
45
+ }
46
+ return true;
47
+ });
48
+ if (isJsonMode()) {
49
+ json(filtered);
50
+ return;
51
+ }
52
+ header(`Supported countries (${filtered.length})`);
53
+ if (filtered.length === 0) {
54
+ console.log(colors.dim(" (no matches)"));
55
+ return;
56
+ }
57
+ // Column widths derived from the data so the table stays compact.
58
+ const codeW = 4;
59
+ const dialW = Math.max(...filtered.map((c) => c.dial_code.length), 5);
60
+ const nameW = Math.max(...filtered.map((c) => c.name.length), 12);
61
+ console.log(" " +
62
+ colors.dim("Code".padEnd(codeW)) +
63
+ " " +
64
+ colors.dim("Dial".padEnd(dialW)) +
65
+ " " +
66
+ colors.dim("Name".padEnd(nameW)) +
67
+ " " +
68
+ colors.dim("Tier".padEnd(14)) +
69
+ " " +
70
+ colors.dim("Credits/SMS".padStart(11)) +
71
+ " " +
72
+ colors.dim("Price (USD)".padStart(11)) +
73
+ " " +
74
+ colors.dim("Registration"));
75
+ for (const c of filtered) {
76
+ const regHint = c.requires_registration
77
+ ? colors.warning("required")
78
+ : colors.dim("not required");
79
+ console.log(" " +
80
+ c.code.padEnd(codeW) +
81
+ " " +
82
+ c.dial_code.padEnd(dialW) +
83
+ " " +
84
+ c.name.padEnd(nameW) +
85
+ " " +
86
+ c.tier.padEnd(14) +
87
+ " " +
88
+ String(c.credits_per_sms).padStart(11) +
89
+ " " +
90
+ c.price_usd.toFixed(4).padStart(11) +
91
+ " " +
92
+ regHint);
93
+ }
94
+ }
95
+ }
96
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY291bnRyaWVzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2NvbW1hbmRzL2NvdW50cmllcy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsS0FBSyxFQUFFLE1BQU0sYUFBYSxDQUFDO0FBQ3BDLE9BQU8sRUFBRSxXQUFXLEVBQUUsTUFBTSx3QkFBd0IsQ0FBQztBQUNyRCxPQUFPLEVBQUUsU0FBUyxFQUFFLE1BQU0sc0JBQXNCLENBQUM7QUFDakQsT0FBTyxFQUFFLElBQUksRUFBRSxVQUFVLEVBQUUsTUFBTSxFQUFFLE1BQU0sRUFBRSxNQUFNLGtCQUFrQixDQUFDO0FBWXBFOzs7Ozs7OztHQVFHO0FBQ0gsTUFBTSxDQUFDLE9BQU8sT0FBTyxTQUFVLFNBQVEsV0FBVztJQUNoRCxNQUFNLENBQUMsV0FBVyxHQUNoQiw2RkFBNkYsQ0FBQztJQUVoRyxNQUFNLENBQUMsUUFBUSxHQUFHO1FBQ2hCLDZCQUE2QjtRQUM3Qix5Q0FBeUM7UUFDekMsNkNBQTZDO1FBQzdDLG9DQUFvQztLQUNyQyxDQUFDO0lBRUYsTUFBTSxDQUFDLEtBQUssR0FBRztRQUNiLEdBQUcsV0FBVyxDQUFDLFNBQVM7UUFDeEIsTUFBTSxFQUFFLEtBQUssQ0FBQyxNQUFNLENBQUM7WUFDbkIsV0FBVyxFQUFFLDRDQUE0QztTQUMxRCxDQUFDO1FBQ0YsSUFBSSxFQUFFLEtBQUssQ0FBQyxNQUFNLENBQUM7WUFDakIsV0FBVyxFQUFFLHdEQUF3RDtTQUN0RSxDQUFDO0tBQ0gsQ0FBQztJQUVGLEtBQUssQ0FBQyxHQUFHO1FBQ1AsTUFBTSxFQUFFLEtBQUssRUFBRSxHQUFHLE1BQU0sSUFBSSxDQUFDLEtBQUssQ0FBQyxTQUFTLENBQUMsQ0FBQztRQUU5QyxNQUFNLElBQUksR0FBRyxNQUFNLFNBQVMsQ0FBQyxHQUFHLENBQzlCLG1CQUFtQixDQUNwQixDQUFDO1FBQ0YsTUFBTSxHQUFHLEdBQWMsS0FBSyxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUM7WUFDeEMsQ0FBQyxDQUFDLElBQUk7WUFDTixDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsU0FBUyxJQUFJLEVBQUUsQ0FBQyxDQUFDO1FBRTNCLE1BQU0sUUFBUSxHQUFHLEdBQUcsQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLEVBQUUsRUFBRTtZQUNoQyxJQUFJLEtBQUssQ0FBQyxJQUFJLElBQUksQ0FBQyxDQUFDLElBQUksS0FBSyxLQUFLLENBQUMsSUFBSTtnQkFBRSxPQUFPLEtBQUssQ0FBQztZQUN0RCxJQUFJLEtBQUssQ0FBQyxNQUFNLEVBQUUsQ0FBQztnQkFDakIsTUFBTSxDQUFDLEdBQUcsS0FBSyxDQUFDLE1BQU0sQ0FBQyxXQUFXLEVBQUUsQ0FBQztnQkFDckMsT0FBTyxDQUNMLENBQUMsQ0FBQyxJQUFJLENBQUMsV0FBVyxFQUFFLENBQUMsUUFBUSxDQUFDLENBQUMsQ0FBQztvQkFDaEMsQ0FBQyxDQUFDLElBQUksQ0FBQyxXQUFXLEVBQUUsQ0FBQyxRQUFRLENBQUMsQ0FBQyxDQUFDO29CQUNoQyxDQUFDLENBQUMsU0FBUyxDQUFDLFFBQVEsQ0FBQyxDQUFDLENBQUMsQ0FDeEIsQ0FBQztZQUNKLENBQUM7WUFDRCxPQUFPLElBQUksQ0FBQztRQUNkLENBQUMsQ0FBQyxDQUFDO1FBRUgsSUFBSSxVQUFVLEVBQUUsRUFBRSxDQUFDO1lBQ2pCLElBQUksQ0FBQyxRQUFRLENBQUMsQ0FBQztZQUNmLE9BQU87UUFDVCxDQUFDO1FBRUQsTUFBTSxDQUFDLHdCQUF3QixRQUFRLENBQUMsTUFBTSxHQUFHLENBQUMsQ0FBQztRQUNuRCxJQUFJLFFBQVEsQ0FBQyxNQUFNLEtBQUssQ0FBQyxFQUFFLENBQUM7WUFDMUIsT0FBTyxDQUFDLEdBQUcsQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLGdCQUFnQixDQUFDLENBQUMsQ0FBQztZQUMxQyxPQUFPO1FBQ1QsQ0FBQztRQUVELGtFQUFrRTtRQUNsRSxNQUFNLEtBQUssR0FBRyxDQUFDLENBQUM7UUFDaEIsTUFBTSxLQUFLLEdBQUcsSUFBSSxDQUFDLEdBQUcsQ0FBQyxHQUFHLFFBQVEsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUMsQ0FBQyxTQUFTLENBQUMsTUFBTSxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUM7UUFDdEUsTUFBTSxLQUFLLEdBQUcsSUFBSSxDQUFDLEdBQUcsQ0FBQyxHQUFHLFFBQVEsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUM7UUFFbEUsT0FBTyxDQUFDLEdBQUcsQ0FDVCxJQUFJO1lBQ0YsTUFBTSxDQUFDLEdBQUcsQ0FBQyxNQUFNLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQyxDQUFDO1lBQ2hDLElBQUk7WUFDSixNQUFNLENBQUMsR0FBRyxDQUFDLE1BQU0sQ0FBQyxNQUFNLENBQUMsS0FBSyxDQUFDLENBQUM7WUFDaEMsSUFBSTtZQUNKLE1BQU0sQ0FBQyxHQUFHLENBQUMsTUFBTSxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsQ0FBQztZQUNoQyxJQUFJO1lBQ0osTUFBTSxDQUFDLEdBQUcsQ0FBQyxNQUFNLENBQUMsTUFBTSxDQUFDLEVBQUUsQ0FBQyxDQUFDO1lBQzdCLElBQUk7WUFDSixNQUFNLENBQUMsR0FBRyxDQUFDLGFBQWEsQ0FBQyxRQUFRLENBQUMsRUFBRSxDQUFDLENBQUM7WUFDdEMsSUFBSTtZQUNKLE1BQU0sQ0FBQyxHQUFHLENBQUMsYUFBYSxDQUFDLFFBQVEsQ0FBQyxFQUFFLENBQUMsQ0FBQztZQUN0QyxJQUFJO1lBQ0osTUFBTSxDQUFDLEdBQUcsQ0FBQyxjQUFjLENBQUMsQ0FDN0IsQ0FBQztRQUNGLEtBQUssTUFBTSxDQUFDLElBQUksUUFBUSxFQUFFLENBQUM7WUFDekIsTUFBTSxPQUFPLEdBQUcsQ0FBQyxDQUFDLHFCQUFxQjtnQkFDckMsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxPQUFPLENBQUMsVUFBVSxDQUFDO2dCQUM1QixDQUFDLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxjQUFjLENBQUMsQ0FBQztZQUMvQixPQUFPLENBQUMsR0FBRyxDQUNULElBQUk7Z0JBQ0YsQ0FBQyxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsS0FBSyxDQUFDO2dCQUNwQixJQUFJO2dCQUNKLENBQUMsQ0FBQyxTQUFTLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQztnQkFDekIsSUFBSTtnQkFDSixDQUFDLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUM7Z0JBQ3BCLElBQUk7Z0JBQ0osQ0FBQyxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsRUFBRSxDQUFDO2dCQUNqQixJQUFJO2dCQUNKLE1BQU0sQ0FBQyxDQUFDLENBQUMsZUFBZSxDQUFDLENBQUMsUUFBUSxDQUFDLEVBQUUsQ0FBQztnQkFDdEMsSUFBSTtnQkFDSixDQUFDLENBQUMsU0FBUyxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUMsQ0FBQyxRQUFRLENBQUMsRUFBRSxDQUFDO2dCQUNuQyxJQUFJO2dCQUNKLE9BQU8sQ0FDVixDQUFDO1FBQ0osQ0FBQztJQUNILENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBGbGFncyB9IGZyb20gXCJAb2NsaWYvY29yZVwiO1xuaW1wb3J0IHsgQmFzZUNvbW1hbmQgfSBmcm9tIFwiLi4vbGliL2Jhc2UtY29tbWFuZC5qc1wiO1xuaW1wb3J0IHsgYXBpQ2xpZW50IH0gZnJvbSBcIi4uL2xpYi9hcGktY2xpZW50LmpzXCI7XG5pbXBvcnQgeyBqc29uLCBpc0pzb25Nb2RlLCBjb2xvcnMsIGhlYWRlciB9IGZyb20gXCIuLi9saWIvb3V0cHV0LmpzXCI7XG5cbmludGVyZmFjZSBDb3VudHJ5IHtcbiAgY29kZTogc3RyaW5nO1xuICBuYW1lOiBzdHJpbmc7XG4gIGRpYWxfY29kZTogc3RyaW5nO1xuICB0aWVyOiBzdHJpbmc7XG4gIGNyZWRpdHNfcGVyX3NtczogbnVtYmVyO1xuICBwcmljZV91c2Q6IG51bWJlcjtcbiAgcmVxdWlyZXNfcmVnaXN0cmF0aW9uOiBib29sZWFuO1xufVxuXG4vKipcbiAqIGBzZW5kbHkgY291bnRyaWVzYCDigJQgbGlzdCBzdXBwb3J0ZWQgY291bnRyaWVzIHdpdGggcHJpY2luZy5cbiAqXG4gKiBCYWNrcyB0aGUgcHVibGljIGBHRVQgL2FwaS92MS9jb3VudHJpZXNgIGVuZHBvaW50LiBObyBhdXRoIHJlcXVpcmVkIHNvXG4gKiBjdXN0b21lcnMgY2FuIGJyb3dzZSBiZWZvcmUgc2lnbmluZyB1cC4gVXNlZnVsIGZvciBwb3B1bGF0aW5nIGNvdW50cnlcbiAqIHBpY2tlcnMgaW4gc2lnbi11cCBmb3JtcywgdmFsaWRhdGluZyBwaG9uZSBudW1iZXJzIGNsaWVudC1zaWRlLCBhbmRcbiAqIGNoZWNraW5nIHdoaWNoIHRpZXJzIHJlcXVpcmUgcmVnaXN0cmF0aW9uICh0b2xsLWZyZWUgYnJhbmRcbiAqIHZlcmlmaWNhdGlvbiBpbiBVUy9DQSwgZm9yIGluc3RhbmNlKS5cbiAqL1xuZXhwb3J0IGRlZmF1bHQgY2xhc3MgQ291bnRyaWVzIGV4dGVuZHMgQmFzZUNvbW1hbmQge1xuICBzdGF0aWMgZGVzY3JpcHRpb24gPVxuICAgIFwiTGlzdCBzdXBwb3J0ZWQgY291bnRyaWVzIHdpdGggcHJpY2luZyAoY3JlZGl0cyBwZXIgU01TLCBVU0QgcHJpY2UsIHRpZXIpLiBObyBhdXRoIHJlcXVpcmVkLlwiO1xuXG4gIHN0YXRpYyBleGFtcGxlcyA9IFtcbiAgICBcIjwlPSBjb25maWcuYmluICU+IGNvdW50cmllc1wiLFxuICAgIFwiPCU9IGNvbmZpZy5iaW4gJT4gY291bnRyaWVzIC0tc2VhcmNoIFVLXCIsXG4gICAgXCI8JT0gY29uZmlnLmJpbiAlPiBjb3VudHJpZXMgLS10aWVyIGRvbWVzdGljXCIsXG4gICAgXCI8JT0gY29uZmlnLmJpbiAlPiBjb3VudHJpZXMgLS1qc29uXCIsXG4gIF07XG5cbiAgc3RhdGljIGZsYWdzID0ge1xuICAgIC4uLkJhc2VDb21tYW5kLmJhc2VGbGFncyxcbiAgICBzZWFyY2g6IEZsYWdzLnN0cmluZyh7XG4gICAgICBkZXNjcmlwdGlvbjogXCJGaWx0ZXIgYnkgbmFtZSwgY291bnRyeSBjb2RlLCBvciBkaWFsIGNvZGVcIixcbiAgICB9KSxcbiAgICB0aWVyOiBGbGFncy5zdHJpbmcoe1xuICAgICAgZGVzY3JpcHRpb246IFwiRmlsdGVyIGJ5IHByaWNpbmcgdGllciAoZG9tZXN0aWMsIGludGVybmF0aW9uYWwsIGV0Yy4pXCIsXG4gICAgfSksXG4gIH07XG5cbiAgYXN5bmMgcnVuKCk6IFByb21pc2U8dm9pZD4ge1xuICAgIGNvbnN0IHsgZmxhZ3MgfSA9IGF3YWl0IHRoaXMucGFyc2UoQ291bnRyaWVzKTtcblxuICAgIGNvbnN0IHJlc3AgPSBhd2FpdCBhcGlDbGllbnQuZ2V0PHsgY291bnRyaWVzPzogQ291bnRyeVtdIH0gfCBDb3VudHJ5W10+KFxuICAgICAgXCIvYXBpL3YxL2NvdW50cmllc1wiLFxuICAgICk7XG4gICAgY29uc3QgYWxsOiBDb3VudHJ5W10gPSBBcnJheS5pc0FycmF5KHJlc3ApXG4gICAgICA/IHJlc3BcbiAgICAgIDogKHJlc3AuY291bnRyaWVzID8/IFtdKTtcblxuICAgIGNvbnN0IGZpbHRlcmVkID0gYWxsLmZpbHRlcigoYykgPT4ge1xuICAgICAgaWYgKGZsYWdzLnRpZXIgJiYgYy50aWVyICE9PSBmbGFncy50aWVyKSByZXR1cm4gZmFsc2U7XG4gICAgICBpZiAoZmxhZ3Muc2VhcmNoKSB7XG4gICAgICAgIGNvbnN0IHEgPSBmbGFncy5zZWFyY2gudG9Mb3dlckNhc2UoKTtcbiAgICAgICAgcmV0dXJuIChcbiAgICAgICAgICBjLmNvZGUudG9Mb3dlckNhc2UoKS5pbmNsdWRlcyhxKSB8fFxuICAgICAgICAgIGMubmFtZS50b0xvd2VyQ2FzZSgpLmluY2x1ZGVzKHEpIHx8XG4gICAgICAgICAgYy5kaWFsX2NvZGUuaW5jbHVkZXMocSlcbiAgICAgICAgKTtcbiAgICAgIH1cbiAgICAgIHJldHVybiB0cnVlO1xuICAgIH0pO1xuXG4gICAgaWYgKGlzSnNvbk1vZGUoKSkge1xuICAgICAganNvbihmaWx0ZXJlZCk7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgaGVhZGVyKGBTdXBwb3J0ZWQgY291bnRyaWVzICgke2ZpbHRlcmVkLmxlbmd0aH0pYCk7XG4gICAgaWYgKGZpbHRlcmVkLmxlbmd0aCA9PT0gMCkge1xuICAgICAgY29uc29sZS5sb2coY29sb3JzLmRpbShcIiAgKG5vIG1hdGNoZXMpXCIpKTtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICAvLyBDb2x1bW4gd2lkdGhzIGRlcml2ZWQgZnJvbSB0aGUgZGF0YSBzbyB0aGUgdGFibGUgc3RheXMgY29tcGFjdC5cbiAgICBjb25zdCBjb2RlVyA9IDQ7XG4gICAgY29uc3QgZGlhbFcgPSBNYXRoLm1heCguLi5maWx0ZXJlZC5tYXAoKGMpID0+IGMuZGlhbF9jb2RlLmxlbmd0aCksIDUpO1xuICAgIGNvbnN0IG5hbWVXID0gTWF0aC5tYXgoLi4uZmlsdGVyZWQubWFwKChjKSA9PiBjLm5hbWUubGVuZ3RoKSwgMTIpO1xuXG4gICAgY29uc29sZS5sb2coXG4gICAgICBcIiAgXCIgK1xuICAgICAgICBjb2xvcnMuZGltKFwiQ29kZVwiLnBhZEVuZChjb2RlVykpICtcbiAgICAgICAgXCIgIFwiICtcbiAgICAgICAgY29sb3JzLmRpbShcIkRpYWxcIi5wYWRFbmQoZGlhbFcpKSArXG4gICAgICAgIFwiICBcIiArXG4gICAgICAgIGNvbG9ycy5kaW0oXCJOYW1lXCIucGFkRW5kKG5hbWVXKSkgK1xuICAgICAgICBcIiAgXCIgK1xuICAgICAgICBjb2xvcnMuZGltKFwiVGllclwiLnBhZEVuZCgxNCkpICtcbiAgICAgICAgXCIgIFwiICtcbiAgICAgICAgY29sb3JzLmRpbShcIkNyZWRpdHMvU01TXCIucGFkU3RhcnQoMTEpKSArXG4gICAgICAgIFwiICBcIiArXG4gICAgICAgIGNvbG9ycy5kaW0oXCJQcmljZSAoVVNEKVwiLnBhZFN0YXJ0KDExKSkgK1xuICAgICAgICBcIiAgXCIgK1xuICAgICAgICBjb2xvcnMuZGltKFwiUmVnaXN0cmF0aW9uXCIpLFxuICAgICk7XG4gICAgZm9yIChjb25zdCBjIG9mIGZpbHRlcmVkKSB7XG4gICAgICBjb25zdCByZWdIaW50ID0gYy5yZXF1aXJlc19yZWdpc3RyYXRpb25cbiAgICAgICAgPyBjb2xvcnMud2FybmluZyhcInJlcXVpcmVkXCIpXG4gICAgICAgIDogY29sb3JzLmRpbShcIm5vdCByZXF1aXJlZFwiKTtcbiAgICAgIGNvbnNvbGUubG9nKFxuICAgICAgICBcIiAgXCIgK1xuICAgICAgICAgIGMuY29kZS5wYWRFbmQoY29kZVcpICtcbiAgICAgICAgICBcIiAgXCIgK1xuICAgICAgICAgIGMuZGlhbF9jb2RlLnBhZEVuZChkaWFsVykgK1xuICAgICAgICAgIFwiICBcIiArXG4gICAgICAgICAgYy5uYW1lLnBhZEVuZChuYW1lVykgK1xuICAgICAgICAgIFwiICBcIiArXG4gICAgICAgICAgYy50aWVyLnBhZEVuZCgxNCkgK1xuICAgICAgICAgIFwiICBcIiArXG4gICAgICAgICAgU3RyaW5nKGMuY3JlZGl0c19wZXJfc21zKS5wYWRTdGFydCgxMSkgK1xuICAgICAgICAgIFwiICBcIiArXG4gICAgICAgICAgYy5wcmljZV91c2QudG9GaXhlZCg0KS5wYWRTdGFydCgxMSkgK1xuICAgICAgICAgIFwiICBcIiArXG4gICAgICAgICAgcmVnSGludCxcbiAgICAgICk7XG4gICAgfVxuICB9XG59XG4iXX0=
@@ -0,0 +1,22 @@
1
+ import { AuthenticatedCommand } from "../../lib/base-command.js";
2
+ /**
3
+ * `sendly sms upload-media <file>` — upload an image for MMS.
4
+ *
5
+ * Wraps `POST /api/v1/media`. Returns a public URL the caller passes as
6
+ * `--media` to `sms send` / `sms batch`. Flag-gated behind `enable_mms`
7
+ * on the server — if your account doesn't have MMS, the server returns
8
+ * 403 and this command surfaces it.
9
+ */
10
+ export default class SmsUploadMedia extends AuthenticatedCommand {
11
+ static description: string;
12
+ static examples: string[];
13
+ static args: {
14
+ file: import("@oclif/core/lib/interfaces/parser.js").Arg<string, Record<string, unknown>>;
15
+ };
16
+ static flags: {
17
+ "content-type": import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
18
+ json: import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
19
+ quiet: import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
20
+ };
21
+ run(): Promise<void>;
22
+ }
@@ -0,0 +1,82 @@
1
+ import { Args, Flags } from "@oclif/core";
2
+ import * as fs from "node:fs";
3
+ import * as path from "node:path";
4
+ import { AuthenticatedCommand } from "../../lib/base-command.js";
5
+ import { apiClient } from "../../lib/api-client.js";
6
+ import { success, json, isJsonMode, colors, error, } from "../../lib/output.js";
7
+ const ALLOWED_EXTS = [".jpg", ".jpeg", ".png", ".gif"];
8
+ /**
9
+ * `sendly sms upload-media <file>` — upload an image for MMS.
10
+ *
11
+ * Wraps `POST /api/v1/media`. Returns a public URL the caller passes as
12
+ * `--media` to `sms send` / `sms batch`. Flag-gated behind `enable_mms`
13
+ * on the server — if your account doesn't have MMS, the server returns
14
+ * 403 and this command surfaces it.
15
+ */
16
+ export default class SmsUploadMedia extends AuthenticatedCommand {
17
+ static description = "Upload a JPEG/PNG/GIF image for MMS and get back a public URL to pass to `sms send --media`. Requires MMS to be enabled for your account.";
18
+ static examples = [
19
+ "<%= config.bin %> sms upload-media ./promo.jpg",
20
+ "<%= config.bin %> sms upload-media ./animated.gif --json",
21
+ ];
22
+ static args = {
23
+ file: Args.string({
24
+ description: "Path to the image file (JPEG, PNG, or GIF)",
25
+ required: true,
26
+ }),
27
+ };
28
+ static flags = {
29
+ ...AuthenticatedCommand.baseFlags,
30
+ "content-type": Flags.string({
31
+ description: "Override the detected mime type",
32
+ }),
33
+ };
34
+ async run() {
35
+ const { args, flags } = await this.parse(SmsUploadMedia);
36
+ const filePath = path.resolve(args.file);
37
+ if (!fs.existsSync(filePath)) {
38
+ error(`File not found: ${filePath}`);
39
+ this.exit(1);
40
+ }
41
+ const ext = path.extname(filePath).toLowerCase();
42
+ if (!ALLOWED_EXTS.includes(ext)) {
43
+ error(`Unsupported file extension '${ext}'. MMS supports JPEG, PNG, and GIF.`);
44
+ this.exit(1);
45
+ }
46
+ const buffer = fs.readFileSync(filePath);
47
+ const filename = path.basename(filePath);
48
+ const detected = flags["content-type"] ??
49
+ (ext === ".png"
50
+ ? "image/png"
51
+ : ext === ".gif"
52
+ ? "image/gif"
53
+ : "image/jpeg");
54
+ const resp = await apiClient.uploadFile("/api/v1/media", {
55
+ buffer,
56
+ filename,
57
+ mimetype: detected,
58
+ });
59
+ if (isJsonMode()) {
60
+ json(resp);
61
+ return;
62
+ }
63
+ success("Media uploaded", {
64
+ ID: resp.id,
65
+ URL: resp.url,
66
+ "Content-Type": resp.contentType,
67
+ Size: formatBytes(resp.sizeBytes),
68
+ });
69
+ console.log();
70
+ console.log(colors.dim(" Send an MMS with this image:"));
71
+ console.log(" " +
72
+ colors.code(`sendly sms send --to +15551234567 --text "Hi" --media "${resp.url}"`));
73
+ }
74
+ }
75
+ function formatBytes(n) {
76
+ if (n < 1024)
77
+ return `${n} B`;
78
+ if (n < 1024 * 1024)
79
+ return `${(n / 1024).toFixed(1)} KB`;
80
+ return `${(n / (1024 * 1024)).toFixed(1)} MB`;
81
+ }
82
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidXBsb2FkLW1lZGlhLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL2NvbW1hbmRzL3Ntcy91cGxvYWQtbWVkaWEudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLElBQUksRUFBRSxLQUFLLEVBQUUsTUFBTSxhQUFhLENBQUM7QUFDMUMsT0FBTyxLQUFLLEVBQUUsTUFBTSxTQUFTLENBQUM7QUFDOUIsT0FBTyxLQUFLLElBQUksTUFBTSxXQUFXLENBQUM7QUFDbEMsT0FBTyxFQUFFLG9CQUFvQixFQUFFLE1BQU0sMkJBQTJCLENBQUM7QUFDakUsT0FBTyxFQUFFLFNBQVMsRUFBRSxNQUFNLHlCQUF5QixDQUFDO0FBQ3BELE9BQU8sRUFDTCxPQUFPLEVBQ1AsSUFBSSxFQUNKLFVBQVUsRUFDVixNQUFNLEVBQ04sS0FBSyxHQUVOLE1BQU0scUJBQXFCLENBQUM7QUFTN0IsTUFBTSxZQUFZLEdBQUcsQ0FBQyxNQUFNLEVBQUUsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLENBQUMsQ0FBQztBQUV2RDs7Ozs7OztHQU9HO0FBQ0gsTUFBTSxDQUFDLE9BQU8sT0FBTyxjQUFlLFNBQVEsb0JBQW9CO0lBQzlELE1BQU0sQ0FBQyxXQUFXLEdBQ2hCLDJJQUEySSxDQUFDO0lBRTlJLE1BQU0sQ0FBQyxRQUFRLEdBQUc7UUFDaEIsZ0RBQWdEO1FBQ2hELDBEQUEwRDtLQUMzRCxDQUFDO0lBRUYsTUFBTSxDQUFDLElBQUksR0FBRztRQUNaLElBQUksRUFBRSxJQUFJLENBQUMsTUFBTSxDQUFDO1lBQ2hCLFdBQVcsRUFBRSw0Q0FBNEM7WUFDekQsUUFBUSxFQUFFLElBQUk7U0FDZixDQUFDO0tBQ0gsQ0FBQztJQUVGLE1BQU0sQ0FBQyxLQUFLLEdBQUc7UUFDYixHQUFHLG9CQUFvQixDQUFDLFNBQVM7UUFDakMsY0FBYyxFQUFFLEtBQUssQ0FBQyxNQUFNLENBQUM7WUFDM0IsV0FBVyxFQUFFLGlDQUFpQztTQUMvQyxDQUFDO0tBQ0gsQ0FBQztJQUVGLEtBQUssQ0FBQyxHQUFHO1FBQ1AsTUFBTSxFQUFFLElBQUksRUFBRSxLQUFLLEVBQUUsR0FBRyxNQUFNLElBQUksQ0FBQyxLQUFLLENBQUMsY0FBYyxDQUFDLENBQUM7UUFFekQsTUFBTSxRQUFRLEdBQUcsSUFBSSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUM7UUFDekMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxVQUFVLENBQUMsUUFBUSxDQUFDLEVBQUUsQ0FBQztZQUM3QixLQUFLLENBQUMsbUJBQW1CLFFBQVEsRUFBRSxDQUFDLENBQUM7WUFDckMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUNmLENBQUM7UUFFRCxNQUFNLEdBQUcsR0FBRyxJQUFJLENBQUMsT0FBTyxDQUFDLFFBQVEsQ0FBQyxDQUFDLFdBQVcsRUFBRSxDQUFDO1FBQ2pELElBQUksQ0FBQyxZQUFZLENBQUMsUUFBUSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUM7WUFDaEMsS0FBSyxDQUNILCtCQUErQixHQUFHLHFDQUFxQyxDQUN4RSxDQUFDO1lBQ0YsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUNmLENBQUM7UUFFRCxNQUFNLE1BQU0sR0FBRyxFQUFFLENBQUMsWUFBWSxDQUFDLFFBQVEsQ0FBQyxDQUFDO1FBQ3pDLE1BQU0sUUFBUSxHQUFHLElBQUksQ0FBQyxRQUFRLENBQUMsUUFBUSxDQUFDLENBQUM7UUFFekMsTUFBTSxRQUFRLEdBQ1osS0FBSyxDQUFDLGNBQWMsQ0FBQztZQUNyQixDQUFDLEdBQUcsS0FBSyxNQUFNO2dCQUNiLENBQUMsQ0FBQyxXQUFXO2dCQUNiLENBQUMsQ0FBQyxHQUFHLEtBQUssTUFBTTtvQkFDZCxDQUFDLENBQUMsV0FBVztvQkFDYixDQUFDLENBQUMsWUFBWSxDQUFDLENBQUM7UUFFdEIsTUFBTSxJQUFJLEdBQUcsTUFBTSxTQUFTLENBQUMsVUFBVSxDQUNyQyxlQUFlLEVBQ2Y7WUFDRSxNQUFNO1lBQ04sUUFBUTtZQUNSLFFBQVEsRUFBRSxRQUFRO1NBQ25CLENBQ0YsQ0FBQztRQUVGLElBQUksVUFBVSxFQUFFLEVBQUUsQ0FBQztZQUNqQixJQUFJLENBQUMsSUFBSSxDQUFDLENBQUM7WUFDWCxPQUFPO1FBQ1QsQ0FBQztRQUVELE9BQU8sQ0FBQyxnQkFBZ0IsRUFBRTtZQUN4QixFQUFFLEVBQUUsSUFBSSxDQUFDLEVBQUU7WUFDWCxHQUFHLEVBQUUsSUFBSSxDQUFDLEdBQUc7WUFDYixjQUFjLEVBQUUsSUFBSSxDQUFDLFdBQVc7WUFDaEMsSUFBSSxFQUFFLFdBQVcsQ0FBQyxJQUFJLENBQUMsU0FBUyxDQUFDO1NBQ2xDLENBQUMsQ0FBQztRQUNILE9BQU8sQ0FBQyxHQUFHLEVBQUUsQ0FBQztRQUNkLE9BQU8sQ0FBQyxHQUFHLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxnQ0FBZ0MsQ0FBQyxDQUFDLENBQUM7UUFDMUQsT0FBTyxDQUFDLEdBQUcsQ0FDVCxNQUFNO1lBQ0osTUFBTSxDQUFDLElBQUksQ0FDVCwwREFBMEQsSUFBSSxDQUFDLEdBQUcsR0FBRyxDQUN0RSxDQUNKLENBQUM7SUFDSixDQUFDOztBQUdILFNBQVMsV0FBVyxDQUFDLENBQVM7SUFDNUIsSUFBSSxDQUFDLEdBQUcsSUFBSTtRQUFFLE9BQU8sR0FBRyxDQUFDLElBQUksQ0FBQztJQUM5QixJQUFJLENBQUMsR0FBRyxJQUFJLEdBQUcsSUFBSTtRQUFFLE9BQU8sR0FBRyxDQUFDLENBQUMsR0FBRyxJQUFJLENBQUMsQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDLEtBQUssQ0FBQztJQUMxRCxPQUFPLEdBQUcsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxJQUFJLEdBQUcsSUFBSSxDQUFDLENBQUMsQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDLEtBQUssQ0FBQztBQUNoRCxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgQXJncywgRmxhZ3MgfSBmcm9tIFwiQG9jbGlmL2NvcmVcIjtcbmltcG9ydCAqIGFzIGZzIGZyb20gXCJub2RlOmZzXCI7XG5pbXBvcnQgKiBhcyBwYXRoIGZyb20gXCJub2RlOnBhdGhcIjtcbmltcG9ydCB7IEF1dGhlbnRpY2F0ZWRDb21tYW5kIH0gZnJvbSBcIi4uLy4uL2xpYi9iYXNlLWNvbW1hbmQuanNcIjtcbmltcG9ydCB7IGFwaUNsaWVudCB9IGZyb20gXCIuLi8uLi9saWIvYXBpLWNsaWVudC5qc1wiO1xuaW1wb3J0IHtcbiAgc3VjY2VzcyxcbiAganNvbixcbiAgaXNKc29uTW9kZSxcbiAgY29sb3JzLFxuICBlcnJvcixcbiAga2V5VmFsdWUsXG59IGZyb20gXCIuLi8uLi9saWIvb3V0cHV0LmpzXCI7XG5cbmludGVyZmFjZSBNZWRpYVVwbG9hZFJlc3BvbnNlIHtcbiAgaWQ6IHN0cmluZztcbiAgdXJsOiBzdHJpbmc7XG4gIGNvbnRlbnRUeXBlOiBzdHJpbmc7XG4gIHNpemVCeXRlczogbnVtYmVyO1xufVxuXG5jb25zdCBBTExPV0VEX0VYVFMgPSBbXCIuanBnXCIsIFwiLmpwZWdcIiwgXCIucG5nXCIsIFwiLmdpZlwiXTtcblxuLyoqXG4gKiBgc2VuZGx5IHNtcyB1cGxvYWQtbWVkaWEgPGZpbGU+YCDigJQgdXBsb2FkIGFuIGltYWdlIGZvciBNTVMuXG4gKlxuICogV3JhcHMgYFBPU1QgL2FwaS92MS9tZWRpYWAuIFJldHVybnMgYSBwdWJsaWMgVVJMIHRoZSBjYWxsZXIgcGFzc2VzIGFzXG4gKiBgLS1tZWRpYWAgdG8gYHNtcyBzZW5kYCAvIGBzbXMgYmF0Y2hgLiBGbGFnLWdhdGVkIGJlaGluZCBgZW5hYmxlX21tc2BcbiAqIG9uIHRoZSBzZXJ2ZXIg4oCUIGlmIHlvdXIgYWNjb3VudCBkb2Vzbid0IGhhdmUgTU1TLCB0aGUgc2VydmVyIHJldHVybnNcbiAqIDQwMyBhbmQgdGhpcyBjb21tYW5kIHN1cmZhY2VzIGl0LlxuICovXG5leHBvcnQgZGVmYXVsdCBjbGFzcyBTbXNVcGxvYWRNZWRpYSBleHRlbmRzIEF1dGhlbnRpY2F0ZWRDb21tYW5kIHtcbiAgc3RhdGljIGRlc2NyaXB0aW9uID1cbiAgICBcIlVwbG9hZCBhIEpQRUcvUE5HL0dJRiBpbWFnZSBmb3IgTU1TIGFuZCBnZXQgYmFjayBhIHB1YmxpYyBVUkwgdG8gcGFzcyB0byBgc21zIHNlbmQgLS1tZWRpYWAuIFJlcXVpcmVzIE1NUyB0byBiZSBlbmFibGVkIGZvciB5b3VyIGFjY291bnQuXCI7XG5cbiAgc3RhdGljIGV4YW1wbGVzID0gW1xuICAgIFwiPCU9IGNvbmZpZy5iaW4gJT4gc21zIHVwbG9hZC1tZWRpYSAuL3Byb21vLmpwZ1wiLFxuICAgIFwiPCU9IGNvbmZpZy5iaW4gJT4gc21zIHVwbG9hZC1tZWRpYSAuL2FuaW1hdGVkLmdpZiAtLWpzb25cIixcbiAgXTtcblxuICBzdGF0aWMgYXJncyA9IHtcbiAgICBmaWxlOiBBcmdzLnN0cmluZyh7XG4gICAgICBkZXNjcmlwdGlvbjogXCJQYXRoIHRvIHRoZSBpbWFnZSBmaWxlIChKUEVHLCBQTkcsIG9yIEdJRilcIixcbiAgICAgIHJlcXVpcmVkOiB0cnVlLFxuICAgIH0pLFxuICB9O1xuXG4gIHN0YXRpYyBmbGFncyA9IHtcbiAgICAuLi5BdXRoZW50aWNhdGVkQ29tbWFuZC5iYXNlRmxhZ3MsXG4gICAgXCJjb250ZW50LXR5cGVcIjogRmxhZ3Muc3RyaW5nKHtcbiAgICAgIGRlc2NyaXB0aW9uOiBcIk92ZXJyaWRlIHRoZSBkZXRlY3RlZCBtaW1lIHR5cGVcIixcbiAgICB9KSxcbiAgfTtcblxuICBhc3luYyBydW4oKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgY29uc3QgeyBhcmdzLCBmbGFncyB9ID0gYXdhaXQgdGhpcy5wYXJzZShTbXNVcGxvYWRNZWRpYSk7XG5cbiAgICBjb25zdCBmaWxlUGF0aCA9IHBhdGgucmVzb2x2ZShhcmdzLmZpbGUpO1xuICAgIGlmICghZnMuZXhpc3RzU3luYyhmaWxlUGF0aCkpIHtcbiAgICAgIGVycm9yKGBGaWxlIG5vdCBmb3VuZDogJHtmaWxlUGF0aH1gKTtcbiAgICAgIHRoaXMuZXhpdCgxKTtcbiAgICB9XG5cbiAgICBjb25zdCBleHQgPSBwYXRoLmV4dG5hbWUoZmlsZVBhdGgpLnRvTG93ZXJDYXNlKCk7XG4gICAgaWYgKCFBTExPV0VEX0VYVFMuaW5jbHVkZXMoZXh0KSkge1xuICAgICAgZXJyb3IoXG4gICAgICAgIGBVbnN1cHBvcnRlZCBmaWxlIGV4dGVuc2lvbiAnJHtleHR9Jy4gTU1TIHN1cHBvcnRzIEpQRUcsIFBORywgYW5kIEdJRi5gLFxuICAgICAgKTtcbiAgICAgIHRoaXMuZXhpdCgxKTtcbiAgICB9XG5cbiAgICBjb25zdCBidWZmZXIgPSBmcy5yZWFkRmlsZVN5bmMoZmlsZVBhdGgpO1xuICAgIGNvbnN0IGZpbGVuYW1lID0gcGF0aC5iYXNlbmFtZShmaWxlUGF0aCk7XG5cbiAgICBjb25zdCBkZXRlY3RlZCA9XG4gICAgICBmbGFnc1tcImNvbnRlbnQtdHlwZVwiXSA/P1xuICAgICAgKGV4dCA9PT0gXCIucG5nXCJcbiAgICAgICAgPyBcImltYWdlL3BuZ1wiXG4gICAgICAgIDogZXh0ID09PSBcIi5naWZcIlxuICAgICAgICAgID8gXCJpbWFnZS9naWZcIlxuICAgICAgICAgIDogXCJpbWFnZS9qcGVnXCIpO1xuXG4gICAgY29uc3QgcmVzcCA9IGF3YWl0IGFwaUNsaWVudC51cGxvYWRGaWxlPE1lZGlhVXBsb2FkUmVzcG9uc2U+KFxuICAgICAgXCIvYXBpL3YxL21lZGlhXCIsXG4gICAgICB7XG4gICAgICAgIGJ1ZmZlcixcbiAgICAgICAgZmlsZW5hbWUsXG4gICAgICAgIG1pbWV0eXBlOiBkZXRlY3RlZCxcbiAgICAgIH0sXG4gICAgKTtcblxuICAgIGlmIChpc0pzb25Nb2RlKCkpIHtcbiAgICAgIGpzb24ocmVzcCk7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgc3VjY2VzcyhcIk1lZGlhIHVwbG9hZGVkXCIsIHtcbiAgICAgIElEOiByZXNwLmlkLFxuICAgICAgVVJMOiByZXNwLnVybCxcbiAgICAgIFwiQ29udGVudC1UeXBlXCI6IHJlc3AuY29udGVudFR5cGUsXG4gICAgICBTaXplOiBmb3JtYXRCeXRlcyhyZXNwLnNpemVCeXRlcyksXG4gICAgfSk7XG4gICAgY29uc29sZS5sb2coKTtcbiAgICBjb25zb2xlLmxvZyhjb2xvcnMuZGltKFwiICBTZW5kIGFuIE1NUyB3aXRoIHRoaXMgaW1hZ2U6XCIpKTtcbiAgICBjb25zb2xlLmxvZyhcbiAgICAgIFwiICAgIFwiICtcbiAgICAgICAgY29sb3JzLmNvZGUoXG4gICAgICAgICAgYHNlbmRseSBzbXMgc2VuZCAtLXRvICsxNTU1MTIzNDU2NyAtLXRleHQgXCJIaVwiIC0tbWVkaWEgXCIke3Jlc3AudXJsfVwiYCxcbiAgICAgICAgKSxcbiAgICApO1xuICB9XG59XG5cbmZ1bmN0aW9uIGZvcm1hdEJ5dGVzKG46IG51bWJlcik6IHN0cmluZyB7XG4gIGlmIChuIDwgMTAyNCkgcmV0dXJuIGAke259IEJgO1xuICBpZiAobiA8IDEwMjQgKiAxMDI0KSByZXR1cm4gYCR7KG4gLyAxMDI0KS50b0ZpeGVkKDEpfSBLQmA7XG4gIHJldHVybiBgJHsobiAvICgxMDI0ICogMTAyNCkpLnRvRml4ZWQoMSl9IE1CYDtcbn1cbiJdfQ==
@@ -0,0 +1,25 @@
1
+ import { AuthenticatedCommand } from "../../lib/base-command.js";
2
+ /**
3
+ * `sendly templates enhance` — AI rewrite a message template for
4
+ * compliance/clarity/conversions.
5
+ *
6
+ * Wraps `POST /api/v1/ai/enhance` (flag-gated behind `ai_classification`
7
+ * on the server — if your account doesn't have the flag, the server
8
+ * returns 404 and this command surfaces it). Accepts text directly via
9
+ * `--text`, from a file via `--file`, or piped on stdin.
10
+ */
11
+ export default class TemplatesEnhance extends AuthenticatedCommand {
12
+ static description: string;
13
+ static examples: string[];
14
+ static args: {
15
+ text: import("@oclif/core/lib/interfaces/parser.js").Arg<string | undefined, Record<string, unknown>>;
16
+ };
17
+ static flags: {
18
+ text: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
19
+ file: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
20
+ "message-type": import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
21
+ json: import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
22
+ quiet: import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
23
+ };
24
+ run(): Promise<void>;
25
+ }
@@ -0,0 +1,77 @@
1
+ import { Args, Flags } from "@oclif/core";
2
+ import * as fs from "node:fs";
3
+ import { AuthenticatedCommand } from "../../lib/base-command.js";
4
+ import { apiClient } from "../../lib/api-client.js";
5
+ import { success, json, isJsonMode, colors, header, info, error, } from "../../lib/output.js";
6
+ /**
7
+ * `sendly templates enhance` — AI rewrite a message template for
8
+ * compliance/clarity/conversions.
9
+ *
10
+ * Wraps `POST /api/v1/ai/enhance` (flag-gated behind `ai_classification`
11
+ * on the server — if your account doesn't have the flag, the server
12
+ * returns 404 and this command surfaces it). Accepts text directly via
13
+ * `--text`, from a file via `--file`, or piped on stdin.
14
+ */
15
+ export default class TemplatesEnhance extends AuthenticatedCommand {
16
+ static description = "AI-enhance a message template for clarity, compliance, and send-readiness. Returns the rewritten text + an explanation.";
17
+ static examples = [
18
+ '<%= config.bin %> templates enhance --text "hey come check out our sale"',
19
+ '<%= config.bin %> templates enhance --file ./draft.txt --message-type marketing',
20
+ '<%= config.bin %> echo "your sale msg" | sendly templates enhance --message-type promotional',
21
+ ];
22
+ static args = {
23
+ text: Args.string({
24
+ description: "Message text to enhance (alternative to --text / --file)",
25
+ required: false,
26
+ }),
27
+ };
28
+ static flags = {
29
+ ...AuthenticatedCommand.baseFlags,
30
+ text: Flags.string({
31
+ description: "Message text to enhance",
32
+ }),
33
+ file: Flags.string({
34
+ description: "Read message text from a file path",
35
+ }),
36
+ "message-type": Flags.string({
37
+ description: "Hint the kind of message (e.g. marketing, transactional, notification) for targeted enhancements",
38
+ }),
39
+ };
40
+ async run() {
41
+ const { args, flags } = await this.parse(TemplatesEnhance);
42
+ let text = flags.text ?? args.text;
43
+ if (!text && flags.file) {
44
+ text = fs.readFileSync(flags.file, "utf8").trim();
45
+ }
46
+ if (!text && !process.stdin.isTTY) {
47
+ text = await new Promise((resolve) => {
48
+ let buf = "";
49
+ process.stdin.setEncoding("utf8");
50
+ process.stdin.on("data", (c) => (buf += c));
51
+ process.stdin.on("end", () => resolve(buf.trim()));
52
+ });
53
+ }
54
+ if (!text && !flags["message-type"]) {
55
+ error("Provide text via --text, --file, positional arg, or stdin (or at least --message-type for a template suggestion).");
56
+ this.exit(1);
57
+ }
58
+ const resp = await apiClient.post("/api/v1/ai/enhance", {
59
+ text: text ?? "",
60
+ messageType: flags["message-type"],
61
+ });
62
+ if (isJsonMode()) {
63
+ json(resp);
64
+ return;
65
+ }
66
+ header("Enhanced text");
67
+ console.log(" " + (resp.enhanced || colors.dim("(no change)")));
68
+ if (resp.explanation) {
69
+ console.log();
70
+ info(resp.explanation);
71
+ }
72
+ if (!resp.enhanced || resp.enhanced === text) {
73
+ success("No enhancement suggested — original text looks good.");
74
+ }
75
+ }
76
+ }
77
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZW5oYW5jZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9jb21tYW5kcy90ZW1wbGF0ZXMvZW5oYW5jZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsSUFBSSxFQUFFLEtBQUssRUFBRSxNQUFNLGFBQWEsQ0FBQztBQUMxQyxPQUFPLEtBQUssRUFBRSxNQUFNLFNBQVMsQ0FBQztBQUM5QixPQUFPLEVBQUUsb0JBQW9CLEVBQUUsTUFBTSwyQkFBMkIsQ0FBQztBQUNqRSxPQUFPLEVBQUUsU0FBUyxFQUFFLE1BQU0seUJBQXlCLENBQUM7QUFDcEQsT0FBTyxFQUNMLE9BQU8sRUFDUCxJQUFJLEVBQ0osVUFBVSxFQUNWLE1BQU0sRUFDTixNQUFNLEVBQ04sSUFBSSxFQUNKLEtBQUssR0FDTixNQUFNLHFCQUFxQixDQUFDO0FBTzdCOzs7Ozs7OztHQVFHO0FBQ0gsTUFBTSxDQUFDLE9BQU8sT0FBTyxnQkFBaUIsU0FBUSxvQkFBb0I7SUFDaEUsTUFBTSxDQUFDLFdBQVcsR0FDaEIseUhBQXlILENBQUM7SUFFNUgsTUFBTSxDQUFDLFFBQVEsR0FBRztRQUNoQiwwRUFBMEU7UUFDMUUsaUZBQWlGO1FBQ2pGLDhGQUE4RjtLQUMvRixDQUFDO0lBRUYsTUFBTSxDQUFDLElBQUksR0FBRztRQUNaLElBQUksRUFBRSxJQUFJLENBQUMsTUFBTSxDQUFDO1lBQ2hCLFdBQVcsRUFBRSwwREFBMEQ7WUFDdkUsUUFBUSxFQUFFLEtBQUs7U0FDaEIsQ0FBQztLQUNILENBQUM7SUFFRixNQUFNLENBQUMsS0FBSyxHQUFHO1FBQ2IsR0FBRyxvQkFBb0IsQ0FBQyxTQUFTO1FBQ2pDLElBQUksRUFBRSxLQUFLLENBQUMsTUFBTSxDQUFDO1lBQ2pCLFdBQVcsRUFBRSx5QkFBeUI7U0FDdkMsQ0FBQztRQUNGLElBQUksRUFBRSxLQUFLLENBQUMsTUFBTSxDQUFDO1lBQ2pCLFdBQVcsRUFBRSxvQ0FBb0M7U0FDbEQsQ0FBQztRQUNGLGNBQWMsRUFBRSxLQUFLLENBQUMsTUFBTSxDQUFDO1lBQzNCLFdBQVcsRUFDVCxrR0FBa0c7U0FDckcsQ0FBQztLQUNILENBQUM7SUFFRixLQUFLLENBQUMsR0FBRztRQUNQLE1BQU0sRUFBRSxJQUFJLEVBQUUsS0FBSyxFQUFFLEdBQUcsTUFBTSxJQUFJLENBQUMsS0FBSyxDQUFDLGdCQUFnQixDQUFDLENBQUM7UUFFM0QsSUFBSSxJQUFJLEdBQXVCLEtBQUssQ0FBQyxJQUFJLElBQUksSUFBSSxDQUFDLElBQUksQ0FBQztRQUN2RCxJQUFJLENBQUMsSUFBSSxJQUFJLEtBQUssQ0FBQyxJQUFJLEVBQUUsQ0FBQztZQUN4QixJQUFJLEdBQUcsRUFBRSxDQUFDLFlBQVksQ0FBQyxLQUFLLENBQUMsSUFBSSxFQUFFLE1BQU0sQ0FBQyxDQUFDLElBQUksRUFBRSxDQUFDO1FBQ3BELENBQUM7UUFDRCxJQUFJLENBQUMsSUFBSSxJQUFJLENBQUMsT0FBTyxDQUFDLEtBQUssQ0FBQyxLQUFLLEVBQUUsQ0FBQztZQUNsQyxJQUFJLEdBQUcsTUFBTSxJQUFJLE9BQU8sQ0FBUyxDQUFDLE9BQU8sRUFBRSxFQUFFO2dCQUMzQyxJQUFJLEdBQUcsR0FBRyxFQUFFLENBQUM7Z0JBQ2IsT0FBTyxDQUFDLEtBQUssQ0FBQyxXQUFXLENBQUMsTUFBTSxDQUFDLENBQUM7Z0JBQ2xDLE9BQU8sQ0FBQyxLQUFLLENBQUMsRUFBRSxDQUFDLE1BQU0sRUFBRSxDQUFDLENBQUMsRUFBRSxFQUFFLENBQUMsQ0FBQyxHQUFHLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQztnQkFDNUMsT0FBTyxDQUFDLEtBQUssQ0FBQyxFQUFFLENBQUMsS0FBSyxFQUFFLEdBQUcsRUFBRSxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsSUFBSSxFQUFFLENBQUMsQ0FBQyxDQUFDO1lBQ3JELENBQUMsQ0FBQyxDQUFDO1FBQ0wsQ0FBQztRQUVELElBQUksQ0FBQyxJQUFJLElBQUksQ0FBQyxLQUFLLENBQUMsY0FBYyxDQUFDLEVBQUUsQ0FBQztZQUNwQyxLQUFLLENBQ0gsbUhBQW1ILENBQ3BILENBQUM7WUFDRixJQUFJLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDO1FBQ2YsQ0FBQztRQUVELE1BQU0sSUFBSSxHQUFHLE1BQU0sU0FBUyxDQUFDLElBQUksQ0FDL0Isb0JBQW9CLEVBQ3BCO1lBQ0UsSUFBSSxFQUFFLElBQUksSUFBSSxFQUFFO1lBQ2hCLFdBQVcsRUFBRSxLQUFLLENBQUMsY0FBYyxDQUFDO1NBQ25DLENBQ0YsQ0FBQztRQUVGLElBQUksVUFBVSxFQUFFLEVBQUUsQ0FBQztZQUNqQixJQUFJLENBQUMsSUFBSSxDQUFDLENBQUM7WUFDWCxPQUFPO1FBQ1QsQ0FBQztRQUVELE1BQU0sQ0FBQyxlQUFlLENBQUMsQ0FBQztRQUN4QixPQUFPLENBQUMsR0FBRyxDQUFDLElBQUksR0FBRyxDQUFDLElBQUksQ0FBQyxRQUFRLElBQUksTUFBTSxDQUFDLEdBQUcsQ0FBQyxhQUFhLENBQUMsQ0FBQyxDQUFDLENBQUM7UUFDakUsSUFBSSxJQUFJLENBQUMsV0FBVyxFQUFFLENBQUM7WUFDckIsT0FBTyxDQUFDLEdBQUcsRUFBRSxDQUFDO1lBQ2QsSUFBSSxDQUFDLElBQUksQ0FBQyxXQUFXLENBQUMsQ0FBQztRQUN6QixDQUFDO1FBQ0QsSUFBSSxDQUFDLElBQUksQ0FBQyxRQUFRLElBQUksSUFBSSxDQUFDLFFBQVEsS0FBSyxJQUFJLEVBQUUsQ0FBQztZQUM3QyxPQUFPLENBQUMsc0RBQXNELENBQUMsQ0FBQztRQUNsRSxDQUFDO0lBQ0gsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IEFyZ3MsIEZsYWdzIH0gZnJvbSBcIkBvY2xpZi9jb3JlXCI7XG5pbXBvcnQgKiBhcyBmcyBmcm9tIFwibm9kZTpmc1wiO1xuaW1wb3J0IHsgQXV0aGVudGljYXRlZENvbW1hbmQgfSBmcm9tIFwiLi4vLi4vbGliL2Jhc2UtY29tbWFuZC5qc1wiO1xuaW1wb3J0IHsgYXBpQ2xpZW50IH0gZnJvbSBcIi4uLy4uL2xpYi9hcGktY2xpZW50LmpzXCI7XG5pbXBvcnQge1xuICBzdWNjZXNzLFxuICBqc29uLFxuICBpc0pzb25Nb2RlLFxuICBjb2xvcnMsXG4gIGhlYWRlcixcbiAgaW5mbyxcbiAgZXJyb3IsXG59IGZyb20gXCIuLi8uLi9saWIvb3V0cHV0LmpzXCI7XG5cbmludGVyZmFjZSBFbmhhbmNlUmVzcG9uc2Uge1xuICBlbmhhbmNlZDogc3RyaW5nO1xuICBleHBsYW5hdGlvbjogc3RyaW5nO1xufVxuXG4vKipcbiAqIGBzZW5kbHkgdGVtcGxhdGVzIGVuaGFuY2VgIOKAlCBBSSByZXdyaXRlIGEgbWVzc2FnZSB0ZW1wbGF0ZSBmb3JcbiAqIGNvbXBsaWFuY2UvY2xhcml0eS9jb252ZXJzaW9ucy5cbiAqXG4gKiBXcmFwcyBgUE9TVCAvYXBpL3YxL2FpL2VuaGFuY2VgIChmbGFnLWdhdGVkIGJlaGluZCBgYWlfY2xhc3NpZmljYXRpb25gXG4gKiBvbiB0aGUgc2VydmVyIOKAlCBpZiB5b3VyIGFjY291bnQgZG9lc24ndCBoYXZlIHRoZSBmbGFnLCB0aGUgc2VydmVyXG4gKiByZXR1cm5zIDQwNCBhbmQgdGhpcyBjb21tYW5kIHN1cmZhY2VzIGl0KS4gQWNjZXB0cyB0ZXh0IGRpcmVjdGx5IHZpYVxuICogYC0tdGV4dGAsIGZyb20gYSBmaWxlIHZpYSBgLS1maWxlYCwgb3IgcGlwZWQgb24gc3RkaW4uXG4gKi9cbmV4cG9ydCBkZWZhdWx0IGNsYXNzIFRlbXBsYXRlc0VuaGFuY2UgZXh0ZW5kcyBBdXRoZW50aWNhdGVkQ29tbWFuZCB7XG4gIHN0YXRpYyBkZXNjcmlwdGlvbiA9XG4gICAgXCJBSS1lbmhhbmNlIGEgbWVzc2FnZSB0ZW1wbGF0ZSBmb3IgY2xhcml0eSwgY29tcGxpYW5jZSwgYW5kIHNlbmQtcmVhZGluZXNzLiBSZXR1cm5zIHRoZSByZXdyaXR0ZW4gdGV4dCArIGFuIGV4cGxhbmF0aW9uLlwiO1xuXG4gIHN0YXRpYyBleGFtcGxlcyA9IFtcbiAgICAnPCU9IGNvbmZpZy5iaW4gJT4gdGVtcGxhdGVzIGVuaGFuY2UgLS10ZXh0IFwiaGV5IGNvbWUgY2hlY2sgb3V0IG91ciBzYWxlXCInLFxuICAgICc8JT0gY29uZmlnLmJpbiAlPiB0ZW1wbGF0ZXMgZW5oYW5jZSAtLWZpbGUgLi9kcmFmdC50eHQgLS1tZXNzYWdlLXR5cGUgbWFya2V0aW5nJyxcbiAgICAnPCU9IGNvbmZpZy5iaW4gJT4gZWNobyBcInlvdXIgc2FsZSBtc2dcIiB8IHNlbmRseSB0ZW1wbGF0ZXMgZW5oYW5jZSAtLW1lc3NhZ2UtdHlwZSBwcm9tb3Rpb25hbCcsXG4gIF07XG5cbiAgc3RhdGljIGFyZ3MgPSB7XG4gICAgdGV4dDogQXJncy5zdHJpbmcoe1xuICAgICAgZGVzY3JpcHRpb246IFwiTWVzc2FnZSB0ZXh0IHRvIGVuaGFuY2UgKGFsdGVybmF0aXZlIHRvIC0tdGV4dCAvIC0tZmlsZSlcIixcbiAgICAgIHJlcXVpcmVkOiBmYWxzZSxcbiAgICB9KSxcbiAgfTtcblxuICBzdGF0aWMgZmxhZ3MgPSB7XG4gICAgLi4uQXV0aGVudGljYXRlZENvbW1hbmQuYmFzZUZsYWdzLFxuICAgIHRleHQ6IEZsYWdzLnN0cmluZyh7XG4gICAgICBkZXNjcmlwdGlvbjogXCJNZXNzYWdlIHRleHQgdG8gZW5oYW5jZVwiLFxuICAgIH0pLFxuICAgIGZpbGU6IEZsYWdzLnN0cmluZyh7XG4gICAgICBkZXNjcmlwdGlvbjogXCJSZWFkIG1lc3NhZ2UgdGV4dCBmcm9tIGEgZmlsZSBwYXRoXCIsXG4gICAgfSksXG4gICAgXCJtZXNzYWdlLXR5cGVcIjogRmxhZ3Muc3RyaW5nKHtcbiAgICAgIGRlc2NyaXB0aW9uOlxuICAgICAgICBcIkhpbnQgdGhlIGtpbmQgb2YgbWVzc2FnZSAoZS5nLiBtYXJrZXRpbmcsIHRyYW5zYWN0aW9uYWwsIG5vdGlmaWNhdGlvbikgZm9yIHRhcmdldGVkIGVuaGFuY2VtZW50c1wiLFxuICAgIH0pLFxuICB9O1xuXG4gIGFzeW5jIHJ1bigpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBjb25zdCB7IGFyZ3MsIGZsYWdzIH0gPSBhd2FpdCB0aGlzLnBhcnNlKFRlbXBsYXRlc0VuaGFuY2UpO1xuXG4gICAgbGV0IHRleHQ6IHN0cmluZyB8IHVuZGVmaW5lZCA9IGZsYWdzLnRleHQgPz8gYXJncy50ZXh0O1xuICAgIGlmICghdGV4dCAmJiBmbGFncy5maWxlKSB7XG4gICAgICB0ZXh0ID0gZnMucmVhZEZpbGVTeW5jKGZsYWdzLmZpbGUsIFwidXRmOFwiKS50cmltKCk7XG4gICAgfVxuICAgIGlmICghdGV4dCAmJiAhcHJvY2Vzcy5zdGRpbi5pc1RUWSkge1xuICAgICAgdGV4dCA9IGF3YWl0IG5ldyBQcm9taXNlPHN0cmluZz4oKHJlc29sdmUpID0+IHtcbiAgICAgICAgbGV0IGJ1ZiA9IFwiXCI7XG4gICAgICAgIHByb2Nlc3Muc3RkaW4uc2V0RW5jb2RpbmcoXCJ1dGY4XCIpO1xuICAgICAgICBwcm9jZXNzLnN0ZGluLm9uKFwiZGF0YVwiLCAoYykgPT4gKGJ1ZiArPSBjKSk7XG4gICAgICAgIHByb2Nlc3Muc3RkaW4ub24oXCJlbmRcIiwgKCkgPT4gcmVzb2x2ZShidWYudHJpbSgpKSk7XG4gICAgICB9KTtcbiAgICB9XG5cbiAgICBpZiAoIXRleHQgJiYgIWZsYWdzW1wibWVzc2FnZS10eXBlXCJdKSB7XG4gICAgICBlcnJvcihcbiAgICAgICAgXCJQcm92aWRlIHRleHQgdmlhIC0tdGV4dCwgLS1maWxlLCBwb3NpdGlvbmFsIGFyZywgb3Igc3RkaW4gKG9yIGF0IGxlYXN0IC0tbWVzc2FnZS10eXBlIGZvciBhIHRlbXBsYXRlIHN1Z2dlc3Rpb24pLlwiLFxuICAgICAgKTtcbiAgICAgIHRoaXMuZXhpdCgxKTtcbiAgICB9XG5cbiAgICBjb25zdCByZXNwID0gYXdhaXQgYXBpQ2xpZW50LnBvc3Q8RW5oYW5jZVJlc3BvbnNlPihcbiAgICAgIFwiL2FwaS92MS9haS9lbmhhbmNlXCIsXG4gICAgICB7XG4gICAgICAgIHRleHQ6IHRleHQgPz8gXCJcIixcbiAgICAgICAgbWVzc2FnZVR5cGU6IGZsYWdzW1wibWVzc2FnZS10eXBlXCJdLFxuICAgICAgfSxcbiAgICApO1xuXG4gICAgaWYgKGlzSnNvbk1vZGUoKSkge1xuICAgICAganNvbihyZXNwKTtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICBoZWFkZXIoXCJFbmhhbmNlZCB0ZXh0XCIpO1xuICAgIGNvbnNvbGUubG9nKFwiICBcIiArIChyZXNwLmVuaGFuY2VkIHx8IGNvbG9ycy5kaW0oXCIobm8gY2hhbmdlKVwiKSkpO1xuICAgIGlmIChyZXNwLmV4cGxhbmF0aW9uKSB7XG4gICAgICBjb25zb2xlLmxvZygpO1xuICAgICAgaW5mbyhyZXNwLmV4cGxhbmF0aW9uKTtcbiAgICB9XG4gICAgaWYgKCFyZXNwLmVuaGFuY2VkIHx8IHJlc3AuZW5oYW5jZWQgPT09IHRleHQpIHtcbiAgICAgIHN1Y2Nlc3MoXCJObyBlbmhhbmNlbWVudCBzdWdnZXN0ZWQg4oCUIG9yaWdpbmFsIHRleHQgbG9va3MgZ29vZC5cIik7XG4gICAgfVxuICB9XG59XG4iXX0=
@@ -0,0 +1,25 @@
1
+ import { Command } from "@oclif/core";
2
+ /**
3
+ * `sendly upgrade` — bring the CLI up to the latest version.
4
+ *
5
+ * Detects the install path (Homebrew vs npm-global vs unknown) and
6
+ * executes the matching package-manager upgrade command. If the install
7
+ * path is unknown we print both options for the user to pick.
8
+ *
9
+ * The `--check` flag does a dry-run: prints what would happen without
10
+ * executing anything. Useful in restricted shells where we don't want
11
+ * to trigger a brew/npm run accidentally.
12
+ *
13
+ * The unattended in-band update banner that prompts users to run this
14
+ * comes from the `@oclif/plugin-warn-if-update-available` plugin wired
15
+ * in package.json; this command is the companion action for that
16
+ * banner.
17
+ */
18
+ export default class Upgrade extends Command {
19
+ static description: string;
20
+ static examples: string[];
21
+ static flags: {
22
+ check: import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
23
+ };
24
+ run(): Promise<void>;
25
+ }