switchroom 0.14.87 → 0.14.89

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.
@@ -49815,8 +49815,8 @@ var {
49815
49815
  } = import__.default;
49816
49816
 
49817
49817
  // src/build-info.ts
49818
- var VERSION = "0.14.87";
49819
- var COMMIT_SHA = "5ad3e254";
49818
+ var VERSION = "0.14.89";
49819
+ var COMMIT_SHA = "125cbcc5";
49820
49820
 
49821
49821
  // src/cli/agent.ts
49822
49822
  init_source();
@@ -79037,7 +79037,7 @@ function registerNotionMcpLauncherCommand(program3) {
79037
79037
  // src/cli/deliver-file.ts
79038
79038
  init_client2();
79039
79039
  import { readFileSync as readFileSync58, statSync as statSync28 } from "node:fs";
79040
- import { basename as basename6 } from "node:path";
79040
+ import { basename as basename7 } from "node:path";
79041
79041
 
79042
79042
  // src/delivery/onedrive.ts
79043
79043
  import { basename as basename5 } from "node:path";
@@ -79169,7 +79169,119 @@ async function deliverToOneDrive(args) {
79169
79169
  return { itemId: item.id, link, folderPath: `Switchroom/${args.agentName}` };
79170
79170
  }
79171
79171
 
79172
+ // src/delivery/gdrive.ts
79173
+ import { basename as basename6 } from "node:path";
79174
+ var DRIVE = "https://www.googleapis.com/drive/v3";
79175
+ var UPLOAD = "https://www.googleapis.com/upload/drive/v3";
79176
+ var FOLDER_MIME = "application/vnd.google-apps.folder";
79177
+ function authHeaders2(token) {
79178
+ return { Authorization: `Bearer ${token}`, Accept: "application/json" };
79179
+ }
79180
+ async function readBody2(resp) {
79181
+ try {
79182
+ const t = await resp.text();
79183
+ return t.length > 300 ? `${t.slice(0, 300)}\u2026` : t;
79184
+ } catch {
79185
+ return "";
79186
+ }
79187
+ }
79188
+ async function ensureFolder2(deps, name, parentId) {
79189
+ const f = deps.fetchImpl ?? fetch;
79190
+ const safeName = name.replace(/'/g, "\\'");
79191
+ const q = `name='${safeName}' and mimeType='${FOLDER_MIME}' and '${parentId}' in parents and trashed=false`;
79192
+ const listUrl = `${DRIVE}/files?q=${encodeURIComponent(q)}&fields=files(id,name)&spaces=drive`;
79193
+ const list2 = await f(listUrl, { headers: authHeaders2(deps.accessToken) });
79194
+ if (!list2.ok) {
79195
+ throw new Error(`Drive folder lookup failed: HTTP ${list2.status} \u2014 ${await readBody2(list2)}`);
79196
+ }
79197
+ const found = await list2.json();
79198
+ if (found.files && found.files.length > 0)
79199
+ return found.files[0];
79200
+ const created = await f(`${DRIVE}/files?fields=id,name`, {
79201
+ method: "POST",
79202
+ headers: { ...authHeaders2(deps.accessToken), "Content-Type": "application/json" },
79203
+ body: JSON.stringify({ name, mimeType: FOLDER_MIME, parents: [parentId] })
79204
+ });
79205
+ if (!created.ok) {
79206
+ throw new Error(`Drive folder create failed: HTTP ${created.status} \u2014 ${await readBody2(created)}`);
79207
+ }
79208
+ return await created.json();
79209
+ }
79210
+ async function ensureSwitchroomFolder2(deps, agentName) {
79211
+ const top = await ensureFolder2(deps, "Switchroom", "root");
79212
+ return ensureFolder2(deps, agentName, top.id);
79213
+ }
79214
+ async function uploadFile2(deps, parentId, filename, bytes, mimeType = "application/octet-stream") {
79215
+ const f = deps.fetchImpl ?? fetch;
79216
+ const boundary = "switchroom-deliver-boundary";
79217
+ const metadata = JSON.stringify({ name: filename, parents: [parentId] });
79218
+ const enc = new TextEncoder;
79219
+ const head = enc.encode(`--${boundary}\r
79220
+ Content-Type: application/json; charset=UTF-8\r
79221
+ \r
79222
+ ${metadata}\r
79223
+ ` + `--${boundary}\r
79224
+ Content-Type: ${mimeType}\r
79225
+ \r
79226
+ `);
79227
+ const tail = enc.encode(`\r
79228
+ --${boundary}--`);
79229
+ const body = new Uint8Array(head.length + bytes.length + tail.length);
79230
+ body.set(head, 0);
79231
+ body.set(bytes, head.length);
79232
+ body.set(tail, head.length + bytes.length);
79233
+ const resp = await f(`${UPLOAD}/files?uploadType=multipart&fields=id,name,webViewLink`, {
79234
+ method: "POST",
79235
+ headers: {
79236
+ Authorization: `Bearer ${deps.accessToken}`,
79237
+ "Content-Type": `multipart/related; boundary=${boundary}`
79238
+ },
79239
+ body
79240
+ });
79241
+ if (!resp.ok) {
79242
+ throw new Error(`Drive upload failed: HTTP ${resp.status} \u2014 ${await readBody2(resp)}`);
79243
+ }
79244
+ return await resp.json();
79245
+ }
79246
+ async function createShareLink2(deps, file, scopes = ["anyone"]) {
79247
+ const f = deps.fetchImpl ?? fetch;
79248
+ for (const type of scopes) {
79249
+ const resp = await f(`${DRIVE}/files/${file.id}/permissions`, {
79250
+ method: "POST",
79251
+ headers: { ...authHeaders2(deps.accessToken), "Content-Type": "application/json" },
79252
+ body: JSON.stringify(type === "domain" ? { role: "reader", type: "domain" } : { role: "reader", type: "anyone" })
79253
+ });
79254
+ if (resp.ok)
79255
+ break;
79256
+ }
79257
+ if (file.webViewLink)
79258
+ return file.webViewLink;
79259
+ const meta = await f(`${DRIVE}/files/${file.id}?fields=webViewLink`, {
79260
+ headers: authHeaders2(deps.accessToken)
79261
+ });
79262
+ if (meta.ok) {
79263
+ const j = await meta.json();
79264
+ if (j.webViewLink)
79265
+ return j.webViewLink;
79266
+ }
79267
+ throw new Error("Drive: could not resolve a webViewLink for the uploaded file");
79268
+ }
79269
+ async function deliverToGoogleDrive(args) {
79270
+ const deps = { accessToken: args.accessToken, fetchImpl: args.fetchImpl };
79271
+ const folder = await ensureSwitchroomFolder2(deps, args.agentName);
79272
+ const filename = basename6(args.localPath);
79273
+ const file = await uploadFile2(deps, folder.id, filename, args.bytes);
79274
+ const link = await createShareLink2(deps, file, args.linkScopes);
79275
+ return { itemId: file.id, link, folderPath: `Switchroom/${args.agentName}` };
79276
+ }
79277
+
79172
79278
  // src/cli/deliver-file.ts
79279
+ function resolveGoogleLinkScopes(env2 = process.env) {
79280
+ const raw = (env2.SWITCHROOM_DELIVER_LINK_SCOPE ?? "").trim().toLowerCase();
79281
+ if (raw === "organization")
79282
+ return ["domain"];
79283
+ return ["anyone"];
79284
+ }
79173
79285
  function resolveLinkScopes(env2 = process.env) {
79174
79286
  const raw = (env2.SWITCHROOM_DELIVER_LINK_SCOPE ?? "").trim().toLowerCase();
79175
79287
  if (raw === "organization")
@@ -79182,22 +79294,41 @@ function safeAgentName(name) {
79182
79294
  const n = (name ?? "").trim();
79183
79295
  return /^[a-z0-9][a-z0-9_-]{0,50}$/.test(n) ? n : "agent";
79184
79296
  }
79185
- async function brokerAccessToken(provider) {
79297
+ async function brokerToken(provider) {
79186
79298
  const client2 = new AuthBrokerClient;
79187
- const data = await client2.getCredentials(provider);
79188
- const creds = data.credentials;
79189
- const token = creds?.microsoftOauth?.accessToken;
79190
- if (!token) {
79191
- throw new Error("broker returned no Microsoft access token (is an account connected + enabled for this agent?)");
79299
+ try {
79300
+ const data = await client2.getCredentials(provider);
79301
+ const creds = data.credentials;
79302
+ const token = provider === "microsoft" ? creds?.microsoftOauth?.accessToken : creds?.googleOauth?.accessToken;
79303
+ return token ?? null;
79304
+ } catch {
79305
+ return null;
79306
+ } finally {
79307
+ await client2.close().catch(() => {});
79192
79308
  }
79193
- return token;
79309
+ }
79310
+ async function defaultResolveProvider() {
79311
+ const ms = await brokerToken("microsoft");
79312
+ if (ms) {
79313
+ return {
79314
+ name: "OneDrive",
79315
+ deliver: (a) => deliverToOneDrive({ ...a, accessToken: ms, linkScopes: resolveLinkScopes() })
79316
+ };
79317
+ }
79318
+ const g = await brokerToken("google");
79319
+ if (g) {
79320
+ return {
79321
+ name: "Google Drive",
79322
+ deliver: (a) => deliverToGoogleDrive({ ...a, accessToken: g, linkScopes: resolveGoogleLinkScopes() })
79323
+ };
79324
+ }
79325
+ return null;
79194
79326
  }
79195
79327
  async function runDeliverFile(localPath, deps = {}) {
79196
79328
  const agentName = safeAgentName(deps.agentName ?? process.env.SWITCHROOM_AGENT_NAME);
79197
79329
  const sizeOf = deps.fileSize ?? ((p) => statSync28(p).size);
79198
79330
  const read = deps.readFile ?? ((p) => new Uint8Array(readFileSync58(p)));
79199
- const getToken = deps.getAccessToken ?? brokerAccessToken;
79200
- const deliver = deps.deliver ?? ((a) => deliverToOneDrive({ ...a, linkScopes: resolveLinkScopes() }));
79331
+ const resolveProvider = deps.resolveProvider ?? defaultResolveProvider;
79201
79332
  let size;
79202
79333
  try {
79203
79334
  size = sizeOf(localPath);
@@ -79207,28 +79338,26 @@ async function runDeliverFile(localPath, deps = {}) {
79207
79338
  if (size === 0) {
79208
79339
  return { ok: false, error: `file is empty: ${localPath}` };
79209
79340
  }
79210
- let accessToken;
79211
- try {
79212
- accessToken = await getToken("microsoft");
79213
- } catch (err) {
79341
+ const provider = await resolveProvider();
79342
+ if (!provider) {
79214
79343
  return {
79215
79344
  ok: false,
79216
- error: `no connected drive for delivery \u2014 ${err.message}. ` + `Connect a Microsoft account from the dashboard, or send the file ` + `directly with the reply tool (files: ["${localPath}"]) for files under 50MB.`
79345
+ error: `no connected drive for delivery. Connect a Microsoft or Google ` + `account from the dashboard, or send the file directly with the ` + `reply tool (files: ["${localPath}"]) for files under 50MB.`
79217
79346
  };
79218
79347
  }
79219
79348
  try {
79220
79349
  const bytes = read(localPath);
79221
- const out = await deliver({ accessToken, agentName, localPath, bytes });
79222
- return { ok: true, link: out.link, folderPath: out.folderPath, filename: basename6(localPath) };
79350
+ const out = await provider.deliver({ agentName, localPath, bytes });
79351
+ return { ok: true, provider: provider.name, link: out.link, folderPath: out.folderPath, filename: basename7(localPath) };
79223
79352
  } catch (err) {
79224
- return { ok: false, error: `upload failed: ${err.message}` };
79353
+ return { ok: false, provider: provider.name, error: `upload failed: ${err.message}` };
79225
79354
  }
79226
79355
  }
79227
79356
  function registerDeliverFileCommand(program3) {
79228
79357
  program3.command("deliver-file").description("Deliver a file you produced to the user: upload it to their Switchroom/<agent>/ folder on the connected drive and print a shareable link. Reply with that link \u2014 never a local container path.").argument("<path>", "absolute local path of the file to deliver").action(async (path7) => {
79229
79358
  const res = await runDeliverFile(path7);
79230
79359
  if (res.ok) {
79231
- process.stdout.write(`Delivered ${res.filename} to the user's drive \u2192 ${res.folderPath}/
79360
+ process.stdout.write(`Delivered ${res.filename} to the user's ${res.provider} \u2192 ${res.folderPath}/
79232
79361
  ` + `Share link (reply with this): ${res.link}
79233
79362
  `);
79234
79363
  return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "switchroom",
3
- "version": "0.14.87",
3
+ "version": "0.14.89",
4
4
  "description": "Run Claude Code 24/7 on your Claude Pro/Max subscription over Telegram. Open-source alternative to OpenClaw and NanoClaw — no API keys.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -52889,11 +52889,11 @@ function sweepStaleTurnActiveMarker(stateDir, opts) {
52889
52889
  }
52890
52890
 
52891
52891
  // ../src/build-info.ts
52892
- var VERSION = "0.14.87";
52893
- var COMMIT_SHA = "5ad3e254";
52894
- var COMMIT_DATE = "2026-06-07T08:49:13Z";
52895
- var LATEST_PR = 2227;
52896
- var COMMITS_AHEAD_OF_TAG = 0;
52892
+ var VERSION = "0.14.89";
52893
+ var COMMIT_SHA = "125cbcc5";
52894
+ var COMMIT_DATE = "2026-06-08T07:42:50+10:00";
52895
+ var LATEST_PR = null;
52896
+ var COMMITS_AHEAD_OF_TAG = 2;
52897
52897
 
52898
52898
  // gateway/boot-version.ts
52899
52899
  function formatRelativeAgo(iso) {