drafted 1.19.23 → 1.19.25
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/mcp/server.mjs +28 -3
- package/package.json +1 -1
- package/src/shared/root-route.mjs +45 -0
package/mcp/server.mjs
CHANGED
|
@@ -415,7 +415,7 @@ const TOOL_ANNOTATIONS = {
|
|
|
415
415
|
screenshot: { title: 'Screenshot', readOnlyHint: true, destructiveHint: false, openWorldHint: false, description: 'Render a PNG via headless browser. `scope=frame` for a single frame, `scope=canvas` for a region of the project surface.' },
|
|
416
416
|
|
|
417
417
|
// Minions — checklist-driven intake surfaces bound to a project
|
|
418
|
-
minion: { title: 'Minions', readOnlyHint: false, destructiveHint: true, openWorldHint: false, description: 'Manage Minions: checklist-driven intake surfaces that guide a consumer through a checklist (via a shareable /c/<slug> link) and write a producible into the project. Dispatch by `action`: meta, list, get, create, update, enable, disable, delete. QA your own Minions with test_start/test_say/test_resolve — drive the checklist conversation yourself (works even when disabled). Requires the agent allowlist.' },
|
|
418
|
+
minion: { title: 'Minions', readOnlyHint: false, destructiveHint: true, openWorldHint: false, description: 'Manage Minions: checklist-driven intake surfaces that guide a consumer through a checklist (via a shareable /c/<slug>-<token> link) and write a producible into the project. Dispatch by `action`: meta, list, get, create, update, enable, disable, delete. QA your own Minions with test_start/test_say/test_resolve — drive the checklist conversation yourself (works even when disabled). Requires the agent allowlist.' },
|
|
419
419
|
trigger: { title: 'Inbound triggers', readOnlyHint: false, destructiveHint: true, openWorldHint: true, description: 'Manage inbound webhook triggers for the ACTIVE PROJECT: an external system (AppSheet bot, GitHub, form tool) POSTs to the trigger URL and the server runs an agent conversation in the project from the stored prompt template + payload. Dispatch by `action`: create (returns URL + secret token ONCE — relay it to the user immediately, not retrievable later), list, update (enable/disable, edit template, daily limit, executor), rotate (new token), test (fire a synthetic delivery), deliveries (audit log), delete; for executor="queue" triggers, pending/claim/complete let a LOCAL agent poll and work queued deliveries. Requires the agent allowlist.' },
|
|
420
420
|
fs: { title: 'Filesystem', readOnlyHint: false, destructiveHint: true, openWorldHint: false, description: 'Navigate Drafted like a local filesystem: /wiki/<path> pages, /skills/<slug> procedures, /projects/<folder?>/<project>/<layer>/<lane>/<file> frames. Verbs: ls, read, write, edit, mv, rm, search, link, unlink, links.' },
|
|
421
421
|
repo: { title: 'Git repos', readOnlyHint: false, destructiveHint: true, openWorldHint: true, description: 'Registered git repos — the org index of .agents/ skills + identities. A connected repo is the source of truth for its skills; EVERY repo connected to ANY folder in the org is searchable and usable org-wide (no org-level repo — the union is the library). Skills from connected repos are readable via fs(read, path="/skills/<slug>") — fetched from git at read time, always fresh. Authoring a Drafted skill whose slug collides with a repo-indexed skill returns 409 repo_owned pointing at the repo. Dispatch by `action`: list (paginated, compact mode), rescan (re-fetch the tracked branch), entries (search the index). Linking and unlinking are NOT here — a human does them in the Drafted UI. Content stays in git; Drafted keeps a read-only index.' },
|
|
@@ -2218,7 +2218,32 @@ if (!isRemote) tool('auth', 'Sign in to Drafted.\n\n`action=get_link` ALWAYS ret
|
|
|
2218
2218
|
});
|
|
2219
2219
|
if (res.ok) {
|
|
2220
2220
|
const me = await res.json();
|
|
2221
|
-
|
|
2221
|
+
// "Signed in" has to mean THIS MACHINE is signed in, not "this process
|
|
2222
|
+
// happens to hold a session". The two came apart the first time anything
|
|
2223
|
+
// deleted ~/.drafted/auth.json underneath a running MCP (the desktop's
|
|
2224
|
+
// credential burn does exactly that, by design): the in-process session
|
|
2225
|
+
// stayed valid, so login answered already_authenticated — truthfully, and
|
|
2226
|
+
// uselessly — while every OTHER reader on the machine stayed signed out.
|
|
2227
|
+
// The desktop's focus listener is one of those readers, so an agent asking
|
|
2228
|
+
// for a human reached nobody and the tool that exists to fix it reported
|
|
2229
|
+
// success.
|
|
2230
|
+
let restoredAuthFile = false;
|
|
2231
|
+
if (!getBootstrapSessionId()) {
|
|
2232
|
+
try {
|
|
2233
|
+
persistAuthSession({ sessionId: existing, userId: me.userId, orgId: me.currentOrg?.id });
|
|
2234
|
+
restoredAuthFile = true;
|
|
2235
|
+
} catch { /* read-only home: report the truth below rather than throwing */ }
|
|
2236
|
+
}
|
|
2237
|
+
return ok({
|
|
2238
|
+
status: 'already_authenticated',
|
|
2239
|
+
userId: me.userId,
|
|
2240
|
+
email: me.userEmail,
|
|
2241
|
+
org: me.currentOrg?.name,
|
|
2242
|
+
...(restoredAuthFile ? {
|
|
2243
|
+
restoredAuthFile: true,
|
|
2244
|
+
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.',
|
|
2245
|
+
} : {}),
|
|
2246
|
+
});
|
|
2222
2247
|
}
|
|
2223
2248
|
} catch { /* session invalid, proceed with login */ }
|
|
2224
2249
|
}
|
|
@@ -2972,7 +2997,7 @@ tool('minion', {
|
|
|
2972
2997
|
projectId: z.string().optional().describe('[meta|list|create] the project this Minion belongs to — REQUIRED, as a path (/projects/<name> or /projects/<folder>/<name>), slug, or UUID. Minions are project-scoped: they write producibles into that project\'s surface. No session fallback — name the project.'),
|
|
2973
2998
|
name: z.string().optional().describe('[create|update] Minion name'),
|
|
2974
2999
|
description: z.string().optional().describe('[create|update] one-line description shown to the consumer'),
|
|
2975
|
-
enabled: z.boolean().optional().describe('[create|update] whether the Minion is live; a disabled Minion 404s on its /c/<slug> link. enable/disable set this directly.'),
|
|
3000
|
+
enabled: z.boolean().optional().describe('[create|update] whether the Minion is live; a disabled Minion 404s on its /c/<slug>-<token> link. enable/disable set this directly.'),
|
|
2976
3001
|
target: z.object({
|
|
2977
3002
|
type: z.enum(['new-record', 'layer', 'frame']).describe('what the run writes against'),
|
|
2978
3003
|
layer: z.string().optional(),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "drafted",
|
|
3
|
-
"version": "1.19.
|
|
3
|
+
"version": "1.19.25",
|
|
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": [
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ONE definition of the root-URL grammar, shared by the server, the client
|
|
3
|
+
* router and the tests:
|
|
4
|
+
*
|
|
5
|
+
* /o/<org>[/<folder chain>]/wiki|skills|tasks|projects[/<path>]
|
|
6
|
+
*
|
|
7
|
+
* The regex is a literal copy of the four root routes in server/server.mjs. It
|
|
8
|
+
* must stay that way: the client router uses it to decide what it may handle
|
|
9
|
+
* without a round trip, so any divergence renders a different page than the URL
|
|
10
|
+
* names.
|
|
11
|
+
*
|
|
12
|
+
* The source form carries ZERO BACKSLASHES on purpose. This function is
|
|
13
|
+
* embedded into client JS via `String(parseRootPath)` inside a template literal
|
|
14
|
+
* (see AGENTS.md's escape trap: a `\/` written inside a template literal
|
|
15
|
+
* collapses to `/`). Interpolating a runtime string is safe either way, but a
|
|
16
|
+
* body with no backslashes cannot be broken by a later copy-paste into a
|
|
17
|
+
* literal region.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
export const ROOTS = ['wiki', 'skills', 'tasks', 'projects'];
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* @param {string} pathname
|
|
24
|
+
* @returns {{orgParam: string, chain: string, root: string, path: string}|null}
|
|
25
|
+
* `null` means "not a root URL" — the caller must let the server decide.
|
|
26
|
+
* `chain` is decoded ('' = the org itself); `path` is left RAW/encoded,
|
|
27
|
+
* exactly as the server's `req.params[2]` is, so both halves agree.
|
|
28
|
+
*/
|
|
29
|
+
export function parseRootPath(pathname) {
|
|
30
|
+
var re = new RegExp('^/o/([^/]+)/(?:(.+?)/)?(wiki|skills|tasks|projects)(?:/(.*))?$');
|
|
31
|
+
var m = re.exec(String(pathname || ''));
|
|
32
|
+
if (!m) return null;
|
|
33
|
+
var path = m[4] || '';
|
|
34
|
+
while (path.length && path.charAt(path.length - 1) === '/') path = path.slice(0, -1);
|
|
35
|
+
// The projects root takes NO sub-path on the server, because
|
|
36
|
+
// /o/<org>/projects/<slug> is the PROJECT CANVAS. Claiming it here would make
|
|
37
|
+
// the client router swallow every project link and render the folder list.
|
|
38
|
+
if (m[3] === 'projects' && path) return null;
|
|
39
|
+
return {
|
|
40
|
+
orgParam: decodeURIComponent(m[1]),
|
|
41
|
+
chain: m[2] ? decodeURIComponent(m[2]) : '',
|
|
42
|
+
root: m[3],
|
|
43
|
+
path: path,
|
|
44
|
+
};
|
|
45
|
+
}
|