kimiflare 0.86.0 → 0.87.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +2466 -1279
- package/dist/index.js.map +1 -1
- package/dist/sdk/index.d.ts +8 -0
- package/dist/sdk/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -385,6 +385,396 @@ var init_lsp_config = __esm({
|
|
|
385
385
|
}
|
|
386
386
|
});
|
|
387
387
|
|
|
388
|
+
// src/util/version.ts
|
|
389
|
+
import { readFileSync } from "fs";
|
|
390
|
+
import { fileURLToPath } from "url";
|
|
391
|
+
import { dirname as dirname2, join as join3 } from "path";
|
|
392
|
+
function getAppVersion() {
|
|
393
|
+
if (cachedVersion !== null) return cachedVersion;
|
|
394
|
+
const here = dirname2(fileURLToPath(import.meta.url));
|
|
395
|
+
const candidates = [join3(here, "..", "..", "package.json"), join3(here, "..", "package.json")];
|
|
396
|
+
for (const path of candidates) {
|
|
397
|
+
try {
|
|
398
|
+
const pkg = JSON.parse(readFileSync(path, "utf8"));
|
|
399
|
+
cachedVersion = pkg.version ?? "0.0.0";
|
|
400
|
+
return cachedVersion;
|
|
401
|
+
} catch {
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
cachedVersion = "0.0.0";
|
|
405
|
+
return cachedVersion;
|
|
406
|
+
}
|
|
407
|
+
function getUserAgent() {
|
|
408
|
+
return `kimiflare/${getAppVersion()} (+https://github.com/sinameraji/kimiflare)`;
|
|
409
|
+
}
|
|
410
|
+
var cachedVersion;
|
|
411
|
+
var init_version = __esm({
|
|
412
|
+
"src/util/version.ts"() {
|
|
413
|
+
"use strict";
|
|
414
|
+
cachedVersion = null;
|
|
415
|
+
}
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
// src/util/update-check.ts
|
|
419
|
+
import { readFile as readFile3, writeFile as writeFile3, mkdir as mkdir3 } from "fs/promises";
|
|
420
|
+
import { homedir as homedir2 } from "os";
|
|
421
|
+
import { join as join4, dirname as dirname3 } from "path";
|
|
422
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
423
|
+
function cachePath() {
|
|
424
|
+
const xdg = process.env.XDG_CONFIG_HOME || join4(homedir2(), ".config");
|
|
425
|
+
return join4(xdg, "kimiflare", "update-check.json");
|
|
426
|
+
}
|
|
427
|
+
async function findPackageJson(startDir) {
|
|
428
|
+
let dir = startDir;
|
|
429
|
+
while (true) {
|
|
430
|
+
const candidate = join4(dir, "package.json");
|
|
431
|
+
try {
|
|
432
|
+
const raw = await readFile3(candidate, "utf8");
|
|
433
|
+
const parsed = JSON.parse(raw);
|
|
434
|
+
if (parsed.name === "kimiflare" && parsed.version) {
|
|
435
|
+
return { path: candidate, version: parsed.version };
|
|
436
|
+
}
|
|
437
|
+
} catch {
|
|
438
|
+
}
|
|
439
|
+
const parent = dirname3(dir);
|
|
440
|
+
if (parent === dir) break;
|
|
441
|
+
dir = parent;
|
|
442
|
+
}
|
|
443
|
+
return null;
|
|
444
|
+
}
|
|
445
|
+
async function readLocalVersion() {
|
|
446
|
+
const here = dirname3(fileURLToPath2(import.meta.url));
|
|
447
|
+
const found = await findPackageJson(here);
|
|
448
|
+
return found?.version ?? null;
|
|
449
|
+
}
|
|
450
|
+
async function readCache() {
|
|
451
|
+
try {
|
|
452
|
+
const raw = await readFile3(cachePath(), "utf8");
|
|
453
|
+
const parsed = JSON.parse(raw);
|
|
454
|
+
if (Date.now() - parsed.checkedAt < CACHE_TTL_MS) {
|
|
455
|
+
return parsed;
|
|
456
|
+
}
|
|
457
|
+
} catch {
|
|
458
|
+
}
|
|
459
|
+
return null;
|
|
460
|
+
}
|
|
461
|
+
async function writeCache(entry) {
|
|
462
|
+
const p = cachePath();
|
|
463
|
+
await mkdir3(dirname3(p), { recursive: true });
|
|
464
|
+
await writeFile3(p, JSON.stringify(entry), "utf8");
|
|
465
|
+
}
|
|
466
|
+
async function fetchLatestVersion() {
|
|
467
|
+
try {
|
|
468
|
+
const res = await fetch(NPM_REGISTRY, {
|
|
469
|
+
headers: { "User-Agent": getUserAgent(), Accept: "application/json" }
|
|
470
|
+
});
|
|
471
|
+
if (!res.ok) return null;
|
|
472
|
+
const data = await res.json();
|
|
473
|
+
return data.version ?? null;
|
|
474
|
+
} catch {
|
|
475
|
+
return null;
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
function stripV(v) {
|
|
479
|
+
return v.startsWith("v") ? v.slice(1) : v;
|
|
480
|
+
}
|
|
481
|
+
function isNewer(local, remote) {
|
|
482
|
+
const a = stripV(local).split(".").map(Number);
|
|
483
|
+
const b = stripV(remote).split(".").map(Number);
|
|
484
|
+
for (let i = 0; i < Math.max(a.length, b.length); i++) {
|
|
485
|
+
const av = a[i] ?? 0;
|
|
486
|
+
const bv = b[i] ?? 0;
|
|
487
|
+
if (av < bv) return true;
|
|
488
|
+
if (av > bv) return false;
|
|
489
|
+
}
|
|
490
|
+
return false;
|
|
491
|
+
}
|
|
492
|
+
async function readOptionalDepVersion(name) {
|
|
493
|
+
try {
|
|
494
|
+
const here = dirname3(fileURLToPath2(import.meta.url));
|
|
495
|
+
const candidate = join4(here, "..", "..", "node_modules", name, "package.json");
|
|
496
|
+
const raw = await readFile3(candidate, "utf8");
|
|
497
|
+
const parsed = JSON.parse(raw);
|
|
498
|
+
return parsed.version ?? null;
|
|
499
|
+
} catch {
|
|
500
|
+
return null;
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
async function fetchDistTagVersion(name, tag2) {
|
|
504
|
+
try {
|
|
505
|
+
const res = await fetch(`https://registry.npmjs.org/${name}/${tag2}`, {
|
|
506
|
+
headers: { "User-Agent": getUserAgent(), Accept: "application/json" }
|
|
507
|
+
});
|
|
508
|
+
if (!res.ok) return null;
|
|
509
|
+
const data = await res.json();
|
|
510
|
+
return data.version ?? null;
|
|
511
|
+
} catch {
|
|
512
|
+
return null;
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
async function checkOptionalDependency(name, tag2) {
|
|
516
|
+
const localVersion = await readOptionalDepVersion(name);
|
|
517
|
+
if (!localVersion) {
|
|
518
|
+
return { name, hasUpdate: false, localVersion: null, latestVersion: null };
|
|
519
|
+
}
|
|
520
|
+
const latestVersion = await fetchDistTagVersion(name, tag2);
|
|
521
|
+
if (!latestVersion) {
|
|
522
|
+
return { name, hasUpdate: false, localVersion, latestVersion: null };
|
|
523
|
+
}
|
|
524
|
+
const hasUpdate = isNewer(localVersion, latestVersion);
|
|
525
|
+
return { name, hasUpdate, localVersion, latestVersion };
|
|
526
|
+
}
|
|
527
|
+
async function checkForUpdate(force = false) {
|
|
528
|
+
const localVersion = await readLocalVersion();
|
|
529
|
+
if (!localVersion) return { hasUpdate: false, localVersion: null, latestVersion: null };
|
|
530
|
+
if (!force) {
|
|
531
|
+
const cached = await readCache();
|
|
532
|
+
if (cached) {
|
|
533
|
+
const hasUpdate2 = isNewer(localVersion, cached.latestVersion);
|
|
534
|
+
return { hasUpdate: hasUpdate2, localVersion, latestVersion: cached.latestVersion };
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
const latestVersion = await fetchLatestVersion();
|
|
538
|
+
if (!latestVersion) {
|
|
539
|
+
return { hasUpdate: false, localVersion, latestVersion: null };
|
|
540
|
+
}
|
|
541
|
+
const hasUpdate = isNewer(localVersion, latestVersion);
|
|
542
|
+
await writeCache({ checkedAt: Date.now(), latestVersion });
|
|
543
|
+
return { hasUpdate, localVersion, latestVersion };
|
|
544
|
+
}
|
|
545
|
+
var CACHE_TTL_MS, NPM_REGISTRY;
|
|
546
|
+
var init_update_check = __esm({
|
|
547
|
+
"src/util/update-check.ts"() {
|
|
548
|
+
"use strict";
|
|
549
|
+
init_version();
|
|
550
|
+
CACHE_TTL_MS = 60 * 60 * 1e3;
|
|
551
|
+
NPM_REGISTRY = "https://registry.npmjs.org/kimiflare/latest";
|
|
552
|
+
}
|
|
553
|
+
});
|
|
554
|
+
|
|
555
|
+
// src/remote/session-store.ts
|
|
556
|
+
import { readFile as readFile4, writeFile as writeFile4, mkdir as mkdir4, readdir } from "fs/promises";
|
|
557
|
+
import { homedir as homedir3 } from "os";
|
|
558
|
+
import { join as join5 } from "path";
|
|
559
|
+
function remoteDir() {
|
|
560
|
+
const xdg = process.env.XDG_DATA_HOME || join5(homedir3(), ".config");
|
|
561
|
+
return join5(xdg, "kimiflare", "remote");
|
|
562
|
+
}
|
|
563
|
+
async function saveRemoteSession(session) {
|
|
564
|
+
const dir = remoteDir();
|
|
565
|
+
await mkdir4(dir, { recursive: true });
|
|
566
|
+
const path = join5(dir, `${session.sessionId}.json`);
|
|
567
|
+
await writeFile4(path, JSON.stringify(session, null, 2) + "\n", "utf8");
|
|
568
|
+
}
|
|
569
|
+
async function loadRemoteSession(sessionId) {
|
|
570
|
+
try {
|
|
571
|
+
const path = join5(remoteDir(), `${sessionId}.json`);
|
|
572
|
+
const raw = await readFile4(path, "utf8");
|
|
573
|
+
return JSON.parse(raw);
|
|
574
|
+
} catch {
|
|
575
|
+
return null;
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
async function listRemoteSessions() {
|
|
579
|
+
const dir = remoteDir();
|
|
580
|
+
try {
|
|
581
|
+
const files = await readdir(dir);
|
|
582
|
+
const sessions = [];
|
|
583
|
+
for (const file of files) {
|
|
584
|
+
if (!file.endsWith(".json")) continue;
|
|
585
|
+
try {
|
|
586
|
+
const raw = await readFile4(join5(dir, file), "utf8");
|
|
587
|
+
sessions.push(JSON.parse(raw));
|
|
588
|
+
} catch {
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
return sessions.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());
|
|
592
|
+
} catch {
|
|
593
|
+
return [];
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
async function getMostRecentRemoteSession() {
|
|
597
|
+
const sessions = await listRemoteSessions();
|
|
598
|
+
return sessions[0] ?? null;
|
|
599
|
+
}
|
|
600
|
+
var init_session_store = __esm({
|
|
601
|
+
"src/remote/session-store.ts"() {
|
|
602
|
+
"use strict";
|
|
603
|
+
}
|
|
604
|
+
});
|
|
605
|
+
|
|
606
|
+
// src/remote/deploy.ts
|
|
607
|
+
import { execSync } from "child_process";
|
|
608
|
+
import { join as join6, dirname as dirname4 } from "path";
|
|
609
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
610
|
+
import { randomBytes } from "crypto";
|
|
611
|
+
function generateSecret() {
|
|
612
|
+
return randomBytes(32).toString("hex");
|
|
613
|
+
}
|
|
614
|
+
function runCapture(cmd, cwd) {
|
|
615
|
+
return execSync(cmd, { cwd, encoding: "utf8", stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
616
|
+
}
|
|
617
|
+
async function* deployForTui() {
|
|
618
|
+
yield { message: "Checking prerequisites..." };
|
|
619
|
+
try {
|
|
620
|
+
runCapture("wrangler --version");
|
|
621
|
+
} catch {
|
|
622
|
+
yield { message: "wrangler not found. Install: npm install -g wrangler", error: true };
|
|
623
|
+
yield { message: "Then run: wrangler login", error: true };
|
|
624
|
+
throw new Error("wrangler not installed");
|
|
625
|
+
}
|
|
626
|
+
yield { message: "wrangler OK" };
|
|
627
|
+
try {
|
|
628
|
+
runCapture("wrangler whoami");
|
|
629
|
+
} catch {
|
|
630
|
+
yield { message: "wrangler not authenticated. Run: wrangler login", error: true };
|
|
631
|
+
throw new Error("wrangler not authenticated");
|
|
632
|
+
}
|
|
633
|
+
yield { message: "wrangler authenticated" };
|
|
634
|
+
try {
|
|
635
|
+
runCapture("docker --version");
|
|
636
|
+
} catch {
|
|
637
|
+
yield { message: "Docker not found. Install: https://docs.docker.com/get-docker/", error: true };
|
|
638
|
+
throw new Error("docker not installed");
|
|
639
|
+
}
|
|
640
|
+
yield { message: "Docker OK" };
|
|
641
|
+
yield { message: "Building remote agent bundle..." };
|
|
642
|
+
try {
|
|
643
|
+
runCapture("npm run build:remote-agent", join6(REMOTE_DIR, ".."));
|
|
644
|
+
yield { message: "Agent bundle built" };
|
|
645
|
+
} catch (err) {
|
|
646
|
+
yield { message: `Build failed: ${err instanceof Error ? err.message : String(err)}`, error: true };
|
|
647
|
+
throw err;
|
|
648
|
+
}
|
|
649
|
+
yield { message: "Deploying Worker to Cloudflare..." };
|
|
650
|
+
try {
|
|
651
|
+
runCapture("wrangler deploy", WORKER_DIR);
|
|
652
|
+
yield { message: "Worker deployed" };
|
|
653
|
+
} catch (err) {
|
|
654
|
+
yield { message: `Deploy failed: ${err instanceof Error ? err.message : String(err)}`, error: true };
|
|
655
|
+
throw err;
|
|
656
|
+
}
|
|
657
|
+
let workerUrl;
|
|
658
|
+
try {
|
|
659
|
+
const info = runCapture("wrangler info", WORKER_DIR);
|
|
660
|
+
const match = info.match(/https:\/\/[^\s]+\.workers\.dev/);
|
|
661
|
+
if (match) workerUrl = match[0];
|
|
662
|
+
} catch {
|
|
663
|
+
}
|
|
664
|
+
if (!workerUrl) {
|
|
665
|
+
yield { message: "Could not auto-detect Worker URL", error: true };
|
|
666
|
+
throw new Error("Worker URL not found");
|
|
667
|
+
}
|
|
668
|
+
yield { message: `Worker URL: ${workerUrl}` };
|
|
669
|
+
const authSecret = generateSecret();
|
|
670
|
+
const cfg = await loadConfig();
|
|
671
|
+
const cfToken = process.env.CF_API_TOKEN ?? cfg?.apiToken;
|
|
672
|
+
if (!cfToken) {
|
|
673
|
+
yield { message: "CF_API_TOKEN not found. Set CF_API_TOKEN env var or apiToken in config", error: true };
|
|
674
|
+
throw new Error("CF_API_TOKEN missing");
|
|
675
|
+
}
|
|
676
|
+
yield { message: "Setting Worker secrets..." };
|
|
677
|
+
try {
|
|
678
|
+
execSync(`wrangler secret put REMOTE_AUTH_SECRET`, {
|
|
679
|
+
cwd: WORKER_DIR,
|
|
680
|
+
input: authSecret,
|
|
681
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
682
|
+
});
|
|
683
|
+
execSync(`wrangler secret put CF_API_TOKEN`, {
|
|
684
|
+
cwd: WORKER_DIR,
|
|
685
|
+
input: cfToken,
|
|
686
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
687
|
+
});
|
|
688
|
+
yield { message: "Secrets set" };
|
|
689
|
+
} catch (err) {
|
|
690
|
+
yield { message: `Secret setup failed: ${err instanceof Error ? err.message : String(err)}`, error: true };
|
|
691
|
+
throw err;
|
|
692
|
+
}
|
|
693
|
+
const imageTag = "ghcr.io/sinameraji/kimiflare-remote-agent:latest";
|
|
694
|
+
yield { message: "Building container image..." };
|
|
695
|
+
try {
|
|
696
|
+
runCapture(`docker build -t ${imageTag} .`, REMOTE_DIR);
|
|
697
|
+
yield { message: "Image built" };
|
|
698
|
+
} catch (err) {
|
|
699
|
+
yield { message: `Image build failed: ${err instanceof Error ? err.message : String(err)}`, error: true };
|
|
700
|
+
throw err;
|
|
701
|
+
}
|
|
702
|
+
yield { message: `Pushing ${imageTag}...` };
|
|
703
|
+
try {
|
|
704
|
+
runCapture(`docker push ${imageTag}`, REMOTE_DIR);
|
|
705
|
+
yield { message: "Image pushed" };
|
|
706
|
+
} catch (err) {
|
|
707
|
+
yield { message: `Push failed: ${err instanceof Error ? err.message : String(err)}`, error: true };
|
|
708
|
+
yield { message: "Make sure you're logged into ghcr.io: docker login ghcr.io -u USERNAME -p GITHUB_TOKEN", error: true };
|
|
709
|
+
throw err;
|
|
710
|
+
}
|
|
711
|
+
const nextCfg = {
|
|
712
|
+
...cfg ?? { accountId: "", apiToken: "", model: "@cf/moonshotai/kimi-k2.6" },
|
|
713
|
+
remoteWorkerUrl: workerUrl,
|
|
714
|
+
remoteAuthSecret: authSecret
|
|
715
|
+
};
|
|
716
|
+
await saveConfig(nextCfg);
|
|
717
|
+
yield { message: "Config saved" };
|
|
718
|
+
yield { message: "Remote infrastructure ready!", done: true };
|
|
719
|
+
return { workerUrl, authSecret };
|
|
720
|
+
}
|
|
721
|
+
async function runDeploy() {
|
|
722
|
+
console.log("kimiflare remote deploy\n");
|
|
723
|
+
try {
|
|
724
|
+
for await (const step of deployForTui()) {
|
|
725
|
+
console.log(step.message);
|
|
726
|
+
if (step.done) break;
|
|
727
|
+
if (step.error) process.exit(1);
|
|
728
|
+
}
|
|
729
|
+
console.log("\nDeploy complete!");
|
|
730
|
+
} catch (err) {
|
|
731
|
+
console.error(`Deploy failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
732
|
+
process.exit(1);
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
async function checkDeployStatus() {
|
|
736
|
+
let wrangler = false;
|
|
737
|
+
let wranglerAuth = false;
|
|
738
|
+
let docker = false;
|
|
739
|
+
let workerUrl;
|
|
740
|
+
try {
|
|
741
|
+
execSync("wrangler --version", { stdio: "pipe" });
|
|
742
|
+
wrangler = true;
|
|
743
|
+
} catch {
|
|
744
|
+
}
|
|
745
|
+
if (wrangler) {
|
|
746
|
+
try {
|
|
747
|
+
execSync("wrangler whoami", { stdio: "pipe" });
|
|
748
|
+
wranglerAuth = true;
|
|
749
|
+
} catch {
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
try {
|
|
753
|
+
execSync("docker --version", { stdio: "pipe" });
|
|
754
|
+
docker = true;
|
|
755
|
+
} catch {
|
|
756
|
+
}
|
|
757
|
+
const cfg = await loadConfig();
|
|
758
|
+
if (cfg?.remoteWorkerUrl) {
|
|
759
|
+
try {
|
|
760
|
+
const res = await fetch(`${cfg.remoteWorkerUrl}/health`, { signal: AbortSignal.timeout(5e3) });
|
|
761
|
+
if (res.ok) workerUrl = cfg.remoteWorkerUrl;
|
|
762
|
+
} catch {
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
return { wrangler, wranglerAuth, docker, workerUrl };
|
|
766
|
+
}
|
|
767
|
+
var __dirname2, REMOTE_DIR, WORKER_DIR;
|
|
768
|
+
var init_deploy = __esm({
|
|
769
|
+
"src/remote/deploy.ts"() {
|
|
770
|
+
"use strict";
|
|
771
|
+
init_config();
|
|
772
|
+
__dirname2 = dirname4(fileURLToPath3(import.meta.url));
|
|
773
|
+
REMOTE_DIR = join6(__dirname2, "..", "..", "..", "remote");
|
|
774
|
+
WORKER_DIR = join6(REMOTE_DIR, "worker");
|
|
775
|
+
}
|
|
776
|
+
});
|
|
777
|
+
|
|
388
778
|
// src/util/log-sink.ts
|
|
389
779
|
var log_sink_exports = {};
|
|
390
780
|
__export(log_sink_exports, {
|
|
@@ -401,12 +791,12 @@ __export(log_sink_exports, {
|
|
|
401
791
|
setLogTurnId: () => setLogTurnId,
|
|
402
792
|
writeLogLine: () => writeLogLine
|
|
403
793
|
});
|
|
404
|
-
import { homedir as
|
|
405
|
-
import { join as
|
|
794
|
+
import { homedir as homedir4 } from "os";
|
|
795
|
+
import { join as join7 } from "path";
|
|
406
796
|
import { mkdirSync, createWriteStream, readdirSync, statSync, unlinkSync } from "fs";
|
|
407
797
|
function defaultLogDir() {
|
|
408
|
-
const xdg = process.env.XDG_CONFIG_HOME ||
|
|
409
|
-
return
|
|
798
|
+
const xdg = process.env.XDG_CONFIG_HOME || join7(homedir4(), ".config");
|
|
799
|
+
return join7(xdg, "kimiflare", "logs");
|
|
410
800
|
}
|
|
411
801
|
function isInNodeTestContext() {
|
|
412
802
|
if (process.env.NODE_TEST_CONTEXT) return true;
|
|
@@ -418,7 +808,7 @@ function logDir() {
|
|
|
418
808
|
return overrideDir ?? defaultLogDir();
|
|
419
809
|
}
|
|
420
810
|
function logPathFor(date = /* @__PURE__ */ new Date()) {
|
|
421
|
-
return
|
|
811
|
+
return join7(logDir(), `${date.toISOString().slice(0, 10)}.jsonl`);
|
|
422
812
|
}
|
|
423
813
|
function setLogDirForTesting(dir) {
|
|
424
814
|
if (currentStream) {
|
|
@@ -441,8 +831,8 @@ async function flushAndCloseForTesting() {
|
|
|
441
831
|
const s = currentStream;
|
|
442
832
|
currentStream = null;
|
|
443
833
|
currentDate = null;
|
|
444
|
-
await new Promise((
|
|
445
|
-
s.end(() =>
|
|
834
|
+
await new Promise((resolve8) => {
|
|
835
|
+
s.end(() => resolve8());
|
|
446
836
|
});
|
|
447
837
|
}
|
|
448
838
|
function isLogSinkEnabled() {
|
|
@@ -488,7 +878,7 @@ function pruneOldLogs(retentionDays = RETENTION_DAYS) {
|
|
|
488
878
|
const cutoff = Date.now() - retentionDays * 24 * 60 * 60 * 1e3;
|
|
489
879
|
for (const name of entries) {
|
|
490
880
|
if (!name.endsWith(".jsonl")) continue;
|
|
491
|
-
const full =
|
|
881
|
+
const full = join7(logDir(), name);
|
|
492
882
|
try {
|
|
493
883
|
const st = statSync(full);
|
|
494
884
|
if (st.mtimeMs < cutoff) {
|
|
@@ -919,36 +1309,6 @@ var init_errors = __esm({
|
|
|
919
1309
|
}
|
|
920
1310
|
});
|
|
921
1311
|
|
|
922
|
-
// src/util/version.ts
|
|
923
|
-
import { readFileSync } from "fs";
|
|
924
|
-
import { fileURLToPath } from "url";
|
|
925
|
-
import { dirname as dirname2, join as join4 } from "path";
|
|
926
|
-
function getAppVersion() {
|
|
927
|
-
if (cachedVersion !== null) return cachedVersion;
|
|
928
|
-
const here = dirname2(fileURLToPath(import.meta.url));
|
|
929
|
-
const candidates = [join4(here, "..", "..", "package.json"), join4(here, "..", "package.json")];
|
|
930
|
-
for (const path of candidates) {
|
|
931
|
-
try {
|
|
932
|
-
const pkg = JSON.parse(readFileSync(path, "utf8"));
|
|
933
|
-
cachedVersion = pkg.version ?? "0.0.0";
|
|
934
|
-
return cachedVersion;
|
|
935
|
-
} catch {
|
|
936
|
-
}
|
|
937
|
-
}
|
|
938
|
-
cachedVersion = "0.0.0";
|
|
939
|
-
return cachedVersion;
|
|
940
|
-
}
|
|
941
|
-
function getUserAgent() {
|
|
942
|
-
return `kimiflare/${getAppVersion()} (+https://github.com/sinameraji/kimiflare)`;
|
|
943
|
-
}
|
|
944
|
-
var cachedVersion;
|
|
945
|
-
var init_version = __esm({
|
|
946
|
-
"src/util/version.ts"() {
|
|
947
|
-
"use strict";
|
|
948
|
-
cachedVersion = null;
|
|
949
|
-
}
|
|
950
|
-
});
|
|
951
|
-
|
|
952
1312
|
// src/agent/messages.ts
|
|
953
1313
|
function sanitizeString(str) {
|
|
954
1314
|
return str.replace(/[\uD800-\uDFFF]/g, "\uFFFD");
|
|
@@ -1432,11 +1792,11 @@ function extractCloudflareError(parsed, rawText) {
|
|
|
1432
1792
|
return null;
|
|
1433
1793
|
}
|
|
1434
1794
|
function sleep(ms, signal) {
|
|
1435
|
-
return new Promise((
|
|
1795
|
+
return new Promise((resolve8, reject) => {
|
|
1436
1796
|
if (signal?.aborted) return reject(new DOMException("aborted", "AbortError"));
|
|
1437
1797
|
const t = setTimeout(() => {
|
|
1438
1798
|
signal?.removeEventListener("abort", onAbort);
|
|
1439
|
-
|
|
1799
|
+
resolve8();
|
|
1440
1800
|
}, ms);
|
|
1441
1801
|
const onAbort = () => {
|
|
1442
1802
|
clearTimeout(t);
|
|
@@ -1501,21 +1861,21 @@ var init_registry2 = __esm({
|
|
|
1501
1861
|
});
|
|
1502
1862
|
|
|
1503
1863
|
// src/storage-limits.ts
|
|
1504
|
-
import { readdir, stat, unlink } from "fs/promises";
|
|
1505
|
-
import { join as
|
|
1864
|
+
import { readdir as readdir2, stat as stat2, unlink } from "fs/promises";
|
|
1865
|
+
import { join as join8 } from "path";
|
|
1506
1866
|
async function listFilesByMtime(dir, pattern = /.*/) {
|
|
1507
1867
|
let entries;
|
|
1508
1868
|
try {
|
|
1509
|
-
entries = await
|
|
1869
|
+
entries = await readdir2(dir);
|
|
1510
1870
|
} catch {
|
|
1511
1871
|
return [];
|
|
1512
1872
|
}
|
|
1513
1873
|
const files = [];
|
|
1514
1874
|
for (const name of entries) {
|
|
1515
1875
|
if (!pattern.test(name)) continue;
|
|
1516
|
-
const p =
|
|
1876
|
+
const p = join8(dir, name);
|
|
1517
1877
|
try {
|
|
1518
|
-
const s = await
|
|
1878
|
+
const s = await stat2(p);
|
|
1519
1879
|
if (s.isFile()) files.push({ path: p, mtime: s.mtime });
|
|
1520
1880
|
} catch {
|
|
1521
1881
|
}
|
|
@@ -1554,7 +1914,7 @@ async function rotateJsonl(path, maxBytes, rotations) {
|
|
|
1554
1914
|
const { rename } = await import("fs/promises");
|
|
1555
1915
|
let s;
|
|
1556
1916
|
try {
|
|
1557
|
-
s = await
|
|
1917
|
+
s = await stat2(path);
|
|
1558
1918
|
} catch {
|
|
1559
1919
|
return;
|
|
1560
1920
|
}
|
|
@@ -1596,15 +1956,15 @@ var init_storage_limits = __esm({
|
|
|
1596
1956
|
});
|
|
1597
1957
|
|
|
1598
1958
|
// src/cost-debug.ts
|
|
1599
|
-
import { appendFile, mkdir as
|
|
1600
|
-
import { homedir as
|
|
1601
|
-
import { join as
|
|
1959
|
+
import { appendFile, mkdir as mkdir5 } from "fs/promises";
|
|
1960
|
+
import { homedir as homedir5 } from "os";
|
|
1961
|
+
import { join as join9 } from "path";
|
|
1602
1962
|
function debugDir() {
|
|
1603
|
-
const xdg = process.env.XDG_DATA_HOME ||
|
|
1604
|
-
return
|
|
1963
|
+
const xdg = process.env.XDG_DATA_HOME || join9(homedir5(), ".local", "share");
|
|
1964
|
+
return join9(xdg, "kimiflare");
|
|
1605
1965
|
}
|
|
1606
1966
|
function debugPath() {
|
|
1607
|
-
return
|
|
1967
|
+
return join9(debugDir(), "cost-debug.jsonl");
|
|
1608
1968
|
}
|
|
1609
1969
|
function now() {
|
|
1610
1970
|
return (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -1659,7 +2019,7 @@ function buildToolStats(results) {
|
|
|
1659
2019
|
});
|
|
1660
2020
|
}
|
|
1661
2021
|
async function logCostDebug(entry) {
|
|
1662
|
-
await
|
|
2022
|
+
await mkdir5(debugDir(), { recursive: true });
|
|
1663
2023
|
await rotateJsonl(debugPath(), RETENTION.costDebugMaxBytes, RETENTION.costDebugRotations);
|
|
1664
2024
|
await appendFile(debugPath(), JSON.stringify(entry) + "\n", "utf8");
|
|
1665
2025
|
}
|
|
@@ -2159,9 +2519,9 @@ var require_node_gyp_build = __commonJS({
|
|
|
2159
2519
|
var debug = getFirst(path.join(dir, "build/Debug"), matchBuild);
|
|
2160
2520
|
if (debug) return debug;
|
|
2161
2521
|
}
|
|
2162
|
-
var prebuild =
|
|
2522
|
+
var prebuild = resolve8(dir);
|
|
2163
2523
|
if (prebuild) return prebuild;
|
|
2164
|
-
var nearby =
|
|
2524
|
+
var nearby = resolve8(path.dirname(process.execPath));
|
|
2165
2525
|
if (nearby) return nearby;
|
|
2166
2526
|
var target = [
|
|
2167
2527
|
"platform=" + platform8,
|
|
@@ -2177,7 +2537,7 @@ var require_node_gyp_build = __commonJS({
|
|
|
2177
2537
|
// eslint-disable-line
|
|
2178
2538
|
].filter(Boolean).join(" ");
|
|
2179
2539
|
throw new Error("No native build was found for " + target + "\n loaded from: " + dir + "\n");
|
|
2180
|
-
function
|
|
2540
|
+
function resolve8(dir2) {
|
|
2181
2541
|
var tuples = readdirSync2(path.join(dir2, "prebuilds")).map(parseTuple);
|
|
2182
2542
|
var tuple = tuples.filter(matchTuple(platform8, arch)).sort(compareTuples)[0];
|
|
2183
2543
|
if (!tuple) return;
|
|
@@ -2317,7 +2677,7 @@ var require_isolated_vm = __commonJS({
|
|
|
2317
2677
|
});
|
|
2318
2678
|
|
|
2319
2679
|
// src/code-mode/sandbox.ts
|
|
2320
|
-
import { join as
|
|
2680
|
+
import { join as join10, dirname as dirname5 } from "path";
|
|
2321
2681
|
import { pathToFileURL } from "url";
|
|
2322
2682
|
function stripTypescript(code) {
|
|
2323
2683
|
let js = code;
|
|
@@ -2342,13 +2702,13 @@ async function loadTypescript(cwd) {
|
|
|
2342
2702
|
} catch {
|
|
2343
2703
|
}
|
|
2344
2704
|
let dir = cwd;
|
|
2345
|
-
while (dir !==
|
|
2705
|
+
while (dir !== dirname5(dir)) {
|
|
2346
2706
|
try {
|
|
2347
|
-
const tsPath =
|
|
2707
|
+
const tsPath = join10(dir, "node_modules", "typescript", "lib", "typescript.js");
|
|
2348
2708
|
return await import(pathToFileURL(tsPath).href);
|
|
2349
2709
|
} catch {
|
|
2350
2710
|
}
|
|
2351
|
-
dir =
|
|
2711
|
+
dir = dirname5(dir);
|
|
2352
2712
|
}
|
|
2353
2713
|
return null;
|
|
2354
2714
|
}
|
|
@@ -3025,7 +3385,7 @@ function truncateForEmbedding(text) {
|
|
|
3025
3385
|
return text.slice(0, MAX_EMBED_CHARS);
|
|
3026
3386
|
}
|
|
3027
3387
|
async function sleep2(ms) {
|
|
3028
|
-
return new Promise((
|
|
3388
|
+
return new Promise((resolve8) => setTimeout(resolve8, ms));
|
|
3029
3389
|
}
|
|
3030
3390
|
async function fetchWithRetry(url, init, retries = 3) {
|
|
3031
3391
|
let lastError;
|
|
@@ -3080,10 +3440,10 @@ async function fetchEmbeddings(opts2) {
|
|
|
3080
3440
|
const truncated = truncateForEmbedding(text);
|
|
3081
3441
|
const body = JSON.stringify({ text: [truncated] });
|
|
3082
3442
|
const res = await fetchWithRetry(url, { method: "POST", headers, body });
|
|
3083
|
-
const
|
|
3443
|
+
const json2 = await res.json();
|
|
3084
3444
|
let vectors = [];
|
|
3085
|
-
if (
|
|
3086
|
-
const result =
|
|
3445
|
+
if (json2 && typeof json2 === "object") {
|
|
3446
|
+
const result = json2.result;
|
|
3087
3447
|
if (result && typeof result === "object") {
|
|
3088
3448
|
const data = result.data;
|
|
3089
3449
|
if (Array.isArray(data)) {
|
|
@@ -3572,12 +3932,12 @@ var init_mode = __esm({
|
|
|
3572
3932
|
});
|
|
3573
3933
|
|
|
3574
3934
|
// src/agent/system-prompt.ts
|
|
3575
|
-
import { platform, release, homedir as
|
|
3576
|
-
import { basename, join as
|
|
3935
|
+
import { platform, release, homedir as homedir6 } from "os";
|
|
3936
|
+
import { basename, join as join11 } from "path";
|
|
3577
3937
|
import { readFileSync as readFileSync2, statSync as statSync2 } from "fs";
|
|
3578
3938
|
function loadContextFile(cwd) {
|
|
3579
3939
|
for (const name of CONTEXT_FILENAMES) {
|
|
3580
|
-
const path =
|
|
3940
|
+
const path = join11(cwd, name);
|
|
3581
3941
|
try {
|
|
3582
3942
|
const s = statSync2(path);
|
|
3583
3943
|
if (!s.isFile() || s.size > MAX_CONTEXT_BYTES) continue;
|
|
@@ -3627,7 +3987,7 @@ If the user asks what model you are, answer with exactly: \`${opts2.model}\`. Th
|
|
|
3627
3987
|
- Working directory: ${opts2.cwd}
|
|
3628
3988
|
- Platform: ${platform()} ${release()}
|
|
3629
3989
|
- Shell: ${shell}
|
|
3630
|
-
- Home: ${
|
|
3990
|
+
- Home: ${homedir6()}
|
|
3631
3991
|
- Today: ${date}`;
|
|
3632
3992
|
const hasLsp = opts2.tools.some((t) => t.name.startsWith("lsp_"));
|
|
3633
3993
|
const lspBlock = hasLsp ? "\n\nLSP tools are available for semantic code intelligence. Prefer `lsp_definition` over `grep` when looking for the source of a symbol. Prefer `lsp_references` over `grep` when finding usages. Use `lsp_hover` to confirm types before refactoring." : "";
|
|
@@ -4828,10 +5188,10 @@ var init_tool_error = __esm({
|
|
|
4828
5188
|
|
|
4829
5189
|
// src/util/paths.ts
|
|
4830
5190
|
import { resolve, isAbsolute, relative, sep } from "path";
|
|
4831
|
-
import { homedir as
|
|
5191
|
+
import { homedir as homedir7 } from "os";
|
|
4832
5192
|
function resolvePath(cwd, input) {
|
|
4833
5193
|
if (input.startsWith("~/") || input === "~") {
|
|
4834
|
-
return resolve(
|
|
5194
|
+
return resolve(homedir7(), input === "~" ? "." : input.slice(2));
|
|
4835
5195
|
}
|
|
4836
5196
|
return isAbsolute(input) ? input : resolve(cwd, input);
|
|
4837
5197
|
}
|
|
@@ -4865,7 +5225,7 @@ var init_paths = __esm({
|
|
|
4865
5225
|
});
|
|
4866
5226
|
|
|
4867
5227
|
// src/tools/read.ts
|
|
4868
|
-
import { readFile as
|
|
5228
|
+
import { readFile as readFile5, stat as stat3 } from "fs/promises";
|
|
4869
5229
|
import { createReadStream } from "fs";
|
|
4870
5230
|
import { createInterface } from "readline";
|
|
4871
5231
|
function aborted(signal) {
|
|
@@ -4936,7 +5296,7 @@ var init_read = __esm({
|
|
|
4936
5296
|
async run(args, ctx) {
|
|
4937
5297
|
if (aborted(ctx.signal)) throw abortError();
|
|
4938
5298
|
const abs = resolvePath(ctx.cwd, args.path);
|
|
4939
|
-
const st = await
|
|
5299
|
+
const st = await stat3(abs);
|
|
4940
5300
|
if (aborted(ctx.signal)) throw abortError();
|
|
4941
5301
|
if (st.size > MAX_BYTES) {
|
|
4942
5302
|
if (args.offset === void 0 || args.limit === void 0) {
|
|
@@ -4947,7 +5307,7 @@ var init_read = __esm({
|
|
|
4947
5307
|
const lines2 = await readSliceStreaming(abs, args.offset, args.limit, ctx.signal);
|
|
4948
5308
|
return formatLines(lines2, args.offset);
|
|
4949
5309
|
}
|
|
4950
|
-
const text = await
|
|
5310
|
+
const text = await readFile5(abs, { encoding: "utf8", signal: ctx.signal });
|
|
4951
5311
|
if (aborted(ctx.signal)) throw abortError();
|
|
4952
5312
|
const lines = text.split("\n");
|
|
4953
5313
|
const start = Math.max(0, (args.offset ?? 1) - 1);
|
|
@@ -4960,8 +5320,8 @@ var init_read = __esm({
|
|
|
4960
5320
|
});
|
|
4961
5321
|
|
|
4962
5322
|
// src/tools/write.ts
|
|
4963
|
-
import { mkdir as
|
|
4964
|
-
import { dirname as
|
|
5323
|
+
import { mkdir as mkdir6, readFile as readFile6, writeFile as writeFile5 } from "fs/promises";
|
|
5324
|
+
import { dirname as dirname6 } from "path";
|
|
4965
5325
|
var writeTool;
|
|
4966
5326
|
var init_write = __esm({
|
|
4967
5327
|
"src/tools/write.ts"() {
|
|
@@ -4988,11 +5348,11 @@ var init_write = __esm({
|
|
|
4988
5348
|
const abs = resolvePath(ctx.cwd, args.path);
|
|
4989
5349
|
let before = "";
|
|
4990
5350
|
try {
|
|
4991
|
-
before = await
|
|
5351
|
+
before = await readFile6(abs, "utf8");
|
|
4992
5352
|
} catch {
|
|
4993
5353
|
}
|
|
4994
|
-
await
|
|
4995
|
-
await
|
|
5354
|
+
await mkdir6(dirname6(abs), { recursive: true });
|
|
5355
|
+
await writeFile5(abs, args.content, "utf8");
|
|
4996
5356
|
const verb = before ? "Overwrote" : "Created";
|
|
4997
5357
|
return `${verb} ${args.path} (${args.content.length} chars).`;
|
|
4998
5358
|
}
|
|
@@ -5001,7 +5361,7 @@ var init_write = __esm({
|
|
|
5001
5361
|
});
|
|
5002
5362
|
|
|
5003
5363
|
// src/tools/edit.ts
|
|
5004
|
-
import { readFile as
|
|
5364
|
+
import { readFile as readFile7, writeFile as writeFile6 } from "fs/promises";
|
|
5005
5365
|
function countOccurrences(haystack, needle) {
|
|
5006
5366
|
if (!needle) return 0;
|
|
5007
5367
|
let count = 0;
|
|
@@ -5039,7 +5399,7 @@ var init_edit = __esm({
|
|
|
5039
5399
|
}),
|
|
5040
5400
|
async run(args, ctx) {
|
|
5041
5401
|
const abs = resolvePath(ctx.cwd, args.path);
|
|
5042
|
-
const orig = await
|
|
5402
|
+
const orig = await readFile7(abs, "utf8");
|
|
5043
5403
|
const occurrences = countOccurrences(orig, args.old_string);
|
|
5044
5404
|
if (occurrences === 0) throw new Error(`old_string not found in ${args.path}`);
|
|
5045
5405
|
if (occurrences > 1 && !args.replace_all) {
|
|
@@ -5048,7 +5408,7 @@ var init_edit = __esm({
|
|
|
5048
5408
|
);
|
|
5049
5409
|
}
|
|
5050
5410
|
const next = args.replace_all ? orig.split(args.old_string).join(args.new_string) : orig.replace(args.old_string, args.new_string);
|
|
5051
|
-
await
|
|
5411
|
+
await writeFile6(abs, next, "utf8");
|
|
5052
5412
|
return `Replaced ${occurrences} occurrence(s) in ${args.path}.`;
|
|
5053
5413
|
}
|
|
5054
5414
|
};
|
|
@@ -5058,7 +5418,7 @@ var init_edit = __esm({
|
|
|
5058
5418
|
// src/tools/bash.ts
|
|
5059
5419
|
import { spawn } from "child_process";
|
|
5060
5420
|
import { tmpdir, platform as platform2 } from "os";
|
|
5061
|
-
import { join as
|
|
5421
|
+
import { join as join12 } from "path";
|
|
5062
5422
|
function getShellCommand(override) {
|
|
5063
5423
|
const raw = override?.trim();
|
|
5064
5424
|
if (raw && raw !== "auto") {
|
|
@@ -5104,7 +5464,7 @@ function injectCoauthor(command, coauthor) {
|
|
|
5104
5464
|
const mentionsGit = /\bgit\b/.test(trimmed);
|
|
5105
5465
|
if (!createsCommit && !isRebaseContinue && !mentionsGit) return command;
|
|
5106
5466
|
if (movesHeadOnly) return command;
|
|
5107
|
-
const tmpFile =
|
|
5467
|
+
const tmpFile = join12(tmpdir(), `kf-coauthor-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`);
|
|
5108
5468
|
const amendBlock = `
|
|
5109
5469
|
if ! git log -1 --pretty=%B 2>/dev/null | grep -qF "${trailer}"; then
|
|
5110
5470
|
git log -1 --pretty=%B | git interpret-trailers --trailer "${trailer}" > "${tmpFile}" && git commit --amend -F "${tmpFile}" --no-edit && rm -f "${tmpFile}"
|
|
@@ -5126,7 +5486,7 @@ function runBash(args, ctx) {
|
|
|
5126
5486
|
const timeout = Math.min(Math.max(1e3, args.timeout_ms ?? DEFAULT_TIMEOUT), MAX_TIMEOUT);
|
|
5127
5487
|
const { shell, args: shellArgs, isPosix } = getShellCommand(ctx.shell);
|
|
5128
5488
|
const command = isPosix ? injectCoauthor(args.command, ctx.coauthor) : args.command;
|
|
5129
|
-
return new Promise((
|
|
5489
|
+
return new Promise((resolve8, reject) => {
|
|
5130
5490
|
logger.debug("bash:spawn", { command: args.command.slice(0, 200), cwd: ctx.cwd, shell });
|
|
5131
5491
|
const child = spawn(shell, [...shellArgs, command], {
|
|
5132
5492
|
cwd: ctx.cwd,
|
|
@@ -5179,7 +5539,7 @@ ${stdout.trimEnd()}`);
|
|
|
5179
5539
|
${stderr.trimEnd()}`);
|
|
5180
5540
|
if (!stdout && !stderr) parts.push("(no output)");
|
|
5181
5541
|
const raw = parts.join("\n");
|
|
5182
|
-
|
|
5542
|
+
resolve8({
|
|
5183
5543
|
content: raw,
|
|
5184
5544
|
rawBytes: Buffer.byteLength(raw, "utf8"),
|
|
5185
5545
|
reducedBytes: Buffer.byteLength(raw, "utf8")
|
|
@@ -5219,8 +5579,8 @@ var init_bash = __esm({
|
|
|
5219
5579
|
});
|
|
5220
5580
|
|
|
5221
5581
|
// src/util/glob.ts
|
|
5222
|
-
import { readdir as
|
|
5223
|
-
import { join as
|
|
5582
|
+
import { readdir as readdir3, lstat, realpath } from "fs/promises";
|
|
5583
|
+
import { join as join13, relative as relative2, resolve as resolve2 } from "path";
|
|
5224
5584
|
function parsePattern(pattern) {
|
|
5225
5585
|
const parts = pattern.split(/\//g);
|
|
5226
5586
|
return parts.map((p) => {
|
|
@@ -5318,7 +5678,7 @@ async function* walk(root, pattern, options) {
|
|
|
5318
5678
|
if (segIdx + 1 >= segs.length) {
|
|
5319
5679
|
let entries2;
|
|
5320
5680
|
try {
|
|
5321
|
-
entries2 = await
|
|
5681
|
+
entries2 = await readdir3(dirPath, { withFileTypes: true, encoding: "utf8" });
|
|
5322
5682
|
} catch (err) {
|
|
5323
5683
|
if (!suppressErrors) throw err;
|
|
5324
5684
|
return;
|
|
@@ -5329,7 +5689,7 @@ async function* walk(root, pattern, options) {
|
|
|
5329
5689
|
if (!dot && name.startsWith(".")) continue;
|
|
5330
5690
|
const matched = seg.type === "literal" ? seg.value === name : matchSegment(name, seg.value);
|
|
5331
5691
|
if (!matched) continue;
|
|
5332
|
-
const childPath =
|
|
5692
|
+
const childPath = join13(dirPath, name);
|
|
5333
5693
|
const childRel = [...relativeParts, name];
|
|
5334
5694
|
const childRelStr = childRel.join("/");
|
|
5335
5695
|
if (shouldIgnore(childRelStr, ignorePatterns)) continue;
|
|
@@ -5343,7 +5703,7 @@ async function* walk(root, pattern, options) {
|
|
|
5343
5703
|
}
|
|
5344
5704
|
let entries;
|
|
5345
5705
|
try {
|
|
5346
|
-
entries = await
|
|
5706
|
+
entries = await readdir3(dirPath, { withFileTypes: true, encoding: "utf8" });
|
|
5347
5707
|
} catch (err) {
|
|
5348
5708
|
if (!suppressErrors) throw err;
|
|
5349
5709
|
return;
|
|
@@ -5354,7 +5714,7 @@ async function* walk(root, pattern, options) {
|
|
|
5354
5714
|
if (!dot && name.startsWith(".")) continue;
|
|
5355
5715
|
const matched = seg.type === "literal" ? seg.value === name : matchSegment(name, seg.value);
|
|
5356
5716
|
if (!matched) continue;
|
|
5357
|
-
const childPath =
|
|
5717
|
+
const childPath = join13(dirPath, name);
|
|
5358
5718
|
const childRel = [...relativeParts, name];
|
|
5359
5719
|
const childRelStr = childRel.join("/");
|
|
5360
5720
|
if (shouldIgnore(childRelStr, ignorePatterns)) continue;
|
|
@@ -5387,7 +5747,7 @@ async function* walk(root, pattern, options) {
|
|
|
5387
5747
|
yield* processEntries(dirPath, segIdx + 1, relativeParts);
|
|
5388
5748
|
let entries;
|
|
5389
5749
|
try {
|
|
5390
|
-
entries = await
|
|
5750
|
+
entries = await readdir3(dirPath, { withFileTypes: true, encoding: "utf8" });
|
|
5391
5751
|
} catch (err) {
|
|
5392
5752
|
if (!suppressErrors) throw err;
|
|
5393
5753
|
return;
|
|
@@ -5396,7 +5756,7 @@ async function* walk(root, pattern, options) {
|
|
|
5396
5756
|
const name = String(ent.name);
|
|
5397
5757
|
if (name === "." || name === "..") continue;
|
|
5398
5758
|
if (!dot && name.startsWith(".")) continue;
|
|
5399
|
-
const childPath =
|
|
5759
|
+
const childPath = join13(dirPath, name);
|
|
5400
5760
|
const childRel = [...relativeParts, name];
|
|
5401
5761
|
const childRelStr = childRel.join("/");
|
|
5402
5762
|
if (shouldIgnore(childRelStr, ignorePatterns)) continue;
|
|
@@ -5543,7 +5903,7 @@ var init_glob2 = __esm({
|
|
|
5543
5903
|
// src/tools/grep.ts
|
|
5544
5904
|
import { execFile } from "child_process";
|
|
5545
5905
|
import { promisify } from "util";
|
|
5546
|
-
import { readFile as
|
|
5906
|
+
import { readFile as readFile8 } from "fs/promises";
|
|
5547
5907
|
async function hasRipgrep() {
|
|
5548
5908
|
if (cachedHasRg !== null) return cachedHasRg;
|
|
5549
5909
|
try {
|
|
@@ -5590,7 +5950,7 @@ async function runJsFallback(args, root, mode, signal) {
|
|
|
5590
5950
|
if (signal?.aborted) throw new DOMException("aborted", "AbortError");
|
|
5591
5951
|
const file = files[fi];
|
|
5592
5952
|
try {
|
|
5593
|
-
const content = await
|
|
5953
|
+
const content = await readFile8(file, "utf8");
|
|
5594
5954
|
if (mode === "files") {
|
|
5595
5955
|
if (re.test(content)) out.push(file);
|
|
5596
5956
|
} else {
|
|
@@ -6080,9 +6440,9 @@ var changelog_image_exports = {};
|
|
|
6080
6440
|
__export(changelog_image_exports, {
|
|
6081
6441
|
changelogImageTool: () => changelogImageTool
|
|
6082
6442
|
});
|
|
6083
|
-
import { readFile as
|
|
6084
|
-
import { writeFile as
|
|
6085
|
-
import { join as
|
|
6443
|
+
import { readFile as readFile9 } from "fs/promises";
|
|
6444
|
+
import { writeFile as writeFile7 } from "fs/promises";
|
|
6445
|
+
import { join as join14 } from "path";
|
|
6086
6446
|
import { Resvg } from "@resvg/resvg-js";
|
|
6087
6447
|
async function githubFetch2(path, token) {
|
|
6088
6448
|
const controller = new AbortController();
|
|
@@ -6114,7 +6474,7 @@ function escapeXml(str) {
|
|
|
6114
6474
|
}
|
|
6115
6475
|
async function loadLogoBase64() {
|
|
6116
6476
|
try {
|
|
6117
|
-
const buf = await
|
|
6477
|
+
const buf = await readFile9(join14(process.cwd(), "docs", "logo.png"));
|
|
6118
6478
|
return `data:image/png;base64,${buf.toString("base64")}`;
|
|
6119
6479
|
} catch {
|
|
6120
6480
|
return null;
|
|
@@ -6407,7 +6767,7 @@ Format your response as plain text bullet points, one per line, starting with "\
|
|
|
6407
6767
|
setTask("4", "completed");
|
|
6408
6768
|
setTask("5", "in_progress");
|
|
6409
6769
|
const outputPath = args.output ?? `./changelog-${args.repo}-${version.replace(/[^a-zA-Z0-9._-]/g, "_")}.png`;
|
|
6410
|
-
await
|
|
6770
|
+
await writeFile7(outputPath, pngBuffer);
|
|
6411
6771
|
setTask("5", "completed");
|
|
6412
6772
|
const periodLabel = days === 1 ? "past day" : `past ${days} days`;
|
|
6413
6773
|
const content = `\u2713 Changelog image saved to ${outputPath}
|
|
@@ -6420,12 +6780,12 @@ Format your response as plain text bullet points, one per line, starting with "\
|
|
|
6420
6780
|
});
|
|
6421
6781
|
|
|
6422
6782
|
// src/tools/browser.ts
|
|
6423
|
-
import { mkdir as
|
|
6783
|
+
import { mkdir as mkdir7 } from "fs/promises";
|
|
6424
6784
|
import { tmpdir as tmpdir2 } from "os";
|
|
6425
|
-
import { join as
|
|
6785
|
+
import { join as join15, dirname as dirname7 } from "path";
|
|
6426
6786
|
async function autoScroll(page) {
|
|
6427
6787
|
await page.evaluate(async () => {
|
|
6428
|
-
await new Promise((
|
|
6788
|
+
await new Promise((resolve8) => {
|
|
6429
6789
|
let totalHeight = 0;
|
|
6430
6790
|
const distance = 300;
|
|
6431
6791
|
const timer2 = setInterval(() => {
|
|
@@ -6434,12 +6794,12 @@ async function autoScroll(page) {
|
|
|
6434
6794
|
totalHeight += distance;
|
|
6435
6795
|
if (totalHeight >= scrollHeight) {
|
|
6436
6796
|
clearInterval(timer2);
|
|
6437
|
-
|
|
6797
|
+
resolve8();
|
|
6438
6798
|
}
|
|
6439
6799
|
}, 100);
|
|
6440
6800
|
setTimeout(() => {
|
|
6441
6801
|
clearInterval(timer2);
|
|
6442
|
-
|
|
6802
|
+
resolve8();
|
|
6443
6803
|
}, 1e4);
|
|
6444
6804
|
});
|
|
6445
6805
|
});
|
|
@@ -6498,8 +6858,8 @@ var init_browser = __esm({
|
|
|
6498
6858
|
}
|
|
6499
6859
|
let screenshotPath;
|
|
6500
6860
|
if (args.screenshot) {
|
|
6501
|
-
screenshotPath =
|
|
6502
|
-
await
|
|
6861
|
+
screenshotPath = join15(tmpdir2(), `kimiflare-browser-${Date.now()}.png`);
|
|
6862
|
+
await mkdir7(dirname7(screenshotPath), { recursive: true });
|
|
6503
6863
|
await page.screenshot({ path: screenshotPath, fullPage: true });
|
|
6504
6864
|
}
|
|
6505
6865
|
const text = await page.evaluate(() => {
|
|
@@ -7866,369 +8226,598 @@ var init_executor = __esm({
|
|
|
7866
8226
|
}
|
|
7867
8227
|
});
|
|
7868
8228
|
|
|
7869
|
-
// src/
|
|
7870
|
-
|
|
7871
|
-
|
|
7872
|
-
|
|
7873
|
-
|
|
7874
|
-
|
|
7875
|
-
|
|
7876
|
-
|
|
8229
|
+
// src/sessions.ts
|
|
8230
|
+
var sessions_exports = {};
|
|
8231
|
+
__export(sessions_exports, {
|
|
8232
|
+
addCheckpoint: () => addCheckpoint,
|
|
8233
|
+
generateSessionTitle: () => generateSessionTitle,
|
|
8234
|
+
listSessions: () => listSessions,
|
|
8235
|
+
loadSession: () => loadSession,
|
|
8236
|
+
loadSessionFromCheckpoint: () => loadSessionFromCheckpoint,
|
|
8237
|
+
makeSessionId: () => makeSessionId,
|
|
8238
|
+
pruneSessions: () => pruneSessions,
|
|
8239
|
+
saveSession: () => saveSession,
|
|
8240
|
+
sessionsDir: () => sessionsDir
|
|
8241
|
+
});
|
|
8242
|
+
import { readFile as readFile10, writeFile as writeFile9, mkdir as mkdir8, readdir as readdir4, stat as stat4 } from "fs/promises";
|
|
8243
|
+
import { homedir as homedir8 } from "os";
|
|
8244
|
+
import { join as join16 } from "path";
|
|
8245
|
+
function sessionsDir() {
|
|
8246
|
+
const xdg = process.env.XDG_DATA_HOME || join16(homedir8(), ".local", "share");
|
|
8247
|
+
return join16(xdg, "kimiflare", "sessions");
|
|
7877
8248
|
}
|
|
7878
|
-
|
|
7879
|
-
|
|
7880
|
-
|
|
7881
|
-
|
|
8249
|
+
function sanitize(text) {
|
|
8250
|
+
return text.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40);
|
|
8251
|
+
}
|
|
8252
|
+
function generateSessionTitle(firstPrompt, intent) {
|
|
8253
|
+
const prefix = INTENT_PREFIX_MAP[intent] ?? INTENT_PREFIX_MAP.default;
|
|
8254
|
+
const cleaned = firstPrompt.replace(/\s+/g, " ").replace(/[\n\r]/g, " ").trim();
|
|
8255
|
+
const words = cleaned.split(" ").slice(0, 6).join(" ");
|
|
8256
|
+
const title = `${prefix} ${words}`;
|
|
8257
|
+
return title.length > 40 ? title.slice(0, 37) + "..." : title;
|
|
8258
|
+
}
|
|
8259
|
+
function makeSessionId(firstPrompt) {
|
|
8260
|
+
const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
8261
|
+
const slug = sanitize(firstPrompt) || "session";
|
|
8262
|
+
return `${ts}_${slug}`;
|
|
8263
|
+
}
|
|
8264
|
+
async function saveSession(file) {
|
|
8265
|
+
const dir = sessionsDir();
|
|
8266
|
+
await mkdir8(dir, { recursive: true });
|
|
8267
|
+
const path = join16(dir, `${file.id}.json`);
|
|
8268
|
+
await writeFile9(path, JSON.stringify(file, null, 2), "utf8");
|
|
8269
|
+
return path;
|
|
8270
|
+
}
|
|
8271
|
+
async function pruneSessions() {
|
|
8272
|
+
const dir = sessionsDir();
|
|
8273
|
+
const files = await listFilesByMtime(dir, /\.json$/);
|
|
8274
|
+
return pruneFiles(files, RETENTION.sessionMaxAgeDays, RETENTION.sessionMaxCount);
|
|
8275
|
+
}
|
|
8276
|
+
async function listSessions(limit = 30, cwd) {
|
|
8277
|
+
const dir = sessionsDir();
|
|
8278
|
+
let entries;
|
|
8279
|
+
try {
|
|
8280
|
+
entries = await readdir4(dir);
|
|
8281
|
+
} catch {
|
|
8282
|
+
return [];
|
|
8283
|
+
}
|
|
8284
|
+
const summaries = [];
|
|
8285
|
+
for (const name of entries) {
|
|
8286
|
+
if (!name.endsWith(".json")) continue;
|
|
8287
|
+
const path = join16(dir, name);
|
|
7882
8288
|
try {
|
|
7883
|
-
const raw = await
|
|
8289
|
+
const [s, raw] = await Promise.all([stat4(path), readFile10(path, "utf8")]);
|
|
7884
8290
|
const parsed = JSON.parse(raw);
|
|
7885
|
-
if (
|
|
7886
|
-
|
|
7887
|
-
|
|
8291
|
+
if (cwd && parsed.cwd !== cwd) continue;
|
|
8292
|
+
const firstUser = parsed.messages.find((m) => m.role === "user");
|
|
8293
|
+
const firstPrompt = typeof firstUser?.content === "string" ? firstUser.content : firstUser?.content ? firstUser.content.find((p) => p.type === "text")?.text ?? "(no prompt)" : "(no prompt)";
|
|
8294
|
+
summaries.push({
|
|
8295
|
+
id: parsed.id,
|
|
8296
|
+
filePath: path,
|
|
8297
|
+
cwd: parsed.cwd,
|
|
8298
|
+
firstPrompt: firstPrompt.slice(0, 80),
|
|
8299
|
+
title: parsed.title,
|
|
8300
|
+
messageCount: parsed.messages.filter((m) => m.role !== "system").length,
|
|
8301
|
+
updatedAt: parsed.updatedAt ?? s.mtime.toISOString(),
|
|
8302
|
+
checkpointCount: parsed.checkpoints?.length ?? 0
|
|
8303
|
+
});
|
|
7888
8304
|
} catch {
|
|
7889
8305
|
}
|
|
7890
|
-
const parent = dirname6(dir);
|
|
7891
|
-
if (parent === dir) break;
|
|
7892
|
-
dir = parent;
|
|
7893
8306
|
}
|
|
7894
|
-
|
|
8307
|
+
summaries.sort((a, b) => b.updatedAt < a.updatedAt ? -1 : 1);
|
|
8308
|
+
return summaries.slice(0, limit);
|
|
7895
8309
|
}
|
|
7896
|
-
async function
|
|
7897
|
-
const
|
|
7898
|
-
|
|
7899
|
-
return found?.version ?? null;
|
|
8310
|
+
async function loadSession(filePath) {
|
|
8311
|
+
const raw = await readFile10(filePath, "utf8");
|
|
8312
|
+
return JSON.parse(raw);
|
|
7900
8313
|
}
|
|
7901
|
-
async function
|
|
7902
|
-
|
|
7903
|
-
|
|
7904
|
-
|
|
7905
|
-
|
|
7906
|
-
return parsed;
|
|
7907
|
-
}
|
|
7908
|
-
} catch {
|
|
7909
|
-
}
|
|
7910
|
-
return null;
|
|
8314
|
+
async function addCheckpoint(filePath, checkpoint) {
|
|
8315
|
+
const file = await loadSession(filePath);
|
|
8316
|
+
if (!file.checkpoints) file.checkpoints = [];
|
|
8317
|
+
file.checkpoints.push(checkpoint);
|
|
8318
|
+
await saveSession(file);
|
|
7911
8319
|
}
|
|
7912
|
-
async function
|
|
7913
|
-
const
|
|
7914
|
-
|
|
7915
|
-
|
|
8320
|
+
async function loadSessionFromCheckpoint(filePath, checkpointId) {
|
|
8321
|
+
const file = await loadSession(filePath);
|
|
8322
|
+
const checkpoint = file.checkpoints?.find((c) => c.id === checkpointId);
|
|
8323
|
+
if (!checkpoint) {
|
|
8324
|
+
throw new Error(`checkpoint ${checkpointId} not found`);
|
|
8325
|
+
}
|
|
8326
|
+
const truncated = file.messages.slice(0, checkpoint.turnIndex);
|
|
8327
|
+
return {
|
|
8328
|
+
file: {
|
|
8329
|
+
...file,
|
|
8330
|
+
messages: truncated,
|
|
8331
|
+
sessionState: checkpoint.sessionState ?? file.sessionState,
|
|
8332
|
+
artifactStore: checkpoint.artifactStore ?? file.artifactStore
|
|
8333
|
+
},
|
|
8334
|
+
checkpoint
|
|
8335
|
+
};
|
|
7916
8336
|
}
|
|
7917
|
-
|
|
7918
|
-
|
|
7919
|
-
|
|
7920
|
-
|
|
7921
|
-
|
|
7922
|
-
|
|
7923
|
-
|
|
7924
|
-
|
|
7925
|
-
|
|
7926
|
-
|
|
8337
|
+
var INTENT_PREFIX_MAP;
|
|
8338
|
+
var init_sessions = __esm({
|
|
8339
|
+
"src/sessions.ts"() {
|
|
8340
|
+
"use strict";
|
|
8341
|
+
init_storage_limits();
|
|
8342
|
+
INTENT_PREFIX_MAP = {
|
|
8343
|
+
diagnose: "Bug:",
|
|
8344
|
+
feature_bounded: "Feature:",
|
|
8345
|
+
feature_exploratory: "Feature:",
|
|
8346
|
+
polish: "Refactor:",
|
|
8347
|
+
meta: "Plan:",
|
|
8348
|
+
explore: "Explore:",
|
|
8349
|
+
qa: "Q&A:",
|
|
8350
|
+
verify: "Verify:",
|
|
8351
|
+
small_edit: "Edit:",
|
|
8352
|
+
default: "Task:"
|
|
8353
|
+
};
|
|
7927
8354
|
}
|
|
8355
|
+
});
|
|
8356
|
+
|
|
8357
|
+
// src/util/image.ts
|
|
8358
|
+
import { readFile as readFile11 } from "fs/promises";
|
|
8359
|
+
import { basename as basename2 } from "path";
|
|
8360
|
+
async function encodeImageFile(filePath) {
|
|
8361
|
+
const buf = await readFile11(filePath);
|
|
8362
|
+
if (buf.byteLength > MAX_IMAGE_BYTES) {
|
|
8363
|
+
throw new Error(
|
|
8364
|
+
`image too large (${(buf.byteLength / 1024 / 1024).toFixed(1)} MB); max is ${MAX_IMAGE_BYTES / 1024 / 1024} MB`
|
|
8365
|
+
);
|
|
8366
|
+
}
|
|
8367
|
+
const ext = filePath.slice(filePath.lastIndexOf(".")).toLowerCase();
|
|
8368
|
+
const mime = EXT_TO_MIME[ext] ?? "image/jpeg";
|
|
8369
|
+
const b64 = buf.toString("base64");
|
|
8370
|
+
return {
|
|
8371
|
+
filename: basename2(filePath),
|
|
8372
|
+
mime,
|
|
8373
|
+
dataUrl: `data:${mime};base64,${b64}`
|
|
8374
|
+
};
|
|
7928
8375
|
}
|
|
7929
|
-
function
|
|
7930
|
-
|
|
8376
|
+
function isImagePath(path) {
|
|
8377
|
+
const ext = path.slice(path.lastIndexOf(".")).toLowerCase();
|
|
8378
|
+
return ext in EXT_TO_MIME;
|
|
7931
8379
|
}
|
|
7932
|
-
|
|
7933
|
-
|
|
7934
|
-
|
|
7935
|
-
|
|
7936
|
-
|
|
7937
|
-
|
|
7938
|
-
|
|
7939
|
-
|
|
8380
|
+
var MAX_IMAGE_BYTES, EXT_TO_MIME;
|
|
8381
|
+
var init_image = __esm({
|
|
8382
|
+
"src/util/image.ts"() {
|
|
8383
|
+
"use strict";
|
|
8384
|
+
MAX_IMAGE_BYTES = 5 * 1024 * 1024;
|
|
8385
|
+
EXT_TO_MIME = {
|
|
8386
|
+
".png": "image/png",
|
|
8387
|
+
".jpg": "image/jpeg",
|
|
8388
|
+
".jpeg": "image/jpeg",
|
|
8389
|
+
".gif": "image/gif",
|
|
8390
|
+
".webp": "image/webp",
|
|
8391
|
+
".bmp": "image/bmp"
|
|
8392
|
+
};
|
|
7940
8393
|
}
|
|
7941
|
-
|
|
8394
|
+
});
|
|
8395
|
+
|
|
8396
|
+
// src/permissions-evaluator.ts
|
|
8397
|
+
import { resolve as resolve3, relative as relative3 } from "path";
|
|
8398
|
+
import { homedir as homedir9 } from "os";
|
|
8399
|
+
function evaluatePermissionRules(req, rules) {
|
|
8400
|
+
const toolRules = rules[req.tool];
|
|
8401
|
+
if (!toolRules) return "ask";
|
|
8402
|
+
const target = extractTarget(req.tool, req.args, req.cwd);
|
|
8403
|
+
if (!target) return "ask";
|
|
8404
|
+
const entries = Object.entries(toolRules).sort((a, b) => b[0].length - a[0].length);
|
|
8405
|
+
for (const [pattern, rule] of entries) {
|
|
8406
|
+
if (matchPattern(target, pattern, req.cwd)) {
|
|
8407
|
+
return rule;
|
|
8408
|
+
}
|
|
8409
|
+
}
|
|
8410
|
+
return "ask";
|
|
7942
8411
|
}
|
|
7943
|
-
|
|
7944
|
-
|
|
7945
|
-
|
|
7946
|
-
const candidate = join13(here, "..", "..", "node_modules", name, "package.json");
|
|
7947
|
-
const raw = await readFile8(candidate, "utf8");
|
|
7948
|
-
const parsed = JSON.parse(raw);
|
|
7949
|
-
return parsed.version ?? null;
|
|
7950
|
-
} catch {
|
|
7951
|
-
return null;
|
|
8412
|
+
function expandHome(path) {
|
|
8413
|
+
if (path.startsWith("~/")) {
|
|
8414
|
+
return resolve3(homedir9(), path.slice(2));
|
|
7952
8415
|
}
|
|
8416
|
+
return path;
|
|
7953
8417
|
}
|
|
7954
|
-
|
|
7955
|
-
|
|
7956
|
-
|
|
7957
|
-
|
|
7958
|
-
|
|
7959
|
-
|
|
7960
|
-
|
|
7961
|
-
|
|
7962
|
-
|
|
7963
|
-
|
|
8418
|
+
function extractTarget(tool, args, cwd) {
|
|
8419
|
+
switch (tool) {
|
|
8420
|
+
case "write":
|
|
8421
|
+
case "edit":
|
|
8422
|
+
case "read":
|
|
8423
|
+
case "glob":
|
|
8424
|
+
case "grep":
|
|
8425
|
+
return typeof args.path === "string" ? resolve3(cwd, expandHome(args.path)) : null;
|
|
8426
|
+
case "bash":
|
|
8427
|
+
return typeof args.command === "string" ? args.command : null;
|
|
8428
|
+
case "browser_fetch":
|
|
8429
|
+
case "web_fetch":
|
|
8430
|
+
return typeof args.url === "string" ? args.url : null;
|
|
8431
|
+
case "search_web":
|
|
8432
|
+
return typeof args.query === "string" ? args.query : null;
|
|
8433
|
+
default:
|
|
8434
|
+
return (typeof args.path === "string" ? resolve3(cwd, expandHome(args.path)) : null) ?? (typeof args.url === "string" ? args.url : null) ?? (typeof args.command === "string" ? args.command : null);
|
|
7964
8435
|
}
|
|
7965
8436
|
}
|
|
7966
|
-
|
|
7967
|
-
|
|
7968
|
-
if (
|
|
7969
|
-
|
|
8437
|
+
function matchPattern(target, pattern, cwd) {
|
|
8438
|
+
let expandedPattern = pattern;
|
|
8439
|
+
if (pattern.startsWith("~/")) {
|
|
8440
|
+
expandedPattern = resolve3(homedir9(), pattern.slice(2));
|
|
7970
8441
|
}
|
|
7971
|
-
|
|
7972
|
-
|
|
7973
|
-
return { name, hasUpdate: false, localVersion, latestVersion: null };
|
|
8442
|
+
if (target.startsWith(expandedPattern)) {
|
|
8443
|
+
return true;
|
|
7974
8444
|
}
|
|
7975
|
-
const
|
|
7976
|
-
|
|
7977
|
-
|
|
7978
|
-
|
|
7979
|
-
|
|
7980
|
-
|
|
7981
|
-
|
|
7982
|
-
|
|
7983
|
-
if (cached) {
|
|
7984
|
-
const hasUpdate2 = isNewer(localVersion, cached.latestVersion);
|
|
7985
|
-
return { hasUpdate: hasUpdate2, localVersion, latestVersion: cached.latestVersion };
|
|
8445
|
+
const regex = globToRegex2(expandedPattern);
|
|
8446
|
+
if (regex.test(target)) {
|
|
8447
|
+
return true;
|
|
8448
|
+
}
|
|
8449
|
+
try {
|
|
8450
|
+
const relTarget = relative3(cwd, target);
|
|
8451
|
+
if (regex.test(relTarget)) {
|
|
8452
|
+
return true;
|
|
7986
8453
|
}
|
|
8454
|
+
} catch {
|
|
7987
8455
|
}
|
|
7988
|
-
|
|
7989
|
-
|
|
7990
|
-
|
|
8456
|
+
return false;
|
|
8457
|
+
}
|
|
8458
|
+
function globToRegex2(pattern) {
|
|
8459
|
+
let regex = pattern.replace(/\*\*/g, "<<<DOUBLESTAR>>>").replace(/\*/g, "[^/]*").replace(/<<<DOUBLESTAR>>>/g, ".*").replace(/\?/g, ".");
|
|
8460
|
+
if (!regex.startsWith("/") && !regex.startsWith(".*")) {
|
|
8461
|
+
regex = ".*/" + regex;
|
|
7991
8462
|
}
|
|
7992
|
-
|
|
7993
|
-
await writeCache({ checkedAt: Date.now(), latestVersion });
|
|
7994
|
-
return { hasUpdate, localVersion, latestVersion };
|
|
8463
|
+
return new RegExp(`^${regex}$`);
|
|
7995
8464
|
}
|
|
7996
|
-
var
|
|
7997
|
-
|
|
7998
|
-
"src/util/update-check.ts"() {
|
|
8465
|
+
var init_permissions_evaluator = __esm({
|
|
8466
|
+
"src/permissions-evaluator.ts"() {
|
|
7999
8467
|
"use strict";
|
|
8000
|
-
init_version();
|
|
8001
|
-
CACHE_TTL_MS = 60 * 60 * 1e3;
|
|
8002
|
-
NPM_REGISTRY = "https://registry.npmjs.org/kimiflare/latest";
|
|
8003
8468
|
}
|
|
8004
8469
|
});
|
|
8005
8470
|
|
|
8006
|
-
// src/
|
|
8007
|
-
|
|
8008
|
-
|
|
8009
|
-
|
|
8010
|
-
|
|
8011
|
-
|
|
8012
|
-
|
|
8471
|
+
// src/hooks/types.ts
|
|
8472
|
+
var HOOK_EVENTS;
|
|
8473
|
+
var init_types = __esm({
|
|
8474
|
+
"src/hooks/types.ts"() {
|
|
8475
|
+
"use strict";
|
|
8476
|
+
HOOK_EVENTS = [
|
|
8477
|
+
"PreToolUse",
|
|
8478
|
+
"PostToolUse",
|
|
8479
|
+
"UserPromptSubmit",
|
|
8480
|
+
"Stop",
|
|
8481
|
+
"PreCompact"
|
|
8482
|
+
];
|
|
8483
|
+
}
|
|
8484
|
+
});
|
|
8485
|
+
|
|
8486
|
+
// src/hooks/settings.ts
|
|
8487
|
+
import { existsSync as existsSync2, readFileSync as readFileSync3, mkdirSync as mkdirSync2, writeFileSync } from "fs";
|
|
8488
|
+
import { homedir as homedir10 } from "os";
|
|
8489
|
+
import { join as join17, dirname as dirname8 } from "path";
|
|
8490
|
+
import { createHash } from "crypto";
|
|
8491
|
+
function globalSettingsPath() {
|
|
8492
|
+
const xdg = process.env.XDG_CONFIG_HOME || join17(homedir10(), ".config");
|
|
8493
|
+
return join17(xdg, "kimiflare", "settings.json");
|
|
8013
8494
|
}
|
|
8014
|
-
|
|
8015
|
-
|
|
8016
|
-
await mkdir7(dir, { recursive: true });
|
|
8017
|
-
const path = join14(dir, `${session.sessionId}.json`);
|
|
8018
|
-
await writeFile8(path, JSON.stringify(session, null, 2) + "\n", "utf8");
|
|
8495
|
+
function projectSettingsPath(cwd) {
|
|
8496
|
+
return join17(cwd, ".kimiflare", "settings.json");
|
|
8019
8497
|
}
|
|
8020
|
-
|
|
8498
|
+
function readSettingsFile(path) {
|
|
8499
|
+
if (!existsSync2(path)) return null;
|
|
8021
8500
|
try {
|
|
8022
|
-
const
|
|
8023
|
-
const
|
|
8024
|
-
return
|
|
8501
|
+
const raw = readFileSync3(path, "utf8");
|
|
8502
|
+
const parsed = JSON.parse(raw);
|
|
8503
|
+
return parsed && typeof parsed === "object" ? parsed : null;
|
|
8025
8504
|
} catch {
|
|
8026
8505
|
return null;
|
|
8027
8506
|
}
|
|
8028
8507
|
}
|
|
8029
|
-
|
|
8030
|
-
const
|
|
8031
|
-
|
|
8032
|
-
|
|
8033
|
-
|
|
8034
|
-
|
|
8035
|
-
|
|
8036
|
-
|
|
8037
|
-
|
|
8038
|
-
|
|
8039
|
-
|
|
8040
|
-
|
|
8508
|
+
function deriveHookId(event, command) {
|
|
8509
|
+
const h = createHash("sha256").update(`${event}\0${command}`).digest("hex");
|
|
8510
|
+
return h.slice(0, 8);
|
|
8511
|
+
}
|
|
8512
|
+
function normalizeHook(event, raw, source) {
|
|
8513
|
+
if (!raw || typeof raw !== "object") return null;
|
|
8514
|
+
const r = raw;
|
|
8515
|
+
if (typeof r.command !== "string" || !r.command.trim()) return null;
|
|
8516
|
+
const enabled = typeof r.enabled === "boolean" ? r.enabled : true;
|
|
8517
|
+
const matcher = typeof r.matcher === "string" ? r.matcher : void 0;
|
|
8518
|
+
const timeoutMs = typeof r.timeoutMs === "number" && r.timeoutMs > 0 ? r.timeoutMs : void 0;
|
|
8519
|
+
const description = typeof r.description === "string" ? r.description : void 0;
|
|
8520
|
+
const id = typeof r.id === "string" && r.id.length > 0 ? r.id : deriveHookId(event, r.command);
|
|
8521
|
+
return { id, matcher, command: r.command, timeoutMs, enabled, description, source };
|
|
8522
|
+
}
|
|
8523
|
+
function mergeHookMaps(a, b) {
|
|
8524
|
+
const out = {};
|
|
8525
|
+
for (const ev of HOOK_EVENTS) {
|
|
8526
|
+
const left = a?.[ev] ?? [];
|
|
8527
|
+
const right = b?.[ev] ?? [];
|
|
8528
|
+
if (left.length + right.length === 0) continue;
|
|
8529
|
+
out[ev] = [...left, ...right];
|
|
8530
|
+
}
|
|
8531
|
+
return out;
|
|
8532
|
+
}
|
|
8533
|
+
function loadHooksSettings(cwd) {
|
|
8534
|
+
const global = readSettingsFile(globalSettingsPath());
|
|
8535
|
+
const project = readSettingsFile(projectSettingsPath(cwd));
|
|
8536
|
+
const normalized = (raw, source) => {
|
|
8537
|
+
const out = {};
|
|
8538
|
+
for (const ev of HOOK_EVENTS) {
|
|
8539
|
+
const list = raw?.hooks?.[ev];
|
|
8540
|
+
if (!Array.isArray(list)) continue;
|
|
8541
|
+
const cleaned = list.map((h) => normalizeHook(ev, h, source)).filter((h) => h !== null);
|
|
8542
|
+
if (cleaned.length > 0) out[ev] = cleaned;
|
|
8041
8543
|
}
|
|
8042
|
-
return
|
|
8043
|
-
}
|
|
8044
|
-
|
|
8544
|
+
return out;
|
|
8545
|
+
};
|
|
8546
|
+
const merged = mergeHookMaps(normalized(global, "global"), normalized(project, "project"));
|
|
8547
|
+
return { hooks: merged };
|
|
8548
|
+
}
|
|
8549
|
+
function appendHook(scope, cwd, event, hook) {
|
|
8550
|
+
const path = scope === "global" ? globalSettingsPath() : projectSettingsPath(cwd);
|
|
8551
|
+
const existing = readSettingsFile(path) ?? {};
|
|
8552
|
+
const hooks = existing.hooks ?? {};
|
|
8553
|
+
const list = hooks[event] ?? [];
|
|
8554
|
+
const { source: _src, ...toWrite } = hook;
|
|
8555
|
+
const newId = toWrite.id ?? deriveHookId(event, toWrite.command);
|
|
8556
|
+
const byId = /* @__PURE__ */ new Map();
|
|
8557
|
+
for (const raw of list) {
|
|
8558
|
+
if (!raw || typeof raw !== "object") continue;
|
|
8559
|
+
const h = raw;
|
|
8560
|
+
if (typeof h.command !== "string") continue;
|
|
8561
|
+
const id = h.id ?? deriveHookId(event, h.command);
|
|
8562
|
+
if (byId.has(id)) continue;
|
|
8563
|
+
byId.set(id, h);
|
|
8045
8564
|
}
|
|
8565
|
+
byId.set(newId, { ...toWrite, id: newId });
|
|
8566
|
+
hooks[event] = Array.from(byId.values()).map((h) => {
|
|
8567
|
+
if (hook.id) return h;
|
|
8568
|
+
const { id: _id, ...rest } = h;
|
|
8569
|
+
return rest;
|
|
8570
|
+
});
|
|
8571
|
+
existing.hooks = hooks;
|
|
8572
|
+
mkdirSync2(dirname8(path), { recursive: true });
|
|
8573
|
+
writeFileSync(path, JSON.stringify(existing, null, 2) + "\n", "utf8");
|
|
8574
|
+
return path;
|
|
8046
8575
|
}
|
|
8047
|
-
|
|
8048
|
-
const
|
|
8049
|
-
|
|
8576
|
+
function setHookEnabled(cwd, id, enabled) {
|
|
8577
|
+
for (const [scope, path] of [
|
|
8578
|
+
["global", globalSettingsPath()],
|
|
8579
|
+
["project", projectSettingsPath(cwd)]
|
|
8580
|
+
]) {
|
|
8581
|
+
const existing = readSettingsFile(path);
|
|
8582
|
+
if (!existing?.hooks) continue;
|
|
8583
|
+
let changed = false;
|
|
8584
|
+
for (const ev of HOOK_EVENTS) {
|
|
8585
|
+
const list = existing.hooks[ev];
|
|
8586
|
+
if (!Array.isArray(list)) continue;
|
|
8587
|
+
for (const hook of list) {
|
|
8588
|
+
if (!hook || typeof hook !== "object") continue;
|
|
8589
|
+
const h = hook;
|
|
8590
|
+
const hookId = h.id ?? deriveHookId(ev, h.command);
|
|
8591
|
+
if (hookId === id) {
|
|
8592
|
+
h.enabled = enabled;
|
|
8593
|
+
changed = true;
|
|
8594
|
+
}
|
|
8595
|
+
}
|
|
8596
|
+
}
|
|
8597
|
+
if (changed) {
|
|
8598
|
+
mkdirSync2(dirname8(path), { recursive: true });
|
|
8599
|
+
writeFileSync(path, JSON.stringify(existing, null, 2) + "\n", "utf8");
|
|
8600
|
+
return path;
|
|
8601
|
+
}
|
|
8602
|
+
void scope;
|
|
8603
|
+
}
|
|
8604
|
+
return null;
|
|
8050
8605
|
}
|
|
8051
|
-
var
|
|
8052
|
-
"src/
|
|
8606
|
+
var init_settings = __esm({
|
|
8607
|
+
"src/hooks/settings.ts"() {
|
|
8053
8608
|
"use strict";
|
|
8609
|
+
init_types();
|
|
8054
8610
|
}
|
|
8055
8611
|
});
|
|
8056
8612
|
|
|
8057
|
-
// src/
|
|
8058
|
-
import {
|
|
8059
|
-
|
|
8060
|
-
|
|
8061
|
-
import { randomBytes } from "crypto";
|
|
8062
|
-
function generateSecret() {
|
|
8063
|
-
return randomBytes(32).toString("hex");
|
|
8064
|
-
}
|
|
8065
|
-
function runCapture(cmd, cwd) {
|
|
8066
|
-
return execSync(cmd, { cwd, encoding: "utf8", stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
8613
|
+
// src/hooks/runner.ts
|
|
8614
|
+
import { spawn as spawn2 } from "child_process";
|
|
8615
|
+
function isVetoEvent(event) {
|
|
8616
|
+
return event === "PreToolUse" || event === "UserPromptSubmit";
|
|
8067
8617
|
}
|
|
8068
|
-
|
|
8069
|
-
|
|
8070
|
-
|
|
8071
|
-
|
|
8072
|
-
|
|
8073
|
-
|
|
8074
|
-
|
|
8075
|
-
|
|
8076
|
-
|
|
8077
|
-
|
|
8078
|
-
|
|
8079
|
-
|
|
8080
|
-
|
|
8081
|
-
|
|
8082
|
-
|
|
8083
|
-
|
|
8084
|
-
|
|
8085
|
-
|
|
8086
|
-
runCapture("docker --version");
|
|
8087
|
-
} catch {
|
|
8088
|
-
yield { message: "Docker not found. Install: https://docs.docker.com/get-docker/", error: true };
|
|
8089
|
-
throw new Error("docker not installed");
|
|
8090
|
-
}
|
|
8091
|
-
yield { message: "Docker OK" };
|
|
8092
|
-
yield { message: "Building remote agent bundle..." };
|
|
8093
|
-
try {
|
|
8094
|
-
runCapture("npm run build:remote-agent", join15(REMOTE_DIR, ".."));
|
|
8095
|
-
yield { message: "Agent bundle built" };
|
|
8096
|
-
} catch (err) {
|
|
8097
|
-
yield { message: `Build failed: ${err instanceof Error ? err.message : String(err)}`, error: true };
|
|
8098
|
-
throw err;
|
|
8099
|
-
}
|
|
8100
|
-
yield { message: "Deploying Worker to Cloudflare..." };
|
|
8101
|
-
try {
|
|
8102
|
-
runCapture("wrangler deploy", WORKER_DIR);
|
|
8103
|
-
yield { message: "Worker deployed" };
|
|
8104
|
-
} catch (err) {
|
|
8105
|
-
yield { message: `Deploy failed: ${err instanceof Error ? err.message : String(err)}`, error: true };
|
|
8106
|
-
throw err;
|
|
8107
|
-
}
|
|
8108
|
-
let workerUrl;
|
|
8109
|
-
try {
|
|
8110
|
-
const info = runCapture("wrangler info", WORKER_DIR);
|
|
8111
|
-
const match = info.match(/https:\/\/[^\s]+\.workers\.dev/);
|
|
8112
|
-
if (match) workerUrl = match[0];
|
|
8113
|
-
} catch {
|
|
8114
|
-
}
|
|
8115
|
-
if (!workerUrl) {
|
|
8116
|
-
yield { message: "Could not auto-detect Worker URL", error: true };
|
|
8117
|
-
throw new Error("Worker URL not found");
|
|
8118
|
-
}
|
|
8119
|
-
yield { message: `Worker URL: ${workerUrl}` };
|
|
8120
|
-
const authSecret = generateSecret();
|
|
8121
|
-
const cfg = await loadConfig();
|
|
8122
|
-
const cfToken = process.env.CF_API_TOKEN ?? cfg?.apiToken;
|
|
8123
|
-
if (!cfToken) {
|
|
8124
|
-
yield { message: "CF_API_TOKEN not found. Set CF_API_TOKEN env var or apiToken in config", error: true };
|
|
8125
|
-
throw new Error("CF_API_TOKEN missing");
|
|
8126
|
-
}
|
|
8127
|
-
yield { message: "Setting Worker secrets..." };
|
|
8128
|
-
try {
|
|
8129
|
-
execSync(`wrangler secret put REMOTE_AUTH_SECRET`, {
|
|
8130
|
-
cwd: WORKER_DIR,
|
|
8131
|
-
input: authSecret,
|
|
8132
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
8133
|
-
});
|
|
8134
|
-
execSync(`wrangler secret put CF_API_TOKEN`, {
|
|
8135
|
-
cwd: WORKER_DIR,
|
|
8136
|
-
input: cfToken,
|
|
8137
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
8138
|
-
});
|
|
8139
|
-
yield { message: "Secrets set" };
|
|
8140
|
-
} catch (err) {
|
|
8141
|
-
yield { message: `Secret setup failed: ${err instanceof Error ? err.message : String(err)}`, error: true };
|
|
8142
|
-
throw err;
|
|
8143
|
-
}
|
|
8144
|
-
const imageTag = "ghcr.io/sinameraji/kimiflare-remote-agent:latest";
|
|
8145
|
-
yield { message: "Building container image..." };
|
|
8146
|
-
try {
|
|
8147
|
-
runCapture(`docker build -t ${imageTag} .`, REMOTE_DIR);
|
|
8148
|
-
yield { message: "Image built" };
|
|
8149
|
-
} catch (err) {
|
|
8150
|
-
yield { message: `Image build failed: ${err instanceof Error ? err.message : String(err)}`, error: true };
|
|
8151
|
-
throw err;
|
|
8618
|
+
function buildHookEnv(payload) {
|
|
8619
|
+
const env2 = {
|
|
8620
|
+
KIMIFLARE_HOOK_EVENT: payload.event,
|
|
8621
|
+
KIMIFLARE_HOOK_CWD: payload.cwd,
|
|
8622
|
+
KIMIFLARE_HOOK_PAYLOAD: JSON.stringify(payload)
|
|
8623
|
+
};
|
|
8624
|
+
if (payload.session_id) env2.KIMIFLARE_HOOK_SESSION_ID = payload.session_id;
|
|
8625
|
+
if (payload.event === "PreToolUse" || payload.event === "PostToolUse") {
|
|
8626
|
+
const p = payload;
|
|
8627
|
+
env2.KIMIFLARE_HOOK_TOOL = p.tool;
|
|
8628
|
+
const path = p.args.path;
|
|
8629
|
+
if (typeof path === "string") env2.KIMIFLARE_HOOK_PATH = path;
|
|
8630
|
+
if (p.tier) env2.KIMIFLARE_HOOK_TIER = p.tier;
|
|
8631
|
+
if (p.event === "PostToolUse") {
|
|
8632
|
+
env2.KIMIFLARE_HOOK_RESULT_OK = String(p.result.ok);
|
|
8633
|
+
const ec = p.result.errorCode;
|
|
8634
|
+
if (ec) env2.KIMIFLARE_HOOK_RESULT_ERROR_CODE = ec;
|
|
8635
|
+
}
|
|
8152
8636
|
}
|
|
8153
|
-
|
|
8154
|
-
|
|
8155
|
-
|
|
8156
|
-
yield { message: "Image pushed" };
|
|
8157
|
-
} catch (err) {
|
|
8158
|
-
yield { message: `Push failed: ${err instanceof Error ? err.message : String(err)}`, error: true };
|
|
8159
|
-
yield { message: "Make sure you're logged into ghcr.io: docker login ghcr.io -u USERNAME -p GITHUB_TOKEN", error: true };
|
|
8160
|
-
throw err;
|
|
8637
|
+
if (payload.event === "UserPromptSubmit") {
|
|
8638
|
+
const p = payload;
|
|
8639
|
+
if (p.tier) env2.KIMIFLARE_HOOK_TIER = p.tier;
|
|
8161
8640
|
}
|
|
8162
|
-
|
|
8163
|
-
...cfg ?? { accountId: "", apiToken: "", model: "@cf/moonshotai/kimi-k2.6" },
|
|
8164
|
-
remoteWorkerUrl: workerUrl,
|
|
8165
|
-
remoteAuthSecret: authSecret
|
|
8166
|
-
};
|
|
8167
|
-
await saveConfig(nextCfg);
|
|
8168
|
-
yield { message: "Config saved" };
|
|
8169
|
-
yield { message: "Remote infrastructure ready!", done: true };
|
|
8170
|
-
return { workerUrl, authSecret };
|
|
8641
|
+
return env2;
|
|
8171
8642
|
}
|
|
8172
|
-
|
|
8173
|
-
|
|
8174
|
-
|
|
8175
|
-
|
|
8176
|
-
|
|
8177
|
-
if (step.done) break;
|
|
8178
|
-
if (step.error) process.exit(1);
|
|
8179
|
-
}
|
|
8180
|
-
console.log("\nDeploy complete!");
|
|
8181
|
-
} catch (err) {
|
|
8182
|
-
console.error(`Deploy failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
8183
|
-
process.exit(1);
|
|
8643
|
+
function capStream(s) {
|
|
8644
|
+
if (Buffer.byteLength(s, "utf8") <= STREAM_CAP_BYTES) return s;
|
|
8645
|
+
let cut = s;
|
|
8646
|
+
while (Buffer.byteLength(cut, "utf8") > STREAM_CAP_BYTES) {
|
|
8647
|
+
cut = cut.slice(0, Math.floor(cut.length * 0.9));
|
|
8184
8648
|
}
|
|
8649
|
+
return `${cut}
|
|
8650
|
+
[\u2026truncated]`;
|
|
8185
8651
|
}
|
|
8186
|
-
async function
|
|
8187
|
-
|
|
8188
|
-
|
|
8189
|
-
|
|
8190
|
-
|
|
8652
|
+
async function runHook(hook, payload, signal) {
|
|
8653
|
+
const start = Date.now();
|
|
8654
|
+
const timeoutMs = hook.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
8655
|
+
const env2 = { ...process.env, ...buildHookEnv(payload) };
|
|
8656
|
+
const json2 = JSON.stringify(payload);
|
|
8657
|
+
const id = hook.id ?? "anonymous";
|
|
8658
|
+
let result;
|
|
8191
8659
|
try {
|
|
8192
|
-
|
|
8193
|
-
|
|
8194
|
-
|
|
8660
|
+
result = await spawnImpl(hook.command, json2, env2, payload.cwd, timeoutMs, signal);
|
|
8661
|
+
} catch (e) {
|
|
8662
|
+
return {
|
|
8663
|
+
id,
|
|
8664
|
+
exitCode: null,
|
|
8665
|
+
stdout: "",
|
|
8666
|
+
stderr: e instanceof Error ? e.message : String(e),
|
|
8667
|
+
timedOut: false,
|
|
8668
|
+
durationMs: Date.now() - start
|
|
8669
|
+
};
|
|
8195
8670
|
}
|
|
8196
|
-
|
|
8671
|
+
return {
|
|
8672
|
+
id,
|
|
8673
|
+
exitCode: result.exitCode,
|
|
8674
|
+
stdout: capStream(result.stdout.trim()),
|
|
8675
|
+
stderr: capStream(result.stderr.trim()),
|
|
8676
|
+
timedOut: result.timedOut,
|
|
8677
|
+
durationMs: Date.now() - start
|
|
8678
|
+
};
|
|
8679
|
+
}
|
|
8680
|
+
function filterHooks(hooks, toolName) {
|
|
8681
|
+
if (!hooks || hooks.length === 0) return [];
|
|
8682
|
+
return hooks.filter((h) => {
|
|
8683
|
+
if (h.enabled === false) return false;
|
|
8684
|
+
if (!h.matcher) return true;
|
|
8685
|
+
if (!toolName) return true;
|
|
8197
8686
|
try {
|
|
8198
|
-
|
|
8199
|
-
wranglerAuth = true;
|
|
8687
|
+
return new RegExp(h.matcher).test(toolName);
|
|
8200
8688
|
} catch {
|
|
8689
|
+
return false;
|
|
8201
8690
|
}
|
|
8202
|
-
}
|
|
8203
|
-
|
|
8204
|
-
|
|
8205
|
-
|
|
8206
|
-
|
|
8207
|
-
|
|
8208
|
-
const
|
|
8209
|
-
|
|
8210
|
-
|
|
8211
|
-
|
|
8212
|
-
|
|
8213
|
-
|
|
8691
|
+
});
|
|
8692
|
+
}
|
|
8693
|
+
async function runHooks(event, hooks, payload, toolName = null, signal) {
|
|
8694
|
+
const matched = filterHooks(hooks, toolName);
|
|
8695
|
+
const outcomes = [];
|
|
8696
|
+
const veto = isVetoEvent(event);
|
|
8697
|
+
const vetoReasons = [];
|
|
8698
|
+
let vetoed = false;
|
|
8699
|
+
for (const hook of matched) {
|
|
8700
|
+
const outcome = await runHook(hook, payload, signal);
|
|
8701
|
+
outcomes.push(outcome);
|
|
8702
|
+
if (outcome.timedOut) {
|
|
8703
|
+
logger.warn("hook:timeout", {
|
|
8704
|
+
event,
|
|
8705
|
+
id: outcome.id,
|
|
8706
|
+
timeoutMs: hook.timeoutMs ?? DEFAULT_TIMEOUT_MS
|
|
8707
|
+
});
|
|
8708
|
+
} else if (outcome.exitCode !== 0 && outcome.exitCode !== null) {
|
|
8709
|
+
logger.info("hook:nonzero_exit", {
|
|
8710
|
+
event,
|
|
8711
|
+
id: outcome.id,
|
|
8712
|
+
exitCode: outcome.exitCode
|
|
8713
|
+
});
|
|
8714
|
+
}
|
|
8715
|
+
if (veto && (outcome.exitCode !== 0 || outcome.timedOut)) {
|
|
8716
|
+
vetoed = true;
|
|
8717
|
+
const reason = outcome.stdout || outcome.stderr || `hook ${outcome.id} exited ${outcome.exitCode}`;
|
|
8718
|
+
vetoReasons.push(reason);
|
|
8719
|
+
break;
|
|
8214
8720
|
}
|
|
8215
8721
|
}
|
|
8216
|
-
return {
|
|
8722
|
+
return { outcomes, vetoed, vetoReason: vetoReasons.join("\n") };
|
|
8217
8723
|
}
|
|
8218
|
-
var
|
|
8219
|
-
var
|
|
8220
|
-
"src/
|
|
8724
|
+
var DEFAULT_TIMEOUT_MS, STREAM_CAP_BYTES, defaultSpawn, spawnImpl;
|
|
8725
|
+
var init_runner = __esm({
|
|
8726
|
+
"src/hooks/runner.ts"() {
|
|
8221
8727
|
"use strict";
|
|
8222
|
-
|
|
8223
|
-
|
|
8224
|
-
|
|
8225
|
-
|
|
8728
|
+
init_logger();
|
|
8729
|
+
DEFAULT_TIMEOUT_MS = 3e4;
|
|
8730
|
+
STREAM_CAP_BYTES = 4 * 1024;
|
|
8731
|
+
defaultSpawn = (command, payloadJson, env2, cwd, timeoutMs, signal) => new Promise((resolve8) => {
|
|
8732
|
+
const child = spawn2(command, {
|
|
8733
|
+
shell: true,
|
|
8734
|
+
cwd,
|
|
8735
|
+
env: env2,
|
|
8736
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
8737
|
+
});
|
|
8738
|
+
let stdout = "";
|
|
8739
|
+
let stderr = "";
|
|
8740
|
+
let settled = false;
|
|
8741
|
+
const finish = (exitCode, timedOut = false) => {
|
|
8742
|
+
if (settled) return;
|
|
8743
|
+
settled = true;
|
|
8744
|
+
clearTimeout(timer2);
|
|
8745
|
+
signal?.removeEventListener("abort", onAbort);
|
|
8746
|
+
resolve8({ exitCode, stdout, stderr, timedOut });
|
|
8747
|
+
};
|
|
8748
|
+
const onAbort = () => {
|
|
8749
|
+
child.kill("SIGTERM");
|
|
8750
|
+
finish(null, true);
|
|
8751
|
+
};
|
|
8752
|
+
const timer2 = setTimeout(() => {
|
|
8753
|
+
child.kill("SIGTERM");
|
|
8754
|
+
finish(null, true);
|
|
8755
|
+
}, timeoutMs);
|
|
8756
|
+
signal?.addEventListener("abort", onAbort);
|
|
8757
|
+
child.stdout.setEncoding("utf8");
|
|
8758
|
+
child.stderr.setEncoding("utf8");
|
|
8759
|
+
child.stdout.on("data", (d) => {
|
|
8760
|
+
stdout += d;
|
|
8761
|
+
});
|
|
8762
|
+
child.stderr.on("data", (d) => {
|
|
8763
|
+
stderr += d;
|
|
8764
|
+
});
|
|
8765
|
+
child.on("error", () => finish(null));
|
|
8766
|
+
child.on("exit", (code) => finish(code));
|
|
8767
|
+
try {
|
|
8768
|
+
child.stdin.end(payloadJson);
|
|
8769
|
+
} catch {
|
|
8770
|
+
}
|
|
8771
|
+
});
|
|
8772
|
+
spawnImpl = defaultSpawn;
|
|
8773
|
+
}
|
|
8774
|
+
});
|
|
8775
|
+
|
|
8776
|
+
// src/hooks/manager.ts
|
|
8777
|
+
var manager_exports = {};
|
|
8778
|
+
__export(manager_exports, {
|
|
8779
|
+
HooksManager: () => HooksManager
|
|
8780
|
+
});
|
|
8781
|
+
var HooksManager;
|
|
8782
|
+
var init_manager = __esm({
|
|
8783
|
+
"src/hooks/manager.ts"() {
|
|
8784
|
+
"use strict";
|
|
8785
|
+
init_settings();
|
|
8786
|
+
init_runner();
|
|
8787
|
+
HooksManager = class {
|
|
8788
|
+
cwd;
|
|
8789
|
+
settings;
|
|
8790
|
+
constructor(cwd) {
|
|
8791
|
+
this.cwd = cwd;
|
|
8792
|
+
this.settings = loadHooksSettings(cwd);
|
|
8793
|
+
}
|
|
8794
|
+
/** Re-read settings from disk. */
|
|
8795
|
+
reload() {
|
|
8796
|
+
this.settings = loadHooksSettings(this.cwd);
|
|
8797
|
+
}
|
|
8798
|
+
/** All hooks registered for an event, before matcher filtering. */
|
|
8799
|
+
hooksFor(event) {
|
|
8800
|
+
return this.settings.hooks?.[event] ?? [];
|
|
8801
|
+
}
|
|
8802
|
+
/** True if at least one enabled hook would match this event. Cheap
|
|
8803
|
+
* pre-check the loop can use to avoid building payloads for events
|
|
8804
|
+
* that have no listeners. */
|
|
8805
|
+
hasEnabledHooks(event) {
|
|
8806
|
+
const list = this.hooksFor(event);
|
|
8807
|
+
return list.some((h) => h.enabled !== false);
|
|
8808
|
+
}
|
|
8809
|
+
/** Fire all matching hooks for `event`. Toolname is used only by
|
|
8810
|
+
* PreToolUse / PostToolUse matchers. */
|
|
8811
|
+
fire(event, payload, toolName = null, signal) {
|
|
8812
|
+
return runHooks(event, this.hooksFor(event), payload, toolName, signal);
|
|
8813
|
+
}
|
|
8814
|
+
};
|
|
8226
8815
|
}
|
|
8227
8816
|
});
|
|
8228
8817
|
|
|
8229
8818
|
// src/cost-attribution/types.ts
|
|
8230
8819
|
var ALL_CATEGORIES;
|
|
8231
|
-
var
|
|
8820
|
+
var init_types2 = __esm({
|
|
8232
8821
|
"src/cost-attribution/types.ts"() {
|
|
8233
8822
|
"use strict";
|
|
8234
8823
|
ALL_CATEGORIES = [
|
|
@@ -8307,7 +8896,7 @@ function buildReport(opts2) {
|
|
|
8307
8896
|
var init_report = __esm({
|
|
8308
8897
|
"src/cost-attribution/report.ts"() {
|
|
8309
8898
|
"use strict";
|
|
8310
|
-
|
|
8899
|
+
init_types2();
|
|
8311
8900
|
}
|
|
8312
8901
|
});
|
|
8313
8902
|
|
|
@@ -8431,10 +9020,10 @@ async function fetchGatewayLogs(accountId, apiToken, gatewayId, startDate, endDa
|
|
|
8431
9020
|
if (!res.ok) {
|
|
8432
9021
|
throw new Error(`gateway logs HTTP ${res.status}`);
|
|
8433
9022
|
}
|
|
8434
|
-
const
|
|
8435
|
-
const page = Array.isArray(
|
|
9023
|
+
const json2 = await res.json();
|
|
9024
|
+
const page = Array.isArray(json2.result) ? json2.result : [];
|
|
8436
9025
|
out.push(...page);
|
|
8437
|
-
cursor =
|
|
9026
|
+
cursor = json2.result_info?.cursor;
|
|
8438
9027
|
if (!cursor || page.length === 0) break;
|
|
8439
9028
|
}
|
|
8440
9029
|
return out;
|
|
@@ -8524,12 +9113,12 @@ function extOf(path) {
|
|
|
8524
9113
|
const idx = path.lastIndexOf(".");
|
|
8525
9114
|
return idx >= 0 ? path.slice(idx).toLowerCase() : "";
|
|
8526
9115
|
}
|
|
8527
|
-
function
|
|
9116
|
+
function basename4(path) {
|
|
8528
9117
|
const idx = Math.max(path.lastIndexOf("/"), path.lastIndexOf("\\"));
|
|
8529
9118
|
return idx >= 0 ? path.slice(idx + 1) : path;
|
|
8530
9119
|
}
|
|
8531
9120
|
function classifyFile(path) {
|
|
8532
|
-
const base =
|
|
9121
|
+
const base = basename4(path);
|
|
8533
9122
|
const ext = extOf(path);
|
|
8534
9123
|
if (TEST_PATTERNS.test(base)) return "writing-tests";
|
|
8535
9124
|
if (base.toLowerCase().startsWith("readme")) return "reading-documentation";
|
|
@@ -8540,7 +9129,7 @@ function classifyFile(path) {
|
|
|
8540
9129
|
return null;
|
|
8541
9130
|
}
|
|
8542
9131
|
function classifyWriteFile(path) {
|
|
8543
|
-
const base =
|
|
9132
|
+
const base = basename4(path);
|
|
8544
9133
|
const ext = extOf(path);
|
|
8545
9134
|
if (TEST_PATTERNS.test(base)) return "writing-tests";
|
|
8546
9135
|
if (base.toLowerCase().startsWith("readme") || DOC_EXTS.has(ext)) return "writing-documentation";
|
|
@@ -8549,7 +9138,7 @@ function classifyWriteFile(path) {
|
|
|
8549
9138
|
return null;
|
|
8550
9139
|
}
|
|
8551
9140
|
function classifyEditFile(path) {
|
|
8552
|
-
const base =
|
|
9141
|
+
const base = basename4(path);
|
|
8553
9142
|
const ext = extOf(path);
|
|
8554
9143
|
if (base.toLowerCase().startsWith("readme") || DOC_EXTS.has(ext)) return "editing-documentation";
|
|
8555
9144
|
if (CONFIG_EXTS.has(ext)) return "editing-configuration";
|
|
@@ -8736,12 +9325,12 @@ var init_heuristic = __esm({
|
|
|
8736
9325
|
});
|
|
8737
9326
|
|
|
8738
9327
|
// src/cost-attribution/classify-from-session.ts
|
|
8739
|
-
import { readFile as
|
|
8740
|
-
import { join as
|
|
8741
|
-
import { homedir as
|
|
8742
|
-
function
|
|
8743
|
-
const xdg = process.env.XDG_DATA_HOME ||
|
|
8744
|
-
return
|
|
9328
|
+
import { readFile as readFile13 } from "fs/promises";
|
|
9329
|
+
import { join as join18 } from "path";
|
|
9330
|
+
import { homedir as homedir11 } from "os";
|
|
9331
|
+
function sessionsDir2() {
|
|
9332
|
+
const xdg = process.env.XDG_DATA_HOME || join18(homedir11(), ".local", "share");
|
|
9333
|
+
return join18(xdg, "kimiflare", "sessions");
|
|
8745
9334
|
}
|
|
8746
9335
|
function parseToolCalls(calls) {
|
|
8747
9336
|
return calls.map((c) => {
|
|
@@ -8755,7 +9344,7 @@ function parseToolCalls(calls) {
|
|
|
8755
9344
|
}
|
|
8756
9345
|
async function classifyFromSessionFile(sessionId) {
|
|
8757
9346
|
try {
|
|
8758
|
-
const raw = await
|
|
9347
|
+
const raw = await readFile13(join18(sessionsDir2(), `${sessionId}.json`), "utf8");
|
|
8759
9348
|
const session = JSON.parse(raw);
|
|
8760
9349
|
const messages = session.messages ?? [];
|
|
8761
9350
|
const turns = [];
|
|
@@ -8788,15 +9377,15 @@ var cli_exports = {};
|
|
|
8788
9377
|
__export(cli_exports, {
|
|
8789
9378
|
runCostCommand: () => runCostCommand
|
|
8790
9379
|
});
|
|
8791
|
-
import { readFile as
|
|
8792
|
-
import { join as
|
|
8793
|
-
import { homedir as
|
|
9380
|
+
import { readFile as readFile14 } from "fs/promises";
|
|
9381
|
+
import { join as join19 } from "path";
|
|
9382
|
+
import { homedir as homedir12 } from "os";
|
|
8794
9383
|
function usageDir() {
|
|
8795
|
-
const xdg = process.env.XDG_DATA_HOME ||
|
|
8796
|
-
return
|
|
9384
|
+
const xdg = process.env.XDG_DATA_HOME || join19(homedir12(), ".local", "share");
|
|
9385
|
+
return join19(xdg, "kimiflare");
|
|
8797
9386
|
}
|
|
8798
9387
|
function usagePath() {
|
|
8799
|
-
return
|
|
9388
|
+
return join19(usageDir(), "usage.json");
|
|
8800
9389
|
}
|
|
8801
9390
|
function today() {
|
|
8802
9391
|
return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
@@ -8808,7 +9397,7 @@ function daysAgo(n) {
|
|
|
8808
9397
|
}
|
|
8809
9398
|
async function loadLog() {
|
|
8810
9399
|
try {
|
|
8811
|
-
const raw = await
|
|
9400
|
+
const raw = await readFile14(usagePath(), "utf8");
|
|
8812
9401
|
return JSON.parse(raw);
|
|
8813
9402
|
} catch {
|
|
8814
9403
|
return { version: 1, days: [], sessions: [] };
|
|
@@ -8894,134 +9483,6 @@ var init_cli = __esm({
|
|
|
8894
9483
|
}
|
|
8895
9484
|
});
|
|
8896
9485
|
|
|
8897
|
-
// src/sessions.ts
|
|
8898
|
-
var sessions_exports = {};
|
|
8899
|
-
__export(sessions_exports, {
|
|
8900
|
-
addCheckpoint: () => addCheckpoint,
|
|
8901
|
-
generateSessionTitle: () => generateSessionTitle,
|
|
8902
|
-
listSessions: () => listSessions,
|
|
8903
|
-
loadSession: () => loadSession,
|
|
8904
|
-
loadSessionFromCheckpoint: () => loadSessionFromCheckpoint,
|
|
8905
|
-
makeSessionId: () => makeSessionId,
|
|
8906
|
-
pruneSessions: () => pruneSessions,
|
|
8907
|
-
saveSession: () => saveSession,
|
|
8908
|
-
sessionsDir: () => sessionsDir2
|
|
8909
|
-
});
|
|
8910
|
-
import { readFile as readFile12, writeFile as writeFile9, mkdir as mkdir8, readdir as readdir4, stat as stat4 } from "fs/promises";
|
|
8911
|
-
import { homedir as homedir10 } from "os";
|
|
8912
|
-
import { join as join18 } from "path";
|
|
8913
|
-
function sessionsDir2() {
|
|
8914
|
-
const xdg = process.env.XDG_DATA_HOME || join18(homedir10(), ".local", "share");
|
|
8915
|
-
return join18(xdg, "kimiflare", "sessions");
|
|
8916
|
-
}
|
|
8917
|
-
function sanitize(text) {
|
|
8918
|
-
return text.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40);
|
|
8919
|
-
}
|
|
8920
|
-
function generateSessionTitle(firstPrompt, intent) {
|
|
8921
|
-
const prefix = INTENT_PREFIX_MAP[intent] ?? INTENT_PREFIX_MAP.default;
|
|
8922
|
-
const cleaned = firstPrompt.replace(/\s+/g, " ").replace(/[\n\r]/g, " ").trim();
|
|
8923
|
-
const words = cleaned.split(" ").slice(0, 6).join(" ");
|
|
8924
|
-
const title = `${prefix} ${words}`;
|
|
8925
|
-
return title.length > 40 ? title.slice(0, 37) + "..." : title;
|
|
8926
|
-
}
|
|
8927
|
-
function makeSessionId(firstPrompt) {
|
|
8928
|
-
const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
8929
|
-
const slug = sanitize(firstPrompt) || "session";
|
|
8930
|
-
return `${ts}_${slug}`;
|
|
8931
|
-
}
|
|
8932
|
-
async function saveSession(file) {
|
|
8933
|
-
const dir = sessionsDir2();
|
|
8934
|
-
await mkdir8(dir, { recursive: true });
|
|
8935
|
-
const path = join18(dir, `${file.id}.json`);
|
|
8936
|
-
await writeFile9(path, JSON.stringify(file, null, 2), "utf8");
|
|
8937
|
-
return path;
|
|
8938
|
-
}
|
|
8939
|
-
async function pruneSessions() {
|
|
8940
|
-
const dir = sessionsDir2();
|
|
8941
|
-
const files = await listFilesByMtime(dir, /\.json$/);
|
|
8942
|
-
return pruneFiles(files, RETENTION.sessionMaxAgeDays, RETENTION.sessionMaxCount);
|
|
8943
|
-
}
|
|
8944
|
-
async function listSessions(limit = 30, cwd) {
|
|
8945
|
-
const dir = sessionsDir2();
|
|
8946
|
-
let entries;
|
|
8947
|
-
try {
|
|
8948
|
-
entries = await readdir4(dir);
|
|
8949
|
-
} catch {
|
|
8950
|
-
return [];
|
|
8951
|
-
}
|
|
8952
|
-
const summaries = [];
|
|
8953
|
-
for (const name of entries) {
|
|
8954
|
-
if (!name.endsWith(".json")) continue;
|
|
8955
|
-
const path = join18(dir, name);
|
|
8956
|
-
try {
|
|
8957
|
-
const [s, raw] = await Promise.all([stat4(path), readFile12(path, "utf8")]);
|
|
8958
|
-
const parsed = JSON.parse(raw);
|
|
8959
|
-
if (cwd && parsed.cwd !== cwd) continue;
|
|
8960
|
-
const firstUser = parsed.messages.find((m) => m.role === "user");
|
|
8961
|
-
const firstPrompt = typeof firstUser?.content === "string" ? firstUser.content : firstUser?.content ? firstUser.content.find((p) => p.type === "text")?.text ?? "(no prompt)" : "(no prompt)";
|
|
8962
|
-
summaries.push({
|
|
8963
|
-
id: parsed.id,
|
|
8964
|
-
filePath: path,
|
|
8965
|
-
cwd: parsed.cwd,
|
|
8966
|
-
firstPrompt: firstPrompt.slice(0, 80),
|
|
8967
|
-
title: parsed.title,
|
|
8968
|
-
messageCount: parsed.messages.filter((m) => m.role !== "system").length,
|
|
8969
|
-
updatedAt: parsed.updatedAt ?? s.mtime.toISOString(),
|
|
8970
|
-
checkpointCount: parsed.checkpoints?.length ?? 0
|
|
8971
|
-
});
|
|
8972
|
-
} catch {
|
|
8973
|
-
}
|
|
8974
|
-
}
|
|
8975
|
-
summaries.sort((a, b) => b.updatedAt < a.updatedAt ? -1 : 1);
|
|
8976
|
-
return summaries.slice(0, limit);
|
|
8977
|
-
}
|
|
8978
|
-
async function loadSession(filePath) {
|
|
8979
|
-
const raw = await readFile12(filePath, "utf8");
|
|
8980
|
-
return JSON.parse(raw);
|
|
8981
|
-
}
|
|
8982
|
-
async function addCheckpoint(filePath, checkpoint) {
|
|
8983
|
-
const file = await loadSession(filePath);
|
|
8984
|
-
if (!file.checkpoints) file.checkpoints = [];
|
|
8985
|
-
file.checkpoints.push(checkpoint);
|
|
8986
|
-
await saveSession(file);
|
|
8987
|
-
}
|
|
8988
|
-
async function loadSessionFromCheckpoint(filePath, checkpointId) {
|
|
8989
|
-
const file = await loadSession(filePath);
|
|
8990
|
-
const checkpoint = file.checkpoints?.find((c) => c.id === checkpointId);
|
|
8991
|
-
if (!checkpoint) {
|
|
8992
|
-
throw new Error(`checkpoint ${checkpointId} not found`);
|
|
8993
|
-
}
|
|
8994
|
-
const truncated = file.messages.slice(0, checkpoint.turnIndex);
|
|
8995
|
-
return {
|
|
8996
|
-
file: {
|
|
8997
|
-
...file,
|
|
8998
|
-
messages: truncated,
|
|
8999
|
-
sessionState: checkpoint.sessionState ?? file.sessionState,
|
|
9000
|
-
artifactStore: checkpoint.artifactStore ?? file.artifactStore
|
|
9001
|
-
},
|
|
9002
|
-
checkpoint
|
|
9003
|
-
};
|
|
9004
|
-
}
|
|
9005
|
-
var INTENT_PREFIX_MAP;
|
|
9006
|
-
var init_sessions = __esm({
|
|
9007
|
-
"src/sessions.ts"() {
|
|
9008
|
-
"use strict";
|
|
9009
|
-
init_storage_limits();
|
|
9010
|
-
INTENT_PREFIX_MAP = {
|
|
9011
|
-
diagnose: "Bug:",
|
|
9012
|
-
feature_bounded: "Feature:",
|
|
9013
|
-
feature_exploratory: "Feature:",
|
|
9014
|
-
polish: "Refactor:",
|
|
9015
|
-
meta: "Plan:",
|
|
9016
|
-
explore: "Explore:",
|
|
9017
|
-
qa: "Q&A:",
|
|
9018
|
-
verify: "Verify:",
|
|
9019
|
-
small_edit: "Edit:",
|
|
9020
|
-
default: "Task:"
|
|
9021
|
-
};
|
|
9022
|
-
}
|
|
9023
|
-
});
|
|
9024
|
-
|
|
9025
9486
|
// src/camouflage-resume.ts
|
|
9026
9487
|
var camouflage_resume_exports = {};
|
|
9027
9488
|
__export(camouflage_resume_exports, {
|
|
@@ -9111,7 +9572,7 @@ __export(tui_auth_exports, {
|
|
|
9111
9572
|
authGitHubForTui: () => authGitHubForTui
|
|
9112
9573
|
});
|
|
9113
9574
|
function sleep3(ms) {
|
|
9114
|
-
return new Promise((
|
|
9575
|
+
return new Promise((resolve8) => setTimeout(resolve8, ms));
|
|
9115
9576
|
}
|
|
9116
9577
|
async function* authGitHubForTui() {
|
|
9117
9578
|
yield { message: "Starting GitHub OAuth device flow..." };
|
|
@@ -9193,6 +9654,701 @@ var init_tui_auth = __esm({
|
|
|
9193
9654
|
}
|
|
9194
9655
|
});
|
|
9195
9656
|
|
|
9657
|
+
// src/server/sse.ts
|
|
9658
|
+
function createSseStream(res) {
|
|
9659
|
+
res.writeHead(200, {
|
|
9660
|
+
"Content-Type": "text/event-stream",
|
|
9661
|
+
"Cache-Control": "no-cache",
|
|
9662
|
+
Connection: "keep-alive"
|
|
9663
|
+
});
|
|
9664
|
+
const heartbeat = setInterval(() => {
|
|
9665
|
+
res.write(":heartbeat\n\n");
|
|
9666
|
+
}, 3e4);
|
|
9667
|
+
res.on("close", () => {
|
|
9668
|
+
clearInterval(heartbeat);
|
|
9669
|
+
});
|
|
9670
|
+
return {
|
|
9671
|
+
send(event, data) {
|
|
9672
|
+
const payload = JSON.stringify(data);
|
|
9673
|
+
res.write(`event: ${event}
|
|
9674
|
+
`);
|
|
9675
|
+
res.write(`data: ${payload}
|
|
9676
|
+
|
|
9677
|
+
`);
|
|
9678
|
+
},
|
|
9679
|
+
close() {
|
|
9680
|
+
clearInterval(heartbeat);
|
|
9681
|
+
res.end();
|
|
9682
|
+
}
|
|
9683
|
+
};
|
|
9684
|
+
}
|
|
9685
|
+
var init_sse2 = __esm({
|
|
9686
|
+
"src/server/sse.ts"() {
|
|
9687
|
+
"use strict";
|
|
9688
|
+
}
|
|
9689
|
+
});
|
|
9690
|
+
|
|
9691
|
+
// src/server/openapi.ts
|
|
9692
|
+
function getOpenApiSpec() {
|
|
9693
|
+
const spec = {
|
|
9694
|
+
openapi: "3.1.0",
|
|
9695
|
+
info: {
|
|
9696
|
+
title: "KimiFlare Headless Server API",
|
|
9697
|
+
version: "1.0.0",
|
|
9698
|
+
description: "HTTP API for running KimiFlare agent sessions headlessly."
|
|
9699
|
+
},
|
|
9700
|
+
servers: [{ url: "/", description: "Local server" }],
|
|
9701
|
+
paths: {
|
|
9702
|
+
"/": {
|
|
9703
|
+
get: {
|
|
9704
|
+
summary: "Health check",
|
|
9705
|
+
responses: {
|
|
9706
|
+
"200": {
|
|
9707
|
+
description: "Server is running",
|
|
9708
|
+
content: {
|
|
9709
|
+
"application/json": {
|
|
9710
|
+
schema: { type: "object", properties: { status: { type: "string" }, version: { type: "string" } } }
|
|
9711
|
+
}
|
|
9712
|
+
}
|
|
9713
|
+
}
|
|
9714
|
+
}
|
|
9715
|
+
}
|
|
9716
|
+
},
|
|
9717
|
+
"/prompt": {
|
|
9718
|
+
post: {
|
|
9719
|
+
summary: "Start a new agent session with a prompt",
|
|
9720
|
+
requestBody: {
|
|
9721
|
+
required: true,
|
|
9722
|
+
content: {
|
|
9723
|
+
"application/json": {
|
|
9724
|
+
schema: {
|
|
9725
|
+
type: "object",
|
|
9726
|
+
required: ["prompt"],
|
|
9727
|
+
properties: {
|
|
9728
|
+
prompt: { type: "string", description: "The user prompt" },
|
|
9729
|
+
model: { type: "string", description: "Model ID to use" },
|
|
9730
|
+
cwd: { type: "string", description: "Working directory" },
|
|
9731
|
+
title: { type: "string", description: "Session title" },
|
|
9732
|
+
files: { type: "array", items: { type: "string" }, description: "File paths or globs to attach" },
|
|
9733
|
+
allowAll: { type: "boolean", description: "Auto-approve all tool calls" }
|
|
9734
|
+
}
|
|
9735
|
+
}
|
|
9736
|
+
}
|
|
9737
|
+
}
|
|
9738
|
+
},
|
|
9739
|
+
responses: {
|
|
9740
|
+
"202": {
|
|
9741
|
+
description: "Session started",
|
|
9742
|
+
content: {
|
|
9743
|
+
"application/json": {
|
|
9744
|
+
schema: {
|
|
9745
|
+
type: "object",
|
|
9746
|
+
properties: {
|
|
9747
|
+
sessionId: { type: "string" },
|
|
9748
|
+
status: { type: "string" }
|
|
9749
|
+
}
|
|
9750
|
+
}
|
|
9751
|
+
}
|
|
9752
|
+
}
|
|
9753
|
+
}
|
|
9754
|
+
}
|
|
9755
|
+
}
|
|
9756
|
+
},
|
|
9757
|
+
"/session": {
|
|
9758
|
+
get: {
|
|
9759
|
+
summary: "List sessions",
|
|
9760
|
+
parameters: [
|
|
9761
|
+
{
|
|
9762
|
+
name: "cwd",
|
|
9763
|
+
in: "query",
|
|
9764
|
+
schema: { type: "string" },
|
|
9765
|
+
description: "Filter by working directory"
|
|
9766
|
+
}
|
|
9767
|
+
],
|
|
9768
|
+
responses: {
|
|
9769
|
+
"200": {
|
|
9770
|
+
description: "List of sessions",
|
|
9771
|
+
content: {
|
|
9772
|
+
"application/json": {
|
|
9773
|
+
schema: {
|
|
9774
|
+
type: "object",
|
|
9775
|
+
properties: {
|
|
9776
|
+
sessions: {
|
|
9777
|
+
type: "array",
|
|
9778
|
+
items: {
|
|
9779
|
+
type: "object",
|
|
9780
|
+
properties: {
|
|
9781
|
+
id: { type: "string" },
|
|
9782
|
+
cwd: { type: "string" },
|
|
9783
|
+
firstPrompt: { type: "string" },
|
|
9784
|
+
title: { type: "string" },
|
|
9785
|
+
messageCount: { type: "number" },
|
|
9786
|
+
updatedAt: { type: "string" }
|
|
9787
|
+
}
|
|
9788
|
+
}
|
|
9789
|
+
}
|
|
9790
|
+
}
|
|
9791
|
+
}
|
|
9792
|
+
}
|
|
9793
|
+
}
|
|
9794
|
+
}
|
|
9795
|
+
}
|
|
9796
|
+
}
|
|
9797
|
+
},
|
|
9798
|
+
"/session/{id}": {
|
|
9799
|
+
get: {
|
|
9800
|
+
summary: "Get session state",
|
|
9801
|
+
parameters: [
|
|
9802
|
+
{
|
|
9803
|
+
name: "id",
|
|
9804
|
+
in: "path",
|
|
9805
|
+
required: true,
|
|
9806
|
+
schema: { type: "string" }
|
|
9807
|
+
}
|
|
9808
|
+
],
|
|
9809
|
+
responses: {
|
|
9810
|
+
"200": {
|
|
9811
|
+
description: "Session state",
|
|
9812
|
+
content: {
|
|
9813
|
+
"application/json": {
|
|
9814
|
+
schema: {
|
|
9815
|
+
type: "object",
|
|
9816
|
+
properties: {
|
|
9817
|
+
id: { type: "string" },
|
|
9818
|
+
cwd: { type: "string" },
|
|
9819
|
+
model: { type: "string" },
|
|
9820
|
+
messages: { type: "array" },
|
|
9821
|
+
title: { type: "string" },
|
|
9822
|
+
updatedAt: { type: "string" }
|
|
9823
|
+
}
|
|
9824
|
+
}
|
|
9825
|
+
}
|
|
9826
|
+
}
|
|
9827
|
+
},
|
|
9828
|
+
"404": { description: "Session not found" }
|
|
9829
|
+
}
|
|
9830
|
+
},
|
|
9831
|
+
delete: {
|
|
9832
|
+
summary: "Delete a session",
|
|
9833
|
+
parameters: [
|
|
9834
|
+
{
|
|
9835
|
+
name: "id",
|
|
9836
|
+
in: "path",
|
|
9837
|
+
required: true,
|
|
9838
|
+
schema: { type: "string" }
|
|
9839
|
+
}
|
|
9840
|
+
],
|
|
9841
|
+
responses: {
|
|
9842
|
+
"200": {
|
|
9843
|
+
description: "Session deleted",
|
|
9844
|
+
content: {
|
|
9845
|
+
"application/json": {
|
|
9846
|
+
schema: {
|
|
9847
|
+
type: "object",
|
|
9848
|
+
properties: { deleted: { type: "string" } }
|
|
9849
|
+
}
|
|
9850
|
+
}
|
|
9851
|
+
}
|
|
9852
|
+
}
|
|
9853
|
+
}
|
|
9854
|
+
}
|
|
9855
|
+
},
|
|
9856
|
+
"/session/{id}/prompt": {
|
|
9857
|
+
post: {
|
|
9858
|
+
summary: "Send a follow-up prompt to a session",
|
|
9859
|
+
parameters: [
|
|
9860
|
+
{
|
|
9861
|
+
name: "id",
|
|
9862
|
+
in: "path",
|
|
9863
|
+
required: true,
|
|
9864
|
+
schema: { type: "string" }
|
|
9865
|
+
}
|
|
9866
|
+
],
|
|
9867
|
+
requestBody: {
|
|
9868
|
+
required: true,
|
|
9869
|
+
content: {
|
|
9870
|
+
"application/json": {
|
|
9871
|
+
schema: {
|
|
9872
|
+
type: "object",
|
|
9873
|
+
required: ["prompt"],
|
|
9874
|
+
properties: {
|
|
9875
|
+
prompt: { type: "string" },
|
|
9876
|
+
files: { type: "array", items: { type: "string" } },
|
|
9877
|
+
allowAll: { type: "boolean" }
|
|
9878
|
+
}
|
|
9879
|
+
}
|
|
9880
|
+
}
|
|
9881
|
+
}
|
|
9882
|
+
},
|
|
9883
|
+
responses: {
|
|
9884
|
+
"202": {
|
|
9885
|
+
description: "Follow-up started",
|
|
9886
|
+
content: {
|
|
9887
|
+
"application/json": {
|
|
9888
|
+
schema: {
|
|
9889
|
+
type: "object",
|
|
9890
|
+
properties: {
|
|
9891
|
+
sessionId: { type: "string" },
|
|
9892
|
+
status: { type: "string" }
|
|
9893
|
+
}
|
|
9894
|
+
}
|
|
9895
|
+
}
|
|
9896
|
+
}
|
|
9897
|
+
},
|
|
9898
|
+
"404": { description: "Session not found" }
|
|
9899
|
+
}
|
|
9900
|
+
}
|
|
9901
|
+
},
|
|
9902
|
+
"/event": {
|
|
9903
|
+
get: {
|
|
9904
|
+
summary: "Server-Sent Events stream",
|
|
9905
|
+
responses: {
|
|
9906
|
+
"200": {
|
|
9907
|
+
description: "SSE stream of session events",
|
|
9908
|
+
content: {
|
|
9909
|
+
"text/event-stream": {
|
|
9910
|
+
schema: {
|
|
9911
|
+
type: "object",
|
|
9912
|
+
description: "Stream of events: server.connected, assistant.delta, tool.call, tool.result, usage.update, session.completed, error"
|
|
9913
|
+
}
|
|
9914
|
+
}
|
|
9915
|
+
}
|
|
9916
|
+
}
|
|
9917
|
+
}
|
|
9918
|
+
}
|
|
9919
|
+
}
|
|
9920
|
+
}
|
|
9921
|
+
};
|
|
9922
|
+
return `<!DOCTYPE html>
|
|
9923
|
+
<html>
|
|
9924
|
+
<head>
|
|
9925
|
+
<meta charset="utf-8">
|
|
9926
|
+
<title>KimiFlare Headless Server API</title>
|
|
9927
|
+
<style>
|
|
9928
|
+
body { font-family: system-ui, sans-serif; max-width: 900px; margin: 40px auto; padding: 0 20px; }
|
|
9929
|
+
pre { background: #f5f5f5; padding: 16px; border-radius: 8px; overflow-x: auto; }
|
|
9930
|
+
h1 { border-bottom: 2px solid #333; padding-bottom: 8px; }
|
|
9931
|
+
h2 { margin-top: 32px; }
|
|
9932
|
+
code { background: #f0f0f0; padding: 2px 6px; border-radius: 4px; }
|
|
9933
|
+
</style>
|
|
9934
|
+
</head>
|
|
9935
|
+
<body>
|
|
9936
|
+
<h1>KimiFlare Headless Server API</h1>
|
|
9937
|
+
<p>OpenAPI 3.1 specification for the local HTTP server.</p>
|
|
9938
|
+
<h2>Spec</h2>
|
|
9939
|
+
<pre>${JSON.stringify(spec, null, 2)}</pre>
|
|
9940
|
+
</body>
|
|
9941
|
+
</html>`;
|
|
9942
|
+
}
|
|
9943
|
+
var init_openapi = __esm({
|
|
9944
|
+
"src/server/openapi.ts"() {
|
|
9945
|
+
"use strict";
|
|
9946
|
+
}
|
|
9947
|
+
});
|
|
9948
|
+
|
|
9949
|
+
// src/server/routes.ts
|
|
9950
|
+
import { URL as URL2 } from "url";
|
|
9951
|
+
import { readFile as readFile15 } from "fs/promises";
|
|
9952
|
+
import { resolve as resolve5, basename as basename5 } from "path";
|
|
9953
|
+
function json(res, status, data) {
|
|
9954
|
+
res.writeHead(status, { "Content-Type": "application/json" });
|
|
9955
|
+
res.end(JSON.stringify(data));
|
|
9956
|
+
}
|
|
9957
|
+
function badRequest(res, message2) {
|
|
9958
|
+
json(res, 400, { error: message2 });
|
|
9959
|
+
}
|
|
9960
|
+
function notFound(res, message2) {
|
|
9961
|
+
json(res, 404, { error: message2 });
|
|
9962
|
+
}
|
|
9963
|
+
async function readBody(req) {
|
|
9964
|
+
const chunks = [];
|
|
9965
|
+
for await (const chunk of req) {
|
|
9966
|
+
chunks.push(chunk);
|
|
9967
|
+
}
|
|
9968
|
+
const text = Buffer.concat(chunks).toString("utf8");
|
|
9969
|
+
try {
|
|
9970
|
+
return JSON.parse(text);
|
|
9971
|
+
} catch {
|
|
9972
|
+
return text;
|
|
9973
|
+
}
|
|
9974
|
+
}
|
|
9975
|
+
async function resolveFiles2(filePatterns, cwd) {
|
|
9976
|
+
const resolved = /* @__PURE__ */ new Set();
|
|
9977
|
+
for (const pattern of filePatterns) {
|
|
9978
|
+
try {
|
|
9979
|
+
const stat8 = await import("fs/promises").then((m) => m.stat(resolve5(cwd, pattern)));
|
|
9980
|
+
if (stat8.isFile()) {
|
|
9981
|
+
resolved.add(resolve5(cwd, pattern));
|
|
9982
|
+
continue;
|
|
9983
|
+
}
|
|
9984
|
+
} catch {
|
|
9985
|
+
}
|
|
9986
|
+
const matches = await glob(pattern, { cwd, absolute: true });
|
|
9987
|
+
for (const m of matches) {
|
|
9988
|
+
resolved.add(m);
|
|
9989
|
+
}
|
|
9990
|
+
}
|
|
9991
|
+
return [...resolved];
|
|
9992
|
+
}
|
|
9993
|
+
async function buildUserMessage2(prompt, files, cwd) {
|
|
9994
|
+
let text = prompt;
|
|
9995
|
+
const imageParts = [];
|
|
9996
|
+
const fileContents = [];
|
|
9997
|
+
for (const filePath of files) {
|
|
9998
|
+
if (isImagePath(filePath)) {
|
|
9999
|
+
try {
|
|
10000
|
+
const img = await encodeImageFile(filePath);
|
|
10001
|
+
imageParts.push({ type: "image_url", image_url: { url: img.dataUrl } });
|
|
10002
|
+
} catch (e) {
|
|
10003
|
+
fileContents.push(`
|
|
10004
|
+
<!-- failed to attach image ${basename5(filePath)}: ${e.message} -->
|
|
10005
|
+
`);
|
|
10006
|
+
}
|
|
10007
|
+
} else {
|
|
10008
|
+
try {
|
|
10009
|
+
const content = await readFile15(filePath, "utf8");
|
|
10010
|
+
const relPath = filePath.startsWith(cwd) ? filePath.slice(cwd.length + 1) : filePath;
|
|
10011
|
+
fileContents.push(`
|
|
10012
|
+
--- ${relPath} ---
|
|
10013
|
+
${content}
|
|
10014
|
+
--- end ${relPath} ---
|
|
10015
|
+
`);
|
|
10016
|
+
} catch (e) {
|
|
10017
|
+
fileContents.push(`
|
|
10018
|
+
<!-- failed to read ${basename5(filePath)}: ${e.message} -->
|
|
10019
|
+
`);
|
|
10020
|
+
}
|
|
10021
|
+
}
|
|
10022
|
+
}
|
|
10023
|
+
if (fileContents.length > 0) {
|
|
10024
|
+
text += "\n\n" + fileContents.join("\n");
|
|
10025
|
+
}
|
|
10026
|
+
if (imageParts.length > 0) {
|
|
10027
|
+
const parts = [{ type: "text", text }];
|
|
10028
|
+
parts.push(...imageParts);
|
|
10029
|
+
return parts;
|
|
10030
|
+
}
|
|
10031
|
+
return text;
|
|
10032
|
+
}
|
|
10033
|
+
function setupRoutes(config2) {
|
|
10034
|
+
async function handleRequest(req, res) {
|
|
10035
|
+
const url = new URL2(req.url ?? "/", `http://${req.headers.host}`);
|
|
10036
|
+
const method = req.method ?? "GET";
|
|
10037
|
+
const pathname = url.pathname;
|
|
10038
|
+
try {
|
|
10039
|
+
if (pathname === "/" && method === "GET") {
|
|
10040
|
+
json(res, 200, { status: "ok", version: process.env.npm_package_version ?? "dev" });
|
|
10041
|
+
return;
|
|
10042
|
+
}
|
|
10043
|
+
if (pathname === "/doc" && method === "GET") {
|
|
10044
|
+
res.writeHead(200, { "Content-Type": "text/html" });
|
|
10045
|
+
res.end(getOpenApiSpec());
|
|
10046
|
+
return;
|
|
10047
|
+
}
|
|
10048
|
+
if (pathname === "/event" && method === "GET") {
|
|
10049
|
+
const client = createSseStream(res);
|
|
10050
|
+
client.send("server.connected", { timestamp: Date.now() });
|
|
10051
|
+
return;
|
|
10052
|
+
}
|
|
10053
|
+
if (pathname === "/session" && method === "GET") {
|
|
10054
|
+
const cwd = url.searchParams.get("cwd") ?? void 0;
|
|
10055
|
+
const sessions = await listSessions(30, cwd);
|
|
10056
|
+
json(res, 200, { sessions });
|
|
10057
|
+
return;
|
|
10058
|
+
}
|
|
10059
|
+
const sessionMatch = pathname.match(/^\/session\/([^/]+)$/);
|
|
10060
|
+
if (sessionMatch && method === "GET") {
|
|
10061
|
+
const sessionId = sessionMatch[1];
|
|
10062
|
+
const active = activeSessions.get(sessionId);
|
|
10063
|
+
if (active) {
|
|
10064
|
+
json(res, 200, {
|
|
10065
|
+
id: active.sessionFile.id,
|
|
10066
|
+
cwd: active.sessionFile.cwd,
|
|
10067
|
+
model: active.sessionFile.model,
|
|
10068
|
+
messages: active.messages,
|
|
10069
|
+
title: active.sessionFile.title,
|
|
10070
|
+
updatedAt: active.sessionFile.updatedAt
|
|
10071
|
+
});
|
|
10072
|
+
return;
|
|
10073
|
+
}
|
|
10074
|
+
try {
|
|
10075
|
+
const file = await loadSession(resolve5(sessionsDir(), `${sessionId}.json`));
|
|
10076
|
+
json(res, 200, {
|
|
10077
|
+
id: file.id,
|
|
10078
|
+
cwd: file.cwd,
|
|
10079
|
+
model: file.model,
|
|
10080
|
+
messages: file.messages,
|
|
10081
|
+
title: file.title,
|
|
10082
|
+
updatedAt: file.updatedAt
|
|
10083
|
+
});
|
|
10084
|
+
return;
|
|
10085
|
+
} catch {
|
|
10086
|
+
notFound(res, `session ${sessionId} not found`);
|
|
10087
|
+
return;
|
|
10088
|
+
}
|
|
10089
|
+
}
|
|
10090
|
+
if (sessionMatch && method === "DELETE") {
|
|
10091
|
+
const sessionId = sessionMatch[1];
|
|
10092
|
+
activeSessions.delete(sessionId);
|
|
10093
|
+
try {
|
|
10094
|
+
const { unlink: unlink6 } = await import("fs/promises");
|
|
10095
|
+
await unlink6(resolve5(sessionsDir(), `${sessionId}.json`));
|
|
10096
|
+
} catch {
|
|
10097
|
+
}
|
|
10098
|
+
json(res, 200, { deleted: sessionId });
|
|
10099
|
+
return;
|
|
10100
|
+
}
|
|
10101
|
+
if (pathname === "/prompt" && method === "POST") {
|
|
10102
|
+
const body = await readBody(req);
|
|
10103
|
+
const prompt = typeof body.prompt === "string" ? body.prompt : "";
|
|
10104
|
+
const model = typeof body.model === "string" ? body.model : config2.model ?? "@cf/moonshotai/kimi-k2.6";
|
|
10105
|
+
const cwd = typeof body.cwd === "string" ? body.cwd : process.cwd();
|
|
10106
|
+
const title = typeof body.title === "string" ? body.title : void 0;
|
|
10107
|
+
const files = Array.isArray(body.files) ? body.files.filter((f) => typeof f === "string") : [];
|
|
10108
|
+
const allowAll = body.allowAll === true;
|
|
10109
|
+
if (!prompt) {
|
|
10110
|
+
badRequest(res, "prompt is required");
|
|
10111
|
+
return;
|
|
10112
|
+
}
|
|
10113
|
+
const { makeSessionId: makeSessionId2 } = await Promise.resolve().then(() => (init_sessions(), sessions_exports));
|
|
10114
|
+
const sessionId = makeSessionId2(prompt);
|
|
10115
|
+
const sessionFile = {
|
|
10116
|
+
id: sessionId,
|
|
10117
|
+
cwd,
|
|
10118
|
+
model,
|
|
10119
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
10120
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
10121
|
+
messages: [],
|
|
10122
|
+
title
|
|
10123
|
+
};
|
|
10124
|
+
const executor = new ToolExecutor(ALL_TOOLS);
|
|
10125
|
+
const messages = [
|
|
10126
|
+
{ role: "system", content: buildSystemPrompt({ cwd, tools: ALL_TOOLS, model }) }
|
|
10127
|
+
];
|
|
10128
|
+
const resolvedFiles = await resolveFiles2(files, cwd);
|
|
10129
|
+
const userContent = await buildUserMessage2(prompt, resolvedFiles, cwd);
|
|
10130
|
+
messages.push({ role: "user", content: userContent });
|
|
10131
|
+
const active = {
|
|
10132
|
+
sessionFile,
|
|
10133
|
+
messages,
|
|
10134
|
+
executor,
|
|
10135
|
+
sseClients: /* @__PURE__ */ new Set()
|
|
10136
|
+
};
|
|
10137
|
+
activeSessions.set(sessionId, active);
|
|
10138
|
+
runAgentTurnForSession(active, config2, allowAll);
|
|
10139
|
+
json(res, 202, { sessionId, status: "started" });
|
|
10140
|
+
return;
|
|
10141
|
+
}
|
|
10142
|
+
if (pathname === "/session/:id/prompt" && method === "POST") {
|
|
10143
|
+
}
|
|
10144
|
+
const sessionPromptMatch = pathname.match(/^\/session\/([^/]+)\/prompt$/);
|
|
10145
|
+
if (sessionPromptMatch && method === "POST") {
|
|
10146
|
+
const sessionId = sessionPromptMatch[1];
|
|
10147
|
+
const active = activeSessions.get(sessionId);
|
|
10148
|
+
if (!active) {
|
|
10149
|
+
notFound(res, `session ${sessionId} not found or expired`);
|
|
10150
|
+
return;
|
|
10151
|
+
}
|
|
10152
|
+
const body = await readBody(req);
|
|
10153
|
+
const prompt = typeof body.prompt === "string" ? body.prompt : "";
|
|
10154
|
+
const files = Array.isArray(body.files) ? body.files.filter((f) => typeof f === "string") : [];
|
|
10155
|
+
const allowAll = body.allowAll === true;
|
|
10156
|
+
if (!prompt) {
|
|
10157
|
+
badRequest(res, "prompt is required");
|
|
10158
|
+
return;
|
|
10159
|
+
}
|
|
10160
|
+
const resolvedFiles = await resolveFiles2(files, active.sessionFile.cwd);
|
|
10161
|
+
const userContent = await buildUserMessage2(prompt, resolvedFiles, active.sessionFile.cwd);
|
|
10162
|
+
active.messages.push({ role: "user", content: userContent });
|
|
10163
|
+
runAgentTurnForSession(active, config2, allowAll);
|
|
10164
|
+
json(res, 202, { sessionId, status: "started" });
|
|
10165
|
+
return;
|
|
10166
|
+
}
|
|
10167
|
+
notFound(res, `unknown endpoint: ${method} ${pathname}`);
|
|
10168
|
+
} catch (err) {
|
|
10169
|
+
logger.error("server: request error", { error: err.message, path: pathname });
|
|
10170
|
+
json(res, 500, { error: err.message });
|
|
10171
|
+
}
|
|
10172
|
+
}
|
|
10173
|
+
function cleanup() {
|
|
10174
|
+
for (const [, active] of activeSessions) {
|
|
10175
|
+
for (const client of active.sseClients) {
|
|
10176
|
+
client.close();
|
|
10177
|
+
}
|
|
10178
|
+
}
|
|
10179
|
+
activeSessions.clear();
|
|
10180
|
+
}
|
|
10181
|
+
return { handleRequest, cleanup };
|
|
10182
|
+
}
|
|
10183
|
+
async function runAgentTurnForSession(active, config2, allowAll) {
|
|
10184
|
+
const controller = new AbortController();
|
|
10185
|
+
const { sessionFile, messages, executor } = active;
|
|
10186
|
+
const callbacks = {
|
|
10187
|
+
onTextDelta: (delta) => {
|
|
10188
|
+
for (const client of active.sseClients) {
|
|
10189
|
+
client.send("assistant.delta", { delta });
|
|
10190
|
+
}
|
|
10191
|
+
},
|
|
10192
|
+
onToolCallFinalized: (call) => {
|
|
10193
|
+
for (const client of active.sseClients) {
|
|
10194
|
+
client.send("tool.call", {
|
|
10195
|
+
id: call.id,
|
|
10196
|
+
name: call.function.name,
|
|
10197
|
+
arguments: call.function.arguments
|
|
10198
|
+
});
|
|
10199
|
+
}
|
|
10200
|
+
},
|
|
10201
|
+
onToolResult: (result) => {
|
|
10202
|
+
for (const client of active.sseClients) {
|
|
10203
|
+
client.send("tool.result", {
|
|
10204
|
+
toolCallId: result.tool_call_id,
|
|
10205
|
+
name: result.name,
|
|
10206
|
+
content: result.content,
|
|
10207
|
+
ok: result.ok
|
|
10208
|
+
});
|
|
10209
|
+
}
|
|
10210
|
+
},
|
|
10211
|
+
onUsage: (usage) => {
|
|
10212
|
+
for (const client of active.sseClients) {
|
|
10213
|
+
client.send("usage.update", {
|
|
10214
|
+
promptTokens: usage.prompt_tokens,
|
|
10215
|
+
completionTokens: usage.completion_tokens,
|
|
10216
|
+
totalTokens: usage.total_tokens
|
|
10217
|
+
});
|
|
10218
|
+
}
|
|
10219
|
+
},
|
|
10220
|
+
onWarning: (msg) => {
|
|
10221
|
+
for (const client of active.sseClients) {
|
|
10222
|
+
client.send("warning", { message: msg });
|
|
10223
|
+
}
|
|
10224
|
+
},
|
|
10225
|
+
askPermission: async ({ tool, args }) => {
|
|
10226
|
+
if (allowAll) return "allow";
|
|
10227
|
+
if (config2.permissions) {
|
|
10228
|
+
const rule = evaluatePermissionRules({ tool: tool.name, args, cwd: sessionFile.cwd }, config2.permissions);
|
|
10229
|
+
if (rule === "allow") return "allow";
|
|
10230
|
+
if (rule === "deny") {
|
|
10231
|
+
for (const client of active.sseClients) {
|
|
10232
|
+
client.send("permission.denied", { tool: tool.name, args, reason: "config_rule" });
|
|
10233
|
+
}
|
|
10234
|
+
return "deny";
|
|
10235
|
+
}
|
|
10236
|
+
}
|
|
10237
|
+
for (const client of active.sseClients) {
|
|
10238
|
+
client.send("permission.request", { tool: tool.name, args });
|
|
10239
|
+
}
|
|
10240
|
+
return "deny";
|
|
10241
|
+
}
|
|
10242
|
+
};
|
|
10243
|
+
try {
|
|
10244
|
+
await runAgentTurn({
|
|
10245
|
+
accountId: config2.accountId,
|
|
10246
|
+
apiToken: config2.apiToken,
|
|
10247
|
+
model: sessionFile.model,
|
|
10248
|
+
messages,
|
|
10249
|
+
tools: ALL_TOOLS,
|
|
10250
|
+
executor,
|
|
10251
|
+
cwd: sessionFile.cwd,
|
|
10252
|
+
signal: controller.signal,
|
|
10253
|
+
codeMode: config2.codeMode,
|
|
10254
|
+
callbacks
|
|
10255
|
+
});
|
|
10256
|
+
sessionFile.messages = messages;
|
|
10257
|
+
sessionFile.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
10258
|
+
await saveSession(sessionFile);
|
|
10259
|
+
for (const client of active.sseClients) {
|
|
10260
|
+
client.send("session.completed", { sessionId: sessionFile.id });
|
|
10261
|
+
}
|
|
10262
|
+
} catch (err) {
|
|
10263
|
+
const message2 = err.message;
|
|
10264
|
+
logger.error("server: agent turn failed", { sessionId: sessionFile.id, error: message2 });
|
|
10265
|
+
for (const client of active.sseClients) {
|
|
10266
|
+
client.send("error", { message: message2, sessionId: sessionFile.id });
|
|
10267
|
+
}
|
|
10268
|
+
}
|
|
10269
|
+
}
|
|
10270
|
+
var activeSessions;
|
|
10271
|
+
var init_routes = __esm({
|
|
10272
|
+
"src/server/routes.ts"() {
|
|
10273
|
+
"use strict";
|
|
10274
|
+
init_loop();
|
|
10275
|
+
init_system_prompt();
|
|
10276
|
+
init_executor();
|
|
10277
|
+
init_sessions();
|
|
10278
|
+
init_logger();
|
|
10279
|
+
init_sse2();
|
|
10280
|
+
init_openapi();
|
|
10281
|
+
init_permissions_evaluator();
|
|
10282
|
+
init_image();
|
|
10283
|
+
init_glob();
|
|
10284
|
+
activeSessions = /* @__PURE__ */ new Map();
|
|
10285
|
+
}
|
|
10286
|
+
});
|
|
10287
|
+
|
|
10288
|
+
// src/server/index.ts
|
|
10289
|
+
var server_exports = {};
|
|
10290
|
+
__export(server_exports, {
|
|
10291
|
+
startServer: () => startServer
|
|
10292
|
+
});
|
|
10293
|
+
import { createServer } from "http";
|
|
10294
|
+
async function startServer(opts2) {
|
|
10295
|
+
const { handleRequest, cleanup } = setupRoutes(opts2.config);
|
|
10296
|
+
const server = createServer((req, res) => {
|
|
10297
|
+
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
10298
|
+
res.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
|
|
10299
|
+
res.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization, Last-Event-ID");
|
|
10300
|
+
if (req.method === "OPTIONS") {
|
|
10301
|
+
res.writeHead(204);
|
|
10302
|
+
res.end();
|
|
10303
|
+
return;
|
|
10304
|
+
}
|
|
10305
|
+
const password = process.env.KIMIFLARE_SERVER_PASSWORD;
|
|
10306
|
+
if (password) {
|
|
10307
|
+
const auth = req.headers.authorization ?? "";
|
|
10308
|
+
const expected = "Basic " + Buffer.from(`kimiflare:${password}`).toString("base64");
|
|
10309
|
+
if (auth !== expected) {
|
|
10310
|
+
res.writeHead(401, { "WWW-Authenticate": 'Basic realm="kimiflare"' });
|
|
10311
|
+
res.end(JSON.stringify({ error: "Unauthorized" }));
|
|
10312
|
+
return;
|
|
10313
|
+
}
|
|
10314
|
+
}
|
|
10315
|
+
void handleRequest(req, res);
|
|
10316
|
+
});
|
|
10317
|
+
server.on("close", () => {
|
|
10318
|
+
cleanup();
|
|
10319
|
+
});
|
|
10320
|
+
const shutdown = () => {
|
|
10321
|
+
logger.info("server: shutting down gracefully");
|
|
10322
|
+
server.close(() => {
|
|
10323
|
+
cleanup();
|
|
10324
|
+
process.exit(0);
|
|
10325
|
+
});
|
|
10326
|
+
setTimeout(() => {
|
|
10327
|
+
logger.warn("server: forced shutdown");
|
|
10328
|
+
process.exit(1);
|
|
10329
|
+
}, 1e4);
|
|
10330
|
+
};
|
|
10331
|
+
process.on("SIGINT", shutdown);
|
|
10332
|
+
process.on("SIGTERM", shutdown);
|
|
10333
|
+
return new Promise((resolve8, reject) => {
|
|
10334
|
+
server.listen(opts2.port, opts2.hostname, () => {
|
|
10335
|
+
logger.info("server: listening", { hostname: opts2.hostname, port: opts2.port });
|
|
10336
|
+
console.log(`kimiflare serve: http://${opts2.hostname}:${opts2.port}`);
|
|
10337
|
+
console.log(` API docs: http://${opts2.hostname}:${opts2.port}/doc`);
|
|
10338
|
+
console.log(` SSE stream: http://${opts2.hostname}:${opts2.port}/event`);
|
|
10339
|
+
resolve8(server);
|
|
10340
|
+
});
|
|
10341
|
+
server.on("error", reject);
|
|
10342
|
+
});
|
|
10343
|
+
}
|
|
10344
|
+
var init_server = __esm({
|
|
10345
|
+
"src/server/index.ts"() {
|
|
10346
|
+
"use strict";
|
|
10347
|
+
init_logger();
|
|
10348
|
+
init_routes();
|
|
10349
|
+
}
|
|
10350
|
+
});
|
|
10351
|
+
|
|
9196
10352
|
// src/memory/schema.ts
|
|
9197
10353
|
var DEFAULT_EMBEDDING_DIM;
|
|
9198
10354
|
var init_schema = __esm({
|
|
@@ -9230,8 +10386,8 @@ __export(db_exports, {
|
|
|
9230
10386
|
updateMemoryEmbedding: () => updateMemoryEmbedding
|
|
9231
10387
|
});
|
|
9232
10388
|
import Database from "better-sqlite3";
|
|
9233
|
-
import { dirname as
|
|
9234
|
-
import { mkdirSync as
|
|
10389
|
+
import { dirname as dirname9 } from "path";
|
|
10390
|
+
import { mkdirSync as mkdirSync3, statSync as statSync3 } from "fs";
|
|
9235
10391
|
function initSchema(db) {
|
|
9236
10392
|
db.exec(`
|
|
9237
10393
|
CREATE TABLE IF NOT EXISTS memories (
|
|
@@ -9315,7 +10471,7 @@ function openMemoryDb(dbPath) {
|
|
|
9315
10471
|
if (dbInstance) {
|
|
9316
10472
|
dbInstance.close();
|
|
9317
10473
|
}
|
|
9318
|
-
|
|
10474
|
+
mkdirSync3(dirname9(dbPath), { recursive: true });
|
|
9319
10475
|
dbInstance = new Database(dbPath);
|
|
9320
10476
|
dbInstance.pragma("journal_mode = WAL");
|
|
9321
10477
|
dbInstance.pragma("foreign_keys = ON");
|
|
@@ -9656,8 +10812,8 @@ function computeExactScore(memory, queryText, cwd) {
|
|
|
9656
10812
|
let score = 0;
|
|
9657
10813
|
const lowerQuery = queryText.toLowerCase();
|
|
9658
10814
|
for (const file of memory.relatedFiles) {
|
|
9659
|
-
const
|
|
9660
|
-
if (lowerQuery.includes(
|
|
10815
|
+
const basename8 = file.split("/").pop() ?? file;
|
|
10816
|
+
if (lowerQuery.includes(basename8.toLowerCase()) || basename8.toLowerCase().includes(lowerQuery)) {
|
|
9661
10817
|
score += 0.3;
|
|
9662
10818
|
}
|
|
9663
10819
|
if (cwd && file.startsWith(cwd)) {
|
|
@@ -9892,7 +11048,7 @@ function pickTopicKey(content, existingKeys) {
|
|
|
9892
11048
|
return normalized;
|
|
9893
11049
|
}
|
|
9894
11050
|
var SECRET_PATTERNS, VERIFY_SYSTEM, HYPOTHETICAL_QUERIES_SYSTEM, MemoryManager;
|
|
9895
|
-
var
|
|
11051
|
+
var init_manager2 = __esm({
|
|
9896
11052
|
"src/memory/manager.ts"() {
|
|
9897
11053
|
"use strict";
|
|
9898
11054
|
init_client();
|
|
@@ -10237,7 +11393,7 @@ Context: This memory was explicitly provided by the user during a conversation.`
|
|
|
10237
11393
|
});
|
|
10238
11394
|
|
|
10239
11395
|
// src/lsp/connection.ts
|
|
10240
|
-
import { spawn as
|
|
11396
|
+
import { spawn as spawn3 } from "child_process";
|
|
10241
11397
|
import { EventEmitter } from "events";
|
|
10242
11398
|
var LspConnection;
|
|
10243
11399
|
var init_connection = __esm({
|
|
@@ -10259,7 +11415,7 @@ var init_connection = __esm({
|
|
|
10259
11415
|
if (this.child) {
|
|
10260
11416
|
throw new Error("LSP connection already started");
|
|
10261
11417
|
}
|
|
10262
|
-
return new Promise((
|
|
11418
|
+
return new Promise((resolve8, reject) => {
|
|
10263
11419
|
const abortController = new AbortController();
|
|
10264
11420
|
const spawnTimer = setTimeout(() => {
|
|
10265
11421
|
abortController.abort();
|
|
@@ -10267,7 +11423,7 @@ var init_connection = __esm({
|
|
|
10267
11423
|
reject(new Error(`LSP server spawn timed out after ${spawnTimeoutMs}ms`));
|
|
10268
11424
|
}, spawnTimeoutMs);
|
|
10269
11425
|
try {
|
|
10270
|
-
const child =
|
|
11426
|
+
const child = spawn3(command[0], command.slice(1), {
|
|
10271
11427
|
env: { ...process.env, ...env2 },
|
|
10272
11428
|
stdio: ["pipe", "pipe", "pipe"]
|
|
10273
11429
|
});
|
|
@@ -10298,7 +11454,7 @@ var init_connection = __esm({
|
|
|
10298
11454
|
if (child.pid) {
|
|
10299
11455
|
clearTimeout(spawnTimer);
|
|
10300
11456
|
this.child = child;
|
|
10301
|
-
|
|
11457
|
+
resolve8();
|
|
10302
11458
|
}
|
|
10303
11459
|
});
|
|
10304
11460
|
} catch (err) {
|
|
@@ -10313,12 +11469,12 @@ var init_connection = __esm({
|
|
|
10313
11469
|
}
|
|
10314
11470
|
const id = this.nextId++;
|
|
10315
11471
|
const msg = { jsonrpc: "2.0", id, method, params };
|
|
10316
|
-
return new Promise((
|
|
11472
|
+
return new Promise((resolve8, reject) => {
|
|
10317
11473
|
const timer2 = setTimeout(() => {
|
|
10318
11474
|
this.pending.delete(id);
|
|
10319
11475
|
reject(toolTimeoutError(`LSP request '${method}'`, this.requestTimeoutMs));
|
|
10320
11476
|
}, this.requestTimeoutMs);
|
|
10321
|
-
const pending = { resolve:
|
|
11477
|
+
const pending = { resolve: resolve8, reject, signal, timer: timer2 };
|
|
10322
11478
|
this.pending.set(id, pending);
|
|
10323
11479
|
if (signal) {
|
|
10324
11480
|
const onAbort = () => {
|
|
@@ -10654,7 +11810,7 @@ var init_client2 = __esm({
|
|
|
10654
11810
|
|
|
10655
11811
|
// src/lsp/manager.ts
|
|
10656
11812
|
var DEFAULT_LSP_TIMEOUT_MS, DEFAULT_LSP_MAX_RESTART_ATTEMPTS, RESTART_BASE_DELAY_MS, RESTART_MAX_DELAY_MS, LspManager;
|
|
10657
|
-
var
|
|
11813
|
+
var init_manager3 = __esm({
|
|
10658
11814
|
"src/lsp/manager.ts"() {
|
|
10659
11815
|
"use strict";
|
|
10660
11816
|
init_connection();
|
|
@@ -10882,10 +12038,10 @@ var init_manager2 = __esm({
|
|
|
10882
12038
|
});
|
|
10883
12039
|
|
|
10884
12040
|
// src/lsp/adapter.ts
|
|
10885
|
-
import { relative as
|
|
12041
|
+
import { relative as relative4 } from "path";
|
|
10886
12042
|
function formatLocation(loc, cwd) {
|
|
10887
12043
|
const path = fromUri(loc.uri);
|
|
10888
|
-
const rel =
|
|
12044
|
+
const rel = relative4(cwd, path) || path;
|
|
10889
12045
|
return `${rel}:${loc.range.start.line + 1}:${loc.range.start.character + 1}`;
|
|
10890
12046
|
}
|
|
10891
12047
|
function formatLocations(locs, cwd) {
|
|
@@ -10922,7 +12078,7 @@ function formatWorkspaceSymbols(symbols, cwd) {
|
|
|
10922
12078
|
if (!symbols || symbols.length === 0) return "No symbols found.";
|
|
10923
12079
|
return symbols.map((s) => {
|
|
10924
12080
|
const path = fromUri(s.location.uri);
|
|
10925
|
-
const rel =
|
|
12081
|
+
const rel = relative4(cwd, path) || path;
|
|
10926
12082
|
const container = s.containerName ? ` in ${s.containerName}` : "";
|
|
10927
12083
|
return `${s.name} (${symbolKindName(s.kind)})${container} \u2014 ${rel}:${s.location.range.start.line + 1}:${s.location.range.start.character + 1}`;
|
|
10928
12084
|
}).join("\n");
|
|
@@ -10942,7 +12098,7 @@ function formatWorkspaceEdit(edit, cwd) {
|
|
|
10942
12098
|
if (edit.changes) {
|
|
10943
12099
|
for (const [uri, edits] of Object.entries(edit.changes)) {
|
|
10944
12100
|
const path = fromUri(uri);
|
|
10945
|
-
const rel =
|
|
12101
|
+
const rel = relative4(cwd, path) || path;
|
|
10946
12102
|
lines.push(`File: ${rel}`);
|
|
10947
12103
|
for (const e of edits) {
|
|
10948
12104
|
lines.push(` ${e.range.start.line + 1}:${e.range.start.character + 1}-${e.range.end.line + 1}:${e.range.end.character + 1}: ${e.newText}`);
|
|
@@ -10967,7 +12123,7 @@ var init_adapter = __esm({
|
|
|
10967
12123
|
});
|
|
10968
12124
|
|
|
10969
12125
|
// src/tools/lsp.ts
|
|
10970
|
-
import { relative as
|
|
12126
|
+
import { relative as relative5 } from "path";
|
|
10971
12127
|
function makeOutput2(content) {
|
|
10972
12128
|
const bytes = Buffer.byteLength(content, "utf8");
|
|
10973
12129
|
return { content, rawBytes: bytes, reducedBytes: bytes };
|
|
@@ -10975,7 +12131,7 @@ function makeOutput2(content) {
|
|
|
10975
12131
|
function resolveLspPath(args, ctx) {
|
|
10976
12132
|
const raw = typeof args.path === "string" ? args.path : "";
|
|
10977
12133
|
const resolved = resolvePath(ctx.cwd, raw);
|
|
10978
|
-
const rel =
|
|
12134
|
+
const rel = relative5(ctx.cwd, resolved);
|
|
10979
12135
|
if (isPathOutside(rel)) {
|
|
10980
12136
|
throw new Error(`Path outside workspace: ${raw}`);
|
|
10981
12137
|
}
|
|
@@ -11259,20 +12415,20 @@ var init_pricing = __esm({
|
|
|
11259
12415
|
});
|
|
11260
12416
|
|
|
11261
12417
|
// src/usage-tracker.ts
|
|
11262
|
-
import { readFile as
|
|
11263
|
-
import { homedir as
|
|
11264
|
-
import { join as
|
|
12418
|
+
import { readFile as readFile16, writeFile as writeFile10, mkdir as mkdir9 } from "fs/promises";
|
|
12419
|
+
import { homedir as homedir13 } from "os";
|
|
12420
|
+
import { join as join21 } from "path";
|
|
11265
12421
|
import { EventEmitter as EventEmitter2 } from "events";
|
|
11266
12422
|
import { randomUUID } from "crypto";
|
|
11267
12423
|
function usageDir2() {
|
|
11268
|
-
const xdg = process.env.XDG_DATA_HOME ||
|
|
11269
|
-
return
|
|
12424
|
+
const xdg = process.env.XDG_DATA_HOME || join21(homedir13(), ".local", "share");
|
|
12425
|
+
return join21(xdg, "kimiflare");
|
|
11270
12426
|
}
|
|
11271
12427
|
function usagePath2() {
|
|
11272
|
-
return
|
|
12428
|
+
return join21(usageDir2(), "usage.json");
|
|
11273
12429
|
}
|
|
11274
12430
|
function historyPath() {
|
|
11275
|
-
return
|
|
12431
|
+
return join21(usageDir2(), "history.jsonl");
|
|
11276
12432
|
}
|
|
11277
12433
|
function today2() {
|
|
11278
12434
|
return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
@@ -11283,7 +12439,7 @@ function cutoffDate(daysBack) {
|
|
|
11283
12439
|
}
|
|
11284
12440
|
async function loadLog2() {
|
|
11285
12441
|
try {
|
|
11286
|
-
const raw = await
|
|
12442
|
+
const raw = await readFile16(usagePath2(), "utf8");
|
|
11287
12443
|
const parsed = JSON.parse(raw);
|
|
11288
12444
|
if (parsed.version === LOG_VERSION2) return parsed;
|
|
11289
12445
|
} catch {
|
|
@@ -11301,7 +12457,7 @@ function withLock(fn) {
|
|
|
11301
12457
|
}
|
|
11302
12458
|
async function loadHistory() {
|
|
11303
12459
|
try {
|
|
11304
|
-
const raw = await
|
|
12460
|
+
const raw = await readFile16(historyPath(), "utf8");
|
|
11305
12461
|
const lines = raw.split("\n").filter((l) => l.trim());
|
|
11306
12462
|
const entries = [];
|
|
11307
12463
|
for (const line of lines) {
|
|
@@ -11717,372 +12873,25 @@ var init_permissions = __esm({
|
|
|
11717
12873
|
}
|
|
11718
12874
|
});
|
|
11719
12875
|
|
|
11720
|
-
// src/hooks/types.ts
|
|
11721
|
-
var HOOK_EVENTS;
|
|
11722
|
-
var init_types2 = __esm({
|
|
11723
|
-
"src/hooks/types.ts"() {
|
|
11724
|
-
"use strict";
|
|
11725
|
-
HOOK_EVENTS = [
|
|
11726
|
-
"PreToolUse",
|
|
11727
|
-
"PostToolUse",
|
|
11728
|
-
"UserPromptSubmit",
|
|
11729
|
-
"Stop",
|
|
11730
|
-
"PreCompact"
|
|
11731
|
-
];
|
|
11732
|
-
}
|
|
11733
|
-
});
|
|
11734
|
-
|
|
11735
|
-
// src/hooks/settings.ts
|
|
11736
|
-
import { existsSync as existsSync2, readFileSync as readFileSync3, mkdirSync as mkdirSync3, writeFileSync } from "fs";
|
|
11737
|
-
import { homedir as homedir12 } from "os";
|
|
11738
|
-
import { join as join21, dirname as dirname9 } from "path";
|
|
11739
|
-
import { createHash } from "crypto";
|
|
11740
|
-
function globalSettingsPath() {
|
|
11741
|
-
const xdg = process.env.XDG_CONFIG_HOME || join21(homedir12(), ".config");
|
|
11742
|
-
return join21(xdg, "kimiflare", "settings.json");
|
|
11743
|
-
}
|
|
11744
|
-
function projectSettingsPath(cwd) {
|
|
11745
|
-
return join21(cwd, ".kimiflare", "settings.json");
|
|
11746
|
-
}
|
|
11747
|
-
function readSettingsFile(path) {
|
|
11748
|
-
if (!existsSync2(path)) return null;
|
|
11749
|
-
try {
|
|
11750
|
-
const raw = readFileSync3(path, "utf8");
|
|
11751
|
-
const parsed = JSON.parse(raw);
|
|
11752
|
-
return parsed && typeof parsed === "object" ? parsed : null;
|
|
11753
|
-
} catch {
|
|
11754
|
-
return null;
|
|
11755
|
-
}
|
|
11756
|
-
}
|
|
11757
|
-
function deriveHookId(event, command) {
|
|
11758
|
-
const h = createHash("sha256").update(`${event}\0${command}`).digest("hex");
|
|
11759
|
-
return h.slice(0, 8);
|
|
11760
|
-
}
|
|
11761
|
-
function normalizeHook(event, raw, source) {
|
|
11762
|
-
if (!raw || typeof raw !== "object") return null;
|
|
11763
|
-
const r = raw;
|
|
11764
|
-
if (typeof r.command !== "string" || !r.command.trim()) return null;
|
|
11765
|
-
const enabled = typeof r.enabled === "boolean" ? r.enabled : true;
|
|
11766
|
-
const matcher = typeof r.matcher === "string" ? r.matcher : void 0;
|
|
11767
|
-
const timeoutMs = typeof r.timeoutMs === "number" && r.timeoutMs > 0 ? r.timeoutMs : void 0;
|
|
11768
|
-
const description = typeof r.description === "string" ? r.description : void 0;
|
|
11769
|
-
const id = typeof r.id === "string" && r.id.length > 0 ? r.id : deriveHookId(event, r.command);
|
|
11770
|
-
return { id, matcher, command: r.command, timeoutMs, enabled, description, source };
|
|
11771
|
-
}
|
|
11772
|
-
function mergeHookMaps(a, b) {
|
|
11773
|
-
const out = {};
|
|
11774
|
-
for (const ev of HOOK_EVENTS) {
|
|
11775
|
-
const left = a?.[ev] ?? [];
|
|
11776
|
-
const right = b?.[ev] ?? [];
|
|
11777
|
-
if (left.length + right.length === 0) continue;
|
|
11778
|
-
out[ev] = [...left, ...right];
|
|
11779
|
-
}
|
|
11780
|
-
return out;
|
|
11781
|
-
}
|
|
11782
|
-
function loadHooksSettings(cwd) {
|
|
11783
|
-
const global = readSettingsFile(globalSettingsPath());
|
|
11784
|
-
const project = readSettingsFile(projectSettingsPath(cwd));
|
|
11785
|
-
const normalized = (raw, source) => {
|
|
11786
|
-
const out = {};
|
|
11787
|
-
for (const ev of HOOK_EVENTS) {
|
|
11788
|
-
const list = raw?.hooks?.[ev];
|
|
11789
|
-
if (!Array.isArray(list)) continue;
|
|
11790
|
-
const cleaned = list.map((h) => normalizeHook(ev, h, source)).filter((h) => h !== null);
|
|
11791
|
-
if (cleaned.length > 0) out[ev] = cleaned;
|
|
11792
|
-
}
|
|
11793
|
-
return out;
|
|
11794
|
-
};
|
|
11795
|
-
const merged = mergeHookMaps(normalized(global, "global"), normalized(project, "project"));
|
|
11796
|
-
return { hooks: merged };
|
|
11797
|
-
}
|
|
11798
|
-
function appendHook(scope, cwd, event, hook) {
|
|
11799
|
-
const path = scope === "global" ? globalSettingsPath() : projectSettingsPath(cwd);
|
|
11800
|
-
const existing = readSettingsFile(path) ?? {};
|
|
11801
|
-
const hooks = existing.hooks ?? {};
|
|
11802
|
-
const list = hooks[event] ?? [];
|
|
11803
|
-
const { source: _src, ...toWrite } = hook;
|
|
11804
|
-
const newId = toWrite.id ?? deriveHookId(event, toWrite.command);
|
|
11805
|
-
const byId = /* @__PURE__ */ new Map();
|
|
11806
|
-
for (const raw of list) {
|
|
11807
|
-
if (!raw || typeof raw !== "object") continue;
|
|
11808
|
-
const h = raw;
|
|
11809
|
-
if (typeof h.command !== "string") continue;
|
|
11810
|
-
const id = h.id ?? deriveHookId(event, h.command);
|
|
11811
|
-
if (byId.has(id)) continue;
|
|
11812
|
-
byId.set(id, h);
|
|
11813
|
-
}
|
|
11814
|
-
byId.set(newId, { ...toWrite, id: newId });
|
|
11815
|
-
hooks[event] = Array.from(byId.values()).map((h) => {
|
|
11816
|
-
if (hook.id) return h;
|
|
11817
|
-
const { id: _id, ...rest } = h;
|
|
11818
|
-
return rest;
|
|
11819
|
-
});
|
|
11820
|
-
existing.hooks = hooks;
|
|
11821
|
-
mkdirSync3(dirname9(path), { recursive: true });
|
|
11822
|
-
writeFileSync(path, JSON.stringify(existing, null, 2) + "\n", "utf8");
|
|
11823
|
-
return path;
|
|
11824
|
-
}
|
|
11825
|
-
function setHookEnabled(cwd, id, enabled) {
|
|
11826
|
-
for (const [scope, path] of [
|
|
11827
|
-
["global", globalSettingsPath()],
|
|
11828
|
-
["project", projectSettingsPath(cwd)]
|
|
11829
|
-
]) {
|
|
11830
|
-
const existing = readSettingsFile(path);
|
|
11831
|
-
if (!existing?.hooks) continue;
|
|
11832
|
-
let changed = false;
|
|
11833
|
-
for (const ev of HOOK_EVENTS) {
|
|
11834
|
-
const list = existing.hooks[ev];
|
|
11835
|
-
if (!Array.isArray(list)) continue;
|
|
11836
|
-
for (const hook of list) {
|
|
11837
|
-
if (!hook || typeof hook !== "object") continue;
|
|
11838
|
-
const h = hook;
|
|
11839
|
-
const hookId = h.id ?? deriveHookId(ev, h.command);
|
|
11840
|
-
if (hookId === id) {
|
|
11841
|
-
h.enabled = enabled;
|
|
11842
|
-
changed = true;
|
|
11843
|
-
}
|
|
11844
|
-
}
|
|
11845
|
-
}
|
|
11846
|
-
if (changed) {
|
|
11847
|
-
mkdirSync3(dirname9(path), { recursive: true });
|
|
11848
|
-
writeFileSync(path, JSON.stringify(existing, null, 2) + "\n", "utf8");
|
|
11849
|
-
return path;
|
|
11850
|
-
}
|
|
11851
|
-
void scope;
|
|
11852
|
-
}
|
|
11853
|
-
return null;
|
|
11854
|
-
}
|
|
11855
|
-
var init_settings = __esm({
|
|
11856
|
-
"src/hooks/settings.ts"() {
|
|
11857
|
-
"use strict";
|
|
11858
|
-
init_types2();
|
|
11859
|
-
}
|
|
11860
|
-
});
|
|
11861
|
-
|
|
11862
|
-
// src/hooks/runner.ts
|
|
11863
|
-
import { spawn as spawn3 } from "child_process";
|
|
11864
|
-
function isVetoEvent(event) {
|
|
11865
|
-
return event === "PreToolUse" || event === "UserPromptSubmit";
|
|
11866
|
-
}
|
|
11867
|
-
function buildHookEnv(payload) {
|
|
11868
|
-
const env2 = {
|
|
11869
|
-
KIMIFLARE_HOOK_EVENT: payload.event,
|
|
11870
|
-
KIMIFLARE_HOOK_CWD: payload.cwd,
|
|
11871
|
-
KIMIFLARE_HOOK_PAYLOAD: JSON.stringify(payload)
|
|
11872
|
-
};
|
|
11873
|
-
if (payload.session_id) env2.KIMIFLARE_HOOK_SESSION_ID = payload.session_id;
|
|
11874
|
-
if (payload.event === "PreToolUse" || payload.event === "PostToolUse") {
|
|
11875
|
-
const p = payload;
|
|
11876
|
-
env2.KIMIFLARE_HOOK_TOOL = p.tool;
|
|
11877
|
-
const path = p.args.path;
|
|
11878
|
-
if (typeof path === "string") env2.KIMIFLARE_HOOK_PATH = path;
|
|
11879
|
-
if (p.tier) env2.KIMIFLARE_HOOK_TIER = p.tier;
|
|
11880
|
-
if (p.event === "PostToolUse") {
|
|
11881
|
-
env2.KIMIFLARE_HOOK_RESULT_OK = String(p.result.ok);
|
|
11882
|
-
const ec = p.result.errorCode;
|
|
11883
|
-
if (ec) env2.KIMIFLARE_HOOK_RESULT_ERROR_CODE = ec;
|
|
11884
|
-
}
|
|
11885
|
-
}
|
|
11886
|
-
if (payload.event === "UserPromptSubmit") {
|
|
11887
|
-
const p = payload;
|
|
11888
|
-
if (p.tier) env2.KIMIFLARE_HOOK_TIER = p.tier;
|
|
11889
|
-
}
|
|
11890
|
-
return env2;
|
|
11891
|
-
}
|
|
11892
|
-
function capStream(s) {
|
|
11893
|
-
if (Buffer.byteLength(s, "utf8") <= STREAM_CAP_BYTES) return s;
|
|
11894
|
-
let cut = s;
|
|
11895
|
-
while (Buffer.byteLength(cut, "utf8") > STREAM_CAP_BYTES) {
|
|
11896
|
-
cut = cut.slice(0, Math.floor(cut.length * 0.9));
|
|
11897
|
-
}
|
|
11898
|
-
return `${cut}
|
|
11899
|
-
[\u2026truncated]`;
|
|
11900
|
-
}
|
|
11901
|
-
async function runHook(hook, payload, signal) {
|
|
11902
|
-
const start = Date.now();
|
|
11903
|
-
const timeoutMs = hook.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
11904
|
-
const env2 = { ...process.env, ...buildHookEnv(payload) };
|
|
11905
|
-
const json = JSON.stringify(payload);
|
|
11906
|
-
const id = hook.id ?? "anonymous";
|
|
11907
|
-
let result;
|
|
11908
|
-
try {
|
|
11909
|
-
result = await spawnImpl(hook.command, json, env2, payload.cwd, timeoutMs, signal);
|
|
11910
|
-
} catch (e) {
|
|
11911
|
-
return {
|
|
11912
|
-
id,
|
|
11913
|
-
exitCode: null,
|
|
11914
|
-
stdout: "",
|
|
11915
|
-
stderr: e instanceof Error ? e.message : String(e),
|
|
11916
|
-
timedOut: false,
|
|
11917
|
-
durationMs: Date.now() - start
|
|
11918
|
-
};
|
|
11919
|
-
}
|
|
11920
|
-
return {
|
|
11921
|
-
id,
|
|
11922
|
-
exitCode: result.exitCode,
|
|
11923
|
-
stdout: capStream(result.stdout.trim()),
|
|
11924
|
-
stderr: capStream(result.stderr.trim()),
|
|
11925
|
-
timedOut: result.timedOut,
|
|
11926
|
-
durationMs: Date.now() - start
|
|
11927
|
-
};
|
|
11928
|
-
}
|
|
11929
|
-
function filterHooks(hooks, toolName) {
|
|
11930
|
-
if (!hooks || hooks.length === 0) return [];
|
|
11931
|
-
return hooks.filter((h) => {
|
|
11932
|
-
if (h.enabled === false) return false;
|
|
11933
|
-
if (!h.matcher) return true;
|
|
11934
|
-
if (!toolName) return true;
|
|
11935
|
-
try {
|
|
11936
|
-
return new RegExp(h.matcher).test(toolName);
|
|
11937
|
-
} catch {
|
|
11938
|
-
return false;
|
|
11939
|
-
}
|
|
11940
|
-
});
|
|
11941
|
-
}
|
|
11942
|
-
async function runHooks(event, hooks, payload, toolName = null, signal) {
|
|
11943
|
-
const matched = filterHooks(hooks, toolName);
|
|
11944
|
-
const outcomes = [];
|
|
11945
|
-
const veto = isVetoEvent(event);
|
|
11946
|
-
const vetoReasons = [];
|
|
11947
|
-
let vetoed = false;
|
|
11948
|
-
for (const hook of matched) {
|
|
11949
|
-
const outcome = await runHook(hook, payload, signal);
|
|
11950
|
-
outcomes.push(outcome);
|
|
11951
|
-
if (outcome.timedOut) {
|
|
11952
|
-
logger.warn("hook:timeout", {
|
|
11953
|
-
event,
|
|
11954
|
-
id: outcome.id,
|
|
11955
|
-
timeoutMs: hook.timeoutMs ?? DEFAULT_TIMEOUT_MS
|
|
11956
|
-
});
|
|
11957
|
-
} else if (outcome.exitCode !== 0 && outcome.exitCode !== null) {
|
|
11958
|
-
logger.info("hook:nonzero_exit", {
|
|
11959
|
-
event,
|
|
11960
|
-
id: outcome.id,
|
|
11961
|
-
exitCode: outcome.exitCode
|
|
11962
|
-
});
|
|
11963
|
-
}
|
|
11964
|
-
if (veto && (outcome.exitCode !== 0 || outcome.timedOut)) {
|
|
11965
|
-
vetoed = true;
|
|
11966
|
-
const reason = outcome.stdout || outcome.stderr || `hook ${outcome.id} exited ${outcome.exitCode}`;
|
|
11967
|
-
vetoReasons.push(reason);
|
|
11968
|
-
break;
|
|
11969
|
-
}
|
|
11970
|
-
}
|
|
11971
|
-
return { outcomes, vetoed, vetoReason: vetoReasons.join("\n") };
|
|
11972
|
-
}
|
|
11973
|
-
var DEFAULT_TIMEOUT_MS, STREAM_CAP_BYTES, defaultSpawn, spawnImpl;
|
|
11974
|
-
var init_runner = __esm({
|
|
11975
|
-
"src/hooks/runner.ts"() {
|
|
11976
|
-
"use strict";
|
|
11977
|
-
init_logger();
|
|
11978
|
-
DEFAULT_TIMEOUT_MS = 3e4;
|
|
11979
|
-
STREAM_CAP_BYTES = 4 * 1024;
|
|
11980
|
-
defaultSpawn = (command, payloadJson, env2, cwd, timeoutMs, signal) => new Promise((resolve5) => {
|
|
11981
|
-
const child = spawn3(command, {
|
|
11982
|
-
shell: true,
|
|
11983
|
-
cwd,
|
|
11984
|
-
env: env2,
|
|
11985
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
11986
|
-
});
|
|
11987
|
-
let stdout = "";
|
|
11988
|
-
let stderr = "";
|
|
11989
|
-
let settled = false;
|
|
11990
|
-
const finish = (exitCode, timedOut = false) => {
|
|
11991
|
-
if (settled) return;
|
|
11992
|
-
settled = true;
|
|
11993
|
-
clearTimeout(timer2);
|
|
11994
|
-
signal?.removeEventListener("abort", onAbort);
|
|
11995
|
-
resolve5({ exitCode, stdout, stderr, timedOut });
|
|
11996
|
-
};
|
|
11997
|
-
const onAbort = () => {
|
|
11998
|
-
child.kill("SIGTERM");
|
|
11999
|
-
finish(null, true);
|
|
12000
|
-
};
|
|
12001
|
-
const timer2 = setTimeout(() => {
|
|
12002
|
-
child.kill("SIGTERM");
|
|
12003
|
-
finish(null, true);
|
|
12004
|
-
}, timeoutMs);
|
|
12005
|
-
signal?.addEventListener("abort", onAbort);
|
|
12006
|
-
child.stdout.setEncoding("utf8");
|
|
12007
|
-
child.stderr.setEncoding("utf8");
|
|
12008
|
-
child.stdout.on("data", (d) => {
|
|
12009
|
-
stdout += d;
|
|
12010
|
-
});
|
|
12011
|
-
child.stderr.on("data", (d) => {
|
|
12012
|
-
stderr += d;
|
|
12013
|
-
});
|
|
12014
|
-
child.on("error", () => finish(null));
|
|
12015
|
-
child.on("exit", (code) => finish(code));
|
|
12016
|
-
try {
|
|
12017
|
-
child.stdin.end(payloadJson);
|
|
12018
|
-
} catch {
|
|
12019
|
-
}
|
|
12020
|
-
});
|
|
12021
|
-
spawnImpl = defaultSpawn;
|
|
12022
|
-
}
|
|
12023
|
-
});
|
|
12024
|
-
|
|
12025
|
-
// src/hooks/manager.ts
|
|
12026
|
-
var manager_exports = {};
|
|
12027
|
-
__export(manager_exports, {
|
|
12028
|
-
HooksManager: () => HooksManager
|
|
12029
|
-
});
|
|
12030
|
-
var HooksManager;
|
|
12031
|
-
var init_manager3 = __esm({
|
|
12032
|
-
"src/hooks/manager.ts"() {
|
|
12033
|
-
"use strict";
|
|
12034
|
-
init_settings();
|
|
12035
|
-
init_runner();
|
|
12036
|
-
HooksManager = class {
|
|
12037
|
-
cwd;
|
|
12038
|
-
settings;
|
|
12039
|
-
constructor(cwd) {
|
|
12040
|
-
this.cwd = cwd;
|
|
12041
|
-
this.settings = loadHooksSettings(cwd);
|
|
12042
|
-
}
|
|
12043
|
-
/** Re-read settings from disk. */
|
|
12044
|
-
reload() {
|
|
12045
|
-
this.settings = loadHooksSettings(this.cwd);
|
|
12046
|
-
}
|
|
12047
|
-
/** All hooks registered for an event, before matcher filtering. */
|
|
12048
|
-
hooksFor(event) {
|
|
12049
|
-
return this.settings.hooks?.[event] ?? [];
|
|
12050
|
-
}
|
|
12051
|
-
/** True if at least one enabled hook would match this event. Cheap
|
|
12052
|
-
* pre-check the loop can use to avoid building payloads for events
|
|
12053
|
-
* that have no listeners. */
|
|
12054
|
-
hasEnabledHooks(event) {
|
|
12055
|
-
const list = this.hooksFor(event);
|
|
12056
|
-
return list.some((h) => h.enabled !== false);
|
|
12057
|
-
}
|
|
12058
|
-
/** Fire all matching hooks for `event`. Toolname is used only by
|
|
12059
|
-
* PreToolUse / PostToolUse matchers. */
|
|
12060
|
-
fire(event, payload, toolName = null, signal) {
|
|
12061
|
-
return runHooks(event, this.hooksFor(event), payload, toolName, signal);
|
|
12062
|
-
}
|
|
12063
|
-
};
|
|
12064
|
-
}
|
|
12065
|
-
});
|
|
12066
|
-
|
|
12067
12876
|
// src/sdk/session.ts
|
|
12068
|
-
import { resolve as
|
|
12069
|
-
import { homedir as
|
|
12877
|
+
import { resolve as resolve6 } from "path";
|
|
12878
|
+
import { homedir as homedir14 } from "os";
|
|
12070
12879
|
import { join as join22 } from "path";
|
|
12071
12880
|
import { existsSync as existsSync3 } from "fs";
|
|
12072
12881
|
async function createAgentSession(opts2) {
|
|
12073
12882
|
const config2 = await resolveSdkConfig(opts2);
|
|
12074
|
-
const cwd =
|
|
12883
|
+
const cwd = resolve6(opts2.cwd ?? process.cwd());
|
|
12075
12884
|
const tools = opts2.tools ?? ALL_TOOLS;
|
|
12076
12885
|
let hooks;
|
|
12077
12886
|
if (opts2.enableHooks) {
|
|
12078
|
-
const { HooksManager: HooksManager2 } = await Promise.resolve().then(() => (
|
|
12887
|
+
const { HooksManager: HooksManager2 } = await Promise.resolve().then(() => (init_manager(), manager_exports));
|
|
12079
12888
|
hooks = new HooksManager2(cwd);
|
|
12080
12889
|
}
|
|
12081
12890
|
const executor = new ToolExecutor(tools, { hooks });
|
|
12082
12891
|
let memoryManager = null;
|
|
12083
12892
|
const memoryEnabled = opts2.memoryEnabled ?? config2.memoryEnabled ?? false;
|
|
12084
12893
|
if (memoryEnabled) {
|
|
12085
|
-
const dbPath = config2.memoryDbPath ?? join22(
|
|
12894
|
+
const dbPath = config2.memoryDbPath ?? join22(homedir14(), ".local", "share", "kimiflare", "memory.db");
|
|
12086
12895
|
memoryManager = new MemoryManager({
|
|
12087
12896
|
dbPath,
|
|
12088
12897
|
accountId: config2.accountId,
|
|
@@ -12113,7 +12922,7 @@ async function createAgentSession(opts2) {
|
|
|
12113
12922
|
}
|
|
12114
12923
|
let sessionFile;
|
|
12115
12924
|
if (opts2.sessionId) {
|
|
12116
|
-
const filePath = join22(
|
|
12925
|
+
const filePath = join22(sessionsDir(), `${opts2.sessionId}.json`);
|
|
12117
12926
|
try {
|
|
12118
12927
|
sessionFile = await loadSession(filePath);
|
|
12119
12928
|
} catch {
|
|
@@ -12175,8 +12984,8 @@ var init_session = __esm({
|
|
|
12175
12984
|
init_loop();
|
|
12176
12985
|
init_system_prompt();
|
|
12177
12986
|
init_executor();
|
|
12178
|
-
init_manager();
|
|
12179
12987
|
init_manager2();
|
|
12988
|
+
init_manager3();
|
|
12180
12989
|
init_lsp();
|
|
12181
12990
|
init_sessions();
|
|
12182
12991
|
init_usage_tracker();
|
|
@@ -12272,8 +13081,8 @@ var init_session = __esm({
|
|
|
12272
13081
|
const parts = [{ type: "text", text }];
|
|
12273
13082
|
for (const img of options.images) {
|
|
12274
13083
|
if ("path" in img) {
|
|
12275
|
-
const { readFile:
|
|
12276
|
-
const data = await
|
|
13084
|
+
const { readFile: readFile25 } = await import("fs/promises");
|
|
13085
|
+
const data = await readFile25(img.path, "base64");
|
|
12277
13086
|
const mimeType = img.path.endsWith(".png") ? "image/png" : img.path.endsWith(".jpg") || img.path.endsWith(".jpeg") ? "image/jpeg" : "image/webp";
|
|
12278
13087
|
parts.push({ type: "image_url", image_url: { url: `data:${mimeType};base64,${data}` } });
|
|
12279
13088
|
} else {
|
|
@@ -12465,12 +13274,12 @@ var init_session = __esm({
|
|
|
12465
13274
|
toolName: req.tool.name,
|
|
12466
13275
|
args: req.args
|
|
12467
13276
|
});
|
|
12468
|
-
const decision = await new Promise((
|
|
12469
|
-
this.permissionResolvers.set(requestId,
|
|
13277
|
+
const decision = await new Promise((resolve8) => {
|
|
13278
|
+
this.permissionResolvers.set(requestId, resolve8);
|
|
12470
13279
|
setTimeout(() => {
|
|
12471
13280
|
if (this.permissionResolvers.has(requestId)) {
|
|
12472
13281
|
this.permissionResolvers.delete(requestId);
|
|
12473
|
-
|
|
13282
|
+
resolve8("deny");
|
|
12474
13283
|
}
|
|
12475
13284
|
}, 3e5);
|
|
12476
13285
|
});
|
|
@@ -12519,7 +13328,7 @@ var init_session = __esm({
|
|
|
12519
13328
|
this.lspManager?.notifyChange(path, content);
|
|
12520
13329
|
} else {
|
|
12521
13330
|
void import("fs/promises").then(
|
|
12522
|
-
({ readFile:
|
|
13331
|
+
({ readFile: readFile25 }) => readFile25(path, "utf8").then((c) => this.lspManager?.notifyChange(path, c)).catch(() => {
|
|
12523
13332
|
})
|
|
12524
13333
|
).catch(() => {
|
|
12525
13334
|
});
|
|
@@ -12801,20 +13610,20 @@ var init_repo_info = __esm({
|
|
|
12801
13610
|
});
|
|
12802
13611
|
|
|
12803
13612
|
// src/agent/supervisor.ts
|
|
12804
|
-
import { readdir as readdir5, readFile as
|
|
13613
|
+
import { readdir as readdir5, readFile as readFile17, stat as stat5 } from "fs/promises";
|
|
12805
13614
|
import { createHash as createHash2 } from "crypto";
|
|
12806
|
-
import { resolve as
|
|
13615
|
+
import { resolve as resolve7, join as join23 } from "path";
|
|
12807
13616
|
async function preReadFilesForWorkers(files, repoRoot, maxChars = DEFAULT_PRE_READ_MAX_CHARS) {
|
|
12808
13617
|
const results = [];
|
|
12809
13618
|
const filesRead = [];
|
|
12810
13619
|
let chars = 0;
|
|
12811
13620
|
for (const file of files) {
|
|
12812
13621
|
if (chars >= maxChars) break;
|
|
12813
|
-
const path =
|
|
13622
|
+
const path = resolve7(repoRoot, file);
|
|
12814
13623
|
try {
|
|
12815
13624
|
const s = await stat5(path);
|
|
12816
13625
|
if (!s.isFile()) continue;
|
|
12817
|
-
const raw = await
|
|
13626
|
+
const raw = await readFile17(path, "utf8");
|
|
12818
13627
|
const remaining = maxChars - chars;
|
|
12819
13628
|
const content = raw.length > remaining ? raw.slice(0, remaining) + "\n\u2026 (truncated)" : raw;
|
|
12820
13629
|
results.push(`--- ${file} ---
|
|
@@ -13813,8 +14622,8 @@ async function runEmitMode(opts2) {
|
|
|
13813
14622
|
async function nextFollowUp() {
|
|
13814
14623
|
if (followUpQueue.length > 0) return followUpQueue.shift();
|
|
13815
14624
|
if (stdinClosed) return null;
|
|
13816
|
-
return new Promise((
|
|
13817
|
-
followUpResolver =
|
|
14625
|
+
return new Promise((resolve8) => {
|
|
14626
|
+
followUpResolver = resolve8;
|
|
13818
14627
|
});
|
|
13819
14628
|
}
|
|
13820
14629
|
const cwd = process.cwd();
|
|
@@ -13996,8 +14805,8 @@ ${conflicts.join("\n")}`,
|
|
|
13996
14805
|
return "allow";
|
|
13997
14806
|
}
|
|
13998
14807
|
if (opts2.multiTurn) {
|
|
13999
|
-
const choice = await new Promise((
|
|
14000
|
-
pendingPermissions.set(reqId,
|
|
14808
|
+
const choice = await new Promise((resolve8) => {
|
|
14809
|
+
pendingPermissions.set(reqId, resolve8);
|
|
14001
14810
|
});
|
|
14002
14811
|
if (choice === "allow" || choice === "allow_session") {
|
|
14003
14812
|
emit("PermissionGranted", { request_id: reqId });
|
|
@@ -14098,9 +14907,246 @@ var init_emit_mode = __esm({
|
|
|
14098
14907
|
}
|
|
14099
14908
|
});
|
|
14100
14909
|
|
|
14910
|
+
// src/attach-mode.ts
|
|
14911
|
+
var attach_mode_exports = {};
|
|
14912
|
+
__export(attach_mode_exports, {
|
|
14913
|
+
runAttachMode: () => runAttachMode
|
|
14914
|
+
});
|
|
14915
|
+
async function runAttachMode(opts2) {
|
|
14916
|
+
const url = opts2.attachUrl.replace(/\/$/, "");
|
|
14917
|
+
const format = opts2.format ?? "text";
|
|
14918
|
+
const endpoint = opts2.sessionId ? `${url}/session/${opts2.sessionId}/prompt` : `${url}/prompt`;
|
|
14919
|
+
const body = {
|
|
14920
|
+
prompt: opts2.prompt,
|
|
14921
|
+
allowAll: opts2.allowAll ?? false
|
|
14922
|
+
};
|
|
14923
|
+
if (opts2.model) body.model = opts2.model;
|
|
14924
|
+
if (opts2.files) body.files = opts2.files;
|
|
14925
|
+
const response = await fetch(endpoint, {
|
|
14926
|
+
method: "POST",
|
|
14927
|
+
headers: { "Content-Type": "application/json" },
|
|
14928
|
+
body: JSON.stringify(body)
|
|
14929
|
+
});
|
|
14930
|
+
if (!response.ok) {
|
|
14931
|
+
const text = await response.text().catch(() => "unknown error");
|
|
14932
|
+
console.error(`kimiflare attach: ${response.status} ${text}`);
|
|
14933
|
+
process.exit(1);
|
|
14934
|
+
}
|
|
14935
|
+
const result = await response.json();
|
|
14936
|
+
const sessionId = result.sessionId;
|
|
14937
|
+
if (format === "json") {
|
|
14938
|
+
await streamSseJson(url, sessionId);
|
|
14939
|
+
} else {
|
|
14940
|
+
await streamSse(url, sessionId, format);
|
|
14941
|
+
}
|
|
14942
|
+
}
|
|
14943
|
+
async function streamSse(url, sessionId, format) {
|
|
14944
|
+
const eventSource = new EventSource(`${url}/event`);
|
|
14945
|
+
let done = false;
|
|
14946
|
+
return new Promise((resolve8, reject) => {
|
|
14947
|
+
eventSource.onmessage = (event) => {
|
|
14948
|
+
try {
|
|
14949
|
+
const data = JSON.parse(event.data);
|
|
14950
|
+
if (data.event === "server.connected") return;
|
|
14951
|
+
switch (data.event) {
|
|
14952
|
+
case "assistant.delta":
|
|
14953
|
+
if (format === "text") {
|
|
14954
|
+
process.stdout.write(data.delta);
|
|
14955
|
+
} else if (format === "stream-json") {
|
|
14956
|
+
process.stdout.write(JSON.stringify({ event: "text_delta", delta: data.delta }) + "\n");
|
|
14957
|
+
}
|
|
14958
|
+
break;
|
|
14959
|
+
case "tool.call":
|
|
14960
|
+
if (format === "text") {
|
|
14961
|
+
process.stderr.write(`\x1B[2m[tool ${data.name}(${JSON.stringify(data.arguments)})]\x1B[0m
|
|
14962
|
+
`);
|
|
14963
|
+
} else if (format === "stream-json") {
|
|
14964
|
+
process.stdout.write(JSON.stringify({ event: "tool_call", id: data.id, name: data.name, arguments: data.arguments }) + "\n");
|
|
14965
|
+
}
|
|
14966
|
+
break;
|
|
14967
|
+
case "tool.result":
|
|
14968
|
+
if (format === "text") {
|
|
14969
|
+
const snippet = data.content.length > 400 ? data.content.slice(0, 400) + "..." : data.content;
|
|
14970
|
+
process.stderr.write(`\x1B[2m[result: ${snippet.replace(/\n/g, " \u23CE ")}]\x1B[0m
|
|
14971
|
+
`);
|
|
14972
|
+
} else if (format === "stream-json") {
|
|
14973
|
+
process.stdout.write(JSON.stringify({ event: "tool_result", toolCallId: data.toolCallId, name: data.name, content: data.content, ok: data.ok }) + "\n");
|
|
14974
|
+
}
|
|
14975
|
+
break;
|
|
14976
|
+
case "usage.update":
|
|
14977
|
+
if (format === "stream-json") {
|
|
14978
|
+
process.stdout.write(JSON.stringify({ event: "usage", promptTokens: data.promptTokens, completionTokens: data.completionTokens, totalTokens: data.totalTokens }) + "\n");
|
|
14979
|
+
}
|
|
14980
|
+
break;
|
|
14981
|
+
case "warning":
|
|
14982
|
+
if (format === "text") {
|
|
14983
|
+
process.stderr.write(`\x1B[33mkimiflare: ${data.message}\x1B[0m
|
|
14984
|
+
`);
|
|
14985
|
+
} else if (format === "stream-json") {
|
|
14986
|
+
process.stdout.write(JSON.stringify({ event: "warning", message: data.message }) + "\n");
|
|
14987
|
+
}
|
|
14988
|
+
break;
|
|
14989
|
+
case "session.completed":
|
|
14990
|
+
done = true;
|
|
14991
|
+
eventSource.close();
|
|
14992
|
+
if (format === "text") process.stdout.write("\n");
|
|
14993
|
+
resolve8(void 0);
|
|
14994
|
+
break;
|
|
14995
|
+
case "error":
|
|
14996
|
+
done = true;
|
|
14997
|
+
eventSource.close();
|
|
14998
|
+
if (format === "text") {
|
|
14999
|
+
process.stderr.write(`
|
|
15000
|
+
\x1B[31mError: ${data.message}\x1B[0m
|
|
15001
|
+
`);
|
|
15002
|
+
} else if (format === "stream-json") {
|
|
15003
|
+
process.stdout.write(JSON.stringify({ event: "error", message: data.message }) + "\n");
|
|
15004
|
+
}
|
|
15005
|
+
process.exitCode = 1;
|
|
15006
|
+
resolve8(void 0);
|
|
15007
|
+
break;
|
|
15008
|
+
}
|
|
15009
|
+
} catch {
|
|
15010
|
+
}
|
|
15011
|
+
};
|
|
15012
|
+
eventSource.onerror = () => {
|
|
15013
|
+
if (!done) {
|
|
15014
|
+
eventSource.close();
|
|
15015
|
+
reject(new Error("SSE connection failed"));
|
|
15016
|
+
}
|
|
15017
|
+
};
|
|
15018
|
+
setTimeout(() => {
|
|
15019
|
+
if (!done) {
|
|
15020
|
+
eventSource.close();
|
|
15021
|
+
reject(new Error("SSE stream timed out after 10 minutes"));
|
|
15022
|
+
}
|
|
15023
|
+
}, 6e5);
|
|
15024
|
+
});
|
|
15025
|
+
}
|
|
15026
|
+
async function streamSseJson(url, sessionId) {
|
|
15027
|
+
const text = [];
|
|
15028
|
+
const toolCalls = [];
|
|
15029
|
+
const toolResults = [];
|
|
15030
|
+
let usage;
|
|
15031
|
+
const eventSource = new EventSource(`${url}/event`);
|
|
15032
|
+
return new Promise((resolve8, reject) => {
|
|
15033
|
+
eventSource.onmessage = (event) => {
|
|
15034
|
+
try {
|
|
15035
|
+
const data = JSON.parse(event.data);
|
|
15036
|
+
if (data.event === "server.connected") return;
|
|
15037
|
+
switch (data.event) {
|
|
15038
|
+
case "assistant.delta":
|
|
15039
|
+
text.push(data.delta);
|
|
15040
|
+
break;
|
|
15041
|
+
case "tool.call":
|
|
15042
|
+
toolCalls.push({ id: data.id, name: data.name, arguments: data.arguments });
|
|
15043
|
+
break;
|
|
15044
|
+
case "tool.result":
|
|
15045
|
+
toolResults.push({ toolCallId: data.toolCallId, name: data.name, content: data.content, ok: data.ok });
|
|
15046
|
+
break;
|
|
15047
|
+
case "usage.update":
|
|
15048
|
+
usage = {
|
|
15049
|
+
promptTokens: data.promptTokens,
|
|
15050
|
+
completionTokens: data.completionTokens,
|
|
15051
|
+
totalTokens: data.totalTokens
|
|
15052
|
+
};
|
|
15053
|
+
break;
|
|
15054
|
+
case "session.completed":
|
|
15055
|
+
eventSource.close();
|
|
15056
|
+
process.stdout.write(
|
|
15057
|
+
JSON.stringify(
|
|
15058
|
+
{
|
|
15059
|
+
text: text.join(""),
|
|
15060
|
+
toolCalls,
|
|
15061
|
+
toolResults,
|
|
15062
|
+
usage,
|
|
15063
|
+
sessionId
|
|
15064
|
+
},
|
|
15065
|
+
null,
|
|
15066
|
+
2
|
|
15067
|
+
) + "\n"
|
|
15068
|
+
);
|
|
15069
|
+
resolve8(void 0);
|
|
15070
|
+
break;
|
|
15071
|
+
case "error":
|
|
15072
|
+
eventSource.close();
|
|
15073
|
+
process.stdout.write(
|
|
15074
|
+
JSON.stringify({ error: data.message, sessionId }, null, 2) + "\n"
|
|
15075
|
+
);
|
|
15076
|
+
process.exitCode = 1;
|
|
15077
|
+
resolve8(void 0);
|
|
15078
|
+
break;
|
|
15079
|
+
}
|
|
15080
|
+
} catch {
|
|
15081
|
+
}
|
|
15082
|
+
};
|
|
15083
|
+
eventSource.onerror = () => {
|
|
15084
|
+
eventSource.close();
|
|
15085
|
+
reject(new Error("SSE connection failed"));
|
|
15086
|
+
};
|
|
15087
|
+
setTimeout(() => {
|
|
15088
|
+
eventSource.close();
|
|
15089
|
+
reject(new Error("SSE stream timed out after 10 minutes"));
|
|
15090
|
+
}, 6e5);
|
|
15091
|
+
});
|
|
15092
|
+
}
|
|
15093
|
+
var EventSource;
|
|
15094
|
+
var init_attach_mode = __esm({
|
|
15095
|
+
"src/attach-mode.ts"() {
|
|
15096
|
+
"use strict";
|
|
15097
|
+
EventSource = class {
|
|
15098
|
+
url;
|
|
15099
|
+
controller;
|
|
15100
|
+
onmessage = null;
|
|
15101
|
+
onerror = null;
|
|
15102
|
+
constructor(url) {
|
|
15103
|
+
this.url = url;
|
|
15104
|
+
this.controller = new AbortController();
|
|
15105
|
+
this.start();
|
|
15106
|
+
}
|
|
15107
|
+
async start() {
|
|
15108
|
+
try {
|
|
15109
|
+
const response = await fetch(this.url, {
|
|
15110
|
+
signal: this.controller.signal,
|
|
15111
|
+
headers: { Accept: "text/event-stream" }
|
|
15112
|
+
});
|
|
15113
|
+
if (!response.ok || !response.body) {
|
|
15114
|
+
this.onerror?.();
|
|
15115
|
+
return;
|
|
15116
|
+
}
|
|
15117
|
+
const reader = response.body.getReader();
|
|
15118
|
+
const decoder = new TextDecoder();
|
|
15119
|
+
let buffer = "";
|
|
15120
|
+
while (true) {
|
|
15121
|
+
const { done, value } = await reader.read();
|
|
15122
|
+
if (done) break;
|
|
15123
|
+
buffer += decoder.decode(value, { stream: true });
|
|
15124
|
+
const lines = buffer.split("\n");
|
|
15125
|
+
buffer = lines.pop() ?? "";
|
|
15126
|
+
let currentData = "";
|
|
15127
|
+
for (const line of lines) {
|
|
15128
|
+
if (line.startsWith("data: ")) {
|
|
15129
|
+
currentData = line.slice(6);
|
|
15130
|
+
} else if (line === "" && currentData) {
|
|
15131
|
+
this.onmessage?.({ data: currentData });
|
|
15132
|
+
currentData = "";
|
|
15133
|
+
}
|
|
15134
|
+
}
|
|
15135
|
+
}
|
|
15136
|
+
} catch {
|
|
15137
|
+
this.onerror?.();
|
|
15138
|
+
}
|
|
15139
|
+
}
|
|
15140
|
+
close() {
|
|
15141
|
+
this.controller.abort();
|
|
15142
|
+
}
|
|
15143
|
+
};
|
|
15144
|
+
}
|
|
15145
|
+
});
|
|
15146
|
+
|
|
14101
15147
|
// src/remote/deploy-commute.ts
|
|
14102
15148
|
import { spawn as spawn4 } from "child_process";
|
|
14103
|
-
import { mkdtemp, readFile as
|
|
15149
|
+
import { mkdtemp, readFile as readFile18, writeFile as writeFile11, rm } from "fs/promises";
|
|
14104
15150
|
import { tmpdir as tmpdir3 } from "os";
|
|
14105
15151
|
import { join as join24 } from "path";
|
|
14106
15152
|
import { randomBytes as randomBytes2 } from "crypto";
|
|
@@ -14115,56 +15161,56 @@ async function cfApiFetch(accountId, apiToken, path, init) {
|
|
|
14115
15161
|
...init?.headers
|
|
14116
15162
|
}
|
|
14117
15163
|
});
|
|
14118
|
-
const
|
|
14119
|
-
return
|
|
15164
|
+
const json2 = await res.json();
|
|
15165
|
+
return json2;
|
|
14120
15166
|
}
|
|
14121
15167
|
async function listDurableObjectNamespaces(accountId, apiToken) {
|
|
14122
|
-
const
|
|
15168
|
+
const json2 = await cfApiFetch(
|
|
14123
15169
|
accountId,
|
|
14124
15170
|
apiToken,
|
|
14125
15171
|
"/workers/durable_objects/namespaces"
|
|
14126
15172
|
);
|
|
14127
|
-
if (!
|
|
15173
|
+
if (!json2.success || !json2.result) {
|
|
14128
15174
|
throw new Error(
|
|
14129
|
-
|
|
15175
|
+
json2.errors?.map((e) => e.message).join(", ") ?? "Failed to list DO namespaces"
|
|
14130
15176
|
);
|
|
14131
15177
|
}
|
|
14132
|
-
return
|
|
15178
|
+
return json2.result;
|
|
14133
15179
|
}
|
|
14134
15180
|
async function deleteDurableObjectNamespace(accountId, apiToken, namespaceId) {
|
|
14135
|
-
const
|
|
15181
|
+
const json2 = await cfApiFetch(
|
|
14136
15182
|
accountId,
|
|
14137
15183
|
apiToken,
|
|
14138
15184
|
`/workers/durable_objects/namespaces/${encodeURIComponent(namespaceId)}`,
|
|
14139
15185
|
{ method: "DELETE" }
|
|
14140
15186
|
);
|
|
14141
|
-
if (!
|
|
15187
|
+
if (!json2.success) {
|
|
14142
15188
|
throw new Error(
|
|
14143
|
-
|
|
15189
|
+
json2.errors?.map((e) => e.message).join(", ") ?? "Failed to delete DO namespace"
|
|
14144
15190
|
);
|
|
14145
15191
|
}
|
|
14146
15192
|
}
|
|
14147
15193
|
async function listContainerApplications(accountId, apiToken) {
|
|
14148
|
-
const
|
|
15194
|
+
const json2 = await cfApiFetch(
|
|
14149
15195
|
accountId,
|
|
14150
15196
|
apiToken,
|
|
14151
15197
|
"/containers/applications"
|
|
14152
15198
|
);
|
|
14153
|
-
if (!
|
|
15199
|
+
if (!json2.success || !json2.result) {
|
|
14154
15200
|
return [];
|
|
14155
15201
|
}
|
|
14156
|
-
return
|
|
15202
|
+
return json2.result;
|
|
14157
15203
|
}
|
|
14158
15204
|
async function deleteContainerApplication(accountId, apiToken, appId) {
|
|
14159
|
-
const
|
|
15205
|
+
const json2 = await cfApiFetch(
|
|
14160
15206
|
accountId,
|
|
14161
15207
|
apiToken,
|
|
14162
15208
|
`/containers/applications/${encodeURIComponent(appId)}`,
|
|
14163
15209
|
{ method: "DELETE" }
|
|
14164
15210
|
);
|
|
14165
|
-
if (!
|
|
15211
|
+
if (!json2.success) {
|
|
14166
15212
|
throw new Error(
|
|
14167
|
-
|
|
15213
|
+
json2.errors?.map((e) => e.message).join(", ") ?? "Failed to delete container application"
|
|
14168
15214
|
);
|
|
14169
15215
|
}
|
|
14170
15216
|
}
|
|
@@ -14172,7 +15218,7 @@ function generateSecret2() {
|
|
|
14172
15218
|
return randomBytes2(32).toString("hex");
|
|
14173
15219
|
}
|
|
14174
15220
|
function runCmd(cmd, args, opts2 = {}) {
|
|
14175
|
-
return new Promise((
|
|
15221
|
+
return new Promise((resolve8) => {
|
|
14176
15222
|
const child = spawn4(cmd, args, {
|
|
14177
15223
|
cwd: opts2.cwd,
|
|
14178
15224
|
env: { ...process.env, ...opts2.env ?? {} },
|
|
@@ -14193,11 +15239,11 @@ function runCmd(cmd, args, opts2 = {}) {
|
|
|
14193
15239
|
const timer2 = opts2.timeoutMs ? setTimeout(() => child.kill("SIGKILL"), opts2.timeoutMs) : null;
|
|
14194
15240
|
child.on("close", (code) => {
|
|
14195
15241
|
if (timer2) clearTimeout(timer2);
|
|
14196
|
-
|
|
15242
|
+
resolve8({ stdout, stderr, code: code ?? -1 });
|
|
14197
15243
|
});
|
|
14198
15244
|
child.on("error", (err) => {
|
|
14199
15245
|
if (timer2) clearTimeout(timer2);
|
|
14200
|
-
|
|
15246
|
+
resolve8({ stdout, stderr: stderr + String(err), code: -1 });
|
|
14201
15247
|
});
|
|
14202
15248
|
});
|
|
14203
15249
|
}
|
|
@@ -14433,7 +15479,7 @@ ${(install.stderr || install.stdout).slice(-1200).trim()}`,
|
|
|
14433
15479
|
ok: true
|
|
14434
15480
|
};
|
|
14435
15481
|
yield { message: "Patching wrangler.toml\u2026" };
|
|
14436
|
-
let toml = await
|
|
15482
|
+
let toml = await readFile18(wranglerToml, "utf8");
|
|
14437
15483
|
toml = toml.replace(/^name\s*=\s*"[^"]+"/m, `name = "${workerName}"`);
|
|
14438
15484
|
toml = toml.replace(
|
|
14439
15485
|
/(\[\[kv_namespaces\]\][\s\S]*?binding\s*=\s*"OAUTH_KV"[\s\S]*?id\s*=\s*")[^"]+(")/,
|
|
@@ -14659,45 +15705,6 @@ var init_deploy_commute = __esm({
|
|
|
14659
15705
|
}
|
|
14660
15706
|
});
|
|
14661
15707
|
|
|
14662
|
-
// src/util/image.ts
|
|
14663
|
-
import { readFile as readFile16 } from "fs/promises";
|
|
14664
|
-
import { basename as basename3 } from "path";
|
|
14665
|
-
async function encodeImageFile(filePath) {
|
|
14666
|
-
const buf = await readFile16(filePath);
|
|
14667
|
-
if (buf.byteLength > MAX_IMAGE_BYTES) {
|
|
14668
|
-
throw new Error(
|
|
14669
|
-
`image too large (${(buf.byteLength / 1024 / 1024).toFixed(1)} MB); max is ${MAX_IMAGE_BYTES / 1024 / 1024} MB`
|
|
14670
|
-
);
|
|
14671
|
-
}
|
|
14672
|
-
const ext = filePath.slice(filePath.lastIndexOf(".")).toLowerCase();
|
|
14673
|
-
const mime = EXT_TO_MIME[ext] ?? "image/jpeg";
|
|
14674
|
-
const b64 = buf.toString("base64");
|
|
14675
|
-
return {
|
|
14676
|
-
filename: basename3(filePath),
|
|
14677
|
-
mime,
|
|
14678
|
-
dataUrl: `data:${mime};base64,${b64}`
|
|
14679
|
-
};
|
|
14680
|
-
}
|
|
14681
|
-
function isImagePath(path) {
|
|
14682
|
-
const ext = path.slice(path.lastIndexOf(".")).toLowerCase();
|
|
14683
|
-
return ext in EXT_TO_MIME;
|
|
14684
|
-
}
|
|
14685
|
-
var MAX_IMAGE_BYTES, EXT_TO_MIME;
|
|
14686
|
-
var init_image = __esm({
|
|
14687
|
-
"src/util/image.ts"() {
|
|
14688
|
-
"use strict";
|
|
14689
|
-
MAX_IMAGE_BYTES = 5 * 1024 * 1024;
|
|
14690
|
-
EXT_TO_MIME = {
|
|
14691
|
-
".png": "image/png",
|
|
14692
|
-
".jpg": "image/jpeg",
|
|
14693
|
-
".jpeg": "image/jpeg",
|
|
14694
|
-
".gif": "image/gif",
|
|
14695
|
-
".webp": "image/webp",
|
|
14696
|
-
".bmp": "image/bmp"
|
|
14697
|
-
};
|
|
14698
|
-
}
|
|
14699
|
-
});
|
|
14700
|
-
|
|
14701
15708
|
// src/ui/app-helpers.ts
|
|
14702
15709
|
var app_helpers_exports = {};
|
|
14703
15710
|
__export(app_helpers_exports, {
|
|
@@ -15342,7 +16349,7 @@ var init_frontmatter = __esm({
|
|
|
15342
16349
|
});
|
|
15343
16350
|
|
|
15344
16351
|
// src/skills/loader.ts
|
|
15345
|
-
import { readFile as
|
|
16352
|
+
import { readFile as readFile19, readdir as readdir6, stat as stat6 } from "fs/promises";
|
|
15346
16353
|
import { join as join26, extname } from "path";
|
|
15347
16354
|
function normalizeManifest(raw, filePath) {
|
|
15348
16355
|
const name = typeof raw.name === "string" ? raw.name : "";
|
|
@@ -15357,7 +16364,7 @@ function normalizeManifest(raw, filePath) {
|
|
|
15357
16364
|
return { name, description, match, scope, priority, enabled };
|
|
15358
16365
|
}
|
|
15359
16366
|
async function loadSkillFile(filePath) {
|
|
15360
|
-
const raw = await
|
|
16367
|
+
const raw = await readFile19(filePath, "utf-8");
|
|
15361
16368
|
const parsed = parseFrontmatter(raw);
|
|
15362
16369
|
const manifest = normalizeManifest(parsed.data, filePath);
|
|
15363
16370
|
const body = parsed.content.trim();
|
|
@@ -15407,7 +16414,7 @@ var init_loader = __esm({
|
|
|
15407
16414
|
});
|
|
15408
16415
|
|
|
15409
16416
|
// src/skills/discovery.ts
|
|
15410
|
-
import { readdir as readdir7, stat as stat7, readFile as
|
|
16417
|
+
import { readdir as readdir7, stat as stat7, readFile as readFile20 } from "fs/promises";
|
|
15411
16418
|
import { join as join27, extname as extname2 } from "path";
|
|
15412
16419
|
async function dirExists(path) {
|
|
15413
16420
|
try {
|
|
@@ -15445,7 +16452,7 @@ async function discoverSkills(cwd) {
|
|
|
15445
16452
|
return ordered;
|
|
15446
16453
|
}
|
|
15447
16454
|
async function readSkillFile(filePath) {
|
|
15448
|
-
const bytes = await
|
|
16455
|
+
const bytes = await readFile20(filePath);
|
|
15449
16456
|
return { bytes, text: bytes.toString("utf-8") };
|
|
15450
16457
|
}
|
|
15451
16458
|
var SKILL_EXTENSIONS;
|
|
@@ -16677,8 +17684,8 @@ var init_one_dark = __esm({
|
|
|
16677
17684
|
});
|
|
16678
17685
|
|
|
16679
17686
|
// src/ui/theme.ts
|
|
16680
|
-
function normalizeTheme(
|
|
16681
|
-
const obj =
|
|
17687
|
+
function normalizeTheme(json2) {
|
|
17688
|
+
const obj = json2;
|
|
16682
17689
|
const palette = obj.palette;
|
|
16683
17690
|
const normalizeDim = (v) => {
|
|
16684
17691
|
if (v === void 0) return void 0;
|
|
@@ -16860,8 +17867,8 @@ async function listGateways(accountId, apiToken) {
|
|
|
16860
17867
|
if (!res.ok) {
|
|
16861
17868
|
throw new AiGatewayError(await parseCloudflareError(res));
|
|
16862
17869
|
}
|
|
16863
|
-
const
|
|
16864
|
-
return Array.isArray(
|
|
17870
|
+
const json2 = await res.json();
|
|
17871
|
+
return Array.isArray(json2.result) ? json2.result : [];
|
|
16865
17872
|
}
|
|
16866
17873
|
async function createGateway(accountId, apiToken, id) {
|
|
16867
17874
|
const res = await doFetch(baseUrl(accountId), {
|
|
@@ -16887,15 +17894,15 @@ async function createGateway(accountId, apiToken, id) {
|
|
|
16887
17894
|
if (!res.ok) {
|
|
16888
17895
|
throw new AiGatewayError(await parseCloudflareError(res));
|
|
16889
17896
|
}
|
|
16890
|
-
const
|
|
16891
|
-
if (!
|
|
17897
|
+
const json2 = await res.json();
|
|
17898
|
+
if (!json2.result) {
|
|
16892
17899
|
throw new AiGatewayError({
|
|
16893
17900
|
kind: "other",
|
|
16894
17901
|
status: res.status,
|
|
16895
17902
|
message: "Cloudflare returned no gateway result"
|
|
16896
17903
|
});
|
|
16897
17904
|
}
|
|
16898
|
-
return
|
|
17905
|
+
return json2.result;
|
|
16899
17906
|
}
|
|
16900
17907
|
async function enableGatewayAuth(accountId, apiToken, gatewayId) {
|
|
16901
17908
|
const url = `${baseUrl(accountId)}/${encodeURIComponent(gatewayId)}`;
|
|
@@ -16952,7 +17959,7 @@ var init_ai_gateway_api = __esm({
|
|
|
16952
17959
|
});
|
|
16953
17960
|
|
|
16954
17961
|
// src/skills/manager.ts
|
|
16955
|
-
import { mkdir as mkdir10, writeFile as writeFile12, unlink as unlink2, readFile as
|
|
17962
|
+
import { mkdir as mkdir10, writeFile as writeFile12, unlink as unlink2, readFile as readFile21 } from "fs/promises";
|
|
16956
17963
|
import { join as join28 } from "path";
|
|
16957
17964
|
function getSkillDirs(cwd) {
|
|
16958
17965
|
return {
|
|
@@ -17007,7 +18014,7 @@ async function setSkillEnabled(name, enabled, cwd) {
|
|
|
17007
18014
|
const all = await listAllSkills(cwd);
|
|
17008
18015
|
const skill = all.project.find((s) => s.name === name) ?? all.global.find((s) => s.name === name);
|
|
17009
18016
|
if (!skill) throw new Error(`skill "${name}" not found`);
|
|
17010
|
-
const raw = await
|
|
18017
|
+
const raw = await readFile21(skill.filePath, "utf-8");
|
|
17011
18018
|
const parsed = parseFrontmatter(raw);
|
|
17012
18019
|
parsed.data.enabled = enabled;
|
|
17013
18020
|
const yaml = Object.entries(parsed.data).map(([k, v]) => {
|
|
@@ -17091,13 +18098,13 @@ var init_frontmatter2 = __esm({
|
|
|
17091
18098
|
|
|
17092
18099
|
// src/commands/loader.ts
|
|
17093
18100
|
import { open, realpath as realpath2 } from "fs/promises";
|
|
17094
|
-
import { homedir as
|
|
17095
|
-
import { join as join29, relative as
|
|
18101
|
+
import { homedir as homedir15 } from "os";
|
|
18102
|
+
import { join as join29, relative as relative6, sep as sep2 } from "path";
|
|
17096
18103
|
function projectCommandsDir(cwd = process.cwd()) {
|
|
17097
18104
|
return join29(cwd, ".kimiflare", "commands");
|
|
17098
18105
|
}
|
|
17099
18106
|
function globalCommandsDir() {
|
|
17100
|
-
const xdg = process.env.XDG_CONFIG_HOME || join29(
|
|
18107
|
+
const xdg = process.env.XDG_CONFIG_HOME || join29(homedir15(), ".config");
|
|
17101
18108
|
return join29(xdg, "kimiflare", "commands");
|
|
17102
18109
|
}
|
|
17103
18110
|
async function loadCustomCommands(cwd = process.cwd()) {
|
|
@@ -17152,7 +18159,7 @@ async function resolveSafeDir(dir, source, cwd, warnings) {
|
|
|
17152
18159
|
} catch {
|
|
17153
18160
|
return null;
|
|
17154
18161
|
}
|
|
17155
|
-
const rel =
|
|
18162
|
+
const rel = relative6(realCwd, realDir);
|
|
17156
18163
|
if (rel !== "" && isPathOutside(rel)) {
|
|
17157
18164
|
warnings.push(`commands dir ${dir} escapes workspace via symlink \u2014 skipped`);
|
|
17158
18165
|
return null;
|
|
@@ -17227,7 +18234,7 @@ async function loadOne(file, rootDir, source, warnings) {
|
|
|
17227
18234
|
return cmd;
|
|
17228
18235
|
}
|
|
17229
18236
|
function filenameToCommandName(file, rootDir) {
|
|
17230
|
-
const rel =
|
|
18237
|
+
const rel = relative6(rootDir, file);
|
|
17231
18238
|
if (!rel || isPathOutside(rel)) return null;
|
|
17232
18239
|
const noExt = rel.replace(/\.md$/i, "");
|
|
17233
18240
|
const parts = noExt.split(sep2).filter((p) => p.length > 0);
|
|
@@ -17909,9 +18916,9 @@ import { execSync as execSync6, spawn as spawn6 } from "child_process";
|
|
|
17909
18916
|
import { appendFileSync, mkdirSync as mkdirSync4, openSync } from "fs";
|
|
17910
18917
|
import { unlink as unlink4 } from "fs/promises";
|
|
17911
18918
|
import { join as join31 } from "path";
|
|
17912
|
-
import { homedir as
|
|
18919
|
+
import { homedir as homedir16, platform as platform6 } from "os";
|
|
17913
18920
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
17914
|
-
import { readFile as
|
|
18921
|
+
import { readFile as readFile22 } from "fs/promises";
|
|
17915
18922
|
import QRCode from "qrcode";
|
|
17916
18923
|
function kimiLog(payload) {
|
|
17917
18924
|
if (!KIMI_LOG_PATH) return;
|
|
@@ -17949,7 +18956,7 @@ function gatewayFromOpts2(opts2) {
|
|
|
17949
18956
|
}
|
|
17950
18957
|
async function runUiMode(opts2) {
|
|
17951
18958
|
await loadCamouflage();
|
|
17952
|
-
const xdgConfig = process.env.XDG_CONFIG_HOME || join31(
|
|
18959
|
+
const xdgConfig = process.env.XDG_CONFIG_HOME || join31(homedir16(), ".config");
|
|
17953
18960
|
const camoDbDir = join31(xdgConfig, "kimiflare");
|
|
17954
18961
|
try {
|
|
17955
18962
|
mkdirSync4(camoDbDir, { recursive: true });
|
|
@@ -18812,8 +19819,8 @@ Executor opened PR: ${prUrl}` : plan });
|
|
|
18812
19819
|
cam.send("PermissionGranted", { request_id: reqId });
|
|
18813
19820
|
return "allow";
|
|
18814
19821
|
}
|
|
18815
|
-
const choice = await new Promise((
|
|
18816
|
-
pendingPermissions.set(reqId,
|
|
19822
|
+
const choice = await new Promise((resolve8) => {
|
|
19823
|
+
pendingPermissions.set(reqId, resolve8);
|
|
18817
19824
|
});
|
|
18818
19825
|
cam.send(
|
|
18819
19826
|
choice === "deny" ? "PermissionDenied" : "PermissionGranted",
|
|
@@ -18947,12 +19954,12 @@ Executor opened PR: ${prUrl}` : plan });
|
|
|
18947
19954
|
async function nextFollowUp() {
|
|
18948
19955
|
if (followUpQueue.length > 0) return followUpQueue.shift();
|
|
18949
19956
|
if (aborted2) return null;
|
|
18950
|
-
return new Promise((
|
|
18951
|
-
followUpResolver =
|
|
19957
|
+
return new Promise((resolve8) => {
|
|
19958
|
+
followUpResolver = resolve8;
|
|
18952
19959
|
});
|
|
18953
19960
|
}
|
|
18954
19961
|
async function openInboxModal() {
|
|
18955
|
-
const
|
|
19962
|
+
const URL3 = "https://hello.kimiflare.com";
|
|
18956
19963
|
const f = await form(cam, {
|
|
18957
19964
|
id: `inbox-${Date.now()}`,
|
|
18958
19965
|
title: "/inbox \xB7 check for a voice reply",
|
|
@@ -18970,7 +19977,7 @@ Executor opened PR: ${prUrl}` : plan });
|
|
|
18970
19977
|
let data;
|
|
18971
19978
|
try {
|
|
18972
19979
|
const res = await fetch(
|
|
18973
|
-
`${
|
|
19980
|
+
`${URL3}/inbox/check?u=${encodeURIComponent(handle)}&s=${encodeURIComponent(secret)}`
|
|
18974
19981
|
);
|
|
18975
19982
|
if (!res.ok) throw new Error(`server returned ${res.status}`);
|
|
18976
19983
|
data = await res.json();
|
|
@@ -18995,7 +20002,7 @@ Executor opened PR: ${prUrl}` : plan });
|
|
|
18995
20002
|
});
|
|
18996
20003
|
if (pick3.cancelled || !pick3.value) return;
|
|
18997
20004
|
openBrowser2(
|
|
18998
|
-
`${
|
|
20005
|
+
`${URL3}/inbox?u=${encodeURIComponent(handle)}&s=${encodeURIComponent(secret)}&m=${encodeURIComponent(pick3.value)}`
|
|
18999
20006
|
);
|
|
19000
20007
|
cam.send("ShowToast", { text: "opened in browser", kind: "success", ttl_ms: 1500 });
|
|
19001
20008
|
}
|
|
@@ -20311,7 +21318,7 @@ Executor opened PR: ${prUrl}` : plan });
|
|
|
20311
21318
|
try {
|
|
20312
21319
|
const result2 = await createSkill({ name: name2, description: description || void 0, scope, cwd: process.cwd() });
|
|
20313
21320
|
if (content) {
|
|
20314
|
-
const { writeFile:
|
|
21321
|
+
const { writeFile: writeFile14 } = await import("fs/promises");
|
|
20315
21322
|
const yamlLines = [
|
|
20316
21323
|
`name: ${name2}`,
|
|
20317
21324
|
"enabled: true",
|
|
@@ -20326,7 +21333,7 @@ ${yamlLines.join("\n")}
|
|
|
20326
21333
|
|
|
20327
21334
|
${content}
|
|
20328
21335
|
`;
|
|
20329
|
-
await
|
|
21336
|
+
await writeFile14(result2.filepath, fileContent, "utf8");
|
|
20330
21337
|
}
|
|
20331
21338
|
cam.send("ShowToast", { text: `created skill '${name2}' \u2192 ${result2.filepath}`, kind: "success", ttl_ms: 3e3 });
|
|
20332
21339
|
} catch (err) {
|
|
@@ -20369,7 +21376,7 @@ ${content}
|
|
|
20369
21376
|
}
|
|
20370
21377
|
let currentContent;
|
|
20371
21378
|
try {
|
|
20372
|
-
currentContent = await
|
|
21379
|
+
currentContent = await readFile22(filepath, "utf-8");
|
|
20373
21380
|
} catch (err) {
|
|
20374
21381
|
cam.send("ShowToast", { text: `failed to read skill: ${err instanceof Error ? err.message : String(err)}`, kind: "error", ttl_ms: 3e3 });
|
|
20375
21382
|
return true;
|
|
@@ -20389,8 +21396,8 @@ ${content}
|
|
|
20389
21396
|
return true;
|
|
20390
21397
|
}
|
|
20391
21398
|
try {
|
|
20392
|
-
const { writeFile:
|
|
20393
|
-
await
|
|
21399
|
+
const { writeFile: writeFile14 } = await import("fs/promises");
|
|
21400
|
+
await writeFile14(filepath, newContent, "utf8");
|
|
20394
21401
|
cam.send("ShowToast", { text: `updated skill '${name2}' \u2192 ${filepath}`, kind: "success", ttl_ms: 3e3 });
|
|
20395
21402
|
} catch (err) {
|
|
20396
21403
|
cam.send("ShowToast", { text: `failed to save skill: ${err instanceof Error ? err.message : String(err)}`, kind: "error", ttl_ms: 3e3 });
|
|
@@ -20873,11 +21880,11 @@ var init_ui_mode = __esm({
|
|
|
20873
21880
|
init_glob();
|
|
20874
21881
|
init_errors();
|
|
20875
21882
|
init_builtins();
|
|
20876
|
-
|
|
21883
|
+
init_manager2();
|
|
20877
21884
|
init_db2();
|
|
20878
21885
|
init_manager4();
|
|
20879
|
-
init_manager2();
|
|
20880
21886
|
init_manager3();
|
|
21887
|
+
init_manager();
|
|
20881
21888
|
init_lsp();
|
|
20882
21889
|
init_skills();
|
|
20883
21890
|
init_storage_limits();
|
|
@@ -20900,7 +21907,7 @@ var init_ui_mode = __esm({
|
|
|
20900
21907
|
init_worker_client();
|
|
20901
21908
|
init_builtins();
|
|
20902
21909
|
init_settings();
|
|
20903
|
-
|
|
21910
|
+
init_types();
|
|
20904
21911
|
init_recommended();
|
|
20905
21912
|
init_context_generator();
|
|
20906
21913
|
init_mode();
|
|
@@ -23414,19 +24421,19 @@ function usePermissionController(getMode, onPlanModeBlocked) {
|
|
|
23414
24421
|
const onPlanModeBlockedRef = useRef2(onPlanModeBlocked);
|
|
23415
24422
|
onPlanModeBlockedRef.current = onPlanModeBlocked;
|
|
23416
24423
|
const askPermission = useCallback2(
|
|
23417
|
-
(req, askOpts) => new Promise((
|
|
24424
|
+
(req, askOpts) => new Promise((resolve8) => {
|
|
23418
24425
|
const outcome = decidePermission(req, getModeRef.current(), askOpts);
|
|
23419
24426
|
if (outcome.kind === "resolve") {
|
|
23420
|
-
|
|
24427
|
+
resolve8(outcome.decision);
|
|
23421
24428
|
return;
|
|
23422
24429
|
}
|
|
23423
24430
|
if (outcome.kind === "plan_blocked") {
|
|
23424
24431
|
onPlanModeBlockedRef.current(outcome.toolName);
|
|
23425
|
-
|
|
24432
|
+
resolve8(AUTO_DENY);
|
|
23426
24433
|
return;
|
|
23427
24434
|
}
|
|
23428
|
-
resolveRef.current =
|
|
23429
|
-
setPending({ tool: req.tool, args: req.args, resolve:
|
|
24435
|
+
resolveRef.current = resolve8;
|
|
24436
|
+
setPending({ tool: req.tool, args: req.args, resolve: resolve8 });
|
|
23430
24437
|
}),
|
|
23431
24438
|
[]
|
|
23432
24439
|
);
|
|
@@ -25431,7 +26438,7 @@ var init_welcome = __esm({
|
|
|
25431
26438
|
// src/commands/renderer.ts
|
|
25432
26439
|
import { exec } from "child_process";
|
|
25433
26440
|
import { open as open2, realpath as realpath3 } from "fs/promises";
|
|
25434
|
-
import { isAbsolute as isAbsolute2, relative as
|
|
26441
|
+
import { isAbsolute as isAbsolute2, relative as relative8, resolve as resolvePathJoin, basename as pathBasename } from "path";
|
|
25435
26442
|
import { promisify as promisify2 } from "util";
|
|
25436
26443
|
function tokenizeArgs(s) {
|
|
25437
26444
|
return [...s.matchAll(ARG_TOKEN_RE)].map((match) => {
|
|
@@ -25536,7 +26543,7 @@ async function replaceFiles(prompt, warnings, cmd, cwd, maxFileBytes) {
|
|
|
25536
26543
|
return "";
|
|
25537
26544
|
}
|
|
25538
26545
|
const resolved = resolvePathJoin(cwd, rawPath);
|
|
25539
|
-
if (isPathOutside(
|
|
26546
|
+
if (isPathOutside(relative8(cwd, resolved))) {
|
|
25540
26547
|
warnings.push(`file inclusion skipped: @${rawPath} \u2014 outside workspace`);
|
|
25541
26548
|
return "";
|
|
25542
26549
|
}
|
|
@@ -25547,7 +26554,7 @@ async function replaceFiles(prompt, warnings, cmd, cwd, maxFileBytes) {
|
|
|
25547
26554
|
warnings.push(`file inclusion failed: @${rawPath} \u2014 ${message(error)}`);
|
|
25548
26555
|
return "";
|
|
25549
26556
|
}
|
|
25550
|
-
if (isPathOutside(
|
|
26557
|
+
if (isPathOutside(relative8(realCwd, real))) {
|
|
25551
26558
|
warnings.push(`file inclusion skipped: @${rawPath} \u2014 symlink escapes workspace`);
|
|
25552
26559
|
return "";
|
|
25553
26560
|
}
|
|
@@ -25660,9 +26667,9 @@ var init_wcag = __esm({
|
|
|
25660
26667
|
});
|
|
25661
26668
|
|
|
25662
26669
|
// src/ui/theme-loader.ts
|
|
25663
|
-
import { readFile as
|
|
26670
|
+
import { readFile as readFile23, readdir as readdir9 } from "fs/promises";
|
|
25664
26671
|
import { join as join32 } from "path";
|
|
25665
|
-
import { homedir as
|
|
26672
|
+
import { homedir as homedir17 } from "os";
|
|
25666
26673
|
function projectThemesDir(cwd = process.cwd()) {
|
|
25667
26674
|
return join32(cwd, ".kimiflare", "themes");
|
|
25668
26675
|
}
|
|
@@ -25756,23 +26763,23 @@ async function loadThemesFromDir(dir, source) {
|
|
|
25756
26763
|
const path = join32(dir, file);
|
|
25757
26764
|
let raw;
|
|
25758
26765
|
try {
|
|
25759
|
-
raw = await
|
|
26766
|
+
raw = await readFile23(path, "utf-8");
|
|
25760
26767
|
} catch (e) {
|
|
25761
26768
|
errors.push(`${path}: ${e instanceof Error ? e.message : String(e)}`);
|
|
25762
26769
|
continue;
|
|
25763
26770
|
}
|
|
25764
|
-
let
|
|
26771
|
+
let json2;
|
|
25765
26772
|
try {
|
|
25766
|
-
|
|
26773
|
+
json2 = JSON.parse(raw);
|
|
25767
26774
|
} catch (e) {
|
|
25768
26775
|
errors.push(`${path}: invalid JSON \u2014 ${e instanceof Error ? e.message : String(e)}`);
|
|
25769
26776
|
continue;
|
|
25770
26777
|
}
|
|
25771
|
-
if (!
|
|
26778
|
+
if (!json2 || typeof json2 !== "object") {
|
|
25772
26779
|
errors.push(`${path}: root must be an object`);
|
|
25773
26780
|
continue;
|
|
25774
26781
|
}
|
|
25775
|
-
const obj =
|
|
26782
|
+
const obj = json2;
|
|
25776
26783
|
const fileErrors = [];
|
|
25777
26784
|
if (typeof obj.name !== "string" || obj.name.length === 0) {
|
|
25778
26785
|
fileErrors.push("name is required");
|
|
@@ -25903,7 +26910,7 @@ var init_theme_loader = __esm({
|
|
|
25903
26910
|
init_wcag();
|
|
25904
26911
|
init_theme();
|
|
25905
26912
|
USER_THEMES_DIR = join32(
|
|
25906
|
-
process.env.XDG_CONFIG_HOME || join32(
|
|
26913
|
+
process.env.XDG_CONFIG_HOME || join32(homedir17(), ".config"),
|
|
25907
26914
|
"kimiflare",
|
|
25908
26915
|
"themes"
|
|
25909
26916
|
);
|
|
@@ -30279,11 +31286,11 @@ var tui_report_exports = {};
|
|
|
30279
31286
|
__export(tui_report_exports, {
|
|
30280
31287
|
getCategoryReportText: () => getCategoryReportText
|
|
30281
31288
|
});
|
|
30282
|
-
import { readFile as
|
|
31289
|
+
import { readFile as readFile24 } from "fs/promises";
|
|
30283
31290
|
import { join as join33 } from "path";
|
|
30284
|
-
import { homedir as
|
|
31291
|
+
import { homedir as homedir18 } from "os";
|
|
30285
31292
|
function usageDir3() {
|
|
30286
|
-
const xdg = process.env.XDG_DATA_HOME || join33(
|
|
31293
|
+
const xdg = process.env.XDG_DATA_HOME || join33(homedir18(), ".local", "share");
|
|
30287
31294
|
return join33(xdg, "kimiflare");
|
|
30288
31295
|
}
|
|
30289
31296
|
function usagePath3() {
|
|
@@ -30299,7 +31306,7 @@ function daysAgo2(n) {
|
|
|
30299
31306
|
}
|
|
30300
31307
|
async function loadLog3() {
|
|
30301
31308
|
try {
|
|
30302
|
-
const raw = await
|
|
31309
|
+
const raw = await readFile24(usagePath3(), "utf8");
|
|
30303
31310
|
return JSON.parse(raw);
|
|
30304
31311
|
} catch {
|
|
30305
31312
|
return { version: 1, days: [], sessions: [] };
|
|
@@ -30429,7 +31436,7 @@ var init_slash_commands = __esm({
|
|
|
30429
31436
|
init_executor();
|
|
30430
31437
|
init_recommended();
|
|
30431
31438
|
init_settings();
|
|
30432
|
-
|
|
31439
|
+
init_types();
|
|
30433
31440
|
init_update_check();
|
|
30434
31441
|
init_version();
|
|
30435
31442
|
init_app_helpers();
|
|
@@ -31972,7 +32979,7 @@ async function runInit(deps) {
|
|
|
31972
32979
|
lspManagerRef.current.notifyChange(path, content);
|
|
31973
32980
|
} else {
|
|
31974
32981
|
void import("fs/promises").then(
|
|
31975
|
-
({ readFile:
|
|
32982
|
+
({ readFile: readFile25 }) => readFile25(path, "utf8").then((c) => lspManagerRef.current.notifyChange(path, c)).catch(() => {
|
|
31976
32983
|
})
|
|
31977
32984
|
);
|
|
31978
32985
|
}
|
|
@@ -32058,9 +33065,9 @@ async function runInit(deps) {
|
|
|
32058
33065
|
},
|
|
32059
33066
|
onGatewayMeta: updateGatewayMeta,
|
|
32060
33067
|
askPermission: (req) => askForPermission(req, { promptOnBlockedBash: true }),
|
|
32061
|
-
onLoopDetected: () => new Promise((
|
|
32062
|
-
loopResolveRef.current =
|
|
32063
|
-
setLoopModal({ resolve:
|
|
33068
|
+
onLoopDetected: () => new Promise((resolve8) => {
|
|
33069
|
+
loopResolveRef.current = resolve8;
|
|
33070
|
+
setLoopModal({ resolve: resolve8 });
|
|
32064
33071
|
}),
|
|
32065
33072
|
onKimiMdStale: () => {
|
|
32066
33073
|
if (!kimiMdStaleNudgedRef.current) {
|
|
@@ -32171,45 +33178,9 @@ var init_run_init = __esm({
|
|
|
32171
33178
|
}
|
|
32172
33179
|
});
|
|
32173
33180
|
|
|
32174
|
-
// src/util/state.ts
|
|
32175
|
-
import { readFile as readFile23, writeFile as writeFile14, mkdir as mkdir12 } from "fs/promises";
|
|
32176
|
-
import { homedir as homedir18 } from "os";
|
|
32177
|
-
import { join as join36 } from "path";
|
|
32178
|
-
function statePath() {
|
|
32179
|
-
const xdg = process.env.XDG_CONFIG_HOME || join36(homedir18(), ".config");
|
|
32180
|
-
return join36(xdg, "kimiflare", "state.json");
|
|
32181
|
-
}
|
|
32182
|
-
async function readState() {
|
|
32183
|
-
try {
|
|
32184
|
-
const raw = await readFile23(statePath(), "utf8");
|
|
32185
|
-
return JSON.parse(raw);
|
|
32186
|
-
} catch {
|
|
32187
|
-
return {};
|
|
32188
|
-
}
|
|
32189
|
-
}
|
|
32190
|
-
async function writeState(state) {
|
|
32191
|
-
const path = statePath();
|
|
32192
|
-
await mkdir12(join36(path, ".."), { recursive: true });
|
|
32193
|
-
await writeFile14(path, JSON.stringify(state, null, 2) + "\n", "utf8");
|
|
32194
|
-
}
|
|
32195
|
-
async function markCreatorMessageSeen(version) {
|
|
32196
|
-
const state = await readState();
|
|
32197
|
-
state.creatorMessageSeenVersion = version;
|
|
32198
|
-
await writeState(state);
|
|
32199
|
-
}
|
|
32200
|
-
async function shouldShowCreatorMessage(version) {
|
|
32201
|
-
const state = await readState();
|
|
32202
|
-
return state.creatorMessageSeenVersion !== version;
|
|
32203
|
-
}
|
|
32204
|
-
var init_state = __esm({
|
|
32205
|
-
"src/util/state.ts"() {
|
|
32206
|
-
"use strict";
|
|
32207
|
-
}
|
|
32208
|
-
});
|
|
32209
|
-
|
|
32210
33181
|
// src/ui/run-startup-tasks.ts
|
|
32211
33182
|
import { existsSync as existsSync7 } from "fs";
|
|
32212
|
-
import { join as
|
|
33183
|
+
import { join as join36 } from "path";
|
|
32213
33184
|
function runStartupTasks(deps) {
|
|
32214
33185
|
const {
|
|
32215
33186
|
cfg,
|
|
@@ -32221,46 +33192,16 @@ function runStartupTasks(deps) {
|
|
|
32221
33192
|
customCommandsRef,
|
|
32222
33193
|
setCustomCommandsVersion
|
|
32223
33194
|
} = deps;
|
|
32224
|
-
void Promise.resolve().then(() => (init_sessions(), sessions_exports)).then(
|
|
32225
|
-
|
|
32226
|
-
if (removed > 0) {
|
|
32227
|
-
setEvents((e) => [
|
|
32228
|
-
...e,
|
|
32229
|
-
{ kind: "info", key: mkKey2(), text: `pruned ${removed} old session files` }
|
|
32230
|
-
]);
|
|
32231
|
-
}
|
|
32232
|
-
})
|
|
32233
|
-
);
|
|
32234
|
-
void Promise.resolve().then(() => (init_log_sink(), log_sink_exports)).then(({ pruneOldLogs: pruneOldLogs2, logPathFor: logPathFor2, isLogSinkEnabled: isLogSinkEnabled2 }) => {
|
|
33195
|
+
void Promise.resolve().then(() => (init_sessions(), sessions_exports)).then(({ pruneSessions: pruneSessions2 }) => pruneSessions2());
|
|
33196
|
+
void Promise.resolve().then(() => (init_log_sink(), log_sink_exports)).then(({ pruneOldLogs: pruneOldLogs2, isLogSinkEnabled: isLogSinkEnabled2 }) => {
|
|
32235
33197
|
if (!isLogSinkEnabled2()) return;
|
|
32236
33198
|
try {
|
|
32237
33199
|
pruneOldLogs2();
|
|
32238
33200
|
} catch {
|
|
32239
33201
|
}
|
|
32240
|
-
setEvents((e) => [
|
|
32241
|
-
...e,
|
|
32242
|
-
{
|
|
32243
|
-
kind: "info",
|
|
32244
|
-
key: mkKey2(),
|
|
32245
|
-
text: `structured logs: ${logPathFor2()} (tail with: tail -f $(kimiflare logs path) | jq)`
|
|
32246
|
-
}
|
|
32247
|
-
]);
|
|
32248
|
-
});
|
|
32249
|
-
void shouldShowCreatorMessage(getAppVersion()).then((shouldShow) => {
|
|
32250
|
-
if (shouldShow) {
|
|
32251
|
-
setEvents((e) => [
|
|
32252
|
-
...e,
|
|
32253
|
-
{
|
|
32254
|
-
kind: "info",
|
|
32255
|
-
key: mkKey2(),
|
|
32256
|
-
text: "Hey, how do you like this version? I'd love to hear from you \u2014 type /hello to send me a voice note. Only I see it, and I may DM you back."
|
|
32257
|
-
}
|
|
32258
|
-
]);
|
|
32259
|
-
void markCreatorMessageSeen(getAppVersion());
|
|
32260
|
-
}
|
|
32261
33202
|
});
|
|
32262
33203
|
if (cfg.memoryEnabled) {
|
|
32263
|
-
const dbPath = cfg.memoryDbPath ??
|
|
33204
|
+
const dbPath = cfg.memoryDbPath ?? join36(process.cwd(), ".kimiflare", "memory.db");
|
|
32264
33205
|
const manager = new MemoryManager({
|
|
32265
33206
|
dbPath,
|
|
32266
33207
|
accountId: cfg.accountId,
|
|
@@ -32294,7 +33235,7 @@ function runStartupTasks(deps) {
|
|
|
32294
33235
|
});
|
|
32295
33236
|
const cwd = process.cwd();
|
|
32296
33237
|
sessionStartRecallRef.current = manager.recall({ text: cwd, repoPath: cwd, limit: 5 });
|
|
32297
|
-
if (existsSync7(
|
|
33238
|
+
if (existsSync7(join36(cwd, "KIMI.md"))) {
|
|
32298
33239
|
const lastRefresh = manager.getLastKimiMdRefreshTime(cwd);
|
|
32299
33240
|
const driftCount = manager.countHighSignalMemoriesSince(cwd, lastRefresh);
|
|
32300
33241
|
if (driftCount >= 5) {
|
|
@@ -32305,7 +33246,7 @@ function runStartupTasks(deps) {
|
|
|
32305
33246
|
memoryManagerRef.current?.close();
|
|
32306
33247
|
memoryManagerRef.current = null;
|
|
32307
33248
|
}
|
|
32308
|
-
const skillDbPath = cfg.memoryDbPath ??
|
|
33249
|
+
const skillDbPath = cfg.memoryDbPath ?? join36(process.cwd(), ".kimiflare", "memory.db");
|
|
32309
33250
|
const skillDb = getMemoryDb() ?? openMemoryDb(skillDbPath);
|
|
32310
33251
|
initSkillsSchema(skillDb);
|
|
32311
33252
|
void indexSkills({
|
|
@@ -32348,11 +33289,9 @@ var init_run_startup_tasks = __esm({
|
|
|
32348
33289
|
"use strict";
|
|
32349
33290
|
init_builtins();
|
|
32350
33291
|
init_loader2();
|
|
32351
|
-
|
|
33292
|
+
init_manager2();
|
|
32352
33293
|
init_db2();
|
|
32353
33294
|
init_skills();
|
|
32354
|
-
init_state();
|
|
32355
|
-
init_version();
|
|
32356
33295
|
init_storage_limits();
|
|
32357
33296
|
init_app_helpers();
|
|
32358
33297
|
}
|
|
@@ -32458,10 +33397,7 @@ async function initLsp(deps) {
|
|
|
32458
33397
|
if (!cfg.lspEnabled || !cfg.lspServers || lspInitRef.current) {
|
|
32459
33398
|
if (lspInitRef.current) return;
|
|
32460
33399
|
if (!cfg.lspEnabled) {
|
|
32461
|
-
|
|
32462
|
-
...es,
|
|
32463
|
-
{ kind: "info", key: mkKey2(), text: "LSP is disabled. Enable it in config to use language servers." }
|
|
32464
|
-
]);
|
|
33400
|
+
return;
|
|
32465
33401
|
} else if (!cfg.lspServers || Object.keys(cfg.lspServers).length === 0) {
|
|
32466
33402
|
setEvents((es) => [
|
|
32467
33403
|
...es,
|
|
@@ -32774,7 +33710,7 @@ __export(app_exports, {
|
|
|
32774
33710
|
import React25, { useState as useState29, useRef as useRef7, useEffect as useEffect11, useCallback as useCallback10, useMemo as useMemo6 } from "react";
|
|
32775
33711
|
import { Box as Box42, Text as Text43, useApp, useInput as useInput21, render } from "ink";
|
|
32776
33712
|
import { existsSync as existsSync8 } from "fs";
|
|
32777
|
-
import { join as
|
|
33713
|
+
import { join as join37 } from "path";
|
|
32778
33714
|
import { jsx as jsx44, jsxs as jsxs42 } from "react/jsx-runtime";
|
|
32779
33715
|
function App({
|
|
32780
33716
|
initialCfg,
|
|
@@ -34178,7 +35114,7 @@ ${wcagWarnings.join("\n")}` }
|
|
|
34178
35114
|
}
|
|
34179
35115
|
}
|
|
34180
35116
|
turnCounterRef.current += 1;
|
|
34181
|
-
if (turnCounterRef.current % 15 === 0 && existsSync8(
|
|
35117
|
+
if (turnCounterRef.current % 15 === 0 && existsSync8(join37(process.cwd(), "KIMI.md")) && !kimiMdStale) {
|
|
34182
35118
|
setEvents((e) => [
|
|
34183
35119
|
...e,
|
|
34184
35120
|
{ kind: "info", key: mkKey(), text: "Tip: Rerunning /init occasionally helps KimiFlare stay accurate as your project evolves." }
|
|
@@ -34202,7 +35138,7 @@ ${wcagWarnings.join("\n")}` }
|
|
|
34202
35138
|
};
|
|
34203
35139
|
ensureSessionId();
|
|
34204
35140
|
const { sessionsDir: sessionsDir3 } = await Promise.resolve().then(() => (init_sessions(), sessions_exports));
|
|
34205
|
-
const filePath =
|
|
35141
|
+
const filePath = join37(sessionsDir3(), `${sessionIdRef.current}.json`);
|
|
34206
35142
|
await addCheckpoint(filePath, cp);
|
|
34207
35143
|
}
|
|
34208
35144
|
const summary = await generateContinuationSummary({
|
|
@@ -34457,13 +35393,13 @@ ${conflicts.join("\n")}` }
|
|
|
34457
35393
|
planOptionsRef.current = options;
|
|
34458
35394
|
},
|
|
34459
35395
|
askPermission: askForPermission,
|
|
34460
|
-
onToolLimitReached: () => new Promise((
|
|
34461
|
-
limitResolveRef.current =
|
|
34462
|
-
setLimitModal({ limit: 200, resolve:
|
|
35396
|
+
onToolLimitReached: () => new Promise((resolve8) => {
|
|
35397
|
+
limitResolveRef.current = resolve8;
|
|
35398
|
+
setLimitModal({ limit: 200, resolve: resolve8 });
|
|
34463
35399
|
}),
|
|
34464
|
-
onLoopDetected: () => new Promise((
|
|
34465
|
-
loopResolveRef.current =
|
|
34466
|
-
setLoopModal({ resolve:
|
|
35400
|
+
onLoopDetected: () => new Promise((resolve8) => {
|
|
35401
|
+
loopResolveRef.current = resolve8;
|
|
35402
|
+
setLoopModal({ resolve: resolve8 });
|
|
34467
35403
|
}),
|
|
34468
35404
|
onKimiMdStale: () => {
|
|
34469
35405
|
if (!kimiMdStaleNudgedRef.current) {
|
|
@@ -34564,7 +35500,7 @@ ${conflicts.join("\n")}` }
|
|
|
34564
35500
|
lspManagerRef.current.notifyChange(path, content2);
|
|
34565
35501
|
} else {
|
|
34566
35502
|
void import("fs/promises").then(
|
|
34567
|
-
({ readFile:
|
|
35503
|
+
({ readFile: readFile25 }) => readFile25(path, "utf8").then((c) => lspManagerRef.current.notifyChange(path, c)).catch(() => {
|
|
34568
35504
|
})
|
|
34569
35505
|
);
|
|
34570
35506
|
}
|
|
@@ -34851,7 +35787,7 @@ ${conflicts.join("\n")}` }
|
|
|
34851
35787
|
onCommandDelete: handleCommandDelete2,
|
|
34852
35788
|
lspServers: cfg?.lspServers ?? {},
|
|
34853
35789
|
lspScope,
|
|
34854
|
-
hasProjectDir: existsSync8(
|
|
35790
|
+
hasProjectDir: existsSync8(join37(process.cwd(), ".kimiflare")),
|
|
34855
35791
|
onLspSave: handleLspSave2,
|
|
34856
35792
|
themes: themeList(),
|
|
34857
35793
|
onPickTheme: handleThemePick,
|
|
@@ -35218,8 +36154,8 @@ var init_app = __esm({
|
|
|
35218
36154
|
init_session_state();
|
|
35219
36155
|
init_executor();
|
|
35220
36156
|
init_manager4();
|
|
35221
|
-
init_manager2();
|
|
35222
36157
|
init_manager3();
|
|
36158
|
+
init_manager();
|
|
35223
36159
|
init_messages();
|
|
35224
36160
|
init_errors();
|
|
35225
36161
|
init_abort_scope();
|
|
@@ -35278,10 +36214,6 @@ var init_app = __esm({
|
|
|
35278
36214
|
// src/index.tsx
|
|
35279
36215
|
init_config();
|
|
35280
36216
|
init_lsp_config();
|
|
35281
|
-
init_loop();
|
|
35282
|
-
init_errors();
|
|
35283
|
-
init_system_prompt();
|
|
35284
|
-
init_executor();
|
|
35285
36217
|
init_update_check();
|
|
35286
36218
|
init_version();
|
|
35287
36219
|
import { Command as Command2 } from "commander";
|
|
@@ -35461,9 +36393,334 @@ function renderLogo(version) {
|
|
|
35461
36393
|
return out.join("\n");
|
|
35462
36394
|
}
|
|
35463
36395
|
|
|
36396
|
+
// src/print-mode.ts
|
|
36397
|
+
init_loop();
|
|
36398
|
+
init_system_prompt();
|
|
36399
|
+
init_executor();
|
|
36400
|
+
init_errors();
|
|
36401
|
+
init_sessions();
|
|
36402
|
+
init_image();
|
|
36403
|
+
init_glob();
|
|
36404
|
+
init_permissions_evaluator();
|
|
36405
|
+
import { readFile as readFile12 } from "fs/promises";
|
|
36406
|
+
import { resolve as resolve4, basename as basename3 } from "path";
|
|
36407
|
+
function gatewayFromPrintOpts(opts2) {
|
|
36408
|
+
if (!opts2.aiGatewayId) return void 0;
|
|
36409
|
+
return {
|
|
36410
|
+
id: opts2.aiGatewayId,
|
|
36411
|
+
cacheTtl: opts2.aiGatewayCacheTtl,
|
|
36412
|
+
skipCache: opts2.aiGatewaySkipCache,
|
|
36413
|
+
collectLogPayload: opts2.aiGatewayCollectLogPayload,
|
|
36414
|
+
metadata: opts2.aiGatewayMetadata
|
|
36415
|
+
};
|
|
36416
|
+
}
|
|
36417
|
+
async function resolveSession(opts2) {
|
|
36418
|
+
if (opts2.sessionId) {
|
|
36419
|
+
const filePath = resolve4(sessionsDir(), `${opts2.sessionId}.json`);
|
|
36420
|
+
try {
|
|
36421
|
+
const file = await loadSession(filePath);
|
|
36422
|
+
return { sessionFile: file, isNew: false };
|
|
36423
|
+
} catch {
|
|
36424
|
+
}
|
|
36425
|
+
}
|
|
36426
|
+
if (opts2.continueSession) {
|
|
36427
|
+
const cwd = opts2.dir ? resolve4(opts2.dir) : process.cwd();
|
|
36428
|
+
const sessions = await listSessions(1, cwd);
|
|
36429
|
+
if (sessions.length > 0) {
|
|
36430
|
+
const filePath = sessions[0].filePath;
|
|
36431
|
+
const file = await loadSession(filePath);
|
|
36432
|
+
return { sessionFile: file, isNew: false };
|
|
36433
|
+
}
|
|
36434
|
+
}
|
|
36435
|
+
const { makeSessionId: makeSessionId2 } = await Promise.resolve().then(() => (init_sessions(), sessions_exports));
|
|
36436
|
+
const id = makeSessionId2(opts2.prompt);
|
|
36437
|
+
return {
|
|
36438
|
+
sessionFile: {
|
|
36439
|
+
id,
|
|
36440
|
+
cwd: opts2.dir ? resolve4(opts2.dir) : process.cwd(),
|
|
36441
|
+
model: opts2.model,
|
|
36442
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
36443
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
36444
|
+
messages: [],
|
|
36445
|
+
title: opts2.title
|
|
36446
|
+
},
|
|
36447
|
+
isNew: true
|
|
36448
|
+
};
|
|
36449
|
+
}
|
|
36450
|
+
async function resolveFiles(filePatterns, cwd) {
|
|
36451
|
+
const resolved = /* @__PURE__ */ new Set();
|
|
36452
|
+
for (const pattern of filePatterns) {
|
|
36453
|
+
try {
|
|
36454
|
+
const stat8 = await import("fs/promises").then((m) => m.stat(resolve4(cwd, pattern)));
|
|
36455
|
+
if (stat8.isFile()) {
|
|
36456
|
+
resolved.add(resolve4(cwd, pattern));
|
|
36457
|
+
continue;
|
|
36458
|
+
}
|
|
36459
|
+
} catch {
|
|
36460
|
+
}
|
|
36461
|
+
const matches = await glob(pattern, { cwd, absolute: true });
|
|
36462
|
+
for (const m of matches) {
|
|
36463
|
+
resolved.add(m);
|
|
36464
|
+
}
|
|
36465
|
+
}
|
|
36466
|
+
return [...resolved];
|
|
36467
|
+
}
|
|
36468
|
+
async function buildUserMessage(prompt, files, cwd) {
|
|
36469
|
+
let text = prompt;
|
|
36470
|
+
const imageParts = [];
|
|
36471
|
+
const fileContents = [];
|
|
36472
|
+
for (const filePath of files) {
|
|
36473
|
+
if (isImagePath(filePath)) {
|
|
36474
|
+
try {
|
|
36475
|
+
const img = await encodeImageFile(filePath);
|
|
36476
|
+
imageParts.push({ type: "image_url", image_url: { url: img.dataUrl } });
|
|
36477
|
+
} catch (e) {
|
|
36478
|
+
fileContents.push(`
|
|
36479
|
+
<!-- failed to attach image ${basename3(filePath)}: ${e.message} -->
|
|
36480
|
+
`);
|
|
36481
|
+
}
|
|
36482
|
+
} else {
|
|
36483
|
+
try {
|
|
36484
|
+
const content = await readFile12(filePath, "utf8");
|
|
36485
|
+
const relPath = filePath.startsWith(cwd) ? filePath.slice(cwd.length + 1) : filePath;
|
|
36486
|
+
fileContents.push(`
|
|
36487
|
+
--- ${relPath} ---
|
|
36488
|
+
${content}
|
|
36489
|
+
--- end ${relPath} ---
|
|
36490
|
+
`);
|
|
36491
|
+
} catch (e) {
|
|
36492
|
+
fileContents.push(`
|
|
36493
|
+
<!-- failed to read ${basename3(filePath)}: ${e.message} -->
|
|
36494
|
+
`);
|
|
36495
|
+
}
|
|
36496
|
+
}
|
|
36497
|
+
}
|
|
36498
|
+
if (fileContents.length > 0) {
|
|
36499
|
+
text += "\n\n" + fileContents.join("\n");
|
|
36500
|
+
}
|
|
36501
|
+
const display = prompt;
|
|
36502
|
+
if (imageParts.length > 0) {
|
|
36503
|
+
const parts = [{ type: "text", text }];
|
|
36504
|
+
parts.push(...imageParts);
|
|
36505
|
+
return { content: parts, display };
|
|
36506
|
+
}
|
|
36507
|
+
return { content: text, display };
|
|
36508
|
+
}
|
|
36509
|
+
async function runPrintMode(opts2) {
|
|
36510
|
+
const startMs = Date.now();
|
|
36511
|
+
if (opts2.updateResult.hasUpdate) {
|
|
36512
|
+
process.stderr.write(
|
|
36513
|
+
`\x1B[33mkimiflare update available: ${opts2.updateResult.localVersion} \u2192 ${opts2.updateResult.latestVersion}\x1B[0m
|
|
36514
|
+
\x1B[33m npm update -g kimiflare then restart\x1B[0m
|
|
36515
|
+
|
|
36516
|
+
`
|
|
36517
|
+
);
|
|
36518
|
+
}
|
|
36519
|
+
const cwd = opts2.dir ? resolve4(opts2.dir) : process.cwd();
|
|
36520
|
+
const { HooksManager: HooksManager2 } = await Promise.resolve().then(() => (init_manager(), manager_exports));
|
|
36521
|
+
const hooks = new HooksManager2(cwd);
|
|
36522
|
+
const executor = new ToolExecutor(ALL_TOOLS, { hooks });
|
|
36523
|
+
const { sessionFile, isNew } = await resolveSession(opts2);
|
|
36524
|
+
if (opts2.title && isNew) {
|
|
36525
|
+
sessionFile.title = opts2.title;
|
|
36526
|
+
}
|
|
36527
|
+
const messages = [];
|
|
36528
|
+
if (isNew || sessionFile.messages.length === 0) {
|
|
36529
|
+
messages.push({ role: "system", content: buildSystemPrompt({ cwd, tools: ALL_TOOLS, model: opts2.model }) });
|
|
36530
|
+
} else {
|
|
36531
|
+
const nonSystem = sessionFile.messages.filter((m) => m.role !== "system");
|
|
36532
|
+
messages.push({ role: "system", content: buildSystemPrompt({ cwd, tools: ALL_TOOLS, model: opts2.model }) });
|
|
36533
|
+
messages.push(...nonSystem);
|
|
36534
|
+
}
|
|
36535
|
+
const files = opts2.files ? await resolveFiles(opts2.files, cwd) : [];
|
|
36536
|
+
const { content: userContent } = await buildUserMessage(opts2.prompt, files, cwd);
|
|
36537
|
+
messages.push({ role: "user", content: userContent });
|
|
36538
|
+
const controller = new AbortController();
|
|
36539
|
+
process.on("SIGINT", () => controller.abort());
|
|
36540
|
+
const format = opts2.format ?? "text";
|
|
36541
|
+
let printedReasoningHeader = false;
|
|
36542
|
+
let printedAnswerHeader = false;
|
|
36543
|
+
const jsonOutput = {
|
|
36544
|
+
text: "",
|
|
36545
|
+
toolCalls: [],
|
|
36546
|
+
toolResults: [],
|
|
36547
|
+
durationMs: 0,
|
|
36548
|
+
sessionId: sessionFile.id
|
|
36549
|
+
};
|
|
36550
|
+
function emitStreamJson(eventType, payload) {
|
|
36551
|
+
if (format === "stream-json") {
|
|
36552
|
+
process.stdout.write(JSON.stringify({ event: eventType, ...payload }) + "\n");
|
|
36553
|
+
}
|
|
36554
|
+
}
|
|
36555
|
+
const callbacks = {
|
|
36556
|
+
onReasoningDelta: opts2.showReasoning ? (delta) => {
|
|
36557
|
+
if (format === "text") {
|
|
36558
|
+
if (!printedReasoningHeader) {
|
|
36559
|
+
process.stderr.write("\x1B[2m--- reasoning ---\n");
|
|
36560
|
+
printedReasoningHeader = true;
|
|
36561
|
+
}
|
|
36562
|
+
process.stderr.write(delta);
|
|
36563
|
+
}
|
|
36564
|
+
} : void 0,
|
|
36565
|
+
onTextDelta: (delta) => {
|
|
36566
|
+
if (format === "text") {
|
|
36567
|
+
if (opts2.showReasoning && printedReasoningHeader && !printedAnswerHeader) {
|
|
36568
|
+
process.stderr.write("\n--- answer ---\x1B[0m\n");
|
|
36569
|
+
printedAnswerHeader = true;
|
|
36570
|
+
}
|
|
36571
|
+
process.stdout.write(delta);
|
|
36572
|
+
} else if (format === "json") {
|
|
36573
|
+
jsonOutput.text += delta;
|
|
36574
|
+
} else if (format === "stream-json") {
|
|
36575
|
+
emitStreamJson("text_delta", { delta });
|
|
36576
|
+
}
|
|
36577
|
+
},
|
|
36578
|
+
onToolCallFinalized: (call) => {
|
|
36579
|
+
if (format === "text") {
|
|
36580
|
+
process.stderr.write(`\x1B[2m[tool ${call.function.name}(${call.function.arguments})]\x1B[0m
|
|
36581
|
+
`);
|
|
36582
|
+
} else if (format === "json") {
|
|
36583
|
+
let args = {};
|
|
36584
|
+
try {
|
|
36585
|
+
args = JSON.parse(call.function.arguments);
|
|
36586
|
+
} catch {
|
|
36587
|
+
}
|
|
36588
|
+
jsonOutput.toolCalls.push({ id: call.id, name: call.function.name, arguments: args });
|
|
36589
|
+
} else if (format === "stream-json") {
|
|
36590
|
+
emitStreamJson("tool_call", { id: call.id, name: call.function.name, arguments: call.function.arguments });
|
|
36591
|
+
}
|
|
36592
|
+
},
|
|
36593
|
+
onToolResult: (result) => {
|
|
36594
|
+
if (format === "text") {
|
|
36595
|
+
const snippet = result.content.length > 400 ? result.content.slice(0, 400) + "..." : result.content;
|
|
36596
|
+
process.stderr.write(`\x1B[2m[result: ${snippet.replace(/\n/g, " \u23CE ")}]\x1B[0m
|
|
36597
|
+
`);
|
|
36598
|
+
} else if (format === "json") {
|
|
36599
|
+
jsonOutput.toolResults.push({
|
|
36600
|
+
toolCallId: result.tool_call_id,
|
|
36601
|
+
name: result.name,
|
|
36602
|
+
content: result.content,
|
|
36603
|
+
ok: result.ok
|
|
36604
|
+
});
|
|
36605
|
+
} else if (format === "stream-json") {
|
|
36606
|
+
emitStreamJson("tool_result", {
|
|
36607
|
+
toolCallId: result.tool_call_id,
|
|
36608
|
+
name: result.name,
|
|
36609
|
+
content: result.content,
|
|
36610
|
+
ok: result.ok
|
|
36611
|
+
});
|
|
36612
|
+
}
|
|
36613
|
+
},
|
|
36614
|
+
onUsage: (usage) => {
|
|
36615
|
+
if (format === "json") {
|
|
36616
|
+
jsonOutput.usage = {
|
|
36617
|
+
promptTokens: usage.prompt_tokens ?? 0,
|
|
36618
|
+
completionTokens: usage.completion_tokens ?? 0,
|
|
36619
|
+
totalTokens: usage.total_tokens ?? 0
|
|
36620
|
+
};
|
|
36621
|
+
} else if (format === "stream-json") {
|
|
36622
|
+
emitStreamJson("usage", {
|
|
36623
|
+
promptTokens: usage.prompt_tokens ?? 0,
|
|
36624
|
+
completionTokens: usage.completion_tokens ?? 0,
|
|
36625
|
+
totalTokens: usage.total_tokens ?? 0
|
|
36626
|
+
});
|
|
36627
|
+
}
|
|
36628
|
+
},
|
|
36629
|
+
onWarning: (msg) => {
|
|
36630
|
+
if (format === "text") {
|
|
36631
|
+
process.stderr.write(`\x1B[33mkimiflare: ${msg}\x1B[0m
|
|
36632
|
+
`);
|
|
36633
|
+
} else if (format === "stream-json") {
|
|
36634
|
+
emitStreamJson("warning", { message: msg });
|
|
36635
|
+
}
|
|
36636
|
+
},
|
|
36637
|
+
askPermission: async ({ tool, args }) => {
|
|
36638
|
+
if (opts2.allowAll) return "allow";
|
|
36639
|
+
if (opts2.permissions) {
|
|
36640
|
+
const rule = evaluatePermissionRules({ tool: tool.name, args, cwd }, opts2.permissions);
|
|
36641
|
+
if (rule === "allow") return "allow";
|
|
36642
|
+
if (rule === "deny") {
|
|
36643
|
+
const msg2 = `[permission denied by config rule: ${tool.name}(${JSON.stringify(args)})]`;
|
|
36644
|
+
if (format === "text") process.stderr.write(`\x1B[31m${msg2}\x1B[0m
|
|
36645
|
+
`);
|
|
36646
|
+
else if (format === "stream-json") emitStreamJson("permission_denied", { tool: tool.name, args, reason: "config_rule" });
|
|
36647
|
+
return "deny";
|
|
36648
|
+
}
|
|
36649
|
+
}
|
|
36650
|
+
const msg = `[permission denied: ${tool.name}(${JSON.stringify(args)}) \u2014 pass --dangerously-allow-all to approve in print mode, or configure permissions in config.json]`;
|
|
36651
|
+
if (format === "text") {
|
|
36652
|
+
process.stderr.write(`\x1B[31m${msg}\x1B[0m
|
|
36653
|
+
`);
|
|
36654
|
+
} else if (format === "stream-json") {
|
|
36655
|
+
emitStreamJson("permission_denied", { tool: tool.name, args });
|
|
36656
|
+
}
|
|
36657
|
+
return "deny";
|
|
36658
|
+
}
|
|
36659
|
+
};
|
|
36660
|
+
try {
|
|
36661
|
+
await runAgentTurn({
|
|
36662
|
+
accountId: opts2.accountId,
|
|
36663
|
+
apiToken: opts2.apiToken,
|
|
36664
|
+
model: opts2.model,
|
|
36665
|
+
gateway: gatewayFromPrintOpts(opts2),
|
|
36666
|
+
messages,
|
|
36667
|
+
tools: ALL_TOOLS,
|
|
36668
|
+
executor,
|
|
36669
|
+
hooks,
|
|
36670
|
+
cwd,
|
|
36671
|
+
signal: controller.signal,
|
|
36672
|
+
codeMode: opts2.codeMode,
|
|
36673
|
+
continueOnLimit: opts2.continueOnLimit,
|
|
36674
|
+
maxInputTokens: opts2.maxInputTokens,
|
|
36675
|
+
coauthor: opts2.coauthor !== false ? { name: opts2.coauthorName || "kimiflare", email: opts2.coauthorEmail || "kimiflare@proton.me" } : void 0,
|
|
36676
|
+
callbacks
|
|
36677
|
+
});
|
|
36678
|
+
} catch (err) {
|
|
36679
|
+
if (err instanceof BudgetExhaustedError) {
|
|
36680
|
+
const msg = "[Budget exhausted \u2014 exiting with code 42]";
|
|
36681
|
+
if (format === "text") process.stderr.write(`
|
|
36682
|
+
\x1B[33m${msg}\x1B[0m
|
|
36683
|
+
`);
|
|
36684
|
+
else if (format === "stream-json") emitStreamJson("error", { message: msg, code: 42 });
|
|
36685
|
+
process.exitCode = 42;
|
|
36686
|
+
return;
|
|
36687
|
+
}
|
|
36688
|
+
if (err instanceof AgentLoopError) {
|
|
36689
|
+
const msg = "[Agent loop detected \u2014 exiting with code 43]";
|
|
36690
|
+
if (format === "text") process.stderr.write(`
|
|
36691
|
+
\x1B[33m${msg}\x1B[0m
|
|
36692
|
+
`);
|
|
36693
|
+
else if (format === "stream-json") emitStreamJson("error", { message: msg, code: 43 });
|
|
36694
|
+
process.exitCode = 43;
|
|
36695
|
+
return;
|
|
36696
|
+
}
|
|
36697
|
+
if (err instanceof KimiApiError) {
|
|
36698
|
+
const msg = `Error: ${humanizeCloudflareError(err)}`;
|
|
36699
|
+
if (format === "text") process.stderr.write(`
|
|
36700
|
+
\x1B[31m${msg}\x1B[0m
|
|
36701
|
+
`);
|
|
36702
|
+
else if (format === "stream-json") emitStreamJson("error", { message: msg, code: 1 });
|
|
36703
|
+
process.exitCode = 1;
|
|
36704
|
+
return;
|
|
36705
|
+
}
|
|
36706
|
+
throw err;
|
|
36707
|
+
}
|
|
36708
|
+
sessionFile.messages = messages;
|
|
36709
|
+
sessionFile.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
36710
|
+
await saveSession(sessionFile);
|
|
36711
|
+
jsonOutput.durationMs = Date.now() - startMs;
|
|
36712
|
+
if (format === "text") {
|
|
36713
|
+
process.stdout.write("\n");
|
|
36714
|
+
} else if (format === "json") {
|
|
36715
|
+
process.stdout.write(JSON.stringify(jsonOutput, null, 2) + "\n");
|
|
36716
|
+
} else if (format === "stream-json") {
|
|
36717
|
+
emitStreamJson("done", { sessionId: sessionFile.id, durationMs: jsonOutput.durationMs });
|
|
36718
|
+
}
|
|
36719
|
+
}
|
|
36720
|
+
|
|
35464
36721
|
// src/index.tsx
|
|
35465
36722
|
var program = new Command2();
|
|
35466
|
-
program.name("kimiflare").description("Terminal coding agent powered by Kimi-K2.6 on Cloudflare Workers AI.").version(getAppVersion()).option("-p, --print <prompt>", "one-shot mode: send prompt, stream reply to stdout, exit").option("-m, --model <id>", "model id (defaults to @cf/moonshotai/kimi-k2.6)").option("--dangerously-allow-all", "auto-approve every permission prompt (print mode only)").option("--reasoning", "include reasoning in stdout (print mode only)").option("--continue-on-limit", "reset tool-call counter and continue when the 200-call limit is hit (print mode only)").option("--max-input-tokens <n>", "cumulative prompt token budget; exits 42 when exhausted (print mode only)", (v) => parseInt(v, 10)).option("--emit-events", "emit Camouflage NDJSON events to stdout; requires -p (for initial prompt)").option("--multi-turn", "with --emit-events: keep reading stdin for UserInputSubmitted follow-ups after the initial turn").option("--ui <name>", "render UI with the given engine: `ink` (default, stable) or `camouflage` (experimental Rust TUI). Can also be set via the KIMIFLARE_UI environment variable.").option("--camouflage-bin <path>", "with --ui camouflage: path to the camouflage-tui binary (defaults to PATH lookup)").option("--mode <mode>", "run mode: interactive (default), print, rpc");
|
|
36723
|
+
program.name("kimiflare").description("Terminal coding agent powered by Kimi-K2.6 on Cloudflare Workers AI.").version(getAppVersion()).option("-p, --print <prompt>", "one-shot mode: send prompt, stream reply to stdout, exit").option("-m, --model <id>", "model id (defaults to @cf/moonshotai/kimi-k2.6)").option("--dangerously-allow-all", "auto-approve every permission prompt (print mode only)").option("--reasoning", "include reasoning in stdout (print mode only)").option("--thinking", "alias for --reasoning").option("--continue-on-limit", "reset tool-call counter and continue when the 200-call limit is hit (print mode only)").option("--max-input-tokens <n>", "cumulative prompt token budget; exits 42 when exhausted (print mode only)", (v) => parseInt(v, 10)).option("--emit-events", "emit Camouflage NDJSON events to stdout; requires -p (for initial prompt)").option("--multi-turn", "with --emit-events: keep reading stdin for UserInputSubmitted follow-ups after the initial turn").option("--ui <name>", "render UI with the given engine: `ink` (default, stable) or `camouflage` (experimental Rust TUI). Can also be set via the KIMIFLARE_UI environment variable.").option("--camouflage-bin <path>", "with --ui camouflage: path to the camouflage-tui binary (defaults to PATH lookup)").option("--mode <mode>", "run mode: interactive (default), print, rpc").option("-c, --continue", "continue the most recent session in the current working directory (print mode only)").option("-S, --session <id>", "resume a specific session by id (print mode only)").option("-f, --file <path>", "attach file(s) to the prompt; repeatable, supports globs (print mode only)", (v, prev) => (prev ?? []).concat(v)).option("--format <mode>", "output format for print mode: text (default), json, stream-json").option("--dir <path>", "run in the specified directory instead of the current one (print mode only)").option("--title <title>", "override the auto-generated session title (print mode only)").option("--attach <url>", "attach to a running kimiflare serve instance (print mode only)");
|
|
35467
36724
|
program.command("cost").description("Show cost attribution by task type (requires costAttribution enabled)").option("-w, --week", "last 7 days (default)").option("-m, --month", "last 30 days").option("-d, --day", "today only").option("-s, --session <id>", "single session detail").option("-c, --category <name>", "filter by category").option("--json", "machine-readable output").option("--reclassify", "re-run classification on all sessions").option("--local-only", "skip Cloudflare reconciliation").action(async (cmdOpts) => {
|
|
35468
36725
|
const cfg = await loadConfig();
|
|
35469
36726
|
const enabled = cfg?.costAttribution ?? false;
|
|
@@ -35516,6 +36773,19 @@ Open: ${step.url}`);
|
|
|
35516
36773
|
}
|
|
35517
36774
|
})
|
|
35518
36775
|
);
|
|
36776
|
+
program.command("serve").description("Start a headless HTTP server for API access and CI integration").option("--port <n>", "port to listen on", (v) => parseInt(v, 10), 4096).option("--hostname <host>", "hostname to listen on", "127.0.0.1").action(async (cmdOpts) => {
|
|
36777
|
+
const cfg = await loadConfig();
|
|
36778
|
+
if (!cfg) {
|
|
36779
|
+
console.error("kimiflare serve: missing credentials.");
|
|
36780
|
+
process.exit(2);
|
|
36781
|
+
}
|
|
36782
|
+
const { startServer: startServer2 } = await Promise.resolve().then(() => (init_server(), server_exports));
|
|
36783
|
+
await startServer2({
|
|
36784
|
+
port: cmdOpts.port,
|
|
36785
|
+
hostname: cmdOpts.hostname,
|
|
36786
|
+
config: cfg
|
|
36787
|
+
});
|
|
36788
|
+
});
|
|
35519
36789
|
program.action(async () => {
|
|
35520
36790
|
await main();
|
|
35521
36791
|
});
|
|
@@ -35580,16 +36850,41 @@ async function main() {
|
|
|
35580
36850
|
process.exit(2);
|
|
35581
36851
|
}
|
|
35582
36852
|
const model = opts.model ?? cfg.model ?? DEFAULT_MODEL;
|
|
36853
|
+
const format = opts.format ?? "text";
|
|
36854
|
+
if (format !== "text" && format !== "json" && format !== "stream-json") {
|
|
36855
|
+
console.error(`kimiflare: invalid --format "${format}". Use: text, json, stream-json`);
|
|
36856
|
+
process.exit(2);
|
|
36857
|
+
}
|
|
36858
|
+
if (opts.attach) {
|
|
36859
|
+
const { runAttachMode: runAttachMode2 } = await Promise.resolve().then(() => (init_attach_mode(), attach_mode_exports));
|
|
36860
|
+
await runAttachMode2({
|
|
36861
|
+
attachUrl: opts.attach,
|
|
36862
|
+
prompt: opts.print,
|
|
36863
|
+
model,
|
|
36864
|
+
files: opts.file,
|
|
36865
|
+
format,
|
|
36866
|
+
allowAll: !!opts.dangerouslyAllowAll,
|
|
36867
|
+
sessionId: opts.session
|
|
36868
|
+
});
|
|
36869
|
+
return;
|
|
36870
|
+
}
|
|
35583
36871
|
await runPrintMode({
|
|
35584
36872
|
...cfg,
|
|
35585
36873
|
model,
|
|
35586
36874
|
prompt: opts.print,
|
|
35587
36875
|
allowAll: !!opts.dangerouslyAllowAll,
|
|
35588
|
-
showReasoning: !!opts.reasoning,
|
|
36876
|
+
showReasoning: !!(opts.reasoning || opts.thinking),
|
|
35589
36877
|
codeMode: cfg.codeMode,
|
|
35590
36878
|
continueOnLimit: !!opts.continueOnLimit,
|
|
35591
36879
|
maxInputTokens: opts.maxInputTokens,
|
|
35592
|
-
updateResult
|
|
36880
|
+
updateResult,
|
|
36881
|
+
continueSession: !!opts.continue,
|
|
36882
|
+
sessionId: opts.session,
|
|
36883
|
+
files: opts.file,
|
|
36884
|
+
format,
|
|
36885
|
+
dir: opts.dir,
|
|
36886
|
+
title: opts.title,
|
|
36887
|
+
permissions: cfg.permissions
|
|
35593
36888
|
});
|
|
35594
36889
|
return;
|
|
35595
36890
|
}
|
|
@@ -35646,112 +36941,4 @@ async function main() {
|
|
|
35646
36941
|
await renderApp2(null, updateResult, lspScope, lspProjectPath);
|
|
35647
36942
|
}
|
|
35648
36943
|
}
|
|
35649
|
-
function gatewayFromPrintOpts(opts2) {
|
|
35650
|
-
if (!opts2.aiGatewayId) return void 0;
|
|
35651
|
-
return {
|
|
35652
|
-
id: opts2.aiGatewayId,
|
|
35653
|
-
cacheTtl: opts2.aiGatewayCacheTtl,
|
|
35654
|
-
skipCache: opts2.aiGatewaySkipCache,
|
|
35655
|
-
collectLogPayload: opts2.aiGatewayCollectLogPayload,
|
|
35656
|
-
metadata: opts2.aiGatewayMetadata
|
|
35657
|
-
};
|
|
35658
|
-
}
|
|
35659
|
-
async function runPrintMode(opts2) {
|
|
35660
|
-
if (opts2.updateResult.hasUpdate) {
|
|
35661
|
-
process.stderr.write(
|
|
35662
|
-
`\x1B[33mkimiflare update available: ${opts2.updateResult.localVersion} \u2192 ${opts2.updateResult.latestVersion}\x1B[0m
|
|
35663
|
-
\x1B[33m npm update -g kimiflare then restart\x1B[0m
|
|
35664
|
-
|
|
35665
|
-
`
|
|
35666
|
-
);
|
|
35667
|
-
}
|
|
35668
|
-
const cwd = process.cwd();
|
|
35669
|
-
const { HooksManager: HooksManager2 } = await Promise.resolve().then(() => (init_manager3(), manager_exports));
|
|
35670
|
-
const hooks = new HooksManager2(cwd);
|
|
35671
|
-
const executor = new ToolExecutor(ALL_TOOLS, { hooks });
|
|
35672
|
-
const messages = [
|
|
35673
|
-
{ role: "system", content: buildSystemPrompt({ cwd, tools: ALL_TOOLS, model: opts2.model }) },
|
|
35674
|
-
{ role: "user", content: opts2.prompt }
|
|
35675
|
-
];
|
|
35676
|
-
const controller = new AbortController();
|
|
35677
|
-
process.on("SIGINT", () => controller.abort());
|
|
35678
|
-
let printedReasoningHeader = false;
|
|
35679
|
-
let printedAnswerHeader = false;
|
|
35680
|
-
try {
|
|
35681
|
-
await runAgentTurn({
|
|
35682
|
-
accountId: opts2.accountId,
|
|
35683
|
-
apiToken: opts2.apiToken,
|
|
35684
|
-
model: opts2.model,
|
|
35685
|
-
gateway: gatewayFromPrintOpts(opts2),
|
|
35686
|
-
messages,
|
|
35687
|
-
tools: ALL_TOOLS,
|
|
35688
|
-
executor,
|
|
35689
|
-
hooks,
|
|
35690
|
-
// M6.1: Stop fires at end of print-mode turn too.
|
|
35691
|
-
cwd,
|
|
35692
|
-
signal: controller.signal,
|
|
35693
|
-
codeMode: opts2.codeMode,
|
|
35694
|
-
continueOnLimit: opts2.continueOnLimit,
|
|
35695
|
-
maxInputTokens: opts2.maxInputTokens,
|
|
35696
|
-
coauthor: opts2.coauthor !== false ? { name: opts2.coauthorName || "kimiflare", email: opts2.coauthorEmail || "kimiflare@proton.me" } : void 0,
|
|
35697
|
-
callbacks: {
|
|
35698
|
-
onReasoningDelta: opts2.showReasoning ? (delta) => {
|
|
35699
|
-
if (!printedReasoningHeader) {
|
|
35700
|
-
process.stderr.write("\x1B[2m--- reasoning ---\n");
|
|
35701
|
-
printedReasoningHeader = true;
|
|
35702
|
-
}
|
|
35703
|
-
process.stderr.write(delta);
|
|
35704
|
-
} : void 0,
|
|
35705
|
-
onTextDelta: (delta) => {
|
|
35706
|
-
if (opts2.showReasoning && printedReasoningHeader && !printedAnswerHeader) {
|
|
35707
|
-
process.stderr.write("\n--- answer ---\x1B[0m\n");
|
|
35708
|
-
printedAnswerHeader = true;
|
|
35709
|
-
}
|
|
35710
|
-
process.stdout.write(delta);
|
|
35711
|
-
},
|
|
35712
|
-
onToolCallFinalized: (call) => {
|
|
35713
|
-
process.stderr.write(`\x1B[2m[tool ${call.function.name}(${call.function.arguments})]\x1B[0m
|
|
35714
|
-
`);
|
|
35715
|
-
},
|
|
35716
|
-
onToolResult: (result) => {
|
|
35717
|
-
const snippet = result.content.length > 400 ? result.content.slice(0, 400) + "..." : result.content;
|
|
35718
|
-
process.stderr.write(`\x1B[2m[result: ${snippet.replace(/\n/g, " \u23CE ")}]\x1B[0m
|
|
35719
|
-
`);
|
|
35720
|
-
},
|
|
35721
|
-
onWarning: (msg) => {
|
|
35722
|
-
process.stderr.write(`\x1B[33mkimiflare: ${msg}\x1B[0m
|
|
35723
|
-
`);
|
|
35724
|
-
},
|
|
35725
|
-
askPermission: async ({ tool, args }) => {
|
|
35726
|
-
if (opts2.allowAll) return "allow";
|
|
35727
|
-
process.stderr.write(
|
|
35728
|
-
`\x1B[31m[permission denied: ${tool.name}(${JSON.stringify(args)}) \u2014 pass --dangerously-allow-all to approve in print mode]\x1B[0m
|
|
35729
|
-
`
|
|
35730
|
-
);
|
|
35731
|
-
return "deny";
|
|
35732
|
-
}
|
|
35733
|
-
}
|
|
35734
|
-
});
|
|
35735
|
-
} catch (err) {
|
|
35736
|
-
if (err instanceof BudgetExhaustedError) {
|
|
35737
|
-
process.stderr.write("\n\x1B[33m[Budget exhausted \u2014 exiting with code 42]\x1B[0m\n");
|
|
35738
|
-
process.exitCode = 42;
|
|
35739
|
-
return;
|
|
35740
|
-
}
|
|
35741
|
-
if (err instanceof AgentLoopError) {
|
|
35742
|
-
process.stderr.write("\n\x1B[33m[Agent loop detected \u2014 exiting with code 43]\x1B[0m\n");
|
|
35743
|
-
process.exitCode = 43;
|
|
35744
|
-
return;
|
|
35745
|
-
}
|
|
35746
|
-
if (err instanceof KimiApiError) {
|
|
35747
|
-
process.stderr.write(`
|
|
35748
|
-
\x1B[31mError: ${humanizeCloudflareError(err)}\x1B[0m
|
|
35749
|
-
`);
|
|
35750
|
-
process.exitCode = 1;
|
|
35751
|
-
return;
|
|
35752
|
-
}
|
|
35753
|
-
throw err;
|
|
35754
|
-
}
|
|
35755
|
-
process.stdout.write("\n");
|
|
35756
|
-
}
|
|
35757
36944
|
//# sourceMappingURL=index.js.map
|