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
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 photon-msg contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,276 @@
1
- # Temporary Holding Version
1
+ # photon-msg
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ A command line client for the [Photon](https://photon.codes) messaging API. It sends text, images, voice notes, polls, reactions, edits and unsends to iMessage, SMS, email, WhatsApp and Telegram, and it reads messages back out.
4
+
5
+ ```sh
6
+ photon-msg send --to +14155550123 --as +16285024649 --text "hello"
7
+ photon-msg messages list --platform imessage --limit 20
8
+ ```
9
+
10
+ Written in TypeScript on top of [`@photon-ai/api`](https://www.npmjs.com/package/@photon-ai/api).
11
+
12
+ The official `photon` CLI sets up projects, keys, numbers and billing. It doesn't send anything itself, so this fills that gap.
13
+
14
+ ## Install
15
+
16
+ ```sh
17
+ npm install --global photon-msg
18
+ ```
19
+
20
+ From a checkout instead:
21
+
22
+ ```sh
23
+ git clone <this repo> && cd photon-msg
24
+ npm install && npm run build && npm link
25
+ ```
26
+
27
+ Node 20 or newer. Installs `photon-msg` and `pmsg`.
28
+
29
+ ## Credentials
30
+
31
+ You need a project API key and the project ID it belongs to.
32
+
33
+ ```sh
34
+ photon login
35
+ photon project create --name my-agent --slug my-agent
36
+ photon project api-key create \
37
+ --name photon-msg \
38
+ --permission project:read \
39
+ --permission project:write \
40
+ --permission platforms:read \
41
+ --permission platforms:write
42
+ ```
43
+
44
+ ```sh
45
+ export PHOTON_API_KEY="pho_sk_..."
46
+ export PHOTON_PROJECT_ID="pho_prj_..."
47
+ ```
48
+
49
+ `platforms:read` and `platforms:write` are only needed for the `line` commands. Without them, `line list` returns 403.
50
+
51
+ `PHOTON_AGENT_HANDLE` sets a default sender, so you can leave out `--as`:
52
+
53
+ ```sh
54
+ export PHOTON_AGENT_HANDLE="+16285024649"
55
+ ```
56
+
57
+ Flags beat environment variables, which beat `~/.config/photon-msg/config.json`:
58
+
59
+ ```json
60
+ { "projectId": "pho_prj_...", "agentHandle": "+16285024649" }
61
+ ```
62
+
63
+ Keep keys out of that file. Use the environment or a secret manager.
64
+
65
+ Check everything resolves before you send:
66
+
67
+ ```sh
68
+ photon-msg doctor
69
+ ```
70
+
71
+ ## Getting a line to send from
72
+
73
+ Every message needs a sender that belongs to the project. For iMessage there are two kinds of line.
74
+
75
+ | | Pooled shared line | Dedicated line |
76
+ | --- | --- | --- |
77
+ | Cost | 25 seats on the free plan | Paid plan |
78
+ | Can start a conversation | No | Yes |
79
+ | Can reply once written to | Yes | Yes |
80
+
81
+ Assign a pooled line to the person you want to test with:
82
+
83
+ ```sh
84
+ photon-msg line assign --handle +14155550123
85
+ # line +16285024649 assigned for +14155550123
86
+ ```
87
+
88
+ A pooled line can only reply. If you try to send first, the API returns `403 SHARED_LINE_INBOUND_REQUIRED`, and the CLI prints what to do about it. Have the person text the line, then reply inside that conversation with `--space`. Starting conversations yourself needs a purchased SMS number (`photon project platform sms purchase`, $2/month) or a dedicated line.
89
+
90
+ ## Addressing
91
+
92
+ Two ways to say where a message goes.
93
+
94
+ ```sh
95
+ # Start or reuse a direct conversation. Repeat calls reuse the same space.
96
+ photon-msg send --to +14155550123 --as +16285024649 --text "hello"
97
+
98
+ # Reply inside an existing conversation.
99
+ photon-msg send --space pho_spc_... --as pho_usr_... --text "hi again"
100
+ ```
101
+
102
+ `--as` takes a handle or a `pho_usr_` ID. `--to` takes a phone number, an email address or a user ID. The platform defaults to iMessage, or to email when the handle contains `@`. Override it with `--platform`.
103
+
104
+ Get IDs from `photon-msg spaces list` and `photon-msg users list`.
105
+
106
+ ## Sending
107
+
108
+ Text:
109
+
110
+ ```sh
111
+ photon-msg send --to +14155550123 --as +16285024649 --text "hello"
112
+ photon-msg send --space pho_spc_... --as pho_usr_... --text-file ./body.txt
113
+ echo "from stdin" | photon-msg send --space pho_spc_... --as pho_usr_... --text-file -
114
+ ```
115
+
116
+ Images and files:
117
+
118
+ ```sh
119
+ photon-msg send --space pho_spc_... --as pho_usr_... --image ./photo.png --caption "look"
120
+ photon-msg send --space pho_spc_... --as pho_usr_... --file https://example.com/report.pdf
121
+ photon-msg send --space pho_spc_... --as pho_usr_... --voice ./memo.m4a
122
+ ```
123
+
124
+ Polls:
125
+
126
+ ```sh
127
+ photon-msg send --space pho_spc_... --as pho_usr_... \
128
+ --poll "Lunch?" --poll-option Yes --poll-option No --poll-mode single
129
+ ```
130
+
131
+ Reactions, edits, unsends and read receipts all need the message they act on:
132
+
133
+ ```sh
134
+ photon-msg send --space pho_spc_... --as pho_usr_... --reply-to pho_msg_... --reaction "❤️"
135
+ photon-msg send --space pho_spc_... --as pho_usr_... --reply-to pho_msg_... \
136
+ --reaction "❤️" --reaction-action remove
137
+ photon-msg send --space pho_spc_... --as pho_usr_... --edit pho_msg_... --text "fixed typo"
138
+ photon-msg send --space pho_spc_... --as pho_usr_... --delete pho_msg_... --reason "wrong chat"
139
+ photon-msg send --space pho_spc_... --as pho_usr_... --reply-to pho_msg_... --read
140
+ ```
141
+
142
+ Typing indicators, as content or through their own endpoint:
143
+
144
+ ```sh
145
+ photon-msg send --space pho_spc_... --as pho_usr_... --typing on
146
+ photon-msg typing --space pho_spc_... --as pho_usr_... --on
147
+ ```
148
+
149
+ Several parts as one message:
150
+
151
+ ```sh
152
+ photon-msg send --space pho_spc_... --as pho_usr_... --group \
153
+ --text "two photos" --image ./a.png --image ./b.png
154
+ ```
155
+
156
+ Email options, which are ignored on other platforms:
157
+
158
+ ```sh
159
+ photon-msg send --platform email --to you@example.com --as agent@yourdomain.com \
160
+ --text "report" --subject "Weekly" --cc teammate@example.com
161
+ ```
162
+
163
+ `--reply-to` threads a message under another one. `--idempotency-key` makes a retry part of the same logical send. `--dry-run` prints the request without sending it.
164
+
165
+ A successful send means the API queued the message. Delivery is reported later, so check the message if you need to know it arrived.
166
+
167
+ ## Reading
168
+
169
+ ```sh
170
+ photon-msg messages list --limit 20
171
+ photon-msg messages list --platform imessage --direction received --created-since 2026-11-01T00:00:00Z
172
+ photon-msg messages list --space pho_spc_... --order asc --page-size 50
173
+ photon-msg messages get pho_msg_...
174
+ photon-msg messages watch --platform imessage --interval 2
175
+ ```
176
+
177
+ `messages watch` prints one JSON object per line, so it pipes into `jq` or another process. It only reports messages created after it starts.
178
+
179
+ Conversations and people:
180
+
181
+ ```sh
182
+ photon-msg spaces list --platform imessage
183
+ photon-msg spaces get pho_spc_...
184
+ photon-msg spaces create --platform imessage --participant +16285024649 --participant +14155550123
185
+ photon-msg users list
186
+ photon-msg users ensure --handle +14155550123
187
+ ```
188
+
189
+ On iMessage the first `--participant` becomes the creator of the conversation, so put the agent first.
190
+
191
+ Attachments and lines:
192
+
193
+ ```sh
194
+ photon-msg attachments upload ./photo.png
195
+ photon-msg attachments get pho_att_...
196
+ photon-msg attachments download pho_att_... --out ./photo.png
197
+ photon-msg line list --include-released
198
+ photon-msg line get pho_asg_...
199
+ photon-msg line release pho_asg_...
200
+ ```
201
+
202
+ ## Output and exit codes
203
+
204
+ `--json` writes one JSON document to stdout and leaves stdout clean enough to pipe. Errors go to stderr, as JSON too when `--json` is set.
205
+
206
+ | Code | Meaning |
207
+ | --- | --- |
208
+ | 0 | Success |
209
+ | 1 | API or runtime failure |
210
+ | 2 | Usage error, including missing credentials |
211
+ | 3 | Authentication failed |
212
+ | 4 | Not found |
213
+ | 5 | Forbidden, including shared-line rules |
214
+ | 6 | Conflict, such as a reused idempotency key |
215
+
216
+ Problem codes the API returns carry a hint that says what to change.
217
+
218
+ ## As a library
219
+
220
+ The pieces are exported with types, so a script can skip the CLI.
221
+
222
+ ```ts
223
+ import { randomUUID } from "node:crypto";
224
+ import { createPhotonClient, buildMessage, resolveTarget } from "photon-msg";
225
+
226
+ const apiKey = process.env.PHOTON_API_KEY!;
227
+ const projectId = process.env.PHOTON_PROJECT_ID!;
228
+ const client = createPhotonClient({ apiKey });
229
+
230
+ const { content } = await buildMessage({
231
+ flags: { text: "hello" },
232
+ upload: async () => {
233
+ throw new Error("no attachments in this example");
234
+ },
235
+ getUsers: async () => [],
236
+ });
237
+
238
+ const target = await resolveTarget({
239
+ client,
240
+ projectId,
241
+ flags: { to: "+14155550123", as: "+16285024649" },
242
+ });
243
+
244
+ await client.projects.sendMessage({
245
+ path: { id: projectId },
246
+ headers: { "idempotency-key": randomUUID() },
247
+ body: { spaceId: target.spaceId, senderId: target.senderId, content },
248
+ });
249
+ ```
250
+
251
+ `buildMessage` returns the SDK's `SendContent` union, so a payload that compiles matches what the API accepts in shape.
252
+
253
+ ## What has been tested
254
+
255
+ Text sends and replies, message listing, attachment upload and download, and line assignment have all been run against a live project. The attachment round trip is byte-identical.
256
+
257
+ Not verified against a live project: SMS, email, WhatsApp and Telegram sends, and the poll, reaction, edit, unsend and participant commands. The code paths exist and the payloads typecheck, but the platforms differ in what they accept. Beta iMessage carries text and images only, so polls and reactions will fail there even though the API defines them.
258
+
259
+ There's no bulk send and no retry queue. One invocation, one message.
260
+
261
+ ## Development
262
+
263
+ ```sh
264
+ npm install
265
+ npm run typecheck
266
+ npm test
267
+ npm run build
268
+ ```
269
+
270
+ `npm test` compiles `src` and `test` and runs 65 tests. None of them need credentials or network access. CI runs the typecheck, the tests and a build smoke test on Node 20, 22 and 24.
271
+
272
+ TypeScript is strict, with `noUncheckedIndexedAccess` and `verbatimModuleSyntax`, on ESM with `NodeNext` resolution. Sources are in `src`, tests in `test`, and `dist` is built by `prepublishOnly`.
273
+
274
+ ## License
275
+
276
+ MIT
package/dist/args.d.ts ADDED
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Argument parsing built on node:util parseArgs, with a small routing layer for
3
+ * two-word commands such as `messages list`.
4
+ */
5
+ import { type ParseArgsOptionsConfig } from "node:util";
6
+ import type { Flags } from "./types.js";
7
+ /** Options every command accepts. */
8
+ export declare const GLOBAL_OPTIONS: {
9
+ readonly help: {
10
+ readonly type: "boolean";
11
+ readonly short: "h";
12
+ };
13
+ readonly version: {
14
+ readonly type: "boolean";
15
+ readonly short: "v";
16
+ };
17
+ readonly json: {
18
+ readonly type: "boolean";
19
+ };
20
+ readonly color: {
21
+ readonly type: "boolean";
22
+ };
23
+ readonly "api-key": {
24
+ readonly type: "string";
25
+ };
26
+ readonly project: {
27
+ readonly type: "string";
28
+ };
29
+ readonly "base-url": {
30
+ readonly type: "string";
31
+ };
32
+ readonly timeout: {
33
+ readonly type: "string";
34
+ };
35
+ readonly "dry-run": {
36
+ readonly type: "boolean";
37
+ };
38
+ readonly quiet: {
39
+ readonly type: "boolean";
40
+ readonly short: "q";
41
+ };
42
+ };
43
+ export interface ParsedArguments {
44
+ flags: Flags;
45
+ positionals: string[];
46
+ }
47
+ /** Parses arguments for one command, merging in the global options. */
48
+ export declare function parseOptions(argv: string[], options?: ParseArgsOptionsConfig): ParsedArguments;
49
+ export interface RouteMatch<T> {
50
+ name: keyof T | null;
51
+ rest: string[];
52
+ spec: T[keyof T] | null;
53
+ }
54
+ /**
55
+ * Splits argv into a command path and the remaining arguments, matching the
56
+ * longest registered route first so `messages get` wins over a bare `messages`.
57
+ */
58
+ export declare function route<T extends Record<string, unknown>>(argv: string[], routes: T): RouteMatch<T>;
59
+ /** True when the flag pair asks for help. */
60
+ export declare const wantsHelp: (flags: Flags) => boolean;
61
+ /** Coerces a flag that may be given once or many times into an array. */
62
+ export declare const toArray: (value: string | string[] | undefined) => string[];
63
+ //# sourceMappingURL=args.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"args.d.ts","sourceRoot":"","sources":["../src/args.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAa,KAAK,sBAAsB,EAAE,MAAM,WAAW,CAAC;AAEnE,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAExC,qCAAqC;AACrC,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAWgB,CAAC;AAE5C,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,KAAK,CAAC;IACb,WAAW,EAAE,MAAM,EAAE,CAAC;CACvB;AAED,uEAAuE;AACvE,wBAAgB,YAAY,CAC1B,IAAI,EAAE,MAAM,EAAE,EACd,OAAO,GAAE,sBAA2B,GACnC,eAAe,CAmBjB;AAED,MAAM,WAAW,UAAU,CAAC,CAAC;IAC3B,IAAI,EAAE,MAAM,CAAC,GAAG,IAAI,CAAC;IACrB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,IAAI,CAAC;CACzB;AAED;;;GAGG;AACH,wBAAgB,KAAK,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC,CAejG;AAED,6CAA6C;AAC7C,eAAO,MAAM,SAAS,GAAI,OAAO,KAAK,KAAG,OAA8B,CAAC;AAExE,yEAAyE;AACzE,eAAO,MAAM,OAAO,GAAI,OAAO,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,KAAG,MAAM,EACF,CAAC"}
package/dist/args.js ADDED
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Argument parsing built on node:util parseArgs, with a small routing layer for
3
+ * two-word commands such as `messages list`.
4
+ */
5
+ import { parseArgs } from "node:util";
6
+ import { usageError } from "./errors.js";
7
+ /** Options every command accepts. */
8
+ export const GLOBAL_OPTIONS = {
9
+ help: { type: "boolean", short: "h" },
10
+ version: { type: "boolean", short: "v" },
11
+ json: { type: "boolean" },
12
+ color: { type: "boolean" },
13
+ "api-key": { type: "string" },
14
+ project: { type: "string" },
15
+ "base-url": { type: "string" },
16
+ timeout: { type: "string" },
17
+ "dry-run": { type: "boolean" },
18
+ quiet: { type: "boolean", short: "q" },
19
+ };
20
+ /** Parses arguments for one command, merging in the global options. */
21
+ export function parseOptions(argv, options = {}) {
22
+ // `allowNegative` only exists from Node 22.4, and this package supports Node
23
+ // 20, so `--no-color` is translated here instead of relying on it.
24
+ const negated = argv.includes("--no-color");
25
+ const args = negated ? argv.filter((token) => token !== "--no-color") : argv;
26
+ try {
27
+ const { values, positionals } = parseArgs({
28
+ args,
29
+ options: { ...GLOBAL_OPTIONS, ...options },
30
+ allowPositionals: true,
31
+ strict: true,
32
+ });
33
+ const flags = values;
34
+ if (negated)
35
+ flags.color = false;
36
+ return { flags, positionals };
37
+ }
38
+ catch (error) {
39
+ throw usageError(error.message.replace(/^TypeError: /, ""));
40
+ }
41
+ }
42
+ /**
43
+ * Splits argv into a command path and the remaining arguments, matching the
44
+ * longest registered route first so `messages get` wins over a bare `messages`.
45
+ */
46
+ export function route(argv, routes) {
47
+ for (const size of [2, 1]) {
48
+ const head = argv.slice(0, size);
49
+ if (head.length !== size)
50
+ continue;
51
+ if (head.some((token) => token.startsWith("-")))
52
+ continue;
53
+ const name = head.join(" ");
54
+ if (Object.hasOwn(routes, name)) {
55
+ return {
56
+ name: name,
57
+ rest: argv.slice(size),
58
+ spec: routes[name],
59
+ };
60
+ }
61
+ }
62
+ return { name: null, rest: argv, spec: null };
63
+ }
64
+ /** True when the flag pair asks for help. */
65
+ export const wantsHelp = (flags) => flags.help === true;
66
+ /** Coerces a flag that may be given once or many times into an array. */
67
+ export const toArray = (value) => value === undefined ? [] : Array.isArray(value) ? value : [value];
68
+ //# sourceMappingURL=args.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"args.js","sourceRoot":"","sources":["../src/args.ts"],"names":[],"mappings":"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"}
package/dist/bin.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=bin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bin.d.ts","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":""}
package/dist/bin.js ADDED
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env node
2
+ import { main } from "./cli.js";
3
+ main(process.argv.slice(2)).then((code) => {
4
+ process.exitCode = code;
5
+ }, (error) => {
6
+ console.error(error);
7
+ process.exitCode = 1;
8
+ });
9
+ //# sourceMappingURL=bin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bin.js","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC;AAEhC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAC9B,CAAC,IAAI,EAAE,EAAE;IACP,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC;AAC1B,CAAC,EACD,CAAC,KAAc,EAAE,EAAE;IACjB,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IACrB,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;AACvB,CAAC,CACF,CAAC"}
package/dist/cli.d.ts ADDED
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Command router, context construction, and top-level error handling.
3
+ */
4
+ import { CliError } from "./errors.js";
5
+ import type { Command } from "./types.js";
6
+ export declare const ROUTES: Record<string, Command>;
7
+ /** Runs the CLI and resolves to a process exit code. */
8
+ export declare function main(argv?: string[]): Promise<number>;
9
+ /** True when the CLI has a route for the given name. */
10
+ export declare const hasRoute: (name: string) => boolean;
11
+ export { CliError };
12
+ export declare const commands: string[];
13
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAAA;;GAEG;AAOH,OAAO,EAAE,QAAQ,EAA8C,MAAM,aAAa,CAAC;AAGnF,OAAO,KAAK,EAAE,OAAO,EAAmC,MAAM,YAAY,CAAC;AAW3E,eAAO,MAAM,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAqB1C,CAAC;AAsBF,wDAAwD;AACxD,wBAAsB,IAAI,CAAC,IAAI,GAAE,MAAM,EAA0B,GAAG,OAAO,CAAC,MAAM,CAAC,CAiHlF;AAED,wDAAwD;AACxD,eAAO,MAAM,QAAQ,GAAI,MAAM,MAAM,KAAG,OAAsC,CAAC;AAE/E,OAAO,EAAE,QAAQ,EAAE,CAAC;AACpB,eAAO,MAAM,QAAQ,EAAE,MAAM,EAAwB,CAAC"}
package/dist/cli.js ADDED
@@ -0,0 +1,163 @@
1
+ /**
2
+ * Command router, context construction, and top-level error handling.
3
+ */
4
+ import { parseOptions, route, wantsHelp } from "./args.js";
5
+ import { resolveConfig, requireCredentials } from "./config.js";
6
+ import { createOfflineClient, createPhotonClient } from "./client.js";
7
+ import { createUploader } from "./upload.js";
8
+ import { createOutput } from "./output.js";
9
+ import { CliError, EXIT, errorPayload, toCliError, usageError } from "./errors.js";
10
+ import { COMMAND_HELP, MAIN_HELP, VERSION } from "./help.js";
11
+ import { validateContentFlags } from "./content.js";
12
+ import * as send from "./commands/send.js";
13
+ import * as messages from "./commands/messages.js";
14
+ import * as spaces from "./commands/spaces.js";
15
+ import * as users from "./commands/users.js";
16
+ import * as attachments from "./commands/attachments.js";
17
+ import * as misc from "./commands/misc.js";
18
+ import * as line from "./commands/line.js";
19
+ import * as doctor from "./commands/doctor.js";
20
+ export const ROUTES = {
21
+ send: { options: send.options, run: send.run },
22
+ "messages list": { options: messages.list.options, run: messages.list.run },
23
+ "messages get": { options: messages.get.options, run: messages.get.run },
24
+ "messages watch": { options: messages.watch.options, run: messages.watch.run },
25
+ "spaces list": { options: spaces.list.options, run: spaces.list.run },
26
+ "spaces get": { options: spaces.get.options, run: spaces.get.run },
27
+ "spaces create": { options: spaces.create.options, run: spaces.create.run },
28
+ "users list": { options: users.list.options, run: users.list.run },
29
+ "users get": { options: users.get.options, run: users.get.run },
30
+ "users ensure": { options: users.ensure.options, run: users.ensure.run },
31
+ "attachments upload": { options: attachments.upload.options, run: attachments.upload.run },
32
+ "attachments get": { options: attachments.get.options, run: attachments.get.run },
33
+ "attachments download": { options: attachments.download.options, run: attachments.download.run },
34
+ typing: { options: misc.typing.options, run: misc.typing.run },
35
+ read: { options: misc.read.options, run: misc.read.run },
36
+ "line assign": { options: line.assign.options, run: line.assign.run },
37
+ "line list": { options: line.list.options, run: line.list.run },
38
+ "line get": { options: line.get.options, run: line.get.run },
39
+ "line release": { options: line.release.options, run: line.release.run },
40
+ doctor: { options: doctor.options, run: doctor.run },
41
+ };
42
+ /** Commands that fully handle --dry-run without touching the network. */
43
+ const DRY_RUN_SAFE = new Set([
44
+ "send",
45
+ "spaces create",
46
+ "users ensure",
47
+ "typing",
48
+ "read",
49
+ "line assign",
50
+ "line release",
51
+ "attachments upload",
52
+ ]);
53
+ const colorEnabled = () => Boolean(process.stdout.isTTY) && process.env.NO_COLOR === undefined;
54
+ /** Builds the credential-free context used by --dry-run. */
55
+ const offlineUploader = () => () => {
56
+ throw usageError("--dry-run cannot upload attachments.");
57
+ };
58
+ /** Runs the CLI and resolves to a process exit code. */
59
+ export async function main(argv = process.argv.slice(2)) {
60
+ // Help and version short-circuit before any credential work.
61
+ if (argv.length === 0) {
62
+ process.stdout.write(MAIN_HELP);
63
+ return EXIT.OK;
64
+ }
65
+ const first = argv[0];
66
+ if (first === "--help" || first === "-h" || first === "help") {
67
+ process.stdout.write(MAIN_HELP);
68
+ return EXIT.OK;
69
+ }
70
+ if (first === "--version" || first === "-v") {
71
+ process.stdout.write(`${VERSION}\n`);
72
+ return EXIT.OK;
73
+ }
74
+ const routed = route(argv, ROUTES);
75
+ const commandName = routed.name;
76
+ if (commandName === null || routed.spec === null) {
77
+ const output = createOutput({ json: argv.includes("--json"), color: colorEnabled() });
78
+ const cliError = usageError(`Unknown command: ${argv.filter((token) => !token.startsWith("-")).slice(0, 2).join(" ") || argv[0]}`, "Run `photon-msg --help` to see the available commands.");
79
+ output.error(cliError);
80
+ return cliError.exitCode;
81
+ }
82
+ const spec = routed.spec;
83
+ let flags;
84
+ let positionals;
85
+ try {
86
+ ({ flags, positionals } = parseOptions(routed.rest, spec.options));
87
+ }
88
+ catch (error) {
89
+ const cliError = toCliError(error);
90
+ createOutput({ color: colorEnabled() }).error(cliError);
91
+ return cliError.exitCode;
92
+ }
93
+ const json = flags.json === true;
94
+ if (wantsHelp(flags)) {
95
+ process.stdout.write(COMMAND_HELP[commandName] ?? MAIN_HELP);
96
+ return EXIT.OK;
97
+ }
98
+ if (flags.version === true) {
99
+ process.stdout.write(`${VERSION}\n`);
100
+ return EXIT.OK;
101
+ }
102
+ const out = createOutput({ json, color: colorEnabled() && flags.color !== false });
103
+ const dryRun = flags["dry-run"] === true;
104
+ try {
105
+ if (dryRun && !DRY_RUN_SAFE.has(commandName)) {
106
+ throw usageError(`--dry-run is not supported for "${commandName}".`, `Supported for: ${[...DRY_RUN_SAFE].join(", ")}.`);
107
+ }
108
+ // Validate contradictory flags before demanding credentials: a typo should
109
+ // not look like an auth problem.
110
+ if (commandName === "send")
111
+ validateContentFlags(flags);
112
+ const config = resolveConfig({ flags });
113
+ // Dry runs stay offline; requireCredentials is only enforced when the
114
+ // command will actually talk to the API.
115
+ const creds = dryRun ? null : requireCredentials(config);
116
+ const projectId = creds?.projectId ?? config.projectId ?? "(no project)";
117
+ const client = creds
118
+ ? createPhotonClient({
119
+ apiKey: creds.apiKey,
120
+ ...(config.baseUrl ? { baseUrl: config.baseUrl } : {}),
121
+ ...(config.timeoutMs ? { timeoutMs: config.timeoutMs } : {}),
122
+ })
123
+ : createOfflineClient();
124
+ const upload = creds
125
+ ? createUploader({
126
+ apiKey: creds.apiKey,
127
+ projectId: creds.projectId,
128
+ ...(config.baseUrl ? { baseUrl: config.baseUrl } : {}),
129
+ ...(config.timeoutMs ? { timeoutMs: config.timeoutMs } : {}),
130
+ })
131
+ : offlineUploader();
132
+ const context = {
133
+ flags,
134
+ positionals,
135
+ config,
136
+ projectId,
137
+ client,
138
+ upload,
139
+ out,
140
+ dryRun,
141
+ };
142
+ await spec.run(context);
143
+ return EXIT.OK;
144
+ }
145
+ catch (error) {
146
+ const cliError = toCliError(error);
147
+ if (json) {
148
+ process.stderr.write(`${JSON.stringify(errorPayload(cliError), null, 2)}\n`);
149
+ }
150
+ else {
151
+ out.error(cliError);
152
+ }
153
+ if (process.env.PHOTON_MSG_DEBUG) {
154
+ process.stderr.write(`${cliError.stack ?? ""}\n`);
155
+ }
156
+ return cliError.exitCode;
157
+ }
158
+ }
159
+ /** True when the CLI has a route for the given name. */
160
+ export const hasRoute = (name) => Object.hasOwn(ROUTES, name);
161
+ export { CliError };
162
+ export const commands = Object.keys(ROUTES);
163
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,YAAY,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAC3D,OAAO,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAChE,OAAO,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACtE,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACnF,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC7D,OAAO,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAGpD,OAAO,KAAK,IAAI,MAAM,oBAAoB,CAAC;AAC3C,OAAO,KAAK,QAAQ,MAAM,wBAAwB,CAAC;AACnD,OAAO,KAAK,MAAM,MAAM,sBAAsB,CAAC;AAC/C,OAAO,KAAK,KAAK,MAAM,qBAAqB,CAAC;AAC7C,OAAO,KAAK,WAAW,MAAM,2BAA2B,CAAC;AACzD,OAAO,KAAK,IAAI,MAAM,oBAAoB,CAAC;AAC3C,OAAO,KAAK,IAAI,MAAM,oBAAoB,CAAC;AAC3C,OAAO,KAAK,MAAM,MAAM,sBAAsB,CAAC;AAE/C,MAAM,CAAC,MAAM,MAAM,GAA4B;IAC7C,IAAI,EAAE,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE;IAC9C,eAAe,EAAE,EAAE,OAAO,EAAE,QAAQ,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE,QAAQ,CAAC,IAAI,CAAC,GAAG,EAAE;IAC3E,cAAc,EAAE,EAAE,OAAO,EAAE,QAAQ,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,EAAE,QAAQ,CAAC,GAAG,CAAC,GAAG,EAAE;IACxE,gBAAgB,EAAE,EAAE,OAAO,EAAE,QAAQ,CAAC,KAAK,CAAC,OAAO,EAAE,GAAG,EAAE,QAAQ,CAAC,KAAK,CAAC,GAAG,EAAE;IAC9E,aAAa,EAAE,EAAE,OAAO,EAAE,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE;IACrE,YAAY,EAAE,EAAE,OAAO,EAAE,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,EAAE,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE;IAClE,eAAe,EAAE,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,GAAG,EAAE,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE;IAC3E,YAAY,EAAE,EAAE,OAAO,EAAE,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE;IAClE,WAAW,EAAE,EAAE,OAAO,EAAE,KAAK,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,EAAE,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE;IAC/D,cAAc,EAAE,EAAE,OAAO,EAAE,KAAK,CAAC,MAAM,CAAC,OAAO,EAAE,GAAG,EAAE,KAAK,CAAC,MAAM,CAAC,GAAG,EAAE;IACxE,oBAAoB,EAAE,EAAE,OAAO,EAAE,WAAW,CAAC,MAAM,CAAC,OAAO,EAAE,GAAG,EAAE,WAAW,CAAC,MAAM,CAAC,GAAG,EAAE;IAC1F,iBAAiB,EAAE,EAAE,OAAO,EAAE,WAAW,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,EAAE,WAAW,CAAC,GAAG,CAAC,GAAG,EAAE;IACjF,sBAAsB,EAAE,EAAE,OAAO,EAAE,WAAW,CAAC,QAAQ,CAAC,OAAO,EAAE,GAAG,EAAE,WAAW,CAAC,QAAQ,CAAC,GAAG,EAAE;IAChG,MAAM,EAAE,EAAE,OAAO,EAAE,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,GAAG,EAAE,IAAI,CAAC,MAAM,CAAC,GAAG,EAAE;IAC9D,IAAI,EAAE,EAAE,OAAO,EAAE,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE;IACxD,aAAa,EAAE,EAAE,OAAO,EAAE,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,GAAG,EAAE,IAAI,CAAC,MAAM,CAAC,GAAG,EAAE;IACrE,WAAW,EAAE,EAAE,OAAO,EAAE,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE;IAC/D,UAAU,EAAE,EAAE,OAAO,EAAE,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE;IAC5D,cAAc,EAAE,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,GAAG,EAAE,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE;IACxE,MAAM,EAAE,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,GAAG,EAAE,MAAM,CAAC,GAAG,EAAE;CACrD,CAAC;AAEF,yEAAyE;AACzE,MAAM,YAAY,GAAwB,IAAI,GAAG,CAAC;IAChD,MAAM;IACN,eAAe;IACf,cAAc;IACd,QAAQ;IACR,MAAM;IACN,aAAa;IACb,cAAc;IACd,oBAAoB;CACrB,CAAC,CAAC;AAEH,MAAM,YAAY,GAAG,GAAY,EAAE,CACjC,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,SAAS,CAAC;AAEtE,4DAA4D;AAC5D,MAAM,eAAe,GAAG,GAAa,EAAE,CAAC,GAAG,EAAE;IAC3C,MAAM,UAAU,CAAC,sCAAsC,CAAC,CAAC;AAC3D,CAAC,CAAC;AAEF,wDAAwD;AACxD,MAAM,CAAC,KAAK,UAAU,IAAI,CAAC,OAAiB,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;IAC/D,6DAA6D;IAC7D,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACtB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;QAChC,OAAO,IAAI,CAAC,EAAE,CAAC;IACjB,CAAC;IAED,MAAM,KAAK,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;IACtB,IAAI,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,MAAM,EAAE,CAAC;QAC7D,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;QAChC,OAAO,IAAI,CAAC,EAAE,CAAC;IACjB,CAAC;IACD,IAAI,KAAK,KAAK,WAAW,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;QAC5C,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,OAAO,IAAI,CAAC,CAAC;QACrC,OAAO,IAAI,CAAC,EAAE,CAAC;IACjB,CAAC;IAED,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACnC,MAAM,WAAW,GAAG,MAAM,CAAC,IAAqB,CAAC;IAEjD,IAAI,WAAW,KAAK,IAAI,IAAI,MAAM,CAAC,IAAI,KAAK,IAAI,EAAE,CAAC;QACjD,MAAM,MAAM,GAAG,YAAY,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,KAAK,EAAE,YAAY,EAAE,EAAE,CAAC,CAAC;QACtF,MAAM,QAAQ,GAAG,UAAU,CACzB,oBAAoB,IAAI,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,EAAE,EACrG,wDAAwD,CACzD,CAAC;QACF,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QACvB,OAAO,QAAQ,CAAC,QAAQ,CAAC;IAC3B,CAAC;IAED,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC;IAEzB,IAAI,KAAY,CAAC;IACjB,IAAI,WAAqB,CAAC;IAC1B,IAAI,CAAC;QACH,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,GAAG,YAAY,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC;IACrE,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,QAAQ,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC;QACnC,YAAY,CAAC,EAAE,KAAK,EAAE,YAAY,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QACxD,OAAO,QAAQ,CAAC,QAAQ,CAAC;IAC3B,CAAC;IAED,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC;IAEjC,IAAI,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC;QACrB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,YAAY,CAAC,WAAW,CAAC,IAAI,SAAS,CAAC,CAAC;QAC7D,OAAO,IAAI,CAAC,EAAE,CAAC;IACjB,CAAC;IACD,IAAI,KAAK,CAAC,OAAO,KAAK,IAAI,EAAE,CAAC;QAC3B,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,OAAO,IAAI,CAAC,CAAC;QACrC,OAAO,IAAI,CAAC,EAAE,CAAC;IACjB,CAAC;IAED,MAAM,GAAG,GAAG,YAAY,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,YAAY,EAAE,IAAI,KAAK,CAAC,KAAK,KAAK,KAAK,EAAE,CAAC,CAAC;IACnF,MAAM,MAAM,GAAG,KAAK,CAAC,SAAS,CAAC,KAAK,IAAI,CAAC;IAEzC,IAAI,CAAC;QACH,IAAI,MAAM,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,WAAW,CAAC,EAAE,CAAC;YAC7C,MAAM,UAAU,CAAC,mCAAmC,WAAW,IAAI,EAAE,kBAAkB,CAAC,GAAG,YAAY,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC1H,CAAC;QAED,2EAA2E;QAC3E,iCAAiC;QACjC,IAAI,WAAW,KAAK,MAAM;YAAE,oBAAoB,CAAC,KAAK,CAAC,CAAC;QAExD,MAAM,MAAM,GAAG,aAAa,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;QACxC,sEAAsE;QACtE,yCAAyC;QACzC,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,kBAAkB,CAAC,MAAM,CAAC,CAAC;QACzD,MAAM,SAAS,GAAG,KAAK,EAAE,SAAS,IAAI,MAAM,CAAC,SAAS,IAAI,cAAc,CAAC;QAEzE,MAAM,MAAM,GAAG,KAAK;YAClB,CAAC,CAAC,kBAAkB,CAAC;gBACjB,MAAM,EAAE,KAAK,CAAC,MAAM;gBACpB,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBACtD,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aAC7D,CAAC;YACJ,CAAC,CAAC,mBAAmB,EAAE,CAAC;QAE1B,MAAM,MAAM,GAAa,KAAK;YAC5B,CAAC,CAAC,cAAc,CAAC;gBACb,MAAM,EAAE,KAAK,CAAC,MAAM;gBACpB,SAAS,EAAE,KAAK,CAAC,SAAS;gBAC1B,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBACtD,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aAC7D,CAAC;YACJ,CAAC,CAAC,eAAe,EAAE,CAAC;QAEtB,MAAM,OAAO,GAAmB;YAC9B,KAAK;YACL,WAAW;YACX,MAAM;YACN,SAAS;YACT,MAAM;YACN,MAAM;YACN,GAAG;YACH,MAAM;SACP,CAAC;QAEF,MAAM,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QACxB,OAAO,IAAI,CAAC,EAAE,CAAC;IACjB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,QAAQ,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC;QACnC,IAAI,IAAI,EAAE,CAAC;YACT,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,QAAQ,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;QAC/E,CAAC;aAAM,CAAC;YACN,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QACtB,CAAC;QACD,IAAI,OAAO,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;YACjC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,QAAQ,CAAC,KAAK,IAAI,EAAE,IAAI,CAAC,CAAC;QACpD,CAAC;QACD,OAAO,QAAQ,CAAC,QAAQ,CAAC;IAC3B,CAAC;AACH,CAAC;AAED,wDAAwD;AACxD,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,IAAY,EAAW,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;AAE/E,OAAO,EAAE,QAAQ,EAAE,CAAC;AACpB,MAAM,CAAC,MAAM,QAAQ,GAAa,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC"}