@wspc/cli 0.0.13 → 0.0.15

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/dist/cli.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/cli.ts
4
- import { Command as Command62 } from "commander";
4
+ import { Command as Command63 } from "commander";
5
5
 
6
6
  // src/generated/cli/invite/accept.ts
7
7
  import { Command } from "commander";
@@ -970,6 +970,11 @@ var emailDelete = (options) => (options.client ?? client).post({
970
970
  ...options.headers
971
971
  }
972
972
  });
973
+ var emailDomainDelete = (options) => (options.client ?? client).delete({
974
+ security: [{ scheme: "bearer", type: "http" }],
975
+ url: "/email/domains/{domain}",
976
+ ...options
977
+ });
973
978
  var emailDomainGet = (options) => (options.client ?? client).get({
974
979
  security: [{ scheme: "bearer", type: "http" }],
975
980
  url: "/email/domains/{domain}",
@@ -1175,6 +1180,7 @@ var V1_CRED_KEYS = [
1175
1180
  function migrateEnv(raw) {
1176
1181
  const api_base = typeof raw.api_base === "string" ? raw.api_base : "";
1177
1182
  const env = { api_base, accounts: {} };
1183
+ if (typeof raw.consistency_bookmark === "string") env.consistency_bookmark = raw.consistency_bookmark;
1178
1184
  if (typeof raw.client_id === "string") env.client_id = raw.client_id;
1179
1185
  if (raw.accounts && typeof raw.accounts === "object") {
1180
1186
  env.accounts = raw.accounts;
@@ -1252,10 +1258,79 @@ var ConfigStore = class {
1252
1258
  }
1253
1259
  };
1254
1260
 
1261
+ // src/handwritten/auth/consistency-fetch.ts
1262
+ var HEADER = "x-consistency-bookmark";
1263
+ var INVALID_BOOKMARK = "INVALID_CONSISTENCY_BOOKMARK";
1264
+ function normalizeBasePath(pathname) {
1265
+ const trimmed = pathname.replace(/\/+$/, "");
1266
+ return trimmed === "" ? "/" : trimmed;
1267
+ }
1268
+ function isUnderApiBase(url, apiBase) {
1269
+ const base = new URL(apiBase);
1270
+ const basePath = normalizeBasePath(base.pathname);
1271
+ return url.origin === base.origin && (basePath === "/" || url.pathname === basePath || url.pathname.startsWith(`${basePath}/`));
1272
+ }
1273
+ function isJsonContentType(contentType) {
1274
+ const mediaType = contentType.toLowerCase().split(";")[0]?.trim() ?? "";
1275
+ return mediaType === "application/json" || mediaType.endsWith("+json");
1276
+ }
1277
+ async function responseHasInvalidBookmark(response) {
1278
+ const contentType = response.headers.get("content-type") ?? "";
1279
+ if (!isJsonContentType(contentType)) return false;
1280
+ try {
1281
+ const body = await response.clone().json();
1282
+ return body.error?.code === INVALID_BOOKMARK;
1283
+ } catch {
1284
+ return false;
1285
+ }
1286
+ }
1287
+ function createConsistencyFetch(opts) {
1288
+ const fetchImpl = opts.fetchImpl ?? fetch;
1289
+ return async (input, init) => {
1290
+ const request = new Request(input, init);
1291
+ const url = new URL(request.url);
1292
+ const applies = isUnderApiBase(url, opts.apiBase);
1293
+ let outgoing = request;
1294
+ let injectedStoredBookmark = false;
1295
+ if (applies && !outgoing.headers.has(HEADER)) {
1296
+ const config2 = await opts.store.read();
1297
+ const bookmark = config2.envs[opts.envName]?.consistency_bookmark;
1298
+ if (bookmark) {
1299
+ const headers = new Headers(outgoing.headers);
1300
+ headers.set(HEADER, bookmark);
1301
+ outgoing = new Request(outgoing, { headers });
1302
+ injectedStoredBookmark = true;
1303
+ }
1304
+ }
1305
+ if (!applies && outgoing.headers.has(HEADER)) {
1306
+ const headers = new Headers(outgoing.headers);
1307
+ headers.delete(HEADER);
1308
+ outgoing = new Request(outgoing, { headers });
1309
+ }
1310
+ const response = await fetchImpl(outgoing);
1311
+ if (!applies) return response;
1312
+ const nextBookmark = response.headers.get(HEADER);
1313
+ const shouldCheckInvalidBookmark = injectedStoredBookmark && !nextBookmark;
1314
+ const invalidBookmark = shouldCheckInvalidBookmark ? await responseHasInvalidBookmark(response) : false;
1315
+ const shouldClearBookmark = invalidBookmark;
1316
+ if (!nextBookmark && !shouldClearBookmark) return response;
1317
+ const config = await opts.store.read();
1318
+ const env = config.envs[opts.envName];
1319
+ if (!env) return response;
1320
+ if (nextBookmark) {
1321
+ env.consistency_bookmark = nextBookmark;
1322
+ } else if (shouldClearBookmark) {
1323
+ delete env.consistency_bookmark;
1324
+ }
1325
+ await opts.store.write(config);
1326
+ return response;
1327
+ };
1328
+ }
1329
+
1255
1330
  // src/version.ts
1256
- var VERSION = "0.0.13";
1257
- var SPEC_SHA = "7842b7a9";
1258
- var SPEC_FETCHED_AT = "2026-06-09T08:10:52.164Z";
1331
+ var VERSION = "0.0.15";
1332
+ var SPEC_SHA = "dac502bc";
1333
+ var SPEC_FETCHED_AT = "2026-06-15T16:41:41.264Z";
1259
1334
  var API_BASE = "https://api.wspc.ai";
1260
1335
 
1261
1336
  // src/index.ts
@@ -1271,6 +1346,7 @@ var WspcAuthExpiredError = class extends Error {
1271
1346
  function createAuthInterceptor(mode) {
1272
1347
  if ("apiKey" in mode) {
1273
1348
  const apiKey = mode.apiKey;
1349
+ const fetchImpl2 = mode.fetchImpl ?? fetch;
1274
1350
  return {
1275
1351
  async onRequest(req) {
1276
1352
  req.headers.set("authorization", `Bearer ${apiKey}`);
@@ -1278,7 +1354,7 @@ function createAuthInterceptor(mode) {
1278
1354
  },
1279
1355
  async execute(req) {
1280
1356
  const out = await this.onRequest(req.clone());
1281
- return fetch(out);
1357
+ return fetchImpl2(out);
1282
1358
  }
1283
1359
  };
1284
1360
  }
@@ -1357,10 +1433,10 @@ function resolveAccount(config, opts = {}) {
1357
1433
  }
1358
1434
 
1359
1435
  // src/handwritten/auth/load-sdk-client.ts
1360
- function buildInterceptor(store, resolved) {
1436
+ function buildInterceptor(store, resolved, fetchImpl) {
1361
1437
  const { envName, apiBase, clientId, email, creds } = resolved;
1362
1438
  if (creds.api_key) {
1363
- return createAuthInterceptor({ apiKey: creds.api_key });
1439
+ return createAuthInterceptor({ apiKey: creds.api_key, fetchImpl });
1364
1440
  }
1365
1441
  if (!clientId) {
1366
1442
  throw new Error(
@@ -1372,6 +1448,7 @@ function buildInterceptor(store, resolved) {
1372
1448
  refreshToken: creds.refresh_token,
1373
1449
  baseUrl: apiBase,
1374
1450
  clientId,
1451
+ fetchImpl,
1375
1452
  onTokenRefresh: async ({ accessToken, refreshToken, expiresAt }) => {
1376
1453
  const cfg = await store.read();
1377
1454
  const a = cfg.envs[envName]?.accounts?.[email];
@@ -1387,7 +1464,13 @@ async function loadSdkClient(opts = {}) {
1387
1464
  const store = opts.store ?? new ConfigStore();
1388
1465
  const config = await store.read();
1389
1466
  const resolved = resolveAccount(config, { accountOverride: process.env.WSPC_ACCOUNT });
1390
- const interceptor = buildInterceptor(store, resolved);
1467
+ const consistencyFetch = createConsistencyFetch({
1468
+ store,
1469
+ envName: resolved.envName,
1470
+ apiBase: resolved.apiBase,
1471
+ fetchImpl: opts.fetchImpl
1472
+ });
1473
+ const interceptor = buildInterceptor(store, resolved, consistencyFetch);
1391
1474
  const rawClient = createClient(
1392
1475
  createConfig({
1393
1476
  baseUrl: resolved.apiBase,
@@ -1400,7 +1483,13 @@ async function loadAuthedFetch(opts = {}) {
1400
1483
  const store = opts.store ?? new ConfigStore();
1401
1484
  const config = await store.read();
1402
1485
  const resolved = resolveAccount(config, { accountOverride: process.env.WSPC_ACCOUNT });
1403
- const interceptor = buildInterceptor(store, resolved);
1486
+ const consistencyFetch = createConsistencyFetch({
1487
+ store,
1488
+ envName: resolved.envName,
1489
+ apiBase: resolved.apiBase,
1490
+ fetchImpl: opts.fetchImpl
1491
+ });
1492
+ const interceptor = buildInterceptor(store, resolved, consistencyFetch);
1404
1493
  const authedFetch = (input, init) => interceptor.execute(new Request(input, init));
1405
1494
  return { fetch: authedFetch, baseUrl: resolved.apiBase };
1406
1495
  }
@@ -2416,7 +2505,7 @@ var eventGetCommand = new Command19("show").description("Get a calendar event by
2416
2505
 
2417
2506
  // src/generated/cli/event/set.ts
2418
2507
  import { Command as Command20 } from "commander";
2419
- var eventUpdateCommand = new Command20("set").description("Update a calendar event").argument("<id>", "id").option("--expected-version <value>", "Optional optimistic lock. Omit to let the server use the current version; pass only to fail the call if someone else has mutated the event since you last read. On mismatch the server returns 409 `VERSION_CONFLICT` with `extra.expected_version` and `extra.actual_version`.").option("--title <value>", "New event title. Omit to leave unchanged.").option("--description <value>", "New description. Markdown formatted (CommonMark + GFM tables, strikethrough, task lists). Pass an empty string to clear; omit to leave unchanged.").option("--start <value>", "Accepts ISO 8601 datetime with offset (e.g. `2026-06-01T12:30:00+08:00`) for timed events, or ISO date-only (e.g. `2026-06-01`) for all-day. The `wspc` CLI additionally accepts natural-language phrases (`tomorrow 12:30pm`, `next Monday 9am`) and resolves them to ISO before sending; the server itself only accepts ISO. All-day uses RFC 5545 exclusive end: a one-day event on 6/1 is `start=2026-06-01, end=2026-06-02`; both endpoints must be the same type.").option("--end <value>", "Accepts ISO 8601 datetime with offset (e.g. `2026-06-01T12:30:00+08:00`) for timed events, or ISO date-only (e.g. `2026-06-01`) for all-day. The `wspc` CLI additionally accepts natural-language phrases (`tomorrow 12:30pm`, `next Monday 9am`) and resolves them to ISO before sending; the server itself only accepts ISO. All-day uses RFC 5545 exclusive end: a one-day event on 6/1 is `start=2026-06-01, end=2026-06-02`; both endpoints must be the same type.").option("-l, --location <value>", "New location. Pass an empty string to clear; omit to leave unchanged.").option("-u, --url <value>", "New meeting link. Pass an empty string to clear; omit to leave unchanged.").option("--status <value>", "Lifecycle status. `confirmed`: the event will happen (default). `tentative`: organizer has not finalized; still visible in lists. `cancelled`: the event was called off but the record is kept so attendees can be notified and history audited; distinct from soft-delete (DELETE `/calendar/events/{id}`) which hides the event from default list responses.").option("--attendee <value>", "If provided, REPLACES the attendee list (after case-insensitive email dedupe, up to 50). Added attendees receive a fresh invitation, kept attendees receive an update email, removed attendees receive a cancellation.", (val, memo) => {
2508
+ var eventUpdateCommand = new Command20("set").description("Update a calendar event").argument("<id>", "id").option("--expected-version <value>", "Optional optimistic lock. Omit to let the server use the current version; pass only to fail the call if someone else has mutated the event since you last read. On mismatch the server returns 409 `VERSION_CONFLICT` and includes the current and sent versions in the message.").option("--title <value>", "New event title. Omit to leave unchanged.").option("--description <value>", "New description. Markdown formatted (CommonMark + GFM tables, strikethrough, task lists). Pass an empty string to clear; omit to leave unchanged.").option("--start <value>", "Accepts ISO 8601 datetime with offset (e.g. `2026-06-01T12:30:00+08:00`) for timed events, or ISO date-only (e.g. `2026-06-01`) for all-day. The `wspc` CLI additionally accepts natural-language phrases (`tomorrow 12:30pm`, `next Monday 9am`) and resolves them to ISO before sending; the server itself only accepts ISO. All-day uses RFC 5545 exclusive end: a one-day event on 6/1 is `start=2026-06-01, end=2026-06-02`; both endpoints must be the same type.").option("--end <value>", "Accepts ISO 8601 datetime with offset (e.g. `2026-06-01T12:30:00+08:00`) for timed events, or ISO date-only (e.g. `2026-06-01`) for all-day. The `wspc` CLI additionally accepts natural-language phrases (`tomorrow 12:30pm`, `next Monday 9am`) and resolves them to ISO before sending; the server itself only accepts ISO. All-day uses RFC 5545 exclusive end: a one-day event on 6/1 is `start=2026-06-01, end=2026-06-02`; both endpoints must be the same type.").option("-l, --location <value>", "New location. Pass an empty string to clear; omit to leave unchanged.").option("-u, --url <value>", "New meeting link. Pass an empty string to clear; omit to leave unchanged.").option("--status <value>", "Lifecycle status. `confirmed`: the event will happen (default). `tentative`: organizer has not finalized; still visible in lists. `cancelled`: the event was called off but the record is kept so attendees can be notified and history audited; distinct from soft-delete (DELETE `/calendar/events/{id}`) which hides the event from default list responses.").option("--attendee <value>", "If provided, REPLACES the attendee list (after case-insensitive email dedupe, up to 50). Added attendees receive a fresh invitation, kept attendees receive an update email, removed attendees receive a cancellation.", (val, memo) => {
2420
2509
  memo.push(val);
2421
2510
  return memo;
2422
2511
  }, []).option("--all-day", "all_day").option("--tz <zone>", "IANA timezone for relative time parsing").action(async (id, opts) => {
@@ -2614,9 +2703,30 @@ var emailDeleteCommand = new Command27("rm").description("Soft-delete inbound em
2614
2703
  render({ kind: "email_delete", display: { "shape": "object", "format": {} } }, result.data);
2615
2704
  });
2616
2705
 
2617
- // src/generated/cli/domain/show.ts
2706
+ // src/generated/cli/domain/rm.ts
2618
2707
  import { Command as Command28 } from "commander";
2619
- var emailDomainGetCommand = new Command28("show").description("Get one cached custom domain").argument("<domain>", "domain").action(async (domain, opts) => {
2708
+ var emailDomainDeleteCommand = new Command28("rm").description("Delete a custom email domain").argument("<domain>", "domain").action(async (domain, opts) => {
2709
+ const client2 = await loadSdkClient();
2710
+ const result = await emailDomainDelete({
2711
+ client: client2._rawClient,
2712
+ path: {
2713
+ domain
2714
+ }
2715
+ });
2716
+ if (result.error || !result.response?.ok) {
2717
+ process.stderr.write(
2718
+ `HTTP ${result.response?.status ?? "?"}: ${JSON.stringify(result.error ?? "unknown error", null, 2)}
2719
+ `
2720
+ );
2721
+ process.exitCode = 1;
2722
+ return;
2723
+ }
2724
+ render({ kind: "email_domain_delete", display: void 0 }, result.data);
2725
+ });
2726
+
2727
+ // src/generated/cli/domain/show.ts
2728
+ import { Command as Command29 } from "commander";
2729
+ var emailDomainGetCommand = new Command29("show").description("Get one cached custom domain").argument("<domain>", "domain").action(async (domain, opts) => {
2620
2730
  const client2 = await loadSdkClient();
2621
2731
  const result = await emailDomainGet({
2622
2732
  client: client2._rawClient,
@@ -2636,8 +2746,8 @@ var emailDomainGetCommand = new Command28("show").description("Get one cached cu
2636
2746
  });
2637
2747
 
2638
2748
  // src/generated/cli/email/show.ts
2639
- import { Command as Command29 } from "commander";
2640
- var emailGetCommand = new Command29("show").description("Get an inbound email by id").argument("<id>", "id").option("--include-html <value>", "When `true`, fetch the HTML body from R2 and include it as `html_body` in the response. Costs an extra R2 read; omit if you only need text.").option("--include-deleted <value>", "When `true`, allow fetching a soft-deleted email. Defaults to `false` (returns 404 for soft-deleted rows).").action(async (id, opts) => {
2749
+ import { Command as Command30 } from "commander";
2750
+ var emailGetCommand = new Command30("show").description("Get an inbound email by id").argument("<id>", "id").option("--include-html <value>", "When `true`, fetch the HTML body from R2 and include it as `html_body` in the response. Costs an extra R2 read; omit if you only need text.").option("--include-deleted <value>", "When `true`, allow fetching a soft-deleted email. Defaults to `false` (returns 404 for soft-deleted rows).").action(async (id, opts) => {
2641
2751
  const client2 = await loadSdkClient();
2642
2752
  const result = await emailGet({
2643
2753
  client: client2._rawClient,
@@ -2661,8 +2771,8 @@ var emailGetCommand = new Command29("show").description("Get an inbound email by
2661
2771
  });
2662
2772
 
2663
2773
  // src/generated/cli/email/ls.ts
2664
- import { Command as Command30 } from "commander";
2665
- var emailListCommand = new Command30("ls").description("List inbound emails").option("--limit <value>", "Max items to return (clamped to 1-100). Defaults to 20 server-side.").option("--alias-email <value>", "If set, only return emails received on this full alias email address.").option("--unread-only <value>", "When `true`, only return emails with `is_read=false`.").option("--since <value>", "Unix epoch milliseconds \u2014 only return emails with `received_at >= since`. Useful for incremental sync.").option("--cursor <value>", "Opaque pagination cursor returned in `next_cursor` of a previous response.").option("--include-deleted <value>", "When `true`, also return soft-deleted emails. Defaults to `false`.").action(async (opts) => {
2774
+ import { Command as Command31 } from "commander";
2775
+ var emailListCommand = new Command31("ls").description("List inbound emails").option("--limit <value>", "Max items to return (clamped to 1-100). Defaults to 20 server-side.").option("--alias-email <value>", "If set, only return emails received on this full alias email address.").option("--unread-only <value>", "When `true`, only return emails with `is_read=false`.").option("--since <value>", "Unix epoch milliseconds \u2014 only return emails with `received_at >= since`. Useful for incremental sync.").option("--cursor <value>", "Opaque pagination cursor returned in `next_cursor` of a previous response.").option("--include-deleted <value>", "When `true`, also return soft-deleted emails. Defaults to `false`.").action(async (opts) => {
2666
2776
  const client2 = await loadSdkClient();
2667
2777
  const result = await emailList({
2668
2778
  client: client2._rawClient,
@@ -2687,8 +2797,8 @@ var emailListCommand = new Command30("ls").description("List inbound emails").op
2687
2797
  });
2688
2798
 
2689
2799
  // src/generated/cli/email/read.ts
2690
- import { Command as Command31 } from "commander";
2691
- var emailMarkReadCommand = new Command31("read").description("Mark inbound emails as read").argument("<id...>", "id").action(async (id, opts) => {
2800
+ import { Command as Command32 } from "commander";
2801
+ var emailMarkReadCommand = new Command32("read").description("Mark inbound emails as read").argument("<id...>", "id").action(async (id, opts) => {
2692
2802
  const idRaw = id;
2693
2803
  const ids = idRaw.length > 0 ? idRaw : void 0;
2694
2804
  const client2 = await loadSdkClient();
@@ -2710,8 +2820,8 @@ var emailMarkReadCommand = new Command31("read").description("Mark inbound email
2710
2820
  });
2711
2821
 
2712
2822
  // src/generated/cli/email/unread.ts
2713
- import { Command as Command32 } from "commander";
2714
- var emailMarkUnreadCommand = new Command32("unread").description("Mark inbound emails as unread").argument("<id...>", "id").action(async (id, opts) => {
2823
+ import { Command as Command33 } from "commander";
2824
+ var emailMarkUnreadCommand = new Command33("unread").description("Mark inbound emails as unread").argument("<id...>", "id").action(async (id, opts) => {
2715
2825
  const idRaw = id;
2716
2826
  const ids = idRaw.length > 0 ? idRaw : void 0;
2717
2827
  const client2 = await loadSdkClient();
@@ -2733,8 +2843,8 @@ var emailMarkUnreadCommand = new Command32("unread").description("Mark inbound e
2733
2843
  });
2734
2844
 
2735
2845
  // src/generated/cli/domain/verify.ts
2736
- import { Command as Command33 } from "commander";
2737
- var emailDomainVerifyCommand = new Command33("verify").description("Verify a custom domain with the provider").argument("<domain>", "domain").action(async (domain, opts) => {
2846
+ import { Command as Command34 } from "commander";
2847
+ var emailDomainVerifyCommand = new Command34("verify").description("Verify a custom domain with the provider").argument("<domain>", "domain").action(async (domain, opts) => {
2738
2848
  const client2 = await loadSdkClient();
2739
2849
  const result = await emailDomainVerify({
2740
2850
  client: client2._rawClient,
@@ -2754,8 +2864,8 @@ var emailDomainVerifyCommand = new Command33("verify").description("Verify a cus
2754
2864
  });
2755
2865
 
2756
2866
  // src/generated/cli/push/config/rm.ts
2757
- import { Command as Command34 } from "commander";
2758
- var pushConfigDeleteCommand = new Command34("rm").description("Remove a push transport").argument("<transport>", "transport").action(async (transport, opts) => {
2867
+ import { Command as Command35 } from "commander";
2868
+ var pushConfigDeleteCommand = new Command35("rm").description("Remove a push transport").argument("<transport>", "transport").action(async (transport, opts) => {
2759
2869
  const client2 = await loadSdkClient();
2760
2870
  const result = await pushConfigDelete({
2761
2871
  client: client2._rawClient,
@@ -2775,8 +2885,8 @@ var pushConfigDeleteCommand = new Command34("rm").description("Remove a push tra
2775
2885
  });
2776
2886
 
2777
2887
  // src/generated/cli/push/config/set.ts
2778
- import { Command as Command35 } from "commander";
2779
- var pushConfigSetCommand = new Command35("set").description("Register or update a push transport").option("--transport <value>", "Transport discriminator. `telegram` is the only supported value today \u2014 push delivers via a Telegram bot DM. Future transports (web push, iOS/Android, generic webhook) will be added as additional discriminator values.").option("--target-bot-username <value>", "Telegram bot username (with leading `@`, 5\u201332 alphanumeric/underscore characters). This is the bot the user has already started a chat with \u2014 wspc DMs notifications to it via the Telegram Bot API.").action(async (opts) => {
2888
+ import { Command as Command36 } from "commander";
2889
+ var pushConfigSetCommand = new Command36("set").description("Register or update a push transport").option("--transport <value>", "Transport discriminator. `telegram` is the only supported value today \u2014 push delivers via a Telegram bot DM. Future transports (web push, iOS/Android, generic webhook) will be added as additional discriminator values.").option("--target-bot-username <value>", "Telegram bot username (with leading `@`, 5\u201332 alphanumeric/underscore characters). This is the bot the user has already started a chat with \u2014 wspc DMs notifications to it via the Telegram Bot API.").action(async (opts) => {
2780
2890
  const client2 = await loadSdkClient();
2781
2891
  const result = await pushConfigSet({
2782
2892
  client: client2._rawClient,
@@ -2799,8 +2909,8 @@ var pushConfigSetCommand = new Command35("set").description("Register or update
2799
2909
  });
2800
2910
 
2801
2911
  // src/generated/cli/push/config/show.ts
2802
- import { Command as Command36 } from "commander";
2803
- var pushConfigGetCommand = new Command36("show").description("List the caller's push transports").action(async (opts) => {
2912
+ import { Command as Command37 } from "commander";
2913
+ var pushConfigGetCommand = new Command37("show").description("List the caller's push transports").action(async (opts) => {
2804
2914
  const client2 = await loadSdkClient();
2805
2915
  const result = await pushConfigGet({
2806
2916
  client: client2._rawClient
@@ -2817,8 +2927,8 @@ var pushConfigGetCommand = new Command36("show").description("List the caller's
2817
2927
  });
2818
2928
 
2819
2929
  // src/generated/cli/push/test.ts
2820
- import { Command as Command37 } from "commander";
2821
- var pushTestCommand = new Command37("test").description("Send a test push notification").option("--transport <value>", "Which transport to send the test message through. Must match a transport the caller has already registered via `POST /push/config`; today only `telegram` is supported.").action(async (opts) => {
2930
+ import { Command as Command38 } from "commander";
2931
+ var pushTestCommand = new Command38("test").description("Send a test push notification").option("--transport <value>", "Which transport to send the test message through. Must match a transport the caller has already registered via `POST /push/config`; today only `telegram` is supported.").action(async (opts) => {
2822
2932
  const client2 = await loadSdkClient();
2823
2933
  const result = await pushTest({
2824
2934
  client: client2._rawClient,
@@ -2841,8 +2951,8 @@ var pushTestCommand = new Command37("test").description("Send a test push notifi
2841
2951
  });
2842
2952
 
2843
2953
  // src/generated/cli/todo/comment/add.ts
2844
- import { Command as Command38 } from "commander";
2845
- var todoCommentCreateCommand = new Command38("add").description("Add a comment to a todo").argument("<id>", "id").argument("<content>", "content").action(async (id, content, opts) => {
2954
+ import { Command as Command39 } from "commander";
2955
+ var todoCommentCreateCommand = new Command39("add").description("Add a comment to a todo").argument("<id>", "id").argument("<content>", "content").action(async (id, content, opts) => {
2846
2956
  const client2 = await loadSdkClient();
2847
2957
  const result = await todoCommentCreate({
2848
2958
  client: client2._rawClient,
@@ -2865,8 +2975,8 @@ var todoCommentCreateCommand = new Command38("add").description("Add a comment t
2865
2975
  });
2866
2976
 
2867
2977
  // src/generated/cli/todo/comment/ls.ts
2868
- import { Command as Command39 } from "commander";
2869
- var todoCommentListCommand = new Command39("ls").description("List comments on a todo").argument("<id>", "id").option("--order <value>", "order").option("--include-deleted <value>", "include_deleted").option("--limit <value>", "Max comments to return. Clamped to [1, 200]. Default 50 server-side.").option("--cursor <value>", "Opaque pagination cursor returned in `next_cursor` of a previous response.").action(async (id, opts) => {
2978
+ import { Command as Command40 } from "commander";
2979
+ var todoCommentListCommand = new Command40("ls").description("List comments on a todo").argument("<id>", "id").option("--order <value>", "order").option("--include-deleted <value>", "include_deleted").option("--limit <value>", "Max comments to return. Clamped to [1, 200]. Default 50 server-side.").option("--cursor <value>", "Opaque pagination cursor returned in `next_cursor` of a previous response.").action(async (id, opts) => {
2870
2980
  const client2 = await loadSdkClient();
2871
2981
  const result = await todoCommentList({
2872
2982
  client: client2._rawClient,
@@ -2892,8 +3002,8 @@ var todoCommentListCommand = new Command39("ls").description("List comments on a
2892
3002
  });
2893
3003
 
2894
3004
  // src/generated/cli/todo/project/add.ts
2895
- import { Command as Command40 } from "commander";
2896
- var projectCreateCommand = new Command40("add").description("Create a project").argument("<name>", "name").option("--default-todo-type-id <value>", "default_todo_type_id").action(async (name, opts) => {
3005
+ import { Command as Command41 } from "commander";
3006
+ var projectCreateCommand = new Command41("add").description("Create a project").argument("<name>", "name").option("--default-todo-type-id <value>", "default_todo_type_id").action(async (name, opts) => {
2897
3007
  const client2 = await loadSdkClient();
2898
3008
  const result = await projectCreate({
2899
3009
  client: client2._rawClient,
@@ -2914,8 +3024,8 @@ var projectCreateCommand = new Command40("add").description("Create a project").
2914
3024
  });
2915
3025
 
2916
3026
  // src/generated/cli/todo/project/ls.ts
2917
- import { Command as Command41 } from "commander";
2918
- var projectListCommand = new Command41("ls").description("List projects").option("--include-deleted <value>", "Set to `true` to include soft-deleted projects in the response.").action(async (opts) => {
3027
+ import { Command as Command42 } from "commander";
3028
+ var projectListCommand = new Command42("ls").description("List projects").option("--include-deleted <value>", "Set to `true` to include soft-deleted projects in the response.").action(async (opts) => {
2919
3029
  const client2 = await loadSdkClient();
2920
3030
  const result = await projectList({
2921
3031
  client: client2._rawClient,
@@ -2935,8 +3045,8 @@ var projectListCommand = new Command41("ls").description("List projects").option
2935
3045
  });
2936
3046
 
2937
3047
  // src/generated/cli/todo/rule/add.ts
2938
- import { Command as Command42 } from "commander";
2939
- var recurrenceRuleCreateCommand = new Command42("add").description("Create a recurring todo rule").argument("<title>", "title").option("--rrule <value>", "rrule").option("--dtstart <value>", "dtstart").option("--description <value>", "description").option("--parent-id <value>", "parent_id").option("-p, --project <value>", "Project for the recurrence rule, its template todo, and all materialized instances. Must be an active project in the caller's organization.").option("-t, --type <value>", "type_id").action(async (title, opts) => {
3048
+ import { Command as Command43 } from "commander";
3049
+ var recurrenceRuleCreateCommand = new Command43("add").description("Create a recurring todo rule").argument("<title>", "title").option("--rrule <value>", "rrule").option("--dtstart <value>", "dtstart").option("--description <value>", "description").option("--parent-id <value>", "parent_id").option("-p, --project <value>", "Project for the recurrence rule, its template todo, and all materialized instances. Must be an active project in the caller's organization.").option("-t, --type <value>", "type_id").action(async (title, opts) => {
2940
3050
  const client2 = await loadSdkClient();
2941
3051
  const result = await recurrenceRuleCreate({
2942
3052
  client: client2._rawClient,
@@ -2962,8 +3072,8 @@ var recurrenceRuleCreateCommand = new Command42("add").description("Create a rec
2962
3072
  });
2963
3073
 
2964
3074
  // src/generated/cli/todo/rule/ls.ts
2965
- import { Command as Command43 } from "commander";
2966
- var recurrenceRuleListCommand = new Command43("ls").description("List recurring todo rules").option("--project-id <value>", "Project id filter. Required. Unknown, cross-organization, or soft-deleted project ids return NOT_FOUND.").option("--user-id <value>", "user_id").action(async (opts) => {
3075
+ import { Command as Command44 } from "commander";
3076
+ var recurrenceRuleListCommand = new Command44("ls").description("List recurring todo rules").option("--project-id <value>", "Project id filter. Required. Unknown, cross-organization, or soft-deleted project ids return NOT_FOUND.").option("--user-id <value>", "user_id").action(async (opts) => {
2967
3077
  const client2 = await loadSdkClient();
2968
3078
  const result = await recurrenceRuleList({
2969
3079
  client: client2._rawClient,
@@ -2984,7 +3094,7 @@ var recurrenceRuleListCommand = new Command43("ls").description("List recurring
2984
3094
  });
2985
3095
 
2986
3096
  // src/generated/cli/todo/add.ts
2987
- import { Command as Command44 } from "commander";
3097
+ import { Command as Command45 } from "commander";
2988
3098
 
2989
3099
  // src/handwritten/utils/parse-json-field.ts
2990
3100
  function parseJsonField(raw, flag) {
@@ -2999,7 +3109,7 @@ function parseJsonField(raw, flag) {
2999
3109
  }
3000
3110
 
3001
3111
  // src/generated/cli/todo/add.ts
3002
- var todoCreateCommand = new Command44("add").description("Create a todo").argument("<title>", "title").option("-p, --project <value>", "Project id to assign this todo to. It must be an active project in the caller's organization.").option("--description <value>", "Free-form details about the todo. Fully supports GFM Markdown (tables, strikethrough, task lists). Stored verbatim; client applications are responsible for rendering. Optional. Passing `null` is strictly rejected.").option("--parent-id <value>", "Parent todo ID (`tod_<ULID>`) to attach this todo as a child under another todo. Omit or pass `null` to create a root-level todo. Nesting is limited to one level; attempting to set a child todo as a parent will trigger `PARENT_IS_CHILD`. To make a subtask appear on every occurrence of a recurring rule, set this to that rule's template todo id (the template id returned when the rule is created); the server re-materializes future occurrences so each carries the subtask.").option("--status <value>", "Initial status of the todo. Omit to default to `open`. Allowed values: `open`, `in_progress`, `done`, `cancelled`.").option("--due-at <value>", 'Optional calendar due date in ISO date-only format (`YYYY-MM-DD`). Stored without timezone offsets to represent the same local calendar day globally. Pass `""` or omit the field to skip setting a due date. Passing `null` is strictly rejected.').option("--type-id <value>", "Type id this todo belongs to. Omit to use the project's default type. When project_id is also supplied, the type must belong to the same project. New server-generated type ids use typ_<ULID>; legacy ids remain accepted.").option("--custom-fields <value>", "Custom field values keyed by the field's immutable `key` (not the human `label`). Each value must match the declared field type: string fields require string values, and string_array fields require string arrays. Providing a key that is not declared on the resolved todo type is strictly rejected with `UNDECLARED_FIELD`. Missing required fields that lack a default value are rejected with `FIELD_REQUIRED`. Defaults declared on the type are auto-applied at create time.").action(async (title, opts) => {
3112
+ var todoCreateCommand = new Command45("add").description("Create a todo").argument("<title>", "title").option("-p, --project <value>", "Project id to assign this todo to. It must be an active project in the caller's organization.").option("--description <value>", "Free-form details about the todo. Fully supports GFM Markdown (tables, strikethrough, task lists). Stored verbatim; client applications are responsible for rendering. Optional. Passing `null` is strictly rejected.").option("--parent-id <value>", "Parent todo ID (`tod_<ULID>`) to attach this todo as a child under another todo. Omit or pass `null` to create a root-level todo. Nesting is limited to one level; attempting to set a child todo as a parent will trigger `PARENT_IS_CHILD`. To make a subtask appear on every occurrence of a recurring rule, set this to that rule's template todo id (the template id returned when the rule is created); the server re-materializes future occurrences so each carries the subtask.").option("--status <value>", "Initial status of the todo. Omit to default to `open`. Allowed values: `open`, `in_progress`, `done`, `cancelled`.").option("--due-at <value>", 'Optional calendar due date in ISO date-only format (`YYYY-MM-DD`). Stored without timezone offsets to represent the same local calendar day globally. Pass `""` or omit the field to skip setting a due date. Passing `null` is strictly rejected.').option("--type-id <value>", "Type id this todo belongs to. Omit to use the project's default type. When project_id is also supplied, the type must belong to the same project. New server-generated type ids use typ_<ULID>; legacy ids remain accepted.").option("--custom-fields <value>", "Custom field values keyed by the field's immutable `key` (not the human `label`). Each value must match the declared field type: string fields require string values, and string_array fields require string arrays. Providing a key that is not declared on the resolved todo type is strictly rejected with `UNDECLARED_FIELD`. Missing required fields that lack a default value are rejected with `FIELD_REQUIRED`. Defaults declared on the type are auto-applied at create time.").action(async (title, opts) => {
3003
3113
  const client2 = await loadSdkClient();
3004
3114
  const result = await todoCreate({
3005
3115
  client: client2._rawClient,
@@ -3026,8 +3136,8 @@ var todoCreateCommand = new Command44("add").description("Create a todo").argume
3026
3136
  });
3027
3137
 
3028
3138
  // src/generated/cli/todo/ls.ts
3029
- import { Command as Command45 } from "commander";
3030
- var todoListCommand = new Command45("ls").description("List todos with filters").option("-p, --project <value>", "Filter by project. Required. Unknown, cross-organization, or soft-deleted project ids return NOT_FOUND.").option("--user-id <value>", "user_id").option("--parent-id <value>", "parent_id").option("-s, --status <value>", "status").option("--include-deleted <value>", "include_deleted").option("--include-templates <value>", "include_templates").option("--due-after <value>", "due_after").option("--due-before <value>", "due_before").option("--type-id <value>", "type_id").option("--sort-by <value>", "sort_by").option("--order <value>", "order").option("--include-orphan-fields <value>", "include_orphan_fields").option("--limit <value>", "Max todos to return. Clamped to [1, 200]. Default 50 server-side.").option("--cursor <value>", "Opaque pagination cursor returned in `next_cursor` of a previous response.").action(async (opts) => {
3139
+ import { Command as Command46 } from "commander";
3140
+ var todoListCommand = new Command46("ls").description("List todos with filters").option("-p, --project <value>", "Filter by project. Required. Unknown, cross-organization, or soft-deleted project ids return NOT_FOUND.").option("--user-id <value>", "user_id").option("--parent-id <value>", "parent_id").option("-s, --status <value>", "status").option("--include-deleted <value>", "include_deleted").option("--include-templates <value>", "include_templates").option("--due-after <value>", "due_after").option("--due-before <value>", "due_before").option("--type-id <value>", "type_id").option("--sort-by <value>", "sort_by").option("--order <value>", "order").option("--include-orphan-fields <value>", "include_orphan_fields").option("--limit <value>", "Max todos to return. Clamped to [1, 200]. Default 50 server-side.").option("--cursor <value>", "Opaque pagination cursor returned in `next_cursor` of a previous response.").action(async (opts) => {
3031
3141
  const client2 = await loadSdkClient();
3032
3142
  const result = await todoList({
3033
3143
  client: client2._rawClient,
@@ -3060,8 +3170,8 @@ var todoListCommand = new Command45("ls").description("List todos with filters")
3060
3170
  });
3061
3171
 
3062
3172
  // src/generated/cli/todo/type/ls.ts
3063
- import { Command as Command46 } from "commander";
3064
- var todoTypeListCommand = new Command46("ls").description("List todo types").option("--project-id <value>", "Project id filter. Required. Unknown, cross-organization, or soft-deleted project ids return NOT_FOUND.").option("--user-id <value>", "user_id").option("--include-deleted <value>", "include_deleted").action(async (opts) => {
3173
+ import { Command as Command47 } from "commander";
3174
+ var todoTypeListCommand = new Command47("ls").description("List todo types").option("--project-id <value>", "Project id filter. Required. Unknown, cross-organization, or soft-deleted project ids return NOT_FOUND.").option("--user-id <value>", "user_id").option("--include-deleted <value>", "include_deleted").action(async (opts) => {
3065
3175
  const client2 = await loadSdkClient();
3066
3176
  const result = await todoTypeList({
3067
3177
  client: client2._rawClient,
@@ -3083,8 +3193,8 @@ var todoTypeListCommand = new Command46("ls").description("List todo types").opt
3083
3193
  });
3084
3194
 
3085
3195
  // src/generated/cli/todo/comment/rm.ts
3086
- import { Command as Command47 } from "commander";
3087
- var todoCommentDeleteCommand = new Command47("rm").description("Soft-delete a comment").argument("<id>", "id").action(async (id, opts) => {
3196
+ import { Command as Command48 } from "commander";
3197
+ var todoCommentDeleteCommand = new Command48("rm").description("Soft-delete a comment").argument("<id>", "id").action(async (id, opts) => {
3088
3198
  const client2 = await loadSdkClient();
3089
3199
  const result = await todoCommentDelete({
3090
3200
  client: client2._rawClient,
@@ -3104,8 +3214,8 @@ var todoCommentDeleteCommand = new Command47("rm").description("Soft-delete a co
3104
3214
  });
3105
3215
 
3106
3216
  // src/generated/cli/todo/comment/edit.ts
3107
- import { Command as Command48 } from "commander";
3108
- var todoCommentUpdateCommand = new Command48("edit").description("Edit a comment").argument("<id>", "id").argument("<content>", "content").action(async (id, content, opts) => {
3217
+ import { Command as Command49 } from "commander";
3218
+ var todoCommentUpdateCommand = new Command49("edit").description("Edit a comment").argument("<id>", "id").argument("<content>", "content").action(async (id, content, opts) => {
3109
3219
  const client2 = await loadSdkClient();
3110
3220
  const result = await todoCommentUpdate({
3111
3221
  client: client2._rawClient,
@@ -3128,8 +3238,8 @@ var todoCommentUpdateCommand = new Command48("edit").description("Edit a comment
3128
3238
  });
3129
3239
 
3130
3240
  // src/generated/cli/todo/rule/rm.ts
3131
- import { Command as Command49 } from "commander";
3132
- var recurrenceRuleDeleteCommand = new Command49("rm").description("Delete a recurring todo rule").argument("<id>", "id").option("--expected-version <value>", "expected_version").action(async (id, opts) => {
3241
+ import { Command as Command50 } from "commander";
3242
+ var recurrenceRuleDeleteCommand = new Command50("rm").description("Delete a recurring todo rule").argument("<id>", "id").option("--expected-version <value>", "expected_version").action(async (id, opts) => {
3133
3243
  const client2 = await loadSdkClient();
3134
3244
  const result = await recurrenceRuleDelete({
3135
3245
  client: client2._rawClient,
@@ -3152,8 +3262,8 @@ var recurrenceRuleDeleteCommand = new Command49("rm").description("Delete a recu
3152
3262
  });
3153
3263
 
3154
3264
  // src/generated/cli/todo/rule/show.ts
3155
- import { Command as Command50 } from "commander";
3156
- var recurrenceRuleGetCommand = new Command50("show").description("Get a recurring todo rule").argument("<id>", "id").action(async (id, opts) => {
3265
+ import { Command as Command51 } from "commander";
3266
+ var recurrenceRuleGetCommand = new Command51("show").description("Get a recurring todo rule").argument("<id>", "id").action(async (id, opts) => {
3157
3267
  const client2 = await loadSdkClient();
3158
3268
  const result = await recurrenceRuleGet({
3159
3269
  client: client2._rawClient,
@@ -3173,8 +3283,8 @@ var recurrenceRuleGetCommand = new Command50("show").description("Get a recurrin
3173
3283
  });
3174
3284
 
3175
3285
  // src/generated/cli/todo/rm.ts
3176
- import { Command as Command51 } from "commander";
3177
- var todoDeleteCommand = new Command51("rm").description("Soft-delete a todo").argument("<id>", "id").option("--expected-version <value>", "expected_version").option("--cascade <value>", "cascade").action(async (id, opts) => {
3286
+ import { Command as Command52 } from "commander";
3287
+ var todoDeleteCommand = new Command52("rm").description("Soft-delete a todo").argument("<id>", "id").option("--expected-version <value>", "expected_version").option("--cascade <value>", "cascade").action(async (id, opts) => {
3178
3288
  const client2 = await loadSdkClient();
3179
3289
  const result = await todoDelete({
3180
3290
  client: client2._rawClient,
@@ -3198,8 +3308,8 @@ var todoDeleteCommand = new Command51("rm").description("Soft-delete a todo").ar
3198
3308
  });
3199
3309
 
3200
3310
  // src/generated/cli/todo/show.ts
3201
- import { Command as Command52 } from "commander";
3202
- var todoGetCommand = new Command52("show").description("Get a todo by id").argument("<id>", "id").option("--include-deleted <value>", "include_deleted").option("--include-orphan-fields <value>", "include_orphan_fields").action(async (id, opts) => {
3311
+ import { Command as Command53 } from "commander";
3312
+ var todoGetCommand = new Command53("show").description("Get a todo by id").argument("<id>", "id").option("--include-deleted <value>", "include_deleted").option("--include-orphan-fields <value>", "include_orphan_fields").action(async (id, opts) => {
3203
3313
  const client2 = await loadSdkClient();
3204
3314
  const result = await todoGet({
3205
3315
  client: client2._rawClient,
@@ -3224,8 +3334,8 @@ var todoGetCommand = new Command52("show").description("Get a todo by id").argum
3224
3334
  });
3225
3335
 
3226
3336
  // src/generated/cli/todo/update.ts
3227
- import { Command as Command53 } from "commander";
3228
- var todoUpdateCommand = new Command53("update").description("Update a todo").argument("<id>", "id").option("--expected-version <value>", "expected_version").option("--title <value>", "New title. Omit to leave the existing title unchanged. Must be non-empty when supplied.").option("--description <value>", 'New description. Markdown formatted (CommonMark + GFM tables, strikethrough, task lists). Pass empty string `""` explicitly to clear an existing description, or omit to leave unchanged. Passing `null` is strictly rejected.').option("--parent-id <value>", "Re-parent the todo. Pass a valid parent ID to attach under another todo, pass `null` to move it back to the root level, or omit to leave unchanged. Nesting is limited to one level; attempting to set a child todo as a parent will trigger `PARENT_IS_CHILD`.").option("--status <value>", "New status of the todo. Allowed transitions: `open` \u2794 `in_progress` \u2794 `done`. `cancelled` represents a terminal state. Transitioning to `done` automatically emits a `captureTodoCompleted` analytics event. Omit to leave the existing status unchanged.").option("--due-at <value>", 'Update calendar due date in ISO date-only format (`YYYY-MM-DD`). Pass `""` explicitly to clear an existing due date, or omit to leave it unchanged. Passing `null` is strictly rejected.').option("--type-id <value>", "Re-assign this todo to a different active type. The new type must belong to the todo's same project; otherwise the request fails with TYPE_PROJECT_MISMATCH. New server-generated type ids use typ_<ULID>; legacy ids remain accepted.").option("--custom-fields <value>", "PATCH semantics: only the keys present in this map change. Pass `null` for a key (e.g. `custom_fields: { priority: null }`) to explicitly delete that custom field value. Array values are replaced wholesale with no element-level diff. Providing a key that is not declared on the effective todo type is rejected with `UNDECLARED_FIELD`.").option("--user-id <value>", "Reassign the owner (assignee) user ID of this todo. Target user must belong to the same organization.").action(async (id, opts) => {
3337
+ import { Command as Command54 } from "commander";
3338
+ var todoUpdateCommand = new Command54("update").description("Update a todo").argument("<id>", "id").option("--expected-version <value>", "expected_version").option("--title <value>", "New title. Omit to leave the existing title unchanged. Must be non-empty when supplied.").option("--description <value>", 'New description. Markdown formatted (CommonMark + GFM tables, strikethrough, task lists). Pass empty string `""` explicitly to clear an existing description, or omit to leave unchanged. Passing `null` is strictly rejected.').option("--parent-id <value>", "Re-parent the todo. Pass a valid parent ID to attach under another todo, pass `null` to move it back to the root level, or omit to leave unchanged. Nesting is limited to one level; attempting to set a child todo as a parent will trigger `PARENT_IS_CHILD`.").option("--status <value>", "New status of the todo. Allowed transitions: `open` \u2794 `in_progress` \u2794 `done`. `cancelled` represents a terminal state. Transitioning to `done` automatically emits a `captureTodoCompleted` analytics event. Omit to leave the existing status unchanged.").option("--due-at <value>", 'Update calendar due date in ISO date-only format (`YYYY-MM-DD`). Pass `""` explicitly to clear an existing due date, or omit to leave it unchanged. Passing `null` is strictly rejected.').option("--type-id <value>", "Re-assign this todo to a different active type. The new type must belong to the todo's same project; otherwise the request fails with TYPE_PROJECT_MISMATCH. New server-generated type ids use typ_<ULID>; legacy ids remain accepted.").option("--custom-fields <value>", "PATCH semantics: only the keys present in this map change. Pass `null` for a key (e.g. `custom_fields: { priority: null }`) to explicitly delete that custom field value. Array values are replaced wholesale with no element-level diff. Providing a key that is not declared on the effective todo type is rejected with `UNDECLARED_FIELD`.").option("--user-id <value>", "Reassign the owner (assignee) user ID of this todo. Target user must belong to the same organization.").action(async (id, opts) => {
3229
3339
  const client2 = await loadSdkClient();
3230
3340
  const result = await todoUpdate({
3231
3341
  client: client2._rawClient,
@@ -3291,6 +3401,7 @@ function registerGeneratedCommands(root) {
3291
3401
  const root_domain = root.command("domain").description("domain commands");
3292
3402
  root_domain.addCommand(emailDomainCreateCommand);
3293
3403
  root_domain.addCommand(emailDomainListCommand);
3404
+ root_domain.addCommand(emailDomainDeleteCommand);
3294
3405
  root_domain.addCommand(emailDomainGetCommand);
3295
3406
  root_domain.addCommand(emailDomainVerifyCommand);
3296
3407
  const root_email = root.command("email").description("email commands");
@@ -3329,12 +3440,17 @@ function registerGeneratedCommands(root) {
3329
3440
  }
3330
3441
 
3331
3442
  // src/handwritten/commands/login.ts
3332
- import { Command as Command54 } from "commander";
3443
+ import { Command as Command55 } from "commander";
3333
3444
 
3334
3445
  // src/handwritten/auth/device-flow.ts
3335
3446
  var DEFAULT_SLEEP = (ms) => new Promise((r) => setTimeout(r, ms));
3336
3447
  async function runDeviceFlow(opts) {
3337
- const fetchImpl = opts.fetchImpl ?? fetch;
3448
+ const fetchImpl = opts.store && opts.envName ? createConsistencyFetch({
3449
+ store: opts.store,
3450
+ envName: opts.envName,
3451
+ apiBase: opts.baseUrl,
3452
+ fetchImpl: opts.fetchImpl
3453
+ }) : opts.fetchImpl ?? fetch;
3338
3454
  const sleep = opts.sleepMs ?? DEFAULT_SLEEP;
3339
3455
  const codeRes = await fetchImpl(`${opts.baseUrl}/auth/oauth/device`, {
3340
3456
  method: "POST",
@@ -3390,10 +3506,19 @@ async function runDeviceFlow(opts) {
3390
3506
  var DEFAULT_CLIENT_NAME = "wspc CLI";
3391
3507
  var DEFAULT_REDIRECT_URI = "http://localhost";
3392
3508
  async function ensureClientId(opts) {
3393
- const fetchImpl = opts.fetchImpl ?? fetch;
3509
+ const fetchImpl = createConsistencyFetch({
3510
+ store: opts.store,
3511
+ envName: opts.envName,
3512
+ apiBase: opts.baseUrl,
3513
+ fetchImpl: opts.fetchImpl
3514
+ });
3394
3515
  const c = await opts.store.read();
3395
3516
  const existing = c.envs[opts.envName]?.client_id;
3396
3517
  if (existing) return existing;
3518
+ const targetEnv = c.envs[opts.envName] ??= { api_base: opts.baseUrl, accounts: {} };
3519
+ targetEnv.api_base = opts.baseUrl;
3520
+ targetEnv.accounts ??= {};
3521
+ await opts.store.write(c);
3397
3522
  const res = await fetchImpl(`${opts.baseUrl}/auth/oauth/register`, {
3398
3523
  method: "POST",
3399
3524
  headers: { "content-type": "application/json" },
@@ -3419,7 +3544,12 @@ async function ensureClientId(opts) {
3419
3544
 
3420
3545
  // src/handwritten/auth/fetch-me.ts
3421
3546
  async function fetchMe(opts) {
3422
- const f = opts.fetchImpl ?? fetch;
3547
+ const f = opts.store && opts.envName ? createConsistencyFetch({
3548
+ store: opts.store,
3549
+ envName: opts.envName,
3550
+ apiBase: opts.baseUrl,
3551
+ fetchImpl: opts.fetchImpl
3552
+ }) : opts.fetchImpl ?? fetch;
3423
3553
  const res = await f(`${opts.baseUrl}/auth/me`, {
3424
3554
  headers: { authorization: `Bearer ${opts.token}` }
3425
3555
  });
@@ -3448,7 +3578,16 @@ async function runLogin(opts) {
3448
3578
  const now = opts.now ?? Date.now;
3449
3579
  const me = opts.fetchMe ?? ((o) => fetchMe(o));
3450
3580
  if (opts.apiKey) {
3451
- const who2 = await me({ baseUrl: opts.baseUrl, token: opts.apiKey });
3581
+ const initial = await opts.store.read();
3582
+ getOrCreateEnv(initial, envName, opts.baseUrl);
3583
+ initial.current_env = envName;
3584
+ await opts.store.write(initial);
3585
+ const who2 = await me({
3586
+ baseUrl: opts.baseUrl,
3587
+ token: opts.apiKey,
3588
+ store: opts.store,
3589
+ envName
3590
+ });
3452
3591
  const c2 = await opts.store.read();
3453
3592
  const env2 = getOrCreateEnv(c2, envName, opts.baseUrl);
3454
3593
  const prev2 = env2.accounts[who2.email] ?? { email: who2.email };
@@ -3474,6 +3613,8 @@ async function runLogin(opts) {
3474
3613
  const result = await flow({
3475
3614
  baseUrl: opts.baseUrl,
3476
3615
  clientId,
3616
+ store: opts.store,
3617
+ envName,
3477
3618
  onPrompt: (p) => {
3478
3619
  const prompt = p;
3479
3620
  opts.output.writeJson({ event: "device_code_issued", ...prompt });
@@ -3486,7 +3627,12 @@ async function runLogin(opts) {
3486
3627
  `);
3487
3628
  }
3488
3629
  });
3489
- const who = await me({ baseUrl: opts.baseUrl, token: result.access_token });
3630
+ const who = await me({
3631
+ baseUrl: opts.baseUrl,
3632
+ token: result.access_token,
3633
+ store: opts.store,
3634
+ envName
3635
+ });
3490
3636
  const c = await opts.store.read();
3491
3637
  const env = getOrCreateEnv(c, envName, opts.baseUrl);
3492
3638
  const prev = env.accounts[who.email] ?? { email: who.email };
@@ -3508,9 +3654,18 @@ async function runLogin(opts) {
3508
3654
  }
3509
3655
 
3510
3656
  // src/handwritten/commands/login.ts
3511
- var loginCommand = new Command54("login").description("Log in via OAuth device flow (default) or API key").option("--api-key <key>", "Log in with a wspc API key (escape hatch)").option("--json", "Emit machine-readable events to stdout").action(async (opts) => {
3657
+ function resolveLoginTarget(opts, env) {
3658
+ const baseUrl = opts.apiBase ?? env.WSPC_API_BASE ?? API_BASE;
3659
+ const envName = opts.env ?? (baseUrl === API_BASE ? "prod" : "local");
3660
+ return { baseUrl, envName };
3661
+ }
3662
+ function wantsJson(opts, env) {
3663
+ return opts.json === true || env.WSPC_OUTPUT === "json";
3664
+ }
3665
+ var loginCommand = new Command55("login").description("Log in via OAuth device flow (default) or API key").option("--api-key <key>", "Log in with a wspc API key (escape hatch)").option("--api-base <url>", "Target API base URL (default: production)").option("--env <name>", "Config env name to store credentials under").option("--json", "Emit machine-readable events to stdout").action(async (opts) => {
3512
3666
  const store = new ConfigStore();
3513
- const output = opts.json ? { write: () => {
3667
+ const { baseUrl, envName } = resolveLoginTarget(opts, process.env);
3668
+ const output = wantsJson(opts, process.env) ? { write: () => {
3514
3669
  }, writeJson: (e) => process.stdout.write(JSON.stringify(e) + "\n") } : {
3515
3670
  write: (s) => process.stdout.write(s + "\n"),
3516
3671
  writeJson: () => {
@@ -3518,14 +3673,15 @@ var loginCommand = new Command54("login").description("Log in via OAuth device f
3518
3673
  };
3519
3674
  await runLogin({
3520
3675
  store,
3521
- baseUrl: API_BASE,
3676
+ baseUrl,
3677
+ envName,
3522
3678
  apiKey: opts.apiKey,
3523
3679
  output
3524
3680
  });
3525
3681
  });
3526
3682
 
3527
3683
  // src/handwritten/commands/logout.ts
3528
- import { Command as Command55 } from "commander";
3684
+ import { Command as Command56 } from "commander";
3529
3685
 
3530
3686
  // src/handwritten/auth/logout.ts
3531
3687
  async function runLogout(opts) {
@@ -3553,7 +3709,7 @@ async function runLogout(opts) {
3553
3709
  }
3554
3710
 
3555
3711
  // src/handwritten/commands/logout.ts
3556
- var logoutCommand = new Command55("logout").description("Log out an account (default: the active account in the current env)").argument("[email]", "Email of the account to log out").option("--all", "Log out every account in the current env").action(async (email, opts) => {
3712
+ var logoutCommand = new Command56("logout").description("Log out an account (default: the active account in the current env)").argument("[email]", "Email of the account to log out").option("--all", "Log out every account in the current env").action(async (email, opts) => {
3557
3713
  const res = await runLogout({ store: new ConfigStore(), email, all: opts.all });
3558
3714
  if (res.removed.length === 0) {
3559
3715
  process.stdout.write("nothing to log out\n");
@@ -3566,7 +3722,7 @@ var logoutCommand = new Command55("logout").description("Log out an account (def
3566
3722
  });
3567
3723
 
3568
3724
  // src/handwritten/commands/whoami.ts
3569
- import { Command as Command56 } from "commander";
3725
+ import { Command as Command57 } from "commander";
3570
3726
  var ENV_DISPLAY = {
3571
3727
  shape: "object",
3572
3728
  fields: ["name", "api_base", "account", "actor", "agent_label"]
@@ -3598,7 +3754,7 @@ async function backfillActiveEmail(store, envName, email, userId) {
3598
3754
  await store.write(cfg);
3599
3755
  }
3600
3756
  }
3601
- var whoamiCommand = new Command56("whoami").description("Show the active env, signed-in account, and organization").action(async () => {
3757
+ var whoamiCommand = new Command57("whoami").description("Show the active env, signed-in account, and organization").action(async () => {
3602
3758
  const store = new ConfigStore();
3603
3759
  const config = await store.read();
3604
3760
  let resolved;
@@ -3651,8 +3807,8 @@ function printLoggedOut() {
3651
3807
  }
3652
3808
 
3653
3809
  // src/handwritten/commands/config.ts
3654
- import { Command as Command57 } from "commander";
3655
- var configCommand = new Command57("config").description("Manage wspc local config");
3810
+ import { Command as Command58 } from "commander";
3811
+ var configCommand = new Command58("config").description("Manage wspc local config");
3656
3812
  registerRenderer("config_show", (data) => {
3657
3813
  const d = data;
3658
3814
  if (d.envs.length === 0) {
@@ -3723,7 +3879,7 @@ configCommand.command("use <env>").description("Switch current_env").action(asyn
3723
3879
  });
3724
3880
 
3725
3881
  // src/handwritten/commands/account.ts
3726
- import { Command as Command58 } from "commander";
3882
+ import { Command as Command59 } from "commander";
3727
3883
  async function listAccounts(store) {
3728
3884
  const c = await store.read();
3729
3885
  const envName = c.current_env;
@@ -3764,7 +3920,7 @@ registerRenderer("account_ls", (data) => {
3764
3920
  ]);
3765
3921
  process.stdout.write(table(headers, body));
3766
3922
  });
3767
- var accountCommand = new Command58("account").description("Manage logged-in accounts");
3923
+ var accountCommand = new Command59("account").description("Manage logged-in accounts");
3768
3924
  accountCommand.command("ls").description("List accounts in the current env (active marked with \u2713)").action(async () => {
3769
3925
  const accounts = await listAccounts(new ConfigStore());
3770
3926
  render({ kind: "account_ls" }, { accounts });
@@ -3776,7 +3932,7 @@ accountCommand.command("switch <email>").description("Set the active account for
3776
3932
  });
3777
3933
 
3778
3934
  // src/handwritten/commands/todo-done.ts
3779
- import { Command as Command59 } from "commander";
3935
+ import { Command as Command60 } from "commander";
3780
3936
  var TODO_UPDATE_DISPLAY = {
3781
3937
  shape: "object",
3782
3938
  format: {
@@ -3794,7 +3950,7 @@ var TODO_UPDATE_DISPLAY = {
3794
3950
  deleted_at: "relative-time"
3795
3951
  }
3796
3952
  };
3797
- var todoDoneCommand = new Command59("done").description("Mark a todo done (sugar for `update <id> --status done`)").argument("<id>", "Todo id").action(async (id) => {
3953
+ var todoDoneCommand = new Command60("done").description("Mark a todo done (sugar for `update <id> --status done`)").argument("<id>", "Todo id").action(async (id) => {
3798
3954
  const client2 = await loadSdkClient();
3799
3955
  const result = await todoUpdate({
3800
3956
  client: client2._rawClient,
@@ -3813,7 +3969,7 @@ var todoDoneCommand = new Command59("done").description("Mark a todo done (sugar
3813
3969
  });
3814
3970
 
3815
3971
  // src/handwritten/commands/email/send.ts
3816
- import { Command as Command60 } from "commander";
3972
+ import { Command as Command61 } from "commander";
3817
3973
  import { readFile, stat } from "fs/promises";
3818
3974
  import { basename } from "path";
3819
3975
 
@@ -3871,7 +4027,7 @@ async function resolveAttachment(input) {
3871
4027
  `--attach ${input}: neither a readable file nor a valid <prefix>_<ulid>:<idx> reference.`
3872
4028
  );
3873
4029
  }
3874
- var sendCommand = new Command60("send").description("Send an outbound email").requiredOption("--from <alias-email>", "alias email to send from").option("--to <addr...>", "recipient address (repeatable)", []).option("--subject <text>", "subject").option("--text <body>", "plain-text body").option("--text-file <path>", "read text body from file").option("--reply <id>", "inbound email id to reply to").option("--attach <path-or-ref...>", "attachment (file path or eml_xxx:idx)", []).requiredOption("--idempotency-key <key>", "idempotency key").action(async (opts) => {
4030
+ var sendCommand = new Command61("send").description("Send an outbound email").requiredOption("--from <alias-email>", "alias email to send from").option("--to <addr...>", "recipient address (repeatable)", []).option("--subject <text>", "subject").option("--text <body>", "plain-text body").option("--text-file <path>", "read text body from file").option("--reply <id>", "inbound email id to reply to").option("--attach <path-or-ref...>", "attachment (file path or eml_xxx:idx)", []).requiredOption("--idempotency-key <key>", "idempotency key").action(async (opts) => {
3875
4031
  const isReply = Boolean(opts.reply);
3876
4032
  const to = opts.to;
3877
4033
  const attachInputs = opts.attach;
@@ -3958,7 +4114,7 @@ var sendCommand = new Command60("send").description("Send an outbound email").re
3958
4114
  });
3959
4115
 
3960
4116
  // src/handwritten/commands/email/attachment.ts
3961
- import { Command as Command61 } from "commander";
4117
+ import { Command as Command62 } from "commander";
3962
4118
  import { createWriteStream } from "fs";
3963
4119
  import { Readable } from "stream";
3964
4120
  import { pipeline } from "stream/promises";
@@ -3975,7 +4131,7 @@ function parseContentDispositionFilename(header) {
3975
4131
  }
3976
4132
 
3977
4133
  // src/handwritten/commands/email/attachment.ts
3978
- var attachmentCommand = new Command61("attachment").description("Download an inbound email attachment by index").argument("<email-id>").argument("<idx>").option("--output <path>", "output file path").option("--include-deleted", "allow downloads from soft-deleted parent emails").action(async (emailId, idxArg, opts) => {
4134
+ var attachmentCommand = new Command62("attachment").description("Download an inbound email attachment by index").argument("<email-id>").argument("<idx>").option("--output <path>", "output file path").option("--include-deleted", "allow downloads from soft-deleted parent emails").action(async (emailId, idxArg, opts) => {
3979
4135
  const idx = Number(idxArg);
3980
4136
  if (!Number.isInteger(idx) || idx < 0) {
3981
4137
  process.stderr.write(`<idx> must be a non-negative integer (got "${idxArg}")
@@ -4008,7 +4164,7 @@ var attachmentCommand = new Command61("attachment").description("Download an inb
4008
4164
 
4009
4165
  // src/cli.ts
4010
4166
  function buildProgram() {
4011
- const program = new Command62().name("wspc").description("Official CLI for wspc.ai").version(`wspc ${VERSION} (spec ${SPEC_SHA}, fetched ${SPEC_FETCHED_AT})`).option("--json", "Output raw JSON (machine-readable)").option("--account <email>", "Run as a specific account (overrides the active account)").hook("preAction", (_thisCommand, actionCommand) => {
4167
+ const program = new Command63().name("wspc").description("Official CLI for wspc.ai").version(`wspc ${VERSION} (spec ${SPEC_SHA}, fetched ${SPEC_FETCHED_AT})`).option("--json", "Output raw JSON (machine-readable)").option("--account <email>", "Run as a specific account (overrides the active account)").hook("preAction", (_thisCommand, actionCommand) => {
4012
4168
  const globals = actionCommand.optsWithGlobals();
4013
4169
  if (globals.json) process.env.WSPC_OUTPUT = "json";
4014
4170
  if (globals.account) process.env.WSPC_ACCOUNT = String(globals.account);