miii-agent 0.1.26 → 0.1.27
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/README.md +16 -1
- package/dist/cli.js +192 -20
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -30,10 +30,12 @@ Your code never leaves your disk. There's nothing to log in to. Pull a model, ty
|
|
|
30
30
|
|
|
31
31
|
```bash
|
|
32
32
|
ollama pull qwen2.5-coder:14b # any coding model works
|
|
33
|
-
|
|
33
|
+
curl -fsSL https://raw.githubusercontent.com/maruakshay/miii-cli/main/install.sh | sh
|
|
34
34
|
miii
|
|
35
35
|
```
|
|
36
36
|
|
|
37
|
+
Prefer npm? `npm install -g miii-agent`.
|
|
38
|
+
|
|
37
39
|
Then just talk to it:
|
|
38
40
|
|
|
39
41
|
```
|
|
@@ -44,6 +46,19 @@ Then just talk to it:
|
|
|
44
46
|
|
|
45
47
|
> **Needs:** Node ≥ 18 and [Ollama](https://ollama.com/download) running locally.
|
|
46
48
|
|
|
49
|
+
## Staying up to date
|
|
50
|
+
|
|
51
|
+
miii checks npm on launch and, when a newer release exists, pulls it in the
|
|
52
|
+
background — it applies the next time you start. Manual options:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
miii update # update now
|
|
56
|
+
miii --version # what you're running
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Opt out of background updates by adding `"autoUpdate": false` to `~/.miii/config.json`,
|
|
60
|
+
or re-run the install script (`curl … | sh`) any time to update by hand.
|
|
61
|
+
|
|
47
62
|
## Why local-first?
|
|
48
63
|
|
|
49
64
|
Most "AI coding tools" are just wrappers around a cloud API — slow, metered, and they ship your private codebase to someone else's server.
|
package/dist/cli.js
CHANGED
|
@@ -35,9 +35,13 @@ function migrate(raw) {
|
|
|
35
35
|
provider: raw.provider,
|
|
36
36
|
effort: raw.effort,
|
|
37
37
|
providers,
|
|
38
|
-
modelContexts: raw.modelContexts
|
|
38
|
+
modelContexts: raw.modelContexts,
|
|
39
|
+
autoUpdate: raw.autoUpdate
|
|
39
40
|
};
|
|
40
41
|
}
|
|
42
|
+
function autoUpdateEnabled(cfg = loadConfig()) {
|
|
43
|
+
return cfg.autoUpdate !== false;
|
|
44
|
+
}
|
|
41
45
|
function readRawConfig() {
|
|
42
46
|
if (!existsSync(CONFIG_PATH)) return {};
|
|
43
47
|
try {
|
|
@@ -1303,7 +1307,8 @@ Ask in a numbered list. One round of questions per turn. Then wait.
|
|
|
1303
1307
|
|
|
1304
1308
|
# Tool calls
|
|
1305
1309
|
- When you need a tool, emit the tool call directly. No preamble, no narration, no "I will use X".
|
|
1306
|
-
- Never
|
|
1310
|
+
- Use the native function-calling interface as the ONLY channel for tool calls. Never print a tool call as text \u2014 not as JSON, not as a fenced code block, not as \`call:name{...}\`, not in any custom or tagged syntax. Text-form calls do NOT execute; they leak to the user and nothing happens.
|
|
1311
|
+
- If you cannot emit a real function call, do not fake one in prose \u2014 answer in plain text instead.
|
|
1307
1312
|
- After a tool result, move directly to the next tool call or the final answer. Do not restate what the previous tool did.
|
|
1308
1313
|
|
|
1309
1314
|
# Tools
|
|
@@ -1475,6 +1480,11 @@ function parseTextToolCalls(text, knownToolNames) {
|
|
|
1475
1480
|
if (!text) return { calls: [], cleanedText: text };
|
|
1476
1481
|
const calls = [];
|
|
1477
1482
|
let cleaned = text;
|
|
1483
|
+
const callSyntax = parseCallSyntax(cleaned, knownToolNames);
|
|
1484
|
+
if (callSyntax.calls.length > 0) {
|
|
1485
|
+
calls.push(...callSyntax.calls);
|
|
1486
|
+
cleaned = callSyntax.cleanedText;
|
|
1487
|
+
}
|
|
1478
1488
|
const tagRe = /<\|?tool_call\|?>\s*([\s\S]*?)\s*<\|?\/?tool_call\|?>/g;
|
|
1479
1489
|
cleaned = cleaned.replace(tagRe, (_m, body) => {
|
|
1480
1490
|
const c = tryParse(body, knownToolNames);
|
|
@@ -1515,6 +1525,74 @@ function tryParse(raw, knownToolNames) {
|
|
|
1515
1525
|
return null;
|
|
1516
1526
|
}
|
|
1517
1527
|
}
|
|
1528
|
+
function parseCallSyntax(text, knownToolNames) {
|
|
1529
|
+
const calls = [];
|
|
1530
|
+
const headRe = /call:\s*([a-zA-Z_]\w*)\s*\{/g;
|
|
1531
|
+
let m;
|
|
1532
|
+
let cleaned = "";
|
|
1533
|
+
let lastEnd = 0;
|
|
1534
|
+
while ((m = headRe.exec(text)) !== null) {
|
|
1535
|
+
const name = m[1];
|
|
1536
|
+
const body = parseCallBody(text, m.index + m[0].length);
|
|
1537
|
+
if (!body) continue;
|
|
1538
|
+
headRe.lastIndex = body.end;
|
|
1539
|
+
if (!knownToolNames.includes(name)) continue;
|
|
1540
|
+
calls.push({ function: { name, arguments: body.args } });
|
|
1541
|
+
cleaned += text.slice(lastEnd, m.index);
|
|
1542
|
+
lastEnd = body.end;
|
|
1543
|
+
}
|
|
1544
|
+
cleaned += text.slice(lastEnd);
|
|
1545
|
+
return { calls, cleanedText: cleaned.trim() };
|
|
1546
|
+
}
|
|
1547
|
+
function parseCallBody(text, start) {
|
|
1548
|
+
const args2 = {};
|
|
1549
|
+
let i = start;
|
|
1550
|
+
const isWs = (c) => c === " " || c === " " || c === "\n" || c === "\r";
|
|
1551
|
+
while (i < text.length) {
|
|
1552
|
+
while (i < text.length && (isWs(text[i]) || text[i] === ",")) i++;
|
|
1553
|
+
if (i >= text.length) return null;
|
|
1554
|
+
if (text[i] === "}") return { args: args2, end: i + 1 };
|
|
1555
|
+
const keyStart = i;
|
|
1556
|
+
while (i < text.length && text[i] !== ":" && text[i] !== "}") i++;
|
|
1557
|
+
if (i >= text.length || text[i] !== ":") return null;
|
|
1558
|
+
const key = text.slice(keyStart, i).trim().replace(/^["']|["']$/g, "");
|
|
1559
|
+
i++;
|
|
1560
|
+
while (i < text.length && isWs(text[i])) i++;
|
|
1561
|
+
if (text.startsWith(VAL_DELIM, i)) {
|
|
1562
|
+
const vStart = i + VAL_DELIM.length;
|
|
1563
|
+
const vEnd = text.indexOf(VAL_DELIM, vStart);
|
|
1564
|
+
if (vEnd === -1) return null;
|
|
1565
|
+
args2[key] = text.slice(vStart, vEnd);
|
|
1566
|
+
i = vEnd + VAL_DELIM.length;
|
|
1567
|
+
} else {
|
|
1568
|
+
const vStart = i;
|
|
1569
|
+
let depth = 0;
|
|
1570
|
+
let inStr = null;
|
|
1571
|
+
let esc = false;
|
|
1572
|
+
while (i < text.length) {
|
|
1573
|
+
const ch = text[i];
|
|
1574
|
+
if (inStr) {
|
|
1575
|
+
if (esc) esc = false;
|
|
1576
|
+
else if (ch === "\\") esc = true;
|
|
1577
|
+
else if (ch === inStr) inStr = null;
|
|
1578
|
+
} else if (ch === '"' || ch === "'") inStr = ch;
|
|
1579
|
+
else if (ch === "{" || ch === "[") depth++;
|
|
1580
|
+
else if (ch === "}" || ch === "]") {
|
|
1581
|
+
if (depth === 0) break;
|
|
1582
|
+
depth--;
|
|
1583
|
+
} else if (ch === "," && depth === 0) break;
|
|
1584
|
+
i++;
|
|
1585
|
+
}
|
|
1586
|
+
const rawVal = text.slice(vStart, i).trim();
|
|
1587
|
+
try {
|
|
1588
|
+
args2[key] = JSON.parse(rawVal);
|
|
1589
|
+
} catch {
|
|
1590
|
+
args2[key] = rawVal.replace(/^["']|["']$/g, "");
|
|
1591
|
+
}
|
|
1592
|
+
}
|
|
1593
|
+
}
|
|
1594
|
+
return null;
|
|
1595
|
+
}
|
|
1518
1596
|
function extractFirstJsonObject(s) {
|
|
1519
1597
|
const start = s.indexOf("{");
|
|
1520
1598
|
if (start === -1) return null;
|
|
@@ -1541,6 +1619,18 @@ function extractFirstJsonObject(s) {
|
|
|
1541
1619
|
}
|
|
1542
1620
|
return null;
|
|
1543
1621
|
}
|
|
1622
|
+
function looksLikeLeakedToolCall(text, knownToolNames) {
|
|
1623
|
+
if (!text) return false;
|
|
1624
|
+
if (text.includes(VAL_DELIM)) return true;
|
|
1625
|
+
if (/<\|?tool_call/i.test(text)) return true;
|
|
1626
|
+
const callMatch = text.match(/\bcall:\s*(\w+)\s*\{/);
|
|
1627
|
+
if (callMatch && knownToolNames.includes(callMatch[1])) return true;
|
|
1628
|
+
if (/"name"\s*:\s*"([^"]+)"/.test(text) && /"(arguments|parameters|input)"\s*:/.test(text)) {
|
|
1629
|
+
const name = text.match(/"name"\s*:\s*"([^"]+)"/)?.[1];
|
|
1630
|
+
if (name && knownToolNames.includes(name)) return true;
|
|
1631
|
+
}
|
|
1632
|
+
return false;
|
|
1633
|
+
}
|
|
1544
1634
|
function blocksFromOllama(text, tool_calls, knownToolNames = []) {
|
|
1545
1635
|
const blocks = [];
|
|
1546
1636
|
let finalText = text;
|
|
@@ -1563,9 +1653,11 @@ function blocksFromOllama(text, tool_calls, knownToolNames = []) {
|
|
|
1563
1653
|
}
|
|
1564
1654
|
return blocks;
|
|
1565
1655
|
}
|
|
1656
|
+
var VAL_DELIM;
|
|
1566
1657
|
var init_adapter = __esm({
|
|
1567
1658
|
"src/agent/adapter.ts"() {
|
|
1568
1659
|
"use strict";
|
|
1660
|
+
VAL_DELIM = '<|"|>';
|
|
1569
1661
|
}
|
|
1570
1662
|
});
|
|
1571
1663
|
|
|
@@ -1638,6 +1730,7 @@ async function* runAgent(opts) {
|
|
|
1638
1730
|
let evalTokens = 0;
|
|
1639
1731
|
let lastAssistantSig = "";
|
|
1640
1732
|
let repeatCount = 0;
|
|
1733
|
+
let leakNudges = 0;
|
|
1641
1734
|
const seenPaths = /* @__PURE__ */ new Set();
|
|
1642
1735
|
for (let turn = 0; turn < MAX_TURNS; turn++) {
|
|
1643
1736
|
let text = "";
|
|
@@ -1722,6 +1815,16 @@ async function* runAgent(opts) {
|
|
|
1722
1815
|
continue;
|
|
1723
1816
|
}
|
|
1724
1817
|
if (tool_uses.length === 0) {
|
|
1818
|
+
const assistantText = blocks.filter((b) => b.type === "text").map((b) => b.text).join("");
|
|
1819
|
+
if (leakNudges < MAX_LEAK_NUDGES && looksLikeLeakedToolCall(assistantText, toolNames)) {
|
|
1820
|
+
leakNudges++;
|
|
1821
|
+
history.push({
|
|
1822
|
+
role: "user",
|
|
1823
|
+
content: "That tool call was written as plain text, so it did not run and nothing happened. Re-issue it using the function-calling interface only \u2014 do not print the call as text, JSON, or any custom syntax. If you did not mean to call a tool, answer in prose."
|
|
1824
|
+
});
|
|
1825
|
+
yield { type: "turn-end", stop_reason: "tool_use" };
|
|
1826
|
+
continue;
|
|
1827
|
+
}
|
|
1725
1828
|
yield { type: "turn-end", stop_reason: "end_turn" };
|
|
1726
1829
|
break;
|
|
1727
1830
|
}
|
|
@@ -1829,7 +1932,7 @@ async function* runAgent(opts) {
|
|
|
1829
1932
|
yield { type: "done", prompt_tokens: promptTokens, eval_tokens: evalTokens };
|
|
1830
1933
|
return history;
|
|
1831
1934
|
}
|
|
1832
|
-
var MAX_TURNS, REPEAT_TAIL, REPEAT_KILL, BIG_WRITE_TOOLS;
|
|
1935
|
+
var MAX_TURNS, REPEAT_TAIL, REPEAT_KILL, MAX_LEAK_NUDGES, BIG_WRITE_TOOLS;
|
|
1833
1936
|
var init_loop = __esm({
|
|
1834
1937
|
"src/agent/loop.ts"() {
|
|
1835
1938
|
"use strict";
|
|
@@ -1845,21 +1948,22 @@ var init_loop = __esm({
|
|
|
1845
1948
|
MAX_TURNS = 25;
|
|
1846
1949
|
REPEAT_TAIL = 120;
|
|
1847
1950
|
REPEAT_KILL = 4;
|
|
1951
|
+
MAX_LEAK_NUDGES = 2;
|
|
1848
1952
|
BIG_WRITE_TOOLS = /* @__PURE__ */ new Set(["write_file", "edit_file"]);
|
|
1849
1953
|
}
|
|
1850
1954
|
});
|
|
1851
1955
|
|
|
1852
1956
|
// eval/runner.ts
|
|
1853
|
-
import { mkdtempSync, writeFileSync as
|
|
1854
|
-
import { dirname as dirname3, join as
|
|
1957
|
+
import { mkdtempSync, writeFileSync as writeFileSync8, mkdirSync as mkdirSync7, rmSync as rmSync4 } from "fs";
|
|
1958
|
+
import { dirname as dirname3, join as join11 } from "path";
|
|
1855
1959
|
import { tmpdir as tmpdir2 } from "os";
|
|
1856
1960
|
async function runScenario(model, s) {
|
|
1857
|
-
const dir = mkdtempSync(
|
|
1961
|
+
const dir = mkdtempSync(join11(tmpdir2(), "miii-eval-"));
|
|
1858
1962
|
const prevCwd = process.cwd();
|
|
1859
1963
|
for (const [rel, content] of Object.entries(s.files ?? {})) {
|
|
1860
|
-
const abs =
|
|
1861
|
-
|
|
1862
|
-
|
|
1964
|
+
const abs = join11(dir, rel);
|
|
1965
|
+
mkdirSync7(dirname3(abs), { recursive: true });
|
|
1966
|
+
writeFileSync8(abs, content, "utf-8");
|
|
1863
1967
|
}
|
|
1864
1968
|
const r = {
|
|
1865
1969
|
name: s.name,
|
|
@@ -1920,13 +2024,13 @@ var init_runner = __esm({
|
|
|
1920
2024
|
});
|
|
1921
2025
|
|
|
1922
2026
|
// eval/scenarios.ts
|
|
1923
|
-
import { readFileSync as
|
|
1924
|
-
import { join as
|
|
2027
|
+
import { readFileSync as readFileSync10, existsSync as existsSync8 } from "fs";
|
|
2028
|
+
import { join as join12 } from "path";
|
|
1925
2029
|
var read, scenarios;
|
|
1926
2030
|
var init_scenarios = __esm({
|
|
1927
2031
|
"eval/scenarios.ts"() {
|
|
1928
2032
|
"use strict";
|
|
1929
|
-
read = (dir, f) => existsSync8(
|
|
2033
|
+
read = (dir, f) => existsSync8(join12(dir, f)) ? readFileSync10(join12(dir, f), "utf-8") : null;
|
|
1930
2034
|
scenarios = [
|
|
1931
2035
|
{
|
|
1932
2036
|
name: "edit-exact-string",
|
|
@@ -2076,13 +2180,25 @@ init_client();
|
|
|
2076
2180
|
init_config();
|
|
2077
2181
|
import { useState as useState5, useEffect as useEffect4, useRef as useRef2 } from "react";
|
|
2078
2182
|
import { Box as Box13, Text as Text13, useApp } from "ink";
|
|
2079
|
-
import { homedir as
|
|
2183
|
+
import { homedir as homedir8 } from "os";
|
|
2080
2184
|
import { sep as sep2 } from "path";
|
|
2081
2185
|
|
|
2082
2186
|
// src/ui/WelcomeBlock.tsx
|
|
2083
2187
|
import { Box, Text } from "ink";
|
|
2084
2188
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2085
|
-
function
|
|
2189
|
+
function updateBannerText(version, status) {
|
|
2190
|
+
switch (status) {
|
|
2191
|
+
case "downloading":
|
|
2192
|
+
return `\u2191 v${version} downloading in the background \u2014 restart miii to apply`;
|
|
2193
|
+
case "installed":
|
|
2194
|
+
return `\u2713 v${version} installed \u2014 restart miii to apply`;
|
|
2195
|
+
case "failed":
|
|
2196
|
+
return `\u2191 v${version} update failed \u2014 run \`miii update\` manually`;
|
|
2197
|
+
default:
|
|
2198
|
+
return `\u2191 v${version} available \u2014 run \`miii update\` to upgrade`;
|
|
2199
|
+
}
|
|
2200
|
+
}
|
|
2201
|
+
function WelcomeBlock({ model, activeCtx, effort, cwd, updateAvailable, updateStatus = "idle" }) {
|
|
2086
2202
|
const ctxLabel = activeCtx != null ? `${Math.round(activeCtx / 1024)}k ctx` : "\u2014 ctx";
|
|
2087
2203
|
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [
|
|
2088
2204
|
/* @__PURE__ */ jsxs(
|
|
@@ -2109,7 +2225,7 @@ function WelcomeBlock({ model, activeCtx, effort, cwd, updateAvailable }) {
|
|
|
2109
2225
|
]
|
|
2110
2226
|
}
|
|
2111
2227
|
),
|
|
2112
|
-
updateAvailable && /* @__PURE__ */ jsx(Text, { color: "
|
|
2228
|
+
updateAvailable && /* @__PURE__ */ jsx(Text, { color: updateStatus === "failed" ? "red" : updateStatus === "installed" ? "green" : "yellow", children: updateBannerText(updateAvailable, updateStatus) })
|
|
2113
2229
|
] });
|
|
2114
2230
|
}
|
|
2115
2231
|
|
|
@@ -4477,8 +4593,28 @@ function useKeyboard(opts) {
|
|
|
4477
4593
|
|
|
4478
4594
|
// src/updateCheck.ts
|
|
4479
4595
|
import { createRequire } from "module";
|
|
4596
|
+
import { homedir as homedir7 } from "os";
|
|
4597
|
+
import { join as join10 } from "path";
|
|
4598
|
+
import { readFileSync as readFileSync9, writeFileSync as writeFileSync7, mkdirSync as mkdirSync6 } from "fs";
|
|
4480
4599
|
var require2 = createRequire(import.meta.url);
|
|
4481
4600
|
var PKG_NAME = "miii-agent";
|
|
4601
|
+
var UPDATE_ATTEMPT_PATH = join10(homedir7(), ".miii", ".last-update-attempt");
|
|
4602
|
+
var UPDATE_ATTEMPT_COOLDOWN_MS = 10 * 60 * 1e3;
|
|
4603
|
+
function recentlyAttemptedUpdate() {
|
|
4604
|
+
try {
|
|
4605
|
+
const last = Number(readFileSync9(UPDATE_ATTEMPT_PATH, "utf8").trim());
|
|
4606
|
+
return Number.isFinite(last) && Date.now() - last < UPDATE_ATTEMPT_COOLDOWN_MS;
|
|
4607
|
+
} catch {
|
|
4608
|
+
return false;
|
|
4609
|
+
}
|
|
4610
|
+
}
|
|
4611
|
+
function markUpdateAttempt() {
|
|
4612
|
+
try {
|
|
4613
|
+
mkdirSync6(join10(homedir7(), ".miii"), { recursive: true });
|
|
4614
|
+
writeFileSync7(UPDATE_ATTEMPT_PATH, String(Date.now()));
|
|
4615
|
+
} catch {
|
|
4616
|
+
}
|
|
4617
|
+
}
|
|
4482
4618
|
function currentVersion() {
|
|
4483
4619
|
try {
|
|
4484
4620
|
return require2("../package.json").version;
|
|
@@ -4494,6 +4630,30 @@ function newerVersion(current, latest) {
|
|
|
4494
4630
|
if (lb !== cb) return lb > cb;
|
|
4495
4631
|
return lc > cc;
|
|
4496
4632
|
}
|
|
4633
|
+
function autoUpdate(onDone) {
|
|
4634
|
+
if (recentlyAttemptedUpdate()) return false;
|
|
4635
|
+
try {
|
|
4636
|
+
markUpdateAttempt();
|
|
4637
|
+
const { spawn } = require2("child_process");
|
|
4638
|
+
const child = spawn("npm", ["i", "-g", `${PKG_NAME}@latest`], {
|
|
4639
|
+
detached: true,
|
|
4640
|
+
stdio: "ignore",
|
|
4641
|
+
shell: process.platform === "win32"
|
|
4642
|
+
});
|
|
4643
|
+
let settled = false;
|
|
4644
|
+
const settle = (ok) => {
|
|
4645
|
+
if (settled) return;
|
|
4646
|
+
settled = true;
|
|
4647
|
+
onDone?.(ok);
|
|
4648
|
+
};
|
|
4649
|
+
child.on("error", () => settle(false));
|
|
4650
|
+
child.on("exit", (code) => settle(code === 0));
|
|
4651
|
+
child.unref();
|
|
4652
|
+
return true;
|
|
4653
|
+
} catch {
|
|
4654
|
+
return false;
|
|
4655
|
+
}
|
|
4656
|
+
}
|
|
4497
4657
|
async function checkForUpdate() {
|
|
4498
4658
|
try {
|
|
4499
4659
|
const res = await fetch(`https://registry.npmjs.org/${PKG_NAME}/latest`, {
|
|
@@ -4514,7 +4674,7 @@ async function checkForUpdate() {
|
|
|
4514
4674
|
import { Fragment as Fragment3, jsx as jsx13, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
4515
4675
|
function App() {
|
|
4516
4676
|
const { exit } = useApp();
|
|
4517
|
-
const cwd = process.cwd().replace(
|
|
4677
|
+
const cwd = process.cwd().replace(homedir8(), "~").split(sep2).join("/");
|
|
4518
4678
|
const [cfg, setCfg] = useState5(loadConfig());
|
|
4519
4679
|
const [models, setModels] = useState5([]);
|
|
4520
4680
|
const [contexts, setContexts] = useState5(() => cfg.modelContexts ?? {});
|
|
@@ -4525,6 +4685,7 @@ function App() {
|
|
|
4525
4685
|
const [cursor, setCursor] = useState5(0);
|
|
4526
4686
|
const [pickerQuery, setPickerQuery] = useState5("");
|
|
4527
4687
|
const [updateAvailable, setUpdateAvailable] = useState5(null);
|
|
4688
|
+
const [updateStatus, setUpdateStatus] = useState5("idle");
|
|
4528
4689
|
const [providerDown, setProviderDown] = useState5(false);
|
|
4529
4690
|
const [sessionId, setSessionId] = useState5(() => newSessionId());
|
|
4530
4691
|
const [sessions, setSessions] = useState5([]);
|
|
@@ -4536,7 +4697,12 @@ function App() {
|
|
|
4536
4697
|
const agent = useAgentRunner(cfg.model, activeCtx);
|
|
4537
4698
|
useEffect4(() => {
|
|
4538
4699
|
checkForUpdate().then((v) => {
|
|
4539
|
-
if (v)
|
|
4700
|
+
if (!v) return;
|
|
4701
|
+
setUpdateAvailable(v);
|
|
4702
|
+
if (autoUpdateEnabled()) {
|
|
4703
|
+
const started = autoUpdate((ok) => setUpdateStatus(ok ? "installed" : "failed"));
|
|
4704
|
+
if (started) setUpdateStatus("downloading");
|
|
4705
|
+
}
|
|
4540
4706
|
});
|
|
4541
4707
|
}, []);
|
|
4542
4708
|
const titledSessions = useRef2(/* @__PURE__ */ new Set());
|
|
@@ -4653,7 +4819,7 @@ function App() {
|
|
|
4653
4819
|
return Math.round(used / activeCtx * 100);
|
|
4654
4820
|
})();
|
|
4655
4821
|
return /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", paddingX: 1, children: [
|
|
4656
|
-
state !== "ready" && /* @__PURE__ */ jsx13(WelcomeBlock, { model: cfg.model, activeCtx, effort, cwd, error: agent.error, updateAvailable }),
|
|
4822
|
+
state !== "ready" && /* @__PURE__ */ jsx13(WelcomeBlock, { model: cfg.model, activeCtx, effort, cwd, error: agent.error, updateAvailable, updateStatus }),
|
|
4657
4823
|
state === "loading" && !agent.error && /* @__PURE__ */ jsx13(Box13, { marginLeft: 2, marginBottom: 1, children: /* @__PURE__ */ jsx13(Text13, { dimColor: true, children: `connecting to ${provName}\u2026` }) }),
|
|
4658
4824
|
agent.error && state !== "ready" && /* @__PURE__ */ jsx13(
|
|
4659
4825
|
ChatView,
|
|
@@ -4714,7 +4880,8 @@ function App() {
|
|
|
4714
4880
|
return /* @__PURE__ */ jsx13(FilePicker, { matches: searchFiles(process.cwd(), m.query), cursor: filePickerCursor });
|
|
4715
4881
|
})(),
|
|
4716
4882
|
/* @__PURE__ */ jsx13(InputBar, { input, caret, disabled: agent.busy, processingLabel: agent.processingLabel }),
|
|
4717
|
-
!agent.busy && /* @__PURE__ */ jsx13(Box13, { marginLeft: 2, marginBottom: 1, children: /* @__PURE__ */ jsx13(Text13, { dimColor: true, children: providerDown ? "provider unavailable \u2014 /provider to switch \xB7 /models to pick a model" : "type / to see commands" }) })
|
|
4883
|
+
!agent.busy && /* @__PURE__ */ jsx13(Box13, { marginLeft: 2, marginBottom: 1, children: /* @__PURE__ */ jsx13(Text13, { dimColor: true, children: providerDown ? "provider unavailable \u2014 /provider to switch \xB7 /models to pick a model" : "type / to see commands" }) }),
|
|
4884
|
+
updateAvailable && /* @__PURE__ */ jsx13(Box13, { marginLeft: 2, marginBottom: 1, children: /* @__PURE__ */ jsx13(Text13, { color: updateStatus === "failed" ? "red" : updateStatus === "installed" ? "green" : "yellow", children: updateBannerText(updateAvailable, updateStatus) }) })
|
|
4718
4885
|
] })
|
|
4719
4886
|
] });
|
|
4720
4887
|
}
|
|
@@ -4733,7 +4900,12 @@ for (let i = 0; i < args.length; i++) {
|
|
|
4733
4900
|
cmd = args[i];
|
|
4734
4901
|
}
|
|
4735
4902
|
}
|
|
4736
|
-
if (cmd === "
|
|
4903
|
+
if (cmd === "version" || cmd === "--version" || cmd === "-v") {
|
|
4904
|
+
const { createRequire: createRequire2 } = await import("module");
|
|
4905
|
+
const pkg = createRequire2(import.meta.url)("../package.json");
|
|
4906
|
+
console.log(pkg.version);
|
|
4907
|
+
process.exit(0);
|
|
4908
|
+
} else if (cmd === "update" || cmd === "--update" || cmd === "-u") {
|
|
4737
4909
|
const { spawnSync } = await import("child_process");
|
|
4738
4910
|
console.log("Updating miii-agent\u2026");
|
|
4739
4911
|
const r = spawnSync("npm", ["i", "-g", "miii-agent@latest"], { stdio: "inherit", shell: process.platform === "win32" });
|
package/package.json
CHANGED