agentsw 0.9.2 → 0.10.1
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 +50 -4
- package/README.zh-CN.md +44 -3
- package/dist/app-paths.js +127 -0
- package/dist/apps.js +11 -15
- package/dist/commands.js +375 -205
- package/dist/discover.js +21 -11
- package/dist/gateway.js +77 -18
- package/dist/i18n.js +147 -3
- package/dist/index.js +14 -2
- package/dist/menu.js +141 -121
- package/dist/metadata.js +34 -9
- package/dist/modelsdev.js +51 -21
- package/dist/progress.js +116 -0
- package/dist/provider-actions.js +29 -9
- package/dist/remove.js +15 -30
- package/dist/rename.js +17 -32
- package/dist/report.js +67 -0
- package/dist/store.js +25 -0
- package/dist/targets/claudecode.js +12 -9
- package/dist/targets/codex.js +22 -13
- package/dist/targets/dsh.js +20 -35
- package/dist/targets/hermes.js +11 -20
- package/dist/targets/omp.js +15 -7
- package/dist/targets/opencode.js +42 -25
- package/dist/targets/pistyle.js +10 -21
- package/dist/targets/wire.js +22 -7
- package/dist/targets/workbuddy.js +6 -4
- package/dist/ui.js +157 -0
- package/package.json +2 -2
package/dist/ui.js
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import pc from "picocolors";
|
|
2
|
+
import { t } from "./i18n.js";
|
|
3
|
+
/** Thrown by cancelInteractive() inside the menu so a Ctrl-C returns to the menu instead of exiting. */
|
|
4
|
+
export class ActionCancelled extends Error {
|
|
5
|
+
constructor() {
|
|
6
|
+
super("cancelled");
|
|
7
|
+
this.name = "ActionCancelled";
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
let json = false;
|
|
11
|
+
let quiet = false;
|
|
12
|
+
let version = "";
|
|
13
|
+
let command = "";
|
|
14
|
+
let inMenu = false;
|
|
15
|
+
export function configureOutput(config) {
|
|
16
|
+
if (config.json !== undefined)
|
|
17
|
+
json = config.json;
|
|
18
|
+
if (config.quiet !== undefined)
|
|
19
|
+
quiet = config.quiet;
|
|
20
|
+
if (config.version !== undefined)
|
|
21
|
+
version = config.version;
|
|
22
|
+
}
|
|
23
|
+
export function setCommandName(name) {
|
|
24
|
+
command = name;
|
|
25
|
+
}
|
|
26
|
+
export function setInMenu(value) {
|
|
27
|
+
inMenu = value;
|
|
28
|
+
}
|
|
29
|
+
export function isJson() {
|
|
30
|
+
return json;
|
|
31
|
+
}
|
|
32
|
+
export function isQuiet() {
|
|
33
|
+
return quiet;
|
|
34
|
+
}
|
|
35
|
+
/** Primary result channel. Machine mode suppresses it so stdout carries exactly one JSON document. */
|
|
36
|
+
export function out(line = "") {
|
|
37
|
+
if (json)
|
|
38
|
+
return;
|
|
39
|
+
console.log(line);
|
|
40
|
+
}
|
|
41
|
+
/** Dimmed guidance ("next: ...", "run agentsw sync"). Dropped by --quiet and --json. */
|
|
42
|
+
export function note(message) {
|
|
43
|
+
if (json || quiet)
|
|
44
|
+
return;
|
|
45
|
+
process.stderr.write(pc.dim(message) + "\n");
|
|
46
|
+
}
|
|
47
|
+
/** Non-fatal problem. Keeps the literal "warning: " prefix callers and tests rely on. */
|
|
48
|
+
export function warn(message) {
|
|
49
|
+
if (json || quiet)
|
|
50
|
+
return;
|
|
51
|
+
process.stderr.write(pc.yellow(`warning: ${message}`) + "\n");
|
|
52
|
+
}
|
|
53
|
+
function emitJsonError(message) {
|
|
54
|
+
process.stdout.write(JSON.stringify({ ok: false, command, version, error: { message } }) + "\n");
|
|
55
|
+
}
|
|
56
|
+
export function emitJson(data) {
|
|
57
|
+
process.stdout.write(JSON.stringify({ ok: true, command, version, data }) + "\n");
|
|
58
|
+
}
|
|
59
|
+
/** Fatal error. In machine mode the failure becomes the single stdout document, so pipes stay parseable. */
|
|
60
|
+
export function error(message) {
|
|
61
|
+
if (json)
|
|
62
|
+
emitJsonError(message);
|
|
63
|
+
else
|
|
64
|
+
process.stderr.write(pc.red(`error: ${message}`) + "\n");
|
|
65
|
+
process.exit(1);
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Ctrl-C / Esc at an interactive prompt. Inside the menu this unwinds to the dispatch loop, which keeps the
|
|
69
|
+
* session alive; standalone it exits 130 (the SIGINT convention), so a cancelled run is never mistaken for
|
|
70
|
+
* a successful one. Prompts awaits onCancel inside a catch, so throwing here reliably rejects the prompt.
|
|
71
|
+
*/
|
|
72
|
+
export function cancelInteractive() {
|
|
73
|
+
if (inMenu)
|
|
74
|
+
throw new ActionCancelled();
|
|
75
|
+
process.stderr.write(pc.dim(t("common.cancelled")) + "\n");
|
|
76
|
+
process.exit(130);
|
|
77
|
+
}
|
|
78
|
+
/** 0 means "no known width" (pipe, file, test runner), which disables truncation entirely. */
|
|
79
|
+
export function terminalWidth() {
|
|
80
|
+
const columns = process.stdout.columns;
|
|
81
|
+
return typeof columns === "number" && Number.isFinite(columns) && columns > 0 ? columns : 0;
|
|
82
|
+
}
|
|
83
|
+
const SGR = /\u001b\[[0-9;]*m/g;
|
|
84
|
+
export function visibleWidth(text) {
|
|
85
|
+
return text.replace(SGR, "").length;
|
|
86
|
+
}
|
|
87
|
+
/** Clip to a visible width without ever cutting inside an SGR sequence; closes color that was left open. */
|
|
88
|
+
export function truncateVisible(text, max) {
|
|
89
|
+
if (max <= 0)
|
|
90
|
+
return "";
|
|
91
|
+
if (visibleWidth(text) <= max)
|
|
92
|
+
return text;
|
|
93
|
+
const target = Math.max(0, max - 1);
|
|
94
|
+
let out = "";
|
|
95
|
+
let visible = 0;
|
|
96
|
+
let colored = false;
|
|
97
|
+
for (let i = 0; i < text.length && visible < target;) {
|
|
98
|
+
if (text.charAt(i) === "\u001b") {
|
|
99
|
+
const match = /^\u001b\[[0-9;]*m/.exec(text.slice(i));
|
|
100
|
+
if (match) {
|
|
101
|
+
out += match[0];
|
|
102
|
+
colored = true;
|
|
103
|
+
i += match[0].length;
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
out += text.charAt(i);
|
|
108
|
+
visible++;
|
|
109
|
+
i++;
|
|
110
|
+
}
|
|
111
|
+
return `${out}…${colored ? "\u001b[0m" : ""}`;
|
|
112
|
+
}
|
|
113
|
+
export function padCell(text, width, align = "left") {
|
|
114
|
+
const padding = " ".repeat(Math.max(0, width - visibleWidth(text)));
|
|
115
|
+
return align === "right" ? padding + text : text + padding;
|
|
116
|
+
}
|
|
117
|
+
export function table(rows, header, options = {}) {
|
|
118
|
+
const all = header ? [header, ...rows] : rows;
|
|
119
|
+
if (all.length === 0)
|
|
120
|
+
return "";
|
|
121
|
+
const widths = [];
|
|
122
|
+
for (const row of all) {
|
|
123
|
+
row.forEach((cell, i) => {
|
|
124
|
+
widths[i] = Math.max(widths[i] ?? 0, visibleWidth(cell));
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
const minWidth = options.minWidth ?? 8;
|
|
128
|
+
const width = options.width ?? terminalWidth();
|
|
129
|
+
const eligible = (options.truncate ?? []).filter((i) => i < widths.length);
|
|
130
|
+
if (width > 0 && eligible.length > 0) {
|
|
131
|
+
const total = () => widths.reduce((sum, w, i) => sum + w + (i > 0 ? 2 : 0), 0);
|
|
132
|
+
while (total() > width) {
|
|
133
|
+
let widest = -1;
|
|
134
|
+
for (const i of eligible) {
|
|
135
|
+
if ((widths[i] ?? 0) <= minWidth)
|
|
136
|
+
continue;
|
|
137
|
+
if (widest === -1 || (widths[i] ?? 0) > (widths[widest] ?? 0))
|
|
138
|
+
widest = i;
|
|
139
|
+
}
|
|
140
|
+
if (widest === -1)
|
|
141
|
+
break;
|
|
142
|
+
widths[widest] = (widths[widest] ?? 0) - 1;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
const render = (row) => row
|
|
146
|
+
.map((cell, i) => {
|
|
147
|
+
const column = widths[i] ?? 0;
|
|
148
|
+
const clipped = visibleWidth(cell) > column ? truncateVisible(cell, column) : cell;
|
|
149
|
+
return padCell(clipped, column, options.align?.[i] ?? "left");
|
|
150
|
+
})
|
|
151
|
+
.join(" ")
|
|
152
|
+
.trimEnd();
|
|
153
|
+
const lines = all.map(render);
|
|
154
|
+
if (header)
|
|
155
|
+
lines.splice(1, 0, widths.map((w) => "-".repeat(w)).join(" "));
|
|
156
|
+
return lines.join("\n");
|
|
157
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agentsw",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.1",
|
|
4
4
|
"description": "CLI provider switcher for coding agents: manage OpenAI/Anthropic-protocol providers in one place and sync them, with models.dev metadata, into Claude Code, Codex, omp, pi, prime-agent, opencode, Hermes, WorkBuddy and DeepSeek Harness",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"prepublishOnly": "npm run build",
|
|
38
38
|
"dev": "tsx src/index.ts",
|
|
39
39
|
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
40
|
-
"test": "tsx --test test/*.test.ts"
|
|
40
|
+
"test": "tsx --import ./test/setup.ts --test test/*.test.ts"
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
43
|
"commander": "^15.0.0",
|