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