@wspc/cli 0.1.1 → 0.1.2

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 Command66 } from "commander";
4
+ import { Command as Command73 } from "commander";
5
5
  import { realpathSync } from "fs";
6
6
  import { fileURLToPath } from "url";
7
7
 
@@ -930,6 +930,20 @@ var eventIcsDownload = (options) => (options.client ?? client).get({
930
930
  url: "/calendar/events/{filename}",
931
931
  ...options
932
932
  });
933
+ var driveLibraryList = (options) => (options?.client ?? client).get({
934
+ security: [{ scheme: "bearer", type: "http" }],
935
+ url: "/drive/libraries",
936
+ ...options
937
+ });
938
+ var driveLibraryCreate = (options) => (options?.client ?? client).post({
939
+ security: [{ scheme: "bearer", type: "http" }],
940
+ url: "/drive/libraries",
941
+ ...options,
942
+ headers: {
943
+ "Content-Type": "application/json",
944
+ ...options?.headers
945
+ }
946
+ });
933
947
  var driveFileDelete = (options) => (options.client ?? client).post({
934
948
  security: [{ scheme: "bearer", type: "http" }],
935
949
  url: "/drive/libraries/{id}/files/delete",
@@ -939,11 +953,29 @@ var driveFileDelete = (options) => (options.client ?? client).post({
939
953
  ...options.headers
940
954
  }
941
955
  });
956
+ var driveLibraryDelete = (options) => (options.client ?? client).delete({
957
+ security: [{ scheme: "bearer", type: "http" }],
958
+ url: "/drive/libraries/{id}",
959
+ ...options,
960
+ headers: {
961
+ "Content-Type": "application/json",
962
+ ...options.headers
963
+ }
964
+ });
942
965
  var driveLibraryGet = (options) => (options.client ?? client).get({
943
966
  security: [{ scheme: "bearer", type: "http" }],
944
967
  url: "/drive/libraries/{id}",
945
968
  ...options
946
969
  });
970
+ var driveLibraryUpdate = (options) => (options.client ?? client).patch({
971
+ security: [{ scheme: "bearer", type: "http" }],
972
+ url: "/drive/libraries/{id}",
973
+ ...options,
974
+ headers: {
975
+ "Content-Type": "application/json",
976
+ ...options.headers
977
+ }
978
+ });
947
979
  var driveManifestGet = (options) => (options.client ?? client).get({
948
980
  security: [{ scheme: "bearer", type: "http" }],
949
981
  url: "/drive/libraries/{id}/manifest",
@@ -1432,9 +1464,9 @@ function createConsistencyFetch(opts) {
1432
1464
  }
1433
1465
 
1434
1466
  // src/version.ts
1435
- var VERSION = "0.1.1";
1436
- var SPEC_SHA = "51d0418e";
1437
- var SPEC_FETCHED_AT = "2026-06-21T12:30:00.342Z";
1467
+ var VERSION = "0.1.2";
1468
+ var SPEC_SHA = "e0bbc1e3";
1469
+ var SPEC_FETCHED_AT = "2026-06-21T13:29:36.026Z";
1438
1470
  var API_BASE = "https://api.wspc.ai";
1439
1471
 
1440
1472
  // src/index.ts
@@ -2697,9 +2729,174 @@ var eventIcsDownloadCommand = new Command21("ics").description("Download event a
2697
2729
  render({ kind: "event_ics_download", display: { "shape": "raw" } }, result.data);
2698
2730
  });
2699
2731
 
2700
- // src/generated/cli/alias/add.ts
2732
+ // src/generated/cli/drive/library/add.ts
2701
2733
  import { Command as Command22 } from "commander";
2702
- var emailAliasCreateCommand = new Command22("add").description("Create a receiving alias").argument("<email>", "email").action(async (email, opts) => {
2734
+ var driveLibraryCreateCommand = new Command22("add").description("Create a drive library").argument("<name>", "name").action(async (name, opts) => {
2735
+ const client2 = await loadSdkClient();
2736
+ const result = await driveLibraryCreate({
2737
+ client: client2._rawClient,
2738
+ body: {
2739
+ name
2740
+ }
2741
+ });
2742
+ if (result.error || !result.response?.ok) {
2743
+ process.stderr.write(
2744
+ `HTTP ${result.response?.status ?? "?"}: ${JSON.stringify(result.error ?? "unknown error", null, 2)}
2745
+ `
2746
+ );
2747
+ process.exitCode = 1;
2748
+ return;
2749
+ }
2750
+ render({ kind: "drive_library_create", display: { "shape": "object", "columns": ["id", "name", "version", "file_count", "storage_bytes", "updated_at"] } }, result.data);
2751
+ });
2752
+
2753
+ // src/generated/cli/drive/library/ls.ts
2754
+ import { Command as Command23 } from "commander";
2755
+ var driveLibraryListCommand = new Command23("ls").description("List drive libraries").option("--limit <value>", "limit").option("--cursor <value>", "cursor").option("--include-deleted <value>", "include_deleted").action(async (opts) => {
2756
+ const client2 = await loadSdkClient();
2757
+ const result = await driveLibraryList({
2758
+ client: client2._rawClient,
2759
+ query: {
2760
+ limit: opts.limit,
2761
+ cursor: opts.cursor,
2762
+ include_deleted: opts.includeDeleted
2763
+ }
2764
+ });
2765
+ if (result.error || !result.response?.ok) {
2766
+ process.stderr.write(
2767
+ `HTTP ${result.response?.status ?? "?"}: ${JSON.stringify(result.error ?? "unknown error", null, 2)}
2768
+ `
2769
+ );
2770
+ process.exitCode = 1;
2771
+ return;
2772
+ }
2773
+ render({ kind: "drive_library_list", display: { "shape": "list", "dataPath": "libraries", "columns": ["id", "name", "file_count", "storage_bytes", "updated_at"], "emptyMessage": "no drive libraries" } }, result.data);
2774
+ });
2775
+
2776
+ // src/generated/cli/drive/file/rm.ts
2777
+ import { Command as Command24 } from "commander";
2778
+ var driveFileDeleteCommand = new Command24("rm").description("Delete a drive file").argument("<id>", "id").argument("<path>", "path").option("--expected-entry-version <value>", "expected_entry_version").action(async (id, path, opts) => {
2779
+ const client2 = await loadSdkClient();
2780
+ const result = await driveFileDelete({
2781
+ client: client2._rawClient,
2782
+ path: {
2783
+ id
2784
+ },
2785
+ body: {
2786
+ path,
2787
+ expected_entry_version: opts.expectedEntryVersion
2788
+ }
2789
+ });
2790
+ if (result.error || !result.response?.ok) {
2791
+ process.stderr.write(
2792
+ `HTTP ${result.response?.status ?? "?"}: ${JSON.stringify(result.error ?? "unknown error", null, 2)}
2793
+ `
2794
+ );
2795
+ process.exitCode = 1;
2796
+ return;
2797
+ }
2798
+ render({ kind: "drive_file_delete", display: { "shape": "object", "dataPath": "entry", "columns": ["path", "entry_version", "deleted_at"] } }, result.data);
2799
+ });
2800
+
2801
+ // src/generated/cli/drive/library/rm.ts
2802
+ import { Command as Command25 } from "commander";
2803
+ var driveLibraryDeleteCommand = new Command25("rm").description("Delete a drive library").argument("<id>", "id").option("--expected-version <value>", "expected_version").action(async (id, opts) => {
2804
+ const client2 = await loadSdkClient();
2805
+ const result = await driveLibraryDelete({
2806
+ client: client2._rawClient,
2807
+ path: {
2808
+ id
2809
+ },
2810
+ body: {
2811
+ expected_version: opts.expectedVersion
2812
+ }
2813
+ });
2814
+ if (result.error || !result.response?.ok) {
2815
+ process.stderr.write(
2816
+ `HTTP ${result.response?.status ?? "?"}: ${JSON.stringify(result.error ?? "unknown error", null, 2)}
2817
+ `
2818
+ );
2819
+ process.exitCode = 1;
2820
+ return;
2821
+ }
2822
+ render({ kind: "drive_library_delete", display: { "shape": "object", "columns": ["id", "name", "version", "file_count", "storage_bytes", "updated_at"] } }, result.data);
2823
+ });
2824
+
2825
+ // src/generated/cli/drive/library/show.ts
2826
+ import { Command as Command26 } from "commander";
2827
+ var driveLibraryGetCommand = new Command26("show").description("Get a drive library").argument("<id>", "id").action(async (id, opts) => {
2828
+ const client2 = await loadSdkClient();
2829
+ const result = await driveLibraryGet({
2830
+ client: client2._rawClient,
2831
+ path: {
2832
+ id
2833
+ }
2834
+ });
2835
+ if (result.error || !result.response?.ok) {
2836
+ process.stderr.write(
2837
+ `HTTP ${result.response?.status ?? "?"}: ${JSON.stringify(result.error ?? "unknown error", null, 2)}
2838
+ `
2839
+ );
2840
+ process.exitCode = 1;
2841
+ return;
2842
+ }
2843
+ render({ kind: "drive_library_get", display: { "shape": "object", "columns": ["id", "name", "version", "file_count", "storage_bytes", "updated_at"] } }, result.data);
2844
+ });
2845
+
2846
+ // src/generated/cli/drive/library/update.ts
2847
+ import { Command as Command27 } from "commander";
2848
+ var driveLibraryUpdateCommand = new Command27("update").description("Update a drive library").argument("<id>", "id").option("--name <value>", "name").option("--expected-version <value>", "expected_version").action(async (id, opts) => {
2849
+ const client2 = await loadSdkClient();
2850
+ const result = await driveLibraryUpdate({
2851
+ client: client2._rawClient,
2852
+ path: {
2853
+ id
2854
+ },
2855
+ body: {
2856
+ name: opts.name,
2857
+ expected_version: opts.expectedVersion
2858
+ }
2859
+ });
2860
+ if (result.error || !result.response?.ok) {
2861
+ process.stderr.write(
2862
+ `HTTP ${result.response?.status ?? "?"}: ${JSON.stringify(result.error ?? "unknown error", null, 2)}
2863
+ `
2864
+ );
2865
+ process.exitCode = 1;
2866
+ return;
2867
+ }
2868
+ render({ kind: "drive_library_update", display: { "shape": "object", "columns": ["id", "name", "version", "file_count", "storage_bytes", "updated_at"] } }, result.data);
2869
+ });
2870
+
2871
+ // src/generated/cli/drive/manifest/get.ts
2872
+ import { Command as Command28 } from "commander";
2873
+ var driveManifestGetCommand = new Command28("get").description("Get a drive library manifest").argument("<id>", "id").option("--limit <value>", "limit").option("--cursor <value>", "cursor").option("--include-deleted <value>", "include_deleted").action(async (id, opts) => {
2874
+ const client2 = await loadSdkClient();
2875
+ const result = await driveManifestGet({
2876
+ client: client2._rawClient,
2877
+ path: {
2878
+ id
2879
+ },
2880
+ query: {
2881
+ limit: opts.limit,
2882
+ cursor: opts.cursor,
2883
+ include_deleted: opts.includeDeleted
2884
+ }
2885
+ });
2886
+ if (result.error || !result.response?.ok) {
2887
+ process.stderr.write(
2888
+ `HTTP ${result.response?.status ?? "?"}: ${JSON.stringify(result.error ?? "unknown error", null, 2)}
2889
+ `
2890
+ );
2891
+ process.exitCode = 1;
2892
+ return;
2893
+ }
2894
+ render({ kind: "drive_manifest_get", display: { "shape": "list", "dataPath": "entries", "columns": ["path", "entry_version", "size_bytes", "updated_at"], "emptyMessage": "no drive files" } }, result.data);
2895
+ });
2896
+
2897
+ // src/generated/cli/alias/add.ts
2898
+ import { Command as Command29 } from "commander";
2899
+ var emailAliasCreateCommand = new Command29("add").description("Create a receiving alias").argument("<email>", "email").action(async (email, opts) => {
2703
2900
  const client2 = await loadSdkClient();
2704
2901
  const result = await emailAliasCreate({
2705
2902
  client: client2._rawClient,
@@ -2719,8 +2916,8 @@ var emailAliasCreateCommand = new Command22("add").description("Create a receivi
2719
2916
  });
2720
2917
 
2721
2918
  // src/generated/cli/alias/ls.ts
2722
- import { Command as Command23 } from "commander";
2723
- var emailAliasListCommand = new Command23("ls").description("List the caller's aliases").option("--include-deleted <value>", "When `true`, include soft-deleted aliases (with `deleted_at` set) alongside active ones. Defaults to `false`.").action(async (opts) => {
2919
+ import { Command as Command30 } from "commander";
2920
+ var emailAliasListCommand = new Command30("ls").description("List the caller's aliases").option("--include-deleted <value>", "When `true`, include soft-deleted aliases (with `deleted_at` set) alongside active ones. Defaults to `false`.").action(async (opts) => {
2724
2921
  const client2 = await loadSdkClient();
2725
2922
  const result = await emailAliasList({
2726
2923
  client: client2._rawClient,
@@ -2740,8 +2937,8 @@ var emailAliasListCommand = new Command23("ls").description("List the caller's a
2740
2937
  });
2741
2938
 
2742
2939
  // src/generated/cli/domain/add.ts
2743
- import { Command as Command24 } from "commander";
2744
- var emailDomainCreateCommand = new Command24("add").description("Register a custom email domain").argument("<domain>", "domain").action(async (domain, opts) => {
2940
+ import { Command as Command31 } from "commander";
2941
+ var emailDomainCreateCommand = new Command31("add").description("Register a custom email domain").argument("<domain>", "domain").action(async (domain, opts) => {
2745
2942
  const client2 = await loadSdkClient();
2746
2943
  const result = await emailDomainCreate({
2747
2944
  client: client2._rawClient,
@@ -2761,8 +2958,8 @@ var emailDomainCreateCommand = new Command24("add").description("Register a cust
2761
2958
  });
2762
2959
 
2763
2960
  // src/generated/cli/domain/ls.ts
2764
- import { Command as Command25 } from "commander";
2765
- var emailDomainListCommand = new Command25("ls").description("List cached custom domains").action(async (opts) => {
2961
+ import { Command as Command32 } from "commander";
2962
+ var emailDomainListCommand = new Command32("ls").description("List cached custom domains").action(async (opts) => {
2766
2963
  const client2 = await loadSdkClient();
2767
2964
  const result = await emailDomainList({
2768
2965
  client: client2._rawClient
@@ -2779,8 +2976,8 @@ var emailDomainListCommand = new Command25("ls").description("List cached custom
2779
2976
  });
2780
2977
 
2781
2978
  // src/generated/cli/alias/rm.ts
2782
- import { Command as Command26 } from "commander";
2783
- var emailAliasDeleteCommand = new Command26("rm").description("Soft-delete an alias").argument("<email>", "email").action(async (email, opts) => {
2979
+ import { Command as Command33 } from "commander";
2980
+ var emailAliasDeleteCommand = new Command33("rm").description("Soft-delete an alias").argument("<email>", "email").action(async (email, opts) => {
2784
2981
  const client2 = await loadSdkClient();
2785
2982
  const result = await emailAliasDelete({
2786
2983
  client: client2._rawClient,
@@ -2800,8 +2997,8 @@ var emailAliasDeleteCommand = new Command26("rm").description("Soft-delete an al
2800
2997
  });
2801
2998
 
2802
2999
  // src/generated/cli/email/rm.ts
2803
- import { Command as Command27 } from "commander";
2804
- var emailDeleteCommand = new Command27("rm").description("Soft-delete inbound emails").argument("<id...>", "id").action(async (id, opts) => {
3000
+ import { Command as Command34 } from "commander";
3001
+ var emailDeleteCommand = new Command34("rm").description("Soft-delete inbound emails").argument("<id...>", "id").action(async (id, opts) => {
2805
3002
  const idRaw = id;
2806
3003
  const ids = idRaw.length > 0 ? idRaw : void 0;
2807
3004
  const client2 = await loadSdkClient();
@@ -2823,8 +3020,8 @@ var emailDeleteCommand = new Command27("rm").description("Soft-delete inbound em
2823
3020
  });
2824
3021
 
2825
3022
  // src/generated/cli/domain/rm.ts
2826
- import { Command as Command28 } from "commander";
2827
- var emailDomainDeleteCommand = new Command28("rm").description("Delete a custom email domain").argument("<domain>", "domain").action(async (domain, opts) => {
3023
+ import { Command as Command35 } from "commander";
3024
+ var emailDomainDeleteCommand = new Command35("rm").description("Delete a custom email domain").argument("<domain>", "domain").action(async (domain, opts) => {
2828
3025
  const client2 = await loadSdkClient();
2829
3026
  const result = await emailDomainDelete({
2830
3027
  client: client2._rawClient,
@@ -2844,8 +3041,8 @@ var emailDomainDeleteCommand = new Command28("rm").description("Delete a custom
2844
3041
  });
2845
3042
 
2846
3043
  // src/generated/cli/domain/show.ts
2847
- import { Command as Command29 } from "commander";
2848
- var emailDomainGetCommand = new Command29("show").description("Get one cached custom domain").argument("<domain>", "domain").action(async (domain, opts) => {
3044
+ import { Command as Command36 } from "commander";
3045
+ var emailDomainGetCommand = new Command36("show").description("Get one cached custom domain").argument("<domain>", "domain").action(async (domain, opts) => {
2849
3046
  const client2 = await loadSdkClient();
2850
3047
  const result = await emailDomainGet({
2851
3048
  client: client2._rawClient,
@@ -2865,8 +3062,8 @@ var emailDomainGetCommand = new Command29("show").description("Get one cached cu
2865
3062
  });
2866
3063
 
2867
3064
  // src/generated/cli/email/show.ts
2868
- import { Command as Command30 } from "commander";
2869
- 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) => {
3065
+ import { Command as Command37 } from "commander";
3066
+ var emailGetCommand = new Command37("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) => {
2870
3067
  const client2 = await loadSdkClient();
2871
3068
  const result = await emailGet({
2872
3069
  client: client2._rawClient,
@@ -2890,8 +3087,8 @@ var emailGetCommand = new Command30("show").description("Get an inbound email by
2890
3087
  });
2891
3088
 
2892
3089
  // src/generated/cli/email/ls.ts
2893
- import { Command as Command31 } from "commander";
2894
- 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) => {
3090
+ import { Command as Command38 } from "commander";
3091
+ var emailListCommand = new Command38("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) => {
2895
3092
  const client2 = await loadSdkClient();
2896
3093
  const result = await emailList({
2897
3094
  client: client2._rawClient,
@@ -2916,8 +3113,8 @@ var emailListCommand = new Command31("ls").description("List inbound emails").op
2916
3113
  });
2917
3114
 
2918
3115
  // src/generated/cli/email/read.ts
2919
- import { Command as Command32 } from "commander";
2920
- var emailMarkReadCommand = new Command32("read").description("Mark inbound emails as read").argument("<id...>", "id").action(async (id, opts) => {
3116
+ import { Command as Command39 } from "commander";
3117
+ var emailMarkReadCommand = new Command39("read").description("Mark inbound emails as read").argument("<id...>", "id").action(async (id, opts) => {
2921
3118
  const idRaw = id;
2922
3119
  const ids = idRaw.length > 0 ? idRaw : void 0;
2923
3120
  const client2 = await loadSdkClient();
@@ -2939,8 +3136,8 @@ var emailMarkReadCommand = new Command32("read").description("Mark inbound email
2939
3136
  });
2940
3137
 
2941
3138
  // src/generated/cli/email/unread.ts
2942
- import { Command as Command33 } from "commander";
2943
- var emailMarkUnreadCommand = new Command33("unread").description("Mark inbound emails as unread").argument("<id...>", "id").action(async (id, opts) => {
3139
+ import { Command as Command40 } from "commander";
3140
+ var emailMarkUnreadCommand = new Command40("unread").description("Mark inbound emails as unread").argument("<id...>", "id").action(async (id, opts) => {
2944
3141
  const idRaw = id;
2945
3142
  const ids = idRaw.length > 0 ? idRaw : void 0;
2946
3143
  const client2 = await loadSdkClient();
@@ -2962,8 +3159,8 @@ var emailMarkUnreadCommand = new Command33("unread").description("Mark inbound e
2962
3159
  });
2963
3160
 
2964
3161
  // src/generated/cli/domain/verify.ts
2965
- import { Command as Command34 } from "commander";
2966
- var emailDomainVerifyCommand = new Command34("verify").description("Verify a custom domain with the provider").argument("<domain>", "domain").action(async (domain, opts) => {
3162
+ import { Command as Command41 } from "commander";
3163
+ var emailDomainVerifyCommand = new Command41("verify").description("Verify a custom domain with the provider").argument("<domain>", "domain").action(async (domain, opts) => {
2967
3164
  const client2 = await loadSdkClient();
2968
3165
  const result = await emailDomainVerify({
2969
3166
  client: client2._rawClient,
@@ -2983,8 +3180,8 @@ var emailDomainVerifyCommand = new Command34("verify").description("Verify a cus
2983
3180
  });
2984
3181
 
2985
3182
  // src/generated/cli/push/config/rm.ts
2986
- import { Command as Command35 } from "commander";
2987
- var pushConfigDeleteCommand = new Command35("rm").description("Remove a push transport").argument("<transport>", "transport").action(async (transport, opts) => {
3183
+ import { Command as Command42 } from "commander";
3184
+ var pushConfigDeleteCommand = new Command42("rm").description("Remove a push transport").argument("<transport>", "transport").action(async (transport, opts) => {
2988
3185
  const client2 = await loadSdkClient();
2989
3186
  const result = await pushConfigDelete({
2990
3187
  client: client2._rawClient,
@@ -3004,8 +3201,8 @@ var pushConfigDeleteCommand = new Command35("rm").description("Remove a push tra
3004
3201
  });
3005
3202
 
3006
3203
  // src/generated/cli/push/config/set.ts
3007
- import { Command as Command36 } from "commander";
3008
- 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) => {
3204
+ import { Command as Command43 } from "commander";
3205
+ var pushConfigSetCommand = new Command43("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) => {
3009
3206
  const client2 = await loadSdkClient();
3010
3207
  const result = await pushConfigSet({
3011
3208
  client: client2._rawClient,
@@ -3028,8 +3225,8 @@ var pushConfigSetCommand = new Command36("set").description("Register or update
3028
3225
  });
3029
3226
 
3030
3227
  // src/generated/cli/push/config/show.ts
3031
- import { Command as Command37 } from "commander";
3032
- var pushConfigGetCommand = new Command37("show").description("List the caller's push transports").action(async (opts) => {
3228
+ import { Command as Command44 } from "commander";
3229
+ var pushConfigGetCommand = new Command44("show").description("List the caller's push transports").action(async (opts) => {
3033
3230
  const client2 = await loadSdkClient();
3034
3231
  const result = await pushConfigGet({
3035
3232
  client: client2._rawClient
@@ -3046,8 +3243,8 @@ var pushConfigGetCommand = new Command37("show").description("List the caller's
3046
3243
  });
3047
3244
 
3048
3245
  // src/generated/cli/push/test.ts
3049
- import { Command as Command38 } from "commander";
3050
- 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) => {
3246
+ import { Command as Command45 } from "commander";
3247
+ var pushTestCommand = new Command45("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) => {
3051
3248
  const client2 = await loadSdkClient();
3052
3249
  const result = await pushTest({
3053
3250
  client: client2._rawClient,
@@ -3070,8 +3267,8 @@ var pushTestCommand = new Command38("test").description("Send a test push notifi
3070
3267
  });
3071
3268
 
3072
3269
  // src/generated/cli/todo/comment/add.ts
3073
- import { Command as Command39 } from "commander";
3074
- var todoCommentCreateCommand = new Command39("add").description("Add a comment to a todo").argument("<id>", "id").argument("<content>", "content").action(async (id, content, opts) => {
3270
+ import { Command as Command46 } from "commander";
3271
+ var todoCommentCreateCommand = new Command46("add").description("Add a comment to a todo").argument("<id>", "id").argument("<content>", "content").action(async (id, content, opts) => {
3075
3272
  const client2 = await loadSdkClient();
3076
3273
  const result = await todoCommentCreate({
3077
3274
  client: client2._rawClient,
@@ -3094,8 +3291,8 @@ var todoCommentCreateCommand = new Command39("add").description("Add a comment t
3094
3291
  });
3095
3292
 
3096
3293
  // src/generated/cli/todo/comment/ls.ts
3097
- import { Command as Command40 } from "commander";
3098
- 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) => {
3294
+ import { Command as Command47 } from "commander";
3295
+ var todoCommentListCommand = new Command47("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) => {
3099
3296
  const client2 = await loadSdkClient();
3100
3297
  const result = await todoCommentList({
3101
3298
  client: client2._rawClient,
@@ -3121,8 +3318,8 @@ var todoCommentListCommand = new Command40("ls").description("List comments on a
3121
3318
  });
3122
3319
 
3123
3320
  // src/generated/cli/todo/project/add.ts
3124
- import { Command as Command41 } from "commander";
3125
- 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) => {
3321
+ import { Command as Command48 } from "commander";
3322
+ var projectCreateCommand = new Command48("add").description("Create a project").argument("<name>", "name").option("--default-todo-type-id <value>", "default_todo_type_id").action(async (name, opts) => {
3126
3323
  const client2 = await loadSdkClient();
3127
3324
  const result = await projectCreate({
3128
3325
  client: client2._rawClient,
@@ -3143,8 +3340,8 @@ var projectCreateCommand = new Command41("add").description("Create a project").
3143
3340
  });
3144
3341
 
3145
3342
  // src/generated/cli/todo/project/ls.ts
3146
- import { Command as Command42 } from "commander";
3147
- 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) => {
3343
+ import { Command as Command49 } from "commander";
3344
+ var projectListCommand = new Command49("ls").description("List projects").option("--include-deleted <value>", "Set to `true` to include soft-deleted projects in the response.").action(async (opts) => {
3148
3345
  const client2 = await loadSdkClient();
3149
3346
  const result = await projectList({
3150
3347
  client: client2._rawClient,
@@ -3164,8 +3361,8 @@ var projectListCommand = new Command42("ls").description("List projects").option
3164
3361
  });
3165
3362
 
3166
3363
  // src/generated/cli/todo/rule/add.ts
3167
- import { Command as Command43 } from "commander";
3168
- 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) => {
3364
+ import { Command as Command50 } from "commander";
3365
+ var recurrenceRuleCreateCommand = new Command50("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) => {
3169
3366
  const client2 = await loadSdkClient();
3170
3367
  const result = await recurrenceRuleCreate({
3171
3368
  client: client2._rawClient,
@@ -3191,8 +3388,8 @@ var recurrenceRuleCreateCommand = new Command43("add").description("Create a rec
3191
3388
  });
3192
3389
 
3193
3390
  // src/generated/cli/todo/rule/ls.ts
3194
- import { Command as Command44 } from "commander";
3195
- 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) => {
3391
+ import { Command as Command51 } from "commander";
3392
+ var recurrenceRuleListCommand = new Command51("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) => {
3196
3393
  const client2 = await loadSdkClient();
3197
3394
  const result = await recurrenceRuleList({
3198
3395
  client: client2._rawClient,
@@ -3213,7 +3410,7 @@ var recurrenceRuleListCommand = new Command44("ls").description("List recurring
3213
3410
  });
3214
3411
 
3215
3412
  // src/generated/cli/todo/add.ts
3216
- import { Command as Command45 } from "commander";
3413
+ import { Command as Command52 } from "commander";
3217
3414
 
3218
3415
  // src/handwritten/utils/parse-json-field.ts
3219
3416
  function parseJsonField(raw, flag) {
@@ -3228,7 +3425,7 @@ function parseJsonField(raw, flag) {
3228
3425
  }
3229
3426
 
3230
3427
  // src/generated/cli/todo/add.ts
3231
- 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("--idempotency-key <value>", "idempotency_key").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) => {
3428
+ var todoCreateCommand = new Command52("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("--idempotency-key <value>", "idempotency_key").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) => {
3232
3429
  const client2 = await loadSdkClient();
3233
3430
  const result = await todoCreate({
3234
3431
  client: client2._rawClient,
@@ -3256,8 +3453,8 @@ var todoCreateCommand = new Command45("add").description("Create a todo").argume
3256
3453
  });
3257
3454
 
3258
3455
  // src/generated/cli/todo/ls.ts
3259
- import { Command as Command46 } from "commander";
3260
- 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) => {
3456
+ import { Command as Command53 } from "commander";
3457
+ var todoListCommand = new Command53("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) => {
3261
3458
  const client2 = await loadSdkClient();
3262
3459
  const result = await todoList({
3263
3460
  client: client2._rawClient,
@@ -3290,8 +3487,8 @@ var todoListCommand = new Command46("ls").description("List todos with filters")
3290
3487
  });
3291
3488
 
3292
3489
  // src/generated/cli/todo/type/ls.ts
3293
- import { Command as Command47 } from "commander";
3294
- 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) => {
3490
+ import { Command as Command54 } from "commander";
3491
+ var todoTypeListCommand = new Command54("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) => {
3295
3492
  const client2 = await loadSdkClient();
3296
3493
  const result = await todoTypeList({
3297
3494
  client: client2._rawClient,
@@ -3313,8 +3510,8 @@ var todoTypeListCommand = new Command47("ls").description("List todo types").opt
3313
3510
  });
3314
3511
 
3315
3512
  // src/generated/cli/todo/comment/rm.ts
3316
- import { Command as Command48 } from "commander";
3317
- var todoCommentDeleteCommand = new Command48("rm").description("Soft-delete a comment").argument("<id>", "id").action(async (id, opts) => {
3513
+ import { Command as Command55 } from "commander";
3514
+ var todoCommentDeleteCommand = new Command55("rm").description("Soft-delete a comment").argument("<id>", "id").action(async (id, opts) => {
3318
3515
  const client2 = await loadSdkClient();
3319
3516
  const result = await todoCommentDelete({
3320
3517
  client: client2._rawClient,
@@ -3334,8 +3531,8 @@ var todoCommentDeleteCommand = new Command48("rm").description("Soft-delete a co
3334
3531
  });
3335
3532
 
3336
3533
  // src/generated/cli/todo/comment/edit.ts
3337
- import { Command as Command49 } from "commander";
3338
- var todoCommentUpdateCommand = new Command49("edit").description("Edit a comment").argument("<id>", "id").argument("<content>", "content").action(async (id, content, opts) => {
3534
+ import { Command as Command56 } from "commander";
3535
+ var todoCommentUpdateCommand = new Command56("edit").description("Edit a comment").argument("<id>", "id").argument("<content>", "content").action(async (id, content, opts) => {
3339
3536
  const client2 = await loadSdkClient();
3340
3537
  const result = await todoCommentUpdate({
3341
3538
  client: client2._rawClient,
@@ -3358,8 +3555,8 @@ var todoCommentUpdateCommand = new Command49("edit").description("Edit a comment
3358
3555
  });
3359
3556
 
3360
3557
  // src/generated/cli/todo/rule/rm.ts
3361
- import { Command as Command50 } from "commander";
3362
- var recurrenceRuleDeleteCommand = new Command50("rm").description("Delete a recurring todo rule").argument("<id>", "id").option("--expected-version <value>", "expected_version").action(async (id, opts) => {
3558
+ import { Command as Command57 } from "commander";
3559
+ var recurrenceRuleDeleteCommand = new Command57("rm").description("Delete a recurring todo rule").argument("<id>", "id").option("--expected-version <value>", "expected_version").action(async (id, opts) => {
3363
3560
  const client2 = await loadSdkClient();
3364
3561
  const result = await recurrenceRuleDelete({
3365
3562
  client: client2._rawClient,
@@ -3382,8 +3579,8 @@ var recurrenceRuleDeleteCommand = new Command50("rm").description("Delete a recu
3382
3579
  });
3383
3580
 
3384
3581
  // src/generated/cli/todo/rule/show.ts
3385
- import { Command as Command51 } from "commander";
3386
- var recurrenceRuleGetCommand = new Command51("show").description("Get a recurring todo rule").argument("<id>", "id").action(async (id, opts) => {
3582
+ import { Command as Command58 } from "commander";
3583
+ var recurrenceRuleGetCommand = new Command58("show").description("Get a recurring todo rule").argument("<id>", "id").action(async (id, opts) => {
3387
3584
  const client2 = await loadSdkClient();
3388
3585
  const result = await recurrenceRuleGet({
3389
3586
  client: client2._rawClient,
@@ -3403,8 +3600,8 @@ var recurrenceRuleGetCommand = new Command51("show").description("Get a recurrin
3403
3600
  });
3404
3601
 
3405
3602
  // src/generated/cli/todo/rm.ts
3406
- import { Command as Command52 } from "commander";
3407
- 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) => {
3603
+ import { Command as Command59 } from "commander";
3604
+ var todoDeleteCommand = new Command59("rm").description("Soft-delete a todo").argument("<id>", "id").option("--expected-version <value>", "expected_version").option("--cascade <value>", "cascade").action(async (id, opts) => {
3408
3605
  const client2 = await loadSdkClient();
3409
3606
  const result = await todoDelete({
3410
3607
  client: client2._rawClient,
@@ -3428,8 +3625,8 @@ var todoDeleteCommand = new Command52("rm").description("Soft-delete a todo").ar
3428
3625
  });
3429
3626
 
3430
3627
  // src/generated/cli/todo/show.ts
3431
- import { Command as Command53 } from "commander";
3432
- 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) => {
3628
+ import { Command as Command60 } from "commander";
3629
+ var todoGetCommand = new Command60("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) => {
3433
3630
  const client2 = await loadSdkClient();
3434
3631
  const result = await todoGet({
3435
3632
  client: client2._rawClient,
@@ -3454,8 +3651,8 @@ var todoGetCommand = new Command53("show").description("Get a todo by id").argum
3454
3651
  });
3455
3652
 
3456
3653
  // src/generated/cli/todo/update.ts
3457
- import { Command as Command54 } from "commander";
3458
- 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) => {
3654
+ import { Command as Command61 } from "commander";
3655
+ var todoUpdateCommand = new Command61("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) => {
3459
3656
  const client2 = await loadSdkClient();
3460
3657
  const result = await todoUpdate({
3461
3658
  client: client2._rawClient,
@@ -3514,6 +3711,17 @@ function registerGeneratedCommands(root) {
3514
3711
  root_event.addCommand(eventGetCommand);
3515
3712
  root_event.addCommand(eventUpdateCommand);
3516
3713
  root_event.addCommand(eventIcsDownloadCommand);
3714
+ const root_drive = root.command("drive").description("drive commands");
3715
+ const root_drive_library = root_drive.command("library").description("library commands");
3716
+ root_drive_library.addCommand(driveLibraryCreateCommand);
3717
+ root_drive_library.addCommand(driveLibraryListCommand);
3718
+ root_drive_library.addCommand(driveLibraryDeleteCommand);
3719
+ root_drive_library.addCommand(driveLibraryGetCommand);
3720
+ root_drive_library.addCommand(driveLibraryUpdateCommand);
3721
+ const root_drive_file = root_drive.command("file").description("file commands");
3722
+ root_drive_file.addCommand(driveFileDeleteCommand);
3723
+ const root_drive_manifest = root_drive.command("manifest").description("manifest commands");
3724
+ root_drive_manifest.addCommand(driveManifestGetCommand);
3517
3725
  const root_alias = root.command("alias").description("alias commands");
3518
3726
  root_alias.addCommand(emailAliasCreateCommand);
3519
3727
  root_alias.addCommand(emailAliasListCommand);
@@ -3560,7 +3768,7 @@ function registerGeneratedCommands(root) {
3560
3768
  }
3561
3769
 
3562
3770
  // src/handwritten/commands/login.ts
3563
- import { Command as Command55 } from "commander";
3771
+ import { Command as Command62 } from "commander";
3564
3772
 
3565
3773
  // src/handwritten/auth/device-flow.ts
3566
3774
  var DEFAULT_SLEEP = (ms) => new Promise((r) => setTimeout(r, ms));
@@ -3794,7 +4002,7 @@ function resolveLoginTarget(opts, env) {
3794
4002
  function wantsJson(opts, env) {
3795
4003
  return opts.json === true || env.WSPC_OUTPUT === "json";
3796
4004
  }
3797
- 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) => {
4005
+ var loginCommand = new Command62("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) => {
3798
4006
  const store = new ConfigStore();
3799
4007
  const { baseUrl, envName } = resolveLoginTarget(opts, process.env);
3800
4008
  const output = wantsJson(opts, process.env) ? { write: () => {
@@ -3813,7 +4021,7 @@ var loginCommand = new Command55("login").description("Log in via OAuth device f
3813
4021
  });
3814
4022
 
3815
4023
  // src/handwritten/commands/logout.ts
3816
- import { Command as Command56 } from "commander";
4024
+ import { Command as Command63 } from "commander";
3817
4025
 
3818
4026
  // src/handwritten/auth/logout.ts
3819
4027
  async function runLogout(opts) {
@@ -3841,7 +4049,7 @@ async function runLogout(opts) {
3841
4049
  }
3842
4050
 
3843
4051
  // src/handwritten/commands/logout.ts
3844
- 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) => {
4052
+ var logoutCommand = new Command63("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) => {
3845
4053
  const res = await runLogout({ store: new ConfigStore(), email, all: opts.all });
3846
4054
  if (res.removed.length === 0) {
3847
4055
  process.stdout.write("nothing to log out\n");
@@ -3854,7 +4062,7 @@ var logoutCommand = new Command56("logout").description("Log out an account (def
3854
4062
  });
3855
4063
 
3856
4064
  // src/handwritten/commands/whoami.ts
3857
- import { Command as Command57 } from "commander";
4065
+ import { Command as Command64 } from "commander";
3858
4066
  var ENV_DISPLAY = {
3859
4067
  shape: "object",
3860
4068
  fields: ["name", "api_base", "account", "actor", "agent_label"]
@@ -3886,7 +4094,7 @@ async function backfillActiveEmail(store, envName, email, userId) {
3886
4094
  await store.write(cfg);
3887
4095
  }
3888
4096
  }
3889
- var whoamiCommand = new Command57("whoami").description("Show the active env, signed-in account, and organization").action(async () => {
4097
+ var whoamiCommand = new Command64("whoami").description("Show the active env, signed-in account, and organization").action(async () => {
3890
4098
  const store = new ConfigStore();
3891
4099
  const config = await store.read();
3892
4100
  let resolved;
@@ -3939,8 +4147,8 @@ function printLoggedOut() {
3939
4147
  }
3940
4148
 
3941
4149
  // src/handwritten/commands/config.ts
3942
- import { Command as Command58 } from "commander";
3943
- var configCommand = new Command58("config").description("Manage wspc local config");
4150
+ import { Command as Command65 } from "commander";
4151
+ var configCommand = new Command65("config").description("Manage wspc local config");
3944
4152
  registerRenderer("config_show", (data) => {
3945
4153
  const d = data;
3946
4154
  if (d.envs.length === 0) {
@@ -4011,7 +4219,7 @@ configCommand.command("use <env>").description("Switch current_env").action(asyn
4011
4219
  });
4012
4220
 
4013
4221
  // src/handwritten/commands/account.ts
4014
- import { Command as Command59 } from "commander";
4222
+ import { Command as Command66 } from "commander";
4015
4223
  async function listAccounts(store) {
4016
4224
  const c = await store.read();
4017
4225
  const envName = c.current_env;
@@ -4052,7 +4260,7 @@ registerRenderer("account_ls", (data) => {
4052
4260
  ]);
4053
4261
  process.stdout.write(table(headers, body));
4054
4262
  });
4055
- var accountCommand = new Command59("account").description("Manage logged-in accounts");
4263
+ var accountCommand = new Command66("account").description("Manage logged-in accounts");
4056
4264
  accountCommand.command("ls").description("List accounts in the current env (active marked with \u2713)").action(async () => {
4057
4265
  const accounts = await listAccounts(new ConfigStore());
4058
4266
  render({ kind: "account_ls" }, { accounts });
@@ -4064,7 +4272,7 @@ accountCommand.command("switch <email>").description("Set the active account for
4064
4272
  });
4065
4273
 
4066
4274
  // src/handwritten/commands/todo-done.ts
4067
- import { Command as Command60 } from "commander";
4275
+ import { Command as Command67 } from "commander";
4068
4276
  var TODO_UPDATE_DISPLAY = {
4069
4277
  shape: "object",
4070
4278
  format: {
@@ -4082,7 +4290,7 @@ var TODO_UPDATE_DISPLAY = {
4082
4290
  deleted_at: "relative-time"
4083
4291
  }
4084
4292
  };
4085
- var todoDoneCommand = new Command60("done").description("Mark a todo done (sugar for `update <id> --status done`)").argument("<id>", "Todo id").action(async (id) => {
4293
+ var todoDoneCommand = new Command67("done").description("Mark a todo done (sugar for `update <id> --status done`)").argument("<id>", "Todo id").action(async (id) => {
4086
4294
  const client2 = await loadSdkClient();
4087
4295
  const result = await todoUpdate({
4088
4296
  client: client2._rawClient,
@@ -4101,7 +4309,7 @@ var todoDoneCommand = new Command60("done").description("Mark a todo done (sugar
4101
4309
  });
4102
4310
 
4103
4311
  // src/handwritten/commands/email/send.ts
4104
- import { Command as Command61 } from "commander";
4312
+ import { Command as Command68 } from "commander";
4105
4313
  import { readFile, stat } from "fs/promises";
4106
4314
  import { basename } from "path";
4107
4315
 
@@ -4159,7 +4367,7 @@ async function resolveAttachment(input) {
4159
4367
  `--attach ${input}: neither a readable file nor a valid <prefix>_<ulid>:<idx> reference.`
4160
4368
  );
4161
4369
  }
4162
- 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) => {
4370
+ var sendCommand = new Command68("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) => {
4163
4371
  const isReply = Boolean(opts.reply);
4164
4372
  const to = opts.to;
4165
4373
  const attachInputs = opts.attach;
@@ -4246,7 +4454,7 @@ var sendCommand = new Command61("send").description("Send an outbound email").re
4246
4454
  });
4247
4455
 
4248
4456
  // src/handwritten/commands/email/attachment.ts
4249
- import { Command as Command62 } from "commander";
4457
+ import { Command as Command69 } from "commander";
4250
4458
  import { createWriteStream } from "fs";
4251
4459
  import { Readable } from "stream";
4252
4460
  import { pipeline } from "stream/promises";
@@ -4263,7 +4471,7 @@ function parseContentDispositionFilename(header) {
4263
4471
  }
4264
4472
 
4265
4473
  // src/handwritten/commands/email/attachment.ts
4266
- 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) => {
4474
+ var attachmentCommand = new Command69("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) => {
4267
4475
  const idx = Number(idxArg);
4268
4476
  if (!Number.isInteger(idx) || idx < 0) {
4269
4477
  process.stderr.write(`<idx> must be a non-negative integer (got "${idxArg}")
@@ -4295,7 +4503,7 @@ var attachmentCommand = new Command62("attachment").description("Download an inb
4295
4503
  });
4296
4504
 
4297
4505
  // src/handwritten/commands/drive/bind.ts
4298
- import { Command as Command63 } from "commander";
4506
+ import { Command as Command70 } from "commander";
4299
4507
  import { stat as stat2 } from "fs/promises";
4300
4508
  import { resolve } from "path";
4301
4509
 
@@ -4367,9 +4575,12 @@ async function createDriveApi(opts = {}) {
4367
4575
  }
4368
4576
  return payload;
4369
4577
  },
4370
- async downloadFile(id, path) {
4578
+ async downloadFile(id, path, versionId) {
4371
4579
  const url = driveContentUrl(client2.baseUrl, id);
4372
4580
  url.searchParams.set("path", path);
4581
+ if (versionId !== void 0) {
4582
+ url.searchParams.set("version_id", versionId);
4583
+ }
4373
4584
  const res = await client2.fetch(url, { method: "GET" });
4374
4585
  if (!res.ok) {
4375
4586
  const text = await res.text();
@@ -4470,7 +4681,7 @@ function isDriveStateEntry(value) {
4470
4681
  return isRecord(value) && typeof value.entry_id === "string" && typeof value.entry_version === "number" && typeof value.size_bytes === "number" && typeof value.last_synced_at === "string" && value.status === "synced" && (value.current_version_id === void 0 || typeof value.current_version_id === "string") && (value.content_sha256 === void 0 || typeof value.content_sha256 === "string") && (value.last_local_sha256 === void 0 || typeof value.last_local_sha256 === "string");
4471
4682
  }
4472
4683
  function isDriveConflict(value) {
4473
- return isRecord(value) && typeof value.detected_at === "string" && typeof value.reason === "string" && (value.remote_entry_version === void 0 || typeof value.remote_entry_version === "number") && (value.remote_version_id === void 0 || typeof value.remote_version_id === "string");
4684
+ return isRecord(value) && typeof value.detected_at === "string" && typeof value.reason === "string" && (value.type === void 0 || value.type === "edit_edit" || value.type === "create_create" || value.type === "delete_edit" || value.type === "edit_delete") && (value.strategy === void 0 || value.strategy === "clean_merge" || value.strategy === "conflict_copy" || value.strategy === "record_only") && (value.base_version_id === void 0 || typeof value.base_version_id === "string") && (value.remote_entry_version === void 0 || typeof value.remote_entry_version === "number") && (value.remote_version_id === void 0 || typeof value.remote_version_id === "string") && (value.conflict_paths === void 0 || Array.isArray(value.conflict_paths) && value.conflict_paths.every((path) => typeof path === "string"));
4474
4685
  }
4475
4686
  function isValidDriveState(value) {
4476
4687
  if (!isRecord(value)) return false;
@@ -4499,7 +4710,7 @@ async function assertExistingDirectory(path) {
4499
4710
  }
4500
4711
  }
4501
4712
  function driveBindCommand() {
4502
- return new Command63("bind").description("Bind a local folder to an existing Drive library").requiredOption("--library <id>", "existing Drive library id").argument("[path]", "local folder path", ".").action(async (path, opts) => {
4713
+ return new Command70("bind").description("Bind a local folder to an existing Drive library").requiredOption("--library <id>", "existing Drive library id").argument("[path]", "local folder path", ".").action(async (path, opts) => {
4503
4714
  const root = resolve(path);
4504
4715
  await assertExistingDirectory(root);
4505
4716
  const api = await createDriveApi();
@@ -4517,10 +4728,10 @@ function driveBindCommand() {
4517
4728
  }
4518
4729
 
4519
4730
  // src/handwritten/commands/drive/sync.ts
4520
- import { Command as Command64 } from "commander";
4731
+ import { Command as Command71 } from "commander";
4521
4732
  import { createWriteStream as createWriteStream2 } from "fs";
4522
- import { link, mkdir as mkdir2, readFile as readFile3, rename as rename2, rm as rm2, unlink } from "fs/promises";
4523
- import { basename as basename2, dirname, join as join4, resolve as resolve3 } from "path";
4733
+ import { link, mkdir as mkdir2, readFile as readFile3, rename as rename2, rm as rm2, unlink, writeFile as writeFile2 } from "fs/promises";
4734
+ import { basename as basename2, dirname, join as join4, posix as pathPosix2, resolve as resolve3 } from "path";
4524
4735
  import { createHash as createHash2, randomUUID as randomUUID2 } from "crypto";
4525
4736
  import { Readable as Readable2, Transform } from "stream";
4526
4737
  import { pipeline as pipeline2 } from "stream/promises";
@@ -4595,6 +4806,102 @@ function getRemoteStatus(entry, remote) {
4595
4806
  return "changed";
4596
4807
  }
4597
4808
 
4809
+ // src/handwritten/commands/drive/merge.ts
4810
+ import { posix as pathPosix } from "path";
4811
+ import { TextDecoder } from "util";
4812
+ import { diff3Merge } from "node-diff3";
4813
+ var MAX_MERGE_TEXT_BYTES = 1024 * 1024;
4814
+ var SNIFF_BYTES = 8192;
4815
+ var TEXT_EXTENSIONS = /* @__PURE__ */ new Set([
4816
+ ".txt",
4817
+ ".md",
4818
+ ".markdown",
4819
+ ".json",
4820
+ ".yaml",
4821
+ ".yml",
4822
+ ".csv",
4823
+ ".tsv",
4824
+ ".html",
4825
+ ".htm",
4826
+ ".css",
4827
+ ".js",
4828
+ ".jsx",
4829
+ ".ts",
4830
+ ".tsx",
4831
+ ".xml",
4832
+ ".svg"
4833
+ ]);
4834
+ var UTF8_DECODER = new TextDecoder("utf-8", { fatal: true });
4835
+ function classifyMergeText(path, bytes, mimeType) {
4836
+ if (bytes.byteLength > MAX_MERGE_TEXT_BYTES) {
4837
+ return { mergeable: false, reason: "too_large" };
4838
+ }
4839
+ if (hasBinaryControlBytes(bytes)) {
4840
+ return { mergeable: false, reason: "binary" };
4841
+ }
4842
+ let text;
4843
+ try {
4844
+ text = UTF8_DECODER.decode(bytes);
4845
+ } catch {
4846
+ return { mergeable: false, reason: "invalid_utf8" };
4847
+ }
4848
+ if (!hasTextHint(path, mimeType)) {
4849
+ return { mergeable: false, reason: "not_text" };
4850
+ }
4851
+ return { mergeable: true, text };
4852
+ }
4853
+ function mergeText3(base, local, remote) {
4854
+ const localNewline = local.includes("\r\n") ? "\r\n" : "\n";
4855
+ const regions = diff3Merge(normalizeLines(local), normalizeLines(base), normalizeLines(remote));
4856
+ const mergedLines = [];
4857
+ for (const region of regions) {
4858
+ if (region.conflict !== void 0) {
4859
+ return { clean: false };
4860
+ }
4861
+ if (region.ok !== void 0) {
4862
+ mergedLines.push(...region.ok);
4863
+ }
4864
+ }
4865
+ return { clean: true, text: mergedLines.join(localNewline) };
4866
+ }
4867
+ function conflictCopyPath(path, side, now, versionId) {
4868
+ const parsed = pathPosix.parse(path);
4869
+ const timestamp = now.toISOString().replace(/[-:]/g, "").replace(/\.\d{3}Z$/, "Z");
4870
+ const shortVersionId = safeShortVersionId(versionId);
4871
+ const fileName = `${parsed.name}.${side}-conflict-${timestamp}-${shortVersionId}${parsed.ext}`;
4872
+ if (parsed.dir === "") {
4873
+ return fileName;
4874
+ }
4875
+ return pathPosix.join(parsed.dir, fileName);
4876
+ }
4877
+ function hasBinaryControlBytes(bytes) {
4878
+ const sniffLength = Math.min(bytes.byteLength, SNIFF_BYTES);
4879
+ for (let index = 0; index < sniffLength; index += 1) {
4880
+ const byte = bytes[index];
4881
+ if (byte === void 0) {
4882
+ continue;
4883
+ }
4884
+ if (byte === 0) {
4885
+ return true;
4886
+ }
4887
+ if (byte < 32 && byte !== 9 && byte !== 10 && byte !== 13) {
4888
+ return true;
4889
+ }
4890
+ }
4891
+ return false;
4892
+ }
4893
+ function hasTextHint(path, mimeType) {
4894
+ const extension = pathPosix.extname(path).toLowerCase();
4895
+ return TEXT_EXTENSIONS.has(extension) || mimeType?.toLowerCase().startsWith("text/") === true;
4896
+ }
4897
+ function normalizeLines(text) {
4898
+ return text.replace(/\r\n/g, "\n").replace(/\r/g, "\n").split("\n");
4899
+ }
4900
+ function safeShortVersionId(versionId) {
4901
+ const safeVersionId = versionId.replace(/[^A-Za-z0-9_-]/g, "_");
4902
+ return (safeVersionId.length > 0 ? safeVersionId : "unknown").slice(0, 8);
4903
+ }
4904
+
4598
4905
  // src/handwritten/commands/drive/path-policy.ts
4599
4906
  import { isAbsolute, relative, resolve as resolve2, sep } from "path";
4600
4907
  var UTF8_SEGMENT_LIMIT = 255;
@@ -4722,7 +5029,7 @@ async function scanDriveFiles(root, options = {}) {
4722
5029
  }
4723
5030
  function isInternalSyncArtifactName(name) {
4724
5031
  if (!name.startsWith(".") || !name.endsWith(".tmp")) return false;
4725
- return name.includes(".wspc-download-") || name.includes(".wspc-backup-");
5032
+ return name.includes(".wspc-download-") || name.includes(".wspc-backup-") || name.includes(".wspc-conflict-") || name.includes(".wspc-merge-");
4726
5033
  }
4727
5034
  async function hashDriveFile(path) {
4728
5035
  const useNoFollow = fsConstants.O_NOFOLLOW !== void 0;
@@ -4772,8 +5079,10 @@ function emptySummary() {
4772
5079
  downloaded: 0,
4773
5080
  deleted: 0,
4774
5081
  unchanged: 0,
5082
+ merged: 0,
4775
5083
  conflicts: 0,
4776
5084
  errors: 0,
5085
+ conflict_paths: [],
4777
5086
  paths: []
4778
5087
  };
4779
5088
  }
@@ -4804,7 +5113,7 @@ async function runDriveSyncOnce(root, api) {
4804
5113
  });
4805
5114
  }
4806
5115
  function driveSyncCommand(api) {
4807
- const sync = new Command64("sync").description("Drive sync commands");
5116
+ const sync = new Command71("sync").description("Drive sync commands");
4808
5117
  sync.command("once").description("Run one Drive sync pass").argument("[path]", "local folder path", ".").action(async (path) => {
4809
5118
  const summary = await runDriveSyncOnce(resolve3(path), api);
4810
5119
  render({ kind: "drive_sync_once", display: { shape: "object" } }, summary);
@@ -4932,6 +5241,88 @@ async function processPath(args) {
4932
5241
  return { state: nextState, stop: false };
4933
5242
  }
4934
5243
  if (action.type === "conflict") {
5244
+ if (action.reason === "local_and_remote_changed") {
5245
+ try {
5246
+ const mergedState = await tryResolveConflict({
5247
+ root,
5248
+ state,
5249
+ api,
5250
+ path,
5251
+ remote,
5252
+ local,
5253
+ onLocalMutation: () => {
5254
+ durableStateRequired = true;
5255
+ }
5256
+ });
5257
+ if (mergedState) {
5258
+ summary.merged += 1;
5259
+ summary.paths[summary.paths.length - 1] = { path, action: "merged" };
5260
+ return { state: mergedState, stop: false };
5261
+ }
5262
+ } catch (error) {
5263
+ if (!isLocalChangedDuringMerge(error)) {
5264
+ throw error;
5265
+ }
5266
+ const nextState2 = await recordTypedConflict(root, state, path, action.reason, remote, {
5267
+ type: "edit_edit",
5268
+ strategy: "record_only",
5269
+ reason: "local_changed_during_merge"
5270
+ });
5271
+ summary.conflicts += 1;
5272
+ return { state: nextState2, stop: false };
5273
+ }
5274
+ const conflictCopyState = await recordRemoteConflictCopy({
5275
+ root,
5276
+ state,
5277
+ api,
5278
+ path,
5279
+ reason: action.reason,
5280
+ type: "edit_edit",
5281
+ remote
5282
+ });
5283
+ if (conflictCopyState) {
5284
+ summary.conflicts += 1;
5285
+ return { state: conflictCopyState, stop: false };
5286
+ }
5287
+ }
5288
+ if (action.reason === "local_and_remote_without_base") {
5289
+ const conflictCopyState = await recordRemoteConflictCopy({
5290
+ root,
5291
+ state,
5292
+ api,
5293
+ path,
5294
+ reason: action.reason,
5295
+ type: "create_create",
5296
+ remote
5297
+ });
5298
+ if (conflictCopyState) {
5299
+ summary.conflicts += 1;
5300
+ return { state: conflictCopyState, stop: false };
5301
+ }
5302
+ }
5303
+ if (action.reason === "local_changed_remote_deleted") {
5304
+ const nextState2 = await recordTypedConflict(root, state, path, action.reason, remote, {
5305
+ type: "edit_delete",
5306
+ strategy: "record_only"
5307
+ });
5308
+ summary.conflicts += 1;
5309
+ return { state: nextState2, stop: false };
5310
+ }
5311
+ if (action.reason === "remote_changed_before_delete") {
5312
+ const conflictCopyState = await recordRemoteConflictCopy({
5313
+ root,
5314
+ state,
5315
+ api,
5316
+ path,
5317
+ reason: action.reason,
5318
+ type: "delete_edit",
5319
+ remote
5320
+ });
5321
+ if (conflictCopyState) {
5322
+ summary.conflicts += 1;
5323
+ return { state: conflictCopyState, stop: false };
5324
+ }
5325
+ }
4935
5326
  const nextState = await recordConflict(root, state, path, action.reason, remote);
4936
5327
  summary.conflicts += 1;
4937
5328
  return { state: nextState, stop: false };
@@ -4954,23 +5345,268 @@ async function processPath(args) {
4954
5345
  }
4955
5346
  return { state, stop: false };
4956
5347
  }
5348
+ async function tryResolveConflict(args) {
5349
+ const { root, state, api, path, remote, local, onLocalMutation } = args;
5350
+ const entry = state.entries[path];
5351
+ const baseVersionId = entry?.current_version_id;
5352
+ const remoteVersionId = remote?.current_version_id;
5353
+ if (!entry || !remote || !local || baseVersionId === void 0 || remoteVersionId === void 0) {
5354
+ return void 0;
5355
+ }
5356
+ const localPath = resolveInsideRoot(root, path);
5357
+ let baseBytes;
5358
+ let remoteBytes;
5359
+ try {
5360
+ const [downloadedBaseBytes, downloadedRemoteBytes] = await Promise.all([
5361
+ downloadBytes(api, state.library_id, path, baseVersionId),
5362
+ downloadBytes(api, state.library_id, path, remoteVersionId)
5363
+ ]);
5364
+ baseBytes = downloadedBaseBytes;
5365
+ remoteBytes = downloadedRemoteBytes;
5366
+ } catch (error) {
5367
+ if (isExpectedVersionDownloadMissing(error)) {
5368
+ return void 0;
5369
+ }
5370
+ throw error;
5371
+ }
5372
+ const localBytes = await readFile3(localPath);
5373
+ const baseText = classifyMergeText(path, baseBytes, void 0);
5374
+ const localText = classifyMergeText(path, localBytes, void 0);
5375
+ const remoteText = classifyMergeText(path, remoteBytes, void 0);
5376
+ if (!baseText.mergeable || !localText.mergeable || !remoteText.mergeable) {
5377
+ return void 0;
5378
+ }
5379
+ const merged = mergeText3(baseText.text, localText.text, remoteText.text);
5380
+ if (!merged.clean) {
5381
+ return void 0;
5382
+ }
5383
+ await assertLocalStillScanned(localPath, local);
5384
+ const mergedBytes = new TextEncoder().encode(merged.text);
5385
+ const mergedDigest = createHash2("sha256").update(mergedBytes).digest("hex");
5386
+ const install = await writeMergedLocalFile(root, path, mergedBytes, mergedDigest, local, onLocalMutation);
5387
+ let uploaded;
5388
+ try {
5389
+ uploaded = await api.uploadFile(state.library_id, path, mergedBytes, mergedDigest, remote.entry_version);
5390
+ } catch (error) {
5391
+ await install.restore();
5392
+ throw error;
5393
+ }
5394
+ await install.finalize();
5395
+ const nextState = cloneDriveState(state);
5396
+ nextState.entries[path] = stateEntryFromRemote(uploaded.entry, mergedDigest);
5397
+ delete nextState.conflicts[path];
5398
+ await commitDriveState(root, nextState);
5399
+ return nextState;
5400
+ }
5401
+ async function downloadBytes(api, libraryId, path, versionId) {
5402
+ const response = await api.downloadFile(libraryId, path, versionId);
5403
+ return new Uint8Array(await response.arrayBuffer());
5404
+ }
5405
+ async function recordRemoteConflictCopy(args) {
5406
+ const { root, state, api, path, reason, type, remote } = args;
5407
+ const entry = state.entries[path];
5408
+ const remoteVersionId = remote?.current_version_id;
5409
+ if (!remote || remoteVersionId === void 0) {
5410
+ return void 0;
5411
+ }
5412
+ if (await canReuseConflictCopy(root, state.conflicts[path], remoteVersionId)) {
5413
+ return state;
5414
+ }
5415
+ const remoteBytes = await downloadBytes(api, state.library_id, path, remoteVersionId);
5416
+ const copyPath = await writeConflictCopy(root, path, "remote", remoteVersionId, remoteBytes);
5417
+ const nextState = cloneDriveState(state);
5418
+ nextState.conflicts[path] = {
5419
+ detected_at: (/* @__PURE__ */ new Date()).toISOString(),
5420
+ reason,
5421
+ type,
5422
+ strategy: "conflict_copy",
5423
+ base_version_id: entry?.current_version_id,
5424
+ remote_version_id: remoteVersionId,
5425
+ remote_entry_version: remote.entry_version,
5426
+ conflict_paths: [copyPath]
5427
+ };
5428
+ await commitDriveState(root, nextState);
5429
+ return nextState;
5430
+ }
5431
+ async function canReuseConflictCopy(root, conflict2, remoteVersionId) {
5432
+ if (conflict2?.strategy !== "conflict_copy" || conflict2.remote_version_id !== remoteVersionId || !Array.isArray(conflict2.conflict_paths) || conflict2.conflict_paths.length === 0) {
5433
+ return false;
5434
+ }
5435
+ for (const conflictPath of conflict2.conflict_paths) {
5436
+ try {
5437
+ validateDrivePath(conflictPath);
5438
+ if (!await localFileExists(resolveInsideRoot(root, conflictPath))) {
5439
+ return false;
5440
+ }
5441
+ } catch {
5442
+ return false;
5443
+ }
5444
+ }
5445
+ return true;
5446
+ }
5447
+ async function writeConflictCopy(root, path, side, versionId, bytes) {
5448
+ const baseCopyPath = conflictCopyPath(path, side, /* @__PURE__ */ new Date(), versionId);
5449
+ for (let suffix = 1; ; suffix += 1) {
5450
+ const candidate = conflictCopyPathWithSuffix(baseCopyPath, suffix);
5451
+ validateDrivePath(candidate);
5452
+ const target = resolveInsideRoot(root, candidate);
5453
+ await mkdir2(dirname(target), { recursive: true });
5454
+ for (; ; ) {
5455
+ const tmp = join4(dirname(target), `.${basename2(target)}.wspc-conflict-${randomUUID2()}.tmp`);
5456
+ let tmpWritten = false;
5457
+ try {
5458
+ await writeFile2(tmp, bytes, { flag: "wx" });
5459
+ tmpWritten = true;
5460
+ await installNoOverwrite(tmp, target);
5461
+ return candidate;
5462
+ } catch (error) {
5463
+ if (tmpWritten) {
5464
+ await rm2(tmp, { force: true }).catch(() => {
5465
+ });
5466
+ }
5467
+ if (isAlreadyExistsError(error)) {
5468
+ if (tmpWritten) break;
5469
+ continue;
5470
+ }
5471
+ throw error;
5472
+ }
5473
+ }
5474
+ }
5475
+ }
5476
+ function conflictCopyPathWithSuffix(path, suffix) {
5477
+ if (suffix === 1) {
5478
+ return path;
5479
+ }
5480
+ const parsed = pathPosix2.parse(path);
5481
+ const fileName = `${parsed.name}-${suffix}${parsed.ext}`;
5482
+ if (parsed.dir === "") {
5483
+ return fileName;
5484
+ }
5485
+ return pathPosix2.join(parsed.dir, fileName);
5486
+ }
5487
+ function isExpectedVersionDownloadMissing(error) {
5488
+ const structured = error;
5489
+ if (structured?.status === 404 || structured?.status === 410) return true;
5490
+ if (structured?.response?.status === 404 || structured?.response?.status === 410) return true;
5491
+ if (structured?.code === "VERSION_NOT_FOUND" || structured?.code === "NOT_FOUND") return true;
5492
+ return /\b(?:HTTP 40[410]|missing version|version not found|not found)\b/i.test(errorMessage(error));
5493
+ }
5494
+ async function assertLocalStillScanned(localPath, scanned) {
5495
+ const snapshot = await hashDriveFile(localPath).catch((error) => {
5496
+ if (isNotFoundError(error)) return void 0;
5497
+ throw error;
5498
+ });
5499
+ if (!snapshot || snapshot.sha256 !== scanned.sha256 || snapshot.sizeBytes !== scanned.size_bytes) {
5500
+ throw new Error("local file changed after scan");
5501
+ }
5502
+ }
4957
5503
  function recordUnresolvedConflicts(summary, state) {
4958
5504
  const newlyRecorded = new Set(summary.paths.filter((result) => result.action === "conflict").map((result) => result.path));
4959
5505
  const reportedPaths = new Set(summary.paths.map((result) => result.path));
4960
5506
  for (const path of Object.keys(state.conflicts).sort((left, right) => left.localeCompare(right))) {
5507
+ const conflictPaths = state.conflicts[path]?.conflict_paths;
5508
+ if (conflictPaths) {
5509
+ summary.conflict_paths.push(...conflictPaths);
5510
+ }
4961
5511
  if (!newlyRecorded.has(path)) {
4962
5512
  summary.conflicts += 1;
4963
5513
  }
4964
5514
  const existingResult = summary.paths.find((result) => result.path === path);
4965
5515
  if (existingResult?.action === "unchanged") {
4966
5516
  existingResult.action = "conflict";
5517
+ if (conflictPaths) existingResult.conflict_paths = conflictPaths;
4967
5518
  continue;
4968
5519
  }
5520
+ if (existingResult?.action === "conflict" && conflictPaths) {
5521
+ existingResult.conflict_paths = conflictPaths;
5522
+ }
4969
5523
  if (!reportedPaths.has(path)) {
4970
- summary.paths.push({ path, action: "conflict" });
5524
+ summary.paths.push({ path, action: "conflict", ...conflictPaths ? { conflict_paths: conflictPaths } : {} });
4971
5525
  }
4972
5526
  }
4973
5527
  }
5528
+ async function writeMergedLocalFile(root, path, bytes, digest, scanned, onLocalMutation) {
5529
+ const target = resolveInsideRoot(root, path);
5530
+ await mkdir2(dirname(target), { recursive: true });
5531
+ const tmp = join4(dirname(target), `.${basename2(target)}.wspc-merge-${randomUUID2()}.tmp`);
5532
+ try {
5533
+ await writeFile2(tmp, bytes, { flag: "wx" });
5534
+ return await installMergedLocalFile(root, path, tmp, scanned, digest, bytes.byteLength, onLocalMutation);
5535
+ } finally {
5536
+ await rm2(tmp, { force: true }).catch(() => {
5537
+ });
5538
+ }
5539
+ }
5540
+ async function installMergedLocalFile(root, path, tmp, scanned, mergedSha256, mergedSizeBytes, onLocalMutation) {
5541
+ const target = resolveInsideRoot(root, path);
5542
+ const backup = localMutationBackupPath(target);
5543
+ let backupIsScannedLocal = false;
5544
+ try {
5545
+ try {
5546
+ await rename2(target, backup);
5547
+ onLocalMutation();
5548
+ } catch (error) {
5549
+ if (isNotFoundError(error)) {
5550
+ throw new Error("local file changed after scan");
5551
+ }
5552
+ throw error;
5553
+ }
5554
+ const backupDigest = await hashDriveFile(backup);
5555
+ if (!backupDigest || backupDigest.sha256 !== scanned.sha256 || backupDigest.sizeBytes !== scanned.size_bytes) {
5556
+ await restoreBackupWhenPossible(backup, target);
5557
+ throw new Error("local file changed after scan");
5558
+ }
5559
+ backupIsScannedLocal = true;
5560
+ try {
5561
+ await installNoOverwrite(tmp, target);
5562
+ } catch (error) {
5563
+ await restoreBackupWhenPossible(backup, target);
5564
+ throw error;
5565
+ }
5566
+ return {
5567
+ finalize: async () => {
5568
+ await rm2(backup, { force: true }).catch(() => {
5569
+ });
5570
+ },
5571
+ restore: async () => {
5572
+ await restoreMergedLocalFile(target, backup, scanned.sha256, scanned.size_bytes, mergedSha256, mergedSizeBytes);
5573
+ }
5574
+ };
5575
+ } catch (error) {
5576
+ if (!backupIsScannedLocal) {
5577
+ await restoreBackupWhenPossible(backup, target);
5578
+ }
5579
+ throw error;
5580
+ }
5581
+ }
5582
+ async function restoreMergedLocalFile(target, backup, backupSha256, backupSizeBytes, mergedSha256, mergedSizeBytes) {
5583
+ const quarantine = join4(dirname(target), `.${basename2(target)}.wspc-merge-restore-${randomUUID2()}.tmp`);
5584
+ try {
5585
+ await rename2(target, quarantine);
5586
+ } catch (error) {
5587
+ if (!isNotFoundError(error)) throw error;
5588
+ }
5589
+ const quarantineDigest = await hashDriveFile(quarantine).catch((error) => {
5590
+ if (isNotFoundError(error)) return void 0;
5591
+ throw error;
5592
+ });
5593
+ if (quarantineDigest !== void 0 && (quarantineDigest.sha256 !== mergedSha256 || quarantineDigest.sizeBytes !== mergedSizeBytes)) {
5594
+ await restoreBackupWhenPossible(quarantine, target);
5595
+ return;
5596
+ }
5597
+ const restored = await restoreBackupWhenPossible(backup, target);
5598
+ if (!restored) {
5599
+ await rm2(quarantine, { force: true }).catch(() => {
5600
+ });
5601
+ return;
5602
+ }
5603
+ await unlink(quarantine).catch(() => {
5604
+ });
5605
+ const restoredDigest = await hashDriveFile(target);
5606
+ if (!restoredDigest || restoredDigest.sha256 !== backupSha256 || restoredDigest.sizeBytes !== backupSizeBytes) {
5607
+ throw new Error("local file restore failed");
5608
+ }
5609
+ }
4974
5610
  async function downloadRemote(root, libraryId, path, api, expectedSha256, entry, onLocalMutation) {
4975
5611
  const target = resolveInsideRoot(root, path);
4976
5612
  await mkdir2(dirname(target), { recursive: true });
@@ -5190,6 +5826,17 @@ async function recordConflict(root, state, path, reason, remote) {
5190
5826
  await commitDriveState(root, nextState);
5191
5827
  return nextState;
5192
5828
  }
5829
+ async function recordTypedConflict(root, state, path, reason, remote, metadata) {
5830
+ const nextState = cloneDriveState(state);
5831
+ nextState.conflicts[path] = {
5832
+ ...conflict(metadata.reason ?? reason, remote),
5833
+ type: metadata.type,
5834
+ strategy: metadata.strategy,
5835
+ base_version_id: state.entries[path]?.current_version_id
5836
+ };
5837
+ await commitDriveState(root, nextState);
5838
+ return nextState;
5839
+ }
5193
5840
  async function recordPathError(summary, blockedPaths, path, error, options = {}) {
5194
5841
  blockedPaths?.add(path);
5195
5842
  const lastPath = summary.paths.at(-1);
@@ -5217,6 +5864,9 @@ function isVersionConflict(error) {
5217
5864
  if (structured?.code === "VERSION_CONFLICT") return true;
5218
5865
  return [errorMessage(error), structured?.body, structured?.response?.body].some(containsVersionConflict);
5219
5866
  }
5867
+ function isLocalChangedDuringMerge(error) {
5868
+ return errorMessage(error) === "local file changed after scan";
5869
+ }
5220
5870
  function containsVersionConflict(value) {
5221
5871
  if (value === void 0) return false;
5222
5872
  if (typeof value === "string") return value.includes("VERSION_CONFLICT");
@@ -5234,7 +5884,7 @@ function isAlreadyExistsError(error) {
5234
5884
  }
5235
5885
 
5236
5886
  // src/handwritten/commands/drive/watch.ts
5237
- import { Command as Command65 } from "commander";
5887
+ import { Command as Command72 } from "commander";
5238
5888
  import chokidar from "chokidar";
5239
5889
  import { relative as relative2, resolve as resolve4 } from "path";
5240
5890
  async function runDriveWatch(root, options = {}) {
@@ -5341,7 +5991,7 @@ async function runDriveWatch(root, options = {}) {
5341
5991
  }
5342
5992
  }
5343
5993
  function driveWatchCommand(options = {}) {
5344
- return new Command65("watch").description("Watch a bound Drive folder and sync local changes").argument("[path]", "local folder path", ".").action(async (path) => {
5994
+ return new Command72("watch").description("Watch a bound Drive folder and sync local changes").argument("[path]", "local folder path", ".").action(async (path) => {
5345
5995
  await runDriveWatch(resolve4(path), options);
5346
5996
  });
5347
5997
  }
@@ -5396,7 +6046,7 @@ function waitForStopSignal(onRegistered) {
5396
6046
  function mountDriveCommands(program) {
5397
6047
  let drive = program.commands.find((c) => c.name() === "drive");
5398
6048
  if (!drive) {
5399
- drive = new Command66("drive").description("Drive commands");
6049
+ drive = new Command73("drive").description("Drive commands");
5400
6050
  program.addCommand(drive);
5401
6051
  }
5402
6052
  if (!drive.commands.some((c) => c.name() === "bind")) {
@@ -5422,7 +6072,7 @@ function isCliEntrypoint(argv = process.argv, metaUrl = import.meta.url) {
5422
6072
  }
5423
6073
  }
5424
6074
  function buildProgram() {
5425
- const program = new Command66().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) => {
6075
+ const program = new Command73().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) => {
5426
6076
  const globals = actionCommand.optsWithGlobals();
5427
6077
  if (globals.json) process.env.WSPC_OUTPUT = "json";
5428
6078
  if (globals.account) process.env.WSPC_ACCOUNT = String(globals.account);