nworks 0.2.2 → 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
@@ -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 Command8 } from "commander";
10
+ import { Command as Command9 } from "commander";
11
11
 
12
12
  // src/commands/login.ts
13
13
  import { Command } from "commander";
@@ -827,9 +827,237 @@ var listCommand = new Command6("list").description("List calendar events (requir
827
827
  });
828
828
  var calendarCommand = new Command6("calendar").description("Calendar operations (requires User OAuth)").addCommand(listCommand);
829
829
 
830
- // 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";
831
833
  import { Command as Command7 } from "commander";
832
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
+
833
1061
  // src/mcp/server.ts
834
1062
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
835
1063
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
@@ -14712,6 +14940,105 @@ function registerTools(server) {
14712
14940
  }
14713
14941
  }
14714
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
+ );
14715
15042
  server.tool(
14716
15043
  "nworks_whoami",
14717
15044
  "\uD604\uC7AC \uC778\uC99D\uB41C NAVER WORKS \uACC4\uC815 \uC815\uBCF4\uB97C \uD655\uC778\uD569\uB2C8\uB2E4",
@@ -14753,7 +15080,7 @@ async function startMcpServer() {
14753
15080
  }
14754
15081
 
14755
15082
  // src/commands/mcp-cmd.ts
14756
- 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) => {
14757
15084
  if (opts.listTools) {
14758
15085
  console.log("nworks_message_send \u2014 Send message to user or channel");
14759
15086
  console.log("nworks_message_members \u2014 List channel members");
@@ -14768,13 +15095,14 @@ var mcpCommand = new Command7("mcp").description("Start MCP server (stdio transp
14768
15095
  // src/index.ts
14769
15096
  var require2 = createRequire(import.meta.url);
14770
15097
  var { version: version2 } = require2("../package.json");
14771
- var program = new Command8().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");
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");
14772
15099
  program.addCommand(loginCommand);
14773
15100
  program.addCommand(logoutCommand);
14774
15101
  program.addCommand(whoamiCommand);
14775
15102
  program.addCommand(messageCommand);
14776
15103
  program.addCommand(directoryCommand);
14777
15104
  program.addCommand(calendarCommand);
15105
+ program.addCommand(driveCommand);
14778
15106
  program.addCommand(mcpCommand);
14779
15107
  program.parse();
14780
15108
  //# sourceMappingURL=index.js.map