mindwire 0.1.13 → 0.1.14
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/dist/client.d.ts +2 -0
- package/dist/index.cjs +102 -19
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +102 -20
- package/dist/index.js.map +1 -1
- package/dist/surfaces.d.ts +177 -0
- package/dist/target/host.d.ts +5 -3
- package/dist/target/oblien.d.ts +1 -1
- package/dist/target/ssh.d.ts +1 -1
- package/dist/types.d.ts +6 -0
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -35,3 +35,4 @@ export * from "./types.js";
|
|
|
35
35
|
export { WorkspaceApi, WorkspaceCollection, ProjectOperationsApi } from "./workspace.js";
|
|
36
36
|
export type { ProjectRequest, ProjectAuth, ProjectOperation, ProjectRemoveRequest } from "./workspace.js";
|
|
37
37
|
export type { WorkspaceRecord, WorkspaceAgent, WorkspaceProject, WorkspaceChat, WorkspaceKind, WorkspaceInput, WorkspaceImport, WorkspaceSnapshot } from "./workspace.js";
|
|
38
|
+
export * from "./surfaces.js";
|
package/dist/index.js
CHANGED
|
@@ -580,8 +580,68 @@ var WorkspaceApi = class {
|
|
|
580
580
|
}
|
|
581
581
|
};
|
|
582
582
|
|
|
583
|
+
// src/surfaces.ts
|
|
584
|
+
var SurfacesApi = class {
|
|
585
|
+
constructor(mw) {
|
|
586
|
+
this.mw = mw;
|
|
587
|
+
}
|
|
588
|
+
mw;
|
|
589
|
+
list() {
|
|
590
|
+
return this.mw.http.request("GET", "/surfaces");
|
|
591
|
+
}
|
|
592
|
+
status(refresh = false) {
|
|
593
|
+
return this.mw.http.request("GET", "/surfaces/desktop", { query: { refresh } });
|
|
594
|
+
}
|
|
595
|
+
bind(binding) {
|
|
596
|
+
return this.mw.http.request("PUT", "/surfaces/desktop/binding", { body: binding });
|
|
597
|
+
}
|
|
598
|
+
open(request) {
|
|
599
|
+
return this.mw.http.request("POST", "/surfaces/desktop/sessions", { body: request });
|
|
600
|
+
}
|
|
601
|
+
control(id, request) {
|
|
602
|
+
return this.mw.http.request("POST", `/surfaces/desktop/sessions/${encodeURIComponent(id)}/control`, { body: request });
|
|
603
|
+
}
|
|
604
|
+
close(id) {
|
|
605
|
+
return this.mw.http.request("DELETE", `/surfaces/desktop/sessions/${encodeURIComponent(id)}`);
|
|
606
|
+
}
|
|
607
|
+
capture(id) {
|
|
608
|
+
return this.mw.http.request("POST", `/surfaces/desktop/sessions/${encodeURIComponent(id)}/captures`);
|
|
609
|
+
}
|
|
610
|
+
action(request) {
|
|
611
|
+
return this.mw.http.request("POST", "/surfaces/desktop/actions", { body: request });
|
|
612
|
+
}
|
|
613
|
+
receipt(id) {
|
|
614
|
+
return this.mw.http.request("GET", `/surfaces/desktop/actions/${encodeURIComponent(id)}`);
|
|
615
|
+
}
|
|
616
|
+
artifact(id) {
|
|
617
|
+
return this.mw.http.request("GET", `/artifacts/${encodeURIComponent(id)}`);
|
|
618
|
+
}
|
|
619
|
+
/** Every connection starts with the current snapshot, then revisions. Never replays input. */
|
|
620
|
+
async *watch(opts = {}) {
|
|
621
|
+
const controller = new AbortController();
|
|
622
|
+
const abort = () => controller.abort();
|
|
623
|
+
opts.signal?.addEventListener("abort", abort, { once: true });
|
|
624
|
+
if (opts.signal?.aborted) controller.abort();
|
|
625
|
+
try {
|
|
626
|
+
const response = await this.mw.http.open("GET", "/surfaces/desktop/events", { signal: controller.signal });
|
|
627
|
+
let instance;
|
|
628
|
+
let revision = -1;
|
|
629
|
+
for await (const snapshot of readSSE(response.body, controller.signal)) {
|
|
630
|
+
if (instance !== snapshot.instanceId || snapshot.revision > revision) {
|
|
631
|
+
instance = snapshot.instanceId;
|
|
632
|
+
revision = snapshot.revision;
|
|
633
|
+
yield snapshot;
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
} finally {
|
|
637
|
+
controller.abort();
|
|
638
|
+
opts.signal?.removeEventListener("abort", abort);
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
};
|
|
642
|
+
|
|
583
643
|
// src/version.ts
|
|
584
|
-
var SDK_VERSION = "0.1.
|
|
644
|
+
var SDK_VERSION = "0.1.14" ;
|
|
585
645
|
|
|
586
646
|
// src/daemon-binary.ts
|
|
587
647
|
function supported(platform, arch) {
|
|
@@ -841,6 +901,7 @@ var handleByTransport = /* @__PURE__ */ new WeakMap();
|
|
|
841
901
|
var Mindwire = class _Mindwire {
|
|
842
902
|
/** Workspace registry: saved agent profiles, projects and chat relationships. */
|
|
843
903
|
workspace;
|
|
904
|
+
surfaces;
|
|
844
905
|
http;
|
|
845
906
|
/** The default agent type applied to agent-scoped calls, if set. */
|
|
846
907
|
defaultAgent;
|
|
@@ -875,6 +936,7 @@ var Mindwire = class _Mindwire {
|
|
|
875
936
|
});
|
|
876
937
|
this.defaultAgent = opts.agent;
|
|
877
938
|
this.workspace = new WorkspaceApi(this);
|
|
939
|
+
this.surfaces = new SurfacesApi(this);
|
|
878
940
|
this.auth = new AuthApi(this);
|
|
879
941
|
this.prompts = new PromptsApi(this);
|
|
880
942
|
this.mcp = new McpApi(this);
|
|
@@ -896,6 +958,7 @@ var Mindwire = class _Mindwire {
|
|
|
896
958
|
clone.http = this.http;
|
|
897
959
|
clone.defaultAgent = agent;
|
|
898
960
|
clone.workspace = new WorkspaceApi(clone);
|
|
961
|
+
clone.surfaces = new SurfacesApi(clone);
|
|
899
962
|
clone.auth = new AuthApi(clone);
|
|
900
963
|
clone.prompts = new PromptsApi(clone);
|
|
901
964
|
clone.mcp = new McpApi(clone);
|
|
@@ -1474,32 +1537,36 @@ async function deploy(host, cfg, emit2, token, directory) {
|
|
|
1474
1537
|
const BIN = shellQuote(directory + "/mindwired"), BIN_NEW = shellQuote(newPath);
|
|
1475
1538
|
const STATE = shellQuote(directory + "/agent-state.json"), LOG = shellQuote(directory + "/daemon.log");
|
|
1476
1539
|
const TOKEN = shellQuote(directory + "/daemon.token");
|
|
1477
|
-
const arch = await
|
|
1540
|
+
const { platform, arch } = await probePlatform(host);
|
|
1478
1541
|
const desired = cfg.desiredVersion ?? SDK_VERSION;
|
|
1479
1542
|
let acquire = "";
|
|
1480
1543
|
let stagedUpload;
|
|
1481
1544
|
if (cfg.daemonBin) {
|
|
1482
|
-
const binPath = await
|
|
1545
|
+
const binPath = await resolveHostDaemon(cfg.daemonBin, platform, arch);
|
|
1483
1546
|
const bytes = await readBytes(binPath);
|
|
1484
|
-
emit2({ phase: "upload", message: `uploading daemon (${arch}, ${formatMiB(bytes.length)})`, arch, bytes: bytes.length });
|
|
1547
|
+
emit2({ phase: "upload", message: `uploading daemon (${platform}-${arch}, ${formatMiB(bytes.length)})`, platform, arch, bytes: bytes.length });
|
|
1485
1548
|
stagedUpload = `${newPath}-${(await import('crypto')).randomUUID()}`;
|
|
1549
|
+
await host.exec(["sh", "-lc", `mkdir -p ${shellQuote(directory)}`], { timeoutSeconds: 15 });
|
|
1486
1550
|
await host.putFile(stagedUpload, bytes, { mode: "0755" });
|
|
1487
1551
|
acquire = `mv -f ${shellQuote(stagedUpload)} ${BIN_NEW}`;
|
|
1488
1552
|
} else {
|
|
1489
1553
|
if (!/^[0-9]+\.[0-9]+\.[0-9]+(?:-[0-9A-Za-z.-]+)?$/.test(desired)) {
|
|
1490
1554
|
throw new MindwireError(`mindwire: cannot download daemon for non-release SDK version ${desired}`);
|
|
1491
1555
|
}
|
|
1492
|
-
const asset = `mindwired-v${desired}
|
|
1556
|
+
const asset = `mindwired-v${desired}-${platform}-${arch}`;
|
|
1493
1557
|
const release = `https://github.com/oblien/mindwire/releases/download/v${desired}`;
|
|
1494
|
-
emit2({ phase: "download", message: `downloading daemon v${desired} on the destination (${arch})`, arch });
|
|
1558
|
+
emit2({ phase: "download", message: `downloading daemon v${desired} on the destination (${platform}-${arch})`, platform, arch });
|
|
1495
1559
|
acquire = [
|
|
1496
1560
|
`release=${shellQuote(release)}`,
|
|
1497
1561
|
`asset=${shellQuote(asset)}`,
|
|
1562
|
+
"if command -v sha256sum >/dev/null 2>&1; then mw_sha256=(sha256sum);",
|
|
1563
|
+
"elif command -v shasum >/dev/null 2>&1; then mw_sha256=(shasum -a 256);",
|
|
1564
|
+
'else echo "MINDWIRE_FAIL SHA-256 verification requires sha256sum or shasum"; exit 1; fi',
|
|
1498
1565
|
"download() {",
|
|
1499
1566
|
` expected=$(curl -fsSL "$release/checksums.txt" | awk -v asset="$asset" '$2 == asset { print $1; exit }') || return 1`,
|
|
1500
1567
|
' [ -n "$expected" ] || return 1',
|
|
1501
1568
|
` curl -fsSL "$release/$asset" -o ${BIN_NEW} || return 1`,
|
|
1502
|
-
` actual=$(
|
|
1569
|
+
` actual=$("\${mw_sha256[@]}" ${BIN_NEW} | awk '{print $1}') || return 1`,
|
|
1503
1570
|
' [ "$actual" = "$expected" ] || return 2',
|
|
1504
1571
|
"}",
|
|
1505
1572
|
"if download; then :; else",
|
|
@@ -1507,20 +1574,23 @@ async function deploy(host, cfg, emit2, token, directory) {
|
|
|
1507
1574
|
` latest=$(curl -fsSL https://api.github.com/repos/oblien/mindwire/releases/latest | sed -n 's/.*"tag_name"[[:space:]]*:[[:space:]]*"\\([^"]*\\)".*/\\1/p')`,
|
|
1508
1575
|
' case "$latest" in v[0-9]*.[0-9]*.[0-9]*) ;; *) echo "MINDWIRE_FAIL no matching or latest release"; exit 1 ;; esac',
|
|
1509
1576
|
' release="https://github.com/oblien/mindwire/releases/download/$latest"',
|
|
1510
|
-
` asset="mindwired-$latest
|
|
1577
|
+
` asset="mindwired-$latest-${platform}-${arch}"`,
|
|
1511
1578
|
' download || { echo "MINDWIRE_FAIL latest release download failed"; exit 1; }',
|
|
1512
1579
|
"fi",
|
|
1513
1580
|
`chmod +x ${BIN_NEW}`
|
|
1514
1581
|
].join("\n");
|
|
1515
1582
|
}
|
|
1516
1583
|
const script = [
|
|
1517
|
-
"set -
|
|
1584
|
+
"set -eo pipefail",
|
|
1518
1585
|
`mkdir -p ${shellQuote(directory)}`,
|
|
1519
1586
|
// iOS takes this same workspace lock. Unique staging also protects concurrent local uploads.
|
|
1520
1587
|
stagedUpload ? `trap ${shellQuote(`rm -f ${shellQuote(stagedUpload)}`)} EXIT` : "",
|
|
1521
|
-
'command -v flock >/dev/null 2>&1 || { echo "MINDWIRE_FAIL flock is required to lock daemon updates"; exit 1; }',
|
|
1522
1588
|
`exec 9>${shellQuote(directory + "/daemon-install.lock")}`,
|
|
1523
|
-
|
|
1589
|
+
"if command -v flock >/dev/null 2>&1; then",
|
|
1590
|
+
' flock -w 360 9 || { echo "MINDWIRE_FAIL another daemon update is still running"; exit 1; }',
|
|
1591
|
+
"elif command -v lockf >/dev/null 2>&1; then",
|
|
1592
|
+
' lockf -s -t 360 9 || { echo "MINDWIRE_FAIL another daemon update is still running"; exit 1; }',
|
|
1593
|
+
'else echo "MINDWIRE_FAIL No supported update lock is available (flock on Linux, lockf on macOS)."; exit 1; fi',
|
|
1524
1594
|
`mw_token=${shellQuote(token)}`,
|
|
1525
1595
|
!cfg.token ? `if [ -s ${TOKEN} ]; then mw_token=$(cat ${TOKEN}); fi` : "",
|
|
1526
1596
|
!cfg.forceDeploy ? [
|
|
@@ -1528,6 +1598,10 @@ async function deploy(host, cfg, emit2, token, directory) {
|
|
|
1528
1598
|
`mw_version=$(printf '%s' "$mw_health" | sed -n 's/.*"version"[[:space:]]*:[[:space:]]*"\\([^" ]*\\)".*/\\1/p')`,
|
|
1529
1599
|
`if [ -n "$mw_health" ] && ${cfg.autoUpdate ? `[ "$mw_version" = ${shellQuote(desired)} ]` : "true"}; then echo MINDWIRE_READY; exit 0; fi`
|
|
1530
1600
|
].join("\n") : "",
|
|
1601
|
+
// macOS ships POSIX setsid in Perl, but does not include Linux's setsid executable.
|
|
1602
|
+
"if command -v setsid >/dev/null 2>&1; then mw_detach=(setsid);",
|
|
1603
|
+
`elif command -v perl >/dev/null 2>&1; then mw_detach=(perl -MPOSIX -e 'defined(my $sid = POSIX::setsid()) && $sid >= 0 or die "setsid: $!"; exec @ARGV; die "exec: $!";');`,
|
|
1604
|
+
'else echo "MINDWIRE_FAIL Cannot start the Mindwire service: setsid or Perl is required."; exit 1; fi',
|
|
1531
1605
|
acquire,
|
|
1532
1606
|
// Stop a prior daemon by exact process NAME, never `pkill -f <path>`: this whole script (which
|
|
1533
1607
|
// contains `${BIN}` several times) is the argv of the `bash -lc` shell running it, so a full-cmdline
|
|
@@ -1547,9 +1621,9 @@ async function deploy(host, cfg, emit2, token, directory) {
|
|
|
1547
1621
|
`mv -f ${BIN_NEW} ${BIN}`,
|
|
1548
1622
|
`chmod +x ${BIN}`,
|
|
1549
1623
|
// Detach so the daemon survives this exec's shell exiting. ADDR=":<port>" binds 0.0.0.0.
|
|
1550
|
-
`
|
|
1624
|
+
`"\${mw_detach[@]}" nohup env ADDR=":${cfg.port}" AGENT_TYPE=${shellQuote(cfg.agent)} AGENT_CWD=${shellQuote(cfg.agentCwd)} STATE_PATH=${STATE} DAEMON_TOKEN="$mw_token" ${BIN} > ${LOG} 2>&1 < /dev/null 9>&- &`,
|
|
1551
1625
|
// Health-poll from inside the VM (loopback) and emit a marker — exit codes are unreliable here.
|
|
1552
|
-
`for
|
|
1626
|
+
`for ((mw_attempt=0; mw_attempt<60; mw_attempt++)); do curl -fsS --max-time 2 -H "Authorization: Bearer $mw_token" http://127.0.0.1:${cfg.port}/healthz >/dev/null 2>&1 && { echo MINDWIRE_READY; exit 0; }; sleep 0.25; done`,
|
|
1553
1627
|
"echo MINDWIRE_FAIL",
|
|
1554
1628
|
`tail -n 40 ${LOG} 2>/dev/null || true`
|
|
1555
1629
|
].join("\n");
|
|
@@ -1575,10 +1649,15 @@ async function probeHealth(host, port, token) {
|
|
|
1575
1649
|
return { reachable: true };
|
|
1576
1650
|
}
|
|
1577
1651
|
}
|
|
1578
|
-
async function
|
|
1579
|
-
const res = await host.exec(["bash", "-lc", `printf '<<ARCH:%s>>' "$(uname -m)"`], { timeoutSeconds: 15 });
|
|
1580
|
-
const
|
|
1581
|
-
|
|
1652
|
+
async function probePlatform(host) {
|
|
1653
|
+
const res = await host.exec(["bash", "-lc", `printf '<<OS:%s>><<ARCH:%s>>' "$(uname -s)" "$(uname -m)"`], { timeoutSeconds: 15 });
|
|
1654
|
+
const os = ((res.stdout ?? "").match(/<<OS:([^>]*)>>/)?.[1] ?? "").trim().toLowerCase();
|
|
1655
|
+
const rawArch = ((res.stdout ?? "").match(/<<ARCH:([^>]*)>>/)?.[1] ?? "").trim();
|
|
1656
|
+
const arch = rawArch === "aarch64" || rawArch === "arm64" ? "arm64" : rawArch === "x86_64" || rawArch === "amd64" ? "amd64" : void 0;
|
|
1657
|
+
if (os !== "linux" && os !== "darwin" || !arch) {
|
|
1658
|
+
throw new MindwireError(`mindwire: unsupported workspace platform ${os || "unknown"}/${rawArch || "unknown"}; expected Linux or macOS on amd64 or arm64`);
|
|
1659
|
+
}
|
|
1660
|
+
return { platform: os, arch };
|
|
1582
1661
|
}
|
|
1583
1662
|
async function waitHostReady(host, timeoutMs = 6e4) {
|
|
1584
1663
|
const deadline = Date.now() + timeoutMs;
|
|
@@ -1598,15 +1677,18 @@ async function waitHostReady(host, timeoutMs = 6e4) {
|
|
|
1598
1677
|
);
|
|
1599
1678
|
}
|
|
1600
1679
|
async function resolveLinuxDaemon(explicit, arch) {
|
|
1680
|
+
return resolveHostDaemon(explicit, "linux", arch);
|
|
1681
|
+
}
|
|
1682
|
+
async function resolveHostDaemon(explicit, platform, arch) {
|
|
1601
1683
|
const fs = await import('fs');
|
|
1602
1684
|
if (explicit) {
|
|
1603
|
-
const resolved = explicit.replaceAll("{arch}", arch);
|
|
1685
|
+
const resolved = explicit.replaceAll("{os}", platform).replaceAll("{arch}", arch);
|
|
1604
1686
|
if (!fs.existsSync(resolved)) {
|
|
1605
1687
|
throw new MindwireError(`mindwire: sandbox daemonBin not found at ${resolved}`);
|
|
1606
1688
|
}
|
|
1607
1689
|
return resolved;
|
|
1608
1690
|
}
|
|
1609
|
-
return ensureDaemonBinary({ platform
|
|
1691
|
+
return ensureDaemonBinary({ platform, arch: arch === "arm64" ? "arm64" : "x64" });
|
|
1610
1692
|
}
|
|
1611
1693
|
async function readBytes(p) {
|
|
1612
1694
|
const fs = await import('fs/promises');
|
|
@@ -2459,6 +2541,6 @@ function clearCatalogCache() {
|
|
|
2459
2541
|
inflight = null;
|
|
2460
2542
|
}
|
|
2461
2543
|
|
|
2462
|
-
export { ApiError, AuthApi, ContainerHost, Http, MODELS_DEV_URL, McpApi, Mindwire, MindwireError, NotifyApi, ProjectOperationsApi, PromptsApi, ProvidersApi, Run, RunFailedError, SDK_VERSION, TimeoutError, WorkspaceApi, WorkspaceCollection, catalogModels, catalogProvider, catalogProviders, clearCatalogCache, docker, ensureDaemon, ensureDaemonBinary, loadCatalog, local, lookupModel, oblien, provisionContainer, provisionDocker, provisionOblien, provisionSsh, provisionSshContainer, remote, resolveLinuxDaemon, ssh, startEmbedded };
|
|
2544
|
+
export { ApiError, AuthApi, ContainerHost, Http, MODELS_DEV_URL, McpApi, Mindwire, MindwireError, NotifyApi, ProjectOperationsApi, PromptsApi, ProvidersApi, Run, RunFailedError, SDK_VERSION, SurfacesApi, TimeoutError, WorkspaceApi, WorkspaceCollection, catalogModels, catalogProvider, catalogProviders, clearCatalogCache, docker, ensureDaemon, ensureDaemonBinary, loadCatalog, local, lookupModel, oblien, provisionContainer, provisionDocker, provisionOblien, provisionSsh, provisionSshContainer, remote, resolveLinuxDaemon, ssh, startEmbedded };
|
|
2463
2545
|
//# sourceMappingURL=index.js.map
|
|
2464
2546
|
//# sourceMappingURL=index.js.map
|