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/mcp.js CHANGED
@@ -14143,6 +14143,129 @@ async function listEvents(fromDateTime, untilDateTime, userId = "me", profile =
14143
14143
  return { events: data.events ?? [] };
14144
14144
  }
14145
14145
 
14146
+ // src/api/drive.ts
14147
+ import { readFile as readFile3, stat } from "fs/promises";
14148
+ import { basename } from "path";
14149
+ var BASE_URL3 = "https://www.worksapis.com/v1.0";
14150
+ async function authedFetch(url2, init, profile) {
14151
+ const token = await getValidUserToken(profile);
14152
+ const headers = new Headers(init.headers);
14153
+ headers.set("Authorization", `Bearer ${token}`);
14154
+ return fetch(url2, { ...init, headers });
14155
+ }
14156
+ async function handleError(res) {
14157
+ if (res.status === 401) {
14158
+ throw new AuthError("User token expired. Run `nworks login --user --scope file` again.");
14159
+ }
14160
+ let code = "UNKNOWN";
14161
+ let description = `HTTP ${res.status}`;
14162
+ try {
14163
+ const body = await res.json();
14164
+ code = body.code ?? code;
14165
+ description = body.description ?? description;
14166
+ } catch {
14167
+ }
14168
+ throw new ApiError(code, description, res.status);
14169
+ }
14170
+ async function listFiles(userId = "me", folderId, count = 20, cursor, profile = "default") {
14171
+ const base = `${BASE_URL3}/users/${userId}/drive/files`;
14172
+ const path = folderId ? `${base}/${folderId}/children` : base;
14173
+ const params = new URLSearchParams();
14174
+ params.set("count", String(count));
14175
+ if (cursor) params.set("cursor", cursor);
14176
+ const url2 = `${path}?${params.toString()}`;
14177
+ if (process.env["NWORKS_VERBOSE"] === "1") {
14178
+ console.error(`[nworks] GET ${url2}`);
14179
+ }
14180
+ const res = await authedFetch(url2, { method: "GET" }, profile);
14181
+ if (!res.ok) return handleError(res);
14182
+ const data = await res.json();
14183
+ return { files: data.files ?? [], responseMetaData: data.responseMetaData };
14184
+ }
14185
+ async function uploadFile(localPath, userId = "me", folderId, overwrite = false, profile = "default") {
14186
+ const fileName = basename(localPath);
14187
+ const fileStat = await stat(localPath);
14188
+ const fileSize = fileStat.size;
14189
+ const base = `${BASE_URL3}/users/${userId}/drive/files`;
14190
+ const createUrl = folderId ? `${base}/${folderId}` : base;
14191
+ if (process.env["NWORKS_VERBOSE"] === "1") {
14192
+ console.error(`[nworks] POST ${createUrl} (create upload URL)`);
14193
+ }
14194
+ const createRes = await authedFetch(
14195
+ createUrl,
14196
+ {
14197
+ method: "POST",
14198
+ headers: { "Content-Type": "application/json" },
14199
+ body: JSON.stringify({ fileName, fileSize, overwrite })
14200
+ },
14201
+ profile
14202
+ );
14203
+ if (!createRes.ok) return handleError(createRes);
14204
+ const { uploadUrl } = await createRes.json();
14205
+ const fileBuffer = await readFile3(localPath);
14206
+ const boundary = `----nworks${Date.now()}`;
14207
+ const header = Buffer.from(
14208
+ `--${boundary}\r
14209
+ Content-Disposition: form-data; name="Filedata"; filename="${fileName}"\r
14210
+ Content-Type: application/octet-stream\r
14211
+ \r
14212
+ `
14213
+ );
14214
+ const footer = Buffer.from(`\r
14215
+ --${boundary}--\r
14216
+ `);
14217
+ const body = Buffer.concat([header, fileBuffer, footer]);
14218
+ if (process.env["NWORKS_VERBOSE"] === "1") {
14219
+ console.error(`[nworks] POST ${uploadUrl} (upload content, ${fileSize} bytes)`);
14220
+ }
14221
+ const uploadRes = await authedFetch(
14222
+ uploadUrl,
14223
+ {
14224
+ method: "POST",
14225
+ headers: { "Content-Type": `multipart/form-data; boundary=${boundary}` },
14226
+ body
14227
+ },
14228
+ profile
14229
+ );
14230
+ if (!uploadRes.ok) return handleError(uploadRes);
14231
+ return await uploadRes.json();
14232
+ }
14233
+ async function downloadFile(fileId, userId = "me", profile = "default") {
14234
+ const url2 = `${BASE_URL3}/users/${userId}/drive/files/${fileId}/download`;
14235
+ if (process.env["NWORKS_VERBOSE"] === "1") {
14236
+ console.error(`[nworks] GET ${url2} (get download URL)`);
14237
+ }
14238
+ const redirectRes = await authedFetch(
14239
+ url2,
14240
+ { method: "GET", redirect: "manual" },
14241
+ profile
14242
+ );
14243
+ if (redirectRes.status === 401) {
14244
+ throw new AuthError("User token expired. Run `nworks login --user --scope file` again.");
14245
+ }
14246
+ const location = redirectRes.headers.get("location");
14247
+ if (!location) {
14248
+ if (!redirectRes.ok) return handleError(redirectRes);
14249
+ throw new ApiError("NO_REDIRECT", "No download URL returned", redirectRes.status);
14250
+ }
14251
+ if (process.env["NWORKS_VERBOSE"] === "1") {
14252
+ console.error(`[nworks] GET ${location} (download content)`);
14253
+ }
14254
+ const downloadRes = await authedFetch(location, { method: "GET" }, profile);
14255
+ if (!downloadRes.ok) return handleError(downloadRes);
14256
+ const arrayBuffer = await downloadRes.arrayBuffer();
14257
+ const buffer = Buffer.from(arrayBuffer);
14258
+ const disposition = downloadRes.headers.get("content-disposition");
14259
+ let fileName;
14260
+ if (disposition) {
14261
+ const match = disposition.match(/filename\*?=(?:UTF-8''|"?)([^";]+)/i);
14262
+ if (match?.[1]) {
14263
+ fileName = decodeURIComponent(match[1].replace(/"/g, ""));
14264
+ }
14265
+ }
14266
+ return { buffer, fileName };
14267
+ }
14268
+
14146
14269
  // src/mcp/tools.ts
14147
14270
  function registerTools(server) {
14148
14271
  server.tool(
@@ -14253,6 +14376,105 @@ function registerTools(server) {
14253
14376
  }
14254
14377
  }
14255
14378
  );
14379
+ server.tool(
14380
+ "nworks_drive_list",
14381
+ "\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)",
14382
+ {
14383
+ userId: external_exports.string().optional().describe("\uB300\uC0C1 \uC0AC\uC6A9\uC790 ID (\uBBF8\uC9C0\uC815 \uC2DC me)"),
14384
+ folderId: external_exports.string().optional().describe("\uD3F4\uB354 ID (\uBBF8\uC9C0\uC815 \uC2DC \uB8E8\uD2B8)"),
14385
+ count: external_exports.number().optional().describe("\uD398\uC774\uC9C0\uB2F9 \uD56D\uBAA9 \uC218 (\uAE30\uBCF8: 20, \uCD5C\uB300: 200)"),
14386
+ cursor: external_exports.string().optional().describe("\uD398\uC774\uC9C0\uB124\uC774\uC158 \uCEE4\uC11C")
14387
+ },
14388
+ async ({ userId, folderId, count, cursor }) => {
14389
+ try {
14390
+ const result = await listFiles(
14391
+ userId ?? "me",
14392
+ folderId,
14393
+ count ?? 20,
14394
+ cursor
14395
+ );
14396
+ const files = result.files.map((f) => ({
14397
+ fileId: f.fileId,
14398
+ name: f.fileName,
14399
+ type: f.fileType,
14400
+ size: f.fileSize,
14401
+ modified: f.modifiedTime,
14402
+ path: f.filePath
14403
+ }));
14404
+ return {
14405
+ content: [{ type: "text", text: JSON.stringify({ files, count: files.length, nextCursor: result.responseMetaData?.nextCursor ?? null }) }]
14406
+ };
14407
+ } catch (err) {
14408
+ const error48 = err;
14409
+ return {
14410
+ content: [{ type: "text", text: `Error: ${error48.message}` }],
14411
+ isError: true
14412
+ };
14413
+ }
14414
+ }
14415
+ );
14416
+ server.tool(
14417
+ "nworks_drive_upload",
14418
+ "\uB85C\uCEEC \uD30C\uC77C\uC744 \uB4DC\uB77C\uC774\uBE0C\uC5D0 \uC5C5\uB85C\uB4DC\uD569\uB2C8\uB2E4 (User OAuth file scope \uD544\uC694)",
14419
+ {
14420
+ filePath: external_exports.string().describe("\uC5C5\uB85C\uB4DC\uD560 \uB85C\uCEEC \uD30C\uC77C \uACBD\uB85C"),
14421
+ userId: external_exports.string().optional().describe("\uB300\uC0C1 \uC0AC\uC6A9\uC790 ID (\uBBF8\uC9C0\uC815 \uC2DC me)"),
14422
+ folderId: external_exports.string().optional().describe("\uC5C5\uB85C\uB4DC\uD560 \uD3F4\uB354 ID (\uBBF8\uC9C0\uC815 \uC2DC \uB8E8\uD2B8)"),
14423
+ overwrite: external_exports.boolean().optional().describe("\uB3D9\uC77C \uD30C\uC77C\uBA85 \uB36E\uC5B4\uC4F0\uAE30 (\uAE30\uBCF8: false)")
14424
+ },
14425
+ async ({ filePath, userId, folderId, overwrite }) => {
14426
+ try {
14427
+ const result = await uploadFile(
14428
+ filePath,
14429
+ userId ?? "me",
14430
+ folderId,
14431
+ overwrite ?? false
14432
+ );
14433
+ return {
14434
+ content: [{ type: "text", text: JSON.stringify({ success: true, ...result }) }]
14435
+ };
14436
+ } catch (err) {
14437
+ const error48 = err;
14438
+ return {
14439
+ content: [{ type: "text", text: `Error: ${error48.message}` }],
14440
+ isError: true
14441
+ };
14442
+ }
14443
+ }
14444
+ );
14445
+ server.tool(
14446
+ "nworks_drive_download",
14447
+ "\uB4DC\uB77C\uC774\uBE0C \uD30C\uC77C\uC744 \uB2E4\uC6B4\uB85C\uB4DC\uD569\uB2C8\uB2E4 (User OAuth file \uB610\uB294 file.read scope \uD544\uC694)",
14448
+ {
14449
+ fileId: external_exports.string().describe("\uB2E4\uC6B4\uB85C\uB4DC\uD560 \uD30C\uC77C ID"),
14450
+ outputDir: external_exports.string().optional().describe("\uC800\uC7A5 \uB514\uB809\uD1A0\uB9AC (\uBBF8\uC9C0\uC815 \uC2DC \uD604\uC7AC \uB514\uB809\uD1A0\uB9AC)"),
14451
+ outputName: external_exports.string().optional().describe("\uC800\uC7A5 \uD30C\uC77C\uBA85 (\uBBF8\uC9C0\uC815 \uC2DC \uC6D0\uBCF8 \uD30C\uC77C\uBA85)"),
14452
+ userId: external_exports.string().optional().describe("\uB300\uC0C1 \uC0AC\uC6A9\uC790 ID (\uBBF8\uC9C0\uC815 \uC2DC me)")
14453
+ },
14454
+ async ({ fileId, outputDir, outputName, userId }) => {
14455
+ try {
14456
+ const { writeFile: writeFile2 } = await import("fs/promises");
14457
+ const { join: join2 } = await import("path");
14458
+ const result = await downloadFile(
14459
+ fileId,
14460
+ userId ?? "me"
14461
+ );
14462
+ const fileName = outputName ?? result.fileName ?? fileId;
14463
+ const dir = outputDir ?? process.cwd();
14464
+ const outPath = join2(dir, fileName);
14465
+ await writeFile2(outPath, result.buffer);
14466
+ return {
14467
+ content: [{ type: "text", text: JSON.stringify({ success: true, fileName, path: outPath, size: result.buffer.length }) }]
14468
+ };
14469
+ } catch (err) {
14470
+ const error48 = err;
14471
+ return {
14472
+ content: [{ type: "text", text: `Error: ${error48.message}` }],
14473
+ isError: true
14474
+ };
14475
+ }
14476
+ }
14477
+ );
14256
14478
  server.tool(
14257
14479
  "nworks_whoami",
14258
14480
  "\uD604\uC7AC \uC778\uC99D\uB41C NAVER WORKS \uACC4\uC815 \uC815\uBCF4\uB97C \uD655\uC778\uD569\uB2C8\uB2E4",