drafted 1.19.26 → 1.19.27

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.
Files changed (2) hide show
  1. package/mcp/server.mjs +47 -46
  2. package/package.json +1 -1
package/mcp/server.mjs CHANGED
@@ -2106,6 +2106,51 @@ function desktopAppBinary() {
2106
2106
  return null; // e.g. Linux — no desktop app → device-code fallback
2107
2107
  }
2108
2108
 
2109
+ /**
2110
+ * The answer to "am I signed in?", which is a question about the MACHINE, not this
2111
+ * process. Two things went wrong before this existed, and they are the same mistake:
2112
+ *
2113
+ * - ~/.drafted/auth.json could be missing while the in-process session was fine. The
2114
+ * desktop's credential burn deletes it on purpose, so `login` answered
2115
+ * already_authenticated — truthfully, and uselessly — while every other reader on the
2116
+ * machine stayed signed out. Restore it from the live session.
2117
+ * - The DESKTOP app could be signed out, which is what actually decides whether an agent
2118
+ * asking for a human reaches anybody: its focus listener needs desktop.json. An agent
2119
+ * must never MINT that credential (origin='browser' is the class that may approve
2120
+ * actions), but opening the app's sign-in window is this tool's documented job — and it
2121
+ * usually completes with NO interaction, because the shared cookie store still holds a
2122
+ * valid login.
2123
+ */
2124
+ async function alreadyAuthenticated(me, sessionId) {
2125
+ const out = { status: 'already_authenticated', userId: me.userId, email: me.userEmail, org: me.currentOrg?.name };
2126
+ if (!getBootstrapSessionId()) {
2127
+ try {
2128
+ persistAuthSession({ sessionId, userId: me.userId, orgId: me.currentOrg?.id });
2129
+ out.restoredAuthFile = true;
2130
+ out.note = 'This session was signed in but ~/.drafted/auth.json was missing, so other readers on this machine (the desktop app, the CLI) were not. Rewrote it from the live session.';
2131
+ } catch { /* read-only home: report the truth rather than throwing */ }
2132
+ }
2133
+ const bin = desktopAppBinary();
2134
+ if (!bin) return out; // no app here: saying anything about it is noise
2135
+ if (!existsSync(DESKTOP_SESSION_FILE)) {
2136
+ if (await launchDesktopSignin()) {
2137
+ out.openedDesktopSignin = true;
2138
+ // Wait for the result rather than reporting an optimistic one — answering before it
2139
+ // lands is the same class of lie this whole helper exists to stop.
2140
+ for (let i = 0; i < 20 && !existsSync(DESKTOP_SESSION_FILE); i++) {
2141
+ await new Promise((r) => setTimeout(r, 500));
2142
+ }
2143
+ }
2144
+ }
2145
+ out.desktopSignedIn = existsSync(DESKTOP_SESSION_FILE);
2146
+ if (out.openedDesktopSignin) {
2147
+ out.desktopNote = out.desktopSignedIn
2148
+ ? 'The desktop app was signed out; opened its sign-in window and it completed on its own from the existing browser session.'
2149
+ : 'The desktop app is signed out and its sign-in window is open — it needs one approval there. Until then it holds no session, so an agent asking for attention cannot reach this machine.';
2150
+ }
2151
+ return out;
2152
+ }
2153
+
2109
2154
  async function launchDesktopSignin() {
2110
2155
  const bin = desktopAppBinary();
2111
2156
  if (!bin) return false;
@@ -2186,7 +2231,7 @@ if (!isRemote) tool('auth', 'Sign in to Drafted.\n\n`action=get_link` ALWAYS ret
2186
2231
  await cloneSession();
2187
2232
  connectAgentWs();
2188
2233
  }
2189
- return ok({ status: 'already_authenticated', userId: me.userId, email: me.userEmail, org: me.currentOrg?.name });
2234
+ return ok(await alreadyAuthenticated(me, existing));
2190
2235
  }
2191
2236
  } catch { /* stale session — continue to sign-in */ }
2192
2237
  }
@@ -2232,51 +2277,7 @@ if (!isRemote) tool('auth', 'Sign in to Drafted.\n\n`action=get_link` ALWAYS ret
2232
2277
  // The desktop's focus listener is one of those readers, so an agent asking
2233
2278
  // for a human reached nobody and the tool that exists to fix it reported
2234
2279
  // success.
2235
- let restoredAuthFile = false;
2236
- if (!getBootstrapSessionId()) {
2237
- try {
2238
- persistAuthSession({ sessionId: existing, userId: me.userId, orgId: me.currentOrg?.id });
2239
- restoredAuthFile = true;
2240
- } catch { /* read-only home: report the truth below rather than throwing */ }
2241
- }
2242
- // The DESKTOP's own browser credential is a separate file, and an agent
2243
- // cannot mint one — it is the origin='browser' class that may approve
2244
- // actions, which is exactly what the two-credential split protects. But
2245
- // "cannot mint it" is not "cannot ask for it": opening the app's sign-in
2246
- // window is this tool's documented job, and the window usually completes
2247
- // with no interaction at all because the shared cookie store still holds a
2248
- // valid login. Short-circuiting on the agent session skipped that entirely,
2249
- // so `login` reported success while the app stayed signed out and every
2250
- // agent that pinged the human reached nobody.
2251
- let openedDesktopSignin = false;
2252
- if (desktopAppBinary() && !existsSync(DESKTOP_SESSION_FILE)) {
2253
- openedDesktopSignin = await launchDesktopSignin();
2254
- if (openedDesktopSignin) {
2255
- // Give the app a moment to re-capture the cookie so the caller can act
2256
- // on a true answer rather than an optimistic one.
2257
- for (let i = 0; i < 20 && !existsSync(DESKTOP_SESSION_FILE); i++) {
2258
- await new Promise((r) => setTimeout(r, 500));
2259
- }
2260
- }
2261
- }
2262
- const desktopSignedIn = !desktopAppBinary() ? null : existsSync(DESKTOP_SESSION_FILE);
2263
- return ok({
2264
- status: 'already_authenticated',
2265
- userId: me.userId,
2266
- email: me.userEmail,
2267
- org: me.currentOrg?.name,
2268
- ...(restoredAuthFile ? {
2269
- restoredAuthFile: true,
2270
- note: 'This session was signed in but ~/.drafted/auth.json was missing, so other readers on this machine (the desktop app, the CLI) were not. Rewrote it from the live session.',
2271
- } : {}),
2272
- ...(desktopSignedIn === null ? {} : { desktopSignedIn }),
2273
- ...(openedDesktopSignin ? {
2274
- openedDesktopSignin: true,
2275
- desktopNote: desktopSignedIn
2276
- ? 'The desktop app was signed out; opened its sign-in window and it completed on its own from the existing browser session.'
2277
- : 'The desktop app is signed out and its sign-in window is open — it needs one approval there. Until then it holds no session, so an agent asking for attention cannot reach this machine.',
2278
- } : {}),
2279
- });
2280
+ return ok(await alreadyAuthenticated(me, existing));
2280
2281
  }
2281
2282
  } catch { /* session invalid, proceed with login */ }
2282
2283
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "drafted",
3
- "version": "1.19.26",
3
+ "version": "1.19.27",
4
4
  "description": "Drafted — visual thinking surface for humans and AI agents. Renders HTML, markdown, images, and code as frames on a zoomable canvas, with MCP tools for AI agents and real-time sync for humans.",
5
5
  "type": "module",
6
6
  "files": [