@usesocial/cli 0.3.5 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # @usesocial/cli
2
2
 
3
+ ## 0.4.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#25](https://github.com/usesocial/monorepo/pull/25) [`1121b7c`](https://github.com/usesocial/monorepo/commit/1121b7c938bfb213606722247d50a5eafca138c4) Thanks [@CyrusNuevoDia](https://github.com/CyrusNuevoDia)! - Add `social feedback bug|feature` — submit a bug report or feature request by piping text via stdin (`echo "..." | social feedback bug`). Each submission is captured server-side and acknowledged with a reference id.
8
+
9
+ ## 0.3.6
10
+
11
+ ### Patch Changes
12
+
13
+ - [#19](https://github.com/usesocial/monorepo/pull/19) [`297845c`](https://github.com/usesocial/monorepo/commit/297845cb3c8422c70942cb8504f9e4beb171743c) Thanks [@CyrusNuevoDia](https://github.com/CyrusNuevoDia)! - Capture sanitized diagnostic exception traces for unexpected CLI failures and publish CLI source maps for PostHog error tracking.
14
+
3
15
  ## 0.3.5
4
16
 
5
17
  ### Patch Changes
package/dist/index.d.mts CHANGED
@@ -1334,7 +1334,8 @@ type Resolvable<T> = T | Promise<T> | (() => T) | (() => Promise<T>); //#endregi
1334
1334
  /** Every analytics event name the CLI emits. */
1335
1335
  type AnalyticsEvent = "command_executed" | "cli_first_run" | "login_started" | "login_phase_completed" | "login_completed" | "login_failed" | "logout";
1336
1336
  type Analytics = {
1337
- /** Queue an event. No-op when disabled. Never throws. */capture(event: AnalyticsEvent, properties?: Record<string, unknown>): void; /** True when telemetry is active (token present, not opted-out, not CI). */
1337
+ /** Queue an event. No-op when disabled. Never throws. */capture(event: AnalyticsEvent, properties?: Record<string, unknown>): void; /** Queue a sanitized exception. No-op when disabled. Never throws. */
1338
+ captureException(error: unknown, properties?: Record<string, unknown>): void; /** True when telemetry is active (token present, not opted-out, not CI). */
1338
1339
  readonly enabled: boolean;
1339
1340
  /**
1340
1341
  * On login: alias(anonymousId → userId) once, persist userId as the
@@ -1391,6 +1392,9 @@ declare const createSocialCommand: (deps?: {
1391
1392
  summary: any;
1392
1393
  list: any;
1393
1394
  };
1395
+ feedback: {
1396
+ submit: any;
1397
+ };
1394
1398
  };
1395
1399
  api: KyInstance;
1396
1400
  parseResourceId: (name: string, value: unknown) => string;
@@ -1491,4 +1495,5 @@ declare const runSocial: ({
1491
1495
  }) => Promise<number>;
1492
1496
  declare const main: () => Promise<never>;
1493
1497
  //#endregion
1494
- export { createSocialCommand, main, resolveCommandPath, runSocial };
1498
+ export { createSocialCommand, main, resolveCommandPath, runSocial };
1499
+ //# sourceMappingURL=index.d.mts.map
package/dist/index.mjs CHANGED
@@ -9740,6 +9740,14 @@ pgTable("profiles", {
9740
9740
  index$1("profiles_platform_handle_idx").on(table.platform, table.handle),
9741
9741
  check("profiles_platform_check", sql`${table.platform} in ('linkedin', 'x')`)
9742
9742
  ]);
9743
+ pgTable("feedback_submissions", {
9744
+ id: text$1("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
9745
+ userId: text$1("user_id").notNull().references(() => user.id, { onDelete: "cascade" }),
9746
+ type: text$1("type", { enum: ["bug", "feature"] }).notNull(),
9747
+ text: text$1("text").notNull(),
9748
+ issueNumber: integer$1("issue_number"),
9749
+ createdAt: timestamptz("created_at").notNull().defaultNow()
9750
+ }, (table) => [index$1("feedback_submissions_user_created_idx").on(table.userId, table.createdAt.desc()), check("feedback_submissions_type_check", sql`${table.type} in ('bug', 'feature')`)]);
9743
9751
  pgTable("cli_session_grants", {
9744
9752
  sessionId: text$1("session_id").primaryKey().references(() => session.id, { onDelete: "cascade" }),
9745
9753
  userId: text$1("user_id").notNull().references(() => user.id, { onDelete: "cascade" }),
@@ -10675,6 +10683,73 @@ const liftedTimestamp = (collection, item) => {
10675
10683
  const value = collection.lift(item)[collection.sinceField];
10676
10684
  return typeof value === "number" ? value : null;
10677
10685
  };
10686
+ const stepPage = (state, page, opts) => {
10687
+ const newestId = state.firstPage && page.items[0] ? String(page.items[0].id) : state.newestId;
10688
+ const rows = [];
10689
+ const ids = [];
10690
+ for (const item of page.items) {
10691
+ const id = String(item.id);
10692
+ if (state.checkpoint !== null && id === state.checkpoint) return {
10693
+ rows,
10694
+ ids,
10695
+ nextState: {
10696
+ ...state,
10697
+ newestId,
10698
+ firstPage: false
10699
+ },
10700
+ stoppedReason: "checkpoint",
10701
+ done: true
10702
+ };
10703
+ if (opts.since != null && opts.collection.sinceField) {
10704
+ const timestamp = liftedTimestamp(opts.collection, item);
10705
+ if (timestamp != null && timestamp < opts.since) return {
10706
+ rows,
10707
+ ids,
10708
+ nextState: {
10709
+ ...state,
10710
+ newestId,
10711
+ firstPage: false
10712
+ },
10713
+ stoppedReason: "since",
10714
+ done: true
10715
+ };
10716
+ }
10717
+ const row = opts.collection.lift(item);
10718
+ applyBakedQueryOverlay(row, opts.request.bakedQuery);
10719
+ applyParentOverlay(row, opts.parentIdToken, opts.parentId);
10720
+ rows.push(row);
10721
+ if (opts.collectIds) ids.push(id);
10722
+ }
10723
+ if (opts.collection.pagination.style === "offset") {
10724
+ const nextOffset = state.offset + opts.collection.pageSize;
10725
+ const done = page.items.length === 0 || page.totalCount != null && nextOffset >= page.totalCount || page.totalCount == null && page.items.length < opts.collection.pageSize;
10726
+ return {
10727
+ rows,
10728
+ ids,
10729
+ nextState: {
10730
+ ...state,
10731
+ offset: done ? state.offset : nextOffset,
10732
+ newestId,
10733
+ firstPage: false
10734
+ },
10735
+ stoppedReason: "complete",
10736
+ done
10737
+ };
10738
+ }
10739
+ const nextCursor = page.cursor;
10740
+ return {
10741
+ rows,
10742
+ ids,
10743
+ nextState: {
10744
+ ...state,
10745
+ cursor: nextCursor || null,
10746
+ newestId,
10747
+ firstPage: false
10748
+ },
10749
+ stoppedReason: "complete",
10750
+ done: !nextCursor
10751
+ };
10752
+ };
10678
10753
  const estimateFor = (collection, limitCredits, totalCount) => ({
10679
10754
  collectionKey: collection.key,
10680
10755
  ...estimateSync({
@@ -10698,16 +10773,19 @@ const refinedEstimateMessage = (collection, limitCredits, totalCount) => {
10698
10773
  const walkRequest = async (args) => {
10699
10774
  const checkpoint = args.useCheckpoint ? args.deps.cache.getCursor(args.collection.key)?.cursor ?? null : null;
10700
10775
  const ids = [];
10701
- let cursor = null;
10702
- let offset = 0;
10703
- let newestId = null;
10776
+ let state = {
10777
+ checkpoint,
10778
+ cursor: null,
10779
+ offset: 0,
10780
+ newestId: null,
10781
+ firstPage: true
10782
+ };
10704
10783
  let lastPageCredits = null;
10705
10784
  let pages = 0;
10706
10785
  let upserted = 0;
10707
- let firstPage = true;
10708
10786
  let stoppedReason = "complete";
10709
10787
  while (true) {
10710
- const predictedNext = firstPage ? estimatePageCredits(args.collection.estCostBPS, args.collection.estUnitsPerPage) : lastPageCredits ?? estimatePageCredits(args.collection.estCostBPS, args.collection.estUnitsPerPage);
10788
+ const predictedNext = state.firstPage ? estimatePageCredits(args.collection.estCostBPS, args.collection.estUnitsPerPage) : lastPageCredits ?? estimatePageCredits(args.collection.estCostBPS, args.collection.estUnitsPerPage);
10711
10789
  if (args.tracker.wouldExceed(predictedNext)) {
10712
10790
  stoppedReason = "billing-limit";
10713
10791
  break;
@@ -10717,61 +10795,34 @@ const walkRequest = async (args) => {
10717
10795
  ownIdToken: args.collection.ownIdToken,
10718
10796
  chatId: args.parentId,
10719
10797
  parentIdToken: args.parentIdToken
10720
- }) + buildQuery(args.collection.pagination, args.collection.pageSize, cursor, offset, args.request.bakedQuery);
10798
+ }) + buildQuery(args.collection.pagination, args.collection.pageSize, state.cursor, state.offset, args.request.bakedQuery);
10721
10799
  const response = await args.deps.api.get(path, { headers: args.deps.accountHeaders });
10722
10800
  const credits = creditsFromHeader(response.headers);
10723
10801
  args.tracker.add(credits);
10724
10802
  lastPageCredits = credits;
10725
10803
  pages++;
10726
10804
  const page = validateEnvelope(await response.json(), args.collection, args.onProgress);
10727
- if (firstPage && page.totalCount != null) args.onProgress?.(refinedEstimateMessage(args.collection, args.billingLimitCredits, page.totalCount));
10728
- const [firstItem] = page.items;
10729
- if (firstPage && firstItem) newestId = String(firstItem.id);
10730
- firstPage = false;
10731
- const rows = [];
10732
- let hitStop = false;
10733
- for (const item of page.items) {
10734
- const id = String(item.id);
10735
- if (checkpoint !== null && id === checkpoint) {
10736
- stoppedReason = "checkpoint";
10737
- hitStop = true;
10738
- break;
10739
- }
10740
- if (args.since != null && args.collection.sinceField) {
10741
- const timestamp = liftedTimestamp(args.collection, item);
10742
- if (timestamp != null && timestamp < args.since) {
10743
- stoppedReason = "since";
10744
- hitStop = true;
10745
- break;
10746
- }
10747
- }
10748
- const row = args.collection.lift(item);
10749
- applyBakedQueryOverlay(row, args.request.bakedQuery);
10750
- applyParentOverlay(row, args.parentIdToken, args.parentId);
10751
- rows.push(row);
10752
- if (args.collectIds) ids.push(id);
10753
- }
10754
- args.deps.cache.upsert(args.collection.key, rows);
10755
- upserted += rows.length;
10756
- if (hitStop) break;
10757
- if (args.collection.pagination.style === "offset") {
10758
- const nextOffset = offset + args.collection.pageSize;
10759
- if (page.items.length === 0 || page.totalCount != null && nextOffset >= page.totalCount || page.totalCount == null && page.items.length < args.collection.pageSize) {
10760
- stoppedReason = "complete";
10761
- break;
10762
- }
10763
- offset = nextOffset;
10764
- continue;
10765
- }
10766
- cursor = page.cursor;
10767
- if (!cursor) {
10768
- stoppedReason = "complete";
10805
+ if (state.firstPage && page.totalCount != null) args.onProgress?.(refinedEstimateMessage(args.collection, args.billingLimitCredits, page.totalCount));
10806
+ const step = stepPage(state, page, {
10807
+ collection: args.collection,
10808
+ request: args.request,
10809
+ since: args.since,
10810
+ collectIds: args.collectIds,
10811
+ parentId: args.parentId,
10812
+ parentIdToken: args.parentIdToken
10813
+ });
10814
+ args.deps.cache.upsert(args.collection.key, step.rows);
10815
+ upserted += step.rows.length;
10816
+ ids.push(...step.ids);
10817
+ state = step.nextState;
10818
+ if (step.done) {
10819
+ stoppedReason = step.stoppedReason;
10769
10820
  break;
10770
10821
  }
10771
10822
  }
10772
10823
  return {
10773
10824
  ids,
10774
- newestId,
10825
+ newestId: state.newestId,
10775
10826
  pages,
10776
10827
  upserted,
10777
10828
  checkpoint,
@@ -13290,8 +13341,8 @@ const dataWithAuditableWriteTarget = (op, data, body) => {
13290
13341
  post_id: postId
13291
13342
  };
13292
13343
  };
13293
- const trimTrailingNewline$1 = (value) => value.endsWith("\r\n") ? value.slice(0, -2) : value.endsWith("\n") ? value.slice(0, -1) : value;
13294
- const noBodyTextError$1 = (commandLabel) => new UsageError(`No body text. Pipe it via stdin: \`echo "..." | ${commandLabel}\`.`);
13344
+ const trimTrailingNewline$2 = (value) => value.endsWith("\r\n") ? value.slice(0, -2) : value.endsWith("\n") ? value.slice(0, -1) : value;
13345
+ const noBodyTextError$2 = (commandLabel) => new UsageError(`No body text. Pipe it via stdin: \`echo "..." | ${commandLabel}\`.`);
13295
13346
  const parseStdinBody$1 = (deps, bodyTextName, bodyTextKey, value) => {
13296
13347
  try {
13297
13348
  JSON.parse(value);
@@ -13307,12 +13358,12 @@ const resolveBodyTextFromStdin$1 = async (op, deps, args, commandLabel) => {
13307
13358
  const displayTextArg = bodyTextDisplayKeyFor$1(op) ?? bodyTextArg;
13308
13359
  const required = stdinBodyTextRequired$1(op);
13309
13360
  if (deps.isStdinTTY()) {
13310
- if (required) throw noBodyTextError$1(commandLabel);
13361
+ if (required) throw noBodyTextError$2(commandLabel);
13311
13362
  return;
13312
13363
  }
13313
- const piped = trimTrailingNewline$1(await deps.readStdin());
13364
+ const piped = trimTrailingNewline$2(await deps.readStdin());
13314
13365
  if (piped.length > 0) args.body = JSON.stringify(parseStdinBody$1(deps, displayTextArg, bodyTextArg, piped));
13315
- else if (required) throw noBodyTextError$1(commandLabel);
13366
+ else if (required) throw noBodyTextError$2(commandLabel);
13316
13367
  };
13317
13368
  const runOperation$1 = async (op, contract, deps, args, rawArgs = []) => {
13318
13369
  rejectUnsupportedArgs(commandPathFor(op), args, Object.keys(buildArgs$1(op)));
@@ -13959,11 +14010,11 @@ const buildMessageEdit = (deps) => {
13959
14010
  const account = typeof args.account === "string" ? args.account : void 0;
13960
14011
  const message_id = messageIdFrom(deps, args.message_id ?? positionals[1]);
13961
14012
  let textInput;
13962
- if (deps.isStdinTTY()) throw noBodyTextError$1("social linkedin message <target> edit message_id:<id>");
13963
- const piped = trimTrailingNewline$1(await deps.readStdin());
14013
+ if (deps.isStdinTTY()) throw noBodyTextError$2("social linkedin message <target> edit message_id:<id>");
14014
+ const piped = trimTrailingNewline$2(await deps.readStdin());
13964
14015
  if (piped.length > 0) textInput = piped;
13965
14016
  const text = textInput === void 0 ? void 0 : deps.parseBodyText("<text>", textInput);
13966
- if (text === void 0) throw noBodyTextError$1("social linkedin message <target> edit message_id:<id>");
14017
+ if (text === void 0) throw noBodyTextError$2("social linkedin message <target> edit message_id:<id>");
13967
14018
  const resolved = [];
13968
14019
  const chatIdResolved = await deps.resolveTarget("target", args.target ?? positionals[0], {
13969
14020
  platform: "linkedin",
@@ -14042,10 +14093,10 @@ const buildMessage$1 = (deps, sendCommand) => {
14042
14093
  }
14043
14094
  const target = args.target ?? (Array.isArray(args._) && args._.length > 0 ? args._[0] : void 0);
14044
14095
  let body;
14045
- if (deps.isStdinTTY()) throw noBodyTextError$1("social linkedin message <target>");
14046
- const piped = trimTrailingNewline$1(await deps.readStdin());
14096
+ if (deps.isStdinTTY()) throw noBodyTextError$2("social linkedin message <target>");
14097
+ const piped = trimTrailingNewline$2(await deps.readStdin());
14047
14098
  if (piped.length > 0) body = parseStdinBody$1(deps, "text", "text", piped);
14048
- else throw noBodyTextError$1("social linkedin message <target>");
14099
+ else throw noBodyTextError$2("social linkedin message <target>");
14049
14100
  if (target === void 0) throw new UsageError("Pass a conversation or person target.");
14050
14101
  if (body === void 0 || typeof body.text !== "string") throw new UsageError("Pass body text via stdin.");
14051
14102
  const account = typeof args.account === "string" ? args.account : void 0;
@@ -17380,8 +17431,8 @@ const withEchoedTargetId = (op, data, body) => {
17380
17431
  }
17381
17432
  };
17382
17433
  };
17383
- const trimTrailingNewline = (value) => value.endsWith("\r\n") ? value.slice(0, -2) : value.endsWith("\n") ? value.slice(0, -1) : value;
17384
- const noBodyTextError = (commandLabel) => new UsageError(`No body text. Pipe it via stdin: \`echo "..." | ${commandLabel}\`.`);
17434
+ const trimTrailingNewline$1 = (value) => value.endsWith("\r\n") ? value.slice(0, -2) : value.endsWith("\n") ? value.slice(0, -1) : value;
17435
+ const noBodyTextError$1 = (commandLabel) => new UsageError(`No body text. Pipe it via stdin: \`echo "..." | ${commandLabel}\`.`);
17385
17436
  const parseStdinBody = (deps, bodyTextName, bodyTextKey, value) => {
17386
17437
  try {
17387
17438
  JSON.parse(value);
@@ -17397,12 +17448,12 @@ const resolveBodyTextFromStdin = async (op, deps, args, commandLabel) => {
17397
17448
  const displayTextArg = bodyTextDisplayKeyFor(op) ?? bodyTextArg;
17398
17449
  const required = stdinBodyTextRequired(op);
17399
17450
  if (deps.isStdinTTY()) {
17400
- if (required) throw noBodyTextError(commandLabel);
17451
+ if (required) throw noBodyTextError$1(commandLabel);
17401
17452
  return;
17402
17453
  }
17403
- const piped = trimTrailingNewline(await deps.readStdin());
17454
+ const piped = trimTrailingNewline$1(await deps.readStdin());
17404
17455
  if (piped.length > 0) args.body = JSON.stringify(parseStdinBody(deps, displayTextArg, bodyTextArg, piped));
17405
- else if (required) throw noBodyTextError(commandLabel);
17456
+ else if (required) throw noBodyTextError$1(commandLabel);
17406
17457
  };
17407
17458
  const runOperation = async (op, contract, deps, args, rawArgs = []) => {
17408
17459
  rejectUnknownArgs(op, args);
@@ -17976,14 +18027,14 @@ const buildMessage = (deps) => {
17976
18027
  if (typeof args.body === "string") throw new UsageError("Unsupported option --body for x message.");
17977
18028
  const recipientsInput = args.recipients ?? args.target ?? (Array.isArray(args._) && args._.length > 0 ? args._[0] : void 0);
17978
18029
  let body;
17979
- if (deps.isStdinTTY()) throw noBodyTextError("social x message <recipients>");
17980
- const piped = trimTrailingNewline(await deps.readStdin());
18030
+ if (deps.isStdinTTY()) throw noBodyTextError$1("social x message <recipients>");
18031
+ const piped = trimTrailingNewline$1(await deps.readStdin());
17981
18032
  if (piped.length > 0) body = parseStdinBody(deps, "text", "text", piped);
17982
- else throw noBodyTextError("social x message <recipients>");
18033
+ else throw noBodyTextError$1("social x message <recipients>");
17983
18034
  if (recipientsInput === void 0) throw new UsageError("Pass a conversation or user target.");
17984
18035
  const recipients = String(recipientsInput).split(",").map((recipient) => recipient.trim()).filter((recipient) => recipient.length > 0);
17985
18036
  if (recipients.length === 0) throw new UsageError("Pass at least one recipient.");
17986
- if (body === void 0) throw noBodyTextError("social x message <recipients>");
18037
+ if (body === void 0) throw noBodyTextError$1("social x message <recipients>");
17987
18038
  const resolved = [];
17988
18039
  const account = typeof args.account === "string" ? args.account : void 0;
17989
18040
  const selectedAccount = await deps.resolveAccount({
@@ -21059,7 +21110,7 @@ const createInstance = (defaults) => {
21059
21110
  const ky = createInstance();
21060
21111
  //#endregion
21061
21112
  //#region package.json
21062
- var version$1 = "0.3.5";
21113
+ var version$1 = "0.4.0";
21063
21114
  //#endregion
21064
21115
  //#region src/lib/env.ts
21065
21116
  const URLWithTrailingSlash = url().transform(ensureTrailingSlash);
@@ -21070,7 +21121,7 @@ const env = createEnv({
21070
21121
  SOCIAL_WEB_URL: process.env.SOCIAL_WEB_URL,
21071
21122
  WSL_DISTRO_NAME: process.env.WSL_DISTRO_NAME,
21072
21123
  WSL_INTEROP: process.env.WSL_INTEROP,
21073
- NEXT_PUBLIC_POSTHOG_PROJECT_TOKEN: "phc_xxr5gJRiRMMSoyxQYCpT5zJQZmF88uMXf7mmYjKzLdki",
21124
+ NEXT_PUBLIC_POSTHOG_PROJECT_TOKEN: "phc_A8HpmMUWsgUaHuyMoskLWtNDp69Rww9V3VEbCT4AxXdF",
21074
21125
  NEXT_PUBLIC_POSTHOG_HOST: "https://us.i.posthog.com",
21075
21126
  CI: process.env.CI,
21076
21127
  DO_NOT_TRACK: process.env.DO_NOT_TRACK,
@@ -26065,9 +26116,19 @@ const writeState = (path, state) => {
26065
26116
  };
26066
26117
  const FIRST_RUN_NOTICE = [
26067
26118
  "social collects anonymous usage analytics (command names, outcomes, timings,",
26068
- "and coarse environment — never your content, handles, IDs, or arguments).",
26119
+ "coarse environment, and diagnostic exception traces — never your content,",
26120
+ "handles, IDs, arguments, stdin, or response bodies).",
26069
26121
  "Opt out any time with DO_NOT_TRACK=1 or SOCIAL_DO_NOT_TRACK=1."
26070
26122
  ].join("\n");
26123
+ const sanitizeExceptionForPostHog = (error) => {
26124
+ const sanitized = /* @__PURE__ */ new Error("social command failed");
26125
+ sanitized.name = error instanceof Error && error.name ? error.name : "Error";
26126
+ if (error instanceof Error && error.stack) {
26127
+ const [, ...frames] = error.stack.split("\n");
26128
+ sanitized.stack = [`${sanitized.name}: ${sanitized.message}`, ...frames].join("\n");
26129
+ }
26130
+ return sanitized;
26131
+ };
26071
26132
  var PostHogAnalytics = class {
26072
26133
  enabled = true;
26073
26134
  #client;
@@ -26115,6 +26176,15 @@ var PostHogAnalytics = class {
26115
26176
  });
26116
26177
  } catch {}
26117
26178
  }
26179
+ captureException(error, properties = {}) {
26180
+ if (!this.#client) return;
26181
+ try {
26182
+ this.#client.captureException(sanitizeExceptionForPostHog(error), this.#distinctId, {
26183
+ ...this.#baseProps,
26184
+ ...properties
26185
+ });
26186
+ } catch {}
26187
+ }
26118
26188
  identifyUser(userId, properties = {}) {
26119
26189
  if (!(this.#client && userId)) return;
26120
26190
  const previousUserId = this.#state.userId;
@@ -26155,6 +26225,7 @@ var PostHogAnalytics = class {
26155
26225
  var DisabledAnalytics = class {
26156
26226
  enabled = false;
26157
26227
  capture() {}
26228
+ captureException() {}
26158
26229
  identifyUser() {}
26159
26230
  reset() {}
26160
26231
  shutdown() {
@@ -28430,6 +28501,66 @@ const createAccountCommand = (deps) => {
28430
28501
  });
28431
28502
  };
28432
28503
  //#endregion
28504
+ //#region src/lib/stdin.ts
28505
+ const trimTrailingNewline = (value) => value.endsWith("\r\n") ? value.slice(0, -2) : value.endsWith("\n") ? value.slice(0, -1) : value;
28506
+ const noBodyTextError = (commandLabel) => new UsageError(`No body text. Pipe it via stdin: \`echo "..." | ${commandLabel}\`.`);
28507
+ const readPipedText = async (deps, commandLabel) => {
28508
+ if (deps.isStdinTTY()) throw noBodyTextError(commandLabel);
28509
+ const text = trimTrailingNewline(await deps.readStdin());
28510
+ if (text.length === 0) throw noBodyTextError(commandLabel);
28511
+ return text;
28512
+ };
28513
+ //#endregion
28514
+ //#region src/feedback/index.ts
28515
+ const submitContract = {
28516
+ capability: "write",
28517
+ auth: {
28518
+ required: true,
28519
+ scope: "read,write"
28520
+ },
28521
+ mutates: true,
28522
+ outputSchema: object({
28523
+ id: string(),
28524
+ type: _enum(["bug", "feature"]),
28525
+ issueNumber: number().int().nullable(),
28526
+ createdAt: string()
28527
+ }),
28528
+ confirmation: false,
28529
+ hazards: [{
28530
+ code: "feedback_submit",
28531
+ message: "Sends your text to the maintainers as a tracked report."
28532
+ }]
28533
+ };
28534
+ const createSubmitCommand = (deps, type) => {
28535
+ const noun = type === "bug" ? "bug report" : "feature request";
28536
+ const label = `social feedback ${type}`;
28537
+ return defineCommand({
28538
+ meta: commandMeta({
28539
+ name: type,
28540
+ description: `Submit a ${noun} [write] | usage: \`echo "..." | ${label}\``,
28541
+ capability: "write",
28542
+ contract: submitContract
28543
+ }),
28544
+ run: async () => {
28545
+ const text = await readPipedText(deps, label);
28546
+ deps.printData(await deps.client.feedback.submit({
28547
+ type,
28548
+ text
28549
+ }));
28550
+ }
28551
+ });
28552
+ };
28553
+ const createFeedbackCommand = (deps) => defineCommand({
28554
+ meta: {
28555
+ name: "feedback",
28556
+ description: "Report a bug or request a feature."
28557
+ },
28558
+ subCommands: {
28559
+ bug: createSubmitCommand(deps, "bug"),
28560
+ feature: createSubmitCommand(deps, "feature")
28561
+ }
28562
+ });
28563
+ //#endregion
28433
28564
  //#region src/lib/account.ts
28434
28565
  const LEADING_AT_PATTERN = /^@+/;
28435
28566
  const normalizeAccountIdentifier = (value) => value.trim().replace(LEADING_AT_PATTERN, "").toLowerCase();
@@ -28588,7 +28719,8 @@ const createCLIDeps = () => {
28588
28719
  usage: {
28589
28720
  summary: client.usage.summary,
28590
28721
  list: client.usage.list
28591
- }
28722
+ },
28723
+ feedback: { submit: client.feedback.submit }
28592
28724
  },
28593
28725
  api,
28594
28726
  parseResourceId,
@@ -29113,6 +29245,16 @@ const captureCommandFinished = (exitCode, telemetry) => {
29113
29245
  duration_ms: Date.now() - commandContext.startedAt
29114
29246
  });
29115
29247
  };
29248
+ const captureCommandException = (error, exitCode, telemetry) => {
29249
+ if (!(commandContext && exitCode === EXIT.api)) return;
29250
+ telemetry.captureException(error, {
29251
+ command: commandContext.command,
29252
+ group: commandContext.group,
29253
+ exit_code: exitCode,
29254
+ outcome: outcomeForExitCode(exitCode),
29255
+ duration_ms: Date.now() - commandContext.startedAt
29256
+ });
29257
+ };
29116
29258
  const finish = async (code, telemetry) => {
29117
29259
  captureCommandFinished(code, telemetry);
29118
29260
  await telemetry.shutdown(ANALYTICS_FLUSH_MS);
@@ -29126,6 +29268,7 @@ const createSocialCommand = (deps = createCLIDeps()) => {
29126
29268
  const accountCommand = createAccountCommand(deps);
29127
29269
  const linkedinCommand = createLinkedinCommands(deps);
29128
29270
  const xCommand = createXCommands(deps);
29271
+ const feedbackCommand = createFeedbackCommand(deps);
29129
29272
  const socialCommand = defineCommand({
29130
29273
  meta: {
29131
29274
  name: "social",
@@ -29136,7 +29279,8 @@ const createSocialCommand = (deps = createCLIDeps()) => {
29136
29279
  account: accountCommand,
29137
29280
  schema: schemaCommand,
29138
29281
  x: xCommand,
29139
- linkedin: linkedinCommand
29282
+ linkedin: linkedinCommand,
29283
+ feedback: feedbackCommand
29140
29284
  }
29141
29285
  });
29142
29286
  setSchemaRootCommand(socialCommand);
@@ -29176,7 +29320,9 @@ const runSocial = async ({ command = createSocialCommand(), rawArgs = process.ar
29176
29320
  if (error instanceof SilentExit) return await finish(error.code, telemetry);
29177
29321
  if (isCittyUsageError(error) && process.stderr.isTTY) await printUsage(command, rawArgs);
29178
29322
  printError(error);
29179
- return await finish(exitCodeFor(error), telemetry);
29323
+ const exitCode = exitCodeFor(error);
29324
+ captureCommandException(error, exitCode, telemetry);
29325
+ return await finish(exitCode, telemetry);
29180
29326
  }
29181
29327
  return await finish(0, telemetry);
29182
29328
  };
@@ -29195,3 +29341,5 @@ const directEntryPath = () => {
29195
29341
  if (directEntryPath()) await main();
29196
29342
  //#endregion
29197
29343
  export { createSocialCommand, main, resolveCommandPath, runSocial };
29344
+
29345
+ //# sourceMappingURL=index.mjs.map