@1agh/maude 0.50.0 → 0.51.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.
@@ -27,6 +27,7 @@ import { TranspileError } from './canvas-pipeline.ts';
27
27
  import type { AiActivity } from './collab/ai-activity.ts';
28
28
  import type { Context } from './context.ts';
29
29
  import { reloadConfig } from './context.ts';
30
+ import { buildDebugBundle } from './debug-bundle.ts';
30
31
  import { probeSetupReadiness } from './design-setup-readiness.ts';
31
32
  import { type Format, isFormat, isScope, type Scope } from './exporters/index.ts';
32
33
  import { type ExportJobQueue, ExportQueueFullError } from './exporters/jobs.ts';
@@ -88,7 +89,7 @@ import { getRuntimeBundle, packageForSlug } from './runtime-bundle.ts';
88
89
  import { linkHub } from './sync/hub-link.ts';
89
90
  import { signInToWorkspace, workspaceDisclosure } from './sync/workspace-signin.ts';
90
91
  import { readUiPrefs, type UiPrefs, writeUiPrefs } from './ui-prefs.ts';
91
- import { loadWhatsNew } from './whats-new.ts';
92
+ import { loadWhatsNew, resolveMaudeVersion } from './whats-new.ts';
92
93
  import { isLoopbackHost } from './ws.ts';
93
94
 
94
95
  // Real disk install root — never the virtual `/$bunfs/root` of compiled bins.
@@ -1170,6 +1171,84 @@ export function createHttp(
1170
1171
  '/_api/whats-new': () =>
1171
1172
  Response.json(loadWhatsNew(), { headers: { 'Cache-Control': 'no-store' } }),
1172
1173
 
1174
+ // feature-bug-report-button — the scrubbed diagnostic bundle the Report-a-Bug
1175
+ // dialog previews for consent. PRIVILEGED (logs could reveal paths pre-scrub;
1176
+ // the scrubber runs here, server-side): MAIN-ORIGIN ONLY — absent from
1177
+ // CANVAS_SAFE_API + startCanvasServer routes — plus the same double gate as
1178
+ // /_api/acp/status (a drive-by page must not read logs via DNS rebinding).
1179
+ '/_api/debug-bundle': (req: Request) => {
1180
+ if (!sameOriginRead(req)) return new Response('cross-origin rejected', { status: 403 });
1181
+ if (!isLoopbackHost(req.headers.get('host')))
1182
+ return new Response('local request required (DNS-rebinding guard)', { status: 403 });
1183
+ return Response.json(
1184
+ buildDebugBundle({
1185
+ maudeVersion: resolveMaudeVersion(),
1186
+ projectName: ctx.cfg.name ?? null,
1187
+ activeCanvas: (inspect.state as { active?: string | null }).active ?? null,
1188
+ repoRoot: ctx.paths.repoRoot,
1189
+ }),
1190
+ { headers: { 'Cache-Control': 'no-store' } }
1191
+ );
1192
+ },
1193
+
1194
+ // feature-bug-report-button — submit proxy. The client never talks to
1195
+ // cloud.maude.sh directly (no CORS surface to open, and the endpoint can be
1196
+ // overridden for self-hosters/tests via MAUDE_REPORT_URL). Forwards the
1197
+ // multipart body verbatim; the cloud route owns validation + quota.
1198
+ // PRIVILEGED: main-origin only + double gate (it triggers outbound network).
1199
+ '/_api/report': async (req: Request) => {
1200
+ if (req.method !== 'POST') return new Response('Method not allowed', { status: 405 });
1201
+ if (!sameOriginWrite(req)) return new Response('cross-origin rejected', { status: 403 });
1202
+ if (!isLoopbackHost(req.headers.get('host')))
1203
+ return new Response('local request required (DNS-rebinding guard)', { status: 403 });
1204
+ const endpoint = process.env.MAUDE_REPORT_URL || 'https://cloud.maude.sh/report';
1205
+ try {
1206
+ const upstream = await fetch(endpoint, {
1207
+ method: 'POST',
1208
+ body: await req.formData(),
1209
+ signal: AbortSignal.timeout(30_000),
1210
+ });
1211
+ const body = await upstream.text();
1212
+ return new Response(body, {
1213
+ status: upstream.status,
1214
+ headers: { 'content-type': 'application/json', 'Cache-Control': 'no-store' },
1215
+ });
1216
+ } catch {
1217
+ // Unreachable/timeout — the client falls back to the local bundle path.
1218
+ return Response.json({ error: 'report service unreachable' }, { status: 502 });
1219
+ }
1220
+ },
1221
+
1222
+ // feature-bug-report-button — offline/declined fallback: persist the bundle
1223
+ // locally under `<designRoot>/_reports/<ts>/` (gitignored runtime state,
1224
+ // DDR-115 taxonomy) so the user can attach it to a hand-filed issue.
1225
+ // PRIVILEGED: main-origin only + double gate (filesystem write).
1226
+ '/_api/report-fallback': async (req: Request) => {
1227
+ if (req.method !== 'POST') return new Response('Method not allowed', { status: 405 });
1228
+ if (!sameOriginWrite(req)) return new Response('cross-origin rejected', { status: 403 });
1229
+ if (!isLoopbackHost(req.headers.get('host')))
1230
+ return new Response('local request required (DNS-rebinding guard)', { status: 403 });
1231
+ const body = await readJson<{ report?: unknown; screenshots?: unknown }>(req);
1232
+ if (!body || typeof body.report !== 'object' || body.report === null) {
1233
+ return new Response('body must include { report }', { status: 400 });
1234
+ }
1235
+ const shots = Array.isArray(body.screenshots) ? body.screenshots.slice(0, 3) : [];
1236
+ const dir = join(ctx.paths.designRoot, '_reports', String(Date.now()));
1237
+ await Bun.write(join(dir, 'report.json'), JSON.stringify(body.report, null, 2));
1238
+ let n = 0;
1239
+ for (const shot of shots) {
1240
+ if (typeof shot !== 'string') continue;
1241
+ const m = /^data:image\/(png|jpeg);base64,([A-Za-z0-9+/=]+)$/.exec(shot);
1242
+ if (!m || m[2].length > 7_000_000) continue; // ~5 MB decoded cap
1243
+ n += 1;
1244
+ await Bun.write(join(dir, `screenshot-${n}.${m[1] === 'png' ? 'png' : 'jpg'}`), Buffer.from(m[2], 'base64'));
1245
+ }
1246
+ return Response.json(
1247
+ { dir: relative(ctx.paths.repoRoot, dir), screenshots: n },
1248
+ { headers: { 'Cache-Control': 'no-store' } }
1249
+ );
1250
+ },
1251
+
1173
1252
  '/_index-data': async () =>
1174
1253
  Response.json(await api.buildIndexData(), { headers: { 'Cache-Control': 'no-store' } }),
1175
1254
 
@@ -1775,6 +1854,18 @@ export function createHttp(
1775
1854
  const body = (await req.json().catch(() => ({}))) as { project?: string };
1776
1855
  return gitJson(await cloudApi.attach(String(body.project ?? '')));
1777
1856
  },
1857
+ '/_api/cloud/attach/code': async (req: Request) => {
1858
+ // The maude:// lane (Phase 17): a one-time handoff code from a deep
1859
+ // link. Same gates as attach; the code is exchanged only against the
1860
+ // CONFIGURED cloud address, never one the link named.
1861
+ if (req.method !== 'POST') return new Response('Method not allowed', { status: 405 });
1862
+ if (!sameOriginWrite(req))
1863
+ return new Response('cross-origin write rejected', { status: 403 });
1864
+ if (!isLoopbackHost(req.headers.get('host')))
1865
+ return new Response('local request required', { status: 403 });
1866
+ const body = (await req.json().catch(() => ({}))) as { code?: string };
1867
+ return gitJson(await cloudApi.attachCode(String(body.code ?? '')));
1868
+ },
1778
1869
 
1779
1870
  '/_api/github/identity': async (req: Request) => {
1780
1871
  if (req.method !== 'GET') return new Response('Method not allowed', { status: 405 });
@@ -53,6 +53,7 @@ export type SignInFailure =
53
53
  | 'unreachable'
54
54
  | 'not-a-maude-workspace'
55
55
  | 'bad-credentials'
56
+ | 'cloud-identity'
56
57
  | 'rate-limited'
57
58
  | 'save-failed'
58
59
  | 'server-error';
@@ -148,6 +149,20 @@ export async function signInToWorkspace(
148
149
  if (res.status === 429) {
149
150
  return fail(429, 'Too many attempts. Wait a minute and try again.', 'rate-limited');
150
151
  }
152
+ if (res.status === 400) {
153
+ // The ONE refusal that comes with directions (Phase 23 B3): a cloud
154
+ // workspace saying "this door signs in through Maude Cloud" — or a viewer
155
+ // being told what viewing needs. Swallowing it into "try again shortly"
156
+ // turned the most actionable message on this path into a shrug.
157
+ let message = 'The workspace refused the sign-in.';
158
+ try {
159
+ const body = (await res.json()) as { error?: unknown };
160
+ if (typeof body.error === 'string' && body.error) message = body.error;
161
+ } catch {
162
+ /* keep the generic sentence */
163
+ }
164
+ return fail(400, message, 'cloud-identity');
165
+ }
151
166
  if (!res.ok) {
152
167
  return fail(502, 'The workspace refused the sign-in. Try again shortly.', 'server-error');
153
168
  }
@@ -0,0 +1,130 @@
1
+ // The dev-server's Maude Cloud lane — Cloud Phase 23 C3 + the maude:// code
2
+ // attach (Phase 17). The control plane AND the cell are stubbed on one local
3
+ // server; the assertions are about custody and trust posture, not transport:
4
+ // the code is exchanged only against the CONFIGURED cloud address, the hub
5
+ // token lands in the credential store, and linkedHub is written token-free.
6
+
7
+ import { afterAll, beforeAll, describe, expect, test } from 'bun:test';
8
+ import { mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
9
+ import { tmpdir } from 'node:os';
10
+ import { join } from 'node:path';
11
+
12
+ import { parseDeepLink, shareViewUrl } from '../client/panels/CloudBar.jsx';
13
+
14
+ let stub: ReturnType<typeof Bun.serve> | null = null;
15
+ let scratch = '';
16
+ const seen: string[] = [];
17
+
18
+ beforeAll(() => {
19
+ scratch = mkdtempSync(join(tmpdir(), 'maude-cloud-test-'));
20
+ stub = Bun.serve({
21
+ port: 0,
22
+ hostname: '127.0.0.1',
23
+ fetch: async (req) => {
24
+ const url = new URL(req.url);
25
+ seen.push(`${req.method} ${url.pathname}`);
26
+ const json = (b: unknown, status = 200) =>
27
+ new Response(JSON.stringify(b), { status, headers: { 'content-type': 'application/json' } });
28
+ if (url.pathname === '/auth/handoff/exchange') {
29
+ const body: any = await req.json();
30
+ if (body.code === 'mhc_' + 'a'.repeat(64)) {
31
+ return json({
32
+ token: 'x.y',
33
+ role: 'owner',
34
+ project: 'stub-project',
35
+ // The "cell" is this same stub server.
36
+ url: `http://127.0.0.1:${stub!.port}`,
37
+ expiresAt: Date.now() + 3600_000,
38
+ });
39
+ }
40
+ return json({ error: 'that code is not valid or has expired' }, 400);
41
+ }
42
+ if (url.pathname === '/auth/login') {
43
+ const body: any = await req.json();
44
+ return body?.token === 'x.y'
45
+ ? json({ token: 'mau_stub_hub_token', user: { email: 'e@example.com', role: 'owner' } })
46
+ : json({ error: 'no' }, 401);
47
+ }
48
+ return json({ error: 'not stubbed' }, 404);
49
+ },
50
+ });
51
+ process.env.MAUDE_CLOUD_URL = `http://127.0.0.1:${stub.port}`;
52
+ process.env.MAUDE_CLOUD_CONFIG = join(scratch, 'cloud.json');
53
+ process.env.HUBS_CONFIG_PATH = join(scratch, 'hubs.json');
54
+ });
55
+
56
+ afterAll(() => {
57
+ stub?.stop(true);
58
+ rmSync(scratch, { recursive: true, force: true });
59
+ delete process.env.MAUDE_CLOUD_URL;
60
+ delete process.env.MAUDE_CLOUD_CONFIG;
61
+ delete process.env.HUBS_CONFIG_PATH;
62
+ });
63
+
64
+ describe('parseDeepLink — untrusted input, strict shape', () => {
65
+ test('accepts exactly the minted shape', () => {
66
+ const p = parseDeepLink(`maude://open/alligators?code=mhc_${'a'.repeat(64)}&origin=https://cloud.maude.sh`);
67
+ expect(p).toEqual({ project: 'alligators', code: `mhc_${'a'.repeat(64)}` });
68
+ });
69
+ test('refuses everything else', () => {
70
+ for (const bad of [
71
+ 'maude://open/alligators', // no code
72
+ 'maude://open/Alligators?code=mhc_' + 'a'.repeat(64), // bad slug
73
+ 'maude://open/x?code=nothex', // bad code
74
+ 'https://evil.example/?code=mhc_' + 'a'.repeat(64), // not maude://
75
+ 'maude://join/x/y', // join is not the open shape
76
+ ]) {
77
+ expect(parseDeepLink(bad)).toBeNull();
78
+ }
79
+ });
80
+ });
81
+
82
+ describe('shareViewUrl — the viewer’s browser home', () => {
83
+ test('derives view-<host> from the project url', () => {
84
+ expect(shareViewUrl('https://alligators.cloud.maude.sh', 'alligators')).toBe(
85
+ 'https://view-alligators.cloud.maude.sh'
86
+ );
87
+ });
88
+ test('falls back to the platform zone on garbage', () => {
89
+ expect(shareViewUrl('nonsense', 'p1')).toBe('https://view-p1.cloud.maude.sh');
90
+ });
91
+ });
92
+
93
+ describe('attachCode — the maude:// lane end to end (stubbed)', () => {
94
+ test('a valid code links: hub credential stored, linkedHub written token-free', async () => {
95
+ const { createCloudEndpoints } = await import('../cloud/endpoints.ts');
96
+ const designRoot = join(scratch, '.design');
97
+ const { mkdirSync } = await import('node:fs');
98
+ mkdirSync(designRoot, { recursive: true });
99
+ writeFileSync(join(designRoot, 'config.json'), '{}\n');
100
+
101
+ const apiEndpoints = createCloudEndpoints({ paths: { repoRoot: scratch, designRoot } });
102
+ const r = await apiEndpoints.attachCode(`mhc_${'a'.repeat(64)}`);
103
+ expect(r.status).toBe(200);
104
+ expect((r.json as any).ok).toBe(true);
105
+ expect((r.json as any).project).toBe('stub-project');
106
+
107
+ const cfg = JSON.parse(readFileSync(join(designRoot, 'config.json'), 'utf8'));
108
+ expect(cfg.linkedHub?.url).toContain('127.0.0.1');
109
+ expect(JSON.stringify(cfg)).not.toContain('mau_stub_hub_token');
110
+
111
+ // The exchange went to the CONFIGURED cloud address (this stub) — the
112
+ // only /auth/handoff/exchange call seen is ours.
113
+ expect(seen.filter((s) => s.includes('/auth/handoff/exchange')).length).toBe(1);
114
+ });
115
+
116
+ test('a malformed or dead code never reaches the network', async () => {
117
+ const { createCloudEndpoints } = await import('../cloud/endpoints.ts');
118
+ const apiEndpoints = createCloudEndpoints({
119
+ paths: { repoRoot: scratch, designRoot: join(scratch, '.design') },
120
+ });
121
+ const before = seen.length;
122
+ const r = await apiEndpoints.attachCode('not-a-code');
123
+ expect(r.status).toBe(400);
124
+ expect(seen.length).toBe(before);
125
+
126
+ const dead = await apiEndpoints.attachCode(`mhc_${'b'.repeat(64)}`);
127
+ expect(dead.status).toBe(410);
128
+ expect((dead.json as any).error).toMatch(/works once/);
129
+ });
130
+ });
@@ -0,0 +1,105 @@
1
+ // debug-bundle scrubber + ring — feature-bug-report-button.
2
+ //
3
+ // The scrubber is a PRIVACY GATE, not tidiness: docs/report-schema.md promises
4
+ // every log line is deterministically cleaned server-side before it can enter
5
+ // a report. Extend these tests FIRST when extending the scrubber.
6
+
7
+ import { describe, expect, test } from 'bun:test';
8
+
9
+ import {
10
+ buildDebugBundle,
11
+ installLogRing,
12
+ logRingLines,
13
+ recordLogLine,
14
+ scrub,
15
+ } from '../debug-bundle.ts';
16
+
17
+ describe('scrub()', () => {
18
+ test('repo-absolute paths collapse to <project>/ BEFORE home collapses to ~', () => {
19
+ const out = scrub('error at /Users/jane/git/acme/src/app.ts and /Users/jane/notes.txt', {
20
+ home: '/Users/jane',
21
+ repoRoot: '/Users/jane/git/acme',
22
+ });
23
+ expect(out).toBe('error at <project>/src/app.ts and ~/notes.txt');
24
+ });
25
+
26
+ test('GitHub tokens (classic + fine-grained) are redacted', () => {
27
+ const out = scrub('using ghp_AbC123xyzAbC123xyz and github_pat_11ABCDEF0abcdef', {
28
+ home: '/h',
29
+ });
30
+ expect(out).not.toContain('ghp_');
31
+ expect(out).not.toContain('github_pat_');
32
+ expect(out).toContain('[redacted]');
33
+ });
34
+
35
+ test('bearer headers, sk- keys and JWTs are redacted', () => {
36
+ const out = scrub(
37
+ 'sent Bearer abc.def.ghi then sk-ant-api03-longsecretvalue then eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxIn0.sig-part',
38
+ { home: '/h' }
39
+ );
40
+ expect(out).toContain('Bearer [redacted]');
41
+ expect(out).not.toContain('sk-ant-api03');
42
+ expect(out).not.toContain('eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxIn0');
43
+ // An `Authorization:` header form over-redacts (key rule wins) — that is
44
+ // acceptable: privacy failures here are leaks, never extra redaction.
45
+ const hdr = scrub('Authorization: Bearer abc.def.ghi', { home: '/h' });
46
+ expect(hdr).not.toContain('abc.def.ghi');
47
+ });
48
+
49
+ test('secret-shaped key=value keeps the key, redacts the value', () => {
50
+ const out = scrub('token=abc123secret api_key: "xyz789" password=hunter2', { home: '/h' });
51
+ expect(out).toContain('token=[redacted]');
52
+ expect(out).toContain('password=[redacted]');
53
+ expect(out).not.toContain('abc123secret');
54
+ expect(out).not.toContain('hunter2');
55
+ });
56
+
57
+ test('emails are redacted', () => {
58
+ expect(scrub('reported by jane.doe+test@example.co.uk', { home: '/h' })).toBe(
59
+ 'reported by [email]'
60
+ );
61
+ });
62
+
63
+ test('clean text passes through untouched', () => {
64
+ const line = '[log] canvas ui/Dashboard.tsx rebuilt in 43ms';
65
+ expect(scrub(line, { home: '/h', repoRoot: '/r' })).toBe(line);
66
+ });
67
+ });
68
+
69
+ describe('log ring', () => {
70
+ test('caps at 500 lines, keeps the newest, tail selects the last N', () => {
71
+ for (let i = 0; i < 620; i++) recordLogLine(`line ${i}`);
72
+ const all = logRingLines(500);
73
+ expect(all.length).toBe(500);
74
+ expect(all[0]).toBe('line 120');
75
+ expect(all[all.length - 1]).toBe('line 619');
76
+ expect(logRingLines(3)).toEqual(['line 617', 'line 618', 'line 619']);
77
+ });
78
+
79
+ test('console tap mirrors into the ring and is idempotent', () => {
80
+ installLogRing();
81
+ installLogRing(); // second call must not double-wrap (no duplicate lines)
82
+ console.log('tap-check', { a: 1 });
83
+ const tail = logRingLines(5);
84
+ expect(tail.filter((l) => l.includes('tap-check')).length).toBe(1);
85
+ expect(tail.some((l) => l.includes('[log] tap-check {"a":1}'))).toBe(true);
86
+ });
87
+ });
88
+
89
+ describe('buildDebugBundle()', () => {
90
+ test('assembles scrubbed fields; canvas path scrubbed; no content leaks', () => {
91
+ recordLogLine('boot at /Users/jane/git/acme with token=verysecret');
92
+ const bundle = buildDebugBundle({
93
+ maudeVersion: '9.9.9',
94
+ projectName: 'acme',
95
+ activeCanvas: 'ui/Dashboard.tsx',
96
+ repoRoot: '/Users/jane/git/acme',
97
+ });
98
+ expect(bundle.app.maudeVersion).toBe('9.9.9');
99
+ expect(bundle.app.platform).toBe(process.platform);
100
+ expect(bundle.context.activeCanvas).toBe('ui/Dashboard.tsx');
101
+ expect(bundle.logs.serverLogTail).toContain('<project>');
102
+ expect(bundle.logs.serverLogTail).not.toContain('verysecret');
103
+ expect(bundle.logs.serverLogTail).not.toContain('/Users/jane/git/acme');
104
+ });
105
+ });
@@ -164,6 +164,25 @@ describe('failure modes are distinguishable and actionable', () => {
164
164
  expect((res.json as { error: string }).error).toMatch(/wait a minute/i);
165
165
  });
166
166
 
167
+ test("a cloud workspace's 400 message reaches the person verbatim (Phase 23 B3)", async () => {
168
+ // The hub's cloud-identity refusal comes WITH directions ("sign in through
169
+ // Maude Cloud" / what viewing needs). Swallowing it into "try again
170
+ // shortly" turned the most actionable message on this path into a shrug.
171
+ const { impl } = scriptedFetch({
172
+ '/health': HEALTHY,
173
+ '/auth/login': () =>
174
+ json(
175
+ { error: 'This workspace signs in through Maude Cloud — use Sign in to Maude Cloud in the app.' },
176
+ 400
177
+ ),
178
+ });
179
+ const res = await signInToWorkspace(CREDS, { fetchImpl: impl, save: () => {} });
180
+ expect(res.status).toBe(400);
181
+ expect((res.json as { reason: string }).reason).toBe('cloud-identity');
182
+ expect((res.json as { error: string }).error).toMatch(/signs in through Maude Cloud/);
183
+ expect((res.json as { error: string }).error).not.toMatch(/try again shortly/i);
184
+ });
185
+
167
186
  test('a failed save is reported instead of pretending success', async () => {
168
187
  const { impl } = scriptedFetch({ '/health': HEALTHY, '/auth/login': LOGGED_IN });
169
188
  const res = await signInToWorkspace(CREDS, {
@@ -1,6 +1,15 @@
1
1
  {
2
2
  "$schema": "./whats-new.schema.json",
3
3
  "entries": [
4
+ {
5
+ "id": "maude-deeplink-open-in-app",
6
+ "version": "0.51.0",
7
+ "date": "2026-07-30",
8
+ "kind": "feature",
9
+ "title": "Open a cloud project straight from the browser",
10
+ "summary": "Press “Open in Maude” on your project’s page and the app opens it signed in — a one-time link, nothing to copy. The app always asks before connecting, and viewers get a working “View in the browser” door instead of a dead button.",
11
+ "surface": "design-ui"
12
+ },
4
13
  {
5
14
  "id": "maude-cloud-signin-attach",
6
15
  "version": "0.50.0",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@1agh/maude",
3
- "version": "0.50.0",
3
+ "version": "0.51.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": {
@@ -25,6 +25,7 @@
25
25
  "build:desktop": "PATH=\"$HOME/.cargo/bin:$PATH\" pnpm --filter @maude/desktop tauri build",
26
26
  "test:e2e:desktop:build": "PATH=\"$HOME/.cargo/bin:$PATH\" pnpm --filter @maude/desktop tauri build --debug --config src-tauri/tauri.e2e.conf.json",
27
27
  "test:e2e:desktop": "pnpm --filter @maude/desktop-e2e e2e",
28
+ "test:e2e:desktop:cloud": "pnpm --filter @maude/desktop-e2e e2e:cloud",
28
29
  "test:e2e:desktop:git": "pnpm --filter @maude/desktop-e2e e2e:git",
29
30
  "build": "pnpm -r --if-present --filter '!@maude/desktop' '!@maude/desktop-e2e' run build",
30
31
  "build:binary": "bun run apps/studio/build.ts --release",
@@ -51,13 +52,13 @@
51
52
  "test:e2e:desktop:acp-cold-start": "pnpm --filter @maude/desktop-e2e e2e:acp-cold-start"
52
53
  },
53
54
  "optionalDependencies": {
54
- "@1agh/maude-darwin-arm64": "0.50.0",
55
- "@1agh/maude-darwin-x64": "0.50.0",
56
- "@1agh/maude-linux-arm64": "0.50.0",
57
- "@1agh/maude-linux-arm64-musl": "0.50.0",
58
- "@1agh/maude-linux-x64": "0.50.0",
59
- "@1agh/maude-linux-x64-musl": "0.50.0",
60
- "@1agh/maude-win32-x64": "0.50.0"
55
+ "@1agh/maude-darwin-arm64": "0.51.0",
56
+ "@1agh/maude-darwin-x64": "0.51.0",
57
+ "@1agh/maude-linux-arm64": "0.51.0",
58
+ "@1agh/maude-linux-arm64-musl": "0.51.0",
59
+ "@1agh/maude-linux-x64": "0.51.0",
60
+ "@1agh/maude-linux-x64-musl": "0.51.0",
61
+ "@1agh/maude-win32-x64": "0.51.0"
61
62
  },
62
63
  "files": [
63
64
  "cli",