mindwire 0.1.15 → 0.1.19
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 +17 -3
- package/dist/index.cjs +193 -28
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.js +192 -29
- package/dist/index.js.map +1 -1
- package/dist/run.d.ts +3 -1
- package/dist/service.d.ts +24 -0
- package/dist/surfaces.d.ts +1 -1
- package/dist/target/docker.d.ts +1 -1
- package/dist/target/host.d.ts +1 -1
- package/dist/target/oblien.d.ts +2 -2
- package/dist/target/ssh.d.ts +1 -1
- package/dist/types.d.ts +68 -1
- package/dist/workspace.d.ts +89 -2
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -32,7 +32,9 @@ export { catalogProviders, catalogProvider, catalogModels, lookupModel, loadCata
|
|
|
32
32
|
export type { CatalogOptions } from "./catalog/index.js";
|
|
33
33
|
export { MindwireError, ApiError, RunFailedError, TimeoutError } from "./errors.js";
|
|
34
34
|
export * from "./types.js";
|
|
35
|
-
export {
|
|
36
|
-
export type {
|
|
35
|
+
export { ServiceApi } from "./service.js";
|
|
36
|
+
export type { ServiceUpdateState, ServiceUpdateLease } from "./service.js";
|
|
37
|
+
export { WorkspaceApi, WorkspaceCollection, ProjectOperationsApi, GitAccessApi } from "./workspace.js";
|
|
38
|
+
export type { ProjectRequest, ProjectAuth, ProjectOperation, ProjectRemoveRequest, GitConnection, GitAccessState, ProjectGitState, GitAction, GitIdentity, GitOperationRequest, GitOperation } from "./workspace.js";
|
|
37
39
|
export type { WorkspaceRecord, WorkspaceAgent, WorkspaceProject, WorkspaceChat, WorkspaceKind, WorkspaceInput, WorkspaceImport, WorkspaceSnapshot } from "./workspace.js";
|
|
38
40
|
export * from "./surfaces.js";
|
package/dist/index.js
CHANGED
|
@@ -546,12 +546,14 @@ var WorkspaceCollection = class {
|
|
|
546
546
|
var WorkspaceApi = class {
|
|
547
547
|
constructor(mw) {
|
|
548
548
|
this.mw = mw;
|
|
549
|
+
this.git = new GitAccessApi(mw);
|
|
549
550
|
this.operations = new ProjectOperationsApi(mw);
|
|
550
551
|
this.agents = new WorkspaceCollection(mw, "agents");
|
|
551
552
|
this.projects = new WorkspaceCollection(mw, "projects");
|
|
552
553
|
this.chats = new WorkspaceCollection(mw, "chats");
|
|
553
554
|
}
|
|
554
555
|
mw;
|
|
556
|
+
git;
|
|
555
557
|
operations;
|
|
556
558
|
agents;
|
|
557
559
|
projects;
|
|
@@ -580,6 +582,75 @@ var WorkspaceApi = class {
|
|
|
580
582
|
return this.mw.http.request("POST", "/workspace/import", { body: records });
|
|
581
583
|
}
|
|
582
584
|
};
|
|
585
|
+
var GitAccessApi = class {
|
|
586
|
+
constructor(mw) {
|
|
587
|
+
this.mw = mw;
|
|
588
|
+
}
|
|
589
|
+
mw;
|
|
590
|
+
state() {
|
|
591
|
+
return this.mw.http.request("GET", "/workspace/git");
|
|
592
|
+
}
|
|
593
|
+
setDefault(connection, auth) {
|
|
594
|
+
return this.mw.http.request("PUT", "/workspace/git", { body: { connection, auth } });
|
|
595
|
+
}
|
|
596
|
+
forget(connectionId) {
|
|
597
|
+
return this.mw.http.request("DELETE", `/workspace/git/connections/${encodeURIComponent(connectionId)}`);
|
|
598
|
+
}
|
|
599
|
+
project(projectId) {
|
|
600
|
+
return this.mw.http.request("GET", `/workspace/projects/${encodeURIComponent(projectId)}/git`);
|
|
601
|
+
}
|
|
602
|
+
setProject(projectId, connection, expectedRevision, auth) {
|
|
603
|
+
return this.mw.http.request("PUT", `/workspace/projects/${encodeURIComponent(projectId)}/git`, {
|
|
604
|
+
body: { connection, expectedRevision, auth }
|
|
605
|
+
});
|
|
606
|
+
}
|
|
607
|
+
/** Compatibility call. Prefer start() and operation()/watch() for reconnectable writes. */
|
|
608
|
+
run(projectId, operation, auth) {
|
|
609
|
+
return this.mw.http.request("POST", `/workspace/projects/${encodeURIComponent(projectId)}/git/${operation}`, { body: { auth } });
|
|
610
|
+
}
|
|
611
|
+
/** Requires health.gitOperationsVersion >= 1 (>= 2 for branch changes). Acceptance persists before Git runs;
|
|
612
|
+
* disconnecting only detaches the client. The same ID/intent never runs twice.
|
|
613
|
+
*/
|
|
614
|
+
start(projectId, request) {
|
|
615
|
+
return this.mw.http.request("POST", `/workspace/projects/${encodeURIComponent(projectId)}/git/operations`, { body: request });
|
|
616
|
+
}
|
|
617
|
+
async operations(projectId, activeOnly = false) {
|
|
618
|
+
const result = await this.mw.http.request(
|
|
619
|
+
"GET",
|
|
620
|
+
`/workspace/projects/${encodeURIComponent(projectId)}/git/operations`,
|
|
621
|
+
{ query: { active: activeOnly, actionsVersion: 2 } }
|
|
622
|
+
);
|
|
623
|
+
return result.operations;
|
|
624
|
+
}
|
|
625
|
+
operation(id) {
|
|
626
|
+
return this.mw.http.request("GET", `/workspace/git/operations/${encodeURIComponent(id)}`);
|
|
627
|
+
}
|
|
628
|
+
cancel(id) {
|
|
629
|
+
return this.mw.http.request("POST", `/workspace/git/operations/${encodeURIComponent(id)}/cancel`);
|
|
630
|
+
}
|
|
631
|
+
/** Current snapshot followed by state changes. Aborting observation never cancels the operation. */
|
|
632
|
+
async *watch(id, opts = {}) {
|
|
633
|
+
const controller = new AbortController();
|
|
634
|
+
const abort = () => controller.abort();
|
|
635
|
+
opts.signal?.addEventListener("abort", abort, { once: true });
|
|
636
|
+
if (opts.signal?.aborted) controller.abort();
|
|
637
|
+
try {
|
|
638
|
+
const response = await this.mw.http.open("GET", `/workspace/git/operations/${encodeURIComponent(id)}/stream`, {
|
|
639
|
+
signal: controller.signal
|
|
640
|
+
});
|
|
641
|
+
let sequence = -1;
|
|
642
|
+
for await (const operation of readSSE(response.body, controller.signal)) {
|
|
643
|
+
if (operation.sequence > sequence) {
|
|
644
|
+
sequence = operation.sequence;
|
|
645
|
+
yield operation;
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
} finally {
|
|
649
|
+
controller.abort();
|
|
650
|
+
opts.signal?.removeEventListener("abort", abort);
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
};
|
|
583
654
|
|
|
584
655
|
// src/surfaces.ts
|
|
585
656
|
var SurfacesApi = class {
|
|
@@ -641,8 +712,27 @@ var SurfacesApi = class {
|
|
|
641
712
|
}
|
|
642
713
|
};
|
|
643
714
|
|
|
715
|
+
// src/service.ts
|
|
716
|
+
var ServiceApi = class {
|
|
717
|
+
constructor(client) {
|
|
718
|
+
this.client = client;
|
|
719
|
+
}
|
|
720
|
+
client;
|
|
721
|
+
updateStatus() {
|
|
722
|
+
return this.client.http.request("GET", "/service/update");
|
|
723
|
+
}
|
|
724
|
+
/** Download first. Acquire immediately before replacement; busy work returns 409.
|
|
725
|
+
* Admission stays closed until release, process exit, or the one-minute expiry. */
|
|
726
|
+
acquireUpdate() {
|
|
727
|
+
return this.client.http.request("POST", "/service/update");
|
|
728
|
+
}
|
|
729
|
+
releaseUpdate(id) {
|
|
730
|
+
return this.client.http.request("DELETE", `/service/update/${encodeURIComponent(id)}`);
|
|
731
|
+
}
|
|
732
|
+
};
|
|
733
|
+
|
|
644
734
|
// src/version.ts
|
|
645
|
-
var SDK_VERSION = "0.1.
|
|
735
|
+
var SDK_VERSION = "0.1.19" ;
|
|
646
736
|
|
|
647
737
|
// src/daemon-binary.ts
|
|
648
738
|
function supported(platform, arch) {
|
|
@@ -903,6 +993,7 @@ var Mindwire = class _Mindwire {
|
|
|
903
993
|
/** Workspace registry: saved agent profiles, projects and chat relationships. */
|
|
904
994
|
workspace;
|
|
905
995
|
surfaces;
|
|
996
|
+
service;
|
|
906
997
|
http;
|
|
907
998
|
/** The default agent type applied to agent-scoped calls, if set. */
|
|
908
999
|
defaultAgent;
|
|
@@ -938,6 +1029,7 @@ var Mindwire = class _Mindwire {
|
|
|
938
1029
|
this.defaultAgent = opts.agent;
|
|
939
1030
|
this.workspace = new WorkspaceApi(this);
|
|
940
1031
|
this.surfaces = new SurfacesApi(this);
|
|
1032
|
+
this.service = new ServiceApi(this);
|
|
941
1033
|
this.auth = new AuthApi(this);
|
|
942
1034
|
this.prompts = new PromptsApi(this);
|
|
943
1035
|
this.mcp = new McpApi(this);
|
|
@@ -960,6 +1052,7 @@ var Mindwire = class _Mindwire {
|
|
|
960
1052
|
clone.defaultAgent = agent;
|
|
961
1053
|
clone.workspace = new WorkspaceApi(clone);
|
|
962
1054
|
clone.surfaces = new SurfacesApi(clone);
|
|
1055
|
+
clone.service = new ServiceApi(clone);
|
|
963
1056
|
clone.auth = new AuthApi(clone);
|
|
964
1057
|
clone.prompts = new PromptsApi(clone);
|
|
965
1058
|
clone.mcp = new McpApi(clone);
|
|
@@ -1008,6 +1101,12 @@ var Mindwire = class _Mindwire {
|
|
|
1008
1101
|
agent(scoped) {
|
|
1009
1102
|
return this.http.request("GET", "/agent", { query: this.agentParam(scoped) });
|
|
1010
1103
|
}
|
|
1104
|
+
/** `GET /agent/software` — installed version, compatibility and approved update target. */
|
|
1105
|
+
software(opts = {}) {
|
|
1106
|
+
return this.http.request("GET", "/agent/software", {
|
|
1107
|
+
query: { ...this.agentParam(opts), ...opts.refresh ? { refresh: "true" } : {} }
|
|
1108
|
+
});
|
|
1109
|
+
}
|
|
1011
1110
|
/**
|
|
1012
1111
|
* `GET /models` — the models the selected agent can run for the configured account. An empty array
|
|
1013
1112
|
* is valid (no credentials yet / offline). Throws a 400 {@link ApiError} for an agent whose model is
|
|
@@ -1025,7 +1124,7 @@ var Mindwire = class _Mindwire {
|
|
|
1025
1124
|
setup(scoped) {
|
|
1026
1125
|
return this.http.request("POST", "/setup", { query: this.agentParam(scoped) });
|
|
1027
1126
|
}
|
|
1028
|
-
/** `POST /update` —
|
|
1127
|
+
/** `POST /update` — install the catalog's newest tested version compatible with this daemon. */
|
|
1029
1128
|
update(scoped) {
|
|
1030
1129
|
return this.http.request("POST", "/update", { query: this.agentParam(scoped) });
|
|
1031
1130
|
}
|
|
@@ -1122,6 +1221,7 @@ var Mindwire = class _Mindwire {
|
|
|
1122
1221
|
if (input.options !== void 0) body.options = input.options;
|
|
1123
1222
|
if (input.mode !== void 0) body.mode = input.mode;
|
|
1124
1223
|
if (input.resolve !== void 0) body.resolve = input.resolve;
|
|
1224
|
+
if (input.gitAuth !== void 0) body.gitAuth = input.gitAuth;
|
|
1125
1225
|
const data = await this.http.request("POST", "/turns", {
|
|
1126
1226
|
query: this.agentParam(input),
|
|
1127
1227
|
body
|
|
@@ -1225,12 +1325,26 @@ var AuthApi = class {
|
|
|
1225
1325
|
body: input
|
|
1226
1326
|
});
|
|
1227
1327
|
}
|
|
1328
|
+
/** Read one interactive attempt, scoped by the returned AuthState.flowId. */
|
|
1329
|
+
poll(flowId, scoped) {
|
|
1330
|
+
return this.step({ _flowId: flowId }, scoped);
|
|
1331
|
+
}
|
|
1332
|
+
/** Cancel this native login without affecting a newer sign-in attempt. */
|
|
1333
|
+
cancel(flowId, scoped) {
|
|
1334
|
+
return this.step({ _flowId: flowId, _action: "cancel" }, scoped);
|
|
1335
|
+
}
|
|
1228
1336
|
/** `GET /auth/status` — is the agent authenticated, and via which method. */
|
|
1229
1337
|
status(scoped) {
|
|
1230
1338
|
return this.mw.http.request("GET", "/auth/status", {
|
|
1231
1339
|
query: this.mw.agentParam(scoped)
|
|
1232
1340
|
});
|
|
1233
1341
|
}
|
|
1342
|
+
/** `POST /auth/logout` — disconnect this harness once its running chats finish. */
|
|
1343
|
+
logout(scoped) {
|
|
1344
|
+
return this.mw.http.request("POST", "/auth/logout", {
|
|
1345
|
+
query: this.mw.agentParam(scoped)
|
|
1346
|
+
});
|
|
1347
|
+
}
|
|
1234
1348
|
};
|
|
1235
1349
|
var PromptsApi = class {
|
|
1236
1350
|
constructor(mw) {
|
|
@@ -1472,6 +1586,23 @@ var NotifyApi = class {
|
|
|
1472
1586
|
};
|
|
1473
1587
|
|
|
1474
1588
|
// src/target/host.ts
|
|
1589
|
+
function versionAtLeast(actual, desired) {
|
|
1590
|
+
if (actual === desired) return true;
|
|
1591
|
+
if (!actual || !/^\d+\.\d+\.\d+$/.test(actual) || !/^\d+\.\d+\.\d+$/.test(desired)) return false;
|
|
1592
|
+
const a = actual.split(".").map(Number), b = desired.split(".").map(Number);
|
|
1593
|
+
for (let i = 0; i < 3; i++) {
|
|
1594
|
+
if (a[i] !== b[i]) return a[i] > b[i];
|
|
1595
|
+
}
|
|
1596
|
+
return true;
|
|
1597
|
+
}
|
|
1598
|
+
var versionCheckScript = String.raw`version_at_least() {
|
|
1599
|
+
[ "$1" != "$2" ] || return 0
|
|
1600
|
+
[[ "$1" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] && [[ "$2" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] || return 1
|
|
1601
|
+
local a b c x y z
|
|
1602
|
+
IFS=. read -r a b c <<< "$1"
|
|
1603
|
+
IFS=. read -r x y z <<< "$2"
|
|
1604
|
+
(( 10#$a > 10#$x || (10#$a == 10#$x && 10#$b > 10#$y) || (10#$a == 10#$x && 10#$b == 10#$y && 10#$c >= 10#$z) ))
|
|
1605
|
+
}`;
|
|
1475
1606
|
function makeEmit(cfg) {
|
|
1476
1607
|
const target = cfg.target ?? "sandbox";
|
|
1477
1608
|
return (e) => {
|
|
@@ -1508,19 +1639,24 @@ async function ensureDaemon(host, cfg) {
|
|
|
1508
1639
|
message: health.version ? `daemon reachable (v${health.version})` : "daemon reachable (version unknown)",
|
|
1509
1640
|
version: health.version
|
|
1510
1641
|
});
|
|
1511
|
-
const upToDate = health.version
|
|
1642
|
+
const upToDate = versionAtLeast(health.version, desired);
|
|
1512
1643
|
if (!cfg.forceDeploy && (upToDate || !cfg.autoUpdate)) {
|
|
1513
1644
|
emit2({
|
|
1514
1645
|
phase: "skip",
|
|
1515
|
-
message: upToDate ? `daemon already at v${
|
|
1646
|
+
message: upToDate ? `daemon already at v${health.version}` : "keeping the running daemon",
|
|
1516
1647
|
version: health.version
|
|
1517
1648
|
});
|
|
1518
1649
|
return token;
|
|
1519
1650
|
}
|
|
1651
|
+
if ((health.serviceUpdateVersion ?? 0) < 1) {
|
|
1652
|
+
if (cfg.forceDeploy) throw new MindwireError("This legacy service cannot reserve an idle update. Upgrade it explicitly or stop it after its work finishes before deploying.");
|
|
1653
|
+
emit2({ phase: "skip", message: "automatic update deferred: upgrade this legacy service explicitly to enable idle updates", version: health.version });
|
|
1654
|
+
return token;
|
|
1655
|
+
}
|
|
1520
1656
|
} else {
|
|
1521
1657
|
emit2({ phase: "probe", message: "no daemon reachable; deploying" });
|
|
1522
1658
|
}
|
|
1523
|
-
await deploy(host, cfg, emit2, token, directory);
|
|
1659
|
+
await deploy(host, cfg, emit2, token, directory, health.reachable);
|
|
1524
1660
|
if (!cfg.token) token = await readWorkspaceToken(host, directory) ?? token;
|
|
1525
1661
|
return token;
|
|
1526
1662
|
} catch (err) {
|
|
@@ -1533,7 +1669,7 @@ async function readWorkspaceToken(host, directory) {
|
|
|
1533
1669
|
const candidate = saved.stdout?.trim();
|
|
1534
1670
|
return candidate && candidate.length <= 4096 && /^[\x21-\x7e]+$/.test(candidate) ? candidate : void 0;
|
|
1535
1671
|
}
|
|
1536
|
-
async function deploy(host, cfg, emit2, token, directory) {
|
|
1672
|
+
async function deploy(host, cfg, emit2, token, directory, requireLease) {
|
|
1537
1673
|
const newPath = directory + "/mindwired.new";
|
|
1538
1674
|
const BIN = shellQuote(directory + "/mindwired"), BIN_NEW = shellQuote(newPath);
|
|
1539
1675
|
const STATE = shellQuote(directory + "/agent-state.json"), LOG = shellQuote(directory + "/daemon.log");
|
|
@@ -1575,6 +1711,7 @@ async function deploy(host, cfg, emit2, token, directory) {
|
|
|
1575
1711
|
` latest=$(curl -fsSL https://api.github.com/repos/oblien/mindwire/releases/latest | sed -n 's/.*"tag_name"[[:space:]]*:[[:space:]]*"\\([^"]*\\)".*/\\1/p')`,
|
|
1576
1712
|
' case "$latest" in v[0-9]*.[0-9]*.[0-9]*) ;; *) echo "MINDWIRE_FAIL no matching or latest release"; exit 1 ;; esac',
|
|
1577
1713
|
' release="https://github.com/oblien/mindwire/releases/download/$latest"',
|
|
1714
|
+
' mw_expected_version="${latest#v}"',
|
|
1578
1715
|
` asset="mindwired-$latest-${platform}-${arch}"`,
|
|
1579
1716
|
' download || { echo "MINDWIRE_FAIL latest release download failed"; exit 1; }',
|
|
1580
1717
|
"fi",
|
|
@@ -1594,29 +1731,43 @@ async function deploy(host, cfg, emit2, token, directory) {
|
|
|
1594
1731
|
'else echo "MINDWIRE_FAIL No supported update lock is available (flock on Linux, lockf on macOS)."; exit 1; fi',
|
|
1595
1732
|
`mw_token=${shellQuote(token)}`,
|
|
1596
1733
|
!cfg.token ? `if [ -s ${TOKEN} ]; then mw_token=$(cat ${TOKEN}); fi` : "",
|
|
1734
|
+
'mw_lease=""',
|
|
1735
|
+
`mw_lease_file=${shellQuote(directory)}/update-lease-$$.json`,
|
|
1736
|
+
"cleanup_update() {",
|
|
1737
|
+
` if [ -n "$mw_lease" ]; then curl -s --connect-timeout 2 --max-time 3 -X DELETE -H "Authorization: Bearer $mw_token" "http://127.0.0.1:${cfg.port}/service/update/$mw_lease" >/dev/null 2>&1 || true; fi`,
|
|
1738
|
+
` rm -f ${BIN_NEW} "$mw_lease_file"${stagedUpload ? " " + shellQuote(stagedUpload) : ""}`,
|
|
1739
|
+
"}",
|
|
1740
|
+
// Shared staging is only removed while this process owns the workspace lock.
|
|
1741
|
+
"trap cleanup_update EXIT",
|
|
1742
|
+
versionCheckScript,
|
|
1743
|
+
`mw_desired=${shellQuote(desired)}`,
|
|
1744
|
+
'mw_expected_version="$mw_desired"',
|
|
1745
|
+
`mw_health=$(curl -fsS --max-time 3 -H "Authorization: Bearer $mw_token" http://127.0.0.1:${cfg.port}/healthz 2>/dev/null || true)`,
|
|
1597
1746
|
!cfg.forceDeploy ? [
|
|
1598
|
-
`mw_health=$(curl -fsS --max-time 3 -H "Authorization: Bearer $mw_token" http://127.0.0.1:${cfg.port}/healthz 2>/dev/null || true)`,
|
|
1599
1747
|
`mw_version=$(printf '%s' "$mw_health" | sed -n 's/.*"version"[[:space:]]*:[[:space:]]*"\\([^" ]*\\)".*/\\1/p')`,
|
|
1600
|
-
`if [ -n "$mw_health" ] && ${cfg.autoUpdate ?
|
|
1748
|
+
`if [ -n "$mw_health" ] && ${cfg.autoUpdate ? 'version_at_least "$mw_version" "$mw_desired"' : "true"}; then echo MINDWIRE_READY; exit 0; fi`
|
|
1601
1749
|
].join("\n") : "",
|
|
1602
1750
|
// macOS ships POSIX setsid in Perl, but does not include Linux's setsid executable.
|
|
1603
1751
|
"if command -v setsid >/dev/null 2>&1; then mw_detach=(setsid);",
|
|
1604
1752
|
`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
1753
|
'else echo "MINDWIRE_FAIL Cannot start the Mindwire service: setsid or Perl is required."; exit 1; fi',
|
|
1606
1754
|
acquire,
|
|
1607
|
-
//
|
|
1608
|
-
//
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
"
|
|
1612
|
-
|
|
1613
|
-
"
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
'
|
|
1617
|
-
"
|
|
1618
|
-
|
|
1619
|
-
"
|
|
1755
|
+
// The service decides idleness after transfer. Never stop arbitrary processes
|
|
1756
|
+
// or interrupt a chat that began while the SDK was downloading its binary.
|
|
1757
|
+
`mw_health=$(curl -fsS --max-time 3 -H "Authorization: Bearer $mw_token" http://127.0.0.1:${cfg.port}/healthz 2>/dev/null || true)`,
|
|
1758
|
+
'if [ -n "$mw_health" ]; then',
|
|
1759
|
+
` mw_status=$(curl -s --connect-timeout 2 --max-time 5 -X POST -H "Authorization: Bearer $mw_token" -o "$mw_lease_file" -w '%{http_code}' http://127.0.0.1:${cfg.port}/service/update) || mw_status=000`,
|
|
1760
|
+
' case "$mw_status" in',
|
|
1761
|
+
" 201)",
|
|
1762
|
+
` mw_lease=$(sed -n 's/.*"id"[[:space:]]*:[[:space:]]*"\\([[:alnum:]]*\\)".*/\\1/p' "$mw_lease_file")`,
|
|
1763
|
+
` mw_service_pid=$(sed -n 's/.*"pid"[[:space:]]*:[[:space:]]*\\([0-9][0-9]*\\).*/\\1/p' "$mw_lease_file")`,
|
|
1764
|
+
' [ -n "$mw_lease" ] && [[ "$mw_service_pid" =~ ^[0-9]+$ ]] && [ "$mw_service_pid" -gt 1 ] || { echo "MINDWIRE_FAIL invalid service update lease"; exit 1; }',
|
|
1765
|
+
' kill "$mw_service_pid" ;;',
|
|
1766
|
+
" 409) echo MINDWIRE_UPDATE_DEFERRED; exit 0 ;;",
|
|
1767
|
+
' 404) echo "MINDWIRE_FAIL this legacy service requires an explicit upgrade before idle updates"; exit 1 ;;',
|
|
1768
|
+
' *) echo "MINDWIRE_FAIL could not reserve an idle service update"; exit 1 ;;',
|
|
1769
|
+
" esac",
|
|
1770
|
+
requireLease ? 'else echo "MINDWIRE_FAIL the service went offline before its idle update could be reserved"; exit 1' : "",
|
|
1620
1771
|
"fi",
|
|
1621
1772
|
"sleep 0.3",
|
|
1622
1773
|
`mv -f ${BIN_NEW} ${BIN}`,
|
|
@@ -1626,7 +1777,10 @@ async function deploy(host, cfg, emit2, token, directory) {
|
|
|
1626
1777
|
`"\${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>&- &`,
|
|
1627
1778
|
// Health-poll from inside the VM (loopback) and emit a marker — exit codes are unreliable here.
|
|
1628
1779
|
'mw_pid=$!; mw_exit=""; mw_deadline=$((SECONDS + 30))',
|
|
1629
|
-
|
|
1780
|
+
"while (( SECONDS < mw_deadline )); do",
|
|
1781
|
+
` mw_health=$(curl -fsS --connect-timeout 2 --max-time 3 -H "Authorization: Bearer $mw_token" http://127.0.0.1:${cfg.port}/healthz 2>/dev/null || true)`,
|
|
1782
|
+
` mw_version=$(printf '%s' "$mw_health" | sed -n 's/.*"version"[[:space:]]*:[[:space:]]*"\\([^" ]*\\)".*/\\1/p')`,
|
|
1783
|
+
' if version_at_least "$mw_version" "$mw_expected_version"; then echo MINDWIRE_READY; exit 0; fi',
|
|
1630
1784
|
' if [ -z "$mw_exit" ] && ! kill -0 "$mw_pid" 2>/dev/null; then',
|
|
1631
1785
|
' mw_exit=0; wait "$mw_pid" || mw_exit=$?; [ "$mw_exit" = 0 ] || break',
|
|
1632
1786
|
" fi",
|
|
@@ -1634,13 +1788,18 @@ async function deploy(host, cfg, emit2, token, directory) {
|
|
|
1634
1788
|
"done",
|
|
1635
1789
|
`mw_tail=$(tail -n 40 ${LOG} 2>/dev/null || true)`,
|
|
1636
1790
|
'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',
|
|
1791
|
+
'else mw_reason="The Mindwire service did not become ready at v$mw_expected_version (reported: $mw_version)."; fi',
|
|
1638
1792
|
'[ -z "$mw_tail" ] || mw_reason="$mw_reason $mw_tail"',
|
|
1639
1793
|
'printf "MINDWIRE_FAIL %s\\n" "$mw_reason"'
|
|
1640
1794
|
].join("\n");
|
|
1641
1795
|
emit2({ phase: "launch", message: "launching daemon" });
|
|
1642
1796
|
const res = await host.exec(["bash", "-lc", script], { timeoutSeconds: 420 });
|
|
1643
1797
|
const out = res.stdout ?? "";
|
|
1798
|
+
if (out.includes("MINDWIRE_UPDATE_DEFERRED")) {
|
|
1799
|
+
if (cfg.forceDeploy) throw new MindwireError("The workspace is busy. Wait for its operations to finish before deploying the service.");
|
|
1800
|
+
emit2({ phase: "skip", message: "service update deferred while workspace operations are running" });
|
|
1801
|
+
return;
|
|
1802
|
+
}
|
|
1644
1803
|
if (!out.includes("MINDWIRE_READY")) {
|
|
1645
1804
|
throw new MindwireError(
|
|
1646
1805
|
"mindwire: the in-sandbox daemon did not become healthy after deploy.\n" + (out || res.stderr || res.error || "(no output)").trim()
|
|
@@ -1655,7 +1814,11 @@ async function probeHealth(host, port, token) {
|
|
|
1655
1814
|
if (!body) return { reachable: false };
|
|
1656
1815
|
try {
|
|
1657
1816
|
const j = JSON.parse(body);
|
|
1658
|
-
return {
|
|
1817
|
+
return {
|
|
1818
|
+
reachable: true,
|
|
1819
|
+
version: typeof j.version === "string" ? j.version : void 0,
|
|
1820
|
+
serviceUpdateVersion: typeof j.serviceUpdateVersion === "number" ? j.serviceUpdateVersion : void 0
|
|
1821
|
+
};
|
|
1659
1822
|
} catch {
|
|
1660
1823
|
return { reachable: true };
|
|
1661
1824
|
}
|
|
@@ -1756,7 +1919,7 @@ var ContainerHost = class {
|
|
|
1756
1919
|
base;
|
|
1757
1920
|
containerId;
|
|
1758
1921
|
exec(argv, opts) {
|
|
1759
|
-
return this.base.exec(["docker", "exec", this.containerId, ...argv], opts);
|
|
1922
|
+
return this.base.exec(["docker", "exec", "--env", "MINDWIRE_ISOLATION=container", this.containerId, ...argv], opts);
|
|
1760
1923
|
}
|
|
1761
1924
|
async putFile(path, data, opts) {
|
|
1762
1925
|
const slash = path.lastIndexOf("/");
|
|
@@ -1856,7 +2019,7 @@ async function runContainer(host, cfg, emit2) {
|
|
|
1856
2019
|
"mindwire: running the daemon in a container needs an image (docker.image) to create one, or an existing container (docker.container) to attach to."
|
|
1857
2020
|
);
|
|
1858
2021
|
}
|
|
1859
|
-
const runArgs = ["docker", "run", "-d"];
|
|
2022
|
+
const runArgs = ["docker", "run", "-d", "--env", "MINDWIRE_ISOLATION=container"];
|
|
1860
2023
|
if (cfg.name) runArgs.push("--name", cfg.name);
|
|
1861
2024
|
runArgs.push("-p", `127.0.0.1:0:${port}`);
|
|
1862
2025
|
if (cfg.createArgs?.length) runArgs.push(...cfg.createArgs);
|
|
@@ -2183,7 +2346,7 @@ var DockerHost = class {
|
|
|
2183
2346
|
container;
|
|
2184
2347
|
async exec(argv) {
|
|
2185
2348
|
const { Writable } = await import('stream');
|
|
2186
|
-
const exec = await this.container.exec({ Cmd: argv, AttachStdout: true, AttachStderr: true });
|
|
2349
|
+
const exec = await this.container.exec({ Cmd: argv, Env: ["MINDWIRE_ISOLATION=container"], AttachStdout: true, AttachStderr: true });
|
|
2187
2350
|
const stream = await exec.start({ hijack: true, stdin: false });
|
|
2188
2351
|
const out = [];
|
|
2189
2352
|
const err = [];
|
|
@@ -2237,7 +2400,7 @@ async function provisionDocker(docker2, config = {}, onLog) {
|
|
|
2237
2400
|
// The runtime image starts mindwired itself. Do not replace its command: doing so turns the
|
|
2238
2401
|
// image ENTRYPOINT into `mindwired sleep infinity` and prevents the runtime from starting.
|
|
2239
2402
|
Image: image,
|
|
2240
|
-
Env: [`ADDR=:${port}`, `AGENT_TYPE=${agent}`, `AGENT_CWD=${agentCwd}
|
|
2403
|
+
Env: [`ADDR=:${port}`, `AGENT_TYPE=${agent}`, `AGENT_CWD=${agentCwd}`, "MINDWIRE_ISOLATION=container"],
|
|
2241
2404
|
Tty: false,
|
|
2242
2405
|
ExposedPorts: { [portKey]: {} },
|
|
2243
2406
|
HostConfig: { PortBindings: { [portKey]: [{ HostPort: "0" }] } },
|
|
@@ -2552,6 +2715,6 @@ function clearCatalogCache() {
|
|
|
2552
2715
|
inflight = null;
|
|
2553
2716
|
}
|
|
2554
2717
|
|
|
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 };
|
|
2718
|
+
export { ApiError, AuthApi, ContainerHost, GitAccessApi, Http, MODELS_DEV_URL, McpApi, Mindwire, MindwireError, NotifyApi, ProjectOperationsApi, PromptsApi, ProvidersApi, Run, RunFailedError, SDK_VERSION, ServiceApi, SurfacesApi, TimeoutError, WorkspaceApi, WorkspaceCollection, catalogModels, catalogProvider, catalogProviders, clearCatalogCache, docker, ensureDaemon, ensureDaemonBinary, loadCatalog, local, lookupModel, oblien, provisionContainer, provisionDocker, provisionOblien, provisionSsh, provisionSshContainer, remote, resolveLinuxDaemon, ssh, startEmbedded };
|
|
2556
2719
|
//# sourceMappingURL=index.js.map
|
|
2557
2720
|
//# sourceMappingURL=index.js.map
|