pmx-canvas 0.6.4 → 0.7.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/.github/extensions/pmx-canvas/extension.mjs +67 -3
- package/CHANGELOG.md +73 -0
- package/Readme.md +35 -5
- package/dist/canvas/global.css +498 -28
- package/dist/canvas/index.js +110 -95
- package/dist/canvas/mermaid-entry.js +258 -258
- package/dist/types/cli/agent.d.ts +4 -0
- package/dist/types/cli/commands/backup.d.ts +1 -0
- package/dist/types/cli/commands/boards.d.ts +1 -0
- package/dist/types/cli/commands/export.d.ts +1 -0
- package/dist/types/cli/commands/record.d.ts +12 -0
- package/dist/types/client/canvas/BoardCopyDialog.d.ts +5 -0
- package/dist/types/client/canvas/BoardFolderDialog.d.ts +7 -0
- package/dist/types/client/canvas/BoardLinkDialog.d.ts +3 -0
- package/dist/types/client/canvas/BoardSwitcher.d.ts +8 -0
- package/dist/types/client/canvas/ExportDialog.d.ts +7 -0
- package/dist/types/client/canvas/HomeView.d.ts +9 -0
- package/dist/types/client/canvas/Presentation.d.ts +13 -0
- package/dist/types/client/canvas/use-node-drag.d.ts +2 -1
- package/dist/types/client/nodes/AttachmentNode.d.ts +6 -0
- package/dist/types/client/nodes/BoardNode.d.ts +5 -0
- package/dist/types/client/nodes/McpAppNode.d.ts +24 -1
- package/dist/types/client/state/boards-store.d.ts +79 -0
- package/dist/types/client/state/canvas-store.d.ts +2 -0
- package/dist/types/client/state/intent-bridge.d.ts +1 -0
- package/dist/types/client/state/presentation.d.ts +3 -0
- package/dist/types/client/state/session-store.d.ts +7 -0
- package/dist/types/client/types.d.ts +2 -2
- package/dist/types/json-render/catalog.d.ts +3 -3
- package/dist/types/json-render/charts/definitions.d.ts +1 -1
- package/dist/types/json-render/server.d.ts +8 -0
- package/dist/types/mcp/canvas-access.d.ts +10 -3
- package/dist/types/mcp/context-uri-template.d.ts +6 -0
- package/dist/types/server/agent-presence.d.ts +1 -1
- package/dist/types/server/attribution.d.ts +11 -0
- package/dist/types/server/ax-context.d.ts +3 -1
- package/dist/types/server/ax-interaction.d.ts +2 -2
- package/dist/types/server/ax-state.d.ts +13 -3
- package/dist/types/server/backup.d.ts +36 -0
- package/dist/types/server/board-export.d.ts +45 -0
- package/dist/types/server/canvas-db.d.ts +65 -9
- package/dist/types/server/canvas-operations.d.ts +45 -0
- package/dist/types/server/canvas-provenance.d.ts +1 -1
- package/dist/types/server/canvas-schema.d.ts +1 -0
- package/dist/types/server/canvas-state.d.ts +146 -3
- package/dist/types/server/canvas-validation.d.ts +3 -0
- package/dist/types/server/context-brief.d.ts +111 -0
- package/dist/types/server/context-reads.d.ts +55 -0
- package/dist/types/server/document-import.d.ts +66 -0
- package/dist/types/server/html-surface.d.ts +1 -1
- package/dist/types/server/index.d.ts +107 -4
- package/dist/types/server/mutation-history.d.ts +1 -0
- package/dist/types/server/operations/invoker.d.ts +2 -0
- package/dist/types/server/operations/mcp.d.ts +7 -1
- package/dist/types/server/operations/ops/app.d.ts +2 -0
- package/dist/types/server/operations/ops/ax-shared.d.ts +2 -2
- package/dist/types/server/operations/ops/backup.d.ts +2 -0
- package/dist/types/server/operations/ops/boards.d.ts +10 -0
- package/dist/types/server/operations/ops/export.d.ts +2 -0
- package/dist/types/server/operations/ops/imports.d.ts +2 -0
- package/dist/types/server/operations/ops/json-render.d.ts +2 -0
- package/dist/types/server/operations/ops/nodes.d.ts +4 -2
- package/dist/types/server/operations/ops/query.d.ts +2 -0
- package/dist/types/server/operations/registry.d.ts +9 -1
- package/dist/types/server/operations/types.d.ts +1 -1
- package/dist/types/server/server.d.ts +1 -0
- package/dist/types/server/trace-manager.d.ts +7 -0
- package/dist/types/server/web-artifacts.d.ts +3 -0
- package/dist/types/server/webpage-node.d.ts +22 -1
- package/dist/types/server/workbench-auth.d.ts +3 -0
- package/dist/types/shared/boards.d.ts +6 -0
- package/dist/types/shared/tour.d.ts +72 -0
- package/docs/RELEASE.md +5 -0
- package/docs/cli.md +157 -0
- package/docs/http-api.md +277 -6
- package/docs/mcp.md +96 -7
- package/docs/node-types.md +106 -0
- package/docs/plans/plan-010-attributed-ask-answers.md +39 -0
- package/docs/plans/plan-011-context-read-instrumentation.md +54 -0
- package/docs/plans/plan-012-many-boards-stage-1.md +98 -0
- package/docs/plans/plan-013-static-board-export.md +47 -0
- package/docs/plans/plan-014-connected-board-memory.md +140 -0
- package/docs/product-vision-2026-09.md +118 -18
- package/docs/sdk.md +50 -0
- package/package.json +1 -1
- package/skills/pmx-canvas/SKILL.md +62 -5
- package/skills/pmx-canvas/references/full-reference.md +44 -8
- package/skills/pmx-canvas/references/github-copilot-app-adapter.md +3 -0
- package/skills/pmx-canvas/references/installing-pmx-canvas.md +4 -4
- package/skills/pmx-canvas-orchestration/SKILL.md +5 -0
- package/skills/pmx-canvas-testing/SKILL.md +4 -0
- package/src/cli/agent.ts +9 -0
- package/src/cli/commands/ax.ts +13 -0
- package/src/cli/commands/backup.ts +66 -0
- package/src/cli/commands/boards.ts +71 -0
- package/src/cli/commands/export.ts +30 -0
- package/src/cli/commands/nodes.ts +14 -1
- package/src/cli/commands/pins.ts +6 -2
- package/src/cli/commands/pump.ts +3 -0
- package/src/cli/commands/query.ts +33 -7
- package/src/cli/commands/record.ts +218 -0
- package/src/cli/commands/view.ts +7 -5
- package/src/cli/index.ts +21 -9
- package/src/client/App.tsx +28 -4
- package/src/client/canvas/AgentPresenceLayer.tsx +1 -1
- package/src/client/canvas/BoardCopyDialog.tsx +153 -0
- package/src/client/canvas/BoardFolderDialog.tsx +120 -0
- package/src/client/canvas/BoardLinkDialog.tsx +93 -0
- package/src/client/canvas/BoardSwitcher.tsx +149 -0
- package/src/client/canvas/CanvasNode.tsx +8 -0
- package/src/client/canvas/CanvasViewport.tsx +3 -0
- package/src/client/canvas/CommandPalette.tsx +107 -4
- package/src/client/canvas/ContextMenu.tsx +4 -0
- package/src/client/canvas/ExpandedNodeOverlay.tsx +3 -0
- package/src/client/canvas/ExportDialog.tsx +197 -0
- package/src/client/canvas/HomeView.tsx +277 -0
- package/src/client/canvas/IntentLayer.tsx +9 -2
- package/src/client/canvas/Presentation.tsx +108 -0
- package/src/client/canvas/SessionPanel.tsx +28 -1
- package/src/client/canvas/ToolRail.tsx +38 -3
- package/src/client/canvas/TopBar.tsx +49 -7
- package/src/client/canvas/import-files.ts +19 -1
- package/src/client/canvas/kind-colors.ts +1 -0
- package/src/client/canvas/use-node-drag.ts +8 -3
- package/src/client/icons.tsx +2 -0
- package/src/client/mermaid-entry.ts +39 -0
- package/src/client/nodes/AttachmentNode.tsx +148 -0
- package/src/client/nodes/BoardNode.tsx +35 -0
- package/src/client/nodes/FileNode.tsx +6 -0
- package/src/client/nodes/HtmlNode.tsx +12 -13
- package/src/client/nodes/MarkdownNode.tsx +47 -19
- package/src/client/nodes/McpAppNode.tsx +105 -13
- package/src/client/nodes/MermaidNode.tsx +3 -2
- package/src/client/nodes/ax-node-actions.ts +2 -5
- package/src/client/nodes/use-ax-surface-bridge.ts +1 -1
- package/src/client/state/boards-store.ts +167 -0
- package/src/client/state/canvas-store.ts +22 -0
- package/src/client/state/intent-bridge.ts +30 -2
- package/src/client/state/intent-store.ts +5 -1
- package/src/client/state/node-factory.ts +1 -0
- package/src/client/state/presentation.ts +7 -0
- package/src/client/state/session-store.ts +8 -0
- package/src/client/state/sse-bridge.ts +52 -5
- package/src/client/theme/global.css +498 -28
- package/src/client/types.ts +2 -0
- package/src/json-render/server.ts +45 -9
- package/src/mcp/canvas-access.ts +59 -22
- package/src/mcp/context-uri-template.ts +28 -0
- package/src/mcp/server.ts +122 -15
- package/src/server/agent-context.ts +116 -29
- package/src/server/agent-presence.ts +15 -9
- package/src/server/attribution.ts +29 -0
- package/src/server/ax-context.ts +4 -2
- package/src/server/ax-interaction.ts +1 -0
- package/src/server/ax-state-manager.ts +72 -3
- package/src/server/ax-state.ts +41 -0
- package/src/server/backup.ts +178 -0
- package/src/server/board-export.ts +686 -0
- package/src/server/canvas-db.ts +612 -105
- package/src/server/canvas-operations.ts +183 -31
- package/src/server/canvas-provenance.ts +1 -0
- package/src/server/canvas-schema.ts +22 -0
- package/src/server/canvas-state.ts +812 -48
- package/src/server/canvas-validation.ts +9 -1
- package/src/server/context-brief.ts +372 -0
- package/src/server/context-reads.ts +238 -0
- package/src/server/demo-state.json +670 -9
- package/src/server/document-import.ts +223 -0
- package/src/server/html-primitives.ts +199 -91
- package/src/server/html-surface.ts +8 -3
- package/src/server/index.ts +243 -5
- package/src/server/mutation-history.ts +10 -13
- package/src/server/operations/composites.ts +29 -5
- package/src/server/operations/http.ts +9 -3
- package/src/server/operations/index.ts +8 -0
- package/src/server/operations/invoker.ts +14 -3
- package/src/server/operations/mcp.ts +50 -11
- package/src/server/operations/ops/app.ts +15 -0
- package/src/server/operations/ops/ax-read.ts +10 -6
- package/src/server/operations/ops/ax-timeline.ts +75 -0
- package/src/server/operations/ops/backup.ts +134 -0
- package/src/server/operations/ops/batch.ts +20 -4
- package/src/server/operations/ops/boards.ts +351 -0
- package/src/server/operations/ops/export.ts +74 -0
- package/src/server/operations/ops/ext-app.ts +4 -0
- package/src/server/operations/ops/groups.ts +14 -7
- package/src/server/operations/ops/imports.ts +220 -0
- package/src/server/operations/ops/json-render.ts +37 -5
- package/src/server/operations/ops/nodes.ts +99 -21
- package/src/server/operations/ops/query.ts +206 -9
- package/src/server/operations/ops/viewport.ts +32 -0
- package/src/server/operations/registry.ts +129 -31
- package/src/server/operations/types.ts +1 -1
- package/src/server/scope-fence.ts +1 -1
- package/src/server/server.ts +155 -8
- package/src/server/spatial-analysis.ts +9 -17
- package/src/server/trace-manager.ts +19 -0
- package/src/server/web-artifacts.ts +11 -3
- package/src/server/webpage-node.ts +173 -13
- package/src/server/workbench-auth.ts +11 -0
- package/src/server/workboard.ts +3 -3
- package/src/shared/boards.ts +18 -0
- package/src/shared/node-sizes.ts +1 -0
- package/src/shared/tour.ts +87 -0
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Library backup (plan 012): the whole workspace database — every board, its
|
|
3
|
+
* snapshots and blobs — copied with `VACUUM INTO`, which is safe while the
|
|
4
|
+
* server runs. Backups are plain files in a folder the human chooses; the
|
|
5
|
+
* schedule lives in the database and runs inside the server.
|
|
6
|
+
*/
|
|
7
|
+
import { Database } from 'bun:sqlite';
|
|
8
|
+
import { existsSync, mkdirSync, readdirSync, rmSync, statSync } from 'node:fs';
|
|
9
|
+
import { dirname, isAbsolute, join, resolve } from 'node:path';
|
|
10
|
+
import { canvasState, PMX_CANVAS_DIR } from './canvas-state.js';
|
|
11
|
+
|
|
12
|
+
const FILE_PATTERN = /^canvas-(\d{8}T\d{9}Z)(?:-(\d+))?\.db$/;
|
|
13
|
+
const DEFAULT_KEEP = 14;
|
|
14
|
+
const MIN_INTERVAL_MS = 60_000;
|
|
15
|
+
const CHECK_INTERVAL_MS = 60_000;
|
|
16
|
+
|
|
17
|
+
const META = {
|
|
18
|
+
folder: 'backup_folder',
|
|
19
|
+
everyMs: 'backup_every_ms',
|
|
20
|
+
keep: 'backup_keep',
|
|
21
|
+
lastAt: 'backup_last_at',
|
|
22
|
+
lastPath: 'backup_last_path',
|
|
23
|
+
} as const;
|
|
24
|
+
|
|
25
|
+
export interface BackupFile {
|
|
26
|
+
path: string;
|
|
27
|
+
bytes: number;
|
|
28
|
+
at: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface BackupStatus {
|
|
32
|
+
folder: string;
|
|
33
|
+
keep: number;
|
|
34
|
+
everyMs: number | null;
|
|
35
|
+
lastAt: string | null;
|
|
36
|
+
lastPath: string | null;
|
|
37
|
+
backups: BackupFile[];
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** "90s", "30m", "24h", "7d" → milliseconds. */
|
|
41
|
+
export function parseBackupInterval(value: string): number | null {
|
|
42
|
+
const match = value.trim().match(/^(\d+)\s*([smhd])$/i);
|
|
43
|
+
if (!match) return null;
|
|
44
|
+
const unit = { s: 1_000, m: 60_000, h: 3_600_000, d: 86_400_000 }[match[2].toLowerCase() as 's' | 'm' | 'h' | 'd'];
|
|
45
|
+
return Number(match[1]) * unit;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function resolveFolder(folder: string): string {
|
|
49
|
+
return isAbsolute(folder) ? folder : resolve(canvasState.workspaceRoot, folder);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** Beside the database: `.pmx-canvas/backups` unless PMX_CANVAS_DB_PATH moved it. */
|
|
53
|
+
function defaultFolder(): string {
|
|
54
|
+
const dbPath = canvasState.databasePath;
|
|
55
|
+
return resolve(
|
|
56
|
+
dbPath ? join(dirname(dbPath), 'backups') : join(canvasState.workspaceRoot, PMX_CANVAS_DIR, 'backups'),
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function listBackupFiles(folder: string): BackupFile[] {
|
|
61
|
+
if (!existsSync(folder)) return [];
|
|
62
|
+
// Newest first by the name's timestamp, then its same-millisecond counter.
|
|
63
|
+
const order = (name: string): string => {
|
|
64
|
+
const match = name.match(FILE_PATTERN);
|
|
65
|
+
return `${match?.[1] ?? ''}-${(match?.[2] ?? '1').padStart(6, '0')}`;
|
|
66
|
+
};
|
|
67
|
+
return readdirSync(folder)
|
|
68
|
+
.filter((name) => FILE_PATTERN.test(name))
|
|
69
|
+
.sort((a, b) => order(b).localeCompare(order(a)))
|
|
70
|
+
.map((name) => {
|
|
71
|
+
const path = join(folder, name);
|
|
72
|
+
const stat = statSync(path);
|
|
73
|
+
return { path, bytes: stat.size, at: stat.mtime.toISOString() };
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function getBackupStatus(): BackupStatus {
|
|
78
|
+
const everyMs = Number(canvasState.readWorkspaceMeta(META.everyMs));
|
|
79
|
+
const folder = canvasState.readWorkspaceMeta(META.folder) ?? defaultFolder();
|
|
80
|
+
return {
|
|
81
|
+
folder,
|
|
82
|
+
keep: Number(canvasState.readWorkspaceMeta(META.keep)) || DEFAULT_KEEP,
|
|
83
|
+
everyMs: everyMs > 0 ? everyMs : null,
|
|
84
|
+
lastAt: canvasState.readWorkspaceMeta(META.lastAt),
|
|
85
|
+
lastPath: canvasState.readWorkspaceMeta(META.lastPath),
|
|
86
|
+
backups: listBackupFiles(folder),
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** Back up now; keeps the newest `keep` backups in the folder and removes older ones. */
|
|
91
|
+
export function runBackup(options: { folder?: string; keep?: number } = {}): {
|
|
92
|
+
backup: BackupFile;
|
|
93
|
+
removed: string[];
|
|
94
|
+
} {
|
|
95
|
+
const status = getBackupStatus();
|
|
96
|
+
const folder = options.folder ? resolveFolder(options.folder) : status.folder;
|
|
97
|
+
const keep = options.keep ?? status.keep;
|
|
98
|
+
mkdirSync(folder, { recursive: true });
|
|
99
|
+
|
|
100
|
+
const stamp = new Date().toISOString().replace(/[-:.]/g, '');
|
|
101
|
+
let path = join(folder, `canvas-${stamp}.db`);
|
|
102
|
+
for (let n = 2; existsSync(path); n++) path = join(folder, `canvas-${stamp}-${n}.db`);
|
|
103
|
+
canvasState.backupDatabaseTo(path);
|
|
104
|
+
|
|
105
|
+
const removed = listBackupFiles(folder)
|
|
106
|
+
.slice(keep)
|
|
107
|
+
.map((file) => file.path);
|
|
108
|
+
for (const file of removed) rmSync(file, { force: true });
|
|
109
|
+
|
|
110
|
+
const at = new Date().toISOString();
|
|
111
|
+
canvasState.writeWorkspaceMeta(META.lastAt, at);
|
|
112
|
+
canvasState.writeWorkspaceMeta(META.lastPath, path);
|
|
113
|
+
return { backup: { path, bytes: statSync(path).size, at }, removed };
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/** Set the schedule (`everyMs` null turns it off). Folder and keep also apply to manual backups. */
|
|
117
|
+
export function setBackupSchedule(options: { everyMs: number | null; folder?: string; keep?: number }): BackupStatus {
|
|
118
|
+
if (options.everyMs !== null && options.everyMs < MIN_INTERVAL_MS) {
|
|
119
|
+
throw new Error('Back up at most once a minute.');
|
|
120
|
+
}
|
|
121
|
+
canvasState.writeWorkspaceMeta(META.everyMs, options.everyMs === null ? null : String(options.everyMs));
|
|
122
|
+
if (options.folder) canvasState.writeWorkspaceMeta(META.folder, resolveFolder(options.folder));
|
|
123
|
+
if (options.keep) canvasState.writeWorkspaceMeta(META.keep, String(options.keep));
|
|
124
|
+
return getBackupStatus();
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/** Runs a scheduled backup when one is due. Returns whether it ran. */
|
|
128
|
+
export function runDueBackup(now = Date.now()): boolean {
|
|
129
|
+
if (!canvasState.databasePath) return false;
|
|
130
|
+
const { everyMs, lastAt } = getBackupStatus();
|
|
131
|
+
if (!everyMs) return false;
|
|
132
|
+
if (lastAt && now - Date.parse(lastAt) < everyMs) return false;
|
|
133
|
+
runBackup();
|
|
134
|
+
return true;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/** Rejects anything that is not a canvas database before the live file is touched. */
|
|
138
|
+
export function assertRestorableBackup(file: string): void {
|
|
139
|
+
if (!existsSync(file) || !statSync(file).isFile()) throw new Error(`No backup file at ${file}.`);
|
|
140
|
+
if (resolve(file) === resolve(canvasState.databasePath ?? '')) {
|
|
141
|
+
throw new Error('That is the live database, not a backup.');
|
|
142
|
+
}
|
|
143
|
+
let tables: string[] = [];
|
|
144
|
+
try {
|
|
145
|
+
const db = new Database(file, { readonly: true });
|
|
146
|
+
tables = db
|
|
147
|
+
.query<{ name: string }, []>("SELECT name FROM sqlite_master WHERE type = 'table'")
|
|
148
|
+
.all()
|
|
149
|
+
.map((row) => row.name);
|
|
150
|
+
db.close();
|
|
151
|
+
} catch {
|
|
152
|
+
throw new Error(`${file} is not a SQLite database.`);
|
|
153
|
+
}
|
|
154
|
+
if (!tables.includes('meta') || !tables.includes('nodes')) {
|
|
155
|
+
throw new Error(`${file} is not a PMX Canvas database.`);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
let timer: ReturnType<typeof setInterval> | null = null;
|
|
160
|
+
|
|
161
|
+
export function startBackupScheduler(): void {
|
|
162
|
+
stopBackupScheduler();
|
|
163
|
+
const check = () => {
|
|
164
|
+
try {
|
|
165
|
+
runDueBackup();
|
|
166
|
+
} catch (error) {
|
|
167
|
+
console.warn('[backup] scheduled backup failed', error);
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
check();
|
|
171
|
+
timer = setInterval(check, CHECK_INTERVAL_MS);
|
|
172
|
+
timer.unref?.();
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export function stopBackupScheduler(): void {
|
|
176
|
+
if (timer) clearInterval(timer);
|
|
177
|
+
timer = null;
|
|
178
|
+
}
|