@1agh/maude 0.45.1 → 0.46.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/apps/studio/acp/bridge.ts +628 -74
- package/apps/studio/acp/index.ts +172 -23
- package/apps/studio/acp/probe.ts +31 -7
- package/apps/studio/acp/transcript.ts +91 -5
- package/apps/studio/annotations-snap.ts +50 -10
- package/apps/studio/api.ts +344 -14
- package/apps/studio/artboard-guides-overlay.tsx +270 -0
- package/apps/studio/bin/_import-asset.mjs +35 -5
- package/apps/studio/bin/_pdf-playwright.mjs +35 -4
- package/apps/studio/bin/_png-playwright.mjs +38 -4
- package/apps/studio/bin/_pw-launch.mjs +52 -0
- package/apps/studio/bin/_pw-launch.test.mjs +90 -0
- package/apps/studio/bin/_smart-frames.mjs +419 -0
- package/apps/studio/bin/_smart-frames.test.mjs +140 -0
- package/apps/studio/bin/smart-frames.sh +30 -0
- package/apps/studio/canvas-edit.ts +302 -3
- package/apps/studio/canvas-lib.tsx +203 -11
- package/apps/studio/canvas-shell.tsx +167 -28
- package/apps/studio/client/app.jsx +678 -176
- package/apps/studio/client/panels/CapabilityBar.jsx +101 -0
- package/apps/studio/client/panels/ChatPanel.jsx +846 -133
- package/apps/studio/client/panels/ElicitationPrompt.jsx +429 -0
- package/apps/studio/client/panels/PermissionPrompt.jsx +119 -0
- package/apps/studio/client/panels/ReadinessList.jsx +17 -3
- package/apps/studio/client/panels/SettingsPanel.jsx +211 -0
- package/apps/studio/client/panels/ToolGroup.jsx +79 -0
- package/apps/studio/client/panels/acp-capabilities.js +82 -0
- package/apps/studio/client/panels/acp-elicitation.js +194 -0
- package/apps/studio/client/panels/acp-runtime.js +248 -9
- package/apps/studio/client/panels/acp-usage.js +65 -0
- package/apps/studio/client/panels/transcript-view.js +36 -0
- package/apps/studio/client/styles/6-acp-chat.css +689 -11
- package/apps/studio/dist/client.bundle.js +1738 -1736
- package/apps/studio/dist/comment-mount.js +2 -2
- package/apps/studio/dist/styles.css +1 -1
- package/apps/studio/dom-selection.ts +20 -0
- package/apps/studio/export-dialog.tsx +138 -18
- package/apps/studio/exporters/pdf.ts +332 -18
- package/apps/studio/exporters/png.ts +45 -3
- package/apps/studio/footage/schema.ts +17 -1
- package/apps/studio/generation/gemma-models.ts +224 -0
- package/apps/studio/generation/prefs.ts +43 -0
- package/apps/studio/generation/whisper-models.test.ts +28 -1
- package/apps/studio/generation/whisper-models.ts +22 -7
- package/apps/studio/http.ts +246 -9
- package/apps/studio/print/marks.ts +113 -0
- package/apps/studio/print/units.ts +269 -0
- package/apps/studio/print-overlay-content.tsx +132 -0
- package/apps/studio/test/acp-bridge.test.ts +11 -6
- package/apps/studio/test/acp-capabilities.test.ts +123 -0
- package/apps/studio/test/acp-caps-bridge.test.ts +274 -0
- package/apps/studio/test/acp-elicitation-bridge.test.ts +475 -0
- package/apps/studio/test/acp-elicitation.test.ts +251 -0
- package/apps/studio/test/acp-mode-banner.test.ts +45 -0
- package/apps/studio/test/acp-permission-prompt.test.ts +77 -0
- package/apps/studio/test/acp-permission.test.ts +262 -0
- package/apps/studio/test/acp-session-allowed-tools.test.ts +91 -0
- package/apps/studio/test/acp-toolgroup.test.ts +76 -0
- package/apps/studio/test/acp-transcript-view.test.ts +71 -0
- package/apps/studio/test/acp-transcript.test.ts +75 -2
- package/apps/studio/test/acp-usage-bridge.test.ts +136 -0
- package/apps/studio/test/acp-usage.test.ts +143 -0
- package/apps/studio/test/annotations-snap.test.ts +56 -0
- package/apps/studio/test/artboard-guides-overlay.test.tsx +152 -0
- package/apps/studio/test/artboard-kinds.test.tsx +83 -0
- package/apps/studio/test/artboard-selection-attrs.test.ts +69 -0
- package/apps/studio/test/canvas-meta-api.test.ts +167 -0
- package/apps/studio/test/element-structural-edit.test.ts +250 -0
- package/apps/studio/test/exporters/png.test.ts +49 -1
- package/apps/studio/test/fixtures/mock-acp-agent-caps.mjs +158 -0
- package/apps/studio/test/fixtures/mock-acp-agent-elicit-flood.mjs +46 -0
- package/apps/studio/test/fixtures/mock-acp-agent-elicit-url.mjs +42 -0
- package/apps/studio/test/fixtures/mock-acp-agent-elicit.mjs +63 -0
- package/apps/studio/test/fixtures/mock-acp-agent-permission-flood.mjs +51 -0
- package/apps/studio/test/fixtures/mock-acp-agent-permission.mjs +50 -0
- package/apps/studio/test/fixtures/mock-acp-agent-usage.mjs +56 -0
- package/apps/studio/test/import-asset.test.ts +31 -3
- package/apps/studio/test/pdf-print-boxes.test.ts +272 -0
- package/apps/studio/test/print-marks.test.ts +113 -0
- package/apps/studio/test/print-units.test.ts +173 -0
- package/apps/studio/test/use-snap-guides.test.ts +81 -0
- package/apps/studio/use-chrome-visibility.tsx +19 -0
- package/apps/studio/use-element-resize.tsx +21 -2
- package/apps/studio/use-snap-guides.tsx +73 -5
- package/apps/studio/use-spacing-handles.tsx +9 -5
- package/apps/studio/whats-new.json +62 -0
- package/cli/commands/design.mjs +116 -3
- package/cli/lib/pkg-root.mjs +42 -10
- package/cli/lib/pkg-root.test.mjs +33 -1
- package/package.json +11 -9
- package/plugins/design/dependencies.json +35 -0
package/cli/commands/design.mjs
CHANGED
|
@@ -1,11 +1,21 @@
|
|
|
1
1
|
import { execSync, spawn, spawnSync } from 'node:child_process';
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
chmodSync,
|
|
4
|
+
existsSync,
|
|
5
|
+
mkdirSync,
|
|
6
|
+
mkdtempSync,
|
|
7
|
+
readFileSync,
|
|
8
|
+
rmSync,
|
|
9
|
+
writeFileSync,
|
|
10
|
+
} from 'node:fs';
|
|
3
11
|
import { mkdir, readdir, readFile, stat, writeFile } from 'node:fs/promises';
|
|
4
12
|
import { createRequire } from 'node:module';
|
|
13
|
+
import { homedir } from 'node:os';
|
|
5
14
|
import { basename, dirname, join, resolve } from 'node:path';
|
|
6
15
|
import { parseArgs } from '../lib/argv.mjs';
|
|
7
16
|
import { runAdopt, runLink, runStatus, runUnlink } from '../lib/design-link.mjs';
|
|
8
17
|
import { writeGitignoreBlock } from '../lib/gitignore-block.mjs';
|
|
18
|
+
import { isCompiledBinary } from '../lib/pkg-root.mjs';
|
|
9
19
|
|
|
10
20
|
const SUBCOMMANDS = new Set([
|
|
11
21
|
'serve',
|
|
@@ -77,6 +87,14 @@ const BIN_VERBS = new Set([
|
|
|
77
87
|
// reads the clip as a file:// resource).
|
|
78
88
|
'ingest-footage',
|
|
79
89
|
'probe-footage',
|
|
90
|
+
// feature-scene-aware-keyframes. `smart-frames` is the scene-AWARE sibling of
|
|
91
|
+
// `probe-footage`: it selects keyframes at scene cuts + semantic action beats +
|
|
92
|
+
// endpoints (three tiers, auto-detected — gemma scout / ffmpeg scene-detect /
|
|
93
|
+
// blind Chromium floor) instead of a blind frame rate, and emits a SUPERSET of
|
|
94
|
+
// probe-footage's manifest (drop-in for the footage-analyst). No dev server.
|
|
95
|
+
// Owned by skill `footage-keyframes`; --engine auto self-detects deps so it
|
|
96
|
+
// works standalone as a terminal video analyzer.
|
|
97
|
+
'smart-frames',
|
|
80
98
|
// feature-ai-media-generation (Phase 0, DDR-16x). `generate` is the thin
|
|
81
99
|
// non-browser BYOK verb (curl → /_api/generate-jobs → poll → print the
|
|
82
100
|
// produced /assets/<sha8>.<ext>). The provider call happens server-side (the
|
|
@@ -133,6 +151,89 @@ export async function run({ args, pkgRoot }) {
|
|
|
133
151
|
// pass straight through — preserves `$(maude design slug …)` capture,
|
|
134
152
|
// `eval "$(maude design prep --shell-export …)"`, and non-zero gating idioms.
|
|
135
153
|
// maude writes nothing of its own on this path (no banner on stdout).
|
|
154
|
+
/** True if a `bun` executable is reachable on the given PATH string (POSIX). */
|
|
155
|
+
function bunOnPath(pathStr) {
|
|
156
|
+
for (const dir of (pathStr || '').split(':')) {
|
|
157
|
+
if (dir && existsSync(join(dir, 'bun'))) return true;
|
|
158
|
+
}
|
|
159
|
+
return false;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/** A bun-capable compiled binary to borrow as `bun` (RCA G3), or null.
|
|
163
|
+
* Prefer the dev-server binary the sidecar hands us, else our own compiled
|
|
164
|
+
* self (the `maude` CLI is `bun --compile`d too), else a resolvable server bin. */
|
|
165
|
+
function resolveBunnable(pkgRoot) {
|
|
166
|
+
const dev = process.env.MAUDE_DEV_SERVER_BIN;
|
|
167
|
+
if (dev && existsSync(dev)) return dev;
|
|
168
|
+
if (isCompiledBinary()) return process.execPath;
|
|
169
|
+
const server = resolveServerBinary({ pkgRoot });
|
|
170
|
+
if (server && existsSync(server)) return server;
|
|
171
|
+
return null;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Ensure the design helpers' `command -v bun` / `exec bun run` resolve, even on a
|
|
176
|
+
* machine with NO standalone `bun` (RCA G3 — the desktop `.app` and the npm
|
|
177
|
+
* package ship compiled Bun BINARIES, never a `bun` CLI). The compiled binaries
|
|
178
|
+
* ARE Bun: run with `BUN_BE_BUN=1` a `bun --compile` executable behaves as the
|
|
179
|
+
* `bun` CLI. So if no real `bun` is on PATH, stage a tiny `bun` shim
|
|
180
|
+
* (→ `BUN_BE_BUN=1 <compiled-binary> "$@"`) in a per-user cache dir (0700) and
|
|
181
|
+
* PREPEND it to the child's PATH. No-op when a real `bun` is already present (dev)
|
|
182
|
+
* or no compiled binary exists to borrow (pure-source dev without bun — where a
|
|
183
|
+
* real `bun` is expected anyway). Returns the (possibly PATH-augmented) env.
|
|
184
|
+
*/
|
|
185
|
+
/** POSIX single-quote escape: wrap in '…' and encode embedded quotes as '\''.
|
|
186
|
+
* Neutralizes $, backticks, ${}, and every other sh metachar — unlike
|
|
187
|
+
* JSON.stringify, which leaves them live inside sh double-quotes. */
|
|
188
|
+
function shSingleQuote(s) {
|
|
189
|
+
return `'${String(s).replace(/'/g, "'\\''")}'`;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Provide a `bun` to a design helper that has none (RCA G3), then have the caller
|
|
194
|
+
* REMOVE it after the spawn. Returns `{ env, shimDir }` — `shimDir` is a fresh
|
|
195
|
+
* dir the caller must `rmSync` once the helper exits (null when no shim was made).
|
|
196
|
+
*
|
|
197
|
+
* Security (ethical-hacker review, DDR-177 addendum): the shim goes in a fresh,
|
|
198
|
+
* unpredictable, owner-only `mkdtemp` dir created PER SPAWN and torn down right
|
|
199
|
+
* after — NEVER a stable, guessable `~/.cache/maude/bun-shim/bun`. A stable path
|
|
200
|
+
* on a child's PATH with an unqualified `bun` lookup is the exact PATH-shadow
|
|
201
|
+
* primitive `sidecar.rs` deliberately refuses (Attacker-F4/DDR-168): an untrusted
|
|
202
|
+
* SAME-UID actor (in scope per DDR-054 — a poisoned dependency in an opened
|
|
203
|
+
* project, a prompt-injected agent's Bash tool) could pre-plant/symlink/race a
|
|
204
|
+
* stable shim and hijack the interpreter of every future helper. A per-spawn
|
|
205
|
+
* mkdtemp dir has no pre-existing entry to symlink-follow and no guessable name to
|
|
206
|
+
* race, and is gone before the next spawn. Fails CLOSED with no HOME (never plants
|
|
207
|
+
* an executable in world-writable /tmp).
|
|
208
|
+
*/
|
|
209
|
+
function ensureBunOnPath(childEnv, pkgRoot) {
|
|
210
|
+
if (bunOnPath(childEnv.PATH)) return { env: childEnv, shimDir: null };
|
|
211
|
+
const bunnable = resolveBunnable(pkgRoot);
|
|
212
|
+
if (!bunnable) return { env: childEnv, shimDir: null }; // helper's own guard reports the miss
|
|
213
|
+
const home = homedir();
|
|
214
|
+
if (!home) return { env: childEnv, shimDir: null }; // fail closed — no /tmp fallback for an executable
|
|
215
|
+
try {
|
|
216
|
+
const cacheParent = join(home, '.cache', 'maude');
|
|
217
|
+
mkdirSync(cacheParent, { recursive: true, mode: 0o700 });
|
|
218
|
+
// Fresh + unpredictable + 0700, one per spawn. mkdtemp guarantees a brand-new
|
|
219
|
+
// dir (fails if it exists), so there's nothing to symlink-follow on the write.
|
|
220
|
+
const shimDir = mkdtempSync(join(cacheParent, 'bun-shim-'));
|
|
221
|
+
const shim = join(shimDir, 'bun');
|
|
222
|
+
// Single-quote the binary path (NOT JSON.stringify — that leaves $/backticks
|
|
223
|
+
// live inside sh double-quotes, so a binary path with shell metacharacters
|
|
224
|
+
// would run command substitution when the shim executes).
|
|
225
|
+
writeFileSync(
|
|
226
|
+
shim,
|
|
227
|
+
`#!/bin/sh\n# maude bun shim (RCA G3) — the bundled compiled binary IS Bun;\n# BUN_BE_BUN=1 makes it act as the \`bun\` CLI so design helpers run with no\n# user-installed bun. Ephemeral: written per-spawn, removed after (design.mjs).\nBUN_BE_BUN=1 exec ${shSingleQuote(bunnable)} "$@"\n`,
|
|
228
|
+
{ mode: 0o755 }
|
|
229
|
+
);
|
|
230
|
+
chmodSync(shim, 0o755);
|
|
231
|
+
return { env: { ...childEnv, PATH: `${shimDir}:${childEnv.PATH || ''}` }, shimDir };
|
|
232
|
+
} catch {
|
|
233
|
+
return { env: childEnv, shimDir: null }; // best-effort; the helper reports the miss
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
136
237
|
function runBinDispatch(verb, { args, pkgRoot }) {
|
|
137
238
|
if (!BIN_VERBS.has(verb)) {
|
|
138
239
|
process.stderr.write(`maude design: "${verb}" is not a dev-tooling verb.\n${usage()}`);
|
|
@@ -164,10 +265,22 @@ function runBinDispatch(verb, { args, pkgRoot }) {
|
|
|
164
265
|
const bin = resolveServerBinary({ pkgRoot });
|
|
165
266
|
if (bin) childEnv.MAUDE_DEV_SERVER_BIN = bin;
|
|
166
267
|
}
|
|
268
|
+
// RCA G3 — make `bun` resolvable to the helper from the bundled compiled binary
|
|
269
|
+
// when no standalone `bun` is installed (desktop / npm). No-op in dev. The shim
|
|
270
|
+
// dir is ephemeral (per-spawn mkdtemp) — remove it as soon as the helper exits,
|
|
271
|
+
// so nothing bun-named lingers on a guessable path (ethical-hacker review).
|
|
272
|
+
const { env: finalEnv, shimDir } = ensureBunOnPath(childEnv, pkgRoot);
|
|
167
273
|
const child = spawnSync('bash', [script, ...rest], {
|
|
168
274
|
stdio: 'inherit',
|
|
169
|
-
env:
|
|
275
|
+
env: finalEnv,
|
|
170
276
|
});
|
|
277
|
+
if (shimDir) {
|
|
278
|
+
try {
|
|
279
|
+
rmSync(shimDir, { recursive: true, force: true });
|
|
280
|
+
} catch {
|
|
281
|
+
/* best-effort cleanup */
|
|
282
|
+
}
|
|
283
|
+
}
|
|
171
284
|
if (child.error) {
|
|
172
285
|
process.stderr.write(`maude design ${verb}: ${child.error.message}\n`);
|
|
173
286
|
process.exit(1);
|
|
@@ -185,7 +298,7 @@ Dev-tooling (dispatch to the dev-server bash helpers — DDR-062):
|
|
|
185
298
|
screenshot · server-up · prep · slug · bootstrap-check · runtime-health
|
|
186
299
|
smoke · canvas-edit · handoff · asset-sweep · visual-sanity · fetch-asset
|
|
187
300
|
draw-build · draw-proof · svg-optimize · to-lottie · read-annotations · annotate
|
|
188
|
-
canvas-rects · ingest-footage · probe-footage
|
|
301
|
+
canvas-rects · ingest-footage · probe-footage · smart-frames
|
|
189
302
|
generate · transcribe · audio-search
|
|
190
303
|
Invoke the bundled helper of the same name. maude resolves it from its
|
|
191
304
|
own package root and sets CLAUDE_PLUGIN_ROOT for the child; stdout,
|
package/cli/lib/pkg-root.mjs
CHANGED
|
@@ -59,14 +59,41 @@ export function isPkgRoot(dir) {
|
|
|
59
59
|
|
|
60
60
|
/**
|
|
61
61
|
* Resolve maude's own package root — real disk path, safe inside a compiled
|
|
62
|
-
* binary. Priority: (0) explicit override
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
* `@1agh/maude-<platform>/maude`
|
|
66
|
-
* `apps/studio/` via `package.json`
|
|
67
|
-
*
|
|
68
|
-
*
|
|
62
|
+
* binary. Priority: (0) explicit `MAUDE_PKG_ROOT` override (sidecar.rs sets this
|
|
63
|
+
* on the desktop), (1) dev-mode `import.meta.url` (plain `node`/`bun run
|
|
64
|
+
* cli/bin/maude.mjs`), (2) compiled-binary walk-up from `process.execPath`
|
|
65
|
+
* (matches the npm-install layout: binary at `@1agh/maude-<platform>/maude`
|
|
66
|
+
* walks up to `@1agh/maude/`, which ships `apps/studio/` via `package.json`
|
|
67
|
+
* `files`), (3) the Tauri **`.app` sibling probe** below.
|
|
68
|
+
*
|
|
69
|
+
* Why (3) exists: in a packaged `Maude.app` the bundled `maude` binary lives in
|
|
70
|
+
* `Contents/MacOS/` but the staged package tree (`apps/studio/`, `cli/`,
|
|
71
|
+
* `package.json`) lives in the SIBLING `Contents/Resources/` — NOT an ancestor
|
|
72
|
+
* of the binary, so the walk-up in (2) never reaches it and used to fall through
|
|
73
|
+
* to `dirname(execPath)` = `Contents/MacOS/`, breaking every `maude design
|
|
74
|
+
* <verb>` with "helper not found" (RCA issue-acp-panel-hangs-on-nodeless-machine
|
|
75
|
+
* G2, reproduced on the shipped v0.45.1 `.app`). The earlier claim that the
|
|
76
|
+
* binary + `Resources/apps/studio` "share a common ancestor within the walk-up
|
|
77
|
+
* depth" was WRONG — `Contents/` is the common ancestor but `apps/studio` sits
|
|
78
|
+
* under `Contents/Resources/`, not `Contents/`, and `cli/` isn't in the ancestry
|
|
79
|
+
* at all. We probe the known Tauri resource-dir siblings explicitly instead.
|
|
69
80
|
*/
|
|
81
|
+
/**
|
|
82
|
+
* Probe the Tauri packaged-app resource dir relative to the binary's own dir.
|
|
83
|
+
* From `<app>/Contents/MacOS/maude`, the staged package tree is at the SIBLING
|
|
84
|
+
* `<app>/Contents/Resources`. Covers macOS (`Resources`), the lower-cased
|
|
85
|
+
* resource dir other Tauri targets use, and a binary co-located with a
|
|
86
|
+
* `resources/` dir (dev staging). Returns the first valid pkgRoot, or null.
|
|
87
|
+
* Exported so the RCA-G2 fix is unit-testable without spawning a real `.app`.
|
|
88
|
+
*/
|
|
89
|
+
export function resolveTauriResourcePkgRoot(execDir) {
|
|
90
|
+
for (const rel of [['..', 'Resources'], ['..', 'resources'], ['resources']]) {
|
|
91
|
+
const cand = join(execDir, ...rel);
|
|
92
|
+
if (isPkgRoot(cand)) return cand;
|
|
93
|
+
}
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
|
|
70
97
|
export function resolvePkgRoot() {
|
|
71
98
|
const override = process.env.MAUDE_PKG_ROOT;
|
|
72
99
|
if (override && !isVirtualBunfsPath(override) && isPkgRoot(override)) return override;
|
|
@@ -84,12 +111,13 @@ export function resolvePkgRoot() {
|
|
|
84
111
|
// from there (e.g. ~/Library/Caches/…/bin-link/) never reaches the repo
|
|
85
112
|
// root. Dereference first. Falls back to the raw path if the file somehow
|
|
86
113
|
// doesn't exist (shouldn't happen — we ARE that running process).
|
|
87
|
-
let
|
|
114
|
+
let execDir;
|
|
88
115
|
try {
|
|
89
|
-
|
|
116
|
+
execDir = dirname(realpathSync(process.execPath));
|
|
90
117
|
} catch {
|
|
91
|
-
|
|
118
|
+
execDir = dirname(process.execPath);
|
|
92
119
|
}
|
|
120
|
+
let cur = execDir;
|
|
93
121
|
for (let i = 0; i < 10; i++) {
|
|
94
122
|
if (isPkgRoot(cur)) return cur;
|
|
95
123
|
const parent = dirname(cur);
|
|
@@ -97,6 +125,10 @@ export function resolvePkgRoot() {
|
|
|
97
125
|
cur = parent;
|
|
98
126
|
}
|
|
99
127
|
|
|
128
|
+
// (3) Tauri `.app` sibling probe — the packaged layout the walk-up can't reach.
|
|
129
|
+
const sibling = resolveTauriResourcePkgRoot(execDir);
|
|
130
|
+
if (sibling) return sibling;
|
|
131
|
+
|
|
100
132
|
// Final fallback for unanchored test contexts — callers should expect
|
|
101
133
|
// existsSync-based lookups to fail and surface a clear error, same
|
|
102
134
|
// contract as paths.ts's own resolver. Never join a VIRTUAL importDir here
|
|
@@ -16,7 +16,7 @@ import { tmpdir } from 'node:os';
|
|
|
16
16
|
import { join } from 'node:path';
|
|
17
17
|
import { test } from 'node:test';
|
|
18
18
|
|
|
19
|
-
import { isPkgRoot, resolvePkgRoot } from './pkg-root.mjs';
|
|
19
|
+
import { isPkgRoot, resolvePkgRoot, resolveTauriResourcePkgRoot } from './pkg-root.mjs';
|
|
20
20
|
|
|
21
21
|
function makeRealRoot(base) {
|
|
22
22
|
mkdirSync(join(base, 'apps', 'studio', 'bin'), { recursive: true });
|
|
@@ -52,6 +52,38 @@ test('isPkgRoot requires BOTH anchors — the staged-resources false-positive is
|
|
|
52
52
|
}
|
|
53
53
|
});
|
|
54
54
|
|
|
55
|
+
test('resolveTauriResourcePkgRoot finds the .app sibling Resources tree (RCA G2)', () => {
|
|
56
|
+
// Mirror the packaged macOS layout: binary in Contents/MacOS, staged tree in
|
|
57
|
+
// the SIBLING Contents/Resources — NOT an ancestor of the binary, which is why
|
|
58
|
+
// the plain walk-up misses it and every `maude design <verb>` broke.
|
|
59
|
+
const app = mkdtempSync(join(tmpdir(), 'maude-app-'));
|
|
60
|
+
try {
|
|
61
|
+
const macosDir = join(app, 'Contents', 'MacOS');
|
|
62
|
+
const resources = join(app, 'Contents', 'Resources');
|
|
63
|
+
mkdirSync(macosDir, { recursive: true });
|
|
64
|
+
makeRealRoot(resources); // stages apps/studio/bin/screenshot.sh + cli/commands/design.mjs
|
|
65
|
+
|
|
66
|
+
// From the binary's own dir, the sibling probe must find Resources.
|
|
67
|
+
assert.equal(resolveTauriResourcePkgRoot(macosDir), resources);
|
|
68
|
+
|
|
69
|
+
// Regression guard: Resources WITHOUT cli/ (the shipped-broken v0.45.1 shape)
|
|
70
|
+
// must NOT resolve — that's exactly the false-positive isPkgRoot rejects.
|
|
71
|
+
const broken = mkdtempSync(join(tmpdir(), 'maude-app-broken-'));
|
|
72
|
+
try {
|
|
73
|
+
const bMacos = join(broken, 'Contents', 'MacOS');
|
|
74
|
+
const bRes = join(broken, 'Contents', 'Resources', 'apps', 'studio', 'bin');
|
|
75
|
+
mkdirSync(bMacos, { recursive: true });
|
|
76
|
+
mkdirSync(bRes, { recursive: true });
|
|
77
|
+
writeFileSync(join(bRes, 'screenshot.sh'), '#!/bin/sh\n'); // no cli/ staged
|
|
78
|
+
assert.equal(resolveTauriResourcePkgRoot(bMacos), null);
|
|
79
|
+
} finally {
|
|
80
|
+
rmSync(broken, { recursive: true, force: true });
|
|
81
|
+
}
|
|
82
|
+
} finally {
|
|
83
|
+
rmSync(app, { recursive: true, force: true });
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
|
|
55
87
|
test('resolvePkgRoot honors MAUDE_PKG_ROOT only when it actually satisfies both anchors', () => {
|
|
56
88
|
const real = mkdtempSync(join(tmpdir(), 'maude-pkgroot-real-'));
|
|
57
89
|
const fake = mkdtempSync(join(tmpdir(), 'maude-pkgroot-fake-'));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@1agh/maude",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.46.0",
|
|
4
4
|
"description": "Marketplace of Claude Code plugins by Michal Dovrtěl: `design` (canvas-first design iteration) + `flow` (generic agentic workflow loop with .ai second brain). Ships the `maude` CLI (with `mdcc` legacy alias) to scaffold workspace, run the design dev server, and manage configs.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
@@ -51,13 +51,13 @@
|
|
|
51
51
|
"test:e2e:desktop:acp-cold-start": "pnpm --filter @maude/desktop-e2e e2e:acp-cold-start"
|
|
52
52
|
},
|
|
53
53
|
"optionalDependencies": {
|
|
54
|
-
"@1agh/maude-darwin-arm64": "0.
|
|
55
|
-
"@1agh/maude-darwin-x64": "0.
|
|
56
|
-
"@1agh/maude-linux-arm64": "0.
|
|
57
|
-
"@1agh/maude-linux-arm64-musl": "0.
|
|
58
|
-
"@1agh/maude-linux-x64": "0.
|
|
59
|
-
"@1agh/maude-linux-x64-musl": "0.
|
|
60
|
-
"@1agh/maude-win32-x64": "0.
|
|
54
|
+
"@1agh/maude-darwin-arm64": "0.46.0",
|
|
55
|
+
"@1agh/maude-darwin-x64": "0.46.0",
|
|
56
|
+
"@1agh/maude-linux-arm64": "0.46.0",
|
|
57
|
+
"@1agh/maude-linux-arm64-musl": "0.46.0",
|
|
58
|
+
"@1agh/maude-linux-x64": "0.46.0",
|
|
59
|
+
"@1agh/maude-linux-x64-musl": "0.46.0",
|
|
60
|
+
"@1agh/maude-win32-x64": "0.46.0"
|
|
61
61
|
},
|
|
62
62
|
"files": [
|
|
63
63
|
"cli",
|
|
@@ -102,6 +102,8 @@
|
|
|
102
102
|
},
|
|
103
103
|
"dependencies": {
|
|
104
104
|
"ajv": "^8.20.0",
|
|
105
|
-
"ajv-formats": "^3.0.1"
|
|
105
|
+
"ajv-formats": "^3.0.1",
|
|
106
|
+
"happy-dom": "20.10.6",
|
|
107
|
+
"svgo": "^4.0.1"
|
|
106
108
|
}
|
|
107
109
|
}
|
|
@@ -213,6 +213,41 @@
|
|
|
213
213
|
"fallbackBehavior": "feature-ai-media-generation Phase 2 (DDR-164) — `maude design transcribe` uses whisper.cpp for local, no-key, word-timestamped subtitles (the DEFAULT subtitle path). Without it, transcription falls back to cloud STT (ElevenLabs Scribe / Groq Whisper) via a provider key. whisper.cpp also needs a ggml model file (--model / $MAUDE_WHISPER_MODEL); it ships no default and does not auto-download — see https://huggingface.co/ggerganov/whisper.cpp (e.g. ggml-large-v3-turbo.bin).",
|
|
214
214
|
"usedBy": ["dev-server/bin/transcribe.sh", "dev-server/bin/_transcribe.mjs"],
|
|
215
215
|
"docsUrl": "https://github.com/ggml-org/whisper.cpp"
|
|
216
|
+
},
|
|
217
|
+
{
|
|
218
|
+
"id": "ffmpeg",
|
|
219
|
+
"type": "cli",
|
|
220
|
+
"hardness": "soft",
|
|
221
|
+
"check": {
|
|
222
|
+
"command": "ffmpeg -version",
|
|
223
|
+
"expectExit": 0
|
|
224
|
+
},
|
|
225
|
+
"install": {
|
|
226
|
+
"darwin": "brew install ffmpeg",
|
|
227
|
+
"linux": "apt-get install -y ffmpeg || dnf install -y ffmpeg",
|
|
228
|
+
"preferred": "https://ffmpeg.org/download.html"
|
|
229
|
+
},
|
|
230
|
+
"autoInstall": false,
|
|
231
|
+
"fallbackBehavior": "feature-scene-aware-keyframes — the `ffmpeg` tier of `maude design smart-frames` (skill footage-keyframes) uses ffmpeg scene-detection to pick keyframes at real shot cuts. Without it, the extractor falls back to the blind Chromium tier (probe-footage, even-spaced) — still works, just not scene-aware.",
|
|
232
|
+
"usedBy": ["dev-server/bin/smart-frames.sh", "dev-server/bin/_smart-frames.mjs"],
|
|
233
|
+
"docsUrl": "https://ffmpeg.org"
|
|
234
|
+
},
|
|
235
|
+
{
|
|
236
|
+
"id": "mlx-vlm",
|
|
237
|
+
"type": "cli",
|
|
238
|
+
"hardness": "soft",
|
|
239
|
+
"check": {
|
|
240
|
+
"command": "python3 -c \"import mlx_vlm\"",
|
|
241
|
+
"expectExit": 0
|
|
242
|
+
},
|
|
243
|
+
"install": {
|
|
244
|
+
"darwin": "pip install mlx-vlm",
|
|
245
|
+
"preferred": "https://github.com/Blaizzy/mlx-vlm"
|
|
246
|
+
},
|
|
247
|
+
"autoInstall": false,
|
|
248
|
+
"fallbackBehavior": "feature-scene-aware-keyframes — OPT-IN. The `gemma` tier of `maude design smart-frames` runs a local Gemma-4 MLX scout (Apple Silicon only) to flag semantic action beats. Without it, smart-frames uses the ffmpeg or blind tier — no model download required. The scout also needs a Gemma-4 MLX model (--engine gemma / $MAUDE_GEMMA_MODEL, one-click in Studio Settings → Scene-aware keyframes).",
|
|
249
|
+
"usedBy": ["dev-server/bin/_smart-frames.mjs"],
|
|
250
|
+
"docsUrl": "https://github.com/Blaizzy/mlx-vlm"
|
|
216
251
|
}
|
|
217
252
|
]
|
|
218
253
|
}
|