@sofer_agent/cli 0.3.1 → 0.3.2
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/bin.js +3 -1
- package/dist/bin.js.map +1 -1
- package/dist/chat.d.ts +1 -1
- package/dist/chat.d.ts.map +1 -1
- package/dist/chat.js +10 -6
- package/dist/chat.js.map +1 -1
- package/dist/init.d.ts.map +1 -1
- package/dist/init.js +11 -3
- package/dist/init.js.map +1 -1
- package/dist/secrets.d.ts.map +1 -1
- package/dist/secrets.js.map +1 -1
- package/dist/tui.d.ts +15 -22
- package/dist/tui.d.ts.map +1 -1
- package/dist/tui.js +178 -116
- package/dist/tui.js.map +1 -1
- package/package.json +2 -2
- package/src/bin.ts +8 -2
- package/src/chat.ts +9 -6
- package/src/init.ts +16 -17
- package/src/secrets.ts +6 -2
- package/src/tui.ts +187 -129
package/dist/bin.js
CHANGED
|
@@ -40,7 +40,9 @@ async function main() {
|
|
|
40
40
|
main().catch((e) => {
|
|
41
41
|
const msg = e instanceof Error ? e.message : String(e);
|
|
42
42
|
console.error(msg);
|
|
43
|
-
if (msg.includes("SOFER_SUI_SECRET_KEY") ||
|
|
43
|
+
if (msg.includes("SOFER_SUI_SECRET_KEY") ||
|
|
44
|
+
msg.includes("ANTHROPIC_API_KEY") ||
|
|
45
|
+
msg.includes("SOFER_PACKAGE_ID")) {
|
|
44
46
|
console.error(`\nFirst time? Run \`sofer init\` — an interactive wizard (creates ${agentPaths.root}/).`);
|
|
45
47
|
}
|
|
46
48
|
process.exit(1);
|
package/dist/bin.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bin.js","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACxC,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACxC,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAExC,SAAS,SAAS;IAChB,OAAO,CAAC,GAAG,CACT;;;;;;;6BAOyB,UAAU,CAAC,IAAI;gDACI,CAC7C,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,IAAI;IACjB,MAAM,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,MAAM,CAAC;IACtC,QAAQ,GAAG,EAAE,CAAC;QACZ,KAAK,MAAM;YACT,MAAM,WAAW,EAAE,CAAC;YACpB,MAAM;QACR,KAAK,MAAM;YACT,MAAM,WAAW,EAAE,CAAC;YACpB,MAAM;QACR,KAAK,MAAM;YACT,MAAM,WAAW,EAAE,CAAC;YACpB,MAAM;QACR,KAAK,MAAM,CAAC;QACZ,KAAK,IAAI,CAAC;QACV,KAAK,QAAQ;YACX,SAAS,EAAE,CAAC;YACZ,MAAM;QACR;YACE,OAAO,CAAC,KAAK,CAAC,oBAAoB,GAAG,IAAI,CAAC,CAAC;YAC3C,SAAS,EAAE,CAAC;YACZ,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACpB,CAAC;AACH,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE;IACjB,MAAM,GAAG,GAAG,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;IACvD,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACnB,
|
|
1
|
+
{"version":3,"file":"bin.js","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACxC,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACxC,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAExC,SAAS,SAAS;IAChB,OAAO,CAAC,GAAG,CACT;;;;;;;6BAOyB,UAAU,CAAC,IAAI;gDACI,CAC7C,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,IAAI;IACjB,MAAM,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,MAAM,CAAC;IACtC,QAAQ,GAAG,EAAE,CAAC;QACZ,KAAK,MAAM;YACT,MAAM,WAAW,EAAE,CAAC;YACpB,MAAM;QACR,KAAK,MAAM;YACT,MAAM,WAAW,EAAE,CAAC;YACpB,MAAM;QACR,KAAK,MAAM;YACT,MAAM,WAAW,EAAE,CAAC;YACpB,MAAM;QACR,KAAK,MAAM,CAAC;QACZ,KAAK,IAAI,CAAC;QACV,KAAK,QAAQ;YACX,SAAS,EAAE,CAAC;YACZ,MAAM;QACR;YACE,OAAO,CAAC,KAAK,CAAC,oBAAoB,GAAG,IAAI,CAAC,CAAC;YAC3C,SAAS,EAAE,CAAC;YACZ,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACpB,CAAC;AACH,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE;IACjB,MAAM,GAAG,GAAG,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;IACvD,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACnB,IACE,GAAG,CAAC,QAAQ,CAAC,sBAAsB,CAAC;QACpC,GAAG,CAAC,QAAQ,CAAC,mBAAmB,CAAC;QACjC,GAAG,CAAC,QAAQ,CAAC,kBAAkB,CAAC,EAChC,CAAC;QACD,OAAO,CAAC,KAAK,CACX,qEAAqE,UAAU,CAAC,IAAI,KAAK,CAC1F,CAAC;IACJ,CAAC;IACD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
|
package/dist/chat.d.ts
CHANGED
package/dist/chat.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chat.d.ts","sourceRoot":"","sources":["../src/chat.ts"],"names":[],"mappings":"AAIA,
|
|
1
|
+
{"version":3,"file":"chat.d.ts","sourceRoot":"","sources":["../src/chat.ts"],"names":[],"mappings":"AAIA,qDAAqD;AACrD,wBAAsB,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAmBjD"}
|
package/dist/chat.js
CHANGED
|
@@ -1,19 +1,23 @@
|
|
|
1
1
|
import { SoferAgent, loadConfig } from "@sofer_agent/core";
|
|
2
2
|
import { requireSecrets } from "./secrets.js";
|
|
3
3
|
import { SoferTui } from "./tui.js";
|
|
4
|
-
/** Interactive chat TUI with
|
|
4
|
+
/** Interactive chat TUI with promus-style polish. */
|
|
5
5
|
export async function chatCommand() {
|
|
6
6
|
const config = loadConfig();
|
|
7
7
|
const agent = await SoferAgent.create(config, requireSecrets());
|
|
8
|
-
const tui = new SoferTui(agent
|
|
9
|
-
await tui.run(async (line) => {
|
|
8
|
+
const tui = new SoferTui(agent);
|
|
9
|
+
await tui.run(async (line, signal) => {
|
|
10
10
|
try {
|
|
11
|
-
const { text
|
|
11
|
+
const { text } = await agent.chat(line, signal);
|
|
12
12
|
tui.addMessage("agent", text);
|
|
13
|
-
tui.setUsage(turnUsage.inputTokens, turnUsage.outputTokens);
|
|
14
13
|
}
|
|
15
14
|
catch (e) {
|
|
16
|
-
|
|
15
|
+
if (signal?.aborted) {
|
|
16
|
+
tui.addMessage("system", "turn interrupted (esc).");
|
|
17
|
+
}
|
|
18
|
+
else {
|
|
19
|
+
tui.addMessage("error", e instanceof Error ? e.message : String(e));
|
|
20
|
+
}
|
|
17
21
|
}
|
|
18
22
|
tui.render();
|
|
19
23
|
});
|
package/dist/chat.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chat.js","sourceRoot":"","sources":["../src/chat.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC3D,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AAEpC,
|
|
1
|
+
{"version":3,"file":"chat.js","sourceRoot":"","sources":["../src/chat.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC3D,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AAEpC,qDAAqD;AACrD,MAAM,CAAC,KAAK,UAAU,WAAW;IAC/B,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;IAC5B,MAAM,KAAK,GAAG,MAAM,UAAU,CAAC,MAAM,CAAC,MAAM,EAAE,cAAc,EAAE,CAAC,CAAC;IAEhE,MAAM,GAAG,GAAG,IAAI,QAAQ,CAAC,KAAK,CAAC,CAAC;IAEhC,MAAM,GAAG,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE;QACnC,IAAI,CAAC;YACH,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;YAChD,GAAG,CAAC,UAAU,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;QAChC,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,IAAI,MAAM,EAAE,OAAO,EAAE,CAAC;gBACpB,GAAG,CAAC,UAAU,CAAC,QAAQ,EAAE,yBAAyB,CAAC,CAAC;YACtD,CAAC;iBAAM,CAAC;gBACN,GAAG,CAAC,UAAU,CAAC,OAAO,EAAE,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;YACtE,CAAC;QACH,CAAC;QACD,GAAG,CAAC,MAAM,EAAE,CAAC;IACf,CAAC,CAAC,CAAC;AACL,CAAC"}
|
package/dist/init.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../src/init.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../src/init.ts"],"names":[],"mappings":"AAkBA;;;;;GAKG;AACH,wBAAsB,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CA0LjD"}
|
package/dist/init.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { cancel, intro, isCancel, note, outro, password, select, spinner
|
|
1
|
+
import { cancel, intro, isCancel, note, outro, password, select, spinner } from "@clack/prompts";
|
|
2
2
|
import { AgentClient, agentPaths, generateKeypair, keypairFromSecret, loadConfig, makeSuiClient, provisionMemwal, requestFaucet, shortAgentId, suiBalance, waitForBalance, writePersistedConfig, writeSecret, } from "@sofer_agent/core";
|
|
3
3
|
/**
|
|
4
4
|
* Interactive init wizard — mint the agent + provision Walrus memory.
|
|
@@ -136,6 +136,10 @@ export async function initCommand() {
|
|
|
136
136
|
note(accountId, "reusing memory account");
|
|
137
137
|
}
|
|
138
138
|
// ── 7. Link memory to agent on-chain ───────────────────────────────────
|
|
139
|
+
if (!agentId || !accountId) {
|
|
140
|
+
cancel("Agent or memory not provisioned. Re-run `sofer init`.");
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
139
143
|
const sLink = spinner();
|
|
140
144
|
sLink.start("Linking memory account to agent on-chain");
|
|
141
145
|
try {
|
|
@@ -147,6 +151,10 @@ export async function initCommand() {
|
|
|
147
151
|
sLink.stop(`link failed (may already be linked): ${String(e).slice(0, 80)}`);
|
|
148
152
|
}
|
|
149
153
|
// ── 8. Persist config + secrets to ~/.sofer/ ───────────────────────────
|
|
154
|
+
if (!delegateKey) {
|
|
155
|
+
cancel("Memory delegate key missing. Re-run `sofer init`.");
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
150
158
|
const sSave = spinner();
|
|
151
159
|
sSave.start("Saving to ~/.sofer/");
|
|
152
160
|
writeSecret(agentPaths.suiKey, secret);
|
|
@@ -157,8 +165,8 @@ export async function initCommand() {
|
|
|
157
165
|
agentAddress: address,
|
|
158
166
|
brain: { model: config.brain.model },
|
|
159
167
|
memwal: {
|
|
160
|
-
accountId
|
|
161
|
-
delegateKey
|
|
168
|
+
accountId,
|
|
169
|
+
delegateKey,
|
|
162
170
|
},
|
|
163
171
|
};
|
|
164
172
|
writePersistedConfig(persisted);
|
package/dist/init.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"init.js","sourceRoot":"","sources":["../src/init.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"init.js","sourceRoot":"","sources":["../src/init.ts"],"names":[],"mappings":"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"}
|
package/dist/secrets.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"secrets.d.ts","sourceRoot":"","sources":["../src/secrets.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAGtD,gFAAgF;AAChF,wBAAgB,cAAc,IAAI,YAAY,
|
|
1
|
+
{"version":3,"file":"secrets.d.ts","sourceRoot":"","sources":["../src/secrets.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAGtD,gFAAgF;AAChF,wBAAgB,cAAc,IAAI,YAAY,CAa7C"}
|
package/dist/secrets.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"secrets.js","sourceRoot":"","sources":["../src/secrets.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAEvC,gFAAgF;AAChF,MAAM,UAAU,cAAc;IAC5B,MAAM,EAAE,YAAY,EAAE,eAAe,EAAE,GAAG,WAAW,EAAE,CAAC;IACxD,IAAI,CAAC,YAAY,EAAE,CAAC;QAClB,MAAM,IAAI,KAAK,
|
|
1
|
+
{"version":3,"file":"secrets.js","sourceRoot":"","sources":["../src/secrets.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAEvC,gFAAgF;AAChF,MAAM,UAAU,cAAc;IAC5B,MAAM,EAAE,YAAY,EAAE,eAAe,EAAE,GAAG,WAAW,EAAE,CAAC;IACxD,IAAI,CAAC,YAAY,EAAE,CAAC;QAClB,MAAM,IAAI,KAAK,CACb,oFAAoF,CACrF,CAAC;IACJ,CAAC;IACD,IAAI,CAAC,eAAe,EAAE,CAAC;QACrB,MAAM,IAAI,KAAK,CACb,mFAAmF,CACpF,CAAC;IACJ,CAAC;IACD,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,CAAC;AAC3C,CAAC"}
|
package/dist/tui.d.ts
CHANGED
|
@@ -1,49 +1,42 @@
|
|
|
1
|
-
import type { SoferConfig } from "@sofer_agent/core";
|
|
2
1
|
import type { SoferAgent } from "@sofer_agent/core";
|
|
3
|
-
interface ChatLine {
|
|
4
|
-
role: "you" | "agent" | "error";
|
|
2
|
+
export interface ChatLine {
|
|
3
|
+
role: "you" | "agent" | "system" | "error";
|
|
5
4
|
text: string;
|
|
6
5
|
}
|
|
7
|
-
export interface TuiState {
|
|
8
|
-
messages: ChatLine[];
|
|
9
|
-
lastTurnIn: number;
|
|
10
|
-
lastTurnOut: number;
|
|
11
|
-
}
|
|
12
6
|
export declare class SoferTui {
|
|
13
7
|
private readonly agent;
|
|
14
|
-
private readonly config;
|
|
15
8
|
private state;
|
|
16
9
|
private inputBuf;
|
|
17
10
|
private cursor;
|
|
18
|
-
private rl;
|
|
19
11
|
private columns;
|
|
20
12
|
private rows;
|
|
21
|
-
private chatH;
|
|
22
13
|
private chatW;
|
|
14
|
+
private chatH;
|
|
23
15
|
private scrollOff;
|
|
16
|
+
private status;
|
|
17
|
+
private spinnerFrame;
|
|
18
|
+
private spinnerTimer;
|
|
19
|
+
private turnStartedAt;
|
|
20
|
+
private abortCtrl;
|
|
24
21
|
private resolve;
|
|
25
22
|
private onLine;
|
|
26
|
-
constructor(agent: SoferAgent
|
|
27
|
-
get width(): number;
|
|
28
|
-
get height(): number;
|
|
23
|
+
constructor(agent: SoferAgent);
|
|
29
24
|
private measure;
|
|
30
25
|
addMessage(role: ChatLine["role"], text: string): void;
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
26
|
+
private startSpinner;
|
|
27
|
+
private stopSpinner;
|
|
28
|
+
/** Start the interactive TUI loop. */
|
|
29
|
+
run(onLine: (line: string, signal?: AbortSignal) => Promise<void>): Promise<void>;
|
|
34
30
|
private shutdown;
|
|
35
31
|
private handleInput;
|
|
36
32
|
private submit;
|
|
37
33
|
private handleResize;
|
|
38
|
-
/** Reset cursor, clear, redraw everything. */
|
|
39
34
|
render(): void;
|
|
40
35
|
private buildFrame;
|
|
41
|
-
private
|
|
36
|
+
private wrapMessage;
|
|
37
|
+
private roleLabel;
|
|
42
38
|
private pad;
|
|
43
|
-
private blank;
|
|
44
39
|
private sidebarRow;
|
|
45
40
|
private buildSidebar;
|
|
46
|
-
private shortModel;
|
|
47
41
|
}
|
|
48
|
-
export {};
|
|
49
42
|
//# sourceMappingURL=tui.d.ts.map
|
package/dist/tui.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tui.d.ts","sourceRoot":"","sources":["../src/tui.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"tui.d.ts","sourceRoot":"","sources":["../src/tui.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAUpD,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,KAAK,GAAG,OAAO,GAAG,QAAQ,GAAG,OAAO,CAAC;IAC3C,IAAI,EAAE,MAAM,CAAC;CACd;AAED,qBAAa,QAAQ;IAoBP,OAAO,CAAC,QAAQ,CAAC,KAAK;IAnBlC,OAAO,CAAC,KAAK,CAAkB;IAC/B,OAAO,CAAC,QAAQ,CAAM;IACtB,OAAO,CAAC,MAAM,CAAK;IACnB,OAAO,CAAC,OAAO,CAAM;IACrB,OAAO,CAAC,IAAI,CAAM;IAClB,OAAO,CAAC,KAAK,CAAM;IACnB,OAAO,CAAC,KAAK,CAAM;IACnB,OAAO,CAAC,SAAS,CAAK;IAEtB,OAAO,CAAC,MAAM,CAA+B;IAC7C,OAAO,CAAC,YAAY,CAAK;IACzB,OAAO,CAAC,YAAY,CAA+C;IACnE,OAAO,CAAC,aAAa,CAAK;IAE1B,OAAO,CAAC,SAAS,CAAgC;IAEjD,OAAO,CAAC,OAAO,CAA6B;IAC5C,OAAO,CAAC,MAAM,CAAwE;gBAEzD,KAAK,EAAE,UAAU;IAE9C,OAAO,CAAC,OAAO;IAOf,UAAU,CAAC,IAAI,EAAE,QAAQ,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAItD,OAAO,CAAC,YAAY;IAWpB,OAAO,CAAC,WAAW;IASnB,sCAAsC;IAChC,GAAG,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAevF,OAAO,CAAC,QAAQ;IAahB,OAAO,CAAC,WAAW,CA4CjB;YAEY,MAAM;IA2BpB,OAAO,CAAC,YAAY,CAGlB;IAEF,MAAM,IAAI,IAAI;IASd,OAAO,CAAC,UAAU;IAsDlB,OAAO,CAAC,WAAW;IAUnB,OAAO,CAAC,SAAS;IAajB,OAAO,CAAC,GAAG;IAQX,OAAO,CAAC,UAAU;IAalB,OAAO,CAAC,YAAY;CAiBrB"}
|
package/dist/tui.js
CHANGED
|
@@ -1,28 +1,32 @@
|
|
|
1
|
-
const SIDEBAR_W =
|
|
1
|
+
const SIDEBAR_W = 26;
|
|
2
2
|
const MIN_CHAT_W = 30;
|
|
3
|
+
const GUTTER = " ";
|
|
4
|
+
const LABEL_W = 6;
|
|
5
|
+
const INDENT = `${GUTTER}${" ".repeat(LABEL_W + 1)}`;
|
|
6
|
+
const SPINNER_FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
|
|
7
|
+
const SPINNER_MS = 80;
|
|
3
8
|
export class SoferTui {
|
|
4
9
|
agent;
|
|
5
|
-
|
|
6
|
-
state = { messages: [], lastTurnIn: 0, lastTurnOut: 0 };
|
|
10
|
+
state = [];
|
|
7
11
|
inputBuf = "";
|
|
8
12
|
cursor = 0;
|
|
9
|
-
rl = null;
|
|
10
13
|
columns = 80;
|
|
11
14
|
rows = 24;
|
|
12
|
-
chatH = 20;
|
|
13
15
|
chatW = 50;
|
|
16
|
+
chatH = 20;
|
|
14
17
|
scrollOff = 0;
|
|
18
|
+
// spinner
|
|
19
|
+
status = "idle";
|
|
20
|
+
spinnerFrame = 0;
|
|
21
|
+
spinnerTimer = null;
|
|
22
|
+
turnStartedAt = 0;
|
|
23
|
+
// interrupt
|
|
24
|
+
abortCtrl = null;
|
|
25
|
+
// callbacks
|
|
15
26
|
resolve = null;
|
|
16
27
|
onLine = null;
|
|
17
|
-
constructor(agent
|
|
28
|
+
constructor(agent) {
|
|
18
29
|
this.agent = agent;
|
|
19
|
-
this.config = config;
|
|
20
|
-
}
|
|
21
|
-
get width() {
|
|
22
|
-
return this.columns;
|
|
23
|
-
}
|
|
24
|
-
get height() {
|
|
25
|
-
return this.rows;
|
|
26
30
|
}
|
|
27
31
|
measure() {
|
|
28
32
|
this.columns = process.stdout.columns ?? 80;
|
|
@@ -31,13 +35,28 @@ export class SoferTui {
|
|
|
31
35
|
this.chatH = this.rows - 3;
|
|
32
36
|
}
|
|
33
37
|
addMessage(role, text) {
|
|
34
|
-
this.state.
|
|
38
|
+
this.state.push({ role, text });
|
|
39
|
+
}
|
|
40
|
+
startSpinner() {
|
|
41
|
+
if (this.spinnerTimer)
|
|
42
|
+
return;
|
|
43
|
+
this.status = "thinking";
|
|
44
|
+
this.turnStartedAt = Date.now();
|
|
45
|
+
this.spinnerFrame = 0;
|
|
46
|
+
this.spinnerTimer = setInterval(() => {
|
|
47
|
+
this.spinnerFrame = (this.spinnerFrame + 1) % SPINNER_FRAMES.length;
|
|
48
|
+
this.render();
|
|
49
|
+
}, SPINNER_MS);
|
|
35
50
|
}
|
|
36
|
-
|
|
37
|
-
this.
|
|
38
|
-
this.
|
|
51
|
+
stopSpinner() {
|
|
52
|
+
this.status = "idle";
|
|
53
|
+
this.turnStartedAt = 0;
|
|
54
|
+
if (this.spinnerTimer) {
|
|
55
|
+
clearInterval(this.spinnerTimer);
|
|
56
|
+
this.spinnerTimer = null;
|
|
57
|
+
}
|
|
39
58
|
}
|
|
40
|
-
/** Start the interactive TUI loop.
|
|
59
|
+
/** Start the interactive TUI loop. */
|
|
41
60
|
async run(onLine) {
|
|
42
61
|
this.onLine = onLine;
|
|
43
62
|
this.measure();
|
|
@@ -52,11 +71,12 @@ export class SoferTui {
|
|
|
52
71
|
});
|
|
53
72
|
}
|
|
54
73
|
shutdown() {
|
|
74
|
+
this.stopSpinner();
|
|
55
75
|
process.stdin.setRawMode?.(false);
|
|
56
76
|
process.stdin.removeAllListeners("data");
|
|
57
77
|
process.stdout.removeAllListeners("resize");
|
|
58
78
|
process.removeAllListeners("SIGWINCH");
|
|
59
|
-
this.render();
|
|
79
|
+
this.render();
|
|
60
80
|
process.stdout.write("\n");
|
|
61
81
|
this.resolve?.();
|
|
62
82
|
}
|
|
@@ -66,12 +86,26 @@ export class SoferTui {
|
|
|
66
86
|
for (const ch of str) {
|
|
67
87
|
const code = ch.charCodeAt(0);
|
|
68
88
|
if (code === 3) {
|
|
69
|
-
|
|
89
|
+
this.shutdown();
|
|
90
|
+
return;
|
|
70
91
|
} // Ctrl-C
|
|
71
92
|
if (code === 4 && this.inputBuf.length === 0) {
|
|
72
93
|
this.shutdown();
|
|
73
94
|
return;
|
|
74
|
-
} // Ctrl-D
|
|
95
|
+
} // Ctrl-D
|
|
96
|
+
if (code === 27) {
|
|
97
|
+
// Escape
|
|
98
|
+
if (this.status === "thinking" && this.abortCtrl) {
|
|
99
|
+
this.abortCtrl.abort();
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
this.inputBuf = ""; // clear input on Esc when idle
|
|
103
|
+
this.cursor = 0;
|
|
104
|
+
this.render();
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
if (this.status === "thinking")
|
|
108
|
+
continue; // reject input while thinking
|
|
75
109
|
if (code === 13) {
|
|
76
110
|
this.submit();
|
|
77
111
|
return;
|
|
@@ -86,13 +120,6 @@ export class SoferTui {
|
|
|
86
120
|
this.render();
|
|
87
121
|
return;
|
|
88
122
|
}
|
|
89
|
-
if (code === 27 && this.inputBuf.length > 0) {
|
|
90
|
-
// Escape clears input
|
|
91
|
-
this.inputBuf = "";
|
|
92
|
-
this.cursor = 0;
|
|
93
|
-
this.render();
|
|
94
|
-
return;
|
|
95
|
-
}
|
|
96
123
|
if (ch >= " ") {
|
|
97
124
|
this.inputBuf = this.inputBuf.slice(0, this.cursor) + ch + this.inputBuf.slice(this.cursor);
|
|
98
125
|
this.cursor++;
|
|
@@ -113,41 +140,46 @@ export class SoferTui {
|
|
|
113
140
|
return;
|
|
114
141
|
}
|
|
115
142
|
this.addMessage("you", line);
|
|
116
|
-
this.scrollOff = 0;
|
|
143
|
+
this.scrollOff = 0;
|
|
144
|
+
this.abortCtrl = new AbortController();
|
|
145
|
+
this.startSpinner();
|
|
146
|
+
this.render();
|
|
147
|
+
await this.onLine?.(line, this.abortCtrl.signal);
|
|
148
|
+
this.abortCtrl = null;
|
|
149
|
+
this.stopSpinner();
|
|
117
150
|
this.render();
|
|
118
|
-
await this.onLine?.(line);
|
|
119
151
|
}
|
|
120
|
-
// ──
|
|
152
|
+
// ── Rendering ───────────────────────────────────────────────────────────
|
|
121
153
|
handleResize = () => {
|
|
122
154
|
this.measure();
|
|
123
155
|
this.render();
|
|
124
156
|
};
|
|
125
|
-
/** Reset cursor, clear, redraw everything. */
|
|
126
157
|
render() {
|
|
127
158
|
this.measure();
|
|
128
159
|
const out = process.stdout;
|
|
129
|
-
out.write("\x1b[?25l");
|
|
160
|
+
out.write("\x1b[?25l");
|
|
161
|
+
out.write("\x1b[H");
|
|
130
162
|
out.write(this.buildFrame());
|
|
131
|
-
out.write("\x1b[?25h");
|
|
163
|
+
out.write("\x1b[?25h");
|
|
132
164
|
}
|
|
133
165
|
buildFrame() {
|
|
134
|
-
const {
|
|
135
|
-
const rightW = columns - chatW - 1;
|
|
136
|
-
let buf = "
|
|
137
|
-
// ──
|
|
138
|
-
|
|
139
|
-
const
|
|
140
|
-
const wrappedLines = msgs.flatMap((m) => this.wrapLine(m, chatW));
|
|
141
|
-
const totalLines = wrappedLines.length;
|
|
142
|
-
const maxScroll = Math.max(0, totalLines - chatH);
|
|
166
|
+
const { chatW, chatH } = this;
|
|
167
|
+
const rightW = this.columns - chatW - 1;
|
|
168
|
+
let buf = "";
|
|
169
|
+
// ── Chat body ─────────────────────────────────────────────────────────
|
|
170
|
+
const wrapped = this.state.flatMap((m) => this.wrapMessage(m, chatW));
|
|
171
|
+
const maxScroll = Math.max(0, wrapped.length - chatH);
|
|
143
172
|
if (this.scrollOff > maxScroll)
|
|
144
173
|
this.scrollOff = maxScroll;
|
|
145
174
|
if (this.scrollOff < 0)
|
|
146
175
|
this.scrollOff = 0;
|
|
147
|
-
const visible =
|
|
176
|
+
const visible = wrapped.slice(this.scrollOff, this.scrollOff + chatH);
|
|
148
177
|
for (let r = 0; r < chatH; r++) {
|
|
149
178
|
const line = visible[r];
|
|
150
|
-
|
|
179
|
+
if (line)
|
|
180
|
+
buf += this.pad(line, chatW);
|
|
181
|
+
else
|
|
182
|
+
buf += " ".repeat(chatW);
|
|
151
183
|
if (r === 0)
|
|
152
184
|
buf += "┬";
|
|
153
185
|
else if (r === chatH - 1)
|
|
@@ -159,64 +191,59 @@ export class SoferTui {
|
|
|
159
191
|
}
|
|
160
192
|
// ── Divider ───────────────────────────────────────────────────────────
|
|
161
193
|
buf += `${"─".repeat(chatW)}┴${"─".repeat(rightW)}\n`;
|
|
162
|
-
// ──
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
if (typed.length > maxInputW) {
|
|
168
|
-
const start = typed.length - maxInputW;
|
|
169
|
-
typed = typed.slice(start);
|
|
194
|
+
// ── Spinner row ───────────────────────────────────────────────────────
|
|
195
|
+
if (this.status === "thinking") {
|
|
196
|
+
const frame = SPINNER_FRAMES[this.spinnerFrame];
|
|
197
|
+
const elapsed = this.turnStartedAt ? formatElapsed(Date.now() - this.turnStartedAt) : "";
|
|
198
|
+
buf += `\x1b[36m${frame} thinking…${elapsed ? ` ${elapsed}` : ""} (esc to interrupt)\x1b[0m\x1b[K\n`;
|
|
170
199
|
}
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
const
|
|
174
|
-
buf +=
|
|
200
|
+
// ── Input bar ─────────────────────────────────────────────────────────
|
|
201
|
+
const prefix = "\x1b[36m> \x1b[0m";
|
|
202
|
+
const cursorChar = this.status === "idle" ? "\x1b[5m▋\x1b[25m" : "";
|
|
203
|
+
buf += `${prefix}${this.inputBuf}${cursorChar}\x1b[K`;
|
|
204
|
+
// Footer
|
|
205
|
+
const totalIn = this.agent.usage.tokens.inputTokens;
|
|
206
|
+
const totalOut = this.agent.usage.tokens.outputTokens;
|
|
207
|
+
const cost = this.agent.usage.costUsd.toFixed(4);
|
|
208
|
+
const footer = `\x1b[90m${this.agent.address.slice(0, 10)}… · tokens ${totalIn}/${totalOut} · $${cost} · /exit to quit\x1b[0m`;
|
|
209
|
+
buf += `\n${this.pad(footer, this.columns)}`;
|
|
210
|
+
// Place cursor at input position
|
|
211
|
+
const cursorCol = 2 + Math.min(this.cursor, this.columns - 5);
|
|
212
|
+
buf += `\x1b[${this.chatH + 2 + (this.status === "thinking" ? 1 : 0)};${cursorCol}H`;
|
|
175
213
|
return buf;
|
|
176
214
|
}
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
current = ` ${word}`; // indent continuation
|
|
199
|
-
}
|
|
215
|
+
// ── Message wrapping with gutter ────────────────────────────────────────
|
|
216
|
+
wrapMessage(msg, width) {
|
|
217
|
+
const label = this.roleLabel(msg.role);
|
|
218
|
+
const bodyWidth = width - GUTTER.length - LABEL_W - 1;
|
|
219
|
+
const lines = wrapText(msg.text, bodyWidth);
|
|
220
|
+
return lines.map((l, i) => {
|
|
221
|
+
if (i === 0)
|
|
222
|
+
return `${GUTTER}${label} ${l}`;
|
|
223
|
+
return `${INDENT}${l}`;
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
roleLabel(role) {
|
|
227
|
+
switch (role) {
|
|
228
|
+
case "you":
|
|
229
|
+
return "\x1b[36myou \x1b[0m"; // cyan "you", padded to LABEL_W
|
|
230
|
+
case "agent":
|
|
231
|
+
return `\x1b[32m${this.agent.name.toLowerCase().slice(0, LABEL_W).padEnd(LABEL_W)}\x1b[0m`;
|
|
232
|
+
case "system":
|
|
233
|
+
return "\x1b[90msys \x1b[0m";
|
|
234
|
+
case "error":
|
|
235
|
+
return "\x1b[31merr \x1b[0m";
|
|
200
236
|
}
|
|
201
|
-
if (current.length > label.length)
|
|
202
|
-
lines.push(current);
|
|
203
|
-
return lines.length > 0 ? lines : [full];
|
|
204
237
|
}
|
|
205
238
|
pad(s, w) {
|
|
206
|
-
|
|
207
|
-
const
|
|
208
|
-
const stripped = s.replace(ansiRe, "");
|
|
209
|
-
const padW = Math.max(0, w - stripped.length);
|
|
239
|
+
const stripped = stripAnsi(s);
|
|
240
|
+
const padW = Math.max(0, w - visibleLen(stripped));
|
|
210
241
|
return s + " ".repeat(padW);
|
|
211
242
|
}
|
|
212
|
-
blank(w) {
|
|
213
|
-
return " ".repeat(w);
|
|
214
|
-
}
|
|
215
243
|
// ── Sidebar ─────────────────────────────────────────────────────────────
|
|
216
244
|
sidebarRow(row, total, width) {
|
|
217
|
-
const innerW = width - 2;
|
|
245
|
+
const innerW = width - 2;
|
|
218
246
|
const lines = this.buildSidebar(innerW);
|
|
219
|
-
// center the sidebar content vertically
|
|
220
247
|
const gap = Math.max(0, total - lines.length);
|
|
221
248
|
const topPad = Math.floor(gap / 2);
|
|
222
249
|
const idx = row - topPad;
|
|
@@ -225,41 +252,76 @@ export class SoferTui {
|
|
|
225
252
|
const line = lines[idx];
|
|
226
253
|
if (!line)
|
|
227
254
|
return " ".repeat(width);
|
|
228
|
-
|
|
255
|
+
const padded = padRight(line, innerW);
|
|
256
|
+
return ` ${padded} `;
|
|
229
257
|
}
|
|
230
258
|
buildSidebar(width) {
|
|
231
|
-
const { agent
|
|
232
|
-
const addr = `${agent.address.slice(0, 6)}
|
|
259
|
+
const { agent } = this;
|
|
260
|
+
const addr = `${agent.address.slice(0, 6)}…${agent.address.slice(-4)}`;
|
|
233
261
|
const totalIn = agent.usage.tokens.inputTokens;
|
|
234
262
|
const totalOut = agent.usage.tokens.outputTokens;
|
|
235
263
|
const cost = agent.usage.costUsd.toFixed(4);
|
|
236
264
|
return [
|
|
237
265
|
`\x1b[1m${agent.name}\x1b[0m`,
|
|
238
266
|
"─".repeat(width),
|
|
239
|
-
`
|
|
240
|
-
`Max out: ${config.brain.maxOutputTokens}`,
|
|
241
|
-
"",
|
|
242
|
-
`Turn in: ${state.lastTurnIn}`,
|
|
243
|
-
`Turn out: ${state.lastTurnOut}`,
|
|
244
|
-
`Total in: ${totalIn}`,
|
|
245
|
-
`Total out:${totalOut}`,
|
|
246
|
-
`Cost: \x1b[33m$${cost}\x1b[0m`,
|
|
267
|
+
` \x1b[90maddr\x1b[0m ${addr}`,
|
|
247
268
|
"",
|
|
248
|
-
`
|
|
249
|
-
`
|
|
269
|
+
` \x1b[90min\x1b[0m ${totalIn}`,
|
|
270
|
+
` \x1b[90mout\x1b[0m ${totalOut}`,
|
|
271
|
+
` \x1b[33m$${cost}\x1b[0m`,
|
|
250
272
|
];
|
|
251
273
|
}
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
274
|
+
}
|
|
275
|
+
// ── Helpers ────────────────────────────────────────────────────────────────
|
|
276
|
+
function wrapText(text, width) {
|
|
277
|
+
if (!text)
|
|
278
|
+
return [""];
|
|
279
|
+
const lines = [];
|
|
280
|
+
const words = text.split(" ");
|
|
281
|
+
let current = "";
|
|
282
|
+
for (const word of words) {
|
|
283
|
+
if (current && current.length + 1 + word.length > width) {
|
|
284
|
+
lines.push(current);
|
|
285
|
+
current = word;
|
|
286
|
+
}
|
|
287
|
+
else {
|
|
288
|
+
current = current ? `${current} ${word}` : word;
|
|
289
|
+
}
|
|
263
290
|
}
|
|
291
|
+
if (current)
|
|
292
|
+
lines.push(current);
|
|
293
|
+
return lines.length > 0 ? lines : [""];
|
|
294
|
+
}
|
|
295
|
+
function stripAnsi(s) {
|
|
296
|
+
// biome-ignore lint/suspicious/noControlCharactersInRegex: ANSI escape stripping
|
|
297
|
+
return s.replace(/\x1b\[\d*;?\d*m/g, "");
|
|
298
|
+
}
|
|
299
|
+
function visibleLen(s) {
|
|
300
|
+
// biome-ignore lint/suspicious/noControlCharactersInRegex: ANSI escape stripping
|
|
301
|
+
const ansiRe = /\x1b\[\d*;?\d*m/g;
|
|
302
|
+
let len = 0;
|
|
303
|
+
let lastIdx = 0;
|
|
304
|
+
let match = ansiRe.exec(s);
|
|
305
|
+
while (match !== null) {
|
|
306
|
+
len += match.index - lastIdx;
|
|
307
|
+
lastIdx = match.index + match[0].length;
|
|
308
|
+
match = ansiRe.exec(s);
|
|
309
|
+
}
|
|
310
|
+
len += s.length - lastIdx;
|
|
311
|
+
return len;
|
|
312
|
+
}
|
|
313
|
+
function padRight(s, width) {
|
|
314
|
+
const len = visibleLen(s);
|
|
315
|
+
if (len >= width)
|
|
316
|
+
return s;
|
|
317
|
+
return s + " ".repeat(width - len);
|
|
318
|
+
}
|
|
319
|
+
function formatElapsed(ms) {
|
|
320
|
+
const sec = Math.floor(ms / 1000);
|
|
321
|
+
if (sec < 60)
|
|
322
|
+
return `${sec}s`;
|
|
323
|
+
const m = Math.floor(sec / 60);
|
|
324
|
+
const s = sec % 60;
|
|
325
|
+
return `${m}m${String(s).padStart(2, "0")}s`;
|
|
264
326
|
}
|
|
265
327
|
//# sourceMappingURL=tui.js.map
|