drafted 1.11.14 → 1.11.16

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 +20 -5
  2. package/package.json +1 -1
package/mcp/server.mjs CHANGED
@@ -1230,7 +1230,13 @@ async function consumePendingDeviceCode() {
1230
1230
  return false;
1231
1231
  }
1232
1232
 
1233
- tool('auth', 'Sign in to Drafted. `action=get_link` returns a verification URL immediately (use for SSH/headless/tmux where a browser may not open) and starts background polling; after the user opens the link, later Drafted tool calls also auto-consume the approved login. `action=login` opens a browser when needed and explicitly waits/polls for approval. If get_link was called first, login reuses that pending code instead of opening a new browser.', {
1233
+ // Device-flow sign-in is stdio-only. Web/Cowork connectors authenticate via
1234
+ // OAuth at the transport layer (the hosted /mcp handler injects a Drafted
1235
+ // session per request), so advertising a device-flow auth tool there is
1236
+ // meaningless and disruptive — it returns spurious sign-in URLs and, on login,
1237
+ // spawns a server-side browser-open and blocks polling until timeout. Register
1238
+ // it only on stdio.
1239
+ if (!isRemote) tool('auth', 'Sign in to Drafted. `action=get_link` returns a verification URL immediately (use for SSH/headless/tmux where a browser may not open) and starts background polling; after the user opens the link, later Drafted tool calls also auto-consume the approved login. `action=login` opens a browser when needed and explicitly waits/polls for approval. If get_link was called first, login reuses that pending code instead of opening a new browser.', {
1234
1240
  action: z.enum(['get_link', 'login']).describe('Operation to perform.'),
1235
1241
  }, async ({ action }) => {
1236
1242
  try {
@@ -1242,7 +1248,11 @@ tool('auth', 'Sign in to Drafted. `action=get_link` returns a verification URL i
1242
1248
  return ok(data.verificationUrl);
1243
1249
  }
1244
1250
  if (action === 'login') {
1245
- const existing = getBootstrapSessionId();
1251
+ // Prefer the active request session (injected by runWithRequestState on
1252
+ // remote, or cloneSession on stdio) over the on-disk bootstrap session, so
1253
+ // an already-authenticated caller short-circuits to already_authenticated
1254
+ // instead of starting a needless device flow.
1255
+ const existing = getState().sessionId || getBootstrapSessionId();
1246
1256
  if (existing) {
1247
1257
  try {
1248
1258
  const res = await fetch(`${getServerUrl()}/auth/me`, {
@@ -2812,7 +2822,6 @@ tool('skill', 'Manage the Drafted skill library. Skills are reusable prompts/gui
2812
2822
  tags: z.array(z.string()).optional().describe('[search] filter by tags; [add|update] tag list'),
2813
2823
  scope: z.enum(['all', 'org', 'global']).optional().describe('[search|list] library scope (default: all for search; when provided to list, lists the library instead of project/org attachments)'),
2814
2824
  limit: z.number().optional().describe('[search|list] max results per page (default 25, max 100)'),
2815
- offset: z.number().optional().describe('[search|list] skip N results for pagination (default 0). Response reports totalAvailable/returned/truncated.'),
2816
2825
  compact: z.boolean().optional().describe('[search|list] return only {slug,name,tags} per skill instead of full summaries — for browsing large catalogs within token budget'),
2817
2826
  skill: z.string().optional().describe('[load|history] skill ID (UUID) or slug'),
2818
2827
  version: z.number().optional().describe('[history] fetch this version number\'s full snapshot (content included); omit for the reverse-chron version list'),
@@ -2823,6 +2832,8 @@ tool('skill', 'Manage the Drafted skill library. Skills are reusable prompts/gui
2823
2832
  content: z.string().optional().describe('[add|update] root SKILL.md content; [update_file] file content'),
2824
2833
  triggerPatterns: z.array(z.string()).optional().describe('[add|update] patterns that suggest this skill'),
2825
2834
  path: z.string().optional().describe('[read_file|update_file] relative path inside skill directory (e.g. "examples/react.md")'),
2835
+ offset: z.number().optional().describe('[search|list] skip N results for pagination; [read_file] start reading at this byte offset (default 0) — for large files (e.g. a >90KB app-frame bundle) read in chunks using the returned nextOffset until truncated=false'),
2836
+ maxBytes: z.number().optional().describe('[read_file] return at most this many bytes from offset (default: whole remaining file). Response reports totalSize/offset/truncated/nextOffset.'),
2826
2837
  org: z.string().optional().describe('[fork|push|update] resolve/fork into this Drafted org (id or name); scopes the request without switching the session'),
2827
2838
  setup: z.array(z.string()).optional().describe('[add|update] setup command(s) (in order) run on materialize to build a source-only skill, e.g. ["npm ci","npm run build"]'),
2828
2839
  files: z.array(z.object({ path: z.string(), content: z.string() })).optional().describe('[push] source files to push (path + UTF-8 content); server strips artifacts + enforces caps'),
@@ -3010,9 +3021,13 @@ tool('skill', 'Manage the Drafted skill library. Skills are reusable prompts/gui
3010
3021
  return ok(await api('DELETE', `/api/skills/favorites/${skillId}`));
3011
3022
  }
3012
3023
  case 'read_file': {
3013
- const { skillId, path } = args;
3024
+ const { skillId, path, offset, maxBytes } = args;
3014
3025
  if (!skillId || !path) throw new Error('skillId and path required for action=read_file');
3015
- return ok(await api('GET', `/api/skills/${skillId}/files/${path}`));
3026
+ const params = new URLSearchParams();
3027
+ if (offset != null) params.set('offset', String(offset));
3028
+ if (maxBytes != null) params.set('maxBytes', String(maxBytes));
3029
+ const qs = params.toString();
3030
+ return ok(await api('GET', `/api/skills/${skillId}/files/${path}${qs ? `?${qs}` : ''}`));
3016
3031
  }
3017
3032
  case 'update_file': {
3018
3033
  const { skillId, path, content } = args;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "drafted",
3
- "version": "1.11.14",
3
+ "version": "1.11.16",
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": [