atomix-cli 1.2.0 → 1.3.0
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/cli.js +5420 -349
- package/dist/session.d.ts +256 -3
- package/dist/session.js +4983 -345
- package/dist/session.mjs +4980 -344
- package/dist/tui.mjs +1221 -255
- package/package.json +3 -1
package/dist/tui.mjs
CHANGED
|
@@ -7941,7 +7941,7 @@ var require_react_reconciler_development = __commonJS({
|
|
|
7941
7941
|
var HostPortal = 4;
|
|
7942
7942
|
var HostComponent = 5;
|
|
7943
7943
|
var HostText = 6;
|
|
7944
|
-
var
|
|
7944
|
+
var Fragment = 7;
|
|
7945
7945
|
var Mode = 8;
|
|
7946
7946
|
var ContextConsumer = 9;
|
|
7947
7947
|
var ContextProvider = 10;
|
|
@@ -8081,7 +8081,7 @@ var require_react_reconciler_development = __commonJS({
|
|
|
8081
8081
|
return "DehydratedFragment";
|
|
8082
8082
|
case ForwardRef:
|
|
8083
8083
|
return getWrappedName$1(type, type.render, "ForwardRef");
|
|
8084
|
-
case
|
|
8084
|
+
case Fragment:
|
|
8085
8085
|
return "Fragment";
|
|
8086
8086
|
case HostComponent:
|
|
8087
8087
|
return type;
|
|
@@ -11215,7 +11215,7 @@ var require_react_reconciler_development = __commonJS({
|
|
|
11215
11215
|
}
|
|
11216
11216
|
}
|
|
11217
11217
|
function updateFragment2(returnFiber, current2, fragment, lanes, key) {
|
|
11218
|
-
if (current2 === null || current2.tag !==
|
|
11218
|
+
if (current2 === null || current2.tag !== Fragment) {
|
|
11219
11219
|
var created = createFiberFromFragment(fragment, returnFiber.mode, lanes, key);
|
|
11220
11220
|
created.return = returnFiber;
|
|
11221
11221
|
return created;
|
|
@@ -11618,7 +11618,7 @@ var require_react_reconciler_development = __commonJS({
|
|
|
11618
11618
|
if (child.key === key) {
|
|
11619
11619
|
var elementType = element.type;
|
|
11620
11620
|
if (elementType === REACT_FRAGMENT_TYPE) {
|
|
11621
|
-
if (child.tag ===
|
|
11621
|
+
if (child.tag === Fragment) {
|
|
11622
11622
|
deleteRemainingChildren(returnFiber, child.sibling);
|
|
11623
11623
|
var existing = useFiber(child, element.props.children);
|
|
11624
11624
|
existing.return = returnFiber;
|
|
@@ -17109,7 +17109,7 @@ var require_react_reconciler_development = __commonJS({
|
|
|
17109
17109
|
var _resolvedProps2 = workInProgress2.elementType === type ? _unresolvedProps2 : resolveDefaultProps(type, _unresolvedProps2);
|
|
17110
17110
|
return updateForwardRef(current2, workInProgress2, type, _resolvedProps2, renderLanes2);
|
|
17111
17111
|
}
|
|
17112
|
-
case
|
|
17112
|
+
case Fragment:
|
|
17113
17113
|
return updateFragment(current2, workInProgress2, renderLanes2);
|
|
17114
17114
|
case Mode:
|
|
17115
17115
|
return updateMode(current2, workInProgress2, renderLanes2);
|
|
@@ -17546,7 +17546,7 @@ var require_react_reconciler_development = __commonJS({
|
|
|
17546
17546
|
case SimpleMemoComponent:
|
|
17547
17547
|
case FunctionComponent:
|
|
17548
17548
|
case ForwardRef:
|
|
17549
|
-
case
|
|
17549
|
+
case Fragment:
|
|
17550
17550
|
case Mode:
|
|
17551
17551
|
case Profiler:
|
|
17552
17552
|
case ContextConsumer:
|
|
@@ -22314,7 +22314,7 @@ var require_react_reconciler_development = __commonJS({
|
|
|
22314
22314
|
return fiber;
|
|
22315
22315
|
}
|
|
22316
22316
|
function createFiberFromFragment(elements, mode, lanes, key) {
|
|
22317
|
-
var fiber = createFiber(
|
|
22317
|
+
var fiber = createFiber(Fragment, elements, key, mode);
|
|
22318
22318
|
fiber.lanes = lanes;
|
|
22319
22319
|
return fiber;
|
|
22320
22320
|
}
|
|
@@ -23358,7 +23358,7 @@ var require_permessage_deflate = __commonJS({
|
|
|
23358
23358
|
acceptAsServer(offers) {
|
|
23359
23359
|
const opts = this._options;
|
|
23360
23360
|
const accepted = offers.find((params) => {
|
|
23361
|
-
if (opts.serverNoContextTakeover === false && params.server_no_context_takeover || params.server_max_window_bits && (opts.serverMaxWindowBits === false || typeof opts.serverMaxWindowBits === "number" && opts.serverMaxWindowBits > params.server_max_window_bits) || typeof opts.clientMaxWindowBits === "number" && !params.client_max_window_bits) {
|
|
23361
|
+
if (opts.serverNoContextTakeover === false && params.server_no_context_takeover || params.server_max_window_bits && (opts.serverMaxWindowBits === false || typeof opts.serverMaxWindowBits === "number" && opts.serverMaxWindowBits > params.server_max_window_bits) || typeof opts.clientMaxWindowBits === "number" && (typeof params.client_max_window_bits === "number" ? opts.clientMaxWindowBits > params.client_max_window_bits : !params.client_max_window_bits)) {
|
|
23362
23362
|
return false;
|
|
23363
23363
|
}
|
|
23364
23364
|
return true;
|
|
@@ -23878,6 +23878,7 @@ var require_receiver = __commonJS({
|
|
|
23878
23878
|
this._opcode = 0;
|
|
23879
23879
|
this._totalPayloadLength = 0;
|
|
23880
23880
|
this._messageLength = 0;
|
|
23881
|
+
this._numFragments = 0;
|
|
23881
23882
|
this._fragments = [];
|
|
23882
23883
|
this._errored = false;
|
|
23883
23884
|
this._loop = false;
|
|
@@ -24228,23 +24229,23 @@ var require_receiver = __commonJS({
|
|
|
24228
24229
|
this.controlMessage(data, cb);
|
|
24229
24230
|
return;
|
|
24230
24231
|
}
|
|
24232
|
+
if (this._maxFragments > 0 && ++this._numFragments > this._maxFragments) {
|
|
24233
|
+
const error = this.createError(
|
|
24234
|
+
RangeError,
|
|
24235
|
+
"Too many message fragments",
|
|
24236
|
+
false,
|
|
24237
|
+
1008,
|
|
24238
|
+
"WS_ERR_TOO_MANY_BUFFERED_PARTS"
|
|
24239
|
+
);
|
|
24240
|
+
cb(error);
|
|
24241
|
+
return;
|
|
24242
|
+
}
|
|
24231
24243
|
if (this._compressed) {
|
|
24232
24244
|
this._state = INFLATING;
|
|
24233
24245
|
this.decompress(data, cb);
|
|
24234
24246
|
return;
|
|
24235
24247
|
}
|
|
24236
24248
|
if (data.length) {
|
|
24237
|
-
if (this._maxFragments > 0 && this._fragments.length >= this._maxFragments) {
|
|
24238
|
-
const error = this.createError(
|
|
24239
|
-
RangeError,
|
|
24240
|
-
"Too many message fragments",
|
|
24241
|
-
false,
|
|
24242
|
-
1008,
|
|
24243
|
-
"WS_ERR_TOO_MANY_BUFFERED_PARTS"
|
|
24244
|
-
);
|
|
24245
|
-
cb(error);
|
|
24246
|
-
return;
|
|
24247
|
-
}
|
|
24248
24249
|
this._messageLength = this._totalPayloadLength;
|
|
24249
24250
|
this._fragments.push(data);
|
|
24250
24251
|
}
|
|
@@ -24274,17 +24275,6 @@ var require_receiver = __commonJS({
|
|
|
24274
24275
|
cb(error);
|
|
24275
24276
|
return;
|
|
24276
24277
|
}
|
|
24277
|
-
if (this._maxFragments > 0 && this._fragments.length >= this._maxFragments) {
|
|
24278
|
-
const error = this.createError(
|
|
24279
|
-
RangeError,
|
|
24280
|
-
"Too many message fragments",
|
|
24281
|
-
false,
|
|
24282
|
-
1008,
|
|
24283
|
-
"WS_ERR_TOO_MANY_BUFFERED_PARTS"
|
|
24284
|
-
);
|
|
24285
|
-
cb(error);
|
|
24286
|
-
return;
|
|
24287
|
-
}
|
|
24288
24278
|
this._fragments.push(buf);
|
|
24289
24279
|
}
|
|
24290
24280
|
this.dataMessage(cb);
|
|
@@ -24307,6 +24297,7 @@ var require_receiver = __commonJS({
|
|
|
24307
24297
|
this._totalPayloadLength = 0;
|
|
24308
24298
|
this._messageLength = 0;
|
|
24309
24299
|
this._fragmented = 0;
|
|
24300
|
+
this._numFragments = 0;
|
|
24310
24301
|
this._fragments = [];
|
|
24311
24302
|
if (this._opcode === 2) {
|
|
24312
24303
|
let data;
|
|
@@ -25807,8 +25798,8 @@ var require_websocket = __commonJS({
|
|
|
25807
25798
|
autoPong: true,
|
|
25808
25799
|
closeTimeout: CLOSE_TIMEOUT,
|
|
25809
25800
|
protocolVersion: protocolVersions[1],
|
|
25810
|
-
maxBufferedChunks:
|
|
25811
|
-
maxFragments:
|
|
25801
|
+
maxBufferedChunks: 256 * 1024,
|
|
25802
|
+
maxFragments: 16 * 1024,
|
|
25812
25803
|
maxPayload: 100 * 1024 * 1024,
|
|
25813
25804
|
skipUTF8Validation: false,
|
|
25814
25805
|
perMessageDeflate: true,
|
|
@@ -26395,9 +26386,9 @@ var require_websocket_server = __commonJS({
|
|
|
26395
26386
|
* called
|
|
26396
26387
|
* @param {Function} [options.handleProtocols] A hook to handle protocols
|
|
26397
26388
|
* @param {String} [options.host] The hostname where to bind the server
|
|
26398
|
-
* @param {Number} [options.maxBufferedChunks=
|
|
26389
|
+
* @param {Number} [options.maxBufferedChunks=262144] The maximum number of
|
|
26399
26390
|
* buffered data chunks
|
|
26400
|
-
* @param {Number} [options.maxFragments=
|
|
26391
|
+
* @param {Number} [options.maxFragments=16384] The maximum number of message
|
|
26401
26392
|
* fragments
|
|
26402
26393
|
* @param {Number} [options.maxPayload=104857600] The maximum allowed message
|
|
26403
26394
|
* size
|
|
@@ -26420,8 +26411,8 @@ var require_websocket_server = __commonJS({
|
|
|
26420
26411
|
options2 = {
|
|
26421
26412
|
allowSynchronousEvents: true,
|
|
26422
26413
|
autoPong: true,
|
|
26423
|
-
maxBufferedChunks:
|
|
26424
|
-
maxFragments:
|
|
26414
|
+
maxBufferedChunks: 256 * 1024,
|
|
26415
|
+
maxFragments: 16 * 1024,
|
|
26425
26416
|
maxPayload: 100 * 1024 * 1024,
|
|
26426
26417
|
skipUTF8Validation: false,
|
|
26427
26418
|
perMessageDeflate: false,
|
|
@@ -27772,7 +27763,7 @@ var require_config = __commonJS({
|
|
|
27772
27763
|
exports2.LOG_DIR_PATH = "logs";
|
|
27773
27764
|
exports2.SERVICE_LOG_FILES_RETAIN_COUNT = 7;
|
|
27774
27765
|
exports2.LLM_LOG_DIR_PATH = "llm_logs";
|
|
27775
|
-
exports2.LLM_LOG_FILES_RETAIN_COUNT =
|
|
27766
|
+
exports2.LLM_LOG_FILES_RETAIN_COUNT = 50;
|
|
27776
27767
|
exports2.LLM_LOG_CLEANUP_INTERVAL = 60 * 60 * 1e3;
|
|
27777
27768
|
exports2.TRACKS_DIR_PATH = "tracks";
|
|
27778
27769
|
exports2.TRACKS_FILES_RETAIN_COUNT = 30;
|
|
@@ -45120,9 +45111,11 @@ var require_logLLM = __commonJS({
|
|
|
45120
45111
|
};
|
|
45121
45112
|
})();
|
|
45122
45113
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
45123
|
-
exports2.logEvent = exports2.logLLMResponse = exports2.logLLMRequest = void 0;
|
|
45114
|
+
exports2.logEvent = exports2.cleanupLLMLogFiles = exports2.logLLMResponse = exports2.logLLMRequest = exports2.readActiveLLMLogSessionsAcrossProcesses = exports2.getActiveLLMLogSessions = exports2.acquireActiveLLMLogSession = exports2.REGISTER_TIMEOUT_MS = exports2.holdLLMLogLockForTest = exports2.withLLMLogLock = void 0;
|
|
45124
45115
|
var fs14 = __importStar(__require("fs"));
|
|
45125
45116
|
var path15 = __importStar(__require("path"));
|
|
45117
|
+
var crypto_1 = __require("crypto");
|
|
45118
|
+
var child_process_1 = __require("child_process");
|
|
45126
45119
|
var time_1 = require_time();
|
|
45127
45120
|
var config_1 = require_config();
|
|
45128
45121
|
var savePath_1 = require_savePath();
|
|
@@ -45131,6 +45124,280 @@ var require_logLLM = __commonJS({
|
|
|
45131
45124
|
var StateManager_1 = require_StateManager();
|
|
45132
45125
|
var lastLLMCleanupTime = 0;
|
|
45133
45126
|
var lastEventCleanupTime = 0;
|
|
45127
|
+
var LOCK_DIR = ".lock";
|
|
45128
|
+
var LOCK_POLL_MS = 15;
|
|
45129
|
+
var getLockDir = () => path15.join((0, savePath_1.getLLMLogsDir)(), LOCK_DIR);
|
|
45130
|
+
var isPidAlive = (pid) => {
|
|
45131
|
+
if (!Number.isInteger(pid) || pid <= 0)
|
|
45132
|
+
return false;
|
|
45133
|
+
try {
|
|
45134
|
+
process.kill(pid, 0);
|
|
45135
|
+
return true;
|
|
45136
|
+
} catch (err) {
|
|
45137
|
+
return err.code !== "ESRCH";
|
|
45138
|
+
}
|
|
45139
|
+
};
|
|
45140
|
+
var sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
45141
|
+
var ticketPid = (name) => {
|
|
45142
|
+
const match = /^(\d+)-[0-9a-f-]+\.ticket$/.exec(name);
|
|
45143
|
+
return match ? Number(match[1]) : null;
|
|
45144
|
+
};
|
|
45145
|
+
var PID_REUSE_GRACE_MS = 2e3;
|
|
45146
|
+
var PID_START_CACHE_MS = 1e4;
|
|
45147
|
+
var pidStartCache = /* @__PURE__ */ new Map();
|
|
45148
|
+
var pidStartTimeMs = (pid) => {
|
|
45149
|
+
const cached = pidStartCache.get(pid);
|
|
45150
|
+
if (cached && Date.now() - cached.at < PID_START_CACHE_MS)
|
|
45151
|
+
return cached.startMs;
|
|
45152
|
+
let startMs = null;
|
|
45153
|
+
try {
|
|
45154
|
+
if (process.platform === "linux") {
|
|
45155
|
+
const stat = fs14.readFileSync(`/proc/${pid}/stat`, "utf8");
|
|
45156
|
+
const fields = stat.slice(stat.lastIndexOf(")") + 2).split(" ");
|
|
45157
|
+
const startTicks = Number(fields[19]);
|
|
45158
|
+
const btime = Number(/^btime (\d+)$/m.exec(fs14.readFileSync("/proc/stat", "utf8"))?.[1]);
|
|
45159
|
+
if (Number.isFinite(startTicks) && Number.isFinite(btime))
|
|
45160
|
+
startMs = (btime + startTicks / 100) * 1e3;
|
|
45161
|
+
} else if (process.platform !== "win32") {
|
|
45162
|
+
const out = (0, child_process_1.execFileSync)("ps", ["-o", "lstart=", "-p", String(pid)], { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"], timeout: 2e3 }).trim();
|
|
45163
|
+
const t = Date.parse(out);
|
|
45164
|
+
if (Number.isFinite(t))
|
|
45165
|
+
startMs = t;
|
|
45166
|
+
}
|
|
45167
|
+
} catch {
|
|
45168
|
+
}
|
|
45169
|
+
pidStartCache.set(pid, { startMs, at: Date.now() });
|
|
45170
|
+
return startMs;
|
|
45171
|
+
};
|
|
45172
|
+
var fileBirthMs = (file) => {
|
|
45173
|
+
const st = fs14.statSync(file);
|
|
45174
|
+
return st.birthtimeMs > 0 ? st.birthtimeMs : st.mtimeMs;
|
|
45175
|
+
};
|
|
45176
|
+
var isTicketOwnerDead = (pid, file) => {
|
|
45177
|
+
if (!isPidAlive(pid))
|
|
45178
|
+
return true;
|
|
45179
|
+
if (pid === process.pid)
|
|
45180
|
+
return false;
|
|
45181
|
+
const startMs = pidStartTimeMs(pid);
|
|
45182
|
+
if (startMs === null)
|
|
45183
|
+
return false;
|
|
45184
|
+
try {
|
|
45185
|
+
return startMs - fileBirthMs(file) > PID_REUSE_GRACE_MS;
|
|
45186
|
+
} catch {
|
|
45187
|
+
return false;
|
|
45188
|
+
}
|
|
45189
|
+
};
|
|
45190
|
+
var readTickets = (dir) => {
|
|
45191
|
+
const tickets = [];
|
|
45192
|
+
for (const name of fs14.readdirSync(dir)) {
|
|
45193
|
+
const pid = ticketPid(name);
|
|
45194
|
+
if (pid === null)
|
|
45195
|
+
continue;
|
|
45196
|
+
const file = path15.join(dir, name);
|
|
45197
|
+
if (isTicketOwnerDead(pid, file)) {
|
|
45198
|
+
fs14.rmSync(file, { force: true });
|
|
45199
|
+
continue;
|
|
45200
|
+
}
|
|
45201
|
+
try {
|
|
45202
|
+
const value = fs14.readFileSync(file, "utf8");
|
|
45203
|
+
tickets.push({ name, number: /^[1-9]\d*$/.test(value) ? BigInt(value) : null });
|
|
45204
|
+
} catch (err) {
|
|
45205
|
+
if (err.code !== "ENOENT")
|
|
45206
|
+
throw err;
|
|
45207
|
+
}
|
|
45208
|
+
}
|
|
45209
|
+
return tickets;
|
|
45210
|
+
};
|
|
45211
|
+
var acquireLock = async (timeoutMs, signal) => {
|
|
45212
|
+
if (signal?.aborted)
|
|
45213
|
+
throw new Error("\u767B\u8BB0\u5DF2\u53D6\u6D88");
|
|
45214
|
+
const dir = getLockDir();
|
|
45215
|
+
fs14.mkdirSync(dir, { recursive: true });
|
|
45216
|
+
const ticket = `${process.pid}-${(0, crypto_1.randomUUID)()}.ticket`;
|
|
45217
|
+
const mine = path15.join(dir, ticket);
|
|
45218
|
+
const staging = `${mine}.publishing`;
|
|
45219
|
+
fs14.closeSync(fs14.openSync(mine, "wx"));
|
|
45220
|
+
let acquired = false;
|
|
45221
|
+
const release = () => {
|
|
45222
|
+
fs14.rmSync(mine, { force: true });
|
|
45223
|
+
fs14.rmSync(staging, { force: true });
|
|
45224
|
+
};
|
|
45225
|
+
const started = process.hrtime.bigint();
|
|
45226
|
+
try {
|
|
45227
|
+
const number = readTickets(dir).reduce((max2, t) => t.number !== null && t.number > max2 ? t.number : max2, 0n) + 1n;
|
|
45228
|
+
fs14.writeFileSync(staging, String(number), { flag: "wx" });
|
|
45229
|
+
fs14.renameSync(staging, mine);
|
|
45230
|
+
for (; ; ) {
|
|
45231
|
+
if (signal?.aborted)
|
|
45232
|
+
throw new Error("\u767B\u8BB0\u5DF2\u53D6\u6D88");
|
|
45233
|
+
const tickets = readTickets(dir);
|
|
45234
|
+
if (!tickets.some((t) => t.name === ticket))
|
|
45235
|
+
throw new Error("LLM \u65E5\u5FD7\u9501\u7968\u636E\u4E22\u5931");
|
|
45236
|
+
const blocked = tickets.some((t) => t.name !== ticket && (t.number === null || t.number < number || t.number === number && t.name < ticket));
|
|
45237
|
+
if (!blocked) {
|
|
45238
|
+
acquired = true;
|
|
45239
|
+
return { ticket, release };
|
|
45240
|
+
}
|
|
45241
|
+
if (Number(process.hrtime.bigint() - started) / 1e6 >= timeoutMs)
|
|
45242
|
+
return null;
|
|
45243
|
+
await sleep(LOCK_POLL_MS);
|
|
45244
|
+
}
|
|
45245
|
+
} finally {
|
|
45246
|
+
if (!acquired)
|
|
45247
|
+
release();
|
|
45248
|
+
}
|
|
45249
|
+
};
|
|
45250
|
+
var withLLMLogLock = async (fn, timeoutMs = 5e3, signal) => {
|
|
45251
|
+
const lock = await acquireLock(timeoutMs, signal);
|
|
45252
|
+
if (!lock)
|
|
45253
|
+
return null;
|
|
45254
|
+
try {
|
|
45255
|
+
if (signal?.aborted)
|
|
45256
|
+
throw new Error("\u767B\u8BB0\u5DF2\u53D6\u6D88");
|
|
45257
|
+
return await fn();
|
|
45258
|
+
} finally {
|
|
45259
|
+
lock.release();
|
|
45260
|
+
}
|
|
45261
|
+
};
|
|
45262
|
+
exports2.withLLMLogLock = withLLMLogLock;
|
|
45263
|
+
var holdLLMLogLockForTest = async (timeoutMs = 1e3) => {
|
|
45264
|
+
const lock = await acquireLock(timeoutMs);
|
|
45265
|
+
return lock ? { ticketPath: path15.join(getLockDir(), lock.ticket), release: lock.release } : null;
|
|
45266
|
+
};
|
|
45267
|
+
exports2.holdLLMLogLockForTest = holdLLMLogLockForTest;
|
|
45268
|
+
var ACTIVE_MARK_DIR = ".active";
|
|
45269
|
+
exports2.REGISTER_TIMEOUT_MS = 3e4;
|
|
45270
|
+
var getActiveMarkDir = () => path15.join((0, savePath_1.getLLMLogsDir)(), ACTIVE_MARK_DIR);
|
|
45271
|
+
var encodeSessionId = (sessionId) => encodeURIComponent(sessionId.replace(/[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g, "\uFFFD"));
|
|
45272
|
+
var decodeSessionId = (encoded) => {
|
|
45273
|
+
try {
|
|
45274
|
+
return decodeURIComponent(encoded);
|
|
45275
|
+
} catch {
|
|
45276
|
+
return null;
|
|
45277
|
+
}
|
|
45278
|
+
};
|
|
45279
|
+
var markFilePath = (sessionId) => path15.join(getActiveMarkDir(), `${encodeSessionId(sessionId)}.${process.pid}`);
|
|
45280
|
+
var registrations = /* @__PURE__ */ new Map();
|
|
45281
|
+
var removeMark = (sessionId) => {
|
|
45282
|
+
try {
|
|
45283
|
+
fs14.rmSync(markFilePath(sessionId), { force: true });
|
|
45284
|
+
} catch {
|
|
45285
|
+
}
|
|
45286
|
+
};
|
|
45287
|
+
var acquireActiveLLMLogSession = async (sessionId, opts = {}) => {
|
|
45288
|
+
if (opts.signal?.aborted)
|
|
45289
|
+
throw new Error("\u767B\u8BB0\u5DF2\u53D6\u6D88");
|
|
45290
|
+
let reg = registrations.get(sessionId);
|
|
45291
|
+
if (!reg) {
|
|
45292
|
+
const created = { refs: 0, state: "registering", ready: Promise.resolve(), abort: new AbortController() };
|
|
45293
|
+
created.ready = (async () => {
|
|
45294
|
+
const done = await (0, exports2.withLLMLogLock)(() => {
|
|
45295
|
+
if (created.refs === 0)
|
|
45296
|
+
return "abandoned";
|
|
45297
|
+
fs14.mkdirSync(getActiveMarkDir(), { recursive: true });
|
|
45298
|
+
try {
|
|
45299
|
+
fs14.writeFileSync(markFilePath(sessionId), String(process.pid), "utf8");
|
|
45300
|
+
} catch (err) {
|
|
45301
|
+
removeMark(sessionId);
|
|
45302
|
+
throw err;
|
|
45303
|
+
}
|
|
45304
|
+
created.state = "active";
|
|
45305
|
+
return "written";
|
|
45306
|
+
}, opts.timeoutMs ?? exports2.REGISTER_TIMEOUT_MS, created.abort.signal);
|
|
45307
|
+
if (done === null)
|
|
45308
|
+
throw new Error(`LLM \u65E5\u5FD7\u6E05\u7406\u9501\u7B49\u5F85\u8D85\u65F6(${opts.timeoutMs ?? exports2.REGISTER_TIMEOUT_MS}ms),\u4F1A\u8BDD ${sessionId} \u672A\u80FD\u767B\u8BB0\u4E3A\u6D3B\u8DC3`);
|
|
45309
|
+
if (done === "abandoned")
|
|
45310
|
+
return;
|
|
45311
|
+
})();
|
|
45312
|
+
void created.ready.then(() => {
|
|
45313
|
+
if (created.refs === 0 && registrations.get(sessionId) === created)
|
|
45314
|
+
registrations.delete(sessionId);
|
|
45315
|
+
}, () => {
|
|
45316
|
+
if (registrations.get(sessionId) === created)
|
|
45317
|
+
registrations.delete(sessionId);
|
|
45318
|
+
});
|
|
45319
|
+
registrations.set(sessionId, created);
|
|
45320
|
+
reg = created;
|
|
45321
|
+
}
|
|
45322
|
+
const mine = reg;
|
|
45323
|
+
mine.refs++;
|
|
45324
|
+
let released = false;
|
|
45325
|
+
const release = () => {
|
|
45326
|
+
if (released)
|
|
45327
|
+
return;
|
|
45328
|
+
released = true;
|
|
45329
|
+
mine.refs--;
|
|
45330
|
+
if (mine.refs > 0)
|
|
45331
|
+
return;
|
|
45332
|
+
mine.abort.abort();
|
|
45333
|
+
if (mine.state === "active")
|
|
45334
|
+
removeMark(sessionId);
|
|
45335
|
+
if (registrations.get(sessionId) === mine)
|
|
45336
|
+
registrations.delete(sessionId);
|
|
45337
|
+
};
|
|
45338
|
+
if (opts.signal?.aborted) {
|
|
45339
|
+
release();
|
|
45340
|
+
throw new Error("\u767B\u8BB0\u5DF2\u53D6\u6D88");
|
|
45341
|
+
}
|
|
45342
|
+
let rejectAbort;
|
|
45343
|
+
const aborted = new Promise((_, reject2) => {
|
|
45344
|
+
rejectAbort = reject2;
|
|
45345
|
+
});
|
|
45346
|
+
const onAbort = () => {
|
|
45347
|
+
release();
|
|
45348
|
+
rejectAbort(new Error("\u767B\u8BB0\u5DF2\u53D6\u6D88"));
|
|
45349
|
+
};
|
|
45350
|
+
opts.signal?.addEventListener("abort", onAbort, { once: true });
|
|
45351
|
+
try {
|
|
45352
|
+
await Promise.race([mine.ready, aborted]);
|
|
45353
|
+
} catch (err) {
|
|
45354
|
+
release();
|
|
45355
|
+
throw err;
|
|
45356
|
+
} finally {
|
|
45357
|
+
opts.signal?.removeEventListener("abort", onAbort);
|
|
45358
|
+
}
|
|
45359
|
+
if (released)
|
|
45360
|
+
throw new Error("\u767B\u8BB0\u5DF2\u53D6\u6D88");
|
|
45361
|
+
return { sessionId, release };
|
|
45362
|
+
};
|
|
45363
|
+
exports2.acquireActiveLLMLogSession = acquireActiveLLMLogSession;
|
|
45364
|
+
var getActiveLLMLogSessions = () => [...registrations.entries()].filter(([, r]) => r.state === "active").map(([id]) => id);
|
|
45365
|
+
exports2.getActiveLLMLogSessions = getActiveLLMLogSessions;
|
|
45366
|
+
var readActiveLLMLogSessionsAcrossProcesses = () => {
|
|
45367
|
+
const active = new Set((0, exports2.getActiveLLMLogSessions)());
|
|
45368
|
+
const dir = getActiveMarkDir();
|
|
45369
|
+
let names;
|
|
45370
|
+
try {
|
|
45371
|
+
names = fs14.readdirSync(dir);
|
|
45372
|
+
} catch (err) {
|
|
45373
|
+
if (err.code === "ENOENT")
|
|
45374
|
+
return active;
|
|
45375
|
+
throw err;
|
|
45376
|
+
}
|
|
45377
|
+
for (const name of names) {
|
|
45378
|
+
const dot = name.lastIndexOf(".");
|
|
45379
|
+
if (dot <= 0)
|
|
45380
|
+
continue;
|
|
45381
|
+
const sessionId = decodeSessionId(name.slice(0, dot));
|
|
45382
|
+
const pid = Number(name.slice(dot + 1));
|
|
45383
|
+
if (sessionId === null)
|
|
45384
|
+
continue;
|
|
45385
|
+
if (isPidAlive(pid)) {
|
|
45386
|
+
active.add(sessionId);
|
|
45387
|
+
} else {
|
|
45388
|
+
try {
|
|
45389
|
+
fs14.rmSync(path15.join(dir, name), { force: true });
|
|
45390
|
+
} catch {
|
|
45391
|
+
}
|
|
45392
|
+
}
|
|
45393
|
+
}
|
|
45394
|
+
return active;
|
|
45395
|
+
};
|
|
45396
|
+
exports2.readActiveLLMLogSessionsAcrossProcesses = readActiveLLMLogSessionsAcrossProcesses;
|
|
45397
|
+
var sessionIdOfLogFile = (name) => {
|
|
45398
|
+
const m = /^\d{4}-\d{2}-\d{2}_(.+)\.log$/.exec(name);
|
|
45399
|
+
return m ? m[1] : null;
|
|
45400
|
+
};
|
|
45134
45401
|
var getProjectName = () => {
|
|
45135
45402
|
try {
|
|
45136
45403
|
const coreConfig = (0, ConfManager_1.getConfManager)().getCoreConfig();
|
|
@@ -45165,7 +45432,7 @@ var require_logLLM = __commonJS({
|
|
|
45165
45432
|
if (nowTimestamp - lastLLMCleanupTime > config_1.LLM_LOG_CLEANUP_INTERVAL) {
|
|
45166
45433
|
lastLLMCleanupTime = nowTimestamp;
|
|
45167
45434
|
setImmediate(() => {
|
|
45168
|
-
cleanupLLMLogFiles();
|
|
45435
|
+
void (0, exports2.cleanupLLMLogFiles)();
|
|
45169
45436
|
});
|
|
45170
45437
|
}
|
|
45171
45438
|
} catch (err) {
|
|
@@ -45202,7 +45469,7 @@ var require_logLLM = __commonJS({
|
|
|
45202
45469
|
if (nowTimestamp - lastLLMCleanupTime > config_1.LLM_LOG_CLEANUP_INTERVAL) {
|
|
45203
45470
|
lastLLMCleanupTime = nowTimestamp;
|
|
45204
45471
|
setImmediate(() => {
|
|
45205
|
-
cleanupLLMLogFiles();
|
|
45472
|
+
void (0, exports2.cleanupLLMLogFiles)();
|
|
45206
45473
|
});
|
|
45207
45474
|
}
|
|
45208
45475
|
} catch (err) {
|
|
@@ -45320,25 +45587,35 @@ var require_logLLM = __commonJS({
|
|
|
45320
45587
|
} catch (err) {
|
|
45321
45588
|
}
|
|
45322
45589
|
};
|
|
45323
|
-
var cleanupLLMLogFiles = () => {
|
|
45590
|
+
var cleanupLLMLogFiles = async () => {
|
|
45324
45591
|
try {
|
|
45325
45592
|
const llmLogsDir = (0, savePath_1.getLLMLogsDir)();
|
|
45326
45593
|
if (!fs14.existsSync(llmLogsDir)) {
|
|
45327
45594
|
return;
|
|
45328
45595
|
}
|
|
45329
|
-
const
|
|
45330
|
-
|
|
45331
|
-
|
|
45332
|
-
mtime: fs14.statSync(path15.join(llmLogsDir, file)).mtime
|
|
45333
|
-
})).sort((a, b) => b.mtime.getTime() - a.mtime.getTime());
|
|
45334
|
-
if (files.length > config_1.LLM_LOG_FILES_RETAIN_COUNT) {
|
|
45335
|
-
const filesToArchive = files.slice(config_1.LLM_LOG_FILES_RETAIN_COUNT);
|
|
45336
|
-
archiveLLMLogFiles(filesToArchive);
|
|
45337
|
-
}
|
|
45596
|
+
const done = await (0, exports2.withLLMLogLock)(() => cleanupLLMLogFilesLocked(llmLogsDir));
|
|
45597
|
+
if (done === null)
|
|
45598
|
+
(0, log_1.logError)("LLM \u65E5\u5FD7\u6E05\u7406\u9501\u7B49\u5F85\u8D85\u65F6,\u672C\u8F6E\u8DF3\u8FC7");
|
|
45338
45599
|
} catch (err) {
|
|
45339
45600
|
(0, log_1.logError)(`\u6E05\u7406LLM\u65E5\u5FD7\u6587\u4EF6\u51FA\u9519: ${err}`);
|
|
45340
45601
|
}
|
|
45341
45602
|
};
|
|
45603
|
+
exports2.cleanupLLMLogFiles = cleanupLLMLogFiles;
|
|
45604
|
+
var cleanupLLMLogFilesLocked = (llmLogsDir) => {
|
|
45605
|
+
const active = (0, exports2.readActiveLLMLogSessionsAcrossProcesses)();
|
|
45606
|
+
const files = fs14.readdirSync(llmLogsDir).filter((file) => file.endsWith(".log")).filter((file) => {
|
|
45607
|
+
const sid = sessionIdOfLogFile(file);
|
|
45608
|
+
return sid === null || !active.has(sid);
|
|
45609
|
+
}).map((file) => ({
|
|
45610
|
+
name: file,
|
|
45611
|
+
path: path15.join(llmLogsDir, file),
|
|
45612
|
+
mtime: fs14.statSync(path15.join(llmLogsDir, file)).mtime
|
|
45613
|
+
})).sort((a, b) => b.mtime.getTime() - a.mtime.getTime());
|
|
45614
|
+
if (files.length > config_1.LLM_LOG_FILES_RETAIN_COUNT) {
|
|
45615
|
+
const filesToArchive = files.slice(config_1.LLM_LOG_FILES_RETAIN_COUNT);
|
|
45616
|
+
archiveLLMLogFiles(filesToArchive);
|
|
45617
|
+
}
|
|
45618
|
+
};
|
|
45342
45619
|
var getEventLogFilePath = () => {
|
|
45343
45620
|
const dateStr = (0, time_1.getDayTimeString)();
|
|
45344
45621
|
const eventDir = (0, savePath_1.getEventDir)();
|
|
@@ -46014,9 +46291,13 @@ var require_StateManager = __commonJS({
|
|
|
46014
46291
|
}
|
|
46015
46292
|
/**
|
|
46016
46293
|
* 清空所有状态数据
|
|
46294
|
+
* @param options.keepAgentStates 保留各代理的 processing/idle 状态记录(/clear 在处理输入的过程中调用:
|
|
46295
|
+
* 主代理此刻是 processing,若连状态一起清掉,重建出来的记录默认 idle,随后的 updateState('idle')
|
|
46296
|
+
* 因"状态未变"不再发 state:update,等 idle 的消费方(session 库 send、TUI 处理态)会挂住)
|
|
46017
46297
|
*/
|
|
46018
|
-
clearAllState() {
|
|
46019
|
-
|
|
46298
|
+
clearAllState(options2 = {}) {
|
|
46299
|
+
if (!options2.keepAgentStates)
|
|
46300
|
+
this.statesMap.clear();
|
|
46020
46301
|
this.messageHistoryMap.clear();
|
|
46021
46302
|
this.readFileTimestampsMap.clear();
|
|
46022
46303
|
this.todosMap.clear();
|
|
@@ -50795,12 +51076,12 @@ var require_frontmatter = __commonJS({
|
|
|
50795
51076
|
function parseFrontmatter(text) {
|
|
50796
51077
|
const metadata = {};
|
|
50797
51078
|
const length = text.length;
|
|
50798
|
-
let
|
|
51079
|
+
let lineStart2 = 0;
|
|
50799
51080
|
for (let i = 0; i <= length; i++) {
|
|
50800
51081
|
if (i === length || text[i] === "\n") {
|
|
50801
|
-
if (i >
|
|
50802
|
-
const
|
|
50803
|
-
const line = text.slice(
|
|
51082
|
+
if (i > lineStart2) {
|
|
51083
|
+
const lineEnd2 = i > 0 && text[i - 1] === "\r" ? i - 1 : i;
|
|
51084
|
+
const line = text.slice(lineStart2, lineEnd2);
|
|
50804
51085
|
if (line && line[0] !== "#") {
|
|
50805
51086
|
const colonIndex = line.indexOf(":");
|
|
50806
51087
|
if (colonIndex !== -1) {
|
|
@@ -50835,7 +51116,7 @@ var require_frontmatter = __commonJS({
|
|
|
50835
51116
|
}
|
|
50836
51117
|
}
|
|
50837
51118
|
}
|
|
50838
|
-
|
|
51119
|
+
lineStart2 = i + 1;
|
|
50839
51120
|
}
|
|
50840
51121
|
}
|
|
50841
51122
|
return metadata;
|
|
@@ -90817,7 +91098,7 @@ var require_lib3 = __commonJS({
|
|
|
90817
91098
|
} catch (e) {
|
|
90818
91099
|
}
|
|
90819
91100
|
var INTERNALS = /* @__PURE__ */ Symbol("Body internals");
|
|
90820
|
-
var
|
|
91101
|
+
var PassThrough3 = Stream2.PassThrough;
|
|
90821
91102
|
function Body(body) {
|
|
90822
91103
|
var _this = this;
|
|
90823
91104
|
var _ref = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : {}, _ref$size = _ref.size;
|
|
@@ -91069,8 +91350,8 @@ var require_lib3 = __commonJS({
|
|
|
91069
91350
|
throw new Error("cannot clone body after it is used");
|
|
91070
91351
|
}
|
|
91071
91352
|
if (body instanceof Stream2 && typeof body.getBoundary !== "function") {
|
|
91072
|
-
p1 = new
|
|
91073
|
-
p2 = new
|
|
91353
|
+
p1 = new PassThrough3();
|
|
91354
|
+
p2 = new PassThrough3();
|
|
91074
91355
|
body.pipe(p1);
|
|
91075
91356
|
body.pipe(p2);
|
|
91076
91357
|
instance[INTERNALS].body = p1;
|
|
@@ -132070,10 +132351,10 @@ ${value}`, dataLines++;
|
|
|
132070
132351
|
for (; searchIndex < chunk2.length; ) {
|
|
132071
132352
|
const crIndex = chunk2.indexOf("\r", searchIndex), lfIndex = chunk2.indexOf(`
|
|
132072
132353
|
`, searchIndex);
|
|
132073
|
-
let
|
|
132074
|
-
if (crIndex !== -1 && lfIndex !== -1 ?
|
|
132354
|
+
let lineEnd2 = -1;
|
|
132355
|
+
if (crIndex !== -1 && lfIndex !== -1 ? lineEnd2 = crIndex < lfIndex ? crIndex : lfIndex : crIndex !== -1 ? crIndex === chunk2.length - 1 ? lineEnd2 = -1 : lineEnd2 = crIndex : lfIndex !== -1 && (lineEnd2 = lfIndex), lineEnd2 === -1)
|
|
132075
132356
|
break;
|
|
132076
|
-
parseLine(chunk2, searchIndex,
|
|
132357
|
+
parseLine(chunk2, searchIndex, lineEnd2), searchIndex = lineEnd2 + 1, chunk2.charCodeAt(searchIndex - 1) === CR && chunk2.charCodeAt(searchIndex) === LF && searchIndex++;
|
|
132077
132358
|
}
|
|
132078
132359
|
return chunk2.slice(searchIndex);
|
|
132079
132360
|
}
|
|
@@ -134538,7 +134819,10 @@ var require_mcpShared = __commonJS({
|
|
|
134538
134819
|
})();
|
|
134539
134820
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
134540
134821
|
exports2.mergeServerConfigs = mergeServerConfigs;
|
|
134822
|
+
exports2.originalToolName = originalToolName;
|
|
134541
134823
|
exports2.filterToolsByConfig = filterToolsByConfig;
|
|
134824
|
+
exports2.isServerScopedOut = isServerScopedOut;
|
|
134825
|
+
exports2.applyScopeToTools = applyScopeToTools;
|
|
134542
134826
|
exports2.configHash = configHash;
|
|
134543
134827
|
exports2.isValidServerConfig = isValidServerConfig;
|
|
134544
134828
|
exports2.loadConfigFile = loadConfigFile;
|
|
@@ -134563,15 +134847,37 @@ var require_mcpShared = __commonJS({
|
|
|
134563
134847
|
}
|
|
134564
134848
|
return merged;
|
|
134565
134849
|
}
|
|
134566
|
-
function
|
|
134850
|
+
function originalToolName(fullName, serverName) {
|
|
134851
|
+
if (serverName) {
|
|
134852
|
+
const prefix = `mcp__${serverName}__`;
|
|
134853
|
+
if (fullName.startsWith(prefix))
|
|
134854
|
+
return fullName.slice(prefix.length);
|
|
134855
|
+
}
|
|
134856
|
+
const parts = fullName.split("__");
|
|
134857
|
+
return parts.length >= 3 ? parts.slice(2).join("__") : fullName;
|
|
134858
|
+
}
|
|
134859
|
+
function filterToolsByConfig(tools, useTools, serverName) {
|
|
134567
134860
|
if (!useTools) {
|
|
134568
134861
|
return tools;
|
|
134569
134862
|
}
|
|
134570
|
-
return tools.filter((tool) =>
|
|
134571
|
-
|
|
134572
|
-
|
|
134573
|
-
|
|
134574
|
-
|
|
134863
|
+
return tools.filter((tool) => useTools.includes(originalToolName(tool.name, serverName)));
|
|
134864
|
+
}
|
|
134865
|
+
function isServerScopedOut(serverName, scope) {
|
|
134866
|
+
if (!scope)
|
|
134867
|
+
return false;
|
|
134868
|
+
if (scope.servers !== null && !scope.servers.includes(serverName))
|
|
134869
|
+
return true;
|
|
134870
|
+
return scope.disabledServers?.includes(serverName) ?? false;
|
|
134871
|
+
}
|
|
134872
|
+
function applyScopeToTools(serverName, tools, scope) {
|
|
134873
|
+
if (!scope)
|
|
134874
|
+
return tools;
|
|
134875
|
+
if (isServerScopedOut(serverName, scope))
|
|
134876
|
+
return [];
|
|
134877
|
+
const allow = scope.tools?.[serverName];
|
|
134878
|
+
if (!allow)
|
|
134879
|
+
return tools;
|
|
134880
|
+
return filterToolsByConfig(tools, allow, serverName);
|
|
134575
134881
|
}
|
|
134576
134882
|
function configHash(config) {
|
|
134577
134883
|
const identity2 = {
|
|
@@ -134736,6 +135042,7 @@ var require_MCPManager = __commonJS({
|
|
|
134736
135042
|
this.clients = /* @__PURE__ */ new Map();
|
|
134737
135043
|
this.connectingPromises = /* @__PURE__ */ new Map();
|
|
134738
135044
|
this.serverInfoCache = null;
|
|
135045
|
+
this.scope = null;
|
|
134739
135046
|
this.globalConfigPath = globalConfigPath;
|
|
134740
135047
|
this.projectConfigPath = projectConfigPath;
|
|
134741
135048
|
}
|
|
@@ -134872,7 +135179,7 @@ var require_MCPManager = __commonJS({
|
|
|
134872
135179
|
const mergedConfigs = this.getMergedConfigs();
|
|
134873
135180
|
for (const [serverName, tools] of this.toolsCache.toolsByServer) {
|
|
134874
135181
|
const config = mergedConfigs.get(serverName);
|
|
134875
|
-
const filteredTools = this.filterToolsByConfig(tools, config?.useTools);
|
|
135182
|
+
const filteredTools = this.filterToolsByConfig(tools, config?.useTools, serverName);
|
|
134876
135183
|
toolStats[serverName] = filteredTools.length;
|
|
134877
135184
|
}
|
|
134878
135185
|
(0, log_1.logInfo)(`MCP \u5DE5\u5177\u7EDF\u8BA1 (\u8FC7\u6EE4\u540E): ${JSON.stringify(toolStats)}`);
|
|
@@ -134969,8 +135276,8 @@ var require_MCPManager = __commonJS({
|
|
|
134969
135276
|
/**
|
|
134970
135277
|
* 根据 useTools 配置过滤工具
|
|
134971
135278
|
*/
|
|
134972
|
-
filterToolsByConfig(tools, useTools) {
|
|
134973
|
-
return (0, mcpShared_1.filterToolsByConfig)(tools, useTools);
|
|
135279
|
+
filterToolsByConfig(tools, useTools, serverName) {
|
|
135280
|
+
return (0, mcpShared_1.filterToolsByConfig)(tools, useTools, serverName);
|
|
134974
135281
|
}
|
|
134975
135282
|
/**
|
|
134976
135283
|
* 获取所有 MCP 工具(同步,返回缓存)
|
|
@@ -134983,11 +135290,19 @@ var require_MCPManager = __commonJS({
|
|
|
134983
135290
|
const mergedConfigs = this.getMergedConfigs();
|
|
134984
135291
|
for (const [serverName, tools] of this.toolsCache.toolsByServer) {
|
|
134985
135292
|
const config = mergedConfigs.get(serverName);
|
|
134986
|
-
const filteredTools = this.filterToolsByConfig(tools, config?.useTools);
|
|
134987
|
-
result2.push(...filteredTools);
|
|
135293
|
+
const filteredTools = this.filterToolsByConfig(tools, config?.useTools, serverName);
|
|
135294
|
+
result2.push(...(0, mcpShared_1.applyScopeToTools)(serverName, filteredTools, this.scope));
|
|
134988
135295
|
}
|
|
134989
135296
|
return result2;
|
|
134990
135297
|
}
|
|
135298
|
+
/**
|
|
135299
|
+
* 设置 / 清除(null)会话级 MCP 可见性 overlay(harness `mcp` 段)。
|
|
135300
|
+
* 不写 mcp.json、不断连接;ServerInfo 缓存里带 scopedOut,故一并失效。
|
|
135301
|
+
*/
|
|
135302
|
+
setScope(scope) {
|
|
135303
|
+
this.scope = scope;
|
|
135304
|
+
this.serverInfoCache = null;
|
|
135305
|
+
}
|
|
134991
135306
|
/**
|
|
134992
135307
|
* 获取或创建客户端连接
|
|
134993
135308
|
*/
|
|
@@ -135067,7 +135382,8 @@ var require_MCPManager = __commonJS({
|
|
|
135067
135382
|
status: client?.status ?? "disconnected",
|
|
135068
135383
|
capabilities: client?.capabilities ?? void 0,
|
|
135069
135384
|
error: void 0,
|
|
135070
|
-
connectedAt: void 0
|
|
135385
|
+
connectedAt: void 0,
|
|
135386
|
+
scopedOut: config.enabled !== false && (0, mcpShared_1.isServerScopedOut)(name, this.scope)
|
|
135071
135387
|
};
|
|
135072
135388
|
}
|
|
135073
135389
|
/**
|
|
@@ -135872,7 +136188,7 @@ var require_session = __commonJS({
|
|
|
135872
136188
|
}
|
|
135873
136189
|
}
|
|
135874
136190
|
function generateSessionId() {
|
|
135875
|
-
return crypto3.randomUUID()
|
|
136191
|
+
return crypto3.randomUUID();
|
|
135876
136192
|
}
|
|
135877
136193
|
function initializeSessionId(historyPath) {
|
|
135878
136194
|
if (historyPath) {
|
|
@@ -136235,10 +136551,10 @@ var require_runCommand = __commonJS({
|
|
|
136235
136551
|
var compact_1 = require_compact();
|
|
136236
136552
|
var errors_1 = require_errors2();
|
|
136237
136553
|
var customCommands_1 = require_customCommands();
|
|
136238
|
-
async function handleSystemCommand(input) {
|
|
136554
|
+
async function handleSystemCommand(input, handlers) {
|
|
136239
136555
|
switch (input) {
|
|
136240
136556
|
case "/clear":
|
|
136241
|
-
await
|
|
136557
|
+
await handlers.clear();
|
|
136242
136558
|
return true;
|
|
136243
136559
|
case "/compact":
|
|
136244
136560
|
await handleCompactCommand();
|
|
@@ -136279,17 +136595,6 @@ var require_runCommand = __commonJS({
|
|
|
136279
136595
|
return { processedInput: input, handled: false };
|
|
136280
136596
|
}
|
|
136281
136597
|
}
|
|
136282
|
-
async function handleClearCommand() {
|
|
136283
|
-
(0, log_1.logInfo)("\u6267\u884C\u6E05\u7A7A\u547D\u4EE4...");
|
|
136284
|
-
const eventBus = EventSystem_1.EventBus.getInstance();
|
|
136285
|
-
const stateManager = (0, StateManager_1.getStateManager)();
|
|
136286
|
-
stateManager.setMessageHistory([]);
|
|
136287
|
-
stateManager.updateState("idle");
|
|
136288
|
-
eventBus.emit("session:cleared", {
|
|
136289
|
-
sessionId: stateManager.getSessionId()
|
|
136290
|
-
});
|
|
136291
|
-
stateManager.clearAllState();
|
|
136292
|
-
}
|
|
136293
136598
|
async function handleCompactCommand() {
|
|
136294
136599
|
(0, log_1.logInfo)("\u6267\u884C\u538B\u7F29\u547D\u4EE4...");
|
|
136295
136600
|
const eventBus = EventSystem_1.EventBus.getInstance();
|
|
@@ -136434,12 +136739,53 @@ var require_HookManager = __commonJS({
|
|
|
136434
136739
|
var require_SemaEngine = __commonJS({
|
|
136435
136740
|
"../atomix-core/dist/core/SemaEngine.js"(exports2) {
|
|
136436
136741
|
"use strict";
|
|
136742
|
+
var __createBinding = exports2 && exports2.__createBinding || (Object.create ? (function(o, m, k, k2) {
|
|
136743
|
+
if (k2 === void 0) k2 = k;
|
|
136744
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
136745
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
136746
|
+
desc = { enumerable: true, get: function() {
|
|
136747
|
+
return m[k];
|
|
136748
|
+
} };
|
|
136749
|
+
}
|
|
136750
|
+
Object.defineProperty(o, k2, desc);
|
|
136751
|
+
}) : (function(o, m, k, k2) {
|
|
136752
|
+
if (k2 === void 0) k2 = k;
|
|
136753
|
+
o[k2] = m[k];
|
|
136754
|
+
}));
|
|
136755
|
+
var __setModuleDefault = exports2 && exports2.__setModuleDefault || (Object.create ? (function(o, v) {
|
|
136756
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
136757
|
+
}) : function(o, v) {
|
|
136758
|
+
o["default"] = v;
|
|
136759
|
+
});
|
|
136760
|
+
var __importStar = exports2 && exports2.__importStar || /* @__PURE__ */ (function() {
|
|
136761
|
+
var ownKeys = function(o) {
|
|
136762
|
+
ownKeys = Object.getOwnPropertyNames || function(o2) {
|
|
136763
|
+
var ar = [];
|
|
136764
|
+
for (var k in o2) if (Object.prototype.hasOwnProperty.call(o2, k)) ar[ar.length] = k;
|
|
136765
|
+
return ar;
|
|
136766
|
+
};
|
|
136767
|
+
return ownKeys(o);
|
|
136768
|
+
};
|
|
136769
|
+
return function(mod) {
|
|
136770
|
+
if (mod && mod.__esModule) return mod;
|
|
136771
|
+
var result2 = {};
|
|
136772
|
+
if (mod != null) {
|
|
136773
|
+
for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result2, mod, k[i]);
|
|
136774
|
+
}
|
|
136775
|
+
__setModuleDefault(result2, mod);
|
|
136776
|
+
return result2;
|
|
136777
|
+
};
|
|
136778
|
+
})();
|
|
136437
136779
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
136438
136780
|
exports2.SemaEngine = void 0;
|
|
136439
136781
|
var log_1 = require_log();
|
|
136782
|
+
var logLLM_1 = require_logLLM();
|
|
136783
|
+
var LLM_LOG_STRICT = process.env.ATOMIX_LLM_LOG_STRICT === "1";
|
|
136440
136784
|
var session_1 = require_session();
|
|
136441
136785
|
var tokens_1 = require_tokens();
|
|
136442
136786
|
var history_1 = require_history();
|
|
136787
|
+
var savePath_1 = require_savePath();
|
|
136788
|
+
var fs14 = __importStar(__require("fs"));
|
|
136443
136789
|
var topic_1 = require_topic();
|
|
136444
136790
|
var fileReference_1 = require_fileReference();
|
|
136445
136791
|
var message_1 = require_message2();
|
|
@@ -136466,6 +136812,9 @@ var require_SemaEngine = __commonJS({
|
|
|
136466
136812
|
var SemaEngine = class {
|
|
136467
136813
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
136468
136814
|
constructor(instanceId, config, mcpManager, kernel) {
|
|
136815
|
+
this.turnGeneration = 0;
|
|
136816
|
+
this.llmLogHandle = null;
|
|
136817
|
+
this.llmLogAbort = new AbortController();
|
|
136469
136818
|
this.emit = (event, data) => {
|
|
136470
136819
|
const store = this.getEngineStore();
|
|
136471
136820
|
EngineContext_1.engineStorage.run(store, () => {
|
|
@@ -136603,6 +136952,7 @@ var require_SemaEngine = __commonJS({
|
|
|
136603
136952
|
*/
|
|
136604
136953
|
processUserInput(input, originalInput) {
|
|
136605
136954
|
(0, EngineContext_1.runWithEngine)(this.getEngineStore(), () => {
|
|
136955
|
+
const turnGeneration = ++this.turnGeneration;
|
|
136606
136956
|
const stateManager = (0, StateManager_1.getStateManager)();
|
|
136607
136957
|
const mainAgentState = stateManager.forAgent(StateManager_1.MAIN_AGENT_ID);
|
|
136608
136958
|
mainAgentState.updateState("processing");
|
|
@@ -136635,7 +136985,7 @@ var require_SemaEngine = __commonJS({
|
|
|
136635
136985
|
tools,
|
|
136636
136986
|
model: "main"
|
|
136637
136987
|
};
|
|
136638
|
-
return this.processQuery(textInput, originalInput, agentContext, blocks);
|
|
136988
|
+
return this.processQuery(textInput, originalInput, agentContext, turnGeneration, blocks);
|
|
136639
136989
|
}).catch((err) => (0, log_1.logInfo)(`[${this.instanceId}] processUserInput \u9876\u5C42\u9519\u8BEF: ${err}`));
|
|
136640
136990
|
}
|
|
136641
136991
|
/**
|
|
@@ -136643,7 +136993,7 @@ var require_SemaEngine = __commonJS({
|
|
|
136643
136993
|
*
|
|
136644
136994
|
* extraBlocks: blocks 路径下额外的非文本内容(如 image blocks),追加到当前 turn 的 user message。
|
|
136645
136995
|
*/
|
|
136646
|
-
async processQuery(input, originalInput, agentContext, extraBlocks) {
|
|
136996
|
+
async processQuery(input, originalInput, agentContext, turnGeneration, extraBlocks) {
|
|
136647
136997
|
const turnStartedAt = Date.now();
|
|
136648
136998
|
const stateManager = (0, StateManager_1.getStateManager)();
|
|
136649
136999
|
const mainAgentState = stateManager.forAgent(StateManager_1.MAIN_AGENT_ID);
|
|
@@ -136679,7 +137029,7 @@ var require_SemaEngine = __commonJS({
|
|
|
136679
137029
|
}
|
|
136680
137030
|
try {
|
|
136681
137031
|
(0, ConfManager_1.getConfManager)().saveUserInputToHistory(originalInput || input);
|
|
136682
|
-
const isSystemCommand = await (0, runCommand_1.handleSystemCommand)(input);
|
|
137032
|
+
const isSystemCommand = await (0, runCommand_1.handleSystemCommand)(input, { clear: () => this.clearSessionInternal(agentContext.abortController?.signal) });
|
|
136683
137033
|
if (isSystemCommand) {
|
|
136684
137034
|
return;
|
|
136685
137035
|
}
|
|
@@ -136715,23 +137065,25 @@ var require_SemaEngine = __commonJS({
|
|
|
136715
137065
|
(0, log_1.logDebug)("\u7528\u6237\u4E2D\u65AD\u64CD\u4F5C");
|
|
136716
137066
|
}
|
|
136717
137067
|
} finally {
|
|
136718
|
-
|
|
136719
|
-
|
|
136720
|
-
|
|
136721
|
-
|
|
136722
|
-
|
|
136723
|
-
|
|
136724
|
-
|
|
136725
|
-
|
|
136726
|
-
|
|
136727
|
-
|
|
136728
|
-
|
|
136729
|
-
|
|
136730
|
-
|
|
136731
|
-
|
|
136732
|
-
|
|
136733
|
-
|
|
136734
|
-
|
|
137068
|
+
if (turnGeneration === this.turnGeneration) {
|
|
137069
|
+
stateManager.currentAbortController = null;
|
|
137070
|
+
if (this.myHookManager?.hasHooksForEvent("Stop")) {
|
|
137071
|
+
const stopHookInput = {
|
|
137072
|
+
hook_event_name: "Stop",
|
|
137073
|
+
session_id: stateManager.getSessionId() || "",
|
|
137074
|
+
agent_id: StateManager_1.MAIN_AGENT_ID,
|
|
137075
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
137076
|
+
cwd: this.initialConfig.workingDir || (0, cwd_1.getCwd)(),
|
|
137077
|
+
turn_duration_ms: Date.now() - turnStartedAt
|
|
137078
|
+
};
|
|
137079
|
+
await (0, executeHooks_1.executeHooks)(this.myHookManager, "Stop", stopHookInput, {
|
|
137080
|
+
env: this.initialConfig.hookEnv,
|
|
137081
|
+
messages: mainAgentState.getMessageHistory()
|
|
137082
|
+
});
|
|
137083
|
+
}
|
|
137084
|
+
if (turnGeneration === this.turnGeneration && stateManager.getCurrentState(StateManager_1.MAIN_AGENT_ID) !== "paused") {
|
|
137085
|
+
mainAgentState.updateState("idle");
|
|
137086
|
+
}
|
|
136735
137087
|
}
|
|
136736
137088
|
}
|
|
136737
137089
|
}
|
|
@@ -136762,6 +137114,7 @@ var require_SemaEngine = __commonJS({
|
|
|
136762
137114
|
* 不更新状态,用于内部调用
|
|
136763
137115
|
*/
|
|
136764
137116
|
abortCurrentRequest() {
|
|
137117
|
+
++this.turnGeneration;
|
|
136765
137118
|
const abortController = this.myStateManager.currentAbortController;
|
|
136766
137119
|
if (abortController && !abortController.signal.aborted) {
|
|
136767
137120
|
(0, log_1.logInfo)("\u901A\u8FC7 AbortController \u53D1\u9001\u4E2D\u65AD\u4FE1\u53F7");
|
|
@@ -136835,6 +137188,10 @@ var require_SemaEngine = __commonJS({
|
|
|
136835
137188
|
cfg.temperatureOverrides = partial2.temperatureOverrides ?? null;
|
|
136836
137189
|
if ("thinking" in partial2)
|
|
136837
137190
|
cfg.thinking = partial2.thinking === true;
|
|
137191
|
+
if ("mcpScope" in partial2) {
|
|
137192
|
+
cfg.mcpScope = partial2.mcpScope ?? null;
|
|
137193
|
+
this.myMCPManager.setScope(cfg.mcpScope);
|
|
137194
|
+
}
|
|
136838
137195
|
}
|
|
136839
137196
|
/**
|
|
136840
137197
|
* 当前 session 的 coreConfig 只读快照(= initialConfig,含运行时 mutation 结果)。
|
|
@@ -136889,12 +137246,130 @@ var require_SemaEngine = __commonJS({
|
|
|
136889
137246
|
}
|
|
136890
137247
|
}
|
|
136891
137248
|
// 初始化系统
|
|
137249
|
+
/**
|
|
137250
|
+
* /clear:清空上下文并换新 sessionId(events/types.ts SessionClearedData 注释有语义说明)。
|
|
137251
|
+
* 三段式"准备 → 检查 → 同步提交":
|
|
137252
|
+
* 1. 准备:生成候选 id(完整 UUID),先做占用检查(当前 id / 项目历史文件 / 宿主回调 isSessionIdTaken),
|
|
137253
|
+
* 再为候选 id 登记 LLM 日志句柄(唯一可能失败、可能 await 的步骤)。此时旧 id、旧消息、旧句柄一概不动。
|
|
137254
|
+
* 2. 检查:await 回来后确认本轮未被中断、引擎未 dispose,并再查一次占用(等待期间宿主可能打开了同 id);
|
|
137255
|
+
* 冲突则释放候选句柄重新生成(有重试上限)。
|
|
137256
|
+
* 3. 提交:换 id → 清消息 / todos / 状态 → 装新句柄 → 发 session:cleared,中间不再 await——宿主(serve)在
|
|
137257
|
+
* 事件回调里同步 rekey,与最后一次检查处于同一同步段,其他 open 插不进来。随后释放旧句柄、进入 idle。
|
|
137258
|
+
* 准备失败或被取消:释放候选句柄,旧会话原样保留。严格模式(ATOMIX_LLM_LOG_STRICT=1)下登记失败发
|
|
137259
|
+
* session:error(session 库的 send 据此返回错误);缺省模式沿用"告警后无日志保护继续"。
|
|
137260
|
+
*/
|
|
137261
|
+
async clearSessionInternal(turnSignal) {
|
|
137262
|
+
(0, log_1.logInfo)("\u6267\u884C\u6E05\u7A7A\u547D\u4EE4...");
|
|
137263
|
+
const stateManager = (0, StateManager_1.getStateManager)();
|
|
137264
|
+
const mainAgentState = stateManager.forAgent(StateManager_1.MAIN_AGENT_ID);
|
|
137265
|
+
const previousSessionId = stateManager.getSessionId();
|
|
137266
|
+
const workingDir = (0, ConfManager_1.getConfManager)().getCoreConfig()?.workingDir;
|
|
137267
|
+
const cancelled = () => !!turnSignal?.aborted || this.llmLogAbort.signal.aborted;
|
|
137268
|
+
const fail = (message) => {
|
|
137269
|
+
(0, log_1.logWarn)(`\u6E05\u7A7A\u5931\u8D25,\u4FDD\u7559\u539F\u4F1A\u8BDD ${previousSessionId}:${message}`);
|
|
137270
|
+
this.emit("session:error", { type: "clear_failed", error: { code: "CLEAR_FAILED", message } });
|
|
137271
|
+
mainAgentState.updateState("idle");
|
|
137272
|
+
};
|
|
137273
|
+
if (cancelled())
|
|
137274
|
+
return;
|
|
137275
|
+
const MAX_TRIES = 8;
|
|
137276
|
+
let newSessionId = null;
|
|
137277
|
+
let newHandle = null;
|
|
137278
|
+
for (let attempt2 = 1; attempt2 <= MAX_TRIES && newSessionId === null; attempt2++) {
|
|
137279
|
+
const candidate = (0, session_1.initializeSessionId)();
|
|
137280
|
+
if (this.isSessionIdTaken(candidate, previousSessionId, workingDir))
|
|
137281
|
+
continue;
|
|
137282
|
+
let handle;
|
|
137283
|
+
try {
|
|
137284
|
+
handle = await this.acquireLlmLogHandle(candidate);
|
|
137285
|
+
} catch (err) {
|
|
137286
|
+
if (cancelled())
|
|
137287
|
+
return;
|
|
137288
|
+
return fail(`LLM \u65E5\u5FD7\u6D3B\u8DC3\u767B\u8BB0\u5931\u8D25:${err instanceof Error ? err.message : String(err)}`);
|
|
137289
|
+
}
|
|
137290
|
+
if (cancelled()) {
|
|
137291
|
+
handle?.release();
|
|
137292
|
+
(0, log_1.logInfo)("\u6E05\u7A7A\u5728\u7B49\u5F85\u65E5\u5FD7\u767B\u8BB0\u671F\u95F4\u88AB\u53D6\u6D88,\u4FDD\u7559\u539F\u4F1A\u8BDD");
|
|
137293
|
+
return;
|
|
137294
|
+
}
|
|
137295
|
+
if (this.isSessionIdTaken(candidate, previousSessionId, workingDir)) {
|
|
137296
|
+
handle?.release();
|
|
137297
|
+
(0, log_1.logWarn)(`\u5019\u9009 sessionId ${candidate} \u5728\u767B\u8BB0\u671F\u95F4\u88AB\u5360\u7528,\u91CD\u65B0\u751F\u6210(\u7B2C ${attempt2} \u6B21)`);
|
|
137298
|
+
continue;
|
|
137299
|
+
}
|
|
137300
|
+
newSessionId = candidate;
|
|
137301
|
+
newHandle = handle;
|
|
137302
|
+
}
|
|
137303
|
+
if (newSessionId === null)
|
|
137304
|
+
return fail(`\u8FDE\u7EED ${MAX_TRIES} \u6B21\u751F\u6210\u7684 sessionId \u5747\u88AB\u5360\u7528`);
|
|
137305
|
+
const oldHandle = this.llmLogHandle;
|
|
137306
|
+
stateManager.setSessionId(newSessionId);
|
|
137307
|
+
stateManager.clearAllState({ keepAgentStates: true });
|
|
137308
|
+
mainAgentState.setMessageHistory([]);
|
|
137309
|
+
mainAgentState.setTodos([]);
|
|
137310
|
+
this.llmLogHandle = newHandle;
|
|
137311
|
+
(0, log_1.logInfo)(`\u4E0A\u4E0B\u6587\u5DF2\u6E05\u7A7A\uFF0C\u65B0\u4F1A\u8BDD ${newSessionId}\uFF08\u539F\u4F1A\u8BDD ${previousSessionId}\uFF09`);
|
|
137312
|
+
this.emit("session:cleared", {
|
|
137313
|
+
sessionId: newSessionId,
|
|
137314
|
+
previousSessionId,
|
|
137315
|
+
usage: (0, tokens_1.getTokens)([])
|
|
137316
|
+
});
|
|
137317
|
+
oldHandle?.release();
|
|
137318
|
+
mainAgentState.updateState("idle");
|
|
137319
|
+
}
|
|
137320
|
+
/** 候选 sessionId 是否已被占用:当前 id / 本项目已有历史文件 / 宿主回调(serve 进程内会话表等) */
|
|
137321
|
+
isSessionIdTaken(candidate, currentSessionId, workingDir) {
|
|
137322
|
+
if (candidate === currentSessionId)
|
|
137323
|
+
return true;
|
|
137324
|
+
try {
|
|
137325
|
+
if (fs14.existsSync((0, savePath_1.getHistoryFilePath)(candidate, workingDir)))
|
|
137326
|
+
return true;
|
|
137327
|
+
} catch {
|
|
137328
|
+
}
|
|
137329
|
+
try {
|
|
137330
|
+
return this.initialConfig.isSessionIdTaken?.(candidate) === true;
|
|
137331
|
+
} catch (err) {
|
|
137332
|
+
(0, log_1.logWarn)(`\u5BBF\u4E3B isSessionIdTaken \u56DE\u8C03\u629B\u9519,\u6309\u5DF2\u5360\u7528\u5904\u7406:${err instanceof Error ? err.message : String(err)}`);
|
|
137333
|
+
return true;
|
|
137334
|
+
}
|
|
137335
|
+
}
|
|
137336
|
+
/**
|
|
137337
|
+
* 为 sessionId 登记 LLM 日志活跃句柄(不动 this.llmLogHandle,由调用方决定何时装上/释放旧的)。
|
|
137338
|
+
* 登记为活跃会话:LLM 日志清理不动活跃会话的文件。resolve 即标记已在清理锁内落盘。engine 持句柄,换 id / dispose 只释放自己那一份。
|
|
137339
|
+
* 拿不到锁(如某张票据的 pid 被复用且平台上拿不到进程启动时间来判死):不能无锁写标记假装受保护;
|
|
137340
|
+
* 缺省告警后无保护继续(返回 null)——日志被归档的风险是原状,拒绝启动会话则是把清理的 fail-open 变成启动的 fail-closed,
|
|
137341
|
+
* 影响不对称;ATOMIX_LLM_LOG_STRICT=1 时改为抛出。dispose 中的取消照常抛出。
|
|
137342
|
+
*/
|
|
137343
|
+
async acquireLlmLogHandle(sessionId) {
|
|
137344
|
+
let handle = null;
|
|
137345
|
+
try {
|
|
137346
|
+
handle = await (0, logLLM_1.acquireActiveLLMLogSession)(sessionId, { signal: this.llmLogAbort.signal });
|
|
137347
|
+
} catch (err) {
|
|
137348
|
+
if (this.llmLogAbort.signal.aborted || LLM_LOG_STRICT)
|
|
137349
|
+
throw err;
|
|
137350
|
+
(0, log_1.logWarn)(`LLM \u65E5\u5FD7\u6D3B\u8DC3\u767B\u8BB0\u5931\u8D25,\u672C\u4F1A\u8BDD\u65E5\u5FD7\u4E0D\u53D7\u6E05\u7406\u4FDD\u62A4(\u53EF\u80FD\u88AB\u5F52\u6863\u622A\u65AD):${err instanceof Error ? err.message : String(err)}`);
|
|
137351
|
+
}
|
|
137352
|
+
if (this.llmLogAbort.signal.aborted) {
|
|
137353
|
+
handle?.release();
|
|
137354
|
+
throw new Error("\u4F1A\u8BDD\u5DF2\u91CA\u653E");
|
|
137355
|
+
}
|
|
137356
|
+
return handle;
|
|
137357
|
+
}
|
|
137358
|
+
/** createSession 用:换绑到 sessionId(先登记新的,成功后释放旧的) */
|
|
137359
|
+
async bindLlmLogSession(sessionId) {
|
|
137360
|
+
if (this.llmLogHandle?.sessionId === sessionId)
|
|
137361
|
+
return;
|
|
137362
|
+
const handle = await this.acquireLlmLogHandle(sessionId);
|
|
137363
|
+
this.llmLogHandle?.release();
|
|
137364
|
+
this.llmLogHandle = handle;
|
|
137365
|
+
}
|
|
136892
137366
|
async initialize(sessionId) {
|
|
136893
137367
|
const coreConfig = (0, ConfManager_1.getConfManager)().getCoreConfig();
|
|
136894
137368
|
(0, log_1.setLogLevel)(coreConfig?.logLevel || "info");
|
|
136895
137369
|
const finalSessionId = sessionId || (0, session_1.initializeSessionId)();
|
|
136896
137370
|
const stateManager = (0, StateManager_1.getStateManager)();
|
|
136897
137371
|
stateManager.setSessionId(finalSessionId);
|
|
137372
|
+
await this.bindLlmLogSession(finalSessionId);
|
|
136898
137373
|
try {
|
|
136899
137374
|
const modelManager = (0, ModelManager_1.getModelManager)();
|
|
136900
137375
|
const modelProfile = modelManager.getModel("main");
|
|
@@ -136923,6 +137398,9 @@ var require_SemaEngine = __commonJS({
|
|
|
136923
137398
|
dispose() {
|
|
136924
137399
|
(0, log_1.logInfo)("\u5F00\u59CB\u6E05\u7406 SemaEngine \u8D44\u6E90...");
|
|
136925
137400
|
this.abortCurrentRequest();
|
|
137401
|
+
this.llmLogAbort.abort();
|
|
137402
|
+
this.llmLogHandle?.release();
|
|
137403
|
+
this.llmLogHandle = null;
|
|
136926
137404
|
this.myStateManager.clearAllState();
|
|
136927
137405
|
this.myEventBus.removeAllListeners();
|
|
136928
137406
|
(0, log_1.logInfo)("SemaEngine \u8D44\u6E90\u6E05\u7406\u5B8C\u6210");
|
|
@@ -137049,6 +137527,8 @@ var require_SemaSession = __commonJS({
|
|
|
137049
137527
|
this.initialWorkingDir = workingDir;
|
|
137050
137528
|
const agentDataDir = resolvedConfig.agentDataDir || workingDir;
|
|
137051
137529
|
this.instanceMCPManager = this.acquireMCPManager(agentDataDir);
|
|
137530
|
+
if (resolvedConfig.mcpScope !== void 0)
|
|
137531
|
+
this.instanceMCPManager.setScope(resolvedConfig.mcpScope ?? null);
|
|
137052
137532
|
this.engine = new SemaEngine_1.SemaEngine(instanceId, resolvedConfig, this.instanceMCPManager, kernel);
|
|
137053
137533
|
if (resolvedConfig.skipMCPInit) {
|
|
137054
137534
|
this.configPromise = (0, ConfManager_1.getConfManager)().registerProjectConfig(resolvedConfig);
|
|
@@ -137219,6 +137699,10 @@ var require_MCPManagerView = __commonJS({
|
|
|
137219
137699
|
var mcpShared_1 = require_mcpShared();
|
|
137220
137700
|
var log_1 = require_log();
|
|
137221
137701
|
var MCPManagerView = class {
|
|
137702
|
+
/** 设置 / 清除(null)本 session 的 MCP 可见性 overlay。 */
|
|
137703
|
+
setScope(scope) {
|
|
137704
|
+
this.scope = scope;
|
|
137705
|
+
}
|
|
137222
137706
|
constructor(mux, globalConfigPath, projectConfigPath) {
|
|
137223
137707
|
this.mux = mux;
|
|
137224
137708
|
this.globalConfigPath = globalConfigPath;
|
|
@@ -137226,6 +137710,7 @@ var require_MCPManagerView = __commonJS({
|
|
|
137226
137710
|
this.globalConfigs = /* @__PURE__ */ new Map();
|
|
137227
137711
|
this.projectConfigs = /* @__PURE__ */ new Map();
|
|
137228
137712
|
this.heldHashes = /* @__PURE__ */ new Set();
|
|
137713
|
+
this.scope = null;
|
|
137229
137714
|
}
|
|
137230
137715
|
// ==================== 生命周期 ====================
|
|
137231
137716
|
/**
|
|
@@ -137269,7 +137754,7 @@ var require_MCPManagerView = __commonJS({
|
|
|
137269
137754
|
if (!shared || shared.status !== "connected" || !shared.client)
|
|
137270
137755
|
continue;
|
|
137271
137756
|
const tools = shared.toolDefs.map((def) => (0, MCPToolAdapter_1.createMCPToolAdapter)(shared.client, serverName, def));
|
|
137272
|
-
result2.push(...(0, mcpShared_1.filterToolsByConfig)(tools, config.useTools));
|
|
137757
|
+
result2.push(...(0, mcpShared_1.applyScopeToTools)(serverName, (0, mcpShared_1.filterToolsByConfig)(tools, config.useTools, serverName), this.scope));
|
|
137273
137758
|
}
|
|
137274
137759
|
return result2;
|
|
137275
137760
|
}
|
|
@@ -137291,7 +137776,8 @@ var require_MCPManagerView = __commonJS({
|
|
|
137291
137776
|
status: shared?.status === "connected" ? "connected" : shared?.status === "connecting" ? "connecting" : "disconnected",
|
|
137292
137777
|
capabilities: shared?.client?.capabilities ?? void 0,
|
|
137293
137778
|
error: void 0,
|
|
137294
|
-
connectedAt: void 0
|
|
137779
|
+
connectedAt: void 0,
|
|
137780
|
+
scopedOut: config.enabled !== false && (0, mcpShared_1.isServerScopedOut)(config.name, this.scope)
|
|
137295
137781
|
};
|
|
137296
137782
|
}
|
|
137297
137783
|
// ==================== 配置写入(本 session 的 mcp.json)====================
|
|
@@ -152927,6 +153413,11 @@ function recordAgentUniverse(core, names) {
|
|
|
152927
153413
|
const rt = runtimeOf(core);
|
|
152928
153414
|
if (rt) rt.fullAgentNames = names.slice();
|
|
152929
153415
|
}
|
|
153416
|
+
function isServerScopedOutBy(server, scope) {
|
|
153417
|
+
if (!scope) return false;
|
|
153418
|
+
if (scope.servers !== null && !scope.servers.includes(server)) return true;
|
|
153419
|
+
return scope.disabledServers?.includes(server) ?? false;
|
|
153420
|
+
}
|
|
152930
153421
|
function computeDisabledFrom(spec, allNames) {
|
|
152931
153422
|
if (!spec) return /* @__PURE__ */ new Set();
|
|
152932
153423
|
return spec.mode === "whitelist" ? new Set(allNames.filter((n) => !spec.enable.includes(n))) : new Set(spec.disable);
|
|
@@ -153101,6 +153592,37 @@ function loadHarnessDoc(name) {
|
|
|
153101
153592
|
};
|
|
153102
153593
|
const memory = parseToggle(raw.memory, "memory");
|
|
153103
153594
|
const persona = parseToggle(raw.persona, "persona");
|
|
153595
|
+
let mcp = null;
|
|
153596
|
+
if (raw.mcp !== void 0 && raw.mcp !== null) {
|
|
153597
|
+
if (typeof raw.mcp !== "object" || Array.isArray(raw.mcp)) {
|
|
153598
|
+
toggleWarnings.push(`mcp \u6BB5\u53EA\u8BA4\u5BF9\u8C61 { mode, enable, disable, tools },\u5FFD\u7565\u975E\u6CD5\u503C ${JSON.stringify(raw.mcp)}`);
|
|
153599
|
+
} else {
|
|
153600
|
+
const m = raw.mcp;
|
|
153601
|
+
const tools = {};
|
|
153602
|
+
if (m.tools !== void 0 && m.tools !== null) {
|
|
153603
|
+
if (typeof m.tools !== "object" || Array.isArray(m.tools)) toggleWarnings.push("mcp.tools \u53EA\u8BA4 { <server>: [\u5DE5\u5177\u540D...] },\u5DF2\u5FFD\u7565");
|
|
153604
|
+
else for (const [server, v] of Object.entries(m.tools)) {
|
|
153605
|
+
if (Array.isArray(v)) tools[server] = asStrArr(v);
|
|
153606
|
+
else toggleWarnings.push(`mcp.tools.${server} \u53EA\u8BA4\u6570\u7EC4,\u5DF2\u5FFD\u7565`);
|
|
153607
|
+
}
|
|
153608
|
+
}
|
|
153609
|
+
if (m.mode !== void 0 && m.mode !== null && m.mode !== "whitelist" && m.mode !== "blacklist") {
|
|
153610
|
+
toggleWarnings.push(`mcp.mode \u53EA\u8BA4 whitelist / blacklist,\u6536\u5230 ${JSON.stringify(m.mode)},\u6309 blacklist \u5904\u7406`);
|
|
153611
|
+
}
|
|
153612
|
+
const strList = (key) => {
|
|
153613
|
+
const v = m[key];
|
|
153614
|
+
if (v === void 0 || v === null) return [];
|
|
153615
|
+
if (!Array.isArray(v)) {
|
|
153616
|
+
toggleWarnings.push(`mcp.${key} \u53EA\u8BA4\u6570\u7EC4,\u6536\u5230 ${JSON.stringify(v)},\u5DF2\u5FFD\u7565`);
|
|
153617
|
+
return [];
|
|
153618
|
+
}
|
|
153619
|
+
const bad = v.filter((x) => typeof x !== "string");
|
|
153620
|
+
if (bad.length) toggleWarnings.push(`mcp.${key} \u542B\u975E\u5B57\u7B26\u4E32\u9879 ${JSON.stringify(bad)},\u5DF2\u5FFD\u7565\u8FD9\u4E9B\u9879`);
|
|
153621
|
+
return asStrArr(v);
|
|
153622
|
+
};
|
|
153623
|
+
mcp = { mode: asMode(m.mode), enable: strList("enable"), disable: strList("disable"), tools };
|
|
153624
|
+
}
|
|
153625
|
+
}
|
|
153104
153626
|
const parseModel = (v) => {
|
|
153105
153627
|
if (v === void 0 || v === null) return null;
|
|
153106
153628
|
if (typeof v === "string") {
|
|
@@ -153177,6 +153699,7 @@ function loadHarnessDoc(name) {
|
|
|
153177
153699
|
},
|
|
153178
153700
|
skills: { mode: asMode(sk.mode), disable: asStrArr(sk.disable), enable: asStrArr(sk.enable) },
|
|
153179
153701
|
agents: { mode: asMode(ag.mode), disable: asStrArr(ag.disable), enable: asStrArr(ag.enable) },
|
|
153702
|
+
mcp,
|
|
153180
153703
|
prompt: promptGroup.overrides,
|
|
153181
153704
|
promptWarnings: promptGroup.warnings,
|
|
153182
153705
|
memory,
|
|
@@ -153210,6 +153733,8 @@ function resolveBase(ctx) {
|
|
|
153210
153733
|
thinking: ctx.baseline.thinking,
|
|
153211
153734
|
skillsSpec: null,
|
|
153212
153735
|
agentsSpec: null,
|
|
153736
|
+
mcpScope: null,
|
|
153737
|
+
mcpDeclared: null,
|
|
153213
153738
|
skillsDisabled: /* @__PURE__ */ new Set(),
|
|
153214
153739
|
agentsDisabled: /* @__PURE__ */ new Set()
|
|
153215
153740
|
};
|
|
@@ -153290,9 +153815,33 @@ function resolveHarness(doc, ctx) {
|
|
|
153290
153815
|
temperatureOverrides[slot] = t;
|
|
153291
153816
|
}
|
|
153292
153817
|
}
|
|
153818
|
+
let mcpScope = null;
|
|
153819
|
+
if (doc.mcp) {
|
|
153820
|
+
const known = new Set(ctx.mcpServerNames);
|
|
153821
|
+
const unknown = (names) => names.filter((n) => !known.has(n));
|
|
153822
|
+
if (doc.mcp.mode === "whitelist") {
|
|
153823
|
+
mcpScope = { servers: doc.mcp.enable.slice() };
|
|
153824
|
+
const u = unknown(doc.mcp.enable);
|
|
153825
|
+
if (u.length) warnings.push(`mcp.enable \u91CC\u7684 server \u4E0D\u5B58\u5728(\u672C\u9879\u76EE mcp.json \u672A\u914D\u7F6E\u6216\u5DF2\u7981\u7528):${u.join(", ")}`);
|
|
153826
|
+
} else {
|
|
153827
|
+
mcpScope = { servers: null, disabledServers: doc.mcp.disable.slice() };
|
|
153828
|
+
const u = unknown(doc.mcp.disable);
|
|
153829
|
+
if (u.length) warnings.push(`mcp.disable \u91CC\u7684 server \u4E0D\u5B58\u5728(\u672C\u9879\u76EE mcp.json \u672A\u914D\u7F6E\u6216\u5DF2\u7981\u7528):${u.join(", ")}`);
|
|
153830
|
+
}
|
|
153831
|
+
const toolEntries = Object.entries(doc.mcp.tools);
|
|
153832
|
+
if (toolEntries.length) {
|
|
153833
|
+
mcpScope.tools = Object.fromEntries(toolEntries.map(([k, v]) => [k, v.slice()]));
|
|
153834
|
+
const u = unknown(toolEntries.map(([k]) => k));
|
|
153835
|
+
if (u.length) warnings.push(`mcp.tools \u91CC\u7684 server \u4E0D\u5B58\u5728(\u672C\u9879\u76EE mcp.json \u672A\u914D\u7F6E\u6216\u5DF2\u7981\u7528):${u.join(", ")}`);
|
|
153836
|
+
const shadowed = toolEntries.map(([k]) => k).filter((k) => isServerScopedOutBy(k, mcpScope));
|
|
153837
|
+
if (shadowed.length) warnings.push(`mcp.tools \u5BF9\u5DF2\u88AB\u5C4F\u853D\u7684 server \u65E0\u6548:${shadowed.join(", ")}`);
|
|
153838
|
+
}
|
|
153839
|
+
}
|
|
153293
153840
|
return {
|
|
153294
153841
|
name: doc.name,
|
|
153295
153842
|
dirName,
|
|
153843
|
+
mcpScope,
|
|
153844
|
+
mcpDeclared: doc.mcp,
|
|
153296
153845
|
temperatureOverrides: Object.keys(temperatureOverrides).length ? temperatureOverrides : null,
|
|
153297
153846
|
temperatureDeclared: doc.temperature,
|
|
153298
153847
|
modelOverrides: Object.keys(modelOverrides).length ? modelOverrides : null,
|
|
@@ -153326,11 +153875,20 @@ function buildContext(core) {
|
|
|
153326
153875
|
allToolNames: core.getToolInfos().map((t) => t.name),
|
|
153327
153876
|
allSkillNames: rt.fullSkillNames ?? core.getSkillsInfo({ includeDisabled: true }).map((s) => s.name),
|
|
153328
153877
|
allAgentNames: rt.fullAgentNames ?? core.getAgentsInfo().map((a) => a.name),
|
|
153878
|
+
mcpServerNames: mcpServerNamesOf(core),
|
|
153329
153879
|
modelNames: core.getModelNames(),
|
|
153330
153880
|
baseModels: core.getModelPointers(),
|
|
153331
153881
|
modelProfiles: core.getModelProfiles()
|
|
153332
153882
|
};
|
|
153333
153883
|
}
|
|
153884
|
+
function mcpServerNamesOf(core) {
|
|
153885
|
+
const byScope = core.getMCPServerConfigs();
|
|
153886
|
+
const merged = /* @__PURE__ */ new Map();
|
|
153887
|
+
for (const scope of ["user", "project"]) {
|
|
153888
|
+
for (const s of byScope.get(scope) ?? []) merged.set(s.config.name, s.config.enabled !== false);
|
|
153889
|
+
}
|
|
153890
|
+
return [...merged.entries()].filter(([, on]) => on).map(([n]) => n);
|
|
153891
|
+
}
|
|
153334
153892
|
function applyAssembly(core, rt, r, opts = {}) {
|
|
153335
153893
|
core.updateAssemblyConfig({
|
|
153336
153894
|
useTools: r.useTools,
|
|
@@ -153346,6 +153904,8 @@ function applyAssembly(core, rt, r, opts = {}) {
|
|
|
153346
153904
|
// null = 回 model.conf 指针(base 清场)
|
|
153347
153905
|
temperatureOverrides: r.temperatureOverrides,
|
|
153348
153906
|
// null = 回 profile / 协议默认(base 清场)
|
|
153907
|
+
mcpScope: r.mcpScope,
|
|
153908
|
+
// null = 回 mcp.json 基线(base / 未声明都清场,不残留上一个 harness 的屏蔽)
|
|
153349
153909
|
...!opts.preserveThinking && r.thinking !== void 0 ? { thinking: r.thinking } : {}
|
|
153350
153910
|
// 声明 ?? 基线;只在 use/reset/启动 下发,不做热切换
|
|
153351
153911
|
});
|
|
@@ -153486,6 +154046,7 @@ function summarize(r, ctx) {
|
|
|
153486
154046
|
const agentsDisabled = computeDisabledFrom(r.agentsSpec, ctx.allAgentNames);
|
|
153487
154047
|
lines.push(`skill ${skillsDisabled.size ? `\u7981\u7528 ${[...skillsDisabled].join(", ")}` : "\u5168\u90E8\u53EF\u89C1"}`);
|
|
153488
154048
|
lines.push(`agent ${agentsDisabled.size ? `\u7981\u7528 ${[...agentsDisabled].join(", ")}` : "\u5168\u90E8\u53EF\u89C1"}`);
|
|
154049
|
+
if (r.mcpDeclared) lines.push(`MCP ${describeMcpScope(r.mcpScope, ctx.mcpServerNames)}`);
|
|
153489
154050
|
lines.push(`\u89C4\u5219\u6BB5 ${r.customRules ? `${r.customRules.split("\n").length} \u884C` : "\u65E0"}`);
|
|
153490
154051
|
const p = r.promptOverrides;
|
|
153491
154052
|
const promptDesc = p ? [
|
|
@@ -153521,6 +154082,18 @@ function summarize(r, ctx) {
|
|
|
153521
154082
|
if (r.warnings.length) lines.push(...r.warnings.map((w) => `\u26A0 ${w}`));
|
|
153522
154083
|
return lines.join("\n ");
|
|
153523
154084
|
}
|
|
154085
|
+
function describeMcpScope(scope, allServers) {
|
|
154086
|
+
if (!scope) return "\u5168\u90E8\u53EF\u89C1";
|
|
154087
|
+
const blocked = allServers.filter((n) => isServerScopedOutBy(n, scope));
|
|
154088
|
+
const visible = allServers.filter((n) => !isServerScopedOutBy(n, scope));
|
|
154089
|
+
const parts = [];
|
|
154090
|
+
if (scope.servers !== null) parts.push(`\u767D\u540D\u5355 ${scope.servers.length ? scope.servers.join(", ") : "(\u7A7A:\u4E00\u4E2A server \u90FD\u4E0D\u7ED9)"}`);
|
|
154091
|
+
else if (scope.disabledServers?.length) parts.push(`\u5C4F\u853D ${scope.disabledServers.join(", ")}`);
|
|
154092
|
+
parts.push(`\u53EF\u89C1 ${visible.length}/${allServers.length}${blocked.length ? `(\u5C4F\u853D:${blocked.join(", ")})` : ""}`);
|
|
154093
|
+
const narrowed = Object.entries(scope.tools ?? {}).filter(([k]) => !isServerScopedOutBy(k, scope));
|
|
154094
|
+
if (narrowed.length) parts.push(`\u6536\u7A84 ${narrowed.map(([k, v]) => `${k}\u2192[${v.join(", ")}]`).join(" ")}`);
|
|
154095
|
+
return parts.join(";") + "(\u8FDE\u63A5\u4E0D\u91CA\u653E,\u5207\u8D70\u5373\u56DE mcp.json \u57FA\u7EBF)";
|
|
154096
|
+
}
|
|
153524
154097
|
async function harnessCommand(core, args) {
|
|
153525
154098
|
const [sub, ...rest2] = args;
|
|
153526
154099
|
const arg = rest2.join(" ").trim() || void 0;
|
|
@@ -160716,7 +161289,8 @@ async function mcpCommand(core, args) {
|
|
|
160716
161289
|
const name = s.config?.name ?? "?";
|
|
160717
161290
|
const enabled = s.config?.enabled !== false;
|
|
160718
161291
|
const inheritedTag = name.startsWith(INHERIT_PREFIX) ? "\uFF08\u7EE7\u627F\u81EA semaclaw\uFF09" : "";
|
|
160719
|
-
|
|
161292
|
+
const scopedTag = s.scopedOut ? "\uFF08harness \u5C4F\u853D\uFF0C\u5DE5\u5177\u4E0D\u53EF\u89C1\uFF09" : "";
|
|
161293
|
+
lines.push(`${enabled ? s.scopedOut ? "\u25CC" : "\u25CF" : "\u25CB"} ${name} [${scope}] ${s.status}${s.error ? ` \u2014 ${s.error}` : ""}${inheritedTag}${scopedTag}`);
|
|
160720
161294
|
}
|
|
160721
161295
|
}
|
|
160722
161296
|
if (!lines.length) lines.push("\uFF08\u672A\u914D\u7F6E MCP \u670D\u52A1\u5668\uFF09");
|
|
@@ -161071,7 +161645,8 @@ function createSessionCore(opts) {
|
|
|
161071
161645
|
// 交互模式下文件编辑必须过权限卡;无人值守靠 permissionMode(free-style 全放行,其余由自动应答器 fail-closed)
|
|
161072
161646
|
skipFileEditPermission: false,
|
|
161073
161647
|
multiSession: opts.multiSession ?? false,
|
|
161074
|
-
systemPrompt: opts.systemPrompt ?? DEFAULT_SYSTEM_PROMPT
|
|
161648
|
+
systemPrompt: opts.systemPrompt ?? DEFAULT_SYSTEM_PROMPT,
|
|
161649
|
+
...opts.isSessionIdTaken ? { isSessionIdTaken: opts.isSessionIdTaken } : {}
|
|
161075
161650
|
});
|
|
161076
161651
|
registerSessionDirs(core, dirs);
|
|
161077
161652
|
const potential = buildPotentialContextFiles(opts.cwd, appConfig);
|
|
@@ -161084,7 +161659,7 @@ function createSessionCore(opts) {
|
|
|
161084
161659
|
thinking: false
|
|
161085
161660
|
// cli 构造 core 时 thinking 关;harness 未声明即回到这里
|
|
161086
161661
|
};
|
|
161087
|
-
return { [SESSION_HANDLE_BRAND]: true, core, cwd: opts.cwd, interactive, permissionMode, appConfig, contextFiles, baseline, notes };
|
|
161662
|
+
return { [SESSION_HANDLE_BRAND]: true, core, cwd: opts.cwd, interactive, permissionMode, appConfig, contextFiles, baseline, notes, permissionPolicy: opts.permissionPolicy, questionPolicy: opts.questionPolicy, formPolicy: opts.formPolicy };
|
|
161088
161663
|
}
|
|
161089
161664
|
async function discardSessionCore(handle) {
|
|
161090
161665
|
const h = internalHandle(handle);
|
|
@@ -161116,29 +161691,100 @@ async function startSession(handle, opts = {}) {
|
|
|
161116
161691
|
});
|
|
161117
161692
|
const mcpNotes = await applyMcpHotUpdates(core).catch(() => []);
|
|
161118
161693
|
notes.push(...mcpNotes.map((n) => `${n}\uFF08\u9879\u76EE\u7EA7 override\uFF09`));
|
|
161119
|
-
const responder = h.interactive ? null : attachHeadlessResponder(core);
|
|
161694
|
+
const responder = h.interactive ? null : attachHeadlessResponder(core, { permission: h.permissionPolicy, question: h.questionPolicy, form: h.formPolicy });
|
|
161120
161695
|
return new SessionImpl(h, ready, notes, responder);
|
|
161121
161696
|
}
|
|
161122
|
-
function
|
|
161697
|
+
function isPlainRecord(v) {
|
|
161698
|
+
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
161699
|
+
}
|
|
161700
|
+
function isQuestionAnswers(v) {
|
|
161701
|
+
return isPlainRecord(v) && Object.values(v).every((x) => typeof x === "string");
|
|
161702
|
+
}
|
|
161703
|
+
function isFormAnswer(v) {
|
|
161704
|
+
return isPlainRecord(v) && isPlainRecord(v.values) && typeof v.submitted === "boolean";
|
|
161705
|
+
}
|
|
161706
|
+
function attachHeadlessResponder(core, policies = {}) {
|
|
161707
|
+
const policy = policies.permission;
|
|
161123
161708
|
const blocked = [];
|
|
161709
|
+
let gen = 0;
|
|
161124
161710
|
const onPermission = (d) => {
|
|
161125
|
-
|
|
161126
|
-
|
|
161711
|
+
const g = gen;
|
|
161712
|
+
const fresh = () => gen === g;
|
|
161713
|
+
const respond = (selected) => {
|
|
161714
|
+
setImmediate(() => {
|
|
161715
|
+
if (fresh()) core.respondToToolPermission({ toolName: d.toolName, selected, agentId: d.agentId });
|
|
161716
|
+
});
|
|
161717
|
+
};
|
|
161718
|
+
const deny = () => {
|
|
161719
|
+
if (!fresh()) return;
|
|
161720
|
+
blocked.push({ kind: "permission", name: d.toolName, title: d.title, agentId: d.agentId });
|
|
161721
|
+
respond("refuse");
|
|
161722
|
+
};
|
|
161723
|
+
if (!policy) return deny();
|
|
161724
|
+
let decided;
|
|
161725
|
+
try {
|
|
161726
|
+
decided = Promise.resolve(policy({ agentId: d.agentId, toolName: d.toolName, title: d.title, content: d.content, options: d.options }));
|
|
161727
|
+
} catch {
|
|
161728
|
+
decided = Promise.resolve("deny");
|
|
161729
|
+
}
|
|
161730
|
+
void decided.then(
|
|
161731
|
+
(decision) => {
|
|
161732
|
+
if (!fresh()) return;
|
|
161733
|
+
if (decision === "allow") respond("agree");
|
|
161734
|
+
else deny();
|
|
161735
|
+
},
|
|
161736
|
+
() => deny()
|
|
161737
|
+
);
|
|
161738
|
+
};
|
|
161739
|
+
const decide = (fn, valid, fallback, g, apply2) => {
|
|
161740
|
+
let decided;
|
|
161741
|
+
try {
|
|
161742
|
+
decided = fn ? Promise.resolve(fn()) : Promise.resolve(fallback);
|
|
161743
|
+
} catch {
|
|
161744
|
+
decided = Promise.resolve(fallback);
|
|
161745
|
+
}
|
|
161746
|
+
void decided.then(
|
|
161747
|
+
(v) => apply2(valid(v) ? v : fallback, gen === g),
|
|
161748
|
+
() => apply2(fallback, gen === g)
|
|
161749
|
+
);
|
|
161127
161750
|
};
|
|
161128
161751
|
const onQuestion = (d) => {
|
|
161129
|
-
|
|
161130
|
-
|
|
161752
|
+
const g = gen;
|
|
161753
|
+
const q = policies.question;
|
|
161754
|
+
decide(q ? () => q({ agentId: d.agentId, questions: d.questions }) : void 0, isQuestionAnswers, {}, g, (answers, fresh) => {
|
|
161755
|
+
if (!fresh) return;
|
|
161756
|
+
const skipped = Object.keys(answers).length === 0;
|
|
161757
|
+
if (skipped) blocked.push({ kind: "question", name: "AskUserQuestion", title: d.questions.map((x) => x.question).join(" / "), agentId: d.agentId });
|
|
161758
|
+
setImmediate(() => {
|
|
161759
|
+
if (gen === g) core.respondToAskQuestion({ agentId: d.agentId, answers });
|
|
161760
|
+
});
|
|
161761
|
+
});
|
|
161131
161762
|
};
|
|
161132
161763
|
const onForm = (d) => {
|
|
161133
|
-
|
|
161134
|
-
|
|
161764
|
+
const g = gen;
|
|
161765
|
+
const f = policies.form;
|
|
161766
|
+
const fallback = { values: {}, submitted: false };
|
|
161767
|
+
decide(f ? () => f({ agentId: d.agentId, title: d.title, surface: d.surface, submitLabel: d.submitLabel, fields: d.fields }) : void 0, isFormAnswer, fallback, g, (a, fresh) => {
|
|
161768
|
+
if (!fresh) return;
|
|
161769
|
+
if (!a.submitted) blocked.push({ kind: "form", name: "FormUI", title: d.title, agentId: d.agentId });
|
|
161770
|
+
setImmediate(() => {
|
|
161771
|
+
if (gen === g) core.respondToForm({ agentId: d.agentId, values: a.values, submitted: a.submitted });
|
|
161772
|
+
});
|
|
161773
|
+
});
|
|
161135
161774
|
};
|
|
161136
161775
|
core.on("tool:permission:request", onPermission);
|
|
161137
161776
|
core.on("ask:question:request", onQuestion);
|
|
161138
161777
|
core.on("form:request", onForm);
|
|
161139
161778
|
return {
|
|
161140
161779
|
blocked,
|
|
161780
|
+
beginTurn() {
|
|
161781
|
+
gen++;
|
|
161782
|
+
},
|
|
161783
|
+
endTurn() {
|
|
161784
|
+
gen++;
|
|
161785
|
+
},
|
|
161141
161786
|
detach() {
|
|
161787
|
+
gen++;
|
|
161142
161788
|
core.off("tool:permission:request", onPermission);
|
|
161143
161789
|
core.off("ask:question:request", onQuestion);
|
|
161144
161790
|
core.off("form:request", onForm);
|
|
@@ -161198,6 +161844,7 @@ var SessionImpl = class {
|
|
|
161198
161844
|
const core = this.core;
|
|
161199
161845
|
const timeoutMs = opts.timeoutMs ?? DEFAULT_SEND_TIMEOUT_MS;
|
|
161200
161846
|
const blockedStart = this.responder?.blocked.length ?? 0;
|
|
161847
|
+
this.responder?.beginTurn();
|
|
161201
161848
|
return new Promise((resolve9) => {
|
|
161202
161849
|
const texts = [];
|
|
161203
161850
|
let usage2;
|
|
@@ -161228,6 +161875,7 @@ var SessionImpl = class {
|
|
|
161228
161875
|
done = true;
|
|
161229
161876
|
clearTimeout(timer);
|
|
161230
161877
|
this.abortCurrent = null;
|
|
161878
|
+
this.responder?.endTurn();
|
|
161231
161879
|
core.off("message:complete", onMessage);
|
|
161232
161880
|
core.off("state:update", onState);
|
|
161233
161881
|
core.off("session:error", onError);
|
|
@@ -161348,7 +161996,6 @@ var fancy = {
|
|
|
161348
161996
|
refresh: "\u27F3",
|
|
161349
161997
|
updown: "\u2191\u2193",
|
|
161350
161998
|
permMode: "\u21E7\u21E5",
|
|
161351
|
-
newline: "\u23CE",
|
|
161352
161999
|
sBullet: "\u25CF",
|
|
161353
162000
|
sToolDot: "\u23FA",
|
|
161354
162001
|
sThink: "\u273B",
|
|
@@ -161382,7 +162029,6 @@ var ascii = {
|
|
|
161382
162029
|
refresh: "~",
|
|
161383
162030
|
updown: "\u4E0A\u4E0B\u952E",
|
|
161384
162031
|
permMode: "[perm]",
|
|
161385
|
-
newline: "\\n",
|
|
161386
162032
|
sBullet: "\u25CF",
|
|
161387
162033
|
sToolDot: "\u25CF",
|
|
161388
162034
|
sThink: "\u203B",
|
|
@@ -161553,12 +162199,28 @@ function headByRows(text, maxRows, columns) {
|
|
|
161553
162199
|
}
|
|
161554
162200
|
return { text: lines.slice(0, i).join("\n"), hiddenLines: lines.length - i, truncated: true };
|
|
161555
162201
|
}
|
|
162202
|
+
var isJoinAfter = (cp) => cp === 65039 || // VS16
|
|
162203
|
+
cp >= 127995 && cp <= 127999 || // 肤色修饰符
|
|
162204
|
+
cp >= 768 && cp <= 879 || // 组合附加符号
|
|
162205
|
+
cp >= 8400 && cp <= 8447;
|
|
162206
|
+
function clusterEnd(chars, i) {
|
|
162207
|
+
let j = i + 1;
|
|
162208
|
+
while (j < chars.length) {
|
|
162209
|
+
const cp = chars[j].codePointAt(0);
|
|
162210
|
+
if (cp === 8205 && j + 1 < chars.length) {
|
|
162211
|
+
j += 2;
|
|
162212
|
+
continue;
|
|
162213
|
+
}
|
|
162214
|
+
if (isJoinAfter(cp)) {
|
|
162215
|
+
j++;
|
|
162216
|
+
continue;
|
|
162217
|
+
}
|
|
162218
|
+
break;
|
|
162219
|
+
}
|
|
162220
|
+
return j;
|
|
162221
|
+
}
|
|
161556
162222
|
function hardWrapByWidth(text, columns) {
|
|
161557
162223
|
const cols = Math.max(1, columns);
|
|
161558
|
-
const isJoinAfter = (cp) => cp === 65039 || // VS16
|
|
161559
|
-
cp >= 127995 && cp <= 127999 || // 肤色修饰符
|
|
161560
|
-
cp >= 768 && cp <= 879 || // 组合附加符号
|
|
161561
|
-
cp >= 8400 && cp <= 8447;
|
|
161562
162224
|
const out = [];
|
|
161563
162225
|
for (const line of text.split("\n")) {
|
|
161564
162226
|
if (!line.includes(" ") && estimateWidth(line) <= cols) {
|
|
@@ -161594,19 +162256,7 @@ function hardWrapByWidth(text, columns) {
|
|
|
161594
162256
|
i++;
|
|
161595
162257
|
continue;
|
|
161596
162258
|
}
|
|
161597
|
-
|
|
161598
|
-
while (j < chars.length) {
|
|
161599
|
-
const cp = chars[j].codePointAt(0);
|
|
161600
|
-
if (cp === 8205 && j + 1 < chars.length) {
|
|
161601
|
-
j += 2;
|
|
161602
|
-
continue;
|
|
161603
|
-
}
|
|
161604
|
-
if (isJoinAfter(cp)) {
|
|
161605
|
-
j++;
|
|
161606
|
-
continue;
|
|
161607
|
-
}
|
|
161608
|
-
break;
|
|
161609
|
-
}
|
|
162259
|
+
const j = clusterEnd(chars, i);
|
|
161610
162260
|
const cluster = chars.slice(i, j).join("");
|
|
161611
162261
|
const cw = estimateWidth(cluster);
|
|
161612
162262
|
if (w + cw > cols && w > 0) {
|
|
@@ -161628,30 +162278,6 @@ function clipToWidth(line, maxWidth) {
|
|
|
161628
162278
|
if (estimateWidth(line) <= w) return line;
|
|
161629
162279
|
return headOfLine(line, w - 1) + "\u2026";
|
|
161630
162280
|
}
|
|
161631
|
-
function windowAroundCursor(chars, cursor, budget, newlineGlyph) {
|
|
161632
|
-
const glyph = (ch) => ch === "\n" ? newlineGlyph : ch;
|
|
161633
|
-
const half = Math.floor(budget / 2);
|
|
161634
|
-
let w = 0;
|
|
161635
|
-
let lo = cursor;
|
|
161636
|
-
while (lo > 0) {
|
|
161637
|
-
const cw = estimateWidth(glyph(chars[lo - 1]));
|
|
161638
|
-
if (w + cw > half) break;
|
|
161639
|
-
w += cw;
|
|
161640
|
-
lo--;
|
|
161641
|
-
}
|
|
161642
|
-
const at2 = cursor < chars.length ? glyph(chars[cursor]) : "";
|
|
161643
|
-
let rest2 = budget - w - estimateWidth(at2);
|
|
161644
|
-
let hi = Math.min(cursor + 1, chars.length);
|
|
161645
|
-
while (hi < chars.length) {
|
|
161646
|
-
const cw = estimateWidth(glyph(chars[hi]));
|
|
161647
|
-
if (rest2 - cw < 0) break;
|
|
161648
|
-
rest2 -= cw;
|
|
161649
|
-
hi++;
|
|
161650
|
-
}
|
|
161651
|
-
const before2 = (lo > 0 ? "\u2026" : "") + chars.slice(lo, cursor).map(glyph).join("");
|
|
161652
|
-
const after2 = chars.slice(Math.min(cursor + 1, chars.length), hi).map(glyph).join("") + (hi < chars.length ? "\u2026" : "");
|
|
161653
|
-
return { before: before2, at: at2, after: after2 };
|
|
161654
|
-
}
|
|
161655
162281
|
|
|
161656
162282
|
// src/tui/bridge.ts
|
|
161657
162283
|
var MAIN2 = "main";
|
|
@@ -161752,6 +162378,10 @@ var Bridge = class {
|
|
|
161752
162378
|
setUsage(usage2) {
|
|
161753
162379
|
this.set({ usage: usage2 });
|
|
161754
162380
|
}
|
|
162381
|
+
/** 会话切换(/resume 的 session:ready、/clear 的 session:cleared)后同步 UI 镜像:新 sessionId/usage,清掉上一会话的 todos 与流式残留 */
|
|
162382
|
+
resetSession(d) {
|
|
162383
|
+
this.set({ sessionId: d.sessionId, usage: d.usage, todos: [], streamText: "", streamThinkingChars: 0 });
|
|
162384
|
+
}
|
|
161755
162385
|
setThinking(enabled) {
|
|
161756
162386
|
this.set({ thinkingEnabled: enabled });
|
|
161757
162387
|
this.core.updateThinking(enabled);
|
|
@@ -161960,6 +162590,10 @@ var Bridge = class {
|
|
|
161960
162590
|
core.on("session:interrupted", (d) => {
|
|
161961
162591
|
if (d.agentId === MAIN2) this.notice("\u23F9 \u5DF2\u4E2D\u65AD", "warn");
|
|
161962
162592
|
});
|
|
162593
|
+
core.on("session:cleared", (d) => {
|
|
162594
|
+
this.resetSession(d);
|
|
162595
|
+
this.notice(`\u4E0A\u4E0B\u6587\u5DF2\u6E05\u7A7A\uFF0C\u65B0\u4F1A\u8BDD ${d.sessionId}${d.previousSessionId ? `\uFF08\u539F\u4F1A\u8BDD ${d.previousSessionId} \u53EF\u7528 /resume \u6062\u590D\uFF09` : ""}`, "success");
|
|
162596
|
+
});
|
|
161963
162597
|
core.on("session:error", (d) => {
|
|
161964
162598
|
this.notice(`\u4F1A\u8BDD\u9519\u8BEF [${d.type}] ${d.error?.message ?? d.error?.code ?? ""}`, "error");
|
|
161965
162599
|
});
|
|
@@ -162397,10 +163031,284 @@ function findAtToken(chars, cursor) {
|
|
|
162397
163031
|
return { start, end, prefix: chars.slice(start + 1, cursor).join("") };
|
|
162398
163032
|
}
|
|
162399
163033
|
|
|
163034
|
+
// src/tui/pasteText.ts
|
|
163035
|
+
var PASTE_COLLAPSE_CHARS = 200;
|
|
163036
|
+
var PLACEHOLDER_RE = /\[粘贴#\d+ [^[\]]*\]/g;
|
|
163037
|
+
var IMAGE_PLACEHOLDER_RE = /\[图片#\d+\]/g;
|
|
163038
|
+
var pastes = /* @__PURE__ */ new Map();
|
|
163039
|
+
var pasteSeq = 0;
|
|
163040
|
+
function sanitizePastedText(raw) {
|
|
163041
|
+
return raw.replace(/\r\n?/g, "\n").replace(/\t/g, " ").replace(/[\x00-\x08\x0b-\x1f\x7f]/g, "").replace(/\n+$/, "");
|
|
163042
|
+
}
|
|
163043
|
+
function collapsePaste(text) {
|
|
163044
|
+
const chars = [...text];
|
|
163045
|
+
if (chars.length <= PASTE_COLLAPSE_CHARS) return null;
|
|
163046
|
+
const lines = text.split("\n").length;
|
|
163047
|
+
const placeholder = `[\u7C98\u8D34#${++pasteSeq} ${lines > 1 ? `${lines}\u884C` : `${chars.length}\u5B57`}]`;
|
|
163048
|
+
pastes.set(placeholder, text);
|
|
163049
|
+
return placeholder;
|
|
163050
|
+
}
|
|
163051
|
+
function expandPastes(value) {
|
|
163052
|
+
return value.replace(PLACEHOLDER_RE, (m) => {
|
|
163053
|
+
const text = pastes.get(m);
|
|
163054
|
+
if (text === void 0) return m;
|
|
163055
|
+
pastes.delete(m);
|
|
163056
|
+
return text;
|
|
163057
|
+
});
|
|
163058
|
+
}
|
|
163059
|
+
function atomicSpans(value) {
|
|
163060
|
+
const spans = [];
|
|
163061
|
+
const collect = (re, accept) => {
|
|
163062
|
+
for (const m of value.matchAll(re)) {
|
|
163063
|
+
if (!accept(m[0])) continue;
|
|
163064
|
+
const start = [...value.slice(0, m.index)].length;
|
|
163065
|
+
spans.push({ start, end: start + [...m[0]].length });
|
|
163066
|
+
}
|
|
163067
|
+
};
|
|
163068
|
+
collect(PLACEHOLDER_RE, (t) => pastes.has(t));
|
|
163069
|
+
collect(IMAGE_PLACEHOLDER_RE, () => true);
|
|
163070
|
+
return spans.sort((a, b) => a.start - b.start);
|
|
163071
|
+
}
|
|
163072
|
+
function spanEndingAt(spans, i) {
|
|
163073
|
+
return spans.find((s) => s.end === i);
|
|
163074
|
+
}
|
|
163075
|
+
function spanStartingAt(spans, i) {
|
|
163076
|
+
return spans.find((s) => s.start === i);
|
|
163077
|
+
}
|
|
163078
|
+
|
|
163079
|
+
// src/tui/terminalInput.ts
|
|
163080
|
+
import { PassThrough as PassThrough2 } from "node:stream";
|
|
163081
|
+
import { emitKeypressEvents } from "node:readline";
|
|
163082
|
+
var BP_START = "\x1B[200~";
|
|
163083
|
+
var BP_END = "\x1B[201~";
|
|
163084
|
+
var SETTLE_MS = 30;
|
|
163085
|
+
var CHUNK_MIN = 256;
|
|
163086
|
+
function createTerminalInput(emit) {
|
|
163087
|
+
let bracketed = null;
|
|
163088
|
+
let fallback = "";
|
|
163089
|
+
let prefix = "";
|
|
163090
|
+
let timer;
|
|
163091
|
+
const keys2 = new PassThrough2();
|
|
163092
|
+
emitKeypressEvents(keys2);
|
|
163093
|
+
const onKey = (input, key) => {
|
|
163094
|
+
emit({
|
|
163095
|
+
kind: "key",
|
|
163096
|
+
input: key.ctrl ? key.name ?? "" : input ?? "",
|
|
163097
|
+
key: {
|
|
163098
|
+
upArrow: key.name === "up",
|
|
163099
|
+
downArrow: key.name === "down",
|
|
163100
|
+
leftArrow: key.name === "left",
|
|
163101
|
+
rightArrow: key.name === "right",
|
|
163102
|
+
pageUp: key.name === "pageup",
|
|
163103
|
+
pageDown: key.name === "pagedown",
|
|
163104
|
+
return: key.name === "return" || key.name === "enter",
|
|
163105
|
+
escape: key.name === "escape",
|
|
163106
|
+
tab: key.name === "tab",
|
|
163107
|
+
backspace: key.name === "backspace",
|
|
163108
|
+
delete: key.name === "delete",
|
|
163109
|
+
ctrl: !!key.ctrl,
|
|
163110
|
+
shift: !!key.shift,
|
|
163111
|
+
meta: !!key.meta
|
|
163112
|
+
}
|
|
163113
|
+
});
|
|
163114
|
+
};
|
|
163115
|
+
keys2.on("keypress", onKey);
|
|
163116
|
+
const flush = () => {
|
|
163117
|
+
if (!fallback) return;
|
|
163118
|
+
emit({ kind: "paste", text: fallback });
|
|
163119
|
+
fallback = "";
|
|
163120
|
+
};
|
|
163121
|
+
const ordinary = (data, asKeys = false) => {
|
|
163122
|
+
if (!data) return;
|
|
163123
|
+
if (data === "\x1B") {
|
|
163124
|
+
flush();
|
|
163125
|
+
onKey("", { name: "escape", sequence: data });
|
|
163126
|
+
return;
|
|
163127
|
+
}
|
|
163128
|
+
if (asKeys) {
|
|
163129
|
+
flush();
|
|
163130
|
+
keys2.write(data);
|
|
163131
|
+
return;
|
|
163132
|
+
}
|
|
163133
|
+
const singleControl = data.length === 1 && /[\x00-\x1f\x7f]/.test(data);
|
|
163134
|
+
if (!data.startsWith("\x1B") && !singleControl && (fallback || [...data].length >= CHUNK_MIN)) {
|
|
163135
|
+
fallback += data;
|
|
163136
|
+
} else {
|
|
163137
|
+
flush();
|
|
163138
|
+
if ([...data].length > 1 && !data.startsWith("\x1B")) emit({ kind: "paste", text: data });
|
|
163139
|
+
else keys2.write(data);
|
|
163140
|
+
}
|
|
163141
|
+
};
|
|
163142
|
+
const write = (chunk2) => {
|
|
163143
|
+
clearTimeout(timer);
|
|
163144
|
+
let data = prefix + chunk2;
|
|
163145
|
+
let afterBracket = false;
|
|
163146
|
+
prefix = "";
|
|
163147
|
+
while (data) {
|
|
163148
|
+
if (bracketed !== null) {
|
|
163149
|
+
const searchFrom = Math.max(0, bracketed.length - BP_END.length + 1);
|
|
163150
|
+
bracketed += data;
|
|
163151
|
+
const end = bracketed.indexOf(BP_END, searchFrom);
|
|
163152
|
+
if (end < 0) return;
|
|
163153
|
+
emit({ kind: "paste", text: bracketed.slice(0, end) });
|
|
163154
|
+
data = bracketed.slice(end + BP_END.length);
|
|
163155
|
+
bracketed = null;
|
|
163156
|
+
afterBracket = true;
|
|
163157
|
+
continue;
|
|
163158
|
+
}
|
|
163159
|
+
const start = data.indexOf(BP_START);
|
|
163160
|
+
if (start >= 0) {
|
|
163161
|
+
ordinary(data.slice(0, start), afterBracket);
|
|
163162
|
+
flush();
|
|
163163
|
+
bracketed = "";
|
|
163164
|
+
data = data.slice(start + BP_START.length);
|
|
163165
|
+
continue;
|
|
163166
|
+
}
|
|
163167
|
+
for (let n = Math.min(data.length, BP_START.length - 1); n > 0; n--) {
|
|
163168
|
+
if (data.endsWith(BP_START.slice(0, n))) {
|
|
163169
|
+
prefix = data.slice(-n);
|
|
163170
|
+
data = data.slice(0, -n);
|
|
163171
|
+
break;
|
|
163172
|
+
}
|
|
163173
|
+
}
|
|
163174
|
+
ordinary(data, afterBracket);
|
|
163175
|
+
break;
|
|
163176
|
+
}
|
|
163177
|
+
if (prefix || fallback) {
|
|
163178
|
+
timer = setTimeout(() => {
|
|
163179
|
+
const pending = prefix;
|
|
163180
|
+
prefix = "";
|
|
163181
|
+
ordinary(pending);
|
|
163182
|
+
flush();
|
|
163183
|
+
}, SETTLE_MS);
|
|
163184
|
+
}
|
|
163185
|
+
};
|
|
163186
|
+
return {
|
|
163187
|
+
write,
|
|
163188
|
+
dispose() {
|
|
163189
|
+
clearTimeout(timer);
|
|
163190
|
+
keys2.removeAllListeners("keypress");
|
|
163191
|
+
keys2.destroy();
|
|
163192
|
+
}
|
|
163193
|
+
};
|
|
163194
|
+
}
|
|
163195
|
+
|
|
163196
|
+
// src/tui/inputLayout.ts
|
|
163197
|
+
function layoutRows(chars, columns) {
|
|
163198
|
+
const cols = Math.max(1, columns);
|
|
163199
|
+
const rows = [];
|
|
163200
|
+
let rowStart = 0;
|
|
163201
|
+
let w = 0;
|
|
163202
|
+
let i = 0;
|
|
163203
|
+
while (i < chars.length) {
|
|
163204
|
+
if (chars[i] === "\n") {
|
|
163205
|
+
rows.push({ start: rowStart, end: i });
|
|
163206
|
+
i++;
|
|
163207
|
+
rowStart = i;
|
|
163208
|
+
w = 0;
|
|
163209
|
+
continue;
|
|
163210
|
+
}
|
|
163211
|
+
const j = clusterEnd(chars, i);
|
|
163212
|
+
const cw = estimateWidth(chars.slice(i, j).join(""));
|
|
163213
|
+
if (w + cw > cols && w > 0) {
|
|
163214
|
+
rows.push({ start: rowStart, end: i });
|
|
163215
|
+
rowStart = i;
|
|
163216
|
+
w = 0;
|
|
163217
|
+
}
|
|
163218
|
+
w += cw;
|
|
163219
|
+
i = j;
|
|
163220
|
+
}
|
|
163221
|
+
rows.push({ start: rowStart, end: chars.length });
|
|
163222
|
+
return rows;
|
|
163223
|
+
}
|
|
163224
|
+
function rowOfCursor(rows, cursor) {
|
|
163225
|
+
let r = 0;
|
|
163226
|
+
for (let i = 0; i < rows.length; i++) {
|
|
163227
|
+
if (rows[i].start <= cursor) r = i;
|
|
163228
|
+
else break;
|
|
163229
|
+
}
|
|
163230
|
+
return r;
|
|
163231
|
+
}
|
|
163232
|
+
function columnOf(chars, row, cursor) {
|
|
163233
|
+
return estimateWidth(chars.slice(row.start, Math.min(cursor, row.end)).join(""));
|
|
163234
|
+
}
|
|
163235
|
+
function indexAtColumn(chars, row, column) {
|
|
163236
|
+
let w = 0;
|
|
163237
|
+
let i = row.start;
|
|
163238
|
+
while (i < row.end) {
|
|
163239
|
+
const j = clusterEnd(chars, i);
|
|
163240
|
+
const cw = estimateWidth(chars.slice(i, j).join(""));
|
|
163241
|
+
if (w + cw > column) break;
|
|
163242
|
+
w += cw;
|
|
163243
|
+
i = j;
|
|
163244
|
+
}
|
|
163245
|
+
return i;
|
|
163246
|
+
}
|
|
163247
|
+
function moveVertical(chars, rows, cursor, delta) {
|
|
163248
|
+
const r = rowOfCursor(rows, cursor);
|
|
163249
|
+
const target = r + delta;
|
|
163250
|
+
if (target < 0 || target >= rows.length) return null;
|
|
163251
|
+
const row = rows[target];
|
|
163252
|
+
const index = indexAtColumn(chars, row, columnOf(chars, rows[r], cursor));
|
|
163253
|
+
if (index === row.end && rows[target + 1]?.start === row.end) {
|
|
163254
|
+
let last2 = row.start;
|
|
163255
|
+
for (let i = row.start; i < row.end; i = clusterEnd(chars, i)) last2 = i;
|
|
163256
|
+
return last2;
|
|
163257
|
+
}
|
|
163258
|
+
return index;
|
|
163259
|
+
}
|
|
163260
|
+
function lineStart(chars, cursor) {
|
|
163261
|
+
let i = cursor;
|
|
163262
|
+
while (i > 0 && chars[i - 1] !== "\n") i--;
|
|
163263
|
+
return i;
|
|
163264
|
+
}
|
|
163265
|
+
function lineEnd(chars, cursor) {
|
|
163266
|
+
let i = cursor;
|
|
163267
|
+
while (i < chars.length && chars[i] !== "\n") i++;
|
|
163268
|
+
return i;
|
|
163269
|
+
}
|
|
163270
|
+
function scrollTop(total, cursorRow, visible, prevTop) {
|
|
163271
|
+
const maxTop = Math.max(0, total - visible);
|
|
163272
|
+
let top = Math.min(Math.max(0, prevTop), maxTop);
|
|
163273
|
+
if (cursorRow < top) top = cursorRow;
|
|
163274
|
+
else if (cursorRow >= top + visible) top = cursorRow - visible + 1;
|
|
163275
|
+
return top;
|
|
163276
|
+
}
|
|
163277
|
+
function segmentRow(chars, row, spans, cursor) {
|
|
163278
|
+
const segs = [];
|
|
163279
|
+
const push = (text, kind) => {
|
|
163280
|
+
const last2 = segs[segs.length - 1];
|
|
163281
|
+
if (last2 && last2.kind === kind && kind !== "cursor") last2.text += text;
|
|
163282
|
+
else segs.push({ text, kind });
|
|
163283
|
+
};
|
|
163284
|
+
let s = 0;
|
|
163285
|
+
for (let i = row.start; i < row.end; i++) {
|
|
163286
|
+
while (s < spans.length && spans[s].end <= i) s++;
|
|
163287
|
+
const inSpan = s < spans.length && spans[s].start <= i && i < spans[s].end;
|
|
163288
|
+
if (i === cursor) push(chars[i], "cursor");
|
|
163289
|
+
else push(chars[i], inSpan ? "placeholder" : "text");
|
|
163290
|
+
}
|
|
163291
|
+
if (cursor === row.end) segs.push({ text: "", kind: "cursor" });
|
|
163292
|
+
return segs;
|
|
163293
|
+
}
|
|
163294
|
+
|
|
162400
163295
|
// src/tui/components/LineInput.tsx
|
|
162401
163296
|
var import_jsx_runtime3 = __toESM(require_jsx_runtime());
|
|
162402
163297
|
var MENU_MAX = 8;
|
|
162403
|
-
var
|
|
163298
|
+
var BP_ENABLE = "\x1B[?2004h";
|
|
163299
|
+
var BP_DISABLE = "\x1B[?2004l";
|
|
163300
|
+
function insertAt({ value, cursor }, text, pad2) {
|
|
163301
|
+
const chars = [...value];
|
|
163302
|
+
let insert = text;
|
|
163303
|
+
if (pad2) {
|
|
163304
|
+
const spaceBefore = cursor > 0 && chars[cursor - 1] !== " " ? " " : "";
|
|
163305
|
+
const spaceAfter = chars[cursor] === " " || chars[cursor] === "\n" ? "" : " ";
|
|
163306
|
+
insert = spaceBefore + text + spaceAfter;
|
|
163307
|
+
}
|
|
163308
|
+
const ins = [...insert];
|
|
163309
|
+
chars.splice(cursor, 0, ...ins);
|
|
163310
|
+
return { value: chars.join(""), cursor: cursor + ins.length };
|
|
163311
|
+
}
|
|
162404
163312
|
function LineInput({ prompt = G.prompt, placeholder = "", history = [], color = theme.accent, onSubmit, onPaste, commands = [], completePaths }) {
|
|
162405
163313
|
const [state, setState] = (0, import_react22.useState)({ value: "", cursor: 0 });
|
|
162406
163314
|
const [histIdx, setHistIdx] = (0, import_react22.useState)(-1);
|
|
@@ -162414,6 +163322,7 @@ function LineInput({ prompt = G.prompt, placeholder = "", history = [], color =
|
|
|
162414
163322
|
const sel = menu.length > 0 ? Math.min(selIdx ?? 0, menu.length - 1) : 0;
|
|
162415
163323
|
const histValueRef = (0, import_react22.useRef)(null);
|
|
162416
163324
|
const browsingHistory = histIdx >= 0 && state.value === histValueRef.current;
|
|
163325
|
+
const draftRef = (0, import_react22.useRef)({ value: "", cursor: 0 });
|
|
162417
163326
|
const arrowsToMenu = menuCapturesArrows(menu, isPathMenu && atToken ? "@" + atToken.prefix : state.value, browsingHistory);
|
|
162418
163327
|
(0, import_react22.useEffect)(() => {
|
|
162419
163328
|
setSelIdx(null);
|
|
@@ -162427,23 +163336,48 @@ function LineInput({ prompt = G.prompt, placeholder = "", history = [], color =
|
|
|
162427
163336
|
}
|
|
162428
163337
|
const pastingRef = (0, import_react22.useRef)(false);
|
|
162429
163338
|
const [pasting, setPasting] = (0, import_react22.useState)(false);
|
|
162430
|
-
const
|
|
162431
|
-
const
|
|
163339
|
+
const inputQueue = (0, import_react22.useRef)([]);
|
|
163340
|
+
const [inputVersion, wakeInput] = (0, import_react22.useState)(0);
|
|
163341
|
+
function insertPaste(raw) {
|
|
163342
|
+
const text = sanitizePastedText(raw);
|
|
163343
|
+
if (!text) return;
|
|
163344
|
+
const ph = collapsePaste(text);
|
|
163345
|
+
setState((s) => ph ? insertAt(s, ph, true) : insertAt(s, text, false));
|
|
163346
|
+
}
|
|
163347
|
+
const { internal_eventEmitter, setRawMode } = use_stdin_default();
|
|
163348
|
+
const { stdout } = use_stdout_default();
|
|
162432
163349
|
(0, import_react22.useEffect)(() => {
|
|
162433
|
-
const
|
|
162434
|
-
|
|
162435
|
-
|
|
162436
|
-
|
|
162437
|
-
|
|
162438
|
-
|
|
162439
|
-
|
|
162440
|
-
|
|
162441
|
-
|
|
163350
|
+
const reader = createTerminalInput((event) => {
|
|
163351
|
+
inputQueue.current.push(event);
|
|
163352
|
+
wakeInput((n) => n + 1);
|
|
163353
|
+
});
|
|
163354
|
+
setRawMode(true);
|
|
163355
|
+
stdout?.write(BP_ENABLE);
|
|
163356
|
+
internal_eventEmitter?.on("input", reader.write);
|
|
163357
|
+
return () => {
|
|
163358
|
+
internal_eventEmitter?.off("input", reader.write);
|
|
163359
|
+
reader.dispose();
|
|
163360
|
+
inputQueue.current = [];
|
|
163361
|
+
stdout?.write(BP_DISABLE);
|
|
163362
|
+
setRawMode(false);
|
|
162442
163363
|
};
|
|
162443
|
-
|
|
162444
|
-
|
|
162445
|
-
|
|
162446
|
-
|
|
163364
|
+
}, [internal_eventEmitter, stdout, setRawMode]);
|
|
163365
|
+
const { value, cursor } = state;
|
|
163366
|
+
const chars = [...value];
|
|
163367
|
+
const termColumns = stdout?.columns ?? 80;
|
|
163368
|
+
const termRows = stdout?.rows ?? 24;
|
|
163369
|
+
const promptWidth = estimateWidth(prompt);
|
|
163370
|
+
const columns = Math.max(10, termColumns - 5 - promptWidth);
|
|
163371
|
+
const inputRows = Math.max(2, Math.min(6, termRows - 16));
|
|
163372
|
+
const rows = layoutRows(chars, columns);
|
|
163373
|
+
const curRow = rowOfCursor(rows, cursor);
|
|
163374
|
+
const clipped = rows.length > inputRows;
|
|
163375
|
+
const visible = clipped ? inputRows - 1 : inputRows;
|
|
163376
|
+
const topRef = (0, import_react22.useRef)(0);
|
|
163377
|
+
topRef.current = scrollTop(rows.length, curRow, visible, topRef.current);
|
|
163378
|
+
const top = topRef.current;
|
|
163379
|
+
const spans = atomicSpans(value);
|
|
163380
|
+
function handleKey(input, key) {
|
|
162447
163381
|
if (key.return) {
|
|
162448
163382
|
if (pastingRef.current) return;
|
|
162449
163383
|
if (isPathMenu && selIdx !== null && menu.length > 0) {
|
|
@@ -162454,8 +163388,9 @@ function LineInput({ prompt = G.prompt, placeholder = "", history = [], color =
|
|
|
162454
163388
|
setState({ value: "", cursor: 0 });
|
|
162455
163389
|
setHistIdx(-1);
|
|
162456
163390
|
histValueRef.current = null;
|
|
163391
|
+
draftRef.current = { value: "", cursor: 0 };
|
|
162457
163392
|
setSelIdx(null);
|
|
162458
|
-
onSubmit(v);
|
|
163393
|
+
onSubmit(expandPastes(v));
|
|
162459
163394
|
return;
|
|
162460
163395
|
}
|
|
162461
163396
|
if (key.tab) {
|
|
@@ -162468,22 +163403,34 @@ function LineInput({ prompt = G.prompt, placeholder = "", history = [], color =
|
|
|
162468
163403
|
}
|
|
162469
163404
|
return;
|
|
162470
163405
|
}
|
|
162471
|
-
if (key.
|
|
162472
|
-
|
|
163406
|
+
if (key.delete) {
|
|
163407
|
+
setState(({ value: value2, cursor: cursor2 }) => {
|
|
163408
|
+
const chars2 = [...value2];
|
|
163409
|
+
const span = spanStartingAt(atomicSpans(value2), cursor2);
|
|
163410
|
+
chars2.splice(cursor2, span ? span.end - span.start : 1);
|
|
163411
|
+
return { value: chars2.join(""), cursor: cursor2 };
|
|
163412
|
+
});
|
|
163413
|
+
return;
|
|
163414
|
+
}
|
|
163415
|
+
if (key.backspace) {
|
|
162473
163416
|
setState(({ value: value2, cursor: cursor2 }) => {
|
|
162474
163417
|
if (cursor2 === 0) return { value: value2, cursor: cursor2 };
|
|
162475
163418
|
const chars2 = [...value2];
|
|
162476
|
-
|
|
162477
|
-
|
|
163419
|
+
const span = spanEndingAt(atomicSpans(value2), cursor2);
|
|
163420
|
+
const n = span ? span.end - span.start : 1;
|
|
163421
|
+
chars2.splice(cursor2 - n, n);
|
|
163422
|
+
return { value: chars2.join(""), cursor: cursor2 - n };
|
|
162478
163423
|
});
|
|
162479
163424
|
return;
|
|
162480
163425
|
}
|
|
162481
163426
|
if (key.leftArrow) {
|
|
162482
|
-
|
|
163427
|
+
const span = spanEndingAt(spans, cursor);
|
|
163428
|
+
setState(({ value: value2, cursor: cursor2 }) => ({ value: value2, cursor: span ? span.start : Math.max(0, cursor2 - 1) }));
|
|
162483
163429
|
return;
|
|
162484
163430
|
}
|
|
162485
163431
|
if (key.rightArrow) {
|
|
162486
|
-
|
|
163432
|
+
const span = spanStartingAt(spans, cursor);
|
|
163433
|
+
setState(({ value: value2, cursor: cursor2 }) => ({ value: value2, cursor: span ? span.end : Math.min([...value2].length, cursor2 + 1) }));
|
|
162487
163434
|
return;
|
|
162488
163435
|
}
|
|
162489
163436
|
if (key.upArrow) {
|
|
@@ -162491,8 +163438,15 @@ function LineInput({ prompt = G.prompt, placeholder = "", history = [], color =
|
|
|
162491
163438
|
setSelIdx((sel - 1 + menu.length) % menu.length);
|
|
162492
163439
|
return;
|
|
162493
163440
|
}
|
|
163441
|
+
const moved = moveVertical(chars, rows, cursor, -1);
|
|
163442
|
+
if (moved !== null) {
|
|
163443
|
+
setState({ value, cursor: moved });
|
|
163444
|
+
return;
|
|
163445
|
+
}
|
|
162494
163446
|
if (history.length === 0) return;
|
|
162495
|
-
const
|
|
163447
|
+
const base = browsingHistory ? histIdx : -1;
|
|
163448
|
+
if (base === -1) draftRef.current = state;
|
|
163449
|
+
const next = Math.min(base + 1, history.length - 1);
|
|
162496
163450
|
const v = history[next] ?? "";
|
|
162497
163451
|
setHistIdx(next);
|
|
162498
163452
|
histValueRef.current = v;
|
|
@@ -162504,10 +163458,16 @@ function LineInput({ prompt = G.prompt, placeholder = "", history = [], color =
|
|
|
162504
163458
|
setSelIdx((sel + 1) % menu.length);
|
|
162505
163459
|
return;
|
|
162506
163460
|
}
|
|
163461
|
+
const moved = moveVertical(chars, rows, cursor, 1);
|
|
163462
|
+
if (moved !== null) {
|
|
163463
|
+
setState({ value, cursor: moved });
|
|
163464
|
+
return;
|
|
163465
|
+
}
|
|
163466
|
+
if (!browsingHistory) return;
|
|
162507
163467
|
if (histIdx <= 0) {
|
|
162508
163468
|
setHistIdx(-1);
|
|
162509
163469
|
histValueRef.current = null;
|
|
162510
|
-
setState(
|
|
163470
|
+
setState(draftRef.current);
|
|
162511
163471
|
return;
|
|
162512
163472
|
}
|
|
162513
163473
|
const next = histIdx - 1;
|
|
@@ -162518,11 +163478,11 @@ function LineInput({ prompt = G.prompt, placeholder = "", history = [], color =
|
|
|
162518
163478
|
return;
|
|
162519
163479
|
}
|
|
162520
163480
|
if (key.ctrl && input === "a") {
|
|
162521
|
-
setState(({ value: value2 }) => ({ value: value2, cursor:
|
|
163481
|
+
setState(({ value: value2, cursor: cursor2 }) => ({ value: value2, cursor: lineStart([...value2], cursor2) }));
|
|
162522
163482
|
return;
|
|
162523
163483
|
}
|
|
162524
163484
|
if (key.ctrl && input === "e") {
|
|
162525
|
-
setState(({ value: value2 }) => ({ value: value2, cursor: [...value2]
|
|
163485
|
+
setState(({ value: value2, cursor: cursor2 }) => ({ value: value2, cursor: lineEnd([...value2], cursor2) }));
|
|
162526
163486
|
return;
|
|
162527
163487
|
}
|
|
162528
163488
|
if (key.ctrl && input === "v" && onPaste) {
|
|
@@ -162530,20 +163490,9 @@ function LineInput({ prompt = G.prompt, placeholder = "", history = [], color =
|
|
|
162530
163490
|
pastingRef.current = true;
|
|
162531
163491
|
setPasting(true);
|
|
162532
163492
|
void onPaste().then((r) => {
|
|
162533
|
-
if (r)
|
|
162534
|
-
|
|
162535
|
-
|
|
162536
|
-
let insert = r.insert;
|
|
162537
|
-
if (r.pad) {
|
|
162538
|
-
const spaceBefore = cursor2 > 0 && chars2[cursor2 - 1] !== " " ? " " : "";
|
|
162539
|
-
const spaceAfter = chars2[cursor2] === " " ? "" : " ";
|
|
162540
|
-
insert = spaceBefore + r.insert + spaceAfter;
|
|
162541
|
-
}
|
|
162542
|
-
const ins = [...insert];
|
|
162543
|
-
chars2.splice(cursor2, 0, ...ins);
|
|
162544
|
-
return { value: chars2.join(""), cursor: cursor2 + ins.length };
|
|
162545
|
-
});
|
|
162546
|
-
}
|
|
163493
|
+
if (!r) return;
|
|
163494
|
+
if (r.pad) setState((s) => insertAt(s, r.insert, true));
|
|
163495
|
+
else insertPaste(r.insert);
|
|
162547
163496
|
}).catch(() => {
|
|
162548
163497
|
}).finally(() => {
|
|
162549
163498
|
pastingRef.current = false;
|
|
@@ -162555,39 +163504,40 @@ function LineInput({ prompt = G.prompt, placeholder = "", history = [], color =
|
|
|
162555
163504
|
return;
|
|
162556
163505
|
}
|
|
162557
163506
|
if (input) {
|
|
162558
|
-
setState((
|
|
162559
|
-
const chars2 = [...value2];
|
|
162560
|
-
const ins = [...input];
|
|
162561
|
-
chars2.splice(cursor2, 0, ...ins);
|
|
162562
|
-
return { value: chars2.join(""), cursor: cursor2 + ins.length };
|
|
162563
|
-
});
|
|
163507
|
+
setState((s) => insertAt(s, input, false));
|
|
162564
163508
|
}
|
|
162565
|
-
}
|
|
162566
|
-
|
|
162567
|
-
|
|
162568
|
-
|
|
162569
|
-
|
|
162570
|
-
|
|
162571
|
-
|
|
162572
|
-
|
|
162573
|
-
const
|
|
163509
|
+
}
|
|
163510
|
+
(0, import_react22.useEffect)(() => {
|
|
163511
|
+
const event = inputQueue.current.shift();
|
|
163512
|
+
if (!event) return;
|
|
163513
|
+
if (event.kind === "paste") insertPaste(event.text);
|
|
163514
|
+
else handleKey(event.input, event.key);
|
|
163515
|
+
if (inputQueue.current.length) wakeInput((n) => n + 1);
|
|
163516
|
+
}, [inputVersion]);
|
|
163517
|
+
const pastingHint = pasting ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(Text, { dimColor: true, children: [
|
|
163518
|
+
" ",
|
|
163519
|
+
G.refresh,
|
|
163520
|
+
" \u7C98\u8D34\u4E2D\u2026"
|
|
163521
|
+
] }) : null;
|
|
163522
|
+
const indent = " ".repeat(promptWidth);
|
|
162574
163523
|
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(Box_default, { flexDirection: "column", children: [
|
|
162575
|
-
|
|
163524
|
+
value ? rows.slice(top, top + visible).map((row, i) => {
|
|
163525
|
+
const r = top + i;
|
|
163526
|
+
const segs = segmentRow(chars, row, spans, r === curRow ? cursor : null);
|
|
163527
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(Text, { wrap: "truncate-end", children: [
|
|
163528
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { color, children: r === 0 ? prompt : indent }),
|
|
163529
|
+
segs.map(
|
|
163530
|
+
(s, j) => s.kind === "cursor" ? s.text ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { inverse: true, children: s.text }, j) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { color, children: G.cursor }, j) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { color: s.kind === "placeholder" ? color : void 0, children: s.text }, j)
|
|
163531
|
+
),
|
|
163532
|
+
r === curRow ? pastingHint : null
|
|
163533
|
+
] }, r);
|
|
163534
|
+
}) : /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(Text, { children: [
|
|
162576
163535
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { color, children: prompt }),
|
|
162577
|
-
|
|
162578
|
-
|
|
162579
|
-
|
|
162580
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { children: after2 })
|
|
162581
|
-
] }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
|
|
162582
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { color, children: G.cursor }),
|
|
162583
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { dimColor: true, children: placeholder })
|
|
162584
|
-
] }),
|
|
162585
|
-
pasting ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(Text, { dimColor: true, children: [
|
|
162586
|
-
" ",
|
|
162587
|
-
G.refresh,
|
|
162588
|
-
" \u7C98\u8D34\u4E2D\u2026"
|
|
162589
|
-
] }) : null
|
|
163536
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { color, children: G.cursor }),
|
|
163537
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { dimColor: true, children: placeholder }),
|
|
163538
|
+
pastingHint
|
|
162590
163539
|
] }),
|
|
163540
|
+
clipped ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { dimColor: true, children: ` \u2026\u5171 ${rows.length} \u884C\uFF0C\u663E\u793A\u7B2C ${top + 1}-${top + visible} \u884C\uFF08${G.updown} \u79FB\u52A8\u5149\u6807\uFF09` }) : null,
|
|
162591
163541
|
menu.map((c, i) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(Text, { color: i === sel ? color : void 0, dimColor: i !== sel, children: [
|
|
162592
163542
|
i === sel ? G.menuPointer : " ",
|
|
162593
163543
|
c
|
|
@@ -162645,8 +163595,8 @@ function SelectList({ options: options2, multi = false, color = theme.accent, on
|
|
|
162645
163595
|
}
|
|
162646
163596
|
});
|
|
162647
163597
|
const { stdout } = use_stdout_default();
|
|
162648
|
-
const maxVisible = Math.max(
|
|
162649
|
-
const winStart = Math.
|
|
163598
|
+
const maxVisible = Math.max(1, Math.min(options2.length, Math.max(3, (stdout?.rows ?? 24) - 8)));
|
|
163599
|
+
const winStart = Math.max(0, Math.min(index - maxVisible + 1, options2.length - maxVisible));
|
|
162650
163600
|
const visible = options2.slice(winStart, winStart + maxVisible);
|
|
162651
163601
|
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(Box_default, { flexDirection: "column", children: [
|
|
162652
163602
|
winStart > 0 ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Text, { dimColor: true, children: ` \u2026 \u4E0A\u65B9\u8FD8\u6709 ${winStart} \u9879` }) : null,
|
|
@@ -162872,7 +163822,7 @@ import { promisify as promisify2 } from "util";
|
|
|
162872
163822
|
var execFileP2 = promisify2(execFile2);
|
|
162873
163823
|
var IMAGE_EXT_RE = /\.(png|jpe?g|gif|webp)$/i;
|
|
162874
163824
|
var pastedImages = /* @__PURE__ */ new Map();
|
|
162875
|
-
var
|
|
163825
|
+
var pasteSeq2 = 0;
|
|
162876
163826
|
var housekeepingDone = false;
|
|
162877
163827
|
function ensureHousekeeping(dir) {
|
|
162878
163828
|
if (housekeepingDone) return;
|
|
@@ -162944,11 +163894,11 @@ async function readClipboardText() {
|
|
|
162944
163894
|
} catch {
|
|
162945
163895
|
return null;
|
|
162946
163896
|
}
|
|
162947
|
-
const text = raw
|
|
163897
|
+
const text = sanitizePastedText(raw);
|
|
162948
163898
|
return text || null;
|
|
162949
163899
|
}
|
|
162950
163900
|
async function capturePastedImage() {
|
|
162951
|
-
const seq = ++
|
|
163901
|
+
const seq = ++pasteSeq2;
|
|
162952
163902
|
const file = await readClipboardImage(seq);
|
|
162953
163903
|
if (!file) return null;
|
|
162954
163904
|
const placeholder = `[\u56FE\u7247#${seq}]`;
|
|
@@ -163159,6 +164109,29 @@ async function runShellCommand(cmd, opts) {
|
|
|
163159
164109
|
return finalize(killed ? "killed" : "exited");
|
|
163160
164110
|
}
|
|
163161
164111
|
|
|
164112
|
+
// src/sessionSwitch.ts
|
|
164113
|
+
init_skills();
|
|
164114
|
+
init_agents();
|
|
164115
|
+
async function switchSession(core, sessionId) {
|
|
164116
|
+
let ready;
|
|
164117
|
+
const onReady = (d) => {
|
|
164118
|
+
ready = d;
|
|
164119
|
+
};
|
|
164120
|
+
core.once("session:ready", onReady);
|
|
164121
|
+
try {
|
|
164122
|
+
await core.createSession(sessionId);
|
|
164123
|
+
} finally {
|
|
164124
|
+
core.off("session:ready", onReady);
|
|
164125
|
+
}
|
|
164126
|
+
if (!ready) throw new Error("createSession \u5DF2\u8FD4\u56DE\u4F46\u672A\u6536\u5230 session:ready");
|
|
164127
|
+
const notes = [];
|
|
164128
|
+
applyDisabledSkills(core);
|
|
164129
|
+
await applyDisabledAgents(core).catch((e) => {
|
|
164130
|
+
notes.push(`subagent \u542F\u505C\u540D\u5355\u5E94\u7528\u5931\u8D25\uFF08harness agent \u53D6\u820D\u53EF\u80FD\u672A\u751F\u6548\uFF09\uFF1A${e instanceof Error ? e.message : String(e)}`);
|
|
164131
|
+
});
|
|
164132
|
+
return { ready, notes };
|
|
164133
|
+
}
|
|
164134
|
+
|
|
163162
164135
|
// src/tui/App.tsx
|
|
163163
164136
|
var import_jsx_runtime6 = __toESM(require_jsx_runtime());
|
|
163164
164137
|
function ShellRunningLine({ cmd }) {
|
|
@@ -163179,7 +164152,7 @@ function ShellRunningLine({ cmd }) {
|
|
|
163179
164152
|
}
|
|
163180
164153
|
var HELP = [
|
|
163181
164154
|
"/help \u672C\u5E2E\u52A9 \xB7 /model \u6A21\u578B\u914D\u7F6E \xB7 /skills skill \u542F\u505C \xB7 /agents \u4EBA\u8BBE\u542F\u505C \xB7 /mcp MCP \u5217\u8868 \xB7 /marketplace \u63D2\u4EF6\u5E02\u573A \xB7 /memory \u8BB0\u5FC6/\u4EBA\u8BBE\u6587\u4EF6 \xB7 /resume \u5207\u6362\u5386\u53F2\u4F1A\u8BDD \xB7 /status \u72B6\u6001 \xB7 /tools \u5DE5\u5177\u5217\u8868",
|
|
163182
|
-
"/theme [\u540D\u79F0] \u4E3B\u9898\u5207\u6362 \xB7 /glyphs [auto|fancy|ascii] \u5B57\u5F62\u98CE\u683C\uFF08cmd \u9ED8\u8BA4\u964D\u7EA7 ASCII \u9632\u9519\u4F4D\uFF09 \xB7 /thinking [on|off] \u601D\u8003\u5F00\u5173 \xB7 /clear /compact \u4E0A\u4E0B\u6587 \xB7 /exit \u9000\u51FA",
|
|
164155
|
+
"/theme [\u540D\u79F0] \u4E3B\u9898\u5207\u6362 \xB7 /glyphs [auto|fancy|ascii] \u5B57\u5F62\u98CE\u683C\uFF08cmd \u9ED8\u8BA4\u964D\u7EA7 ASCII \u9632\u9519\u4F4D\uFF09 \xB7 /thinking [on|off] \u601D\u8003\u5F00\u5173 \xB7 /clear \u6E05\u7A7A\u4E0A\u4E0B\u6587\uFF08\u5F00\u65B0\u4F1A\u8BDD\uFF0C\u65E7\u4F1A\u8BDD\u53EF /resume\uFF09\xB7 /compact \u538B\u7F29\u4E0A\u4E0B\u6587 \xB7 /exit \u9000\u51FA",
|
|
163183
164156
|
"/permissions [step-by-step|action-check|free-style] \u6743\u9650\u6A21\u5F0F\uFF08Shift+Tab \u5FAA\u73AF\u5207\u6362\uFF1Baction check \u53EA\u8BFB\u64CD\u4F5C\u514D\u786E\u8BA4\uFF0Cfree style \u5168\u90E8\u514D\u786E\u8BA4\uFF09",
|
|
163184
164157
|
"\u8F93\u5165 / \u663E\u793A\u547D\u4EE4\u83DC\u5355\uFF1ATab \u8865\u5168 \xB7 \u2191\u2193 \u9009\u62E9 \xB7 \u552F\u4E00\u524D\u7F00\u53EF\u76F4\u63A5\u6267\u884C\uFF08\u5982 /st \u2192 /status\uFF09",
|
|
163185
164158
|
"@\u6587\u4EF6\u8DEF\u5F84 \u5F15\u7528\u6587\u4EF6\uFF08\u8F93\u5165 @ \u5F39\u51FA\u8DEF\u5F84\u8865\u5168\uFF0CTab/\u2191\u2193 \u9009\u62E9\uFF1B\u56FE\u7247\u6269\u5C55\u540D\u81EA\u52A8\u8F6C\u591A\u6A21\u6001\uFF09 \xB7 Ctrl+V \u7C98\u8D34\u526A\u8D34\u677F\u56FE\u7247\u6216\u6587\u672C\uFF08\u56FE\u7247\u652F\u6301 macOS/Windows\uFF09 \xB7 Esc \u4E2D\u65AD\u5F53\u524D\u8F6E \xB7 \u2191\u2193 \u8F93\u5165\u5386\u53F2",
|
|
@@ -163320,17 +164293,9 @@ function App2({ bridge, cwd: cwd2, initialHistory, appConfig }) {
|
|
|
163320
164293
|
]);
|
|
163321
164294
|
if (!picked) return;
|
|
163322
164295
|
try {
|
|
163323
|
-
await
|
|
163324
|
-
|
|
163325
|
-
|
|
163326
|
-
bridge.setUsage(d.usage);
|
|
163327
|
-
resolve9();
|
|
163328
|
-
});
|
|
163329
|
-
bridge.core.createSession(picked).catch(reject2);
|
|
163330
|
-
});
|
|
163331
|
-
applyDisabledSkills(bridge.core);
|
|
163332
|
-
await applyDisabledAgents(bridge.core).catch(() => {
|
|
163333
|
-
});
|
|
164296
|
+
const { ready, notes } = await switchSession(bridge.core, picked);
|
|
164297
|
+
bridge.resetSession(ready);
|
|
164298
|
+
for (const n of notes) bridge.notice(n, "warn");
|
|
163334
164299
|
bridge.seedReplay(loadReplay(cwd2, picked));
|
|
163335
164300
|
bridge.notice(`\u5DF2\u5207\u6362\u5230\u4F1A\u8BDD ${picked}`, "success");
|
|
163336
164301
|
} catch (e) {
|
|
@@ -163544,7 +164509,7 @@ function App2({ bridge, cwd: cwd2, initialHistory, appConfig }) {
|
|
|
163544
164509
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Text, { color: theme[permMeta.tone], children: ` ${G.permMode} ${permMeta.label}` }),
|
|
163545
164510
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Text, { dimColor: true, children: `${G.sep}Shift+Tab switch` })
|
|
163546
164511
|
] }),
|
|
163547
|
-
showHint ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Text, { dimColor: true, children: ` Enter \u53D1\u9001${G.sep}${G.updown} \u5386\u53F2${G.sep}/help \u547D\u4EE4\uFF08/ \u53EF Tab \u8865\u5168\uFF09${G.sep}Ctrl+C \u9000\u51FA` }) : null
|
|
164512
|
+
showHint ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Text, { dimColor: true, children: ` Enter \u53D1\u9001${G.sep}${G.updown} \u884C/\u5386\u53F2${G.sep}/help \u547D\u4EE4\uFF08/ \u53EF Tab \u8865\u5168\uFF09${G.sep}Ctrl+C \u9000\u51FA` }) : null
|
|
163548
164513
|
] }) : null
|
|
163549
164514
|
] });
|
|
163550
164515
|
}
|
|
@@ -163617,7 +164582,7 @@ async function startTui(cwd2, options2 = {}) {
|
|
|
163617
164582
|
});
|
|
163618
164583
|
};
|
|
163619
164584
|
const GESTURE_GAP_MS = 300;
|
|
163620
|
-
const
|
|
164585
|
+
const SETTLE_MS2 = 150;
|
|
163621
164586
|
let lastResizeAt = 0;
|
|
163622
164587
|
let resizeTimer = null;
|
|
163623
164588
|
const onResize = () => {
|
|
@@ -163625,7 +164590,7 @@ async function startTui(cwd2, options2 = {}) {
|
|
|
163625
164590
|
if (now2 - lastResizeAt > GESTURE_GAP_MS) scrollOutViewport();
|
|
163626
164591
|
lastResizeAt = now2;
|
|
163627
164592
|
if (resizeTimer) clearTimeout(resizeTimer);
|
|
163628
|
-
resizeTimer = setTimeout(() => refreshTail(true),
|
|
164593
|
+
resizeTimer = setTimeout(() => refreshTail(true), SETTLE_MS2);
|
|
163629
164594
|
};
|
|
163630
164595
|
process.stdout.on("resize", onResize);
|
|
163631
164596
|
const guard = createScrollbackGuard(process.stdout, { onBurstEnd: () => refreshTail(false) });
|
|
@@ -163640,6 +164605,7 @@ async function startTui(cwd2, options2 = {}) {
|
|
|
163640
164605
|
process.stdout.off("resize", onResize);
|
|
163641
164606
|
if (resizeTimer) clearTimeout(resizeTimer);
|
|
163642
164607
|
guard.flush();
|
|
164608
|
+
process.stdout.write(`${ESC4}[?2004l`);
|
|
163643
164609
|
await core.dispose().catch(() => {
|
|
163644
164610
|
});
|
|
163645
164611
|
}
|