nworks 0.2.1 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -6,7 +6,8 @@ var __export = (target, all) => {
6
6
  };
7
7
 
8
8
  // src/index.ts
9
- import { Command as Command8 } from "commander";
9
+ import { createRequire } from "module";
10
+ import { Command as Command9 } from "commander";
10
11
 
11
12
  // src/commands/login.ts
12
13
  import { Command } from "commander";
@@ -826,9 +827,237 @@ var listCommand = new Command6("list").description("List calendar events (requir
826
827
  });
827
828
  var calendarCommand = new Command6("calendar").description("Calendar operations (requires User OAuth)").addCommand(listCommand);
828
829
 
829
- // src/commands/mcp-cmd.ts
830
+ // src/commands/drive.ts
831
+ import { writeFile as writeFile2 } from "fs/promises";
832
+ import { join as join2 } from "path";
830
833
  import { Command as Command7 } from "commander";
831
834
 
835
+ // src/api/drive.ts
836
+ import { readFile as readFile4, stat } from "fs/promises";
837
+ import { basename } from "path";
838
+ var BASE_URL3 = "https://www.worksapis.com/v1.0";
839
+ async function authedFetch(url2, init, profile) {
840
+ const token = await getValidUserToken(profile);
841
+ const headers = new Headers(init.headers);
842
+ headers.set("Authorization", `Bearer ${token}`);
843
+ return fetch(url2, { ...init, headers });
844
+ }
845
+ async function handleError(res) {
846
+ if (res.status === 401) {
847
+ throw new AuthError("User token expired. Run `nworks login --user --scope file` again.");
848
+ }
849
+ let code = "UNKNOWN";
850
+ let description = `HTTP ${res.status}`;
851
+ try {
852
+ const body = await res.json();
853
+ code = body.code ?? code;
854
+ description = body.description ?? description;
855
+ } catch {
856
+ }
857
+ throw new ApiError(code, description, res.status);
858
+ }
859
+ async function listFiles(userId = "me", folderId, count = 20, cursor, profile = "default") {
860
+ const base = `${BASE_URL3}/users/${userId}/drive/files`;
861
+ const path = folderId ? `${base}/${folderId}/children` : base;
862
+ const params = new URLSearchParams();
863
+ params.set("count", String(count));
864
+ if (cursor) params.set("cursor", cursor);
865
+ const url2 = `${path}?${params.toString()}`;
866
+ if (process.env["NWORKS_VERBOSE"] === "1") {
867
+ console.error(`[nworks] GET ${url2}`);
868
+ }
869
+ const res = await authedFetch(url2, { method: "GET" }, profile);
870
+ if (!res.ok) return handleError(res);
871
+ const data = await res.json();
872
+ return { files: data.files ?? [], responseMetaData: data.responseMetaData };
873
+ }
874
+ async function uploadFile(localPath, userId = "me", folderId, overwrite = false, profile = "default") {
875
+ const fileName = basename(localPath);
876
+ const fileStat = await stat(localPath);
877
+ const fileSize = fileStat.size;
878
+ const base = `${BASE_URL3}/users/${userId}/drive/files`;
879
+ const createUrl = folderId ? `${base}/${folderId}` : base;
880
+ if (process.env["NWORKS_VERBOSE"] === "1") {
881
+ console.error(`[nworks] POST ${createUrl} (create upload URL)`);
882
+ }
883
+ const createRes = await authedFetch(
884
+ createUrl,
885
+ {
886
+ method: "POST",
887
+ headers: { "Content-Type": "application/json" },
888
+ body: JSON.stringify({ fileName, fileSize, overwrite })
889
+ },
890
+ profile
891
+ );
892
+ if (!createRes.ok) return handleError(createRes);
893
+ const { uploadUrl } = await createRes.json();
894
+ const fileBuffer = await readFile4(localPath);
895
+ const boundary = `----nworks${Date.now()}`;
896
+ const header = Buffer.from(
897
+ `--${boundary}\r
898
+ Content-Disposition: form-data; name="Filedata"; filename="${fileName}"\r
899
+ Content-Type: application/octet-stream\r
900
+ \r
901
+ `
902
+ );
903
+ const footer = Buffer.from(`\r
904
+ --${boundary}--\r
905
+ `);
906
+ const body = Buffer.concat([header, fileBuffer, footer]);
907
+ if (process.env["NWORKS_VERBOSE"] === "1") {
908
+ console.error(`[nworks] POST ${uploadUrl} (upload content, ${fileSize} bytes)`);
909
+ }
910
+ const uploadRes = await authedFetch(
911
+ uploadUrl,
912
+ {
913
+ method: "POST",
914
+ headers: { "Content-Type": `multipart/form-data; boundary=${boundary}` },
915
+ body
916
+ },
917
+ profile
918
+ );
919
+ if (!uploadRes.ok) return handleError(uploadRes);
920
+ return await uploadRes.json();
921
+ }
922
+ async function downloadFile(fileId, userId = "me", profile = "default") {
923
+ const url2 = `${BASE_URL3}/users/${userId}/drive/files/${fileId}/download`;
924
+ if (process.env["NWORKS_VERBOSE"] === "1") {
925
+ console.error(`[nworks] GET ${url2} (get download URL)`);
926
+ }
927
+ const redirectRes = await authedFetch(
928
+ url2,
929
+ { method: "GET", redirect: "manual" },
930
+ profile
931
+ );
932
+ if (redirectRes.status === 401) {
933
+ throw new AuthError("User token expired. Run `nworks login --user --scope file` again.");
934
+ }
935
+ const location = redirectRes.headers.get("location");
936
+ if (!location) {
937
+ if (!redirectRes.ok) return handleError(redirectRes);
938
+ throw new ApiError("NO_REDIRECT", "No download URL returned", redirectRes.status);
939
+ }
940
+ if (process.env["NWORKS_VERBOSE"] === "1") {
941
+ console.error(`[nworks] GET ${location} (download content)`);
942
+ }
943
+ const downloadRes = await authedFetch(location, { method: "GET" }, profile);
944
+ if (!downloadRes.ok) return handleError(downloadRes);
945
+ const arrayBuffer = await downloadRes.arrayBuffer();
946
+ const buffer = Buffer.from(arrayBuffer);
947
+ const disposition = downloadRes.headers.get("content-disposition");
948
+ let fileName;
949
+ if (disposition) {
950
+ const match = disposition.match(/filename\*?=(?:UTF-8''|"?)([^";]+)/i);
951
+ if (match?.[1]) {
952
+ fileName = decodeURIComponent(match[1].replace(/"/g, ""));
953
+ }
954
+ }
955
+ return { buffer, fileName };
956
+ }
957
+
958
+ // src/commands/drive.ts
959
+ var listCommand2 = new Command7("list").description("List files in Drive (requires User OAuth with file or file.read scope)").option("--user <userId>", "Target user ID (default: me)").option("--folder <folderId>", "Folder ID to list (default: root)").option("--count <n>", "Items per page (default: 20)", "20").option("--cursor <cursor>", "Pagination cursor").option("--profile <name>", "Profile name", "default").option("--json", "JSON output").action(async (opts) => {
960
+ try {
961
+ const result = await listFiles(
962
+ opts.user ?? "me",
963
+ opts.folder,
964
+ parseInt(opts.count, 10),
965
+ opts.cursor,
966
+ opts.profile
967
+ );
968
+ const files = result.files.map((f) => ({
969
+ name: f.fileName,
970
+ type: f.fileType,
971
+ size: f.fileSize,
972
+ modified: f.modifiedTime,
973
+ fileId: f.fileId
974
+ }));
975
+ output(
976
+ {
977
+ files,
978
+ count: files.length,
979
+ nextCursor: result.responseMetaData?.nextCursor ?? null
980
+ },
981
+ opts
982
+ );
983
+ } catch (err) {
984
+ const error48 = err;
985
+ errorOutput({ code: error48.code, message: error48.message }, opts);
986
+ process.exitCode = 1;
987
+ }
988
+ });
989
+ var uploadCommand = new Command7("upload").description("Upload a file to Drive (requires User OAuth with file scope)").requiredOption("--file <path>", "Local file path to upload").option("--user <userId>", "Target user ID (default: me)").option("--folder <folderId>", "Destination folder ID (default: root)").option("--overwrite", "Overwrite if file exists", false).option("--profile <name>", "Profile name", "default").option("--json", "JSON output").option("--dry-run", "Print request without uploading").action(async (opts) => {
990
+ try {
991
+ if (opts.dryRun) {
992
+ output(
993
+ {
994
+ dryRun: true,
995
+ request: {
996
+ file: opts.file,
997
+ user: opts.user ?? "me",
998
+ folder: opts.folder ?? "(root)",
999
+ overwrite: opts.overwrite
1000
+ }
1001
+ },
1002
+ opts
1003
+ );
1004
+ return;
1005
+ }
1006
+ const result = await uploadFile(
1007
+ opts.file,
1008
+ opts.user ?? "me",
1009
+ opts.folder,
1010
+ opts.overwrite,
1011
+ opts.profile
1012
+ );
1013
+ output(
1014
+ {
1015
+ success: true,
1016
+ fileId: result.fileId,
1017
+ fileName: result.fileName,
1018
+ fileSize: result.fileSize,
1019
+ filePath: result.filePath,
1020
+ fileType: result.fileType
1021
+ },
1022
+ opts
1023
+ );
1024
+ } catch (err) {
1025
+ const error48 = err;
1026
+ errorOutput({ code: error48.code, message: error48.message }, opts);
1027
+ process.exitCode = 1;
1028
+ }
1029
+ });
1030
+ var downloadCommand = new Command7("download").description("Download a file from Drive (requires User OAuth with file or file.read scope)").requiredOption("--file-id <fileId>", "File ID to download").option("--out <path>", "Output directory (default: current directory)").option("--name <filename>", "Output filename (default: original name)").option("--user <userId>", "Target user ID (default: me)").option("--profile <name>", "Profile name", "default").option("--json", "JSON output").action(async (opts) => {
1031
+ try {
1032
+ const result = await downloadFile(
1033
+ opts.fileId,
1034
+ opts.user ?? "me",
1035
+ opts.profile
1036
+ );
1037
+ const fileName = opts.name ?? result.fileName ?? opts.fileId;
1038
+ const outDir = opts.out ?? process.cwd();
1039
+ const outPath = join2(outDir, fileName);
1040
+ await writeFile2(outPath, result.buffer);
1041
+ output(
1042
+ {
1043
+ success: true,
1044
+ fileName,
1045
+ path: outPath,
1046
+ size: result.buffer.length
1047
+ },
1048
+ opts
1049
+ );
1050
+ } catch (err) {
1051
+ const error48 = err;
1052
+ errorOutput({ code: error48.code, message: error48.message }, opts);
1053
+ process.exitCode = 1;
1054
+ }
1055
+ });
1056
+ var driveCommand = new Command7("drive").description("Drive operations (requires User OAuth with file scope)").addCommand(listCommand2).addCommand(uploadCommand).addCommand(downloadCommand);
1057
+
1058
+ // src/commands/mcp-cmd.ts
1059
+ import { Command as Command8 } from "commander";
1060
+
832
1061
  // src/mcp/server.ts
833
1062
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
834
1063
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
@@ -2401,10 +2630,10 @@ var nanoid = /^[a-zA-Z0-9_-]{21}$/;
2401
2630
  var duration = /^P(?:(\d+W)|(?!.*W)(?=\d|T\d)(\d+Y)?(\d+M)?(\d+D)?(T(?=\d)(\d+H)?(\d+M)?(\d+([.,]\d+)?S)?)?)$/;
2402
2631
  var extendedDuration = /^[-+]?P(?!$)(?:(?:[-+]?\d+Y)|(?:[-+]?\d+[.,]\d+Y$))?(?:(?:[-+]?\d+M)|(?:[-+]?\d+[.,]\d+M$))?(?:(?:[-+]?\d+W)|(?:[-+]?\d+[.,]\d+W$))?(?:(?:[-+]?\d+D)|(?:[-+]?\d+[.,]\d+D$))?(?:T(?=[\d+-])(?:(?:[-+]?\d+H)|(?:[-+]?\d+[.,]\d+H$))?(?:(?:[-+]?\d+M)|(?:[-+]?\d+[.,]\d+M$))?(?:[-+]?\d+(?:[.,]\d+)?S)?)??$/;
2403
2632
  var guid = /^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})$/;
2404
- var uuid = (version2) => {
2405
- if (!version2)
2633
+ var uuid = (version3) => {
2634
+ if (!version3)
2406
2635
  return /^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$/;
2407
- return new RegExp(`^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-${version2}[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$`);
2636
+ return new RegExp(`^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-${version3}[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$`);
2408
2637
  };
2409
2638
  var uuid4 = /* @__PURE__ */ uuid(4);
2410
2639
  var uuid6 = /* @__PURE__ */ uuid(6);
@@ -14560,10 +14789,10 @@ function fromJSONSchema(schema, params) {
14560
14789
  if (typeof schema === "boolean") {
14561
14790
  return schema ? z.any() : z.never();
14562
14791
  }
14563
- const version2 = detectVersion(schema, params?.defaultTarget);
14792
+ const version3 = detectVersion(schema, params?.defaultTarget);
14564
14793
  const defs = schema.$defs || schema.definitions || {};
14565
14794
  const ctx = {
14566
- version: version2,
14795
+ version: version3,
14567
14796
  defs,
14568
14797
  refs: /* @__PURE__ */ new Map(),
14569
14798
  processing: /* @__PURE__ */ new Set(),
@@ -14711,6 +14940,105 @@ function registerTools(server) {
14711
14940
  }
14712
14941
  }
14713
14942
  );
14943
+ server.tool(
14944
+ "nworks_drive_list",
14945
+ "\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)",
14946
+ {
14947
+ userId: external_exports.string().optional().describe("\uB300\uC0C1 \uC0AC\uC6A9\uC790 ID (\uBBF8\uC9C0\uC815 \uC2DC me)"),
14948
+ folderId: external_exports.string().optional().describe("\uD3F4\uB354 ID (\uBBF8\uC9C0\uC815 \uC2DC \uB8E8\uD2B8)"),
14949
+ count: external_exports.number().optional().describe("\uD398\uC774\uC9C0\uB2F9 \uD56D\uBAA9 \uC218 (\uAE30\uBCF8: 20, \uCD5C\uB300: 200)"),
14950
+ cursor: external_exports.string().optional().describe("\uD398\uC774\uC9C0\uB124\uC774\uC158 \uCEE4\uC11C")
14951
+ },
14952
+ async ({ userId, folderId, count, cursor }) => {
14953
+ try {
14954
+ const result = await listFiles(
14955
+ userId ?? "me",
14956
+ folderId,
14957
+ count ?? 20,
14958
+ cursor
14959
+ );
14960
+ const files = result.files.map((f) => ({
14961
+ fileId: f.fileId,
14962
+ name: f.fileName,
14963
+ type: f.fileType,
14964
+ size: f.fileSize,
14965
+ modified: f.modifiedTime,
14966
+ path: f.filePath
14967
+ }));
14968
+ return {
14969
+ content: [{ type: "text", text: JSON.stringify({ files, count: files.length, nextCursor: result.responseMetaData?.nextCursor ?? null }) }]
14970
+ };
14971
+ } catch (err) {
14972
+ const error48 = err;
14973
+ return {
14974
+ content: [{ type: "text", text: `Error: ${error48.message}` }],
14975
+ isError: true
14976
+ };
14977
+ }
14978
+ }
14979
+ );
14980
+ server.tool(
14981
+ "nworks_drive_upload",
14982
+ "\uB85C\uCEEC \uD30C\uC77C\uC744 \uB4DC\uB77C\uC774\uBE0C\uC5D0 \uC5C5\uB85C\uB4DC\uD569\uB2C8\uB2E4 (User OAuth file scope \uD544\uC694)",
14983
+ {
14984
+ filePath: external_exports.string().describe("\uC5C5\uB85C\uB4DC\uD560 \uB85C\uCEEC \uD30C\uC77C \uACBD\uB85C"),
14985
+ userId: external_exports.string().optional().describe("\uB300\uC0C1 \uC0AC\uC6A9\uC790 ID (\uBBF8\uC9C0\uC815 \uC2DC me)"),
14986
+ folderId: external_exports.string().optional().describe("\uC5C5\uB85C\uB4DC\uD560 \uD3F4\uB354 ID (\uBBF8\uC9C0\uC815 \uC2DC \uB8E8\uD2B8)"),
14987
+ overwrite: external_exports.boolean().optional().describe("\uB3D9\uC77C \uD30C\uC77C\uBA85 \uB36E\uC5B4\uC4F0\uAE30 (\uAE30\uBCF8: false)")
14988
+ },
14989
+ async ({ filePath, userId, folderId, overwrite }) => {
14990
+ try {
14991
+ const result = await uploadFile(
14992
+ filePath,
14993
+ userId ?? "me",
14994
+ folderId,
14995
+ overwrite ?? false
14996
+ );
14997
+ return {
14998
+ content: [{ type: "text", text: JSON.stringify({ success: true, ...result }) }]
14999
+ };
15000
+ } catch (err) {
15001
+ const error48 = err;
15002
+ return {
15003
+ content: [{ type: "text", text: `Error: ${error48.message}` }],
15004
+ isError: true
15005
+ };
15006
+ }
15007
+ }
15008
+ );
15009
+ server.tool(
15010
+ "nworks_drive_download",
15011
+ "\uB4DC\uB77C\uC774\uBE0C \uD30C\uC77C\uC744 \uB2E4\uC6B4\uB85C\uB4DC\uD569\uB2C8\uB2E4 (User OAuth file \uB610\uB294 file.read scope \uD544\uC694)",
15012
+ {
15013
+ fileId: external_exports.string().describe("\uB2E4\uC6B4\uB85C\uB4DC\uD560 \uD30C\uC77C ID"),
15014
+ outputDir: external_exports.string().optional().describe("\uC800\uC7A5 \uB514\uB809\uD1A0\uB9AC (\uBBF8\uC9C0\uC815 \uC2DC \uD604\uC7AC \uB514\uB809\uD1A0\uB9AC)"),
15015
+ outputName: external_exports.string().optional().describe("\uC800\uC7A5 \uD30C\uC77C\uBA85 (\uBBF8\uC9C0\uC815 \uC2DC \uC6D0\uBCF8 \uD30C\uC77C\uBA85)"),
15016
+ userId: external_exports.string().optional().describe("\uB300\uC0C1 \uC0AC\uC6A9\uC790 ID (\uBBF8\uC9C0\uC815 \uC2DC me)")
15017
+ },
15018
+ async ({ fileId, outputDir, outputName, userId }) => {
15019
+ try {
15020
+ const { writeFile: writeFile3 } = await import("fs/promises");
15021
+ const { join: join3 } = await import("path");
15022
+ const result = await downloadFile(
15023
+ fileId,
15024
+ userId ?? "me"
15025
+ );
15026
+ const fileName = outputName ?? result.fileName ?? fileId;
15027
+ const dir = outputDir ?? process.cwd();
15028
+ const outPath = join3(dir, fileName);
15029
+ await writeFile3(outPath, result.buffer);
15030
+ return {
15031
+ content: [{ type: "text", text: JSON.stringify({ success: true, fileName, path: outPath, size: result.buffer.length }) }]
15032
+ };
15033
+ } catch (err) {
15034
+ const error48 = err;
15035
+ return {
15036
+ content: [{ type: "text", text: `Error: ${error48.message}` }],
15037
+ isError: true
15038
+ };
15039
+ }
15040
+ }
15041
+ );
14714
15042
  server.tool(
14715
15043
  "nworks_whoami",
14716
15044
  "\uD604\uC7AC \uC778\uC99D\uB41C NAVER WORKS \uACC4\uC815 \uC815\uBCF4\uB97C \uD655\uC778\uD569\uB2C8\uB2E4",
@@ -14752,7 +15080,7 @@ async function startMcpServer() {
14752
15080
  }
14753
15081
 
14754
15082
  // src/commands/mcp-cmd.ts
14755
- var mcpCommand = new Command7("mcp").description("Start MCP server (stdio transport)").option("--list-tools", "List available MCP tools").action(async (opts) => {
15083
+ var mcpCommand = new Command8("mcp").description("Start MCP server (stdio transport)").option("--list-tools", "List available MCP tools").action(async (opts) => {
14756
15084
  if (opts.listTools) {
14757
15085
  console.log("nworks_message_send \u2014 Send message to user or channel");
14758
15086
  console.log("nworks_message_members \u2014 List channel members");
@@ -14765,13 +15093,16 @@ var mcpCommand = new Command7("mcp").description("Start MCP server (stdio transp
14765
15093
  });
14766
15094
 
14767
15095
  // src/index.ts
14768
- var program = new Command8().name("nworks").description("NAVER WORKS CLI \u2014 built for humans and AI agents").version("0.1.0").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");
15096
+ var require2 = createRequire(import.meta.url);
15097
+ var { version: version2 } = require2("../package.json");
15098
+ var program = new Command9().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");
14769
15099
  program.addCommand(loginCommand);
14770
15100
  program.addCommand(logoutCommand);
14771
15101
  program.addCommand(whoamiCommand);
14772
15102
  program.addCommand(messageCommand);
14773
15103
  program.addCommand(directoryCommand);
14774
15104
  program.addCommand(calendarCommand);
15105
+ program.addCommand(driveCommand);
14775
15106
  program.addCommand(mcpCommand);
14776
15107
  program.parse();
14777
15108
  //# sourceMappingURL=index.js.map