nworks 0.5.0 → 0.6.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/index.js CHANGED
@@ -736,6 +736,9 @@ var directoryCommand = new Command5("directory").description("Directory (organiz
736
736
  // src/commands/calendar.ts
737
737
  import { Command as Command6 } from "commander";
738
738
 
739
+ // src/api/calendar.ts
740
+ import { randomUUID } from "crypto";
741
+
739
742
  // src/auth/token-user.ts
740
743
  async function getValidUserToken(profile = "default") {
741
744
  const cached2 = await loadUserToken(profile);
@@ -754,37 +757,164 @@ async function getValidUserToken(profile = "default") {
754
757
 
755
758
  // src/api/calendar.ts
756
759
  var BASE_URL2 = "https://www.worksapis.com/v1.0";
757
- async function listEvents(fromDateTime, untilDateTime, userId = "me", profile = "default") {
760
+ async function authedFetch(url2, init, profile) {
758
761
  const token = await getValidUserToken(profile);
762
+ const headers = new Headers(init.headers);
763
+ headers.set("Authorization", `Bearer ${token}`);
764
+ return fetch(url2, { ...init, headers });
765
+ }
766
+ async function handleError(res) {
767
+ if (res.status === 401) {
768
+ throw new AuthError("User token expired. Run `nworks login --user --scope calendar` again.");
769
+ }
770
+ let code = "UNKNOWN";
771
+ let description = `HTTP ${res.status}`;
772
+ try {
773
+ const body = await res.json();
774
+ code = body.code ?? code;
775
+ description = body.description ?? description;
776
+ } catch {
777
+ }
778
+ throw new ApiError(code, description, res.status);
779
+ }
780
+ function generateEventId() {
781
+ return `event-${randomUUID()}`;
782
+ }
783
+ function normalizeDateTime(dt) {
784
+ const match = dt.match(/^(\d{4}-\d{2}-\d{2}T\d{2}:\d{2})([+-]\d{2}:\d{2}|Z)?$/);
785
+ if (match) {
786
+ return `${match[1]}:00${match[2] ?? ""}`;
787
+ }
788
+ return dt;
789
+ }
790
+ async function listEvents(fromDateTime, untilDateTime, userId = "me", profile = "default") {
759
791
  const from = encodeURIComponent(fromDateTime);
760
792
  const until = encodeURIComponent(untilDateTime);
761
793
  const url2 = `${BASE_URL2}/users/${userId}/calendar/events?fromDateTime=${from}&untilDateTime=${until}`;
762
794
  if (process.env["NWORKS_VERBOSE"] === "1") {
763
795
  console.error(`[nworks] GET ${url2}`);
764
796
  }
765
- const res = await fetch(url2, {
766
- method: "GET",
767
- headers: {
768
- Authorization: `Bearer ${token}`,
769
- "Content-Type": "application/json"
770
- }
771
- });
772
- if (res.status === 401) {
773
- throw new AuthError("User token expired. Run `nworks login --user` again.");
797
+ const res = await authedFetch(url2, { method: "GET" }, profile);
798
+ if (!res.ok) return handleError(res);
799
+ const data = await res.json();
800
+ return { events: data.events ?? [] };
801
+ }
802
+ async function createEvent(opts) {
803
+ const userId = opts.userId ?? "me";
804
+ const profile = opts.profile ?? "default";
805
+ const timeZone = opts.timeZone ?? "Asia/Seoul";
806
+ const eventId = generateEventId();
807
+ const eventComponent = {
808
+ eventId,
809
+ summary: opts.summary,
810
+ start: { dateTime: normalizeDateTime(opts.start), timeZone },
811
+ end: { dateTime: normalizeDateTime(opts.end), timeZone }
812
+ };
813
+ if (opts.description) eventComponent.description = opts.description;
814
+ if (opts.location) eventComponent.location = opts.location;
815
+ if (opts.transparency) eventComponent.transparency = opts.transparency;
816
+ if (opts.visibility) eventComponent.visibility = opts.visibility;
817
+ if (opts.attendees) {
818
+ eventComponent.attendees = opts.attendees.map((a) => ({
819
+ email: a.email,
820
+ displayName: a.displayName ?? "",
821
+ partstat: "NEEDS-ACTION",
822
+ isOptional: false,
823
+ isResource: false
824
+ }));
774
825
  }
775
- if (!res.ok) {
776
- let code = "UNKNOWN";
777
- let description = `HTTP ${res.status}`;
778
- try {
779
- const errorBody = await res.json();
780
- code = errorBody.code ?? code;
781
- description = errorBody.description ?? description;
782
- } catch {
783
- }
784
- throw new ApiError(code, description, res.status);
826
+ const body = {
827
+ eventComponents: [eventComponent],
828
+ sendNotification: opts.sendNotification ?? false
829
+ };
830
+ const url2 = `${BASE_URL2}/users/${userId}/calendar/events`;
831
+ if (process.env["NWORKS_VERBOSE"] === "1") {
832
+ console.error(`[nworks] POST ${url2}`);
833
+ console.error(`[nworks] Body: ${JSON.stringify(body, null, 2)}`);
785
834
  }
835
+ const res = await authedFetch(
836
+ url2,
837
+ {
838
+ method: "POST",
839
+ headers: { "Content-Type": "application/json" },
840
+ body: JSON.stringify(body)
841
+ },
842
+ profile
843
+ );
844
+ if (res.status === 201) {
845
+ return await res.json();
846
+ }
847
+ if (!res.ok) return handleError(res);
848
+ return await res.json();
849
+ }
850
+ async function getEvent(eventId, userId = "me", profile = "default") {
851
+ const url2 = `${BASE_URL2}/users/${userId}/calendar/events/${eventId}`;
852
+ if (process.env["NWORKS_VERBOSE"] === "1") {
853
+ console.error(`[nworks] GET ${url2}`);
854
+ }
855
+ const res = await authedFetch(url2, { method: "GET" }, profile);
856
+ if (!res.ok) return handleError(res);
786
857
  const data = await res.json();
787
- return { events: data.events ?? [] };
858
+ return data.eventComponents[0];
859
+ }
860
+ async function updateEvent(opts) {
861
+ const userId = opts.userId ?? "me";
862
+ const profile = opts.profile ?? "default";
863
+ const timeZone = opts.timeZone ?? "Asia/Seoul";
864
+ const existing = await getEvent(opts.eventId, userId, profile);
865
+ const eventComponent = {
866
+ eventId: opts.eventId,
867
+ summary: opts.summary ?? existing.summary,
868
+ start: opts.start ? { dateTime: normalizeDateTime(opts.start), timeZone } : existing.start,
869
+ end: opts.end ? { dateTime: normalizeDateTime(opts.end), timeZone } : existing.end
870
+ };
871
+ if (opts.description !== void 0) eventComponent.description = opts.description;
872
+ else if (existing.description) eventComponent.description = existing.description;
873
+ if (opts.location !== void 0) eventComponent.location = opts.location;
874
+ else if (existing.location) eventComponent.location = existing.location;
875
+ if (opts.transparency !== void 0) eventComponent.transparency = opts.transparency;
876
+ if (opts.visibility !== void 0) eventComponent.visibility = opts.visibility;
877
+ if (opts.attendees !== void 0) {
878
+ eventComponent.attendees = opts.attendees.map((a) => ({
879
+ email: a.email,
880
+ displayName: a.displayName ?? "",
881
+ partstat: "NEEDS-ACTION",
882
+ isOptional: false,
883
+ isResource: false
884
+ }));
885
+ } else if (existing.attendees) {
886
+ eventComponent.attendees = existing.attendees;
887
+ }
888
+ const body = {
889
+ eventComponents: [eventComponent],
890
+ sendNotification: opts.sendNotification ?? false
891
+ };
892
+ const url2 = `${BASE_URL2}/users/${userId}/calendar/events/${opts.eventId}`;
893
+ if (process.env["NWORKS_VERBOSE"] === "1") {
894
+ console.error(`[nworks] PUT ${url2}`);
895
+ console.error(`[nworks] Body: ${JSON.stringify(body, null, 2)}`);
896
+ }
897
+ const res = await authedFetch(
898
+ url2,
899
+ {
900
+ method: "PUT",
901
+ headers: { "Content-Type": "application/json" },
902
+ body: JSON.stringify(body)
903
+ },
904
+ profile
905
+ );
906
+ if (!res.ok) return handleError(res);
907
+ }
908
+ async function deleteEvent(eventId, userId = "me", sendNotification = false, profile = "default") {
909
+ const params = new URLSearchParams();
910
+ params.set("sendNotification", String(sendNotification));
911
+ const url2 = `${BASE_URL2}/users/${userId}/calendar/events/${eventId}?${params.toString()}`;
912
+ if (process.env["NWORKS_VERBOSE"] === "1") {
913
+ console.error(`[nworks] DELETE ${url2}`);
914
+ }
915
+ const res = await authedFetch(url2, { method: "DELETE" }, profile);
916
+ if (res.status === 204) return;
917
+ if (!res.ok) return handleError(res);
788
918
  }
789
919
 
790
920
  // src/commands/calendar.ts
@@ -798,7 +928,7 @@ function todayRange() {
798
928
  until: `${yyyy}-${mm}-${dd}T23:59:59+09:00`
799
929
  };
800
930
  }
801
- var listCommand = new Command6("list").description("List calendar events (requires User OAuth with calendar.read scope)").option("--user <userId>", "Target user ID (default: me)").option("--from <dateTime>", "Start (YYYY-MM-DDThh:mm:ss+09:00, default: today 00:00)").option("--until <dateTime>", "End (YYYY-MM-DDThh:mm:ss+09:00, default: today 23:59)").option("--profile <name>", "Profile name", "default").option("--json", "JSON output").action(async (opts) => {
931
+ var listCommand = new Command6("list").description("List calendar events (requires User OAuth with calendar.read or calendar scope)").option("--user <userId>", "Target user ID (default: me)").option("--from <dateTime>", "Start (YYYY-MM-DDThh:mm:ss+09:00, default: today 00:00)").option("--until <dateTime>", "End (YYYY-MM-DDThh:mm:ss+09:00, default: today 23:59)").option("--profile <name>", "Profile name", "default").option("--json", "JSON output").action(async (opts) => {
802
932
  try {
803
933
  const defaults = todayRange();
804
934
  const from = opts.from ?? defaults.from;
@@ -812,6 +942,7 @@ var listCommand = new Command6("list").description("List calendar events (requir
812
942
  );
813
943
  const events = result.events.flatMap(
814
944
  (e) => e.eventComponents.map((c) => ({
945
+ eventId: c.eventId,
815
946
  summary: c.summary,
816
947
  start: c.start.dateTime ? `${c.start.dateTime} (${c.start.timeZone ?? ""})` : c.start.date ?? "",
817
948
  end: c.end.dateTime ? `${c.end.dateTime} (${c.end.timeZone ?? ""})` : c.end.date ?? "",
@@ -825,7 +956,85 @@ var listCommand = new Command6("list").description("List calendar events (requir
825
956
  process.exitCode = 1;
826
957
  }
827
958
  });
828
- var calendarCommand = new Command6("calendar").description("Calendar operations (requires User OAuth)").addCommand(listCommand);
959
+ var createCommand = new Command6("create").description("Create a calendar event (requires User OAuth with calendar scope)").requiredOption("--title <title>", "Event title (summary)").requiredOption("--start <dateTime>", "Start (YYYY-MM-DDThh:mm:ss)").requiredOption("--end <dateTime>", "End (YYYY-MM-DDThh:mm:ss)").option("--tz <timeZone>", "Time zone (default: Asia/Seoul)", "Asia/Seoul").option("--description <text>", "Event description").option("--location <place>", "Event location").option("--attendees <emails>", "Attendee emails (comma-separated)").option("--notify", "Send notification to attendees").option("--user <userId>", "Target user ID (default: me)").option("--profile <name>", "Profile name", "default").option("--json", "JSON output").action(async (opts) => {
960
+ try {
961
+ const attendees = opts.attendees ? opts.attendees.split(",").map((e) => ({ email: e.trim() })) : void 0;
962
+ const result = await createEvent({
963
+ summary: opts.title,
964
+ start: opts.start,
965
+ end: opts.end,
966
+ timeZone: opts.tz,
967
+ description: opts.description,
968
+ location: opts.location,
969
+ attendees,
970
+ sendNotification: opts.notify ?? false,
971
+ userId: opts.user ?? "me",
972
+ profile: opts.profile
973
+ });
974
+ const event = result.eventComponents?.[0];
975
+ const fmt = (t) => t?.dateTime ? `${t.dateTime} (${t.timeZone ?? ""})` : "";
976
+ output(
977
+ {
978
+ success: true,
979
+ eventId: event?.eventId,
980
+ summary: event?.summary,
981
+ start: fmt(event?.start),
982
+ end: fmt(event?.end)
983
+ },
984
+ opts
985
+ );
986
+ } catch (err) {
987
+ const error48 = err;
988
+ errorOutput({ code: error48.code, message: error48.message }, opts);
989
+ process.exitCode = 1;
990
+ }
991
+ });
992
+ var updateCommand = new Command6("update").description("Update a calendar event (requires User OAuth with calendar scope)").requiredOption("--id <eventId>", "Event ID").option("--title <title>", "New title (summary)").option("--start <dateTime>", "New start (YYYY-MM-DDThh:mm:ss)").option("--end <dateTime>", "New end (YYYY-MM-DDThh:mm:ss)").option("--tz <timeZone>", "Time zone (default: Asia/Seoul)", "Asia/Seoul").option("--description <text>", "New description").option("--location <place>", "New location").option("--attendees <emails>", "Attendee emails (comma-separated)").option("--notify", "Send notification to attendees").option("--user <userId>", "Target user ID (default: me)").option("--profile <name>", "Profile name", "default").option("--json", "JSON output").action(async (opts) => {
993
+ try {
994
+ const hasUpdate = opts.title || opts.start || opts.end || opts.description || opts.location || opts.attendees;
995
+ if (!hasUpdate) {
996
+ throw new Error("Specify at least one of: --title, --start, --end, --description, --location, --attendees");
997
+ }
998
+ const attendees = opts.attendees ? opts.attendees.split(",").map((e) => ({ email: e.trim() })) : void 0;
999
+ await updateEvent({
1000
+ eventId: opts.id,
1001
+ summary: opts.title,
1002
+ start: opts.start,
1003
+ end: opts.end,
1004
+ timeZone: opts.tz,
1005
+ description: opts.description,
1006
+ location: opts.location,
1007
+ attendees,
1008
+ sendNotification: opts.notify ?? false,
1009
+ userId: opts.user ?? "me",
1010
+ profile: opts.profile
1011
+ });
1012
+ output(
1013
+ { success: true, eventId: opts.id, message: "Event updated" },
1014
+ opts
1015
+ );
1016
+ } catch (err) {
1017
+ const error48 = err;
1018
+ errorOutput({ code: error48.code, message: error48.message }, opts);
1019
+ process.exitCode = 1;
1020
+ }
1021
+ });
1022
+ var deleteCommand = new Command6("delete").description("Delete a calendar event (requires User OAuth with calendar scope)").requiredOption("--id <eventId>", "Event ID").option("--notify", "Send notification to attendees").option("--user <userId>", "Target user ID (default: me)").option("--profile <name>", "Profile name", "default").option("--json", "JSON output").action(async (opts) => {
1023
+ try {
1024
+ await deleteEvent(
1025
+ opts.id,
1026
+ opts.user ?? "me",
1027
+ opts.notify ?? false,
1028
+ opts.profile
1029
+ );
1030
+ output({ success: true, eventId: opts.id, message: "Event deleted" }, opts);
1031
+ } catch (err) {
1032
+ const error48 = err;
1033
+ errorOutput({ code: error48.code, message: error48.message }, opts);
1034
+ process.exitCode = 1;
1035
+ }
1036
+ });
1037
+ var calendarCommand = new Command6("calendar").description("Calendar operations (requires User OAuth)").addCommand(listCommand).addCommand(createCommand).addCommand(updateCommand).addCommand(deleteCommand);
829
1038
 
830
1039
  // src/commands/drive.ts
831
1040
  import { writeFile as writeFile2 } from "fs/promises";
@@ -836,13 +1045,13 @@ import { Command as Command7 } from "commander";
836
1045
  import { readFile as readFile4, stat } from "fs/promises";
837
1046
  import { basename } from "path";
838
1047
  var BASE_URL3 = "https://www.worksapis.com/v1.0";
839
- async function authedFetch(url2, init, profile) {
1048
+ async function authedFetch2(url2, init, profile) {
840
1049
  const token = await getValidUserToken(profile);
841
1050
  const headers = new Headers(init.headers);
842
1051
  headers.set("Authorization", `Bearer ${token}`);
843
1052
  return fetch(url2, { ...init, headers });
844
1053
  }
845
- async function handleError(res) {
1054
+ async function handleError2(res) {
846
1055
  if (res.status === 401) {
847
1056
  throw new AuthError("User token expired. Run `nworks login --user --scope file` again.");
848
1057
  }
@@ -866,8 +1075,8 @@ async function listFiles(userId = "me", folderId, count = 20, cursor, profile =
866
1075
  if (process.env["NWORKS_VERBOSE"] === "1") {
867
1076
  console.error(`[nworks] GET ${url2}`);
868
1077
  }
869
- const res = await authedFetch(url2, { method: "GET" }, profile);
870
- if (!res.ok) return handleError(res);
1078
+ const res = await authedFetch2(url2, { method: "GET" }, profile);
1079
+ if (!res.ok) return handleError2(res);
871
1080
  const data = await res.json();
872
1081
  return { files: data.files ?? [], responseMetaData: data.responseMetaData };
873
1082
  }
@@ -880,7 +1089,7 @@ async function uploadFile(localPath, userId = "me", folderId, overwrite = false,
880
1089
  if (process.env["NWORKS_VERBOSE"] === "1") {
881
1090
  console.error(`[nworks] POST ${createUrl} (create upload URL)`);
882
1091
  }
883
- const createRes = await authedFetch(
1092
+ const createRes = await authedFetch2(
884
1093
  createUrl,
885
1094
  {
886
1095
  method: "POST",
@@ -889,7 +1098,7 @@ async function uploadFile(localPath, userId = "me", folderId, overwrite = false,
889
1098
  },
890
1099
  profile
891
1100
  );
892
- if (!createRes.ok) return handleError(createRes);
1101
+ if (!createRes.ok) return handleError2(createRes);
893
1102
  const { uploadUrl } = await createRes.json();
894
1103
  const fileBuffer = await readFile4(localPath);
895
1104
  const boundary = `----nworks${Date.now()}`;
@@ -907,7 +1116,7 @@ Content-Type: application/octet-stream\r
907
1116
  if (process.env["NWORKS_VERBOSE"] === "1") {
908
1117
  console.error(`[nworks] POST ${uploadUrl} (upload content, ${fileSize} bytes)`);
909
1118
  }
910
- const uploadRes = await authedFetch(
1119
+ const uploadRes = await authedFetch2(
911
1120
  uploadUrl,
912
1121
  {
913
1122
  method: "POST",
@@ -916,7 +1125,7 @@ Content-Type: application/octet-stream\r
916
1125
  },
917
1126
  profile
918
1127
  );
919
- if (!uploadRes.ok) return handleError(uploadRes);
1128
+ if (!uploadRes.ok) return handleError2(uploadRes);
920
1129
  return await uploadRes.json();
921
1130
  }
922
1131
  async function uploadBuffer(fileBuffer, fileName, userId = "me", folderId, overwrite = false, profile = "default") {
@@ -926,7 +1135,7 @@ async function uploadBuffer(fileBuffer, fileName, userId = "me", folderId, overw
926
1135
  if (process.env["NWORKS_VERBOSE"] === "1") {
927
1136
  console.error(`[nworks] POST ${createUrl} (create upload URL for buffer)`);
928
1137
  }
929
- const createRes = await authedFetch(
1138
+ const createRes = await authedFetch2(
930
1139
  createUrl,
931
1140
  {
932
1141
  method: "POST",
@@ -935,7 +1144,7 @@ async function uploadBuffer(fileBuffer, fileName, userId = "me", folderId, overw
935
1144
  },
936
1145
  profile
937
1146
  );
938
- if (!createRes.ok) return handleError(createRes);
1147
+ if (!createRes.ok) return handleError2(createRes);
939
1148
  const { uploadUrl } = await createRes.json();
940
1149
  const boundary = `----nworks${Date.now()}`;
941
1150
  const header = Buffer.from(
@@ -952,7 +1161,7 @@ Content-Type: application/octet-stream\r
952
1161
  if (process.env["NWORKS_VERBOSE"] === "1") {
953
1162
  console.error(`[nworks] POST ${uploadUrl} (upload buffer, ${fileSize} bytes)`);
954
1163
  }
955
- const uploadRes = await authedFetch(
1164
+ const uploadRes = await authedFetch2(
956
1165
  uploadUrl,
957
1166
  {
958
1167
  method: "POST",
@@ -961,7 +1170,7 @@ Content-Type: application/octet-stream\r
961
1170
  },
962
1171
  profile
963
1172
  );
964
- if (!uploadRes.ok) return handleError(uploadRes);
1173
+ if (!uploadRes.ok) return handleError2(uploadRes);
965
1174
  return await uploadRes.json();
966
1175
  }
967
1176
  async function downloadFile(fileId, userId = "me", profile = "default") {
@@ -969,7 +1178,7 @@ async function downloadFile(fileId, userId = "me", profile = "default") {
969
1178
  if (process.env["NWORKS_VERBOSE"] === "1") {
970
1179
  console.error(`[nworks] GET ${url2} (get download URL)`);
971
1180
  }
972
- const redirectRes = await authedFetch(
1181
+ const redirectRes = await authedFetch2(
973
1182
  url2,
974
1183
  { method: "GET", redirect: "manual" },
975
1184
  profile
@@ -979,14 +1188,14 @@ async function downloadFile(fileId, userId = "me", profile = "default") {
979
1188
  }
980
1189
  const location = redirectRes.headers.get("location");
981
1190
  if (!location) {
982
- if (!redirectRes.ok) return handleError(redirectRes);
1191
+ if (!redirectRes.ok) return handleError2(redirectRes);
983
1192
  throw new ApiError("NO_REDIRECT", "No download URL returned", redirectRes.status);
984
1193
  }
985
1194
  if (process.env["NWORKS_VERBOSE"] === "1") {
986
1195
  console.error(`[nworks] GET ${location} (download content)`);
987
1196
  }
988
- const downloadRes = await authedFetch(location, { method: "GET" }, profile);
989
- if (!downloadRes.ok) return handleError(downloadRes);
1197
+ const downloadRes = await authedFetch2(location, { method: "GET" }, profile);
1198
+ if (!downloadRes.ok) return handleError2(downloadRes);
990
1199
  const arrayBuffer = await downloadRes.arrayBuffer();
991
1200
  const buffer = Buffer.from(arrayBuffer);
992
1201
  const disposition = downloadRes.headers.get("content-disposition");
@@ -1105,13 +1314,13 @@ import { Command as Command8 } from "commander";
1105
1314
 
1106
1315
  // src/api/mail.ts
1107
1316
  var BASE_URL4 = "https://www.worksapis.com/v1.0";
1108
- async function authedFetch2(url2, init, profile) {
1317
+ async function authedFetch3(url2, init, profile) {
1109
1318
  const token = await getValidUserToken(profile);
1110
1319
  const headers = new Headers(init.headers);
1111
1320
  headers.set("Authorization", `Bearer ${token}`);
1112
1321
  return fetch(url2, { ...init, headers });
1113
1322
  }
1114
- async function handleError2(res) {
1323
+ async function handleError3(res) {
1115
1324
  if (res.status === 401) {
1116
1325
  throw new AuthError("User token expired. Run `nworks login --user --scope mail` again.");
1117
1326
  }
@@ -1140,7 +1349,7 @@ async function sendMail(opts) {
1140
1349
  if (opts.cc) body.cc = opts.cc;
1141
1350
  if (opts.bcc) body.bcc = opts.bcc;
1142
1351
  if (opts.contentType) body.contentType = opts.contentType;
1143
- const res = await authedFetch2(
1352
+ const res = await authedFetch3(
1144
1353
  url2,
1145
1354
  {
1146
1355
  method: "POST",
@@ -1150,7 +1359,7 @@ async function sendMail(opts) {
1150
1359
  profile
1151
1360
  );
1152
1361
  if (res.status === 202) return;
1153
- if (!res.ok) return handleError2(res);
1362
+ if (!res.ok) return handleError3(res);
1154
1363
  }
1155
1364
  async function listMails(folderId = 0, userId = "me", count = 30, cursor, isUnread, profile = "default") {
1156
1365
  const params = new URLSearchParams();
@@ -1161,8 +1370,8 @@ async function listMails(folderId = 0, userId = "me", count = 30, cursor, isUnre
1161
1370
  if (process.env["NWORKS_VERBOSE"] === "1") {
1162
1371
  console.error(`[nworks] GET ${url2}`);
1163
1372
  }
1164
- const res = await authedFetch2(url2, { method: "GET" }, profile);
1165
- if (!res.ok) return handleError2(res);
1373
+ const res = await authedFetch3(url2, { method: "GET" }, profile);
1374
+ if (!res.ok) return handleError3(res);
1166
1375
  const data = await res.json();
1167
1376
  return {
1168
1377
  mails: data.mails ?? [],
@@ -1177,8 +1386,8 @@ async function readMail(mailId, userId = "me", profile = "default") {
1177
1386
  if (process.env["NWORKS_VERBOSE"] === "1") {
1178
1387
  console.error(`[nworks] GET ${url2}`);
1179
1388
  }
1180
- const res = await authedFetch2(url2, { method: "GET" }, profile);
1181
- if (!res.ok) return handleError2(res);
1389
+ const res = await authedFetch3(url2, { method: "GET" }, profile);
1390
+ if (!res.ok) return handleError3(res);
1182
1391
  return await res.json();
1183
1392
  }
1184
1393
 
@@ -1281,13 +1490,13 @@ import { Command as Command9 } from "commander";
1281
1490
 
1282
1491
  // src/api/task.ts
1283
1492
  var BASE_URL5 = "https://www.worksapis.com/v1.0";
1284
- async function authedFetch3(url2, init, profile) {
1493
+ async function authedFetch4(url2, init, profile) {
1285
1494
  const token = await getValidUserToken(profile);
1286
1495
  const headers = new Headers(init.headers);
1287
1496
  headers.set("Authorization", `Bearer ${token}`);
1288
1497
  return fetch(url2, { ...init, headers });
1289
1498
  }
1290
- async function handleError3(res) {
1499
+ async function handleError4(res) {
1291
1500
  if (res.status === 401) {
1292
1501
  throw new AuthError("User token expired. Run `nworks login --user --scope task` again.");
1293
1502
  }
@@ -1304,8 +1513,8 @@ async function handleError3(res) {
1304
1513
  async function resolveUserId(userId, profile) {
1305
1514
  if (userId !== "me") return userId;
1306
1515
  const url2 = `${BASE_URL5}/users/me`;
1307
- const res = await authedFetch3(url2, { method: "GET" }, profile);
1308
- if (!res.ok) return handleError3(res);
1516
+ const res = await authedFetch4(url2, { method: "GET" }, profile);
1517
+ if (!res.ok) return handleError4(res);
1309
1518
  const data = await res.json();
1310
1519
  if (process.env["NWORKS_VERBOSE"] === "1") {
1311
1520
  console.error(`[nworks] Resolved "me" \u2192 ${data.userId}`);
@@ -1322,8 +1531,8 @@ async function listTasks(categoryId = "default", userId = "me", count = 50, curs
1322
1531
  if (process.env["NWORKS_VERBOSE"] === "1") {
1323
1532
  console.error(`[nworks] GET ${url2}`);
1324
1533
  }
1325
- const res = await authedFetch3(url2, { method: "GET" }, profile);
1326
- if (!res.ok) return handleError3(res);
1534
+ const res = await authedFetch4(url2, { method: "GET" }, profile);
1535
+ if (!res.ok) return handleError4(res);
1327
1536
  const data = await res.json();
1328
1537
  return { tasks: data.tasks ?? [], responseMetaData: data.responseMetaData };
1329
1538
  }
@@ -1347,7 +1556,7 @@ async function createTask(opts) {
1347
1556
  console.error(`[nworks] POST ${url2}`);
1348
1557
  console.error(`[nworks] Body: ${JSON.stringify(body, null, 2)}`);
1349
1558
  }
1350
- const res = await authedFetch3(
1559
+ const res = await authedFetch4(
1351
1560
  url2,
1352
1561
  {
1353
1562
  method: "POST",
@@ -1359,7 +1568,7 @@ async function createTask(opts) {
1359
1568
  if (res.status === 201) {
1360
1569
  return await res.json();
1361
1570
  }
1362
- if (!res.ok) return handleError3(res);
1571
+ if (!res.ok) return handleError4(res);
1363
1572
  return await res.json();
1364
1573
  }
1365
1574
  async function updateTask(opts) {
@@ -1372,7 +1581,7 @@ async function updateTask(opts) {
1372
1581
  if (process.env["NWORKS_VERBOSE"] === "1") {
1373
1582
  console.error(`[nworks] PATCH ${url2}`);
1374
1583
  }
1375
- const res = await authedFetch3(
1584
+ const res = await authedFetch4(
1376
1585
  url2,
1377
1586
  {
1378
1587
  method: "PATCH",
@@ -1381,7 +1590,7 @@ async function updateTask(opts) {
1381
1590
  },
1382
1591
  profile
1383
1592
  );
1384
- if (!res.ok) return handleError3(res);
1593
+ if (!res.ok) return handleError4(res);
1385
1594
  return await res.json();
1386
1595
  }
1387
1596
  async function completeTask(taskId, profile = "default") {
@@ -1389,39 +1598,39 @@ async function completeTask(taskId, profile = "default") {
1389
1598
  if (process.env["NWORKS_VERBOSE"] === "1") {
1390
1599
  console.error(`[nworks] POST ${url2}`);
1391
1600
  }
1392
- const res = await authedFetch3(
1601
+ const res = await authedFetch4(
1393
1602
  url2,
1394
1603
  { method: "POST" },
1395
1604
  profile
1396
1605
  );
1397
1606
  if (res.status === 204) return;
1398
- if (!res.ok) return handleError3(res);
1607
+ if (!res.ok) return handleError4(res);
1399
1608
  }
1400
1609
  async function incompleteTask(taskId, profile = "default") {
1401
1610
  const url2 = `${BASE_URL5}/tasks/${taskId}/incomplete`;
1402
1611
  if (process.env["NWORKS_VERBOSE"] === "1") {
1403
1612
  console.error(`[nworks] POST ${url2}`);
1404
1613
  }
1405
- const res = await authedFetch3(
1614
+ const res = await authedFetch4(
1406
1615
  url2,
1407
1616
  { method: "POST" },
1408
1617
  profile
1409
1618
  );
1410
1619
  if (res.status === 204) return;
1411
- if (!res.ok) return handleError3(res);
1620
+ if (!res.ok) return handleError4(res);
1412
1621
  }
1413
1622
  async function deleteTask(taskId, profile = "default") {
1414
1623
  const url2 = `${BASE_URL5}/tasks/${taskId}`;
1415
1624
  if (process.env["NWORKS_VERBOSE"] === "1") {
1416
1625
  console.error(`[nworks] DELETE ${url2}`);
1417
1626
  }
1418
- const res = await authedFetch3(
1627
+ const res = await authedFetch4(
1419
1628
  url2,
1420
1629
  { method: "DELETE" },
1421
1630
  profile
1422
1631
  );
1423
1632
  if (res.status === 204) return;
1424
- if (!res.ok) return handleError3(res);
1633
+ if (!res.ok) return handleError4(res);
1425
1634
  }
1426
1635
 
1427
1636
  // src/commands/task.ts
@@ -1457,7 +1666,7 @@ var listCommand4 = new Command9("list").description("List tasks (requires User O
1457
1666
  process.exitCode = 1;
1458
1667
  }
1459
1668
  });
1460
- var createCommand = new Command9("create").description("Create a task (requires User OAuth with task scope)").requiredOption("--title <title>", "Task title").option("--body <content>", "Task content/description").option("--due <date>", "Due date (YYYY-MM-DD)").option("--category <categoryId>", "Category ID").option("--assignee <userIds>", "Assignee user IDs (comma-separated)").option("--user <userId>", "Creator user ID (default: me)").option("--profile <name>", "Profile name", "default").option("--json", "JSON output").action(async (opts) => {
1669
+ var createCommand2 = new Command9("create").description("Create a task (requires User OAuth with task scope)").requiredOption("--title <title>", "Task title").option("--body <content>", "Task content/description").option("--due <date>", "Due date (YYYY-MM-DD)").option("--category <categoryId>", "Category ID").option("--assignee <userIds>", "Assignee user IDs (comma-separated)").option("--user <userId>", "Creator user ID (default: me)").option("--profile <name>", "Profile name", "default").option("--json", "JSON output").action(async (opts) => {
1461
1670
  try {
1462
1671
  const assigneeIds = opts.assignee ? opts.assignee.split(",").map((s) => s.trim()) : void 0;
1463
1672
  const result = await createTask({
@@ -1485,7 +1694,7 @@ var createCommand = new Command9("create").description("Create a task (requires
1485
1694
  process.exitCode = 1;
1486
1695
  }
1487
1696
  });
1488
- var updateCommand = new Command9("update").description("Update a task (requires User OAuth with task scope)").requiredOption("--id <taskId>", "Task ID").option("--title <title>", "New title").option("--body <content>", "New content").option("--due <date>", "New due date (YYYY-MM-DD)").option("--status <status>", "Set status: done or todo").option("--profile <name>", "Profile name", "default").option("--json", "JSON output").action(async (opts) => {
1697
+ var updateCommand2 = new Command9("update").description("Update a task (requires User OAuth with task scope)").requiredOption("--id <taskId>", "Task ID").option("--title <title>", "New title").option("--body <content>", "New content").option("--due <date>", "New due date (YYYY-MM-DD)").option("--status <status>", "Set status: done or todo").option("--profile <name>", "Profile name", "default").option("--json", "JSON output").action(async (opts) => {
1489
1698
  try {
1490
1699
  const profile = opts.profile;
1491
1700
  const taskId = opts.id;
@@ -1532,7 +1741,7 @@ var updateCommand = new Command9("update").description("Update a task (requires
1532
1741
  process.exitCode = 1;
1533
1742
  }
1534
1743
  });
1535
- var deleteCommand = new Command9("delete").description("Delete a task (requires User OAuth with task scope)").requiredOption("--id <taskId>", "Task ID").option("--profile <name>", "Profile name", "default").option("--json", "JSON output").action(async (opts) => {
1744
+ var deleteCommand2 = new Command9("delete").description("Delete a task (requires User OAuth with task scope)").requiredOption("--id <taskId>", "Task ID").option("--profile <name>", "Profile name", "default").option("--json", "JSON output").action(async (opts) => {
1536
1745
  try {
1537
1746
  await deleteTask(
1538
1747
  opts.id,
@@ -1545,7 +1754,7 @@ var deleteCommand = new Command9("delete").description("Delete a task (requires
1545
1754
  process.exitCode = 1;
1546
1755
  }
1547
1756
  });
1548
- var taskCommand = new Command9("task").description("Task operations (requires User OAuth with task scope)").addCommand(listCommand4).addCommand(createCommand).addCommand(updateCommand).addCommand(deleteCommand);
1757
+ var taskCommand = new Command9("task").description("Task operations (requires User OAuth with task scope)").addCommand(listCommand4).addCommand(createCommand2).addCommand(updateCommand2).addCommand(deleteCommand2);
1549
1758
 
1550
1759
  // src/commands/mcp-cmd.ts
1551
1760
  import { Command as Command10 } from "commander";
@@ -15414,6 +15623,7 @@ function registerTools(server) {
15414
15623
  );
15415
15624
  const events = result.events.flatMap(
15416
15625
  (e) => e.eventComponents.map((c) => ({
15626
+ eventId: c.eventId,
15417
15627
  summary: c.summary,
15418
15628
  start: c.start.dateTime ?? c.start.date ?? "",
15419
15629
  end: c.end.dateTime ?? c.end.date ?? "",
@@ -15432,6 +15642,112 @@ function registerTools(server) {
15432
15642
  }
15433
15643
  }
15434
15644
  );
15645
+ server.tool(
15646
+ "nworks_calendar_create",
15647
+ "\uCE98\uB9B0\uB354 \uC77C\uC815\uC744 \uC0DD\uC131\uD569\uB2C8\uB2E4 (User OAuth calendar scope \uD544\uC694)",
15648
+ {
15649
+ summary: external_exports.string().describe("\uC77C\uC815 \uC81C\uBAA9"),
15650
+ start: external_exports.string().describe("\uC2DC\uC791 \uC77C\uC2DC (YYYY-MM-DDThh:mm:ss)"),
15651
+ end: external_exports.string().describe("\uC885\uB8CC \uC77C\uC2DC (YYYY-MM-DDThh:mm:ss)"),
15652
+ timeZone: external_exports.string().optional().describe("\uD0C0\uC784\uC874 (\uAE30\uBCF8: Asia/Seoul)"),
15653
+ description: external_exports.string().optional().describe("\uC77C\uC815 \uC124\uBA85"),
15654
+ location: external_exports.string().optional().describe("\uC7A5\uC18C"),
15655
+ attendees: external_exports.array(external_exports.object({ email: external_exports.string(), displayName: external_exports.string().optional() })).optional().describe("\uCC38\uC11D\uC790 \uBAA9\uB85D"),
15656
+ sendNotification: external_exports.boolean().optional().describe("\uCC38\uC11D\uC790\uC5D0\uAC8C \uC54C\uB9BC \uBC1C\uC1A1 (\uAE30\uBCF8: false)"),
15657
+ userId: external_exports.string().optional().describe("\uB300\uC0C1 \uC0AC\uC6A9\uC790 ID (\uBBF8\uC9C0\uC815 \uC2DC me)")
15658
+ },
15659
+ async ({ summary, start, end, timeZone, description, location, attendees, sendNotification, userId }) => {
15660
+ try {
15661
+ const result = await createEvent({
15662
+ summary,
15663
+ start,
15664
+ end,
15665
+ timeZone,
15666
+ description,
15667
+ location,
15668
+ attendees,
15669
+ sendNotification,
15670
+ userId: userId ?? "me"
15671
+ });
15672
+ const event = result.eventComponents?.[0];
15673
+ return {
15674
+ content: [{ type: "text", text: JSON.stringify({ success: true, eventId: event?.eventId, summary: event?.summary, start: event?.start, end: event?.end }) }]
15675
+ };
15676
+ } catch (err) {
15677
+ const error48 = err;
15678
+ return {
15679
+ content: [{ type: "text", text: `Error: ${error48.message}` }],
15680
+ isError: true
15681
+ };
15682
+ }
15683
+ }
15684
+ );
15685
+ server.tool(
15686
+ "nworks_calendar_update",
15687
+ "\uCE98\uB9B0\uB354 \uC77C\uC815\uC744 \uC218\uC815\uD569\uB2C8\uB2E4 (User OAuth calendar scope \uD544\uC694)",
15688
+ {
15689
+ eventId: external_exports.string().describe("\uC77C\uC815 ID"),
15690
+ summary: external_exports.string().optional().describe("\uC0C8 \uC81C\uBAA9"),
15691
+ start: external_exports.string().optional().describe("\uC0C8 \uC2DC\uC791 \uC77C\uC2DC (YYYY-MM-DDThh:mm:ss)"),
15692
+ end: external_exports.string().optional().describe("\uC0C8 \uC885\uB8CC \uC77C\uC2DC (YYYY-MM-DDThh:mm:ss)"),
15693
+ timeZone: external_exports.string().optional().describe("\uD0C0\uC784\uC874 (\uAE30\uBCF8: Asia/Seoul)"),
15694
+ description: external_exports.string().optional().describe("\uC0C8 \uC124\uBA85"),
15695
+ location: external_exports.string().optional().describe("\uC0C8 \uC7A5\uC18C"),
15696
+ sendNotification: external_exports.boolean().optional().describe("\uCC38\uC11D\uC790\uC5D0\uAC8C \uC54C\uB9BC \uBC1C\uC1A1 (\uAE30\uBCF8: false)"),
15697
+ userId: external_exports.string().optional().describe("\uB300\uC0C1 \uC0AC\uC6A9\uC790 ID (\uBBF8\uC9C0\uC815 \uC2DC me)")
15698
+ },
15699
+ async ({ eventId, summary, start, end, timeZone, description, location, sendNotification, userId }) => {
15700
+ try {
15701
+ await updateEvent({
15702
+ eventId,
15703
+ summary,
15704
+ start,
15705
+ end,
15706
+ timeZone,
15707
+ description,
15708
+ location,
15709
+ sendNotification,
15710
+ userId: userId ?? "me"
15711
+ });
15712
+ return {
15713
+ content: [{ type: "text", text: JSON.stringify({ success: true, eventId, message: "Event updated" }) }]
15714
+ };
15715
+ } catch (err) {
15716
+ const error48 = err;
15717
+ return {
15718
+ content: [{ type: "text", text: `Error: ${error48.message}` }],
15719
+ isError: true
15720
+ };
15721
+ }
15722
+ }
15723
+ );
15724
+ server.tool(
15725
+ "nworks_calendar_delete",
15726
+ "\uCE98\uB9B0\uB354 \uC77C\uC815\uC744 \uC0AD\uC81C\uD569\uB2C8\uB2E4 (User OAuth calendar scope \uD544\uC694)",
15727
+ {
15728
+ eventId: external_exports.string().describe("\uC0AD\uC81C\uD560 \uC77C\uC815 ID"),
15729
+ sendNotification: external_exports.boolean().optional().describe("\uCC38\uC11D\uC790\uC5D0\uAC8C \uC54C\uB9BC \uBC1C\uC1A1 (\uAE30\uBCF8: false)"),
15730
+ userId: external_exports.string().optional().describe("\uB300\uC0C1 \uC0AC\uC6A9\uC790 ID (\uBBF8\uC9C0\uC815 \uC2DC me)")
15731
+ },
15732
+ async ({ eventId, sendNotification, userId }) => {
15733
+ try {
15734
+ await deleteEvent(
15735
+ eventId,
15736
+ userId ?? "me",
15737
+ sendNotification ?? false
15738
+ );
15739
+ return {
15740
+ content: [{ type: "text", text: JSON.stringify({ success: true, eventId, message: "Event deleted" }) }]
15741
+ };
15742
+ } catch (err) {
15743
+ const error48 = err;
15744
+ return {
15745
+ content: [{ type: "text", text: `Error: ${error48.message}` }],
15746
+ isError: true
15747
+ };
15748
+ }
15749
+ }
15750
+ );
15435
15751
  server.tool(
15436
15752
  "nworks_drive_list",
15437
15753
  "\uB4DC\uB77C\uC774\uBE0C \uD30C\uC77C/\uD3F4\uB354 \uBAA9\uB85D\uC744 \uC870\uD68C\uD569\uB2C8\uB2E4 (User OAuth file \uB610\uB294 file.read scope \uD544\uC694)",