photon-msg 0.0.0-stage → 0.1.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.
Files changed (87) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +275 -2
  3. package/dist/args.d.ts +63 -0
  4. package/dist/args.d.ts.map +1 -0
  5. package/dist/args.js +68 -0
  6. package/dist/args.js.map +1 -0
  7. package/dist/bin.d.ts +3 -0
  8. package/dist/bin.d.ts.map +1 -0
  9. package/dist/bin.js +9 -0
  10. package/dist/bin.js.map +1 -0
  11. package/dist/cli.d.ts +13 -0
  12. package/dist/cli.d.ts.map +1 -0
  13. package/dist/cli.js +163 -0
  14. package/dist/cli.js.map +1 -0
  15. package/dist/client.d.ts +37 -0
  16. package/dist/client.d.ts.map +1 -0
  17. package/dist/client.js +68 -0
  18. package/dist/client.js.map +1 -0
  19. package/dist/commands/attachments.d.ts +25 -0
  20. package/dist/commands/attachments.d.ts.map +1 -0
  21. package/dist/commands/attachments.js +78 -0
  22. package/dist/commands/attachments.js.map +1 -0
  23. package/dist/commands/doctor.d.ts +9 -0
  24. package/dist/commands/doctor.d.ts.map +1 -0
  25. package/dist/commands/doctor.js +54 -0
  26. package/dist/commands/doctor.js.map +1 -0
  27. package/dist/commands/line.d.ts +51 -0
  28. package/dist/commands/line.d.ts.map +1 -0
  29. package/dist/commands/line.js +117 -0
  30. package/dist/commands/line.js.map +1 -0
  31. package/dist/commands/messages.d.ts +82 -0
  32. package/dist/commands/messages.d.ts.map +1 -0
  33. package/dist/commands/messages.js +163 -0
  34. package/dist/commands/messages.js.map +1 -0
  35. package/dist/commands/misc.d.ts +33 -0
  36. package/dist/commands/misc.d.ts.map +1 -0
  37. package/dist/commands/misc.js +61 -0
  38. package/dist/commands/misc.js.map +1 -0
  39. package/dist/commands/send.d.ts +107 -0
  40. package/dist/commands/send.d.ts.map +1 -0
  41. package/dist/commands/send.js +104 -0
  42. package/dist/commands/send.js.map +1 -0
  43. package/dist/commands/spaces.d.ts +56 -0
  44. package/dist/commands/spaces.d.ts.map +1 -0
  45. package/dist/commands/spaces.js +118 -0
  46. package/dist/commands/spaces.js.map +1 -0
  47. package/dist/commands/users.d.ts +34 -0
  48. package/dist/commands/users.d.ts.map +1 -0
  49. package/dist/commands/users.js +82 -0
  50. package/dist/commands/users.js.map +1 -0
  51. package/dist/config.d.ts +47 -0
  52. package/dist/config.d.ts.map +1 -0
  53. package/dist/config.js +77 -0
  54. package/dist/config.js.map +1 -0
  55. package/dist/content.d.ts +33 -0
  56. package/dist/content.d.ts.map +1 -0
  57. package/dist/content.js +287 -0
  58. package/dist/content.js.map +1 -0
  59. package/dist/errors.d.ts +62 -0
  60. package/dist/errors.d.ts.map +1 -0
  61. package/dist/errors.js +95 -0
  62. package/dist/errors.js.map +1 -0
  63. package/dist/help.d.ts +8 -0
  64. package/dist/help.d.ts.map +1 -0
  65. package/dist/help.js +177 -0
  66. package/dist/help.js.map +1 -0
  67. package/dist/index.d.ts +21 -0
  68. package/dist/index.d.ts.map +1 -0
  69. package/dist/index.js +13 -0
  70. package/dist/index.js.map +1 -0
  71. package/dist/output.d.ts +35 -0
  72. package/dist/output.d.ts.map +1 -0
  73. package/dist/output.js +121 -0
  74. package/dist/output.js.map +1 -0
  75. package/dist/target.d.ts +34 -0
  76. package/dist/target.d.ts.map +1 -0
  77. package/dist/target.js +75 -0
  78. package/dist/target.js.map +1 -0
  79. package/dist/types.d.ts +200 -0
  80. package/dist/types.d.ts.map +1 -0
  81. package/dist/types.js +8 -0
  82. package/dist/types.js.map +1 -0
  83. package/dist/upload.d.ts +33 -0
  84. package/dist/upload.d.ts.map +1 -0
  85. package/dist/upload.js +119 -0
  86. package/dist/upload.js.map +1 -0
  87. package/package.json +49 -4
@@ -0,0 +1,287 @@
1
+ /**
2
+ * Turns CLI flags into a Photon `SendContent` payload.
3
+ *
4
+ * The API takes one polymorphic `content` object, which is how edits, unsends,
5
+ * reactions, read receipts, typing indicators and poll votes travel: there is no
6
+ * separate endpoint per action. This module is the single source of truth for
7
+ * that mapping, and it stays pure by taking uploads and user lookups as injected
8
+ * dependencies.
9
+ */
10
+ import { readFileSync } from "node:fs";
11
+ import { usageError } from "./errors.js";
12
+ /** Every content mode the CLI can build. */
13
+ export const MODES = [
14
+ "text",
15
+ "attachment",
16
+ "voice",
17
+ "reaction",
18
+ "poll",
19
+ "edit",
20
+ "delete",
21
+ "read",
22
+ "typing",
23
+ "participants",
24
+ "group",
25
+ ];
26
+ const asArray = (value) => value === undefined ? [] : Array.isArray(value) ? value : [value];
27
+ const isSet = (value) => value !== undefined && value !== null && value !== false;
28
+ /** Lists the content modes implied by the given flags. */
29
+ export function detectModes(flags) {
30
+ const modes = [];
31
+ // An edit carries its replacement text, so --text belongs to the edit rather
32
+ // than being a competing mode.
33
+ const editing = isSet(flags.edit);
34
+ if (!editing && (isSet(flags.text) || isSet(flags["text-file"])))
35
+ modes.push("text");
36
+ if (asArray(flags.image).length > 0 || asArray(flags.file).length > 0)
37
+ modes.push("attachment");
38
+ if (isSet(flags.voice))
39
+ modes.push("voice");
40
+ if (isSet(flags.reaction))
41
+ modes.push("reaction");
42
+ if (isSet(flags.poll))
43
+ modes.push("poll");
44
+ if (editing)
45
+ modes.push("edit");
46
+ if (isSet(flags.delete))
47
+ modes.push("delete");
48
+ if (flags.read === true)
49
+ modes.push("read");
50
+ if (isSet(flags.typing))
51
+ modes.push("typing");
52
+ if (asArray(flags["participants-add"]).length > 0 || asArray(flags["participants-remove"]).length > 0) {
53
+ modes.push("participants");
54
+ }
55
+ if (flags.group === true)
56
+ modes.push("group");
57
+ return modes;
58
+ }
59
+ /** Reads text from a file, or stdin when the path is "-". */
60
+ export function readTextFile(path) {
61
+ try {
62
+ return readFileSync(path === "-" ? 0 : path, "utf8");
63
+ }
64
+ catch (error) {
65
+ throw usageError(`Could not read ${path === "-" ? "stdin" : path}: ${error.message}`);
66
+ }
67
+ }
68
+ const normalizeFormat = (format) => {
69
+ if (!format)
70
+ return "plain";
71
+ const value = format.toLowerCase();
72
+ if (value !== "plain" && value !== "markdown") {
73
+ throw usageError(`--format must be "plain" or "markdown", got "${format}".`);
74
+ }
75
+ return value;
76
+ };
77
+ const textPart = (text, format) => ({ type: "text", format, text });
78
+ /**
79
+ * Cheap, side-effect-free validation of content flags, so the CLI can refuse a
80
+ * contradictory command before it asks for credentials or touches the network.
81
+ */
82
+ export function validateContentFlags(flags) {
83
+ const modes = detectModes(flags);
84
+ if (modes.length === 0) {
85
+ throw usageError("Nothing to send. Pass --text, --image, --poll, --reaction, --edit, and so on.", "Run `photon-msg send --help` for the full list.");
86
+ }
87
+ if (modes.length > 1 && !flags.group) {
88
+ throw usageError(`Conflicting content flags: ${modes.join(", ")}. Use one content flag, or add --group to send several parts together.`);
89
+ }
90
+ return modes;
91
+ }
92
+ /** Builds the send payload from flags. */
93
+ export async function buildMessage({ flags, upload, getUsers }) {
94
+ const modes = validateContentFlags(flags);
95
+ const format = normalizeFormat(flags.format);
96
+ // --group overrides the single-mode rule: several parts travel together.
97
+ const mode = flags.group ? "group" : modes[0];
98
+ const replyTo = flags["reply-to"];
99
+ const initialTexts = asArray(flags.text);
100
+ const texts = initialTexts.length > 0
101
+ ? initialTexts
102
+ : flags["text-file"] !== undefined
103
+ ? [readTextFile(flags["text-file"])]
104
+ : [];
105
+ const attachmentSources = [...asArray(flags.image), ...asArray(flags.file)];
106
+ const build = async () => {
107
+ switch (mode) {
108
+ case "text": {
109
+ if (texts.length === 0)
110
+ throw usageError("--text is empty.");
111
+ return { content: textPart(texts.join("\n"), format) };
112
+ }
113
+ case "attachment": {
114
+ const source = attachmentSources[0];
115
+ if (!source)
116
+ throw usageError("--image needs a path or URL.");
117
+ const attachmentId = await upload(source);
118
+ const content = {
119
+ type: "attachment",
120
+ attachmentId,
121
+ ...(flags.caption
122
+ ? { metadata: { caption: { format, text: flags.caption } } }
123
+ : {}),
124
+ };
125
+ return { content };
126
+ }
127
+ case "voice": {
128
+ if (!flags.voice)
129
+ throw usageError("--voice needs a path.");
130
+ const attachmentId = await upload(flags.voice);
131
+ const content = { type: "voice", attachmentId };
132
+ return { content };
133
+ }
134
+ case "reaction": {
135
+ if (!replyTo)
136
+ throw usageError("--reaction needs --reply-to <messageId> to name the message being reacted to.");
137
+ const action = flags["reaction-action"] ?? "add";
138
+ if (action !== "add" && action !== "remove") {
139
+ throw usageError(`--reaction-action must be "add" or "remove", got "${action}".`);
140
+ }
141
+ const content = {
142
+ type: "reaction",
143
+ reaction: flags.reaction,
144
+ action,
145
+ };
146
+ return { content, parentMessageId: replyTo };
147
+ }
148
+ case "poll": {
149
+ const options = asArray(flags["poll-option"]).map((text, index) => ({
150
+ id: `opt_${index + 1}`,
151
+ text,
152
+ }));
153
+ if (options.length === 0)
154
+ throw usageError("--poll needs at least one --poll-option.");
155
+ const selectionMode = flags["poll-mode"] ?? "single";
156
+ if (selectionMode !== "single" && selectionMode !== "multiple") {
157
+ throw usageError(`--poll-mode must be "single" or "multiple", got "${selectionMode}".`);
158
+ }
159
+ const content = {
160
+ type: "poll",
161
+ question: flags.poll,
162
+ options,
163
+ selectionMode,
164
+ };
165
+ return { content };
166
+ }
167
+ case "edit": {
168
+ if (texts.length === 0)
169
+ throw usageError("--edit needs --text with the replacement text.");
170
+ const content = {
171
+ type: "text.update",
172
+ format,
173
+ text: texts.join("\n"),
174
+ };
175
+ return { content, parentMessageId: flags.edit };
176
+ }
177
+ case "delete": {
178
+ const content = {
179
+ type: "message.delete",
180
+ ...(flags.reason ? { reason: flags.reason } : {}),
181
+ };
182
+ return { content, parentMessageId: flags.delete };
183
+ }
184
+ case "read": {
185
+ if (!replyTo)
186
+ throw usageError("--read needs --reply-to <messageId> to name the message being marked read.");
187
+ const content = { type: "message.read" };
188
+ return { content, parentMessageId: replyTo };
189
+ }
190
+ case "typing": {
191
+ const raw = String(flags.typing).toLowerCase();
192
+ const typing = raw === "on" || raw === "true" || raw === "1";
193
+ if (!typing && raw !== "off" && raw !== "false" && raw !== "0") {
194
+ throw usageError(`--typing must be "on" or "off", got "${flags.typing}".`);
195
+ }
196
+ const content = { type: "space.typing.update", typing };
197
+ return { content };
198
+ }
199
+ case "participants": {
200
+ const added = asArray(flags["participants-add"]);
201
+ const removed = asArray(flags["participants-remove"]);
202
+ const users = await getUsers([...added, ...removed]);
203
+ const byId = new Map(users.map((user) => [user.id, user]));
204
+ const snapshot = (id) => {
205
+ const user = byId.get(id);
206
+ if (!user)
207
+ throw usageError(`Unknown user ID "${id}".`);
208
+ // The API accepts the user record it returned as the snapshot input.
209
+ return user;
210
+ };
211
+ const content = {
212
+ type: "space.participants.update",
213
+ added: added.map(snapshot),
214
+ removed: removed.map(snapshot),
215
+ };
216
+ return { content };
217
+ }
218
+ case "group": {
219
+ const uploads = attachmentSources.length + (isSet(flags.voice) ? 1 : 0);
220
+ if (texts.length + uploads < 2) {
221
+ throw usageError('--group needs at least two parts, for example --text "a" --image ./b.png.');
222
+ }
223
+ const parts = texts.map((text) => textPart(text, format));
224
+ for (const source of attachmentSources) {
225
+ const attachmentId = await upload(source);
226
+ parts.push({ type: "attachment", attachmentId });
227
+ }
228
+ if (flags.voice) {
229
+ const attachmentId = await upload(flags.voice);
230
+ parts.push({ type: "voice", attachmentId });
231
+ }
232
+ const content = { type: "group", parts };
233
+ return { content };
234
+ }
235
+ default: {
236
+ const exhaustive = mode;
237
+ throw usageError(`Unsupported content mode "${String(exhaustive)}".`);
238
+ }
239
+ }
240
+ };
241
+ const built = await build();
242
+ const result = { content: built.content };
243
+ if (built.parentMessageId)
244
+ result.parentMessageId = built.parentMessageId;
245
+ const cc = asArray(flags.cc);
246
+ const bcc = asArray(flags.bcc);
247
+ if (flags.subject || cc.length > 0 || bcc.length > 0) {
248
+ result.email = {
249
+ ...(flags.subject ? { subject: flags.subject } : {}),
250
+ ...(cc.length > 0 ? { cc: cc.map((address) => ({ address })) } : {}),
251
+ ...(bcc.length > 0 ? { bcc: bcc.map((address) => ({ address })) } : {}),
252
+ };
253
+ }
254
+ return result;
255
+ }
256
+ /** Short human description of a payload, used by --dry-run and logs. */
257
+ export function describeContent(content) {
258
+ if (!content)
259
+ return "nothing";
260
+ switch (content.type) {
261
+ case "text":
262
+ return `text(${content.format}): ${JSON.stringify(content.text)}`;
263
+ case "text.update":
264
+ return `edit(${content.format}): ${JSON.stringify(content.text)}`;
265
+ case "attachment":
266
+ return `attachment(${content.attachmentId})`;
267
+ case "voice":
268
+ return `voice(${content.attachmentId})`;
269
+ case "reaction":
270
+ return `reaction(${content.action ?? "add"} ${content.reaction})`;
271
+ case "poll":
272
+ return `poll(${JSON.stringify(content.question)}, ${content.options.length} options)`;
273
+ case "message.delete":
274
+ return "delete";
275
+ case "message.read":
276
+ return "read receipt";
277
+ case "space.typing.update":
278
+ return `typing(${content.typing ? "on" : "off"})`;
279
+ case "space.participants.update":
280
+ return `participants(+${content.added.length}/-${content.removed.length})`;
281
+ case "group":
282
+ return `group(${content.parts.length} parts)`;
283
+ default:
284
+ return "unknown content";
285
+ }
286
+ }
287
+ //# sourceMappingURL=content.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"content.js","sourceRoot":"","sources":["../src/content.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Error types and exit codes.
3
+ *
4
+ * Every failure path funnels through `CliError`, so callers always get a stable
5
+ * `exitCode` plus an optional actionable `hint`.
6
+ */
7
+ export declare const EXIT: {
8
+ readonly OK: 0;
9
+ readonly FAILURE: 1;
10
+ readonly USAGE: 2;
11
+ readonly AUTH: 3;
12
+ readonly NOT_FOUND: 4;
13
+ readonly FORBIDDEN: 5;
14
+ readonly CONFLICT: 6;
15
+ };
16
+ export type ExitCode = (typeof EXIT)[keyof typeof EXIT];
17
+ export interface CliErrorOptions {
18
+ exitCode?: number;
19
+ hint?: string;
20
+ code?: string;
21
+ details?: unknown;
22
+ }
23
+ export declare class CliError extends Error {
24
+ readonly exitCode: number;
25
+ readonly hint?: string;
26
+ readonly code?: string;
27
+ readonly details?: unknown;
28
+ constructor(message: string, options?: CliErrorOptions);
29
+ }
30
+ /** Usage error: bad flags, missing required arguments. */
31
+ export declare const usageError: (message: string, hint?: string) => CliError;
32
+ /** Problem codes the API returns, mapped to guidance. */
33
+ export declare const PROBLEM_HINTS: Record<string, string>;
34
+ /** The shape of an API problem document. */
35
+ export interface ProblemBody {
36
+ code?: string;
37
+ detail?: string;
38
+ title?: string;
39
+ requestId?: string;
40
+ type?: string;
41
+ [key: string]: unknown;
42
+ }
43
+ /** Structural type of an `@photon-ai/api` error, without importing the class. */
44
+ export interface ApiErrorLike {
45
+ status: number;
46
+ message: string;
47
+ body?: ProblemBody;
48
+ }
49
+ export declare const isApiError: (error: unknown) => error is ApiErrorLike;
50
+ /** Normalizes anything thrown into a CliError. */
51
+ export declare function toCliError(error: unknown): CliError;
52
+ /** Serializes an error for `--json` output. */
53
+ export declare const errorPayload: (cliError: CliError) => {
54
+ error: {
55
+ message: string;
56
+ code: string | null;
57
+ exitCode: number;
58
+ hint: string | null;
59
+ details: {} | null;
60
+ };
61
+ };
62
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,eAAO,MAAM,IAAI;;;;;;;;CAQP,CAAC;AAEX,MAAM,MAAM,QAAQ,GAAG,CAAC,OAAO,IAAI,CAAC,CAAC,MAAM,OAAO,IAAI,CAAC,CAAC;AAExD,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,qBAAa,QAAS,SAAQ,KAAK;IACjC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;gBAEf,OAAO,EAAE,MAAM,EAAE,OAAO,GAAE,eAAoB;CAQ3D;AAED,0DAA0D;AAC1D,eAAO,MAAM,UAAU,GAAI,SAAS,MAAM,EAAE,OAAO,MAAM,KAAG,QACU,CAAC;AAEvE,yDAAyD;AACzD,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAmBhD,CAAC;AAWF,4CAA4C;AAC5C,MAAM,WAAW,WAAW;IAC1B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,iFAAiF;AACjF,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,WAAW,CAAC;CACpB;AAED,eAAO,MAAM,UAAU,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,YAIS,CAAC;AAE/D,kDAAkD;AAClD,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,QAAQ,CAsBnD;AAED,+CAA+C;AAC/C,eAAO,MAAM,YAAY,GAAI,UAAU,QAAQ;;;;;;;;CAQ7C,CAAC"}
package/dist/errors.js ADDED
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Error types and exit codes.
3
+ *
4
+ * Every failure path funnels through `CliError`, so callers always get a stable
5
+ * `exitCode` plus an optional actionable `hint`.
6
+ */
7
+ export const EXIT = {
8
+ OK: 0,
9
+ FAILURE: 1,
10
+ USAGE: 2,
11
+ AUTH: 3,
12
+ NOT_FOUND: 4,
13
+ FORBIDDEN: 5,
14
+ CONFLICT: 6,
15
+ };
16
+ export class CliError extends Error {
17
+ exitCode;
18
+ hint;
19
+ code;
20
+ details;
21
+ constructor(message, options = {}) {
22
+ super(message);
23
+ this.name = "CliError";
24
+ this.exitCode = options.exitCode ?? EXIT.FAILURE;
25
+ this.hint = options.hint;
26
+ this.code = options.code;
27
+ this.details = options.details;
28
+ }
29
+ }
30
+ /** Usage error: bad flags, missing required arguments. */
31
+ export const usageError = (message, hint) => new CliError(message, { exitCode: EXIT.USAGE, hint, code: "USAGE" });
32
+ /** Problem codes the API returns, mapped to guidance. */
33
+ export const PROBLEM_HINTS = {
34
+ SHARED_LINE_INBOUND_REQUIRED: "This line is a pooled shared line, so it cannot message someone who has not written to it first. " +
35
+ "Have the recipient send any message to the line, then reply inside that space. " +
36
+ "For agent-initiated messages you need a purchased SMS number or a dedicated iMessage line.",
37
+ SHARED_LINE_GROUP_UNSUPPORTED: "Pooled shared lines only carry direct conversations, not group chats.",
38
+ NOT_AUTHENTICATED: "Set PHOTON_API_KEY to a project API key, or pass --api-key.",
39
+ INSUFFICIENT_SCOPE: "The API key is missing a permission this call needs. Project keys need project:read / project:write; " +
40
+ "line assignments also need platforms:read / platforms:write.",
41
+ FORBIDDEN: "The key is valid but not allowed to touch this resource. Check the project ID and key scope.",
42
+ PROJECT_NOT_SELECTED: "Set PHOTON_PROJECT_ID, or pass --project.",
43
+ IDEMPOTENCY_KEY_REUSED: "That idempotency key was already used for a different request. Omit --idempotency-key to mint a new one.",
44
+ VALIDATION_FAILED: "The API rejected the payload. Re-run with --json to see the full problem detail.",
45
+ ATTACHMENT_NOT_READY: "The attachment is still processing. Retry after the server's Retry-After interval.",
46
+ RATE_LIMITED: "Rate limited. Honor the Retry-After header instead of retrying immediately.",
47
+ ATTACHMENT_TOO_LARGE: "The file exceeds the attachment size limit.",
48
+ UNSUPPORTED_CONTENT: "This platform does not support that content type. Check the platform's feature support.",
49
+ };
50
+ const EXIT_BY_STATUS = {
51
+ 401: EXIT.AUTH,
52
+ 403: EXIT.FORBIDDEN,
53
+ 404: EXIT.NOT_FOUND,
54
+ 409: EXIT.CONFLICT,
55
+ 422: EXIT.USAGE,
56
+ 429: EXIT.FAILURE,
57
+ };
58
+ export const isApiError = (error) => typeof error === "object" &&
59
+ error !== null &&
60
+ typeof error.status === "number" &&
61
+ typeof error.message === "string";
62
+ /** Normalizes anything thrown into a CliError. */
63
+ export function toCliError(error) {
64
+ if (error instanceof CliError)
65
+ return error;
66
+ if (isApiError(error)) {
67
+ const problem = error.body ?? {};
68
+ const code = problem.code ?? `HTTP_${error.status}`;
69
+ const detail = problem.detail ?? problem.title ?? error.message;
70
+ const exitCode = EXIT_BY_STATUS[error.status] ?? EXIT.FAILURE;
71
+ return new CliError(`Photon API error ${error.status}: ${detail}`, {
72
+ exitCode,
73
+ code,
74
+ hint: PROBLEM_HINTS[code],
75
+ details: {
76
+ code,
77
+ status: error.status,
78
+ requestId: problem.requestId,
79
+ type: problem.type,
80
+ },
81
+ });
82
+ }
83
+ return new CliError(error instanceof Error ? error.message : String(error));
84
+ }
85
+ /** Serializes an error for `--json` output. */
86
+ export const errorPayload = (cliError) => ({
87
+ error: {
88
+ message: cliError.message,
89
+ code: cliError.code ?? null,
90
+ exitCode: cliError.exitCode,
91
+ hint: cliError.hint ?? null,
92
+ details: cliError.details ?? null,
93
+ },
94
+ });
95
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,CAAC,MAAM,IAAI,GAAG;IAClB,EAAE,EAAE,CAAC;IACL,OAAO,EAAE,CAAC;IACV,KAAK,EAAE,CAAC;IACR,IAAI,EAAE,CAAC;IACP,SAAS,EAAE,CAAC;IACZ,SAAS,EAAE,CAAC;IACZ,QAAQ,EAAE,CAAC;CACH,CAAC;AAWX,MAAM,OAAO,QAAS,SAAQ,KAAK;IACxB,QAAQ,CAAS;IACjB,IAAI,CAAU;IACd,IAAI,CAAU;IACd,OAAO,CAAW;IAE3B,YAAY,OAAe,EAAE,UAA2B,EAAE;QACxD,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,UAAU,CAAC;QACvB,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,IAAI,CAAC,OAAO,CAAC;QACjD,IAAI,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;QACzB,IAAI,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;QACzB,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;IACjC,CAAC;CACF;AAED,0DAA0D;AAC1D,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,OAAe,EAAE,IAAa,EAAY,EAAE,CACrE,IAAI,QAAQ,CAAC,OAAO,EAAE,EAAE,QAAQ,EAAE,IAAI,CAAC,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC;AAEvE,yDAAyD;AACzD,MAAM,CAAC,MAAM,aAAa,GAA2B;IACnD,4BAA4B,EAC1B,mGAAmG;QACnG,iFAAiF;QACjF,4FAA4F;IAC9F,6BAA6B,EAAE,uEAAuE;IACtG,iBAAiB,EAAE,6DAA6D;IAChF,kBAAkB,EAChB,uGAAuG;QACvG,8DAA8D;IAChE,SAAS,EAAE,8FAA8F;IACzG,oBAAoB,EAAE,2CAA2C;IACjE,sBAAsB,EACpB,0GAA0G;IAC5G,iBAAiB,EAAE,kFAAkF;IACrG,oBAAoB,EAAE,oFAAoF;IAC1G,YAAY,EAAE,6EAA6E;IAC3F,oBAAoB,EAAE,6CAA6C;IACnE,mBAAmB,EAAE,yFAAyF;CAC/G,CAAC;AAEF,MAAM,cAAc,GAA2B;IAC7C,GAAG,EAAE,IAAI,CAAC,IAAI;IACd,GAAG,EAAE,IAAI,CAAC,SAAS;IACnB,GAAG,EAAE,IAAI,CAAC,SAAS;IACnB,GAAG,EAAE,IAAI,CAAC,QAAQ;IAClB,GAAG,EAAE,IAAI,CAAC,KAAK;IACf,GAAG,EAAE,IAAI,CAAC,OAAO;CAClB,CAAC;AAmBF,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,KAAc,EAAyB,EAAE,CAClE,OAAO,KAAK,KAAK,QAAQ;IACzB,KAAK,KAAK,IAAI;IACd,OAAQ,KAA8B,CAAC,MAAM,KAAK,QAAQ;IAC1D,OAAQ,KAA+B,CAAC,OAAO,KAAK,QAAQ,CAAC;AAE/D,kDAAkD;AAClD,MAAM,UAAU,UAAU,CAAC,KAAc;IACvC,IAAI,KAAK,YAAY,QAAQ;QAAE,OAAO,KAAK,CAAC;IAE5C,IAAI,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC;QACtB,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,IAAI,EAAE,CAAC;QACjC,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,QAAQ,KAAK,CAAC,MAAM,EAAE,CAAC;QACpD,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,OAAO,CAAC,KAAK,IAAI,KAAK,CAAC,OAAO,CAAC;QAChE,MAAM,QAAQ,GAAG,cAAc,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC,OAAO,CAAC;QAC9D,OAAO,IAAI,QAAQ,CAAC,oBAAoB,KAAK,CAAC,MAAM,KAAK,MAAM,EAAE,EAAE;YACjE,QAAQ;YACR,IAAI;YACJ,IAAI,EAAE,aAAa,CAAC,IAAI,CAAC;YACzB,OAAO,EAAE;gBACP,IAAI;gBACJ,MAAM,EAAE,KAAK,CAAC,MAAM;gBACpB,SAAS,EAAE,OAAO,CAAC,SAAS;gBAC5B,IAAI,EAAE,OAAO,CAAC,IAAI;aACnB;SACF,CAAC,CAAC;IACL,CAAC;IAED,OAAO,IAAI,QAAQ,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;AAC9E,CAAC;AAED,+CAA+C;AAC/C,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,QAAkB,EAAE,EAAE,CAAC,CAAC;IACnD,KAAK,EAAE;QACL,OAAO,EAAE,QAAQ,CAAC,OAAO;QACzB,IAAI,EAAE,QAAQ,CAAC,IAAI,IAAI,IAAI;QAC3B,QAAQ,EAAE,QAAQ,CAAC,QAAQ;QAC3B,IAAI,EAAE,QAAQ,CAAC,IAAI,IAAI,IAAI;QAC3B,OAAO,EAAE,QAAQ,CAAC,OAAO,IAAI,IAAI;KAClC;CACF,CAAC,CAAC"}
package/dist/help.d.ts ADDED
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Help text. Kept as data so `--help` output stays consistent across commands.
3
+ */
4
+ /** Single source of truth for the version: package.json. */
5
+ export declare const VERSION: string;
6
+ export declare const MAIN_HELP: string;
7
+ export declare const COMMAND_HELP: Record<string, string>;
8
+ //# sourceMappingURL=help.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"help.d.ts","sourceRoot":"","sources":["../src/help.ts"],"names":[],"mappings":"AAAA;;GAEG;AAuCH,4DAA4D;AAC5D,eAAO,MAAM,OAAO,EAAE,MAAsB,CAAC;AAE7C,eAAO,MAAM,SAAS,QAyDrB,CAAC;AAEF,eAAO,MAAM,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAgF/C,CAAC"}
package/dist/help.js ADDED
@@ -0,0 +1,177 @@
1
+ /**
2
+ * Help text. Kept as data so `--help` output stays consistent across commands.
3
+ */
4
+ import { existsSync, readFileSync } from "node:fs";
5
+ import { dirname, join } from "node:path";
6
+ import { fileURLToPath } from "node:url";
7
+ /**
8
+ * Reads the version from the nearest package.json.
9
+ *
10
+ * This module can be loaded from `dist/` (published) or `.test-build/src/`
11
+ * (compiled tests), so the manifest is found by walking up rather than assuming
12
+ * a fixed depth. Walking up also survives a change to the output layout.
13
+ */
14
+ function readVersion() {
15
+ let directory = dirname(fileURLToPath(import.meta.url));
16
+ for (let depth = 0; depth < 5; depth += 1) {
17
+ const candidate = join(directory, "package.json");
18
+ if (existsSync(candidate)) {
19
+ try {
20
+ const pkg = JSON.parse(readFileSync(candidate, "utf8"));
21
+ if (pkg.version && (pkg.name === undefined || pkg.name === "photon-msg")) {
22
+ return pkg.version;
23
+ }
24
+ }
25
+ catch {
26
+ // Fall through to the parent directory.
27
+ }
28
+ }
29
+ const parent = dirname(directory);
30
+ if (parent === directory)
31
+ break;
32
+ directory = parent;
33
+ }
34
+ return "0.0.0";
35
+ }
36
+ /** Single source of truth for the version: package.json. */
37
+ export const VERSION = readVersion();
38
+ export const MAIN_HELP = `photon-msg ${VERSION} — send and read messages through the Photon API
39
+
40
+ USAGE
41
+ photon-msg <command> [options]
42
+
43
+ COMMANDS
44
+ send Send any content type: text, image, voice, poll, reaction, edit, unsend
45
+ messages list List messages with filters
46
+ messages get Fetch one message
47
+ messages watch Follow new messages as JSON lines
48
+ spaces list List conversations
49
+ spaces get Fetch one conversation
50
+ spaces create Create or reuse a conversation
51
+ users list List people and agents in the project
52
+ users get Fetch one user
53
+ users ensure Resolve a handle to a user ID
54
+ attachments upload Upload a file or URL
55
+ attachments get Inspect attachment metadata
56
+ attachments download Write attachment bytes to disk
57
+ typing Start or stop a typing indicator
58
+ read Mark a message read
59
+ line assign Assign a pooled iMessage line to a handle
60
+ line list List shared-line assignments
61
+ line get Fetch one assignment
62
+ line release Release an assignment
63
+ doctor Check credentials and project wiring
64
+ help Show this help
65
+
66
+ GLOBAL OPTIONS
67
+ --json Machine-readable output
68
+ --no-color Disable ANSI color
69
+ --api-key <key> Project API key (or PHOTON_API_KEY)
70
+ --project <id> Project ID (or PHOTON_PROJECT_ID)
71
+ --base-url <url> API origin (or PHOTON_API_BASE_URL)
72
+ --timeout <ms> Per-request timeout
73
+ --dry-run Print the request instead of sending it
74
+ --quiet, -q Suppress the summary lines
75
+ --help, -h Show help
76
+ --version, -v Show version
77
+
78
+ ADDRESSING
79
+ --space <spaceId> --as <agent> reply into an existing conversation
80
+ --to <handle> --as <agent> start or reuse a direct conversation
81
+ --as accepts a handle or a pho_usr_ ID; PHOTON_AGENT_HANDLE is the default.
82
+
83
+ EXAMPLES
84
+ photon-msg send --to +14155550123 --as +16285024649 --text "hello"
85
+ photon-msg send --space pho_spc_... --as pho_usr_... --image ./photo.png --caption "look"
86
+ photon-msg send --space pho_spc_... --as pho_usr_... --poll "Lunch?" --poll-option Yes --poll-option No
87
+ photon-msg send --space pho_spc_... --as pho_usr_... --reply-to pho_msg_... --reaction "❤️"
88
+ photon-msg send --space pho_spc_... --as pho_usr_... --edit pho_msg_... --text "fixed typo"
89
+ photon-msg messages list --platform imessage --direction received --limit 20
90
+ photon-msg line assign --handle +14155550123
91
+
92
+ Addressing a new conversation requires a line that may start it. Pooled shared
93
+ lines cannot: the recipient must message the line first, and the API answers
94
+ 403 SHARED_LINE_INBOUND_REQUIRED until they do.
95
+ `;
96
+ export const COMMAND_HELP = {
97
+ send: `photon-msg send — send one message
98
+
99
+ DESTINATION
100
+ --space <spaceId> Existing conversation (reply, edit, reaction, ...)
101
+ --to <handle> Start or reuse a direct conversation with this handle
102
+ --as <handle|userId> Sender agent (default: PHOTON_AGENT_HANDLE)
103
+ --platform <name> imessage | sms | email | whatsapp | whatsapp_business | telegram
104
+
105
+ CONTENT (choose one, or combine with --group)
106
+ --text <string> Text body (repeatable; lines are joined)
107
+ --text-file <path> Read the body from a file, or "-" for stdin
108
+ --format <plain|markdown>
109
+ --image <path|url> Image attachment (repeatable)
110
+ --file <path|url> Any attachment (repeatable)
111
+ --caption <string> Caption for a single attachment
112
+ --voice <path> Voice note
113
+ --poll <question> --poll-option <text> [--poll-option ...] [--poll-mode single|multiple]
114
+ --reaction <emoji> [--reaction-action add|remove] needs --reply-to
115
+ --edit <messageId> --text <new text> edit a sent message
116
+ --delete <messageId> [--reason <text>] unsend a message
117
+ --read read receipt, needs --reply-to
118
+ --typing <on|off> typing indicator as content
119
+ --participants-add <userId> --participants-remove <userId>
120
+ --group send the supplied parts as one group
121
+
122
+ OTHER
123
+ --reply-to <messageId> Thread the message under another
124
+ --idempotency-key <key> Reuse across retries of the same logical send
125
+ --subject/--cc/--bcc Email-only options
126
+
127
+ Acceptance is asynchronous: a successful send means the API queued the message,
128
+ not that the recipient has seen it.
129
+ `,
130
+ "messages list": `photon-msg messages list — list messages
131
+
132
+ --platform <name> Restrict to one platform
133
+ --direction received|dispatched
134
+ --space <spaceId> Restrict to a conversation
135
+ --resource <resourceId> Restrict to a resource
136
+ --perspective-user <id> Restrict to one user's view (needs --space)
137
+ --created-since/--created-before RFC 3339 bounds on createdAt
138
+ --updated-since/--updated-before RFC 3339 bounds on updatedAt
139
+ --exclude-content-types <list> Comma-separated content types
140
+ --order asc|desc --order-by <field>
141
+ --page-size <n> --page-token <token> --limit <n>
142
+ `,
143
+ "messages watch": `photon-msg messages watch — follow new messages as JSON lines
144
+
145
+ --platform <name> --space <spaceId> --direction <dir>
146
+ --interval <seconds> Poll interval (default 2)
147
+ --for <seconds> Stop after this long (default: run until interrupted)
148
+ --since <rfc3339> Start point (default: now)
149
+ `,
150
+ "spaces create": `photon-msg spaces create — create or reuse a conversation
151
+
152
+ --platform <name>
153
+ --participant <handle|userId> Repeatable; order matters on iMessage (first is creator)
154
+ --idempotency-key <key>
155
+ `,
156
+ "users ensure": `photon-msg users ensure — resolve a handle to a user ID
157
+
158
+ --handle <handle> Phone (E.164) or email
159
+ --platform <name>
160
+ `,
161
+ "attachments upload": `photon-msg attachments upload — upload a file or URL
162
+
163
+ <path|url> --content-type <mime>
164
+ `,
165
+ typing: `photon-msg typing — typing indicator
166
+
167
+ --space <spaceId> --as <agent> --on | --off
168
+ `,
169
+ "line assign": `photon-msg line assign — assign a pooled iMessage line
170
+
171
+ --handle <+15551234567> The human who will write to the line
172
+ --email <address> Optional invite address
173
+ --first-name/--last-name
174
+ --idempotency-key <key> Reuse for retries of the same assignment
175
+ `,
176
+ };
177
+ //# sourceMappingURL=help.js.map