pi-crew 0.9.26 → 0.9.27
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +87 -0
- package/NOTICE.md +14 -0
- package/assets/crew-vibes.ttf +0 -0
- package/assets/runner-spritesheet.png +0 -0
- package/dist/build-meta.json +255 -40
- package/dist/index.mjs +1564 -241
- package/dist/index.mjs.map +4 -4
- package/docs/perf/optimization-plan-2026-07-verified.md +396 -0
- package/package.json +9 -2
- package/scripts/bench-check.mjs +96 -0
- package/scripts/bench-cold-start.mjs +216 -0
- package/scripts/build-bundle.mjs +84 -0
- package/scripts/build-crew-vibes-font.py +328 -0
- package/scripts/check-all-skills.ts +294 -0
- package/scripts/check-bundle-staleness.mjs +97 -0
- package/scripts/check-conflict-markers.mjs +91 -0
- package/scripts/check-lazy-imports.mjs +28 -0
- package/scripts/install-crew-vibes-font.mjs +91 -0
- package/scripts/postinstall.mjs +47 -0
- package/scripts/profile-startup.mjs +125 -0
- package/scripts/release-smoke.mjs +74 -0
- package/scripts/run-bench.mjs +47 -0
- package/scripts/run-real-chain.ts +41 -0
- package/scripts/test-issue-29-crash.ts +111 -0
- package/scripts/test-issue-29-e2e.ts +183 -0
- package/scripts/test-issue-29-real-runtime.ts +330 -0
- package/scripts/test-issue-29-real-tasks.ts +387 -0
- package/scripts/test-issue-29-team-tool.ts +105 -0
- package/scripts/test-runner.mjs +74 -0
- package/scripts/verify-flicker-fix.ts +109 -0
- package/scripts/verify-skill.ts +550 -0
- package/scripts/watch-bundle.mjs +259 -0
- package/scripts/watchdog-harness.ts +119 -0
- package/src/agents/discover-agents.ts +6 -1
- package/src/config/defaults.ts +6 -0
- package/src/extension/crew-vibes/cat-frames.ts +18 -0
- package/src/extension/crew-vibes/config.ts +195 -0
- package/src/extension/crew-vibes/figures.ts +94 -0
- package/src/extension/crew-vibes/font-detect.ts +58 -0
- package/src/extension/crew-vibes/index.ts +396 -0
- package/src/extension/crew-vibes/provider-usage.ts +330 -0
- package/src/extension/crew-vibes/render.ts +206 -0
- package/src/extension/crew-vibes/speed.ts +286 -0
- package/src/extension/knowledge-injection.ts +12 -3
- package/src/extension/management.ts +23 -3
- package/src/extension/register.ts +7 -0
- package/src/runtime/child-pi.ts +117 -10
- package/src/runtime/crew-agent-records.ts +10 -3
- package/src/runtime/retry-executor.ts +4 -1
- package/src/runtime/task-runner/state-helpers.ts +9 -30
- package/src/runtime/team-runner.ts +5 -3
- package/src/state/atomic-write.ts +153 -49
- package/src/state/event-log.ts +16 -10
- package/src/state/locks.ts +7 -8
- package/src/state/mailbox.ts +15 -4
- package/src/state/state-store.ts +39 -10
- package/src/teams/discover-teams.ts +56 -1
- package/src/utils/safe-paths.ts +2 -1
- package/src/workflows/discover-workflows.ts +72 -1
package/dist/index.mjs
CHANGED
|
@@ -8693,6 +8693,7 @@ __export(atomic_write_exports, {
|
|
|
8693
8693
|
atomicWriteJsonAsync: () => atomicWriteJsonAsync,
|
|
8694
8694
|
atomicWriteJsonCoalesced: () => atomicWriteJsonCoalesced,
|
|
8695
8695
|
flushPendingAtomicWrites: () => flushPendingAtomicWrites,
|
|
8696
|
+
invalidateSymlinkSafeCache: () => invalidateSymlinkSafeCache,
|
|
8696
8697
|
isSymlinkSafePath: () => isSymlinkSafePath,
|
|
8697
8698
|
readJsonFile: () => readJsonFile,
|
|
8698
8699
|
renameWithRetry: () => renameWithRetry,
|
|
@@ -8774,6 +8775,32 @@ function isSymlinkSafePath(filePath) {
|
|
|
8774
8775
|
return false;
|
|
8775
8776
|
}
|
|
8776
8777
|
}
|
|
8778
|
+
function invalidateSymlinkSafeCache(dir) {
|
|
8779
|
+
if (dir) symlinkSafeCache.delete(dir);
|
|
8780
|
+
else symlinkSafeCache.clear();
|
|
8781
|
+
}
|
|
8782
|
+
function isTargetNotSymlink(filePath) {
|
|
8783
|
+
try {
|
|
8784
|
+
if (fs2.lstatSync(filePath).isSymbolicLink()) return false;
|
|
8785
|
+
} catch {
|
|
8786
|
+
}
|
|
8787
|
+
return true;
|
|
8788
|
+
}
|
|
8789
|
+
function isSymlinkSafeDirCached(filePath) {
|
|
8790
|
+
const now = Date.now();
|
|
8791
|
+
const dir = path2.dirname(filePath);
|
|
8792
|
+
if (!isTargetNotSymlink(filePath)) return false;
|
|
8793
|
+
const hit = symlinkSafeCache.get(dir);
|
|
8794
|
+
if (hit && now - hit.at < SYMLINK_SAFE_TTL_MS) return hit.safe;
|
|
8795
|
+
const verdict = isSymlinkSafePath(path2.join(dir, ".symlink-safe-check"));
|
|
8796
|
+
symlinkSafeCache.set(dir, { safe: verdict, at: now });
|
|
8797
|
+
while (symlinkSafeCache.size > SYMLINK_SAFE_MAX_ENTRIES) {
|
|
8798
|
+
const oldest = symlinkSafeCache.keys().next().value;
|
|
8799
|
+
if (oldest === void 0) break;
|
|
8800
|
+
symlinkSafeCache.delete(oldest);
|
|
8801
|
+
}
|
|
8802
|
+
return verdict;
|
|
8803
|
+
}
|
|
8777
8804
|
function sleep2(ms) {
|
|
8778
8805
|
return new Promise((resolve21) => setTimeout(resolve21, ms));
|
|
8779
8806
|
}
|
|
@@ -8889,8 +8916,19 @@ async function renameWithRetryAsync(tempPath, filePath, retries = 8, rename = (s
|
|
|
8889
8916
|
}
|
|
8890
8917
|
throw lastError;
|
|
8891
8918
|
}
|
|
8892
|
-
function
|
|
8893
|
-
if (
|
|
8919
|
+
function normalizeOptions(arg) {
|
|
8920
|
+
if (typeof arg === "string") return { expectedHash: arg, durability: "full" };
|
|
8921
|
+
if (arg && typeof arg === "object") {
|
|
8922
|
+
const o = arg;
|
|
8923
|
+
const durability = o.durability === "best-effort" ? "best-effort" : "full";
|
|
8924
|
+
return { expectedHash: o.expectedHash, durability };
|
|
8925
|
+
}
|
|
8926
|
+
return { durability: "full" };
|
|
8927
|
+
}
|
|
8928
|
+
function atomicWriteFile(filePath, content, options) {
|
|
8929
|
+
const { durability } = normalizeOptions(options);
|
|
8930
|
+
if (!isSymlinkSafeDirCached(filePath))
|
|
8931
|
+
throw new Error(`Refusing to write: target is a symlink or inside untrusted directory: ${filePath}`);
|
|
8894
8932
|
const canonicalize = (p) => {
|
|
8895
8933
|
try {
|
|
8896
8934
|
const r = fs2.realpathSync.native(p);
|
|
@@ -8925,14 +8963,14 @@ function atomicWriteFile(filePath, content, expectedHash) {
|
|
|
8925
8963
|
throw new Error(`Refusing to write: opened path is not a regular file: ${tempPath}`);
|
|
8926
8964
|
}
|
|
8927
8965
|
fs2.writeSync(fd, content, void 0, "utf-8");
|
|
8928
|
-
fs2.fsyncSync(fd);
|
|
8966
|
+
if (durability === "full") fs2.fsyncSync(fd);
|
|
8929
8967
|
fs2.closeSync(fd);
|
|
8930
8968
|
try {
|
|
8931
|
-
if (!
|
|
8969
|
+
if (!isSymlinkSafeDirCached(filePath)) {
|
|
8932
8970
|
throw new Error(`Refusing to rename: target became a symlink or inside untrusted directory: ${filePath}`);
|
|
8933
8971
|
}
|
|
8934
8972
|
renameWithLinkSync(tempPath, filePath);
|
|
8935
|
-
if (process.platform !== "win32") {
|
|
8973
|
+
if (durability === "full" && process.platform !== "win32") {
|
|
8936
8974
|
try {
|
|
8937
8975
|
const dirFd = fs2.openSync(path2.dirname(filePath), "r");
|
|
8938
8976
|
fs2.fsyncSync(dirFd);
|
|
@@ -8963,11 +9001,13 @@ function atomicWriteFile(filePath, content, expectedHash) {
|
|
|
8963
9001
|
}
|
|
8964
9002
|
}
|
|
8965
9003
|
}
|
|
8966
|
-
async function atomicWriteFileAsync(filePath, content) {
|
|
9004
|
+
async function atomicWriteFileAsync(filePath, content, options) {
|
|
9005
|
+
const { durability } = normalizeOptions(options);
|
|
8967
9006
|
if (isWorkerAtomicWriterEnabled()) {
|
|
8968
9007
|
return atomicWriteFileViaWorker(filePath, content);
|
|
8969
9008
|
}
|
|
8970
|
-
if (!
|
|
9009
|
+
if (!isSymlinkSafeDirCached(filePath))
|
|
9010
|
+
throw new Error(`Refusing to write: target is a symlink or inside untrusted directory: ${filePath}`);
|
|
8971
9011
|
await fs2.promises.mkdir(path2.dirname(filePath), { recursive: true });
|
|
8972
9012
|
const tempPath = `${filePath}.${crypto.randomUUID()}.tmp`;
|
|
8973
9013
|
try {
|
|
@@ -8979,33 +9019,12 @@ async function atomicWriteFileAsync(filePath, content) {
|
|
|
8979
9019
|
throw new Error(`Refusing to write: opened path is not a regular file: ${tempPath}`);
|
|
8980
9020
|
}
|
|
8981
9021
|
await fd.writeFile(content, "utf-8");
|
|
9022
|
+
if (durability === "full") await fd.sync();
|
|
8982
9023
|
await fd.close();
|
|
8983
|
-
|
|
8984
|
-
|
|
8985
|
-
try {
|
|
8986
|
-
await fs2.promises.rm(tempPath, { force: true });
|
|
8987
|
-
} catch {
|
|
8988
|
-
}
|
|
8989
|
-
throw new Error(`Refusing to rename: target became a symlink or inside untrusted directory: ${filePath}`);
|
|
8990
|
-
}
|
|
8991
|
-
await renameWithLinkAsync(tempPath, filePath);
|
|
8992
|
-
} catch (renameError) {
|
|
8993
|
-
let matches = false;
|
|
8994
|
-
try {
|
|
8995
|
-
const existing = await fs2.promises.readFile(filePath, "utf-8");
|
|
8996
|
-
matches = existing === content;
|
|
8997
|
-
} catch {
|
|
8998
|
-
}
|
|
8999
|
-
if (matches) {
|
|
9000
|
-
try {
|
|
9001
|
-
await fs2.promises.rm(tempPath, { force: true });
|
|
9002
|
-
} catch (cleanupError) {
|
|
9003
|
-
logInternalError("atomic-write.cleanupAsync", cleanupError, `tempPath=${tempPath}`);
|
|
9004
|
-
}
|
|
9005
|
-
return;
|
|
9006
|
-
}
|
|
9007
|
-
throw renameError;
|
|
9024
|
+
if (!isSymlinkSafeDirCached(filePath)) {
|
|
9025
|
+
throw new Error(`Refusing to rename: target became a symlink or inside untrusted directory: ${filePath}`);
|
|
9008
9026
|
}
|
|
9027
|
+
await renameWithLinkAsync(tempPath, filePath);
|
|
9009
9028
|
} catch (error) {
|
|
9010
9029
|
try {
|
|
9011
9030
|
await fs2.promises.rm(tempPath, { force: true });
|
|
@@ -9015,15 +9034,15 @@ async function atomicWriteFileAsync(filePath, content) {
|
|
|
9015
9034
|
throw error;
|
|
9016
9035
|
}
|
|
9017
9036
|
}
|
|
9018
|
-
function atomicWriteJson(filePath, value) {
|
|
9037
|
+
function atomicWriteJson(filePath, value, options) {
|
|
9019
9038
|
atomicWriteFile(filePath, `${JSON.stringify(value, null, 2)}
|
|
9020
|
-
|
|
9039
|
+
`, options);
|
|
9021
9040
|
}
|
|
9022
|
-
async function atomicWriteJsonAsync(filePath, value) {
|
|
9041
|
+
async function atomicWriteJsonAsync(filePath, value, options) {
|
|
9023
9042
|
await atomicWriteFileAsync(filePath, `${JSON.stringify(value, null, 2)}
|
|
9024
|
-
|
|
9043
|
+
`, options);
|
|
9025
9044
|
}
|
|
9026
|
-
function atomicWriteJsonCoalesced(filePath, value, coalesceMs = DEFAULT_ATOMIC_COALESCE_MS) {
|
|
9045
|
+
function atomicWriteJsonCoalesced(filePath, value, coalesceMs = DEFAULT_ATOMIC_COALESCE_MS, options) {
|
|
9027
9046
|
const content = `${JSON.stringify(value, null, 2)}
|
|
9028
9047
|
`;
|
|
9029
9048
|
const previous = pendingAtomicWrites.get(filePath);
|
|
@@ -9031,12 +9050,14 @@ function atomicWriteJsonCoalesced(filePath, value, coalesceMs = DEFAULT_ATOMIC_C
|
|
|
9031
9050
|
const timer = setTimeout(() => flushOnePendingAtomicWrite(filePath), coalesceMs);
|
|
9032
9051
|
timer.unref();
|
|
9033
9052
|
const generation = ++writeGeneration;
|
|
9053
|
+
const normalized = normalizeOptions(options);
|
|
9034
9054
|
pendingAtomicWrites.set(filePath, {
|
|
9035
9055
|
content,
|
|
9036
9056
|
timer,
|
|
9037
9057
|
coalesceMs,
|
|
9038
9058
|
retryCount: 0,
|
|
9039
|
-
generation
|
|
9059
|
+
generation,
|
|
9060
|
+
durability: normalized.durability
|
|
9040
9061
|
});
|
|
9041
9062
|
}
|
|
9042
9063
|
function flushOnePendingAtomicWrite(filePath) {
|
|
@@ -9045,7 +9066,7 @@ function flushOnePendingAtomicWrite(filePath) {
|
|
|
9045
9066
|
const savedGeneration = entry.generation;
|
|
9046
9067
|
clearTimeout(entry.timer);
|
|
9047
9068
|
try {
|
|
9048
|
-
atomicWriteFile(filePath, entry.content);
|
|
9069
|
+
atomicWriteFile(filePath, entry.content, { durability: entry.durability });
|
|
9049
9070
|
if (pendingAtomicWrites.get(filePath)?.generation === savedGeneration) {
|
|
9050
9071
|
pendingAtomicWrites.delete(filePath);
|
|
9051
9072
|
}
|
|
@@ -9090,7 +9111,7 @@ function readJsonFile(filePath) {
|
|
|
9090
9111
|
}
|
|
9091
9112
|
}
|
|
9092
9113
|
}
|
|
9093
|
-
var RETRYABLE_RENAME_CODES, RETRYABLE_LINK_CODES, __test__renameWithRetry, __test__renameWithRetryAsync, MAX_FLUSH_RETRIES, pendingAtomicWrites, DEFAULT_ATOMIC_COALESCE_MS, writeGeneration, flushInProgress;
|
|
9114
|
+
var RETRYABLE_RENAME_CODES, RETRYABLE_LINK_CODES, SYMLINK_SAFE_TTL_MS, SYMLINK_SAFE_MAX_ENTRIES, symlinkSafeCache, __test__renameWithRetry, __test__renameWithRetryAsync, MAX_FLUSH_RETRIES, pendingAtomicWrites, DEFAULT_ATOMIC_COALESCE_MS, writeGeneration, flushInProgress;
|
|
9094
9115
|
var init_atomic_write = __esm({
|
|
9095
9116
|
"src/state/atomic-write.ts"() {
|
|
9096
9117
|
"use strict";
|
|
@@ -9099,6 +9120,9 @@ var init_atomic_write = __esm({
|
|
|
9099
9120
|
init_worker_atomic_writer();
|
|
9100
9121
|
RETRYABLE_RENAME_CODES = /* @__PURE__ */ new Set(["EPERM", "EBUSY", "EACCES"]);
|
|
9101
9122
|
RETRYABLE_LINK_CODES = /* @__PURE__ */ new Set(["EPERM", "EBUSY", "EACCES", "ENOENT"]);
|
|
9123
|
+
SYMLINK_SAFE_TTL_MS = 3e4;
|
|
9124
|
+
SYMLINK_SAFE_MAX_ENTRIES = 128;
|
|
9125
|
+
symlinkSafeCache = /* @__PURE__ */ new Map();
|
|
9102
9126
|
__test__renameWithRetry = renameWithRetry;
|
|
9103
9127
|
__test__renameWithRetryAsync = renameWithRetryAsync;
|
|
9104
9128
|
MAX_FLUSH_RETRIES = 5;
|
|
@@ -9120,6 +9144,7 @@ var init_defaults = __esm({
|
|
|
9120
9144
|
DEFAULT_CHILD_PI = {
|
|
9121
9145
|
postExitStdioGuardMs: 3e3,
|
|
9122
9146
|
finalDrainMs: 5e3,
|
|
9147
|
+
finalDrainQuietMs: 800,
|
|
9123
9148
|
hardKillMs: 3e3,
|
|
9124
9149
|
// Child workers can spend more than a few seconds in provider calls or long-running tools without emitting stdout.
|
|
9125
9150
|
// Keep this as a coarse stuck-worker guard rather than a short per-message latency budget.
|
|
@@ -9325,14 +9350,10 @@ function readLockToken(filePath) {
|
|
|
9325
9350
|
}
|
|
9326
9351
|
}
|
|
9327
9352
|
function timingSafeTokenMatch(a, b) {
|
|
9328
|
-
|
|
9329
|
-
const
|
|
9330
|
-
const
|
|
9331
|
-
|
|
9332
|
-
const safeB = Buffer.alloc(len);
|
|
9333
|
-
bufA.copy(safeA);
|
|
9334
|
-
bufB.copy(safeB);
|
|
9335
|
-
return timingSafeEqual(safeA, safeB);
|
|
9353
|
+
if (a.length !== b.length) return false;
|
|
9354
|
+
const bufA = Buffer.from(a);
|
|
9355
|
+
const bufB = Buffer.from(b);
|
|
9356
|
+
return timingSafeEqual(bufA, bufB);
|
|
9336
9357
|
}
|
|
9337
9358
|
function releaseLock(filePath, token) {
|
|
9338
9359
|
let isSymlink2 = false;
|
|
@@ -11703,9 +11724,6 @@ function resetEventLogMode() {
|
|
|
11703
11724
|
asyncQueues.clear();
|
|
11704
11725
|
}
|
|
11705
11726
|
async function appendEventAsync(eventsPath, event) {
|
|
11706
|
-
if (!TERMINAL_EVENT_TYPES.has(event.type)) {
|
|
11707
|
-
return appendEventBuffered(eventsPath, event);
|
|
11708
|
-
}
|
|
11709
11727
|
const queueKey = eventsPath;
|
|
11710
11728
|
const prev = asyncQueues.get(queueKey) ?? Promise.resolve();
|
|
11711
11729
|
const next = prev.then(async () => {
|
|
@@ -12014,12 +12032,14 @@ function appendEventInsideLock(eventsPath, event) {
|
|
|
12014
12032
|
if (!skippedDueToSize) {
|
|
12015
12033
|
fs8.appendFileSync(eventsPath, `${JSON.stringify(redactSecrets(fullEvent))}
|
|
12016
12034
|
`, "utf-8");
|
|
12017
|
-
|
|
12018
|
-
|
|
12019
|
-
|
|
12020
|
-
|
|
12021
|
-
|
|
12022
|
-
|
|
12035
|
+
if (isTerminal) {
|
|
12036
|
+
const fd = fs8.openSync(eventsPath, "r+");
|
|
12037
|
+
try {
|
|
12038
|
+
fs8.fsyncSync(fd);
|
|
12039
|
+
} catch {
|
|
12040
|
+
} finally {
|
|
12041
|
+
fs8.closeSync(fd);
|
|
12042
|
+
}
|
|
12023
12043
|
}
|
|
12024
12044
|
persistSequence(eventsPath, seq);
|
|
12025
12045
|
try {
|
|
@@ -12212,6 +12232,20 @@ var init_event_log = __esm({
|
|
|
12212
12232
|
}
|
|
12213
12233
|
asyncQueues.clear();
|
|
12214
12234
|
});
|
|
12235
|
+
process.on("beforeExit", async () => {
|
|
12236
|
+
if (bufferedQueues.size > 0) {
|
|
12237
|
+
try {
|
|
12238
|
+
await flushEventLogBuffer();
|
|
12239
|
+
} catch {
|
|
12240
|
+
}
|
|
12241
|
+
}
|
|
12242
|
+
if (asyncQueues.size > 0) {
|
|
12243
|
+
try {
|
|
12244
|
+
await drainAsyncQueues();
|
|
12245
|
+
} catch {
|
|
12246
|
+
}
|
|
12247
|
+
}
|
|
12248
|
+
});
|
|
12215
12249
|
process.on("SIGTERM", () => setImmediate(() => flushEventLogBuffer()));
|
|
12216
12250
|
process.on("SIGINT", () => setImmediate(() => flushEventLogBuffer()));
|
|
12217
12251
|
process.on("uncaughtException", (error) => {
|
|
@@ -12514,7 +12548,7 @@ function resolveRealContainedPath(baseDir, targetPath2) {
|
|
|
12514
12548
|
if (process.platform === "darwin") {
|
|
12515
12549
|
const resolvedSymlink = resolvedAccumulated;
|
|
12516
12550
|
const knownDarwinSymlinks = ["/var", "/tmp", "/etc", "/private/var", "/private/tmp", "/private/etc"];
|
|
12517
|
-
if (knownDarwinSymlinks.
|
|
12551
|
+
if (knownDarwinSymlinks.some((s) => resolvedSymlink === s || resolvedSymlink.startsWith(s + "/"))) continue;
|
|
12518
12552
|
}
|
|
12519
12553
|
throw new Error("Refusing to resolve: target path ancestor is a symlink: " + resolvedAccumulated);
|
|
12520
12554
|
}
|
|
@@ -13155,6 +13189,9 @@ function statManifestWithWindowsRetry(manifestPath) {
|
|
|
13155
13189
|
}
|
|
13156
13190
|
return fs12.statSync(manifestPath);
|
|
13157
13191
|
}
|
|
13192
|
+
function genOf(stateRoot) {
|
|
13193
|
+
return manifestCacheGeneration.get(stateRoot) ?? 0;
|
|
13194
|
+
}
|
|
13158
13195
|
function __test__setManifestCache(stateRoot, entry) {
|
|
13159
13196
|
setManifestCache(stateRoot, entry);
|
|
13160
13197
|
}
|
|
@@ -13164,7 +13201,7 @@ function __test__getManifestCacheEntry(stateRoot) {
|
|
|
13164
13201
|
function setManifestCache(stateRoot, entry) {
|
|
13165
13202
|
if (manifestCache.has(stateRoot)) manifestCache.delete(stateRoot);
|
|
13166
13203
|
entry.cachedAt = Date.now();
|
|
13167
|
-
entry.generation =
|
|
13204
|
+
entry.generation = genOf(stateRoot);
|
|
13168
13205
|
const now = Date.now();
|
|
13169
13206
|
for (const [key, val] of manifestCache.entries()) {
|
|
13170
13207
|
if (val.cachedAt && now - val.cachedAt > MANIFEST_CACHE_TTL_MS) {
|
|
@@ -13191,7 +13228,7 @@ function useProjectState(cwd) {
|
|
|
13191
13228
|
}
|
|
13192
13229
|
function invalidateRunCache(stateRoot) {
|
|
13193
13230
|
manifestCache.delete(stateRoot);
|
|
13194
|
-
manifestCacheGeneration
|
|
13231
|
+
manifestCacheGeneration.set(stateRoot, genOf(stateRoot) + 1);
|
|
13195
13232
|
}
|
|
13196
13233
|
function scopeBaseRoot(cwd) {
|
|
13197
13234
|
return useProjectState(cwd) ? projectCrewRoot(cwd) : userCrewRoot();
|
|
@@ -13332,13 +13369,24 @@ function saveRunManifest(manifest) {
|
|
|
13332
13369
|
const manifestPath = path10.join(manifest.stateRoot, "manifest.json");
|
|
13333
13370
|
atomicWriteJson(manifestPath, manifest);
|
|
13334
13371
|
const manifestStat = fs12.statSync(manifestPath);
|
|
13372
|
+
let tasks = [];
|
|
13373
|
+
let tasksMtimeMs = 0;
|
|
13374
|
+
let tasksSize = 0;
|
|
13375
|
+
try {
|
|
13376
|
+
const tasksPath = path10.join(manifest.stateRoot, "tasks.json");
|
|
13377
|
+
const tasksStat = fs12.statSync(tasksPath);
|
|
13378
|
+
tasks = JSON.parse(fs12.readFileSync(tasksPath, "utf-8"));
|
|
13379
|
+
tasksMtimeMs = tasksStat.mtimeMs;
|
|
13380
|
+
tasksSize = tasksStat.size;
|
|
13381
|
+
} catch {
|
|
13382
|
+
}
|
|
13335
13383
|
setManifestCache(manifest.stateRoot, {
|
|
13336
13384
|
manifest,
|
|
13337
|
-
tasks
|
|
13385
|
+
tasks,
|
|
13338
13386
|
manifestMtimeMs: manifestStat.mtimeMs,
|
|
13339
13387
|
manifestSize: manifestStat.size,
|
|
13340
|
-
tasksMtimeMs
|
|
13341
|
-
tasksSize
|
|
13388
|
+
tasksMtimeMs,
|
|
13389
|
+
tasksSize
|
|
13342
13390
|
});
|
|
13343
13391
|
}
|
|
13344
13392
|
async function saveRunManifestAsync(manifest) {
|
|
@@ -13506,7 +13554,7 @@ function loadRunManifestById(cwd, runId) {
|
|
|
13506
13554
|
tasksStat = void 0;
|
|
13507
13555
|
}
|
|
13508
13556
|
const tasksMtimeMs = tasksStat?.mtimeMs ?? 0;
|
|
13509
|
-
if (cached && cached.manifestMtimeMs === manifestStat.mtimeMs && cached.manifestSize === manifestStat.size && cached.tasksMtimeMs === tasksMtimeMs && cached.tasksSize === (tasksStat?.size ?? 0) && cached.generation ===
|
|
13557
|
+
if (cached && cached.manifestMtimeMs === manifestStat.mtimeMs && cached.manifestSize === manifestStat.size && cached.tasksMtimeMs === tasksMtimeMs && cached.tasksSize === (tasksStat?.size ?? 0) && cached.generation === genOf(stateRoot)) {
|
|
13510
13558
|
if (!cached.cachedAt || Date.now() - cached.cachedAt > MANIFEST_CACHE_TTL_MS) {
|
|
13511
13559
|
manifestCache.delete(stateRoot);
|
|
13512
13560
|
} else if (!validateRunManifestPaths(cwd, runId, cached.manifest, stateRoot, tasksPath)) {
|
|
@@ -13569,7 +13617,7 @@ async function loadRunManifestByIdAsync(cwd, runId) {
|
|
|
13569
13617
|
tasksStat = void 0;
|
|
13570
13618
|
}
|
|
13571
13619
|
const tasksMtimeMs = tasksStat?.mtimeMs ?? 0;
|
|
13572
|
-
if (cached && cached.manifestMtimeMs === manifestStat.mtimeMs && cached.manifestSize === manifestStat.size && cached.tasksMtimeMs === tasksMtimeMs && cached.tasksSize === (tasksStat?.size ?? 0) && cached.generation ===
|
|
13620
|
+
if (cached && cached.manifestMtimeMs === manifestStat.mtimeMs && cached.manifestSize === manifestStat.size && cached.tasksMtimeMs === tasksMtimeMs && cached.tasksSize === (tasksStat?.size ?? 0) && cached.generation === genOf(stateRoot)) {
|
|
13573
13621
|
if (!cached.cachedAt || Date.now() - cached.cachedAt > MANIFEST_CACHE_TTL_MS) {
|
|
13574
13622
|
manifestCache.delete(stateRoot);
|
|
13575
13623
|
} else if (!validateRunManifestPaths(cwd, runId, cached.manifest, stateRoot, tasksPath)) {
|
|
@@ -13629,7 +13677,7 @@ var init_state_store = __esm({
|
|
|
13629
13677
|
init_contracts();
|
|
13630
13678
|
init_event_log();
|
|
13631
13679
|
init_locks();
|
|
13632
|
-
manifestCacheGeneration =
|
|
13680
|
+
manifestCacheGeneration = /* @__PURE__ */ new Map();
|
|
13633
13681
|
MANIFEST_CACHE_TTL_MS = 15 * 1e3;
|
|
13634
13682
|
LOAD_MANIFEST_RETRY_LIMIT = 5;
|
|
13635
13683
|
manifestCache = /* @__PURE__ */ new Map();
|
|
@@ -13950,18 +13998,20 @@ function upsertCrewAgent(manifest, record) {
|
|
|
13950
13998
|
}
|
|
13951
13999
|
function writeCrewAgentStatus(manifest, record) {
|
|
13952
14000
|
ensureAgentStateDir(manifest, record.taskId);
|
|
13953
|
-
atomicWriteJson(agentStatusPath(manifest, record.taskId), redactSecrets(record));
|
|
14001
|
+
atomicWriteJson(agentStatusPath(manifest, record.taskId), redactSecrets(record), { durability: "full" });
|
|
13954
14002
|
}
|
|
13955
14003
|
function saveCrewAgentsCoalesced(manifest, records) {
|
|
13956
14004
|
const filePath = agentsPath(manifest);
|
|
13957
14005
|
fs14.mkdirSync(manifest.stateRoot, { recursive: true });
|
|
13958
|
-
atomicWriteJsonCoalesced(filePath, redactSecrets(records), AGENT_COALESCE_MS);
|
|
14006
|
+
atomicWriteJsonCoalesced(filePath, redactSecrets(records), AGENT_COALESCE_MS, { durability: "best-effort" });
|
|
13959
14007
|
asyncAgentReaderCache.delete(filePath);
|
|
13960
14008
|
for (const record of records) writeCrewAgentStatusCoalesced(manifest, record);
|
|
13961
14009
|
}
|
|
13962
14010
|
function writeCrewAgentStatusCoalesced(manifest, record) {
|
|
13963
14011
|
ensureAgentStateDir(manifest, record.taskId);
|
|
13964
|
-
atomicWriteJsonCoalesced(agentStatusPath(manifest, record.taskId), redactSecrets(record), AGENT_COALESCE_MS
|
|
14012
|
+
atomicWriteJsonCoalesced(agentStatusPath(manifest, record.taskId), redactSecrets(record), AGENT_COALESCE_MS, {
|
|
14013
|
+
durability: "best-effort"
|
|
14014
|
+
});
|
|
13965
14015
|
}
|
|
13966
14016
|
function readCrewAgentStatus(manifest, taskOrAgentId) {
|
|
13967
14017
|
try {
|
|
@@ -15549,12 +15599,12 @@ function getProcessStartTime2(pid) {
|
|
|
15549
15599
|
} catch {
|
|
15550
15600
|
return void 0;
|
|
15551
15601
|
}
|
|
15552
|
-
const
|
|
15553
|
-
if (
|
|
15602
|
+
const platform2 = process.platform;
|
|
15603
|
+
if (platform2 === "linux") {
|
|
15554
15604
|
return getProcessStartTimeLinux(pid);
|
|
15555
|
-
} else if (
|
|
15605
|
+
} else if (platform2 === "darwin") {
|
|
15556
15606
|
return getProcessStartTimeMacOS(pid);
|
|
15557
|
-
} else if (
|
|
15607
|
+
} else if (platform2 === "win32") {
|
|
15558
15608
|
return getProcessStartTimeWindows(pid);
|
|
15559
15609
|
}
|
|
15560
15610
|
return void 0;
|
|
@@ -18714,6 +18764,7 @@ ${JSON.stringify({ type: "message_end", usage: { input: 10, output: 5, cost: 1e-
|
|
|
18714
18764
|
const finalDrainMs = input.finalDrainMs ?? FINAL_DRAIN_MS;
|
|
18715
18765
|
const hardKillMs = input.hardKillMs ?? HARD_KILL_MS;
|
|
18716
18766
|
let finalDrainArmed = false;
|
|
18767
|
+
let lastStdoutActivityMonotonicMs = performance.now();
|
|
18717
18768
|
let finalDrainFiredMonotonicMs;
|
|
18718
18769
|
const spawnMonotonicMs = performance.now();
|
|
18719
18770
|
let finalAssistantEventMonotonicMs;
|
|
@@ -18872,10 +18923,59 @@ ${JSON.stringify({ type: "message_end", usage: { input: 10, output: 5, cost: 1e-
|
|
|
18872
18923
|
completeOperation(eventOpId);
|
|
18873
18924
|
throw err2;
|
|
18874
18925
|
}
|
|
18926
|
+
lastStdoutActivityMonotonicMs = performance.now();
|
|
18875
18927
|
input.onJsonEvent?.(event);
|
|
18876
18928
|
if (!isFinalAssistantEvent(event) || childExited || settled || finalDrainTimer) return;
|
|
18877
18929
|
finalAssistantEventMonotonicMs = performance.now();
|
|
18878
18930
|
finalDrainArmed = true;
|
|
18931
|
+
const quietMs = input.finalDrainQuietMs ?? DEFAULT_CHILD_PI.finalDrainQuietMs;
|
|
18932
|
+
if (quietMs < (input.finalDrainMs ?? DEFAULT_CHILD_PI.finalDrainMs)) {
|
|
18933
|
+
const pollHandle = setInterval(() => {
|
|
18934
|
+
if (settled || childExited) {
|
|
18935
|
+
clearInterval(pollHandle);
|
|
18936
|
+
pollHandle.unref();
|
|
18937
|
+
return;
|
|
18938
|
+
}
|
|
18939
|
+
const sinceLast = performance.now() - lastStdoutActivityMonotonicMs;
|
|
18940
|
+
if (sinceLast >= quietMs) {
|
|
18941
|
+
clearInterval(pollHandle);
|
|
18942
|
+
pollHandle.unref();
|
|
18943
|
+
forcedFinalDrain = true;
|
|
18944
|
+
finalDrainFiredMonotonicMs = performance.now();
|
|
18945
|
+
input.onLifecycleEvent?.({
|
|
18946
|
+
type: "final_drain",
|
|
18947
|
+
pid: child.pid,
|
|
18948
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
18949
|
+
reason: "stdout-quiet"
|
|
18950
|
+
});
|
|
18951
|
+
try {
|
|
18952
|
+
child.kill(process.platform === "win32" ? void 0 : "SIGTERM");
|
|
18953
|
+
} catch (error) {
|
|
18954
|
+
logInternalError("child-pi.quiet-drain-term", error, `pid=${child.pid}`);
|
|
18955
|
+
}
|
|
18956
|
+
hardKillTimer = setTimeout(() => {
|
|
18957
|
+
if (settled || childExited) return;
|
|
18958
|
+
try {
|
|
18959
|
+
hardKilled = true;
|
|
18960
|
+
input.onLifecycleEvent?.({
|
|
18961
|
+
type: "hard_kill",
|
|
18962
|
+
pid: child.pid,
|
|
18963
|
+
ts: (/* @__PURE__ */ new Date()).toISOString()
|
|
18964
|
+
});
|
|
18965
|
+
child.kill(process.platform === "win32" ? void 0 : "SIGKILL");
|
|
18966
|
+
} catch (error) {
|
|
18967
|
+
logInternalError("child-pi.quiet-drain-hard-kill", error, `pid=${child.pid}`);
|
|
18968
|
+
}
|
|
18969
|
+
}, hardKillMs);
|
|
18970
|
+
hardKillTimer.unref();
|
|
18971
|
+
if (finalDrainTimer) {
|
|
18972
|
+
clearTimeout(finalDrainTimer);
|
|
18973
|
+
finalDrainTimer = void 0;
|
|
18974
|
+
}
|
|
18975
|
+
}
|
|
18976
|
+
}, 200);
|
|
18977
|
+
pollHandle.unref();
|
|
18978
|
+
}
|
|
18879
18979
|
finalDrainTimer = setTimeout(() => {
|
|
18880
18980
|
if (settled || childExited) return;
|
|
18881
18981
|
forcedFinalDrain = true;
|
|
@@ -19199,6 +19299,15 @@ var init_child_pi = __esm({
|
|
|
19199
19299
|
MAX_COMPACT_CONTENT_CHARS = DEFAULT_CHILD_PI.maxCompactContentChars;
|
|
19200
19300
|
activeChildProcesses = /* @__PURE__ */ new Map();
|
|
19201
19301
|
childHardKillTimers = /* @__PURE__ */ new Map();
|
|
19302
|
+
setInterval(() => {
|
|
19303
|
+
for (const [pid, child] of activeChildProcesses) {
|
|
19304
|
+
try {
|
|
19305
|
+
process.kill(pid, 0);
|
|
19306
|
+
} catch {
|
|
19307
|
+
activeChildProcesses.delete(pid);
|
|
19308
|
+
}
|
|
19309
|
+
}
|
|
19310
|
+
}, 6e4).unref();
|
|
19202
19311
|
BASE_ALLOWLIST = [
|
|
19203
19312
|
"PATH",
|
|
19204
19313
|
"HOME",
|
|
@@ -19241,20 +19350,25 @@ var init_child_pi = __esm({
|
|
|
19241
19350
|
"PI_TEAMS_MOCK_CHILD_PI",
|
|
19242
19351
|
"PI_CREW_ALLOW_MOCK"
|
|
19243
19352
|
];
|
|
19244
|
-
ChildPiLineObserver = class {
|
|
19353
|
+
ChildPiLineObserver = class _ChildPiLineObserver {
|
|
19245
19354
|
buffer = "";
|
|
19246
19355
|
input;
|
|
19247
|
-
/** RAW
|
|
19248
|
-
*
|
|
19249
|
-
*
|
|
19250
|
-
*
|
|
19251
|
-
*
|
|
19356
|
+
/** F9: bounded ring buffer for RAW assistant-text fragments. Consumers
|
|
19357
|
+
* (getRawFinalText) only read the last element, but the legacy implementation
|
|
19358
|
+
* accumulated every fragment unconditionally, which let a verbose/long-running
|
|
19359
|
+
* worker grow this array linearly with output. We retain the last 2 entries:
|
|
19360
|
+
* the consumer needs the last; we keep the second-to-last only as a defensive
|
|
19361
|
+
* fence against a race where a final event arrives just after the consumer
|
|
19362
|
+
* read (the previous "last" is still the most-recent pre-final text in that
|
|
19363
|
+
* window). 2 is well below any plausible consumer's "tail-only" need while
|
|
19364
|
+
* bounding memory. */
|
|
19365
|
+
static MAX_RAW_TEXT_EVENTS = 2;
|
|
19252
19366
|
rawTextEvents = [];
|
|
19253
|
-
/**
|
|
19254
|
-
*
|
|
19255
|
-
*
|
|
19256
|
-
*
|
|
19257
|
-
|
|
19367
|
+
/** F9: bounded ring buffer for intermediate findings. The downstream digest
|
|
19368
|
+
* (getIntermediateFindings) slices the last 20, but the array previously grew
|
|
19369
|
+
* to 1000s of entries. We keep MAX_INTERMEDIATE_DIGEST_LINES + headroom so
|
|
19370
|
+
* the public API behaviour is preserved (still returns "last 20 lines"). */
|
|
19371
|
+
static MAX_INTERMEDIATE_FINDINGS = 32;
|
|
19258
19372
|
intermediateFindings = [];
|
|
19259
19373
|
constructor(input) {
|
|
19260
19374
|
this.input = input;
|
|
@@ -19298,9 +19412,13 @@ var init_child_pi = __esm({
|
|
|
19298
19412
|
const rawTexts = extractText(rawParsed);
|
|
19299
19413
|
if (rawTexts.length > 0) {
|
|
19300
19414
|
this.rawTextEvents.push(...rawTexts);
|
|
19415
|
+
const rawOverflow = this.rawTextEvents.length - _ChildPiLineObserver.MAX_RAW_TEXT_EVENTS;
|
|
19416
|
+
if (rawOverflow > 0) this.rawTextEvents.splice(0, rawOverflow);
|
|
19301
19417
|
const last = rawTexts[rawTexts.length - 1];
|
|
19302
19418
|
if (last.trim().length > 0) {
|
|
19303
19419
|
this.intermediateFindings.push(last.trim());
|
|
19420
|
+
const findingsOverflow = this.intermediateFindings.length - _ChildPiLineObserver.MAX_INTERMEDIATE_FINDINGS;
|
|
19421
|
+
if (findingsOverflow > 0) this.intermediateFindings.splice(0, findingsOverflow);
|
|
19304
19422
|
}
|
|
19305
19423
|
}
|
|
19306
19424
|
} catch {
|
|
@@ -19321,6 +19439,8 @@ var init_child_pi = __esm({
|
|
|
19321
19439
|
logInternalError("child-pi.on-stdout-line", error, `line=${compact.displayLine}`);
|
|
19322
19440
|
}
|
|
19323
19441
|
this.intermediateFindings.push(compact.displayLine.trim());
|
|
19442
|
+
const findingsOverflow = this.intermediateFindings.length - _ChildPiLineObserver.MAX_INTERMEDIATE_FINDINGS;
|
|
19443
|
+
if (findingsOverflow > 0) this.intermediateFindings.splice(0, findingsOverflow);
|
|
19324
19444
|
}
|
|
19325
19445
|
}
|
|
19326
19446
|
};
|
|
@@ -19970,15 +20090,56 @@ function parseDynamicWorkflowFile(filePath, source) {
|
|
|
19970
20090
|
return void 0;
|
|
19971
20091
|
}
|
|
19972
20092
|
}
|
|
20093
|
+
function workflowDirStamp(cwd) {
|
|
20094
|
+
const dirs = [
|
|
20095
|
+
path26.join(packageRoot(), "workflows"),
|
|
20096
|
+
path26.join(userPiRoot(), "workflows"),
|
|
20097
|
+
path26.join(projectCrewRoot(cwd), "workflows")
|
|
20098
|
+
];
|
|
20099
|
+
let out = "";
|
|
20100
|
+
for (const d of dirs) {
|
|
20101
|
+
try {
|
|
20102
|
+
const st = fs30.statSync(d);
|
|
20103
|
+
out += `${st.mtimeMs}|`;
|
|
20104
|
+
} catch {
|
|
20105
|
+
out += "0|";
|
|
20106
|
+
}
|
|
20107
|
+
}
|
|
20108
|
+
return out;
|
|
20109
|
+
}
|
|
20110
|
+
function invalidateWorkflowDiscoveryCache(cwd) {
|
|
20111
|
+
if (cwd) {
|
|
20112
|
+
workflowCache.delete(cwd);
|
|
20113
|
+
} else {
|
|
20114
|
+
workflowCache.clear();
|
|
20115
|
+
}
|
|
20116
|
+
}
|
|
19973
20117
|
function discoverWorkflows(cwd) {
|
|
19974
20118
|
if (!cwd || typeof cwd !== "string") {
|
|
19975
20119
|
return { builtin: [], user: [], project: [] };
|
|
19976
20120
|
}
|
|
19977
|
-
|
|
20121
|
+
const now = Date.now();
|
|
20122
|
+
const stamp = workflowDirStamp(cwd);
|
|
20123
|
+
const cached = workflowCache.get(cwd);
|
|
20124
|
+
if (cached && cached.expiresAt > now && cached.dirStamp === stamp) {
|
|
20125
|
+
return cached.result;
|
|
20126
|
+
}
|
|
20127
|
+
const result4 = {
|
|
19978
20128
|
builtin: readWorkflowDir(path26.join(packageRoot(), "workflows"), "builtin"),
|
|
19979
20129
|
user: readWorkflowDir(path26.join(userPiRoot(), "workflows"), "user"),
|
|
19980
20130
|
project: readWorkflowDir(path26.join(projectCrewRoot(cwd), "workflows"), "project")
|
|
19981
20131
|
};
|
|
20132
|
+
workflowCache.set(cwd, {
|
|
20133
|
+
result: result4,
|
|
20134
|
+
expiresAt: now + WORKFLOW_DISCOVERY_TTL_MS,
|
|
20135
|
+
dirStamp: stamp
|
|
20136
|
+
});
|
|
20137
|
+
while (workflowCache.size > WORKFLOW_DISCOVERY_MAX_ENTRIES) {
|
|
20138
|
+
const oldest = workflowCache.keys().next().value;
|
|
20139
|
+
if (oldest === void 0) break;
|
|
20140
|
+
workflowCache.delete(oldest);
|
|
20141
|
+
}
|
|
20142
|
+
return result4;
|
|
19982
20143
|
}
|
|
19983
20144
|
function allWorkflows(discovery) {
|
|
19984
20145
|
if (!discovery) return [];
|
|
@@ -19988,7 +20149,7 @@ function allWorkflows(discovery) {
|
|
|
19988
20149
|
}
|
|
19989
20150
|
return [...byName.values()].sort((a, b) => a.name.localeCompare(b.name));
|
|
19990
20151
|
}
|
|
19991
|
-
var STEP_CONFIG_KEYS, parseOptionalInteger, parseOptionalBoolean, parseTopology;
|
|
20152
|
+
var STEP_CONFIG_KEYS, parseOptionalInteger, parseOptionalBoolean, parseTopology, WORKFLOW_DISCOVERY_TTL_MS, WORKFLOW_DISCOVERY_MAX_ENTRIES, workflowCache;
|
|
19992
20153
|
var init_discover_workflows = __esm({
|
|
19993
20154
|
"src/workflows/discover-workflows.ts"() {
|
|
19994
20155
|
"use strict";
|
|
@@ -20033,6 +20194,9 @@ var init_discover_workflows = __esm({
|
|
|
20033
20194
|
}
|
|
20034
20195
|
return void 0;
|
|
20035
20196
|
};
|
|
20197
|
+
WORKFLOW_DISCOVERY_TTL_MS = 5e3;
|
|
20198
|
+
WORKFLOW_DISCOVERY_MAX_ENTRIES = 32;
|
|
20199
|
+
workflowCache = /* @__PURE__ */ new Map();
|
|
20036
20200
|
}
|
|
20037
20201
|
});
|
|
20038
20202
|
|
|
@@ -22165,7 +22329,7 @@ var init_discover_agents = __esm({
|
|
|
22165
22329
|
MAX_SECURITY_LOG_ENTRIES = 1e3;
|
|
22166
22330
|
securityEventLog = [];
|
|
22167
22331
|
warnedForkAgents = /* @__PURE__ */ new Set();
|
|
22168
|
-
DISCOVERY_CACHE_TTL_MS =
|
|
22332
|
+
DISCOVERY_CACHE_TTL_MS = 5e3;
|
|
22169
22333
|
discoveryCache = /* @__PURE__ */ new Map();
|
|
22170
22334
|
DISCOVERY_CACHE_MAX_ENTRIES = 32;
|
|
22171
22335
|
dynamicAgents = /* @__PURE__ */ new Map();
|
|
@@ -22362,7 +22526,8 @@ var init_git = __esm({
|
|
|
22362
22526
|
var discover_teams_exports = {};
|
|
22363
22527
|
__export(discover_teams_exports, {
|
|
22364
22528
|
allTeams: () => allTeams,
|
|
22365
|
-
discoverTeams: () => discoverTeams
|
|
22529
|
+
discoverTeams: () => discoverTeams,
|
|
22530
|
+
invalidateTeamDiscoveryCache: () => invalidateTeamDiscoveryCache
|
|
22366
22531
|
});
|
|
22367
22532
|
import * as fs34 from "node:fs";
|
|
22368
22533
|
import * as path29 from "node:path";
|
|
@@ -22446,12 +22611,48 @@ function readTeamDir(dir, source) {
|
|
|
22446
22611
|
if (!fs34.existsSync(dir)) return [];
|
|
22447
22612
|
return fs34.readdirSync(dir).filter((entry) => entry.endsWith(".team.md")).map((entry) => parseTeamFile(path29.join(dir, entry), source)).filter((team) => team !== void 0).sort((a, b) => a.name.localeCompare(b.name));
|
|
22448
22613
|
}
|
|
22614
|
+
function teamDirStamp(cwd) {
|
|
22615
|
+
const dirs = [path29.join(packageRoot(), "teams"), path29.join(userPiRoot(), "teams"), path29.join(projectCrewRoot(cwd), "teams")];
|
|
22616
|
+
let out = "";
|
|
22617
|
+
for (const d of dirs) {
|
|
22618
|
+
try {
|
|
22619
|
+
out += `${fs34.statSync(d).mtimeMs}|`;
|
|
22620
|
+
} catch {
|
|
22621
|
+
out += "0|";
|
|
22622
|
+
}
|
|
22623
|
+
}
|
|
22624
|
+
return out;
|
|
22625
|
+
}
|
|
22626
|
+
function invalidateTeamDiscoveryCache(cwd) {
|
|
22627
|
+
if (cwd) {
|
|
22628
|
+
teamCache.delete(cwd);
|
|
22629
|
+
} else {
|
|
22630
|
+
teamCache.clear();
|
|
22631
|
+
}
|
|
22632
|
+
}
|
|
22449
22633
|
function discoverTeams(cwd) {
|
|
22450
|
-
|
|
22634
|
+
const now = Date.now();
|
|
22635
|
+
const stamp = teamDirStamp(cwd);
|
|
22636
|
+
const cached = teamCache.get(cwd);
|
|
22637
|
+
if (cached && cached.expiresAt > now && cached.dirStamp === stamp) {
|
|
22638
|
+
return cached.result;
|
|
22639
|
+
}
|
|
22640
|
+
const result4 = {
|
|
22451
22641
|
builtin: readTeamDir(path29.join(packageRoot(), "teams"), "builtin"),
|
|
22452
22642
|
user: readTeamDir(path29.join(userPiRoot(), "teams"), "user"),
|
|
22453
22643
|
project: readTeamDir(path29.join(projectCrewRoot(cwd), "teams"), "project")
|
|
22454
22644
|
};
|
|
22645
|
+
teamCache.set(cwd, {
|
|
22646
|
+
result: result4,
|
|
22647
|
+
expiresAt: now + TEAM_DISCOVERY_TTL_MS,
|
|
22648
|
+
dirStamp: stamp
|
|
22649
|
+
});
|
|
22650
|
+
while (teamCache.size > TEAM_DISCOVERY_MAX_ENTRIES) {
|
|
22651
|
+
const oldest = teamCache.keys().next().value;
|
|
22652
|
+
if (oldest === void 0) break;
|
|
22653
|
+
teamCache.delete(oldest);
|
|
22654
|
+
}
|
|
22655
|
+
return result4;
|
|
22455
22656
|
}
|
|
22456
22657
|
function allTeams(discovery) {
|
|
22457
22658
|
if (!discovery) return [];
|
|
@@ -22461,12 +22662,16 @@ function allTeams(discovery) {
|
|
|
22461
22662
|
}
|
|
22462
22663
|
return [...byName.values()].sort((a, b) => a.name.localeCompare(b.name));
|
|
22463
22664
|
}
|
|
22665
|
+
var TEAM_DISCOVERY_TTL_MS, TEAM_DISCOVERY_MAX_ENTRIES, teamCache;
|
|
22464
22666
|
var init_discover_teams = __esm({
|
|
22465
22667
|
"src/teams/discover-teams.ts"() {
|
|
22466
22668
|
"use strict";
|
|
22467
22669
|
init_frontmatter();
|
|
22468
22670
|
init_git();
|
|
22469
22671
|
init_paths();
|
|
22672
|
+
TEAM_DISCOVERY_TTL_MS = 5e3;
|
|
22673
|
+
TEAM_DISCOVERY_MAX_ENTRIES = 32;
|
|
22674
|
+
teamCache = /* @__PURE__ */ new Map();
|
|
22470
22675
|
}
|
|
22471
22676
|
});
|
|
22472
22677
|
|
|
@@ -23615,7 +23820,7 @@ function readDeliveryState(manifest) {
|
|
|
23615
23820
|
return { messages: {}, updatedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
23616
23821
|
}
|
|
23617
23822
|
}
|
|
23618
|
-
function writeDeliveryState(manifest, state) {
|
|
23823
|
+
function writeDeliveryState(manifest, state, options) {
|
|
23619
23824
|
ensureRunMailbox(manifest);
|
|
23620
23825
|
const MAX_DELIVERY_MESSAGES = 1e4;
|
|
23621
23826
|
if (Object.keys(state.messages).length > MAX_DELIVERY_MESSAGES) {
|
|
@@ -23627,7 +23832,9 @@ function writeDeliveryState(manifest, state) {
|
|
|
23627
23832
|
state.messages = Object.fromEntries(trimmed);
|
|
23628
23833
|
}
|
|
23629
23834
|
atomicWriteFile(deliveryFile(manifest, true), `${JSON.stringify(redactSecrets(state), null, 2)}
|
|
23630
|
-
|
|
23835
|
+
`, {
|
|
23836
|
+
durability: options?.durability ?? "best-effort"
|
|
23837
|
+
});
|
|
23631
23838
|
}
|
|
23632
23839
|
function appendMailboxMessage(manifest, message) {
|
|
23633
23840
|
if (message.taskId) ensureTaskMailbox(manifest, message.taskId);
|
|
@@ -23666,7 +23873,7 @@ function appendMailboxMessage(manifest, message) {
|
|
|
23666
23873
|
const delivery = readDeliveryState(manifest);
|
|
23667
23874
|
delivery.messages[complete.id] = complete.status;
|
|
23668
23875
|
delivery.updatedAt = createdAt;
|
|
23669
|
-
writeDeliveryState(manifest, delivery);
|
|
23876
|
+
writeDeliveryState(manifest, delivery, { durability: "full" });
|
|
23670
23877
|
});
|
|
23671
23878
|
return complete;
|
|
23672
23879
|
}
|
|
@@ -23710,7 +23917,7 @@ function acknowledgeMailboxMessage(manifest, messageId) {
|
|
|
23710
23917
|
if (!delivery.messages[messageId]) throw new Error(`Mailbox message '${messageId}' not found.`);
|
|
23711
23918
|
delivery.messages[messageId] = "acknowledged";
|
|
23712
23919
|
delivery.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
23713
|
-
writeDeliveryState(manifest, delivery);
|
|
23920
|
+
writeDeliveryState(manifest, delivery, { durability: "full" });
|
|
23714
23921
|
return delivery;
|
|
23715
23922
|
});
|
|
23716
23923
|
}
|
|
@@ -24164,6 +24371,11 @@ var init_intent_policy = __esm({
|
|
|
24164
24371
|
import * as crypto3 from "node:crypto";
|
|
24165
24372
|
import * as fs38 from "node:fs";
|
|
24166
24373
|
import * as path33 from "node:path";
|
|
24374
|
+
function invalidateResourceCaches() {
|
|
24375
|
+
invalidateAgentDiscoveryCache();
|
|
24376
|
+
invalidateTeamDiscoveryCache();
|
|
24377
|
+
invalidateWorkflowDiscoveryCache();
|
|
24378
|
+
}
|
|
24167
24379
|
function result2(text, status = "ok", isError = false) {
|
|
24168
24380
|
return toolResult(text, { action: "management", status }, isError);
|
|
24169
24381
|
}
|
|
@@ -24461,6 +24673,7 @@ ${content}`);
|
|
|
24461
24673
|
true
|
|
24462
24674
|
);
|
|
24463
24675
|
}
|
|
24676
|
+
invalidateResourceCaches();
|
|
24464
24677
|
return result2(`Created ${params.resource} '${name}' at ${filePath}.`);
|
|
24465
24678
|
}
|
|
24466
24679
|
function handleUpdate(params, ctx) {
|
|
@@ -24565,6 +24778,7 @@ function handleUpdate(params, ctx) {
|
|
|
24565
24778
|
);
|
|
24566
24779
|
}
|
|
24567
24780
|
const updatedRefs = params.updateReferences ? updateReferencesForRename(ctx, params.resource, current2.name, nextName, source, false) : [];
|
|
24781
|
+
invalidateResourceCaches();
|
|
24568
24782
|
return result2(
|
|
24569
24783
|
[
|
|
24570
24784
|
`Updated ${params.resource} at ${nextPath}. Backup: ${backupPath}.`,
|
|
@@ -24603,6 +24817,7 @@ ${refs.map((ref2) => `- ${ref2}`).join("\n")}` : ""}`
|
|
|
24603
24817
|
true
|
|
24604
24818
|
);
|
|
24605
24819
|
}
|
|
24820
|
+
invalidateResourceCaches();
|
|
24606
24821
|
return result2(`Deleted ${params.resource} at ${resolved.resource.filePath}. Backup: ${backupPath}.`);
|
|
24607
24822
|
}
|
|
24608
24823
|
var init_management = __esm({
|
|
@@ -24658,18 +24873,18 @@ function initializeProject(cwd, options = {}) {
|
|
|
24658
24873
|
const teamsDir = path34.join(crewRoot, "teams");
|
|
24659
24874
|
const workflowsDir = path34.join(crewRoot, "workflows");
|
|
24660
24875
|
const configScope = options.configScope ?? "global";
|
|
24661
|
-
const
|
|
24876
|
+
const configPath3 = configScope === "project" ? path34.join(projectPiRoot(cwd), "pi-crew.json") : configScope === "global" ? configPath() : "";
|
|
24662
24877
|
ensureDir(agentsDir, createdDirs);
|
|
24663
24878
|
ensureDir(teamsDir, createdDirs);
|
|
24664
24879
|
ensureDir(workflowsDir, createdDirs);
|
|
24665
24880
|
ensureDir(path34.join(crewRoot, "imports"), createdDirs);
|
|
24666
24881
|
let configCreated = false;
|
|
24667
24882
|
let configSkipped = false;
|
|
24668
|
-
if (
|
|
24669
|
-
if (configScope === "project") ensureDir(path34.dirname(
|
|
24670
|
-
else fs39.mkdirSync(path34.dirname(
|
|
24671
|
-
if (!fs39.existsSync(
|
|
24672
|
-
fs39.writeFileSync(
|
|
24883
|
+
if (configPath3) {
|
|
24884
|
+
if (configScope === "project") ensureDir(path34.dirname(configPath3), createdDirs);
|
|
24885
|
+
else fs39.mkdirSync(path34.dirname(configPath3), { recursive: true });
|
|
24886
|
+
if (!fs39.existsSync(configPath3) || options.overwrite === true) {
|
|
24887
|
+
fs39.writeFileSync(configPath3, `${JSON.stringify(DEFAULT_PI_CREW_CONFIG, null, 2)}
|
|
24673
24888
|
`, "utf-8");
|
|
24674
24889
|
configCreated = true;
|
|
24675
24890
|
} else {
|
|
@@ -24708,7 +24923,7 @@ ${missing.join("\n")}
|
|
|
24708
24923
|
skippedFiles,
|
|
24709
24924
|
gitignorePath,
|
|
24710
24925
|
gitignoreUpdated,
|
|
24711
|
-
configPath:
|
|
24926
|
+
configPath: configPath3,
|
|
24712
24927
|
configScope,
|
|
24713
24928
|
configCreated,
|
|
24714
24929
|
configSkipped
|
|
@@ -44086,7 +44301,7 @@ var init_crew_hooks = __esm({
|
|
|
44086
44301
|
});
|
|
44087
44302
|
|
|
44088
44303
|
// src/runtime/skill-effectiveness.ts
|
|
44089
|
-
import { existsSync as existsSync38, mkdirSync as mkdirSync26, readFileSync as
|
|
44304
|
+
import { existsSync as existsSync38, mkdirSync as mkdirSync26, readFileSync as readFileSync41, writeFileSync as writeFileSync18 } from "fs";
|
|
44090
44305
|
import { dirname as dirname24, join as join43 } from "path";
|
|
44091
44306
|
function getSkillMetricsPath(cwd, runId) {
|
|
44092
44307
|
return join43(projectCrewRoot(cwd), `state/runs/${runId}/skill-metrics.jsonl`);
|
|
@@ -44149,7 +44364,7 @@ function getSkillActivations(cwd, runId) {
|
|
|
44149
44364
|
if (!existsSync38(path81)) {
|
|
44150
44365
|
return [];
|
|
44151
44366
|
}
|
|
44152
|
-
const content =
|
|
44367
|
+
const content = readFileSync41(path81, "utf-8");
|
|
44153
44368
|
if (!content.trim()) {
|
|
44154
44369
|
return [];
|
|
44155
44370
|
}
|
|
@@ -47311,7 +47526,7 @@ var init_explain = __esm({
|
|
|
47311
47526
|
});
|
|
47312
47527
|
|
|
47313
47528
|
// src/runtime/goal-state-store.ts
|
|
47314
|
-
import { closeSync as closeSync13, existsSync as existsSync45, mkdirSync as mkdirSync29, openSync as openSync13, readdirSync as readdirSync23, readFileSync as
|
|
47529
|
+
import { closeSync as closeSync13, existsSync as existsSync45, mkdirSync as mkdirSync29, openSync as openSync13, readdirSync as readdirSync23, readFileSync as readFileSync47, statSync as statSync31, unlinkSync as unlinkSync9 } from "node:fs";
|
|
47315
47530
|
import { dirname as dirname26 } from "node:path";
|
|
47316
47531
|
function resolveGoalsRoot(cwd) {
|
|
47317
47532
|
const crewRoot = projectCrewRoot(cwd) ?? userCrewRoot();
|
|
@@ -47345,7 +47560,7 @@ var init_goal_state_store = __esm({
|
|
|
47345
47560
|
const path81 = goalFilePath(this.cwd, goalId);
|
|
47346
47561
|
try {
|
|
47347
47562
|
if (!existsSync45(path81)) return void 0;
|
|
47348
|
-
const raw =
|
|
47563
|
+
const raw = readFileSync47(path81, "utf-8");
|
|
47349
47564
|
const parsed = JSON.parse(raw);
|
|
47350
47565
|
if (!parsed || typeof parsed !== "object" || typeof parsed.goalId !== "string") return void 0;
|
|
47351
47566
|
return parsed;
|
|
@@ -47442,7 +47657,7 @@ var init_goal_state_store = __esm({
|
|
|
47442
47657
|
const code = error.code;
|
|
47443
47658
|
if (code !== "EEXIST") return false;
|
|
47444
47659
|
try {
|
|
47445
|
-
const stat2 =
|
|
47660
|
+
const stat2 = statSync31(lockPath2);
|
|
47446
47661
|
if (Date.now() - stat2.mtimeMs > 5e3) {
|
|
47447
47662
|
unlinkSync9(lockPath2);
|
|
47448
47663
|
const fd = openSync13(lockPath2, "wx");
|
|
@@ -47533,7 +47748,7 @@ var init_verification_integrity = __esm({
|
|
|
47533
47748
|
|
|
47534
47749
|
// src/runtime/workspace-lock.ts
|
|
47535
47750
|
import { createHash as createHash7 } from "node:crypto";
|
|
47536
|
-
import { closeSync as closeSync14, existsSync as existsSync46, mkdirSync as mkdirSync30, openSync as openSync14, readdirSync as readdirSync24, readFileSync as
|
|
47751
|
+
import { closeSync as closeSync14, existsSync as existsSync46, mkdirSync as mkdirSync30, openSync as openSync14, readdirSync as readdirSync24, readFileSync as readFileSync49, statSync as statSync33, unlinkSync as unlinkSync10, writeFileSync as writeFileSync23 } from "node:fs";
|
|
47537
47752
|
import * as path51 from "node:path";
|
|
47538
47753
|
function workspaceLockPath(cwd) {
|
|
47539
47754
|
const absCwd = path51.resolve(cwd);
|
|
@@ -47545,7 +47760,7 @@ function workspaceLockPath(cwd) {
|
|
|
47545
47760
|
function readLock(lockPath2) {
|
|
47546
47761
|
if (!existsSync46(lockPath2)) return void 0;
|
|
47547
47762
|
try {
|
|
47548
|
-
const parsed = JSON.parse(
|
|
47763
|
+
const parsed = JSON.parse(readFileSync49(lockPath2, "utf-8"));
|
|
47549
47764
|
if (!parsed || typeof parsed !== "object") return void 0;
|
|
47550
47765
|
return parsed;
|
|
47551
47766
|
} catch {
|
|
@@ -47584,7 +47799,7 @@ var init_workspace_lock = __esm({
|
|
|
47584
47799
|
DEFAULT_HEARTBEAT_STALE_MS = 6e4;
|
|
47585
47800
|
defaultStartTimeResolver = (pid) => {
|
|
47586
47801
|
try {
|
|
47587
|
-
const stat2 =
|
|
47802
|
+
const stat2 = readFileSync49(`/proc/${pid}/stat`, "utf-8");
|
|
47588
47803
|
const lastParen = stat2.lastIndexOf(")");
|
|
47589
47804
|
if (lastParen === -1) return void 0;
|
|
47590
47805
|
const fieldsAfterComm = stat2.slice(lastParen + 1).trim().split(/\s+/);
|
|
@@ -51508,7 +51723,7 @@ var init_tool_progress_formatter = __esm({
|
|
|
51508
51723
|
});
|
|
51509
51724
|
|
|
51510
51725
|
// src/extension/registration/team-tool.ts
|
|
51511
|
-
import { statSync as
|
|
51726
|
+
import { statSync as statSync36 } from "node:fs";
|
|
51512
51727
|
import { Text as Text4 } from "@earendil-works/pi-tui";
|
|
51513
51728
|
async function handleTeamTool(params, ctx) {
|
|
51514
51729
|
if (!_cachedHandleTeamTool) {
|
|
@@ -51521,7 +51736,7 @@ function resolveCwdOverride(baseCwd, override) {
|
|
|
51521
51736
|
if (!override) return { ok: true, cwd: baseCwd };
|
|
51522
51737
|
try {
|
|
51523
51738
|
const resolved = resolveRealContainedPath(baseCwd, override);
|
|
51524
|
-
const stat2 =
|
|
51739
|
+
const stat2 = statSync36(resolved);
|
|
51525
51740
|
if (!stat2.isDirectory())
|
|
51526
51741
|
return {
|
|
51527
51742
|
ok: false,
|
|
@@ -52795,7 +53010,7 @@ var init_status = __esm({
|
|
|
52795
53010
|
});
|
|
52796
53011
|
|
|
52797
53012
|
// src/extension/team-tool/workflow-manage.ts
|
|
52798
|
-
import { existsSync as existsSync58, readFileSync as
|
|
53013
|
+
import { existsSync as existsSync58, readFileSync as readFileSync58, rmSync as rmSync16, writeFileSync as writeFileSync26 } from "node:fs";
|
|
52799
53014
|
import { dirname as dirname33, join as join59 } from "node:path";
|
|
52800
53015
|
function allowedWorkflowDirs(cwd) {
|
|
52801
53016
|
return [join59(projectCrewRoot(cwd), "workflows"), join59(userPiRoot(), "workflows"), join59(packageRoot(), "workflows")];
|
|
@@ -52867,7 +53082,7 @@ function handleWorkflowGet(params, ctx) {
|
|
|
52867
53082
|
let source = "(static workflow \u2014 no script source)";
|
|
52868
53083
|
if (isDynamic && wf.filePath && existsSync58(wf.filePath)) {
|
|
52869
53084
|
try {
|
|
52870
|
-
source =
|
|
53085
|
+
source = readFileSync58(wf.filePath, "utf-8").slice(0, 8e3);
|
|
52871
53086
|
} catch (error) {
|
|
52872
53087
|
logInternalError("workflow-manage.get", error, `filePath=${wf.filePath}`);
|
|
52873
53088
|
}
|
|
@@ -54413,7 +54628,7 @@ function asError(error) {
|
|
|
54413
54628
|
return error instanceof Error ? error : new Error(String(error));
|
|
54414
54629
|
}
|
|
54415
54630
|
function globToRegex(pattern) {
|
|
54416
|
-
const escaped = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*");
|
|
54631
|
+
const escaped = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*").replace(/\?/g, ".");
|
|
54417
54632
|
return new RegExp(`^${escaped}$`, "i");
|
|
54418
54633
|
}
|
|
54419
54634
|
function isRetryable(error, policy) {
|
|
@@ -56385,6 +56600,10 @@ function readKnowledge(cwd, query) {
|
|
|
56385
56600
|
knowledgeCache.delete(p);
|
|
56386
56601
|
return "";
|
|
56387
56602
|
}
|
|
56603
|
+
if (stat2.isSymbolicLink) {
|
|
56604
|
+
logInternalError("knowledge-injection.symlink", new Error(`Refusing to read symlink: ${p}`));
|
|
56605
|
+
return "";
|
|
56606
|
+
}
|
|
56388
56607
|
const cacheKey2 = `${stat2.mtimeMs}:${stat2.size}`;
|
|
56389
56608
|
if (!query || !query.goal && !query.taskText) {
|
|
56390
56609
|
const cached = knowledgeCache.get(p);
|
|
@@ -56434,8 +56653,8 @@ Full file: ${p} -->`
|
|
|
56434
56653
|
}
|
|
56435
56654
|
function tryStat(p) {
|
|
56436
56655
|
try {
|
|
56437
|
-
const s = fs77.
|
|
56438
|
-
return { mtimeMs: s.mtimeMs, size: s.size };
|
|
56656
|
+
const s = fs77.lstatSync(p);
|
|
56657
|
+
return { mtimeMs: s.mtimeMs, size: s.size, isSymbolicLink: s.isSymbolicLink() };
|
|
56439
56658
|
} catch {
|
|
56440
56659
|
return void 0;
|
|
56441
56660
|
}
|
|
@@ -56466,6 +56685,7 @@ var KNOWLEDGE_FILENAME, MAX_SESSION_LOG_BYTES, CONVENTION_HEADERS, STOPWORDS, kn
|
|
|
56466
56685
|
var init_knowledge_injection = __esm({
|
|
56467
56686
|
"src/extension/knowledge-injection.ts"() {
|
|
56468
56687
|
"use strict";
|
|
56688
|
+
init_internal_error();
|
|
56469
56689
|
init_paths();
|
|
56470
56690
|
KNOWLEDGE_FILENAME = "knowledge.md";
|
|
56471
56691
|
MAX_SESSION_LOG_BYTES = 5e3;
|
|
@@ -57087,32 +57307,12 @@ function persistSingleTaskUpdate(manifest, fallbackTasks, updated, checkpointPha
|
|
|
57087
57307
|
try {
|
|
57088
57308
|
currentMtime = fs80.statSync(manifest.tasksPath).mtimeMs;
|
|
57089
57309
|
} catch {
|
|
57090
|
-
|
|
57310
|
+
return fallbackTasks;
|
|
57091
57311
|
}
|
|
57092
57312
|
if (currentMtime !== baseMtime) {
|
|
57093
57313
|
baseMtime = currentMtime;
|
|
57094
57314
|
continue;
|
|
57095
57315
|
}
|
|
57096
|
-
let recheckMtime;
|
|
57097
|
-
try {
|
|
57098
|
-
recheckMtime = fs80.statSync(manifest.tasksPath).mtimeMs;
|
|
57099
|
-
} catch {
|
|
57100
|
-
return fallbackTasks;
|
|
57101
|
-
}
|
|
57102
|
-
if (recheckMtime !== baseMtime) {
|
|
57103
|
-
baseMtime = recheckMtime;
|
|
57104
|
-
continue;
|
|
57105
|
-
}
|
|
57106
|
-
let preWriteMtime;
|
|
57107
|
-
try {
|
|
57108
|
-
preWriteMtime = fs80.statSync(manifest.tasksPath).mtimeMs;
|
|
57109
|
-
} catch {
|
|
57110
|
-
preWriteMtime = 0;
|
|
57111
|
-
}
|
|
57112
|
-
if (preWriteMtime !== baseMtime) {
|
|
57113
|
-
baseMtime = preWriteMtime;
|
|
57114
|
-
continue;
|
|
57115
|
-
}
|
|
57116
57316
|
break;
|
|
57117
57317
|
}
|
|
57118
57318
|
if (merged === void 0) {
|
|
@@ -59593,8 +59793,7 @@ function checkPerTaskBudget(tasks, budgetTotal, budgetWarning, budgetAbort, fair
|
|
|
59593
59793
|
const totalUsed = (usage?.input ?? 0) + (usage?.output ?? 0) + (usage?.cacheWrite ?? 0);
|
|
59594
59794
|
const abort = totalUsed >= budgetAbort * budgetTotal;
|
|
59595
59795
|
const warning = !abort && totalUsed >= budgetWarning * budgetTotal;
|
|
59596
|
-
const
|
|
59597
|
-
const fairShareThreshold = remainingBudget * fairShareFraction;
|
|
59796
|
+
const fairShareThreshold = budgetTotal * fairShareFraction;
|
|
59598
59797
|
const fairShareViolators = [];
|
|
59599
59798
|
for (const task of tasks) {
|
|
59600
59799
|
if (!task.usage) continue;
|
|
@@ -60031,6 +60230,8 @@ async function executeTeamRun(input) {
|
|
|
60031
60230
|
cleanupUsage();
|
|
60032
60231
|
await flushEventLogBuffer();
|
|
60033
60232
|
return result4;
|
|
60233
|
+
} finally {
|
|
60234
|
+
await flushEventLogBuffer();
|
|
60034
60235
|
}
|
|
60035
60236
|
}
|
|
60036
60237
|
async function executeTeamRunCore(input, manifest, workflow) {
|
|
@@ -60906,122 +61107,126 @@ var init_pipeline_runner = __esm({
|
|
|
60906
61107
|
const stages = [];
|
|
60907
61108
|
let previousResults = [];
|
|
60908
61109
|
const startTime = Date.now();
|
|
60909
|
-
|
|
60910
|
-
type: "pipeline:started",
|
|
60911
|
-
runId,
|
|
60912
|
-
message: `Pipeline '${workflow.name}' started`,
|
|
60913
|
-
data: { stages: workflow.stages.map((s) => s.name) }
|
|
60914
|
-
});
|
|
60915
|
-
for (let i2 = 0; i2 < workflow.stages.length; i2++) {
|
|
60916
|
-
const stage = workflow.stages[i2];
|
|
60917
|
-
const stageStartTime = Date.now();
|
|
60918
|
-
const effectiveStopOnError = stage.stopOnError ?? workflow.stopOnError ?? this.stopOnError;
|
|
61110
|
+
try {
|
|
60919
61111
|
await appendEventAsync(eventsPath, {
|
|
60920
|
-
type: "pipeline:
|
|
61112
|
+
type: "pipeline:started",
|
|
60921
61113
|
runId,
|
|
60922
|
-
message: `
|
|
60923
|
-
data: {
|
|
61114
|
+
message: `Pipeline '${workflow.name}' started`,
|
|
61115
|
+
data: { stages: workflow.stages.map((s) => s.name) }
|
|
60924
61116
|
});
|
|
60925
|
-
|
|
60926
|
-
const
|
|
60927
|
-
|
|
60928
|
-
|
|
60929
|
-
previousResults,
|
|
60930
|
-
totalStages: workflow.stages.length,
|
|
60931
|
-
runId
|
|
60932
|
-
};
|
|
60933
|
-
const inputs = this.resolveInputs(stage.inputs, previousResults, context);
|
|
60934
|
-
const results = await this.executeStageInternal(stage, inputs, stageContext, executeStage);
|
|
60935
|
-
const duration = Date.now() - stageStartTime;
|
|
60936
|
-
stages.push({
|
|
60937
|
-
name: stage.name,
|
|
60938
|
-
status: "completed",
|
|
60939
|
-
results,
|
|
60940
|
-
duration,
|
|
60941
|
-
fanOutItems: Array.isArray(inputs) ? inputs.length : void 0
|
|
60942
|
-
});
|
|
60943
|
-
previousResults = results;
|
|
61117
|
+
for (let i2 = 0; i2 < workflow.stages.length; i2++) {
|
|
61118
|
+
const stage = workflow.stages[i2];
|
|
61119
|
+
const stageStartTime = Date.now();
|
|
61120
|
+
const effectiveStopOnError = stage.stopOnError ?? workflow.stopOnError ?? this.stopOnError;
|
|
60944
61121
|
await appendEventAsync(eventsPath, {
|
|
60945
|
-
type: "pipeline:
|
|
61122
|
+
type: "pipeline:stage_started",
|
|
60946
61123
|
runId,
|
|
60947
|
-
message: `Stage '${stage.name}'
|
|
60948
|
-
data: {
|
|
61124
|
+
message: `Stage '${stage.name}' started`,
|
|
61125
|
+
data: { stageIndex: i2, stageName: stage.name }
|
|
61126
|
+
});
|
|
61127
|
+
try {
|
|
61128
|
+
const stageContext = {
|
|
60949
61129
|
stageIndex: i2,
|
|
60950
61130
|
stageName: stage.name,
|
|
60951
|
-
|
|
60952
|
-
|
|
60953
|
-
|
|
60954
|
-
});
|
|
60955
|
-
} catch (error) {
|
|
60956
|
-
const duration = Date.now() - stageStartTime;
|
|
60957
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
60958
|
-
if (effectiveStopOnError) {
|
|
60959
|
-
stages.push({
|
|
60960
|
-
name: stage.name,
|
|
60961
|
-
status: "failed",
|
|
60962
|
-
results: [],
|
|
60963
|
-
error: errorMessage,
|
|
60964
|
-
duration
|
|
60965
|
-
});
|
|
60966
|
-
await appendEventAsync(eventsPath, {
|
|
60967
|
-
type: "pipeline:stage_failed",
|
|
60968
|
-
runId,
|
|
60969
|
-
message: `Stage '${stage.name}' failed: ${errorMessage}`,
|
|
60970
|
-
data: {
|
|
60971
|
-
stageIndex: i2,
|
|
60972
|
-
stageName: stage.name,
|
|
60973
|
-
duration,
|
|
60974
|
-
error: errorMessage
|
|
60975
|
-
}
|
|
60976
|
-
});
|
|
60977
|
-
await appendEventAsync(eventsPath, {
|
|
60978
|
-
type: "pipeline:failed",
|
|
60979
|
-
runId,
|
|
60980
|
-
message: `Pipeline '${workflow.name}' failed at stage '${stage.name}'`,
|
|
60981
|
-
data: { failedStage: stage.name, error: errorMessage }
|
|
60982
|
-
});
|
|
60983
|
-
return {
|
|
60984
|
-
stages,
|
|
60985
|
-
totalDuration: Date.now() - startTime,
|
|
60986
|
-
finalResults: previousResults,
|
|
60987
|
-
status: "failed"
|
|
61131
|
+
previousResults,
|
|
61132
|
+
totalStages: workflow.stages.length,
|
|
61133
|
+
runId
|
|
60988
61134
|
};
|
|
60989
|
-
|
|
61135
|
+
const inputs = this.resolveInputs(stage.inputs, previousResults, context);
|
|
61136
|
+
const results = await this.executeStageInternal(stage, inputs, stageContext, executeStage);
|
|
61137
|
+
const duration = Date.now() - stageStartTime;
|
|
60990
61138
|
stages.push({
|
|
60991
61139
|
name: stage.name,
|
|
60992
|
-
status: "
|
|
60993
|
-
results
|
|
60994
|
-
|
|
60995
|
-
|
|
61140
|
+
status: "completed",
|
|
61141
|
+
results,
|
|
61142
|
+
duration,
|
|
61143
|
+
fanOutItems: Array.isArray(inputs) ? inputs.length : void 0
|
|
60996
61144
|
});
|
|
61145
|
+
previousResults = results;
|
|
60997
61146
|
await appendEventAsync(eventsPath, {
|
|
60998
|
-
type: "pipeline:
|
|
61147
|
+
type: "pipeline:stage_completed",
|
|
60999
61148
|
runId,
|
|
61000
|
-
message: `Stage '${stage.name}'
|
|
61149
|
+
message: `Stage '${stage.name}' completed`,
|
|
61001
61150
|
data: {
|
|
61002
61151
|
stageIndex: i2,
|
|
61003
61152
|
stageName: stage.name,
|
|
61004
61153
|
duration,
|
|
61005
|
-
|
|
61154
|
+
resultCount: results.length
|
|
61006
61155
|
}
|
|
61007
61156
|
});
|
|
61157
|
+
} catch (error) {
|
|
61158
|
+
const duration = Date.now() - stageStartTime;
|
|
61159
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
61160
|
+
if (effectiveStopOnError) {
|
|
61161
|
+
stages.push({
|
|
61162
|
+
name: stage.name,
|
|
61163
|
+
status: "failed",
|
|
61164
|
+
results: [],
|
|
61165
|
+
error: errorMessage,
|
|
61166
|
+
duration
|
|
61167
|
+
});
|
|
61168
|
+
await appendEventAsync(eventsPath, {
|
|
61169
|
+
type: "pipeline:stage_failed",
|
|
61170
|
+
runId,
|
|
61171
|
+
message: `Stage '${stage.name}' failed: ${errorMessage}`,
|
|
61172
|
+
data: {
|
|
61173
|
+
stageIndex: i2,
|
|
61174
|
+
stageName: stage.name,
|
|
61175
|
+
duration,
|
|
61176
|
+
error: errorMessage
|
|
61177
|
+
}
|
|
61178
|
+
});
|
|
61179
|
+
await appendEventAsync(eventsPath, {
|
|
61180
|
+
type: "pipeline:failed",
|
|
61181
|
+
runId,
|
|
61182
|
+
message: `Pipeline '${workflow.name}' failed at stage '${stage.name}'`,
|
|
61183
|
+
data: { failedStage: stage.name, error: errorMessage }
|
|
61184
|
+
});
|
|
61185
|
+
return {
|
|
61186
|
+
stages,
|
|
61187
|
+
totalDuration: Date.now() - startTime,
|
|
61188
|
+
finalResults: previousResults,
|
|
61189
|
+
status: "failed"
|
|
61190
|
+
};
|
|
61191
|
+
} else {
|
|
61192
|
+
stages.push({
|
|
61193
|
+
name: stage.name,
|
|
61194
|
+
status: "failed",
|
|
61195
|
+
results: [],
|
|
61196
|
+
error: errorMessage,
|
|
61197
|
+
duration
|
|
61198
|
+
});
|
|
61199
|
+
await appendEventAsync(eventsPath, {
|
|
61200
|
+
type: "pipeline:stage_skipped",
|
|
61201
|
+
runId,
|
|
61202
|
+
message: `Stage '${stage.name}' skipped due to error`,
|
|
61203
|
+
data: {
|
|
61204
|
+
stageIndex: i2,
|
|
61205
|
+
stageName: stage.name,
|
|
61206
|
+
duration,
|
|
61207
|
+
error: errorMessage
|
|
61208
|
+
}
|
|
61209
|
+
});
|
|
61210
|
+
}
|
|
61008
61211
|
}
|
|
61009
61212
|
}
|
|
61213
|
+
await appendEventAsync(eventsPath, {
|
|
61214
|
+
type: "pipeline:completed",
|
|
61215
|
+
runId,
|
|
61216
|
+
message: `Pipeline '${workflow.name}' completed`,
|
|
61217
|
+
data: {
|
|
61218
|
+
stages: stages.map((s) => ({ name: s.name, status: s.status }))
|
|
61219
|
+
}
|
|
61220
|
+
});
|
|
61221
|
+
return {
|
|
61222
|
+
stages,
|
|
61223
|
+
totalDuration: Date.now() - startTime,
|
|
61224
|
+
finalResults: previousResults,
|
|
61225
|
+
status: stages.some((s) => s.status === "failed") ? "partial" : "completed"
|
|
61226
|
+
};
|
|
61227
|
+
} finally {
|
|
61228
|
+
await flushEventLogBuffer();
|
|
61010
61229
|
}
|
|
61011
|
-
await appendEventAsync(eventsPath, {
|
|
61012
|
-
type: "pipeline:completed",
|
|
61013
|
-
runId,
|
|
61014
|
-
message: `Pipeline '${workflow.name}' completed`,
|
|
61015
|
-
data: {
|
|
61016
|
-
stages: stages.map((s) => ({ name: s.name, status: s.status }))
|
|
61017
|
-
}
|
|
61018
|
-
});
|
|
61019
|
-
return {
|
|
61020
|
-
stages,
|
|
61021
|
-
totalDuration: Date.now() - startTime,
|
|
61022
|
-
finalResults: previousResults,
|
|
61023
|
-
status: stages.some((s) => s.status === "failed") ? "partial" : "completed"
|
|
61024
|
-
};
|
|
61025
61230
|
}
|
|
61026
61231
|
/**
|
|
61027
61232
|
* Execute a single stage, handling fan-out for array inputs.
|
|
@@ -68717,7 +68922,7 @@ var init_deterministic_ast = __esm({
|
|
|
68717
68922
|
});
|
|
68718
68923
|
|
|
68719
68924
|
// src/runtime/dwf-state-store.ts
|
|
68720
|
-
import { existsSync as existsSync70, mkdirSync as mkdirSync42, readFileSync as
|
|
68925
|
+
import { existsSync as existsSync70, mkdirSync as mkdirSync42, readFileSync as readFileSync68, unlinkSync as unlinkSync11 } from "node:fs";
|
|
68721
68926
|
import { dirname as dirname37 } from "node:path";
|
|
68722
68927
|
var DwfStore;
|
|
68723
68928
|
var init_dwf_state_store = __esm({
|
|
@@ -68738,7 +68943,7 @@ var init_dwf_state_store = __esm({
|
|
|
68738
68943
|
const path81 = this.path;
|
|
68739
68944
|
try {
|
|
68740
68945
|
if (!existsSync70(path81)) return void 0;
|
|
68741
|
-
const raw =
|
|
68946
|
+
const raw = readFileSync68(path81, "utf-8");
|
|
68742
68947
|
const parsed = JSON.parse(raw);
|
|
68743
68948
|
if (!parsed || typeof parsed !== "object" || typeof parsed.runId !== "string") return void 0;
|
|
68744
68949
|
return parsed;
|
|
@@ -69560,7 +69765,7 @@ var dynamic_workflow_runner_exports = {};
|
|
|
69560
69765
|
__export(dynamic_workflow_runner_exports, {
|
|
69561
69766
|
runDynamicWorkflow: () => runDynamicWorkflow
|
|
69562
69767
|
});
|
|
69563
|
-
import { readFileSync as
|
|
69768
|
+
import { readFileSync as readFileSync69 } from "node:fs";
|
|
69564
69769
|
import { join as join71 } from "node:path";
|
|
69565
69770
|
function assertStructuredCloneable(value, name) {
|
|
69566
69771
|
try {
|
|
@@ -69585,7 +69790,7 @@ function resolveScriptPath(workflow, cwd) {
|
|
|
69585
69790
|
);
|
|
69586
69791
|
}
|
|
69587
69792
|
async function loadWorkflowModule(scriptPath) {
|
|
69588
|
-
const scriptSource =
|
|
69793
|
+
const scriptSource = readFileSync69(scriptPath, "utf-8");
|
|
69589
69794
|
if (isDeterminismCheckEnabled()) {
|
|
69590
69795
|
assertDeterministicScript(scriptSource);
|
|
69591
69796
|
}
|
|
@@ -69708,7 +69913,7 @@ async function runDynamicWorkflow(input) {
|
|
|
69708
69913
|
}
|
|
69709
69914
|
function readFinalArtifact(artifactPath) {
|
|
69710
69915
|
try {
|
|
69711
|
-
return
|
|
69916
|
+
return readFileSync69(artifactPath, "utf-8");
|
|
69712
69917
|
} catch (error) {
|
|
69713
69918
|
logInternalError("dynamic-workflow-runner.readFinal", error, `artifactPath=${artifactPath}`);
|
|
69714
69919
|
return `(failed to read final artifact ${artifactPath})`;
|
|
@@ -75827,7 +76032,7 @@ function teamCommandContext(ctx) {
|
|
|
75827
76032
|
}
|
|
75828
76033
|
async function openTeamSettingsOverlay(ctx) {
|
|
75829
76034
|
if (!ctx.hasUI) return;
|
|
75830
|
-
const [{ updateConfig: updateConfig2, parseConfig: parseConfig2 }, { asCrewTheme:
|
|
76035
|
+
const [{ updateConfig: updateConfig2, parseConfig: parseConfig2 }, { asCrewTheme: asCrewTheme3 }, { createSettingsOverlay: createSettingsOverlay2 }] = await Promise.all([
|
|
75831
76036
|
Promise.resolve().then(() => (init_config(), config_exports)),
|
|
75832
76037
|
Promise.resolve().then(() => (init_theme_adapter(), theme_adapter_exports)),
|
|
75833
76038
|
Promise.resolve().then(() => (init_settings_overlay(), settings_overlay_exports))
|
|
@@ -75836,7 +76041,7 @@ async function openTeamSettingsOverlay(ctx) {
|
|
|
75836
76041
|
const config = loaded.config;
|
|
75837
76042
|
await ctx.ui.custom(
|
|
75838
76043
|
(_tui, _theme, _keybindings, done) => {
|
|
75839
|
-
const theme =
|
|
76044
|
+
const theme = asCrewTheme3(_theme);
|
|
75840
76045
|
const { overlay } = createSettingsOverlay2(
|
|
75841
76046
|
config,
|
|
75842
76047
|
theme,
|
|
@@ -79776,7 +79981,7 @@ init_orphan_worker_registry();
|
|
|
79776
79981
|
init_peer_dep();
|
|
79777
79982
|
|
|
79778
79983
|
// src/runtime/per-write-validator.ts
|
|
79779
|
-
import { readFileSync as
|
|
79984
|
+
import { readFileSync as readFileSync18 } from "node:fs";
|
|
79780
79985
|
import { extname as pathExtname } from "node:path";
|
|
79781
79986
|
function validateJson(content, _filePath) {
|
|
79782
79987
|
if (content.trim() === "") return { ok: true };
|
|
@@ -79820,7 +80025,7 @@ function validateWrittenFile(filePath) {
|
|
|
79820
80025
|
if (!validator) return null;
|
|
79821
80026
|
let content;
|
|
79822
80027
|
try {
|
|
79823
|
-
content =
|
|
80028
|
+
content = readFileSync18(filePath, "utf-8");
|
|
79824
80029
|
} catch {
|
|
79825
80030
|
return null;
|
|
79826
80031
|
}
|
|
@@ -81478,6 +81683,1119 @@ function registerCrewShortcuts(pi) {
|
|
|
81478
81683
|
}
|
|
81479
81684
|
var CREW_SHORTCUT_KEYS = CREW_SHORTCUTS.map((s) => s.key);
|
|
81480
81685
|
|
|
81686
|
+
// src/extension/crew-vibes/config.ts
|
|
81687
|
+
import { existsSync as existsSync76, mkdirSync as mkdirSync44, readFileSync as readFileSync75, writeFileSync as writeFileSync33 } from "node:fs";
|
|
81688
|
+
import { dirname as dirname39, join as join76 } from "node:path";
|
|
81689
|
+
|
|
81690
|
+
// src/extension/crew-vibes/font-detect.ts
|
|
81691
|
+
import { existsSync as existsSync75, readFileSync as readFileSync74 } from "node:fs";
|
|
81692
|
+
import { homedir as homedir11, platform } from "node:os";
|
|
81693
|
+
import { join as join75 } from "node:path";
|
|
81694
|
+
function fontPath() {
|
|
81695
|
+
const os16 = platform();
|
|
81696
|
+
const home = homedir11();
|
|
81697
|
+
if (os16 === "darwin") return join75(home, "Library", "Fonts", "crew-vibes.ttf");
|
|
81698
|
+
if (os16 === "linux") return join75(home, ".local", "share", "fonts", "crew-vibes.ttf");
|
|
81699
|
+
if (os16 === "win32") {
|
|
81700
|
+
const local = process.env.LOCALAPPDATA ?? join75(home, "AppData", "Local");
|
|
81701
|
+
return join75(local, "Microsoft", "Windows", "Fonts", "crew-vibes.ttf");
|
|
81702
|
+
}
|
|
81703
|
+
return "";
|
|
81704
|
+
}
|
|
81705
|
+
var _hasFontFile = null;
|
|
81706
|
+
function hasCrewFontFile() {
|
|
81707
|
+
if (_hasFontFile !== null) return _hasFontFile;
|
|
81708
|
+
const p = fontPath();
|
|
81709
|
+
_hasFontFile = p !== "" && existsSync75(p);
|
|
81710
|
+
return _hasFontFile;
|
|
81711
|
+
}
|
|
81712
|
+
function isWebTerminal() {
|
|
81713
|
+
if (process.env.GOTTY || process.env.WEBTERM) return true;
|
|
81714
|
+
if (process.env.TERM === "dumb") return true;
|
|
81715
|
+
try {
|
|
81716
|
+
let pid = process.pid;
|
|
81717
|
+
for (let i2 = 0; i2 < 6 && pid > 1; i2++) {
|
|
81718
|
+
const cgroup = readFileSync74(`/proc/${pid}/cgroup`, "utf8");
|
|
81719
|
+
if (cgroup.includes("gotty") || cgroup.includes("wetty")) return true;
|
|
81720
|
+
const match = cgroup.match(/\d+:.*:(.*)/);
|
|
81721
|
+
const status = readFileSync74(`/proc/${pid}/status`, "utf8");
|
|
81722
|
+
const ppid = status.match(/^PPid:\s+(\d+)/m);
|
|
81723
|
+
pid = ppid ? Number.parseInt(ppid[1], 10) : 1;
|
|
81724
|
+
}
|
|
81725
|
+
} catch {
|
|
81726
|
+
}
|
|
81727
|
+
return false;
|
|
81728
|
+
}
|
|
81729
|
+
|
|
81730
|
+
// src/extension/crew-vibes/config.ts
|
|
81731
|
+
var SPEED_STATUS_ID = "pi-crew-speed";
|
|
81732
|
+
var CAPACITY_STATUS_ID = "pi-crew-bar";
|
|
81733
|
+
var PROVIDER_STATUS_ID = "pi-crew-bar";
|
|
81734
|
+
function resolveHome() {
|
|
81735
|
+
return process.env.PI_TEAMS_HOME?.trim() || process.env.HOME || process.env.USERPROFILE || "";
|
|
81736
|
+
}
|
|
81737
|
+
function configPath2() {
|
|
81738
|
+
return join76(resolveHome(), ".pi", "agent", "pi-crew-vibes.json");
|
|
81739
|
+
}
|
|
81740
|
+
var DEFAULT_CONFIG2 = {
|
|
81741
|
+
enabled: true,
|
|
81742
|
+
speed: {
|
|
81743
|
+
enabled: true,
|
|
81744
|
+
footer: false,
|
|
81745
|
+
indicator: true,
|
|
81746
|
+
label: "tok/s",
|
|
81747
|
+
renderIntervalMs: 250,
|
|
81748
|
+
slidingWindowMs: 1e3,
|
|
81749
|
+
minReliableDurationMs: 1e3,
|
|
81750
|
+
maxDisplayTokS: 500,
|
|
81751
|
+
defaultIntervalMs: 167,
|
|
81752
|
+
minIntervalMs: 50,
|
|
81753
|
+
maxIntervalMs: 250,
|
|
81754
|
+
scale: 6e3
|
|
81755
|
+
},
|
|
81756
|
+
capacity: {
|
|
81757
|
+
enabled: true,
|
|
81758
|
+
tokenDisplay: "tokens",
|
|
81759
|
+
showLabel: true,
|
|
81760
|
+
refreshIntervalMs: 2e3,
|
|
81761
|
+
labels: ["Orbit", "Cruise", "Warp", "Black Hole", "Supernova", "Big Bang"],
|
|
81762
|
+
icons: ["\uE710", "\uE711", "\uE712", "\uE713", "\uE714", "\uE715"],
|
|
81763
|
+
providerUsage: true,
|
|
81764
|
+
providerRefreshMs: 3e5
|
|
81765
|
+
}
|
|
81766
|
+
};
|
|
81767
|
+
var FALLBACK_CAPACITY_ICONS = [
|
|
81768
|
+
"\u25CB ",
|
|
81769
|
+
// ○ empty circle (lean)
|
|
81770
|
+
"\u25D4 ",
|
|
81771
|
+
// ◔ circle with dot (chonking)
|
|
81772
|
+
"\u25D1 ",
|
|
81773
|
+
// ◑ circle half filled (chonky)
|
|
81774
|
+
"\u25CF ",
|
|
81775
|
+
// ● filled circle (big chonk)
|
|
81776
|
+
"\u2B24 ",
|
|
81777
|
+
// ⬤ large filled circle (mega chonk)
|
|
81778
|
+
"\u2B22 "
|
|
81779
|
+
// ⬢ filled hexagon (oh lawd)
|
|
81780
|
+
];
|
|
81781
|
+
function capacityIcons() {
|
|
81782
|
+
if (isWebTerminal()) return FALLBACK_CAPACITY_ICONS;
|
|
81783
|
+
return hasCrewFontFile() ? DEFAULT_CONFIG2.capacity.icons : FALLBACK_CAPACITY_ICONS;
|
|
81784
|
+
}
|
|
81785
|
+
function asRecord10(value) {
|
|
81786
|
+
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
81787
|
+
}
|
|
81788
|
+
function boolFrom(raw, fallback2) {
|
|
81789
|
+
return typeof raw === "boolean" ? raw : fallback2;
|
|
81790
|
+
}
|
|
81791
|
+
function stringFrom(raw, fallback2) {
|
|
81792
|
+
return typeof raw === "string" ? raw : fallback2;
|
|
81793
|
+
}
|
|
81794
|
+
function positiveFrom(raw, fallback2) {
|
|
81795
|
+
return typeof raw === "number" && Number.isFinite(raw) && raw > 0 ? raw : fallback2;
|
|
81796
|
+
}
|
|
81797
|
+
function sextet(raw, fallback2) {
|
|
81798
|
+
if (Array.isArray(raw) && raw.length === 6 && raw.every((entry) => typeof entry === "string")) {
|
|
81799
|
+
return raw;
|
|
81800
|
+
}
|
|
81801
|
+
return fallback2;
|
|
81802
|
+
}
|
|
81803
|
+
function tokenDisplayFrom(raw, fallback2) {
|
|
81804
|
+
return raw === "off" || raw === "tokens" || raw === "percentage" ? raw : fallback2;
|
|
81805
|
+
}
|
|
81806
|
+
function normalizeSpeed(raw) {
|
|
81807
|
+
const input = asRecord10(raw);
|
|
81808
|
+
const speed = {
|
|
81809
|
+
enabled: boolFrom(input.enabled, DEFAULT_CONFIG2.speed.enabled),
|
|
81810
|
+
footer: boolFrom(input.footer, DEFAULT_CONFIG2.speed.footer),
|
|
81811
|
+
indicator: boolFrom(input.indicator, DEFAULT_CONFIG2.speed.indicator),
|
|
81812
|
+
label: stringFrom(input.label, DEFAULT_CONFIG2.speed.label),
|
|
81813
|
+
renderIntervalMs: positiveFrom(input.renderIntervalMs, DEFAULT_CONFIG2.speed.renderIntervalMs),
|
|
81814
|
+
slidingWindowMs: positiveFrom(input.slidingWindowMs, DEFAULT_CONFIG2.speed.slidingWindowMs),
|
|
81815
|
+
minReliableDurationMs: positiveFrom(input.minReliableDurationMs, DEFAULT_CONFIG2.speed.minReliableDurationMs),
|
|
81816
|
+
maxDisplayTokS: positiveFrom(input.maxDisplayTokS, DEFAULT_CONFIG2.speed.maxDisplayTokS),
|
|
81817
|
+
defaultIntervalMs: positiveFrom(input.defaultIntervalMs, DEFAULT_CONFIG2.speed.defaultIntervalMs),
|
|
81818
|
+
minIntervalMs: positiveFrom(input.minIntervalMs, DEFAULT_CONFIG2.speed.minIntervalMs),
|
|
81819
|
+
maxIntervalMs: positiveFrom(input.maxIntervalMs, DEFAULT_CONFIG2.speed.maxIntervalMs),
|
|
81820
|
+
scale: positiveFrom(input.scale, DEFAULT_CONFIG2.speed.scale)
|
|
81821
|
+
};
|
|
81822
|
+
if (speed.minIntervalMs > speed.maxIntervalMs) {
|
|
81823
|
+
speed.minIntervalMs = DEFAULT_CONFIG2.speed.minIntervalMs;
|
|
81824
|
+
speed.maxIntervalMs = DEFAULT_CONFIG2.speed.maxIntervalMs;
|
|
81825
|
+
}
|
|
81826
|
+
return speed;
|
|
81827
|
+
}
|
|
81828
|
+
function normalizeCapacity(raw) {
|
|
81829
|
+
const input = asRecord10(raw);
|
|
81830
|
+
return {
|
|
81831
|
+
enabled: boolFrom(input.enabled, DEFAULT_CONFIG2.capacity.enabled),
|
|
81832
|
+
tokenDisplay: tokenDisplayFrom(input.tokenDisplay, DEFAULT_CONFIG2.capacity.tokenDisplay),
|
|
81833
|
+
showLabel: boolFrom(input.showLabel, DEFAULT_CONFIG2.capacity.showLabel),
|
|
81834
|
+
refreshIntervalMs: positiveFrom(input.refreshIntervalMs, DEFAULT_CONFIG2.capacity.refreshIntervalMs),
|
|
81835
|
+
labels: sextet(input.labels, DEFAULT_CONFIG2.capacity.labels),
|
|
81836
|
+
icons: sextet(input.icons, DEFAULT_CONFIG2.capacity.icons),
|
|
81837
|
+
providerUsage: boolFrom(input.providerUsage, DEFAULT_CONFIG2.capacity.providerUsage),
|
|
81838
|
+
providerRefreshMs: positiveFrom(input.providerRefreshMs, DEFAULT_CONFIG2.capacity.providerRefreshMs)
|
|
81839
|
+
};
|
|
81840
|
+
}
|
|
81841
|
+
function normalizeConfig(raw) {
|
|
81842
|
+
const input = asRecord10(raw);
|
|
81843
|
+
return {
|
|
81844
|
+
enabled: boolFrom(input.enabled, DEFAULT_CONFIG2.enabled),
|
|
81845
|
+
speed: normalizeSpeed(input.speed),
|
|
81846
|
+
capacity: normalizeCapacity(input.capacity)
|
|
81847
|
+
};
|
|
81848
|
+
}
|
|
81849
|
+
function loadConfig2() {
|
|
81850
|
+
try {
|
|
81851
|
+
const path81 = configPath2();
|
|
81852
|
+
if (!existsSync76(path81)) return normalizeConfig(void 0);
|
|
81853
|
+
return normalizeConfig(JSON.parse(readFileSync75(path81, "utf8")));
|
|
81854
|
+
} catch {
|
|
81855
|
+
return normalizeConfig(void 0);
|
|
81856
|
+
}
|
|
81857
|
+
}
|
|
81858
|
+
function saveConfig(config) {
|
|
81859
|
+
const path81 = configPath2();
|
|
81860
|
+
mkdirSync44(dirname39(path81), { recursive: true });
|
|
81861
|
+
writeFileSync33(path81, `${JSON.stringify(normalizeConfig(config), null, 2)}
|
|
81862
|
+
`);
|
|
81863
|
+
}
|
|
81864
|
+
|
|
81865
|
+
// src/extension/crew-vibes/provider-usage.ts
|
|
81866
|
+
import { readFileSync as readFileSync76 } from "node:fs";
|
|
81867
|
+
import { homedir as homedir12 } from "node:os";
|
|
81868
|
+
import { join as join77 } from "node:path";
|
|
81869
|
+
function withTimeout(ms, fn) {
|
|
81870
|
+
const controller = new AbortController();
|
|
81871
|
+
const timeoutId = setTimeout(() => controller.abort(), ms);
|
|
81872
|
+
return fn(controller.signal).finally(() => clearTimeout(timeoutId));
|
|
81873
|
+
}
|
|
81874
|
+
function piAuthPath() {
|
|
81875
|
+
return join77(homedir12(), ".pi", "agent", "auth.json");
|
|
81876
|
+
}
|
|
81877
|
+
function loadAnthropicToken() {
|
|
81878
|
+
const envToken = process.env.ANTHROPIC_OAUTH_TOKEN?.trim();
|
|
81879
|
+
if (envToken) return envToken;
|
|
81880
|
+
try {
|
|
81881
|
+
const data2 = JSON.parse(readFileSync76(piAuthPath(), "utf8"));
|
|
81882
|
+
const token = data2.anthropic?.access;
|
|
81883
|
+
return typeof token === "string" && token.length > 0 ? token : void 0;
|
|
81884
|
+
} catch {
|
|
81885
|
+
return void 0;
|
|
81886
|
+
}
|
|
81887
|
+
}
|
|
81888
|
+
function loadZaiToken() {
|
|
81889
|
+
const envKey = process.env.ZAI_API_KEY?.trim() || process.env.Z_AI_API_KEY?.trim();
|
|
81890
|
+
if (envKey) return envKey;
|
|
81891
|
+
try {
|
|
81892
|
+
const data2 = JSON.parse(readFileSync76(piAuthPath(), "utf8"));
|
|
81893
|
+
const key = data2["z-ai"]?.access || data2["z-ai"]?.key || data2.zai?.access || data2.zai?.key;
|
|
81894
|
+
return typeof key === "string" && key.length > 0 ? key : void 0;
|
|
81895
|
+
} catch {
|
|
81896
|
+
return void 0;
|
|
81897
|
+
}
|
|
81898
|
+
}
|
|
81899
|
+
var COPILOT_TOKEN_KEYS = ["oauth_token", "user_token", "github_token", "token"];
|
|
81900
|
+
function tokenFromHostEntry(entry) {
|
|
81901
|
+
if (!entry) return void 0;
|
|
81902
|
+
for (const key of COPILOT_TOKEN_KEYS) {
|
|
81903
|
+
const value = entry[key];
|
|
81904
|
+
if (typeof value === "string" && value.length > 0) return value;
|
|
81905
|
+
}
|
|
81906
|
+
return void 0;
|
|
81907
|
+
}
|
|
81908
|
+
function loadLegacyCopilotToken() {
|
|
81909
|
+
const configHome = process.env.XDG_CONFIG_HOME?.trim() || join77(homedir12(), ".config");
|
|
81910
|
+
const candidates = [join77(configHome, "github-copilot", "hosts.json"), join77(homedir12(), ".github-copilot", "hosts.json")];
|
|
81911
|
+
for (const hostsPath of candidates) {
|
|
81912
|
+
try {
|
|
81913
|
+
const data2 = JSON.parse(readFileSync76(hostsPath, "utf8"));
|
|
81914
|
+
if (!data2 || typeof data2 !== "object") continue;
|
|
81915
|
+
const normalized = {};
|
|
81916
|
+
for (const [host, entry] of Object.entries(data2)) {
|
|
81917
|
+
normalized[host.toLowerCase()] = entry;
|
|
81918
|
+
}
|
|
81919
|
+
const preferred = tokenFromHostEntry(normalized["github.com"]) ?? tokenFromHostEntry(normalized["api.github.com"]);
|
|
81920
|
+
if (preferred) return preferred;
|
|
81921
|
+
for (const entry of Object.values(normalized)) {
|
|
81922
|
+
const token = tokenFromHostEntry(entry);
|
|
81923
|
+
if (token) return token;
|
|
81924
|
+
}
|
|
81925
|
+
} catch {
|
|
81926
|
+
}
|
|
81927
|
+
}
|
|
81928
|
+
return void 0;
|
|
81929
|
+
}
|
|
81930
|
+
function loadCopilotToken() {
|
|
81931
|
+
const envToken = (process.env.COPILOT_GITHUB_TOKEN || process.env.GH_TOKEN || process.env.GITHUB_TOKEN || "").trim();
|
|
81932
|
+
if (envToken) return envToken;
|
|
81933
|
+
try {
|
|
81934
|
+
const data2 = JSON.parse(readFileSync76(piAuthPath(), "utf8"));
|
|
81935
|
+
const piToken = data2["github-copilot"]?.refresh || data2["github-copilot"]?.access;
|
|
81936
|
+
if (typeof piToken === "string" && piToken.length > 0) return piToken;
|
|
81937
|
+
} catch {
|
|
81938
|
+
}
|
|
81939
|
+
return loadLegacyCopilotToken();
|
|
81940
|
+
}
|
|
81941
|
+
async function fetchAnthropicUsage(token) {
|
|
81942
|
+
const data2 = await withTimeout(1e4, async (signal) => {
|
|
81943
|
+
const res = await fetch("https://api.anthropic.com/api/oauth/usage", {
|
|
81944
|
+
headers: {
|
|
81945
|
+
Authorization: `Bearer ${token}`,
|
|
81946
|
+
"anthropic-beta": "oauth-2025-04-20"
|
|
81947
|
+
},
|
|
81948
|
+
signal
|
|
81949
|
+
});
|
|
81950
|
+
if (!res.ok) throw new Error(`anthropic usage HTTP ${res.status}`);
|
|
81951
|
+
return await res.json();
|
|
81952
|
+
});
|
|
81953
|
+
return {
|
|
81954
|
+
fiveHourPercent: data2.five_hour?.utilization ?? 0,
|
|
81955
|
+
weeklyPercent: data2.seven_day?.utilization ?? 0,
|
|
81956
|
+
fiveHourResetAt: data2.five_hour?.resets_at ?? null,
|
|
81957
|
+
weeklyResetAt: data2.seven_day?.resets_at ?? null
|
|
81958
|
+
};
|
|
81959
|
+
}
|
|
81960
|
+
async function fetchCopilotMonthlyPercent(token) {
|
|
81961
|
+
const data2 = await withTimeout(1e4, async (signal) => {
|
|
81962
|
+
const res = await fetch("https://api.github.com/copilot_internal/user", {
|
|
81963
|
+
headers: {
|
|
81964
|
+
Authorization: `token ${token}`,
|
|
81965
|
+
"Editor-Version": "vscode/1.96.2",
|
|
81966
|
+
"User-Agent": "GitHubCopilotChat/0.26.7",
|
|
81967
|
+
"X-Github-Api-Version": "2025-04-01",
|
|
81968
|
+
Accept: "application/json"
|
|
81969
|
+
},
|
|
81970
|
+
signal
|
|
81971
|
+
});
|
|
81972
|
+
if (!res.ok) throw new Error(`copilot user HTTP ${res.status}`);
|
|
81973
|
+
return await res.json();
|
|
81974
|
+
});
|
|
81975
|
+
const percentRemaining = data2.quota_snapshots?.premium_interactions?.percent_remaining;
|
|
81976
|
+
if (typeof percentRemaining !== "number") return void 0;
|
|
81977
|
+
return Math.max(0, 100 - percentRemaining);
|
|
81978
|
+
}
|
|
81979
|
+
async function fetchZaiUsage(token) {
|
|
81980
|
+
const data2 = await withTimeout(1e4, async (signal) => {
|
|
81981
|
+
const res = await fetch("https://api.z.ai/api/monitor/usage/quota/limit", {
|
|
81982
|
+
method: "GET",
|
|
81983
|
+
headers: {
|
|
81984
|
+
Authorization: `Bearer ${token}`,
|
|
81985
|
+
Accept: "application/json"
|
|
81986
|
+
},
|
|
81987
|
+
signal
|
|
81988
|
+
});
|
|
81989
|
+
if (!res.ok) throw new Error(`z.ai usage HTTP ${res.status}`);
|
|
81990
|
+
return await res.json();
|
|
81991
|
+
});
|
|
81992
|
+
if (!data2.success || data2.code !== 200) throw new Error(data2.msg || "z.ai API error");
|
|
81993
|
+
const limits = data2.data?.limits ?? [];
|
|
81994
|
+
let tokensPercent = 0;
|
|
81995
|
+
let monthlyPercent = 0;
|
|
81996
|
+
let tokensResetAt = null;
|
|
81997
|
+
let monthlyResetAt = null;
|
|
81998
|
+
for (const limit of limits) {
|
|
81999
|
+
const pct = limit.percentage ?? 0;
|
|
82000
|
+
const resetIso = typeof limit.nextResetTime === "number" ? new Date(limit.nextResetTime).toISOString() : typeof limit.nextResetTime === "string" ? limit.nextResetTime : null;
|
|
82001
|
+
if (limit.type === "TOKENS_LIMIT") {
|
|
82002
|
+
tokensPercent = pct;
|
|
82003
|
+
tokensResetAt = resetIso;
|
|
82004
|
+
} else if (limit.type === "TIME_LIMIT") {
|
|
82005
|
+
monthlyPercent = pct;
|
|
82006
|
+
monthlyResetAt = resetIso;
|
|
82007
|
+
}
|
|
82008
|
+
}
|
|
82009
|
+
return {
|
|
82010
|
+
providerName: "z.ai",
|
|
82011
|
+
fiveHourPercent: tokensPercent,
|
|
82012
|
+
fiveHourResetAt: tokensResetAt,
|
|
82013
|
+
weeklyPercent: monthlyPercent,
|
|
82014
|
+
weeklyResetAt: monthlyResetAt
|
|
82015
|
+
};
|
|
82016
|
+
}
|
|
82017
|
+
var cachedUsage = null;
|
|
82018
|
+
var cachedAt = 0;
|
|
82019
|
+
function clearProviderUsageCache() {
|
|
82020
|
+
cachedUsage = null;
|
|
82021
|
+
cachedAt = 0;
|
|
82022
|
+
}
|
|
82023
|
+
async function fetchProviderUsage(maxAgeMs = 3e5) {
|
|
82024
|
+
if (cachedUsage !== null && Date.now() - cachedAt < maxAgeMs) {
|
|
82025
|
+
return cachedUsage;
|
|
82026
|
+
}
|
|
82027
|
+
try {
|
|
82028
|
+
const anthropicToken = loadAnthropicToken();
|
|
82029
|
+
if (anthropicToken) {
|
|
82030
|
+
const base = await fetchAnthropicUsage(anthropicToken);
|
|
82031
|
+
const usage = {
|
|
82032
|
+
providerName: "Claude",
|
|
82033
|
+
fiveHourPercent: base.fiveHourPercent,
|
|
82034
|
+
fiveHourResetAt: base.fiveHourResetAt,
|
|
82035
|
+
weeklyPercent: base.weeklyPercent,
|
|
82036
|
+
weeklyResetAt: base.weeklyResetAt
|
|
82037
|
+
};
|
|
82038
|
+
cachedUsage = usage;
|
|
82039
|
+
cachedAt = Date.now();
|
|
82040
|
+
return usage;
|
|
82041
|
+
}
|
|
82042
|
+
const zaiToken = loadZaiToken();
|
|
82043
|
+
if (zaiToken) {
|
|
82044
|
+
const usage = await fetchZaiUsage(zaiToken);
|
|
82045
|
+
usage.providerName = "z.ai";
|
|
82046
|
+
cachedUsage = usage;
|
|
82047
|
+
cachedAt = Date.now();
|
|
82048
|
+
return usage;
|
|
82049
|
+
}
|
|
82050
|
+
const copilotToken = loadCopilotToken();
|
|
82051
|
+
if (copilotToken) {
|
|
82052
|
+
const monthlyPercent = await fetchCopilotMonthlyPercent(copilotToken);
|
|
82053
|
+
if (monthlyPercent !== void 0) {
|
|
82054
|
+
const usage = {
|
|
82055
|
+
providerName: "Copilot",
|
|
82056
|
+
fiveHourPercent: 0,
|
|
82057
|
+
fiveHourResetAt: null,
|
|
82058
|
+
weeklyPercent: monthlyPercent,
|
|
82059
|
+
weeklyResetAt: null,
|
|
82060
|
+
copilotMonthlyPercent: monthlyPercent
|
|
82061
|
+
};
|
|
82062
|
+
cachedUsage = usage;
|
|
82063
|
+
cachedAt = Date.now();
|
|
82064
|
+
return usage;
|
|
82065
|
+
}
|
|
82066
|
+
}
|
|
82067
|
+
return null;
|
|
82068
|
+
} catch {
|
|
82069
|
+
return null;
|
|
82070
|
+
}
|
|
82071
|
+
}
|
|
82072
|
+
|
|
82073
|
+
// src/extension/crew-vibes/figures.ts
|
|
82074
|
+
var BRAILLE_FRAMES = [
|
|
82075
|
+
"\u280B ",
|
|
82076
|
+
// ⠋
|
|
82077
|
+
"\u2819 ",
|
|
82078
|
+
// ⠙
|
|
82079
|
+
"\u2839 ",
|
|
82080
|
+
// ⠹
|
|
82081
|
+
"\u2838 ",
|
|
82082
|
+
// ⠸
|
|
82083
|
+
"\u283C ",
|
|
82084
|
+
// ⠼
|
|
82085
|
+
"\u2834 ",
|
|
82086
|
+
// ⠴
|
|
82087
|
+
"\u2826 ",
|
|
82088
|
+
// ⠦
|
|
82089
|
+
"\u2827 ",
|
|
82090
|
+
// ⠧
|
|
82091
|
+
"\u2807 ",
|
|
82092
|
+
// ⠇
|
|
82093
|
+
"\u280F "
|
|
82094
|
+
// ⠏
|
|
82095
|
+
];
|
|
82096
|
+
var PUA_CREW_FRAMES = [
|
|
82097
|
+
"\uE700 ",
|
|
82098
|
+
"\uE701 ",
|
|
82099
|
+
"\uE702 ",
|
|
82100
|
+
"\uE703 ",
|
|
82101
|
+
"\uE704 ",
|
|
82102
|
+
"\uE705 ",
|
|
82103
|
+
"\uE706 ",
|
|
82104
|
+
"\uE707 ",
|
|
82105
|
+
"\uE708 ",
|
|
82106
|
+
"\uE709 ",
|
|
82107
|
+
"\uE70A ",
|
|
82108
|
+
"\uE70B ",
|
|
82109
|
+
"\uE70C ",
|
|
82110
|
+
"\uE70D ",
|
|
82111
|
+
"\uE70E ",
|
|
82112
|
+
"\uE70F "
|
|
82113
|
+
];
|
|
82114
|
+
function crewFrames(style = "pua") {
|
|
82115
|
+
if (style === "pua" && isWebTerminal()) return BRAILLE_FRAMES;
|
|
82116
|
+
return style === "pua" ? PUA_CREW_FRAMES : BRAILLE_FRAMES;
|
|
82117
|
+
}
|
|
82118
|
+
function intervalForSpeed(config, speed) {
|
|
82119
|
+
if (speed === null || !Number.isFinite(speed) || speed <= 0) return config.defaultIntervalMs;
|
|
82120
|
+
return Math.max(config.minIntervalMs, Math.min(config.maxIntervalMs, Math.round(config.scale / speed)));
|
|
82121
|
+
}
|
|
82122
|
+
function capacityIndex(percent, levels = 6) {
|
|
82123
|
+
if (percent === null || percent === void 0 || !Number.isFinite(percent)) return 0;
|
|
82124
|
+
return Math.max(0, Math.min(levels - 1, Math.floor(Math.max(0, Math.min(100, percent)) / 100 * levels)));
|
|
82125
|
+
}
|
|
82126
|
+
function isDangerStage(index, levels) {
|
|
82127
|
+
return index >= Math.max(0, levels - 2);
|
|
82128
|
+
}
|
|
82129
|
+
|
|
82130
|
+
// src/extension/crew-vibes/render.ts
|
|
82131
|
+
function formatCount(value) {
|
|
82132
|
+
if (value < 1e3) return value.toString();
|
|
82133
|
+
if (value < 1e4) return `${(value / 1e3).toFixed(1)}k`;
|
|
82134
|
+
if (value < 1e6) return `${Math.round(value / 1e3)}k`;
|
|
82135
|
+
if (value < 1e7) return `${(value / 1e6).toFixed(1)}M`;
|
|
82136
|
+
return `${Math.round(value / 1e6)}M`;
|
|
82137
|
+
}
|
|
82138
|
+
function asCrewTheme2(theme) {
|
|
82139
|
+
if (theme && typeof theme === "object" && typeof theme.fg === "function") {
|
|
82140
|
+
return theme;
|
|
82141
|
+
}
|
|
82142
|
+
return void 0;
|
|
82143
|
+
}
|
|
82144
|
+
function getCapacityUsage(ctx) {
|
|
82145
|
+
const fn = ctx.getContextUsage;
|
|
82146
|
+
const usage = typeof fn === "function" ? fn.call(ctx) : null;
|
|
82147
|
+
return {
|
|
82148
|
+
tokens: typeof usage?.tokens === "number" && Number.isFinite(usage.tokens) ? usage.tokens : null,
|
|
82149
|
+
percent: typeof usage?.percent === "number" && Number.isFinite(usage.percent) ? usage.percent : null
|
|
82150
|
+
};
|
|
82151
|
+
}
|
|
82152
|
+
function formatSpeed(config, speed) {
|
|
82153
|
+
return speed === null ? `-- ${config.label}` : `${speed.toFixed(1)} ${config.label}`;
|
|
82154
|
+
}
|
|
82155
|
+
function renderSpeedFooter(theme, config, speed) {
|
|
82156
|
+
const value = speed === null ? "--" : speed.toFixed(1);
|
|
82157
|
+
const valueTone = speed === null ? "dim" : "accent";
|
|
82158
|
+
const styled = theme ? `${theme.fg(valueTone, value)} ${theme.fg("dim", config.label)}` : `${value} ${config.label}`;
|
|
82159
|
+
return styled;
|
|
82160
|
+
}
|
|
82161
|
+
function renderWorkingMessage(theme, config, speed) {
|
|
82162
|
+
const left = "Working";
|
|
82163
|
+
const speedText = theme ? `${theme.fg(speed === null ? "dim" : "accent", speed === null ? "--" : speed.toFixed(1))} ${theme.fg("dim", config.label)}` : `${speed === null ? "--" : speed.toFixed(1)} ${config.label}`;
|
|
82164
|
+
return theme ? `${theme.fg("muted", left)} ${speedText}` : `${left} ${speedText}`;
|
|
82165
|
+
}
|
|
82166
|
+
function crewIndicatorFrames(theme) {
|
|
82167
|
+
const frames = crewFrames();
|
|
82168
|
+
if (!theme) return [...frames];
|
|
82169
|
+
return frames.map((frame) => theme.fg("accent", frame));
|
|
82170
|
+
}
|
|
82171
|
+
function formatCapacityPrefix(config, usage) {
|
|
82172
|
+
const display = config.tokenDisplay;
|
|
82173
|
+
if (display === "off") return "";
|
|
82174
|
+
if (display === "percentage") {
|
|
82175
|
+
return `${usage.percent === null ? "?" : Math.round(Math.max(0, Math.min(999, usage.percent)))}% `;
|
|
82176
|
+
}
|
|
82177
|
+
return `${usage.tokens === null ? "?" : formatCount(usage.tokens)} `;
|
|
82178
|
+
}
|
|
82179
|
+
function colorStage(theme, index, levels, text) {
|
|
82180
|
+
if (!theme || text.length === 0) return text;
|
|
82181
|
+
return theme.fg(isDangerStage(index, levels) ? "error" : "success", text);
|
|
82182
|
+
}
|
|
82183
|
+
function renderCapacity(theme, config, usage) {
|
|
82184
|
+
const icons = capacityIcons();
|
|
82185
|
+
const levels = icons.length;
|
|
82186
|
+
const index = capacityIndex(usage.percent, levels);
|
|
82187
|
+
const icon = icons[index] ?? icons[0];
|
|
82188
|
+
const label = config.labels[index] ?? config.labels[0];
|
|
82189
|
+
const prefix = theme ? theme.fg("muted", formatCapacityPrefix(config, usage)) : formatCapacityPrefix(config, usage);
|
|
82190
|
+
const coloredIcon = colorStage(theme, index, levels, icon);
|
|
82191
|
+
const afterIcon = config.showLabel ? ` ${colorStage(theme, index, levels, label)}` : " ";
|
|
82192
|
+
return `${prefix}${coloredIcon}${afterIcon}`;
|
|
82193
|
+
}
|
|
82194
|
+
function setSpeedStatus(ctx, config, text) {
|
|
82195
|
+
if (!ctx?.hasUI) return;
|
|
82196
|
+
if (!config.enabled || !config.speed.enabled || !config.speed.footer) {
|
|
82197
|
+
ctx.ui.setStatus(SPEED_STATUS_ID, void 0);
|
|
82198
|
+
return;
|
|
82199
|
+
}
|
|
82200
|
+
ctx.ui.setStatus(SPEED_STATUS_ID, text);
|
|
82201
|
+
}
|
|
82202
|
+
function setCapacityStatus(ctx, config, text) {
|
|
82203
|
+
if (!ctx?.hasUI) return;
|
|
82204
|
+
if (!config.enabled || !config.capacity.enabled) {
|
|
82205
|
+
ctx.ui.setStatus(CAPACITY_STATUS_ID, void 0);
|
|
82206
|
+
return;
|
|
82207
|
+
}
|
|
82208
|
+
ctx.ui.setStatus(CAPACITY_STATUS_ID, text);
|
|
82209
|
+
}
|
|
82210
|
+
function clearVibesStatus(ctx) {
|
|
82211
|
+
if (!ctx?.hasUI) return;
|
|
82212
|
+
ctx.ui.setStatus(SPEED_STATUS_ID, void 0);
|
|
82213
|
+
ctx.ui.setStatus(CAPACITY_STATUS_ID, void 0);
|
|
82214
|
+
ctx.ui.setStatus(PROVIDER_STATUS_ID, void 0);
|
|
82215
|
+
if (ctx.ui.setWorkingIndicator) ctx.ui.setWorkingIndicator();
|
|
82216
|
+
if (ctx.ui.setWorkingMessage) ctx.ui.setWorkingMessage();
|
|
82217
|
+
}
|
|
82218
|
+
function formatResetTimer(resetAt) {
|
|
82219
|
+
if (!resetAt) return null;
|
|
82220
|
+
const diffMs = new Date(resetAt).getTime() - Date.now();
|
|
82221
|
+
if (diffMs < 0) return null;
|
|
82222
|
+
const mins = Math.floor(diffMs / 6e4);
|
|
82223
|
+
if (mins < 60) return `${mins}m`;
|
|
82224
|
+
const hours = Math.floor(mins / 60);
|
|
82225
|
+
if (hours < 48) {
|
|
82226
|
+
const remMins = mins % 60;
|
|
82227
|
+
return remMins > 0 ? `${hours}h${remMins}m` : `${hours}h`;
|
|
82228
|
+
}
|
|
82229
|
+
const days = Math.floor(hours / 24);
|
|
82230
|
+
const remHours = hours % 24;
|
|
82231
|
+
return remHours > 0 ? `${days}d${remHours}h` : `${days}d`;
|
|
82232
|
+
}
|
|
82233
|
+
function renderBar(percent, width = 8) {
|
|
82234
|
+
const clamped = Math.max(0, Math.min(100, percent));
|
|
82235
|
+
const filled = Math.round(clamped / 100 * width);
|
|
82236
|
+
return `${"\u2501".repeat(filled)}${"\u2504".repeat(width - filled)}`;
|
|
82237
|
+
}
|
|
82238
|
+
function renderProviderUsage(theme, usage) {
|
|
82239
|
+
if (!usage) return void 0;
|
|
82240
|
+
const parts = [];
|
|
82241
|
+
if (usage.providerName) {
|
|
82242
|
+
const nameText = usage.providerName;
|
|
82243
|
+
parts.push(theme ? theme.fg("muted", nameText) : nameText);
|
|
82244
|
+
}
|
|
82245
|
+
const fiveHourBar = renderBar(usage.fiveHourPercent);
|
|
82246
|
+
const fiveHourRounded = Math.round(usage.fiveHourPercent);
|
|
82247
|
+
const fiveHourReset = formatResetTimer(usage.fiveHourResetAt);
|
|
82248
|
+
const fiveHourText = `5h ${fiveHourBar} ${fiveHourRounded}%${fiveHourReset ? " " + fiveHourReset : ""}`;
|
|
82249
|
+
const fiveHourColor = usage.fiveHourPercent >= 80 ? "error" : "accent";
|
|
82250
|
+
parts.push(theme ? theme.fg(fiveHourColor, fiveHourText) : fiveHourText);
|
|
82251
|
+
const weeklyBar = renderBar(usage.weeklyPercent);
|
|
82252
|
+
const weeklyRounded = Math.round(usage.weeklyPercent);
|
|
82253
|
+
const weeklyReset = formatResetTimer(usage.weeklyResetAt);
|
|
82254
|
+
const weeklyText = `Wk ${weeklyBar} ${weeklyRounded}%${weeklyReset ? " " + weeklyReset : ""}`;
|
|
82255
|
+
parts.push(theme ? theme.fg("dim", weeklyText) : weeklyText);
|
|
82256
|
+
if (typeof usage.copilotMonthlyPercent === "number" && Number.isFinite(usage.copilotMonthlyPercent)) {
|
|
82257
|
+
const monthlyRounded = Math.round(usage.copilotMonthlyPercent);
|
|
82258
|
+
const monthlyText = `Mo: ${monthlyRounded}%`;
|
|
82259
|
+
parts.push(theme ? theme.fg("dim", monthlyText) : monthlyText);
|
|
82260
|
+
}
|
|
82261
|
+
return parts.join(" ");
|
|
82262
|
+
}
|
|
82263
|
+
|
|
82264
|
+
// src/extension/crew-vibes/speed.ts
|
|
82265
|
+
var COMPACTION_THRESHOLD = 5e3;
|
|
82266
|
+
function estimateTokensFromDelta(text) {
|
|
82267
|
+
if (!text) return 0;
|
|
82268
|
+
const matches = text.match(/\w+|[^\s\w]/g);
|
|
82269
|
+
return matches ? matches.length : 0;
|
|
82270
|
+
}
|
|
82271
|
+
var TokenSpeedEngine = class {
|
|
82272
|
+
_isStreaming = false;
|
|
82273
|
+
_tokenCount = 0;
|
|
82274
|
+
_startTime = 0;
|
|
82275
|
+
_endTime = 0;
|
|
82276
|
+
_events = [];
|
|
82277
|
+
_windowStartIndex = 0;
|
|
82278
|
+
_lastStableTokS = 0;
|
|
82279
|
+
_lastUsageOutput = 0;
|
|
82280
|
+
_config;
|
|
82281
|
+
constructor(config) {
|
|
82282
|
+
this._config = config;
|
|
82283
|
+
}
|
|
82284
|
+
updateConfig(config) {
|
|
82285
|
+
this._config = config;
|
|
82286
|
+
}
|
|
82287
|
+
get isStreaming() {
|
|
82288
|
+
return this._isStreaming;
|
|
82289
|
+
}
|
|
82290
|
+
get tokenCount() {
|
|
82291
|
+
return this._tokenCount;
|
|
82292
|
+
}
|
|
82293
|
+
get elapsedMs() {
|
|
82294
|
+
if (this._startTime === 0) return 0;
|
|
82295
|
+
return this._isStreaming ? Date.now() - this._startTime : this._endTime - this._startTime;
|
|
82296
|
+
}
|
|
82297
|
+
get avgTokS() {
|
|
82298
|
+
const elapsedSec = this.elapsedMs / 1e3;
|
|
82299
|
+
if (elapsedSec <= 0) return 0;
|
|
82300
|
+
return this._tokenCount / elapsedSec;
|
|
82301
|
+
}
|
|
82302
|
+
sanitizeTokS(value, durationMs = this.elapsedMs) {
|
|
82303
|
+
if (value === null || !Number.isFinite(value) || value <= 0) return null;
|
|
82304
|
+
if (durationMs < this._config.minReliableDurationMs) return null;
|
|
82305
|
+
if (value > this._config.maxDisplayTokS) return null;
|
|
82306
|
+
return value;
|
|
82307
|
+
}
|
|
82308
|
+
get tokS() {
|
|
82309
|
+
const candidate = this.rawTokS;
|
|
82310
|
+
const stable = this.sanitizeTokS(candidate);
|
|
82311
|
+
if (stable !== null) this._lastStableTokS = stable;
|
|
82312
|
+
return this._lastStableTokS;
|
|
82313
|
+
}
|
|
82314
|
+
get rawTokS() {
|
|
82315
|
+
if (this.elapsedMs < this._config.slidingWindowMs) return this.avgTokS;
|
|
82316
|
+
if (!this._isStreaming) return this.avgTokS;
|
|
82317
|
+
const now = Date.now();
|
|
82318
|
+
const windowStart = now - this._config.slidingWindowMs;
|
|
82319
|
+
while (this._windowStartIndex < this._events.length && this._events[this._windowStartIndex].time < windowStart) {
|
|
82320
|
+
this._windowStartIndex++;
|
|
82321
|
+
}
|
|
82322
|
+
if (this._windowStartIndex >= this._events.length) return this.avgTokS;
|
|
82323
|
+
let windowTokenCount = 0;
|
|
82324
|
+
for (let i2 = this._windowStartIndex; i2 < this._events.length; i2++) {
|
|
82325
|
+
windowTokenCount += this._events[i2].tokens;
|
|
82326
|
+
}
|
|
82327
|
+
if (windowTokenCount === 0) return this.avgTokS;
|
|
82328
|
+
const windowDuration = (now - this._events[this._windowStartIndex].time) / 1e3;
|
|
82329
|
+
if (windowDuration <= 0) return 0;
|
|
82330
|
+
return windowTokenCount / windowDuration;
|
|
82331
|
+
}
|
|
82332
|
+
start() {
|
|
82333
|
+
this._tokenCount = 0;
|
|
82334
|
+
this._isStreaming = true;
|
|
82335
|
+
this._startTime = Date.now();
|
|
82336
|
+
this._endTime = this._startTime;
|
|
82337
|
+
this._events = [];
|
|
82338
|
+
this._windowStartIndex = 0;
|
|
82339
|
+
this._lastStableTokS = 0;
|
|
82340
|
+
this._lastUsageOutput = 0;
|
|
82341
|
+
}
|
|
82342
|
+
stop() {
|
|
82343
|
+
this._isStreaming = false;
|
|
82344
|
+
this._endTime = Date.now();
|
|
82345
|
+
this._events = [];
|
|
82346
|
+
this._windowStartIndex = 0;
|
|
82347
|
+
}
|
|
82348
|
+
recordDelta(delta, usageOutput) {
|
|
82349
|
+
if (!this._isStreaming) return;
|
|
82350
|
+
if (usageOutput !== void 0 && usageOutput > 0) {
|
|
82351
|
+
const increment = usageOutput - this._lastUsageOutput;
|
|
82352
|
+
this._lastUsageOutput = usageOutput;
|
|
82353
|
+
this.recordTokens(Math.max(0, increment));
|
|
82354
|
+
return;
|
|
82355
|
+
}
|
|
82356
|
+
this.recordTokens(estimateTokensFromDelta(delta));
|
|
82357
|
+
}
|
|
82358
|
+
reconcileTotal(tokens) {
|
|
82359
|
+
if (tokens > 0) this._tokenCount = tokens;
|
|
82360
|
+
}
|
|
82361
|
+
recordTokens(tokens) {
|
|
82362
|
+
if (!this._isStreaming || tokens <= 0) return;
|
|
82363
|
+
this._tokenCount += tokens;
|
|
82364
|
+
this._events.push({ time: Date.now(), tokens });
|
|
82365
|
+
if (this._windowStartIndex >= COMPACTION_THRESHOLD) this.compact();
|
|
82366
|
+
}
|
|
82367
|
+
compact() {
|
|
82368
|
+
if (this._windowStartIndex === 0) return;
|
|
82369
|
+
this._events = this._events.slice(this._windowStartIndex);
|
|
82370
|
+
this._windowStartIndex = 0;
|
|
82371
|
+
}
|
|
82372
|
+
};
|
|
82373
|
+
function isSuccessfulStop(stopReason) {
|
|
82374
|
+
return stopReason !== "error" && stopReason !== "aborted";
|
|
82375
|
+
}
|
|
82376
|
+
var SpeedTracker = class {
|
|
82377
|
+
engine;
|
|
82378
|
+
lastStableTokS = null;
|
|
82379
|
+
sessionOutputTokens = 0;
|
|
82380
|
+
sessionDurationMs = 0;
|
|
82381
|
+
constructor(config) {
|
|
82382
|
+
this.engine = new TokenSpeedEngine(config);
|
|
82383
|
+
}
|
|
82384
|
+
updateConfig(config) {
|
|
82385
|
+
this.engine.updateConfig(config);
|
|
82386
|
+
}
|
|
82387
|
+
get isStreaming() {
|
|
82388
|
+
return this.engine.isStreaming;
|
|
82389
|
+
}
|
|
82390
|
+
get lastTokS() {
|
|
82391
|
+
return this.lastStableTokS;
|
|
82392
|
+
}
|
|
82393
|
+
resetSession() {
|
|
82394
|
+
this.sessionOutputTokens = 0;
|
|
82395
|
+
this.sessionDurationMs = 0;
|
|
82396
|
+
}
|
|
82397
|
+
startMessage() {
|
|
82398
|
+
this.engine.start();
|
|
82399
|
+
}
|
|
82400
|
+
recordDelta(delta, usageOutput) {
|
|
82401
|
+
this.engine.recordDelta(delta, usageOutput);
|
|
82402
|
+
}
|
|
82403
|
+
stopMessage() {
|
|
82404
|
+
if (this.engine.isStreaming) this.engine.stop();
|
|
82405
|
+
}
|
|
82406
|
+
liveTokS() {
|
|
82407
|
+
const speed = this.engine.tokS;
|
|
82408
|
+
return speed > 0 ? speed : this.lastStableTokS;
|
|
82409
|
+
}
|
|
82410
|
+
sessionAvgTokS() {
|
|
82411
|
+
return this.sessionDurationMs > 0 ? this.sessionOutputTokens / (this.sessionDurationMs / 1e3) : null;
|
|
82412
|
+
}
|
|
82413
|
+
finishMessage(outputTokens, stopReason) {
|
|
82414
|
+
if (!this.engine.isStreaming) return null;
|
|
82415
|
+
this.engine.reconcileTotal(outputTokens);
|
|
82416
|
+
const durationMs = this.engine.elapsedMs;
|
|
82417
|
+
const tokens = this.engine.tokenCount;
|
|
82418
|
+
const rawAvgTokS = durationMs > 0 ? tokens / (durationMs / 1e3) : null;
|
|
82419
|
+
const tokS = this.engine.sanitizeTokS(rawAvgTokS, durationMs);
|
|
82420
|
+
this.lastStableTokS = tokS;
|
|
82421
|
+
this.engine.stop();
|
|
82422
|
+
if (tokS !== null && isSuccessfulStop(stopReason)) {
|
|
82423
|
+
this.sessionOutputTokens += tokens;
|
|
82424
|
+
this.sessionDurationMs += durationMs;
|
|
82425
|
+
}
|
|
82426
|
+
return { outputTokens: tokens, durationMs, tokS };
|
|
82427
|
+
}
|
|
82428
|
+
};
|
|
82429
|
+
var SpeedAnimator = class {
|
|
82430
|
+
from = null;
|
|
82431
|
+
target = null;
|
|
82432
|
+
startedAt = 0;
|
|
82433
|
+
durationMs;
|
|
82434
|
+
constructor(durationMs) {
|
|
82435
|
+
this.durationMs = durationMs;
|
|
82436
|
+
}
|
|
82437
|
+
updateDuration(durationMs) {
|
|
82438
|
+
this.durationMs = durationMs;
|
|
82439
|
+
}
|
|
82440
|
+
reset(value = null, now = Date.now()) {
|
|
82441
|
+
this.from = value;
|
|
82442
|
+
this.target = value;
|
|
82443
|
+
this.startedAt = now;
|
|
82444
|
+
}
|
|
82445
|
+
setTarget(target, now = Date.now()) {
|
|
82446
|
+
if (target === null) {
|
|
82447
|
+
this.reset(null, now);
|
|
82448
|
+
return null;
|
|
82449
|
+
}
|
|
82450
|
+
const current2 = this.value(now);
|
|
82451
|
+
if (current2 === null) {
|
|
82452
|
+
this.reset(target, now);
|
|
82453
|
+
return target;
|
|
82454
|
+
}
|
|
82455
|
+
if (this.target !== null && Math.abs(target - this.target) < 0.05) return current2;
|
|
82456
|
+
this.from = current2;
|
|
82457
|
+
this.target = target;
|
|
82458
|
+
this.startedAt = now;
|
|
82459
|
+
return current2;
|
|
82460
|
+
}
|
|
82461
|
+
value(now = Date.now()) {
|
|
82462
|
+
if (this.target === null) return null;
|
|
82463
|
+
if (this.from === null || this.durationMs <= 0) return this.target;
|
|
82464
|
+
const progress = Math.max(0, Math.min(1, (now - this.startedAt) / this.durationMs));
|
|
82465
|
+
if (progress >= 1) {
|
|
82466
|
+
this.from = this.target;
|
|
82467
|
+
return this.target;
|
|
82468
|
+
}
|
|
82469
|
+
return this.from + (this.target - this.from) * progress;
|
|
82470
|
+
}
|
|
82471
|
+
isAnimating(now = Date.now()) {
|
|
82472
|
+
return this.target !== null && this.from !== null && Math.abs(this.target - this.from) >= 0.05 && now - this.startedAt < this.durationMs;
|
|
82473
|
+
}
|
|
82474
|
+
};
|
|
82475
|
+
|
|
82476
|
+
// src/extension/crew-vibes/cat-frames.ts
|
|
82477
|
+
var CAT_FRAMES2 = [
|
|
82478
|
+
// Frame 0 — standing
|
|
82479
|
+
["\u2584\u2584 \u2588\u2588\u2588\u2584 ", "\u2580\u2580\u2588\u2588\u2588\u2588\u2588\u2584", " \u2588\u2588\u2580\u2588\u2588 ", " \u2580\u2580 \u2580\u2580"],
|
|
82480
|
+
// Frame 1 — step 1
|
|
82481
|
+
[" \u2588\u2588\u2588\u2588 ", " \u2588\u2588\u2588\u2588\u2588\u2584", "\u2584\u2588\u2588\u2588\u2588\u2588\u2588\u2580", " \u2588\u2588 \u2580\u2588\u2584"],
|
|
82482
|
+
// Frame 2 — step 2
|
|
82483
|
+
[" \u2584\u2588\u2588\u2588\u2588\u2588\u2588", " \u2588\u2588\u2588\u2588\u2588\u2580 ", "\u2588\u2588\u2588\u2588\u2588\u2588\u2588 ", "\u2580\u2588\u2588\u2588\u2588\u2588\u2588 "],
|
|
82484
|
+
// Frame 3 — step 3
|
|
82485
|
+
[" \u2588\u2588\u2588\u2588 ", "\u2584\u2588\u2588\u2588\u2588\u2588\u2584 ", "\u2580\u2588\u2588\u2588\u2588\u2588\u2588\u2584", "\u2584\u2588\u2580 \u2588\u2588 "]
|
|
82486
|
+
];
|
|
82487
|
+
var CAT_FRAME_COUNT = CAT_FRAMES2.length;
|
|
82488
|
+
|
|
82489
|
+
// src/extension/crew-vibes/index.ts
|
|
82490
|
+
function isAssistantMessage(message) {
|
|
82491
|
+
return typeof message === "object" && message !== null && message.role === "assistant";
|
|
82492
|
+
}
|
|
82493
|
+
function assistantUsageOutput(message) {
|
|
82494
|
+
const usage = message.usage;
|
|
82495
|
+
const output = usage?.output;
|
|
82496
|
+
return typeof output === "number" && Number.isFinite(output) ? output : void 0;
|
|
82497
|
+
}
|
|
82498
|
+
function assistantStopReason(message) {
|
|
82499
|
+
const reason = message.stopReason;
|
|
82500
|
+
return typeof reason === "string" ? reason : void 0;
|
|
82501
|
+
}
|
|
82502
|
+
function assistantEventType(event) {
|
|
82503
|
+
return typeof event.type === "string" ? event.type : void 0;
|
|
82504
|
+
}
|
|
82505
|
+
function registerCrewVibes(pi) {
|
|
82506
|
+
let config = loadConfig2();
|
|
82507
|
+
let lastRenderedAt = 0;
|
|
82508
|
+
let currentIntervalMs = 0;
|
|
82509
|
+
const speedTracker = new SpeedTracker(config.speed);
|
|
82510
|
+
const footerAnimator = new SpeedAnimator(config.speed.renderIntervalMs);
|
|
82511
|
+
let liveTimer;
|
|
82512
|
+
let footerTimer;
|
|
82513
|
+
let capacityTimer;
|
|
82514
|
+
let providerTimer;
|
|
82515
|
+
let lastProviderText;
|
|
82516
|
+
let catFrameIndex = 0;
|
|
82517
|
+
function visibleLen(text) {
|
|
82518
|
+
return text.replace(/\x1b\[[0-9;]*m/g, "").length;
|
|
82519
|
+
}
|
|
82520
|
+
function spreadLine(left, right) {
|
|
82521
|
+
const cols = process.stdout.columns || 120;
|
|
82522
|
+
const padding = Math.max(2, cols - visibleLen(left) - visibleLen(right));
|
|
82523
|
+
return left + "\xA0".repeat(padding) + right;
|
|
82524
|
+
}
|
|
82525
|
+
function themeOf(ctx) {
|
|
82526
|
+
return asCrewTheme2(ctx.hasUI ? ctx.ui.theme : void 0);
|
|
82527
|
+
}
|
|
82528
|
+
function publishCapacity(ctx) {
|
|
82529
|
+
if (!ctx?.hasUI) return;
|
|
82530
|
+
if (!config.enabled || !config.capacity.enabled) {
|
|
82531
|
+
setCapacityStatus(ctx, config, void 0);
|
|
82532
|
+
return;
|
|
82533
|
+
}
|
|
82534
|
+
const capText = renderCapacity(themeOf(ctx), config.capacity, getCapacityUsage(ctx));
|
|
82535
|
+
const combined = lastProviderText ? spreadLine(capText, lastProviderText) : capText;
|
|
82536
|
+
setCapacityStatus(ctx, config, combined);
|
|
82537
|
+
}
|
|
82538
|
+
function publishSpeedFooter(ctx, speed = footerAnimator.value()) {
|
|
82539
|
+
if (!config.enabled || !config.speed.enabled || !config.speed.footer) {
|
|
82540
|
+
setSpeedStatus(ctx, config, void 0);
|
|
82541
|
+
return;
|
|
82542
|
+
}
|
|
82543
|
+
setSpeedStatus(ctx, config, renderSpeedFooter(themeOf(ctx), config.speed, speed));
|
|
82544
|
+
}
|
|
82545
|
+
function applyIndicator(ctx, speed, force = false) {
|
|
82546
|
+
if (!ctx.hasUI || !ctx.ui.setWorkingIndicator) return;
|
|
82547
|
+
if (!config.enabled || !config.speed.enabled || !config.speed.indicator) {
|
|
82548
|
+
ctx.ui.setWorkingIndicator();
|
|
82549
|
+
return;
|
|
82550
|
+
}
|
|
82551
|
+
const next = intervalForSpeed(config.speed, speed);
|
|
82552
|
+
if (!force && Math.abs(next - currentIntervalMs) < 10) return;
|
|
82553
|
+
ctx.ui.setWorkingIndicator({
|
|
82554
|
+
frames: crewIndicatorFrames(themeOf(ctx)),
|
|
82555
|
+
intervalMs: next
|
|
82556
|
+
});
|
|
82557
|
+
currentIntervalMs = next;
|
|
82558
|
+
}
|
|
82559
|
+
function renderWorking(ctx, speed) {
|
|
82560
|
+
if (!config.enabled || !config.speed.enabled || !ctx.hasUI) return;
|
|
82561
|
+
ctx.ui.setWorkingMessage(renderWorkingMessage(themeOf(ctx), config.speed, speed));
|
|
82562
|
+
}
|
|
82563
|
+
function stopLiveTimer() {
|
|
82564
|
+
if (!liveTimer) return;
|
|
82565
|
+
clearInterval(liveTimer);
|
|
82566
|
+
liveTimer = void 0;
|
|
82567
|
+
}
|
|
82568
|
+
function stopFooterTimer() {
|
|
82569
|
+
if (!footerTimer) return;
|
|
82570
|
+
clearInterval(footerTimer);
|
|
82571
|
+
footerTimer = void 0;
|
|
82572
|
+
}
|
|
82573
|
+
function stopCapacityTimer() {
|
|
82574
|
+
if (!capacityTimer) return;
|
|
82575
|
+
clearInterval(capacityTimer);
|
|
82576
|
+
capacityTimer = void 0;
|
|
82577
|
+
}
|
|
82578
|
+
function stopProviderTimer() {
|
|
82579
|
+
if (!providerTimer) return;
|
|
82580
|
+
clearInterval(providerTimer);
|
|
82581
|
+
providerTimer = void 0;
|
|
82582
|
+
}
|
|
82583
|
+
function startLiveTimer(ctx) {
|
|
82584
|
+
if (liveTimer || !ctx.hasUI) return;
|
|
82585
|
+
liveTimer = setInterval(() => {
|
|
82586
|
+
if (!config.enabled || !config.speed.enabled || !speedTracker.isStreaming) {
|
|
82587
|
+
stopLiveTimer();
|
|
82588
|
+
return;
|
|
82589
|
+
}
|
|
82590
|
+
const speed = speedTracker.liveTokS();
|
|
82591
|
+
applyIndicator(ctx, speed);
|
|
82592
|
+
renderWorking(ctx, speed);
|
|
82593
|
+
if (isWebTerminal()) {
|
|
82594
|
+
catFrameIndex = (catFrameIndex + 1) % CAT_FRAMES2.length;
|
|
82595
|
+
ctx.ui.setWidget("crew-vibes-cat", [...CAT_FRAMES2[catFrameIndex]], { placement: "aboveEditor" });
|
|
82596
|
+
}
|
|
82597
|
+
}, config.speed.renderIntervalMs);
|
|
82598
|
+
liveTimer.unref?.();
|
|
82599
|
+
}
|
|
82600
|
+
function startFooterTimer(ctx) {
|
|
82601
|
+
if (footerTimer || !ctx.hasUI) return;
|
|
82602
|
+
footerTimer = setInterval(() => {
|
|
82603
|
+
if (!config.enabled || !config.speed.enabled) {
|
|
82604
|
+
stopFooterTimer();
|
|
82605
|
+
return;
|
|
82606
|
+
}
|
|
82607
|
+
publishSpeedFooter(ctx);
|
|
82608
|
+
if (!footerAnimator.isAnimating()) stopFooterTimer();
|
|
82609
|
+
}, config.speed.renderIntervalMs);
|
|
82610
|
+
footerTimer.unref?.();
|
|
82611
|
+
}
|
|
82612
|
+
function startCapacityTimer(ctx) {
|
|
82613
|
+
if (capacityTimer) return;
|
|
82614
|
+
const interval = Math.max(250, config.capacity.refreshIntervalMs);
|
|
82615
|
+
capacityTimer = setInterval(() => publishCapacity(ctx), interval);
|
|
82616
|
+
capacityTimer.unref?.();
|
|
82617
|
+
}
|
|
82618
|
+
function startProviderTimer(ctx) {
|
|
82619
|
+
if (providerTimer) return;
|
|
82620
|
+
if (!config.capacity.providerUsage) return;
|
|
82621
|
+
const interval = Math.max(1e4, config.capacity.providerRefreshMs);
|
|
82622
|
+
async function tick() {
|
|
82623
|
+
if (!config.enabled || !config.capacity.providerUsage) {
|
|
82624
|
+
stopProviderTimer();
|
|
82625
|
+
return;
|
|
82626
|
+
}
|
|
82627
|
+
try {
|
|
82628
|
+
const usage = await fetchProviderUsage(config.capacity.providerRefreshMs);
|
|
82629
|
+
lastProviderText = renderProviderUsage(themeOf(ctx), usage);
|
|
82630
|
+
publishCapacity(ctx);
|
|
82631
|
+
} catch {
|
|
82632
|
+
lastProviderText = void 0;
|
|
82633
|
+
publishCapacity(ctx);
|
|
82634
|
+
}
|
|
82635
|
+
}
|
|
82636
|
+
tick();
|
|
82637
|
+
providerTimer = setInterval(tick, interval);
|
|
82638
|
+
providerTimer.unref?.();
|
|
82639
|
+
}
|
|
82640
|
+
function resetWorking(ctx) {
|
|
82641
|
+
applyIndicator(ctx, null, true);
|
|
82642
|
+
renderWorking(ctx, speedTracker.lastTokS);
|
|
82643
|
+
}
|
|
82644
|
+
function applyConfig(ctx) {
|
|
82645
|
+
saveConfig(config);
|
|
82646
|
+
speedTracker.updateConfig(config.speed);
|
|
82647
|
+
footerAnimator.updateDuration(config.speed.renderIntervalMs);
|
|
82648
|
+
if (!config.enabled) {
|
|
82649
|
+
stopLiveTimer();
|
|
82650
|
+
stopFooterTimer();
|
|
82651
|
+
stopCapacityTimer();
|
|
82652
|
+
stopProviderTimer();
|
|
82653
|
+
clearVibesStatus(ctx);
|
|
82654
|
+
return;
|
|
82655
|
+
}
|
|
82656
|
+
publishCapacity(ctx);
|
|
82657
|
+
publishSpeedFooter(ctx);
|
|
82658
|
+
if (config.capacity.providerUsage) startProviderTimer(ctx);
|
|
82659
|
+
}
|
|
82660
|
+
pi.on("session_start", (_event, ctx) => {
|
|
82661
|
+
stopLiveTimer();
|
|
82662
|
+
stopFooterTimer();
|
|
82663
|
+
stopCapacityTimer();
|
|
82664
|
+
stopProviderTimer();
|
|
82665
|
+
config = loadConfig2();
|
|
82666
|
+
speedTracker.updateConfig(config.speed);
|
|
82667
|
+
footerAnimator.updateDuration(config.speed.renderIntervalMs);
|
|
82668
|
+
speedTracker.resetSession();
|
|
82669
|
+
footerAnimator.reset(null);
|
|
82670
|
+
clearProviderUsageCache();
|
|
82671
|
+
if (!config.enabled) {
|
|
82672
|
+
clearVibesStatus(ctx);
|
|
82673
|
+
return;
|
|
82674
|
+
}
|
|
82675
|
+
publishCapacity(ctx);
|
|
82676
|
+
publishSpeedFooter(ctx);
|
|
82677
|
+
startCapacityTimer(ctx);
|
|
82678
|
+
startProviderTimer(ctx);
|
|
82679
|
+
applyIndicator(ctx, null, true);
|
|
82680
|
+
});
|
|
82681
|
+
pi.on("agent_start", (_event, ctx) => {
|
|
82682
|
+
if (!config.enabled) return;
|
|
82683
|
+
resetWorking(ctx);
|
|
82684
|
+
});
|
|
82685
|
+
pi.on("turn_start", (_event, ctx) => {
|
|
82686
|
+
if (!config.enabled) return;
|
|
82687
|
+
resetWorking(ctx);
|
|
82688
|
+
});
|
|
82689
|
+
pi.on("message_start", (event, ctx) => {
|
|
82690
|
+
if (!config.enabled || !config.speed.enabled || !isAssistantMessage(event.message)) return;
|
|
82691
|
+
speedTracker.startMessage();
|
|
82692
|
+
footerAnimator.reset(speedTracker.lastTokS);
|
|
82693
|
+
startLiveTimer(ctx);
|
|
82694
|
+
lastRenderedAt = 0;
|
|
82695
|
+
if (isWebTerminal() && ctx.hasUI) {
|
|
82696
|
+
ctx.ui.setWidget("crew-vibes-cat", [...CAT_FRAMES2[0]], { placement: "aboveEditor" });
|
|
82697
|
+
}
|
|
82698
|
+
});
|
|
82699
|
+
pi.on("message_update", (event, ctx) => {
|
|
82700
|
+
if (!config.enabled || !config.speed.enabled || !isAssistantMessage(event.message) || !speedTracker.isStreaming) return;
|
|
82701
|
+
const ev = event.assistantMessageEvent;
|
|
82702
|
+
const type = assistantEventType(ev);
|
|
82703
|
+
if (type === "text_delta" || type === "thinking_delta") {
|
|
82704
|
+
const delta = ev.delta ?? "";
|
|
82705
|
+
speedTracker.recordDelta(delta, assistantUsageOutput(event.message));
|
|
82706
|
+
}
|
|
82707
|
+
if (type === "start") resetWorking(ctx);
|
|
82708
|
+
const now = Date.now();
|
|
82709
|
+
if (now - lastRenderedAt < config.speed.renderIntervalMs && type !== "done") return;
|
|
82710
|
+
lastRenderedAt = now;
|
|
82711
|
+
const speed = speedTracker.liveTokS();
|
|
82712
|
+
applyIndicator(ctx, speed);
|
|
82713
|
+
renderWorking(ctx, speed);
|
|
82714
|
+
});
|
|
82715
|
+
pi.on("message_end", (event, ctx) => {
|
|
82716
|
+
if (!isAssistantMessage(event.message)) return;
|
|
82717
|
+
publishCapacity(ctx);
|
|
82718
|
+
if (!config.enabled || !config.speed.enabled || !speedTracker.isStreaming) return;
|
|
82719
|
+
const completed = speedTracker.finishMessage(assistantUsageOutput(event.message) ?? 0, assistantStopReason(event.message));
|
|
82720
|
+
if (!completed) return;
|
|
82721
|
+
footerAnimator.setTarget(speedTracker.sessionAvgTokS());
|
|
82722
|
+
publishSpeedFooter(ctx);
|
|
82723
|
+
startFooterTimer(ctx);
|
|
82724
|
+
applyIndicator(ctx, speedTracker.lastTokS);
|
|
82725
|
+
if (isWebTerminal() && ctx.hasUI) {
|
|
82726
|
+
ctx.ui.setWidget("crew-vibes-cat", void 0);
|
|
82727
|
+
}
|
|
82728
|
+
});
|
|
82729
|
+
pi.on("turn_end", () => {
|
|
82730
|
+
speedTracker.stopMessage();
|
|
82731
|
+
stopLiveTimer();
|
|
82732
|
+
});
|
|
82733
|
+
pi.on("agent_end", (_event, ctx) => {
|
|
82734
|
+
speedTracker.stopMessage();
|
|
82735
|
+
stopLiveTimer();
|
|
82736
|
+
if (ctx && config.enabled && ctx.hasUI) {
|
|
82737
|
+
applyIndicator(ctx, speedTracker.lastTokS);
|
|
82738
|
+
ctx.ui.setWorkingMessage();
|
|
82739
|
+
if (isWebTerminal()) ctx.ui.setWidget("crew-vibes-cat", void 0);
|
|
82740
|
+
}
|
|
82741
|
+
});
|
|
82742
|
+
pi.on("model_select", (_event, ctx) => publishCapacity(ctx));
|
|
82743
|
+
pi.on("session_compact", (_event, ctx) => publishCapacity(ctx));
|
|
82744
|
+
pi.on("session_tree", (_event, ctx) => publishCapacity(ctx));
|
|
82745
|
+
pi.on("session_shutdown", (_event, ctx) => {
|
|
82746
|
+
stopLiveTimer();
|
|
82747
|
+
stopFooterTimer();
|
|
82748
|
+
stopCapacityTimer();
|
|
82749
|
+
stopProviderTimer();
|
|
82750
|
+
clearVibesStatus(ctx);
|
|
82751
|
+
if (ctx.hasUI) ctx.ui.setWidget("crew-vibes-cat", void 0);
|
|
82752
|
+
});
|
|
82753
|
+
async function handleCommand(args, ctx) {
|
|
82754
|
+
const tokens = args.trim().split(/\s+/).filter(Boolean);
|
|
82755
|
+
const [first, second] = tokens;
|
|
82756
|
+
const mutate = (next) => {
|
|
82757
|
+
config = next;
|
|
82758
|
+
applyConfig(ctx);
|
|
82759
|
+
};
|
|
82760
|
+
if (!first) {
|
|
82761
|
+
const speed = speedTracker.liveTokS();
|
|
82762
|
+
const usage = getCapacityUsage(ctx);
|
|
82763
|
+
const stage = config.capacity.icons.length ? config.capacity.labels[Math.max(
|
|
82764
|
+
0,
|
|
82765
|
+
Math.min(
|
|
82766
|
+
config.capacity.labels.length - 1,
|
|
82767
|
+
Math.floor((usage.percent ?? 0) / 100 * config.capacity.labels.length)
|
|
82768
|
+
)
|
|
82769
|
+
)] : "?";
|
|
82770
|
+
ctx.ui.notify(
|
|
82771
|
+
`crew-vibes: ${config.enabled ? "on" : "off"} \xB7 speed ${config.speed.enabled ? "on" : "off"} (${formatSpeed(config.speed, speed)}) \xB7 capacity ${config.capacity.enabled ? "on" : "off"} (${stage})`,
|
|
82772
|
+
"info"
|
|
82773
|
+
);
|
|
82774
|
+
return;
|
|
82775
|
+
}
|
|
82776
|
+
if (first === "on" || first === "off") {
|
|
82777
|
+
mutate({ ...config, enabled: first === "on" });
|
|
82778
|
+
ctx.ui.notify(`crew-vibes ${first === "on" ? "enabled" : "disabled"}`, "info");
|
|
82779
|
+
return;
|
|
82780
|
+
}
|
|
82781
|
+
if (first === "speed" && (second === "on" || second === "off")) {
|
|
82782
|
+
mutate({ ...config, speed: { ...config.speed, enabled: second === "on" } });
|
|
82783
|
+
ctx.ui.notify(`crew-vibes speed ${second === "on" ? "enabled" : "disabled"}`, "info");
|
|
82784
|
+
return;
|
|
82785
|
+
}
|
|
82786
|
+
if (first === "capacity" && (second === "on" || second === "off")) {
|
|
82787
|
+
mutate({ ...config, capacity: { ...config.capacity, enabled: second === "on" } });
|
|
82788
|
+
ctx.ui.notify(`crew-vibes capacity ${second === "on" ? "enabled" : "disabled"}`, "info");
|
|
82789
|
+
return;
|
|
82790
|
+
}
|
|
82791
|
+
ctx.ui.notify("Usage: /team-vibes [on|off|speed on|off|capacity on|off]", "error");
|
|
82792
|
+
}
|
|
82793
|
+
pi.registerCommand("team-vibes", {
|
|
82794
|
+
description: "Toggle crew-vibes speed + context meters (on/off, speed, capacity)",
|
|
82795
|
+
handler: handleCommand
|
|
82796
|
+
});
|
|
82797
|
+
}
|
|
82798
|
+
|
|
81481
82799
|
// src/extension/cross-extension-rpc.ts
|
|
81482
82800
|
init_safe_paths();
|
|
81483
82801
|
|
|
@@ -83617,6 +84935,11 @@ Subagent may need manual intervention.`
|
|
|
83617
84935
|
registerContextStatusInjection(pi, {
|
|
83618
84936
|
enabled: loadConfig(process.cwd()).config.reliability?.ambientStatusInjection !== false
|
|
83619
84937
|
});
|
|
84938
|
+
try {
|
|
84939
|
+
registerCrewVibes(pi);
|
|
84940
|
+
} catch (err2) {
|
|
84941
|
+
console.warn("[pi-crew] crew-vibes initialization failed:", err2 instanceof Error ? err2.message : err2);
|
|
84942
|
+
}
|
|
83620
84943
|
}
|
|
83621
84944
|
|
|
83622
84945
|
// index.bundle.ts
|