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/client.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Http, type FetchLike } from "./http.js";
|
|
2
2
|
import { Run } from "./run.js";
|
|
3
3
|
import { WorkspaceApi } from "./workspace.js";
|
|
4
|
+
import { SurfacesApi } from "./surfaces.js";
|
|
4
5
|
import { type Target } from "./target/index.js";
|
|
5
6
|
import type { EnsureEvent } from "./target/host.js";
|
|
6
7
|
import type { AgentInfo, AuthMethod, AuthState, AuthStatus, Catalog, ChatSummary, CustomProvider, DeleteResult, DoctorReport, Health, MCPServer, MemoryDoc, MemoryScope, Message, ModelInfo, Notification, NotifyChannel, NotifyChannelInput, NotifyChannelTestResult, NotifyConfigInput, NotifyConfigStatus, NotifyRule, NotifyRuleInput, ProcessFrame, PromptTemplate, ResolveOptions, SetupStatus, Stats, Subagent, TurnOptions } from "./types.js";
|
|
@@ -70,6 +71,7 @@ export interface AgentScoped {
|
|
|
70
71
|
export declare class Mindwire {
|
|
71
72
|
/** Workspace registry: saved agent profiles, projects and chat relationships. */
|
|
72
73
|
readonly workspace: WorkspaceApi;
|
|
74
|
+
readonly surfaces: SurfacesApi;
|
|
73
75
|
readonly http: Http;
|
|
74
76
|
/** The default agent type applied to agent-scoped calls, if set. */
|
|
75
77
|
readonly defaultAgent: string | undefined;
|
package/dist/index.cjs
CHANGED
|
@@ -583,8 +583,68 @@ var WorkspaceApi = class {
|
|
|
583
583
|
}
|
|
584
584
|
};
|
|
585
585
|
|
|
586
|
+
// src/surfaces.ts
|
|
587
|
+
var SurfacesApi = class {
|
|
588
|
+
constructor(mw) {
|
|
589
|
+
this.mw = mw;
|
|
590
|
+
}
|
|
591
|
+
mw;
|
|
592
|
+
list() {
|
|
593
|
+
return this.mw.http.request("GET", "/surfaces");
|
|
594
|
+
}
|
|
595
|
+
status(refresh = false) {
|
|
596
|
+
return this.mw.http.request("GET", "/surfaces/desktop", { query: { refresh } });
|
|
597
|
+
}
|
|
598
|
+
bind(binding) {
|
|
599
|
+
return this.mw.http.request("PUT", "/surfaces/desktop/binding", { body: binding });
|
|
600
|
+
}
|
|
601
|
+
open(request) {
|
|
602
|
+
return this.mw.http.request("POST", "/surfaces/desktop/sessions", { body: request });
|
|
603
|
+
}
|
|
604
|
+
control(id, request) {
|
|
605
|
+
return this.mw.http.request("POST", `/surfaces/desktop/sessions/${encodeURIComponent(id)}/control`, { body: request });
|
|
606
|
+
}
|
|
607
|
+
close(id) {
|
|
608
|
+
return this.mw.http.request("DELETE", `/surfaces/desktop/sessions/${encodeURIComponent(id)}`);
|
|
609
|
+
}
|
|
610
|
+
capture(id) {
|
|
611
|
+
return this.mw.http.request("POST", `/surfaces/desktop/sessions/${encodeURIComponent(id)}/captures`);
|
|
612
|
+
}
|
|
613
|
+
action(request) {
|
|
614
|
+
return this.mw.http.request("POST", "/surfaces/desktop/actions", { body: request });
|
|
615
|
+
}
|
|
616
|
+
receipt(id) {
|
|
617
|
+
return this.mw.http.request("GET", `/surfaces/desktop/actions/${encodeURIComponent(id)}`);
|
|
618
|
+
}
|
|
619
|
+
artifact(id) {
|
|
620
|
+
return this.mw.http.request("GET", `/artifacts/${encodeURIComponent(id)}`);
|
|
621
|
+
}
|
|
622
|
+
/** Every connection starts with the current snapshot, then revisions. Never replays input. */
|
|
623
|
+
async *watch(opts = {}) {
|
|
624
|
+
const controller = new AbortController();
|
|
625
|
+
const abort = () => controller.abort();
|
|
626
|
+
opts.signal?.addEventListener("abort", abort, { once: true });
|
|
627
|
+
if (opts.signal?.aborted) controller.abort();
|
|
628
|
+
try {
|
|
629
|
+
const response = await this.mw.http.open("GET", "/surfaces/desktop/events", { signal: controller.signal });
|
|
630
|
+
let instance;
|
|
631
|
+
let revision = -1;
|
|
632
|
+
for await (const snapshot of readSSE(response.body, controller.signal)) {
|
|
633
|
+
if (instance !== snapshot.instanceId || snapshot.revision > revision) {
|
|
634
|
+
instance = snapshot.instanceId;
|
|
635
|
+
revision = snapshot.revision;
|
|
636
|
+
yield snapshot;
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
} finally {
|
|
640
|
+
controller.abort();
|
|
641
|
+
opts.signal?.removeEventListener("abort", abort);
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
};
|
|
645
|
+
|
|
586
646
|
// src/version.ts
|
|
587
|
-
var SDK_VERSION = "0.1.
|
|
647
|
+
var SDK_VERSION = "0.1.14" ;
|
|
588
648
|
|
|
589
649
|
// src/daemon-binary.ts
|
|
590
650
|
function supported(platform, arch) {
|
|
@@ -844,6 +904,7 @@ var handleByTransport = /* @__PURE__ */ new WeakMap();
|
|
|
844
904
|
var Mindwire = class _Mindwire {
|
|
845
905
|
/** Workspace registry: saved agent profiles, projects and chat relationships. */
|
|
846
906
|
workspace;
|
|
907
|
+
surfaces;
|
|
847
908
|
http;
|
|
848
909
|
/** The default agent type applied to agent-scoped calls, if set. */
|
|
849
910
|
defaultAgent;
|
|
@@ -878,6 +939,7 @@ var Mindwire = class _Mindwire {
|
|
|
878
939
|
});
|
|
879
940
|
this.defaultAgent = opts.agent;
|
|
880
941
|
this.workspace = new WorkspaceApi(this);
|
|
942
|
+
this.surfaces = new SurfacesApi(this);
|
|
881
943
|
this.auth = new AuthApi(this);
|
|
882
944
|
this.prompts = new PromptsApi(this);
|
|
883
945
|
this.mcp = new McpApi(this);
|
|
@@ -899,6 +961,7 @@ var Mindwire = class _Mindwire {
|
|
|
899
961
|
clone.http = this.http;
|
|
900
962
|
clone.defaultAgent = agent;
|
|
901
963
|
clone.workspace = new WorkspaceApi(clone);
|
|
964
|
+
clone.surfaces = new SurfacesApi(clone);
|
|
902
965
|
clone.auth = new AuthApi(clone);
|
|
903
966
|
clone.prompts = new PromptsApi(clone);
|
|
904
967
|
clone.mcp = new McpApi(clone);
|
|
@@ -1477,32 +1540,36 @@ async function deploy(host, cfg, emit2, token, directory) {
|
|
|
1477
1540
|
const BIN = shellQuote(directory + "/mindwired"), BIN_NEW = shellQuote(newPath);
|
|
1478
1541
|
const STATE = shellQuote(directory + "/agent-state.json"), LOG = shellQuote(directory + "/daemon.log");
|
|
1479
1542
|
const TOKEN = shellQuote(directory + "/daemon.token");
|
|
1480
|
-
const arch = await
|
|
1543
|
+
const { platform, arch } = await probePlatform(host);
|
|
1481
1544
|
const desired = cfg.desiredVersion ?? SDK_VERSION;
|
|
1482
1545
|
let acquire = "";
|
|
1483
1546
|
let stagedUpload;
|
|
1484
1547
|
if (cfg.daemonBin) {
|
|
1485
|
-
const binPath = await
|
|
1548
|
+
const binPath = await resolveHostDaemon(cfg.daemonBin, platform, arch);
|
|
1486
1549
|
const bytes = await readBytes(binPath);
|
|
1487
|
-
emit2({ phase: "upload", message: `uploading daemon (${arch}, ${formatMiB(bytes.length)})`, arch, bytes: bytes.length });
|
|
1550
|
+
emit2({ phase: "upload", message: `uploading daemon (${platform}-${arch}, ${formatMiB(bytes.length)})`, platform, arch, bytes: bytes.length });
|
|
1488
1551
|
stagedUpload = `${newPath}-${(await import('crypto')).randomUUID()}`;
|
|
1552
|
+
await host.exec(["sh", "-lc", `mkdir -p ${shellQuote(directory)}`], { timeoutSeconds: 15 });
|
|
1489
1553
|
await host.putFile(stagedUpload, bytes, { mode: "0755" });
|
|
1490
1554
|
acquire = `mv -f ${shellQuote(stagedUpload)} ${BIN_NEW}`;
|
|
1491
1555
|
} else {
|
|
1492
1556
|
if (!/^[0-9]+\.[0-9]+\.[0-9]+(?:-[0-9A-Za-z.-]+)?$/.test(desired)) {
|
|
1493
1557
|
throw new MindwireError(`mindwire: cannot download daemon for non-release SDK version ${desired}`);
|
|
1494
1558
|
}
|
|
1495
|
-
const asset = `mindwired-v${desired}
|
|
1559
|
+
const asset = `mindwired-v${desired}-${platform}-${arch}`;
|
|
1496
1560
|
const release = `https://github.com/oblien/mindwire/releases/download/v${desired}`;
|
|
1497
|
-
emit2({ phase: "download", message: `downloading daemon v${desired} on the destination (${arch})`, arch });
|
|
1561
|
+
emit2({ phase: "download", message: `downloading daemon v${desired} on the destination (${platform}-${arch})`, platform, arch });
|
|
1498
1562
|
acquire = [
|
|
1499
1563
|
`release=${shellQuote(release)}`,
|
|
1500
1564
|
`asset=${shellQuote(asset)}`,
|
|
1565
|
+
"if command -v sha256sum >/dev/null 2>&1; then mw_sha256=(sha256sum);",
|
|
1566
|
+
"elif command -v shasum >/dev/null 2>&1; then mw_sha256=(shasum -a 256);",
|
|
1567
|
+
'else echo "MINDWIRE_FAIL SHA-256 verification requires sha256sum or shasum"; exit 1; fi',
|
|
1501
1568
|
"download() {",
|
|
1502
1569
|
` expected=$(curl -fsSL "$release/checksums.txt" | awk -v asset="$asset" '$2 == asset { print $1; exit }') || return 1`,
|
|
1503
1570
|
' [ -n "$expected" ] || return 1',
|
|
1504
1571
|
` curl -fsSL "$release/$asset" -o ${BIN_NEW} || return 1`,
|
|
1505
|
-
` actual=$(
|
|
1572
|
+
` actual=$("\${mw_sha256[@]}" ${BIN_NEW} | awk '{print $1}') || return 1`,
|
|
1506
1573
|
' [ "$actual" = "$expected" ] || return 2',
|
|
1507
1574
|
"}",
|
|
1508
1575
|
"if download; then :; else",
|
|
@@ -1510,20 +1577,23 @@ async function deploy(host, cfg, emit2, token, directory) {
|
|
|
1510
1577
|
` latest=$(curl -fsSL https://api.github.com/repos/oblien/mindwire/releases/latest | sed -n 's/.*"tag_name"[[:space:]]*:[[:space:]]*"\\([^"]*\\)".*/\\1/p')`,
|
|
1511
1578
|
' case "$latest" in v[0-9]*.[0-9]*.[0-9]*) ;; *) echo "MINDWIRE_FAIL no matching or latest release"; exit 1 ;; esac',
|
|
1512
1579
|
' release="https://github.com/oblien/mindwire/releases/download/$latest"',
|
|
1513
|
-
` asset="mindwired-$latest
|
|
1580
|
+
` asset="mindwired-$latest-${platform}-${arch}"`,
|
|
1514
1581
|
' download || { echo "MINDWIRE_FAIL latest release download failed"; exit 1; }',
|
|
1515
1582
|
"fi",
|
|
1516
1583
|
`chmod +x ${BIN_NEW}`
|
|
1517
1584
|
].join("\n");
|
|
1518
1585
|
}
|
|
1519
1586
|
const script = [
|
|
1520
|
-
"set -
|
|
1587
|
+
"set -eo pipefail",
|
|
1521
1588
|
`mkdir -p ${shellQuote(directory)}`,
|
|
1522
1589
|
// iOS takes this same workspace lock. Unique staging also protects concurrent local uploads.
|
|
1523
1590
|
stagedUpload ? `trap ${shellQuote(`rm -f ${shellQuote(stagedUpload)}`)} EXIT` : "",
|
|
1524
|
-
'command -v flock >/dev/null 2>&1 || { echo "MINDWIRE_FAIL flock is required to lock daemon updates"; exit 1; }',
|
|
1525
1591
|
`exec 9>${shellQuote(directory + "/daemon-install.lock")}`,
|
|
1526
|
-
|
|
1592
|
+
"if command -v flock >/dev/null 2>&1; then",
|
|
1593
|
+
' flock -w 360 9 || { echo "MINDWIRE_FAIL another daemon update is still running"; exit 1; }',
|
|
1594
|
+
"elif command -v lockf >/dev/null 2>&1; then",
|
|
1595
|
+
' lockf -s -t 360 9 || { echo "MINDWIRE_FAIL another daemon update is still running"; exit 1; }',
|
|
1596
|
+
'else echo "MINDWIRE_FAIL No supported update lock is available (flock on Linux, lockf on macOS)."; exit 1; fi',
|
|
1527
1597
|
`mw_token=${shellQuote(token)}`,
|
|
1528
1598
|
!cfg.token ? `if [ -s ${TOKEN} ]; then mw_token=$(cat ${TOKEN}); fi` : "",
|
|
1529
1599
|
!cfg.forceDeploy ? [
|
|
@@ -1531,6 +1601,10 @@ async function deploy(host, cfg, emit2, token, directory) {
|
|
|
1531
1601
|
`mw_version=$(printf '%s' "$mw_health" | sed -n 's/.*"version"[[:space:]]*:[[:space:]]*"\\([^" ]*\\)".*/\\1/p')`,
|
|
1532
1602
|
`if [ -n "$mw_health" ] && ${cfg.autoUpdate ? `[ "$mw_version" = ${shellQuote(desired)} ]` : "true"}; then echo MINDWIRE_READY; exit 0; fi`
|
|
1533
1603
|
].join("\n") : "",
|
|
1604
|
+
// macOS ships POSIX setsid in Perl, but does not include Linux's setsid executable.
|
|
1605
|
+
"if command -v setsid >/dev/null 2>&1; then mw_detach=(setsid);",
|
|
1606
|
+
`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: $!";');`,
|
|
1607
|
+
'else echo "MINDWIRE_FAIL Cannot start the Mindwire service: setsid or Perl is required."; exit 1; fi',
|
|
1534
1608
|
acquire,
|
|
1535
1609
|
// Stop a prior daemon by exact process NAME, never `pkill -f <path>`: this whole script (which
|
|
1536
1610
|
// contains `${BIN}` several times) is the argv of the `bash -lc` shell running it, so a full-cmdline
|
|
@@ -1550,9 +1624,9 @@ async function deploy(host, cfg, emit2, token, directory) {
|
|
|
1550
1624
|
`mv -f ${BIN_NEW} ${BIN}`,
|
|
1551
1625
|
`chmod +x ${BIN}`,
|
|
1552
1626
|
// Detach so the daemon survives this exec's shell exiting. ADDR=":<port>" binds 0.0.0.0.
|
|
1553
|
-
`
|
|
1627
|
+
`"\${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>&- &`,
|
|
1554
1628
|
// Health-poll from inside the VM (loopback) and emit a marker — exit codes are unreliable here.
|
|
1555
|
-
`for
|
|
1629
|
+
`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`,
|
|
1556
1630
|
"echo MINDWIRE_FAIL",
|
|
1557
1631
|
`tail -n 40 ${LOG} 2>/dev/null || true`
|
|
1558
1632
|
].join("\n");
|
|
@@ -1578,10 +1652,15 @@ async function probeHealth(host, port, token) {
|
|
|
1578
1652
|
return { reachable: true };
|
|
1579
1653
|
}
|
|
1580
1654
|
}
|
|
1581
|
-
async function
|
|
1582
|
-
const res = await host.exec(["bash", "-lc", `printf '<<ARCH:%s>>' "$(uname -m)"`], { timeoutSeconds: 15 });
|
|
1583
|
-
const
|
|
1584
|
-
|
|
1655
|
+
async function probePlatform(host) {
|
|
1656
|
+
const res = await host.exec(["bash", "-lc", `printf '<<OS:%s>><<ARCH:%s>>' "$(uname -s)" "$(uname -m)"`], { timeoutSeconds: 15 });
|
|
1657
|
+
const os = ((res.stdout ?? "").match(/<<OS:([^>]*)>>/)?.[1] ?? "").trim().toLowerCase();
|
|
1658
|
+
const rawArch = ((res.stdout ?? "").match(/<<ARCH:([^>]*)>>/)?.[1] ?? "").trim();
|
|
1659
|
+
const arch = rawArch === "aarch64" || rawArch === "arm64" ? "arm64" : rawArch === "x86_64" || rawArch === "amd64" ? "amd64" : void 0;
|
|
1660
|
+
if (os !== "linux" && os !== "darwin" || !arch) {
|
|
1661
|
+
throw new MindwireError(`mindwire: unsupported workspace platform ${os || "unknown"}/${rawArch || "unknown"}; expected Linux or macOS on amd64 or arm64`);
|
|
1662
|
+
}
|
|
1663
|
+
return { platform: os, arch };
|
|
1585
1664
|
}
|
|
1586
1665
|
async function waitHostReady(host, timeoutMs = 6e4) {
|
|
1587
1666
|
const deadline = Date.now() + timeoutMs;
|
|
@@ -1601,15 +1680,18 @@ async function waitHostReady(host, timeoutMs = 6e4) {
|
|
|
1601
1680
|
);
|
|
1602
1681
|
}
|
|
1603
1682
|
async function resolveLinuxDaemon(explicit, arch) {
|
|
1683
|
+
return resolveHostDaemon(explicit, "linux", arch);
|
|
1684
|
+
}
|
|
1685
|
+
async function resolveHostDaemon(explicit, platform, arch) {
|
|
1604
1686
|
const fs = await import('fs');
|
|
1605
1687
|
if (explicit) {
|
|
1606
|
-
const resolved = explicit.replaceAll("{arch}", arch);
|
|
1688
|
+
const resolved = explicit.replaceAll("{os}", platform).replaceAll("{arch}", arch);
|
|
1607
1689
|
if (!fs.existsSync(resolved)) {
|
|
1608
1690
|
throw new MindwireError(`mindwire: sandbox daemonBin not found at ${resolved}`);
|
|
1609
1691
|
}
|
|
1610
1692
|
return resolved;
|
|
1611
1693
|
}
|
|
1612
|
-
return ensureDaemonBinary({ platform
|
|
1694
|
+
return ensureDaemonBinary({ platform, arch: arch === "arm64" ? "arm64" : "x64" });
|
|
1613
1695
|
}
|
|
1614
1696
|
async function readBytes(p) {
|
|
1615
1697
|
const fs = await import('fs/promises');
|
|
@@ -2477,6 +2559,7 @@ exports.ProvidersApi = ProvidersApi;
|
|
|
2477
2559
|
exports.Run = Run;
|
|
2478
2560
|
exports.RunFailedError = RunFailedError;
|
|
2479
2561
|
exports.SDK_VERSION = SDK_VERSION;
|
|
2562
|
+
exports.SurfacesApi = SurfacesApi;
|
|
2480
2563
|
exports.TimeoutError = TimeoutError;
|
|
2481
2564
|
exports.WorkspaceApi = WorkspaceApi;
|
|
2482
2565
|
exports.WorkspaceCollection = WorkspaceCollection;
|