nworks 0.4.0 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +69 -7
- package/dist/index.js +774 -51
- package/dist/index.js.map +1 -1
- package/dist/mcp.js +562 -45
- package/dist/mcp.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -7,7 +7,7 @@ var __export = (target, all) => {
|
|
|
7
7
|
|
|
8
8
|
// src/index.ts
|
|
9
9
|
import { createRequire } from "module";
|
|
10
|
-
import { Command as
|
|
10
|
+
import { Command as Command11 } from "commander";
|
|
11
11
|
|
|
12
12
|
// src/commands/login.ts
|
|
13
13
|
import { Command } from "commander";
|
|
@@ -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
|
|
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
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
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
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
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)}`);
|
|
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();
|
|
785
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
870
|
-
if (!res.ok) return
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
989
|
-
if (!downloadRes.ok) return
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
1165
|
-
if (!res.ok) return
|
|
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
|
|
1181
|
-
if (!res.ok) return
|
|
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
|
|
|
@@ -1276,9 +1485,280 @@ var readCommand = new Command8("read").description("Read a specific mail (requir
|
|
|
1276
1485
|
});
|
|
1277
1486
|
var mailCommand = new Command8("mail").description("Mail operations (requires User OAuth with mail scope)").addCommand(sendCommand2).addCommand(listCommand3).addCommand(readCommand);
|
|
1278
1487
|
|
|
1279
|
-
// src/commands/
|
|
1488
|
+
// src/commands/task.ts
|
|
1280
1489
|
import { Command as Command9 } from "commander";
|
|
1281
1490
|
|
|
1491
|
+
// src/api/task.ts
|
|
1492
|
+
var BASE_URL5 = "https://www.worksapis.com/v1.0";
|
|
1493
|
+
async function authedFetch4(url2, init, profile) {
|
|
1494
|
+
const token = await getValidUserToken(profile);
|
|
1495
|
+
const headers = new Headers(init.headers);
|
|
1496
|
+
headers.set("Authorization", `Bearer ${token}`);
|
|
1497
|
+
return fetch(url2, { ...init, headers });
|
|
1498
|
+
}
|
|
1499
|
+
async function handleError4(res) {
|
|
1500
|
+
if (res.status === 401) {
|
|
1501
|
+
throw new AuthError("User token expired. Run `nworks login --user --scope task` again.");
|
|
1502
|
+
}
|
|
1503
|
+
let code = "UNKNOWN";
|
|
1504
|
+
let description = `HTTP ${res.status}`;
|
|
1505
|
+
try {
|
|
1506
|
+
const body = await res.json();
|
|
1507
|
+
code = body.code ?? code;
|
|
1508
|
+
description = body.description ?? description;
|
|
1509
|
+
} catch {
|
|
1510
|
+
}
|
|
1511
|
+
throw new ApiError(code, description, res.status);
|
|
1512
|
+
}
|
|
1513
|
+
async function resolveUserId(userId, profile) {
|
|
1514
|
+
if (userId !== "me") return userId;
|
|
1515
|
+
const url2 = `${BASE_URL5}/users/me`;
|
|
1516
|
+
const res = await authedFetch4(url2, { method: "GET" }, profile);
|
|
1517
|
+
if (!res.ok) return handleError4(res);
|
|
1518
|
+
const data = await res.json();
|
|
1519
|
+
if (process.env["NWORKS_VERBOSE"] === "1") {
|
|
1520
|
+
console.error(`[nworks] Resolved "me" \u2192 ${data.userId}`);
|
|
1521
|
+
}
|
|
1522
|
+
return data.userId;
|
|
1523
|
+
}
|
|
1524
|
+
async function listTasks(categoryId = "default", userId = "me", count = 50, cursor, status = "ALL", profile = "default") {
|
|
1525
|
+
const params = new URLSearchParams();
|
|
1526
|
+
params.set("categoryId", categoryId);
|
|
1527
|
+
params.set("count", String(count));
|
|
1528
|
+
params.set("status", status);
|
|
1529
|
+
if (cursor) params.set("cursor", cursor);
|
|
1530
|
+
const url2 = `${BASE_URL5}/users/${userId}/tasks?${params.toString()}`;
|
|
1531
|
+
if (process.env["NWORKS_VERBOSE"] === "1") {
|
|
1532
|
+
console.error(`[nworks] GET ${url2}`);
|
|
1533
|
+
}
|
|
1534
|
+
const res = await authedFetch4(url2, { method: "GET" }, profile);
|
|
1535
|
+
if (!res.ok) return handleError4(res);
|
|
1536
|
+
const data = await res.json();
|
|
1537
|
+
return { tasks: data.tasks ?? [], responseMetaData: data.responseMetaData };
|
|
1538
|
+
}
|
|
1539
|
+
async function createTask(opts) {
|
|
1540
|
+
const userId = opts.userId ?? "me";
|
|
1541
|
+
const profile = opts.profile ?? "default";
|
|
1542
|
+
const resolvedUserId = await resolveUserId(userId, profile);
|
|
1543
|
+
const assignorId = opts.assignorId ?? resolvedUserId;
|
|
1544
|
+
const assigneeIds = opts.assigneeIds ?? [resolvedUserId];
|
|
1545
|
+
const body = {
|
|
1546
|
+
assignorId,
|
|
1547
|
+
assignees: assigneeIds.map((id) => ({ assigneeId: id, status: "TODO" })),
|
|
1548
|
+
title: opts.title,
|
|
1549
|
+
content: opts.content ?? "",
|
|
1550
|
+
completionCondition: "ANY_ONE"
|
|
1551
|
+
};
|
|
1552
|
+
if (opts.dueDate) body.dueDate = opts.dueDate;
|
|
1553
|
+
if (opts.categoryId) body.categoryId = opts.categoryId;
|
|
1554
|
+
const url2 = `${BASE_URL5}/users/${userId}/tasks`;
|
|
1555
|
+
if (process.env["NWORKS_VERBOSE"] === "1") {
|
|
1556
|
+
console.error(`[nworks] POST ${url2}`);
|
|
1557
|
+
console.error(`[nworks] Body: ${JSON.stringify(body, null, 2)}`);
|
|
1558
|
+
}
|
|
1559
|
+
const res = await authedFetch4(
|
|
1560
|
+
url2,
|
|
1561
|
+
{
|
|
1562
|
+
method: "POST",
|
|
1563
|
+
headers: { "Content-Type": "application/json" },
|
|
1564
|
+
body: JSON.stringify(body)
|
|
1565
|
+
},
|
|
1566
|
+
profile
|
|
1567
|
+
);
|
|
1568
|
+
if (res.status === 201) {
|
|
1569
|
+
return await res.json();
|
|
1570
|
+
}
|
|
1571
|
+
if (!res.ok) return handleError4(res);
|
|
1572
|
+
return await res.json();
|
|
1573
|
+
}
|
|
1574
|
+
async function updateTask(opts) {
|
|
1575
|
+
const profile = opts.profile ?? "default";
|
|
1576
|
+
const body = {};
|
|
1577
|
+
if (opts.title !== void 0) body.title = opts.title;
|
|
1578
|
+
if (opts.content !== void 0) body.content = opts.content;
|
|
1579
|
+
if (opts.dueDate !== void 0) body.dueDate = opts.dueDate;
|
|
1580
|
+
const url2 = `${BASE_URL5}/tasks/${opts.taskId}`;
|
|
1581
|
+
if (process.env["NWORKS_VERBOSE"] === "1") {
|
|
1582
|
+
console.error(`[nworks] PATCH ${url2}`);
|
|
1583
|
+
}
|
|
1584
|
+
const res = await authedFetch4(
|
|
1585
|
+
url2,
|
|
1586
|
+
{
|
|
1587
|
+
method: "PATCH",
|
|
1588
|
+
headers: { "Content-Type": "application/json" },
|
|
1589
|
+
body: JSON.stringify(body)
|
|
1590
|
+
},
|
|
1591
|
+
profile
|
|
1592
|
+
);
|
|
1593
|
+
if (!res.ok) return handleError4(res);
|
|
1594
|
+
return await res.json();
|
|
1595
|
+
}
|
|
1596
|
+
async function completeTask(taskId, profile = "default") {
|
|
1597
|
+
const url2 = `${BASE_URL5}/tasks/${taskId}/complete`;
|
|
1598
|
+
if (process.env["NWORKS_VERBOSE"] === "1") {
|
|
1599
|
+
console.error(`[nworks] POST ${url2}`);
|
|
1600
|
+
}
|
|
1601
|
+
const res = await authedFetch4(
|
|
1602
|
+
url2,
|
|
1603
|
+
{ method: "POST" },
|
|
1604
|
+
profile
|
|
1605
|
+
);
|
|
1606
|
+
if (res.status === 204) return;
|
|
1607
|
+
if (!res.ok) return handleError4(res);
|
|
1608
|
+
}
|
|
1609
|
+
async function incompleteTask(taskId, profile = "default") {
|
|
1610
|
+
const url2 = `${BASE_URL5}/tasks/${taskId}/incomplete`;
|
|
1611
|
+
if (process.env["NWORKS_VERBOSE"] === "1") {
|
|
1612
|
+
console.error(`[nworks] POST ${url2}`);
|
|
1613
|
+
}
|
|
1614
|
+
const res = await authedFetch4(
|
|
1615
|
+
url2,
|
|
1616
|
+
{ method: "POST" },
|
|
1617
|
+
profile
|
|
1618
|
+
);
|
|
1619
|
+
if (res.status === 204) return;
|
|
1620
|
+
if (!res.ok) return handleError4(res);
|
|
1621
|
+
}
|
|
1622
|
+
async function deleteTask(taskId, profile = "default") {
|
|
1623
|
+
const url2 = `${BASE_URL5}/tasks/${taskId}`;
|
|
1624
|
+
if (process.env["NWORKS_VERBOSE"] === "1") {
|
|
1625
|
+
console.error(`[nworks] DELETE ${url2}`);
|
|
1626
|
+
}
|
|
1627
|
+
const res = await authedFetch4(
|
|
1628
|
+
url2,
|
|
1629
|
+
{ method: "DELETE" },
|
|
1630
|
+
profile
|
|
1631
|
+
);
|
|
1632
|
+
if (res.status === 204) return;
|
|
1633
|
+
if (!res.ok) return handleError4(res);
|
|
1634
|
+
}
|
|
1635
|
+
|
|
1636
|
+
// src/commands/task.ts
|
|
1637
|
+
var listCommand4 = new Command9("list").description("List tasks (requires User OAuth with task or task.read scope)").option("--category <categoryId>", "Category ID (default: default)", "default").option("--status <status>", "Filter: TODO or ALL (default: ALL)", "ALL").option("--count <n>", "Items per page (default: 50)", "50").option("--cursor <cursor>", "Pagination cursor").option("--user <userId>", "Target user ID (default: me)").option("--profile <name>", "Profile name", "default").option("--json", "JSON output").action(async (opts) => {
|
|
1638
|
+
try {
|
|
1639
|
+
const result = await listTasks(
|
|
1640
|
+
opts.category,
|
|
1641
|
+
opts.user ?? "me",
|
|
1642
|
+
parseInt(opts.count, 10),
|
|
1643
|
+
opts.cursor,
|
|
1644
|
+
opts.status,
|
|
1645
|
+
opts.profile
|
|
1646
|
+
);
|
|
1647
|
+
const tasks = result.tasks.map((t) => ({
|
|
1648
|
+
taskId: t.taskId,
|
|
1649
|
+
title: t.title,
|
|
1650
|
+
status: t.status,
|
|
1651
|
+
dueDate: t.dueDate ?? "",
|
|
1652
|
+
assignor: t.assignorName ?? t.assignorId,
|
|
1653
|
+
created: t.createdTime
|
|
1654
|
+
}));
|
|
1655
|
+
output(
|
|
1656
|
+
{
|
|
1657
|
+
tasks,
|
|
1658
|
+
count: tasks.length,
|
|
1659
|
+
nextCursor: result.responseMetaData?.nextCursor ?? null
|
|
1660
|
+
},
|
|
1661
|
+
opts
|
|
1662
|
+
);
|
|
1663
|
+
} catch (err) {
|
|
1664
|
+
const error48 = err;
|
|
1665
|
+
errorOutput({ code: error48.code, message: error48.message }, opts);
|
|
1666
|
+
process.exitCode = 1;
|
|
1667
|
+
}
|
|
1668
|
+
});
|
|
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) => {
|
|
1670
|
+
try {
|
|
1671
|
+
const assigneeIds = opts.assignee ? opts.assignee.split(",").map((s) => s.trim()) : void 0;
|
|
1672
|
+
const result = await createTask({
|
|
1673
|
+
title: opts.title,
|
|
1674
|
+
content: opts.body,
|
|
1675
|
+
dueDate: opts.due,
|
|
1676
|
+
categoryId: opts.category,
|
|
1677
|
+
assigneeIds,
|
|
1678
|
+
userId: opts.user ?? "me",
|
|
1679
|
+
profile: opts.profile
|
|
1680
|
+
});
|
|
1681
|
+
output(
|
|
1682
|
+
{
|
|
1683
|
+
success: true,
|
|
1684
|
+
taskId: result.taskId,
|
|
1685
|
+
title: result.title,
|
|
1686
|
+
status: result.status,
|
|
1687
|
+
dueDate: result.dueDate
|
|
1688
|
+
},
|
|
1689
|
+
opts
|
|
1690
|
+
);
|
|
1691
|
+
} catch (err) {
|
|
1692
|
+
const error48 = err;
|
|
1693
|
+
errorOutput({ code: error48.code, message: error48.message }, opts);
|
|
1694
|
+
process.exitCode = 1;
|
|
1695
|
+
}
|
|
1696
|
+
});
|
|
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) => {
|
|
1698
|
+
try {
|
|
1699
|
+
const profile = opts.profile;
|
|
1700
|
+
const taskId = opts.id;
|
|
1701
|
+
const status = opts.status;
|
|
1702
|
+
if (status) {
|
|
1703
|
+
if (status.toLowerCase() === "done") {
|
|
1704
|
+
await completeTask(taskId, profile);
|
|
1705
|
+
} else if (status.toLowerCase() === "todo") {
|
|
1706
|
+
await incompleteTask(taskId, profile);
|
|
1707
|
+
} else {
|
|
1708
|
+
throw new Error("Invalid status. Use 'done' or 'todo'.");
|
|
1709
|
+
}
|
|
1710
|
+
}
|
|
1711
|
+
const hasFieldUpdate = opts.title || opts.body || opts.due;
|
|
1712
|
+
if (hasFieldUpdate) {
|
|
1713
|
+
const result = await updateTask({
|
|
1714
|
+
taskId,
|
|
1715
|
+
title: opts.title,
|
|
1716
|
+
content: opts.body,
|
|
1717
|
+
dueDate: opts.due,
|
|
1718
|
+
profile
|
|
1719
|
+
});
|
|
1720
|
+
output(
|
|
1721
|
+
{
|
|
1722
|
+
success: true,
|
|
1723
|
+
taskId: result.taskId,
|
|
1724
|
+
title: result.title,
|
|
1725
|
+
status: result.status,
|
|
1726
|
+
dueDate: result.dueDate
|
|
1727
|
+
},
|
|
1728
|
+
opts
|
|
1729
|
+
);
|
|
1730
|
+
} else if (status) {
|
|
1731
|
+
output(
|
|
1732
|
+
{ success: true, taskId, status: status.toLowerCase() === "done" ? "DONE" : "TODO" },
|
|
1733
|
+
opts
|
|
1734
|
+
);
|
|
1735
|
+
} else {
|
|
1736
|
+
throw new Error("Specify at least one of: --title, --body, --due, --status");
|
|
1737
|
+
}
|
|
1738
|
+
} catch (err) {
|
|
1739
|
+
const error48 = err;
|
|
1740
|
+
errorOutput({ code: error48.code, message: error48.message }, opts);
|
|
1741
|
+
process.exitCode = 1;
|
|
1742
|
+
}
|
|
1743
|
+
});
|
|
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) => {
|
|
1745
|
+
try {
|
|
1746
|
+
await deleteTask(
|
|
1747
|
+
opts.id,
|
|
1748
|
+
opts.profile
|
|
1749
|
+
);
|
|
1750
|
+
output({ success: true, taskId: opts.id, message: "Task deleted" }, opts);
|
|
1751
|
+
} catch (err) {
|
|
1752
|
+
const error48 = err;
|
|
1753
|
+
errorOutput({ code: error48.code, message: error48.message }, opts);
|
|
1754
|
+
process.exitCode = 1;
|
|
1755
|
+
}
|
|
1756
|
+
});
|
|
1757
|
+
var taskCommand = new Command9("task").description("Task operations (requires User OAuth with task scope)").addCommand(listCommand4).addCommand(createCommand2).addCommand(updateCommand2).addCommand(deleteCommand2);
|
|
1758
|
+
|
|
1759
|
+
// src/commands/mcp-cmd.ts
|
|
1760
|
+
import { Command as Command10 } from "commander";
|
|
1761
|
+
|
|
1282
1762
|
// src/mcp/server.ts
|
|
1283
1763
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
1284
1764
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
@@ -15161,6 +15641,112 @@ function registerTools(server) {
|
|
|
15161
15641
|
}
|
|
15162
15642
|
}
|
|
15163
15643
|
);
|
|
15644
|
+
server.tool(
|
|
15645
|
+
"nworks_calendar_create",
|
|
15646
|
+
"\uCE98\uB9B0\uB354 \uC77C\uC815\uC744 \uC0DD\uC131\uD569\uB2C8\uB2E4 (User OAuth calendar scope \uD544\uC694)",
|
|
15647
|
+
{
|
|
15648
|
+
summary: external_exports.string().describe("\uC77C\uC815 \uC81C\uBAA9"),
|
|
15649
|
+
start: external_exports.string().describe("\uC2DC\uC791 \uC77C\uC2DC (YYYY-MM-DDThh:mm:ss)"),
|
|
15650
|
+
end: external_exports.string().describe("\uC885\uB8CC \uC77C\uC2DC (YYYY-MM-DDThh:mm:ss)"),
|
|
15651
|
+
timeZone: external_exports.string().optional().describe("\uD0C0\uC784\uC874 (\uAE30\uBCF8: Asia/Seoul)"),
|
|
15652
|
+
description: external_exports.string().optional().describe("\uC77C\uC815 \uC124\uBA85"),
|
|
15653
|
+
location: external_exports.string().optional().describe("\uC7A5\uC18C"),
|
|
15654
|
+
attendees: external_exports.array(external_exports.object({ email: external_exports.string(), displayName: external_exports.string().optional() })).optional().describe("\uCC38\uC11D\uC790 \uBAA9\uB85D"),
|
|
15655
|
+
sendNotification: external_exports.boolean().optional().describe("\uCC38\uC11D\uC790\uC5D0\uAC8C \uC54C\uB9BC \uBC1C\uC1A1 (\uAE30\uBCF8: false)"),
|
|
15656
|
+
userId: external_exports.string().optional().describe("\uB300\uC0C1 \uC0AC\uC6A9\uC790 ID (\uBBF8\uC9C0\uC815 \uC2DC me)")
|
|
15657
|
+
},
|
|
15658
|
+
async ({ summary, start, end, timeZone, description, location, attendees, sendNotification, userId }) => {
|
|
15659
|
+
try {
|
|
15660
|
+
const result = await createEvent({
|
|
15661
|
+
summary,
|
|
15662
|
+
start,
|
|
15663
|
+
end,
|
|
15664
|
+
timeZone,
|
|
15665
|
+
description,
|
|
15666
|
+
location,
|
|
15667
|
+
attendees,
|
|
15668
|
+
sendNotification,
|
|
15669
|
+
userId: userId ?? "me"
|
|
15670
|
+
});
|
|
15671
|
+
const event = result.eventComponents?.[0];
|
|
15672
|
+
return {
|
|
15673
|
+
content: [{ type: "text", text: JSON.stringify({ success: true, eventId: event?.eventId, summary: event?.summary, start: event?.start, end: event?.end }) }]
|
|
15674
|
+
};
|
|
15675
|
+
} catch (err) {
|
|
15676
|
+
const error48 = err;
|
|
15677
|
+
return {
|
|
15678
|
+
content: [{ type: "text", text: `Error: ${error48.message}` }],
|
|
15679
|
+
isError: true
|
|
15680
|
+
};
|
|
15681
|
+
}
|
|
15682
|
+
}
|
|
15683
|
+
);
|
|
15684
|
+
server.tool(
|
|
15685
|
+
"nworks_calendar_update",
|
|
15686
|
+
"\uCE98\uB9B0\uB354 \uC77C\uC815\uC744 \uC218\uC815\uD569\uB2C8\uB2E4 (User OAuth calendar scope \uD544\uC694)",
|
|
15687
|
+
{
|
|
15688
|
+
eventId: external_exports.string().describe("\uC77C\uC815 ID"),
|
|
15689
|
+
summary: external_exports.string().optional().describe("\uC0C8 \uC81C\uBAA9"),
|
|
15690
|
+
start: external_exports.string().optional().describe("\uC0C8 \uC2DC\uC791 \uC77C\uC2DC (YYYY-MM-DDThh:mm:ss)"),
|
|
15691
|
+
end: external_exports.string().optional().describe("\uC0C8 \uC885\uB8CC \uC77C\uC2DC (YYYY-MM-DDThh:mm:ss)"),
|
|
15692
|
+
timeZone: external_exports.string().optional().describe("\uD0C0\uC784\uC874 (\uAE30\uBCF8: Asia/Seoul)"),
|
|
15693
|
+
description: external_exports.string().optional().describe("\uC0C8 \uC124\uBA85"),
|
|
15694
|
+
location: external_exports.string().optional().describe("\uC0C8 \uC7A5\uC18C"),
|
|
15695
|
+
sendNotification: external_exports.boolean().optional().describe("\uCC38\uC11D\uC790\uC5D0\uAC8C \uC54C\uB9BC \uBC1C\uC1A1 (\uAE30\uBCF8: false)"),
|
|
15696
|
+
userId: external_exports.string().optional().describe("\uB300\uC0C1 \uC0AC\uC6A9\uC790 ID (\uBBF8\uC9C0\uC815 \uC2DC me)")
|
|
15697
|
+
},
|
|
15698
|
+
async ({ eventId, summary, start, end, timeZone, description, location, sendNotification, userId }) => {
|
|
15699
|
+
try {
|
|
15700
|
+
await updateEvent({
|
|
15701
|
+
eventId,
|
|
15702
|
+
summary,
|
|
15703
|
+
start,
|
|
15704
|
+
end,
|
|
15705
|
+
timeZone,
|
|
15706
|
+
description,
|
|
15707
|
+
location,
|
|
15708
|
+
sendNotification,
|
|
15709
|
+
userId: userId ?? "me"
|
|
15710
|
+
});
|
|
15711
|
+
return {
|
|
15712
|
+
content: [{ type: "text", text: JSON.stringify({ success: true, eventId, message: "Event updated" }) }]
|
|
15713
|
+
};
|
|
15714
|
+
} catch (err) {
|
|
15715
|
+
const error48 = err;
|
|
15716
|
+
return {
|
|
15717
|
+
content: [{ type: "text", text: `Error: ${error48.message}` }],
|
|
15718
|
+
isError: true
|
|
15719
|
+
};
|
|
15720
|
+
}
|
|
15721
|
+
}
|
|
15722
|
+
);
|
|
15723
|
+
server.tool(
|
|
15724
|
+
"nworks_calendar_delete",
|
|
15725
|
+
"\uCE98\uB9B0\uB354 \uC77C\uC815\uC744 \uC0AD\uC81C\uD569\uB2C8\uB2E4 (User OAuth calendar scope \uD544\uC694)",
|
|
15726
|
+
{
|
|
15727
|
+
eventId: external_exports.string().describe("\uC0AD\uC81C\uD560 \uC77C\uC815 ID"),
|
|
15728
|
+
sendNotification: external_exports.boolean().optional().describe("\uCC38\uC11D\uC790\uC5D0\uAC8C \uC54C\uB9BC \uBC1C\uC1A1 (\uAE30\uBCF8: false)"),
|
|
15729
|
+
userId: external_exports.string().optional().describe("\uB300\uC0C1 \uC0AC\uC6A9\uC790 ID (\uBBF8\uC9C0\uC815 \uC2DC me)")
|
|
15730
|
+
},
|
|
15731
|
+
async ({ eventId, sendNotification, userId }) => {
|
|
15732
|
+
try {
|
|
15733
|
+
await deleteEvent(
|
|
15734
|
+
eventId,
|
|
15735
|
+
userId ?? "me",
|
|
15736
|
+
sendNotification ?? false
|
|
15737
|
+
);
|
|
15738
|
+
return {
|
|
15739
|
+
content: [{ type: "text", text: JSON.stringify({ success: true, eventId, message: "Event deleted" }) }]
|
|
15740
|
+
};
|
|
15741
|
+
} catch (err) {
|
|
15742
|
+
const error48 = err;
|
|
15743
|
+
return {
|
|
15744
|
+
content: [{ type: "text", text: `Error: ${error48.message}` }],
|
|
15745
|
+
isError: true
|
|
15746
|
+
};
|
|
15747
|
+
}
|
|
15748
|
+
}
|
|
15749
|
+
);
|
|
15164
15750
|
server.tool(
|
|
15165
15751
|
"nworks_drive_list",
|
|
15166
15752
|
"\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)",
|
|
@@ -15413,6 +15999,142 @@ function registerTools(server) {
|
|
|
15413
15999
|
}
|
|
15414
16000
|
}
|
|
15415
16001
|
);
|
|
16002
|
+
server.tool(
|
|
16003
|
+
"nworks_task_list",
|
|
16004
|
+
"\uD560 \uC77C \uBAA9\uB85D\uC744 \uC870\uD68C\uD569\uB2C8\uB2E4 (User OAuth task \uB610\uB294 task.read scope \uD544\uC694)",
|
|
16005
|
+
{
|
|
16006
|
+
categoryId: external_exports.string().optional().describe("\uCE74\uD14C\uACE0\uB9AC ID (\uAE30\uBCF8: default)"),
|
|
16007
|
+
status: external_exports.enum(["TODO", "ALL"]).optional().describe("\uD544\uD130: TODO \uB610\uB294 ALL (\uAE30\uBCF8: ALL)"),
|
|
16008
|
+
count: external_exports.number().optional().describe("\uD398\uC774\uC9C0\uB2F9 \uD56D\uBAA9 \uC218 (\uAE30\uBCF8: 50, \uCD5C\uB300: 100)"),
|
|
16009
|
+
cursor: external_exports.string().optional().describe("\uD398\uC774\uC9C0\uB124\uC774\uC158 \uCEE4\uC11C"),
|
|
16010
|
+
userId: external_exports.string().optional().describe("\uB300\uC0C1 \uC0AC\uC6A9\uC790 ID (\uBBF8\uC9C0\uC815 \uC2DC me)")
|
|
16011
|
+
},
|
|
16012
|
+
async ({ categoryId, status, count, cursor, userId }) => {
|
|
16013
|
+
try {
|
|
16014
|
+
const result = await listTasks(
|
|
16015
|
+
categoryId ?? "default",
|
|
16016
|
+
userId ?? "me",
|
|
16017
|
+
count ?? 50,
|
|
16018
|
+
cursor,
|
|
16019
|
+
status ?? "ALL"
|
|
16020
|
+
);
|
|
16021
|
+
const tasks = result.tasks.map((t) => ({
|
|
16022
|
+
taskId: t.taskId,
|
|
16023
|
+
title: t.title,
|
|
16024
|
+
status: t.status,
|
|
16025
|
+
dueDate: t.dueDate,
|
|
16026
|
+
assignor: t.assignorName ?? t.assignorId,
|
|
16027
|
+
created: t.createdTime
|
|
16028
|
+
}));
|
|
16029
|
+
return {
|
|
16030
|
+
content: [{ type: "text", text: JSON.stringify({ tasks, count: tasks.length, nextCursor: result.responseMetaData?.nextCursor ?? null }) }]
|
|
16031
|
+
};
|
|
16032
|
+
} catch (err) {
|
|
16033
|
+
const error48 = err;
|
|
16034
|
+
return {
|
|
16035
|
+
content: [{ type: "text", text: `Error: ${error48.message}` }],
|
|
16036
|
+
isError: true
|
|
16037
|
+
};
|
|
16038
|
+
}
|
|
16039
|
+
}
|
|
16040
|
+
);
|
|
16041
|
+
server.tool(
|
|
16042
|
+
"nworks_task_create",
|
|
16043
|
+
"\uD560 \uC77C\uC744 \uC0DD\uC131\uD569\uB2C8\uB2E4 (User OAuth task scope \uD544\uC694). \uAE30\uBCF8\uC801\uC73C\uB85C \uC790\uAE30 \uC790\uC2E0\uC5D0\uAC8C \uD560\uB2F9\uB429\uB2C8\uB2E4.",
|
|
16044
|
+
{
|
|
16045
|
+
title: external_exports.string().describe("\uD560 \uC77C \uC81C\uBAA9"),
|
|
16046
|
+
content: external_exports.string().optional().describe("\uD560 \uC77C \uB0B4\uC6A9"),
|
|
16047
|
+
dueDate: external_exports.string().optional().describe("\uB9C8\uAC10\uC77C (YYYY-MM-DD)"),
|
|
16048
|
+
categoryId: external_exports.string().optional().describe("\uCE74\uD14C\uACE0\uB9AC ID"),
|
|
16049
|
+
assigneeIds: external_exports.array(external_exports.string()).optional().describe("\uB2F4\uB2F9\uC790 user ID \uBAA9\uB85D (\uBBF8\uC9C0\uC815 \uC2DC \uC790\uAE30 \uC790\uC2E0)"),
|
|
16050
|
+
userId: external_exports.string().optional().describe("\uC0DD\uC131\uC790 user ID (\uBBF8\uC9C0\uC815 \uC2DC me)")
|
|
16051
|
+
},
|
|
16052
|
+
async ({ title, content, dueDate, categoryId, assigneeIds, userId }) => {
|
|
16053
|
+
try {
|
|
16054
|
+
const result = await createTask({
|
|
16055
|
+
title,
|
|
16056
|
+
content,
|
|
16057
|
+
dueDate,
|
|
16058
|
+
categoryId,
|
|
16059
|
+
assigneeIds,
|
|
16060
|
+
userId: userId ?? "me"
|
|
16061
|
+
});
|
|
16062
|
+
return {
|
|
16063
|
+
content: [{ type: "text", text: JSON.stringify({ success: true, taskId: result.taskId, title: result.title, status: result.status, dueDate: result.dueDate }) }]
|
|
16064
|
+
};
|
|
16065
|
+
} catch (err) {
|
|
16066
|
+
const error48 = err;
|
|
16067
|
+
return {
|
|
16068
|
+
content: [{ type: "text", text: `Error: ${error48.message}` }],
|
|
16069
|
+
isError: true
|
|
16070
|
+
};
|
|
16071
|
+
}
|
|
16072
|
+
}
|
|
16073
|
+
);
|
|
16074
|
+
server.tool(
|
|
16075
|
+
"nworks_task_update",
|
|
16076
|
+
"\uD560 \uC77C\uC744 \uC218\uC815\uD569\uB2C8\uB2E4 (User OAuth task scope \uD544\uC694). \uC0C1\uD0DC \uBCC0\uACBD(done/todo)\uACFC \uD544\uB4DC \uC218\uC815\uC744 \uBAA8\uB450 \uC9C0\uC6D0\uD569\uB2C8\uB2E4.",
|
|
16077
|
+
{
|
|
16078
|
+
taskId: external_exports.string().describe("\uD560 \uC77C ID"),
|
|
16079
|
+
status: external_exports.enum(["done", "todo"]).optional().describe("\uC0C1\uD0DC \uBCC0\uACBD: done \uB610\uB294 todo"),
|
|
16080
|
+
title: external_exports.string().optional().describe("\uC0C8 \uC81C\uBAA9"),
|
|
16081
|
+
content: external_exports.string().optional().describe("\uC0C8 \uB0B4\uC6A9"),
|
|
16082
|
+
dueDate: external_exports.string().optional().describe("\uC0C8 \uB9C8\uAC10\uC77C (YYYY-MM-DD)")
|
|
16083
|
+
},
|
|
16084
|
+
async ({ taskId, status, title, content, dueDate }) => {
|
|
16085
|
+
try {
|
|
16086
|
+
if (status) {
|
|
16087
|
+
if (status === "done") {
|
|
16088
|
+
await completeTask(taskId);
|
|
16089
|
+
} else {
|
|
16090
|
+
await incompleteTask(taskId);
|
|
16091
|
+
}
|
|
16092
|
+
}
|
|
16093
|
+
if (title !== void 0 || content !== void 0 || dueDate !== void 0) {
|
|
16094
|
+
const result = await updateTask({ taskId, title, content, dueDate });
|
|
16095
|
+
return {
|
|
16096
|
+
content: [{ type: "text", text: JSON.stringify({ success: true, taskId: result.taskId, title: result.title, status: result.status, dueDate: result.dueDate }) }]
|
|
16097
|
+
};
|
|
16098
|
+
}
|
|
16099
|
+
if (status) {
|
|
16100
|
+
return {
|
|
16101
|
+
content: [{ type: "text", text: JSON.stringify({ success: true, taskId, status: status === "done" ? "DONE" : "TODO" }) }]
|
|
16102
|
+
};
|
|
16103
|
+
}
|
|
16104
|
+
return {
|
|
16105
|
+
content: [{ type: "text", text: "Error: status, title, content, dueDate \uC911 \uD558\uB098 \uC774\uC0C1\uC744 \uC9C0\uC815\uD558\uC138\uC694." }],
|
|
16106
|
+
isError: true
|
|
16107
|
+
};
|
|
16108
|
+
} catch (err) {
|
|
16109
|
+
const error48 = err;
|
|
16110
|
+
return {
|
|
16111
|
+
content: [{ type: "text", text: `Error: ${error48.message}` }],
|
|
16112
|
+
isError: true
|
|
16113
|
+
};
|
|
16114
|
+
}
|
|
16115
|
+
}
|
|
16116
|
+
);
|
|
16117
|
+
server.tool(
|
|
16118
|
+
"nworks_task_delete",
|
|
16119
|
+
"\uD560 \uC77C\uC744 \uC0AD\uC81C\uD569\uB2C8\uB2E4 (User OAuth task scope \uD544\uC694)",
|
|
16120
|
+
{
|
|
16121
|
+
taskId: external_exports.string().describe("\uC0AD\uC81C\uD560 \uD560 \uC77C ID")
|
|
16122
|
+
},
|
|
16123
|
+
async ({ taskId }) => {
|
|
16124
|
+
try {
|
|
16125
|
+
await deleteTask(taskId);
|
|
16126
|
+
return {
|
|
16127
|
+
content: [{ type: "text", text: JSON.stringify({ success: true, taskId, message: "Task deleted" }) }]
|
|
16128
|
+
};
|
|
16129
|
+
} catch (err) {
|
|
16130
|
+
const error48 = err;
|
|
16131
|
+
return {
|
|
16132
|
+
content: [{ type: "text", text: `Error: ${error48.message}` }],
|
|
16133
|
+
isError: true
|
|
16134
|
+
};
|
|
16135
|
+
}
|
|
16136
|
+
}
|
|
16137
|
+
);
|
|
15416
16138
|
server.tool(
|
|
15417
16139
|
"nworks_whoami",
|
|
15418
16140
|
"\uD604\uC7AC \uC778\uC99D\uB41C NAVER WORKS \uACC4\uC815 \uC815\uBCF4\uB97C \uD655\uC778\uD569\uB2C8\uB2E4",
|
|
@@ -15454,7 +16176,7 @@ async function startMcpServer() {
|
|
|
15454
16176
|
}
|
|
15455
16177
|
|
|
15456
16178
|
// src/commands/mcp-cmd.ts
|
|
15457
|
-
var mcpCommand = new
|
|
16179
|
+
var mcpCommand = new Command10("mcp").description("Start MCP server (stdio transport)").option("--list-tools", "List available MCP tools").action(async (opts) => {
|
|
15458
16180
|
if (opts.listTools) {
|
|
15459
16181
|
console.log("nworks_message_send \u2014 Send message to user or channel");
|
|
15460
16182
|
console.log("nworks_message_members \u2014 List channel members");
|
|
@@ -15469,7 +16191,7 @@ var mcpCommand = new Command9("mcp").description("Start MCP server (stdio transp
|
|
|
15469
16191
|
// src/index.ts
|
|
15470
16192
|
var require2 = createRequire(import.meta.url);
|
|
15471
16193
|
var { version: version2 } = require2("../package.json");
|
|
15472
|
-
var program = new
|
|
16194
|
+
var program = new Command11().name("nworks").description("NAVER WORKS CLI \u2014 built for humans and AI agents").version(version2).option("--json", "Always output JSON").option("-v, --verbose", "Debug logging").option("--dry-run", "Print request without calling API").option("-p, --profile <name>", "Profile name", "default");
|
|
15473
16195
|
program.addCommand(loginCommand);
|
|
15474
16196
|
program.addCommand(logoutCommand);
|
|
15475
16197
|
program.addCommand(whoamiCommand);
|
|
@@ -15478,6 +16200,7 @@ program.addCommand(directoryCommand);
|
|
|
15478
16200
|
program.addCommand(calendarCommand);
|
|
15479
16201
|
program.addCommand(driveCommand);
|
|
15480
16202
|
program.addCommand(mailCommand);
|
|
16203
|
+
program.addCommand(taskCommand);
|
|
15481
16204
|
program.addCommand(mcpCommand);
|
|
15482
16205
|
program.parse();
|
|
15483
16206
|
//# sourceMappingURL=index.js.map
|