pmx-canvas 0.3.2 → 0.3.4
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/CHANGELOG.md +69 -0
- package/Readme.md +2 -1
- package/dist/canvas/index.js +85 -83
- package/dist/json-render/index.js +47 -47
- package/dist/types/cli/agent.d.ts +13 -8
- package/dist/types/cli/commands/apps.d.ts +1 -0
- package/dist/types/cli/commands/ax.d.ts +1 -0
- package/dist/types/cli/commands/copilot.d.ts +1 -0
- package/dist/types/cli/commands/edges.d.ts +1 -0
- package/dist/types/cli/commands/groups.d.ts +1 -0
- package/dist/types/cli/commands/history.d.ts +1 -0
- package/dist/types/cli/commands/nodes.d.ts +1 -0
- package/dist/types/cli/commands/pins.d.ts +1 -0
- package/dist/types/cli/commands/query.d.ts +1 -0
- package/dist/types/cli/commands/view.d.ts +1 -0
- package/dist/types/cli/commands/webview.d.ts +1 -0
- package/dist/types/cli/daemon.d.ts +17 -0
- package/dist/types/cli/shared.d.ts +73 -0
- package/dist/types/client/canvas/ExpandedNodeOverlay.d.ts +1 -1
- package/dist/types/client/nodes/ExtAppFrame.d.ts +13 -0
- package/dist/types/client/nodes/HtmlNode.d.ts +1 -1
- package/dist/types/client/nodes/McpAppNode.d.ts +1 -1
- package/dist/types/client/nodes/use-ax-surface-bridge.d.ts +20 -0
- package/dist/types/server/canvas-operations.d.ts +2 -0
- package/dist/types/server/diagram-presets.d.ts +8 -0
- package/dist/types/server/operations/composites.d.ts +0 -9
- package/dist/types/shared/ax-surface-protocol.d.ts +18 -0
- package/docs/RELEASE.md +11 -0
- package/docs/bun-webview-integration.md +5 -0
- package/docs/environment.md +48 -0
- package/docs/node-types.md +5 -0
- package/docs/plans/plan-009-tech-debt-backlog.md +27 -9
- package/docs/screenshot.png +0 -0
- package/package.json +8 -5
- package/skills/pmx-canvas/SKILL.md +11 -8
- package/skills/pmx-canvas/evals/evals.json +5 -5
- package/skills/pmx-canvas/references/full-reference.md +8 -1
- package/src/cli/agent.ts +22 -3512
- package/src/cli/commands/apps.ts +165 -0
- package/src/cli/commands/ax.ts +604 -0
- package/src/cli/commands/copilot.ts +43 -0
- package/src/cli/commands/edges.ts +73 -0
- package/src/cli/commands/groups.ts +81 -0
- package/src/cli/commands/history.ts +220 -0
- package/src/cli/commands/nodes.ts +954 -0
- package/src/cli/commands/pins.ts +42 -0
- package/src/cli/commands/query.ts +223 -0
- package/src/cli/commands/view.ts +186 -0
- package/src/cli/commands/webview.ts +227 -0
- package/src/cli/daemon.ts +39 -7
- package/src/cli/shared.ts +860 -0
- package/src/client/canvas/ExpandedNodeOverlay.tsx +3 -2
- package/src/client/nodes/ExtAppFrame.tsx +166 -52
- package/src/client/nodes/HtmlNode.tsx +8 -51
- package/src/client/nodes/McpAppNode.tsx +12 -47
- package/src/client/nodes/use-ax-surface-bridge.ts +71 -0
- package/src/json-render/renderer/index.tsx +3 -2
- package/src/mcp/server.ts +0 -47
- package/src/server/bundled-skills.ts +5 -1
- package/src/server/canvas-operations.ts +8 -0
- package/src/server/diagram-presets.ts +15 -1
- package/src/server/html-surface.ts +11 -6
- package/src/server/operations/composites.ts +1 -13
- package/src/server/operations/invoker.ts +10 -3
- package/src/server/operations/mcp.ts +51 -1
- package/src/server/operations/ops/app.ts +5 -0
- package/src/server/server.ts +61 -3
- package/src/shared/ax-surface-protocol.ts +22 -0
- package/dist/types/client/canvas/ContextPinHud.d.ts +0 -1
- package/src/client/canvas/ContextPinHud.tsx +0 -17
|
@@ -10,12 +10,17 @@
|
|
|
10
10
|
* - Idempotent operations where possible
|
|
11
11
|
* - --yes for destructive actions, --dry-run for preview
|
|
12
12
|
*/
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
13
|
+
import { extractGlobalTargetFlags } from './shared.js';
|
|
14
|
+
import './commands/nodes.js';
|
|
15
|
+
import './commands/edges.js';
|
|
16
|
+
import './commands/groups.js';
|
|
17
|
+
import './commands/view.js';
|
|
18
|
+
import './commands/query.js';
|
|
19
|
+
import './commands/pins.js';
|
|
20
|
+
import './commands/history.js';
|
|
21
|
+
import './commands/ax.js';
|
|
22
|
+
import './commands/webview.js';
|
|
23
|
+
import './commands/apps.js';
|
|
24
|
+
import './commands/copilot.js';
|
|
25
|
+
export { extractGlobalTargetFlags };
|
|
21
26
|
export declare function runAgentCli(rawArgs: string[]): Promise<void>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -17,6 +17,8 @@ export declare function removePidFile(path: string): void;
|
|
|
17
17
|
export interface HealthStatus {
|
|
18
18
|
responsive: boolean;
|
|
19
19
|
workspace: string | null;
|
|
20
|
+
/** The serving process's own pid, self-reported by /health (0.3.3+ servers). */
|
|
21
|
+
pid: number | null;
|
|
20
22
|
}
|
|
21
23
|
export declare function readHealthStatus(url: string): Promise<HealthStatus>;
|
|
22
24
|
export declare function isHealthy(url: string): Promise<boolean>;
|
|
@@ -65,6 +67,21 @@ export declare function startDaemonMode(options: DaemonPaths & {
|
|
|
65
67
|
waitMs: number;
|
|
66
68
|
entry: string;
|
|
67
69
|
}): Promise<void>;
|
|
70
|
+
/**
|
|
71
|
+
* Resolve the pid story `serve status` reports. The AUTHORITATIVE pid is the
|
|
72
|
+
* serving process's self-reported /health pid: the pid file can go stale when
|
|
73
|
+
* something other than `serve --daemon` (re)spawned the server on this port —
|
|
74
|
+
* e.g. a host adapter respawn (0.3.2 report Finding P). Reporting the dead
|
|
75
|
+
* file pid as `pid` while `running` was true made agents read a contradiction
|
|
76
|
+
* (`running: true, pidRunning: false`); report the real listener instead and
|
|
77
|
+
* mark the file explicitly stale. Pre-0.3.3 servers don't self-report a pid —
|
|
78
|
+
* for those, fall back to the pid-file view unchanged.
|
|
79
|
+
*/
|
|
80
|
+
export declare function resolveDaemonPidView(pidFilePid: number | null, pidFilePidRunning: boolean, health: HealthStatus): {
|
|
81
|
+
pid: number | null;
|
|
82
|
+
pidRunning: boolean;
|
|
83
|
+
pidFileStale: boolean;
|
|
84
|
+
};
|
|
68
85
|
export declare function showServeStatus(options: DaemonPaths & {
|
|
69
86
|
entry: string;
|
|
70
87
|
}): Promise<void>;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared core for the agent-native CLI.
|
|
3
|
+
*
|
|
4
|
+
* Holds the command registry (COMMANDS + cmd()), flag parsing, output/die,
|
|
5
|
+
* target resolution (getBaseUrl + global --port/--server-url handling), the
|
|
6
|
+
* operation invoker, and every helper used by two or more command domains.
|
|
7
|
+
* Command modules in src/cli/commands/ import from here only — this module
|
|
8
|
+
* must never import from agent.ts or any command module (cycle-free).
|
|
9
|
+
*/
|
|
10
|
+
declare function getBaseUrl(): string;
|
|
11
|
+
/**
|
|
12
|
+
* Extract the global `--port <n>` / `--server-url <url>` flags (any position,
|
|
13
|
+
* `=` or space-separated value) and set the invocation's target override.
|
|
14
|
+
* Returns the remaining args for command dispatch. Invalid values are a loud
|
|
15
|
+
* `die` — never a silent fallback to the default port. `--server-url` wins
|
|
16
|
+
* over `--port` when both are given.
|
|
17
|
+
*/
|
|
18
|
+
export declare function extractGlobalTargetFlags(args: string[]): string[];
|
|
19
|
+
declare function die(message: string, hint?: string): never;
|
|
20
|
+
declare function output(data: unknown): void;
|
|
21
|
+
declare function invokeOperation(name: string, input: Record<string, unknown>): Promise<unknown>;
|
|
22
|
+
declare function parseFlags(args: string[]): {
|
|
23
|
+
positional: string[];
|
|
24
|
+
flags: Record<string, string | true>;
|
|
25
|
+
};
|
|
26
|
+
declare function requireFlag(flags: Record<string, string | true>, name: string, hint: string): string;
|
|
27
|
+
declare function getStringFlag(flags: Record<string, string | true>, ...names: string[]): string | undefined;
|
|
28
|
+
declare function optionalNumberFlag(flags: Record<string, string | true>, name: string, hint: string): number | undefined;
|
|
29
|
+
/**
|
|
30
|
+
* AX `source` for a CLI-originated action. Defaults to `cli`, but honors an
|
|
31
|
+
* explicit `--source <label>` so an adapterless agent using the CLI as a fallback
|
|
32
|
+
* transport (e.g. `--source codex`) attributes its actions correctly — keeping
|
|
33
|
+
* loop-safety (a consumer never gets back its own steering) accurate (report #69).
|
|
34
|
+
*/
|
|
35
|
+
declare function resolveAxSource(flags: Record<string, string | true>): string;
|
|
36
|
+
declare function optionalFiniteFlag(flags: Record<string, string | true>, name: string, hint: string): number | undefined;
|
|
37
|
+
declare function optionalPositiveFiniteFlag(flags: Record<string, string | true>, name: string, hint: string): number | undefined;
|
|
38
|
+
declare function optionalPositiveFiniteFlagWithAliases(flags: Record<string, string | true>, hint: string, ...names: string[]): number | undefined;
|
|
39
|
+
declare function optionalBooleanFlag(flags: Record<string, string | true>, name: string, hint: string): boolean | undefined;
|
|
40
|
+
declare function applyStrictSizeFlags(body: Record<string, unknown>, flags: Record<string, string | true>): void;
|
|
41
|
+
declare function isRecord(value: unknown): value is Record<string, unknown>;
|
|
42
|
+
declare function parseJsonValue(raw: string, label: string, hint: string): unknown;
|
|
43
|
+
declare function readOptionalTextInput(flags: Record<string, string | true>, options: {
|
|
44
|
+
fileFlags?: string[];
|
|
45
|
+
valueFlags?: string[];
|
|
46
|
+
allowStdin?: boolean;
|
|
47
|
+
label: string;
|
|
48
|
+
hint: string;
|
|
49
|
+
}): Promise<string | undefined>;
|
|
50
|
+
declare function applyCommonGeometryFlags(body: Record<string, unknown>, flags: Record<string, string | true>, hints: {
|
|
51
|
+
x: string;
|
|
52
|
+
y: string;
|
|
53
|
+
width: string;
|
|
54
|
+
height: string;
|
|
55
|
+
}): void;
|
|
56
|
+
declare function buildJsonRenderRequestBody(flags: Record<string, string | true>): Promise<Record<string, unknown>>;
|
|
57
|
+
declare function buildHtmlPrimitiveRequestBody(flags: Record<string, string | true>): Promise<Record<string, unknown>>;
|
|
58
|
+
declare function buildGraphRequestBody(flags: Record<string, string | true>, options?: {
|
|
59
|
+
requireData?: boolean;
|
|
60
|
+
allowStdin?: boolean;
|
|
61
|
+
}): Promise<Record<string, unknown>>;
|
|
62
|
+
declare function runWebArtifactBuildCommand(flags: Record<string, string | true>): Promise<void>;
|
|
63
|
+
declare const COMMANDS: Record<string, {
|
|
64
|
+
run: (args: string[]) => Promise<void>;
|
|
65
|
+
help: string;
|
|
66
|
+
examples: string[];
|
|
67
|
+
}>;
|
|
68
|
+
declare const RESOURCE_COMMAND_ALIASES: Record<string, Record<string, string>>;
|
|
69
|
+
declare const RESOURCE_SUBCOMMAND_HINTS: Record<string, Record<string, string>>;
|
|
70
|
+
declare function cmd(name: string, help: string, examples: string[], run: (args: string[]) => Promise<void>): void;
|
|
71
|
+
declare function showCommandHelp(name: string): void;
|
|
72
|
+
declare function readStdin(): Promise<string>;
|
|
73
|
+
export { COMMANDS, RESOURCE_COMMAND_ALIASES, RESOURCE_SUBCOMMAND_HINTS, applyCommonGeometryFlags, applyStrictSizeFlags, buildGraphRequestBody, buildHtmlPrimitiveRequestBody, buildJsonRenderRequestBody, cmd, die, getBaseUrl, getStringFlag, invokeOperation, isRecord, optionalBooleanFlag, optionalFiniteFlag, optionalNumberFlag, optionalPositiveFiniteFlag, optionalPositiveFiniteFlagWithAliases, output, parseFlags, parseJsonValue, readOptionalTextInput, readStdin, requireFlag, resolveAxSource, runWebArtifactBuildCommand, showCommandHelp, };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare function ExpandedNodeOverlay(): import("preact/
|
|
1
|
+
export declare function ExpandedNodeOverlay(): import("preact/jsx-runtime").JSX.Element | null;
|
|
@@ -38,6 +38,19 @@ export declare function resolveExtAppDisplayModeRequest(requestedMode: DisplayMo
|
|
|
38
38
|
export declare function sendExtAppBootstrapState(bridge: ExtAppBridgeNotifications, toolInput: Record<string, unknown>, toolResult: CallToolResult | undefined): Promise<void>;
|
|
39
39
|
export declare function resolveExtAppSandbox(value: unknown): string;
|
|
40
40
|
export declare function buildExtAppAxBridgeScript(axToken: string, nodeId: string): string;
|
|
41
|
+
/**
|
|
42
|
+
* Boot beacon injected into EVERY ext-app document (not gated on AX): posts one
|
|
43
|
+
* parent message the moment the iframe's scripts execute. This is the liveness
|
|
44
|
+
* signal the WebKit never-booted watchdog keys on — it distinguishes an app
|
|
45
|
+
* that is alive but boots via the 1200ms bootstrap fallback (e.g. a non-SDK
|
|
46
|
+
* widget that never sends ui/notifications/initialized) from a dead window
|
|
47
|
+
* whose scripts never ran. Without it, the watchdog treated every
|
|
48
|
+
* fallback-booted app as never-booted and remounted it up to the attempt cap —
|
|
49
|
+
* a reboot/flicker loop for exactly the apps the fallback exists to support
|
|
50
|
+
* (0.3.2 pre-release review). The token scopes the message to this component;
|
|
51
|
+
* the host also checks event.source against the live iframe.
|
|
52
|
+
*/
|
|
53
|
+
export declare function buildExtAppBootBeaconScript(frameToken: string, nodeId: string): string;
|
|
41
54
|
export declare function injectExtAppAxBridgeScript(html: string, axBridgeScript: string): string;
|
|
42
55
|
export declare function resolveExtAppContainerDimensions(target: ExtAppHostDimensionsTarget | null | undefined, fallback: {
|
|
43
56
|
width: number;
|
|
@@ -3,4 +3,4 @@ export declare function isSameOriginFrameDocumentUrl(url: string, origin?: strin
|
|
|
3
3
|
export declare function McpAppNode({ node, expanded }: {
|
|
4
4
|
node: CanvasNodeState;
|
|
5
5
|
expanded?: boolean;
|
|
6
|
-
}): import("preact/
|
|
6
|
+
}): import("preact/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { RefObject } from 'preact';
|
|
2
|
+
/** Sandboxed surfaces that emit through this bridge (clamped to their own node server-side). */
|
|
3
|
+
export type AxSurfaceBridgeSurface = 'json-render' | 'html-node' | 'mcp-app';
|
|
4
|
+
/**
|
|
5
|
+
* The sandboxed-surface AX trust boundary, single-sited (plan-009 M2; the
|
|
6
|
+
* listener was copy-pasted across HtmlNode, McpAppNode, and ExtAppFrame — a
|
|
7
|
+
* prior near-miss lived exactly in that duplication). Receives a
|
|
8
|
+
* `window.PMX_AX.emit(...)` message from THIS node's iframe, validates the
|
|
9
|
+
* event source against the live contentWindow plus the per-mount nonce token
|
|
10
|
+
* and node id, submits the interaction through the capability-gated endpoint
|
|
11
|
+
* (the server re-validates capabilities and clamps sandboxed surfaces to their
|
|
12
|
+
* own node), and acks the result back so the surface can self-confirm (#55).
|
|
13
|
+
*/
|
|
14
|
+
export declare function useAxSurfaceBridge(options: {
|
|
15
|
+
enabled: boolean;
|
|
16
|
+
token: string;
|
|
17
|
+
nodeId: string;
|
|
18
|
+
sourceSurface: AxSurfaceBridgeSurface;
|
|
19
|
+
iframeRef: RefObject<HTMLIFrameElement>;
|
|
20
|
+
}): void;
|
|
@@ -128,6 +128,8 @@ export declare function validateCanvasNodePatch(patch: {
|
|
|
128
128
|
export declare function mergeTraceNodeDataFields(base: Record<string, unknown>, input: Record<string, unknown>): Record<string, unknown>;
|
|
129
129
|
export declare function hasTraceNodeDataFields(input: Record<string, unknown>): boolean;
|
|
130
130
|
export declare function scheduleCodeGraphRecompute(onComplete?: () => void): void;
|
|
131
|
+
/** Clear a pending recompute so the timer cannot outlive its server (start/stop cycles, tests). */
|
|
132
|
+
export declare function cancelCodeGraphRecompute(): void;
|
|
131
133
|
/**
|
|
132
134
|
* Resolve an html-node `html` field that may be a path to a local .html/.htm file.
|
|
133
135
|
*
|
|
@@ -7,6 +7,14 @@ export declare const EXCALIDRAW_SAVE_CHECKPOINT_TOOL = "save_checkpoint";
|
|
|
7
7
|
export declare const EXCALIDRAW_READ_CHECKPOINT_TOOL = "read_checkpoint";
|
|
8
8
|
export declare const DEFAULT_EXCALIDRAW_ELEMENTS: ReadonlyArray<Record<string, unknown>>;
|
|
9
9
|
export declare const EXCALIDRAW_MCP_TRANSPORT: ExternalMcpTransportConfig;
|
|
10
|
+
/**
|
|
11
|
+
* The diagram preset targets the hosted Excalidraw MCP server. Tests set
|
|
12
|
+
* PMX_CANVAS_EXCALIDRAW_MCP_COMMAND (a stdio command line, e.g.
|
|
13
|
+
* "bun run tests/fixtures/mcp-app-fixture.ts") so the unit suite never does a
|
|
14
|
+
* real network round-trip — the hosted dependency made diagram tests time out
|
|
15
|
+
* under parallel load (plan-009 M8).
|
|
16
|
+
*/
|
|
17
|
+
export declare function resolveExcalidrawTransport(): ExternalMcpTransportConfig;
|
|
10
18
|
export interface DiagramPresetOpenInput {
|
|
11
19
|
elements: unknown;
|
|
12
20
|
nodeId?: string;
|
|
@@ -95,15 +95,6 @@ export interface CompositeToolDefinition {
|
|
|
95
95
|
kind: string;
|
|
96
96
|
action: string;
|
|
97
97
|
}) => string | undefined;
|
|
98
|
-
/**
|
|
99
|
-
* Two-discriminator extension: human-readable `(kind, action)` for a member op
|
|
100
|
-
* (the inverse of `resolveOp`), used to build that op's deprecation note. The
|
|
101
|
-
* `kind` field-collision is resolved here (see `gateFieldRemap`).
|
|
102
|
-
*/
|
|
103
|
-
describeOp?: (opName: string) => {
|
|
104
|
-
kind: string;
|
|
105
|
-
action: string;
|
|
106
|
-
} | undefined;
|
|
107
98
|
/**
|
|
108
99
|
* Field-name remap applied to the composite's advertised schema and undone at
|
|
109
100
|
* dispatch. Resolves a collision between a discriminator name and a member-op
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wire-protocol source tags for sandboxed-surface ↔ canvas postMessage traffic
|
|
3
|
+
* (plan-009 M2): one module owns the strings that used to be spread across the
|
|
4
|
+
* client node components, the server-side surface script generators, the
|
|
5
|
+
* ext-app script builders, and the json-render viewer. These are the trust
|
|
6
|
+
* boundary between opaque-origin iframes and the canvas — a typo'd tag fails
|
|
7
|
+
* SILENTLY (the listener just ignores the message), so single-siting them is a
|
|
8
|
+
* correctness measure, not cosmetics. The values are a persisted wire contract
|
|
9
|
+
* with already-generated surface documents: never change them.
|
|
10
|
+
*/
|
|
11
|
+
/** A sandboxed surface emitting an AX interaction to the parent canvas. */
|
|
12
|
+
export declare const AX_SURFACE_EMIT_SOURCE = "pmx-canvas-ax";
|
|
13
|
+
/** The parent canvas acking an AX interaction back to the emitting surface (#55). */
|
|
14
|
+
export declare const AX_SURFACE_ACK_SOURCE = "pmx-canvas-ax-ack";
|
|
15
|
+
/** Parent→surface pushes (theme updates, live AX state) into html/viewer iframes. */
|
|
16
|
+
export declare const HTML_SURFACE_PUSH_SOURCE = "pmx-canvas-html-node";
|
|
17
|
+
/** Ext-app boot beacon: the iframe's scripts executed (WebKit watchdog liveness). */
|
|
18
|
+
export declare const EXT_APP_BOOT_BEACON_SOURCE = "pmx-canvas-ext-app-alive";
|
package/docs/RELEASE.md
CHANGED
|
@@ -124,6 +124,17 @@ If this is the first release from your machine, run `bunx npm login`
|
|
|
124
124
|
once so Bun can reuse your npm credentials. CI does not need this —
|
|
125
125
|
it uses `NPM_TOKEN` directly.
|
|
126
126
|
|
|
127
|
+
## Sync consumer skill mirrors
|
|
128
|
+
|
|
129
|
+
Every release since 0.3.0 has been retested against **stale** skill mirrors:
|
|
130
|
+
consumer workspaces (e.g. `personalclaude`) carry copies of the pmx-canvas
|
|
131
|
+
skill under `.github/`, `.codex/`, `.claude/`, and `.agents/`, and testers
|
|
132
|
+
repeatedly had to sync them mid-cycle before results were trustworthy. After
|
|
133
|
+
publishing, update the installed package in the consumer workspace and re-sync
|
|
134
|
+
its four mirrors from
|
|
135
|
+
`<global npm root>/pmx-canvas/skills/pmx-canvas/` so the first test pass runs
|
|
136
|
+
against the released skill, not the previous version's guidance.
|
|
137
|
+
|
|
127
138
|
## GitHub release
|
|
128
139
|
|
|
129
140
|
After `npm publish` succeeds:
|
|
@@ -173,6 +173,11 @@ PMX Canvas follows Bun's current stable backend reality.
|
|
|
173
173
|
- required on Linux and Windows
|
|
174
174
|
- also supported on macOS
|
|
175
175
|
- preferred when backend consistency or future CDP-based tooling matters
|
|
176
|
+
- can trigger a native "Allow remote debugging?" dialog on first start in some
|
|
177
|
+
hosts — approve it promptly or the start times out with `ok:false`. Raise
|
|
178
|
+
`PMX_CANVAS_WEBVIEW_TIMEOUT_MS` (e.g. `30000`) when the prompt or a slow
|
|
179
|
+
Chrome launch is expected, and retry once before classifying a timeout as a
|
|
180
|
+
failure.
|
|
176
181
|
|
|
177
182
|
### Current default behavior
|
|
178
183
|
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# Environment variables
|
|
2
|
+
|
|
3
|
+
Every `PMX_*` variable the server, CLI, MCP transport, and build read. All are
|
|
4
|
+
optional; defaults are listed.
|
|
5
|
+
|
|
6
|
+
## Ports and workspace targeting
|
|
7
|
+
|
|
8
|
+
| Variable | Read by | Effect |
|
|
9
|
+
|---|---|---|
|
|
10
|
+
| `PMX_WEB_CANVAS_PORT` | server, CLI `serve` | Preferred server port (default `4313`). CLI `--port` wins over it. |
|
|
11
|
+
| `PMX_CANVAS_PORT` | agent CLI, MCP transport | Client-side default target port for CLI commands and the `--mcp` same-workspace lookup. Does not change what the server binds. |
|
|
12
|
+
| `PMX_CANVAS_URL` | agent CLI, MCP transport | Full base URL target (wins over `PMX_CANVAS_PORT`). |
|
|
13
|
+
| `PMX_CANVAS_WORKSPACE_ROOT` | server, MCP transport | Pins the workspace root for daemon binds and the MCP same-workspace lookup, overriding the launch cwd. Set it when a host spawns `--mcp` from an incidental dir. |
|
|
14
|
+
| `PMX_CANVAS_ALLOW_WORKSPACE_SPLIT` | MCP transport | `1` forces a separate canvas instead of attaching to a different-workspace daemon on the preferred port. |
|
|
15
|
+
|
|
16
|
+
## Persistence
|
|
17
|
+
|
|
18
|
+
| Variable | Read by | Effect |
|
|
19
|
+
|---|---|---|
|
|
20
|
+
| `PMX_CANVAS_DB_PATH` | server | Overrides the SQLite path (default `.pmx-canvas/canvas.db`). |
|
|
21
|
+
| `PMX_CANVAS_STATE_FILE` | server | Legacy JSON state path (back-compat; a `.db` value is treated as the DB path). |
|
|
22
|
+
| `PMX_CANVAS_BLOB_THRESHOLD_BYTES` | server | Node payloads above this many JSON bytes are stored as blobs (default `2048`). |
|
|
23
|
+
|
|
24
|
+
## Server behavior
|
|
25
|
+
|
|
26
|
+
| Variable | Read by | Effect |
|
|
27
|
+
|---|---|---|
|
|
28
|
+
| `PMX_CANVAS_THEME` | server | Startup theme: `dark` (default), `light`, `high-contrast`. |
|
|
29
|
+
| `PMX_CANVAS_DISABLE_BROWSER_OPEN` | server | `1` suppresses auto-opening the browser (used by tests/CI). |
|
|
30
|
+
| `PMX_CANVAS_DIST` | server | Explicit client bundle directory to serve instead of the packaged `dist/canvas`. |
|
|
31
|
+
| `PMX_CANVAS_WEBVIEW_TIMEOUT_MS` | server | Startup timeout for the Bun.WebView automation session. |
|
|
32
|
+
|
|
33
|
+
## MCP app host (security-relevant)
|
|
34
|
+
|
|
35
|
+
| Variable | Read by | Effect |
|
|
36
|
+
|---|---|---|
|
|
37
|
+
| `PMX_MCP_APP_HOST_MODE` | server | Gates which hosted MCP app servers may be opened. |
|
|
38
|
+
| `PMX_MCP_APP_HOST_ALLOWLIST` | server | Additional allowed MCP app server origins. |
|
|
39
|
+
| `PMX_MCP_APP_HOST_STATE_FILE` | server | Overrides the app-host session state file. |
|
|
40
|
+
|
|
41
|
+
## Build and diagnostics
|
|
42
|
+
|
|
43
|
+
| Variable | Read by | Effect |
|
|
44
|
+
|---|---|---|
|
|
45
|
+
| `PMX_CANVAS_JSON_RENDER_DEVTOOLS` | server | `1` (plus `?devtools=1` on the viewer URL) enables the json-render devtools overlay. |
|
|
46
|
+
| `PMX_CANVAS_FORCE_JSON_RENDER_REBUILD` | json-render build | `1` forces the viewer bundle rebuild even when outputs exist. |
|
|
47
|
+
| `PMX_CANVAS_EXCALIDRAW_MCP_COMMAND` | server (tests) | Stdio command replacing the hosted Excalidraw MCP server for the diagram preset, e.g. `bun run tests/fixtures/mcp-app-fixture.ts`. |
|
|
48
|
+
| `PMX_SESSION_LOG` / `PMX_TEST_LOG` | server | Path for MCP-app host session diagnostics logging (session wins over test). |
|
package/docs/node-types.md
CHANGED
|
@@ -268,6 +268,11 @@ The scaffold includes `recharts`. Pass `--deps name,name2` for additional
|
|
|
268
268
|
package dependencies. Failed or empty CLI bundles print `ok: false`, exit
|
|
269
269
|
non-zero, and do not create a canvas node.
|
|
270
270
|
|
|
271
|
+
Platform note: the artifact build pipeline runs through bundled bash scripts
|
|
272
|
+
and requires a POSIX shell — it is not supported on native Windows yet.
|
|
273
|
+
Already-built artifacts render fine on every platform; only the build step is
|
|
274
|
+
POSIX-only.
|
|
275
|
+
|
|
271
276
|
The matching agent skill is at
|
|
272
277
|
[`skills/web-artifacts-builder/SKILL.md`](../skills/web-artifacts-builder/SKILL.md).
|
|
273
278
|
|
|
@@ -6,24 +6,33 @@
|
|
|
6
6
|
|
|
7
7
|
Each item is tagged by shape: **project** (multi-day, own plan/PR), **decision** (needs a maintainer call before code), or **small** (safe standalone change).
|
|
8
8
|
|
|
9
|
+
## Decision analysis (2026-07-08, maintainer-approved)
|
|
10
|
+
|
|
11
|
+
The remaining items were re-analyzed with pros/cons and tiered; the maintainer approved this ordering:
|
|
12
|
+
|
|
13
|
+
- **Tier 1 (do):** CLI raw fetches → operation invoker **together with** the H3 parser/module split (same file, one project); H7 test coverage **as a prerequisite** for any client refactor; M2 AX-bridge dedup (first client task after H7); the M8 flake subset (mock the Excalidraw network tests, share `getAvailablePort`, Playwright `retries`/`forbidOnly`, `e2e-cli` into CI). *C1b done (2026-07-08):* all 60 raw `api()` calls migrated onto the invoker (9-agent verified mapping; `webartifact.build` gained `errorBodyAsResult`; batch failure prints the envelope + exits 1; the `api()` helper is deleted; two deliberate raw `fetch` sites remain — binary screenshot, SSE watch). *H3 module split done (2026-07-09):* `agent.ts` is a 227-line entry over `src/cli/shared.ts` (cycle-free core: registry, parser, transport) + 11 `src/cli/commands/*` domain modules, moved byte-identically; ax-parity reads the whole cli tree. **Parser sub-item reshaped:** `parseFlags`/`BOOL_FLAGS` is now single-sited in shared.ts; `index.ts` keeps its intentionally tiny fixed-flag launcher reader (different domain — server launch, not agent commands). The spec-driven per-command flag parser (`defineCommand`) remains the ideal but is a redesign of all 83 command surfaces — deferred to a future CLI UX change, not worth manufactured churn now.
|
|
14
|
+
- **Tier 2 (reshaped):** the PmxCanvas→executeOperation collapse is **not** mechanical — `executeOperation` is async while 57/76 SDK methods are sync with typed returns, so a full collapse is a breaking SDK change. Verdict: migrate only the already-async/void methods now; decide the full async-SDK question as a **v0.4 breaking item** with the `canvas_snapshot` composite. Post-registry the sync methods are thin presentation over the shared cores (single logic site) — not logic duplication. H5 (sse-bridge factory) proceeds **after H7**. M6 gets the middle path: runtime per-action required-field errors, no `oneOf` schema break (many MCP hosts handle `oneOf` poorly). M1 gets the minimum four (minify, devtools→devDeps, lazy-load, replace the python mtime gate); the React-stack decision stays open.
|
|
15
|
+
- **Tier 3 (cheap batch):** CI check that CLAUDE.md/AGENTS.md stay identical (instead of doc generation); document `PMX_DATA` + the `PMX_CANVAS_JSON_RENDER_*`/`PMX_MCP_APP_HOST_*` env families; publish.yml verifies the tag SHA passed test.yml; `@types/turndown` → devDependencies; L1 dead `ContextPinHud`; L4 one-shot migration marker; L6 timer-leak fixes.
|
|
16
|
+
- **Tier 4 (closed as by-design / parked):** the six server-coupled routes (nothing to dedup — injection machinery for single-copy handlers isn't worth it); M3 ExtAppFrame refactor (the timing hacks are load-bearing WebKit fixes — document invariants, enter only when a feature forces it); M7 notification fan-out, L2, L3, `noUncheckedIndexedAccess` (parked); M10 packaging closed after the `@types` one-liner (`bin`→`src` under Bun and shipped `dist/` are both load-bearing by design).
|
|
17
|
+
|
|
9
18
|
## Phase 1 — finish what was started
|
|
10
19
|
|
|
11
20
|
| Item | Source | What's open | Shape |
|
|
12
21
|
|---|---|---|---|
|
|
13
22
|
| Complete the registry migration | C1 | Fold the remaining hand-written routes in `server.ts` into operations; collapse the 76-method `PmxCanvas` onto `executeOperation`; migrate the CLI's 60 raw `fetch` calls onto the operation invoker. **Slice 1 done post-0.3.0:** the 7 `/api/ext-app/*` routes moved to `ops/ext-app.ts` (~290 lines out of `server.ts`, the 5 clone handlers — old M5 — collapsed to one parametrized definition; wire envelopes byte-identical, no new MCP tools). **Slice 2 done:** the 15 ax wire routes (8 GET lists + `ax/context` + `ax/surface-snapshot` + `pinned-context` + `code-graph` + `ax/activity` + `ax/interaction` + legacy `PATCH /api/canvas/ax`) moved to `ops/ax-read.ts` (~370 more lines out of `server.ts`); `open-external` stays hand-written (coupled to the live server port + system browser opener). **Slice 3 done:** theme GET/POST, canvas update, viewport, annotation POST, and node `:id/refresh` moved to the registry (`ops/canvas-wire.ts`, plus additions to `ops/viewport.ts`, `ops/annotation.ts`, `ops/nodes.ts`); the two dispatch gates unified into one `/api/` gate; `server.ts` is at ~2,980 lines (from 3,873 at the audit). **Remaining routes (~20)** are the deliberate poor fits (SSE, binary, HTML/static, file bodies) plus four server-coupled handlers: `prompt` + workbench `state`/`open`/`intent` (module-local `primaryWorkbench*` session state), `render` (Excalidraw URL-normalizer chain), `open-external` (live port + system browser). Those need state injection like the webview runner — a deliberate follow-up, not mechanical. After routes, the bigger halves remain: `PmxCanvas` (76 methods) onto `executeOperation`, and the CLI's raw fetches onto the invoker. | project (in progress) |
|
|
14
|
-
| One HTTP envelope | M4 residual | Registry ops return bare bodies, legacy ext-app handlers `{ok:true,result}`, 404s plaintext.
|
|
23
|
+
| One HTTP envelope | M4 residual | Registry ops return bare bodies, legacy ext-app handlers `{ok:true,result}`, 404s plaintext. **Reclassified v0.4-breaking (2026-08-03):** unifying changes response shapes that the Copilot adapter and the external test harnesses parse — it cannot ship in a patch. Bundle with the other v0.4 breaking items (`canvas_snapshot` composite, async-SDK decision). The `/health` degraded flag for the 63 catch→warn→continue persistence sites is additive and can ship earlier. | project (v0.4) |
|
|
15
24
|
| `dist/` commit policy | C3 residual | **Decided + done post-0.3.0:** `dist/` stays committed; `test.yml` now has a `dist/types` staleness gate (rebuild + fail on drift). The `dist/canvas`/`dist/json-render` bundles are deliberately not byte-checked — bundler output varies across bun versions; their staleness story folds into M1. | done |
|
|
16
25
|
| Skill-mirror validator | C2 residual | `validate:agent-skills` exits 0 when the (local-only, gitignored) trees are absent. Docs are honest about this now; decide whether to repoint, gate, or delete the check. | decision |
|
|
17
|
-
| Generate CLAUDE.md/AGENTS.md from one source | M9 residual |
|
|
26
|
+
| Generate CLAUDE.md/AGENTS.md from one source | M9 residual | **Done (tier-3 batch):** CI fails on any CLAUDE.md/AGENTS.md byte drift (`test.yml` identity gate) — the cheap alternative to generation the decision analysis chose. Generation/symlinking stays unnecessary while the gate holds. | done |
|
|
18
27
|
|
|
19
28
|
## Phase 2 — make the safety net real
|
|
20
29
|
|
|
21
30
|
| Item | Source | What's open | Shape |
|
|
22
31
|
|---|---|---|---|
|
|
23
32
|
| Lint/format tooling | H6 | **Done post-0.3.0:** Biome 2.5 adopted — `bun run lint` / `lint:fix`, wired into `release:check` (making RELEASE.md's "lints" claim true) and CI. First-run format sweep landed as its own mechanical commit; 7 lint errors hand-fixed (incl. a real conditional-hooks bug in `SelectionBar.tsx`). Deliberately disabled rules to revisit: a11y group, `useExhaustiveDependencies` (misfires on Preact signals), `noNonNullAssertion` (143 deliberate uses), `noArrayIndexKey` (13 sites). ~77 warnings (unused imports/vars, optional-chain) left as a burn-down list — warnings don't fail the gate. | done |
|
|
24
|
-
| Client + server unit coverage | H7 |
|
|
33
|
+
| Client + server unit coverage | H7 | **Done post-0.3.3 (first tranche):** happy-dom + @testing-library/preact harness under `tests/client/` (own preload so DOM globals never leak into server suites); 20 client render tests incl. the SelectionBar conditional-hooks regression; `semantic-attention` (25 tests) and `canvas-schema` (21 tests incl. an M12 stale-tool-drift invariant) covered; tests are TYPECHECKED (`tsconfig.tests.json`, 270 latent errors fixed, wired into `typecheck` + CI); coverage now GATES via `scripts/check-coverage.ts` total floors (bun's bunfig threshold is per-file and its table form is silently ignored — documented). **Remaining (opportunistic):** dedicated tests for `ax-state-manager.ts` and `mcp-app-host.ts`, and growing the client suite alongside H5/M2 work. | first tranche done |
|
|
25
34
|
| Daemon port/lock lifecycle | H4 | **Fixed post-0.3.0** (`src/cli/daemon.ts` + `tests/unit/cli-daemon.test.ts`). Note the original finding was partly wrong: the daemon child was already strict-port (`PmxCanvas.start` pins `allowPortFallback: false`), so fallback-blind polling could not occur. The real defects — fixed — were: the pre-check accepted any responsive `/health` as "already running" (even a foreign workspace's daemon), a TOCTOU double-spawn window, the pid recorded only after health passed (slow start → unkillable orphan), and no PID-recycling guard. The flaky `pmx-canvas-sdk.test.ts` port collision is NOT this — it's M8's copy-pasted `getAvailablePort()` racing. | done |
|
|
26
|
-
| Test-suite structure | M8 | Integration monoliths under `tests/unit/` (`server-api` 5,213 lines, `cli-node` 2,903); `getAvailablePort()` copy-pasted in 5 files; `waitForPersistence(650ms)` fixed sleeps in 9 files + ~19 magic `Bun.sleep`s; Playwright lacks `retries`/`forbidOnly`; `test:e2e-cli` (517-line script) runs in no CI workflow. *Post-0.3.0:* `operation-parity` now binds port 0 and derives the port from the base URL (removes one reserve-then-rebind race); the shared-helper dedup and the rest remain. Also noted: `ax-parity.test.ts` is a string-matching source-analysis test whose needles broke when Biome re-wrapped `cmd(` calls — its CLI needles are now layout-independent, but any future needle should avoid depending on line layout. **Also noted (2026-07-07):** the `server-api` Excalidraw diagram tests do real network round-trips to the hosted `mcp.excalidraw.com` MCP server and intermittently time out (30s) under full parallel runs
|
|
35
|
+
| Test-suite structure | M8 | Integration monoliths under `tests/unit/` (`server-api` 5,213 lines, `cli-node` 2,903); `getAvailablePort()` copy-pasted in 5 files; `waitForPersistence(650ms)` fixed sleeps in 9 files + ~19 magic `Bun.sleep`s; Playwright lacks `retries`/`forbidOnly`; `test:e2e-cli` (517-line script) runs in no CI workflow. *Post-0.3.0:* `operation-parity` now binds port 0 and derives the port from the base URL (removes one reserve-then-rebind race); the shared-helper dedup and the rest remain. Also noted: `ax-parity.test.ts` is a string-matching source-analysis test whose needles broke when Biome re-wrapped `cmd(` calls — its CLI needles are now layout-independent, but any future needle should avoid depending on line layout. **Also noted (2026-07-07):** the `server-api` Excalidraw diagram tests do real network round-trips to the hosted `mcp.excalidraw.com` MCP server and intermittently time out (30s) under full parallel runs. **M8 subset done (2026-07-08):** those tests now run against the local stdio fixture via `PMX_CANVAS_EXCALIDRAW_MCP_COMMAND`; `getAvailablePort` is one shared helper; Playwright has `retries`/`forbidOnly` on CI; `e2e-cli` is a CI job. **Remaining (parked):** the `tests/unit/` monolith split and the `waitForPersistence` fixed-sleep sweep. | subset done |
|
|
27
36
|
| `noUncheckedIndexedAccess` | L7 residual | Enable in tsconfig; will surface many index-access errors — treat as its own sweep. | small (noisy) |
|
|
28
37
|
|
|
29
38
|
## Phase 3 — client consolidation
|
|
@@ -31,7 +40,7 @@ Each item is tagged by shape: **project** (multi-day, own plan/PR), **decision**
|
|
|
31
40
|
| Item | Source | What's open | Shape |
|
|
32
41
|
|---|---|---|---|
|
|
33
42
|
| One node factory | H5 | `sse-bridge.ts` (1,066 lines) keeps its own `DEFAULT_POSITIONS`/`makeNode` divergent from `canvas-store`; `responseToThreadMap` is not cleaned on SSE reconnect; server-side `syncEventToCanvasState` (~330 lines) mirrors it as an if/else chain with geometry defaults duplicated against `ensureDefaultDockedNodes`. Share one factory; treat `canvas-layout-update` as the single source of truth; table-drive the event sync. | project |
|
|
34
|
-
| Shared AX iframe bridge | M2 |
|
|
43
|
+
| Shared AX iframe bridge | M2 | **Done (2026-08-03):** `useAxSurfaceBridge` (`src/client/nodes/use-ax-surface-bridge.ts`) single-sites the sandboxed-surface trust boundary previously copy-pasted in `HtmlNode.tsx`, `McpAppNode.tsx`, `ExtAppFrame.tsx`; `src/shared/ax-surface-protocol.ts` owns the wire tags (`pmx-canvas-ax`, `-ack`, `pmx-canvas-html-node`, the ext-app boot beacon) — client parsers import them, generated surface scripts (html-surface.ts, ext-app builders, json-render viewer) interpolate them byte-identically. Client render tests pin the validation gates + submit→ack round-trip (`tests/client/use-ax-surface-bridge.test.tsx`). | done |
|
|
35
44
|
| `ExtAppFrame.tsx` timing hacks | M3 | 915 lines, 12 refs, 7 `setTimeout`s, 5 rAFs, WebKit remount slot counter, 1200 ms fallback bootstrap, two near-duplicate `buildHostContext` blocks. Extract `useExtAppBridge`; single `buildHostContext`. | project |
|
|
36
45
|
| json-render stack decision | M1 | Preact + React 19 + recharts all runtime deps; 7 exact-pinned `@json-render/*` incl. 3 devtools packages shipped; `dist/json-render/index.js` 1.37 MB and `dist/canvas/index.js` 599 KB both unminified; build script shells to python3 with a stale-bundle fallback. Minimum: minify, move devtools to devDependencies, lazy-load the viewer, replace the python mtime gate. Bigger call: separate optional package if the React stack stays. | decision + project |
|
|
37
46
|
| Client dead code / duplication | L1, L2, L3 | `ContextPinHud.tsx` unreferenced; three pure re-export shims; two parallel markdown-format stacks; `global.css` 3,746 lines with 11 `!important` vs 227 inline styles and unscaled z-indexes. | small (batchable) |
|
|
@@ -40,12 +49,21 @@ Each item is tagged by shape: **project** (multi-day, own plan/PR), **decision**
|
|
|
40
49
|
|
|
41
50
|
| Item | Source | What's open |
|
|
42
51
|
|---|---|---|
|
|
43
|
-
| Per-action composite schemas | M6 |
|
|
52
|
+
| Per-action composite schemas | M6 | **Done (2026-08-03, middle path per the decision analysis):** composite dispatch re-enforces each member op's required MCP fields at runtime and names the missing field (`requiredMcpFields` in `operations/mcp.ts`) — no `oneOf` schema break (many MCP hosts handle `oneOf` poorly). Advertised schemas stay all-optional by design. |
|
|
44
53
|
| Packaging | M10 | `files` ships both `src/` and `dist/` (~double tarball, two import paths); `bin` points at raw `.ts` (undocumented Bun-only constraint); `@types/turndown` in runtime deps. |
|
|
45
|
-
| Env var hygiene | M11 residual | `PMX_CANVAS_PORT` vs `PMX_WEB_CANVAS_PORT` applied inconsistently across entry points
|
|
46
|
-
| `publish.yml` e2e gap | M11 residual |
|
|
54
|
+
| Env var hygiene | M11 residual | Docs side **done (tier-3 batch)**: `docs/environment.md` covers `PMX_DATA` + the `PMX_CANVAS_JSON_RENDER_*`/`PMX_MCP_APP_HOST_*` families. Still open: `PMX_CANVAS_PORT` vs `PMX_WEB_CANVAS_PORT` applied inconsistently across entry points (partly mitigated by the global `--port`/`--server-url` CLI flags). |
|
|
55
|
+
| `publish.yml` e2e gap | M11 residual | **Done (tier-3 batch):** publish.yml verifies the tagged SHA has a successful `test.yml` run before publishing. |
|
|
47
56
|
| MCP notification fan-out | M7 residual | Five `canvas://` resource URIs notify on every mutation regardless of type (`mcp/server.ts:95-99`). |
|
|
48
|
-
| Legacy migration code on every boot | L4 |
|
|
57
|
+
| Legacy migration code on every boot | L4 | **Parked deliberately (2026-07-08):** the "unconditional" run is a couple of `existsSync` stats per boot — a one-shot marker adds state machinery to save nothing measurable and risks breaking upgrades from old layouts. Retire the migration code wholesale in a future major instead. |
|
|
49
58
|
| Suppression/mutation plumbing | L5 | Two parallel suppression depth-counters; `onMutation` is a single-slot setter wired twice (last-write-wins). |
|
|
50
59
|
| Module-global lifecycle leaks | L6 | ~20 module-level `let`s in `server.ts`; code-graph debounce timer survives `stopCanvasServer`; remote SSE watcher loop has no cancellation. |
|
|
51
60
|
| June doc citations | L7 residual | `tech-debt-assessment-2026-06.md` cites gitignored `.learnings/` entries future maintainers can't read. |
|
|
61
|
+
|
|
62
|
+
## Added post-0.3.3 (2026-08-03 sweep)
|
|
63
|
+
|
|
64
|
+
| Item | What's open |
|
|
65
|
+
|---|---|
|
|
66
|
+
| Windows web-artifacts | The artifact build pipeline is bash-script-backed (POSIX-only); `tests/unit/web-artifacts.test.ts` self-skips on win32 and docs now state the boundary. Decide: port the build scripts to a cross-platform runner, or keep the documented "build on POSIX, render anywhere" stance. |
|
|
67
|
+
| Composite parity no longer machine-checked | Since v0.3.0 removed the legacy standalones, the byte-identical composite-vs-legacy guarantee has no head-to-head test — regressions in `buildInput`/`formatResult` reuse surface only via output-shape assertions. Accepted at review; revisit if a composite drifts. |
|
|
68
|
+
| Portal-layer ext-app mount (contingency) | If the maintainer sets the WebKit UX bar at "never any visible gap" (0.3.3 report leaves it open), inline ext-app iframes must render outside the world transform — an architectural change that reopens the parked M3. Only scope on a failing report cycle. |
|
|
69
|
+
| Registry-migration dead-code sweep | **Done (2026-08-03):** removed the orphaned `describeOp`/`describeGateOp` (composites.ts, dead since the v0.3.0 registration filter) and the `compactNodePayload`/`compactLayoutPayload`/`wantsFullPayload` duplicates in `mcp/server.ts` (live copies are in `ops/nodes.ts`). |
|
package/docs/screenshot.png
CHANGED
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pmx-canvas",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.4",
|
|
4
4
|
"description": "Spatial canvas workbench for coding agents — infinite 2D canvas with agent-native CLI, MCP integration, nodes, edges, file watching, and snapshots",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/server/index.ts",
|
|
@@ -40,18 +40,19 @@
|
|
|
40
40
|
"pack:dry-run": "bun pm pack --dry-run",
|
|
41
41
|
"release:check": "bun run build && bun run typecheck && bun run lint && bun run test:all",
|
|
42
42
|
"release:smoke": "bash scripts/release-smoke.sh",
|
|
43
|
-
"typecheck": "tsc --noEmit",
|
|
43
|
+
"typecheck": "tsc --noEmit && tsc -p tsconfig.tests.json",
|
|
44
44
|
"lint": "biome check .",
|
|
45
45
|
"lint:fix": "biome check --write .",
|
|
46
46
|
"test": "PMX_CANVAS_DISABLE_BROWSER_OPEN=1 bun test tests/unit",
|
|
47
47
|
"test:unit": "PMX_CANVAS_DISABLE_BROWSER_OPEN=1 bun test tests/unit",
|
|
48
|
-
"test:
|
|
48
|
+
"test:client": "bun test --preload ./tests/client/setup.ts tests/client",
|
|
49
|
+
"test:coverage": "bun test tests/unit --coverage --coverage-reporter=text --coverage-reporter=lcov --coverage-dir coverage && bun scripts/check-coverage.ts",
|
|
49
50
|
"test:web-canvas": "PMX_CANVAS_DISABLE_BROWSER_OPEN=1 bun run build && PMX_CANVAS_DISABLE_BROWSER_OPEN=1 bash scripts/run-playwright.sh",
|
|
50
51
|
"test:e2e": "bun run test:web-canvas",
|
|
51
52
|
"test:e2e-cli": "bash scripts/e2e-cli-coverage.sh",
|
|
52
53
|
"test:web-canvas:headed": "bun run build && bash scripts/run-playwright.sh --headed",
|
|
53
54
|
"test:e2e:headed": "bun run test:web-canvas:headed",
|
|
54
|
-
"test:all": "bun run test && bun run test:web-canvas",
|
|
55
|
+
"test:all": "bun run test && bun run test:client && bun run test:web-canvas",
|
|
55
56
|
"test:install-browsers": "bun x playwright install chromium",
|
|
56
57
|
"validate:agent-skills": "bash scripts/validate-agent-skill-mirrors.sh",
|
|
57
58
|
"prepublishOnly": "bun run build && bun run typecheck"
|
|
@@ -68,7 +69,6 @@
|
|
|
68
69
|
"@modelcontextprotocol/ext-apps": "^1.3.1",
|
|
69
70
|
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
70
71
|
"@preact/signals": "^2.0.0",
|
|
71
|
-
"@types/turndown": "^5.0.6",
|
|
72
72
|
"marked": "^15.0.0",
|
|
73
73
|
"preact": "^10.25.0",
|
|
74
74
|
"react": "^19.2.3",
|
|
@@ -79,11 +79,14 @@
|
|
|
79
79
|
},
|
|
80
80
|
"devDependencies": {
|
|
81
81
|
"@biomejs/biome": "^2.5.2",
|
|
82
|
+
"@happy-dom/global-registrator": "^20.10.6",
|
|
82
83
|
"@playwright/test": "^1.58.2",
|
|
83
84
|
"@tailwindcss/cli": "^4.1.18",
|
|
85
|
+
"@testing-library/preact": "^3.2.4",
|
|
84
86
|
"@types/bun": "latest",
|
|
85
87
|
"@types/react": "^19.2.14",
|
|
86
88
|
"@types/react-dom": "^19.2.3",
|
|
89
|
+
"@types/turndown": "^5.0.6",
|
|
87
90
|
"tailwindcss": "^4.1.18",
|
|
88
91
|
"tw-animate-css": "^1.4.0",
|
|
89
92
|
"typescript": "^5.7.0"
|
|
@@ -217,14 +217,17 @@ Prefer `canvas_query { action: "search" }` over parsing the full layout.
|
|
|
217
217
|
loads (e.g. the GitHub Copilot app's embedded WKWebView) can render as a black tile — a host
|
|
218
218
|
compositor paint race on the nested iframe, **not** a broken node (the session is healthy and
|
|
219
219
|
`sessionStatus` is `ready`; it renders fine in Chrome, the Codex browser, and for nodes created
|
|
220
|
-
live after the panel hydrates). Under WebKit the canvas auto-
|
|
221
|
-
present-at-load ext-app is remounted through a
|
|
222
|
-
time,
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
from page JS, so retries are bounded)
|
|
226
|
-
black tile (forces a fresh mount in
|
|
227
|
-
|
|
220
|
+
live after the panel hydrates). Under WebKit the canvas ATTEMPTS auto-recovery: after boot,
|
|
221
|
+
each present-at-load ext-app is remounted through a serialized, boot-aware queue (one app at a
|
|
222
|
+
time), with a watchdog retry for iframes that never boot and a re-arm when a panel that loaded
|
|
223
|
+
hidden becomes visible. Treat the auto-recovery as best-effort, NOT guaranteed — the 0.3.2
|
|
224
|
+
Copilot-host report shows tiles can still come up black in real WKWebView panels (compositing
|
|
225
|
+
success is not observable from page JS, so retries are bounded). Keep the cautionary behavior:
|
|
226
|
+
recovery is deterministic via **expand-then-close** of the black tile (forces a fresh mount in
|
|
227
|
+
the fullscreen overlay, which always paints), or open the workbench in a normal browser
|
|
228
|
+
(Chrome). Do not diagnose a healthy app session as a broken node. When reporting a black tile,
|
|
229
|
+
fetch `GET /api/canvas/debug/ext-app-recovery` from the daemon and attach the trace — it shows
|
|
230
|
+
what the recovery queue actually did in that panel.
|
|
228
231
|
- Graph and json-render standalone surfaces use `display=site` and fill the browser viewport, and
|
|
229
232
|
reflow on a live window resize in a normal browser. Some single-tab host browsers (e.g. the
|
|
230
233
|
Codex in-app browser) don't deliver live-resize events, so a resized standalone chart can look
|