pmx-canvas 0.1.27 → 0.1.28
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 +36 -0
- package/dist/canvas/index.js +49 -49
- package/dist/json-render/index.css +1 -1
- package/dist/json-render/index.js +108 -108
- package/dist/types/json-render/charts/components.d.ts +10 -0
- package/dist/types/json-render/directives.d.ts +11 -1
- package/dist/types/server/canvas-operations.d.ts +8 -0
- package/dist/types/server/index.d.ts +6 -1
- package/docs/sdk.md +4 -4
- package/package.json +1 -1
- package/src/cli/agent.ts +22 -1
- package/src/client/nodes/ExtAppFrame.tsx +25 -0
- package/src/json-render/charts/components.tsx +31 -6
- package/src/json-render/charts/tufte-components.tsx +17 -5
- package/src/json-render/directives.ts +37 -2
- package/src/json-render/renderer/index.css +6 -1
- package/src/json-render/server.ts +19 -1
- package/src/mcp/canvas-access.ts +3 -1
- package/src/mcp/server.ts +2 -2
- package/src/server/canvas-operations.ts +16 -2
- package/src/server/canvas-schema.ts +1 -0
- package/src/server/index.ts +25 -5
- package/src/server/server.ts +23 -3
- package/src/server/web-artifacts.ts +31 -5
|
@@ -5,11 +5,12 @@ import {
|
|
|
5
5
|
readdirSync,
|
|
6
6
|
mkdirSync,
|
|
7
7
|
readFileSync,
|
|
8
|
+
realpathSync,
|
|
8
9
|
statSync,
|
|
9
10
|
unlinkSync,
|
|
10
11
|
writeFileSync,
|
|
11
12
|
} from 'node:fs';
|
|
12
|
-
import { basename, delimiter, dirname, isAbsolute, join, relative, resolve } from 'node:path';
|
|
13
|
+
import { basename, delimiter, dirname, isAbsolute, join, relative, resolve, sep } from 'node:path';
|
|
13
14
|
import { ensureArtifactsDir, getWorkspaceRoot } from './artifact-paths.js';
|
|
14
15
|
import { canvasState, type CanvasNodeState } from './canvas-state.js';
|
|
15
16
|
import { findOpenCanvasPosition } from './placement.js';
|
|
@@ -338,6 +339,33 @@ async function runProcess(
|
|
|
338
339
|
return { stdout: stdout.trim(), stderr: stderr.trim() };
|
|
339
340
|
}
|
|
340
341
|
|
|
342
|
+
/**
|
|
343
|
+
* Resolve the init/bundle script path. Without an override the trusted bundled
|
|
344
|
+
* resolver is used. An override (a test/debugging escape hatch exposed on every
|
|
345
|
+
* surface — CLI --init-script-path/--bundle-script-path, the MCP tool, the HTTP
|
|
346
|
+
* endpoint, and the SDK) is only honored when it resolves inside the active
|
|
347
|
+
* workspace root — otherwise it would let a caller exec an arbitrary host script
|
|
348
|
+
* via bash as the server user. This containment is the single chokepoint that
|
|
349
|
+
* makes forwarding the field safe across all of those surfaces.
|
|
350
|
+
*/
|
|
351
|
+
function resolveTrustedScriptPath(override: string | undefined, kind: 'init' | 'bundle'): string {
|
|
352
|
+
if (!override) return resolveWebArtifactScriptPath(kind);
|
|
353
|
+
const resolved = resolve(override);
|
|
354
|
+
// Compare real (symlink-resolved) paths on both sides so a legitimately
|
|
355
|
+
// contained override under a symlinked root (e.g. macOS /var -> /private/var,
|
|
356
|
+
// or a workspace that itself lives beneath a symlink) is not wrongly rejected.
|
|
357
|
+
// The candidate is realpath'd only when it exists; a non-existent escape path
|
|
358
|
+
// still fails the containment check below (or the existsSync guard at the call
|
|
359
|
+
// site). This keeps the guard strict — genuine escapes are still rejected.
|
|
360
|
+
const realRoot = realpathSync(currentWorkspaceRoot());
|
|
361
|
+
const realCandidate = existsSync(resolved) ? realpathSync(resolved) : resolved;
|
|
362
|
+
const workspaceRel = relative(realRoot, realCandidate);
|
|
363
|
+
if (workspaceRel === '..' || workspaceRel.startsWith(`..${sep}`) || isAbsolute(workspaceRel)) {
|
|
364
|
+
throw new Error(`Web-artifact ${kind} script override must resolve inside the workspace: ${override}`);
|
|
365
|
+
}
|
|
366
|
+
return resolved;
|
|
367
|
+
}
|
|
368
|
+
|
|
341
369
|
export function resolveWebArtifactScriptPath(kind: 'init' | 'bundle'): string {
|
|
342
370
|
const scriptFile = kind === 'init' ? 'init-artifact.sh' : 'bundle-artifact.sh';
|
|
343
371
|
const candidates = [
|
|
@@ -459,10 +487,8 @@ export async function executeWebArtifactBuild(
|
|
|
459
487
|
const slug = slugify(input.title);
|
|
460
488
|
const projectPath = resolve(input.projectPath ?? join(artifactsDir, '.web-artifacts', slug));
|
|
461
489
|
const outputPath = resolve(input.outputPath ?? join(artifactsDir, `${slug}.html`));
|
|
462
|
-
const initScriptPath =
|
|
463
|
-
const bundleScriptPath =
|
|
464
|
-
input.bundleScriptPath ?? resolveWebArtifactScriptPath('bundle'),
|
|
465
|
-
);
|
|
490
|
+
const initScriptPath = resolveTrustedScriptPath(input.initScriptPath, 'init');
|
|
491
|
+
const bundleScriptPath = resolveTrustedScriptPath(input.bundleScriptPath, 'bundle');
|
|
466
492
|
const timeoutMs = input.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
467
493
|
|
|
468
494
|
if (!existsSync(initScriptPath)) {
|