@1agh/maude 0.51.1 → 0.52.1

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.
Files changed (48) hide show
  1. package/apps/studio/api.ts +389 -14
  2. package/apps/studio/bin/_video-playwright.mjs +15 -6
  3. package/apps/studio/canvas-edit.ts +141 -20
  4. package/apps/studio/canvas-lib.tsx +61 -0
  5. package/apps/studio/client/app.jsx +824 -121
  6. package/apps/studio/client/panels/ClipInspector.jsx +409 -0
  7. package/apps/studio/client/panels/CloudBar.jsx +12 -2
  8. package/apps/studio/client/panels/TimelinePanel.jsx +1775 -430
  9. package/apps/studio/client/panels/timeline-media-cache.js +274 -0
  10. package/apps/studio/client/panels/timeline-parse.js +46 -1
  11. package/apps/studio/client/panels/timeline-scale.js +64 -0
  12. package/apps/studio/client/report-bug.jsx +15 -15
  13. package/apps/studio/client/styles/3-shell-maude.css +446 -3
  14. package/apps/studio/client/styles/4-components-maude.css +55 -0
  15. package/apps/studio/client/styles/4-components.css +0 -83
  16. package/apps/studio/clip-ops.ts +1980 -0
  17. package/apps/studio/cloud/endpoints.ts +88 -17
  18. package/apps/studio/credential-grammar.ts +100 -0
  19. package/apps/studio/debug-bundle.ts +12 -0
  20. package/apps/studio/dist/client.bundle.js +2048 -2048
  21. package/apps/studio/dist/styles.css +1 -1
  22. package/apps/studio/exporters/video.ts +49 -10
  23. package/apps/studio/http.ts +113 -6
  24. package/apps/studio/ripple.ts +335 -0
  25. package/apps/studio/sync/hub-link.ts +3 -3
  26. package/apps/studio/sync/hubs-config.ts +32 -0
  27. package/apps/studio/sync/workspace-signin.ts +8 -4
  28. package/apps/studio/test/_helpers.ts +5 -3
  29. package/apps/studio/test/canvas-create-api.test.ts +8 -2
  30. package/apps/studio/test/canvas-origin-gate.test.ts +20 -0
  31. package/apps/studio/test/clip-addressing.test.ts +7 -6
  32. package/apps/studio/test/clip-ops.test.ts +836 -0
  33. package/apps/studio/test/cloud-endpoints.test.ts +37 -2
  34. package/apps/studio/test/comments-api.test.ts +49 -0
  35. package/apps/studio/test/credential-grammar.test.ts +107 -0
  36. package/apps/studio/test/read-only-role.test.ts +95 -0
  37. package/apps/studio/test/ripple.test.ts +197 -0
  38. package/apps/studio/test/timeline-scale.test.ts +65 -0
  39. package/apps/studio/test/workspace-signin.test.ts +4 -1
  40. package/apps/studio/whats-new.json +35 -0
  41. package/cli/commands/share.mjs +8 -4
  42. package/cli/lib/ddr-to-kgai.mjs +6 -1
  43. package/cli/lib/ddr-to-kgai.test.mjs +1 -2
  44. package/cli/lib/hubs-config.mjs +15 -1
  45. package/cli/lib/hubs-config.test.mjs +19 -0
  46. package/cli/lib/share-plan.mjs +5 -1
  47. package/cli/lib/share-plan.test.mjs +11 -2
  48. package/package.json +8 -8
@@ -862,7 +862,12 @@ export function buildDocsBatch(aiDir, scope = {}) {
862
862
  // A doc that cites DDRs is context ABOUT them.
863
863
  for (const num of new Set([...t.matchAll(/DDR-(\d+)/g)].map((m) => m[1].padStart(3, '0')))) {
864
864
  muts.push({ op: 'upsert_element', kind: 'decision', name: ddrRef(num, scope) });
865
- muts.push({ op: 'add_link', from: ref, to: `decision:${ddrRef(num, scope)}`, link: 'REFERENCES' });
865
+ muts.push({
866
+ op: 'add_link',
867
+ from: ref,
868
+ to: `decision:${ddrRef(num, scope)}`,
869
+ link: 'REFERENCES',
870
+ });
866
871
  }
867
872
  decisions.push({
868
873
  title: `Doc: ${title}`.slice(0, 160),
@@ -102,8 +102,7 @@ test('decision names are namespaced by repo so DDR numbers do not collide cross-
102
102
  const files = { 'DDR-001-a.md': '# DDR-001: Alpha\n\n**Tags:** infra\n' };
103
103
  const a = buildDdrBatch(fixtureDir(files), { repo: 'vantage', dept: 'marketing' });
104
104
  const b = buildDdrBatch(fixtureDir(files), { repo: 'AI-StudyMate', dept: 'dev' });
105
- const nameOf = (batch) =>
106
- batch.decisions[0].mutations.find((m) => m.kind === 'decision').name;
105
+ const nameOf = (batch) => batch.decisions[0].mutations.find((m) => m.kind === 'decision').name;
107
106
  assert.equal(nameOf(a.batch), 'vantage/DDR-001');
108
107
  assert.equal(nameOf(b.batch), 'AI-StudyMate/DDR-001');
109
108
  assert.notEqual(nameOf(a.batch), nameOf(b.batch));
@@ -83,12 +83,26 @@ export function saveHubsConfig(config) {
83
83
  *
84
84
  * @param {string} url
85
85
  * @param {string} token
86
+ * `role` is the ONE prior field carried across a relink (Cloud Phase 25 C2).
87
+ * It records what the workspace vouched for at sign-in, and this path has no
88
+ * fresher answer — the CLI does not sign in. Dropping it would silently show
89
+ * a viewer the editing UI again, which is the exact experience the flag
90
+ * exists to prevent; a stale value costs a redundant hidden affordance and
91
+ * self-corrects at the next workspace sign-in. Nothing ELSE is preserved:
92
+ * `adoptedAt` is a per-machine attestation that a relink is entitled to clear.
93
+ *
86
94
  * @param {{ adoptedAt?: number }} [extra]
87
95
  */
88
96
  export function addHub(url, token, extra = {}) {
89
97
  const norm = normalizeUrl(url);
90
98
  const cfg = loadHubsConfig();
91
- cfg.hubs[norm] = { token, linkedAt: Date.now(), ...extra };
99
+ const priorRole = cfg.hubs[norm]?.role;
100
+ cfg.hubs[norm] = {
101
+ token,
102
+ linkedAt: Date.now(),
103
+ ...(priorRole ? { role: priorRole } : {}),
104
+ ...extra,
105
+ };
92
106
  saveHubsConfig(cfg);
93
107
  return cfg.hubs[norm];
94
108
  }
@@ -98,3 +98,22 @@ test('addHub keys URLs case-insensitively (same hub after host normalization)',
98
98
  assert.equal(hubs['https://hub.example.com'].token, 'mau_second');
99
99
  });
100
100
  });
101
+
102
+ // Cloud Phase 25 C2 — the studio writes `role` into this shared file, and a
103
+ // CLI relink used to replace the record wholesale and drop it. Not an access
104
+ // hole (the cell refuses the write regardless), but it would silently show a
105
+ // viewer the editing UI again, which is exactly what the flag exists to stop.
106
+ test('a relink preserves the vouched role, and still clears per-machine attestations', () => {
107
+ withTmpConfig(() => {
108
+ addHub('https://p.cloud.maude.sh', 'mau_first', { adoptedAt: 111 });
109
+ // The studio stamps the role after a workspace sign-in.
110
+ const cfg = loadHubsConfig();
111
+ cfg.hubs['https://p.cloud.maude.sh'].role = 'viewer';
112
+ saveHubsConfig(cfg);
113
+
114
+ const after = addHub('https://p.cloud.maude.sh', 'mau_second');
115
+ assert.equal(after.token, 'mau_second');
116
+ assert.equal(after.role, 'viewer', 'the role survives a relink');
117
+ assert.equal(after.adoptedAt, undefined, 'a per-machine attestation does not');
118
+ });
119
+ });
@@ -65,7 +65,11 @@ export function shareMarker(project, { enabled, name }) {
65
65
  if (!id) throw new Error(`invalid project id: ${project}`);
66
66
  return {
67
67
  key: `tenants/${id}/share.json`,
68
- body: JSON.stringify({ enabled: Boolean(enabled), name: String(name ?? id).slice(0, 80) }, null, 2),
68
+ body: JSON.stringify(
69
+ { enabled: Boolean(enabled), name: String(name ?? id).slice(0, 80) },
70
+ null,
71
+ 2
72
+ ),
69
73
  };
70
74
  }
71
75
 
@@ -3,7 +3,13 @@
3
3
  import assert from 'node:assert/strict';
4
4
  import test from 'node:test';
5
5
 
6
- import { isShareable, publishPlan, publishSummary, shareMarker, validProjectId } from './share-plan.mjs';
6
+ import {
7
+ isShareable,
8
+ publishPlan,
9
+ publishSummary,
10
+ shareMarker,
11
+ validProjectId,
12
+ } from './share-plan.mjs';
7
13
 
8
14
  test('SVG is never published, however it is named', () => {
9
15
  // Same reason the share origin refuses to SERVE one: an SVG is a document
@@ -22,7 +28,10 @@ test('a publish cannot climb out of the directory it was pointed at', () => {
22
28
  ['../secret.png', '/etc/x.png', 'a/../../b.png', 'ok.png', 'ui/home.png'],
23
29
  'acme'
24
30
  );
25
- assert.deepEqual(uploads.map((u) => u.from), ['ok.png', 'ui/home.png']);
31
+ assert.deepEqual(
32
+ uploads.map((u) => u.from),
33
+ ['ok.png', 'ui/home.png']
34
+ );
26
35
  assert.equal(skipped.length, 3);
27
36
  });
28
37
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@1agh/maude",
3
- "version": "0.51.1",
3
+ "version": "0.52.1",
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": {
@@ -52,13 +52,13 @@
52
52
  "test:e2e:desktop:acp-cold-start": "pnpm --filter @maude/desktop-e2e e2e:acp-cold-start"
53
53
  },
54
54
  "optionalDependencies": {
55
- "@1agh/maude-darwin-arm64": "0.51.1",
56
- "@1agh/maude-darwin-x64": "0.51.1",
57
- "@1agh/maude-linux-arm64": "0.51.1",
58
- "@1agh/maude-linux-arm64-musl": "0.51.1",
59
- "@1agh/maude-linux-x64": "0.51.1",
60
- "@1agh/maude-linux-x64-musl": "0.51.1",
61
- "@1agh/maude-win32-x64": "0.51.1"
55
+ "@1agh/maude-darwin-arm64": "0.52.1",
56
+ "@1agh/maude-darwin-x64": "0.52.1",
57
+ "@1agh/maude-linux-arm64": "0.52.1",
58
+ "@1agh/maude-linux-arm64-musl": "0.52.1",
59
+ "@1agh/maude-linux-x64": "0.52.1",
60
+ "@1agh/maude-linux-x64-musl": "0.52.1",
61
+ "@1agh/maude-win32-x64": "0.52.1"
62
62
  },
63
63
  "files": [
64
64
  "cli",