mindwire 0.1.13 → 0.1.15
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 +117 -23
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +117 -24
- package/dist/index.js.map +1 -1
- package/dist/run.d.ts +3 -2
- package/dist/surfaces.d.ts +181 -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 +16 -1
- package/dist/workspace.d.ts +4 -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
|
@@ -380,8 +380,9 @@ var Run = class _Run {
|
|
|
380
380
|
await this.http.request("POST", `/runs/${encodeURIComponent(this.id)}/cancel`);
|
|
381
381
|
}
|
|
382
382
|
/**
|
|
383
|
-
* Answer a
|
|
384
|
-
*
|
|
383
|
+
* Answer a pending permission, question or plan. Message-mode questions remain answerable
|
|
384
|
+
* after completion; the daemon steers or resumes the conversation. Read the chat's latest
|
|
385
|
+
* run after replying to a completed run. Requires the agent's `respond` capability.
|
|
385
386
|
*/
|
|
386
387
|
async respond(input = {}) {
|
|
387
388
|
await this.http.request("POST", `/runs/${encodeURIComponent(this.id)}/respond`, { body: input });
|
|
@@ -580,8 +581,68 @@ var WorkspaceApi = class {
|
|
|
580
581
|
}
|
|
581
582
|
};
|
|
582
583
|
|
|
584
|
+
// src/surfaces.ts
|
|
585
|
+
var SurfacesApi = class {
|
|
586
|
+
constructor(mw) {
|
|
587
|
+
this.mw = mw;
|
|
588
|
+
}
|
|
589
|
+
mw;
|
|
590
|
+
list() {
|
|
591
|
+
return this.mw.http.request("GET", "/surfaces");
|
|
592
|
+
}
|
|
593
|
+
status(refresh = false) {
|
|
594
|
+
return this.mw.http.request("GET", "/surfaces/desktop", { query: { refresh } });
|
|
595
|
+
}
|
|
596
|
+
bind(binding) {
|
|
597
|
+
return this.mw.http.request("PUT", "/surfaces/desktop/binding", { body: binding });
|
|
598
|
+
}
|
|
599
|
+
open(request) {
|
|
600
|
+
return this.mw.http.request("POST", "/surfaces/desktop/sessions", { body: request });
|
|
601
|
+
}
|
|
602
|
+
control(id, request) {
|
|
603
|
+
return this.mw.http.request("POST", `/surfaces/desktop/sessions/${encodeURIComponent(id)}/control`, { body: request });
|
|
604
|
+
}
|
|
605
|
+
close(id) {
|
|
606
|
+
return this.mw.http.request("DELETE", `/surfaces/desktop/sessions/${encodeURIComponent(id)}`);
|
|
607
|
+
}
|
|
608
|
+
capture(id) {
|
|
609
|
+
return this.mw.http.request("POST", `/surfaces/desktop/sessions/${encodeURIComponent(id)}/captures`);
|
|
610
|
+
}
|
|
611
|
+
action(request) {
|
|
612
|
+
return this.mw.http.request("POST", "/surfaces/desktop/actions", { body: request });
|
|
613
|
+
}
|
|
614
|
+
receipt(id) {
|
|
615
|
+
return this.mw.http.request("GET", `/surfaces/desktop/actions/${encodeURIComponent(id)}`);
|
|
616
|
+
}
|
|
617
|
+
artifact(id) {
|
|
618
|
+
return this.mw.http.request("GET", `/artifacts/${encodeURIComponent(id)}`);
|
|
619
|
+
}
|
|
620
|
+
/** Every connection starts with the current snapshot, then revisions. Never replays input. */
|
|
621
|
+
async *watch(opts = {}) {
|
|
622
|
+
const controller = new AbortController();
|
|
623
|
+
const abort = () => controller.abort();
|
|
624
|
+
opts.signal?.addEventListener("abort", abort, { once: true });
|
|
625
|
+
if (opts.signal?.aborted) controller.abort();
|
|
626
|
+
try {
|
|
627
|
+
const response = await this.mw.http.open("GET", "/surfaces/desktop/events", { signal: controller.signal });
|
|
628
|
+
let instance;
|
|
629
|
+
let revision = -1;
|
|
630
|
+
for await (const snapshot of readSSE(response.body, controller.signal)) {
|
|
631
|
+
if (instance !== snapshot.instanceId || snapshot.revision > revision) {
|
|
632
|
+
instance = snapshot.instanceId;
|
|
633
|
+
revision = snapshot.revision;
|
|
634
|
+
yield snapshot;
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
} finally {
|
|
638
|
+
controller.abort();
|
|
639
|
+
opts.signal?.removeEventListener("abort", abort);
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
};
|
|
643
|
+
|
|
583
644
|
// src/version.ts
|
|
584
|
-
var SDK_VERSION = "0.1.
|
|
645
|
+
var SDK_VERSION = "0.1.15" ;
|
|
585
646
|
|
|
586
647
|
// src/daemon-binary.ts
|
|
587
648
|
function supported(platform, arch) {
|
|
@@ -841,6 +902,7 @@ var handleByTransport = /* @__PURE__ */ new WeakMap();
|
|
|
841
902
|
var Mindwire = class _Mindwire {
|
|
842
903
|
/** Workspace registry: saved agent profiles, projects and chat relationships. */
|
|
843
904
|
workspace;
|
|
905
|
+
surfaces;
|
|
844
906
|
http;
|
|
845
907
|
/** The default agent type applied to agent-scoped calls, if set. */
|
|
846
908
|
defaultAgent;
|
|
@@ -875,6 +937,7 @@ var Mindwire = class _Mindwire {
|
|
|
875
937
|
});
|
|
876
938
|
this.defaultAgent = opts.agent;
|
|
877
939
|
this.workspace = new WorkspaceApi(this);
|
|
940
|
+
this.surfaces = new SurfacesApi(this);
|
|
878
941
|
this.auth = new AuthApi(this);
|
|
879
942
|
this.prompts = new PromptsApi(this);
|
|
880
943
|
this.mcp = new McpApi(this);
|
|
@@ -896,6 +959,7 @@ var Mindwire = class _Mindwire {
|
|
|
896
959
|
clone.http = this.http;
|
|
897
960
|
clone.defaultAgent = agent;
|
|
898
961
|
clone.workspace = new WorkspaceApi(clone);
|
|
962
|
+
clone.surfaces = new SurfacesApi(clone);
|
|
899
963
|
clone.auth = new AuthApi(clone);
|
|
900
964
|
clone.prompts = new PromptsApi(clone);
|
|
901
965
|
clone.mcp = new McpApi(clone);
|
|
@@ -1474,32 +1538,36 @@ async function deploy(host, cfg, emit2, token, directory) {
|
|
|
1474
1538
|
const BIN = shellQuote(directory + "/mindwired"), BIN_NEW = shellQuote(newPath);
|
|
1475
1539
|
const STATE = shellQuote(directory + "/agent-state.json"), LOG = shellQuote(directory + "/daemon.log");
|
|
1476
1540
|
const TOKEN = shellQuote(directory + "/daemon.token");
|
|
1477
|
-
const arch = await
|
|
1541
|
+
const { platform, arch } = await probePlatform(host);
|
|
1478
1542
|
const desired = cfg.desiredVersion ?? SDK_VERSION;
|
|
1479
1543
|
let acquire = "";
|
|
1480
1544
|
let stagedUpload;
|
|
1481
1545
|
if (cfg.daemonBin) {
|
|
1482
|
-
const binPath = await
|
|
1546
|
+
const binPath = await resolveHostDaemon(cfg.daemonBin, platform, arch);
|
|
1483
1547
|
const bytes = await readBytes(binPath);
|
|
1484
|
-
emit2({ phase: "upload", message: `uploading daemon (${arch}, ${formatMiB(bytes.length)})`, arch, bytes: bytes.length });
|
|
1548
|
+
emit2({ phase: "upload", message: `uploading daemon (${platform}-${arch}, ${formatMiB(bytes.length)})`, platform, arch, bytes: bytes.length });
|
|
1485
1549
|
stagedUpload = `${newPath}-${(await import('crypto')).randomUUID()}`;
|
|
1550
|
+
await host.exec(["sh", "-lc", `mkdir -p ${shellQuote(directory)}`], { timeoutSeconds: 15 });
|
|
1486
1551
|
await host.putFile(stagedUpload, bytes, { mode: "0755" });
|
|
1487
1552
|
acquire = `mv -f ${shellQuote(stagedUpload)} ${BIN_NEW}`;
|
|
1488
1553
|
} else {
|
|
1489
1554
|
if (!/^[0-9]+\.[0-9]+\.[0-9]+(?:-[0-9A-Za-z.-]+)?$/.test(desired)) {
|
|
1490
1555
|
throw new MindwireError(`mindwire: cannot download daemon for non-release SDK version ${desired}`);
|
|
1491
1556
|
}
|
|
1492
|
-
const asset = `mindwired-v${desired}
|
|
1557
|
+
const asset = `mindwired-v${desired}-${platform}-${arch}`;
|
|
1493
1558
|
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 });
|
|
1559
|
+
emit2({ phase: "download", message: `downloading daemon v${desired} on the destination (${platform}-${arch})`, platform, arch });
|
|
1495
1560
|
acquire = [
|
|
1496
1561
|
`release=${shellQuote(release)}`,
|
|
1497
1562
|
`asset=${shellQuote(asset)}`,
|
|
1563
|
+
"if command -v sha256sum >/dev/null 2>&1; then mw_sha256=(sha256sum);",
|
|
1564
|
+
"elif command -v shasum >/dev/null 2>&1; then mw_sha256=(shasum -a 256);",
|
|
1565
|
+
'else echo "MINDWIRE_FAIL SHA-256 verification requires sha256sum or shasum"; exit 1; fi',
|
|
1498
1566
|
"download() {",
|
|
1499
1567
|
` expected=$(curl -fsSL "$release/checksums.txt" | awk -v asset="$asset" '$2 == asset { print $1; exit }') || return 1`,
|
|
1500
1568
|
' [ -n "$expected" ] || return 1',
|
|
1501
1569
|
` curl -fsSL "$release/$asset" -o ${BIN_NEW} || return 1`,
|
|
1502
|
-
` actual=$(
|
|
1570
|
+
` actual=$("\${mw_sha256[@]}" ${BIN_NEW} | awk '{print $1}') || return 1`,
|
|
1503
1571
|
' [ "$actual" = "$expected" ] || return 2',
|
|
1504
1572
|
"}",
|
|
1505
1573
|
"if download; then :; else",
|
|
@@ -1507,20 +1575,23 @@ async function deploy(host, cfg, emit2, token, directory) {
|
|
|
1507
1575
|
` latest=$(curl -fsSL https://api.github.com/repos/oblien/mindwire/releases/latest | sed -n 's/.*"tag_name"[[:space:]]*:[[:space:]]*"\\([^"]*\\)".*/\\1/p')`,
|
|
1508
1576
|
' case "$latest" in v[0-9]*.[0-9]*.[0-9]*) ;; *) echo "MINDWIRE_FAIL no matching or latest release"; exit 1 ;; esac',
|
|
1509
1577
|
' release="https://github.com/oblien/mindwire/releases/download/$latest"',
|
|
1510
|
-
` asset="mindwired-$latest
|
|
1578
|
+
` asset="mindwired-$latest-${platform}-${arch}"`,
|
|
1511
1579
|
' download || { echo "MINDWIRE_FAIL latest release download failed"; exit 1; }',
|
|
1512
1580
|
"fi",
|
|
1513
1581
|
`chmod +x ${BIN_NEW}`
|
|
1514
1582
|
].join("\n");
|
|
1515
1583
|
}
|
|
1516
1584
|
const script = [
|
|
1517
|
-
"set -
|
|
1585
|
+
"set -eo pipefail",
|
|
1518
1586
|
`mkdir -p ${shellQuote(directory)}`,
|
|
1519
1587
|
// iOS takes this same workspace lock. Unique staging also protects concurrent local uploads.
|
|
1520
1588
|
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
1589
|
`exec 9>${shellQuote(directory + "/daemon-install.lock")}`,
|
|
1523
|
-
|
|
1590
|
+
"if command -v flock >/dev/null 2>&1; then",
|
|
1591
|
+
' flock -w 360 9 || { echo "MINDWIRE_FAIL another daemon update is still running"; exit 1; }',
|
|
1592
|
+
"elif command -v lockf >/dev/null 2>&1; then",
|
|
1593
|
+
' lockf -s -t 360 9 || { echo "MINDWIRE_FAIL another daemon update is still running"; exit 1; }',
|
|
1594
|
+
'else echo "MINDWIRE_FAIL No supported update lock is available (flock on Linux, lockf on macOS)."; exit 1; fi',
|
|
1524
1595
|
`mw_token=${shellQuote(token)}`,
|
|
1525
1596
|
!cfg.token ? `if [ -s ${TOKEN} ]; then mw_token=$(cat ${TOKEN}); fi` : "",
|
|
1526
1597
|
!cfg.forceDeploy ? [
|
|
@@ -1528,6 +1599,10 @@ async function deploy(host, cfg, emit2, token, directory) {
|
|
|
1528
1599
|
`mw_version=$(printf '%s' "$mw_health" | sed -n 's/.*"version"[[:space:]]*:[[:space:]]*"\\([^" ]*\\)".*/\\1/p')`,
|
|
1529
1600
|
`if [ -n "$mw_health" ] && ${cfg.autoUpdate ? `[ "$mw_version" = ${shellQuote(desired)} ]` : "true"}; then echo MINDWIRE_READY; exit 0; fi`
|
|
1530
1601
|
].join("\n") : "",
|
|
1602
|
+
// macOS ships POSIX setsid in Perl, but does not include Linux's setsid executable.
|
|
1603
|
+
"if command -v setsid >/dev/null 2>&1; then mw_detach=(setsid);",
|
|
1604
|
+
`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: $!";');`,
|
|
1605
|
+
'else echo "MINDWIRE_FAIL Cannot start the Mindwire service: setsid or Perl is required."; exit 1; fi',
|
|
1531
1606
|
acquire,
|
|
1532
1607
|
// Stop a prior daemon by exact process NAME, never `pkill -f <path>`: this whole script (which
|
|
1533
1608
|
// contains `${BIN}` several times) is the argv of the `bash -lc` shell running it, so a full-cmdline
|
|
@@ -1547,11 +1622,21 @@ async function deploy(host, cfg, emit2, token, directory) {
|
|
|
1547
1622
|
`mv -f ${BIN_NEW} ${BIN}`,
|
|
1548
1623
|
`chmod +x ${BIN}`,
|
|
1549
1624
|
// Detach so the daemon survives this exec's shell exiting. ADDR=":<port>" binds 0.0.0.0.
|
|
1550
|
-
|
|
1625
|
+
// Ignore HUP directly: macOS nohup can fail after setsid in a headless workspace.
|
|
1626
|
+
`"\${mw_detach[@]}" /bin/sh -c 'trap "" HUP; exec "$@"' mindwire-service 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
1627
|
// Health-poll from inside the VM (loopback) and emit a marker — exit codes are unreliable here.
|
|
1552
|
-
|
|
1553
|
-
"echo
|
|
1554
|
-
|
|
1628
|
+
'mw_pid=$!; mw_exit=""; mw_deadline=$((SECONDS + 30))',
|
|
1629
|
+
`while (( SECONDS < mw_deadline )); do curl -fsS --connect-timeout 2 --max-time 3 -H "Authorization: Bearer $mw_token" http://127.0.0.1:${cfg.port}/healthz >/dev/null 2>&1 && { echo MINDWIRE_READY; exit 0; };`,
|
|
1630
|
+
' if [ -z "$mw_exit" ] && ! kill -0 "$mw_pid" 2>/dev/null; then',
|
|
1631
|
+
' mw_exit=0; wait "$mw_pid" || mw_exit=$?; [ "$mw_exit" = 0 ] || break',
|
|
1632
|
+
" fi",
|
|
1633
|
+
" sleep 0.25",
|
|
1634
|
+
"done",
|
|
1635
|
+
`mw_tail=$(tail -n 40 ${LOG} 2>/dev/null || true)`,
|
|
1636
|
+
'if [ -n "$mw_exit" ] && [ "$mw_exit" != 0 ]; then mw_reason="Mindwire exited during startup (status $mw_exit).";',
|
|
1637
|
+
'else mw_reason="The Mindwire service did not become ready."; fi',
|
|
1638
|
+
'[ -z "$mw_tail" ] || mw_reason="$mw_reason $mw_tail"',
|
|
1639
|
+
'printf "MINDWIRE_FAIL %s\\n" "$mw_reason"'
|
|
1555
1640
|
].join("\n");
|
|
1556
1641
|
emit2({ phase: "launch", message: "launching daemon" });
|
|
1557
1642
|
const res = await host.exec(["bash", "-lc", script], { timeoutSeconds: 420 });
|
|
@@ -1575,10 +1660,15 @@ async function probeHealth(host, port, token) {
|
|
|
1575
1660
|
return { reachable: true };
|
|
1576
1661
|
}
|
|
1577
1662
|
}
|
|
1578
|
-
async function
|
|
1579
|
-
const res = await host.exec(["bash", "-lc", `printf '<<ARCH:%s>>' "$(uname -m)"`], { timeoutSeconds: 15 });
|
|
1580
|
-
const
|
|
1581
|
-
|
|
1663
|
+
async function probePlatform(host) {
|
|
1664
|
+
const res = await host.exec(["bash", "-lc", `printf '<<OS:%s>><<ARCH:%s>>' "$(uname -s)" "$(uname -m)"`], { timeoutSeconds: 15 });
|
|
1665
|
+
const os = ((res.stdout ?? "").match(/<<OS:([^>]*)>>/)?.[1] ?? "").trim().toLowerCase();
|
|
1666
|
+
const rawArch = ((res.stdout ?? "").match(/<<ARCH:([^>]*)>>/)?.[1] ?? "").trim();
|
|
1667
|
+
const arch = rawArch === "aarch64" || rawArch === "arm64" ? "arm64" : rawArch === "x86_64" || rawArch === "amd64" ? "amd64" : void 0;
|
|
1668
|
+
if (os !== "linux" && os !== "darwin" || !arch) {
|
|
1669
|
+
throw new MindwireError(`mindwire: unsupported workspace platform ${os || "unknown"}/${rawArch || "unknown"}; expected Linux or macOS on amd64 or arm64`);
|
|
1670
|
+
}
|
|
1671
|
+
return { platform: os, arch };
|
|
1582
1672
|
}
|
|
1583
1673
|
async function waitHostReady(host, timeoutMs = 6e4) {
|
|
1584
1674
|
const deadline = Date.now() + timeoutMs;
|
|
@@ -1598,15 +1688,18 @@ async function waitHostReady(host, timeoutMs = 6e4) {
|
|
|
1598
1688
|
);
|
|
1599
1689
|
}
|
|
1600
1690
|
async function resolveLinuxDaemon(explicit, arch) {
|
|
1691
|
+
return resolveHostDaemon(explicit, "linux", arch);
|
|
1692
|
+
}
|
|
1693
|
+
async function resolveHostDaemon(explicit, platform, arch) {
|
|
1601
1694
|
const fs = await import('fs');
|
|
1602
1695
|
if (explicit) {
|
|
1603
|
-
const resolved = explicit.replaceAll("{arch}", arch);
|
|
1696
|
+
const resolved = explicit.replaceAll("{os}", platform).replaceAll("{arch}", arch);
|
|
1604
1697
|
if (!fs.existsSync(resolved)) {
|
|
1605
1698
|
throw new MindwireError(`mindwire: sandbox daemonBin not found at ${resolved}`);
|
|
1606
1699
|
}
|
|
1607
1700
|
return resolved;
|
|
1608
1701
|
}
|
|
1609
|
-
return ensureDaemonBinary({ platform
|
|
1702
|
+
return ensureDaemonBinary({ platform, arch: arch === "arm64" ? "arm64" : "x64" });
|
|
1610
1703
|
}
|
|
1611
1704
|
async function readBytes(p) {
|
|
1612
1705
|
const fs = await import('fs/promises');
|
|
@@ -2459,6 +2552,6 @@ function clearCatalogCache() {
|
|
|
2459
2552
|
inflight = null;
|
|
2460
2553
|
}
|
|
2461
2554
|
|
|
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 };
|
|
2555
|
+
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
2556
|
//# sourceMappingURL=index.js.map
|
|
2464
2557
|
//# sourceMappingURL=index.js.map
|