@curviate/cli 0.5.0 → 0.6.1

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.
@@ -1,17 +1,21 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ normalizeChatId
4
+ } from "./chunk-SZB3CFRZ.js";
2
5
  import {
3
6
  streamAll
4
- } from "./chunk-SND3NHCT.js";
7
+ } from "./chunk-GXTZION6.js";
5
8
  import {
6
9
  createClient,
7
10
  renderError,
8
11
  renderSuccess,
9
12
  renderUnexpectedError,
10
13
  resolveEffectiveConfig
11
- } from "./chunk-U6ACUNLU.js";
14
+ } from "./chunk-47SYFQRF.js";
12
15
  import {
13
- GLOBAL_FLAGS
14
- } from "./chunk-52RZLSWP.js";
16
+ GLOBAL_FLAGS,
17
+ READ_SINGLE_FLAGS
18
+ } from "./chunk-TDYIQHQX.js";
15
19
 
16
20
  // src/commands/inbox.ts
17
21
  import { defineCommand } from "citty";
@@ -53,6 +57,15 @@ function buildPaginationParams(flags) {
53
57
  if (flags.cursor) params["cursor"] = flags.cursor;
54
58
  return params;
55
59
  }
60
+ function validateIsoZTimestamp(value, flagName, out) {
61
+ if (Number.isNaN(new Date(value).getTime()) || !value.endsWith("Z")) {
62
+ out.stderr.write(
63
+ `error: --${flagName}: must be a UTC ISO-8601 timestamp ending in 'Z' (e.g. 2025-01-01T00:00:00Z).
64
+ `
65
+ );
66
+ process.exit(2);
67
+ }
68
+ }
56
69
  async function handleSdkError(err, outOpts, out) {
57
70
  const { CurviateError } = await import("@curviate/sdk");
58
71
  if (err instanceof CurviateError) {
@@ -71,12 +84,16 @@ async function runInboxList(client, flags, out) {
71
84
  const all = flags.all ?? false;
72
85
  const maxPages = flags["max-pages"] ? parseInt(flags["max-pages"], 10) : 100;
73
86
  const params = buildPaginationParams(flags);
87
+ if (flags.unread !== void 0) {
88
+ params.unread = flags.unread;
89
+ }
74
90
  try {
75
91
  if (all) {
76
92
  const fn = (p) => ns.messaging.listChats(p);
77
93
  for await (const item of streamAll(fn, params, {
78
94
  maxPages,
79
- onTruncated: (msg) => out.stderr.write(msg + "\n")
95
+ onTruncated: (n) => out.stderr.write(`Streaming truncated at ${n} page(s). Use --all --max-pages or --cursor for manual paging.
96
+ `)
80
97
  })) {
81
98
  out.stdout.write(JSON.stringify(item) + "\n");
82
99
  }
@@ -92,7 +109,7 @@ async function runInboxGet(client, flags, out) {
92
109
  rejectPreviewOnRead(flags.preview, out);
93
110
  rejectAllOnNonPaginated(flags.all, out);
94
111
  const accountId = requireAccount(flags.account, out);
95
- const chatId = flags.chatId ?? "";
112
+ const chatId = normalizeChatId(flags.chatId ?? "");
96
113
  const ns = client.account(accountId);
97
114
  const outOpts = resolveOutputOpts(flags);
98
115
  try {
@@ -105,18 +122,27 @@ async function runInboxGet(client, flags, out) {
105
122
  async function runInboxMessages(client, flags, out) {
106
123
  rejectPreviewOnRead(flags.preview, out);
107
124
  const accountId = requireAccount(flags.account, out);
108
- const chatId = flags.chatId ?? "";
125
+ const chatId = normalizeChatId(flags.chatId ?? "");
109
126
  const ns = client.account(accountId);
110
127
  const outOpts = resolveOutputOpts(flags);
111
128
  const all = flags.all ?? false;
112
129
  const maxPages = flags["max-pages"] ? parseInt(flags["max-pages"], 10) : 100;
113
130
  const params = buildPaginationParams(flags);
131
+ if (flags.before !== void 0) {
132
+ validateIsoZTimestamp(flags.before, "before", out);
133
+ params.before = flags.before;
134
+ }
135
+ if (flags.after !== void 0) {
136
+ validateIsoZTimestamp(flags.after, "after", out);
137
+ params.after = flags.after;
138
+ }
114
139
  try {
115
140
  if (all) {
116
141
  const fn = (p) => ns.messaging.listMessages(chatId, p);
117
142
  for await (const item of streamAll(fn, params, {
118
143
  maxPages,
119
- onTruncated: (msg) => out.stderr.write(msg + "\n")
144
+ onTruncated: (n) => out.stderr.write(`Streaming truncated at ${n} page(s). Use --all --max-pages or --cursor for manual paging.
145
+ `)
120
146
  })) {
121
147
  out.stdout.write(JSON.stringify(item) + "\n");
122
148
  }
@@ -141,23 +167,58 @@ async function runInboxSync(client, flags, out) {
141
167
  await handleSdkError(err, outOpts, out);
142
168
  }
143
169
  }
144
- async function runInboxSyncChat(client, flags, out) {
170
+ var SYNC_TERMINAL_STATUSES = /* @__PURE__ */ new Set(["done", "error", "chat_deleted"]);
171
+ var SYNC_POLL_INTERVAL_MS = 2e3;
172
+ async function runInboxSyncChat(client, flags, out, _sleep) {
145
173
  rejectPreviewOnRead(flags.preview, out);
146
174
  rejectAllOnNonPaginated(flags.all, out);
147
175
  const accountId = requireAccount(flags.account, out);
148
- const chatId = flags.chatId ?? "";
176
+ const chatId = normalizeChatId(flags.chatId ?? "");
149
177
  const ns = client.account(accountId);
150
178
  const outOpts = resolveOutputOpts(flags);
151
- try {
152
- const result = await ns.messaging.syncChat(chatId);
153
- renderSuccess(result, outOpts, out);
154
- } catch (err) {
155
- await handleSdkError(err, outOpts, out);
179
+ if (!flags.wait) {
180
+ try {
181
+ const result = await ns.messaging.syncChat(chatId);
182
+ renderSuccess(result, outOpts, out);
183
+ } catch (err) {
184
+ await handleSdkError(err, outOpts, out);
185
+ }
186
+ return;
187
+ }
188
+ const sleep = _sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
189
+ const timeoutSecs = parseInt(flags.timeout ?? "30", 10);
190
+ const timeoutMs = (Number.isNaN(timeoutSecs) ? 30 : timeoutSecs) * 1e3;
191
+ const startTime = Date.now();
192
+ while (true) {
193
+ let result;
194
+ try {
195
+ result = await ns.messaging.syncChat(chatId);
196
+ } catch (err) {
197
+ await handleSdkError(err, outOpts, out);
198
+ }
199
+ const resp = result;
200
+ if (resp.status !== void 0 && SYNC_TERMINAL_STATUSES.has(resp.status)) {
201
+ renderSuccess(result, outOpts, out);
202
+ return;
203
+ }
204
+ if (Date.now() - startTime >= timeoutMs) {
205
+ renderSuccess(result, outOpts, out);
206
+ process.exit(3);
207
+ return;
208
+ }
209
+ await sleep(SYNC_POLL_INTERVAL_MS);
156
210
  }
157
211
  }
158
212
  var inboxListCommand = defineCommand({
159
213
  meta: { name: "list", description: "List inbox chats." },
160
- args: { ...GLOBAL_FLAGS },
214
+ args: {
215
+ ...GLOBAL_FLAGS,
216
+ unread: {
217
+ type: "boolean",
218
+ description: "Show unread chats only (--no-unread for read-only; omit for all)."
219
+ // No default → three-way semantics: undefined when omitted, true for --unread, false for --no-unread
220
+ }
221
+ },
161
222
  async run({ args }) {
162
223
  const flags = args;
163
224
  const cfg = await resolveEffectiveConfig({
@@ -179,7 +240,8 @@ var inboxListCommand = defineCommand({
179
240
  var inboxGetCommand = defineCommand({
180
241
  meta: { name: "get", description: "Get details of a single chat." },
181
242
  args: {
182
- ...GLOBAL_FLAGS,
243
+ // Single-object read: READ_SINGLE_FLAGS omits pagination flags, keeps --fields
244
+ ...READ_SINGLE_FLAGS,
183
245
  chatId: { type: "positional", description: "Chat ID." }
184
246
  },
185
247
  async run({ args }) {
@@ -204,7 +266,15 @@ var inboxMessagesCommand = defineCommand({
204
266
  meta: { name: "messages", description: "List messages in a chat." },
205
267
  args: {
206
268
  ...GLOBAL_FLAGS,
207
- chatId: { type: "positional", description: "Chat ID." }
269
+ chatId: { type: "positional", description: "Chat ID." },
270
+ before: {
271
+ type: "string",
272
+ description: "Return messages before this timestamp (ISO-8601, UTC \u2014 Z suffix required, e.g. 2025-01-01T00:00:00Z)."
273
+ },
274
+ after: {
275
+ type: "string",
276
+ description: "Return messages after this timestamp (ISO-8601, UTC \u2014 Z suffix required, e.g. 2025-01-01T00:00:00Z)."
277
+ }
208
278
  },
209
279
  async run({ args }) {
210
280
  const flags = args;
@@ -226,7 +296,10 @@ var inboxMessagesCommand = defineCommand({
226
296
  });
227
297
  var inboxSyncCommand = defineCommand({
228
298
  meta: { name: "sync", description: "Re-sync account message history." },
229
- args: { ...GLOBAL_FLAGS },
299
+ args: {
300
+ // Single-object read: READ_SINGLE_FLAGS omits pagination flags, keeps --fields
301
+ ...READ_SINGLE_FLAGS
302
+ },
230
303
  async run({ args }) {
231
304
  const flags = args;
232
305
  const cfg = await resolveEffectiveConfig({
@@ -248,15 +321,27 @@ var inboxSyncCommand = defineCommand({
248
321
  var inboxSyncChatCommand = defineCommand({
249
322
  meta: { name: "sync-chat", description: "Re-sync a specific chat's message history." },
250
323
  args: {
251
- ...GLOBAL_FLAGS,
252
- chatId: { type: "positional", description: "Chat ID." }
324
+ // Single-object read: READ_SINGLE_FLAGS omits pagination flags, keeps --fields.
325
+ // The timeout below overrides READ_SINGLE_FLAGS.timeout with a command-specific description.
326
+ ...READ_SINGLE_FLAGS,
327
+ chatId: { type: "positional", description: "Chat ID." },
328
+ wait: {
329
+ type: "boolean",
330
+ description: "Poll until sync completes (or --timeout elapses).",
331
+ default: false
332
+ },
333
+ // Override READ_SINGLE_FLAGS.timeout: here --timeout is the polling wait timeout
334
+ // in seconds (default: 30), not the SDK request timeout.
335
+ timeout: {
336
+ type: "string",
337
+ description: "Polling timeout in seconds (default: 30, requires --wait)."
338
+ }
253
339
  },
254
340
  async run({ args }) {
255
341
  const flags = args;
256
342
  const cfg = await resolveEffectiveConfig({
257
343
  apiKey: flags["api-key"],
258
344
  baseUrl: flags["base-url"],
259
- timeout: flags.timeout,
260
345
  account: flags.account,
261
346
  profile: flags.profile
262
347
  });
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  GLOBAL_FLAGS,
4
4
  writeProfile
5
- } from "./chunk-52RZLSWP.js";
5
+ } from "./chunk-TDYIQHQX.js";
6
6
 
7
7
  // src/commands/login.ts
8
8
  import { defineCommand } from "citty";
@@ -7,22 +7,27 @@ import {
7
7
  AttachError,
8
8
  readAttachment
9
9
  } from "./chunk-Q43HZUN3.js";
10
+ import {
11
+ resolveTextOrStdin
12
+ } from "./chunk-CHFKVAEI.js";
10
13
  import {
11
14
  buildPreviewOutput
12
15
  } from "./chunk-R3VLWLVV.js";
13
16
  import {
17
+ normalizeChatId,
14
18
  resolveIdentifier
15
- } from "./chunk-BNUTM6KD.js";
19
+ } from "./chunk-SZB3CFRZ.js";
16
20
  import {
17
21
  createClient,
18
22
  renderError,
19
23
  renderSuccess,
20
24
  renderUnexpectedError,
21
25
  resolveEffectiveConfig
22
- } from "./chunk-U6ACUNLU.js";
26
+ } from "./chunk-47SYFQRF.js";
23
27
  import {
24
- GLOBAL_FLAGS
25
- } from "./chunk-52RZLSWP.js";
28
+ READ_SINGLE_FLAGS,
29
+ WRITE_FLAGS
30
+ } from "./chunk-TDYIQHQX.js";
26
31
 
27
32
  // src/commands/message.ts
28
33
  import { defineCommand } from "citty";
@@ -64,7 +69,7 @@ function normalizeAttachPaths(attach) {
64
69
  }
65
70
  var INMAIL_SURFACES = ["sales_nav", "recruiter", "classic"];
66
71
  var MEMBER_URN_RE = /^urn:li:member:\d+$/;
67
- var MEMBER_PROVIDER_ID_RE = /^A[CDE][A-Za-z0-9_-]{16,}$/;
72
+ var MEMBER_PROVIDER_ID_RE = /^A[CDE][A-Za-z0-9_-]{4,}$/;
68
73
  async function handleSdkError(err, outOpts, out) {
69
74
  const { CurviateError } = await import("@curviate/sdk");
70
75
  if (err instanceof CurviateError) {
@@ -75,10 +80,10 @@ async function handleSdkError(err, outOpts, out) {
75
80
  renderUnexpectedError(err, out);
76
81
  process.exit(1);
77
82
  }
78
- async function runMessageNew(client, flags, out) {
83
+ async function runMessageNew(client, flags, out, _readStdin) {
79
84
  const accountId = requireAccount(flags.account, out);
80
- const attendeeId = flags.to ?? "";
81
- const text = flags.text ?? "";
85
+ const rawTo = flags.to ?? "";
86
+ const rawText = flags.text ?? "";
82
87
  const attachPaths = normalizeAttachPaths(flags.attach);
83
88
  let attachBuffers = [];
84
89
  try {
@@ -91,14 +96,30 @@ async function runMessageNew(client, flags, out) {
91
96
  }
92
97
  throw err;
93
98
  }
99
+ const ns = client.account(accountId);
100
+ const outOpts = resolveOutputOpts(flags);
101
+ const text = await resolveTextOrStdin(rawText, out, _readStdin);
102
+ const resolvedSlugOrId = resolveIdentifier(rawTo);
103
+ let providerId;
104
+ if (MEMBER_PROVIDER_ID_RE.test(resolvedSlugOrId)) {
105
+ providerId = resolvedSlugOrId;
106
+ } else {
107
+ try {
108
+ const profileData = await ns.profiles.get(resolvedSlugOrId, {});
109
+ providerId = profileData["provider_id"];
110
+ } catch (err) {
111
+ await handleSdkError(err, outOpts, out);
112
+ return;
113
+ }
114
+ }
94
115
  const body = {
95
- attendees_ids: [attendeeId],
116
+ attendees_ids: [providerId],
96
117
  text
97
118
  };
98
119
  if (flags.preview) {
99
120
  const preview = buildPreviewOutput({
100
121
  method: "messaging.startChat",
101
- args: { attendees_ids: [attendeeId] },
122
+ args: { attendees_ids: [providerId] },
102
123
  body: { ...body },
103
124
  account: accountId,
104
125
  attachments: attachBuffers.map((buf, i) => ({
@@ -112,8 +133,6 @@ async function runMessageNew(client, flags, out) {
112
133
  if (attachBuffers.length > 0) {
113
134
  body["attachments"] = attachBuffers;
114
135
  }
115
- const ns = client.account(accountId);
116
- const outOpts = resolveOutputOpts(flags);
117
136
  try {
118
137
  const result = await ns.messaging.startChat(body);
119
138
  renderSuccess(result, outOpts, out);
@@ -121,11 +140,12 @@ async function runMessageNew(client, flags, out) {
121
140
  await handleSdkError(err, outOpts, out);
122
141
  }
123
142
  }
124
- async function runMessageSend(client, flags, out) {
143
+ async function runMessageSend(client, flags, out, _readStdin) {
125
144
  const accountId = requireAccount(flags.account, out);
126
- const chatId = flags.chatId ?? "";
127
- const text = flags.text ?? "";
145
+ const chatId = normalizeChatId(flags.chatId ?? "");
146
+ const rawText = flags.text ?? "";
128
147
  const attachPaths = normalizeAttachPaths(flags.attach);
148
+ const text = await resolveTextOrStdin(rawText, out, _readStdin);
129
149
  let attachBuffers = [];
130
150
  try {
131
151
  attachBuffers = await Promise.all(attachPaths.map((p) => readAttachment(p)));
@@ -178,10 +198,11 @@ async function runMessageGet(client, flags, out) {
178
198
  await handleSdkError(err, outOpts, out);
179
199
  }
180
200
  }
181
- async function runMessageEdit(client, flags, out) {
201
+ async function runMessageEdit(client, flags, out, _readStdin) {
182
202
  const accountId = requireAccount(flags.account, out);
183
203
  const messageId = flags.messageId ?? "";
184
- const text = flags.text ?? "";
204
+ const rawText = flags.text ?? "";
205
+ const text = await resolveTextOrStdin(rawText, out, _readStdin);
185
206
  if (flags.preview) {
186
207
  const preview = buildPreviewOutput({
187
208
  method: "messaging.editMessage",
@@ -269,7 +290,7 @@ async function runMessageAttachment(client, flags, out, isTTY) {
269
290
  await handleSdkError(err, outOpts, out);
270
291
  }
271
292
  }
272
- async function runMessageInMail(client, flags, out) {
293
+ async function runMessageInMail(client, flags, out, _readStdin) {
273
294
  const accountId = requireAccount(flags.account, out);
274
295
  const surface = flags.surface ?? "";
275
296
  if (!INMAIL_SURFACES.includes(surface)) {
@@ -279,15 +300,34 @@ async function runMessageInMail(client, flags, out) {
279
300
  );
280
301
  process.exit(2);
281
302
  }
282
- const recipientUrn = resolveIdentifier(flags.to ?? "");
283
- if (!MEMBER_URN_RE.test(recipientUrn) && !MEMBER_PROVIDER_ID_RE.test(recipientUrn)) {
303
+ const rawTo = flags.to ?? "";
304
+ if (!rawTo) {
284
305
  out.stderr.write(
285
- "error: --to must be a LinkedIn member URN (e.g. urn:li:member:99999) or a member provider id (e.g. ACoAAA\u2026), not a URL or slug.\n"
306
+ "error: --to: not a valid LinkedIn URL, slug, provider-id, or URN.\n"
286
307
  );
287
308
  process.exit(2);
309
+ return;
310
+ }
311
+ const ns = client.account(accountId);
312
+ const outOpts = resolveOutputOpts(flags);
313
+ const resolvedSlugOrId = resolveIdentifier(rawTo);
314
+ let recipientUrn;
315
+ if (MEMBER_URN_RE.test(resolvedSlugOrId)) {
316
+ recipientUrn = resolvedSlugOrId;
317
+ } else if (MEMBER_PROVIDER_ID_RE.test(resolvedSlugOrId)) {
318
+ recipientUrn = resolvedSlugOrId;
319
+ } else {
320
+ try {
321
+ const profileData = await ns.profiles.get(resolvedSlugOrId, {});
322
+ recipientUrn = profileData["provider_id"];
323
+ } catch (err) {
324
+ await handleSdkError(err, outOpts, out);
325
+ return;
326
+ }
288
327
  }
289
328
  const subject = flags.subject ?? "";
290
- const text = flags.text ?? "";
329
+ const rawText = flags.text ?? "";
330
+ const text = await resolveTextOrStdin(rawText, out, _readStdin);
291
331
  const body = {
292
332
  recipient_urn: recipientUrn,
293
333
  surface,
@@ -304,8 +344,6 @@ async function runMessageInMail(client, flags, out) {
304
344
  out.stdout.write(JSON.stringify(preview) + "\n");
305
345
  return;
306
346
  }
307
- const ns = client.account(accountId);
308
- const outOpts = resolveOutputOpts(flags);
309
347
  try {
310
348
  const result = await ns.messaging.sendInMail(body);
311
349
  renderSuccess(result, outOpts, out);
@@ -329,9 +367,14 @@ async function runMessageInMailBalance(client, flags, out) {
329
367
  var messageNewCommand = defineCommand({
330
368
  meta: { name: "new", description: "Start a new chat with one or more members." },
331
369
  args: {
332
- ...GLOBAL_FLAGS,
333
- to: { type: "string", description: "Attendee provider ID (e.g. ACo\u2026).", required: true },
334
- text: { type: "positional", description: "Opening message text." },
370
+ // Write command: WRITE_FLAGS omits pagination/projection flags
371
+ ...WRITE_FLAGS,
372
+ to: {
373
+ type: "string",
374
+ description: "Recipient: LinkedIn profile URL (e.g. https://www.linkedin.com/in/some-slug), bare slug (e.g. some-slug), or provider ID (e.g. ACoAAA\u2026). URL and slug inputs resolve the provider ID automatically.",
375
+ required: true
376
+ },
377
+ text: { type: "positional", description: "Opening message text. Pass - to read from stdin (e.g. via heredoc or pipe)." },
335
378
  attach: { type: "string", description: "File to attach (repeatable)." }
336
379
  },
337
380
  async run({ args }) {
@@ -355,7 +398,8 @@ var messageNewCommand = defineCommand({
355
398
  var messageGetCommand = defineCommand({
356
399
  meta: { name: "get", description: "Get a message by ID." },
357
400
  args: {
358
- ...GLOBAL_FLAGS,
401
+ // Single-object read: READ_SINGLE_FLAGS omits pagination flags, keeps --fields
402
+ ...READ_SINGLE_FLAGS,
359
403
  messageId: { type: "positional", description: "Message ID." }
360
404
  },
361
405
  async run({ args }) {
@@ -379,9 +423,10 @@ var messageGetCommand = defineCommand({
379
423
  var messageEditCommand = defineCommand({
380
424
  meta: { name: "edit", description: "Edit a message (within the allowed window)." },
381
425
  args: {
382
- ...GLOBAL_FLAGS,
426
+ // Write command: WRITE_FLAGS omits pagination/projection flags
427
+ ...WRITE_FLAGS,
383
428
  messageId: { type: "positional", description: "Message ID." },
384
- text: { type: "positional", description: "Replacement text." }
429
+ text: { type: "positional", description: "Replacement text. Pass - to read from stdin." }
385
430
  },
386
431
  async run({ args }) {
387
432
  const flags = args;
@@ -404,7 +449,8 @@ var messageEditCommand = defineCommand({
404
449
  var messageDeleteCommand = defineCommand({
405
450
  meta: { name: "delete", description: "Delete a message." },
406
451
  args: {
407
- ...GLOBAL_FLAGS,
452
+ // Write command: WRITE_FLAGS omits pagination/projection flags
453
+ ...WRITE_FLAGS,
408
454
  messageId: { type: "positional", description: "Message ID." }
409
455
  },
410
456
  async run({ args }) {
@@ -428,7 +474,8 @@ var messageDeleteCommand = defineCommand({
428
474
  var messageReactCommand = defineCommand({
429
475
  meta: { name: "react", description: "Add an emoji reaction to a message." },
430
476
  args: {
431
- ...GLOBAL_FLAGS,
477
+ // Write command: WRITE_FLAGS omits pagination/projection flags
478
+ ...WRITE_FLAGS,
432
479
  messageId: { type: "positional", description: "Message ID." },
433
480
  emoji: { type: "string", description: "Native emoji reaction value (e.g. \u{1F44D}).", required: true }
434
481
  },
@@ -453,7 +500,8 @@ var messageReactCommand = defineCommand({
453
500
  var messageAttachmentCommand = defineCommand({
454
501
  meta: { name: "attachment", description: "Download a message attachment." },
455
502
  args: {
456
- ...GLOBAL_FLAGS,
503
+ // Single-object read: READ_SINGLE_FLAGS omits pagination flags, keeps --fields
504
+ ...READ_SINGLE_FLAGS,
457
505
  messageId: { type: "positional", description: "Message ID." },
458
506
  attachmentId: { type: "positional", description: "Attachment ID." },
459
507
  output: { type: "string", alias: "o", description: "Path to write the file to." }
@@ -484,11 +532,16 @@ var messageAttachmentCommand = defineCommand({
484
532
  var messageInMailCommand = defineCommand({
485
533
  meta: { name: "inmail", description: "Send an InMail to a member." },
486
534
  args: {
487
- ...GLOBAL_FLAGS,
488
- to: { type: "string", description: "Recipient member URN (urn:li:member:<id>) or provider id (ACoAAA\u2026). Not a URL or slug.", required: true },
535
+ // Write command: WRITE_FLAGS omits pagination/projection flags
536
+ ...WRITE_FLAGS,
537
+ to: {
538
+ type: "string",
539
+ description: "Recipient: LinkedIn profile URL, bare slug, provider-id (ACoAAA\u2026), or member URN (urn:li:member:<id>). URL and slug inputs resolve the provider ID automatically.",
540
+ required: true
541
+ },
489
542
  surface: { type: "string", description: "InMail surface: sales_nav, recruiter, or classic (classic uses the account's own premium InMail credits).", required: true },
490
543
  subject: { type: "string", description: "InMail subject line.", required: true },
491
- text: { type: "positional", description: "InMail body text." }
544
+ text: { type: "positional", description: "InMail body text. Pass - to read from stdin." }
492
545
  },
493
546
  async run({ args }) {
494
547
  const flags = args;
@@ -508,9 +561,39 @@ var messageInMailCommand = defineCommand({
508
561
  await runMessageInMail(client, { ...flags, account: flags.account ?? cfg.account }, out);
509
562
  }
510
563
  });
564
+ var messageSendCommand = defineCommand({
565
+ meta: { name: "send", description: "Send a message to an existing chat." },
566
+ args: {
567
+ // Write command: WRITE_FLAGS omits pagination/projection flags
568
+ ...WRITE_FLAGS,
569
+ chatId: { type: "positional", description: "Chat ID or LinkedIn messaging thread URL." },
570
+ text: { type: "positional", description: "Message text. Pass - to read from stdin (e.g. via heredoc or pipe)." },
571
+ attach: { type: "string", description: "File to attach (repeatable)." }
572
+ },
573
+ async run({ args }) {
574
+ const flags = args;
575
+ const cfg = await resolveEffectiveConfig({
576
+ apiKey: flags["api-key"],
577
+ baseUrl: flags["base-url"],
578
+ timeout: flags.timeout,
579
+ account: flags.account,
580
+ profile: flags.profile
581
+ });
582
+ if (!cfg.apiKey) {
583
+ process.stderr.write("error: no API key \u2014 run `curviate login` or pass --api-key.\n");
584
+ process.exit(3);
585
+ }
586
+ const client = createClient({ apiKey: cfg.apiKey, baseUrl: cfg.baseUrl, timeout: cfg.timeout });
587
+ const out = buildOutputStreams();
588
+ await runMessageSend(client, { ...flags, account: flags.account ?? cfg.account }, out);
589
+ }
590
+ });
511
591
  var messageInMailBalanceCommand = defineCommand({
512
592
  meta: { name: "inmail-balance", description: "Get InMail credit balance." },
513
- args: { ...GLOBAL_FLAGS },
593
+ args: {
594
+ // Single-object read: READ_SINGLE_FLAGS omits pagination flags, keeps --fields
595
+ ...READ_SINGLE_FLAGS
596
+ },
514
597
  async run({ args }) {
515
598
  const flags = args;
516
599
  const cfg = await resolveEffectiveConfig({
@@ -532,13 +615,15 @@ var messageInMailBalanceCommand = defineCommand({
532
615
  var messageCommand = defineCommand({
533
616
  meta: { name: "message", description: "Send and manage LinkedIn messages." },
534
617
  args: {
535
- ...GLOBAL_FLAGS,
618
+ // Write command (message send): WRITE_FLAGS omits pagination/projection flags
619
+ ...WRITE_FLAGS,
536
620
  chatId: { type: "positional", description: "Chat ID to send a message to.", required: false },
537
- text: { type: "positional", description: "Message text.", required: false },
621
+ text: { type: "positional", description: "Message text. Pass - to read from stdin.", required: false },
538
622
  attach: { type: "string", description: "File to attach (repeatable)." }
539
623
  },
540
624
  subCommands: {
541
625
  new: messageNewCommand,
626
+ send: messageSendCommand,
542
627
  get: messageGetCommand,
543
628
  edit: messageEditCommand,
544
629
  delete: messageDeleteCommand,
@@ -3,23 +3,26 @@ import {
3
3
  AttachError,
4
4
  readAttachment
5
5
  } from "./chunk-Q43HZUN3.js";
6
+ import {
7
+ resolveTextOrStdin
8
+ } from "./chunk-CHFKVAEI.js";
6
9
  import {
7
10
  buildPreviewOutput
8
11
  } from "./chunk-R3VLWLVV.js";
9
12
  import {
10
13
  streamAll
11
- } from "./chunk-SND3NHCT.js";
14
+ } from "./chunk-GXTZION6.js";
12
15
  import {
13
16
  createClient,
14
17
  renderError,
15
18
  renderSuccess,
16
19
  renderUnexpectedError,
17
20
  resolveEffectiveConfig
18
- } from "./chunk-U6ACUNLU.js";
21
+ } from "./chunk-47SYFQRF.js";
19
22
  import {
20
23
  GLOBAL_FLAGS,
21
24
  WRITE_FLAGS
22
- } from "./chunk-52RZLSWP.js";
25
+ } from "./chunk-TDYIQHQX.js";
23
26
 
24
27
  // src/commands/post.ts
25
28
  import { defineCommand } from "citty";
@@ -89,7 +92,8 @@ async function runPostList(client, flags, out) {
89
92
  const fn = (p) => ns.posts.list(p);
90
93
  for await (const item of streamAll(fn, params, {
91
94
  maxPages,
92
- onTruncated: (msg) => out.stderr.write(msg + "\n")
95
+ onTruncated: (n) => out.stderr.write(`Streaming truncated at ${n} page(s). Use --all --max-pages or --cursor for manual paging.
96
+ `)
93
97
  })) {
94
98
  out.stdout.write(JSON.stringify(item) + "\n");
95
99
  }
@@ -115,11 +119,12 @@ async function runPostGet(client, flags, out) {
115
119
  await handleSdkError(err, outOpts, out);
116
120
  }
117
121
  }
118
- async function runPostCreate(client, flags, out) {
122
+ async function runPostCreate(client, flags, out, _readStdin) {
119
123
  const accountId = requireAccount(flags.account, out);
120
- const text = flags.text ?? "";
124
+ const rawText = flags.text ?? "";
121
125
  const attachPaths = normalizeAttachPaths(flags.attach);
122
126
  const thumbPath = flags["video-thumbnail"];
127
+ const text = await resolveTextOrStdin(rawText, out, _readStdin);
123
128
  let attachBuffers = [];
124
129
  try {
125
130
  attachBuffers = await Promise.all(attachPaths.map((p) => readAttachment(p)));
@@ -181,11 +186,12 @@ async function runPostCreate(client, flags, out) {
181
186
  await handleSdkError(err, outOpts, out);
182
187
  }
183
188
  }
184
- async function runPostComment(client, flags, out) {
189
+ async function runPostComment(client, flags, out, _readStdin) {
185
190
  const accountId = requireAccount(flags.account, out);
186
191
  const postId = flags.postId ?? "";
187
- const text = flags.text ?? "";
192
+ const rawText = flags.text ?? "";
188
193
  const replyTo = flags["reply-to"];
194
+ const text = await resolveTextOrStdin(rawText, out, _readStdin);
189
195
  const attachPaths = normalizeAttachPaths(flags.attach);
190
196
  let attachBuffers = [];
191
197
  try {
@@ -241,7 +247,8 @@ async function runPostComments(client, flags, out) {
241
247
  const fn = (p) => ns.posts.listComments(postId, p);
242
248
  for await (const item of streamAll(fn, params, {
243
249
  maxPages,
244
- onTruncated: (msg) => out.stderr.write(msg + "\n")
250
+ onTruncated: (n) => out.stderr.write(`Streaming truncated at ${n} page(s). Use --all --max-pages or --cursor for manual paging.
251
+ `)
245
252
  })) {
246
253
  out.stdout.write(JSON.stringify(item) + "\n");
247
254
  }
@@ -303,7 +310,8 @@ async function runPostReactions(client, flags, out) {
303
310
  const fn = (p) => ns.posts.listReactions(postId, p);
304
311
  for await (const item of streamAll(fn, params, {
305
312
  maxPages,
306
- onTruncated: (msg) => out.stderr.write(msg + "\n")
313
+ onTruncated: (n) => out.stderr.write(`Streaming truncated at ${n} page(s). Use --all --max-pages or --cursor for manual paging.
314
+ `)
307
315
  })) {
308
316
  out.stdout.write(JSON.stringify(item) + "\n");
309
317
  }
@@ -368,7 +376,7 @@ var postCreateCommand = defineCommand({
368
376
  args: {
369
377
  // Write command: WRITE_FLAGS omits pagination/projection flags
370
378
  ...WRITE_FLAGS,
371
- text: { type: "positional", description: "Post body text." },
379
+ text: { type: "positional", description: "Post body text. Pass - to read from stdin (enables multiline via heredoc or pipe)." },
372
380
  attach: {
373
381
  type: "string",
374
382
  description: "Image/video/document to attach (repeatable for images; use --video-thumbnail when attaching a video). Supported: jpg, png, gif, mp4, pdf."
@@ -405,7 +413,7 @@ var postCommentCommand = defineCommand({
405
413
  type: "positional",
406
414
  description: "Numeric post id, urn:li:activity:N, or full LinkedIn share URL (activity-<N>- extracted). POSTID is always the post's id; use --reply-to <comment_id> to reply to a specific comment within this post."
407
415
  },
408
- text: { type: "positional", description: "Comment text (max ~1,250 characters per LinkedIn limits)." },
416
+ text: { type: "positional", description: "Comment text (max ~1,250 characters per LinkedIn limits). Pass - to read from stdin." },
409
417
  attach: { type: "string", description: "Image to attach to the comment (optional; one image per comment)." },
410
418
  "reply-to": {
411
419
  type: "string",