@cydm/pie 1.0.40 → 1.0.42
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +21 -2
- package/dist/builtin/extensions/ask-user/index.js +4 -4
- package/dist/builtin/extensions/changelog/index.js +1 -1
- package/dist/builtin/extensions/deploy/index.js +1 -1
- package/dist/builtin/extensions/document-attachments/index.js +1 -1
- package/dist/builtin/extensions/files/index.js +1 -1
- package/dist/builtin/extensions/init/index.js +1 -1
- package/dist/builtin/extensions/kimi-attachments/index.js +1 -1
- package/dist/builtin/extensions/plan-mode/index.js +4 -4
- package/dist/builtin/extensions/subagent/index.js +5 -5
- package/dist/builtin/extensions/todo/index.js +4 -4
- package/dist/chunks/{chunk-SNSSIR3E.js → chunk-2GVFMYIV.js} +583 -91
- package/dist/chunks/{chunk-7IFMKPHH.js → chunk-JVLAKTAS.js} +1 -1
- package/dist/chunks/{chunk-TG2EQLX2.js → chunk-LZQXQOPJ.js} +5 -1
- package/dist/chunks/{chunk-HPTWCKJE.js → chunk-NGQ3PUOC.js} +315 -157
- package/dist/chunks/{chunk-F6VAOGFL.js → chunk-VYAW6RAX.js} +2 -2
- package/dist/chunks/{src-N6MKSRB3.js → src-TQNFXZGK.js} +3 -3
- package/dist/chunks/{test-stream-6F6VZ44U.js → test-stream-IPBFEEWH.js} +2 -2
- package/dist/cli.js +1758 -612
- package/dist/code-intel-worker.cjs +5 -1
- package/models.schema.json +412 -93
- package/package.json +3 -3
package/dist/cli.js
CHANGED
|
@@ -19,7 +19,7 @@ import {
|
|
|
19
19
|
getThemesDir,
|
|
20
20
|
migrateConfigFromAgentDir,
|
|
21
21
|
truncateHeadTail
|
|
22
|
-
} from "./chunks/chunk-
|
|
22
|
+
} from "./chunks/chunk-VYAW6RAX.js";
|
|
23
23
|
import {
|
|
24
24
|
AGENTS_CONTEXT_FILE_NAME,
|
|
25
25
|
AgentSessionController,
|
|
@@ -69,8 +69,8 @@ import {
|
|
|
69
69
|
setExtensionEnabledInSettings,
|
|
70
70
|
shouldPreserveExecutionStateForUserText,
|
|
71
71
|
supersedeExecutionState
|
|
72
|
-
} from "./chunks/chunk-
|
|
73
|
-
import "./chunks/chunk-
|
|
72
|
+
} from "./chunks/chunk-2GVFMYIV.js";
|
|
73
|
+
import "./chunks/chunk-JVLAKTAS.js";
|
|
74
74
|
import {
|
|
75
75
|
Deref,
|
|
76
76
|
EMPTY_ASSISTANT_CONTENT_ERROR,
|
|
@@ -87,6 +87,7 @@ import {
|
|
|
87
87
|
Kind,
|
|
88
88
|
MODEL_CLASS_FALLBACK_ORDER,
|
|
89
89
|
Never,
|
|
90
|
+
OPENAI_COMPAT_PROTOCOL_ERROR_CODE,
|
|
90
91
|
Ref,
|
|
91
92
|
TransformDecode,
|
|
92
93
|
TransformDecodeCheckError,
|
|
@@ -105,7 +106,7 @@ import {
|
|
|
105
106
|
setLogger,
|
|
106
107
|
sortToolModelCandidatesByCapability,
|
|
107
108
|
type_exports
|
|
108
|
-
} from "./chunks/chunk-
|
|
109
|
+
} from "./chunks/chunk-NGQ3PUOC.js";
|
|
109
110
|
import {
|
|
110
111
|
resolveCliProjectRoot
|
|
111
112
|
} from "./chunks/chunk-NTYHFBUA.js";
|
|
@@ -113,7 +114,7 @@ import {
|
|
|
113
114
|
__commonJS,
|
|
114
115
|
__require,
|
|
115
116
|
__toESM
|
|
116
|
-
} from "./chunks/chunk-
|
|
117
|
+
} from "./chunks/chunk-LZQXQOPJ.js";
|
|
117
118
|
|
|
118
119
|
// ../../node_modules/color-name/index.js
|
|
119
120
|
var require_color_name = __commonJS({
|
|
@@ -985,15 +986,15 @@ var require_route = __commonJS({
|
|
|
985
986
|
};
|
|
986
987
|
}
|
|
987
988
|
function wrapConversion(toModel, graph) {
|
|
988
|
-
const
|
|
989
|
+
const path33 = [graph[toModel].parent, toModel];
|
|
989
990
|
let fn = conversions[graph[toModel].parent][toModel];
|
|
990
991
|
let cur = graph[toModel].parent;
|
|
991
992
|
while (graph[cur].parent) {
|
|
992
|
-
|
|
993
|
+
path33.unshift(graph[cur].parent);
|
|
993
994
|
fn = link(conversions[graph[cur].parent][cur], fn);
|
|
994
995
|
cur = graph[cur].parent;
|
|
995
996
|
}
|
|
996
|
-
fn.conversion =
|
|
997
|
+
fn.conversion = path33;
|
|
997
998
|
return fn;
|
|
998
999
|
}
|
|
999
1000
|
module.exports = function(fromModel) {
|
|
@@ -1233,7 +1234,7 @@ var require_has_flag = __commonJS({
|
|
|
1233
1234
|
var require_supports_color = __commonJS({
|
|
1234
1235
|
"../../node_modules/chalk/node_modules/supports-color/index.js"(exports, module) {
|
|
1235
1236
|
"use strict";
|
|
1236
|
-
var
|
|
1237
|
+
var os9 = __require("os");
|
|
1237
1238
|
var tty = __require("tty");
|
|
1238
1239
|
var hasFlag2 = require_has_flag();
|
|
1239
1240
|
var { env } = process;
|
|
@@ -1281,7 +1282,7 @@ var require_supports_color = __commonJS({
|
|
|
1281
1282
|
return min;
|
|
1282
1283
|
}
|
|
1283
1284
|
if (process.platform === "win32") {
|
|
1284
|
-
const osRelease =
|
|
1285
|
+
const osRelease = os9.release().split(".");
|
|
1285
1286
|
if (Number(osRelease[0]) >= 10 && Number(osRelease[2]) >= 10586) {
|
|
1286
1287
|
return Number(osRelease[2]) >= 14931 ? 3 : 2;
|
|
1287
1288
|
}
|
|
@@ -1916,7 +1917,7 @@ var require_core = __commonJS({
|
|
|
1916
1917
|
return match && match.index === 0;
|
|
1917
1918
|
}
|
|
1918
1919
|
var BACKREF_RE = /\[(?:[^\\\]]|\\.)*\]|\(\??|\\([1-9][0-9]*)|\\./;
|
|
1919
|
-
function
|
|
1920
|
+
function join21(regexps, separator = "|") {
|
|
1920
1921
|
let numCaptures = 0;
|
|
1921
1922
|
return regexps.map((regex) => {
|
|
1922
1923
|
numCaptures += 1;
|
|
@@ -2220,7 +2221,7 @@ var require_core = __commonJS({
|
|
|
2220
2221
|
this.exec = () => null;
|
|
2221
2222
|
}
|
|
2222
2223
|
const terminators = this.regexes.map((el) => el[1]);
|
|
2223
|
-
this.matcherRe = langRe(
|
|
2224
|
+
this.matcherRe = langRe(join21(terminators), true);
|
|
2224
2225
|
this.lastIndex = 0;
|
|
2225
2226
|
}
|
|
2226
2227
|
/** @param {string} s */
|
|
@@ -43957,8 +43958,8 @@ var require_dist = __commonJS({
|
|
|
43957
43958
|
});
|
|
43958
43959
|
|
|
43959
43960
|
// src/cli.ts
|
|
43960
|
-
import * as
|
|
43961
|
-
import * as
|
|
43961
|
+
import * as fs28 from "fs";
|
|
43962
|
+
import * as path32 from "path";
|
|
43962
43963
|
|
|
43963
43964
|
// src/cli-args.ts
|
|
43964
43965
|
var FLAGS_WITH_VALUES = /* @__PURE__ */ new Set(["--session-id", "--extension-path", "--developer-instructions", "--developer-instructions-file"]);
|
|
@@ -44373,17 +44374,211 @@ function installInteractiveConsoleInterception(runtimeLogger) {
|
|
|
44373
44374
|
// src/doctor.ts
|
|
44374
44375
|
import { spawnSync } from "node:child_process";
|
|
44375
44376
|
import { createRequire } from "node:module";
|
|
44376
|
-
import * as
|
|
44377
|
+
import * as fs5 from "node:fs";
|
|
44377
44378
|
import * as os from "node:os";
|
|
44378
|
-
import * as
|
|
44379
|
+
import * as path5 from "node:path";
|
|
44379
44380
|
import { delimiter } from "node:path";
|
|
44380
44381
|
|
|
44381
44382
|
// src/core/auth-storage.ts
|
|
44382
|
-
|
|
44383
|
+
import * as fs2 from "fs";
|
|
44384
|
+
import * as path2 from "path";
|
|
44385
|
+
var AUTH_LOCK_WRITE_GRACE_MS = 1e3;
|
|
44386
|
+
var DEFAULT_AUTH_LOCK_WAIT_MS = 2e3;
|
|
44387
|
+
var OAUTH_EXPIRY_SKEW_MS = 3e4;
|
|
44388
|
+
function syncSleepMs(ms) {
|
|
44389
|
+
if (ms <= 0) return;
|
|
44390
|
+
const cell = new Int32Array(new SharedArrayBuffer(4));
|
|
44391
|
+
Atomics.wait(cell, 0, 0, ms);
|
|
44392
|
+
}
|
|
44393
|
+
function processIsAlive(pid) {
|
|
44394
|
+
if (!Number.isSafeInteger(pid) || pid <= 0) return false;
|
|
44395
|
+
try {
|
|
44396
|
+
process.kill(pid, 0);
|
|
44397
|
+
return true;
|
|
44398
|
+
} catch (error) {
|
|
44399
|
+
return error.code === "EPERM";
|
|
44400
|
+
}
|
|
44401
|
+
}
|
|
44402
|
+
var InMemoryAuthStorageBackend = class {
|
|
44403
|
+
snapshot = { oauth: {} };
|
|
44404
|
+
read() {
|
|
44405
|
+
return { oauth: { ...this.snapshot.oauth } };
|
|
44406
|
+
}
|
|
44407
|
+
write(snapshot) {
|
|
44408
|
+
this.snapshot = { oauth: { ...snapshot.oauth } };
|
|
44409
|
+
}
|
|
44410
|
+
};
|
|
44411
|
+
var AUTH_FILE_VERSION = 1;
|
|
44412
|
+
var FileAuthStorageBackend = class {
|
|
44413
|
+
constructor(authPath = path2.join(getConfigDir(), "auth.json"), lockWaitMs = DEFAULT_AUTH_LOCK_WAIT_MS) {
|
|
44414
|
+
this.authPath = authPath;
|
|
44415
|
+
this.lockWaitMs = lockWaitMs;
|
|
44416
|
+
}
|
|
44417
|
+
authPath;
|
|
44418
|
+
lockWaitMs;
|
|
44419
|
+
errors = [];
|
|
44420
|
+
read() {
|
|
44421
|
+
try {
|
|
44422
|
+
if (!fs2.existsSync(this.authPath)) return { oauth: {} };
|
|
44423
|
+
const raw = JSON.parse(fs2.readFileSync(this.authPath, "utf8"));
|
|
44424
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return { oauth: {} };
|
|
44425
|
+
const oauth = raw.oauth;
|
|
44426
|
+
if (!oauth || typeof oauth !== "object" || Array.isArray(oauth)) return { oauth: {} };
|
|
44427
|
+
return { oauth: { ...oauth } };
|
|
44428
|
+
} catch (error) {
|
|
44429
|
+
this.errors.push(error instanceof Error ? error : new Error(String(error)));
|
|
44430
|
+
return { oauth: {} };
|
|
44431
|
+
}
|
|
44432
|
+
}
|
|
44433
|
+
/** A lock whose owner is gone (or that never got populated) may be reclaimed. */
|
|
44434
|
+
lockIsStale(lockPath) {
|
|
44435
|
+
const stat2 = fs2.lstatSync(lockPath);
|
|
44436
|
+
if (stat2.isSymbolicLink() || !stat2.isFile()) return true;
|
|
44437
|
+
let owner;
|
|
44438
|
+
try {
|
|
44439
|
+
owner = JSON.parse(fs2.readFileSync(lockPath, "utf8"));
|
|
44440
|
+
} catch {
|
|
44441
|
+
}
|
|
44442
|
+
const age = Date.now() - stat2.mtimeMs;
|
|
44443
|
+
const validOwner = owner?.version === 1 && Number.isSafeInteger(owner.pid) && typeof owner.token === "string" && typeof owner.createdAt === "number";
|
|
44444
|
+
if (validOwner && processIsAlive(Number(owner.pid))) return false;
|
|
44445
|
+
return validOwner || age > AUTH_LOCK_WRITE_GRACE_MS;
|
|
44446
|
+
}
|
|
44447
|
+
/** Atomically claim a stale lock via hard-link identity comparison before removing it. */
|
|
44448
|
+
reclaimStaleLock(lockPath) {
|
|
44449
|
+
if (!this.lockIsStale(lockPath)) return false;
|
|
44450
|
+
const claimPath = `${lockPath}.reclaim-${process.pid}-${Math.random().toString(36).slice(2)}`;
|
|
44451
|
+
try {
|
|
44452
|
+
fs2.linkSync(lockPath, claimPath);
|
|
44453
|
+
const claimed = fs2.lstatSync(claimPath);
|
|
44454
|
+
const current = fs2.lstatSync(lockPath);
|
|
44455
|
+
if (claimed.isSymbolicLink() || !claimed.isFile() || current.isSymbolicLink() || !current.isFile()) return false;
|
|
44456
|
+
if (claimed.dev !== current.dev || claimed.ino !== current.ino) return false;
|
|
44457
|
+
fs2.unlinkSync(lockPath);
|
|
44458
|
+
return true;
|
|
44459
|
+
} catch (error) {
|
|
44460
|
+
const code = error.code;
|
|
44461
|
+
if (code === "ENOENT" || code === "EEXIST") return false;
|
|
44462
|
+
throw error;
|
|
44463
|
+
} finally {
|
|
44464
|
+
try {
|
|
44465
|
+
fs2.unlinkSync(claimPath);
|
|
44466
|
+
} catch {
|
|
44467
|
+
}
|
|
44468
|
+
}
|
|
44469
|
+
}
|
|
44470
|
+
/** Acquire the fixed-path cross-process lock; the release fn is token-checked. */
|
|
44471
|
+
acquireLock(directory, lockPath) {
|
|
44472
|
+
fs2.mkdirSync(directory, { recursive: true, mode: 448 });
|
|
44473
|
+
const deadline = Date.now() + this.lockWaitMs;
|
|
44474
|
+
while (true) {
|
|
44475
|
+
const token = `${Math.random().toString(36).slice(2)}${Date.now().toString(36)}`;
|
|
44476
|
+
try {
|
|
44477
|
+
const fd = fs2.openSync(lockPath, "wx", 384);
|
|
44478
|
+
try {
|
|
44479
|
+
fs2.writeFileSync(fd, `${JSON.stringify({ version: 1, pid: process.pid, token, createdAt: Date.now() })}
|
|
44480
|
+
`);
|
|
44481
|
+
} finally {
|
|
44482
|
+
fs2.closeSync(fd);
|
|
44483
|
+
}
|
|
44484
|
+
return () => {
|
|
44485
|
+
try {
|
|
44486
|
+
const owner = JSON.parse(fs2.readFileSync(lockPath, "utf8"));
|
|
44487
|
+
if (owner?.token === token) fs2.unlinkSync(lockPath);
|
|
44488
|
+
} catch {
|
|
44489
|
+
}
|
|
44490
|
+
};
|
|
44491
|
+
} catch (error) {
|
|
44492
|
+
if (error.code !== "EEXIST") throw error;
|
|
44493
|
+
try {
|
|
44494
|
+
if (this.reclaimStaleLock(lockPath)) continue;
|
|
44495
|
+
} catch (reclaimError) {
|
|
44496
|
+
if (reclaimError.code === "ENOENT") continue;
|
|
44497
|
+
throw reclaimError;
|
|
44498
|
+
}
|
|
44499
|
+
if (Date.now() >= deadline) throw new Error(`timed out waiting for auth storage lock: ${lockPath}`);
|
|
44500
|
+
syncSleepMs(25);
|
|
44501
|
+
}
|
|
44502
|
+
}
|
|
44503
|
+
}
|
|
44504
|
+
/**
|
|
44505
|
+
* Merge-on-write under a fixed-path cross-process lock: re-read the file,
|
|
44506
|
+
* apply explicit removals (logout), merge our snapshot's oauth entries
|
|
44507
|
+
* over whatever is on disk (preserving unrelated edits), then write back
|
|
44508
|
+
* atomically with 0600 permissions.
|
|
44509
|
+
*/
|
|
44510
|
+
write(snapshot) {
|
|
44511
|
+
const directory = path2.dirname(this.authPath);
|
|
44512
|
+
const lockPath = `${this.authPath}.lock`;
|
|
44513
|
+
let release;
|
|
44514
|
+
try {
|
|
44515
|
+
release = this.acquireLock(directory, lockPath);
|
|
44516
|
+
try {
|
|
44517
|
+
const onDisk = this.read();
|
|
44518
|
+
const mergedOauth = { ...onDisk.oauth };
|
|
44519
|
+
for (const removed of snapshot.removedOauth ?? []) {
|
|
44520
|
+
delete mergedOauth[removed];
|
|
44521
|
+
}
|
|
44522
|
+
Object.assign(mergedOauth, snapshot.oauth);
|
|
44523
|
+
const merged = {
|
|
44524
|
+
version: AUTH_FILE_VERSION,
|
|
44525
|
+
oauth: mergedOauth
|
|
44526
|
+
};
|
|
44527
|
+
const temporary = `${this.authPath}.${process.pid}.tmp`;
|
|
44528
|
+
try {
|
|
44529
|
+
fs2.writeFileSync(temporary, `${JSON.stringify(merged, null, 2)}
|
|
44530
|
+
`, { flag: "wx", mode: 384 });
|
|
44531
|
+
fs2.renameSync(temporary, this.authPath);
|
|
44532
|
+
} finally {
|
|
44533
|
+
if (fs2.existsSync(temporary)) fs2.unlinkSync(temporary);
|
|
44534
|
+
}
|
|
44535
|
+
} finally {
|
|
44536
|
+
release();
|
|
44537
|
+
}
|
|
44538
|
+
} catch (error) {
|
|
44539
|
+
this.errors.push(error instanceof Error ? error : new Error(String(error)));
|
|
44540
|
+
}
|
|
44541
|
+
}
|
|
44542
|
+
drainErrors() {
|
|
44543
|
+
return this.errors.splice(0);
|
|
44544
|
+
}
|
|
44545
|
+
};
|
|
44546
|
+
var AuthStorage = class _AuthStorage {
|
|
44383
44547
|
providerApiKeys = /* @__PURE__ */ new Map();
|
|
44384
44548
|
runtimeApiKeys = /* @__PURE__ */ new Map();
|
|
44385
44549
|
providerEnvVars = /* @__PURE__ */ new Map();
|
|
44386
44550
|
legacyApiKey;
|
|
44551
|
+
oauthCredentials = /* @__PURE__ */ new Map();
|
|
44552
|
+
removedOauthProviders = /* @__PURE__ */ new Set();
|
|
44553
|
+
backend;
|
|
44554
|
+
constructor(backend) {
|
|
44555
|
+
this.backend = backend;
|
|
44556
|
+
if (backend) {
|
|
44557
|
+
for (const [provider, credentials] of Object.entries(backend.read().oauth)) {
|
|
44558
|
+
this.oauthCredentials.set(provider, credentials);
|
|
44559
|
+
}
|
|
44560
|
+
}
|
|
44561
|
+
}
|
|
44562
|
+
/** Runtime-only storage (no persistence). */
|
|
44563
|
+
static create() {
|
|
44564
|
+
return new _AuthStorage();
|
|
44565
|
+
}
|
|
44566
|
+
/** Storage bound to a persistence backend (OAuth credentials survive restarts). */
|
|
44567
|
+
static fromStorage(backend) {
|
|
44568
|
+
return new _AuthStorage(backend);
|
|
44569
|
+
}
|
|
44570
|
+
/** In-memory backend, for tests and embedders that manage their own persistence. */
|
|
44571
|
+
static inMemory() {
|
|
44572
|
+
return new _AuthStorage(new InMemoryAuthStorageBackend());
|
|
44573
|
+
}
|
|
44574
|
+
/** Drain buffered backend I/O errors (file backend only). */
|
|
44575
|
+
drainErrors() {
|
|
44576
|
+
const backend = this.backend;
|
|
44577
|
+
if (backend && typeof backend.drainErrors === "function") {
|
|
44578
|
+
return backend.drainErrors();
|
|
44579
|
+
}
|
|
44580
|
+
return [];
|
|
44581
|
+
}
|
|
44387
44582
|
setLegacyApiKey(apiKey) {
|
|
44388
44583
|
this.legacyApiKey = apiKey;
|
|
44389
44584
|
}
|
|
@@ -44411,20 +44606,104 @@ var AuthStorage = class {
|
|
|
44411
44606
|
getProviderEnvVar(provider) {
|
|
44412
44607
|
return this.providerEnvVars.get(provider);
|
|
44413
44608
|
}
|
|
44609
|
+
setOAuthCredentials(provider, credentials) {
|
|
44610
|
+
if (!credentials) {
|
|
44611
|
+
this.oauthCredentials.delete(provider);
|
|
44612
|
+
this.removedOauthProviders.add(provider);
|
|
44613
|
+
this.runtimeApiKeys.delete(provider);
|
|
44614
|
+
} else {
|
|
44615
|
+
this.oauthCredentials.set(provider, credentials);
|
|
44616
|
+
this.removedOauthProviders.delete(provider);
|
|
44617
|
+
}
|
|
44618
|
+
this.persistOAuth();
|
|
44619
|
+
}
|
|
44620
|
+
getOAuthCredentials(provider) {
|
|
44621
|
+
return this.oauthCredentials.get(provider);
|
|
44622
|
+
}
|
|
44623
|
+
/** Providers with persisted OAuth credentials (for restore/refresh passes). */
|
|
44624
|
+
getOAuthProviderNames() {
|
|
44625
|
+
return [...this.oauthCredentials.keys()];
|
|
44626
|
+
}
|
|
44627
|
+
persistOAuth() {
|
|
44628
|
+
if (!this.backend) return;
|
|
44629
|
+
this.backend.write({
|
|
44630
|
+
oauth: Object.fromEntries(this.oauthCredentials),
|
|
44631
|
+
removedOauth: [...this.removedOauthProviders]
|
|
44632
|
+
});
|
|
44633
|
+
}
|
|
44414
44634
|
getApiKey(provider, explicitEnvVar) {
|
|
44415
44635
|
const envVar = explicitEnvVar || this.getProviderEnvVar(provider);
|
|
44416
44636
|
const envApiKey = envVar && typeof process !== "undefined" ? process.env?.[envVar] : void 0;
|
|
44417
|
-
return this.runtimeApiKeys.get(provider) || this.providerApiKeys.get(provider) || envApiKey || this.legacyApiKey;
|
|
44637
|
+
return this.runtimeApiKeys.get(provider) || this.providerApiKeys.get(provider) || envApiKey || this.usableOAuthAccessToken(provider) || this.legacyApiKey;
|
|
44638
|
+
}
|
|
44639
|
+
/** Persisted OAuth access token, when it exists and has not expired. */
|
|
44640
|
+
usableOAuthAccessToken(provider) {
|
|
44641
|
+
const credentials = this.oauthCredentials.get(provider);
|
|
44642
|
+
if (!credentials?.access) return void 0;
|
|
44643
|
+
if (credentials.expires !== void 0 && credentials.expires <= Date.now() + OAUTH_EXPIRY_SKEW_MS) {
|
|
44644
|
+
return void 0;
|
|
44645
|
+
}
|
|
44646
|
+
return credentials.access;
|
|
44418
44647
|
}
|
|
44419
44648
|
};
|
|
44420
44649
|
|
|
44421
44650
|
// src/core/model-registry.ts
|
|
44422
|
-
import * as
|
|
44423
|
-
import * as
|
|
44651
|
+
import * as fs3 from "fs";
|
|
44652
|
+
import * as path3 from "path";
|
|
44424
44653
|
import { createHash, createHmac, randomBytes } from "node:crypto";
|
|
44654
|
+
var MODEL_OVERRIDE_KEYS = [
|
|
44655
|
+
"name",
|
|
44656
|
+
"reasoning",
|
|
44657
|
+
"input",
|
|
44658
|
+
"cost",
|
|
44659
|
+
"contextWindow",
|
|
44660
|
+
"maxTokens",
|
|
44661
|
+
"headers",
|
|
44662
|
+
"compat",
|
|
44663
|
+
"cacheCapability",
|
|
44664
|
+
"webSearch",
|
|
44665
|
+
"fileCapabilities",
|
|
44666
|
+
"displayName"
|
|
44667
|
+
];
|
|
44668
|
+
function validateExtensionProviderConfig(provider, config) {
|
|
44669
|
+
if (!provider || provider !== provider.trim() || /\s/.test(provider)) throw new Error(`registerProvider: invalid provider id "${provider}"`);
|
|
44670
|
+
if (!config || typeof config !== "object") throw new Error("registerProvider: config must be an object");
|
|
44671
|
+
if (typeof config.baseUrl !== "string" || !config.baseUrl) throw new Error(`registerProvider("${provider}"): baseUrl is required`);
|
|
44672
|
+
try {
|
|
44673
|
+
new URL(config.baseUrl);
|
|
44674
|
+
} catch {
|
|
44675
|
+
throw new Error(`registerProvider("${provider}"): baseUrl must be an absolute URL`);
|
|
44676
|
+
}
|
|
44677
|
+
assertHeadersShape(config.headers, `registerProvider("${provider}")`);
|
|
44678
|
+
for (const model of config.models ?? []) {
|
|
44679
|
+
if (!model || typeof model.id !== "string" || !model.id.trim() || /\s/.test(model.id)) throw new Error(`registerProvider("${provider}"): invalid model id`);
|
|
44680
|
+
assertHeadersShape(model.headers, `registerProvider("${provider}") model "${model.id}"`);
|
|
44681
|
+
validateCompat(model.compat, `registerProvider("${provider}") model "${model.id}" compat`);
|
|
44682
|
+
validateCacheCapability(model.cacheCapability, `registerProvider("${provider}") model "${model.id}" cacheCapability`);
|
|
44683
|
+
validateWebSearchCapability(model.webSearch, `registerProvider("${provider}") model "${model.id}" webSearch`);
|
|
44684
|
+
validateFileCapabilities(model.fileCapabilities, `registerProvider("${provider}") model "${model.id}" fileCapabilities`);
|
|
44685
|
+
}
|
|
44686
|
+
}
|
|
44425
44687
|
var REMOTE_CATALOG_CACHE_VERSION = 4;
|
|
44426
44688
|
var SUPPORTED_REMOTE_APIS = /* @__PURE__ */ new Set(["openai-completions", "anthropic-messages"]);
|
|
44427
44689
|
var MAX_REMOTE_CATALOG_BYTES = 2 * 1024 * 1024;
|
|
44690
|
+
var OAUTH_RESTORE_LEEWAY_MS = 6e4;
|
|
44691
|
+
var OAUTH_REFRESH_TIMEOUT_MS = 3e4;
|
|
44692
|
+
function withTimeout(promise, ms, timeoutMessage) {
|
|
44693
|
+
return new Promise((resolve7, reject) => {
|
|
44694
|
+
const timer = setTimeout(() => reject(new Error(timeoutMessage)), ms);
|
|
44695
|
+
Promise.resolve(promise).then(
|
|
44696
|
+
(value) => {
|
|
44697
|
+
clearTimeout(timer);
|
|
44698
|
+
resolve7(value);
|
|
44699
|
+
},
|
|
44700
|
+
(error) => {
|
|
44701
|
+
clearTimeout(timer);
|
|
44702
|
+
reject(error);
|
|
44703
|
+
}
|
|
44704
|
+
);
|
|
44705
|
+
});
|
|
44706
|
+
}
|
|
44428
44707
|
var REMOTE_REFRESH_LOCK_WAIT_MS = 6e4;
|
|
44429
44708
|
var REMOTE_REFRESH_LOCK_WRITE_GRACE_MS = 1e3;
|
|
44430
44709
|
var remoteRefreshTails = /* @__PURE__ */ new Map();
|
|
@@ -44448,40 +44727,40 @@ function remoteIdentityDigest(remote) {
|
|
|
44448
44727
|
return createHash("sha256").update(JSON.stringify({ version: REMOTE_CATALOG_CACHE_VERSION, ...canonicalRemote(remote) })).digest("hex");
|
|
44449
44728
|
}
|
|
44450
44729
|
function remoteCacheDirectory(modelsPath) {
|
|
44451
|
-
return
|
|
44730
|
+
return path3.join(path3.dirname(modelsPath), "cache", "remote-catalogs");
|
|
44452
44731
|
}
|
|
44453
44732
|
function safeRemoteCacheDirectory(modelsPath, create) {
|
|
44454
|
-
const configDirectory =
|
|
44455
|
-
if (!
|
|
44733
|
+
const configDirectory = path3.dirname(modelsPath);
|
|
44734
|
+
if (!fs3.existsSync(configDirectory)) {
|
|
44456
44735
|
if (!create) return void 0;
|
|
44457
|
-
|
|
44736
|
+
fs3.mkdirSync(configDirectory, { recursive: true, mode: 448 });
|
|
44458
44737
|
}
|
|
44459
|
-
const configRoot =
|
|
44460
|
-
const cacheRoot =
|
|
44738
|
+
const configRoot = fs3.realpathSync(configDirectory);
|
|
44739
|
+
const cacheRoot = path3.join(configDirectory, "cache");
|
|
44461
44740
|
const directory = remoteCacheDirectory(modelsPath);
|
|
44462
44741
|
for (const candidate of [cacheRoot, directory]) {
|
|
44463
|
-
if (!
|
|
44742
|
+
if (!fs3.existsSync(candidate)) {
|
|
44464
44743
|
if (!create) return void 0;
|
|
44465
|
-
|
|
44744
|
+
fs3.mkdirSync(candidate, { mode: 448 });
|
|
44466
44745
|
}
|
|
44467
|
-
const stat2 =
|
|
44746
|
+
const stat2 = fs3.lstatSync(candidate);
|
|
44468
44747
|
if (stat2.isSymbolicLink() || !stat2.isDirectory()) throw new Error(`remote catalog cache path must be a real directory: ${candidate}`);
|
|
44469
|
-
const canonical =
|
|
44470
|
-
const
|
|
44471
|
-
if (
|
|
44748
|
+
const canonical = fs3.realpathSync(candidate);
|
|
44749
|
+
const relative3 = path3.relative(configRoot, canonical);
|
|
44750
|
+
if (relative3 === ".." || relative3.startsWith(`..${path3.sep}`) || path3.isAbsolute(relative3)) {
|
|
44472
44751
|
throw new Error(`remote catalog cache path escapes the config directory: ${candidate}`);
|
|
44473
44752
|
}
|
|
44474
44753
|
try {
|
|
44475
|
-
|
|
44754
|
+
fs3.chmodSync(candidate, 448);
|
|
44476
44755
|
} catch {
|
|
44477
44756
|
}
|
|
44478
44757
|
}
|
|
44479
44758
|
return directory;
|
|
44480
44759
|
}
|
|
44481
44760
|
function remoteRefreshLockPath(modelsPath) {
|
|
44482
|
-
return
|
|
44761
|
+
return path3.join(remoteCacheDirectory(modelsPath), ".refresh.lock");
|
|
44483
44762
|
}
|
|
44484
|
-
function
|
|
44763
|
+
function processIsAlive2(pid) {
|
|
44485
44764
|
if (!Number.isSafeInteger(pid) || pid <= 0) return false;
|
|
44486
44765
|
try {
|
|
44487
44766
|
process.kill(pid, 0);
|
|
@@ -44491,40 +44770,40 @@ function processIsAlive(pid) {
|
|
|
44491
44770
|
}
|
|
44492
44771
|
}
|
|
44493
44772
|
function remoteRefreshLockIsStale(lockPath) {
|
|
44494
|
-
const stat2 =
|
|
44773
|
+
const stat2 = fs3.lstatSync(lockPath);
|
|
44495
44774
|
if (stat2.isSymbolicLink() || !stat2.isFile()) throw new Error(`remote catalog refresh lock must be a regular file: ${lockPath}`);
|
|
44496
44775
|
let owner;
|
|
44497
44776
|
try {
|
|
44498
|
-
owner = JSON.parse(
|
|
44777
|
+
owner = JSON.parse(fs3.readFileSync(lockPath, "utf8"));
|
|
44499
44778
|
} catch {
|
|
44500
44779
|
}
|
|
44501
44780
|
const age = Date.now() - stat2.mtimeMs;
|
|
44502
44781
|
const validOwner = owner?.version === 1 && Number.isSafeInteger(owner.pid) && typeof owner.token === "string" && typeof owner.createdAt === "number";
|
|
44503
|
-
if (validOwner &&
|
|
44782
|
+
if (validOwner && processIsAlive2(Number(owner.pid))) return false;
|
|
44504
44783
|
return validOwner || age > REMOTE_REFRESH_LOCK_WRITE_GRACE_MS;
|
|
44505
44784
|
}
|
|
44506
44785
|
function sameFileIdentity(left, right, leftPath, rightPath) {
|
|
44507
44786
|
if (left.dev !== right.dev || left.ino !== right.ino) return false;
|
|
44508
44787
|
if (left.ino !== 0) return true;
|
|
44509
|
-
return left.size === right.size &&
|
|
44788
|
+
return left.size === right.size && fs3.readFileSync(leftPath).equals(fs3.readFileSync(rightPath));
|
|
44510
44789
|
}
|
|
44511
44790
|
function reclaimStaleRemoteRefreshLock(lockPath) {
|
|
44512
44791
|
if (!remoteRefreshLockIsStale(lockPath)) return false;
|
|
44513
44792
|
const claimPath = `${lockPath}.reclaim-${process.pid}-${randomBytes(8).toString("hex")}`;
|
|
44514
44793
|
try {
|
|
44515
|
-
|
|
44516
|
-
const claimed =
|
|
44517
|
-
const current =
|
|
44794
|
+
fs3.linkSync(lockPath, claimPath);
|
|
44795
|
+
const claimed = fs3.lstatSync(claimPath);
|
|
44796
|
+
const current = fs3.lstatSync(lockPath);
|
|
44518
44797
|
if (claimed.isSymbolicLink() || !claimed.isFile() || current.isSymbolicLink() || !current.isFile() || !sameFileIdentity(claimed, current, claimPath, lockPath)) return false;
|
|
44519
44798
|
if (!remoteRefreshLockIsStale(claimPath)) return false;
|
|
44520
|
-
|
|
44799
|
+
fs3.unlinkSync(lockPath);
|
|
44521
44800
|
return true;
|
|
44522
44801
|
} catch (error) {
|
|
44523
44802
|
if (["ENOENT", "EEXIST"].includes(error.code ?? "")) return false;
|
|
44524
44803
|
throw error;
|
|
44525
44804
|
} finally {
|
|
44526
44805
|
try {
|
|
44527
|
-
|
|
44806
|
+
fs3.unlinkSync(claimPath);
|
|
44528
44807
|
} catch {
|
|
44529
44808
|
}
|
|
44530
44809
|
}
|
|
@@ -44536,19 +44815,19 @@ async function acquireRemoteRefreshLock(modelsPath) {
|
|
|
44536
44815
|
while (true) {
|
|
44537
44816
|
const token = randomBytes(16).toString("hex");
|
|
44538
44817
|
try {
|
|
44539
|
-
const fd =
|
|
44818
|
+
const fd = fs3.openSync(lockPath, "wx", 384);
|
|
44540
44819
|
try {
|
|
44541
|
-
|
|
44820
|
+
fs3.writeFileSync(fd, `${JSON.stringify({ version: 1, pid: process.pid, token, createdAt: Date.now() })}
|
|
44542
44821
|
`);
|
|
44543
44822
|
} finally {
|
|
44544
|
-
|
|
44823
|
+
fs3.closeSync(fd);
|
|
44545
44824
|
}
|
|
44546
44825
|
return () => {
|
|
44547
44826
|
try {
|
|
44548
|
-
const stat2 =
|
|
44827
|
+
const stat2 = fs3.lstatSync(lockPath);
|
|
44549
44828
|
if (stat2.isSymbolicLink() || !stat2.isFile()) return;
|
|
44550
|
-
const current = JSON.parse(
|
|
44551
|
-
if (current.token === token)
|
|
44829
|
+
const current = JSON.parse(fs3.readFileSync(lockPath, "utf8"));
|
|
44830
|
+
if (current.token === token) fs3.unlinkSync(lockPath);
|
|
44552
44831
|
} catch {
|
|
44553
44832
|
}
|
|
44554
44833
|
};
|
|
@@ -44561,29 +44840,29 @@ async function acquireRemoteRefreshLock(modelsPath) {
|
|
|
44561
44840
|
throw lockError;
|
|
44562
44841
|
}
|
|
44563
44842
|
if (Date.now() >= deadline) throw new Error(`timed out waiting for remote catalog refresh lock: ${lockPath}`);
|
|
44564
|
-
await new Promise((
|
|
44843
|
+
await new Promise((resolve7) => setTimeout(resolve7, 25));
|
|
44565
44844
|
}
|
|
44566
44845
|
}
|
|
44567
44846
|
}
|
|
44568
44847
|
function readSafeRemoteCacheFile(directory, filepath) {
|
|
44569
|
-
const stat2 =
|
|
44848
|
+
const stat2 = fs3.lstatSync(filepath);
|
|
44570
44849
|
if (stat2.isSymbolicLink() || !stat2.isFile()) throw new Error(`remote catalog cache entry must be a regular file: ${filepath}`);
|
|
44571
|
-
const canonicalDirectory =
|
|
44572
|
-
const canonicalFile =
|
|
44573
|
-
const
|
|
44574
|
-
if (!
|
|
44850
|
+
const canonicalDirectory = fs3.realpathSync(directory);
|
|
44851
|
+
const canonicalFile = fs3.realpathSync(filepath);
|
|
44852
|
+
const relative3 = path3.relative(canonicalDirectory, canonicalFile);
|
|
44853
|
+
if (!relative3 || relative3 === ".." || relative3.startsWith(`..${path3.sep}`) || path3.isAbsolute(relative3)) {
|
|
44575
44854
|
throw new Error(`remote catalog cache entry escapes its cache directory: ${filepath}`);
|
|
44576
44855
|
}
|
|
44577
|
-
return
|
|
44856
|
+
return fs3.readFileSync(filepath);
|
|
44578
44857
|
}
|
|
44579
44858
|
function credentialNamespaceKey(modelsPath, create) {
|
|
44580
44859
|
const directory = safeRemoteCacheDirectory(modelsPath, create);
|
|
44581
44860
|
if (!directory) return void 0;
|
|
44582
|
-
const keyPath =
|
|
44583
|
-
if (!
|
|
44584
|
-
if (create && !
|
|
44861
|
+
const keyPath = path3.join(directory, ".credential-namespace-key");
|
|
44862
|
+
if (!fs3.existsSync(keyPath) && !create) return void 0;
|
|
44863
|
+
if (create && !fs3.existsSync(keyPath)) {
|
|
44585
44864
|
try {
|
|
44586
|
-
|
|
44865
|
+
fs3.writeFileSync(keyPath, randomBytes(32), { flag: "wx", mode: 384 });
|
|
44587
44866
|
} catch (error) {
|
|
44588
44867
|
if (!isPlainRecord(error) || error.code !== "EEXIST") throw error;
|
|
44589
44868
|
}
|
|
@@ -44591,7 +44870,7 @@ function credentialNamespaceKey(modelsPath, create) {
|
|
|
44591
44870
|
const key = readSafeRemoteCacheFile(directory, keyPath);
|
|
44592
44871
|
if (key.byteLength !== 32) throw new Error("remote catalog credential namespace key is invalid");
|
|
44593
44872
|
try {
|
|
44594
|
-
|
|
44873
|
+
fs3.chmodSync(keyPath, 384);
|
|
44595
44874
|
} catch {
|
|
44596
44875
|
}
|
|
44597
44876
|
return key;
|
|
@@ -44602,28 +44881,28 @@ function credentialFingerprint(modelsPath, remote, credential, create = false) {
|
|
|
44602
44881
|
return createHmac("sha256", key).update(remoteIdentityDigest(remote)).update("\0").update(credential).digest("hex");
|
|
44603
44882
|
}
|
|
44604
44883
|
function remoteCachePath(modelsPath, remote, fingerprint) {
|
|
44605
|
-
return
|
|
44884
|
+
return path3.join(remoteCacheDirectory(modelsPath), `${remoteIdentityDigest(remote)}-${fingerprint}.json`);
|
|
44606
44885
|
}
|
|
44607
44886
|
function pruneLegacyRemoteCatalogCaches(modelsPath) {
|
|
44608
44887
|
const directory = safeRemoteCacheDirectory(modelsPath, false);
|
|
44609
44888
|
if (!directory) return;
|
|
44610
|
-
for (const name of
|
|
44889
|
+
for (const name of fs3.readdirSync(directory)) {
|
|
44611
44890
|
if (!name.endsWith(".json")) continue;
|
|
44612
|
-
const filepath =
|
|
44891
|
+
const filepath = path3.join(directory, name);
|
|
44613
44892
|
try {
|
|
44614
44893
|
const value = JSON.parse(readSafeRemoteCacheFile(directory, filepath).toString("utf8"));
|
|
44615
44894
|
if (value.version === REMOTE_CATALOG_CACHE_VERSION) continue;
|
|
44616
44895
|
} catch {
|
|
44617
44896
|
let stat2;
|
|
44618
44897
|
try {
|
|
44619
|
-
stat2 =
|
|
44898
|
+
stat2 = fs3.lstatSync(filepath);
|
|
44620
44899
|
} catch {
|
|
44621
44900
|
continue;
|
|
44622
44901
|
}
|
|
44623
44902
|
if (stat2.isSymbolicLink() || !stat2.isFile()) continue;
|
|
44624
44903
|
}
|
|
44625
44904
|
try {
|
|
44626
|
-
|
|
44905
|
+
fs3.unlinkSync(filepath);
|
|
44627
44906
|
} catch {
|
|
44628
44907
|
}
|
|
44629
44908
|
}
|
|
@@ -44632,7 +44911,7 @@ function scrubActiveSecretsFromRemoteCaches(modelsPath, activeSecrets) {
|
|
|
44632
44911
|
if (activeSecrets.length === 0) return;
|
|
44633
44912
|
const directory = safeRemoteCacheDirectory(modelsPath, false);
|
|
44634
44913
|
if (!directory) return;
|
|
44635
|
-
const handle =
|
|
44914
|
+
const handle = fs3.opendirSync(directory);
|
|
44636
44915
|
try {
|
|
44637
44916
|
let scanned = 0;
|
|
44638
44917
|
while (true) {
|
|
@@ -44642,14 +44921,14 @@ function scrubActiveSecretsFromRemoteCaches(modelsPath, activeSecrets) {
|
|
|
44642
44921
|
const isCache = entry.name.endsWith(".json");
|
|
44643
44922
|
const isOrphanedTemporary = /^\..+\.json\.\d+\.[0-9a-f]+\.tmp$/.test(entry.name);
|
|
44644
44923
|
if (!entry.isFile() || !isCache && !isOrphanedTemporary) continue;
|
|
44645
|
-
const filepath =
|
|
44924
|
+
const filepath = path3.join(directory, entry.name);
|
|
44646
44925
|
try {
|
|
44647
44926
|
const raw = readSafeRemoteCacheFile(directory, filepath).toString("utf8");
|
|
44648
44927
|
assertNoSecretSubstrings(raw, activeSecrets, "remote catalog cache");
|
|
44649
44928
|
} catch {
|
|
44650
44929
|
try {
|
|
44651
|
-
const stat2 =
|
|
44652
|
-
if (!stat2.isSymbolicLink() && stat2.isFile())
|
|
44930
|
+
const stat2 = fs3.lstatSync(filepath);
|
|
44931
|
+
if (!stat2.isSymbolicLink() && stat2.isFile()) fs3.unlinkSync(filepath);
|
|
44653
44932
|
} catch {
|
|
44654
44933
|
}
|
|
44655
44934
|
}
|
|
@@ -44720,7 +44999,7 @@ function readRemoteCache(modelsPath, remote, fingerprint, activeSecrets) {
|
|
|
44720
44999
|
const directory = safeRemoteCacheDirectory(modelsPath, false);
|
|
44721
45000
|
if (!directory) return void 0;
|
|
44722
45001
|
const tombstone = remoteCacheTombstonePath(modelsPath, remote, fingerprint);
|
|
44723
|
-
if (
|
|
45002
|
+
if (fs3.existsSync(tombstone)) return void 0;
|
|
44724
45003
|
const value = JSON.parse(readSafeRemoteCacheFile(directory, remoteCachePath(modelsPath, remote, fingerprint)).toString("utf8"));
|
|
44725
45004
|
if (value.version !== REMOTE_CATALOG_CACHE_VERSION || !sameRemoteIdentity(value.identity, remote) || value.credentialFingerprint !== fingerprint || typeof value.payloadSha256 !== "string" || value.payloadSha256 !== payloadDigest(value.payload)) return void 0;
|
|
44726
45005
|
assertNoSecretSubstrings({ etag: value.etag, payload: value.payload }, activeSecrets, "remote catalog cache");
|
|
@@ -44740,23 +45019,23 @@ function removeRemoteCache(modelsPath, remote, fingerprint) {
|
|
|
44740
45019
|
const directory = safeRemoteCacheDirectory(modelsPath, false);
|
|
44741
45020
|
if (!directory) return;
|
|
44742
45021
|
const filepath = remoteCachePath(modelsPath, remote, fingerprint);
|
|
44743
|
-
if (
|
|
44744
|
-
const stat2 =
|
|
45022
|
+
if (fs3.existsSync(filepath)) {
|
|
45023
|
+
const stat2 = fs3.lstatSync(filepath);
|
|
44745
45024
|
if (stat2.isSymbolicLink() || !stat2.isFile()) throw new Error(`remote catalog cache entry is not a regular file: ${filepath}`);
|
|
44746
|
-
|
|
45025
|
+
fs3.unlinkSync(filepath);
|
|
44747
45026
|
}
|
|
44748
45027
|
const tombstone = remoteCacheTombstonePath(modelsPath, remote, fingerprint);
|
|
44749
|
-
if (
|
|
45028
|
+
if (fs3.existsSync(tombstone)) fs3.unlinkSync(tombstone);
|
|
44750
45029
|
return;
|
|
44751
45030
|
} catch (error) {
|
|
44752
45031
|
const directory = safeRemoteCacheDirectory(modelsPath, true);
|
|
44753
45032
|
if (!directory) throw error;
|
|
44754
45033
|
const tombstone = remoteCacheTombstonePath(modelsPath, remote, fingerprint);
|
|
44755
45034
|
try {
|
|
44756
|
-
|
|
45035
|
+
fs3.writeFileSync(tombstone, `${JSON.stringify({ version: 1, revoked: true })}
|
|
44757
45036
|
`, { flag: "wx", mode: 384 });
|
|
44758
45037
|
} catch (tombstoneError) {
|
|
44759
|
-
if (!
|
|
45038
|
+
if (!fs3.existsSync(tombstone)) throw tombstoneError;
|
|
44760
45039
|
}
|
|
44761
45040
|
}
|
|
44762
45041
|
}
|
|
@@ -44818,7 +45097,7 @@ function validatedRemoteCatalogs(value) {
|
|
|
44818
45097
|
}
|
|
44819
45098
|
function validateModelsDocumentShape(value) {
|
|
44820
45099
|
if (!isPlainRecord(value)) throw new Error("models.json must be an object");
|
|
44821
|
-
assertOnlyKeys(value, ["$schema", "profiles", "remoteCatalogs", "defaults"], "models.json");
|
|
45100
|
+
assertOnlyKeys(value, ["$schema", "profiles", "remoteCatalogs", "defaults", "modelOverrides"], "models.json");
|
|
44822
45101
|
if (value.$schema !== void 0 && (typeof value.$schema !== "string" || !value.$schema)) throw new Error("models.json $schema must be a non-empty string");
|
|
44823
45102
|
const hasProfiles = Object.prototype.hasOwnProperty.call(value, "profiles");
|
|
44824
45103
|
const hasRemoteCatalogs = Object.prototype.hasOwnProperty.call(value, "remoteCatalogs");
|
|
@@ -44831,6 +45110,7 @@ function validateModelsDocumentShape(value) {
|
|
|
44831
45110
|
validateProfileConfig(profileId, profile);
|
|
44832
45111
|
}
|
|
44833
45112
|
}
|
|
45113
|
+
validateModelOverrides(value.modelOverrides);
|
|
44834
45114
|
if (value.defaults !== void 0) {
|
|
44835
45115
|
if (!isPlainRecord(value.defaults)) throw new Error("models.json defaults must be an object");
|
|
44836
45116
|
assertOnlyKeys(value.defaults, ["provider", "modelId", "modelClasses", "toolModels"], "models.json defaults");
|
|
@@ -44915,6 +45195,8 @@ function validateCompat(value, label) {
|
|
|
44915
45195
|
"requiresAssistantAfterToolResult",
|
|
44916
45196
|
"requiresThinkingAsText",
|
|
44917
45197
|
"supportsStrictMode",
|
|
45198
|
+
"streamRequiresDone",
|
|
45199
|
+
"streamRequiresFinishReason",
|
|
44918
45200
|
"cacheCapability"
|
|
44919
45201
|
], label);
|
|
44920
45202
|
if (value.forcedReasoningEffort !== void 0 && value.forcedReasoningEffort !== "none") {
|
|
@@ -44928,7 +45210,9 @@ function validateCompat(value, label) {
|
|
|
44928
45210
|
"requiresToolResultName",
|
|
44929
45211
|
"requiresAssistantAfterToolResult",
|
|
44930
45212
|
"requiresThinkingAsText",
|
|
44931
|
-
"supportsStrictMode"
|
|
45213
|
+
"supportsStrictMode",
|
|
45214
|
+
"streamRequiresDone",
|
|
45215
|
+
"streamRequiresFinishReason"
|
|
44932
45216
|
]) assertOptionalBoolean(value[field], `${label}.${field}`);
|
|
44933
45217
|
if (value.maxTokensField !== void 0 && value.maxTokensField !== "max_completion_tokens" && value.maxTokensField !== "max_tokens") {
|
|
44934
45218
|
throw new Error(`${label}.maxTokensField is invalid`);
|
|
@@ -45107,6 +45391,46 @@ async function readResponseTextBounded(response, maxBytes) {
|
|
|
45107
45391
|
reader.releaseLock();
|
|
45108
45392
|
}
|
|
45109
45393
|
}
|
|
45394
|
+
function deepMergePlainRecords(base, patch) {
|
|
45395
|
+
const result = isPlainRecord(base) ? { ...base } : {};
|
|
45396
|
+
if (!isPlainRecord(patch)) return result;
|
|
45397
|
+
for (const [key, value] of Object.entries(patch)) {
|
|
45398
|
+
const current = result[key];
|
|
45399
|
+
result[key] = isPlainRecord(current) && isPlainRecord(value) ? deepMergePlainRecords(current, value) : value;
|
|
45400
|
+
}
|
|
45401
|
+
return result;
|
|
45402
|
+
}
|
|
45403
|
+
function validateModelOverrides(value) {
|
|
45404
|
+
if (value === void 0) return;
|
|
45405
|
+
if (!isPlainRecord(value)) throw new Error("models.json modelOverrides must be an object");
|
|
45406
|
+
for (const [key, override] of Object.entries(value)) {
|
|
45407
|
+
if (!/^[^\s/]+\/[^\s/]+$/.test(key)) throw new Error(`models.json modelOverrides key must be "provider/modelId": ${key}`);
|
|
45408
|
+
if (!isPlainRecord(override)) throw new Error(`models.json modelOverrides["${key}"] must be an object`);
|
|
45409
|
+
assertOnlyKeys(override, [...MODEL_OVERRIDE_KEYS], `models.json modelOverrides["${key}"]`);
|
|
45410
|
+
if (override.name !== void 0 && (typeof override.name !== "string" || !override.name.trim())) throw new Error(`models.json modelOverrides["${key}"].name must be a non-empty string`);
|
|
45411
|
+
if (override.displayName !== void 0 && (typeof override.displayName !== "string" || !override.displayName.trim())) throw new Error(`models.json modelOverrides["${key}"].displayName must be a non-empty string`);
|
|
45412
|
+
if (override.reasoning !== void 0 && typeof override.reasoning !== "boolean") throw new Error(`models.json modelOverrides["${key}"].reasoning must be a boolean`);
|
|
45413
|
+
if (override.input !== void 0 && (!Array.isArray(override.input) || override.input.length === 0 || override.input.some((kind) => !["text", "image", "audio", "video"].includes(String(kind))))) throw new Error(`models.json modelOverrides["${key}"].input is invalid`);
|
|
45414
|
+
if (override.contextWindow !== void 0 && (!Number.isInteger(override.contextWindow) || Number(override.contextWindow) < 1)) throw new Error(`models.json modelOverrides["${key}"].contextWindow is invalid`);
|
|
45415
|
+
if (override.maxTokens !== void 0 && (!Number.isInteger(override.maxTokens) || Number(override.maxTokens) < 1)) throw new Error(`models.json modelOverrides["${key}"].maxTokens is invalid`);
|
|
45416
|
+
if (override.cost !== void 0) {
|
|
45417
|
+
if (!isPlainRecord(override.cost)) throw new Error(`models.json modelOverrides["${key}"].cost must be an object`);
|
|
45418
|
+
for (const field of ["input", "output", "cacheRead", "cacheWrite"]) {
|
|
45419
|
+
if (override.cost[field] !== void 0 && (typeof override.cost[field] !== "number" || Number.isNaN(override.cost[field]))) throw new Error(`models.json modelOverrides["${key}"].cost.${field} must be a number`);
|
|
45420
|
+
}
|
|
45421
|
+
}
|
|
45422
|
+
if (override.headers !== void 0) {
|
|
45423
|
+
if (!isPlainRecord(override.headers)) throw new Error(`models.json modelOverrides["${key}"].headers must be an object`);
|
|
45424
|
+
for (const [headerName, headerValue] of Object.entries(override.headers)) {
|
|
45425
|
+
if (typeof headerValue !== "string") throw new Error(`models.json modelOverrides["${key}"].headers.${headerName} must be a string`);
|
|
45426
|
+
}
|
|
45427
|
+
}
|
|
45428
|
+
validateCompat(override.compat, `models.json modelOverrides["${key}"].compat`);
|
|
45429
|
+
validateCacheCapability(override.cacheCapability, `models.json modelOverrides["${key}"].cacheCapability`);
|
|
45430
|
+
validateWebSearchCapability(override.webSearch, `models.json modelOverrides["${key}"].webSearch`);
|
|
45431
|
+
validateFileCapabilities(override.fileCapabilities, `models.json modelOverrides["${key}"].fileCapabilities`);
|
|
45432
|
+
}
|
|
45433
|
+
}
|
|
45110
45434
|
function validateProfileConfig(profileId, profileConfig) {
|
|
45111
45435
|
if (!isPlainRecord(profileConfig)) throw new Error(`Profile "${profileId}" must be an object.`);
|
|
45112
45436
|
assertOnlyKeys(profileConfig, [
|
|
@@ -45201,12 +45525,14 @@ var ModelRegistry = class {
|
|
|
45201
45525
|
modelsPath;
|
|
45202
45526
|
configDefaults;
|
|
45203
45527
|
modelOrder = [];
|
|
45528
|
+
modelOverrides = {};
|
|
45529
|
+
extensionOAuthProviders = /* @__PURE__ */ new Map();
|
|
45204
45530
|
usingExternalProviders = false;
|
|
45205
45531
|
localProfileIds = /* @__PURE__ */ new Set();
|
|
45206
45532
|
remoteCredentialEnvByProvider = /* @__PURE__ */ new Map();
|
|
45207
45533
|
remoteCatalogByProvider = /* @__PURE__ */ new Map();
|
|
45208
45534
|
remoteCredentialFingerprintByCatalog = /* @__PURE__ */ new Map();
|
|
45209
|
-
constructor(authStorage, modelsPath =
|
|
45535
|
+
constructor(authStorage, modelsPath = path3.join(getConfigDir(), "models.json")) {
|
|
45210
45536
|
this.authStorage = authStorage;
|
|
45211
45537
|
this.modelsPath = modelsPath;
|
|
45212
45538
|
this.load();
|
|
@@ -45227,6 +45553,7 @@ var ModelRegistry = class {
|
|
|
45227
45553
|
this.loadError = void 0;
|
|
45228
45554
|
this.models.clear();
|
|
45229
45555
|
this.modelOrder = [];
|
|
45556
|
+
this.modelOverrides = {};
|
|
45230
45557
|
this.configDefaults = void 0;
|
|
45231
45558
|
this.usingExternalProviders = false;
|
|
45232
45559
|
this.localProfileIds.clear();
|
|
@@ -45239,16 +45566,17 @@ var ModelRegistry = class {
|
|
|
45239
45566
|
this.loadError = error instanceof Error ? error.message : String(error);
|
|
45240
45567
|
return;
|
|
45241
45568
|
}
|
|
45242
|
-
if (!
|
|
45569
|
+
if (!fs3.existsSync(this.modelsPath)) {
|
|
45243
45570
|
return;
|
|
45244
45571
|
}
|
|
45245
45572
|
try {
|
|
45246
|
-
const raw = JSON.parse(
|
|
45573
|
+
const raw = JSON.parse(fs3.readFileSync(this.modelsPath, "utf-8"));
|
|
45247
45574
|
if (raw && typeof raw === "object" && "providers" in raw) {
|
|
45248
45575
|
throw new Error('Legacy models.json format is no longer supported. Use top-level "profiles".');
|
|
45249
45576
|
}
|
|
45250
45577
|
validateModelsDocumentShape(raw);
|
|
45251
45578
|
const remotes = validatedRemoteCatalogs(raw.remoteCatalogs);
|
|
45579
|
+
this.modelOverrides = raw.modelOverrides ?? {};
|
|
45252
45580
|
const activeRemoteSecrets = configuredRemoteCredentialValues(raw);
|
|
45253
45581
|
scrubActiveSecretsFromRemoteCaches(this.modelsPath, activeRemoteSecrets);
|
|
45254
45582
|
const configuredProviders = Object.entries(raw.profiles || {});
|
|
@@ -45334,10 +45662,40 @@ var ModelRegistry = class {
|
|
|
45334
45662
|
this.registerRemoteProfile(provider, profile, identity.apiKeyEnv, owner);
|
|
45335
45663
|
}
|
|
45336
45664
|
}
|
|
45665
|
+
this.applyModelOverrides();
|
|
45337
45666
|
} catch (error) {
|
|
45338
45667
|
this.loadError = error instanceof Error ? error.message : String(error);
|
|
45339
45668
|
}
|
|
45340
45669
|
}
|
|
45670
|
+
/**
|
|
45671
|
+
* Re-apply models.json modelOverrides after any (re)materialization pass
|
|
45672
|
+
* (initial load, remote catalog refresh). Overrides patch descriptive
|
|
45673
|
+
* fields only; nested plain objects deep-merge, arrays/scalars replace.
|
|
45674
|
+
*/
|
|
45675
|
+
applyModelOverrides() {
|
|
45676
|
+
for (const [key, override] of Object.entries(this.modelOverrides)) {
|
|
45677
|
+
const slash = key.indexOf("/");
|
|
45678
|
+
if (slash <= 0) continue;
|
|
45679
|
+
const provider = key.slice(0, slash);
|
|
45680
|
+
const modelId = key.slice(slash + 1);
|
|
45681
|
+
const existing = this.models.get(provider)?.get(modelId);
|
|
45682
|
+
if (!existing) continue;
|
|
45683
|
+
const patched = { ...existing };
|
|
45684
|
+
if (override.name !== void 0) patched.name = override.name;
|
|
45685
|
+
if (override.displayName !== void 0) patched.displayName = override.displayName;
|
|
45686
|
+
if (override.reasoning !== void 0) patched.reasoning = override.reasoning;
|
|
45687
|
+
if (override.input !== void 0) patched.input = [...override.input];
|
|
45688
|
+
if (override.contextWindow !== void 0) patched.contextWindow = override.contextWindow;
|
|
45689
|
+
if (override.maxTokens !== void 0) patched.maxTokens = override.maxTokens;
|
|
45690
|
+
if (override.cost !== void 0) patched.cost = { ...existing.cost, ...override.cost };
|
|
45691
|
+
if (override.headers !== void 0) patched.headers = { ...existing.headers, ...override.headers };
|
|
45692
|
+
if (override.compat !== void 0) patched.compat = deepMergePlainRecords(existing.compat, override.compat);
|
|
45693
|
+
if (override.cacheCapability !== void 0) patched.cacheCapability = { ...existing.cacheCapability, ...override.cacheCapability };
|
|
45694
|
+
if (override.webSearch !== void 0) patched.webSearch = { ...existing.webSearch, ...override.webSearch };
|
|
45695
|
+
if (override.fileCapabilities !== void 0) patched.fileCapabilities = deepMergePlainRecords(existing.fileCapabilities, override.fileCapabilities);
|
|
45696
|
+
this.upsert(patched);
|
|
45697
|
+
}
|
|
45698
|
+
}
|
|
45341
45699
|
registerRemoteProfile(provider, providerConfig, credentialEnv, catalogId) {
|
|
45342
45700
|
if (this.localProfileIds.has(provider)) return;
|
|
45343
45701
|
const existingCatalog = this.remoteCatalogByProvider.get(provider);
|
|
@@ -45397,27 +45755,28 @@ var ModelRegistry = class {
|
|
|
45397
45755
|
for (const [provider, profile] of candidates) {
|
|
45398
45756
|
this.registerRemoteProfile(provider, profile, credentialEnv, catalogId);
|
|
45399
45757
|
}
|
|
45758
|
+
this.applyModelOverrides();
|
|
45400
45759
|
}
|
|
45401
45760
|
writeRemoteCache(remote, fingerprint, value) {
|
|
45402
45761
|
const directory = safeRemoteCacheDirectory(this.modelsPath, true);
|
|
45403
45762
|
const target = remoteCachePath(this.modelsPath, remote, fingerprint);
|
|
45404
|
-
if (
|
|
45405
|
-
const stat2 =
|
|
45763
|
+
if (fs3.existsSync(target)) {
|
|
45764
|
+
const stat2 = fs3.lstatSync(target);
|
|
45406
45765
|
if (stat2.isSymbolicLink() || !stat2.isFile()) throw new Error(`remote catalog cache entry must be a regular file: ${target}`);
|
|
45407
45766
|
}
|
|
45408
|
-
const temporary =
|
|
45767
|
+
const temporary = path3.join(directory, `.${path3.basename(target)}.${process.pid}.${randomBytes(8).toString("hex")}.tmp`);
|
|
45409
45768
|
try {
|
|
45410
|
-
|
|
45769
|
+
fs3.writeFileSync(temporary, `${JSON.stringify(value)}
|
|
45411
45770
|
`, { flag: "wx", mode: 384 });
|
|
45412
|
-
|
|
45771
|
+
fs3.renameSync(temporary, target);
|
|
45413
45772
|
const tombstone = remoteCacheTombstonePath(this.modelsPath, remote, fingerprint);
|
|
45414
|
-
if (
|
|
45773
|
+
if (fs3.existsSync(tombstone)) fs3.unlinkSync(tombstone);
|
|
45415
45774
|
} finally {
|
|
45416
|
-
if (
|
|
45775
|
+
if (fs3.existsSync(temporary)) fs3.unlinkSync(temporary);
|
|
45417
45776
|
}
|
|
45418
45777
|
}
|
|
45419
45778
|
refreshRemoteCatalogs() {
|
|
45420
|
-
const queueKey =
|
|
45779
|
+
const queueKey = path3.resolve(this.modelsPath);
|
|
45421
45780
|
const previous = remoteRefreshTails.get(queueKey) ?? Promise.resolve();
|
|
45422
45781
|
const refresh = previous.then(() => this.performRemoteCatalogRefresh());
|
|
45423
45782
|
const tail = refresh.catch(() => void 0);
|
|
@@ -45442,14 +45801,14 @@ var ModelRegistry = class {
|
|
|
45442
45801
|
this.loadError = error instanceof Error ? error.message : String(error);
|
|
45443
45802
|
return;
|
|
45444
45803
|
}
|
|
45445
|
-
if (!
|
|
45804
|
+
if (!fs3.existsSync(this.modelsPath)) {
|
|
45446
45805
|
for (const owner of new Set(this.remoteCatalogByProvider.values())) this.removeRemoteCatalogProfiles(owner);
|
|
45447
45806
|
this.remoteCredentialFingerprintByCatalog.clear();
|
|
45448
45807
|
return;
|
|
45449
45808
|
}
|
|
45450
45809
|
let config;
|
|
45451
45810
|
try {
|
|
45452
|
-
const decoded = JSON.parse(
|
|
45811
|
+
const decoded = JSON.parse(fs3.readFileSync(this.modelsPath, "utf8"));
|
|
45453
45812
|
validateModelsDocumentShape(decoded);
|
|
45454
45813
|
config = decoded;
|
|
45455
45814
|
} catch {
|
|
@@ -45457,6 +45816,7 @@ var ModelRegistry = class {
|
|
|
45457
45816
|
this.remoteCredentialFingerprintByCatalog.clear();
|
|
45458
45817
|
return;
|
|
45459
45818
|
}
|
|
45819
|
+
this.modelOverrides = config.modelOverrides ?? {};
|
|
45460
45820
|
let remotes;
|
|
45461
45821
|
try {
|
|
45462
45822
|
remotes = validatedRemoteCatalogs(config.remoteCatalogs);
|
|
@@ -45738,11 +46098,131 @@ var ModelRegistry = class {
|
|
|
45738
46098
|
if (remoteEnv) return typeof process !== "undefined" ? process.env?.[remoteEnv] : void 0;
|
|
45739
46099
|
return this.authStorage.getApiKey(provider);
|
|
45740
46100
|
}
|
|
46101
|
+
/**
|
|
46102
|
+
* Register a provider at runtime (extension API). Not persisted to
|
|
46103
|
+
* models.json: runtime registrations live for the process unless the
|
|
46104
|
+
* extension registers them again. Returns an unregister handle that
|
|
46105
|
+
* restores the provider's previous state (models, order, env binding).
|
|
46106
|
+
*
|
|
46107
|
+
* When the provider already exists (local profile or remote catalog),
|
|
46108
|
+
* models are merged by id: registered models upsert over existing entries
|
|
46109
|
+
* with the same id; existing entries not named in `config.models` survive.
|
|
46110
|
+
* baseUrl/api/headers patch every model of that provider (existing and
|
|
46111
|
+
* new) when provided.
|
|
46112
|
+
*/
|
|
46113
|
+
registerProvider(provider, config) {
|
|
46114
|
+
validateExtensionProviderConfig(provider, config);
|
|
46115
|
+
const previousModels = this.models.get(provider) ? new Map(this.models.get(provider)) : void 0;
|
|
46116
|
+
const previousOrder = [...this.modelOrder];
|
|
46117
|
+
const previousEnvVar = this.authStorage.getProviderEnvVar(provider);
|
|
46118
|
+
if (config.apiKey) {
|
|
46119
|
+
this.authStorage.setRuntimeApiKey(provider, config.apiKey);
|
|
46120
|
+
}
|
|
46121
|
+
if (config.apiKeyEnv) {
|
|
46122
|
+
this.authStorage.setProviderEnvVar(provider, config.apiKeyEnv);
|
|
46123
|
+
}
|
|
46124
|
+
const existing = this.models.get(provider);
|
|
46125
|
+
if (existing && (config.baseUrl || config.api || config.headers)) {
|
|
46126
|
+
for (const model of existing.values()) {
|
|
46127
|
+
this.upsert({
|
|
46128
|
+
...model,
|
|
46129
|
+
baseUrl: config.baseUrl || model.baseUrl,
|
|
46130
|
+
api: config.api || model.api,
|
|
46131
|
+
headers: config.headers ? { ...model.headers, ...config.headers } : model.headers
|
|
46132
|
+
});
|
|
46133
|
+
}
|
|
46134
|
+
}
|
|
46135
|
+
for (const customModel of config.models ?? []) {
|
|
46136
|
+
const baseModel = this.models.get(provider)?.get(customModel.id);
|
|
46137
|
+
this.upsert({
|
|
46138
|
+
id: customModel.id,
|
|
46139
|
+
name: customModel.name || baseModel?.name || customModel.id,
|
|
46140
|
+
api: config.api || baseModel?.api || "openai-completions",
|
|
46141
|
+
provider,
|
|
46142
|
+
baseUrl: config.baseUrl || baseModel?.baseUrl || "",
|
|
46143
|
+
reasoning: customModel.reasoning ?? baseModel?.reasoning ?? false,
|
|
46144
|
+
input: customModel.input || baseModel?.input || ["text"],
|
|
46145
|
+
cost: customModel.cost || baseModel?.cost || { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
46146
|
+
contextWindow: customModel.contextWindow || baseModel?.contextWindow || 128e3,
|
|
46147
|
+
maxTokens: customModel.maxTokens || baseModel?.maxTokens || 8192,
|
|
46148
|
+
apiKeyEnv: config.apiKeyEnv || baseModel?.apiKeyEnv,
|
|
46149
|
+
displayName: customModel.displayName || baseModel?.displayName || provider,
|
|
46150
|
+
headers: config.headers ? { ...baseModel?.headers, ...config.headers, ...customModel.headers } : customModel.headers || baseModel?.headers,
|
|
46151
|
+
compat: customModel.compat || baseModel?.compat,
|
|
46152
|
+
cacheCapability: customModel.cacheCapability || baseModel?.cacheCapability,
|
|
46153
|
+
webSearch: customModel.webSearch || baseModel?.webSearch,
|
|
46154
|
+
fileCapabilities: customModel.fileCapabilities || baseModel?.fileCapabilities
|
|
46155
|
+
});
|
|
46156
|
+
}
|
|
46157
|
+
this.applyModelOverrides();
|
|
46158
|
+
this.usingExternalProviders = true;
|
|
46159
|
+
const registeredOAuth = config.oauth;
|
|
46160
|
+
if (registeredOAuth) this.extensionOAuthProviders.set(provider, registeredOAuth);
|
|
46161
|
+
return () => {
|
|
46162
|
+
if (previousModels) this.models.set(provider, previousModels);
|
|
46163
|
+
else this.models.delete(provider);
|
|
46164
|
+
this.modelOrder = previousOrder;
|
|
46165
|
+
this.authStorage.setRuntimeApiKey(provider, void 0);
|
|
46166
|
+
this.authStorage.setProviderEnvVar(provider, previousEnvVar);
|
|
46167
|
+
if (registeredOAuth) this.extensionOAuthProviders.delete(provider);
|
|
46168
|
+
this.applyModelOverrides();
|
|
46169
|
+
};
|
|
46170
|
+
}
|
|
46171
|
+
/** OAuth flow declared by an extension-registered provider, if any. */
|
|
46172
|
+
getExtensionOAuth(provider) {
|
|
46173
|
+
return this.extensionOAuthProviders.get(provider);
|
|
46174
|
+
}
|
|
46175
|
+
/**
|
|
46176
|
+
* Restore persisted OAuth credentials now that extensions have registered
|
|
46177
|
+
* their providers: refresh expired tokens when the extension declares
|
|
46178
|
+
* refreshToken, and drop the runtime binding when a token cannot be
|
|
46179
|
+
* recovered (getApiKey then reports the provider as unauthenticated).
|
|
46180
|
+
* Never throws; returns per-provider outcomes for the host to surface.
|
|
46181
|
+
*/
|
|
46182
|
+
async restorePersistedOAuth() {
|
|
46183
|
+
const authStorage = this.authStorage;
|
|
46184
|
+
const results = [];
|
|
46185
|
+
for (const provider of authStorage.getOAuthProviderNames()) {
|
|
46186
|
+
const credentials = authStorage.getOAuthCredentials(provider);
|
|
46187
|
+
if (!credentials?.access) continue;
|
|
46188
|
+
const expired = credentials.expires !== void 0 && credentials.expires <= Date.now() + OAUTH_RESTORE_LEEWAY_MS;
|
|
46189
|
+
if (!expired) continue;
|
|
46190
|
+
const oauth = this.extensionOAuthProviders.get(provider);
|
|
46191
|
+
if (!oauth?.refreshToken || !credentials.refresh) {
|
|
46192
|
+
authStorage.setRuntimeApiKey(provider, void 0);
|
|
46193
|
+
results.push({ provider, action: "not-refreshable", detail: "token expired and no refresh flow" });
|
|
46194
|
+
continue;
|
|
46195
|
+
}
|
|
46196
|
+
try {
|
|
46197
|
+
const next = await withTimeout(
|
|
46198
|
+
oauth.refreshToken({
|
|
46199
|
+
access: credentials.access,
|
|
46200
|
+
refresh: credentials.refresh,
|
|
46201
|
+
expires: credentials.expires
|
|
46202
|
+
}),
|
|
46203
|
+
OAUTH_REFRESH_TIMEOUT_MS,
|
|
46204
|
+
`${provider} refreshToken timed out after ${OAUTH_REFRESH_TIMEOUT_MS}ms`
|
|
46205
|
+
);
|
|
46206
|
+
const merged = { ...credentials, ...next };
|
|
46207
|
+
authStorage.setOAuthCredentials(provider, merged);
|
|
46208
|
+
authStorage.setRuntimeApiKey(provider, merged.access);
|
|
46209
|
+
results.push({ provider, action: "refreshed" });
|
|
46210
|
+
} catch (error) {
|
|
46211
|
+
authStorage.setRuntimeApiKey(provider, void 0);
|
|
46212
|
+
results.push({ provider, action: "refresh-failed", detail: error instanceof Error ? error.message : String(error) });
|
|
46213
|
+
}
|
|
46214
|
+
}
|
|
46215
|
+
return results;
|
|
46216
|
+
}
|
|
46217
|
+
/** Credential store backing this registry (for OAuth persistence). */
|
|
46218
|
+
getAuthStorage() {
|
|
46219
|
+
return this.authStorage;
|
|
46220
|
+
}
|
|
45741
46221
|
};
|
|
45742
46222
|
|
|
45743
46223
|
// src/core/settings-manager.ts
|
|
45744
|
-
import * as
|
|
45745
|
-
import * as
|
|
46224
|
+
import * as fs4 from "fs";
|
|
46225
|
+
import * as path4 from "path";
|
|
45746
46226
|
var SettingsManager = class {
|
|
45747
46227
|
settingsPath;
|
|
45748
46228
|
legacyConfigPath;
|
|
@@ -45756,9 +46236,9 @@ var SettingsManager = class {
|
|
|
45756
46236
|
this.legacyConfig = this.loadLegacyConfigFile();
|
|
45757
46237
|
}
|
|
45758
46238
|
readJson(filePath) {
|
|
45759
|
-
if (!
|
|
46239
|
+
if (!fs4.existsSync(filePath)) return null;
|
|
45760
46240
|
try {
|
|
45761
|
-
return JSON.parse(
|
|
46241
|
+
return JSON.parse(fs4.readFileSync(filePath, "utf-8"));
|
|
45762
46242
|
} catch {
|
|
45763
46243
|
return null;
|
|
45764
46244
|
}
|
|
@@ -45770,19 +46250,19 @@ var SettingsManager = class {
|
|
|
45770
46250
|
return this.readJson(this.legacyConfigPath) || {};
|
|
45771
46251
|
}
|
|
45772
46252
|
ensureDir() {
|
|
45773
|
-
|
|
46253
|
+
fs4.mkdirSync(getConfigDir(), { recursive: true });
|
|
45774
46254
|
}
|
|
45775
46255
|
writeSettings() {
|
|
45776
46256
|
this.ensureDir();
|
|
45777
|
-
|
|
46257
|
+
fs4.writeFileSync(this.settingsPath, JSON.stringify(this.settings, null, 2));
|
|
45778
46258
|
}
|
|
45779
46259
|
writeSettingsValue(settings) {
|
|
45780
46260
|
this.ensureDir();
|
|
45781
|
-
|
|
46261
|
+
fs4.writeFileSync(this.settingsPath, JSON.stringify(settings, null, 2));
|
|
45782
46262
|
}
|
|
45783
46263
|
writeLegacyConfig() {
|
|
45784
46264
|
this.ensureDir();
|
|
45785
|
-
|
|
46265
|
+
fs4.writeFileSync(this.legacyConfigPath, JSON.stringify(this.legacyConfig, null, 2));
|
|
45786
46266
|
}
|
|
45787
46267
|
getLegacyApiKey() {
|
|
45788
46268
|
return this.legacyConfig.apiKey;
|
|
@@ -45831,6 +46311,9 @@ var SettingsManager = class {
|
|
|
45831
46311
|
}
|
|
45832
46312
|
return "auto";
|
|
45833
46313
|
}
|
|
46314
|
+
getBellNotificationsEnabled() {
|
|
46315
|
+
return this.settings.notifications?.bell !== false;
|
|
46316
|
+
}
|
|
45834
46317
|
// ==========================================================================
|
|
45835
46318
|
// Extension enablement (user ~/.pie/settings.json + project ./.pie/settings.json)
|
|
45836
46319
|
// ==========================================================================
|
|
@@ -45839,8 +46322,8 @@ var SettingsManager = class {
|
|
|
45839
46322
|
}
|
|
45840
46323
|
writeProjectSettings(cwd, settings) {
|
|
45841
46324
|
const settingsPath = getProjectSettingsPath(cwd);
|
|
45842
|
-
|
|
45843
|
-
|
|
46325
|
+
fs4.mkdirSync(path4.dirname(settingsPath), { recursive: true });
|
|
46326
|
+
fs4.writeFileSync(settingsPath, JSON.stringify(settings, null, 2));
|
|
45844
46327
|
}
|
|
45845
46328
|
/** Raw extension enablement map for one scope. */
|
|
45846
46329
|
getExtensionSettings(scope = "user", cwd = process.cwd()) {
|
|
@@ -45914,19 +46397,19 @@ function checkShell() {
|
|
|
45914
46397
|
}
|
|
45915
46398
|
function canWriteDirectory(dir) {
|
|
45916
46399
|
try {
|
|
45917
|
-
|
|
45918
|
-
const probe =
|
|
45919
|
-
|
|
45920
|
-
|
|
46400
|
+
fs5.mkdirSync(dir, { recursive: true });
|
|
46401
|
+
const probe = path5.join(dir, `.pie-doctor-${process.pid}-${Date.now()}`);
|
|
46402
|
+
fs5.writeFileSync(probe, "ok", "utf8");
|
|
46403
|
+
fs5.rmSync(probe, { force: true });
|
|
45921
46404
|
return { ok: true };
|
|
45922
46405
|
} catch (error) {
|
|
45923
46406
|
return { ok: false, error: error instanceof Error ? error.message : String(error) };
|
|
45924
46407
|
}
|
|
45925
46408
|
}
|
|
45926
46409
|
function isExecutable(candidate) {
|
|
45927
|
-
if (!candidate || !
|
|
46410
|
+
if (!candidate || !fs5.existsSync(candidate)) return false;
|
|
45928
46411
|
try {
|
|
45929
|
-
|
|
46412
|
+
fs5.accessSync(candidate, fs5.constants.X_OK);
|
|
45930
46413
|
return true;
|
|
45931
46414
|
} catch {
|
|
45932
46415
|
return os.platform() === "win32";
|
|
@@ -45936,7 +46419,7 @@ function findOnPath(names) {
|
|
|
45936
46419
|
for (const dir of (process.env.PATH || "").split(delimiter)) {
|
|
45937
46420
|
if (!dir) continue;
|
|
45938
46421
|
for (const name of names) {
|
|
45939
|
-
const candidate =
|
|
46422
|
+
const candidate = path5.join(dir, name);
|
|
45940
46423
|
if (isExecutable(candidate)) return candidate;
|
|
45941
46424
|
}
|
|
45942
46425
|
}
|
|
@@ -45952,10 +46435,10 @@ function findDoctorChromeExecutable() {
|
|
|
45952
46435
|
"/Applications/Chromium.app/Contents/MacOS/Chromium",
|
|
45953
46436
|
"/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge"
|
|
45954
46437
|
] : os.platform() === "win32" ? [
|
|
45955
|
-
|
|
45956
|
-
|
|
45957
|
-
|
|
45958
|
-
|
|
46438
|
+
path5.join(process.env.PROGRAMFILES || "C:\\Program Files", "Google", "Chrome", "Application", "chrome.exe"),
|
|
46439
|
+
path5.join(process.env["PROGRAMFILES(X86)"] || "C:\\Program Files (x86)", "Google", "Chrome", "Application", "chrome.exe"),
|
|
46440
|
+
path5.join(process.env.LOCALAPPDATA || path5.join(home, "AppData", "Local"), "Google", "Chrome", "Application", "chrome.exe"),
|
|
46441
|
+
path5.join(process.env.PROGRAMFILES || "C:\\Program Files", "Microsoft", "Edge", "Application", "msedge.exe")
|
|
45959
46442
|
] : [
|
|
45960
46443
|
"/usr/bin/google-chrome",
|
|
45961
46444
|
"/usr/bin/google-chrome-stable",
|
|
@@ -46004,7 +46487,7 @@ function checkPlaywrightBrowser() {
|
|
|
46004
46487
|
} catch {
|
|
46005
46488
|
executablePath = void 0;
|
|
46006
46489
|
}
|
|
46007
|
-
if (executablePath &&
|
|
46490
|
+
if (executablePath && fs5.existsSync(executablePath)) {
|
|
46008
46491
|
return {
|
|
46009
46492
|
id: "playwright_browser",
|
|
46010
46493
|
label: "Playwright browser",
|
|
@@ -46013,7 +46496,7 @@ function checkPlaywrightBrowser() {
|
|
|
46013
46496
|
details: { executablePath }
|
|
46014
46497
|
};
|
|
46015
46498
|
}
|
|
46016
|
-
const skillWrapper =
|
|
46499
|
+
const skillWrapper = path5.join(getBuiltinDir(), "skills", "browser-tools", "playwright-cli.js");
|
|
46017
46500
|
return {
|
|
46018
46501
|
id: "playwright_browser",
|
|
46019
46502
|
label: "Playwright browser",
|
|
@@ -46044,8 +46527,8 @@ function isLiveUnityInstance(instance, nowUnix = Math.floor(Date.now() / 1e3)) {
|
|
|
46044
46527
|
return isProcessAlive(Number(instance.pid || 0));
|
|
46045
46528
|
}
|
|
46046
46529
|
function checkUnityRegistry() {
|
|
46047
|
-
const registryDir =
|
|
46048
|
-
if (!
|
|
46530
|
+
const registryDir = path5.join(os.homedir(), ".pie-unity", "instances");
|
|
46531
|
+
if (!fs5.existsSync(registryDir)) {
|
|
46049
46532
|
return {
|
|
46050
46533
|
id: "unity_bridge",
|
|
46051
46534
|
label: "Unity bridge",
|
|
@@ -46056,12 +46539,12 @@ function checkUnityRegistry() {
|
|
|
46056
46539
|
}
|
|
46057
46540
|
try {
|
|
46058
46541
|
const nowUnix = Math.floor(Date.now() / 1e3);
|
|
46059
|
-
const files =
|
|
46542
|
+
const files = fs5.readdirSync(registryDir, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".json")).map((entry) => path5.join(registryDir, entry.name));
|
|
46060
46543
|
let malformedFiles = 0;
|
|
46061
46544
|
let invalidFiles = 0;
|
|
46062
46545
|
const liveInstances = files.map((filePath) => {
|
|
46063
46546
|
try {
|
|
46064
|
-
const parsed = JSON.parse(
|
|
46547
|
+
const parsed = JSON.parse(fs5.readFileSync(filePath, "utf8").replace(/^\uFEFF/, ""));
|
|
46065
46548
|
if (!isRecord(parsed)) {
|
|
46066
46549
|
invalidFiles += 1;
|
|
46067
46550
|
return null;
|
|
@@ -46135,13 +46618,13 @@ async function runDoctor() {
|
|
|
46135
46618
|
const packageDir = getPackageDir();
|
|
46136
46619
|
let dir = packageDir;
|
|
46137
46620
|
let lockPath;
|
|
46138
|
-
while (dir !==
|
|
46139
|
-
const candidate =
|
|
46140
|
-
if (
|
|
46621
|
+
while (dir !== path5.dirname(dir)) {
|
|
46622
|
+
const candidate = path5.join(dir, "package-lock.json");
|
|
46623
|
+
if (fs5.existsSync(candidate)) {
|
|
46141
46624
|
lockPath = candidate;
|
|
46142
46625
|
break;
|
|
46143
46626
|
}
|
|
46144
|
-
dir =
|
|
46627
|
+
dir = path5.dirname(dir);
|
|
46145
46628
|
}
|
|
46146
46629
|
checks.push({
|
|
46147
46630
|
id: "package_lock",
|
|
@@ -46163,7 +46646,7 @@ async function runDoctor() {
|
|
|
46163
46646
|
message: result.ok ? `${dirPath} is writable.` : `${dirPath} is not writable: ${result.error}`
|
|
46164
46647
|
});
|
|
46165
46648
|
}
|
|
46166
|
-
const authStorage =
|
|
46649
|
+
const authStorage = AuthStorage.create();
|
|
46167
46650
|
const registry = new ModelRegistry(authStorage);
|
|
46168
46651
|
const loadError = registry.getLoadError();
|
|
46169
46652
|
const models = registry.getAll();
|
|
@@ -46304,10 +46787,10 @@ Disable or narrow capabilities:
|
|
|
46304
46787
|
}
|
|
46305
46788
|
|
|
46306
46789
|
// src/commands/models-config.ts
|
|
46307
|
-
import * as
|
|
46308
|
-
import * as
|
|
46790
|
+
import * as fs6 from "node:fs";
|
|
46791
|
+
import * as path6 from "node:path";
|
|
46309
46792
|
function getDefaultModelsPath() {
|
|
46310
|
-
return
|
|
46793
|
+
return path6.join(getConfigDir(), "models.json");
|
|
46311
46794
|
}
|
|
46312
46795
|
function createModelsTemplate() {
|
|
46313
46796
|
const template = {
|
|
@@ -46402,7 +46885,7 @@ function createModelsTemplate() {
|
|
|
46402
46885
|
`;
|
|
46403
46886
|
}
|
|
46404
46887
|
function resolveModelsPath(input) {
|
|
46405
|
-
return input ?
|
|
46888
|
+
return input ? path6.resolve(input) : getDefaultModelsPath();
|
|
46406
46889
|
}
|
|
46407
46890
|
function initModelsConfig(options = {}) {
|
|
46408
46891
|
const targetPath = resolveModelsPath(options.path);
|
|
@@ -46410,16 +46893,16 @@ function initModelsConfig(options = {}) {
|
|
|
46410
46893
|
if (options.stdout) {
|
|
46411
46894
|
return { path: targetPath, content, wrote: false };
|
|
46412
46895
|
}
|
|
46413
|
-
if (
|
|
46896
|
+
if (fs6.existsSync(targetPath) && !options.force) {
|
|
46414
46897
|
throw new Error(`models.json already exists: ${targetPath}. Use --force to overwrite, or --stdout to print the template.`);
|
|
46415
46898
|
}
|
|
46416
|
-
|
|
46417
|
-
|
|
46899
|
+
fs6.mkdirSync(path6.dirname(targetPath), { recursive: true });
|
|
46900
|
+
fs6.writeFileSync(targetPath, content, "utf8");
|
|
46418
46901
|
return { path: targetPath, content, wrote: true };
|
|
46419
46902
|
}
|
|
46420
46903
|
function validateModelsConfig(options = {}) {
|
|
46421
46904
|
const modelsPath = resolveModelsPath(options.path);
|
|
46422
|
-
if (!
|
|
46905
|
+
if (!fs6.existsSync(modelsPath)) {
|
|
46423
46906
|
return {
|
|
46424
46907
|
ok: false,
|
|
46425
46908
|
path: modelsPath,
|
|
@@ -46428,7 +46911,7 @@ function validateModelsConfig(options = {}) {
|
|
|
46428
46911
|
modelCount: 0
|
|
46429
46912
|
};
|
|
46430
46913
|
}
|
|
46431
|
-
const authStorage =
|
|
46914
|
+
const authStorage = AuthStorage.create();
|
|
46432
46915
|
const registry = new ModelRegistry(authStorage, modelsPath);
|
|
46433
46916
|
const loadError = registry.getLoadError();
|
|
46434
46917
|
if (loadError) {
|
|
@@ -46475,7 +46958,7 @@ function validateModelsConfig(options = {}) {
|
|
|
46475
46958
|
};
|
|
46476
46959
|
}
|
|
46477
46960
|
async function probeModelsConfig(options) {
|
|
46478
|
-
const registry = new ModelRegistry(
|
|
46961
|
+
const registry = new ModelRegistry(AuthStorage.create(), resolveModelsPath(options.path));
|
|
46479
46962
|
const loadError = registry.getLoadError();
|
|
46480
46963
|
if (loadError) {
|
|
46481
46964
|
return {
|
|
@@ -46568,7 +47051,7 @@ function parseModelsCommandArgs(args) {
|
|
|
46568
47051
|
}
|
|
46569
47052
|
|
|
46570
47053
|
// src/commands/sessions.ts
|
|
46571
|
-
import
|
|
47054
|
+
import fs7 from "node:fs";
|
|
46572
47055
|
|
|
46573
47056
|
// src/session-control.ts
|
|
46574
47057
|
var SessionControlError = class extends Error {
|
|
@@ -46713,15 +47196,15 @@ function buildForkResult(params) {
|
|
|
46713
47196
|
}
|
|
46714
47197
|
function pathToEntry(data, entryId) {
|
|
46715
47198
|
const byId = new Map(data.entries.map((entry) => [entry.id, entry]));
|
|
46716
|
-
const
|
|
47199
|
+
const path33 = [];
|
|
46717
47200
|
let current = entryId;
|
|
46718
47201
|
while (current) {
|
|
46719
47202
|
const entry = byId.get(current);
|
|
46720
47203
|
if (!entry) return [];
|
|
46721
|
-
|
|
47204
|
+
path33.unshift(entry);
|
|
46722
47205
|
current = entry.parentId;
|
|
46723
47206
|
}
|
|
46724
|
-
return
|
|
47207
|
+
return path33;
|
|
46725
47208
|
}
|
|
46726
47209
|
function cloneJson(value) {
|
|
46727
47210
|
return JSON.parse(JSON.stringify(value));
|
|
@@ -46967,7 +47450,7 @@ function cwdMatches(sessionCwd, requestedCwd) {
|
|
|
46967
47450
|
}
|
|
46968
47451
|
function realpathOrInput(value) {
|
|
46969
47452
|
try {
|
|
46970
|
-
return
|
|
47453
|
+
return fs7.realpathSync.native(value);
|
|
46971
47454
|
} catch {
|
|
46972
47455
|
return value;
|
|
46973
47456
|
}
|
|
@@ -47056,9 +47539,9 @@ function isRecord2(value) {
|
|
|
47056
47539
|
}
|
|
47057
47540
|
|
|
47058
47541
|
// src/extensions/loader.ts
|
|
47059
|
-
import * as
|
|
47542
|
+
import * as fs8 from "node:fs";
|
|
47060
47543
|
import * as os2 from "os";
|
|
47061
|
-
import * as
|
|
47544
|
+
import * as path7 from "path";
|
|
47062
47545
|
import { pathToFileURL } from "node:url";
|
|
47063
47546
|
|
|
47064
47547
|
// src/command-tree.ts
|
|
@@ -47071,19 +47554,19 @@ var CommandTree = class {
|
|
|
47071
47554
|
* register(["session", "new"], { description, handler })
|
|
47072
47555
|
* register(["tools", "subagent", "spawn", "local"], { description, handler })
|
|
47073
47556
|
*/
|
|
47074
|
-
register(
|
|
47075
|
-
if (
|
|
47557
|
+
register(path33, info) {
|
|
47558
|
+
if (path33.length === 0) {
|
|
47076
47559
|
throw new Error("Command path cannot be empty");
|
|
47077
47560
|
}
|
|
47078
47561
|
let current = this.root;
|
|
47079
47562
|
const fullPath = [];
|
|
47080
|
-
for (let i = 0; i <
|
|
47081
|
-
const segment =
|
|
47563
|
+
for (let i = 0; i < path33.length; i++) {
|
|
47564
|
+
const segment = path33[i];
|
|
47082
47565
|
fullPath.push(segment);
|
|
47083
47566
|
if (!current.children) {
|
|
47084
47567
|
current.children = [];
|
|
47085
47568
|
}
|
|
47086
|
-
const isLast = i ===
|
|
47569
|
+
const isLast = i === path33.length - 1;
|
|
47087
47570
|
let node = current.children.find((n) => n.name === segment);
|
|
47088
47571
|
if (!node) {
|
|
47089
47572
|
node = {
|
|
@@ -47113,31 +47596,31 @@ var CommandTree = class {
|
|
|
47113
47596
|
/**
|
|
47114
47597
|
* Find a node by path
|
|
47115
47598
|
*/
|
|
47116
|
-
find(
|
|
47117
|
-
if (
|
|
47599
|
+
find(path33) {
|
|
47600
|
+
if (path33.length === 0) {
|
|
47118
47601
|
return this.root;
|
|
47119
47602
|
}
|
|
47120
|
-
const key =
|
|
47603
|
+
const key = path33.join("/");
|
|
47121
47604
|
return this.pathCache.get(key) || null;
|
|
47122
47605
|
}
|
|
47123
47606
|
/**
|
|
47124
47607
|
* Get children of a path
|
|
47125
47608
|
*/
|
|
47126
|
-
getChildren(
|
|
47127
|
-
const node = this.find(
|
|
47609
|
+
getChildren(path33) {
|
|
47610
|
+
const node = this.find(path33);
|
|
47128
47611
|
return (node?.children || []).filter((child) => !child.hidden);
|
|
47129
47612
|
}
|
|
47130
47613
|
/**
|
|
47131
47614
|
* Check if a path exists (as category or command)
|
|
47132
47615
|
*/
|
|
47133
|
-
exists(
|
|
47134
|
-
return this.find(
|
|
47616
|
+
exists(path33) {
|
|
47617
|
+
return this.find(path33) !== null;
|
|
47135
47618
|
}
|
|
47136
47619
|
/**
|
|
47137
47620
|
* Check if path is a category (has children)
|
|
47138
47621
|
*/
|
|
47139
|
-
isCategory(
|
|
47140
|
-
const node = this.find(
|
|
47622
|
+
isCategory(path33) {
|
|
47623
|
+
const node = this.find(path33);
|
|
47141
47624
|
return node?.isCategory || false;
|
|
47142
47625
|
}
|
|
47143
47626
|
/**
|
|
@@ -47216,8 +47699,8 @@ var CommandTree = class {
|
|
|
47216
47699
|
* Supports dot notation: "tools.subagent.spawn.local"
|
|
47217
47700
|
*/
|
|
47218
47701
|
registerFromPath(pathStr, info) {
|
|
47219
|
-
const
|
|
47220
|
-
this.register(
|
|
47702
|
+
const path33 = pathStr.split(/[./]/).filter(Boolean);
|
|
47703
|
+
this.register(path33, info);
|
|
47221
47704
|
}
|
|
47222
47705
|
};
|
|
47223
47706
|
var commandTree = new CommandTree();
|
|
@@ -47331,6 +47814,33 @@ function createExtensionContext(extension, cwd, uiContext, abortFn, isIdleFn, ha
|
|
|
47331
47814
|
setSessionMetadata(key, value) {
|
|
47332
47815
|
actions?.setSessionMetadata?.(key, value);
|
|
47333
47816
|
},
|
|
47817
|
+
appendEntry(customType, data) {
|
|
47818
|
+
if (!actions?.appendEntry) throw new Error("appendEntry is not available in this host mode");
|
|
47819
|
+
return actions.appendEntry(customType, data);
|
|
47820
|
+
},
|
|
47821
|
+
setLabel(entryId, label) {
|
|
47822
|
+
if (!actions?.setLabel) throw new Error("setLabel is not available in this host mode");
|
|
47823
|
+
return actions.setLabel(entryId, label);
|
|
47824
|
+
},
|
|
47825
|
+
setSessionName(name) {
|
|
47826
|
+
if (!actions?.setSessionName) throw new Error("setSessionName is not available in this host mode");
|
|
47827
|
+
return actions.setSessionName(name);
|
|
47828
|
+
},
|
|
47829
|
+
getSessionName() {
|
|
47830
|
+
return actions?.getSessionName?.();
|
|
47831
|
+
},
|
|
47832
|
+
registerProvider(name, config) {
|
|
47833
|
+
if (!actions?.registerProvider) throw new Error("registerProvider is not available in this host mode");
|
|
47834
|
+
return actions.registerProvider(name, config);
|
|
47835
|
+
},
|
|
47836
|
+
async loginProvider(name) {
|
|
47837
|
+
if (!actions?.loginProvider) throw new Error("loginProvider is not available in this host mode");
|
|
47838
|
+
return actions.loginProvider(name);
|
|
47839
|
+
},
|
|
47840
|
+
async reload() {
|
|
47841
|
+
if (!actions?.reload) throw new Error("reload is not available in this host mode");
|
|
47842
|
+
return actions.reload();
|
|
47843
|
+
},
|
|
47334
47844
|
get yoloMode() {
|
|
47335
47845
|
return actions?.yoloMode ?? false;
|
|
47336
47846
|
}
|
|
@@ -47352,19 +47862,19 @@ function createExtension(extensionPath, resolvedPath, identity, source) {
|
|
|
47352
47862
|
}
|
|
47353
47863
|
function expandPath(p) {
|
|
47354
47864
|
if (p.startsWith("~/")) {
|
|
47355
|
-
return
|
|
47865
|
+
return path7.join(os2.homedir(), p.slice(2));
|
|
47356
47866
|
}
|
|
47357
47867
|
if (p.startsWith("~")) {
|
|
47358
|
-
return
|
|
47868
|
+
return path7.join(os2.homedir(), p.slice(1));
|
|
47359
47869
|
}
|
|
47360
47870
|
return p;
|
|
47361
47871
|
}
|
|
47362
47872
|
function resolvePath(extPath, cwd) {
|
|
47363
47873
|
const expanded = expandPath(extPath);
|
|
47364
|
-
if (
|
|
47874
|
+
if (path7.isAbsolute(expanded)) {
|
|
47365
47875
|
return expanded;
|
|
47366
47876
|
}
|
|
47367
|
-
return
|
|
47877
|
+
return path7.resolve(cwd, expanded);
|
|
47368
47878
|
}
|
|
47369
47879
|
async function loadExtensionModule(extensionPath) {
|
|
47370
47880
|
try {
|
|
@@ -47384,14 +47894,14 @@ function normalizeExtensionImportPath(extensionPath) {
|
|
|
47384
47894
|
if (/^[a-zA-Z]:[\\/]/.test(extensionPath)) {
|
|
47385
47895
|
return `file:///${extensionPath.replace(/\\/g, "/")}`;
|
|
47386
47896
|
}
|
|
47387
|
-
if (
|
|
47897
|
+
if (path7.isAbsolute(extensionPath)) {
|
|
47388
47898
|
return pathToFileURL(extensionPath).href;
|
|
47389
47899
|
}
|
|
47390
47900
|
return extensionPath;
|
|
47391
47901
|
}
|
|
47392
47902
|
async function loadExtension(extensionPath, cwd, uiContext, abortFn, isIdleFn, hasUI, actions, discovered) {
|
|
47393
47903
|
const resolvedPath = resolvePath(extensionPath, cwd);
|
|
47394
|
-
if (!
|
|
47904
|
+
if (!fs8.existsSync(resolvedPath)) {
|
|
47395
47905
|
return { extension: null, error: `Extension not found: ${extensionPath}` };
|
|
47396
47906
|
}
|
|
47397
47907
|
try {
|
|
@@ -47412,17 +47922,17 @@ async function loadExtension(extensionPath, cwd, uiContext, abortFn, isIdleFn, h
|
|
|
47412
47922
|
}
|
|
47413
47923
|
}
|
|
47414
47924
|
function discoverExtensionsInDir(dir, source) {
|
|
47415
|
-
if (!
|
|
47925
|
+
if (!fs8.existsSync(dir)) {
|
|
47416
47926
|
return [];
|
|
47417
47927
|
}
|
|
47418
47928
|
const discovered = [];
|
|
47419
47929
|
try {
|
|
47420
|
-
const entries =
|
|
47930
|
+
const entries = fs8.readdirSync(dir, { withFileTypes: true });
|
|
47421
47931
|
for (const entry of entries) {
|
|
47422
47932
|
if (entry.name.endsWith(".disabled")) {
|
|
47423
47933
|
continue;
|
|
47424
47934
|
}
|
|
47425
|
-
const entryPath =
|
|
47935
|
+
const entryPath = path7.join(dir, entry.name);
|
|
47426
47936
|
if (entry.isFile() && (entry.name.endsWith(".cjs") || entry.name.endsWith(".js") || entry.name.endsWith(".ts"))) {
|
|
47427
47937
|
discovered.push({
|
|
47428
47938
|
entryPath,
|
|
@@ -47432,13 +47942,13 @@ function discoverExtensionsInDir(dir, source) {
|
|
|
47432
47942
|
continue;
|
|
47433
47943
|
}
|
|
47434
47944
|
if (entry.isDirectory()) {
|
|
47435
|
-
const packageJsonPath =
|
|
47945
|
+
const packageJsonPath = path7.join(entryPath, "package.json");
|
|
47436
47946
|
let packageIdentity;
|
|
47437
47947
|
let packageDescription;
|
|
47438
47948
|
let packageMainPath;
|
|
47439
|
-
if (
|
|
47949
|
+
if (fs8.existsSync(packageJsonPath)) {
|
|
47440
47950
|
try {
|
|
47441
|
-
const pkg = JSON.parse(
|
|
47951
|
+
const pkg = JSON.parse(fs8.readFileSync(packageJsonPath, "utf-8"));
|
|
47442
47952
|
const manifest = pkg.pie && typeof pkg.pie === "object" && !Array.isArray(pkg.pie) ? pkg.pie : void 0;
|
|
47443
47953
|
packageIdentity = resolveExtensionIdentity({
|
|
47444
47954
|
pieName: typeof manifest?.name === "string" ? manifest.name : void 0,
|
|
@@ -47447,8 +47957,8 @@ function discoverExtensionsInDir(dir, source) {
|
|
|
47447
47957
|
});
|
|
47448
47958
|
packageDescription = typeof pkg.description === "string" ? pkg.description : void 0;
|
|
47449
47959
|
if (typeof manifest?.main === "string" && manifest.main.trim().length > 0) {
|
|
47450
|
-
const candidate =
|
|
47451
|
-
if (
|
|
47960
|
+
const candidate = path7.resolve(entryPath, manifest.main);
|
|
47961
|
+
if (fs8.existsSync(candidate)) {
|
|
47452
47962
|
packageMainPath = candidate;
|
|
47453
47963
|
}
|
|
47454
47964
|
}
|
|
@@ -47464,8 +47974,8 @@ function discoverExtensionsInDir(dir, source) {
|
|
|
47464
47974
|
});
|
|
47465
47975
|
continue;
|
|
47466
47976
|
}
|
|
47467
|
-
const indexCjs =
|
|
47468
|
-
if (
|
|
47977
|
+
const indexCjs = path7.join(entryPath, "index.cjs");
|
|
47978
|
+
if (fs8.existsSync(indexCjs)) {
|
|
47469
47979
|
discovered.push({
|
|
47470
47980
|
entryPath: indexCjs,
|
|
47471
47981
|
source,
|
|
@@ -47474,8 +47984,8 @@ function discoverExtensionsInDir(dir, source) {
|
|
|
47474
47984
|
});
|
|
47475
47985
|
continue;
|
|
47476
47986
|
}
|
|
47477
|
-
const indexJs =
|
|
47478
|
-
if (
|
|
47987
|
+
const indexJs = path7.join(entryPath, "index.js");
|
|
47988
|
+
if (fs8.existsSync(indexJs)) {
|
|
47479
47989
|
discovered.push({
|
|
47480
47990
|
entryPath: indexJs,
|
|
47481
47991
|
source,
|
|
@@ -47484,8 +47994,8 @@ function discoverExtensionsInDir(dir, source) {
|
|
|
47484
47994
|
});
|
|
47485
47995
|
continue;
|
|
47486
47996
|
}
|
|
47487
|
-
const indexTs =
|
|
47488
|
-
if (
|
|
47997
|
+
const indexTs = path7.join(entryPath, "index.ts");
|
|
47998
|
+
if (fs8.existsSync(indexTs)) {
|
|
47489
47999
|
discovered.push({
|
|
47490
48000
|
entryPath: indexTs,
|
|
47491
48001
|
source,
|
|
@@ -47506,21 +48016,21 @@ function collectDiscoveredExtensions(configuredPaths, cwd, options) {
|
|
|
47506
48016
|
const errors = [];
|
|
47507
48017
|
if (!options?.noExtensions) {
|
|
47508
48018
|
if (!options?.noBuiltinExtensions) {
|
|
47509
|
-
const builtinExtDir =
|
|
48019
|
+
const builtinExtDir = path7.join(getBuiltinDir(), "extensions");
|
|
47510
48020
|
discovered.push(...discoverExtensionsInDir(builtinExtDir, "builtin"));
|
|
47511
48021
|
}
|
|
47512
|
-
const globalExtDir =
|
|
48022
|
+
const globalExtDir = path7.join(os2.homedir(), ".pie", "extensions");
|
|
47513
48023
|
discovered.push(...discoverExtensionsInDir(globalExtDir, "global"));
|
|
47514
|
-
const localExtDir =
|
|
48024
|
+
const localExtDir = path7.join(cwd, ".pie", "extensions");
|
|
47515
48025
|
discovered.push(...discoverExtensionsInDir(localExtDir, "project"));
|
|
47516
48026
|
}
|
|
47517
48027
|
for (const p of configuredPaths) {
|
|
47518
48028
|
const resolved = resolvePath(p, cwd);
|
|
47519
|
-
if (!
|
|
48029
|
+
if (!fs8.existsSync(resolved)) {
|
|
47520
48030
|
errors.push({ path: p, error: `Extension path not found: ${p}` });
|
|
47521
48031
|
continue;
|
|
47522
48032
|
}
|
|
47523
|
-
if (!
|
|
48033
|
+
if (!fs8.statSync(resolved).isDirectory()) {
|
|
47524
48034
|
errors.push({ path: p, error: `Extension path must be a directory: ${p}` });
|
|
47525
48035
|
continue;
|
|
47526
48036
|
}
|
|
@@ -47611,6 +48121,7 @@ function convertExtensionTools(extensions, customExecutors) {
|
|
|
47611
48121
|
label: tool.label ?? name,
|
|
47612
48122
|
description: tool.description,
|
|
47613
48123
|
parameters: tool.parameters,
|
|
48124
|
+
renderHint: tool.renderHint,
|
|
47614
48125
|
execute: async (toolCallId, args, signal, onUpdate) => {
|
|
47615
48126
|
debugLog("ext-tool", `${name} EXECUTE START`, {
|
|
47616
48127
|
toolCallId: toolCallId.slice(0, 8),
|
|
@@ -47801,6 +48312,9 @@ Enablement overrides are stored sparsely; absent entries default to enabled.
|
|
|
47801
48312
|
Project scope writes ./.pie/settings.json and overrides user scope.`);
|
|
47802
48313
|
}
|
|
47803
48314
|
|
|
48315
|
+
// src/interactive-mode.ts
|
|
48316
|
+
import * as os8 from "os";
|
|
48317
|
+
|
|
47804
48318
|
// ../../packages/tui/src/fuzzy.ts
|
|
47805
48319
|
function fuzzyMatch(query, text) {
|
|
47806
48320
|
const queryLower = query.toLowerCase();
|
|
@@ -49351,9 +49865,9 @@ var KillRing = class {
|
|
|
49351
49865
|
};
|
|
49352
49866
|
|
|
49353
49867
|
// ../../packages/tui/src/tui.ts
|
|
49354
|
-
import * as
|
|
49868
|
+
import * as fs9 from "node:fs";
|
|
49355
49869
|
import * as os3 from "node:os";
|
|
49356
|
-
import * as
|
|
49870
|
+
import * as path8 from "node:path";
|
|
49357
49871
|
|
|
49358
49872
|
// ../../packages/tui/src/terminal-image.ts
|
|
49359
49873
|
var cachedCapabilities = null;
|
|
@@ -50029,11 +50543,11 @@ var TUI = class extends Container {
|
|
|
50029
50543
|
const debugRedraw = process.env.PIE_DEBUG_REDRAW === "1";
|
|
50030
50544
|
const logRedraw = (reason) => {
|
|
50031
50545
|
if (!debugRedraw) return;
|
|
50032
|
-
const logPath =
|
|
50033
|
-
|
|
50546
|
+
const logPath = path8.join(os3.homedir(), ".pie", "logs", "pie-cli.log");
|
|
50547
|
+
fs9.mkdirSync(path8.dirname(logPath), { recursive: true });
|
|
50034
50548
|
const msg = `[${(/* @__PURE__ */ new Date()).toISOString()}] [DEBUG] [ui] fullRender: ${reason} (prev=${this.previousLines.length}, new=${newLines.length}, height=${height})
|
|
50035
50549
|
`;
|
|
50036
|
-
|
|
50550
|
+
fs9.appendFileSync(logPath, msg);
|
|
50037
50551
|
};
|
|
50038
50552
|
if (this.previousLines.length === 0 && !widthChanged) {
|
|
50039
50553
|
logRedraw("first render");
|
|
@@ -50126,7 +50640,7 @@ var TUI = class extends Container {
|
|
|
50126
50640
|
const line = newLines[i];
|
|
50127
50641
|
const isImage = isImageLine(line);
|
|
50128
50642
|
if (!isImage && visibleWidth(line) > width) {
|
|
50129
|
-
const crashLogPath =
|
|
50643
|
+
const crashLogPath = path8.join(os3.homedir(), ".pie", "pie-crash.log");
|
|
50130
50644
|
const crashData = [
|
|
50131
50645
|
`Crash at ${(/* @__PURE__ */ new Date()).toISOString()}`,
|
|
50132
50646
|
`Terminal width: ${width}`,
|
|
@@ -50136,8 +50650,8 @@ var TUI = class extends Container {
|
|
|
50136
50650
|
...newLines.map((l, idx) => `[${idx}] (w=${visibleWidth(l)}) ${l}`),
|
|
50137
50651
|
""
|
|
50138
50652
|
].join("\n");
|
|
50139
|
-
|
|
50140
|
-
|
|
50653
|
+
fs9.mkdirSync(path8.dirname(crashLogPath), { recursive: true });
|
|
50654
|
+
fs9.writeFileSync(crashLogPath, crashData);
|
|
50141
50655
|
this.stop();
|
|
50142
50656
|
const errorMsg = [
|
|
50143
50657
|
`Rendered line ${i} exceeds terminal width (${visibleWidth(line)} > ${width}).`,
|
|
@@ -50167,8 +50681,8 @@ var TUI = class extends Container {
|
|
|
50167
50681
|
buffer += "\x1B[?2026l";
|
|
50168
50682
|
if (process.env.PIE_TUI_DEBUG === "1") {
|
|
50169
50683
|
const debugDir = "/tmp/tui";
|
|
50170
|
-
|
|
50171
|
-
const debugPath =
|
|
50684
|
+
fs9.mkdirSync(debugDir, { recursive: true });
|
|
50685
|
+
const debugPath = path8.join(debugDir, `render-${Date.now()}-${Math.random().toString(36).slice(2)}.log`);
|
|
50172
50686
|
const debugData = [
|
|
50173
50687
|
`firstChanged: ${firstChanged}`,
|
|
50174
50688
|
`viewportTop: ${viewportTop}`,
|
|
@@ -50191,7 +50705,7 @@ var TUI = class extends Container {
|
|
|
50191
50705
|
"=== buffer ===",
|
|
50192
50706
|
JSON.stringify(buffer)
|
|
50193
50707
|
].join("\n");
|
|
50194
|
-
|
|
50708
|
+
fs9.writeFileSync(debugPath, debugData);
|
|
50195
50709
|
}
|
|
50196
50710
|
this.terminal.write(buffer);
|
|
50197
50711
|
this.cursorRow = Math.max(0, newLines.length - 1);
|
|
@@ -54382,7 +54896,7 @@ var StdinBuffer = class extends EventEmitter {
|
|
|
54382
54896
|
};
|
|
54383
54897
|
|
|
54384
54898
|
// ../../packages/tui/src/terminal.ts
|
|
54385
|
-
import * as
|
|
54899
|
+
import * as fs10 from "node:fs";
|
|
54386
54900
|
import koffi from "koffi";
|
|
54387
54901
|
var ProcessTerminal = class {
|
|
54388
54902
|
wasRaw = false;
|
|
@@ -54507,7 +55021,7 @@ var ProcessTerminal = class {
|
|
|
54507
55021
|
const timeLeft = endTime - now;
|
|
54508
55022
|
if (timeLeft <= 0) break;
|
|
54509
55023
|
if (now - lastDataTime >= idleMs) break;
|
|
54510
|
-
await new Promise((
|
|
55024
|
+
await new Promise((resolve7) => setTimeout(resolve7, Math.min(idleMs, timeLeft)));
|
|
54511
55025
|
}
|
|
54512
55026
|
} finally {
|
|
54513
55027
|
process.stdin.removeListener("data", onData);
|
|
@@ -54543,7 +55057,7 @@ var ProcessTerminal = class {
|
|
|
54543
55057
|
process.stdout.write(data);
|
|
54544
55058
|
if (this.writeLogPath) {
|
|
54545
55059
|
try {
|
|
54546
|
-
|
|
55060
|
+
fs10.appendFileSync(this.writeLogPath, data, { encoding: "utf8" });
|
|
54547
55061
|
} catch {
|
|
54548
55062
|
}
|
|
54549
55063
|
}
|
|
@@ -54617,6 +55131,8 @@ var ExtensionRunner = class {
|
|
|
54617
55131
|
actions;
|
|
54618
55132
|
contextActions;
|
|
54619
55133
|
hasUI;
|
|
55134
|
+
/** Unregister handles created through this runner, revoked on reload/teardown. */
|
|
55135
|
+
unregisterHandles = [];
|
|
54620
55136
|
constructor(extensions, actions, contextActions, hasUI = true) {
|
|
54621
55137
|
this.extensions = extensions;
|
|
54622
55138
|
this.actions = actions;
|
|
@@ -54648,6 +55164,39 @@ var ExtensionRunner = class {
|
|
|
54648
55164
|
this.uiContext = uiContext;
|
|
54649
55165
|
this.hasUI = true;
|
|
54650
55166
|
}
|
|
55167
|
+
/**
|
|
55168
|
+
* Wrap an unregister handle so this runner can revoke it during
|
|
55169
|
+
* reload/teardown even if the extension drops the handle it was returned.
|
|
55170
|
+
* The wrapper is idempotent.
|
|
55171
|
+
*/
|
|
55172
|
+
trackUnregisterHandle(unregister) {
|
|
55173
|
+
const entry = { invoked: false, unregister };
|
|
55174
|
+
this.unregisterHandles.push(entry);
|
|
55175
|
+
return () => {
|
|
55176
|
+
if (entry.invoked) return;
|
|
55177
|
+
entry.invoked = true;
|
|
55178
|
+
unregister();
|
|
55179
|
+
const index = this.unregisterHandles.indexOf(entry);
|
|
55180
|
+
if (index >= 0) this.unregisterHandles.splice(index, 1);
|
|
55181
|
+
};
|
|
55182
|
+
}
|
|
55183
|
+
/**
|
|
55184
|
+
* Revoke every provider registration made through this runner (runtime
|
|
55185
|
+
* reload / teardown). Old extension instances must not leave providers,
|
|
55186
|
+
* OAuth flows, or ambient state registered behind them.
|
|
55187
|
+
*/
|
|
55188
|
+
revokeRegistrations() {
|
|
55189
|
+
const pending = this.unregisterHandles.splice(0);
|
|
55190
|
+
for (const entry of pending) {
|
|
55191
|
+
if (entry.invoked) continue;
|
|
55192
|
+
entry.invoked = true;
|
|
55193
|
+
try {
|
|
55194
|
+
entry.unregister();
|
|
55195
|
+
} catch (error) {
|
|
55196
|
+
console.warn("[Extension] unregister provider failed during reload:", error);
|
|
55197
|
+
}
|
|
55198
|
+
}
|
|
55199
|
+
}
|
|
54651
55200
|
/**
|
|
54652
55201
|
* Get all registered tools from all extensions.
|
|
54653
55202
|
*/
|
|
@@ -54738,10 +55287,35 @@ var ExtensionRunner = class {
|
|
|
54738
55287
|
},
|
|
54739
55288
|
getAllTools: () => runner.actions.getAllTools(),
|
|
54740
55289
|
getRuntimeTools: () => runner.actions.getRuntimeTools(),
|
|
54741
|
-
resolveModelClass: (modelClass) => runner.actions.resolveModelClass?.(modelClass),
|
|
55290
|
+
resolveModelClass: (modelClass) => runner.actions.resolveModelClass?.(modelClass) ?? {},
|
|
54742
55291
|
getMessages: () => runner.actions.getMessages(),
|
|
54743
55292
|
getSessionMetadata: () => runner.actions.getSessionMetadata(),
|
|
54744
55293
|
setSessionMetadata: (key, value) => runner.actions.setSessionMetadata(key, value),
|
|
55294
|
+
appendEntry: (customType, data) => {
|
|
55295
|
+
if (!runner.actions.appendEntry) throw new Error("appendEntry is not available in this host mode");
|
|
55296
|
+
return runner.actions.appendEntry(customType, data);
|
|
55297
|
+
},
|
|
55298
|
+
setLabel: (entryId, label) => {
|
|
55299
|
+
if (!runner.actions.setLabel) throw new Error("setLabel is not available in this host mode");
|
|
55300
|
+
return runner.actions.setLabel(entryId, label);
|
|
55301
|
+
},
|
|
55302
|
+
setSessionName: (name) => {
|
|
55303
|
+
if (!runner.actions.setSessionName) throw new Error("setSessionName is not available in this host mode");
|
|
55304
|
+
return runner.actions.setSessionName(name);
|
|
55305
|
+
},
|
|
55306
|
+
getSessionName: () => runner.actions.getSessionName?.(),
|
|
55307
|
+
registerProvider: (name, config) => {
|
|
55308
|
+
if (!runner.actions.registerProvider) throw new Error("registerProvider is not available in this host mode");
|
|
55309
|
+
return runner.trackUnregisterHandle(runner.actions.registerProvider(name, config));
|
|
55310
|
+
},
|
|
55311
|
+
loginProvider: async (name) => {
|
|
55312
|
+
if (!runner.actions.loginProvider) throw new Error("loginProvider is not available in this host mode");
|
|
55313
|
+
return runner.actions.loginProvider(name);
|
|
55314
|
+
},
|
|
55315
|
+
reload: async () => {
|
|
55316
|
+
if (!runner.actions.reload) throw new Error("reload is not available in this host mode");
|
|
55317
|
+
return runner.actions.reload();
|
|
55318
|
+
},
|
|
54745
55319
|
get yoloMode() {
|
|
54746
55320
|
return runner.actions.yoloMode ?? false;
|
|
54747
55321
|
}
|
|
@@ -54821,6 +55395,81 @@ var ExtensionRunner = class {
|
|
|
54821
55395
|
isError: currentEvent.isError ?? event.isError
|
|
54822
55396
|
};
|
|
54823
55397
|
}
|
|
55398
|
+
/**
|
|
55399
|
+
* Emit session:before_switch. The first cancel wins; default allows.
|
|
55400
|
+
*/
|
|
55401
|
+
async emitSessionBeforeSwitch(event) {
|
|
55402
|
+
const ctx = this.createContext();
|
|
55403
|
+
for (const ext of this.extensions) {
|
|
55404
|
+
const handlers = ext.handlers.get("session:before_switch");
|
|
55405
|
+
if (!handlers || handlers.length === 0) continue;
|
|
55406
|
+
for (const handler of handlers) {
|
|
55407
|
+
try {
|
|
55408
|
+
const result = await handler(event, ctx);
|
|
55409
|
+
if (result && typeof result === "object" && result.cancel === true) return { cancel: true };
|
|
55410
|
+
} catch (err) {
|
|
55411
|
+
if (process.env.PIE_VERBOSE) {
|
|
55412
|
+
console.warn(`[Runner] session:before_switch handler failed in ${ext.path}:`, err);
|
|
55413
|
+
}
|
|
55414
|
+
}
|
|
55415
|
+
}
|
|
55416
|
+
}
|
|
55417
|
+
return {};
|
|
55418
|
+
}
|
|
55419
|
+
/**
|
|
55420
|
+
* Emit session:before_compact. The first cancel wins; the first custom
|
|
55421
|
+
* summary wins; default runs the built-in summarizer.
|
|
55422
|
+
*/
|
|
55423
|
+
async emitSessionBeforeCompact(event) {
|
|
55424
|
+
const ctx = this.createContext();
|
|
55425
|
+
for (const ext of this.extensions) {
|
|
55426
|
+
const handlers = ext.handlers.get("session:before_compact");
|
|
55427
|
+
if (!handlers || handlers.length === 0) continue;
|
|
55428
|
+
for (const handler of handlers) {
|
|
55429
|
+
try {
|
|
55430
|
+
const result = await handler(event, ctx);
|
|
55431
|
+
if (!result || typeof result !== "object") continue;
|
|
55432
|
+
if (result.cancel === true) return { cancel: true };
|
|
55433
|
+
if (typeof result.summary === "string" && result.summary.trim()) return { summary: result.summary };
|
|
55434
|
+
} catch (err) {
|
|
55435
|
+
if (process.env.PIE_VERBOSE) {
|
|
55436
|
+
console.warn(`[Runner] session:before_compact handler failed in ${ext.path}:`, err);
|
|
55437
|
+
}
|
|
55438
|
+
}
|
|
55439
|
+
}
|
|
55440
|
+
}
|
|
55441
|
+
return {};
|
|
55442
|
+
}
|
|
55443
|
+
/**
|
|
55444
|
+
* Emit terminal:input to all extensions. Transforms chain through handlers
|
|
55445
|
+
* in registration order (each handler sees the previous transform); a
|
|
55446
|
+
* consume result stops the chain and wins. Default: continue unchanged.
|
|
55447
|
+
*/
|
|
55448
|
+
async emitTerminalInput(event) {
|
|
55449
|
+
const ctx = this.createContext();
|
|
55450
|
+
let current = { action: "continue" };
|
|
55451
|
+
let text = event.text;
|
|
55452
|
+
for (const ext of this.extensions) {
|
|
55453
|
+
const handlers = ext.handlers.get("terminal:input");
|
|
55454
|
+
if (!handlers || handlers.length === 0) continue;
|
|
55455
|
+
for (const handler of handlers) {
|
|
55456
|
+
try {
|
|
55457
|
+
const result = await handler({ ...event, text }, ctx);
|
|
55458
|
+
if (!result || typeof result !== "object" || !("action" in result)) continue;
|
|
55459
|
+
if (result.action === "consume") return { action: "consume" };
|
|
55460
|
+
if (result.action === "transform" && typeof result.text === "string") {
|
|
55461
|
+
text = result.text;
|
|
55462
|
+
current = { action: "transform", text };
|
|
55463
|
+
}
|
|
55464
|
+
} catch (err) {
|
|
55465
|
+
if (process.env.PIE_VERBOSE) {
|
|
55466
|
+
console.warn(`[Runner] terminal:input handler failed in ${ext.path}:`, err);
|
|
55467
|
+
}
|
|
55468
|
+
}
|
|
55469
|
+
}
|
|
55470
|
+
}
|
|
55471
|
+
return current;
|
|
55472
|
+
}
|
|
54824
55473
|
/**
|
|
54825
55474
|
* Emit a generic event to all extensions.
|
|
54826
55475
|
*/
|
|
@@ -54958,8 +55607,8 @@ function wrapToolsWithExtensions(tools, runner) {
|
|
|
54958
55607
|
}
|
|
54959
55608
|
|
|
54960
55609
|
// src/keybindings.ts
|
|
54961
|
-
import { existsSync as
|
|
54962
|
-
import { join as
|
|
55610
|
+
import { existsSync as existsSync8, readFileSync as readFileSync7 } from "fs";
|
|
55611
|
+
import { join as join7 } from "path";
|
|
54963
55612
|
var DEFAULT_APP_KEYBINDINGS = {
|
|
54964
55613
|
interrupt: "escape",
|
|
54965
55614
|
clear: "ctrl+c",
|
|
@@ -55023,7 +55672,7 @@ var KeybindingsManager = class _KeybindingsManager {
|
|
|
55023
55672
|
* Create from config file and set up editor keybindings.
|
|
55024
55673
|
*/
|
|
55025
55674
|
static create(agentDir = getAgentDir()) {
|
|
55026
|
-
const configPath =
|
|
55675
|
+
const configPath = join7(agentDir, "keybindings.json");
|
|
55027
55676
|
const config = _KeybindingsManager.loadFromFile(configPath);
|
|
55028
55677
|
const manager = new _KeybindingsManager(config);
|
|
55029
55678
|
const editorConfig = {};
|
|
@@ -55041,10 +55690,10 @@ var KeybindingsManager = class _KeybindingsManager {
|
|
|
55041
55690
|
static inMemory(config = {}) {
|
|
55042
55691
|
return new _KeybindingsManager(config);
|
|
55043
55692
|
}
|
|
55044
|
-
static loadFromFile(
|
|
55045
|
-
if (!
|
|
55693
|
+
static loadFromFile(path33) {
|
|
55694
|
+
if (!existsSync8(path33)) return {};
|
|
55046
55695
|
try {
|
|
55047
|
-
return JSON.parse(
|
|
55696
|
+
return JSON.parse(readFileSync7(path33, "utf-8"));
|
|
55048
55697
|
} catch {
|
|
55049
55698
|
return {};
|
|
55050
55699
|
}
|
|
@@ -55093,19 +55742,19 @@ var KeybindingsManager = class _KeybindingsManager {
|
|
|
55093
55742
|
};
|
|
55094
55743
|
|
|
55095
55744
|
// src/project-knowledge.ts
|
|
55096
|
-
import
|
|
55097
|
-
import
|
|
55745
|
+
import fs11 from "node:fs";
|
|
55746
|
+
import path9 from "node:path";
|
|
55098
55747
|
function collectAncestorAgentsFiles(cwd) {
|
|
55099
|
-
const resolvedCwd =
|
|
55748
|
+
const resolvedCwd = path9.resolve(cwd || process.cwd());
|
|
55100
55749
|
const projectRoot = resolveCliProjectRoot(resolvedCwd);
|
|
55101
55750
|
const candidates = [];
|
|
55102
55751
|
let current = resolvedCwd;
|
|
55103
55752
|
while (true) {
|
|
55104
|
-
candidates.unshift(
|
|
55753
|
+
candidates.unshift(path9.join(current, AGENTS_CONTEXT_FILE_NAME));
|
|
55105
55754
|
if (current === projectRoot) {
|
|
55106
55755
|
break;
|
|
55107
55756
|
}
|
|
55108
|
-
const parent =
|
|
55757
|
+
const parent = path9.dirname(current);
|
|
55109
55758
|
if (parent === current) {
|
|
55110
55759
|
break;
|
|
55111
55760
|
}
|
|
@@ -55113,13 +55762,13 @@ function collectAncestorAgentsFiles(cwd) {
|
|
|
55113
55762
|
}
|
|
55114
55763
|
const files = [];
|
|
55115
55764
|
for (const candidate of candidates) {
|
|
55116
|
-
if (!
|
|
55765
|
+
if (!fs11.existsSync(candidate)) {
|
|
55117
55766
|
continue;
|
|
55118
55767
|
}
|
|
55119
55768
|
try {
|
|
55120
55769
|
files.push({
|
|
55121
|
-
path:
|
|
55122
|
-
content:
|
|
55770
|
+
path: path9.relative(resolvedCwd, candidate) || AGENTS_CONTEXT_FILE_NAME,
|
|
55771
|
+
content: fs11.readFileSync(candidate, "utf-8")
|
|
55123
55772
|
});
|
|
55124
55773
|
} catch {
|
|
55125
55774
|
}
|
|
@@ -55131,14 +55780,14 @@ function buildCliProjectContextSection(cwd) {
|
|
|
55131
55780
|
}
|
|
55132
55781
|
|
|
55133
55782
|
// src/file-access-context.ts
|
|
55134
|
-
import * as
|
|
55783
|
+
import * as path10 from "node:path";
|
|
55135
55784
|
function uniqueNormalizedPaths(paths) {
|
|
55136
55785
|
const seen = /* @__PURE__ */ new Set();
|
|
55137
55786
|
const result = [];
|
|
55138
55787
|
for (const entry of paths) {
|
|
55139
55788
|
const trimmed = String(entry || "").trim();
|
|
55140
55789
|
if (!trimmed) continue;
|
|
55141
|
-
const normalized =
|
|
55790
|
+
const normalized = path10.normalize(trimmed);
|
|
55142
55791
|
if (seen.has(normalized)) continue;
|
|
55143
55792
|
seen.add(normalized);
|
|
55144
55793
|
result.push(normalized);
|
|
@@ -55157,8 +55806,8 @@ function buildCliFileAccessOptions(params) {
|
|
|
55157
55806
|
}
|
|
55158
55807
|
|
|
55159
55808
|
// src/theme/theme.ts
|
|
55160
|
-
import * as
|
|
55161
|
-
import * as
|
|
55809
|
+
import * as fs12 from "node:fs";
|
|
55810
|
+
import * as path11 from "node:path";
|
|
55162
55811
|
|
|
55163
55812
|
// ../../node_modules/@sinclair/typebox/build/esm/compiler/compiler.mjs
|
|
55164
55813
|
var TypeCheck = class {
|
|
@@ -56045,11 +56694,11 @@ var BUILTIN_THEMES;
|
|
|
56045
56694
|
function getBuiltinThemes() {
|
|
56046
56695
|
if (!BUILTIN_THEMES) {
|
|
56047
56696
|
const themesDir = getThemesDir();
|
|
56048
|
-
const darkPath =
|
|
56049
|
-
const lightPath =
|
|
56697
|
+
const darkPath = path11.join(themesDir, "dark.json");
|
|
56698
|
+
const lightPath = path11.join(themesDir, "light.json");
|
|
56050
56699
|
BUILTIN_THEMES = {
|
|
56051
|
-
dark: JSON.parse(
|
|
56052
|
-
light: JSON.parse(
|
|
56700
|
+
dark: JSON.parse(fs12.readFileSync(darkPath, "utf-8")),
|
|
56701
|
+
light: JSON.parse(fs12.readFileSync(lightPath, "utf-8"))
|
|
56053
56702
|
};
|
|
56054
56703
|
}
|
|
56055
56704
|
return BUILTIN_THEMES;
|
|
@@ -56057,8 +56706,8 @@ function getBuiltinThemes() {
|
|
|
56057
56706
|
function getAvailableThemes() {
|
|
56058
56707
|
const themes = new Set(Object.keys(getBuiltinThemes()));
|
|
56059
56708
|
const customThemesDir = getCustomThemesDir();
|
|
56060
|
-
if (
|
|
56061
|
-
const files =
|
|
56709
|
+
if (fs12.existsSync(customThemesDir)) {
|
|
56710
|
+
const files = fs12.readdirSync(customThemesDir);
|
|
56062
56711
|
for (const file of files) {
|
|
56063
56712
|
if (file.endsWith(".json")) {
|
|
56064
56713
|
themes.add(file.slice(0, -5));
|
|
@@ -56119,18 +56768,18 @@ function loadThemeJson(name) {
|
|
|
56119
56768
|
}
|
|
56120
56769
|
const registeredTheme = registeredThemes.get(name);
|
|
56121
56770
|
if (registeredTheme?.sourcePath) {
|
|
56122
|
-
const content2 =
|
|
56771
|
+
const content2 = fs12.readFileSync(registeredTheme.sourcePath, "utf-8");
|
|
56123
56772
|
return parseThemeJsonContent(registeredTheme.sourcePath, content2);
|
|
56124
56773
|
}
|
|
56125
56774
|
if (registeredTheme) {
|
|
56126
56775
|
throw new Error(`Theme "${name}" does not have a source path for export`);
|
|
56127
56776
|
}
|
|
56128
56777
|
const customThemesDir = getCustomThemesDir();
|
|
56129
|
-
const themePath =
|
|
56130
|
-
if (!
|
|
56778
|
+
const themePath = path11.join(customThemesDir, `${name}.json`);
|
|
56779
|
+
if (!fs12.existsSync(themePath)) {
|
|
56131
56780
|
throw new Error(`Theme not found: ${name}`);
|
|
56132
56781
|
}
|
|
56133
|
-
const content =
|
|
56782
|
+
const content = fs12.readFileSync(themePath, "utf-8");
|
|
56134
56783
|
return parseThemeJsonContent(name, content);
|
|
56135
56784
|
}
|
|
56136
56785
|
function createTheme(themeJson, mode, sourcePath) {
|
|
@@ -56243,12 +56892,12 @@ function startThemeWatcher() {
|
|
|
56243
56892
|
return;
|
|
56244
56893
|
}
|
|
56245
56894
|
const customThemesDir = getCustomThemesDir();
|
|
56246
|
-
const themeFile =
|
|
56247
|
-
if (!
|
|
56895
|
+
const themeFile = path11.join(customThemesDir, `${currentThemeName}.json`);
|
|
56896
|
+
if (!fs12.existsSync(themeFile)) {
|
|
56248
56897
|
return;
|
|
56249
56898
|
}
|
|
56250
56899
|
try {
|
|
56251
|
-
themeWatcher =
|
|
56900
|
+
themeWatcher = fs12.watch(themeFile, (eventType) => {
|
|
56252
56901
|
if (eventType === "change") {
|
|
56253
56902
|
setTimeout(() => {
|
|
56254
56903
|
try {
|
|
@@ -56261,7 +56910,7 @@ function startThemeWatcher() {
|
|
|
56261
56910
|
}, 100);
|
|
56262
56911
|
} else if (eventType === "rename") {
|
|
56263
56912
|
setTimeout(() => {
|
|
56264
|
-
if (!
|
|
56913
|
+
if (!fs12.existsSync(themeFile)) {
|
|
56265
56914
|
currentThemeName = "dark";
|
|
56266
56915
|
setGlobalTheme(loadTheme("dark"));
|
|
56267
56916
|
if (themeWatcher) {
|
|
@@ -56441,8 +57090,9 @@ function getSettingsListTheme() {
|
|
|
56441
57090
|
}
|
|
56442
57091
|
|
|
56443
57092
|
// src/skills/loader.ts
|
|
56444
|
-
import * as
|
|
56445
|
-
import * as
|
|
57093
|
+
import * as fs13 from "fs";
|
|
57094
|
+
import * as os4 from "os";
|
|
57095
|
+
import * as path12 from "path";
|
|
56446
57096
|
function parseSkillDescription(content) {
|
|
56447
57097
|
const frontmatterMatch = content.match(/^---\n[\s\S]*?description:\s*["']?(.+?)["']?(?:\n|$)/m);
|
|
56448
57098
|
if (frontmatterMatch) return frontmatterMatch[1].trim();
|
|
@@ -56457,21 +57107,53 @@ function parseSkillDescription(content) {
|
|
|
56457
57107
|
}
|
|
56458
57108
|
return "No description";
|
|
56459
57109
|
}
|
|
57110
|
+
var USER_CONTROLLED_SOURCES = /* @__PURE__ */ new Set(["user-agents", "user"]);
|
|
57111
|
+
function pathIsInside(child, parent) {
|
|
57112
|
+
const relative3 = path12.relative(parent, child);
|
|
57113
|
+
return relative3 !== "" && !relative3.startsWith(`..${path12.sep}`) && relative3 !== ".." && !path12.isAbsolute(relative3);
|
|
57114
|
+
}
|
|
57115
|
+
function resolveSkillSymlink(fullPath, dir, source) {
|
|
57116
|
+
let target;
|
|
57117
|
+
let stats;
|
|
57118
|
+
try {
|
|
57119
|
+
target = fs13.realpathSync(fullPath);
|
|
57120
|
+
stats = fs13.statSync(target);
|
|
57121
|
+
} catch {
|
|
57122
|
+
return { error: `broken skill symlink: ${fullPath}` };
|
|
57123
|
+
}
|
|
57124
|
+
if (!USER_CONTROLLED_SOURCES.has(source) && !pathIsInside(target, fs13.realpathSync(dir))) {
|
|
57125
|
+
return { error: `skill symlink escapes the "${source}" layer (not loaded): ${fullPath} -> ${target}` };
|
|
57126
|
+
}
|
|
57127
|
+
return { target, isDirectory: stats.isDirectory(), isFile: stats.isFile() };
|
|
57128
|
+
}
|
|
56460
57129
|
function loadSkillsFromDir(dir, source) {
|
|
56461
57130
|
const skills = [];
|
|
56462
57131
|
const diagnostics = [];
|
|
56463
|
-
if (!
|
|
57132
|
+
if (!fs13.existsSync(dir)) {
|
|
56464
57133
|
return { skills, diagnostics };
|
|
56465
57134
|
}
|
|
56466
|
-
const entries =
|
|
57135
|
+
const entries = fs13.readdirSync(dir, { withFileTypes: true });
|
|
56467
57136
|
for (const entry of entries) {
|
|
56468
57137
|
if (entry.name.startsWith(".")) continue;
|
|
56469
|
-
const fullPath =
|
|
56470
|
-
|
|
56471
|
-
|
|
56472
|
-
|
|
57138
|
+
const fullPath = path12.join(dir, entry.name);
|
|
57139
|
+
let entryPath = fullPath;
|
|
57140
|
+
let entryIsDirectory = entry.isDirectory();
|
|
57141
|
+
let entryIsFile = entry.isFile();
|
|
57142
|
+
if (entry.isSymbolicLink()) {
|
|
57143
|
+
const resolved = resolveSkillSymlink(fullPath, dir, source);
|
|
57144
|
+
if ("error" in resolved) {
|
|
57145
|
+
diagnostics.push({ type: "warning", message: resolved.error, path: fullPath });
|
|
57146
|
+
continue;
|
|
57147
|
+
}
|
|
57148
|
+
entryPath = resolved.target;
|
|
57149
|
+
entryIsDirectory = resolved.isDirectory;
|
|
57150
|
+
entryIsFile = resolved.isFile;
|
|
57151
|
+
}
|
|
57152
|
+
if (entryIsDirectory) {
|
|
57153
|
+
const skillMdPath = path12.join(entryPath, "SKILL.md");
|
|
57154
|
+
if (fs13.existsSync(skillMdPath)) {
|
|
56473
57155
|
try {
|
|
56474
|
-
const content =
|
|
57156
|
+
const content = fs13.readFileSync(skillMdPath, "utf-8");
|
|
56475
57157
|
const description = parseSkillDescription(content);
|
|
56476
57158
|
skills.push({
|
|
56477
57159
|
name: entry.name,
|
|
@@ -56479,7 +57161,7 @@ function loadSkillsFromDir(dir, source) {
|
|
|
56479
57161
|
content,
|
|
56480
57162
|
filePath: skillMdPath,
|
|
56481
57163
|
source,
|
|
56482
|
-
baseDir:
|
|
57164
|
+
baseDir: entryPath
|
|
56483
57165
|
});
|
|
56484
57166
|
} catch (e) {
|
|
56485
57167
|
diagnostics.push({
|
|
@@ -56489,64 +57171,72 @@ function loadSkillsFromDir(dir, source) {
|
|
|
56489
57171
|
});
|
|
56490
57172
|
}
|
|
56491
57173
|
}
|
|
56492
|
-
} else if (
|
|
57174
|
+
} else if (entryIsFile && entry.name.endsWith(".md")) {
|
|
56493
57175
|
const name = entry.name.slice(0, -3);
|
|
56494
57176
|
try {
|
|
56495
|
-
const content =
|
|
57177
|
+
const content = fs13.readFileSync(entryPath, "utf-8");
|
|
56496
57178
|
const description = parseSkillDescription(content);
|
|
56497
57179
|
skills.push({
|
|
56498
57180
|
name,
|
|
56499
57181
|
description,
|
|
56500
57182
|
content,
|
|
56501
|
-
filePath:
|
|
57183
|
+
filePath: entryPath,
|
|
56502
57184
|
source,
|
|
56503
|
-
baseDir:
|
|
57185
|
+
baseDir: path12.dirname(entryPath)
|
|
56504
57186
|
});
|
|
56505
57187
|
} catch (e) {
|
|
56506
57188
|
diagnostics.push({
|
|
56507
57189
|
type: "warning",
|
|
56508
57190
|
message: `Failed to load skill: ${name}`,
|
|
56509
|
-
path:
|
|
57191
|
+
path: entryPath
|
|
56510
57192
|
});
|
|
56511
57193
|
}
|
|
56512
57194
|
}
|
|
56513
57195
|
}
|
|
56514
57196
|
return { skills, diagnostics };
|
|
56515
57197
|
}
|
|
57198
|
+
function discoverAgentsSkillsDirs(cwd) {
|
|
57199
|
+
const resolvedCwd = path12.resolve(cwd || process.cwd());
|
|
57200
|
+
const projectRoot = resolveCliProjectRoot(resolvedCwd);
|
|
57201
|
+
const projectAgentsDirs = [];
|
|
57202
|
+
let current = projectRoot;
|
|
57203
|
+
while (true) {
|
|
57204
|
+
projectAgentsDirs.push(path12.join(current, ".agents", "skills"));
|
|
57205
|
+
if (current === resolvedCwd) break;
|
|
57206
|
+
const child = path12.relative(current, resolvedCwd).split(path12.sep)[0];
|
|
57207
|
+
if (!child) break;
|
|
57208
|
+
current = path12.join(current, child);
|
|
57209
|
+
}
|
|
57210
|
+
return { userAgentsDir: path12.join(os4.homedir(), ".agents", "skills"), projectAgentsDirs };
|
|
57211
|
+
}
|
|
57212
|
+
function buildSkillDirs(options) {
|
|
57213
|
+
const { userAgentsDir, projectAgentsDirs } = discoverAgentsSkillsDirs(options.cwd);
|
|
57214
|
+
return [
|
|
57215
|
+
{ dir: options.builtinDir, source: "builtin" },
|
|
57216
|
+
{ dir: userAgentsDir, source: "user-agents" },
|
|
57217
|
+
{ dir: options.userDir, source: "user" },
|
|
57218
|
+
...projectAgentsDirs.map((dir) => ({ dir, source: "project-agents" })),
|
|
57219
|
+
{ dir: options.projectDir, source: "project" }
|
|
57220
|
+
];
|
|
57221
|
+
}
|
|
56516
57222
|
function loadSkills(options) {
|
|
56517
|
-
const { builtinDir, userDir, projectDir } = options;
|
|
56518
57223
|
const skillMap = /* @__PURE__ */ new Map();
|
|
56519
57224
|
const diagnostics = [];
|
|
56520
|
-
const
|
|
56521
|
-
|
|
56522
|
-
|
|
56523
|
-
}
|
|
56524
|
-
diagnostics.push(...builtinResult.diagnostics);
|
|
56525
|
-
const userResult = loadSkillsFromDir(userDir, "user");
|
|
56526
|
-
for (const skill of userResult.skills) {
|
|
56527
|
-
if (skillMap.has(skill.name)) {
|
|
56528
|
-
diagnostics.push({
|
|
56529
|
-
type: "collision",
|
|
56530
|
-
message: `Skill "${skill.name}" collision: user skill overrides builtin`,
|
|
56531
|
-
path: skill.filePath
|
|
56532
|
-
});
|
|
56533
|
-
}
|
|
56534
|
-
skillMap.set(skill.name, skill);
|
|
56535
|
-
}
|
|
56536
|
-
diagnostics.push(...userResult.diagnostics);
|
|
56537
|
-
const projectResult = loadSkillsFromDir(projectDir, "project");
|
|
56538
|
-
for (const skill of projectResult.skills) {
|
|
56539
|
-
if (skillMap.has(skill.name)) {
|
|
57225
|
+
for (const { dir, source } of options.dirs) {
|
|
57226
|
+
const result = loadSkillsFromDir(dir, source);
|
|
57227
|
+
for (const skill of result.skills) {
|
|
56540
57228
|
const existing = skillMap.get(skill.name);
|
|
56541
|
-
|
|
56542
|
-
|
|
56543
|
-
|
|
56544
|
-
|
|
56545
|
-
|
|
57229
|
+
if (existing) {
|
|
57230
|
+
diagnostics.push({
|
|
57231
|
+
type: "collision",
|
|
57232
|
+
message: `Skill "${skill.name}" collision: ${source} skill overrides ${existing.source}`,
|
|
57233
|
+
path: skill.filePath
|
|
57234
|
+
});
|
|
57235
|
+
}
|
|
57236
|
+
skillMap.set(skill.name, skill);
|
|
56546
57237
|
}
|
|
56547
|
-
|
|
57238
|
+
diagnostics.push(...result.diagnostics);
|
|
56548
57239
|
}
|
|
56549
|
-
diagnostics.push(...projectResult.diagnostics);
|
|
56550
57240
|
return { skills: Array.from(skillMap.values()), diagnostics };
|
|
56551
57241
|
}
|
|
56552
57242
|
function formatSkillsForPrompt(skills) {
|
|
@@ -56687,9 +57377,12 @@ function validateSkillName(name) {
|
|
|
56687
57377
|
return { valid: true };
|
|
56688
57378
|
}
|
|
56689
57379
|
|
|
57380
|
+
// src/interactive-mode.ts
|
|
57381
|
+
import * as path29 from "path";
|
|
57382
|
+
|
|
56690
57383
|
// src/capabilities/read-file-understanding.ts
|
|
56691
|
-
import * as
|
|
56692
|
-
import * as
|
|
57384
|
+
import * as path21 from "node:path";
|
|
57385
|
+
import * as fs21 from "node:fs/promises";
|
|
56693
57386
|
|
|
56694
57387
|
// src/core/provider-capabilities.ts
|
|
56695
57388
|
var PLATFORM_DISPLAY_NAMES = {
|
|
@@ -56715,8 +57408,8 @@ function getProviderDisplayName(platform2) {
|
|
|
56715
57408
|
}
|
|
56716
57409
|
|
|
56717
57410
|
// src/files/adapters/kimi-adapter.ts
|
|
56718
|
-
import * as
|
|
56719
|
-
import * as
|
|
57411
|
+
import * as fs14 from "node:fs";
|
|
57412
|
+
import * as path13 from "node:path";
|
|
56720
57413
|
|
|
56721
57414
|
// src/files/errors.ts
|
|
56722
57415
|
var FileErrorMessages = {
|
|
@@ -56853,17 +57546,17 @@ var KimiFileAdapter = class {
|
|
|
56853
57546
|
// Upload
|
|
56854
57547
|
// ============================================================================
|
|
56855
57548
|
async upload(filePath, options = {}) {
|
|
56856
|
-
const fileName = options.sourceName ||
|
|
57549
|
+
const fileName = options.sourceName || path13.basename(filePath);
|
|
56857
57550
|
const mimeType = options.mimeType || this.guessMimeType(fileName);
|
|
56858
57551
|
const purpose = this.detectPurpose(filePath, options.purpose, mimeType);
|
|
56859
|
-
const stats =
|
|
57552
|
+
const stats = fs14.statSync(filePath);
|
|
56860
57553
|
if (stats.size > this.capabilities.maxFileSize) {
|
|
56861
57554
|
throw new FileManagerError(
|
|
56862
57555
|
"FILE_TOO_LARGE" /* FILE_TOO_LARGE */,
|
|
56863
57556
|
`File size ${(stats.size / 1024 / 1024).toFixed(1)}MB exceeds Kimi limit of 100MB`
|
|
56864
57557
|
);
|
|
56865
57558
|
}
|
|
56866
|
-
const fileBuffer =
|
|
57559
|
+
const fileBuffer = fs14.readFileSync(filePath);
|
|
56867
57560
|
const file = new File([fileBuffer], fileName, { type: mimeType });
|
|
56868
57561
|
const formData = new FormData();
|
|
56869
57562
|
formData.append("file", file);
|
|
@@ -56915,7 +57608,7 @@ var KimiFileAdapter = class {
|
|
|
56915
57608
|
return "file-extract";
|
|
56916
57609
|
}
|
|
56917
57610
|
guessMimeType(filename) {
|
|
56918
|
-
const ext =
|
|
57611
|
+
const ext = path13.extname(filename).toLowerCase();
|
|
56919
57612
|
switch (ext) {
|
|
56920
57613
|
case ".jpg":
|
|
56921
57614
|
case ".jpeg":
|
|
@@ -57077,8 +57770,8 @@ var KimiFileAdapter = class {
|
|
|
57077
57770
|
};
|
|
57078
57771
|
|
|
57079
57772
|
// src/files/adapters/gemini-adapter.ts
|
|
57080
|
-
import * as
|
|
57081
|
-
import * as
|
|
57773
|
+
import * as fs15 from "node:fs";
|
|
57774
|
+
import * as path14 from "node:path";
|
|
57082
57775
|
import { Readable } from "node:stream";
|
|
57083
57776
|
var GeminiFileAdapter = class {
|
|
57084
57777
|
platform = "gemini";
|
|
@@ -57135,7 +57828,7 @@ var GeminiFileAdapter = class {
|
|
|
57135
57828
|
// Upload
|
|
57136
57829
|
// ============================================================================
|
|
57137
57830
|
async upload(filePath, options = {}) {
|
|
57138
|
-
const stats =
|
|
57831
|
+
const stats = fs15.statSync(filePath);
|
|
57139
57832
|
if (stats.size > this.capabilities.maxFileSize) {
|
|
57140
57833
|
throw new FileManagerError(
|
|
57141
57834
|
"FILE_TOO_LARGE" /* FILE_TOO_LARGE */,
|
|
@@ -57143,7 +57836,7 @@ var GeminiFileAdapter = class {
|
|
|
57143
57836
|
);
|
|
57144
57837
|
}
|
|
57145
57838
|
const mimeType = options.mimeType || this.guessMimeType(filePath);
|
|
57146
|
-
const displayName = options.sourceName ||
|
|
57839
|
+
const displayName = options.sourceName || path14.basename(filePath);
|
|
57147
57840
|
const startResponse = await fetch(
|
|
57148
57841
|
`${this.baseUrl}/upload/v1beta/files?key=${this.apiKey}`,
|
|
57149
57842
|
{
|
|
@@ -57174,7 +57867,7 @@ var GeminiFileAdapter = class {
|
|
|
57174
57867
|
"No upload URL received"
|
|
57175
57868
|
);
|
|
57176
57869
|
}
|
|
57177
|
-
const fileStream = Readable.toWeb(
|
|
57870
|
+
const fileStream = Readable.toWeb(fs15.createReadStream(filePath));
|
|
57178
57871
|
const uploadResponse = await fetch(uploadUrl, {
|
|
57179
57872
|
method: "POST",
|
|
57180
57873
|
headers: {
|
|
@@ -57378,8 +58071,8 @@ var GeminiFileAdapter = class {
|
|
|
57378
58071
|
};
|
|
57379
58072
|
|
|
57380
58073
|
// src/files/adapters/openai-compatible-files-adapter.ts
|
|
57381
|
-
import * as
|
|
57382
|
-
import * as
|
|
58074
|
+
import * as fs16 from "node:fs";
|
|
58075
|
+
import * as path15 from "node:path";
|
|
57383
58076
|
var OpenAICompatibleFilesAdapter = class {
|
|
57384
58077
|
constructor(apiKey, baseUrl, extraHeaders = {}) {
|
|
57385
58078
|
this.apiKey = apiKey;
|
|
@@ -57405,16 +58098,16 @@ var OpenAICompatibleFilesAdapter = class {
|
|
|
57405
58098
|
]
|
|
57406
58099
|
};
|
|
57407
58100
|
async upload(filePath, options = {}) {
|
|
57408
|
-
const fileName = options.sourceName ||
|
|
58101
|
+
const fileName = options.sourceName || path15.basename(filePath);
|
|
57409
58102
|
const mimeType = options.mimeType || guessMimeType(fileName);
|
|
57410
|
-
const stats =
|
|
58103
|
+
const stats = fs16.statSync(filePath);
|
|
57411
58104
|
if (stats.size > this.capabilities.maxFileSize) {
|
|
57412
58105
|
throw new FileManagerError(
|
|
57413
58106
|
"FILE_TOO_LARGE" /* FILE_TOO_LARGE */,
|
|
57414
58107
|
`File size ${(stats.size / 1024 / 1024).toFixed(1)}MB exceeds OpenAI-compatible Files limit of ${this.capabilities.maxFileSize / 1024 / 1024}MB`
|
|
57415
58108
|
);
|
|
57416
58109
|
}
|
|
57417
|
-
const file = new File([
|
|
58110
|
+
const file = new File([fs16.readFileSync(filePath)], fileName, { type: mimeType });
|
|
57418
58111
|
const formData = new FormData();
|
|
57419
58112
|
formData.append("file", file);
|
|
57420
58113
|
formData.append("purpose", options.purpose && options.purpose !== "auto" ? options.purpose : "file-extract");
|
|
@@ -57531,7 +58224,7 @@ function extractContentText(raw) {
|
|
|
57531
58224
|
return raw;
|
|
57532
58225
|
}
|
|
57533
58226
|
function guessMimeType(filename) {
|
|
57534
|
-
const ext =
|
|
58227
|
+
const ext = path15.extname(filename).toLowerCase();
|
|
57535
58228
|
switch (ext) {
|
|
57536
58229
|
case ".pdf":
|
|
57537
58230
|
return "application/pdf";
|
|
@@ -57547,13 +58240,13 @@ function guessMimeType(filename) {
|
|
|
57547
58240
|
}
|
|
57548
58241
|
|
|
57549
58242
|
// src/files/file-manager.ts
|
|
57550
|
-
import * as
|
|
57551
|
-
import * as
|
|
58243
|
+
import * as fs20 from "node:fs";
|
|
58244
|
+
import * as path20 from "node:path";
|
|
57552
58245
|
import * as crypto2 from "node:crypto";
|
|
57553
58246
|
|
|
57554
58247
|
// src/files/file-cache.ts
|
|
57555
|
-
import * as
|
|
57556
|
-
import * as
|
|
58248
|
+
import * as fs17 from "node:fs";
|
|
58249
|
+
import * as path17 from "node:path";
|
|
57557
58250
|
import * as crypto from "node:crypto";
|
|
57558
58251
|
|
|
57559
58252
|
// src/files/logger.ts
|
|
@@ -57648,14 +58341,14 @@ var DEFAULT_PIC_OPTIONS = {
|
|
|
57648
58341
|
};
|
|
57649
58342
|
|
|
57650
58343
|
// src/files/utils.ts
|
|
57651
|
-
import * as
|
|
57652
|
-
import { homedir as
|
|
58344
|
+
import * as path16 from "node:path";
|
|
58345
|
+
import { homedir as homedir5 } from "node:os";
|
|
57653
58346
|
var WINDOWS_ABSOLUTE_PATH_RE = /^(?:[a-zA-Z]:[\\/]|[\\/]{2}[^\\/]+[\\/][^\\/]+)/;
|
|
57654
58347
|
function isWindowsAbsolutePath(filePath) {
|
|
57655
58348
|
return WINDOWS_ABSOLUTE_PATH_RE.test(filePath);
|
|
57656
58349
|
}
|
|
57657
58350
|
function getHomeDirectory() {
|
|
57658
|
-
return
|
|
58351
|
+
return homedir5() || process.env.USERPROFILE || process.env.HOME || ".";
|
|
57659
58352
|
}
|
|
57660
58353
|
function looksLikeFilePath(inputPath) {
|
|
57661
58354
|
return inputPath.startsWith("/") || inputPath.startsWith("~") || inputPath.startsWith("./") || inputPath.startsWith("../") || inputPath.startsWith(".\\") || inputPath.startsWith("..\\") || isWindowsAbsolutePath(inputPath);
|
|
@@ -57669,13 +58362,13 @@ function formatBytes(bytes) {
|
|
|
57669
58362
|
}
|
|
57670
58363
|
function resolvePath2(inputPath) {
|
|
57671
58364
|
if (inputPath.startsWith("~")) {
|
|
57672
|
-
return
|
|
58365
|
+
return path16.join(getHomeDirectory(), inputPath.slice(1));
|
|
57673
58366
|
}
|
|
57674
58367
|
if (isWindowsAbsolutePath(inputPath)) {
|
|
57675
58368
|
return inputPath;
|
|
57676
58369
|
}
|
|
57677
|
-
if (!
|
|
57678
|
-
return
|
|
58370
|
+
if (!path16.isAbsolute(inputPath)) {
|
|
58371
|
+
return path16.resolve(process.cwd(), inputPath);
|
|
57679
58372
|
}
|
|
57680
58373
|
return inputPath;
|
|
57681
58374
|
}
|
|
@@ -57703,8 +58396,8 @@ var FileCache = class _FileCache {
|
|
|
57703
58396
|
maxEntries;
|
|
57704
58397
|
constructor(baseDir, maxEntries = DEFAULT_MAX_CACHE_ENTRIES) {
|
|
57705
58398
|
this.baseDir = baseDir;
|
|
57706
|
-
this.dataDir =
|
|
57707
|
-
this.indexPath =
|
|
58399
|
+
this.dataDir = path17.join(baseDir, "data");
|
|
58400
|
+
this.indexPath = path17.join(baseDir, "index.json");
|
|
57708
58401
|
this.maxEntries = maxEntries;
|
|
57709
58402
|
this.index = this.loadIndex();
|
|
57710
58403
|
this.ensureDirectories();
|
|
@@ -57713,22 +58406,22 @@ var FileCache = class _FileCache {
|
|
|
57713
58406
|
// Directory Management
|
|
57714
58407
|
// ============================================================================
|
|
57715
58408
|
ensureDirectories() {
|
|
57716
|
-
if (!
|
|
57717
|
-
|
|
58409
|
+
if (!fs17.existsSync(this.baseDir)) {
|
|
58410
|
+
fs17.mkdirSync(this.baseDir, { recursive: true });
|
|
57718
58411
|
}
|
|
57719
|
-
if (!
|
|
57720
|
-
|
|
58412
|
+
if (!fs17.existsSync(this.dataDir)) {
|
|
58413
|
+
fs17.mkdirSync(this.dataDir, { recursive: true });
|
|
57721
58414
|
}
|
|
57722
58415
|
}
|
|
57723
58416
|
// ============================================================================
|
|
57724
58417
|
// Index Management
|
|
57725
58418
|
// ============================================================================
|
|
57726
58419
|
loadIndex() {
|
|
57727
|
-
if (!
|
|
58420
|
+
if (!fs17.existsSync(this.indexPath)) {
|
|
57728
58421
|
return { version: CACHE_VERSION, files: {}, pathHints: {} };
|
|
57729
58422
|
}
|
|
57730
58423
|
try {
|
|
57731
|
-
const data = JSON.parse(
|
|
58424
|
+
const data = JSON.parse(fs17.readFileSync(this.indexPath, "utf-8"));
|
|
57732
58425
|
if (data.version !== CACHE_VERSION) {
|
|
57733
58426
|
cacheLogger.warn(`Index version mismatch: ${data.version} vs ${CACHE_VERSION}`);
|
|
57734
58427
|
}
|
|
@@ -57744,7 +58437,7 @@ var FileCache = class _FileCache {
|
|
|
57744
58437
|
}
|
|
57745
58438
|
saveIndex() {
|
|
57746
58439
|
try {
|
|
57747
|
-
|
|
58440
|
+
fs17.writeFileSync(this.indexPath, JSON.stringify(this.index, null, 2));
|
|
57748
58441
|
} catch (err) {
|
|
57749
58442
|
cacheLogger.error(" Failed to save index:", err);
|
|
57750
58443
|
throw err;
|
|
@@ -57754,11 +58447,11 @@ var FileCache = class _FileCache {
|
|
|
57754
58447
|
// Hash Computation
|
|
57755
58448
|
// ============================================================================
|
|
57756
58449
|
async computeHash(filePath) {
|
|
57757
|
-
return new Promise((
|
|
58450
|
+
return new Promise((resolve7, reject) => {
|
|
57758
58451
|
const hash = crypto.createHash("sha256");
|
|
57759
|
-
const stream =
|
|
58452
|
+
const stream = fs17.createReadStream(filePath);
|
|
57760
58453
|
stream.on("data", (chunk) => hash.update(chunk));
|
|
57761
|
-
stream.on("end", () =>
|
|
58454
|
+
stream.on("end", () => resolve7(hash.digest("hex")));
|
|
57762
58455
|
stream.on("error", reject);
|
|
57763
58456
|
});
|
|
57764
58457
|
}
|
|
@@ -57770,25 +58463,25 @@ var FileCache = class _FileCache {
|
|
|
57770
58463
|
// ============================================================================
|
|
57771
58464
|
getStoragePath(hash) {
|
|
57772
58465
|
const subdir = hash.slice(0, 2);
|
|
57773
|
-
const fullDir =
|
|
57774
|
-
if (!
|
|
57775
|
-
|
|
58466
|
+
const fullDir = path17.join(this.dataDir, subdir);
|
|
58467
|
+
if (!fs17.existsSync(fullDir)) {
|
|
58468
|
+
fs17.mkdirSync(fullDir, { recursive: true });
|
|
57776
58469
|
}
|
|
57777
|
-
return
|
|
58470
|
+
return path17.join(fullDir, hash);
|
|
57778
58471
|
}
|
|
57779
58472
|
getStorageRelativePath(hash) {
|
|
57780
58473
|
const subdir = hash.slice(0, 2);
|
|
57781
|
-
return
|
|
58474
|
+
return path17.join(subdir, hash);
|
|
57782
58475
|
}
|
|
57783
58476
|
async storeFile(sourcePath, name, mimeType) {
|
|
57784
58477
|
const hash = await this.computeHash(sourcePath);
|
|
57785
58478
|
const storagePath = this.getStoragePath(hash);
|
|
57786
58479
|
this.rememberPathHint(sourcePath, hash, name);
|
|
57787
|
-
if (
|
|
58480
|
+
if (fs17.existsSync(storagePath)) {
|
|
57788
58481
|
return { hash, localPath: storagePath };
|
|
57789
58482
|
}
|
|
57790
|
-
|
|
57791
|
-
const stat2 =
|
|
58483
|
+
fs17.copyFileSync(sourcePath, storagePath);
|
|
58484
|
+
const stat2 = fs17.statSync(storagePath);
|
|
57792
58485
|
this.index.files[hash] = {
|
|
57793
58486
|
hash,
|
|
57794
58487
|
name,
|
|
@@ -57805,10 +58498,10 @@ var FileCache = class _FileCache {
|
|
|
57805
58498
|
async storeBuffer(data, name, mimeType) {
|
|
57806
58499
|
const hash = this.computeHashSync(data);
|
|
57807
58500
|
const storagePath = this.getStoragePath(hash);
|
|
57808
|
-
if (
|
|
58501
|
+
if (fs17.existsSync(storagePath)) {
|
|
57809
58502
|
return { hash, localPath: storagePath };
|
|
57810
58503
|
}
|
|
57811
|
-
|
|
58504
|
+
fs17.writeFileSync(storagePath, data);
|
|
57812
58505
|
this.index.files[hash] = {
|
|
57813
58506
|
hash,
|
|
57814
58507
|
name,
|
|
@@ -57840,7 +58533,7 @@ var FileCache = class _FileCache {
|
|
|
57840
58533
|
getLocalPath(hash) {
|
|
57841
58534
|
const entry = this.index.files[hash];
|
|
57842
58535
|
if (!entry) return void 0;
|
|
57843
|
-
return
|
|
58536
|
+
return path17.join(this.dataDir, entry.localPath);
|
|
57844
58537
|
}
|
|
57845
58538
|
exists(hash) {
|
|
57846
58539
|
return hash in this.index.files;
|
|
@@ -57880,7 +58573,7 @@ var FileCache = class _FileCache {
|
|
|
57880
58573
|
getPathHint(filePath) {
|
|
57881
58574
|
try {
|
|
57882
58575
|
const resolvedPath = this.getResolvedHintPath(filePath);
|
|
57883
|
-
const stat2 =
|
|
58576
|
+
const stat2 = fs17.statSync(resolvedPath);
|
|
57884
58577
|
if (!stat2.isFile()) return void 0;
|
|
57885
58578
|
const normalizedMtimeMs = this.normalizeHintMtimeMs(stat2.mtimeMs);
|
|
57886
58579
|
const key = this.getPathHintKey(resolvedPath);
|
|
@@ -57910,7 +58603,7 @@ var FileCache = class _FileCache {
|
|
|
57910
58603
|
rememberPathHint(filePath, hash, name) {
|
|
57911
58604
|
try {
|
|
57912
58605
|
const resolvedPath = this.getResolvedHintPath(filePath);
|
|
57913
|
-
const stat2 =
|
|
58606
|
+
const stat2 = fs17.statSync(resolvedPath);
|
|
57914
58607
|
if (!stat2.isFile()) return;
|
|
57915
58608
|
const entry = this.index.files[hash];
|
|
57916
58609
|
const normalizedMtimeMs = this.normalizeHintMtimeMs(stat2.mtimeMs);
|
|
@@ -57920,7 +58613,7 @@ var FileCache = class _FileCache {
|
|
|
57920
58613
|
this.index.pathHints[this.getPathHintKey(resolvedPath)] = {
|
|
57921
58614
|
path: resolvedPath,
|
|
57922
58615
|
hash,
|
|
57923
|
-
name: name || entry?.name ||
|
|
58616
|
+
name: name || entry?.name || path17.basename(resolvedPath),
|
|
57924
58617
|
size: stat2.size,
|
|
57925
58618
|
mtimeMs: normalizedMtimeMs,
|
|
57926
58619
|
lastSeenAt: Date.now(),
|
|
@@ -57968,9 +58661,9 @@ var FileCache = class _FileCache {
|
|
|
57968
58661
|
deleteEntry(hash, entry) {
|
|
57969
58662
|
entry = entry || this.index.files[hash];
|
|
57970
58663
|
if (!entry) return;
|
|
57971
|
-
const localPath =
|
|
57972
|
-
if (
|
|
57973
|
-
|
|
58664
|
+
const localPath = path17.join(this.dataDir, entry.localPath);
|
|
58665
|
+
if (fs17.existsSync(localPath)) {
|
|
58666
|
+
fs17.unlinkSync(localPath);
|
|
57974
58667
|
}
|
|
57975
58668
|
delete this.index.files[hash];
|
|
57976
58669
|
if (this.index.pathHints) {
|
|
@@ -57984,9 +58677,9 @@ var FileCache = class _FileCache {
|
|
|
57984
58677
|
}
|
|
57985
58678
|
clear() {
|
|
57986
58679
|
for (const entry of Object.values(this.index.files)) {
|
|
57987
|
-
const localPath =
|
|
57988
|
-
if (
|
|
57989
|
-
|
|
58680
|
+
const localPath = path17.join(this.dataDir, entry.localPath);
|
|
58681
|
+
if (fs17.existsSync(localPath)) {
|
|
58682
|
+
fs17.unlinkSync(localPath);
|
|
57990
58683
|
}
|
|
57991
58684
|
}
|
|
57992
58685
|
this.index = { version: CACHE_VERSION, files: {}, pathHints: {} };
|
|
@@ -58039,11 +58732,11 @@ var FileCache = class _FileCache {
|
|
|
58039
58732
|
};
|
|
58040
58733
|
}
|
|
58041
58734
|
static getMimeType(fileName) {
|
|
58042
|
-
const ext =
|
|
58735
|
+
const ext = path17.extname(fileName).toLowerCase();
|
|
58043
58736
|
return MIME_TYPES[ext] || "application/octet-stream";
|
|
58044
58737
|
}
|
|
58045
58738
|
static getFileTypeFromName(fileName) {
|
|
58046
|
-
const baseName =
|
|
58739
|
+
const baseName = path17.basename(fileName);
|
|
58047
58740
|
if (!baseName || baseName.startsWith(".")) {
|
|
58048
58741
|
return void 0;
|
|
58049
58742
|
}
|
|
@@ -58074,7 +58767,7 @@ var FileCache = class _FileCache {
|
|
|
58074
58767
|
getResolvedHintPath(filePath) {
|
|
58075
58768
|
const resolvedPath = resolvePath2(filePath);
|
|
58076
58769
|
try {
|
|
58077
|
-
return
|
|
58770
|
+
return fs17.realpathSync.native ? fs17.realpathSync.native(resolvedPath) : fs17.realpathSync(resolvedPath);
|
|
58078
58771
|
} catch {
|
|
58079
58772
|
return resolvedPath;
|
|
58080
58773
|
}
|
|
@@ -58082,8 +58775,8 @@ var FileCache = class _FileCache {
|
|
|
58082
58775
|
};
|
|
58083
58776
|
|
|
58084
58777
|
// src/files/session-file-manager.ts
|
|
58085
|
-
import * as
|
|
58086
|
-
import * as
|
|
58778
|
+
import * as fs18 from "node:fs";
|
|
58779
|
+
import * as path18 from "node:path";
|
|
58087
58780
|
var INITIAL_REFRESH_BACKOFF_MS = 6e4;
|
|
58088
58781
|
var MAX_REFRESH_BACKOFF_MS = 60 * 60 * 1e3;
|
|
58089
58782
|
var SessionFileManager = class {
|
|
@@ -58097,7 +58790,7 @@ var SessionFileManager = class {
|
|
|
58097
58790
|
constructor(sessionId, sessionsDir, fileCache, authFingerprint) {
|
|
58098
58791
|
this.sessionId = sessionId;
|
|
58099
58792
|
this.sessionsDir = sessionsDir;
|
|
58100
|
-
this.statePath =
|
|
58793
|
+
this.statePath = path18.join(this.sessionsDir, `${sessionId}-files.json`);
|
|
58101
58794
|
this.fileCache = fileCache;
|
|
58102
58795
|
this.authFingerprint = authFingerprint;
|
|
58103
58796
|
this.state = this.loadState();
|
|
@@ -58107,15 +58800,15 @@ var SessionFileManager = class {
|
|
|
58107
58800
|
// Directory Management
|
|
58108
58801
|
// ============================================================================
|
|
58109
58802
|
ensureDirectories() {
|
|
58110
|
-
if (!
|
|
58111
|
-
|
|
58803
|
+
if (!fs18.existsSync(this.sessionsDir)) {
|
|
58804
|
+
fs18.mkdirSync(this.sessionsDir, { recursive: true });
|
|
58112
58805
|
}
|
|
58113
58806
|
}
|
|
58114
58807
|
// ============================================================================
|
|
58115
58808
|
// State Management
|
|
58116
58809
|
// ============================================================================
|
|
58117
58810
|
loadState() {
|
|
58118
|
-
if (!
|
|
58811
|
+
if (!fs18.existsSync(this.statePath)) {
|
|
58119
58812
|
return {
|
|
58120
58813
|
sessionId: this.sessionId,
|
|
58121
58814
|
lastSyncedAt: 0,
|
|
@@ -58123,7 +58816,7 @@ var SessionFileManager = class {
|
|
|
58123
58816
|
};
|
|
58124
58817
|
}
|
|
58125
58818
|
try {
|
|
58126
|
-
return JSON.parse(
|
|
58819
|
+
return JSON.parse(fs18.readFileSync(this.statePath, "utf-8"));
|
|
58127
58820
|
} catch (err) {
|
|
58128
58821
|
sessionLogger.error(" Failed to load state:", err);
|
|
58129
58822
|
return {
|
|
@@ -58136,12 +58829,12 @@ var SessionFileManager = class {
|
|
|
58136
58829
|
saveState() {
|
|
58137
58830
|
try {
|
|
58138
58831
|
if (Object.keys(this.state.files).length === 0) {
|
|
58139
|
-
if (
|
|
58140
|
-
|
|
58832
|
+
if (fs18.existsSync(this.statePath)) {
|
|
58833
|
+
fs18.unlinkSync(this.statePath);
|
|
58141
58834
|
}
|
|
58142
58835
|
return;
|
|
58143
58836
|
}
|
|
58144
|
-
|
|
58837
|
+
fs18.writeFileSync(this.statePath, JSON.stringify(this.state, null, 2));
|
|
58145
58838
|
} catch (err) {
|
|
58146
58839
|
sessionLogger.error(" Failed to save state:", err);
|
|
58147
58840
|
}
|
|
@@ -58287,7 +58980,7 @@ var SessionFileManager = class {
|
|
|
58287
58980
|
return { success: false, coolingDown: true };
|
|
58288
58981
|
}
|
|
58289
58982
|
const localPath = this.fileCache.getLocalPath(entry.hash);
|
|
58290
|
-
if (!localPath || !
|
|
58983
|
+
if (!localPath || !fs18.existsSync(localPath)) {
|
|
58291
58984
|
sessionLogger.warn(`Local file not found for ${fileId}`);
|
|
58292
58985
|
entry.status = "expired";
|
|
58293
58986
|
entry.error = "Local file missing";
|
|
@@ -58438,15 +59131,15 @@ var SessionFileManager = class {
|
|
|
58438
59131
|
// Static Utilities
|
|
58439
59132
|
// ============================================================================
|
|
58440
59133
|
static cleanupOldSessions(sessionsDir, maxAge = 30 * 24 * 60 * 60 * 1e3) {
|
|
58441
|
-
if (!
|
|
59134
|
+
if (!fs18.existsSync(sessionsDir)) return;
|
|
58442
59135
|
const now = Date.now();
|
|
58443
|
-
const files =
|
|
59136
|
+
const files = fs18.readdirSync(sessionsDir);
|
|
58444
59137
|
for (const file of files) {
|
|
58445
59138
|
if (!file.endsWith(".json")) continue;
|
|
58446
|
-
const filePath =
|
|
58447
|
-
const stat2 =
|
|
59139
|
+
const filePath = path18.join(sessionsDir, file);
|
|
59140
|
+
const stat2 = fs18.statSync(filePath);
|
|
58448
59141
|
if (now - stat2.mtime.getTime() > maxAge) {
|
|
58449
|
-
|
|
59142
|
+
fs18.unlinkSync(filePath);
|
|
58450
59143
|
sessionLogger.debug(`Cleaned up old session: ${file}`);
|
|
58451
59144
|
}
|
|
58452
59145
|
}
|
|
@@ -58454,8 +59147,8 @@ var SessionFileManager = class {
|
|
|
58454
59147
|
};
|
|
58455
59148
|
|
|
58456
59149
|
// src/files/ffmpeg-processor.ts
|
|
58457
|
-
import * as
|
|
58458
|
-
import * as
|
|
59150
|
+
import * as fs19 from "node:fs";
|
|
59151
|
+
import * as path19 from "node:path";
|
|
58459
59152
|
import { spawn } from "node:child_process";
|
|
58460
59153
|
var FFmpegProcessor = class {
|
|
58461
59154
|
ffmpegPath = "ffmpeg";
|
|
@@ -58468,17 +59161,17 @@ var FFmpegProcessor = class {
|
|
|
58468
59161
|
// Detection
|
|
58469
59162
|
// ============================================================================
|
|
58470
59163
|
async checkAvailability() {
|
|
58471
|
-
return new Promise((
|
|
59164
|
+
return new Promise((resolve7) => {
|
|
58472
59165
|
const proc = spawn(this.ffmpegPath, ["-version"]);
|
|
58473
|
-
proc.on("error", () =>
|
|
58474
|
-
proc.on("exit", (code) =>
|
|
59166
|
+
proc.on("error", () => resolve7(false));
|
|
59167
|
+
proc.on("exit", (code) => resolve7(code === 0));
|
|
58475
59168
|
});
|
|
58476
59169
|
}
|
|
58477
59170
|
// ============================================================================
|
|
58478
59171
|
// Video Analysis
|
|
58479
59172
|
// ============================================================================
|
|
58480
59173
|
async analyzeVideo(videoPath) {
|
|
58481
|
-
return new Promise((
|
|
59174
|
+
return new Promise((resolve7, reject) => {
|
|
58482
59175
|
const args = [
|
|
58483
59176
|
"-v",
|
|
58484
59177
|
"error",
|
|
@@ -58507,7 +59200,7 @@ var FFmpegProcessor = class {
|
|
|
58507
59200
|
}
|
|
58508
59201
|
try {
|
|
58509
59202
|
const info = this.parseProbeOutput(stdout, videoPath);
|
|
58510
|
-
|
|
59203
|
+
resolve7(info);
|
|
58511
59204
|
} catch (err) {
|
|
58512
59205
|
reject(err);
|
|
58513
59206
|
}
|
|
@@ -58559,7 +59252,7 @@ var FFmpegProcessor = class {
|
|
|
58559
59252
|
}
|
|
58560
59253
|
async compressVideo(inputPath, outputDir, info, opts, onProgress) {
|
|
58561
59254
|
const outputName = `compressed_${Date.now()}.mp4`;
|
|
58562
|
-
const outputPath =
|
|
59255
|
+
const outputPath = path19.join(outputDir, outputName);
|
|
58563
59256
|
const adaptiveParams = this.calculateAdaptiveParams(info.duration, opts);
|
|
58564
59257
|
const args = [
|
|
58565
59258
|
"-i",
|
|
@@ -58590,7 +59283,7 @@ var FFmpegProcessor = class {
|
|
|
58590
59283
|
onProgress?.(percent);
|
|
58591
59284
|
}
|
|
58592
59285
|
});
|
|
58593
|
-
const outputStat =
|
|
59286
|
+
const outputStat = fs19.statSync(outputPath);
|
|
58594
59287
|
return {
|
|
58595
59288
|
type: "video",
|
|
58596
59289
|
output: outputPath,
|
|
@@ -58621,9 +59314,9 @@ var FFmpegProcessor = class {
|
|
|
58621
59314
|
}
|
|
58622
59315
|
async extractKeyframes(inputPath, outputDir, info, opts, onProgress) {
|
|
58623
59316
|
const interval = Math.floor(info.duration / opts.keyframeCount);
|
|
58624
|
-
const framesDir =
|
|
58625
|
-
|
|
58626
|
-
const outputPattern =
|
|
59317
|
+
const framesDir = path19.join(outputDir, `keyframes_${Date.now()}`);
|
|
59318
|
+
fs19.mkdirSync(framesDir, { recursive: true });
|
|
59319
|
+
const outputPattern = path19.join(framesDir, "frame_%03d.jpg");
|
|
58627
59320
|
const args = [
|
|
58628
59321
|
"-i",
|
|
58629
59322
|
inputPath,
|
|
@@ -58641,10 +59334,10 @@ var FFmpegProcessor = class {
|
|
|
58641
59334
|
onProgress?.(percent);
|
|
58642
59335
|
}
|
|
58643
59336
|
});
|
|
58644
|
-
let frames =
|
|
59337
|
+
let frames = fs19.readdirSync(framesDir).filter((f) => f.endsWith(".jpg")).sort().map((f) => path19.join(framesDir, f));
|
|
58645
59338
|
if (frames.length < opts.keyframeCount / 2) {
|
|
58646
|
-
|
|
58647
|
-
|
|
59339
|
+
fs19.rmSync(framesDir, { recursive: true, force: true });
|
|
59340
|
+
fs19.mkdirSync(framesDir, { recursive: true });
|
|
58648
59341
|
const uniformArgs = [
|
|
58649
59342
|
"-i",
|
|
58650
59343
|
inputPath,
|
|
@@ -58660,10 +59353,10 @@ var FFmpegProcessor = class {
|
|
|
58660
59353
|
onProgress?.(percent);
|
|
58661
59354
|
}
|
|
58662
59355
|
});
|
|
58663
|
-
frames =
|
|
59356
|
+
frames = fs19.readdirSync(framesDir).filter((f) => f.endsWith(".jpg")).sort().map((f) => path19.join(framesDir, f));
|
|
58664
59357
|
}
|
|
58665
59358
|
const totalOutputSize = frames.reduce(
|
|
58666
|
-
(sum, f) => sum +
|
|
59359
|
+
(sum, f) => sum + fs19.statSync(f).size,
|
|
58667
59360
|
0
|
|
58668
59361
|
);
|
|
58669
59362
|
return {
|
|
@@ -58684,7 +59377,7 @@ var FFmpegProcessor = class {
|
|
|
58684
59377
|
const opts = { ...DEFAULT_PIC_OPTIONS, ...options };
|
|
58685
59378
|
const ext = opts.format === "webp" ? ".webp" : ".jpg";
|
|
58686
59379
|
const outputName = `optimized_${Date.now()}${ext}`;
|
|
58687
|
-
const outputPath =
|
|
59380
|
+
const outputPath = path19.join(outputDir, outputName);
|
|
58688
59381
|
const args = [
|
|
58689
59382
|
"-i",
|
|
58690
59383
|
inputPath,
|
|
@@ -58702,7 +59395,7 @@ var FFmpegProcessor = class {
|
|
|
58702
59395
|
// FFmpeg Execution
|
|
58703
59396
|
// ============================================================================
|
|
58704
59397
|
runFFmpeg(args) {
|
|
58705
|
-
return new Promise((
|
|
59398
|
+
return new Promise((resolve7, reject) => {
|
|
58706
59399
|
const proc = spawn(this.ffmpegPath, args);
|
|
58707
59400
|
let stderr = "";
|
|
58708
59401
|
proc.stderr.on("data", (data) => {
|
|
@@ -58717,7 +59410,7 @@ var FFmpegProcessor = class {
|
|
|
58717
59410
|
)
|
|
58718
59411
|
);
|
|
58719
59412
|
} else {
|
|
58720
|
-
|
|
59413
|
+
resolve7();
|
|
58721
59414
|
}
|
|
58722
59415
|
});
|
|
58723
59416
|
proc.on("error", (err) => {
|
|
@@ -58734,7 +59427,7 @@ var FFmpegProcessor = class {
|
|
|
58734
59427
|
// Progress Monitoring (for future use)
|
|
58735
59428
|
// ============================================================================
|
|
58736
59429
|
async runWithProgress(args, onProgress) {
|
|
58737
|
-
return new Promise((
|
|
59430
|
+
return new Promise((resolve7, reject) => {
|
|
58738
59431
|
const progressArgs = [
|
|
58739
59432
|
...args,
|
|
58740
59433
|
"-progress",
|
|
@@ -58764,7 +59457,7 @@ var FFmpegProcessor = class {
|
|
|
58764
59457
|
)
|
|
58765
59458
|
);
|
|
58766
59459
|
} else {
|
|
58767
|
-
|
|
59460
|
+
resolve7();
|
|
58768
59461
|
}
|
|
58769
59462
|
});
|
|
58770
59463
|
});
|
|
@@ -58859,8 +59552,8 @@ var DIRECTORY_LISTING_TTL_MS = 1500;
|
|
|
58859
59552
|
var FileManager = class {
|
|
58860
59553
|
constructor(options) {
|
|
58861
59554
|
this.options = options;
|
|
58862
|
-
const baseDir = options.baseDir ||
|
|
58863
|
-
const sessionsDir = options.sessionsDir ||
|
|
59555
|
+
const baseDir = options.baseDir || path20.join(process.env.HOME || ".", ".pie", "files");
|
|
59556
|
+
const sessionsDir = options.sessionsDir || path20.join(process.env.HOME || ".", ".pie", "sessions");
|
|
58864
59557
|
this.fileCache = new FileCache(baseDir);
|
|
58865
59558
|
this.authFingerprint = options.apiKey ? this.buildAuthFingerprint(options.apiKey) : void 0;
|
|
58866
59559
|
this.sessionManager = new SessionFileManager(
|
|
@@ -58871,7 +59564,7 @@ var FileManager = class {
|
|
|
58871
59564
|
);
|
|
58872
59565
|
this.ffmpeg = new FFmpegProcessor(options.ffmpegPath);
|
|
58873
59566
|
this.platform = normalizeFileManagerPlatform(options.platform);
|
|
58874
|
-
this.tempDir =
|
|
59567
|
+
this.tempDir = path20.join(baseDir, "temp");
|
|
58875
59568
|
this.ensureDirectories();
|
|
58876
59569
|
}
|
|
58877
59570
|
options;
|
|
@@ -58883,8 +59576,8 @@ var FileManager = class {
|
|
|
58883
59576
|
directoryListingCache = /* @__PURE__ */ new Map();
|
|
58884
59577
|
authFingerprint;
|
|
58885
59578
|
ensureDirectories() {
|
|
58886
|
-
if (!
|
|
58887
|
-
|
|
59579
|
+
if (!fs20.existsSync(this.tempDir)) {
|
|
59580
|
+
fs20.mkdirSync(this.tempDir, { recursive: true });
|
|
58888
59581
|
}
|
|
58889
59582
|
}
|
|
58890
59583
|
// ============================================================================
|
|
@@ -58914,8 +59607,8 @@ var FileManager = class {
|
|
|
58914
59607
|
// ============================================================================
|
|
58915
59608
|
async stageLocal(filePath, options = {}) {
|
|
58916
59609
|
const resolvedPath = resolvePath2(filePath);
|
|
58917
|
-
const sourceName = options.sourceName ||
|
|
58918
|
-
if (!
|
|
59610
|
+
const sourceName = options.sourceName || path20.basename(resolvedPath);
|
|
59611
|
+
if (!fs20.existsSync(resolvedPath)) {
|
|
58919
59612
|
throw new FileManagerError(
|
|
58920
59613
|
"LOCAL_FILE_MISSING" /* LOCAL_FILE_MISSING */,
|
|
58921
59614
|
`File not found: ${filePath}`
|
|
@@ -58930,7 +59623,7 @@ var FileManager = class {
|
|
|
58930
59623
|
this.fileCache.rememberPathHint(resolvedPath, hash, sourceName);
|
|
58931
59624
|
this.invalidateDirectoryListingCacheForPath(resolvedPath);
|
|
58932
59625
|
const entry = this.fileCache.getByHash(hash);
|
|
58933
|
-
const size = entry?.size ??
|
|
59626
|
+
const size = entry?.size ?? fs20.statSync(resolvedPath).size;
|
|
58934
59627
|
return {
|
|
58935
59628
|
success: true,
|
|
58936
59629
|
hash,
|
|
@@ -58944,8 +59637,8 @@ var FileManager = class {
|
|
|
58944
59637
|
}
|
|
58945
59638
|
async upload(filePath, options = {}) {
|
|
58946
59639
|
const resolvedPath = resolvePath2(filePath);
|
|
58947
|
-
const sourceName = options.sourceName ||
|
|
58948
|
-
if (!
|
|
59640
|
+
const sourceName = options.sourceName || path20.basename(resolvedPath);
|
|
59641
|
+
if (!fs20.existsSync(resolvedPath)) {
|
|
58949
59642
|
throw new FileManagerError(
|
|
58950
59643
|
"LOCAL_FILE_MISSING" /* LOCAL_FILE_MISSING */,
|
|
58951
59644
|
`File not found: ${filePath}`
|
|
@@ -59026,7 +59719,7 @@ var FileManager = class {
|
|
|
59026
59719
|
// ============================================================================
|
|
59027
59720
|
async uploadPic(filePath, options = {}) {
|
|
59028
59721
|
const resolvedPath = resolvePath2(filePath);
|
|
59029
|
-
if (!
|
|
59722
|
+
if (!fs20.existsSync(resolvedPath)) {
|
|
59030
59723
|
throw new FileManagerError(
|
|
59031
59724
|
"LOCAL_FILE_MISSING" /* LOCAL_FILE_MISSING */,
|
|
59032
59725
|
`File not found: ${filePath}`
|
|
@@ -59034,7 +59727,7 @@ var FileManager = class {
|
|
|
59034
59727
|
}
|
|
59035
59728
|
const cachedResult = await this.tryReuseCachedSourceUpload(
|
|
59036
59729
|
resolvedPath,
|
|
59037
|
-
options.sourceName ||
|
|
59730
|
+
options.sourceName || path20.basename(resolvedPath),
|
|
59038
59731
|
options.platform || this.platform
|
|
59039
59732
|
);
|
|
59040
59733
|
if (cachedResult) {
|
|
@@ -59051,12 +59744,12 @@ var FileManager = class {
|
|
|
59051
59744
|
const result = await this.upload(optimizedPath, {
|
|
59052
59745
|
...options,
|
|
59053
59746
|
purpose: "image",
|
|
59054
|
-
sourceName:
|
|
59747
|
+
sourceName: path20.basename(resolvedPath)
|
|
59055
59748
|
});
|
|
59056
|
-
this.fileCache.rememberPathHint(resolvedPath, result.hash,
|
|
59749
|
+
this.fileCache.rememberPathHint(resolvedPath, result.hash, path20.basename(resolvedPath));
|
|
59057
59750
|
this.invalidateDirectoryListingCacheForPath(resolvedPath);
|
|
59058
59751
|
try {
|
|
59059
|
-
|
|
59752
|
+
fs20.unlinkSync(optimizedPath);
|
|
59060
59753
|
} catch {
|
|
59061
59754
|
}
|
|
59062
59755
|
return result;
|
|
@@ -59066,7 +59759,7 @@ var FileManager = class {
|
|
|
59066
59759
|
// ============================================================================
|
|
59067
59760
|
async uploadVideo(filePath, options = {}) {
|
|
59068
59761
|
const resolvedPath = resolvePath2(filePath);
|
|
59069
|
-
if (!
|
|
59762
|
+
if (!fs20.existsSync(resolvedPath)) {
|
|
59070
59763
|
throw new FileManagerError(
|
|
59071
59764
|
"LOCAL_FILE_MISSING" /* LOCAL_FILE_MISSING */,
|
|
59072
59765
|
`File not found: ${filePath}`
|
|
@@ -59074,7 +59767,7 @@ var FileManager = class {
|
|
|
59074
59767
|
}
|
|
59075
59768
|
const cachedResult = await this.tryReuseCachedSourceUpload(
|
|
59076
59769
|
resolvedPath,
|
|
59077
|
-
options.sourceName ||
|
|
59770
|
+
options.sourceName || path20.basename(resolvedPath),
|
|
59078
59771
|
options.platform || this.platform
|
|
59079
59772
|
);
|
|
59080
59773
|
if (cachedResult) {
|
|
@@ -59124,9 +59817,9 @@ var FileManager = class {
|
|
|
59124
59817
|
result = await this.upload(preprocessResult.output, {
|
|
59125
59818
|
...options,
|
|
59126
59819
|
purpose: "video",
|
|
59127
|
-
sourceName:
|
|
59820
|
+
sourceName: path20.basename(resolvedPath)
|
|
59128
59821
|
});
|
|
59129
|
-
this.fileCache.rememberPathHint(resolvedPath, result.hash,
|
|
59822
|
+
this.fileCache.rememberPathHint(resolvedPath, result.hash, path20.basename(resolvedPath));
|
|
59130
59823
|
this.invalidateDirectoryListingCacheForPath(resolvedPath);
|
|
59131
59824
|
} else {
|
|
59132
59825
|
throw new FileManagerError(
|
|
@@ -59418,7 +60111,7 @@ var FileManager = class {
|
|
|
59418
60111
|
* Detect purpose from file path based on extension
|
|
59419
60112
|
*/
|
|
59420
60113
|
detectPurpose(filePath) {
|
|
59421
|
-
const ext =
|
|
60114
|
+
const ext = path20.extname(filePath).toLowerCase();
|
|
59422
60115
|
switch (ext) {
|
|
59423
60116
|
case ".mp4":
|
|
59424
60117
|
case ".mov":
|
|
@@ -59460,13 +60153,13 @@ var FileManager = class {
|
|
|
59460
60153
|
cleanupTempFiles(output) {
|
|
59461
60154
|
try {
|
|
59462
60155
|
if (typeof output === "string") {
|
|
59463
|
-
if (
|
|
59464
|
-
|
|
60156
|
+
if (fs20.existsSync(output)) {
|
|
60157
|
+
fs20.unlinkSync(output);
|
|
59465
60158
|
}
|
|
59466
60159
|
} else {
|
|
59467
60160
|
for (const file of output) {
|
|
59468
|
-
if (
|
|
59469
|
-
|
|
60161
|
+
if (fs20.existsSync(file)) {
|
|
60162
|
+
fs20.unlinkSync(file);
|
|
59470
60163
|
}
|
|
59471
60164
|
}
|
|
59472
60165
|
}
|
|
@@ -59498,7 +60191,7 @@ var FileManager = class {
|
|
|
59498
60191
|
}];
|
|
59499
60192
|
}
|
|
59500
60193
|
const localPath = this.fileCache.getLocalPath(entry.hash);
|
|
59501
|
-
if (localPath &&
|
|
60194
|
+
if (localPath && fs20.existsSync(localPath)) {
|
|
59502
60195
|
return [{
|
|
59503
60196
|
fileId,
|
|
59504
60197
|
name: cacheEntry.name,
|
|
@@ -59595,12 +60288,12 @@ var FileManager = class {
|
|
|
59595
60288
|
directories: []
|
|
59596
60289
|
};
|
|
59597
60290
|
try {
|
|
59598
|
-
const entries =
|
|
60291
|
+
const entries = fs20.readdirSync(resolvedDir, { withFileTypes: true });
|
|
59599
60292
|
for (const entry of entries) {
|
|
59600
60293
|
if (!entry.name || entry.name.startsWith(".")) {
|
|
59601
60294
|
continue;
|
|
59602
60295
|
}
|
|
59603
|
-
const fullPath =
|
|
60296
|
+
const fullPath = path20.join(resolvedDir, entry.name);
|
|
59604
60297
|
if (entry.isDirectory()) {
|
|
59605
60298
|
listing.directories.push({ name: entry.name, path: fullPath });
|
|
59606
60299
|
continue;
|
|
@@ -59609,7 +60302,7 @@ var FileManager = class {
|
|
|
59609
60302
|
let isFile = entry.isFile();
|
|
59610
60303
|
if (entry.isSymbolicLink()) {
|
|
59611
60304
|
try {
|
|
59612
|
-
const targetStat =
|
|
60305
|
+
const targetStat = fs20.statSync(fullPath);
|
|
59613
60306
|
isDirectory = targetStat.isDirectory();
|
|
59614
60307
|
isFile = targetStat.isFile();
|
|
59615
60308
|
} catch {
|
|
@@ -59627,7 +60320,7 @@ var FileManager = class {
|
|
|
59627
60320
|
if (!type) {
|
|
59628
60321
|
continue;
|
|
59629
60322
|
}
|
|
59630
|
-
const stat2 =
|
|
60323
|
+
const stat2 = fs20.statSync(fullPath);
|
|
59631
60324
|
listing.files.push({
|
|
59632
60325
|
name: entry.name,
|
|
59633
60326
|
path: fullPath,
|
|
@@ -59641,7 +60334,7 @@ var FileManager = class {
|
|
|
59641
60334
|
return listing;
|
|
59642
60335
|
}
|
|
59643
60336
|
invalidateDirectoryListingCacheForPath(filePath) {
|
|
59644
|
-
this.directoryListingCache.delete(
|
|
60337
|
+
this.directoryListingCache.delete(path20.dirname(resolvePath2(filePath)));
|
|
59645
60338
|
}
|
|
59646
60339
|
buildAuthFingerprint(apiKey) {
|
|
59647
60340
|
return crypto2.createHash("sha256").update(apiKey).digest("hex").slice(0, 16);
|
|
@@ -59690,8 +60383,8 @@ function createFileManagerForModel(params) {
|
|
|
59690
60383
|
sessionId: params.sessionId,
|
|
59691
60384
|
platform: platform2,
|
|
59692
60385
|
apiKey: params.apiKey,
|
|
59693
|
-
baseDir:
|
|
59694
|
-
sessionsDir:
|
|
60386
|
+
baseDir: path21.join(params.configDir, "files"),
|
|
60387
|
+
sessionsDir: path21.join(params.configDir, "sessions")
|
|
59695
60388
|
});
|
|
59696
60389
|
switch (getFileManagerAdapterKind(platform2)) {
|
|
59697
60390
|
case "kimi":
|
|
@@ -59726,15 +60419,15 @@ function isInlineMediaRequest(request) {
|
|
|
59726
60419
|
return request.kind === "image" || request.kind === "video";
|
|
59727
60420
|
}
|
|
59728
60421
|
async function prepareInlineMediaContent(params) {
|
|
59729
|
-
const stat2 = await
|
|
60422
|
+
const stat2 = await fs21.stat(params.request.absolutePath);
|
|
59730
60423
|
const limitMb = getCapabilityLimitMb(params.model, params.request.kind);
|
|
59731
60424
|
const limitBytes = limitMb * 1024 * 1024;
|
|
59732
60425
|
if (stat2.size > limitBytes) {
|
|
59733
60426
|
throw new Error(
|
|
59734
|
-
`${params.request.kind} understanding for ${params.model.provider}/${params.model.id} supports inline files up to ${limitMb}MB; ${
|
|
60427
|
+
`${params.request.kind} understanding for ${params.model.provider}/${params.model.id} supports inline files up to ${limitMb}MB; ${path21.basename(params.request.displayPath)} is ${(stat2.size / 1024 / 1024).toFixed(1)}MB. Configure a model with a file upload adapter, or compress/trim the file before reading it.`
|
|
59735
60428
|
);
|
|
59736
60429
|
}
|
|
59737
|
-
const data = await
|
|
60430
|
+
const data = await fs21.readFile(params.request.absolutePath, "base64");
|
|
59738
60431
|
const content = params.request.kind === "video" ? [{ type: "video", data, mimeType: params.request.mimeType }] : [{ type: "image", data, mimeType: params.request.mimeType }];
|
|
59739
60432
|
return {
|
|
59740
60433
|
content,
|
|
@@ -59754,7 +60447,7 @@ async function waitForReady(params) {
|
|
|
59754
60447
|
if (status.status === "error" || status.status === "expired") {
|
|
59755
60448
|
throw new Error(`Uploaded file is not ready: ${status.status}${status.error ? ` (${status.error})` : ""}`);
|
|
59756
60449
|
}
|
|
59757
|
-
await new Promise((
|
|
60450
|
+
await new Promise((resolve7) => setTimeout(resolve7, 1500));
|
|
59758
60451
|
}
|
|
59759
60452
|
throw new Error("Timed out waiting for uploaded file to become ready");
|
|
59760
60453
|
}
|
|
@@ -59770,7 +60463,7 @@ async function prepareFileContent(configDir, params) {
|
|
|
59770
60463
|
});
|
|
59771
60464
|
if (params.request.kind === "document") {
|
|
59772
60465
|
const upload2 = await manager.upload(params.request.absolutePath, {
|
|
59773
|
-
sourceName:
|
|
60466
|
+
sourceName: path21.basename(params.request.displayPath),
|
|
59774
60467
|
purpose: "file-extract"
|
|
59775
60468
|
});
|
|
59776
60469
|
await waitForReady({ manager, fileId: upload2.fileId, timeoutMs: 12e4, signal: params.request.signal });
|
|
@@ -59793,8 +60486,8 @@ async function prepareFileContent(configDir, params) {
|
|
|
59793
60486
|
}
|
|
59794
60487
|
};
|
|
59795
60488
|
}
|
|
59796
|
-
const upload = params.request.kind === "video" ? await manager.uploadVideo(params.request.absolutePath, { sourceName:
|
|
59797
|
-
sourceName:
|
|
60489
|
+
const upload = params.request.kind === "video" ? await manager.uploadVideo(params.request.absolutePath, { sourceName: path21.basename(params.request.displayPath), strategy: "compress" }) : await manager.upload(params.request.absolutePath, {
|
|
60490
|
+
sourceName: path21.basename(params.request.displayPath),
|
|
59798
60491
|
purpose: params.request.kind === "image" ? "image" : "file-extract"
|
|
59799
60492
|
});
|
|
59800
60493
|
if (params.request.kind !== "video") {
|
|
@@ -59850,8 +60543,8 @@ function createIsolatedRegexSession(pattern, ignoreCase) {
|
|
|
59850
60543
|
let initError;
|
|
59851
60544
|
let readyResolve;
|
|
59852
60545
|
let readyReject;
|
|
59853
|
-
const ready = new Promise((
|
|
59854
|
-
readyResolve =
|
|
60546
|
+
const ready = new Promise((resolve7, reject) => {
|
|
60547
|
+
readyResolve = resolve7;
|
|
59855
60548
|
readyReject = reject;
|
|
59856
60549
|
});
|
|
59857
60550
|
void ready.catch(() => void 0);
|
|
@@ -59873,7 +60566,7 @@ function createIsolatedRegexSession(pattern, ignoreCase) {
|
|
|
59873
60566
|
if (initError) throw initError;
|
|
59874
60567
|
if (signal?.aborted) throw new Error("Operation aborted");
|
|
59875
60568
|
const id = nextId++;
|
|
59876
|
-
return new Promise((
|
|
60569
|
+
return new Promise((resolve7, reject) => {
|
|
59877
60570
|
let settled = false;
|
|
59878
60571
|
const finish = (error, matches) => {
|
|
59879
60572
|
if (settled) return;
|
|
@@ -59883,7 +60576,7 @@ function createIsolatedRegexSession(pattern, ignoreCase) {
|
|
|
59883
60576
|
worker.off("message", onMessage);
|
|
59884
60577
|
worker.off("error", onWorkerError);
|
|
59885
60578
|
if (error) reject(error);
|
|
59886
|
-
else
|
|
60579
|
+
else resolve7(matches ?? []);
|
|
59887
60580
|
};
|
|
59888
60581
|
const terminate = (error) => {
|
|
59889
60582
|
disposed = true;
|
|
@@ -60213,22 +60906,22 @@ var ExecutionStateManager = class {
|
|
|
60213
60906
|
};
|
|
60214
60907
|
|
|
60215
60908
|
// src/session-trace.ts
|
|
60216
|
-
import * as
|
|
60217
|
-
import * as
|
|
60909
|
+
import * as fs22 from "node:fs";
|
|
60910
|
+
import * as path22 from "node:path";
|
|
60218
60911
|
function saveTraceFileBestEffort(tracePath, content) {
|
|
60219
60912
|
const tempPath = `${tracePath}.${process.pid}.${Date.now()}.tmp`;
|
|
60220
60913
|
try {
|
|
60221
|
-
|
|
60914
|
+
fs22.writeFileSync(tempPath, content, "utf8");
|
|
60222
60915
|
try {
|
|
60223
|
-
|
|
60916
|
+
fs22.renameSync(tempPath, tracePath);
|
|
60224
60917
|
return;
|
|
60225
60918
|
} catch {
|
|
60226
60919
|
try {
|
|
60227
|
-
|
|
60920
|
+
fs22.copyFileSync(tempPath, tracePath);
|
|
60228
60921
|
} catch {
|
|
60229
60922
|
} finally {
|
|
60230
60923
|
try {
|
|
60231
|
-
|
|
60924
|
+
fs22.unlinkSync(tempPath);
|
|
60232
60925
|
} catch {
|
|
60233
60926
|
}
|
|
60234
60927
|
}
|
|
@@ -60241,16 +60934,16 @@ var SessionTraceWriter = class extends SessionTraceSnapshotWriter {
|
|
|
60241
60934
|
super({
|
|
60242
60935
|
getSessionId: () => sessionManager.getActiveSessionId(),
|
|
60243
60936
|
loadTrace: (sessionId) => {
|
|
60244
|
-
const tracePath =
|
|
60245
|
-
if (!
|
|
60937
|
+
const tracePath = path22.join(sessionsDir, `${sessionId}.trace.json`);
|
|
60938
|
+
if (!fs22.existsSync(tracePath)) return null;
|
|
60246
60939
|
try {
|
|
60247
|
-
return JSON.parse(
|
|
60940
|
+
return JSON.parse(fs22.readFileSync(tracePath, "utf8"));
|
|
60248
60941
|
} catch {
|
|
60249
60942
|
return null;
|
|
60250
60943
|
}
|
|
60251
60944
|
},
|
|
60252
60945
|
saveTrace: (trace) => {
|
|
60253
|
-
const tracePath =
|
|
60946
|
+
const tracePath = path22.join(sessionsDir, `${trace.sessionId}.trace.json`);
|
|
60254
60947
|
saveTraceFileBestEffort(tracePath, JSON.stringify(trace, null, 2));
|
|
60255
60948
|
}
|
|
60256
60949
|
});
|
|
@@ -60757,7 +61450,7 @@ function normalizeSessionTitle(raw) {
|
|
|
60757
61450
|
}
|
|
60758
61451
|
|
|
60759
61452
|
// src/components/extension-selector.ts
|
|
60760
|
-
import * as
|
|
61453
|
+
import * as path23 from "node:path";
|
|
60761
61454
|
var ExtensionSelectorComponent = class extends Container {
|
|
60762
61455
|
selectList;
|
|
60763
61456
|
constructor(title, options, onSelect, onCancel) {
|
|
@@ -60827,8 +61520,8 @@ var ExtensionStateSubmenu = class extends Container {
|
|
|
60827
61520
|
}
|
|
60828
61521
|
};
|
|
60829
61522
|
function unidentifiedLabel(info) {
|
|
60830
|
-
const parentName =
|
|
60831
|
-
const fileName =
|
|
61523
|
+
const parentName = path23.basename(path23.dirname(info.entryPath));
|
|
61524
|
+
const fileName = path23.basename(info.entryPath);
|
|
60832
61525
|
return `(unidentified) ${parentName || fileName}`;
|
|
60833
61526
|
}
|
|
60834
61527
|
var ExtensionEnablementSelectorComponent = class extends Container {
|
|
@@ -61118,7 +61811,7 @@ async function handleCacheTest(host) {
|
|
|
61118
61811
|
host.chatContainer.addChild(new Text(theme.fg("dim", ` ${summarizeUsage(result1.usage)}`), 1, 0));
|
|
61119
61812
|
host.chatContainer.addChild(new Text(theme.fg("dim", " Waiting 2s before replaying identical request..."), 1, 0));
|
|
61120
61813
|
host.ui.requestRender();
|
|
61121
|
-
await new Promise((
|
|
61814
|
+
await new Promise((resolve7) => setTimeout(resolve7, 2e3));
|
|
61122
61815
|
host.chatContainer.addChild(new Text(theme.fg("accent", "Request 2/2: replaying identical request..."), 1, 0));
|
|
61123
61816
|
host.ui.requestRender();
|
|
61124
61817
|
result2 = await completeSimple(model, context, options);
|
|
@@ -61299,10 +61992,10 @@ async function handleDebugPackageCommand(host) {
|
|
|
61299
61992
|
}
|
|
61300
61993
|
|
|
61301
61994
|
// src/commands/interactive-session-commands.ts
|
|
61302
|
-
import * as
|
|
61995
|
+
import * as os6 from "os";
|
|
61303
61996
|
|
|
61304
61997
|
// src/components/session-selector.ts
|
|
61305
|
-
import * as
|
|
61998
|
+
import * as path24 from "node:path";
|
|
61306
61999
|
function formatDate(timestamp) {
|
|
61307
62000
|
const date = new Date(timestamp);
|
|
61308
62001
|
const now = /* @__PURE__ */ new Date();
|
|
@@ -61333,7 +62026,7 @@ function getPathFlavor(value, peer) {
|
|
|
61333
62026
|
return "posix";
|
|
61334
62027
|
}
|
|
61335
62028
|
function getPathApi(flavor) {
|
|
61336
|
-
return flavor === "windows" ?
|
|
62029
|
+
return flavor === "windows" ? path24.win32 : path24.posix;
|
|
61337
62030
|
}
|
|
61338
62031
|
function normalizeForComparison(value, flavor) {
|
|
61339
62032
|
const pathApi = getPathApi(flavor);
|
|
@@ -61353,10 +62046,10 @@ function formatSessionCwdTag(sessionCwd, currentCwd, homeDir) {
|
|
|
61353
62046
|
const normalizedSessionCwd = pathApi.normalize(sessionCwd);
|
|
61354
62047
|
if (homeDir && getPathFlavor(homeDir, sessionCwd) === sessionFlavor) {
|
|
61355
62048
|
const normalizedHome = pathApi.normalize(homeDir);
|
|
61356
|
-
const
|
|
61357
|
-
const outsideHome =
|
|
62049
|
+
const relative3 = pathApi.relative(normalizedHome, normalizedSessionCwd);
|
|
62050
|
+
const outsideHome = relative3 === ".." || relative3.startsWith(`..${pathApi.sep}`) || pathApi.isAbsolute(relative3);
|
|
61358
62051
|
if (!outsideHome) {
|
|
61359
|
-
return
|
|
62052
|
+
return relative3 ? `~${pathApi.sep}${relative3}` : "~";
|
|
61360
62053
|
}
|
|
61361
62054
|
}
|
|
61362
62055
|
return normalizedSessionCwd;
|
|
@@ -62006,7 +62699,7 @@ var AssistantMessageComponent = class extends Container {
|
|
|
62006
62699
|
};
|
|
62007
62700
|
|
|
62008
62701
|
// src/components/tool-execution.ts
|
|
62009
|
-
import * as
|
|
62702
|
+
import * as os5 from "node:os";
|
|
62010
62703
|
|
|
62011
62704
|
// ../../node_modules/diff/libesm/diff/base.js
|
|
62012
62705
|
var Diff = class {
|
|
@@ -62108,16 +62801,16 @@ var Diff = class {
|
|
|
62108
62801
|
}
|
|
62109
62802
|
}
|
|
62110
62803
|
}
|
|
62111
|
-
addToPath(
|
|
62112
|
-
const last =
|
|
62804
|
+
addToPath(path33, added, removed, oldPosInc, options) {
|
|
62805
|
+
const last = path33.lastComponent;
|
|
62113
62806
|
if (last && !options.oneChangePerToken && last.added === added && last.removed === removed) {
|
|
62114
62807
|
return {
|
|
62115
|
-
oldPos:
|
|
62808
|
+
oldPos: path33.oldPos + oldPosInc,
|
|
62116
62809
|
lastComponent: { count: last.count + 1, added, removed, previousComponent: last.previousComponent }
|
|
62117
62810
|
};
|
|
62118
62811
|
} else {
|
|
62119
62812
|
return {
|
|
62120
|
-
oldPos:
|
|
62813
|
+
oldPos: path33.oldPos + oldPosInc,
|
|
62121
62814
|
lastComponent: { count: 1, added, removed, previousComponent: last }
|
|
62122
62815
|
};
|
|
62123
62816
|
}
|
|
@@ -62565,13 +63258,13 @@ function formatSize2(bytes) {
|
|
|
62565
63258
|
}
|
|
62566
63259
|
var DEFAULT_MAX_BYTES = 102400;
|
|
62567
63260
|
var DEFAULT_MAX_LINES = 100;
|
|
62568
|
-
function shortenPath(
|
|
62569
|
-
if (typeof
|
|
62570
|
-
const home =
|
|
62571
|
-
if (
|
|
62572
|
-
return `~${
|
|
63261
|
+
function shortenPath(path33) {
|
|
63262
|
+
if (typeof path33 !== "string") return "";
|
|
63263
|
+
const home = os5.homedir();
|
|
63264
|
+
if (path33.startsWith(home)) {
|
|
63265
|
+
return `~${path33.slice(home.length)}`;
|
|
62573
63266
|
}
|
|
62574
|
-
return
|
|
63267
|
+
return path33;
|
|
62575
63268
|
}
|
|
62576
63269
|
function replaceTabs(text) {
|
|
62577
63270
|
return text.replace(/\t/g, " ");
|
|
@@ -62597,11 +63290,13 @@ var ToolExecutionComponent = class extends Container {
|
|
|
62597
63290
|
isPartial = true;
|
|
62598
63291
|
ui;
|
|
62599
63292
|
result;
|
|
63293
|
+
renderHint;
|
|
62600
63294
|
constructor(toolName, args, options = {}, ui) {
|
|
62601
63295
|
super();
|
|
62602
63296
|
this.toolName = toolName;
|
|
62603
63297
|
this.args = args;
|
|
62604
63298
|
this.expanded = options.expanded ?? false;
|
|
63299
|
+
this.renderHint = options.renderHint;
|
|
62605
63300
|
this.ui = ui;
|
|
62606
63301
|
this.addChild(new Spacer(1));
|
|
62607
63302
|
this.contentBox = new Box(1, 1, (text) => theme.bg("toolPendingBg", text));
|
|
@@ -62652,14 +63347,14 @@ ${renderManageTodoListResult(this.result, { expanded: this.expanded }, theme)}`;
|
|
|
62652
63347
|
}
|
|
62653
63348
|
} else if (this.toolName === "read_file") {
|
|
62654
63349
|
const rawPath = str(this.args?.file_path ?? this.args?.path);
|
|
62655
|
-
const
|
|
63350
|
+
const path33 = rawPath !== null ? shortenPath(rawPath) : null;
|
|
62656
63351
|
const offset = typeof this.args?.offset === "number" ? this.args.offset : void 0;
|
|
62657
63352
|
const limit = typeof this.args?.limit === "number" ? this.args.limit : void 0;
|
|
62658
63353
|
let pathDisplay;
|
|
62659
|
-
if (
|
|
63354
|
+
if (path33 === null) {
|
|
62660
63355
|
pathDisplay = invalidArg;
|
|
62661
|
-
} else if (
|
|
62662
|
-
pathDisplay = theme.fg("accent",
|
|
63356
|
+
} else if (path33) {
|
|
63357
|
+
pathDisplay = theme.fg("accent", path33);
|
|
62663
63358
|
} else {
|
|
62664
63359
|
const partialPath = partialStr(this.args?.file_path ?? this.args?.path);
|
|
62665
63360
|
pathDisplay = theme.fg("toolOutput", partialPath);
|
|
@@ -62706,12 +63401,12 @@ ${renderManageTodoListResult(this.result, { expanded: this.expanded }, theme)}`;
|
|
|
62706
63401
|
} else if (this.toolName === "write_file") {
|
|
62707
63402
|
const rawPath = str(this.args?.file_path ?? this.args?.path);
|
|
62708
63403
|
const fileContent = str(this.args?.content);
|
|
62709
|
-
const
|
|
63404
|
+
const path33 = rawPath !== null ? shortenPath(rawPath) : null;
|
|
62710
63405
|
let pathDisplay;
|
|
62711
|
-
if (
|
|
63406
|
+
if (path33 === null) {
|
|
62712
63407
|
pathDisplay = invalidArg;
|
|
62713
|
-
} else if (
|
|
62714
|
-
pathDisplay = theme.fg("accent",
|
|
63408
|
+
} else if (path33) {
|
|
63409
|
+
pathDisplay = theme.fg("accent", path33);
|
|
62715
63410
|
} else {
|
|
62716
63411
|
const partialPath = partialStr(this.args?.file_path ?? this.args?.path);
|
|
62717
63412
|
pathDisplay = theme.fg("toolOutput", partialPath);
|
|
@@ -62748,12 +63443,12 @@ ${theme.fg("error", errorText)}`;
|
|
|
62748
63443
|
}
|
|
62749
63444
|
} else if (this.toolName === "edit_file") {
|
|
62750
63445
|
const rawPath = str(this.args?.file_path ?? this.args?.path);
|
|
62751
|
-
const
|
|
63446
|
+
const path33 = rawPath !== null ? shortenPath(rawPath) : null;
|
|
62752
63447
|
let pathDisplay;
|
|
62753
|
-
if (
|
|
63448
|
+
if (path33 === null) {
|
|
62754
63449
|
pathDisplay = invalidArg;
|
|
62755
|
-
} else if (
|
|
62756
|
-
pathDisplay = theme.fg("accent",
|
|
63450
|
+
} else if (path33) {
|
|
63451
|
+
pathDisplay = theme.fg("accent", path33);
|
|
62757
63452
|
} else {
|
|
62758
63453
|
const partialPath = partialStr(this.args?.file_path ?? this.args?.path);
|
|
62759
63454
|
pathDisplay = theme.fg("toolOutput", partialPath);
|
|
@@ -62778,7 +63473,7 @@ ${theme.fg("error", errorText)}`;
|
|
|
62778
63473
|
const diff = this.result.details.diff;
|
|
62779
63474
|
const firstChangedLine = this.result.details.firstChangedLine;
|
|
62780
63475
|
if (firstChangedLine) {
|
|
62781
|
-
text = `${theme.fg("toolTitle", theme.bold("edit_file"))} ${theme.fg("accent",
|
|
63476
|
+
text = `${theme.fg("toolTitle", theme.bold("edit_file"))} ${theme.fg("accent", path33 || rawPath)}${theme.fg("warning", `:${firstChangedLine}`)}`;
|
|
62782
63477
|
}
|
|
62783
63478
|
const lines = diff.split("\n");
|
|
62784
63479
|
const maxLines = this.expanded ? lines.length : 15;
|
|
@@ -62798,7 +63493,7 @@ ${theme.fg("error", errorText)}`;
|
|
|
62798
63493
|
const diff = generateEditDiff(oldText, newText, rawPath);
|
|
62799
63494
|
const firstChangedLine = findFirstChangedLine(oldText, newText);
|
|
62800
63495
|
if (firstChangedLine) {
|
|
62801
|
-
text = `${theme.fg("toolTitle", theme.bold("edit_file"))} ${theme.fg("accent",
|
|
63496
|
+
text = `${theme.fg("toolTitle", theme.bold("edit_file"))} ${theme.fg("accent", path33 || rawPath)}${theme.fg("warning", `:${firstChangedLine}`)}`;
|
|
62802
63497
|
}
|
|
62803
63498
|
const lines = diff.split("\n");
|
|
62804
63499
|
const maxLines = this.expanded ? lines.length : 15;
|
|
@@ -62817,9 +63512,9 @@ ${theme.fg("error", errorText)}`;
|
|
|
62817
63512
|
}
|
|
62818
63513
|
} else if (this.toolName === "list_dir") {
|
|
62819
63514
|
const rawPath = str(this.args?.path);
|
|
62820
|
-
const
|
|
63515
|
+
const path33 = rawPath !== null ? shortenPath(rawPath || ".") : null;
|
|
62821
63516
|
const limit = this.args?.limit;
|
|
62822
|
-
text = `${theme.fg("toolTitle", theme.bold("list_dir"))} ${
|
|
63517
|
+
text = `${theme.fg("toolTitle", theme.bold("list_dir"))} ${path33 === null ? invalidArg : theme.fg("accent", path33)}`;
|
|
62823
63518
|
if (limit !== void 0) {
|
|
62824
63519
|
text += theme.fg("toolOutput", ` (limit ${limit})`);
|
|
62825
63520
|
}
|
|
@@ -62855,9 +63550,9 @@ ${theme.fg("warning", `[Truncated: ${warnings.join(", ")}]`)}`;
|
|
|
62855
63550
|
} else if (this.toolName === "find_files") {
|
|
62856
63551
|
const pattern = str(this.args?.pattern);
|
|
62857
63552
|
const rawPath = str(this.args?.path);
|
|
62858
|
-
const
|
|
63553
|
+
const path33 = rawPath !== null ? shortenPath(rawPath || ".") : null;
|
|
62859
63554
|
const limit = this.args?.limit;
|
|
62860
|
-
text = theme.fg("toolTitle", theme.bold("find_files")) + " " + (pattern === null ? invalidArg : theme.fg("accent", pattern || "")) + theme.fg("toolOutput", ` in ${
|
|
63555
|
+
text = theme.fg("toolTitle", theme.bold("find_files")) + " " + (pattern === null ? invalidArg : theme.fg("accent", pattern || "")) + theme.fg("toolOutput", ` in ${path33 === null ? invalidArg : path33}`);
|
|
62861
63556
|
if (limit !== void 0) {
|
|
62862
63557
|
text += theme.fg("toolOutput", ` (limit ${limit})`);
|
|
62863
63558
|
}
|
|
@@ -62893,10 +63588,10 @@ ${theme.fg("warning", `[Truncated: ${warnings.join(", ")}]`)}`;
|
|
|
62893
63588
|
} else if (this.toolName === "grep_text") {
|
|
62894
63589
|
const pattern = str(this.args?.pattern);
|
|
62895
63590
|
const rawPath = str(this.args?.path);
|
|
62896
|
-
const
|
|
63591
|
+
const path33 = rawPath !== null ? shortenPath(rawPath || ".") : null;
|
|
62897
63592
|
const glob = str(this.args?.glob);
|
|
62898
63593
|
const limit = this.args?.limit;
|
|
62899
|
-
text = theme.fg("toolTitle", theme.bold("grep_text")) + " " + (pattern === null ? invalidArg : theme.fg("accent", `/${pattern || ""}/`)) + theme.fg("toolOutput", ` in ${
|
|
63594
|
+
text = theme.fg("toolTitle", theme.bold("grep_text")) + " " + (pattern === null ? invalidArg : theme.fg("accent", `/${pattern || ""}/`)) + theme.fg("toolOutput", ` in ${path33 === null ? invalidArg : path33}`);
|
|
62900
63595
|
if (glob) {
|
|
62901
63596
|
text += theme.fg("toolOutput", ` (${glob})`);
|
|
62902
63597
|
}
|
|
@@ -62976,33 +63671,97 @@ ${theme.fg("warning", `[${warnings.join(". ")}]`)}`;
|
|
|
62976
63671
|
}
|
|
62977
63672
|
}
|
|
62978
63673
|
} else {
|
|
62979
|
-
|
|
62980
|
-
if (
|
|
62981
|
-
|
|
62982
|
-
|
|
62983
|
-
|
|
62984
|
-
${theme.fg("toolOutput", content)}`;
|
|
63674
|
+
const hinted = this.formatWithRenderHint();
|
|
63675
|
+
if (hinted !== null) {
|
|
63676
|
+
text = hinted;
|
|
63677
|
+
} else {
|
|
63678
|
+
text = this.formatGenericTool();
|
|
62985
63679
|
}
|
|
62986
|
-
|
|
62987
|
-
|
|
62988
|
-
|
|
62989
|
-
|
|
62990
|
-
|
|
62991
|
-
|
|
62992
|
-
|
|
62993
|
-
|
|
62994
|
-
|
|
62995
|
-
|
|
63680
|
+
}
|
|
63681
|
+
return text;
|
|
63682
|
+
}
|
|
63683
|
+
/** Render via the extension renderHint; null means fall back. */
|
|
63684
|
+
formatWithRenderHint() {
|
|
63685
|
+
const hint = this.renderHint;
|
|
63686
|
+
if (!hint) return null;
|
|
63687
|
+
try {
|
|
63688
|
+
let text = theme.fg("toolTitle", theme.bold(this.toolName));
|
|
63689
|
+
let produced = false;
|
|
63690
|
+
if (hint.summary) {
|
|
63691
|
+
const summary = hint.summary(this.args);
|
|
63692
|
+
if (typeof summary === "string" && summary.trim()) {
|
|
63693
|
+
text += ` ${theme.fg("accent", summary.trim())}`;
|
|
63694
|
+
produced = true;
|
|
63695
|
+
}
|
|
63696
|
+
}
|
|
63697
|
+
if (this.result && hint.sections) {
|
|
63698
|
+
const sections = hint.sections(
|
|
63699
|
+
this.result,
|
|
63700
|
+
{ expanded: this.expanded }
|
|
63701
|
+
);
|
|
63702
|
+
if (Array.isArray(sections)) {
|
|
63703
|
+
for (const section of sections) {
|
|
63704
|
+
if (!section || typeof section.text !== "string" || !section.text) continue;
|
|
63705
|
+
produced = true;
|
|
63706
|
+
if (section.title) {
|
|
63707
|
+
text += `
|
|
63708
|
+
|
|
63709
|
+
${theme.fg("toolTitle", section.title)}`;
|
|
63710
|
+
}
|
|
63711
|
+
const lines = section.text.split("\n");
|
|
63712
|
+
const maxLines = this.expanded ? lines.length : 20;
|
|
63713
|
+
const displayLines = lines.slice(0, maxLines);
|
|
63714
|
+
const remaining = lines.length - maxLines;
|
|
63715
|
+
text += "\n" + displayLines.map((line) => theme.fg("toolOutput", line)).join("\n");
|
|
63716
|
+
if (remaining > 0) {
|
|
63717
|
+
text += theme.fg("muted", `
|
|
62996
63718
|
... (${remaining} more lines, press Ctrl+O to expand)`);
|
|
63719
|
+
}
|
|
62997
63720
|
}
|
|
62998
63721
|
}
|
|
62999
|
-
|
|
63000
|
-
|
|
63001
|
-
|
|
63722
|
+
}
|
|
63723
|
+
if (!produced) return null;
|
|
63724
|
+
if (this.result?.isError) {
|
|
63725
|
+
const errorOutput = this.getTextOutput() || "Unknown error";
|
|
63726
|
+
text += `
|
|
63002
63727
|
|
|
63003
63728
|
${theme.fg("error", "Error: " + errorOutput)}`;
|
|
63729
|
+
}
|
|
63730
|
+
return text;
|
|
63731
|
+
} catch (error) {
|
|
63732
|
+
if (process.env.PIE_VERBOSE) {
|
|
63733
|
+
console.warn(`[ToolExecution] renderHint for ${this.toolName} failed, falling back:`, error);
|
|
63734
|
+
}
|
|
63735
|
+
return null;
|
|
63736
|
+
}
|
|
63737
|
+
}
|
|
63738
|
+
formatGenericTool() {
|
|
63739
|
+
let text = theme.fg("toolTitle", theme.bold(this.toolName));
|
|
63740
|
+
if (this.args && Object.keys(this.args).length > 0) {
|
|
63741
|
+
const content = JSON.stringify(this.args, null, 2);
|
|
63742
|
+
text += `
|
|
63743
|
+
|
|
63744
|
+
${theme.fg("toolOutput", content)}`;
|
|
63745
|
+
}
|
|
63746
|
+
if (this.result) {
|
|
63747
|
+
const output = this.getTextOutput();
|
|
63748
|
+
if (output) {
|
|
63749
|
+
const lines = output.split("\n");
|
|
63750
|
+
const maxLines = this.expanded ? lines.length : 20;
|
|
63751
|
+
const displayLines = lines.slice(0, maxLines);
|
|
63752
|
+
const remaining = lines.length - maxLines;
|
|
63753
|
+
text += "\n\n" + displayLines.map((line) => theme.fg("toolOutput", line)).join("\n");
|
|
63754
|
+
if (remaining > 0) {
|
|
63755
|
+
text += theme.fg("muted", `
|
|
63756
|
+
... (${remaining} more lines, press Ctrl+O to expand)`);
|
|
63004
63757
|
}
|
|
63005
63758
|
}
|
|
63759
|
+
if (this.result.isError) {
|
|
63760
|
+
const errorOutput = this.getTextOutput() || "Unknown error";
|
|
63761
|
+
text += `
|
|
63762
|
+
|
|
63763
|
+
${theme.fg("error", "Error: " + errorOutput)}`;
|
|
63764
|
+
}
|
|
63006
63765
|
}
|
|
63007
63766
|
return text;
|
|
63008
63767
|
}
|
|
@@ -63047,7 +63806,7 @@ async function handleResumeCommand(host) {
|
|
|
63047
63806
|
}
|
|
63048
63807
|
}, {
|
|
63049
63808
|
currentCwd: host.options.cwd,
|
|
63050
|
-
homeDir:
|
|
63809
|
+
homeDir: os6.homedir(),
|
|
63051
63810
|
refreshMetadata: (sessionId) => host.sessionManager.refreshDerivedName(sessionId),
|
|
63052
63811
|
requestRender: () => host.ui.requestRender(),
|
|
63053
63812
|
totalCount
|
|
@@ -63056,6 +63815,15 @@ async function handleResumeCommand(host) {
|
|
|
63056
63815
|
});
|
|
63057
63816
|
}
|
|
63058
63817
|
async function loadSession(host, sessionId) {
|
|
63818
|
+
const beforeSwitch = await host.extensionRunner?.emitSessionBeforeSwitch({
|
|
63819
|
+
type: "session:before_switch",
|
|
63820
|
+
reason: "resume",
|
|
63821
|
+
targetSessionId: sessionId
|
|
63822
|
+
});
|
|
63823
|
+
if (beforeSwitch?.cancel) {
|
|
63824
|
+
host.showStatus("Session switch cancelled by an extension");
|
|
63825
|
+
return;
|
|
63826
|
+
}
|
|
63059
63827
|
try {
|
|
63060
63828
|
const success = await host.sessionManager.loadSession(sessionId);
|
|
63061
63829
|
if (!success) {
|
|
@@ -63350,8 +64118,8 @@ async function forkAtEntry(host, entryId) {
|
|
|
63350
64118
|
host.showError("Failed to branch at entry");
|
|
63351
64119
|
return;
|
|
63352
64120
|
}
|
|
63353
|
-
const
|
|
63354
|
-
const pathNames =
|
|
64121
|
+
const path33 = host.sessionManager.getPathToEntry(entryId);
|
|
64122
|
+
const pathNames = path33.map((e) => e.message?.role || "entry").join(" \u2192 ");
|
|
63355
64123
|
const messages = host.sessionManager.getMessages();
|
|
63356
64124
|
host.syncAgentFromActiveSession(messages);
|
|
63357
64125
|
await host.syncSessionScopedState(messages);
|
|
@@ -63364,7 +64132,7 @@ async function forkAtEntry(host, entryId) {
|
|
|
63364
64132
|
host.updatePendingMessagesDisplay();
|
|
63365
64133
|
host.renderMessageHistory(messages);
|
|
63366
64134
|
await host.sessionManager.save();
|
|
63367
|
-
host.showStatus(`Branched at: ${pathNames} (${
|
|
64135
|
+
host.showStatus(`Branched at: ${pathNames} (${path33.length} messages). Continue to add to this branch.`);
|
|
63368
64136
|
} catch (error) {
|
|
63369
64137
|
host.showError(
|
|
63370
64138
|
`Failed to branch: ${error instanceof Error ? error.message : String(error)}`
|
|
@@ -63392,10 +64160,10 @@ async function navigateToEntry(host, entryId) {
|
|
|
63392
64160
|
host.sessionManager.save().catch((err) => {
|
|
63393
64161
|
console.error("[Session] Save after navigation failed:", err);
|
|
63394
64162
|
});
|
|
63395
|
-
const
|
|
64163
|
+
const path33 = host.sessionManager.getPathToEntry(entryId);
|
|
63396
64164
|
const entry = host.sessionManager.getEntry(entryId);
|
|
63397
64165
|
const role = entry?.message?.role || "entry";
|
|
63398
|
-
host.showStatus(`Switched to branch: ${role} (${
|
|
64166
|
+
host.showStatus(`Switched to branch: ${role} (${path33.length} messages in path)`);
|
|
63399
64167
|
} catch (error) {
|
|
63400
64168
|
host.showError(
|
|
63401
64169
|
`Failed to navigate: ${error instanceof Error ? error.message : String(error)}`
|
|
@@ -63445,9 +64213,9 @@ async function handleForkCommand(host) {
|
|
|
63445
64213
|
}
|
|
63446
64214
|
|
|
63447
64215
|
// src/commands/interactive-skill-commands.ts
|
|
63448
|
-
import * as
|
|
63449
|
-
import * as
|
|
63450
|
-
import * as
|
|
64216
|
+
import * as fs23 from "fs";
|
|
64217
|
+
import * as os7 from "os";
|
|
64218
|
+
import * as path25 from "path";
|
|
63451
64219
|
|
|
63452
64220
|
// src/commands/subagent-command.ts
|
|
63453
64221
|
async function handleSubagentCommand(host, args) {
|
|
@@ -63632,7 +64400,7 @@ function expandSkillCommand(host, text) {
|
|
|
63632
64400
|
const skill = host.skills.find((s) => s.name === skillName);
|
|
63633
64401
|
if (!skill) return text;
|
|
63634
64402
|
try {
|
|
63635
|
-
const content =
|
|
64403
|
+
const content = fs23.readFileSync(skill.filePath, "utf-8");
|
|
63636
64404
|
const body = host.stripFrontmatter(content).trim();
|
|
63637
64405
|
const skillBlock = `<skill name="${skill.name}" location="${skill.filePath}">
|
|
63638
64406
|
References are relative to ${skill.baseDir}.
|
|
@@ -63698,17 +64466,17 @@ async function handleSkillsCreate(host, args) {
|
|
|
63698
64466
|
if (!description) {
|
|
63699
64467
|
description = `Skill for ${name}`;
|
|
63700
64468
|
}
|
|
63701
|
-
const skillsDir =
|
|
63702
|
-
const skillDir =
|
|
63703
|
-
const skillFile =
|
|
64469
|
+
const skillsDir = path25.join(os7.homedir(), ".pie", "skills");
|
|
64470
|
+
const skillDir = path25.join(skillsDir, name);
|
|
64471
|
+
const skillFile = path25.join(skillDir, "SKILL.md");
|
|
63704
64472
|
console.error(`[SkillsCreate] Creating at: ${skillFile}`);
|
|
63705
|
-
if (
|
|
64473
|
+
if (fs23.existsSync(skillDir)) {
|
|
63706
64474
|
host.showError(`Skill "${name}" already exists at ${skillDir}`);
|
|
63707
64475
|
return;
|
|
63708
64476
|
}
|
|
63709
|
-
|
|
64477
|
+
fs23.mkdirSync(skillDir, { recursive: true });
|
|
63710
64478
|
const template = createSkillTemplate(name, description);
|
|
63711
|
-
|
|
64479
|
+
fs23.writeFileSync(skillFile, template, "utf-8");
|
|
63712
64480
|
console.error(`[SkillsCreate] Successfully created skill`);
|
|
63713
64481
|
host.chatContainer.addChild(new Spacer(1));
|
|
63714
64482
|
host.chatContainer.addChild(new Text(theme.fg("accent", `\u2713 Created skill: ${name}`), 1, 0));
|
|
@@ -63744,10 +64512,10 @@ async function handleSkillsReload(host) {
|
|
|
63744
64512
|
host.chatContainer.addChild(new Text(theme.fg("accent", "Reloading skills..."), 1, 0));
|
|
63745
64513
|
host.ui.requestRender();
|
|
63746
64514
|
try {
|
|
63747
|
-
const builtinDir =
|
|
63748
|
-
const userDir =
|
|
63749
|
-
const projectDir =
|
|
63750
|
-
const result = loadSkills({ builtinDir, userDir, projectDir });
|
|
64515
|
+
const builtinDir = path25.join(getBuiltinDir(), "skills");
|
|
64516
|
+
const userDir = path25.join(os7.homedir(), ".pie", "skills");
|
|
64517
|
+
const projectDir = path25.join(process.cwd(), ".pie", "skills");
|
|
64518
|
+
const result = loadSkills({ dirs: buildSkillDirs({ builtinDir, userDir, projectDir, cwd: process.cwd() }) });
|
|
63751
64519
|
host.skills = result.skills;
|
|
63752
64520
|
host.skillsSection = formatSkillsForPrompt(host.skills);
|
|
63753
64521
|
host.applyRuntimeToolMode(host.allTools.length > 0 ? host.allTools : host.agent.state.tools);
|
|
@@ -63762,8 +64530,8 @@ async function handleSkillsReload(host) {
|
|
|
63762
64530
|
}
|
|
63763
64531
|
}
|
|
63764
64532
|
const builtinCount = host.skills.filter((s) => s.source === "builtin").length;
|
|
63765
|
-
const userCount = host.skills.filter((s) => s.source === "user").length;
|
|
63766
|
-
const projectCount = host.skills.filter((s) => s.source === "project").length;
|
|
64533
|
+
const userCount = host.skills.filter((s) => s.source === "user" || s.source === "user-agents").length;
|
|
64534
|
+
const projectCount = host.skills.filter((s) => s.source === "project" || s.source === "project-agents").length;
|
|
63767
64535
|
host.chatContainer.addChild(new Text(theme.fg("accent", `\u2713 Reloaded ${host.skills.length} skill(s)`), 1, 0));
|
|
63768
64536
|
host.chatContainer.addChild(new Text(theme.fg("dim", ` Builtin: ${builtinCount}, User: ${userCount}, Project: ${projectCount}`), 1, 0));
|
|
63769
64537
|
if (result.diagnostics.length > 0) {
|
|
@@ -64134,6 +64902,14 @@ function showThinkingLevelSelector(host) {
|
|
|
64134
64902
|
});
|
|
64135
64903
|
}
|
|
64136
64904
|
async function handleClearCommand(host) {
|
|
64905
|
+
const beforeSwitch = await host.extensionRunner?.emitSessionBeforeSwitch({
|
|
64906
|
+
type: "session:before_switch",
|
|
64907
|
+
reason: "new"
|
|
64908
|
+
});
|
|
64909
|
+
if (beforeSwitch?.cancel) {
|
|
64910
|
+
host.showStatus("Session switch cancelled by an extension");
|
|
64911
|
+
return;
|
|
64912
|
+
}
|
|
64137
64913
|
host.setControllerBusy(false);
|
|
64138
64914
|
host.setWaitingForUser(false);
|
|
64139
64915
|
host.executionState.clear();
|
|
@@ -64432,7 +65208,21 @@ async function handleCompactCommand(host) {
|
|
|
64432
65208
|
apiKey: host.options.apiKey,
|
|
64433
65209
|
systemPrompt: host.agent.state.systemPrompt
|
|
64434
65210
|
};
|
|
64435
|
-
|
|
65211
|
+
let summary;
|
|
65212
|
+
const beforeCompact = await host.extensionRunner?.emitSessionBeforeCompact({
|
|
65213
|
+
type: "session:before_compact",
|
|
65214
|
+
reason: "manual",
|
|
65215
|
+
messageCount: existingMessages.length
|
|
65216
|
+
});
|
|
65217
|
+
if (beforeCompact?.cancel) {
|
|
65218
|
+
host.showStatus("Compaction cancelled by an extension");
|
|
65219
|
+
return;
|
|
65220
|
+
}
|
|
65221
|
+
if (beforeCompact?.summary) {
|
|
65222
|
+
summary = beforeCompact.summary;
|
|
65223
|
+
} else {
|
|
65224
|
+
summary = await createCompactionSummary(existingMessages, compactOptions);
|
|
65225
|
+
}
|
|
64436
65226
|
const tokensBefore = calculateTotalTokens(existingMessages);
|
|
64437
65227
|
host.sessionManager.appendCompaction(summary, firstKeptEntryId, tokensBefore);
|
|
64438
65228
|
const compactedMessages = host.sessionManager.getMessages();
|
|
@@ -64495,6 +65285,31 @@ function formatFooterLifecycleEvent(event) {
|
|
|
64495
65285
|
}
|
|
64496
65286
|
}
|
|
64497
65287
|
|
|
65288
|
+
// src/runtime/terminal-bell.ts
|
|
65289
|
+
var BEL = "\x07";
|
|
65290
|
+
var DEFAULT_MIN_INTERVAL_MS = 500;
|
|
65291
|
+
var lastRingAtMs = 0;
|
|
65292
|
+
function ringTerminalBell(options = {}) {
|
|
65293
|
+
const {
|
|
65294
|
+
enabled = true,
|
|
65295
|
+
stdout = process.stdout,
|
|
65296
|
+
now = Date.now,
|
|
65297
|
+
minIntervalMs = DEFAULT_MIN_INTERVAL_MS
|
|
65298
|
+
} = options;
|
|
65299
|
+
if (!enabled) return false;
|
|
65300
|
+
if (!stdout.isTTY) return false;
|
|
65301
|
+
const atMs = now();
|
|
65302
|
+
if (atMs - lastRingAtMs < minIntervalMs) return false;
|
|
65303
|
+
lastRingAtMs = atMs;
|
|
65304
|
+
stdout.write(BEL);
|
|
65305
|
+
return true;
|
|
65306
|
+
}
|
|
65307
|
+
function ringTurnSettledBell(host) {
|
|
65308
|
+
if ((host.agentSessionController?.pendingMessageCount ?? 0) > 0) return;
|
|
65309
|
+
if (host.executionState.read().awaitingContinuation) return;
|
|
65310
|
+
host.ringAttentionBell?.();
|
|
65311
|
+
}
|
|
65312
|
+
|
|
64498
65313
|
// src/runtime/interactive-runtime-events.ts
|
|
64499
65314
|
function bindInteractiveRuntimeEvents(host) {
|
|
64500
65315
|
host.agent.subscribe((event) => {
|
|
@@ -64665,6 +65480,7 @@ function handleSemanticAgentEvent(host, event) {
|
|
|
64665
65480
|
host.appendWarningSummary(outcome.terminalSummary);
|
|
64666
65481
|
}
|
|
64667
65482
|
host.sessionTrace.completeTurn(event.message, false);
|
|
65483
|
+
ringTurnSettledBell(host);
|
|
64668
65484
|
host._semanticTurnIndex++;
|
|
64669
65485
|
break;
|
|
64670
65486
|
}
|
|
@@ -64699,6 +65515,7 @@ function handleSemanticAgentEvent(host, event) {
|
|
|
64699
65515
|
host.appendWarningSummary(outcome.terminalSummary);
|
|
64700
65516
|
}
|
|
64701
65517
|
host.sessionTrace.completeTurn(event.message, true);
|
|
65518
|
+
host.ringAttentionBell?.();
|
|
64702
65519
|
host._semanticTurnIndex++;
|
|
64703
65520
|
break;
|
|
64704
65521
|
}
|
|
@@ -64747,6 +65564,7 @@ function handleMessageStart(host, event) {
|
|
|
64747
65564
|
host.safeRequestRender();
|
|
64748
65565
|
}
|
|
64749
65566
|
function handleMessageUpdate(host, event) {
|
|
65567
|
+
host.noteContinuationActivity?.();
|
|
64750
65568
|
if (!host.streamingComponent || event.message?.role !== "assistant") return;
|
|
64751
65569
|
host.sessionTrace.updateAssistantMessage(event.message);
|
|
64752
65570
|
if (host.isComposing) {
|
|
@@ -64783,7 +65601,7 @@ function handleMessageUpdate(host, event) {
|
|
|
64783
65601
|
const toolComponent = new ToolExecutionComponent(
|
|
64784
65602
|
toolCall.name,
|
|
64785
65603
|
toolCall.arguments,
|
|
64786
|
-
{ expanded: host.toolOutputExpanded },
|
|
65604
|
+
{ expanded: host.toolOutputExpanded, renderHint: findToolRenderHint(host, toolCall.name) },
|
|
64787
65605
|
host.ui
|
|
64788
65606
|
);
|
|
64789
65607
|
host.pendingTools.set(toolId, toolComponent);
|
|
@@ -64819,6 +65637,15 @@ function handleMessageUpdate(host, event) {
|
|
|
64819
65637
|
});
|
|
64820
65638
|
host.safeRequestRender();
|
|
64821
65639
|
}
|
|
65640
|
+
function findToolRenderHint(host, toolName) {
|
|
65641
|
+
const tools = host.agent?.state?.tools ?? [];
|
|
65642
|
+
for (const tool of tools) {
|
|
65643
|
+
if (tool.name !== toolName) continue;
|
|
65644
|
+
const hint = tool.renderHint;
|
|
65645
|
+
if (hint) return hint;
|
|
65646
|
+
}
|
|
65647
|
+
return void 0;
|
|
65648
|
+
}
|
|
64822
65649
|
function handleToolStart(host, event) {
|
|
64823
65650
|
host.logDebug("tool:start", { toolName: event.toolName, toolCallId: event.toolCallId });
|
|
64824
65651
|
host.sessionTrace.startTool(event.toolCallId, event.toolName, event.args);
|
|
@@ -64830,7 +65657,7 @@ function handleToolStart(host, event) {
|
|
|
64830
65657
|
const newToolComponent = new ToolExecutionComponent(
|
|
64831
65658
|
event.toolName,
|
|
64832
65659
|
event.args,
|
|
64833
|
-
{ expanded: host.toolOutputExpanded },
|
|
65660
|
+
{ expanded: host.toolOutputExpanded, renderHint: findToolRenderHint(host, event.toolName) },
|
|
64834
65661
|
host.ui
|
|
64835
65662
|
);
|
|
64836
65663
|
host.pendingTools.set(event.toolCallId, newToolComponent);
|
|
@@ -64847,6 +65674,7 @@ function handleToolStart(host, event) {
|
|
|
64847
65674
|
host.safeRequestRender();
|
|
64848
65675
|
}
|
|
64849
65676
|
function handleToolUpdate(host, event) {
|
|
65677
|
+
host.noteContinuationActivity?.();
|
|
64850
65678
|
if (event.toolCallId) {
|
|
64851
65679
|
const toolComponent = host.pendingTools.get(event.toolCallId);
|
|
64852
65680
|
if (toolComponent) {
|
|
@@ -64923,6 +65751,20 @@ function setupInteractiveEditorSubmitHandler(host) {
|
|
|
64923
65751
|
host.editor.onSubmit = async (text) => {
|
|
64924
65752
|
text = text.trim();
|
|
64925
65753
|
if (!text) return;
|
|
65754
|
+
if (host.extensionRunner) {
|
|
65755
|
+
const decision = await host.extensionRunner.emitTerminalInput({ type: "terminal:input", text });
|
|
65756
|
+
if (decision.action === "consume") {
|
|
65757
|
+
host.editor.setText("");
|
|
65758
|
+
return;
|
|
65759
|
+
}
|
|
65760
|
+
if (decision.action === "transform") {
|
|
65761
|
+
text = decision.text.trim();
|
|
65762
|
+
if (!text) {
|
|
65763
|
+
host.editor.setText("");
|
|
65764
|
+
return;
|
|
65765
|
+
}
|
|
65766
|
+
}
|
|
65767
|
+
}
|
|
64926
65768
|
if (text.startsWith("/skills/skill:")) {
|
|
64927
65769
|
const skillText = text.slice("/skills/".length);
|
|
64928
65770
|
const expanded = host.expandSkillCommand(skillText);
|
|
@@ -65185,7 +66027,7 @@ function setupInteractiveEditorSubmitHandler(host) {
|
|
|
65185
66027
|
host.chatContainer.addChild(userMsg);
|
|
65186
66028
|
host.editor.setText("");
|
|
65187
66029
|
host.ui.requestRender();
|
|
65188
|
-
await new Promise((
|
|
66030
|
+
await new Promise((resolve7) => setImmediate(resolve7));
|
|
65189
66031
|
let finalContent = messageContent;
|
|
65190
66032
|
const reminderPrefix = host.executionReminder;
|
|
65191
66033
|
const combinedPrefix = [reminderPrefix].filter((value) => Boolean(value)).join("");
|
|
@@ -65221,8 +66063,8 @@ function setupInteractiveEditorSubmitHandler(host) {
|
|
|
65221
66063
|
}
|
|
65222
66064
|
|
|
65223
66065
|
// src/input/interactive-file-pipeline.ts
|
|
65224
|
-
import * as
|
|
65225
|
-
import * as
|
|
66066
|
+
import * as fs26 from "fs";
|
|
66067
|
+
import * as path28 from "path";
|
|
65226
66068
|
|
|
65227
66069
|
// src/components/file-picker.ts
|
|
65228
66070
|
var FilePickerComponent = class extends Container {
|
|
@@ -65443,8 +66285,8 @@ var FilePickerComponent = class extends Container {
|
|
|
65443
66285
|
};
|
|
65444
66286
|
|
|
65445
66287
|
// src/files/commands/file-commands.ts
|
|
65446
|
-
import * as
|
|
65447
|
-
import * as
|
|
66288
|
+
import * as fs24 from "node:fs";
|
|
66289
|
+
import * as path26 from "node:path";
|
|
65448
66290
|
var FileCommands = class {
|
|
65449
66291
|
context;
|
|
65450
66292
|
constructor(context) {
|
|
@@ -65460,11 +66302,11 @@ var FileCommands = class {
|
|
|
65460
66302
|
return;
|
|
65461
66303
|
}
|
|
65462
66304
|
const filePath = resolvePath2(parsed.path);
|
|
65463
|
-
if (!
|
|
66305
|
+
if (!fs24.existsSync(filePath)) {
|
|
65464
66306
|
this.context.showError(`File not found: ${parsed.path}`);
|
|
65465
66307
|
return;
|
|
65466
66308
|
}
|
|
65467
|
-
this.context.showStatus(`Uploading ${
|
|
66309
|
+
this.context.showStatus(`Uploading ${path26.basename(filePath)}...`);
|
|
65468
66310
|
try {
|
|
65469
66311
|
const result = await this.context.fileManager.upload(filePath, {
|
|
65470
66312
|
purpose: parsed.purpose,
|
|
@@ -65499,7 +66341,7 @@ var FileCommands = class {
|
|
|
65499
66341
|
return;
|
|
65500
66342
|
}
|
|
65501
66343
|
const resolvedPath = resolvePath2(filePath);
|
|
65502
|
-
if (!
|
|
66344
|
+
if (!fs24.existsSync(resolvedPath)) {
|
|
65503
66345
|
this.context.showError(`File not found: ${filePath}`);
|
|
65504
66346
|
return;
|
|
65505
66347
|
}
|
|
@@ -65530,7 +66372,7 @@ var FileCommands = class {
|
|
|
65530
66372
|
return;
|
|
65531
66373
|
}
|
|
65532
66374
|
const resolvedPath = resolvePath2(parsed.path);
|
|
65533
|
-
if (!
|
|
66375
|
+
if (!fs24.existsSync(resolvedPath)) {
|
|
65534
66376
|
this.context.showError(`File not found: ${parsed.path}`);
|
|
65535
66377
|
return;
|
|
65536
66378
|
}
|
|
@@ -66008,8 +66850,8 @@ var FileStatusMonitor = class {
|
|
|
66008
66850
|
};
|
|
66009
66851
|
|
|
66010
66852
|
// src/files/mention/mention-handler.ts
|
|
66011
|
-
import * as
|
|
66012
|
-
import * as
|
|
66853
|
+
import * as fs25 from "node:fs";
|
|
66854
|
+
import * as path27 from "node:path";
|
|
66013
66855
|
var MentionHandler = class {
|
|
66014
66856
|
fileManager;
|
|
66015
66857
|
onShowPicker;
|
|
@@ -66064,7 +66906,7 @@ var MentionHandler = class {
|
|
|
66064
66906
|
navigateBrowseParent() {
|
|
66065
66907
|
const currentDir = this.getBrowserDirectory();
|
|
66066
66908
|
if (!currentDir) return;
|
|
66067
|
-
const parentDir =
|
|
66909
|
+
const parentDir = path27.dirname(currentDir);
|
|
66068
66910
|
if (parentDir === currentDir) return;
|
|
66069
66911
|
this.browsingDirectory = parentDir;
|
|
66070
66912
|
this.showMentionPicker("");
|
|
@@ -66084,7 +66926,7 @@ var MentionHandler = class {
|
|
|
66084
66926
|
if (option.type === "parent") {
|
|
66085
66927
|
const currentDir = this.getBrowserDirectory();
|
|
66086
66928
|
if (currentDir) {
|
|
66087
|
-
this.browsingDirectory =
|
|
66929
|
+
this.browsingDirectory = path27.dirname(currentDir);
|
|
66088
66930
|
this.showMentionPicker("");
|
|
66089
66931
|
this.isPickerVisible = true;
|
|
66090
66932
|
}
|
|
@@ -66220,7 +67062,7 @@ var MentionHandler = class {
|
|
|
66220
67062
|
id: part.content,
|
|
66221
67063
|
name: part.name || fileInfo.name,
|
|
66222
67064
|
mimeType: fileInfo.mimeType,
|
|
66223
|
-
ext:
|
|
67065
|
+
ext: path27.extname(part.name || fileInfo.name).toLowerCase(),
|
|
66224
67066
|
size: fileInfo.size,
|
|
66225
67067
|
source: fileInfo.platforms.length > 0 ? "session" : "local",
|
|
66226
67068
|
fileId: part.content,
|
|
@@ -66378,11 +67220,11 @@ var MentionHandler = class {
|
|
|
66378
67220
|
}
|
|
66379
67221
|
if (this.isPath(query)) {
|
|
66380
67222
|
const resolvedPath = resolvePath2(query);
|
|
66381
|
-
if (
|
|
66382
|
-
const stats =
|
|
67223
|
+
if (fs25.existsSync(resolvedPath)) {
|
|
67224
|
+
const stats = fs25.statSync(resolvedPath);
|
|
66383
67225
|
if (stats.isFile()) {
|
|
66384
67226
|
const type = FileCache.getFileTypeFromName(resolvedPath);
|
|
66385
|
-
const name =
|
|
67227
|
+
const name = path27.basename(resolvedPath);
|
|
66386
67228
|
if (!seenNames.has(name.toLowerCase())) {
|
|
66387
67229
|
options.push({
|
|
66388
67230
|
type: "upload",
|
|
@@ -66403,16 +67245,16 @@ var MentionHandler = class {
|
|
|
66403
67245
|
return void 0;
|
|
66404
67246
|
}
|
|
66405
67247
|
const queryPath = this.isPath(query) ? resolvePath2(query) : void 0;
|
|
66406
|
-
const filter = queryPath ?
|
|
67248
|
+
const filter = queryPath ? fs25.existsSync(queryPath) && fs25.statSync(queryPath).isDirectory() ? "" : path27.basename(queryPath) : query;
|
|
66407
67249
|
const entries = this.fileManager.listPathEntries(currentDir, filter);
|
|
66408
67250
|
const options = [];
|
|
66409
67251
|
const homeDir = process.env.HOME || "";
|
|
66410
67252
|
const displayDir = currentDir.startsWith(homeDir) ? `~${currentDir.slice(homeDir.length)}` : currentDir;
|
|
66411
|
-
if (
|
|
67253
|
+
if (path27.dirname(currentDir) !== currentDir) {
|
|
66412
67254
|
options.push({
|
|
66413
67255
|
type: "parent",
|
|
66414
67256
|
name: "..",
|
|
66415
|
-
path:
|
|
67257
|
+
path: path27.dirname(currentDir),
|
|
66416
67258
|
icon: "\u21A9",
|
|
66417
67259
|
meta: "Parent directory"
|
|
66418
67260
|
});
|
|
@@ -66444,10 +67286,10 @@ var MentionHandler = class {
|
|
|
66444
67286
|
getBrowserDirectory(query = this.currentQuery) {
|
|
66445
67287
|
if (this.isPath(query)) {
|
|
66446
67288
|
const resolvedPath = resolvePath2(query);
|
|
66447
|
-
if (
|
|
67289
|
+
if (fs25.existsSync(resolvedPath) && fs25.statSync(resolvedPath).isDirectory()) {
|
|
66448
67290
|
return resolvedPath;
|
|
66449
67291
|
}
|
|
66450
|
-
return
|
|
67292
|
+
return path27.dirname(resolvedPath);
|
|
66451
67293
|
}
|
|
66452
67294
|
return this.browsingDirectory;
|
|
66453
67295
|
}
|
|
@@ -66534,8 +67376,8 @@ async function initializeFileManager(host) {
|
|
|
66534
67376
|
sessionId,
|
|
66535
67377
|
platform: platform2,
|
|
66536
67378
|
apiKey: host.options.apiKey,
|
|
66537
|
-
baseDir:
|
|
66538
|
-
sessionsDir:
|
|
67379
|
+
baseDir: path28.join(host.options.configDir, "files"),
|
|
67380
|
+
sessionsDir: path28.join(host.options.configDir, "sessions")
|
|
66539
67381
|
});
|
|
66540
67382
|
switch (getFileManagerAdapterKind(host.options.model?.filePlatform)) {
|
|
66541
67383
|
case "kimi":
|
|
@@ -66808,13 +67650,13 @@ function convertPreparedAttachmentItems(items) {
|
|
|
66808
67650
|
break;
|
|
66809
67651
|
case "image": {
|
|
66810
67652
|
if (!item.data && !item.path) return void 0;
|
|
66811
|
-
const data = item.data ??
|
|
67653
|
+
const data = item.data ?? fs26.readFileSync(item.path, "base64");
|
|
66812
67654
|
converted.push({ type: "image", data, mimeType: item.mimeType });
|
|
66813
67655
|
break;
|
|
66814
67656
|
}
|
|
66815
67657
|
case "video": {
|
|
66816
67658
|
if (!item.data && !item.path) return void 0;
|
|
66817
|
-
const data = item.data ??
|
|
67659
|
+
const data = item.data ?? fs26.readFileSync(item.path, "base64");
|
|
66818
67660
|
converted.push({ type: "video", data, mimeType: item.mimeType });
|
|
66819
67661
|
break;
|
|
66820
67662
|
}
|
|
@@ -67035,6 +67877,55 @@ function isCommandLikeInput(text) {
|
|
|
67035
67877
|
return text.startsWith("/") || text.startsWith("!!");
|
|
67036
67878
|
}
|
|
67037
67879
|
|
|
67880
|
+
// src/components/extension-widgets.ts
|
|
67881
|
+
var ExtensionWidgetsComponent = class extends Container {
|
|
67882
|
+
slots = /* @__PURE__ */ new Map();
|
|
67883
|
+
setWidget(key, lines) {
|
|
67884
|
+
const normalized = (lines ?? []).map((line) => String(line ?? "")).filter((line) => line.trim().length > 0);
|
|
67885
|
+
if (normalized.length === 0) {
|
|
67886
|
+
this.slots.delete(key);
|
|
67887
|
+
} else {
|
|
67888
|
+
this.slots.set(key, normalized.slice(0, 3));
|
|
67889
|
+
}
|
|
67890
|
+
this.rebuild();
|
|
67891
|
+
}
|
|
67892
|
+
clearAll() {
|
|
67893
|
+
if (this.slots.size === 0) return;
|
|
67894
|
+
this.slots.clear();
|
|
67895
|
+
this.rebuild();
|
|
67896
|
+
}
|
|
67897
|
+
rebuild() {
|
|
67898
|
+
this.clear();
|
|
67899
|
+
for (const lines of this.slots.values()) {
|
|
67900
|
+
for (const line of lines) {
|
|
67901
|
+
this.addChild(new Text(theme.fg("dim", `\xB7 ${line}`), 1, 0));
|
|
67902
|
+
}
|
|
67903
|
+
}
|
|
67904
|
+
}
|
|
67905
|
+
};
|
|
67906
|
+
var ExtensionStatusSlots = class {
|
|
67907
|
+
slots = /* @__PURE__ */ new Map();
|
|
67908
|
+
set(key, text) {
|
|
67909
|
+
const normalized = String(text ?? "").trim();
|
|
67910
|
+
if (!normalized) {
|
|
67911
|
+
if (!this.slots.delete(key)) return false;
|
|
67912
|
+
return true;
|
|
67913
|
+
}
|
|
67914
|
+
if (this.slots.get(key) === normalized) return false;
|
|
67915
|
+
this.slots.set(key, normalized);
|
|
67916
|
+
return true;
|
|
67917
|
+
}
|
|
67918
|
+
clear() {
|
|
67919
|
+
this.slots.clear();
|
|
67920
|
+
}
|
|
67921
|
+
/** Render-ready suffix for the footer line, e.g. " · git:clean · ci:ok". */
|
|
67922
|
+
format(maxWidth = 60) {
|
|
67923
|
+
if (this.slots.size === 0) return "";
|
|
67924
|
+
const joined = [...this.slots.values()].join(" \xB7 ");
|
|
67925
|
+
return ` \xB7 ${truncateToWidth(joined, maxWidth, "")}`;
|
|
67926
|
+
}
|
|
67927
|
+
};
|
|
67928
|
+
|
|
67038
67929
|
// src/components/footer.ts
|
|
67039
67930
|
var SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
67040
67931
|
var SPINNER_INTERVAL = 80;
|
|
@@ -67055,6 +67946,8 @@ var FooterComponent = class {
|
|
|
67055
67946
|
spinnerFrame = 0;
|
|
67056
67947
|
spinnerTimer = null;
|
|
67057
67948
|
onRequestRender;
|
|
67949
|
+
/** Keyed ambient status slots for extensions (ctx.ui.setStatus). */
|
|
67950
|
+
extensionStatuses = new ExtensionStatusSlots();
|
|
67058
67951
|
/** Set callback to request re-render when animation updates (pass true to force) */
|
|
67059
67952
|
setOnRequestRender(callback) {
|
|
67060
67953
|
this.onRequestRender = () => callback(true);
|
|
@@ -67167,6 +68060,7 @@ var FooterComponent = class {
|
|
|
67167
68060
|
const statusWithSpinner = spinner ? `${spinner} ${statusText}` : statusText;
|
|
67168
68061
|
statsParts.push(theme.fg(statusColor[state.status], statusWithSpinner));
|
|
67169
68062
|
let statsLeft = statsParts.join(" ");
|
|
68063
|
+
statsLeft += this.extensionStatuses.format();
|
|
67170
68064
|
const modelName = state.modelName || "no-model";
|
|
67171
68065
|
let rightSide = modelName;
|
|
67172
68066
|
if (state.thinkingLevel && state.thinkingLevel !== "off") {
|
|
@@ -67222,9 +68116,11 @@ var TodoWidgetComponent = class extends Container {
|
|
|
67222
68116
|
|
|
67223
68117
|
// src/agent-guidelines.ts
|
|
67224
68118
|
function buildCodingAgentGuidelinesSection(tools = []) {
|
|
67225
|
-
|
|
68119
|
+
const hasTodoTool = tools.some((tool) => tool.name === "manage_todo_list");
|
|
67226
68120
|
const guidelines = [
|
|
67227
68121
|
"Handle coding tasks by analyzing, planning, or making changes as the available tools and user request allow; do not stop at analysis when execution is expected.",
|
|
68122
|
+
`Keep going until the user's task is completely resolved before ending your turn. If you announce a next step (for example, "now run the tests"), execute it in the same turn instead of describing it; only end your turn when the task is done or you genuinely need user input.`,
|
|
68123
|
+
...hasTodoTool ? ["For multi-step tasks, set up progress tracking with manage_todo_list before starting, and keep exactly one step in progress until every step is complete."] : [],
|
|
67228
68124
|
"Work in the current workspace, preserve user changes, and keep edits focused.",
|
|
67229
68125
|
"Inspect the relevant code before changing it. Use file tools for workspace writes; do not use shell redirection or heredocs to bypass write_file/edit_file guards.",
|
|
67230
68126
|
"Run the most relevant existing test, build, or check after file changes when practical.",
|
|
@@ -67414,6 +68310,12 @@ function classifyCliFailure(context) {
|
|
|
67414
68310
|
terminal: false
|
|
67415
68311
|
});
|
|
67416
68312
|
}
|
|
68313
|
+
if (errorMessage3.includes(OPENAI_COMPAT_PROTOCOL_ERROR_CODE)) {
|
|
68314
|
+
return createClassification("provider_transient", errorMessage3, {
|
|
68315
|
+
retryable: true,
|
|
68316
|
+
terminal: false
|
|
68317
|
+
});
|
|
68318
|
+
}
|
|
67417
68319
|
if (PROVIDER_TRANSIENT_ERROR_PATTERN.test(errorMessage3)) {
|
|
67418
68320
|
return createClassification("provider_transient", errorMessage3, {
|
|
67419
68321
|
retryable: true,
|
|
@@ -67496,6 +68398,9 @@ function setupUI(host) {
|
|
|
67496
68398
|
host.todoWidgetContainer = new Container();
|
|
67497
68399
|
host.todoWidget = new TodoWidgetComponent(host.executionState);
|
|
67498
68400
|
host.todoWidgetContainer.addChild(host.todoWidget);
|
|
68401
|
+
host.extensionWidgetsContainer = new Container();
|
|
68402
|
+
host.extensionWidgets = new ExtensionWidgetsComponent();
|
|
68403
|
+
host.extensionWidgetsContainer.addChild(host.extensionWidgets);
|
|
67499
68404
|
const editorTheme = getEditorTheme();
|
|
67500
68405
|
host.editor = new Editor(host.ui, editorTheme, { paddingX: 1 });
|
|
67501
68406
|
host.editor.onChange = () => {
|
|
@@ -67510,6 +68415,7 @@ function setupUI(host) {
|
|
|
67510
68415
|
host.ui.addChild(host.chatContainer);
|
|
67511
68416
|
host.ui.addChild(host.pendingMessagesContainer);
|
|
67512
68417
|
host.ui.addChild(host.todoWidgetContainer);
|
|
68418
|
+
host.ui.addChild(host.extensionWidgetsContainer);
|
|
67513
68419
|
host.ui.addChild(new Spacer());
|
|
67514
68420
|
host.ui.addChild(host.footer);
|
|
67515
68421
|
host.ui.addChild(host.editorContainer);
|
|
@@ -67839,11 +68745,11 @@ var HierarchicalSelectorComponent = class extends Container {
|
|
|
67839
68745
|
// src/runtime/interactive-ui-helpers.ts
|
|
67840
68746
|
function showHierarchicalMenu(host, initialPath = []) {
|
|
67841
68747
|
const basePath = [...initialPath];
|
|
67842
|
-
const showAtPath = (
|
|
68748
|
+
const showAtPath = (path33) => {
|
|
67843
68749
|
host.showSelector((done) => {
|
|
67844
68750
|
const selector = new HierarchicalSelectorComponent({
|
|
67845
68751
|
tree: commandTree,
|
|
67846
|
-
path:
|
|
68752
|
+
path: path33,
|
|
67847
68753
|
basePath,
|
|
67848
68754
|
onNavigate: (newPath) => {
|
|
67849
68755
|
done();
|
|
@@ -67851,7 +68757,7 @@ function showHierarchicalMenu(host, initialPath = []) {
|
|
|
67851
68757
|
},
|
|
67852
68758
|
onSelect: async (selectedPath, node, searchArgs) => {
|
|
67853
68759
|
if (node.handler) {
|
|
67854
|
-
const args = searchArgs || (selectedPath.length >
|
|
68760
|
+
const args = searchArgs || (selectedPath.length > path33.length ? selectedPath.slice(path33.length).join(" ") : void 0);
|
|
67855
68761
|
const commandPath = "/" + selectedPath.join("/");
|
|
67856
68762
|
if (!args && host.isInteractiveCommand(commandPath)) {
|
|
67857
68763
|
done();
|
|
@@ -67945,10 +68851,17 @@ function getContinuationTimeoutMs() {
|
|
|
67945
68851
|
if (Number.isFinite(raw) && raw > 0) return raw;
|
|
67946
68852
|
return 2e4;
|
|
67947
68853
|
}
|
|
68854
|
+
var STALL_RECHECK_MS = 1e3;
|
|
68855
|
+
function getHardStallTimeoutMs() {
|
|
68856
|
+
const raw = Number(process.env.PIE_INTERACTIVE_HARD_STALL_TIMEOUT_MS || "");
|
|
68857
|
+
if (Number.isFinite(raw) && raw > 0) return raw;
|
|
68858
|
+
return 30 * 6e4;
|
|
68859
|
+
}
|
|
67948
68860
|
function clearContinuationTimeout(host, reason) {
|
|
67949
68861
|
if (!host.continuationTimeoutHandle) return;
|
|
67950
68862
|
clearTimeout(host.continuationTimeoutHandle);
|
|
67951
68863
|
host.continuationTimeoutHandle = void 0;
|
|
68864
|
+
host.continuationStallSinceMs = void 0;
|
|
67952
68865
|
host.logDebug("continuation_timeout:cleared", {
|
|
67953
68866
|
reason,
|
|
67954
68867
|
awaitingContinuation: host.executionState.read().awaitingContinuation
|
|
@@ -67981,6 +68894,36 @@ function appendWarningSummary(host, message) {
|
|
|
67981
68894
|
host.chatContainer.addChild(new Spacer(1));
|
|
67982
68895
|
host.safeRequestRender();
|
|
67983
68896
|
}
|
|
68897
|
+
async function recoverHardStalledTurn(host, stalledMs) {
|
|
68898
|
+
host.continuationStallSinceMs = void 0;
|
|
68899
|
+
host.logDebug("continuation_timeout:hard_stall_recovery", { stalledMs });
|
|
68900
|
+
host.agentSessionController?.abort();
|
|
68901
|
+
try {
|
|
68902
|
+
await Promise.race([
|
|
68903
|
+
host.agentSessionController?.waitForIdle() ?? Promise.resolve(),
|
|
68904
|
+
new Promise((resolve7) => setTimeout(resolve7, 2e3))
|
|
68905
|
+
]);
|
|
68906
|
+
} catch (error) {
|
|
68907
|
+
host.logDebug("continuation_timeout:hard_stall_wait_failed", {
|
|
68908
|
+
error: error instanceof Error ? error.message : String(error)
|
|
68909
|
+
});
|
|
68910
|
+
}
|
|
68911
|
+
host.agent.resetProcessingState();
|
|
68912
|
+
host.isProcessing = false;
|
|
68913
|
+
host.setSubmitLock(false);
|
|
68914
|
+
host.streamingComponent = void 0;
|
|
68915
|
+
host.pendingTools.clear();
|
|
68916
|
+
const abortOutcome = host.executionState.abort();
|
|
68917
|
+
host.persistExecutionState();
|
|
68918
|
+
void host.refreshTodoWidget();
|
|
68919
|
+
host.footer.setState({ status: "ready" });
|
|
68920
|
+
host.safeRequestRender();
|
|
68921
|
+
host.ringAttentionBell?.();
|
|
68922
|
+
appendWarningSummary(host, "Recovered from a stalled turn (no progress for " + Math.round(stalledMs / 1e3) + "s). The previous turn was interrupted; review its partial output before continuing.");
|
|
68923
|
+
if (abortOutcome.terminalSummary) {
|
|
68924
|
+
appendWarningSummary(host, abortOutcome.terminalSummary);
|
|
68925
|
+
}
|
|
68926
|
+
}
|
|
67984
68927
|
function scheduleContinuationTimeout(host) {
|
|
67985
68928
|
host.clearContinuationTimeout("reschedule");
|
|
67986
68929
|
const timeoutMs = getContinuationTimeoutMs();
|
|
@@ -67989,23 +68932,38 @@ function scheduleContinuationTimeout(host) {
|
|
|
67989
68932
|
currentStepId: host.executionState.read().currentStepId,
|
|
67990
68933
|
queueLength: host.agentSessionController?.pendingMessageCount ?? 0
|
|
67991
68934
|
});
|
|
67992
|
-
|
|
68935
|
+
const check = () => {
|
|
67993
68936
|
host.continuationTimeoutHandle = void 0;
|
|
67994
68937
|
const state = host.executionState.read();
|
|
67995
68938
|
if (!state.awaitingContinuation || isWaitingForUserInteraction(host)) {
|
|
68939
|
+
host.continuationStallSinceMs = void 0;
|
|
67996
68940
|
host.logDebug("continuation_timeout:skip", {
|
|
67997
68941
|
reason: !state.awaitingContinuation ? "no_longer_awaiting" : "waiting_for_user_interaction"
|
|
67998
68942
|
});
|
|
67999
68943
|
return;
|
|
68000
68944
|
}
|
|
68001
68945
|
if (host.agent.state.isStreaming || host.isProcessing || host.submitLock || host.pendingTools.size > 0) {
|
|
68002
|
-
|
|
68003
|
-
|
|
68004
|
-
|
|
68005
|
-
|
|
68006
|
-
|
|
68946
|
+
const now = Date.now();
|
|
68947
|
+
const stallSince = host.continuationStallSinceMs ?? now;
|
|
68948
|
+
host.continuationStallSinceMs = stallSince;
|
|
68949
|
+
const stalledMs = now - stallSince;
|
|
68950
|
+
const hardTimeoutMs = getHardStallTimeoutMs();
|
|
68951
|
+
if (stalledMs < hardTimeoutMs) {
|
|
68952
|
+
host.logDebug("continuation_timeout:active_work", {
|
|
68953
|
+
isStreaming: host.agent.state.isStreaming,
|
|
68954
|
+
isProcessing: host.isProcessing,
|
|
68955
|
+
pendingTools: host.pendingTools.size,
|
|
68956
|
+
stalledMs,
|
|
68957
|
+
hardTimeoutMs
|
|
68958
|
+
});
|
|
68959
|
+
const nextCheckMs = Math.max(50, Math.min(STALL_RECHECK_MS, hardTimeoutMs - stalledMs));
|
|
68960
|
+
host.continuationTimeoutHandle = setTimeout(check, nextCheckMs);
|
|
68961
|
+
return;
|
|
68962
|
+
}
|
|
68963
|
+
void recoverHardStalledTurn(host, stalledMs);
|
|
68007
68964
|
return;
|
|
68008
68965
|
}
|
|
68966
|
+
host.continuationStallSinceMs = void 0;
|
|
68009
68967
|
const outcome = host.executionState.markAwaitingContinuationStalled();
|
|
68010
68968
|
host.logDebug("continuation_timeout:expired", {
|
|
68011
68969
|
currentStepId: state.currentStepId,
|
|
@@ -68021,7 +68979,8 @@ function scheduleContinuationTimeout(host) {
|
|
|
68021
68979
|
}
|
|
68022
68980
|
}
|
|
68023
68981
|
host.agentSessionController?.requestQueueDispatch();
|
|
68024
|
-
}
|
|
68982
|
+
};
|
|
68983
|
+
host.continuationTimeoutHandle = setTimeout(check, timeoutMs);
|
|
68025
68984
|
}
|
|
68026
68985
|
|
|
68027
68986
|
// src/runtime/interactive-controller-setup.ts
|
|
@@ -68087,12 +69046,29 @@ function createInteractiveAgentSessionController(host) {
|
|
|
68087
69046
|
baseDelayMs: getCliRetryBaseDelayMs()
|
|
68088
69047
|
},
|
|
68089
69048
|
compaction: {
|
|
68090
|
-
compact: async (context) =>
|
|
68091
|
-
|
|
68092
|
-
|
|
68093
|
-
|
|
68094
|
-
|
|
68095
|
-
|
|
69049
|
+
compact: async (context) => {
|
|
69050
|
+
const beforeCompact = await host.extensionRunner?.emitSessionBeforeCompact({
|
|
69051
|
+
type: "session:before_compact",
|
|
69052
|
+
reason: context.reason === "overflow" ? "overflow" : "threshold",
|
|
69053
|
+
messageCount: context.messages.length
|
|
69054
|
+
});
|
|
69055
|
+
if (beforeCompact?.cancel) {
|
|
69056
|
+
host.showStatus("Auto-compaction cancelled by an extension");
|
|
69057
|
+
return { aborted: true };
|
|
69058
|
+
}
|
|
69059
|
+
if (beforeCompact?.summary) {
|
|
69060
|
+
const activeEntryId = context.sessionManager.getActiveEntryId();
|
|
69061
|
+
const firstKeptEntryId = activeEntryId ? findFirstKeptEntryId(context.sessionManager.getPathToEntry(activeEntryId)) : void 0;
|
|
69062
|
+
if (!firstKeptEntryId) return { aborted: true };
|
|
69063
|
+
return { summary: beforeCompact.summary, firstKeptEntryId };
|
|
69064
|
+
}
|
|
69065
|
+
return createCliAutoCompactionResult({
|
|
69066
|
+
context,
|
|
69067
|
+
model: host.options.model,
|
|
69068
|
+
apiKey: host.options.apiKey,
|
|
69069
|
+
systemPrompt: host.agent.state.systemPrompt
|
|
69070
|
+
});
|
|
69071
|
+
}
|
|
68096
69072
|
},
|
|
68097
69073
|
onEvent: (event) => {
|
|
68098
69074
|
host.sessionTrace.noteRuntimeEvent(event.type, event);
|
|
@@ -68161,6 +69137,7 @@ function createInteractiveAgentSessionController(host) {
|
|
|
68161
69137
|
host.appendWarningSummary(outcome.terminalSummary);
|
|
68162
69138
|
}
|
|
68163
69139
|
host.sessionTrace.completeTurn(event.message, false);
|
|
69140
|
+
ringTurnSettledBell(host);
|
|
68164
69141
|
host._semanticTurnIndex++;
|
|
68165
69142
|
} else if (event.type === "turn_settled") {
|
|
68166
69143
|
host.setControllerBusy(false);
|
|
@@ -68198,6 +69175,7 @@ function createInteractiveAgentSessionController(host) {
|
|
|
68198
69175
|
host.showStatus("Todo list preserved. Send 'continue' when you want to resume it.");
|
|
68199
69176
|
}
|
|
68200
69177
|
host.showError(event.failure.message);
|
|
69178
|
+
host.ringAttentionBell?.();
|
|
68201
69179
|
host.setControllerBusy(false);
|
|
68202
69180
|
} else if (event.type === "turn_cancelled") {
|
|
68203
69181
|
const outcome = host.executionState.abort();
|
|
@@ -68526,7 +69504,7 @@ async function handleDirectBash(host, command) {
|
|
|
68526
69504
|
child.stderr.on("data", (data) => {
|
|
68527
69505
|
output.append(data.toString());
|
|
68528
69506
|
});
|
|
68529
|
-
return new Promise((
|
|
69507
|
+
return new Promise((resolve7) => {
|
|
68530
69508
|
let settled = false;
|
|
68531
69509
|
const finish = (code) => {
|
|
68532
69510
|
if (settled) return;
|
|
@@ -68539,7 +69517,7 @@ async function handleDirectBash(host, command) {
|
|
|
68539
69517
|
}
|
|
68540
69518
|
host.chatContainer.addChild(new Spacer(1));
|
|
68541
69519
|
host.ui.requestRender();
|
|
68542
|
-
|
|
69520
|
+
resolve7();
|
|
68543
69521
|
};
|
|
68544
69522
|
child.on("error", (error) => {
|
|
68545
69523
|
output.append(error.message);
|
|
@@ -68559,7 +69537,15 @@ function createInteractiveExtensionUIContext(host) {
|
|
|
68559
69537
|
confirm: (title, message, opts) => showExtensionConfirm(host, title, message, opts),
|
|
68560
69538
|
input: (title, placeholder, opts) => showExtensionInput(host, title, placeholder, opts),
|
|
68561
69539
|
editor: (title, prefill) => showExtensionEditor(host, title, prefill),
|
|
68562
|
-
custom: (factory, options) => showExtensionCustom(host, factory, options)
|
|
69540
|
+
custom: (factory, options) => showExtensionCustom(host, factory, options),
|
|
69541
|
+
setStatus: (key, text) => {
|
|
69542
|
+
host.footer.extensionStatuses.set(key, text);
|
|
69543
|
+
host.ui.requestRender();
|
|
69544
|
+
},
|
|
69545
|
+
setWidget: (key, lines) => {
|
|
69546
|
+
host.extensionWidgets.setWidget(key, lines);
|
|
69547
|
+
host.ui.requestRender();
|
|
69548
|
+
}
|
|
68563
69549
|
};
|
|
68564
69550
|
}
|
|
68565
69551
|
function installInteractiveInteractionHandler(host) {
|
|
@@ -68568,6 +69554,7 @@ function installInteractiveInteractionHandler(host) {
|
|
|
68568
69554
|
host.activeInteractionCount++;
|
|
68569
69555
|
host.setWaitingForUser(true);
|
|
68570
69556
|
host.footer.setState({ lifecycleEvent: "waiting_for_user_input" });
|
|
69557
|
+
host.ringAttentionBell?.();
|
|
68571
69558
|
host.safeRequestRender();
|
|
68572
69559
|
};
|
|
68573
69560
|
const endWaitingForUser = () => {
|
|
@@ -68578,15 +69565,15 @@ function installInteractiveInteractionHandler(host) {
|
|
|
68578
69565
|
host.footer.setState({ lifecycleEvent: "tool_execution_start" });
|
|
68579
69566
|
host.safeRequestRender();
|
|
68580
69567
|
};
|
|
68581
|
-
const
|
|
69568
|
+
const withTimeout2 = async (promise) => {
|
|
68582
69569
|
if (!("timeoutMs" in request) || !request.timeoutMs || request.timeoutMs <= 0) {
|
|
68583
69570
|
return { value: await promise, timedOut: false };
|
|
68584
69571
|
}
|
|
68585
|
-
return await new Promise((
|
|
68586
|
-
const timer = setTimeout(() =>
|
|
69572
|
+
return await new Promise((resolve7, reject) => {
|
|
69573
|
+
const timer = setTimeout(() => resolve7({ timedOut: true }), request.timeoutMs);
|
|
68587
69574
|
promise.then((value) => {
|
|
68588
69575
|
clearTimeout(timer);
|
|
68589
|
-
|
|
69576
|
+
resolve7({ value, timedOut: false });
|
|
68590
69577
|
}).catch((error) => {
|
|
68591
69578
|
clearTimeout(timer);
|
|
68592
69579
|
reject(error);
|
|
@@ -68596,7 +69583,7 @@ function installInteractiveInteractionHandler(host) {
|
|
|
68596
69583
|
const waitForUser = async (createPromise) => {
|
|
68597
69584
|
beginWaitingForUser();
|
|
68598
69585
|
try {
|
|
68599
|
-
return await
|
|
69586
|
+
return await withTimeout2(createPromise());
|
|
68600
69587
|
} finally {
|
|
68601
69588
|
endWaitingForUser();
|
|
68602
69589
|
}
|
|
@@ -68686,15 +69673,15 @@ function showExtensionNotification(host, message, type = "info") {
|
|
|
68686
69673
|
host.ui.requestRender();
|
|
68687
69674
|
}
|
|
68688
69675
|
function showExtensionSelector2(host, title, options, opts) {
|
|
68689
|
-
return new Promise((
|
|
69676
|
+
return new Promise((resolve7) => {
|
|
68690
69677
|
host.showStatus(`Waiting for selection: ${title}`);
|
|
68691
69678
|
if (opts?.signal?.aborted) {
|
|
68692
|
-
|
|
69679
|
+
resolve7(void 0);
|
|
68693
69680
|
return;
|
|
68694
69681
|
}
|
|
68695
69682
|
const onAbort = () => {
|
|
68696
69683
|
host.restoreEditor();
|
|
68697
|
-
|
|
69684
|
+
resolve7(void 0);
|
|
68698
69685
|
};
|
|
68699
69686
|
opts?.signal?.addEventListener("abort", onAbort, { once: true });
|
|
68700
69687
|
const selector = new ExtensionSelectorComponent(
|
|
@@ -68703,12 +69690,12 @@ function showExtensionSelector2(host, title, options, opts) {
|
|
|
68703
69690
|
(option) => {
|
|
68704
69691
|
opts?.signal?.removeEventListener("abort", onAbort);
|
|
68705
69692
|
host.restoreEditor();
|
|
68706
|
-
|
|
69693
|
+
resolve7(option);
|
|
68707
69694
|
},
|
|
68708
69695
|
() => {
|
|
68709
69696
|
opts?.signal?.removeEventListener("abort", onAbort);
|
|
68710
69697
|
host.restoreEditor();
|
|
68711
|
-
|
|
69698
|
+
resolve7(void 0);
|
|
68712
69699
|
}
|
|
68713
69700
|
);
|
|
68714
69701
|
host.editorContainer.clear();
|
|
@@ -68724,15 +69711,15 @@ ${message}`, ["Yes", "No"], opts);
|
|
|
68724
69711
|
return result === "Yes";
|
|
68725
69712
|
}
|
|
68726
69713
|
function showExtensionInput(host, title, placeholder, opts) {
|
|
68727
|
-
return new Promise((
|
|
69714
|
+
return new Promise((resolve7) => {
|
|
68728
69715
|
host.showStatus(`Waiting for input: ${title}`);
|
|
68729
69716
|
if (opts?.signal?.aborted) {
|
|
68730
|
-
|
|
69717
|
+
resolve7(void 0);
|
|
68731
69718
|
return;
|
|
68732
69719
|
}
|
|
68733
69720
|
const onAbort = () => {
|
|
68734
69721
|
host.restoreEditor();
|
|
68735
|
-
|
|
69722
|
+
resolve7(void 0);
|
|
68736
69723
|
};
|
|
68737
69724
|
opts?.signal?.addEventListener("abort", onAbort, { once: true });
|
|
68738
69725
|
const input = new ExtensionInputComponent(
|
|
@@ -68741,12 +69728,12 @@ function showExtensionInput(host, title, placeholder, opts) {
|
|
|
68741
69728
|
(value) => {
|
|
68742
69729
|
opts?.signal?.removeEventListener("abort", onAbort);
|
|
68743
69730
|
host.restoreEditor();
|
|
68744
|
-
|
|
69731
|
+
resolve7(value);
|
|
68745
69732
|
},
|
|
68746
69733
|
() => {
|
|
68747
69734
|
opts?.signal?.removeEventListener("abort", onAbort);
|
|
68748
69735
|
host.restoreEditor();
|
|
68749
|
-
|
|
69736
|
+
resolve7(void 0);
|
|
68750
69737
|
}
|
|
68751
69738
|
);
|
|
68752
69739
|
host.editorContainer.clear();
|
|
@@ -68757,7 +69744,7 @@ function showExtensionInput(host, title, placeholder, opts) {
|
|
|
68757
69744
|
});
|
|
68758
69745
|
}
|
|
68759
69746
|
function showExtensionEditor(host, title, prefill) {
|
|
68760
|
-
return new Promise((
|
|
69747
|
+
return new Promise((resolve7) => {
|
|
68761
69748
|
host.showStatus(`Waiting for input: ${title}`);
|
|
68762
69749
|
const container = new Container();
|
|
68763
69750
|
container.addChild(new Text(theme.bold(theme.fg("accent", title)), 0, 0));
|
|
@@ -68779,12 +69766,12 @@ function showExtensionEditor(host, title, prefill) {
|
|
|
68779
69766
|
preview: value.slice(0, 120)
|
|
68780
69767
|
});
|
|
68781
69768
|
host.restoreEditor();
|
|
68782
|
-
|
|
69769
|
+
resolve7(value);
|
|
68783
69770
|
};
|
|
68784
69771
|
editor.onEscape = () => {
|
|
68785
69772
|
host.logDebug("extension:editor:cancel", { title });
|
|
68786
69773
|
host.restoreEditor();
|
|
68787
|
-
|
|
69774
|
+
resolve7(void 0);
|
|
68788
69775
|
};
|
|
68789
69776
|
container.addChild(editor);
|
|
68790
69777
|
container.addChild(new Spacer(1));
|
|
@@ -68798,14 +69785,14 @@ function showExtensionEditor(host, title, prefill) {
|
|
|
68798
69785
|
}
|
|
68799
69786
|
function showExtensionCustom(host, factory, options) {
|
|
68800
69787
|
const savedText = host.editor.getText();
|
|
68801
|
-
return new Promise((
|
|
69788
|
+
return new Promise((resolve7, reject) => {
|
|
68802
69789
|
let closed = false;
|
|
68803
69790
|
const close = (result) => {
|
|
68804
69791
|
if (closed) return;
|
|
68805
69792
|
closed = true;
|
|
68806
69793
|
host.restoreEditor();
|
|
68807
69794
|
host.editor.setText(savedText);
|
|
68808
|
-
|
|
69795
|
+
resolve7(result);
|
|
68809
69796
|
};
|
|
68810
69797
|
Promise.resolve(factory(host.ui, theme, close)).then((component) => {
|
|
68811
69798
|
if (closed) return;
|
|
@@ -68826,6 +69813,60 @@ function showExtensionCustom(host, factory, options) {
|
|
|
68826
69813
|
});
|
|
68827
69814
|
}
|
|
68828
69815
|
|
|
69816
|
+
// src/extensions/provider-login.ts
|
|
69817
|
+
var inFlightLogins = /* @__PURE__ */ new Map();
|
|
69818
|
+
async function loginExtensionProvider(options) {
|
|
69819
|
+
const { provider } = options;
|
|
69820
|
+
const existing = inFlightLogins.get(provider);
|
|
69821
|
+
if (existing) return existing;
|
|
69822
|
+
const attempt = runExtensionProviderLogin(options).finally(() => {
|
|
69823
|
+
if (inFlightLogins.get(provider) === attempt) inFlightLogins.delete(provider);
|
|
69824
|
+
});
|
|
69825
|
+
inFlightLogins.set(provider, attempt);
|
|
69826
|
+
return attempt;
|
|
69827
|
+
}
|
|
69828
|
+
async function runExtensionProviderLogin(options) {
|
|
69829
|
+
const { provider, modelRegistry, authStorage, requestInteraction: requestInteraction2 } = options;
|
|
69830
|
+
const oauth = modelRegistry.getExtensionOAuth(provider);
|
|
69831
|
+
if (!oauth) return false;
|
|
69832
|
+
const credentials = await oauth.login({
|
|
69833
|
+
onAuth: ({ url }) => {
|
|
69834
|
+
void requestInteraction2({
|
|
69835
|
+
type: "notify",
|
|
69836
|
+
message: `${oauth.name}: open this URL to sign in:
|
|
69837
|
+
${url}`,
|
|
69838
|
+
level: "info"
|
|
69839
|
+
});
|
|
69840
|
+
},
|
|
69841
|
+
onPrompt: async ({ message, placeholder }) => {
|
|
69842
|
+
const response = await requestInteraction2({
|
|
69843
|
+
type: "text_input",
|
|
69844
|
+
id: `oauth:${provider}`,
|
|
69845
|
+
prompt: `${oauth.name}: ${message}`,
|
|
69846
|
+
placeholder
|
|
69847
|
+
});
|
|
69848
|
+
if (response.type !== "text_input" || response.timedOut || response.skipped || response.value === void 0) {
|
|
69849
|
+
throw new Error(`${oauth.name} login cancelled.`);
|
|
69850
|
+
}
|
|
69851
|
+
return response.value;
|
|
69852
|
+
}
|
|
69853
|
+
});
|
|
69854
|
+
authStorage.setOAuthCredentials(provider, credentials);
|
|
69855
|
+
authStorage.setRuntimeApiKey(provider, credentials.access);
|
|
69856
|
+
const errors = authStorage.drainErrors();
|
|
69857
|
+
if (errors.length > 0) {
|
|
69858
|
+
throw new Error(`Failed to persist ${oauth.name} credentials: ${errors[0].message}`);
|
|
69859
|
+
}
|
|
69860
|
+
return true;
|
|
69861
|
+
}
|
|
69862
|
+
async function restoreExtensionProviderCredentials(modelRegistry) {
|
|
69863
|
+
for (const result of await modelRegistry.restorePersistedOAuth()) {
|
|
69864
|
+
if (process.env.PIE_VERBOSE) {
|
|
69865
|
+
console.error(`[Extension] OAuth restore ${result.provider}: ${result.action}${result.detail ? ` (${result.detail})` : ""}`);
|
|
69866
|
+
}
|
|
69867
|
+
}
|
|
69868
|
+
}
|
|
69869
|
+
|
|
68829
69870
|
// src/attachments/resolver.ts
|
|
68830
69871
|
var AttachmentResolver = class {
|
|
68831
69872
|
constructor(extensions) {
|
|
@@ -68989,6 +70030,26 @@ async function loadInteractiveExtensions(host) {
|
|
|
68989
70030
|
activeSession.metadata[key] = value;
|
|
68990
70031
|
activeSession.isDirty = true;
|
|
68991
70032
|
},
|
|
70033
|
+
appendEntry: (customType, data) => host.sessionManager.appendCustomEntry(customType, data).id,
|
|
70034
|
+
setLabel: (entryId, label) => host.sessionManager.setLabel(entryId, label),
|
|
70035
|
+
setSessionName: (name) => host.sessionManager.setSessionName(name),
|
|
70036
|
+
getSessionName: () => host.sessionManager.getSessionName(),
|
|
70037
|
+
registerProvider: (name, config) => {
|
|
70038
|
+
const unregister = host.options.modelRegistry.registerProvider(name, config);
|
|
70039
|
+
host.noteRuntimeModeChanged("extension_register_provider");
|
|
70040
|
+
return unregister;
|
|
70041
|
+
},
|
|
70042
|
+
loginProvider: async (name) => {
|
|
70043
|
+
const loggedIn = await loginExtensionProvider({
|
|
70044
|
+
provider: name,
|
|
70045
|
+
modelRegistry: host.options.modelRegistry,
|
|
70046
|
+
authStorage: host.options.modelRegistry.getAuthStorage(),
|
|
70047
|
+
requestInteraction
|
|
70048
|
+
});
|
|
70049
|
+
if (loggedIn) host.noteRuntimeModeChanged("extension_login_provider");
|
|
70050
|
+
return loggedIn;
|
|
70051
|
+
},
|
|
70052
|
+
reload: () => host.reloadRuntime(),
|
|
68992
70053
|
get yoloMode() {
|
|
68993
70054
|
return host.yoloMode;
|
|
68994
70055
|
}
|
|
@@ -69016,9 +70077,10 @@ async function loadInteractiveExtensions(host) {
|
|
|
69016
70077
|
true
|
|
69017
70078
|
);
|
|
69018
70079
|
host.extensionRunner.setUIContext(host.extensionUIContext);
|
|
70080
|
+
await restoreExtensionProviderCredentials(host.options.modelRegistry);
|
|
69019
70081
|
if (process.env.PIE_VERBOSE) {
|
|
69020
|
-
for (const { path:
|
|
69021
|
-
console.error(`[Extension] Failed to load ${
|
|
70082
|
+
for (const { path: path33, error } of errors) {
|
|
70083
|
+
console.error(`[Extension] Failed to load ${path33}: ${error}`);
|
|
69022
70084
|
}
|
|
69023
70085
|
}
|
|
69024
70086
|
const extensionTools = convertExtensionTools(extensions);
|
|
@@ -69072,6 +70134,8 @@ var InteractiveMode = class {
|
|
|
69072
70134
|
editorContainer;
|
|
69073
70135
|
todoWidgetContainer;
|
|
69074
70136
|
todoWidget;
|
|
70137
|
+
extensionWidgetsContainer;
|
|
70138
|
+
extensionWidgets;
|
|
69075
70139
|
executionState = new ExecutionStateManager();
|
|
69076
70140
|
sessionTrace;
|
|
69077
70141
|
keybindings;
|
|
@@ -69136,10 +70200,24 @@ var InteractiveMode = class {
|
|
|
69136
70200
|
lastStatusText;
|
|
69137
70201
|
lastSigintTime = 0;
|
|
69138
70202
|
continuationTimeoutHandle;
|
|
70203
|
+
continuationStallSinceMs;
|
|
69139
70204
|
activeInteractionCount = 0;
|
|
69140
70205
|
logDebug(message, data) {
|
|
69141
70206
|
fileLogger.log("interactive", message, data);
|
|
69142
70207
|
}
|
|
70208
|
+
/** Streams and tool updates prove the turn is alive; restart the no-progress clock. */
|
|
70209
|
+
noteContinuationActivity() {
|
|
70210
|
+
this.continuationStallSinceMs = void 0;
|
|
70211
|
+
}
|
|
70212
|
+
/**
|
|
70213
|
+
* Ring the terminal bell (BEL) when a turn needs the user's attention:
|
|
70214
|
+
* interaction prompts, turn completion/failure, and stall recovery. The
|
|
70215
|
+
* bell itself enforces a cross-call-site cooldown; the settings gate here
|
|
70216
|
+
* is the single opt-out point (settings.json `notifications.bell`).
|
|
70217
|
+
*/
|
|
70218
|
+
ringAttentionBell() {
|
|
70219
|
+
ringTerminalBell({ enabled: this.options.settingsManager.getBellNotificationsEnabled() });
|
|
70220
|
+
}
|
|
69143
70221
|
getContinuationTimeoutMs() {
|
|
69144
70222
|
return getContinuationTimeoutMs();
|
|
69145
70223
|
}
|
|
@@ -69382,7 +70460,7 @@ var InteractiveMode = class {
|
|
|
69382
70460
|
if (savedLevel) {
|
|
69383
70461
|
this.thinkingLevel = savedLevel;
|
|
69384
70462
|
}
|
|
69385
|
-
const { Agent } = await import("./chunks/src-
|
|
70463
|
+
const { Agent } = await import("./chunks/src-TQNFXZGK.js");
|
|
69386
70464
|
const activeSession = this.sessionManager.getActiveSession();
|
|
69387
70465
|
this.agent = new Agent({
|
|
69388
70466
|
initialState: {
|
|
@@ -69473,6 +70551,40 @@ var InteractiveMode = class {
|
|
|
69473
70551
|
installInteractiveInteractionHandler(this);
|
|
69474
70552
|
await loadInteractiveExtensions(this.extensionLoaderHost);
|
|
69475
70553
|
}
|
|
70554
|
+
/**
|
|
70555
|
+
* Full runtime reload (extension API `ctx.reload()`): re-discover skills
|
|
70556
|
+
* and extensions from disk and rebuild the tool surface and system prompt.
|
|
70557
|
+
* Rejects while a turn is processing. Listener lifecycle contract: the old
|
|
70558
|
+
* extension instances are dropped entirely (handlers, tools, commands),
|
|
70559
|
+
* the tool surface is reset to the pristine composition baseline, and the
|
|
70560
|
+
* fresh ExtensionRunner re-wraps from that baseline — no stale handlers
|
|
70561
|
+
* and no double-wrapped tools survive a reload.
|
|
70562
|
+
*/
|
|
70563
|
+
async reloadRuntime() {
|
|
70564
|
+
if (this.isProcessing) {
|
|
70565
|
+
throw new Error("Cannot reload while a response is being processed. Wait for the turn to finish or abort it first.");
|
|
70566
|
+
}
|
|
70567
|
+
const builtinDir = path29.join(getBuiltinDir(), "skills");
|
|
70568
|
+
const userDir = path29.join(os8.homedir(), ".pie", "skills");
|
|
70569
|
+
const projectDir = path29.join(this.options.cwd, ".pie", "skills");
|
|
70570
|
+
const { skills, diagnostics } = loadSkills({
|
|
70571
|
+
dirs: buildSkillDirs({ builtinDir, userDir, projectDir, cwd: this.options.cwd })
|
|
70572
|
+
});
|
|
70573
|
+
this.skills = skills;
|
|
70574
|
+
this.skillsSection = formatSkillsForPrompt(skills);
|
|
70575
|
+
for (const diag of diagnostics.filter((entry) => entry.type === "collision")) {
|
|
70576
|
+
this.chatContainer.addChild(new Text(theme.fg("dim", `[reload] ${diag.message}`), 1, 0));
|
|
70577
|
+
}
|
|
70578
|
+
this.allTools = [...this.options.allTools];
|
|
70579
|
+
this.originalTools = [...this.options.allTools];
|
|
70580
|
+
this.agent.state.tools = [...this.options.allTools];
|
|
70581
|
+
this.extensionRunner?.revokeRegistrations();
|
|
70582
|
+
this.extensionWidgets?.clearAll();
|
|
70583
|
+
await this.loadExtensions();
|
|
70584
|
+
this.applyRuntimeToolMode();
|
|
70585
|
+
this.noteRuntimeModeChanged("extension_reload");
|
|
70586
|
+
this.ui.requestRender();
|
|
70587
|
+
}
|
|
69476
70588
|
calculateContextUsage() {
|
|
69477
70589
|
return calculateContextUsage(this.contextUiHost);
|
|
69478
70590
|
}
|
|
@@ -69721,8 +70833,8 @@ var InteractiveMode = class {
|
|
|
69721
70833
|
};
|
|
69722
70834
|
|
|
69723
70835
|
// src/app/runtime-composition.ts
|
|
69724
|
-
import { existsSync as
|
|
69725
|
-
import * as
|
|
70836
|
+
import { existsSync as existsSync18, mkdirSync as mkdirSync13 } from "fs";
|
|
70837
|
+
import * as path30 from "path";
|
|
69726
70838
|
var CliMissingApiKeyError = class extends Error {
|
|
69727
70839
|
constructor(message) {
|
|
69728
70840
|
super(message);
|
|
@@ -69743,13 +70855,13 @@ var CliSessionResumeError = class extends Error {
|
|
|
69743
70855
|
function getCliRuntimePaths() {
|
|
69744
70856
|
const configDir = getConfigDir();
|
|
69745
70857
|
const sessionsDir = getSessionsDir();
|
|
69746
|
-
const skillsDir =
|
|
69747
|
-
const builtinSkillsDir =
|
|
70858
|
+
const skillsDir = path30.join(configDir, "skills");
|
|
70859
|
+
const builtinSkillsDir = path30.join(getBuiltinDir(), "skills");
|
|
69748
70860
|
return { configDir, sessionsDir, skillsDir, builtinSkillsDir };
|
|
69749
70861
|
}
|
|
69750
70862
|
function ensureCliRuntimePaths(paths = getCliRuntimePaths()) {
|
|
69751
|
-
if (!
|
|
69752
|
-
|
|
70863
|
+
if (!existsSync18(paths.skillsDir)) {
|
|
70864
|
+
mkdirSync13(paths.skillsDir, { recursive: true });
|
|
69753
70865
|
}
|
|
69754
70866
|
return paths;
|
|
69755
70867
|
}
|
|
@@ -69775,7 +70887,7 @@ async function composeCliRuntime(params) {
|
|
|
69775
70887
|
const paths = ensureCliRuntimePaths(params.paths);
|
|
69776
70888
|
const compositionTrace = createCliCompositionTrace();
|
|
69777
70889
|
const settingsManager = new SettingsManager();
|
|
69778
|
-
const authStorage = new
|
|
70890
|
+
const authStorage = AuthStorage.fromStorage(new FileAuthStorageBackend());
|
|
69779
70891
|
authStorage.setLegacyApiKey(settingsManager.getLegacyApiKey());
|
|
69780
70892
|
const modelRegistry = new ModelRegistry(authStorage);
|
|
69781
70893
|
await modelRegistry.refreshRemoteCatalogs();
|
|
@@ -69861,11 +70973,14 @@ async function composeCliRuntime(params) {
|
|
|
69861
70973
|
fileLogger.log("cli", `Using API key for provider ${provider}`, {
|
|
69862
70974
|
keyPrefix: apiKey ? maskApiKey2(apiKey) : "none"
|
|
69863
70975
|
});
|
|
69864
|
-
const projectSkillsDir =
|
|
70976
|
+
const projectSkillsDir = path30.join(cwd, ".pie", "skills");
|
|
69865
70977
|
const { skills, diagnostics } = loadSkills({
|
|
69866
|
-
|
|
69867
|
-
|
|
69868
|
-
|
|
70978
|
+
dirs: buildSkillDirs({
|
|
70979
|
+
builtinDir: paths.builtinSkillsDir,
|
|
70980
|
+
userDir: paths.skillsDir,
|
|
70981
|
+
projectDir: projectSkillsDir,
|
|
70982
|
+
cwd
|
|
70983
|
+
})
|
|
69869
70984
|
});
|
|
69870
70985
|
let interactiveOptions;
|
|
69871
70986
|
const getCurrentModel = () => interactiveOptions?.model ?? model;
|
|
@@ -70056,6 +71171,17 @@ async function loadPrintModeExtensions(params) {
|
|
|
70056
71171
|
session.metadata[key] = value;
|
|
70057
71172
|
session.isDirty = true;
|
|
70058
71173
|
},
|
|
71174
|
+
appendEntry: (customType, data) => params.sessionManager.appendCustomEntry(customType, data).id,
|
|
71175
|
+
setLabel: (entryId, label) => params.sessionManager.setLabel(entryId, label),
|
|
71176
|
+
setSessionName: (name) => params.sessionManager.setSessionName(name),
|
|
71177
|
+
getSessionName: () => params.sessionManager.getSessionName(),
|
|
71178
|
+
registerProvider: (name, config) => params.modelRegistry.registerProvider(name, config),
|
|
71179
|
+
loginProvider: (name) => loginExtensionProvider({
|
|
71180
|
+
provider: name,
|
|
71181
|
+
modelRegistry: params.modelRegistry,
|
|
71182
|
+
authStorage: params.modelRegistry.getAuthStorage(),
|
|
71183
|
+
requestInteraction
|
|
71184
|
+
}),
|
|
70059
71185
|
yoloMode: false
|
|
70060
71186
|
};
|
|
70061
71187
|
const { extensions } = await loadExtensions(
|
|
@@ -70078,6 +71204,7 @@ async function loadPrintModeExtensions(params) {
|
|
|
70078
71204
|
false
|
|
70079
71205
|
);
|
|
70080
71206
|
runner.setUIContext(uiContext);
|
|
71207
|
+
await restoreExtensionProviderCredentials(params.modelRegistry);
|
|
70081
71208
|
const extensionTools = convertExtensionTools(extensions);
|
|
70082
71209
|
availableTools = [...availableTools, ...extensionTools];
|
|
70083
71210
|
return {
|
|
@@ -70103,7 +71230,7 @@ async function runPrintMode(params) {
|
|
|
70103
71230
|
`);
|
|
70104
71231
|
console.log("Assistant: ");
|
|
70105
71232
|
}
|
|
70106
|
-
const { Agent: AgentClass } = await import("./chunks/src-
|
|
71233
|
+
const { Agent: AgentClass } = await import("./chunks/src-TQNFXZGK.js");
|
|
70107
71234
|
let agent;
|
|
70108
71235
|
const printModeExtensions = await loadPrintModeExtensions({
|
|
70109
71236
|
cwd: runtime.cwd,
|
|
@@ -70119,6 +71246,8 @@ async function runPrintMode(params) {
|
|
|
70119
71246
|
allTools: runtime.allTools
|
|
70120
71247
|
});
|
|
70121
71248
|
const printModeTools = [...runtime.tools, ...printModeExtensions.extensionTools];
|
|
71249
|
+
const restoredApiKey = runtime.modelRegistry.getApiKey(runtime.model);
|
|
71250
|
+
if (restoredApiKey) runtime.apiKey = restoredApiKey;
|
|
70122
71251
|
recordCliCompositionStep(runtime.compositionTrace, {
|
|
70123
71252
|
id: "extensions_tool_wrapping",
|
|
70124
71253
|
inputSummary: `baseTools=${runtime.tools.length} extensionPaths=${runtime.extensionPaths.length}`,
|
|
@@ -70182,12 +71311,26 @@ async function runPrintMode(params) {
|
|
|
70182
71311
|
isContextOverflowError: isCliContextOverflowError
|
|
70183
71312
|
}),
|
|
70184
71313
|
compaction: {
|
|
70185
|
-
compact: async (context) =>
|
|
70186
|
-
|
|
70187
|
-
|
|
70188
|
-
|
|
70189
|
-
|
|
70190
|
-
|
|
71314
|
+
compact: async (context) => {
|
|
71315
|
+
const beforeCompact = await printModeExtensions.runner.emitSessionBeforeCompact({
|
|
71316
|
+
type: "session:before_compact",
|
|
71317
|
+
reason: context.reason === "overflow" ? "overflow" : "threshold",
|
|
71318
|
+
messageCount: context.messages.length
|
|
71319
|
+
});
|
|
71320
|
+
if (beforeCompact.cancel) return { aborted: true };
|
|
71321
|
+
if (beforeCompact.summary) {
|
|
71322
|
+
const activeEntryId = context.sessionManager.getActiveEntryId();
|
|
71323
|
+
const firstKeptEntryId = activeEntryId ? findFirstKeptEntryId(context.sessionManager.getPathToEntry(activeEntryId)) : void 0;
|
|
71324
|
+
if (!firstKeptEntryId) return { aborted: true };
|
|
71325
|
+
return { summary: beforeCompact.summary, firstKeptEntryId };
|
|
71326
|
+
}
|
|
71327
|
+
return createCliAutoCompactionResult({
|
|
71328
|
+
context,
|
|
71329
|
+
model: runtime.model,
|
|
71330
|
+
apiKey: runtime.apiKey,
|
|
71331
|
+
systemPrompt: agent?.state.systemPrompt ?? baseSystemPrompt
|
|
71332
|
+
});
|
|
71333
|
+
}
|
|
70191
71334
|
},
|
|
70192
71335
|
classifyFailure: (context) => classifyCliFailure(context),
|
|
70193
71336
|
onEvent: (event) => {
|
|
@@ -70509,6 +71652,7 @@ async function loadJsonModeExtensions(params) {
|
|
|
70509
71652
|
false
|
|
70510
71653
|
);
|
|
70511
71654
|
runner.setUIContext(uiContext);
|
|
71655
|
+
await restoreExtensionProviderCredentials(params.runtime.modelRegistry);
|
|
70512
71656
|
const extensionTools = convertExtensionTools(extensions);
|
|
70513
71657
|
availableTools = [...availableTools, ...extensionTools];
|
|
70514
71658
|
return {
|
|
@@ -70542,7 +71686,7 @@ async function runJsonEventsMode(params) {
|
|
|
70542
71686
|
return typeof message.sessionId === "string" && message.sessionId !== sessionId;
|
|
70543
71687
|
}
|
|
70544
71688
|
function delay(ms) {
|
|
70545
|
-
return new Promise((
|
|
71689
|
+
return new Promise((resolve7) => setTimeout(resolve7, Math.max(0, ms)));
|
|
70546
71690
|
}
|
|
70547
71691
|
async function waitForActiveController(timeoutMs = 1e3) {
|
|
70548
71692
|
const deadline = Date.now() + timeoutMs;
|
|
@@ -71053,9 +72197,11 @@ async function runJsonEventsMode(params) {
|
|
|
71053
72197
|
if (runtime.initialModel.unconfigured || controller) {
|
|
71054
72198
|
return;
|
|
71055
72199
|
}
|
|
71056
|
-
const { Agent: AgentClass } = await import("./chunks/src-
|
|
72200
|
+
const { Agent: AgentClass } = await import("./chunks/src-TQNFXZGK.js");
|
|
71057
72201
|
const jsonModeExtensions = await loadJsonModeExtensions({ runtime, getAgent: () => agent });
|
|
71058
72202
|
const tools = [...runtime.tools, ...jsonModeExtensions.extensionTools];
|
|
72203
|
+
const restoredApiKey = runtime.modelRegistry.getApiKey(runtime.model);
|
|
72204
|
+
if (restoredApiKey) runtime.apiKey = restoredApiKey;
|
|
71059
72205
|
recordCliCompositionStep(runtime.compositionTrace, {
|
|
71060
72206
|
id: "extensions_tool_wrapping",
|
|
71061
72207
|
inputSummary: `baseTools=${runtime.tools.length} extensionPaths=${runtime.extensionPaths.length}`,
|
|
@@ -71487,19 +72633,19 @@ async function runJsonEventsMode(params) {
|
|
|
71487
72633
|
}
|
|
71488
72634
|
|
|
71489
72635
|
// src/developer-instructions.ts
|
|
71490
|
-
import * as
|
|
71491
|
-
import * as
|
|
72636
|
+
import * as fs27 from "fs";
|
|
72637
|
+
import * as path31 from "path";
|
|
71492
72638
|
var MAX_DEVELOPER_INSTRUCTIONS_CHARS = 64e3;
|
|
71493
72639
|
function readTextFileBounded(filePath) {
|
|
71494
|
-
const resolved =
|
|
71495
|
-
const stat2 =
|
|
72640
|
+
const resolved = path31.resolve(filePath);
|
|
72641
|
+
const stat2 = fs27.statSync(resolved);
|
|
71496
72642
|
if (!stat2.isFile()) {
|
|
71497
72643
|
throw new Error(`Developer instructions file is not a regular file: ${filePath}`);
|
|
71498
72644
|
}
|
|
71499
72645
|
if (stat2.size > MAX_DEVELOPER_INSTRUCTIONS_CHARS * 4) {
|
|
71500
72646
|
throw new Error(`Developer instructions file is too large: ${filePath}`);
|
|
71501
72647
|
}
|
|
71502
|
-
return
|
|
72648
|
+
return fs27.readFileSync(resolved, "utf8");
|
|
71503
72649
|
}
|
|
71504
72650
|
function collectFlagValues(args, flag) {
|
|
71505
72651
|
const values = [];
|
|
@@ -71577,8 +72723,8 @@ var logError = (msg, err) => {
|
|
|
71577
72723
|
const logPath = getRuntimeLogPath();
|
|
71578
72724
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
71579
72725
|
const stack = err instanceof Error ? err.stack || "" : "";
|
|
71580
|
-
|
|
71581
|
-
|
|
72726
|
+
fs28.mkdirSync(path32.dirname(logPath), { recursive: true });
|
|
72727
|
+
fs28.appendFileSync(logPath, `[${timestamp}] ${msg}
|
|
71582
72728
|
${stack}
|
|
71583
72729
|
|
|
71584
72730
|
`);
|
|
@@ -71685,7 +72831,7 @@ async function runWithJsonEventsConsoleIsolation(fn) {
|
|
|
71685
72831
|
}
|
|
71686
72832
|
async function createCliTestStreamFnFromEnv() {
|
|
71687
72833
|
if (!process.env.PIE_TEST_MOCK_STREAM_SEQUENCE) return void 0;
|
|
71688
|
-
return (await import("./chunks/test-stream-
|
|
72834
|
+
return (await import("./chunks/test-stream-IPBFEEWH.js")).createTestStreamFnFromSequenceEnv();
|
|
71689
72835
|
}
|
|
71690
72836
|
async function startChat(initialPrompt, testCommand) {
|
|
71691
72837
|
const rawOutput = process.argv.includes("--raw-output");
|
|
@@ -71780,9 +72926,9 @@ async function startChat(initialPrompt, testCommand) {
|
|
|
71780
72926
|
flushEarlyLogs();
|
|
71781
72927
|
if (testCommand) {
|
|
71782
72928
|
console.log(`[TEST] Auto-executing command: ${testCommand}`);
|
|
71783
|
-
await new Promise((
|
|
72929
|
+
await new Promise((resolve7) => setTimeout(resolve7, 500));
|
|
71784
72930
|
await interactive.executeCommand(testCommand);
|
|
71785
|
-
await new Promise((
|
|
72931
|
+
await new Promise((resolve7) => setTimeout(resolve7, 2e3));
|
|
71786
72932
|
console.log("[TEST] Command executed, exiting...");
|
|
71787
72933
|
process.exit(0);
|
|
71788
72934
|
return;
|