@1agh/maude 0.60.6 → 0.60.7
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.
|
@@ -567,3 +567,68 @@ export async function pushAssets(opts: {
|
|
|
567
567
|
}
|
|
568
568
|
return out;
|
|
569
569
|
}
|
|
570
|
+
|
|
571
|
+
/**
|
|
572
|
+
* The FAST LANE — push exactly ONE just-written asset, now (2026-08-16
|
|
573
|
+
* latency fix).
|
|
574
|
+
*
|
|
575
|
+
* The sweep is the reconciler, and a good one — but it is also a spawn, a
|
|
576
|
+
* debounce, and a walk-and-probe of the whole project, so a sticker dropped
|
|
577
|
+
* on a canvas reached the hub MINUTES after its annotation stroke got there
|
|
578
|
+
* over the doc lane. The other side rendered the placeholder frame the whole
|
|
579
|
+
* time. This pushes the single file the moment the local write lands: one
|
|
580
|
+
* probe (the batch route, which survives the cloud's HEAD→GET conversion),
|
|
581
|
+
* one PUT. In-process on purpose — the out-of-process boundary exists because
|
|
582
|
+
* the FULL sweep destabilizes Bun next to the dev server; a single fetch is
|
|
583
|
+
* the same class of work `pullAssets` already does in-process on every poll.
|
|
584
|
+
*
|
|
585
|
+
* Failures are the sweep's problem: this never throws, and a miss here is
|
|
586
|
+
* retried by the very next sweep pass for free.
|
|
587
|
+
*/
|
|
588
|
+
export async function pushOneAsset(opts: {
|
|
589
|
+
designRoot: string;
|
|
590
|
+
/** designRoot-relative path, slash-normalised. */
|
|
591
|
+
rel: string;
|
|
592
|
+
hubUrl: string;
|
|
593
|
+
token: () => string;
|
|
594
|
+
canvasGroups?: readonly CanvasGroupLike[];
|
|
595
|
+
fetchImpl?: typeof fetch;
|
|
596
|
+
timeoutFor?: (bytes: number) => number;
|
|
597
|
+
}): Promise<{ ok: boolean; skipped?: boolean; reason?: string }> {
|
|
598
|
+
const fetchImpl = opts.fetchImpl ?? fetch;
|
|
599
|
+
const rel = opts.rel.split('\\').join('/');
|
|
600
|
+
if (!isPushableAssetRel(rel, opts.canvasGroups)) {
|
|
601
|
+
return { ok: false, reason: 'not a pushable asset' };
|
|
602
|
+
}
|
|
603
|
+
const abs = path.join(opts.designRoot, rel);
|
|
604
|
+
try {
|
|
605
|
+
if (statSync(abs).size > MAX_PUSH_BYTES) return { ok: false, reason: 'over the size cap' };
|
|
606
|
+
} catch {
|
|
607
|
+
return { ok: false, reason: 'unreadable' };
|
|
608
|
+
}
|
|
609
|
+
const base = opts.hubUrl.replace(/\/+$/, '');
|
|
610
|
+
const headers = { authorization: `Bearer ${opts.token()}` };
|
|
611
|
+
try {
|
|
612
|
+
// One probe — mainly to skip the echo of a file we just PULLED from this
|
|
613
|
+
// very hub (the pull writes it, the watcher fires, and without this the
|
|
614
|
+
// fast lane would upload the hub's own bytes straight back at it).
|
|
615
|
+
const known = await probePresent({ fetchImpl, base, headers, paths: [rel] });
|
|
616
|
+
if (known?.has(rel)) return { ok: true, skipped: true };
|
|
617
|
+
const put = await putWithRetry({
|
|
618
|
+
fetchImpl,
|
|
619
|
+
url: `${base}${routeFor(rel).url}`,
|
|
620
|
+
headers,
|
|
621
|
+
file: abs,
|
|
622
|
+
sleep: (ms) =>
|
|
623
|
+
new Promise<void>((res) => {
|
|
624
|
+
setTimeout(res, ms);
|
|
625
|
+
}),
|
|
626
|
+
backoff: { remainingMs: 60_000 },
|
|
627
|
+
timeoutFor: opts.timeoutFor ?? putTimeoutMs,
|
|
628
|
+
});
|
|
629
|
+
if (put.ok) return { ok: true };
|
|
630
|
+
return { ok: false, reason: `HTTP ${put.status}` };
|
|
631
|
+
} catch (err) {
|
|
632
|
+
return { ok: false, reason: (err as Error).message };
|
|
633
|
+
}
|
|
634
|
+
}
|
|
@@ -30,7 +30,7 @@ import { createHistory } from '../history.ts';
|
|
|
30
30
|
import { SYNTHETIC_FS_DELAY_MS } from '../hmr-broadcast.ts';
|
|
31
31
|
import { type CanvasSyncAgent, createCanvasSyncAgent } from './agent.ts';
|
|
32
32
|
import { pullAssets } from './asset-pull.ts';
|
|
33
|
-
import { isPushableAssetRel } from './asset-push.ts';
|
|
33
|
+
import { isPushableAssetRel, pushOneAsset } from './asset-push.ts';
|
|
34
34
|
import { type AssetSweepHandle, runAssetSweep } from './asset-sweep.ts';
|
|
35
35
|
import { atomicWrite } from './atomic-write.ts';
|
|
36
36
|
import { createAutoCommit } from './autocommit.ts';
|
|
@@ -645,6 +645,57 @@ export function createSyncRuntime(
|
|
|
645
645
|
assetSweepTimer.unref?.();
|
|
646
646
|
}
|
|
647
647
|
|
|
648
|
+
// ---- FAST LANES (2026-08-16 latency fix) --------------------------------
|
|
649
|
+
// The sweep reconciles; these make the COMMON case instant. A sticker
|
|
650
|
+
// dropped on a canvas syncs its stroke over the doc lane in milliseconds,
|
|
651
|
+
// while its bytes waited for a debounced, spawned, walk-everything sweep
|
|
652
|
+
// (minutes) — and the other direction waited for the next 20 s poll. The
|
|
653
|
+
// fast push sends the ONE just-written file now; the fast pull runs the
|
|
654
|
+
// (missing-only, idempotent) asset pull the moment a reference-bearing
|
|
655
|
+
// file lands, instead of at the next tick.
|
|
656
|
+
const fastPushTimers = new Map<string, ReturnType<typeof setTimeout>>();
|
|
657
|
+
/** Serialize fast pushes — assets run to videos; two at once starve sync. */
|
|
658
|
+
let fastPushChain: Promise<void> = Promise.resolve();
|
|
659
|
+
/** Let the write settle (atomic tmp+rename bursts) before reading it. */
|
|
660
|
+
const FAST_PUSH_DEBOUNCE_MS = 400;
|
|
661
|
+
/** Debounced trigger for an off-schedule pullAssetsOnce — set in start(). */
|
|
662
|
+
let requestFastPull: (() => void) | null = null;
|
|
663
|
+
/** Files whose annotations/tsx/css/meta content can reference assets —
|
|
664
|
+
* mirrors asset-pull's SCANNED_EXT. */
|
|
665
|
+
const REFERENCE_FILE_RE = /\.(?:annotations\.svg|tsx|jsx|css|meta\.json)$/i;
|
|
666
|
+
|
|
667
|
+
function queueFastPush(rel: string, hubUrl: string): void {
|
|
668
|
+
if (stopped || cellPairing) return; // the cell never pushes (same as the sweep)
|
|
669
|
+
const prev = fastPushTimers.get(rel);
|
|
670
|
+
if (prev) clearTimeout(prev);
|
|
671
|
+
const timer = setTimeout(() => {
|
|
672
|
+
fastPushTimers.delete(rel);
|
|
673
|
+
if (stopped) return;
|
|
674
|
+
fastPushChain = fastPushChain.then(async () => {
|
|
675
|
+
if (stopped) return;
|
|
676
|
+
try {
|
|
677
|
+
const r = await pushOneAsset({
|
|
678
|
+
designRoot: ctx.paths.designRoot,
|
|
679
|
+
rel,
|
|
680
|
+
hubUrl,
|
|
681
|
+
token: () => token,
|
|
682
|
+
canvasGroups: ctx.cfg.canvasGroups,
|
|
683
|
+
});
|
|
684
|
+
if (r.ok && !r.skipped) {
|
|
685
|
+
console.log(`[sync/assets] fast-pushed ${rel}`);
|
|
686
|
+
} else if (!r.ok) {
|
|
687
|
+
// Not an error surface — the sweep is the reconciler and retries.
|
|
688
|
+
console.warn(`[sync/assets] fast push ${rel}: ${r.reason} — the sweep will retry.`);
|
|
689
|
+
}
|
|
690
|
+
} catch {
|
|
691
|
+
/* the sweep reconciles */
|
|
692
|
+
}
|
|
693
|
+
});
|
|
694
|
+
}, FAST_PUSH_DEBOUNCE_MS);
|
|
695
|
+
timer.unref?.();
|
|
696
|
+
fastPushTimers.set(rel, timer);
|
|
697
|
+
}
|
|
698
|
+
|
|
648
699
|
// Task 8 — offline-mode status surface, initialized in start() once the
|
|
649
700
|
// canvas count is known. The store writes `_sync.json` + broadcasts
|
|
650
701
|
// 'sync:status' on the bus; the monitor aggregates provider WS status into
|
|
@@ -1222,7 +1273,17 @@ export function createSyncRuntime(
|
|
|
1222
1273
|
// nobody had sent: a permanent empty frame that looked like a broken path.
|
|
1223
1274
|
// Reported three times in one day on alligators, each time with a
|
|
1224
1275
|
// different asset, which is what finally named it.
|
|
1225
|
-
if (isPushableAssetRel(rel, ctx.cfg.canvasGroups))
|
|
1276
|
+
if (isPushableAssetRel(rel, ctx.cfg.canvasGroups)) {
|
|
1277
|
+
// Fast lane first (this ONE file, now — seconds, not minutes), sweep
|
|
1278
|
+
// second (the reconciler that catches anything the fast lane missed).
|
|
1279
|
+
queueFastPush(rel.split('\\').join('/'), linkedHub.url);
|
|
1280
|
+
scheduleAssetSweep(linkedHub.url);
|
|
1281
|
+
}
|
|
1282
|
+
// The DOWNWARD fast lane: a reference-bearing file just landed (a peer's
|
|
1283
|
+
// annotation/body materialized through the doc lane, or a local edit).
|
|
1284
|
+
// Run the missing-only asset pull now instead of at the next 20 s tick,
|
|
1285
|
+
// so the other side's freshly dropped image renders in seconds.
|
|
1286
|
+
if (REFERENCE_FILE_RE.test(rel)) requestFastPull?.();
|
|
1226
1287
|
});
|
|
1227
1288
|
|
|
1228
1289
|
/**
|
|
@@ -2316,6 +2377,39 @@ export function createSyncRuntime(
|
|
|
2316
2377
|
token: () => token,
|
|
2317
2378
|
});
|
|
2318
2379
|
};
|
|
2380
|
+
// The downward fast lane's trigger (2026-08-16 latency fix): debounced,
|
|
2381
|
+
// single-flight, and a no-op in the steady state (the pull is missing-only
|
|
2382
|
+
// — every referenced asset present costs one directory scan and zero
|
|
2383
|
+
// requests). Fired from the fs:any listener when a reference-bearing file
|
|
2384
|
+
// lands; the 20 s poll stays as the schedule-driven reconciler.
|
|
2385
|
+
{
|
|
2386
|
+
let fastPullTimer: ReturnType<typeof setTimeout> | null = null;
|
|
2387
|
+
let fastPullRunning = false;
|
|
2388
|
+
let fastPullAgain = false;
|
|
2389
|
+
const runFastPull = (): void => {
|
|
2390
|
+
if (stopped) return;
|
|
2391
|
+
if (fastPullRunning) {
|
|
2392
|
+
fastPullAgain = true;
|
|
2393
|
+
return;
|
|
2394
|
+
}
|
|
2395
|
+
fastPullRunning = true;
|
|
2396
|
+
void pullAssetsOnce().finally(() => {
|
|
2397
|
+
fastPullRunning = false;
|
|
2398
|
+
if (fastPullAgain && !stopped) {
|
|
2399
|
+
fastPullAgain = false;
|
|
2400
|
+
runFastPull();
|
|
2401
|
+
}
|
|
2402
|
+
});
|
|
2403
|
+
};
|
|
2404
|
+
requestFastPull = () => {
|
|
2405
|
+
if (stopped || fastPullTimer !== null) return;
|
|
2406
|
+
fastPullTimer = setTimeout(() => {
|
|
2407
|
+
fastPullTimer = null;
|
|
2408
|
+
runFastPull();
|
|
2409
|
+
}, 750);
|
|
2410
|
+
fastPullTimer.unref?.();
|
|
2411
|
+
};
|
|
2412
|
+
}
|
|
2319
2413
|
// Cumulative per boot — the Sync panel's one line. `synced` is the last
|
|
2320
2414
|
// pass's converged count; `pulled`/`conflicts` accumulate.
|
|
2321
2415
|
const fileTotals = { synced: 0, pulled: 0, conflicts: 0 };
|
|
@@ -2372,6 +2466,10 @@ export function createSyncRuntime(
|
|
|
2372
2466
|
async function stop(): Promise<void> {
|
|
2373
2467
|
if (stopped) return;
|
|
2374
2468
|
stopped = true;
|
|
2469
|
+
// Fast lanes down first — no push/pull may fire into a dying runtime.
|
|
2470
|
+
for (const t of fastPushTimers.values()) clearTimeout(t);
|
|
2471
|
+
fastPushTimers.clear();
|
|
2472
|
+
requestFastPull = null;
|
|
2375
2473
|
// Nothing may be adopted into a runtime that is going away.
|
|
2376
2474
|
attachOne = null;
|
|
2377
2475
|
discoveryUnsub?.();
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
isPushableAssetRel,
|
|
15
15
|
listPushableAssets,
|
|
16
16
|
pushAssets,
|
|
17
|
+
pushOneAsset,
|
|
17
18
|
putTimeoutMs,
|
|
18
19
|
} from '../sync/asset-push.ts';
|
|
19
20
|
|
|
@@ -837,10 +838,11 @@ describe('the runtime sweeps on change, not only at boot', () => {
|
|
|
837
838
|
test('the fs watcher decides with isPushableAssetRel and schedules a sweep', () => {
|
|
838
839
|
// The predicate gets the project's OWN canvas groups — with the default
|
|
839
840
|
// set a custom-group project's shared module would answer canvas-owned
|
|
840
|
-
// and silently never schedule the sweep that uploads it.
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
);
|
|
841
|
+
// and silently never schedule the sweep that uploads it. (Since the
|
|
842
|
+
// 2026-08-16 latency fix the same gate also feeds the fast lane; the
|
|
843
|
+
// sweep stays the reconciler.)
|
|
844
|
+
expect(SYNC).toContain('if (isPushableAssetRel(rel, ctx.cfg.canvasGroups)) {');
|
|
845
|
+
expect(SYNC).toContain('scheduleAssetSweep(linkedHub.url);');
|
|
844
846
|
});
|
|
845
847
|
|
|
846
848
|
test('bursts coalesce, and a change DURING a sweep is not lost', () => {
|
|
@@ -855,3 +857,112 @@ describe('the runtime sweeps on change, not only at boot', () => {
|
|
|
855
857
|
expect(SYNC).toContain('if (assetSweepTimer !== null) clearTimeout(assetSweepTimer);');
|
|
856
858
|
});
|
|
857
859
|
});
|
|
860
|
+
|
|
861
|
+
describe('pushOneAsset — the fast lane (2026-08-16 latency fix)', () => {
|
|
862
|
+
test('probes once via /_asset-probe, then PUTs the single file', async () => {
|
|
863
|
+
const designRoot = scratchDesignRoot();
|
|
864
|
+
mkdirSync(join(designRoot, 'assets'), { recursive: true });
|
|
865
|
+
writeFileSync(join(designRoot, 'assets/aaaaaaaa.png'), 'fresh sticker');
|
|
866
|
+
|
|
867
|
+
const calls: Array<{ method: string; url: string }> = [];
|
|
868
|
+
const fetchImpl = (async (input: RequestInfo | URL, init?: RequestInit) => {
|
|
869
|
+
const url = String(input);
|
|
870
|
+
const method = init?.method ?? 'GET';
|
|
871
|
+
calls.push({ method, url });
|
|
872
|
+
if (url.endsWith('/_asset-probe')) return Response.json({ present: [] });
|
|
873
|
+
return new Response('{"ok":true}', { status: 200 });
|
|
874
|
+
}) as typeof fetch;
|
|
875
|
+
|
|
876
|
+
const r = await pushOneAsset({
|
|
877
|
+
designRoot,
|
|
878
|
+
rel: 'assets/aaaaaaaa.png',
|
|
879
|
+
hubUrl: 'https://alligators.cloud.maude.sh/',
|
|
880
|
+
token: () => 'mau_tok',
|
|
881
|
+
fetchImpl,
|
|
882
|
+
});
|
|
883
|
+
expect(r).toEqual({ ok: true });
|
|
884
|
+
expect(calls.map((c) => `${c.method} ${c.url}`)).toEqual([
|
|
885
|
+
'POST https://alligators.cloud.maude.sh/_asset-probe',
|
|
886
|
+
'PUT https://alligators.cloud.maude.sh/assets/aaaaaaaa.png',
|
|
887
|
+
]);
|
|
888
|
+
});
|
|
889
|
+
|
|
890
|
+
test('a file the hub already holds is skipped — the pull-echo guard', async () => {
|
|
891
|
+
// The fast lane fires off fs:any, and the asset PULL writes into assets/
|
|
892
|
+
// too — without this skip the lane would upload the hub's own bytes back.
|
|
893
|
+
const designRoot = scratchDesignRoot();
|
|
894
|
+
mkdirSync(join(designRoot, 'assets'), { recursive: true });
|
|
895
|
+
writeFileSync(join(designRoot, 'assets/aaaaaaaa.png'), 'pulled bytes');
|
|
896
|
+
|
|
897
|
+
let puts = 0;
|
|
898
|
+
const fetchImpl = (async (input: RequestInfo | URL, init?: RequestInit) => {
|
|
899
|
+
const url = String(input);
|
|
900
|
+
if (url.endsWith('/_asset-probe')) return Response.json({ present: ['assets/aaaaaaaa.png'] });
|
|
901
|
+
if (init?.method === 'PUT') puts += 1;
|
|
902
|
+
return new Response('{"ok":true}', { status: 200 });
|
|
903
|
+
}) as typeof fetch;
|
|
904
|
+
|
|
905
|
+
const r = await pushOneAsset({
|
|
906
|
+
designRoot,
|
|
907
|
+
rel: 'assets/aaaaaaaa.png',
|
|
908
|
+
hubUrl: 'https://alligators.cloud.maude.sh',
|
|
909
|
+
token: () => 'mau_tok',
|
|
910
|
+
fetchImpl,
|
|
911
|
+
});
|
|
912
|
+
expect(r).toEqual({ ok: true, skipped: true });
|
|
913
|
+
expect(puts).toBe(0);
|
|
914
|
+
});
|
|
915
|
+
|
|
916
|
+
test('DS-tree asset routes to /_asset-file/; non-pushable rels refuse; failures report', async () => {
|
|
917
|
+
const designRoot = scratchDesignRoot();
|
|
918
|
+
mkdirSync(join(designRoot, 'system/ds/assets'), { recursive: true });
|
|
919
|
+
writeFileSync(join(designRoot, 'system/ds/assets/mark.svg'), 'brand');
|
|
920
|
+
|
|
921
|
+
const urls: string[] = [];
|
|
922
|
+
const fetchImpl = (async (input: RequestInfo | URL, init?: RequestInit) => {
|
|
923
|
+
const url = String(input);
|
|
924
|
+
if (url.endsWith('/_asset-probe')) return Response.json({ present: [] });
|
|
925
|
+
if (init?.method === 'PUT') {
|
|
926
|
+
urls.push(url);
|
|
927
|
+
return new Response('refused', { status: 403 });
|
|
928
|
+
}
|
|
929
|
+
return new Response(null, { status: 404 });
|
|
930
|
+
}) as typeof fetch;
|
|
931
|
+
|
|
932
|
+
const r = await pushOneAsset({
|
|
933
|
+
designRoot,
|
|
934
|
+
rel: 'system/ds/assets/mark.svg',
|
|
935
|
+
hubUrl: 'https://hub.example.com',
|
|
936
|
+
token: () => 't',
|
|
937
|
+
fetchImpl,
|
|
938
|
+
});
|
|
939
|
+
expect(urls).toEqual(['https://hub.example.com/_asset-file/system/ds/assets/mark.svg']);
|
|
940
|
+
expect(r.ok).toBe(false);
|
|
941
|
+
expect(r.reason).toContain('403');
|
|
942
|
+
|
|
943
|
+
// Runtime state / config never enter the fast lane.
|
|
944
|
+
const refused = await pushOneAsset({
|
|
945
|
+
designRoot,
|
|
946
|
+
rel: '_history/x/old.png',
|
|
947
|
+
hubUrl: 'https://hub.example.com',
|
|
948
|
+
token: () => 't',
|
|
949
|
+
fetchImpl,
|
|
950
|
+
});
|
|
951
|
+
expect(refused.ok).toBe(false);
|
|
952
|
+
expect(refused.reason).toBe('not a pushable asset');
|
|
953
|
+
});
|
|
954
|
+
|
|
955
|
+
test('the runtime wires the fast lanes: push off fs:any, pull off reference files', () => {
|
|
956
|
+
// Same source-pin style as the sweep-trigger suite above.
|
|
957
|
+
expect(SYNC2).toContain('queueFastPush(rel');
|
|
958
|
+
expect(SYNC2).toMatch(/REFERENCE_FILE_RE\.test\(rel\)/);
|
|
959
|
+
expect(SYNC2).toContain('requestFastPull');
|
|
960
|
+
// stop() tears the lanes down.
|
|
961
|
+
expect(SYNC2).toContain('for (const t of fastPushTimers.values()) clearTimeout(t);');
|
|
962
|
+
expect(SYNC2).toContain('requestFastPull = null;');
|
|
963
|
+
// The cell never pushes — same guard as the sweep.
|
|
964
|
+
expect(SYNC2).toMatch(/if \(stopped \|\| cellPairing\) return; \/\/ the cell never pushes/);
|
|
965
|
+
});
|
|
966
|
+
});
|
|
967
|
+
|
|
968
|
+
const SYNC2 = readFileSync(join(import.meta.dir, '..', 'sync', 'index.ts'), 'utf8');
|
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "./whats-new.schema.json",
|
|
3
3
|
"entries": [
|
|
4
|
+
{
|
|
5
|
+
"id": "instant-asset-sync",
|
|
6
|
+
"version": "0.60.7",
|
|
7
|
+
"date": "2026-08-16",
|
|
8
|
+
"kind": "improvement",
|
|
9
|
+
"title": "Pictures sync in seconds",
|
|
10
|
+
"summary": "A picture you drop on a canvas is pushed by itself the moment it lands, and the other side fetches it the moment the reference arrives — seconds end to end, instead of waiting for the next full sweep or polling tick.",
|
|
11
|
+
"surface": "design-ui"
|
|
12
|
+
},
|
|
4
13
|
{
|
|
5
14
|
"id": "assets-arrive-and-heal",
|
|
6
15
|
"version": "0.60.6",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@1agh/maude",
|
|
3
|
-
"version": "0.60.
|
|
3
|
+
"version": "0.60.7",
|
|
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": {
|
|
@@ -54,13 +54,13 @@
|
|
|
54
54
|
"test:e2e:desktop:parity:cloud": "pnpm --filter @maude/desktop-e2e e2e:parity:cloud"
|
|
55
55
|
},
|
|
56
56
|
"optionalDependencies": {
|
|
57
|
-
"@1agh/maude-darwin-arm64": "0.60.
|
|
58
|
-
"@1agh/maude-darwin-x64": "0.60.
|
|
59
|
-
"@1agh/maude-linux-arm64": "0.60.
|
|
60
|
-
"@1agh/maude-linux-arm64-musl": "0.60.
|
|
61
|
-
"@1agh/maude-linux-x64": "0.60.
|
|
62
|
-
"@1agh/maude-linux-x64-musl": "0.60.
|
|
63
|
-
"@1agh/maude-win32-x64": "0.60.
|
|
57
|
+
"@1agh/maude-darwin-arm64": "0.60.7",
|
|
58
|
+
"@1agh/maude-darwin-x64": "0.60.7",
|
|
59
|
+
"@1agh/maude-linux-arm64": "0.60.7",
|
|
60
|
+
"@1agh/maude-linux-arm64-musl": "0.60.7",
|
|
61
|
+
"@1agh/maude-linux-x64": "0.60.7",
|
|
62
|
+
"@1agh/maude-linux-x64-musl": "0.60.7",
|
|
63
|
+
"@1agh/maude-win32-x64": "0.60.7"
|
|
64
64
|
},
|
|
65
65
|
"files": [
|
|
66
66
|
"cli",
|