@lobehub/cli 0.0.14 → 0.0.15
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 +2086 -503
- package/man/man1/lh.1 +1 -4
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire } from "node:module";
|
|
3
3
|
import { EventEmitter } from "node:events";
|
|
4
|
-
import { execFile, spawn } from "node:child_process";
|
|
5
|
-
import
|
|
6
|
-
import path from "node:path";
|
|
4
|
+
import { execFile, execFileSync, spawn } from "node:child_process";
|
|
5
|
+
import path, { basename, extname } from "node:path";
|
|
7
6
|
import fs, { createReadStream, existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
8
|
-
import os, { tmpdir } from "node:os";
|
|
7
|
+
import os, { platform, tmpdir } from "node:os";
|
|
9
8
|
import crypto$1, { createHash, randomUUID } from "node:crypto";
|
|
10
9
|
import { createInterface } from "node:readline";
|
|
11
10
|
import { text } from "node:stream/consumers";
|
|
12
|
-
import fs$1, { access, mkdir, readFile, readdir, stat, writeFile } from "node:fs/promises";
|
|
11
|
+
import fs$1, { access, mkdir, open, readFile, readdir, stat, writeFile } from "node:fs/promises";
|
|
13
12
|
|
|
14
13
|
//#region \0rolldown/runtime.js
|
|
15
14
|
var __create$2 = Object.create;
|
|
@@ -1034,7 +1033,7 @@ var require_suggestSimilar = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
1034
1033
|
var require_command = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
1035
1034
|
const EventEmitter$7 = __require("node:events").EventEmitter;
|
|
1036
1035
|
const childProcess = __require("node:child_process");
|
|
1037
|
-
const path$
|
|
1036
|
+
const path$11 = __require("node:path");
|
|
1038
1037
|
const fs$12 = __require("node:fs");
|
|
1039
1038
|
const process$1 = __require("node:process");
|
|
1040
1039
|
const { Argument, humanReadableArgName } = require_argument();
|
|
@@ -1941,9 +1940,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
1941
1940
|
".cjs"
|
|
1942
1941
|
];
|
|
1943
1942
|
function findFile(baseDir, baseName) {
|
|
1944
|
-
const localBin = path$
|
|
1943
|
+
const localBin = path$11.resolve(baseDir, baseName);
|
|
1945
1944
|
if (fs$12.existsSync(localBin)) return localBin;
|
|
1946
|
-
if (sourceExt.includes(path$
|
|
1945
|
+
if (sourceExt.includes(path$11.extname(baseName))) return void 0;
|
|
1947
1946
|
const foundExt = sourceExt.find((ext) => fs$12.existsSync(`${localBin}${ext}`));
|
|
1948
1947
|
if (foundExt) return `${localBin}${foundExt}`;
|
|
1949
1948
|
}
|
|
@@ -1958,17 +1957,17 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
1958
1957
|
} catch {
|
|
1959
1958
|
resolvedScriptPath = this._scriptPath;
|
|
1960
1959
|
}
|
|
1961
|
-
executableDir = path$
|
|
1960
|
+
executableDir = path$11.resolve(path$11.dirname(resolvedScriptPath), executableDir);
|
|
1962
1961
|
}
|
|
1963
1962
|
if (executableDir) {
|
|
1964
1963
|
let localFile = findFile(executableDir, executableFile);
|
|
1965
1964
|
if (!localFile && !subcommand._executableFile && this._scriptPath) {
|
|
1966
|
-
const legacyName = path$
|
|
1965
|
+
const legacyName = path$11.basename(this._scriptPath, path$11.extname(this._scriptPath));
|
|
1967
1966
|
if (legacyName !== this._name) localFile = findFile(executableDir, `${legacyName}-${subcommand._name}`);
|
|
1968
1967
|
}
|
|
1969
1968
|
executableFile = localFile || executableFile;
|
|
1970
1969
|
}
|
|
1971
|
-
launchWithNode = sourceExt.includes(path$
|
|
1970
|
+
launchWithNode = sourceExt.includes(path$11.extname(executableFile));
|
|
1972
1971
|
let proc;
|
|
1973
1972
|
if (process$1.platform !== "win32") if (launchWithNode) {
|
|
1974
1973
|
args.unshift(executableFile);
|
|
@@ -2672,7 +2671,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
2672
2671
|
* @return {Command}
|
|
2673
2672
|
*/
|
|
2674
2673
|
nameFromFilename(filename) {
|
|
2675
|
-
this._name = path$
|
|
2674
|
+
this._name = path$11.basename(filename, path$11.extname(filename));
|
|
2676
2675
|
return this;
|
|
2677
2676
|
}
|
|
2678
2677
|
/**
|
|
@@ -6949,7 +6948,10 @@ function registerAgentCommand(program) {
|
|
|
6949
6948
|
return;
|
|
6950
6949
|
}
|
|
6951
6950
|
}
|
|
6952
|
-
const input = {
|
|
6951
|
+
const input = {
|
|
6952
|
+
prompt: options.prompt,
|
|
6953
|
+
trigger: "cli"
|
|
6954
|
+
};
|
|
6953
6955
|
if (options.agentId) input.agentId = options.agentId;
|
|
6954
6956
|
if (deviceId) input.deviceId = deviceId;
|
|
6955
6957
|
if (options.slug) input.slug = options.slug;
|
|
@@ -7346,8 +7348,7 @@ const DEFAULT_AGENT_SEARCH_FC_MODEL = {
|
|
|
7346
7348
|
provider: DEFAULT_PROVIDER
|
|
7347
7349
|
};
|
|
7348
7350
|
const DEFAULT_AGENT_CHAT_CONFIG = {
|
|
7349
|
-
|
|
7350
|
-
enableAutoCreateTopic: true,
|
|
7351
|
+
enableAgentMode: true,
|
|
7351
7352
|
enableCompressHistory: true,
|
|
7352
7353
|
enableContextCompression: true,
|
|
7353
7354
|
enableHistoryCount: false,
|
|
@@ -7414,7 +7415,6 @@ const KeyEnum = {
|
|
|
7414
7415
|
};
|
|
7415
7416
|
const HotkeyEnum = {
|
|
7416
7417
|
AddUserMessage: "addUserMessage",
|
|
7417
|
-
ClearCurrentMessages: "clearCurrentMessages",
|
|
7418
7418
|
CommandPalette: "commandPalette",
|
|
7419
7419
|
DeleteAndRegenerateMessage: "deleteAndRegenerateMessage",
|
|
7420
7420
|
DeleteLastMessage: "deleteLastMessage",
|
|
@@ -7543,16 +7543,6 @@ const HOTKEYS_REGISTRATION = [
|
|
|
7543
7543
|
nonEditable: true,
|
|
7544
7544
|
scopes: [HotkeyScopeEnum.Chat]
|
|
7545
7545
|
},
|
|
7546
|
-
{
|
|
7547
|
-
group: HotkeyGroupEnum.Conversation,
|
|
7548
|
-
id: HotkeyEnum.ClearCurrentMessages,
|
|
7549
|
-
keys: combineKeys([
|
|
7550
|
-
KeyEnum.Alt,
|
|
7551
|
-
KeyEnum.Shift,
|
|
7552
|
-
KeyEnum.Backspace
|
|
7553
|
-
]),
|
|
7554
|
-
scopes: [HotkeyScopeEnum.Chat]
|
|
7555
|
-
},
|
|
7556
7546
|
{
|
|
7557
7547
|
group: HotkeyGroupEnum.Essential,
|
|
7558
7548
|
id: HotkeyEnum.SaveDocument,
|
|
@@ -7589,6 +7579,29 @@ const DEFAULT_INPUT_COMPLETION_SYSTEM_AGENT_ITEM = {
|
|
|
7589
7579
|
//#region ../../packages/const/src/discover.ts
|
|
7590
7580
|
const DEFAULT_CREATED_AT = (/* @__PURE__ */ new Date()).toISOString();
|
|
7591
7581
|
|
|
7582
|
+
//#endregion
|
|
7583
|
+
//#region ../../packages/const/src/interests.ts
|
|
7584
|
+
const INTEREST_AREA_KEYS = [
|
|
7585
|
+
"writing",
|
|
7586
|
+
"coding",
|
|
7587
|
+
"design",
|
|
7588
|
+
"education",
|
|
7589
|
+
"business",
|
|
7590
|
+
"marketing",
|
|
7591
|
+
"product",
|
|
7592
|
+
"sales",
|
|
7593
|
+
"operations",
|
|
7594
|
+
"hr",
|
|
7595
|
+
"finance-legal",
|
|
7596
|
+
"creator",
|
|
7597
|
+
"investing",
|
|
7598
|
+
"parenting",
|
|
7599
|
+
"health",
|
|
7600
|
+
"hobbies",
|
|
7601
|
+
"personal"
|
|
7602
|
+
];
|
|
7603
|
+
const interestAreaKeySet = new Set(INTEREST_AREA_KEYS);
|
|
7604
|
+
|
|
7592
7605
|
//#endregion
|
|
7593
7606
|
//#region ../../../node_modules/.pnpm/react@19.2.4/node_modules/react/cjs/react-jsx-runtime.production.js
|
|
7594
7607
|
/**
|
|
@@ -10011,16 +10024,6 @@ const KLAVIS_SERVER_TYPES = [
|
|
|
10011
10024
|
label: "Google Calendar",
|
|
10012
10025
|
serverName: McpServerName.GoogleCalendar
|
|
10013
10026
|
},
|
|
10014
|
-
{
|
|
10015
|
-
author: "Klavis",
|
|
10016
|
-
authorUrl: "https://klavis.io",
|
|
10017
|
-
description: "Notion is a collaborative productivity and note-taking application",
|
|
10018
|
-
icon: "https://hub-apac-1.lobeobjects.space/assets/logos/notion.svg",
|
|
10019
|
-
identifier: "notion",
|
|
10020
|
-
readme: "Connect to Notion to access and manage your workspace. Create pages, search content, update databases, and organize your knowledge base—all through natural conversation with your AI assistant.",
|
|
10021
|
-
label: "Notion",
|
|
10022
|
-
serverName: McpServerName.Notion
|
|
10023
|
-
},
|
|
10024
10027
|
{
|
|
10025
10028
|
author: "Klavis",
|
|
10026
10029
|
authorUrl: "https://klavis.io",
|
|
@@ -10213,6 +10216,15 @@ const KLAVIS_SERVER_TYPES = [
|
|
|
10213
10216
|
}
|
|
10214
10217
|
];
|
|
10215
10218
|
|
|
10219
|
+
//#endregion
|
|
10220
|
+
//#region ../../package.json
|
|
10221
|
+
var version$3 = "2.2.0";
|
|
10222
|
+
|
|
10223
|
+
//#endregion
|
|
10224
|
+
//#region ../../packages/const/src/version.ts
|
|
10225
|
+
const CURRENT_VERSION = version$3;
|
|
10226
|
+
const isDesktop = typeof __ELECTRON__ !== "undefined" && !!__ELECTRON__;
|
|
10227
|
+
|
|
10216
10228
|
//#endregion
|
|
10217
10229
|
//#region ../../../node_modules/.pnpm/es-toolkit@1.46.1/node_modules/es-toolkit/dist/predicate/isLength.mjs
|
|
10218
10230
|
function isLength(value) {
|
|
@@ -10621,6 +10633,669 @@ const DEFAULT_INBOX_SESSION = merge(DEFAULT_AGENT_LOBE_SESSION, {
|
|
|
10621
10633
|
meta: { avatar: DEFAULT_INBOX_AVATAR }
|
|
10622
10634
|
});
|
|
10623
10635
|
|
|
10636
|
+
//#endregion
|
|
10637
|
+
//#region ../../packages/const/src/taskTemplate.ts
|
|
10638
|
+
const taskTemplates = [
|
|
10639
|
+
{
|
|
10640
|
+
id: "daily-topic-pick",
|
|
10641
|
+
category: "content-creation",
|
|
10642
|
+
cronPattern: "0 9 * * *",
|
|
10643
|
+
interests: ["writing"]
|
|
10644
|
+
},
|
|
10645
|
+
{
|
|
10646
|
+
id: "hot-topic-radar",
|
|
10647
|
+
category: "content-creation",
|
|
10648
|
+
cronPattern: "0 10 * * *",
|
|
10649
|
+
interests: ["writing"]
|
|
10650
|
+
},
|
|
10651
|
+
{
|
|
10652
|
+
id: "headline-inspiration",
|
|
10653
|
+
category: "content-creation",
|
|
10654
|
+
cronPattern: "0 10 * * *",
|
|
10655
|
+
interests: ["writing"]
|
|
10656
|
+
},
|
|
10657
|
+
{
|
|
10658
|
+
id: "viral-content-breakdown",
|
|
10659
|
+
category: "content-creation",
|
|
10660
|
+
cronPattern: "0 10 * * *",
|
|
10661
|
+
interests: ["writing"]
|
|
10662
|
+
},
|
|
10663
|
+
{
|
|
10664
|
+
id: "twitter-weekly-recap",
|
|
10665
|
+
category: "content-creation",
|
|
10666
|
+
cronPattern: "0 10 * * 1",
|
|
10667
|
+
interests: ["writing", "creator"],
|
|
10668
|
+
requiresSkills: [{
|
|
10669
|
+
provider: "twitter",
|
|
10670
|
+
source: "lobehub"
|
|
10671
|
+
}]
|
|
10672
|
+
},
|
|
10673
|
+
{
|
|
10674
|
+
id: "youtube-weekly-recap",
|
|
10675
|
+
category: "content-creation",
|
|
10676
|
+
cronPattern: "0 9 * * 1",
|
|
10677
|
+
interests: ["writing", "creator"],
|
|
10678
|
+
requiresSkills: [{
|
|
10679
|
+
provider: "youtube",
|
|
10680
|
+
source: "klavis"
|
|
10681
|
+
}]
|
|
10682
|
+
},
|
|
10683
|
+
{
|
|
10684
|
+
id: "competitor-creator-tracking",
|
|
10685
|
+
category: "content-creation",
|
|
10686
|
+
cronPattern: "0 9 * * *",
|
|
10687
|
+
interests: ["writing", "creator"]
|
|
10688
|
+
},
|
|
10689
|
+
{
|
|
10690
|
+
id: "content-calendar-weekly",
|
|
10691
|
+
category: "content-creation",
|
|
10692
|
+
cronPattern: "0 20 * * 0",
|
|
10693
|
+
interests: ["writing", "creator"],
|
|
10694
|
+
optionalSkills: [{
|
|
10695
|
+
provider: "notion",
|
|
10696
|
+
source: "lobehub"
|
|
10697
|
+
}]
|
|
10698
|
+
},
|
|
10699
|
+
{
|
|
10700
|
+
id: "oss-intel-daily",
|
|
10701
|
+
category: "engineering",
|
|
10702
|
+
cronPattern: "0 9 * * *",
|
|
10703
|
+
icon: "github",
|
|
10704
|
+
interests: ["coding"]
|
|
10705
|
+
},
|
|
10706
|
+
{
|
|
10707
|
+
id: "repo-health-weekly",
|
|
10708
|
+
category: "engineering",
|
|
10709
|
+
cronPattern: "0 9 * * 1",
|
|
10710
|
+
interests: ["coding"],
|
|
10711
|
+
requiresSkills: [{
|
|
10712
|
+
provider: "github",
|
|
10713
|
+
source: "lobehub"
|
|
10714
|
+
}]
|
|
10715
|
+
},
|
|
10716
|
+
{
|
|
10717
|
+
id: "dependency-security-weekly",
|
|
10718
|
+
category: "engineering",
|
|
10719
|
+
cronPattern: "0 10 * * 1",
|
|
10720
|
+
interests: ["coding"],
|
|
10721
|
+
requiresSkills: [{
|
|
10722
|
+
provider: "github",
|
|
10723
|
+
source: "lobehub"
|
|
10724
|
+
}]
|
|
10725
|
+
},
|
|
10726
|
+
{
|
|
10727
|
+
id: "vercel-health-weekly",
|
|
10728
|
+
category: "engineering",
|
|
10729
|
+
cronPattern: "0 10 * * 1",
|
|
10730
|
+
interests: ["coding"],
|
|
10731
|
+
requiresSkills: [{
|
|
10732
|
+
provider: "vercel",
|
|
10733
|
+
source: "lobehub"
|
|
10734
|
+
}]
|
|
10735
|
+
},
|
|
10736
|
+
{
|
|
10737
|
+
id: "linear-sprint-daily",
|
|
10738
|
+
category: "engineering",
|
|
10739
|
+
cronPattern: "30 8 * * *",
|
|
10740
|
+
interests: ["coding", "product"],
|
|
10741
|
+
requiresSkills: [{
|
|
10742
|
+
provider: "linear",
|
|
10743
|
+
source: "lobehub"
|
|
10744
|
+
}]
|
|
10745
|
+
},
|
|
10746
|
+
{
|
|
10747
|
+
id: "tech-trend-weekly",
|
|
10748
|
+
category: "engineering",
|
|
10749
|
+
cronPattern: "0 8 * * 1",
|
|
10750
|
+
interests: ["coding"]
|
|
10751
|
+
},
|
|
10752
|
+
{
|
|
10753
|
+
id: "keyword-tech-feed",
|
|
10754
|
+
category: "engineering",
|
|
10755
|
+
cronPattern: "0 10 * * *",
|
|
10756
|
+
interests: ["coding"]
|
|
10757
|
+
},
|
|
10758
|
+
{
|
|
10759
|
+
id: "daily-design-inspiration",
|
|
10760
|
+
category: "design",
|
|
10761
|
+
cronPattern: "0 9 * * *",
|
|
10762
|
+
interests: ["design"]
|
|
10763
|
+
},
|
|
10764
|
+
{
|
|
10765
|
+
id: "design-trend-weekly",
|
|
10766
|
+
category: "design",
|
|
10767
|
+
cronPattern: "0 9 * * 1",
|
|
10768
|
+
interests: ["design"]
|
|
10769
|
+
},
|
|
10770
|
+
{
|
|
10771
|
+
id: "figma-files-cleanup",
|
|
10772
|
+
category: "design",
|
|
10773
|
+
cronPattern: "0 17 * * 5",
|
|
10774
|
+
interests: ["design"],
|
|
10775
|
+
requiresSkills: [{
|
|
10776
|
+
provider: "figma",
|
|
10777
|
+
source: "klavis"
|
|
10778
|
+
}]
|
|
10779
|
+
},
|
|
10780
|
+
{
|
|
10781
|
+
id: "aigc-prompt-inspiration",
|
|
10782
|
+
category: "design",
|
|
10783
|
+
cronPattern: "0 10 * * *",
|
|
10784
|
+
interests: ["design"]
|
|
10785
|
+
},
|
|
10786
|
+
{
|
|
10787
|
+
id: "brand-watch-weekly",
|
|
10788
|
+
category: "design",
|
|
10789
|
+
cronPattern: "0 10 * * 1",
|
|
10790
|
+
interests: ["design"]
|
|
10791
|
+
},
|
|
10792
|
+
{
|
|
10793
|
+
id: "font-color-weekly",
|
|
10794
|
+
category: "design",
|
|
10795
|
+
cronPattern: "0 10 * * 3",
|
|
10796
|
+
interests: ["design"]
|
|
10797
|
+
},
|
|
10798
|
+
{
|
|
10799
|
+
id: "arxiv-curated-daily",
|
|
10800
|
+
category: "learning-research",
|
|
10801
|
+
cronPattern: "0 9 * * *",
|
|
10802
|
+
interests: ["education"]
|
|
10803
|
+
},
|
|
10804
|
+
{
|
|
10805
|
+
id: "must-read-papers-weekly",
|
|
10806
|
+
category: "learning-research",
|
|
10807
|
+
cronPattern: "0 20 * * 0",
|
|
10808
|
+
interests: ["education"]
|
|
10809
|
+
},
|
|
10810
|
+
{
|
|
10811
|
+
id: "language-morning-bite",
|
|
10812
|
+
category: "learning-research",
|
|
10813
|
+
cronPattern: "30 7 * * *",
|
|
10814
|
+
interests: ["education"]
|
|
10815
|
+
},
|
|
10816
|
+
{
|
|
10817
|
+
id: "industry-research-weekly",
|
|
10818
|
+
category: "learning-research",
|
|
10819
|
+
cronPattern: "0 9 * * 1",
|
|
10820
|
+
interests: ["education", "business"]
|
|
10821
|
+
},
|
|
10822
|
+
{
|
|
10823
|
+
id: "industry-morning-brief",
|
|
10824
|
+
category: "business",
|
|
10825
|
+
cronPattern: "0 8 * * *",
|
|
10826
|
+
interests: ["business"]
|
|
10827
|
+
},
|
|
10828
|
+
{
|
|
10829
|
+
id: "competitor-radar-daily",
|
|
10830
|
+
category: "business",
|
|
10831
|
+
cronPattern: "0 9 * * *",
|
|
10832
|
+
interests: ["business"]
|
|
10833
|
+
},
|
|
10834
|
+
{
|
|
10835
|
+
id: "funding-intel-daily",
|
|
10836
|
+
category: "business",
|
|
10837
|
+
cronPattern: "0 10 * * *",
|
|
10838
|
+
interests: ["business"]
|
|
10839
|
+
},
|
|
10840
|
+
{
|
|
10841
|
+
id: "macro-economy-weekly",
|
|
10842
|
+
category: "business",
|
|
10843
|
+
cronPattern: "0 8 * * 1",
|
|
10844
|
+
interests: ["business", "investing"]
|
|
10845
|
+
},
|
|
10846
|
+
{
|
|
10847
|
+
id: "weekly-meeting-brief",
|
|
10848
|
+
category: "business",
|
|
10849
|
+
cronPattern: "30 8 * * 1",
|
|
10850
|
+
interests: ["business"]
|
|
10851
|
+
},
|
|
10852
|
+
{
|
|
10853
|
+
id: "marketing-hot-radar",
|
|
10854
|
+
category: "marketing",
|
|
10855
|
+
cronPattern: "0 10 * * *",
|
|
10856
|
+
interests: ["marketing"]
|
|
10857
|
+
},
|
|
10858
|
+
{
|
|
10859
|
+
id: "ad-creative-inspiration",
|
|
10860
|
+
category: "marketing",
|
|
10861
|
+
cronPattern: "0 10 * * *",
|
|
10862
|
+
interests: ["marketing"]
|
|
10863
|
+
},
|
|
10864
|
+
{
|
|
10865
|
+
id: "brand-mention-daily",
|
|
10866
|
+
category: "marketing",
|
|
10867
|
+
cronPattern: "0 18 * * *",
|
|
10868
|
+
interests: ["marketing"],
|
|
10869
|
+
requiresSkills: [{
|
|
10870
|
+
provider: "twitter",
|
|
10871
|
+
source: "lobehub"
|
|
10872
|
+
}]
|
|
10873
|
+
},
|
|
10874
|
+
{
|
|
10875
|
+
id: "seo-weekly-report",
|
|
10876
|
+
category: "marketing",
|
|
10877
|
+
cronPattern: "0 9 * * 1",
|
|
10878
|
+
interests: ["marketing"]
|
|
10879
|
+
},
|
|
10880
|
+
{
|
|
10881
|
+
id: "newsletter-perf-weekly",
|
|
10882
|
+
category: "marketing",
|
|
10883
|
+
cronPattern: "0 10 * * 1",
|
|
10884
|
+
interests: ["marketing"],
|
|
10885
|
+
requiresSkills: [{
|
|
10886
|
+
provider: "gmail",
|
|
10887
|
+
source: "klavis"
|
|
10888
|
+
}]
|
|
10889
|
+
},
|
|
10890
|
+
{
|
|
10891
|
+
id: "kol-collab-calendar",
|
|
10892
|
+
category: "marketing",
|
|
10893
|
+
cronPattern: "0 9 * * 1",
|
|
10894
|
+
interests: ["marketing"],
|
|
10895
|
+
requiresSkills: [{
|
|
10896
|
+
provider: "airtable",
|
|
10897
|
+
source: "klavis"
|
|
10898
|
+
}]
|
|
10899
|
+
},
|
|
10900
|
+
{
|
|
10901
|
+
id: "hubspot-funnel-daily",
|
|
10902
|
+
category: "marketing",
|
|
10903
|
+
cronPattern: "0 9 * * *",
|
|
10904
|
+
interests: ["marketing", "sales"],
|
|
10905
|
+
requiresSkills: [{
|
|
10906
|
+
provider: "hubspot",
|
|
10907
|
+
source: "klavis"
|
|
10908
|
+
}]
|
|
10909
|
+
},
|
|
10910
|
+
{
|
|
10911
|
+
id: "user-feedback-daily",
|
|
10912
|
+
category: "product",
|
|
10913
|
+
cronPattern: "0 9 * * *",
|
|
10914
|
+
interests: ["product"]
|
|
10915
|
+
},
|
|
10916
|
+
{
|
|
10917
|
+
id: "competitor-update-daily",
|
|
10918
|
+
category: "product",
|
|
10919
|
+
cronPattern: "0 10 * * *",
|
|
10920
|
+
interests: ["product"]
|
|
10921
|
+
},
|
|
10922
|
+
{
|
|
10923
|
+
id: "standup-brief",
|
|
10924
|
+
category: "product",
|
|
10925
|
+
cronPattern: "30 8 * * *",
|
|
10926
|
+
interests: ["product"],
|
|
10927
|
+
requiresSkills: [{
|
|
10928
|
+
provider: "linear",
|
|
10929
|
+
source: "lobehub"
|
|
10930
|
+
}]
|
|
10931
|
+
},
|
|
10932
|
+
{
|
|
10933
|
+
id: "iteration-recap-weekly",
|
|
10934
|
+
category: "product",
|
|
10935
|
+
cronPattern: "0 17 * * 5",
|
|
10936
|
+
interests: ["product"],
|
|
10937
|
+
requiresSkills: [{
|
|
10938
|
+
provider: "linear",
|
|
10939
|
+
source: "lobehub"
|
|
10940
|
+
}]
|
|
10941
|
+
},
|
|
10942
|
+
{
|
|
10943
|
+
id: "core-metric-daily",
|
|
10944
|
+
category: "product",
|
|
10945
|
+
cronPattern: "0 9 * * *",
|
|
10946
|
+
interests: ["product"]
|
|
10947
|
+
},
|
|
10948
|
+
{
|
|
10949
|
+
id: "user-interview-schedule",
|
|
10950
|
+
category: "product",
|
|
10951
|
+
cronPattern: "0 9 * * 1",
|
|
10952
|
+
interests: ["product"],
|
|
10953
|
+
requiresSkills: [{
|
|
10954
|
+
provider: "google-calendar",
|
|
10955
|
+
source: "klavis"
|
|
10956
|
+
}]
|
|
10957
|
+
},
|
|
10958
|
+
{
|
|
10959
|
+
id: "prd-review-reminder",
|
|
10960
|
+
category: "product",
|
|
10961
|
+
cronPattern: "0 15 * * 5",
|
|
10962
|
+
interests: ["product"],
|
|
10963
|
+
requiresSkills: [{
|
|
10964
|
+
provider: "notion",
|
|
10965
|
+
source: "lobehub"
|
|
10966
|
+
}]
|
|
10967
|
+
},
|
|
10968
|
+
{
|
|
10969
|
+
id: "daily-followup-list",
|
|
10970
|
+
category: "sales-customer",
|
|
10971
|
+
cronPattern: "0 9 * * *",
|
|
10972
|
+
interests: ["sales"],
|
|
10973
|
+
requiresSkills: [{
|
|
10974
|
+
provider: "hubspot",
|
|
10975
|
+
source: "klavis"
|
|
10976
|
+
}]
|
|
10977
|
+
},
|
|
10978
|
+
{
|
|
10979
|
+
id: "renewal-risk-weekly",
|
|
10980
|
+
category: "sales-customer",
|
|
10981
|
+
cronPattern: "0 9 * * 1",
|
|
10982
|
+
interests: ["sales"],
|
|
10983
|
+
requiresSkills: [{
|
|
10984
|
+
provider: "hubspot",
|
|
10985
|
+
source: "klavis"
|
|
10986
|
+
}]
|
|
10987
|
+
},
|
|
10988
|
+
{
|
|
10989
|
+
id: "deal-pipeline-weekly",
|
|
10990
|
+
category: "sales-customer",
|
|
10991
|
+
cronPattern: "0 16 * * 5",
|
|
10992
|
+
interests: ["sales"],
|
|
10993
|
+
requiresSkills: [{
|
|
10994
|
+
provider: "hubspot",
|
|
10995
|
+
source: "klavis"
|
|
10996
|
+
}]
|
|
10997
|
+
},
|
|
10998
|
+
{
|
|
10999
|
+
id: "key-account-radar",
|
|
11000
|
+
category: "sales-customer",
|
|
11001
|
+
cronPattern: "0 9 * * *",
|
|
11002
|
+
interests: ["sales"]
|
|
11003
|
+
},
|
|
11004
|
+
{
|
|
11005
|
+
id: "zendesk-ticket-daily",
|
|
11006
|
+
category: "sales-customer",
|
|
11007
|
+
cronPattern: "0 9 * * *",
|
|
11008
|
+
interests: ["sales"],
|
|
11009
|
+
requiresSkills: [{
|
|
11010
|
+
provider: "zendesk",
|
|
11011
|
+
source: "klavis"
|
|
11012
|
+
}]
|
|
11013
|
+
},
|
|
11014
|
+
{
|
|
11015
|
+
id: "morning-brief",
|
|
11016
|
+
category: "operations",
|
|
11017
|
+
cronPattern: "0 8 * * *",
|
|
11018
|
+
interests: ["operations"],
|
|
11019
|
+
requiresSkills: [{
|
|
11020
|
+
provider: "google-calendar",
|
|
11021
|
+
source: "klavis"
|
|
11022
|
+
}]
|
|
11023
|
+
},
|
|
11024
|
+
{
|
|
11025
|
+
id: "meeting-brief",
|
|
11026
|
+
category: "operations",
|
|
11027
|
+
cronPattern: "30 8 * * *",
|
|
11028
|
+
interests: ["operations"],
|
|
11029
|
+
requiresSkills: [{
|
|
11030
|
+
provider: "google-calendar",
|
|
11031
|
+
source: "klavis"
|
|
11032
|
+
}]
|
|
11033
|
+
},
|
|
11034
|
+
{
|
|
11035
|
+
id: "calendar-conflict-check",
|
|
11036
|
+
category: "operations",
|
|
11037
|
+
cronPattern: "30 7 * * *",
|
|
11038
|
+
interests: ["operations"],
|
|
11039
|
+
requiresSkills: [{
|
|
11040
|
+
provider: "google-calendar",
|
|
11041
|
+
source: "klavis"
|
|
11042
|
+
}]
|
|
11043
|
+
},
|
|
11044
|
+
{
|
|
11045
|
+
id: "friday-wrap-list",
|
|
11046
|
+
category: "operations",
|
|
11047
|
+
cronPattern: "0 16 * * 5",
|
|
11048
|
+
interests: ["operations"],
|
|
11049
|
+
requiresSkills: [{
|
|
11050
|
+
provider: "linear",
|
|
11051
|
+
source: "lobehub"
|
|
11052
|
+
}]
|
|
11053
|
+
},
|
|
11054
|
+
{
|
|
11055
|
+
id: "recruit-funnel-daily",
|
|
11056
|
+
category: "hr",
|
|
11057
|
+
cronPattern: "0 9 * * *",
|
|
11058
|
+
interests: ["hr"],
|
|
11059
|
+
requiresSkills: [{
|
|
11060
|
+
provider: "airtable",
|
|
11061
|
+
source: "klavis"
|
|
11062
|
+
}]
|
|
11063
|
+
},
|
|
11064
|
+
{
|
|
11065
|
+
id: "onboarding-buddy-weekly",
|
|
11066
|
+
category: "hr",
|
|
11067
|
+
cronPattern: "0 9 * * 1",
|
|
11068
|
+
interests: ["hr"],
|
|
11069
|
+
requiresSkills: [{
|
|
11070
|
+
provider: "notion",
|
|
11071
|
+
source: "lobehub"
|
|
11072
|
+
}]
|
|
11073
|
+
},
|
|
11074
|
+
{
|
|
11075
|
+
id: "team-status-weekly",
|
|
11076
|
+
category: "hr",
|
|
11077
|
+
cronPattern: "0 9 * * 1",
|
|
11078
|
+
interests: ["hr"],
|
|
11079
|
+
requiresSkills: [{
|
|
11080
|
+
provider: "google-calendar",
|
|
11081
|
+
source: "klavis"
|
|
11082
|
+
}]
|
|
11083
|
+
},
|
|
11084
|
+
{
|
|
11085
|
+
id: "precious-metals-daily",
|
|
11086
|
+
category: "finance-legal",
|
|
11087
|
+
cronPattern: "0 16 * * *",
|
|
11088
|
+
interests: ["finance-legal", "investing"]
|
|
11089
|
+
},
|
|
11090
|
+
{
|
|
11091
|
+
id: "pre-market-brief",
|
|
11092
|
+
category: "finance-legal",
|
|
11093
|
+
cronPattern: "0 9 * * *",
|
|
11094
|
+
interests: ["finance-legal", "investing"]
|
|
11095
|
+
},
|
|
11096
|
+
{
|
|
11097
|
+
id: "cashflow-weekly",
|
|
11098
|
+
category: "finance-legal",
|
|
11099
|
+
cronPattern: "0 9 * * 1",
|
|
11100
|
+
interests: ["finance-legal"],
|
|
11101
|
+
requiresSkills: [{
|
|
11102
|
+
provider: "airtable",
|
|
11103
|
+
source: "klavis"
|
|
11104
|
+
}]
|
|
11105
|
+
},
|
|
11106
|
+
{
|
|
11107
|
+
id: "contract-expiry-weekly",
|
|
11108
|
+
category: "finance-legal",
|
|
11109
|
+
cronPattern: "0 9 * * 1",
|
|
11110
|
+
interests: ["finance-legal"],
|
|
11111
|
+
requiresSkills: [{
|
|
11112
|
+
provider: "notion",
|
|
11113
|
+
source: "lobehub"
|
|
11114
|
+
}]
|
|
11115
|
+
},
|
|
11116
|
+
{
|
|
11117
|
+
id: "regulation-watch-weekly",
|
|
11118
|
+
category: "finance-legal",
|
|
11119
|
+
cronPattern: "0 10 * * 1",
|
|
11120
|
+
interests: ["finance-legal"]
|
|
11121
|
+
},
|
|
11122
|
+
{
|
|
11123
|
+
id: "invoice-collection-daily",
|
|
11124
|
+
category: "finance-legal",
|
|
11125
|
+
cronPattern: "0 10 * * *",
|
|
11126
|
+
interests: ["finance-legal"],
|
|
11127
|
+
requiresSkills: [{
|
|
11128
|
+
provider: "gmail",
|
|
11129
|
+
source: "klavis"
|
|
11130
|
+
}]
|
|
11131
|
+
},
|
|
11132
|
+
{
|
|
11133
|
+
id: "cross-platform-engagement-daily",
|
|
11134
|
+
category: "creator",
|
|
11135
|
+
cronPattern: "0 9 * * *",
|
|
11136
|
+
interests: ["creator"],
|
|
11137
|
+
requiresSkills: [{
|
|
11138
|
+
provider: "twitter",
|
|
11139
|
+
source: "lobehub"
|
|
11140
|
+
}]
|
|
11141
|
+
},
|
|
11142
|
+
{
|
|
11143
|
+
id: "brand-collab-weekly",
|
|
11144
|
+
category: "creator",
|
|
11145
|
+
cronPattern: "0 10 * * 1",
|
|
11146
|
+
interests: ["creator"]
|
|
11147
|
+
},
|
|
11148
|
+
{
|
|
11149
|
+
id: "follower-growth-weekly",
|
|
11150
|
+
category: "creator",
|
|
11151
|
+
cronPattern: "0 10 * * 1",
|
|
11152
|
+
interests: ["creator"],
|
|
11153
|
+
requiresSkills: [{
|
|
11154
|
+
provider: "twitter",
|
|
11155
|
+
source: "lobehub"
|
|
11156
|
+
}]
|
|
11157
|
+
},
|
|
11158
|
+
{
|
|
11159
|
+
id: "youtube-channel-weekly",
|
|
11160
|
+
category: "creator",
|
|
11161
|
+
cronPattern: "0 9 * * 1",
|
|
11162
|
+
interests: ["creator"],
|
|
11163
|
+
requiresSkills: [{
|
|
11164
|
+
provider: "youtube",
|
|
11165
|
+
source: "klavis"
|
|
11166
|
+
}]
|
|
11167
|
+
},
|
|
11168
|
+
{
|
|
11169
|
+
id: "monetization-opportunity-weekly",
|
|
11170
|
+
category: "creator",
|
|
11171
|
+
cronPattern: "0 10 * * 3",
|
|
11172
|
+
interests: ["creator"]
|
|
11173
|
+
},
|
|
11174
|
+
{
|
|
11175
|
+
id: "portfolio-daily",
|
|
11176
|
+
category: "investing",
|
|
11177
|
+
cronPattern: "0 16 * * *",
|
|
11178
|
+
interests: ["investing"]
|
|
11179
|
+
},
|
|
11180
|
+
{
|
|
11181
|
+
id: "crypto-market-daily",
|
|
11182
|
+
category: "investing",
|
|
11183
|
+
cronPattern: "0 9 * * *",
|
|
11184
|
+
interests: ["investing"]
|
|
11185
|
+
},
|
|
11186
|
+
{
|
|
11187
|
+
id: "child-growth-weekly",
|
|
11188
|
+
category: "parenting",
|
|
11189
|
+
cronPattern: "0 9 * * 1",
|
|
11190
|
+
interests: ["parenting"]
|
|
11191
|
+
},
|
|
11192
|
+
{
|
|
11193
|
+
id: "child-study-weekly",
|
|
11194
|
+
category: "parenting",
|
|
11195
|
+
cronPattern: "0 20 * * 0",
|
|
11196
|
+
interests: ["parenting", "education"]
|
|
11197
|
+
},
|
|
11198
|
+
{
|
|
11199
|
+
id: "family-finance-weekly",
|
|
11200
|
+
category: "parenting",
|
|
11201
|
+
cronPattern: "0 20 * * 0",
|
|
11202
|
+
interests: ["parenting", "finance-legal"],
|
|
11203
|
+
requiresSkills: [{
|
|
11204
|
+
provider: "google-sheets",
|
|
11205
|
+
source: "klavis"
|
|
11206
|
+
}]
|
|
11207
|
+
},
|
|
11208
|
+
{
|
|
11209
|
+
id: "family-task-schedule",
|
|
11210
|
+
category: "parenting",
|
|
11211
|
+
cronPattern: "0 8 * * 1",
|
|
11212
|
+
interests: ["parenting"],
|
|
11213
|
+
optionalSkills: [{
|
|
11214
|
+
provider: "google-calendar",
|
|
11215
|
+
source: "klavis"
|
|
11216
|
+
}]
|
|
11217
|
+
},
|
|
11218
|
+
{
|
|
11219
|
+
id: "diet-log-companion",
|
|
11220
|
+
category: "health",
|
|
11221
|
+
cronPattern: "0 21 * * *",
|
|
11222
|
+
interests: ["health"]
|
|
11223
|
+
},
|
|
11224
|
+
{
|
|
11225
|
+
id: "podcast-new-episodes",
|
|
11226
|
+
category: "hobbies",
|
|
11227
|
+
cronPattern: "0 9 * * 1",
|
|
11228
|
+
interests: ["hobbies"]
|
|
11229
|
+
},
|
|
11230
|
+
{
|
|
11231
|
+
id: "newsletter-aggregator",
|
|
11232
|
+
category: "hobbies",
|
|
11233
|
+
cronPattern: "0 20 * * 0",
|
|
11234
|
+
interests: ["hobbies"],
|
|
11235
|
+
requiresSkills: [{
|
|
11236
|
+
provider: "gmail",
|
|
11237
|
+
source: "klavis"
|
|
11238
|
+
}]
|
|
11239
|
+
},
|
|
11240
|
+
{
|
|
11241
|
+
id: "series-update-weekly",
|
|
11242
|
+
category: "hobbies",
|
|
11243
|
+
cronPattern: "0 9 * * 1",
|
|
11244
|
+
interests: ["hobbies"]
|
|
11245
|
+
},
|
|
11246
|
+
{
|
|
11247
|
+
id: "travel-inspiration-weekly",
|
|
11248
|
+
category: "hobbies",
|
|
11249
|
+
cronPattern: "0 10 * * 3",
|
|
11250
|
+
interests: ["hobbies"]
|
|
11251
|
+
},
|
|
11252
|
+
{
|
|
11253
|
+
id: "watchlist-friday",
|
|
11254
|
+
category: "hobbies",
|
|
11255
|
+
cronPattern: "0 18 * * 5",
|
|
11256
|
+
interests: ["hobbies"]
|
|
11257
|
+
},
|
|
11258
|
+
{
|
|
11259
|
+
id: "exhibition-event-weekly",
|
|
11260
|
+
category: "hobbies",
|
|
11261
|
+
cronPattern: "0 10 * * 1",
|
|
11262
|
+
interests: ["hobbies"]
|
|
11263
|
+
},
|
|
11264
|
+
{
|
|
11265
|
+
id: "daily-learning-bite",
|
|
11266
|
+
category: "personal-life",
|
|
11267
|
+
cronPattern: "30 7 * * *",
|
|
11268
|
+
interests: ["education", "personal"]
|
|
11269
|
+
},
|
|
11270
|
+
{
|
|
11271
|
+
id: "sunday-reflection",
|
|
11272
|
+
category: "personal-life",
|
|
11273
|
+
cronPattern: "0 21 * * 0",
|
|
11274
|
+
interests: ["personal"]
|
|
11275
|
+
},
|
|
11276
|
+
{
|
|
11277
|
+
id: "morning-ritual",
|
|
11278
|
+
category: "personal-life",
|
|
11279
|
+
cronPattern: "0 7 * * *",
|
|
11280
|
+
interests: ["personal"],
|
|
11281
|
+
optionalSkills: [{
|
|
11282
|
+
provider: "google-calendar",
|
|
11283
|
+
source: "klavis"
|
|
11284
|
+
}]
|
|
11285
|
+
},
|
|
11286
|
+
{
|
|
11287
|
+
id: "bedtime-gratitude",
|
|
11288
|
+
category: "personal-life",
|
|
11289
|
+
cronPattern: "0 22 * * *",
|
|
11290
|
+
interests: ["personal"],
|
|
11291
|
+
optionalSkills: [{
|
|
11292
|
+
provider: "notion",
|
|
11293
|
+
source: "lobehub"
|
|
11294
|
+
}]
|
|
11295
|
+
}
|
|
11296
|
+
];
|
|
11297
|
+
const KNOWN_TASK_TEMPLATE_IDS = new Set(taskTemplates.map((t) => t.id));
|
|
11298
|
+
|
|
10624
11299
|
//#endregion
|
|
10625
11300
|
//#region ../../packages/const/src/url.ts
|
|
10626
11301
|
const isDev = process.env.NODE_ENV === "development";
|
|
@@ -10671,21 +11346,74 @@ const MEMORY_SEARCH_TOP_K_LIMITS = {
|
|
|
10671
11346
|
medium: { ...DEFAULT_SEARCH_USER_MEMORY_TOP_K }
|
|
10672
11347
|
};
|
|
10673
11348
|
|
|
10674
|
-
//#endregion
|
|
10675
|
-
//#region ../../package.json
|
|
10676
|
-
var version$3 = "2.1.56";
|
|
10677
|
-
|
|
10678
|
-
//#endregion
|
|
10679
|
-
//#region ../../packages/const/src/version.ts
|
|
10680
|
-
const CURRENT_VERSION = version$3;
|
|
10681
|
-
const isDesktop = typeof __ELECTRON__ !== "undefined" && !!__ELECTRON__;
|
|
10682
|
-
|
|
10683
11349
|
//#endregion
|
|
10684
11350
|
//#region src/commands/botMessage.ts
|
|
11351
|
+
const MIME_EXT_MAP = {
|
|
11352
|
+
".bmp": "image/bmp",
|
|
11353
|
+
".gif": "image/gif",
|
|
11354
|
+
".jpeg": "image/jpeg",
|
|
11355
|
+
".jpg": "image/jpeg",
|
|
11356
|
+
".m4a": "audio/mp4",
|
|
11357
|
+
".mp3": "audio/mpeg",
|
|
11358
|
+
".mp4": "video/mp4",
|
|
11359
|
+
".ogg": "audio/ogg",
|
|
11360
|
+
".pdf": "application/pdf",
|
|
11361
|
+
".png": "image/png",
|
|
11362
|
+
".svg": "image/svg+xml",
|
|
11363
|
+
".txt": "text/plain",
|
|
11364
|
+
".wav": "audio/wav",
|
|
11365
|
+
".webm": "video/webm",
|
|
11366
|
+
".webp": "image/webp"
|
|
11367
|
+
};
|
|
11368
|
+
const inferMime = (path) => MIME_EXT_MAP[extname(path).toLowerCase()];
|
|
11369
|
+
const inferAttachmentType = (mimeType) => {
|
|
11370
|
+
if (!mimeType) return "file";
|
|
11371
|
+
if (mimeType.startsWith("image/")) return "image";
|
|
11372
|
+
if (mimeType.startsWith("video/")) return "video";
|
|
11373
|
+
if (mimeType.startsWith("audio/")) return "audio";
|
|
11374
|
+
return "file";
|
|
11375
|
+
};
|
|
11376
|
+
/**
|
|
11377
|
+
* Parse a single `--attachment <value>` argument. Accepted forms:
|
|
11378
|
+
* - `https://…` / `http://…` → fetchUrl, type inferred from extension
|
|
11379
|
+
* - any other string → treated as a local file path;
|
|
11380
|
+
* bytes are read + base64-encoded
|
|
11381
|
+
*/
|
|
11382
|
+
const parseAttachmentArg = async (raw) => {
|
|
11383
|
+
if (/^https?:\/\//.test(raw)) {
|
|
11384
|
+
const pathname = new URL(raw).pathname;
|
|
11385
|
+
const mimeType = inferMime(pathname);
|
|
11386
|
+
return {
|
|
11387
|
+
fetchUrl: raw,
|
|
11388
|
+
mimeType,
|
|
11389
|
+
name: basename(pathname) || void 0,
|
|
11390
|
+
type: inferAttachmentType(mimeType)
|
|
11391
|
+
};
|
|
11392
|
+
}
|
|
11393
|
+
const bytes = await readFile(raw);
|
|
11394
|
+
const mimeType = inferMime(raw);
|
|
11395
|
+
return {
|
|
11396
|
+
data: bytes.toString("base64"),
|
|
11397
|
+
mimeType,
|
|
11398
|
+
name: basename(raw),
|
|
11399
|
+
type: inferAttachmentType(mimeType)
|
|
11400
|
+
};
|
|
11401
|
+
};
|
|
10685
11402
|
function registerBotMessageCommands(bot) {
|
|
10686
11403
|
const message = bot.command("message").description("Send and manage messages on connected platforms");
|
|
10687
|
-
message.command("send <botId>").description("Send a message to a channel").requiredOption("--target <channelId>", "Target channel / conversation ID").requiredOption("--message <text>", "Message content").option("--reply-to <messageId>", "Reply to a specific message").option("--json", "Output JSON").action(async (botId, options) => {
|
|
11404
|
+
message.command("send <botId>").description("Send a message to a channel").requiredOption("--target <channelId>", "Target channel / conversation ID").requiredOption("--message <text>", "Message content").option("--attachment <pathOrUrl>", "Attach a file by local path or remote URL (repeatable). Local paths are base64-encoded; http(s) URLs are passed as fetchUrl.", collectOptions, []).option("--reply-to <messageId>", "Reply to a specific message").option("--json", "Output JSON").action(async (botId, options) => {
|
|
11405
|
+
let attachments;
|
|
11406
|
+
if (options.attachment.length > 0) {
|
|
11407
|
+
attachments = [];
|
|
11408
|
+
for (const raw of options.attachment) try {
|
|
11409
|
+
attachments.push(await parseAttachmentArg(raw));
|
|
11410
|
+
} catch (error) {
|
|
11411
|
+
log$7.error(`Failed to load attachment "${raw}": ${error.message}`);
|
|
11412
|
+
process.exit(1);
|
|
11413
|
+
}
|
|
11414
|
+
}
|
|
10688
11415
|
const result = await (await getTrpcClient()).botMessage.sendMessage.mutate({
|
|
11416
|
+
attachments,
|
|
10689
11417
|
botId,
|
|
10690
11418
|
channelId: options.target,
|
|
10691
11419
|
content: options.message,
|
|
@@ -10696,7 +11424,8 @@ function registerBotMessageCommands(bot) {
|
|
|
10696
11424
|
return;
|
|
10697
11425
|
}
|
|
10698
11426
|
const r = result;
|
|
10699
|
-
|
|
11427
|
+
const suffix = attachments?.length ? ` with ${attachments.length} attachment(s)` : "";
|
|
11428
|
+
console.log(`${import_picocolors.default.green("✓")} Message sent${r.messageId ? ` (${import_picocolors.default.dim(r.messageId)})` : ""}${suffix}`);
|
|
10700
11429
|
});
|
|
10701
11430
|
message.command("read <botId>").description("Read messages from a channel").requiredOption("--target <channelId>", "Target channel / conversation ID").option("--limit <n>", "Max messages to fetch", String(50)).option("--before <messageId>", "Read messages before this ID").option("--after <messageId>", "Read messages after this ID").option("--start-time <timestamp>", "Start time as Unix seconds (Feishu/Lark)").option("--end-time <timestamp>", "End time as Unix seconds (Feishu/Lark)").option("--cursor <token>", "Pagination cursor from a previous response (Feishu/Lark)").option("--json", "Output JSON").action(async (botId, options) => {
|
|
10702
11431
|
const result = await (await getTrpcClient()).botMessage.readMessages.query({
|
|
@@ -11144,6 +11873,134 @@ function registerAllowlistCommand(bot, opts) {
|
|
|
11144
11873
|
console.log(`${import_picocolors.default.green("✓")} Cleared ${opts.fieldKey} on bot ${import_picocolors.default.bold(botId)}`);
|
|
11145
11874
|
});
|
|
11146
11875
|
}
|
|
11876
|
+
/**
|
|
11877
|
+
* Normalise `settings.watchKeywords` into the canonical
|
|
11878
|
+
* `{keyword, instruction?}[]` shape. Mirrors `extractWatchKeywordEntries`
|
|
11879
|
+
* in `src/server/services/bot/platforms/const.ts` so the CLI accepts the
|
|
11880
|
+
* same legacy on-disk shapes (`string`, `string[]`, `{keyword, …}[]`)
|
|
11881
|
+
* the runtime is forgiving about — including the rare comma/whitespace
|
|
11882
|
+
* separated string from a hand-pasted upgrade.
|
|
11883
|
+
*/
|
|
11884
|
+
function normalizeWatchKeywords(raw) {
|
|
11885
|
+
const push = (out, keyword, instruction) => {
|
|
11886
|
+
if (typeof keyword !== "string") return;
|
|
11887
|
+
const normalised = keyword.trim().toLowerCase();
|
|
11888
|
+
if (!normalised) return;
|
|
11889
|
+
const trimmedInstruction = typeof instruction === "string" && instruction.trim() ? instruction.trim() : void 0;
|
|
11890
|
+
const existing = out.get(normalised);
|
|
11891
|
+
if (!existing) {
|
|
11892
|
+
out.set(normalised, {
|
|
11893
|
+
instruction: trimmedInstruction,
|
|
11894
|
+
keyword: normalised
|
|
11895
|
+
});
|
|
11896
|
+
return;
|
|
11897
|
+
}
|
|
11898
|
+
if (!existing.instruction && trimmedInstruction) existing.instruction = trimmedInstruction;
|
|
11899
|
+
};
|
|
11900
|
+
const collected = /* @__PURE__ */ new Map();
|
|
11901
|
+
if (typeof raw === "string") for (const piece of raw.split(/[\s,]+/)) push(collected, piece);
|
|
11902
|
+
else if (Array.isArray(raw)) for (const entry of raw) {
|
|
11903
|
+
if (typeof entry === "string") {
|
|
11904
|
+
push(collected, entry);
|
|
11905
|
+
continue;
|
|
11906
|
+
}
|
|
11907
|
+
if (entry && typeof entry === "object" && "keyword" in entry) {
|
|
11908
|
+
const obj = entry;
|
|
11909
|
+
push(collected, obj.keyword, obj.instruction);
|
|
11910
|
+
}
|
|
11911
|
+
}
|
|
11912
|
+
return [...collected.values()];
|
|
11913
|
+
}
|
|
11914
|
+
/**
|
|
11915
|
+
* Build a `list / add / remove / clear` subcommand group around
|
|
11916
|
+
* `settings.watchKeywords`. Shape differs from the user/channel allowlists
|
|
11917
|
+
* (`{keyword, instruction?}` vs `{id, name?}`), so we duplicate the
|
|
11918
|
+
* scaffolding instead of squeezing both shapes through one factory — the
|
|
11919
|
+
* help text, column headers, and `--instruction` flag are all keyword-
|
|
11920
|
+
* specific and would just bloat the unified version.
|
|
11921
|
+
*/
|
|
11922
|
+
function registerWatchKeywordsCommand(bot) {
|
|
11923
|
+
const group = bot.command("watch-keywords").description("Manage watch keywords (non-mention channel triggers; the optional instruction is prepended to the user message before being sent to the AI)");
|
|
11924
|
+
const readEntries = (bot) => normalizeWatchKeywords(bot.settings?.watchKeywords);
|
|
11925
|
+
const buildPayload = (bot, nextEntries) => ({
|
|
11926
|
+
id: bot.id,
|
|
11927
|
+
settings: {
|
|
11928
|
+
...bot.settings,
|
|
11929
|
+
watchKeywords: nextEntries
|
|
11930
|
+
}
|
|
11931
|
+
});
|
|
11932
|
+
group.command("list <botId>").description("List watch-keyword entries").option("--json", "Output JSON").action(async (botId, options) => {
|
|
11933
|
+
const entries = readEntries(await findBot(await getTrpcClient(), botId));
|
|
11934
|
+
if (options.json) {
|
|
11935
|
+
outputJson(entries);
|
|
11936
|
+
return;
|
|
11937
|
+
}
|
|
11938
|
+
if (entries.length === 0) {
|
|
11939
|
+
console.log(`${import_picocolors.default.dim("No watch-keyword entries.")}`);
|
|
11940
|
+
return;
|
|
11941
|
+
}
|
|
11942
|
+
printTable(entries.map((e) => [e.keyword, e.instruction ?? import_picocolors.default.dim("-")]), ["KEYWORD", "INSTRUCTION"]);
|
|
11943
|
+
});
|
|
11944
|
+
group.command("add <botId> <keyword>").description("Add a watch keyword (with optional instruction prefix)").option("--instruction <text>", "Prompt prepended to the user message when this keyword fires (omit for \"just wake the bot\")").action(async (botId, keyword, options) => {
|
|
11945
|
+
const trimmedKeyword = keyword.trim().toLowerCase();
|
|
11946
|
+
if (!trimmedKeyword) {
|
|
11947
|
+
log$7.error("Keyword cannot be empty.");
|
|
11948
|
+
process.exit(1);
|
|
11949
|
+
return;
|
|
11950
|
+
}
|
|
11951
|
+
const trimmedInstruction = options.instruction?.trim();
|
|
11952
|
+
const client = await getTrpcClient();
|
|
11953
|
+
const b = await findBot(client, botId);
|
|
11954
|
+
const entries = readEntries(b);
|
|
11955
|
+
const existing = entries.find((e) => e.keyword === trimmedKeyword);
|
|
11956
|
+
if (existing) {
|
|
11957
|
+
if (trimmedInstruction && existing.instruction !== trimmedInstruction) {
|
|
11958
|
+
existing.instruction = trimmedInstruction;
|
|
11959
|
+
await client.agentBotProvider.update.mutate(buildPayload(b, entries));
|
|
11960
|
+
console.log(`${import_picocolors.default.green("✓")} Updated instruction for ${import_picocolors.default.bold(trimmedKeyword)} (${entries.length} entr${entries.length === 1 ? "y" : "ies"})`);
|
|
11961
|
+
return;
|
|
11962
|
+
}
|
|
11963
|
+
log$7.info(`${trimmedKeyword} is already on watchKeywords — nothing to do.`);
|
|
11964
|
+
return;
|
|
11965
|
+
}
|
|
11966
|
+
const next = [...entries, trimmedInstruction ? {
|
|
11967
|
+
instruction: trimmedInstruction,
|
|
11968
|
+
keyword: trimmedKeyword
|
|
11969
|
+
} : { keyword: trimmedKeyword }];
|
|
11970
|
+
await client.agentBotProvider.update.mutate(buildPayload(b, next));
|
|
11971
|
+
console.log(`${import_picocolors.default.green("✓")} Added ${import_picocolors.default.bold(trimmedKeyword)}${trimmedInstruction ? " (with instruction)" : ""} to watchKeywords (now ${next.length} entr${next.length === 1 ? "y" : "ies"})`);
|
|
11972
|
+
});
|
|
11973
|
+
group.command("remove <botId> <keyword>").description("Remove a watch keyword").action(async (botId, keyword) => {
|
|
11974
|
+
const trimmedKeyword = keyword.trim().toLowerCase();
|
|
11975
|
+
const client = await getTrpcClient();
|
|
11976
|
+
const b = await findBot(client, botId);
|
|
11977
|
+
const entries = readEntries(b);
|
|
11978
|
+
const next = entries.filter((e) => e.keyword !== trimmedKeyword);
|
|
11979
|
+
if (next.length === entries.length) {
|
|
11980
|
+
log$7.info(`${trimmedKeyword} is not on watchKeywords — nothing to do.`);
|
|
11981
|
+
return;
|
|
11982
|
+
}
|
|
11983
|
+
await client.agentBotProvider.update.mutate(buildPayload(b, next));
|
|
11984
|
+
console.log(`${import_picocolors.default.green("✓")} Removed ${import_picocolors.default.bold(trimmedKeyword)} from watchKeywords (${next.length} entr${next.length === 1 ? "y" : "ies"} left)`);
|
|
11985
|
+
});
|
|
11986
|
+
group.command("clear <botId>").description("Clear all watch keywords").option("--yes", "Skip confirmation prompt").action(async (botId, options) => {
|
|
11987
|
+
const client = await getTrpcClient();
|
|
11988
|
+
const b = await findBot(client, botId);
|
|
11989
|
+
const entries = readEntries(b);
|
|
11990
|
+
if (entries.length === 0) {
|
|
11991
|
+
log$7.info("watchKeywords is already empty — nothing to do.");
|
|
11992
|
+
return;
|
|
11993
|
+
}
|
|
11994
|
+
if (!options.yes) {
|
|
11995
|
+
if (!await confirm(`Clear all ${entries.length} watch-keyword entr${entries.length === 1 ? "y" : "ies"} from this bot?`)) {
|
|
11996
|
+
console.log("Cancelled.");
|
|
11997
|
+
return;
|
|
11998
|
+
}
|
|
11999
|
+
}
|
|
12000
|
+
await client.agentBotProvider.update.mutate(buildPayload(b, []));
|
|
12001
|
+
console.log(`${import_picocolors.default.green("✓")} Cleared watchKeywords on bot ${import_picocolors.default.bold(botId)}`);
|
|
12002
|
+
});
|
|
12003
|
+
}
|
|
11147
12004
|
function registerBotCommand(program) {
|
|
11148
12005
|
const bot = program.command("bot").description("Manage bot integrations");
|
|
11149
12006
|
registerBotMessageCommands(bot);
|
|
@@ -11333,6 +12190,7 @@ function registerBotCommand(program) {
|
|
|
11333
12190
|
idLabel: "channel / group / thread ID",
|
|
11334
12191
|
name: "group-allowlist"
|
|
11335
12192
|
});
|
|
12193
|
+
registerWatchKeywordsCommand(bot);
|
|
11336
12194
|
bot.command("remove <botId>").description("Remove a bot integration").option("--yes", "Skip confirmation prompt").action(async (botId, options) => {
|
|
11337
12195
|
if (!options.yes) {
|
|
11338
12196
|
if (!await confirm("Are you sure you want to remove this bot integration?")) {
|
|
@@ -16388,7 +17246,7 @@ var require_path = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
16388
17246
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16389
17247
|
exports.convertPosixPathToPattern = exports.convertWindowsPathToPattern = exports.convertPathToPattern = exports.escapePosixPath = exports.escapeWindowsPath = exports.escape = exports.removeLeadingDotSegment = exports.makeAbsolute = exports.unixify = void 0;
|
|
16390
17248
|
const os$3 = __require("os");
|
|
16391
|
-
const path$
|
|
17249
|
+
const path$10 = __require("path");
|
|
16392
17250
|
const IS_WINDOWS_PLATFORM = os$3.platform() === "win32";
|
|
16393
17251
|
const LEADING_DOT_SEGMENT_CHARACTERS_COUNT = 2;
|
|
16394
17252
|
/**
|
|
@@ -16417,7 +17275,7 @@ var require_path = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
16417
17275
|
}
|
|
16418
17276
|
exports.unixify = unixify;
|
|
16419
17277
|
function makeAbsolute(cwd, filepath) {
|
|
16420
|
-
return path$
|
|
17278
|
+
return path$10.resolve(cwd, filepath);
|
|
16421
17279
|
}
|
|
16422
17280
|
exports.makeAbsolute = makeAbsolute;
|
|
16423
17281
|
function removeLeadingDotSegment(entry) {
|
|
@@ -17683,7 +18541,7 @@ var require_braces = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
17683
18541
|
//#endregion
|
|
17684
18542
|
//#region node_modules/.pnpm/picomatch@2.3.2/node_modules/picomatch/lib/constants.js
|
|
17685
18543
|
var require_constants$2 = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
17686
|
-
const path$
|
|
18544
|
+
const path$9 = __require("path");
|
|
17687
18545
|
const WIN_SLASH = "\\\\/";
|
|
17688
18546
|
const WIN_NO_SLASH = `[^${WIN_SLASH}]`;
|
|
17689
18547
|
const DEFAULT_MAX_EXTGLOB_RECURSION = 0;
|
|
@@ -17812,7 +18670,7 @@ var require_constants$2 = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
17812
18670
|
CHAR_UNDERSCORE: 95,
|
|
17813
18671
|
CHAR_VERTICAL_LINE: 124,
|
|
17814
18672
|
CHAR_ZERO_WIDTH_NOBREAK_SPACE: 65279,
|
|
17815
|
-
SEP: path$
|
|
18673
|
+
SEP: path$9.sep,
|
|
17816
18674
|
/**
|
|
17817
18675
|
* Create EXTGLOB_CHARS
|
|
17818
18676
|
*/
|
|
@@ -17857,7 +18715,7 @@ var require_constants$2 = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
17857
18715
|
//#endregion
|
|
17858
18716
|
//#region node_modules/.pnpm/picomatch@2.3.2/node_modules/picomatch/lib/utils.js
|
|
17859
18717
|
var require_utils$3 = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
17860
|
-
const path$
|
|
18718
|
+
const path$8 = __require("path");
|
|
17861
18719
|
const win32 = process.platform === "win32";
|
|
17862
18720
|
const { REGEX_BACKSLASH, REGEX_REMOVE_BACKSLASH, REGEX_SPECIAL_CHARS, REGEX_SPECIAL_CHARS_GLOBAL } = require_constants$2();
|
|
17863
18721
|
exports.isObject = (val) => val !== null && typeof val === "object" && !Array.isArray(val);
|
|
@@ -17877,7 +18735,7 @@ var require_utils$3 = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
17877
18735
|
};
|
|
17878
18736
|
exports.isWindows = (options) => {
|
|
17879
18737
|
if (options && typeof options.windows === "boolean") return options.windows;
|
|
17880
|
-
return win32 === true || path$
|
|
18738
|
+
return win32 === true || path$8.sep === "\\";
|
|
17881
18739
|
};
|
|
17882
18740
|
exports.escapeLast = (input, char, lastIdx) => {
|
|
17883
18741
|
const idx = input.lastIndexOf(char, lastIdx);
|
|
@@ -19250,7 +20108,7 @@ var require_parse = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
19250
20108
|
//#endregion
|
|
19251
20109
|
//#region node_modules/.pnpm/picomatch@2.3.2/node_modules/picomatch/lib/picomatch.js
|
|
19252
20110
|
var require_picomatch$1 = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
19253
|
-
const path$
|
|
20111
|
+
const path$7 = __require("path");
|
|
19254
20112
|
const scan = require_scan();
|
|
19255
20113
|
const parse = require_parse();
|
|
19256
20114
|
const utils = require_utils$3();
|
|
@@ -19389,7 +20247,7 @@ var require_picomatch$1 = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
19389
20247
|
* @api public
|
|
19390
20248
|
*/
|
|
19391
20249
|
picomatch.matchBase = (input, glob, options, posix = utils.isWindows(options)) => {
|
|
19392
|
-
return (glob instanceof RegExp ? glob : picomatch.makeRe(glob, options)).test(path$
|
|
20250
|
+
return (glob instanceof RegExp ? glob : picomatch.makeRe(glob, options)).test(path$7.basename(input));
|
|
19393
20251
|
};
|
|
19394
20252
|
/**
|
|
19395
20253
|
* Returns true if **any** of the given glob `patterns` match the specified `string`.
|
|
@@ -19947,7 +20805,7 @@ var require_micromatch = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
19947
20805
|
var require_pattern = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
19948
20806
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
19949
20807
|
exports.isAbsolute = exports.partitionAbsoluteAndRelative = exports.removeDuplicateSlashes = exports.matchAny = exports.convertPatternsToRe = exports.makeRe = exports.getPatternParts = exports.expandBraceExpansion = exports.expandPatternsWithBraceExpansion = exports.isAffectDepthOfReadingPattern = exports.endsWithSlashGlobStar = exports.hasGlobStar = exports.getBaseDirectory = exports.isPatternRelatedToParentDirectory = exports.getPatternsOutsideCurrentDirectory = exports.getPatternsInsideCurrentDirectory = exports.getPositivePatterns = exports.getNegativePatterns = exports.isPositivePattern = exports.isNegativePattern = exports.convertToNegativePattern = exports.convertToPositivePattern = exports.isDynamicPattern = exports.isStaticPattern = void 0;
|
|
19950
|
-
const path$
|
|
20808
|
+
const path$6 = __require("path");
|
|
19951
20809
|
const globParent = require_glob_parent();
|
|
19952
20810
|
const micromatch = require_micromatch();
|
|
19953
20811
|
const GLOBSTAR = "**";
|
|
@@ -20055,7 +20913,7 @@ var require_pattern = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
20055
20913
|
}
|
|
20056
20914
|
exports.endsWithSlashGlobStar = endsWithSlashGlobStar;
|
|
20057
20915
|
function isAffectDepthOfReadingPattern(pattern) {
|
|
20058
|
-
const basename = path$
|
|
20916
|
+
const basename = path$6.basename(pattern);
|
|
20059
20917
|
return endsWithSlashGlobStar(pattern) || isStaticPattern(basename);
|
|
20060
20918
|
}
|
|
20061
20919
|
exports.isAffectDepthOfReadingPattern = isAffectDepthOfReadingPattern;
|
|
@@ -20129,7 +20987,7 @@ var require_pattern = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
20129
20987
|
}
|
|
20130
20988
|
exports.partitionAbsoluteAndRelative = partitionAbsoluteAndRelative;
|
|
20131
20989
|
function isAbsolute(pattern) {
|
|
20132
|
-
return path$
|
|
20990
|
+
return path$6.isAbsolute(pattern);
|
|
20133
20991
|
}
|
|
20134
20992
|
exports.isAbsolute = isAbsolute;
|
|
20135
20993
|
}));
|
|
@@ -20788,7 +21646,7 @@ var require_fs = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
20788
21646
|
//#region node_modules/.pnpm/@nodelib+fs.scandir@2.1.5/node_modules/@nodelib/fs.scandir/out/settings.js
|
|
20789
21647
|
var require_settings$2 = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
20790
21648
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20791
|
-
const path$
|
|
21649
|
+
const path$5 = __require("path");
|
|
20792
21650
|
const fsStat = require_out$3();
|
|
20793
21651
|
const fs = require_fs();
|
|
20794
21652
|
var Settings = class {
|
|
@@ -20796,7 +21654,7 @@ var require_settings$2 = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
20796
21654
|
this._options = _options;
|
|
20797
21655
|
this.followSymbolicLinks = this._getValue(this._options.followSymbolicLinks, false);
|
|
20798
21656
|
this.fs = fs.createFileSystemAdapter(this._options.fs);
|
|
20799
|
-
this.pathSegmentSeparator = this._getValue(this._options.pathSegmentSeparator, path$
|
|
21657
|
+
this.pathSegmentSeparator = this._getValue(this._options.pathSegmentSeparator, path$5.sep);
|
|
20800
21658
|
this.stats = this._getValue(this._options.stats, false);
|
|
20801
21659
|
this.throwErrorOnBrokenSymbolicLink = this._getValue(this._options.throwErrorOnBrokenSymbolicLink, true);
|
|
20802
21660
|
this.fsStatSettings = new fsStat.Settings({
|
|
@@ -21401,7 +22259,7 @@ var require_sync$2 = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
21401
22259
|
//#region node_modules/.pnpm/@nodelib+fs.walk@1.2.8/node_modules/@nodelib/fs.walk/out/settings.js
|
|
21402
22260
|
var require_settings$1 = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
21403
22261
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
21404
|
-
const path$
|
|
22262
|
+
const path$4 = __require("path");
|
|
21405
22263
|
const fsScandir = require_out$2();
|
|
21406
22264
|
var Settings = class {
|
|
21407
22265
|
constructor(_options = {}) {
|
|
@@ -21411,7 +22269,7 @@ var require_settings$1 = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
21411
22269
|
this.deepFilter = this._getValue(this._options.deepFilter, null);
|
|
21412
22270
|
this.entryFilter = this._getValue(this._options.entryFilter, null);
|
|
21413
22271
|
this.errorFilter = this._getValue(this._options.errorFilter, null);
|
|
21414
|
-
this.pathSegmentSeparator = this._getValue(this._options.pathSegmentSeparator, path$
|
|
22272
|
+
this.pathSegmentSeparator = this._getValue(this._options.pathSegmentSeparator, path$4.sep);
|
|
21415
22273
|
this.fsScandirSettings = new fsScandir.Settings({
|
|
21416
22274
|
followSymbolicLinks: this._options.followSymbolicLinks,
|
|
21417
22275
|
fs: this._options.fs,
|
|
@@ -21465,7 +22323,7 @@ var require_out$1 = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
21465
22323
|
//#region node_modules/.pnpm/fast-glob@3.3.3/node_modules/fast-glob/out/readers/reader.js
|
|
21466
22324
|
var require_reader$1 = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
21467
22325
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
21468
|
-
const path$
|
|
22326
|
+
const path$3 = __require("path");
|
|
21469
22327
|
const fsStat = require_out$3();
|
|
21470
22328
|
const utils = require_utils$2();
|
|
21471
22329
|
var Reader = class {
|
|
@@ -21478,7 +22336,7 @@ var require_reader$1 = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
21478
22336
|
});
|
|
21479
22337
|
}
|
|
21480
22338
|
_getFullEntryPath(filepath) {
|
|
21481
|
-
return path$
|
|
22339
|
+
return path$3.resolve(this._settings.cwd, filepath);
|
|
21482
22340
|
}
|
|
21483
22341
|
_makeEntry(stats, pattern) {
|
|
21484
22342
|
const entry = {
|
|
@@ -21822,7 +22680,7 @@ var require_entry = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
21822
22680
|
//#region node_modules/.pnpm/fast-glob@3.3.3/node_modules/fast-glob/out/providers/provider.js
|
|
21823
22681
|
var require_provider = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
21824
22682
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
21825
|
-
const path$
|
|
22683
|
+
const path$2 = __require("path");
|
|
21826
22684
|
const deep_1 = require_deep();
|
|
21827
22685
|
const entry_1 = require_entry$1();
|
|
21828
22686
|
const error_1 = require_error();
|
|
@@ -21836,7 +22694,7 @@ var require_provider = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
21836
22694
|
this.entryTransformer = new entry_2.default(this._settings);
|
|
21837
22695
|
}
|
|
21838
22696
|
_getRootDirectory(task) {
|
|
21839
|
-
return path$
|
|
22697
|
+
return path$2.resolve(this._settings.cwd, task.base);
|
|
21840
22698
|
}
|
|
21841
22699
|
_getReaderOptions(task) {
|
|
21842
22700
|
const basePath = task.base === "." ? "" : task.base;
|
|
@@ -22148,16 +23006,36 @@ var require_out = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
22148
23006
|
module.exports = FastGlob;
|
|
22149
23007
|
}));
|
|
22150
23008
|
|
|
23009
|
+
//#endregion
|
|
23010
|
+
//#region ../../packages/local-file-shell/src/file/hasHiddenSegment.ts
|
|
23011
|
+
/**
|
|
23012
|
+
* Detect whether a glob pattern (or path) contains a dot-prefixed segment such
|
|
23013
|
+
* as `.github`, `.husky`, or `foo/.config`. Used to auto-enable hidden-file
|
|
23014
|
+
* matching when the caller's intent is clearly to traverse a hidden directory
|
|
23015
|
+
* — otherwise `fast-glob` (`dot: false`) and `ripgrep` (no `--hidden`) silently
|
|
23016
|
+
* return zero results.
|
|
23017
|
+
*
|
|
23018
|
+
* Skips `.` and `..` (relative path indicators), which are not hidden segments.
|
|
23019
|
+
*/
|
|
23020
|
+
const HIDDEN_SEGMENT_RE = /(?:^|\/)\.[^./]/;
|
|
23021
|
+
const hasHiddenSegment = (pattern) => !!pattern && HIDDEN_SEGMENT_RE.test(pattern);
|
|
23022
|
+
|
|
22151
23023
|
//#endregion
|
|
22152
23024
|
//#region ../../packages/local-file-shell/src/file/glob.ts
|
|
22153
23025
|
var import_out = /* @__PURE__ */ __toESM$2(require_out());
|
|
22154
23026
|
async function globLocalFiles({ pattern, cwd }) {
|
|
22155
23027
|
try {
|
|
22156
|
-
|
|
23028
|
+
const wantsHidden = hasHiddenSegment(pattern);
|
|
23029
|
+
const files = await (0, import_out.default)(pattern, {
|
|
22157
23030
|
cwd: expandTilde(cwd) || process.cwd(),
|
|
22158
|
-
dot:
|
|
23031
|
+
dot: wantsHidden,
|
|
22159
23032
|
ignore: ["**/node_modules/**", "**/.git/**"]
|
|
22160
|
-
})
|
|
23033
|
+
});
|
|
23034
|
+
if (wantsHidden) return {
|
|
23035
|
+
files,
|
|
23036
|
+
hint: `Auto-enabled hidden-file matching because pattern contains a dot-prefixed segment.`
|
|
23037
|
+
};
|
|
23038
|
+
return { files };
|
|
22161
23039
|
} catch (error) {
|
|
22162
23040
|
return {
|
|
22163
23041
|
error: error.message,
|
|
@@ -22169,8 +23047,11 @@ async function globLocalFiles({ pattern, cwd }) {
|
|
|
22169
23047
|
//#endregion
|
|
22170
23048
|
//#region ../../packages/local-file-shell/src/file/grep.ts
|
|
22171
23049
|
async function grepContent({ pattern, cwd, filePattern }) {
|
|
23050
|
+
const wantsHidden = hasHiddenSegment(filePattern);
|
|
23051
|
+
const hint = wantsHidden ? `Auto-enabled hidden-file matching because filePattern contains a dot-prefixed segment.` : void 0;
|
|
22172
23052
|
return new Promise((resolve) => {
|
|
22173
23053
|
const args = ["--json", "-n"];
|
|
23054
|
+
if (wantsHidden) args.push("--hidden", "--glob", "!**/.git/**");
|
|
22174
23055
|
if (filePattern) args.push("--glob", filePattern);
|
|
22175
23056
|
args.push(pattern);
|
|
22176
23057
|
const child = spawn("rg", args, { cwd: expandTilde(cwd) || process.cwd() });
|
|
@@ -22182,6 +23063,7 @@ async function grepContent({ pattern, cwd, filePattern }) {
|
|
|
22182
23063
|
child.on("close", (code) => {
|
|
22183
23064
|
if (code !== 0 && code !== 1) {
|
|
22184
23065
|
resolve({
|
|
23066
|
+
hint,
|
|
22185
23067
|
matches: [],
|
|
22186
23068
|
success: false
|
|
22187
23069
|
});
|
|
@@ -22189,6 +23071,7 @@ async function grepContent({ pattern, cwd, filePattern }) {
|
|
|
22189
23071
|
}
|
|
22190
23072
|
try {
|
|
22191
23073
|
resolve({
|
|
23074
|
+
hint,
|
|
22192
23075
|
matches: stdout.split("\n").filter(Boolean).map((line) => {
|
|
22193
23076
|
try {
|
|
22194
23077
|
return JSON.parse(line);
|
|
@@ -22200,6 +23083,7 @@ async function grepContent({ pattern, cwd, filePattern }) {
|
|
|
22200
23083
|
});
|
|
22201
23084
|
} catch {
|
|
22202
23085
|
resolve({
|
|
23086
|
+
hint,
|
|
22203
23087
|
matches: [],
|
|
22204
23088
|
success: true
|
|
22205
23089
|
});
|
|
@@ -22207,6 +23091,7 @@ async function grepContent({ pattern, cwd, filePattern }) {
|
|
|
22207
23091
|
});
|
|
22208
23092
|
child.on("error", () => {
|
|
22209
23093
|
resolve({
|
|
23094
|
+
hint,
|
|
22210
23095
|
matches: [],
|
|
22211
23096
|
success: false
|
|
22212
23097
|
});
|
|
@@ -22971,6 +23856,120 @@ var require_src = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
22971
23856
|
else module.exports = require_node$1();
|
|
22972
23857
|
}));
|
|
22973
23858
|
|
|
23859
|
+
//#endregion
|
|
23860
|
+
//#region ../../packages/file-loaders/src/utils/detectUtf16.ts
|
|
23861
|
+
const HEURISTIC_SAMPLE_BYTES = 512;
|
|
23862
|
+
const HEURISTIC_THRESHOLD = .3;
|
|
23863
|
+
/**
|
|
23864
|
+
* Detect UTF-16 without BOM by sampling and counting ASCII-shaped code-unit
|
|
23865
|
+
* pairs. ASCII chars in UTF-16 produce a 0x00 byte at the high half: at
|
|
23866
|
+
* odd index for LE, at even index for BE.
|
|
23867
|
+
*
|
|
23868
|
+
* Used both by `TextLoader` (to pick the right decoder) and by the binary
|
|
23869
|
+
* sniffer (so a UTF-16 text file without BOM isn't mistaken for binary
|
|
23870
|
+
* because of its alternating null bytes).
|
|
23871
|
+
*/
|
|
23872
|
+
const detectUtf16NoBom = (buffer) => {
|
|
23873
|
+
const sample = buffer.subarray(0, Math.min(HEURISTIC_SAMPLE_BYTES, buffer.length));
|
|
23874
|
+
if (sample.length < 4 || sample.length % 2 !== 0) return null;
|
|
23875
|
+
let leAsciiPairs = 0;
|
|
23876
|
+
let beAsciiPairs = 0;
|
|
23877
|
+
const totalPairs = sample.length / 2;
|
|
23878
|
+
for (let i = 0; i < sample.length; i += 2) {
|
|
23879
|
+
const lo = sample[i];
|
|
23880
|
+
const hi = sample[i + 1];
|
|
23881
|
+
if (hi === 0 && lo !== 0) leAsciiPairs++;
|
|
23882
|
+
else if (lo === 0 && hi !== 0) beAsciiPairs++;
|
|
23883
|
+
}
|
|
23884
|
+
if (leAsciiPairs > beAsciiPairs && leAsciiPairs / totalPairs >= HEURISTIC_THRESHOLD) return "utf-16le";
|
|
23885
|
+
if (beAsciiPairs > leAsciiPairs && beAsciiPairs / totalPairs >= HEURISTIC_THRESHOLD) return "utf-16be";
|
|
23886
|
+
return null;
|
|
23887
|
+
};
|
|
23888
|
+
|
|
23889
|
+
//#endregion
|
|
23890
|
+
//#region ../../packages/file-loaders/src/loaders/text/index.ts
|
|
23891
|
+
var import_src$5 = /* @__PURE__ */ __toESM$2(require_src());
|
|
23892
|
+
const log$6 = (0, import_src$5.default)("file-loaders:text");
|
|
23893
|
+
/**
|
|
23894
|
+
* Read a text file with automatic encoding detection.
|
|
23895
|
+
* Detects UTF-8, UTF-16LE, and UTF-16BE via BOM, with a heuristic fallback
|
|
23896
|
+
* for UTF-16 without BOM (common in some Windows exports). Falls back to UTF-8.
|
|
23897
|
+
*/
|
|
23898
|
+
const readTextFile = async (filePath) => {
|
|
23899
|
+
const buffer = await readFile(filePath);
|
|
23900
|
+
if (buffer.length >= 2 && buffer[0] === 255 && buffer[1] === 254) {
|
|
23901
|
+
log$6("Detected UTF-16LE BOM");
|
|
23902
|
+
return new TextDecoder("utf-16le").decode(buffer.subarray(2));
|
|
23903
|
+
}
|
|
23904
|
+
if (buffer.length >= 2 && buffer[0] === 254 && buffer[1] === 255) {
|
|
23905
|
+
log$6("Detected UTF-16BE BOM");
|
|
23906
|
+
return new TextDecoder("utf-16be").decode(buffer.subarray(2));
|
|
23907
|
+
}
|
|
23908
|
+
if (buffer.length >= 3 && buffer[0] === 239 && buffer[1] === 187 && buffer[2] === 191) {
|
|
23909
|
+
log$6("Detected UTF-8 BOM");
|
|
23910
|
+
return buffer.subarray(3).toString("utf8");
|
|
23911
|
+
}
|
|
23912
|
+
const variant = detectUtf16NoBom(buffer);
|
|
23913
|
+
if (variant) {
|
|
23914
|
+
log$6("Detected %s without BOM (heuristic)", variant);
|
|
23915
|
+
return new TextDecoder(variant).decode(buffer);
|
|
23916
|
+
}
|
|
23917
|
+
return buffer.toString("utf8");
|
|
23918
|
+
};
|
|
23919
|
+
/**
|
|
23920
|
+
* Loader for loading plain text files.
|
|
23921
|
+
*/
|
|
23922
|
+
var TextLoader = class {
|
|
23923
|
+
async loadPages(filePath) {
|
|
23924
|
+
log$6("Loading text file:", filePath);
|
|
23925
|
+
try {
|
|
23926
|
+
const fileContent = await readTextFile(filePath);
|
|
23927
|
+
log$6("Text file loaded successfully, size:", fileContent.length, "bytes");
|
|
23928
|
+
const lineCount = fileContent.split("\n").length;
|
|
23929
|
+
const charCount = fileContent.length;
|
|
23930
|
+
log$6("Text file stats:", {
|
|
23931
|
+
charCount,
|
|
23932
|
+
lineCount
|
|
23933
|
+
});
|
|
23934
|
+
const page = {
|
|
23935
|
+
charCount,
|
|
23936
|
+
lineCount,
|
|
23937
|
+
metadata: {
|
|
23938
|
+
lineNumberEnd: lineCount,
|
|
23939
|
+
lineNumberStart: 1
|
|
23940
|
+
},
|
|
23941
|
+
pageContent: fileContent
|
|
23942
|
+
};
|
|
23943
|
+
log$6("Text page created successfully");
|
|
23944
|
+
return [page];
|
|
23945
|
+
} catch (e) {
|
|
23946
|
+
const error = e;
|
|
23947
|
+
log$6("Error encountered while loading text file");
|
|
23948
|
+
console.error(`Error loading text file ${filePath}: ${error.message}`);
|
|
23949
|
+
const errorPage = {
|
|
23950
|
+
charCount: 0,
|
|
23951
|
+
lineCount: 0,
|
|
23952
|
+
metadata: { error: `Failed to load text file: ${error.message}` },
|
|
23953
|
+
pageContent: ""
|
|
23954
|
+
};
|
|
23955
|
+
log$6("Created error page for failed text file loading");
|
|
23956
|
+
return [errorPage];
|
|
23957
|
+
}
|
|
23958
|
+
}
|
|
23959
|
+
/**
|
|
23960
|
+
* For plain text, simply concatenate the content of all pages.
|
|
23961
|
+
* (Although TextLoader typically has only one page, this maintains interface consistency)
|
|
23962
|
+
* @param pages Array of pages
|
|
23963
|
+
* @returns Aggregated content
|
|
23964
|
+
*/
|
|
23965
|
+
async aggregateContent(pages) {
|
|
23966
|
+
log$6("Aggregating content from", pages.length, "text pages");
|
|
23967
|
+
const result = pages.map((page) => page.pageContent).join("\n");
|
|
23968
|
+
log$6("Content aggregated successfully, length:", result.length);
|
|
23969
|
+
return result;
|
|
23970
|
+
}
|
|
23971
|
+
};
|
|
23972
|
+
|
|
22974
23973
|
//#endregion
|
|
22975
23974
|
//#region node_modules/.pnpm/word-extractor@1.0.4/node_modules/word-extractor/lib/ole-header.js
|
|
22976
23975
|
var require_ole_header = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
@@ -27169,7 +28168,7 @@ var require_open_office_extractor = /* @__PURE__ */ __commonJSMin(((exports, mod
|
|
|
27169
28168
|
* Note that [WordOleExtractor]{@link module:word-ole-extractor~WordOleExtractor} is
|
|
27170
28169
|
* used for older, OLE-style, compound document files.
|
|
27171
28170
|
*/
|
|
27172
|
-
const path$
|
|
28171
|
+
const path$1 = __require("path");
|
|
27173
28172
|
const SAXES = require_saxes();
|
|
27174
28173
|
const yauzl = require_yauzl$1();
|
|
27175
28174
|
const BufferReader = require_buffer_reader();
|
|
@@ -27204,7 +28203,7 @@ var require_open_office_extractor = /* @__PURE__ */ __commonJSMin(((exports, mod
|
|
|
27204
28203
|
}
|
|
27205
28204
|
shouldProcess(filename) {
|
|
27206
28205
|
if (this._actions[filename]) return true;
|
|
27207
|
-
const extension = path$
|
|
28206
|
+
const extension = path$1.posix.extname(filename).replace(/^\./, "");
|
|
27208
28207
|
if (!extension) return false;
|
|
27209
28208
|
const defaultType = this._defaults[extension];
|
|
27210
28209
|
if (defaultType && this._streamTypes[defaultType]) return true;
|
|
@@ -27457,14 +28456,14 @@ var require_word = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
27457
28456
|
//#endregion
|
|
27458
28457
|
//#region ../../packages/file-loaders/src/loaders/doc/index.ts
|
|
27459
28458
|
var doc_exports = /* @__PURE__ */ __exportAll({ DocLoader: () => DocLoader });
|
|
27460
|
-
var import_src$
|
|
28459
|
+
var import_src$4, import_word, log$5, DocLoader;
|
|
27461
28460
|
var init_doc = __esmMin((() => {
|
|
27462
|
-
import_src$
|
|
28461
|
+
import_src$4 = /* @__PURE__ */ __toESM$2(require_src());
|
|
27463
28462
|
import_word = /* @__PURE__ */ __toESM$2(require_word());
|
|
27464
|
-
log$
|
|
28463
|
+
log$5 = (0, import_src$4.default)("file-loaders:doc");
|
|
27465
28464
|
DocLoader = class {
|
|
27466
28465
|
async loadPages(filePath) {
|
|
27467
|
-
log$
|
|
28466
|
+
log$5("Loading DOC file:", filePath);
|
|
27468
28467
|
try {
|
|
27469
28468
|
const extracted = await new import_word.default().extract(filePath);
|
|
27470
28469
|
const pageContent = extracted && typeof extracted.getBody === "function" ? extracted.getBody() : extracted?.text ?? "";
|
|
@@ -27475,11 +28474,11 @@ var init_doc = __esmMin((() => {
|
|
|
27475
28474
|
metadata: { pageNumber: 1 },
|
|
27476
28475
|
pageContent
|
|
27477
28476
|
};
|
|
27478
|
-
log$
|
|
28477
|
+
log$5("DOC loading completed");
|
|
27479
28478
|
return [page];
|
|
27480
28479
|
} catch (e) {
|
|
27481
28480
|
const error = e;
|
|
27482
|
-
log$
|
|
28481
|
+
log$5("Error encountered while loading DOC file");
|
|
27483
28482
|
console.error(`Error loading DOC file ${filePath}: ${error.message}`);
|
|
27484
28483
|
return [{
|
|
27485
28484
|
charCount: 0,
|
|
@@ -27490,7 +28489,7 @@ var init_doc = __esmMin((() => {
|
|
|
27490
28489
|
}
|
|
27491
28490
|
}
|
|
27492
28491
|
async aggregateContent(pages) {
|
|
27493
|
-
log$
|
|
28492
|
+
log$5("Aggregating content from", pages.length, "DOC pages");
|
|
27494
28493
|
return pages.map((p) => p.pageContent).join("\n\n");
|
|
27495
28494
|
}
|
|
27496
28495
|
};
|
|
@@ -61735,23 +62734,23 @@ var require_lib$1 = /* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
61735
62734
|
//#endregion
|
|
61736
62735
|
//#region ../../packages/file-loaders/src/loaders/docx/index.ts
|
|
61737
62736
|
var docx_exports = /* @__PURE__ */ __exportAll({ DocxLoader: () => DocxLoader });
|
|
61738
|
-
var import_src$
|
|
62737
|
+
var import_src$3, import_lib$1, log$4, DocxLoader;
|
|
61739
62738
|
var init_docx = __esmMin((() => {
|
|
61740
|
-
import_src$
|
|
62739
|
+
import_src$3 = /* @__PURE__ */ __toESM$2(require_src());
|
|
61741
62740
|
import_lib$1 = /* @__PURE__ */ __toESM$2(require_lib$1());
|
|
61742
|
-
log$
|
|
62741
|
+
log$4 = (0, import_src$3.default)("file-loaders:docx");
|
|
61743
62742
|
DocxLoader = class {
|
|
61744
62743
|
async loadPages(filePath) {
|
|
61745
|
-
log$
|
|
62744
|
+
log$4("Loading DOCX file:", filePath);
|
|
61746
62745
|
try {
|
|
61747
62746
|
const buffer = await fs$1.readFile(filePath);
|
|
61748
|
-
log$
|
|
62747
|
+
log$4("File buffer read, size:", buffer.length);
|
|
61749
62748
|
const result = await import_lib$1.extractRawText({ buffer });
|
|
61750
62749
|
const pageContent = result.value;
|
|
61751
|
-
log$
|
|
62750
|
+
log$4("Text extracted, length:", pageContent.length);
|
|
61752
62751
|
const lineCount = pageContent.split("\n").length;
|
|
61753
62752
|
const charCount = pageContent.length;
|
|
61754
|
-
log$
|
|
62753
|
+
log$4("DOCX document processed, lines:", lineCount, "chars:", charCount);
|
|
61755
62754
|
const page = {
|
|
61756
62755
|
charCount,
|
|
61757
62756
|
lineCount,
|
|
@@ -61761,15 +62760,15 @@ var init_docx = __esmMin((() => {
|
|
|
61761
62760
|
if (result.messages.length > 0) {
|
|
61762
62761
|
const warnings = result.messages.filter((msg) => msg.type === "warning");
|
|
61763
62762
|
if (warnings.length > 0) {
|
|
61764
|
-
log$
|
|
61765
|
-
warnings.forEach((warning) => log$
|
|
62763
|
+
log$4("Extraction warnings:", warnings.length);
|
|
62764
|
+
warnings.forEach((warning) => log$4("Warning:", warning.message));
|
|
61766
62765
|
}
|
|
61767
62766
|
}
|
|
61768
|
-
log$
|
|
62767
|
+
log$4("DOCX loading completed");
|
|
61769
62768
|
return [page];
|
|
61770
62769
|
} catch (e) {
|
|
61771
62770
|
const error = e;
|
|
61772
|
-
log$
|
|
62771
|
+
log$4("Error encountered while loading DOCX file");
|
|
61773
62772
|
console.error(`Error loading DOCX file ${filePath}: ${error.message}`);
|
|
61774
62773
|
const errorPage = {
|
|
61775
62774
|
charCount: 0,
|
|
@@ -61777,7 +62776,7 @@ var init_docx = __esmMin((() => {
|
|
|
61777
62776
|
metadata: { error: `Failed to load DOCX file: ${error.message}` },
|
|
61778
62777
|
pageContent: ""
|
|
61779
62778
|
};
|
|
61780
|
-
log$
|
|
62779
|
+
log$4("Created error page for failed DOCX loading");
|
|
61781
62780
|
return [errorPage];
|
|
61782
62781
|
}
|
|
61783
62782
|
}
|
|
@@ -61788,9 +62787,9 @@ var init_docx = __esmMin((() => {
|
|
|
61788
62787
|
* @returns Aggregated content as a string.
|
|
61789
62788
|
*/
|
|
61790
62789
|
async aggregateContent(pages) {
|
|
61791
|
-
log$
|
|
62790
|
+
log$4("Aggregating content from", pages.length, "DOCX pages");
|
|
61792
62791
|
const result = pages.map((page) => page.pageContent).join("\n\n");
|
|
61793
|
-
log$
|
|
62792
|
+
log$4("DOCX content aggregated successfully, length:", result.length);
|
|
61794
62793
|
return result;
|
|
61795
62794
|
}
|
|
61796
62795
|
};
|
|
@@ -85153,68 +86152,68 @@ var excel_exports = /* @__PURE__ */ __exportAll({ ExcelLoader: () => ExcelLoader
|
|
|
85153
86152
|
* Handles empty sheets and escapes pipe characters.
|
|
85154
86153
|
*/
|
|
85155
86154
|
function sheetToMarkdownTable(jsonData) {
|
|
85156
|
-
log$
|
|
86155
|
+
log$3("Converting sheet data to Markdown table, rows:", jsonData?.length || 0);
|
|
85157
86156
|
if (!jsonData || jsonData.length === 0) {
|
|
85158
|
-
log$
|
|
86157
|
+
log$3("Sheet is empty, returning placeholder message");
|
|
85159
86158
|
return "*Sheet is empty or contains no data.*";
|
|
85160
86159
|
}
|
|
85161
86160
|
const headers = Object.keys(jsonData[0] || {});
|
|
85162
|
-
log$
|
|
86161
|
+
log$3("Sheet headers:", headers);
|
|
85163
86162
|
if (headers.length === 0) {
|
|
85164
|
-
log$
|
|
86163
|
+
log$3("Sheet has no headers, returning placeholder message");
|
|
85165
86164
|
return "*Sheet has headers but no data.*";
|
|
85166
86165
|
}
|
|
85167
86166
|
const headerRow = `| ${headers.join(" | ")} |`;
|
|
85168
86167
|
const separatorRow = `| ${headers.map(() => "---").join(" | ")} |`;
|
|
85169
|
-
log$
|
|
86168
|
+
log$3("Building data rows for Markdown table");
|
|
85170
86169
|
const result = `${headerRow}\n${separatorRow}\n${jsonData.map((row) => {
|
|
85171
86170
|
return `| ${headers.map((header) => {
|
|
85172
86171
|
const value = row[header];
|
|
85173
86172
|
return (value === null || value === void 0 ? "" : String(value).replaceAll("|", "\\|")).trim();
|
|
85174
86173
|
}).join(" | ")} |`;
|
|
85175
86174
|
}).join("\n")}`;
|
|
85176
|
-
log$
|
|
86175
|
+
log$3("Markdown table created, length:", result.length);
|
|
85177
86176
|
return result;
|
|
85178
86177
|
}
|
|
85179
|
-
var import_src$
|
|
86178
|
+
var import_src$2, log$3, ExcelLoader;
|
|
85180
86179
|
var init_excel = __esmMin((() => {
|
|
85181
|
-
import_src$
|
|
86180
|
+
import_src$2 = /* @__PURE__ */ __toESM$2(require_src());
|
|
85182
86181
|
init_xlsx();
|
|
85183
86182
|
init_prompt$1();
|
|
85184
|
-
log$
|
|
86183
|
+
log$3 = (0, import_src$2.default)("file-loaders:excel");
|
|
85185
86184
|
ExcelLoader = class {
|
|
85186
86185
|
async loadPages(filePath) {
|
|
85187
|
-
log$
|
|
86186
|
+
log$3("Loading Excel file:", filePath);
|
|
85188
86187
|
const pages = [];
|
|
85189
86188
|
try {
|
|
85190
|
-
log$
|
|
86189
|
+
log$3("Reading Excel file as buffer");
|
|
85191
86190
|
const dataBuffer = await readFile(filePath);
|
|
85192
|
-
log$
|
|
85193
|
-
log$
|
|
86191
|
+
log$3("Excel file read successfully, size:", dataBuffer.length, "bytes");
|
|
86192
|
+
log$3("Parsing Excel workbook");
|
|
85194
86193
|
const workbook = readSync(dataBuffer, { type: "buffer" });
|
|
85195
|
-
log$
|
|
86194
|
+
log$3("Excel workbook parsed successfully, sheets:", workbook.SheetNames.length);
|
|
85196
86195
|
for (const sheetName of workbook.SheetNames) {
|
|
85197
|
-
log$
|
|
86196
|
+
log$3(`Processing sheet: ${sheetName}`);
|
|
85198
86197
|
const worksheet = workbook.Sheets[sheetName];
|
|
85199
86198
|
const jsonData = utils.sheet_to_json(worksheet, {
|
|
85200
86199
|
defval: "",
|
|
85201
86200
|
raw: false
|
|
85202
86201
|
});
|
|
85203
|
-
log$
|
|
86202
|
+
log$3(`Sheet ${sheetName} converted to JSON, rows:`, jsonData.length);
|
|
85204
86203
|
const tableMarkdown = sheetToMarkdownTable(jsonData);
|
|
85205
86204
|
const lineCount = tableMarkdown.split("\n").length;
|
|
85206
86205
|
const charCount = tableMarkdown.length;
|
|
85207
|
-
log$
|
|
86206
|
+
log$3(`Sheet ${sheetName} converted to Markdown, lines: ${lineCount}, chars: ${charCount}`);
|
|
85208
86207
|
pages.push({
|
|
85209
86208
|
charCount,
|
|
85210
86209
|
lineCount,
|
|
85211
86210
|
metadata: { sheetName },
|
|
85212
86211
|
pageContent: tableMarkdown.trim()
|
|
85213
86212
|
});
|
|
85214
|
-
log$
|
|
86213
|
+
log$3(`Added sheet ${sheetName} as page`);
|
|
85215
86214
|
}
|
|
85216
86215
|
if (pages.length === 0) {
|
|
85217
|
-
log$
|
|
86216
|
+
log$3("Excel file contains no sheets, creating empty page with error");
|
|
85218
86217
|
pages.push({
|
|
85219
86218
|
charCount: 0,
|
|
85220
86219
|
lineCount: 0,
|
|
@@ -85222,11 +86221,11 @@ var init_excel = __esmMin((() => {
|
|
|
85222
86221
|
pageContent: ""
|
|
85223
86222
|
});
|
|
85224
86223
|
}
|
|
85225
|
-
log$
|
|
86224
|
+
log$3("Excel loading completed, total pages:", pages.length);
|
|
85226
86225
|
return pages;
|
|
85227
86226
|
} catch (e) {
|
|
85228
86227
|
const error = e;
|
|
85229
|
-
log$
|
|
86228
|
+
log$3("Error encountered while loading Excel file");
|
|
85230
86229
|
console.error(`Error loading Excel file ${filePath}: ${error.message}`);
|
|
85231
86230
|
const errorPage = {
|
|
85232
86231
|
charCount: 0,
|
|
@@ -85234,7 +86233,7 @@ var init_excel = __esmMin((() => {
|
|
|
85234
86233
|
metadata: { error: `Failed to load Excel file: ${error.message}` },
|
|
85235
86234
|
pageContent: ""
|
|
85236
86235
|
};
|
|
85237
|
-
log$
|
|
86236
|
+
log$3("Created error page for failed Excel loading");
|
|
85238
86237
|
return [errorPage];
|
|
85239
86238
|
}
|
|
85240
86239
|
}
|
|
@@ -85245,9 +86244,9 @@ var init_excel = __esmMin((() => {
|
|
|
85245
86244
|
* @returns Aggregated content as a string.
|
|
85246
86245
|
*/
|
|
85247
86246
|
async aggregateContent(pages) {
|
|
85248
|
-
log$
|
|
86247
|
+
log$3("Aggregating content from", pages.length, "Excel pages");
|
|
85249
86248
|
const result = promptTemplate$1(pages);
|
|
85250
|
-
log$
|
|
86249
|
+
log$3("Excel content aggregated successfully, length:", result.length);
|
|
85251
86250
|
return result;
|
|
85252
86251
|
}
|
|
85253
86252
|
};
|
|
@@ -193505,40 +194504,40 @@ ${page.pageContent}
|
|
|
193505
194504
|
//#endregion
|
|
193506
194505
|
//#region ../../packages/file-loaders/src/loaders/pdf/index.ts
|
|
193507
194506
|
var pdf_exports = /* @__PURE__ */ __exportAll({ PdfLoader: () => PdfLoader });
|
|
193508
|
-
var import_src$
|
|
194507
|
+
var import_src$1, log$2, PdfLoader;
|
|
193509
194508
|
var init_pdf = __esmMin((() => {
|
|
193510
|
-
import_src$
|
|
194509
|
+
import_src$1 = /* @__PURE__ */ __toESM$2(require_src());
|
|
193511
194510
|
init_pdf$1();
|
|
193512
194511
|
init_pdf_worker();
|
|
193513
194512
|
init_prompt();
|
|
193514
|
-
log$
|
|
194513
|
+
log$2 = (0, import_src$1.default)("file-loaders:pdf");
|
|
193515
194514
|
PdfLoader = class {
|
|
193516
194515
|
pdfInstance = null;
|
|
193517
194516
|
pdfjsWorker = pdf_worker_exports;
|
|
193518
194517
|
async getPDFFile(filePath) {
|
|
193519
|
-
log$
|
|
194518
|
+
log$2("Reading PDF file:", filePath);
|
|
193520
194519
|
const dataBuffer = await readFile(filePath);
|
|
193521
|
-
log$
|
|
194520
|
+
log$2("PDF file read successfully, size:", dataBuffer.length, "bytes");
|
|
193522
194521
|
const loadingTask = getDocument({
|
|
193523
194522
|
data: new Uint8Array(dataBuffer.buffer, dataBuffer.byteOffset, dataBuffer.length),
|
|
193524
194523
|
useSystemFonts: true
|
|
193525
194524
|
});
|
|
193526
|
-
log$
|
|
194525
|
+
log$2("PDF document loading task created");
|
|
193527
194526
|
const pdf = await loadingTask.promise;
|
|
193528
|
-
log$
|
|
194527
|
+
log$2("PDF document loaded successfully, pages:", pdf.numPages);
|
|
193529
194528
|
return pdf;
|
|
193530
194529
|
}
|
|
193531
194530
|
async loadPages(filePath) {
|
|
193532
|
-
log$
|
|
194531
|
+
log$2("Starting to load PDF pages from:", filePath);
|
|
193533
194532
|
try {
|
|
193534
194533
|
const pdf = await this.getPDFFile(filePath);
|
|
193535
194534
|
const pages = [];
|
|
193536
|
-
log$
|
|
194535
|
+
log$2(`Processing ${pdf.numPages} PDF pages`);
|
|
193537
194536
|
for (let i = 1; i <= pdf.numPages; i += 1) {
|
|
193538
|
-
log$
|
|
194537
|
+
log$2(`Loading page ${i}/${pdf.numPages}`);
|
|
193539
194538
|
const page = await pdf.getPage(i);
|
|
193540
194539
|
const content = await page.getTextContent();
|
|
193541
|
-
log$
|
|
194540
|
+
log$2(`Page ${i} text content retrieved, items:`, content.items.length);
|
|
193542
194541
|
let lastY;
|
|
193543
194542
|
const textItems = [];
|
|
193544
194543
|
for (const item of content.items) if ("str" in item) {
|
|
@@ -193549,23 +194548,23 @@ var init_pdf = __esmMin((() => {
|
|
|
193549
194548
|
const cleanedPageContent = textItems.join("").replaceAll("\0", "");
|
|
193550
194549
|
const lineCount = cleanedPageContent.split("\n").length;
|
|
193551
194550
|
const charCount = cleanedPageContent.length;
|
|
193552
|
-
log$
|
|
194551
|
+
log$2(`Page ${i} processed, lines: ${lineCount}, chars: ${charCount}`);
|
|
193553
194552
|
pages.push({
|
|
193554
194553
|
charCount,
|
|
193555
194554
|
lineCount,
|
|
193556
194555
|
metadata: { pageNumber: i },
|
|
193557
194556
|
pageContent: cleanedPageContent
|
|
193558
194557
|
});
|
|
193559
|
-
log$
|
|
194558
|
+
log$2(`Cleaning up page ${i} resources`);
|
|
193560
194559
|
page.cleanup();
|
|
193561
194560
|
}
|
|
193562
|
-
log$
|
|
194561
|
+
log$2("Cleaning up PDF document resources");
|
|
193563
194562
|
await pdf.destroy();
|
|
193564
|
-
log$
|
|
194563
|
+
log$2(`PDF loading completed for ${filePath}, total pages:`, pages.length);
|
|
193565
194564
|
return pages;
|
|
193566
194565
|
} catch (e) {
|
|
193567
194566
|
const error = e;
|
|
193568
|
-
log$
|
|
194567
|
+
log$2("Error encountered while loading PDF file");
|
|
193569
194568
|
console.error(`Error loading PDF file ${filePath} using pdfjs-dist: ${error.message}`, error.stack);
|
|
193570
194569
|
const errorPage = {
|
|
193571
194570
|
charCount: 0,
|
|
@@ -193576,7 +194575,7 @@ var init_pdf = __esmMin((() => {
|
|
|
193576
194575
|
},
|
|
193577
194576
|
pageContent: ""
|
|
193578
194577
|
};
|
|
193579
|
-
log$
|
|
194578
|
+
log$2("Created error page for failed PDF loading");
|
|
193580
194579
|
return [errorPage];
|
|
193581
194580
|
}
|
|
193582
194581
|
}
|
|
@@ -193587,25 +194586,25 @@ var init_pdf = __esmMin((() => {
|
|
|
193587
194586
|
* @returns Aggregated content as a string.
|
|
193588
194587
|
*/
|
|
193589
194588
|
async aggregateContent(pages) {
|
|
193590
|
-
log$
|
|
194589
|
+
log$2("Aggregating content from", pages.length, "PDF pages");
|
|
193591
194590
|
const validPages = pages.filter((page) => !page.metadata.error);
|
|
193592
|
-
log$
|
|
194591
|
+
log$2(`Found ${validPages.length} valid pages for aggregation (${pages.length - validPages.length} pages with errors filtered out)`);
|
|
193593
194592
|
const result = promptTemplate(validPages);
|
|
193594
|
-
log$
|
|
194593
|
+
log$2("PDF content aggregated successfully, length:", result.length);
|
|
193595
194594
|
return result;
|
|
193596
194595
|
}
|
|
193597
194596
|
async attachDocumentMetadata(filePath) {
|
|
193598
|
-
log$
|
|
194597
|
+
log$2("Attaching document metadata for PDF:", filePath);
|
|
193599
194598
|
const pdf = await this.getPDFFile(filePath);
|
|
193600
|
-
log$
|
|
194599
|
+
log$2("Getting PDF metadata");
|
|
193601
194600
|
const pdfMetadata = await pdf.getMetadata().catch((err) => {
|
|
193602
|
-
log$
|
|
194601
|
+
log$2("Error retrieving PDF metadata");
|
|
193603
194602
|
console.error(`Error getting PDF metadata: ${err.message}`);
|
|
193604
194603
|
return null;
|
|
193605
194604
|
}) ?? null;
|
|
193606
194605
|
const pdfInfo = pdfMetadata?.info ?? {};
|
|
193607
194606
|
const metadata = pdfMetadata?.metadata ?? null;
|
|
193608
|
-
log$
|
|
194607
|
+
log$2("PDF metadata retrieved:", {
|
|
193609
194608
|
hasInfo: !!Object.keys(pdfInfo).length,
|
|
193610
194609
|
hasMetadata: !!metadata
|
|
193611
194610
|
});
|
|
@@ -204656,11 +205655,11 @@ var init_parser_utils = __esmMin((() => {
|
|
|
204656
205655
|
//#endregion
|
|
204657
205656
|
//#region ../../packages/file-loaders/src/loaders/pptx/index.ts
|
|
204658
205657
|
var pptx_exports = /* @__PURE__ */ __exportAll({ PptxLoader: () => PptxLoader });
|
|
204659
|
-
var import_src
|
|
205658
|
+
var import_src, log$1, PptxLoader;
|
|
204660
205659
|
var init_pptx = __esmMin((() => {
|
|
204661
|
-
import_src
|
|
205660
|
+
import_src = /* @__PURE__ */ __toESM$2(require_src());
|
|
204662
205661
|
init_parser_utils();
|
|
204663
|
-
log$
|
|
205662
|
+
log$1 = (0, import_src.default)("file-loaders:pptx");
|
|
204664
205663
|
PptxLoader = class {
|
|
204665
205664
|
/**
|
|
204666
205665
|
* Loads pages from the specified PPTX file path.
|
|
@@ -204671,33 +205670,33 @@ var init_pptx = __esmMin((() => {
|
|
|
204671
205670
|
* `DocumentPage` object with error information in its metadata.
|
|
204672
205671
|
*/
|
|
204673
205672
|
async loadPages(filePath) {
|
|
204674
|
-
log$
|
|
205673
|
+
log$1("Loading PPTX file:", filePath);
|
|
204675
205674
|
const sourceFileName = path.basename(filePath);
|
|
204676
|
-
log$
|
|
205675
|
+
log$1("Source file name:", sourceFileName);
|
|
204677
205676
|
try {
|
|
204678
205677
|
const slidesRegex = /ppt\/slides\/slide\d+\.xml/g;
|
|
204679
205678
|
const slideNumberRegex = /slide(\d+)\.xml/;
|
|
204680
|
-
log$
|
|
205679
|
+
log$1("Extracting slide XML files from PPTX");
|
|
204681
205680
|
const slideFiles = await extractFiles(filePath, (fileName) => slidesRegex.test(fileName));
|
|
204682
|
-
log$
|
|
205681
|
+
log$1("Extracted slide files:", slideFiles.length);
|
|
204683
205682
|
if (slideFiles.length === 0) {
|
|
204684
|
-
log$
|
|
205683
|
+
log$1("No slide XML files found in the PPTX file");
|
|
204685
205684
|
console.warn(`No slide XML files found in ${sourceFileName}. May be corrupted or empty.`);
|
|
204686
205685
|
return [this.createErrorPage("No slides found. The PPTX file might be empty, corrupted, or does not contain standard slide XMLs.", sourceFileName)];
|
|
204687
205686
|
}
|
|
204688
|
-
log$
|
|
205687
|
+
log$1("Sorting slide files by slide number");
|
|
204689
205688
|
slideFiles.sort((a, b) => {
|
|
204690
205689
|
const matchA = a.path.match(slideNumberRegex);
|
|
204691
205690
|
const matchB = b.path.match(slideNumberRegex);
|
|
204692
205691
|
return (matchA ? parseInt(matchA[1], 10) : Infinity) - (matchB ? parseInt(matchB[1], 10) : Infinity);
|
|
204693
205692
|
});
|
|
204694
|
-
log$
|
|
204695
|
-
log$
|
|
205693
|
+
log$1("Slide files sorted");
|
|
205694
|
+
log$1("Creating document pages from slide files");
|
|
204696
205695
|
const pages = slideFiles.map((slideFile, index) => {
|
|
204697
205696
|
try {
|
|
204698
|
-
log$
|
|
205697
|
+
log$1(`Processing slide ${index + 1}/${slideFiles.length}, path: ${slideFile.path}`);
|
|
204699
205698
|
const paragraphNodes = parseString(slideFile.content).getElementsByTagName("a:p");
|
|
204700
|
-
log$
|
|
205699
|
+
log$1(`Found ${paragraphNodes.length} paragraph nodes in slide ${index + 1}`);
|
|
204701
205700
|
const slideText = Array.from(paragraphNodes).map((pNode) => {
|
|
204702
205701
|
const textNodes = pNode.getElementsByTagName("a:t");
|
|
204703
205702
|
return Array.from(textNodes).map((tNode) => tNode.childNodes[0] ? tNode.childNodes[0].nodeValue : "").join("");
|
|
@@ -204705,7 +205704,7 @@ var init_pptx = __esmMin((() => {
|
|
|
204705
205704
|
const lines = slideText.split("\n");
|
|
204706
205705
|
const slideNumberMatch = slideFile.path.match(slideNumberRegex);
|
|
204707
205706
|
const slideNumber = slideNumberMatch ? parseInt(slideNumberMatch[1], 10) : index + 1;
|
|
204708
|
-
log$
|
|
205707
|
+
log$1(`Slide ${index + 1} text extracted, lines: ${lines.length}, characters: ${slideText.length}`);
|
|
204709
205708
|
const metadata = {
|
|
204710
205709
|
pageCount: slideFiles.length,
|
|
204711
205710
|
slideNumber,
|
|
@@ -204718,26 +205717,26 @@ var init_pptx = __esmMin((() => {
|
|
|
204718
205717
|
pageContent: slideText.trim()
|
|
204719
205718
|
};
|
|
204720
205719
|
} catch (parseError) {
|
|
204721
|
-
log$
|
|
205720
|
+
log$1(`Error parsing slide ${slideFile.path}`);
|
|
204722
205721
|
console.error(`Failed to parse XML for slide ${slideFile.path} in ${sourceFileName}: ${parseError instanceof Error ? parseError.message : String(parseError)}`);
|
|
204723
205722
|
return this.createErrorPage(`Error parsing slide ${slideFile.path}: ${parseError instanceof Error ? parseError.message : String(parseError)}`, sourceFileName, slideFile.path);
|
|
204724
205723
|
}
|
|
204725
205724
|
}).filter((page) => page !== null);
|
|
204726
|
-
log$
|
|
205725
|
+
log$1(`Created ${pages.length} document pages from slides`);
|
|
204727
205726
|
if (pages.length === 0) {
|
|
204728
|
-
log$
|
|
205727
|
+
log$1("Parsing resulted in zero valid pages");
|
|
204729
205728
|
console.warn(`Parsing resulted in zero valid pages for ${sourceFileName}`);
|
|
204730
205729
|
return [this.createErrorPage("Parsing resulted in zero valid pages.", sourceFileName)];
|
|
204731
205730
|
}
|
|
204732
205731
|
if (pages.every((page) => page.metadata?.error)) {
|
|
204733
|
-
log$
|
|
205732
|
+
log$1("All slides failed to parse");
|
|
204734
205733
|
console.warn(`All slides failed to parse for ${sourceFileName}`);
|
|
204735
205734
|
return [this.createErrorPage("All slides failed to parse correctly.", sourceFileName)];
|
|
204736
205735
|
}
|
|
204737
|
-
log$
|
|
205736
|
+
log$1("PPTX loading completed successfully");
|
|
204738
205737
|
return pages;
|
|
204739
205738
|
} catch (error) {
|
|
204740
|
-
log$
|
|
205739
|
+
log$1("Error loading or processing PPTX file");
|
|
204741
205740
|
const errorMessage = `Failed to load or process PPTX file: ${error instanceof Error ? error.message : String(error)}`;
|
|
204742
205741
|
console.error(errorMessage, { filePath });
|
|
204743
205742
|
return [this.createErrorPage(errorMessage, sourceFileName)];
|
|
@@ -204753,11 +205752,11 @@ var init_pptx = __esmMin((() => {
|
|
|
204753
205752
|
* @returns A Promise resolving to the aggregated content string.
|
|
204754
205753
|
*/
|
|
204755
205754
|
async aggregateContent(pages) {
|
|
204756
|
-
log$
|
|
205755
|
+
log$1("Aggregating content from", pages.length, "PPTX pages");
|
|
204757
205756
|
const validPages = pages.filter((page) => !page.metadata?.error);
|
|
204758
|
-
log$
|
|
205757
|
+
log$1(`Found ${validPages.length} valid pages for aggregation (${pages.length - validPages.length} error pages filtered out)`);
|
|
204759
205758
|
if (validPages.length === 0) {
|
|
204760
|
-
log$
|
|
205759
|
+
log$1("No valid pages found, returning content of first page (may be error page)");
|
|
204761
205760
|
return pages[0]?.pageContent || "";
|
|
204762
205761
|
}
|
|
204763
205762
|
const result = validPages.map((page) => {
|
|
@@ -204766,7 +205765,7 @@ var init_pptx = __esmMin((() => {
|
|
|
204766
205765
|
${page.pageContent}
|
|
204767
205766
|
</slide_page>`;
|
|
204768
205767
|
}).join("\n\n");
|
|
204769
|
-
log$
|
|
205768
|
+
log$1("PPTX content aggregated successfully, length:", result.length);
|
|
204770
205769
|
return result;
|
|
204771
205770
|
}
|
|
204772
205771
|
/**
|
|
@@ -204778,7 +205777,7 @@ ${page.pageContent}
|
|
|
204778
205777
|
* @returns A `DocumentPage` object representing the error state.
|
|
204779
205778
|
*/
|
|
204780
205779
|
createErrorPage(errorInfo, sourceFileName, sourceFilePath) {
|
|
204781
|
-
log$
|
|
205780
|
+
log$1("Creating error page:", errorInfo);
|
|
204782
205781
|
return {
|
|
204783
205782
|
charCount: 0,
|
|
204784
205783
|
lineCount: 0,
|
|
@@ -204794,104 +205793,6 @@ ${page.pageContent}
|
|
|
204794
205793
|
};
|
|
204795
205794
|
}));
|
|
204796
205795
|
|
|
204797
|
-
//#endregion
|
|
204798
|
-
//#region ../../packages/file-loaders/src/loaders/text/index.ts
|
|
204799
|
-
var text_exports = /* @__PURE__ */ __exportAll({ TextLoader: () => TextLoader });
|
|
204800
|
-
var import_src$1, log$1, HEURISTIC_SAMPLE_BYTES, HEURISTIC_THRESHOLD, detectUtf16NoBom, readTextFile, TextLoader;
|
|
204801
|
-
var init_text = __esmMin((() => {
|
|
204802
|
-
import_src$1 = /* @__PURE__ */ __toESM$2(require_src());
|
|
204803
|
-
log$1 = (0, import_src$1.default)("file-loaders:text");
|
|
204804
|
-
HEURISTIC_SAMPLE_BYTES = 512;
|
|
204805
|
-
HEURISTIC_THRESHOLD = .3;
|
|
204806
|
-
detectUtf16NoBom = (buffer) => {
|
|
204807
|
-
const sample = buffer.subarray(0, Math.min(HEURISTIC_SAMPLE_BYTES, buffer.length));
|
|
204808
|
-
if (sample.length < 4 || sample.length % 2 !== 0) return null;
|
|
204809
|
-
let leAsciiPairs = 0;
|
|
204810
|
-
let beAsciiPairs = 0;
|
|
204811
|
-
const totalPairs = sample.length / 2;
|
|
204812
|
-
for (let i = 0; i < sample.length; i += 2) {
|
|
204813
|
-
const lo = sample[i];
|
|
204814
|
-
const hi = sample[i + 1];
|
|
204815
|
-
if (hi === 0 && lo !== 0) leAsciiPairs++;
|
|
204816
|
-
else if (lo === 0 && hi !== 0) beAsciiPairs++;
|
|
204817
|
-
}
|
|
204818
|
-
if (leAsciiPairs > beAsciiPairs && leAsciiPairs / totalPairs >= HEURISTIC_THRESHOLD) return "utf-16le";
|
|
204819
|
-
if (beAsciiPairs > leAsciiPairs && beAsciiPairs / totalPairs >= HEURISTIC_THRESHOLD) return "utf-16be";
|
|
204820
|
-
return null;
|
|
204821
|
-
};
|
|
204822
|
-
readTextFile = async (filePath) => {
|
|
204823
|
-
const buffer = await readFile(filePath);
|
|
204824
|
-
if (buffer.length >= 2 && buffer[0] === 255 && buffer[1] === 254) {
|
|
204825
|
-
log$1("Detected UTF-16LE BOM");
|
|
204826
|
-
return new TextDecoder("utf-16le").decode(buffer.subarray(2));
|
|
204827
|
-
}
|
|
204828
|
-
if (buffer.length >= 2 && buffer[0] === 254 && buffer[1] === 255) {
|
|
204829
|
-
log$1("Detected UTF-16BE BOM");
|
|
204830
|
-
return new TextDecoder("utf-16be").decode(buffer.subarray(2));
|
|
204831
|
-
}
|
|
204832
|
-
if (buffer.length >= 3 && buffer[0] === 239 && buffer[1] === 187 && buffer[2] === 191) {
|
|
204833
|
-
log$1("Detected UTF-8 BOM");
|
|
204834
|
-
return buffer.subarray(3).toString("utf8");
|
|
204835
|
-
}
|
|
204836
|
-
const variant = detectUtf16NoBom(buffer);
|
|
204837
|
-
if (variant) {
|
|
204838
|
-
log$1("Detected %s without BOM (heuristic)", variant);
|
|
204839
|
-
return new TextDecoder(variant).decode(buffer);
|
|
204840
|
-
}
|
|
204841
|
-
return buffer.toString("utf8");
|
|
204842
|
-
};
|
|
204843
|
-
TextLoader = class {
|
|
204844
|
-
async loadPages(filePath) {
|
|
204845
|
-
log$1("Loading text file:", filePath);
|
|
204846
|
-
try {
|
|
204847
|
-
const fileContent = await readTextFile(filePath);
|
|
204848
|
-
log$1("Text file loaded successfully, size:", fileContent.length, "bytes");
|
|
204849
|
-
const lineCount = fileContent.split("\n").length;
|
|
204850
|
-
const charCount = fileContent.length;
|
|
204851
|
-
log$1("Text file stats:", {
|
|
204852
|
-
charCount,
|
|
204853
|
-
lineCount
|
|
204854
|
-
});
|
|
204855
|
-
const page = {
|
|
204856
|
-
charCount,
|
|
204857
|
-
lineCount,
|
|
204858
|
-
metadata: {
|
|
204859
|
-
lineNumberEnd: lineCount,
|
|
204860
|
-
lineNumberStart: 1
|
|
204861
|
-
},
|
|
204862
|
-
pageContent: fileContent
|
|
204863
|
-
};
|
|
204864
|
-
log$1("Text page created successfully");
|
|
204865
|
-
return [page];
|
|
204866
|
-
} catch (e) {
|
|
204867
|
-
const error = e;
|
|
204868
|
-
log$1("Error encountered while loading text file");
|
|
204869
|
-
console.error(`Error loading text file ${filePath}: ${error.message}`);
|
|
204870
|
-
const errorPage = {
|
|
204871
|
-
charCount: 0,
|
|
204872
|
-
lineCount: 0,
|
|
204873
|
-
metadata: { error: `Failed to load text file: ${error.message}` },
|
|
204874
|
-
pageContent: ""
|
|
204875
|
-
};
|
|
204876
|
-
log$1("Created error page for failed text file loading");
|
|
204877
|
-
return [errorPage];
|
|
204878
|
-
}
|
|
204879
|
-
}
|
|
204880
|
-
/**
|
|
204881
|
-
* For plain text, simply concatenate the content of all pages.
|
|
204882
|
-
* (Although TextLoader typically has only one page, this maintains interface consistency)
|
|
204883
|
-
* @param pages Array of pages
|
|
204884
|
-
* @returns Aggregated content
|
|
204885
|
-
*/
|
|
204886
|
-
async aggregateContent(pages) {
|
|
204887
|
-
log$1("Aggregating content from", pages.length, "text pages");
|
|
204888
|
-
const result = pages.map((page) => page.pageContent).join("\n");
|
|
204889
|
-
log$1("Content aggregated successfully, length:", result.length);
|
|
204890
|
-
return result;
|
|
204891
|
-
}
|
|
204892
|
-
};
|
|
204893
|
-
}));
|
|
204894
|
-
|
|
204895
205796
|
//#endregion
|
|
204896
205797
|
//#region ../../packages/file-loaders/src/loaders/index.ts
|
|
204897
205798
|
const lazyFileLoaders = {
|
|
@@ -204923,23 +205824,18 @@ const lazyFileLoaders = {
|
|
|
204923
205824
|
pptx: async () => {
|
|
204924
205825
|
const { PptxLoader } = await Promise.resolve().then(() => (init_pptx(), pptx_exports));
|
|
204925
205826
|
return PptxLoader;
|
|
204926
|
-
},
|
|
204927
|
-
txt: async () => {
|
|
204928
|
-
const { TextLoader } = await Promise.resolve().then(() => (init_text(), text_exports));
|
|
204929
|
-
return TextLoader;
|
|
204930
205827
|
}
|
|
204931
205828
|
};
|
|
204932
205829
|
/**
|
|
204933
205830
|
* Get a file loader class for the specified file type.
|
|
204934
205831
|
* Uses dynamic imports to avoid loading heavy dependencies (like pdfjs-dist) until needed.
|
|
204935
|
-
*
|
|
205832
|
+
* TextLoader is returned synchronously (statically imported) for `txt` and as the
|
|
205833
|
+
* fallback for unknown types.
|
|
204936
205834
|
*/
|
|
204937
205835
|
const getFileLoader = async (fileType) => {
|
|
205836
|
+
if (fileType === "txt") return TextLoader;
|
|
204938
205837
|
const loaderFactory = lazyFileLoaders[fileType];
|
|
204939
|
-
if (!loaderFactory)
|
|
204940
|
-
const { TextLoader } = await Promise.resolve().then(() => (init_text(), text_exports));
|
|
204941
|
-
return TextLoader;
|
|
204942
|
-
}
|
|
205838
|
+
if (!loaderFactory) return TextLoader;
|
|
204943
205839
|
return loaderFactory();
|
|
204944
205840
|
};
|
|
204945
205841
|
|
|
@@ -204951,6 +205847,8 @@ const TEXT_READABLE_FILE_TYPES = [
|
|
|
204951
205847
|
"markdown",
|
|
204952
205848
|
"mdx",
|
|
204953
205849
|
"json",
|
|
205850
|
+
"jsonc",
|
|
205851
|
+
"json5",
|
|
204954
205852
|
"xml",
|
|
204955
205853
|
"yaml",
|
|
204956
205854
|
"yml",
|
|
@@ -204959,6 +205857,8 @@ const TEXT_READABLE_FILE_TYPES = [
|
|
|
204959
205857
|
"cfg",
|
|
204960
205858
|
"conf",
|
|
204961
205859
|
"csv",
|
|
205860
|
+
"env",
|
|
205861
|
+
"properties",
|
|
204962
205862
|
"html",
|
|
204963
205863
|
"htm",
|
|
204964
205864
|
"css",
|
|
@@ -204969,6 +205869,9 @@ const TEXT_READABLE_FILE_TYPES = [
|
|
|
204969
205869
|
"ts",
|
|
204970
205870
|
"tsx",
|
|
204971
205871
|
"mjs",
|
|
205872
|
+
"cjs",
|
|
205873
|
+
"mts",
|
|
205874
|
+
"cts",
|
|
204972
205875
|
"vue",
|
|
204973
205876
|
"svelte",
|
|
204974
205877
|
"svg",
|
|
@@ -204989,6 +205892,11 @@ const TEXT_READABLE_FILE_TYPES = [
|
|
|
204989
205892
|
"bash",
|
|
204990
205893
|
"bat",
|
|
204991
205894
|
"ps1",
|
|
205895
|
+
"lua",
|
|
205896
|
+
"dart",
|
|
205897
|
+
"scala",
|
|
205898
|
+
"groovy",
|
|
205899
|
+
"gradle",
|
|
204992
205900
|
"log",
|
|
204993
205901
|
"sql",
|
|
204994
205902
|
"patch",
|
|
@@ -204996,6 +205904,18 @@ const TEXT_READABLE_FILE_TYPES = [
|
|
|
204996
205904
|
"db"
|
|
204997
205905
|
];
|
|
204998
205906
|
/**
|
|
205907
|
+
* Extensions that have dedicated parsers in `loadFile`. These are not text but
|
|
205908
|
+
* are explicitly supported file types that we know how to extract text from.
|
|
205909
|
+
*/
|
|
205910
|
+
const SPECIAL_PARSED_FILE_TYPES = [
|
|
205911
|
+
"pdf",
|
|
205912
|
+
"doc",
|
|
205913
|
+
"docx",
|
|
205914
|
+
"xls",
|
|
205915
|
+
"xlsx",
|
|
205916
|
+
"pptx"
|
|
205917
|
+
];
|
|
205918
|
+
/**
|
|
204999
205919
|
* Determine if a file can be read as text based on its extension.
|
|
205000
205920
|
* @param fileType File extension (without the leading dot)
|
|
205001
205921
|
* @returns Whether the file is likely text-readable
|
|
@@ -205003,11 +205923,29 @@ const TEXT_READABLE_FILE_TYPES = [
|
|
|
205003
205923
|
function isTextReadableFile(fileType) {
|
|
205004
205924
|
return TEXT_READABLE_FILE_TYPES.includes(fileType.toLowerCase());
|
|
205005
205925
|
}
|
|
205926
|
+
/**
|
|
205927
|
+
* Whether the agent's `readFile` should be willing to attempt reading this
|
|
205928
|
+
* extension at all. True for known text formats and for the special parsed
|
|
205929
|
+
* binary formats (pdf/doc/etc.) that have dedicated loaders. Anything else —
|
|
205930
|
+
* `.bin`, `.zip`, `.b64`, `.exe`, … — should be hard-rejected before the file
|
|
205931
|
+
* is opened, to avoid feeding a binary blob to the LLM.
|
|
205932
|
+
*/
|
|
205933
|
+
function isReadableFileType(fileType) {
|
|
205934
|
+
const ext = fileType.toLowerCase();
|
|
205935
|
+
return TEXT_READABLE_FILE_TYPES.includes(ext) || SPECIAL_PARSED_FILE_TYPES.includes(ext);
|
|
205936
|
+
}
|
|
205006
205937
|
|
|
205007
205938
|
//#endregion
|
|
205008
205939
|
//#region ../../packages/file-loaders/src/loadFile.ts
|
|
205009
|
-
|
|
205010
|
-
|
|
205940
|
+
const log = (0, import_src$5.default)("file-loaders:loadFile");
|
|
205941
|
+
var UnsupportedFileTypeError = class extends Error {
|
|
205942
|
+
fileType;
|
|
205943
|
+
constructor(fileType, filename) {
|
|
205944
|
+
super(`Unsupported file type '${fileType || "unknown"}' for file '${filename}'.`);
|
|
205945
|
+
this.name = "UnsupportedFileTypeError";
|
|
205946
|
+
this.fileType = fileType;
|
|
205947
|
+
}
|
|
205948
|
+
};
|
|
205011
205949
|
/**
|
|
205012
205950
|
* Determines the file type based on the filename extension.
|
|
205013
205951
|
* @param filePath The path to the file.
|
|
@@ -205015,7 +205953,7 @@ const log = (0, import_src.default)("file-loaders:loadFile");
|
|
|
205015
205953
|
*/
|
|
205016
205954
|
const getFileType = (filePath) => {
|
|
205017
205955
|
log("Determining file type for:", filePath);
|
|
205018
|
-
const extension = path
|
|
205956
|
+
const extension = path.extname(filePath).toLowerCase().replace(".", "");
|
|
205019
205957
|
if (!extension) {
|
|
205020
205958
|
log("No extension found, treating as txt");
|
|
205021
205959
|
return "txt";
|
|
@@ -205070,8 +206008,8 @@ const loadFile = async (filePath, fileMetadata) => {
|
|
|
205070
206008
|
fsError = `Failed to access file stats: ${error.message}`;
|
|
205071
206009
|
}
|
|
205072
206010
|
log("Determining base file info");
|
|
205073
|
-
const fileExtension = path
|
|
205074
|
-
const baseFilename = path
|
|
206011
|
+
const fileExtension = path.extname(filePath).slice(1).toLowerCase();
|
|
206012
|
+
const baseFilename = path.basename(filePath);
|
|
205075
206013
|
const source = fileMetadata?.source ?? filePath;
|
|
205076
206014
|
const filename = fileMetadata?.filename ?? baseFilename;
|
|
205077
206015
|
const fileType = fileMetadata?.fileType ?? fileExtension;
|
|
@@ -205086,10 +206024,13 @@ const loadFile = async (filePath, fileMetadata) => {
|
|
|
205086
206024
|
});
|
|
205087
206025
|
const parserType = getFileType(filePath);
|
|
205088
206026
|
log("Parser type determined as:", parserType);
|
|
206027
|
+
if (!parserType && !fsError) {
|
|
206028
|
+
console.warn(`No specific loader found for file type '${fileType}'. Rejecting unsupported file type.`);
|
|
206029
|
+
throw new UnsupportedFileTypeError(fileType, filename);
|
|
206030
|
+
}
|
|
205089
206031
|
const LoaderClass = await getFileLoader(parserType ?? "txt");
|
|
205090
206032
|
log("Selected loader class:", LoaderClass.name);
|
|
205091
|
-
|
|
205092
|
-
let pages = [];
|
|
206033
|
+
let pages;
|
|
205093
206034
|
let aggregatedContent = "";
|
|
205094
206035
|
let loaderError;
|
|
205095
206036
|
let aggregationError;
|
|
@@ -205187,6 +206128,69 @@ const loadFile = async (filePath, fileMetadata) => {
|
|
|
205187
206128
|
return fileDocument;
|
|
205188
206129
|
};
|
|
205189
206130
|
|
|
206131
|
+
//#endregion
|
|
206132
|
+
//#region ../../packages/file-loaders/src/utils/isBinaryContent.ts
|
|
206133
|
+
const SNIFF_BYTES = 8192;
|
|
206134
|
+
const NON_PRINTABLE_THRESHOLD = .3;
|
|
206135
|
+
const hasUtf8Bom = (buf) => buf.length >= 3 && buf[0] === 239 && buf[1] === 187 && buf[2] === 191;
|
|
206136
|
+
const hasUtf16Bom = (buf) => buf.length >= 2 && (buf[0] === 255 && buf[1] === 254 || buf[0] === 254 && buf[1] === 255);
|
|
206137
|
+
/**
|
|
206138
|
+
* Heuristically determine if a buffer looks like binary data.
|
|
206139
|
+
*
|
|
206140
|
+
* - UTF-8 / UTF-16 BOM → text
|
|
206141
|
+
* - UTF-16 detected without BOM (Windows-style exports) → text, decoded for
|
|
206142
|
+
* the printable-ratio check
|
|
206143
|
+
* - Any null byte (and not UTF-16) → binary
|
|
206144
|
+
* - More than 30% of decoded chars are control or U+FFFD replacement → binary
|
|
206145
|
+
*
|
|
206146
|
+
* Note: this only catches truly binary content. Text-encoded blobs (e.g., a
|
|
206147
|
+
* single 27KB line of base64) will pass this check — the extension whitelist
|
|
206148
|
+
* and the post-load char cap are what stop those.
|
|
206149
|
+
*/
|
|
206150
|
+
const sniffBinaryBuffer = (buffer) => {
|
|
206151
|
+
if (buffer.length === 0) return { isBinary: false };
|
|
206152
|
+
if (hasUtf8Bom(buffer) || hasUtf16Bom(buffer)) return { isBinary: false };
|
|
206153
|
+
const utf16Variant = detectUtf16NoBom(buffer);
|
|
206154
|
+
if (utf16Variant) return checkPrintableRatio(new TextDecoder(utf16Variant, { fatal: false }).decode(buffer), buffer.length);
|
|
206155
|
+
if (buffer.includes(0)) return {
|
|
206156
|
+
isBinary: true,
|
|
206157
|
+
reason: "contains null byte"
|
|
206158
|
+
};
|
|
206159
|
+
return checkPrintableRatio(new TextDecoder("utf-8", { fatal: false }).decode(buffer), buffer.length);
|
|
206160
|
+
};
|
|
206161
|
+
const REPLACEMENT_CHAR = "�";
|
|
206162
|
+
const checkPrintableRatio = (text, sampledBytes) => {
|
|
206163
|
+
if (text.length === 0) return { isBinary: false };
|
|
206164
|
+
let suspect = 0;
|
|
206165
|
+
for (const ch of text) {
|
|
206166
|
+
if (ch === REPLACEMENT_CHAR) {
|
|
206167
|
+
suspect++;
|
|
206168
|
+
continue;
|
|
206169
|
+
}
|
|
206170
|
+
const code = ch.codePointAt(0);
|
|
206171
|
+
if (code < 32 && code !== 9 && code !== 10 && code !== 13) suspect++;
|
|
206172
|
+
}
|
|
206173
|
+
const ratio = suspect / text.length;
|
|
206174
|
+
if (ratio > NON_PRINTABLE_THRESHOLD) return {
|
|
206175
|
+
isBinary: true,
|
|
206176
|
+
reason: `${(ratio * 100).toFixed(1)}% non-printable chars in first ${sampledBytes} bytes`
|
|
206177
|
+
};
|
|
206178
|
+
return { isBinary: false };
|
|
206179
|
+
};
|
|
206180
|
+
/**
|
|
206181
|
+
* Read up to the leading 8KB of a file and run the binary heuristic on it.
|
|
206182
|
+
*/
|
|
206183
|
+
const sniffBinaryFile = async (filePath) => {
|
|
206184
|
+
const fd = await open(filePath, "r");
|
|
206185
|
+
try {
|
|
206186
|
+
const buffer = Buffer.alloc(SNIFF_BYTES);
|
|
206187
|
+
const { bytesRead } = await fd.read(buffer, 0, SNIFF_BYTES, 0);
|
|
206188
|
+
return sniffBinaryBuffer(buffer.subarray(0, bytesRead));
|
|
206189
|
+
} finally {
|
|
206190
|
+
await fd.close();
|
|
206191
|
+
}
|
|
206192
|
+
};
|
|
206193
|
+
|
|
205190
206194
|
//#endregion
|
|
205191
206195
|
//#region ../../packages/local-file-shell/src/file/list.ts
|
|
205192
206196
|
async function listLocalFiles({ path: rawPath, sortBy = "modifiedTime", sortOrder = "desc", limit = 100 }, options) {
|
|
@@ -205247,9 +206251,52 @@ async function listLocalFiles({ path: rawPath, sortBy = "modifiedTime", sortOrde
|
|
|
205247
206251
|
|
|
205248
206252
|
//#endregion
|
|
205249
206253
|
//#region ../../packages/local-file-shell/src/file/read.ts
|
|
206254
|
+
/** Hard cap on file size we will read into memory at all (10MB). */
|
|
206255
|
+
const MAX_FILE_SIZE_BYTES = 10 * 1024 * 1024;
|
|
206256
|
+
/** Cap on the total chars returned to the agent. */
|
|
206257
|
+
const MAX_OUTPUT_CHARS = 5e5;
|
|
206258
|
+
/** Cap on chars per line. Keeps a single 27KB base64 line from blowing up the response. */
|
|
206259
|
+
const MAX_LINE_CHARS = 8e3;
|
|
206260
|
+
const inferFileType = (filePath) => path.extname(filePath).toLowerCase().replace(".", "") || "unknown";
|
|
206261
|
+
const buildErrorResult = (filePath, message) => ({
|
|
206262
|
+
charCount: 0,
|
|
206263
|
+
content: message,
|
|
206264
|
+
createdTime: /* @__PURE__ */ new Date(),
|
|
206265
|
+
fileType: inferFileType(filePath),
|
|
206266
|
+
filename: path.basename(filePath),
|
|
206267
|
+
lineCount: 0,
|
|
206268
|
+
loc: [0, 0],
|
|
206269
|
+
modifiedTime: /* @__PURE__ */ new Date(),
|
|
206270
|
+
totalCharCount: 0,
|
|
206271
|
+
totalLineCount: 0
|
|
206272
|
+
});
|
|
206273
|
+
const truncateLine = (line) => {
|
|
206274
|
+
if (line.length <= MAX_LINE_CHARS) return {
|
|
206275
|
+
line,
|
|
206276
|
+
truncated: false
|
|
206277
|
+
};
|
|
206278
|
+
return {
|
|
206279
|
+
line: `${line.slice(0, MAX_LINE_CHARS)}… [line truncated: was ${line.length} chars, kept first ${MAX_LINE_CHARS}]`,
|
|
206280
|
+
truncated: true
|
|
206281
|
+
};
|
|
206282
|
+
};
|
|
205250
206283
|
async function readLocalFile({ path: rawPath, loc, fullContent }) {
|
|
205251
206284
|
const filePath = expandTilde(rawPath) ?? rawPath;
|
|
205252
206285
|
const effectiveLoc = fullContent ? void 0 : loc ?? [0, 200];
|
|
206286
|
+
let stats;
|
|
206287
|
+
try {
|
|
206288
|
+
stats = await stat(filePath);
|
|
206289
|
+
} catch (error) {
|
|
206290
|
+
return buildErrorResult(filePath, `Error accessing or processing file: ${error.message}`);
|
|
206291
|
+
}
|
|
206292
|
+
if (stats.isDirectory()) return buildErrorResult(filePath, "This is a directory and cannot be read as plain text.");
|
|
206293
|
+
if (stats.size > MAX_FILE_SIZE_BYTES) return buildErrorResult(filePath, `Error: File is too large to read (${stats.size} bytes, limit ${MAX_FILE_SIZE_BYTES}). Use grep / shell tools to inspect specific parts.`);
|
|
206294
|
+
const extension = path.extname(filePath).toLowerCase().replace(".", "");
|
|
206295
|
+
if (extension && !isReadableFileType(extension)) return buildErrorResult(filePath, `Error: Unsupported binary file type: .${extension}. Use a different tool (e.g., 'runCommand' with file/hexdump/strings) to inspect binary files.`);
|
|
206296
|
+
if (!SPECIAL_PARSED_FILE_TYPES.includes(extension)) try {
|
|
206297
|
+
const sniff = await sniffBinaryFile(filePath);
|
|
206298
|
+
if (sniff.isBinary) return buildErrorResult(filePath, `Error: File appears to be binary (${sniff.reason}). Refusing to read as text.`);
|
|
206299
|
+
} catch {}
|
|
205253
206300
|
try {
|
|
205254
206301
|
const fileDocument = await loadFile(filePath);
|
|
205255
206302
|
if (fileDocument.metadata?.error) return {
|
|
@@ -205267,59 +206314,45 @@ async function readLocalFile({ path: rawPath, loc, fullContent }) {
|
|
|
205267
206314
|
const lines = fileDocument.content.split("\n");
|
|
205268
206315
|
const totalLineCount = lines.length;
|
|
205269
206316
|
const totalCharCount = fileDocument.content.length;
|
|
205270
|
-
let
|
|
205271
|
-
let charCount;
|
|
205272
|
-
let lineCount;
|
|
206317
|
+
let workingLines;
|
|
205273
206318
|
let actualLoc;
|
|
205274
206319
|
if (effectiveLoc === void 0) {
|
|
205275
|
-
|
|
205276
|
-
charCount = totalCharCount;
|
|
205277
|
-
lineCount = totalLineCount;
|
|
206320
|
+
workingLines = lines;
|
|
205278
206321
|
actualLoc = [0, totalLineCount];
|
|
205279
206322
|
} else {
|
|
205280
206323
|
const [startLine, endLine] = effectiveLoc;
|
|
205281
|
-
|
|
205282
|
-
content = selectedLines.join("\n");
|
|
205283
|
-
charCount = content.length;
|
|
205284
|
-
lineCount = selectedLines.length;
|
|
206324
|
+
workingLines = lines.slice(startLine, endLine);
|
|
205285
206325
|
actualLoc = effectiveLoc;
|
|
205286
206326
|
}
|
|
205287
|
-
|
|
206327
|
+
let linesTruncated = 0;
|
|
206328
|
+
let content = workingLines.map((line) => {
|
|
206329
|
+
const r = truncateLine(line);
|
|
206330
|
+
if (r.truncated) linesTruncated++;
|
|
206331
|
+
return r.line;
|
|
206332
|
+
}).join("\n");
|
|
206333
|
+
let truncated = false;
|
|
206334
|
+
if (content.length > MAX_OUTPUT_CHARS) {
|
|
206335
|
+
const originalLength = content.length;
|
|
206336
|
+
content = `${content.slice(0, MAX_OUTPUT_CHARS)}\n[content truncated: response was ${originalLength} chars, kept first ${MAX_OUTPUT_CHARS}. Use a smaller line range or grep to narrow down.]`;
|
|
206337
|
+
truncated = true;
|
|
206338
|
+
}
|
|
205288
206339
|
const result = {
|
|
205289
|
-
charCount,
|
|
206340
|
+
charCount: content.length,
|
|
205290
206341
|
content,
|
|
205291
206342
|
createdTime: fileDocument.createdTime,
|
|
205292
|
-
fileType,
|
|
206343
|
+
fileType: fileDocument.fileType || "unknown",
|
|
205293
206344
|
filename: fileDocument.filename,
|
|
205294
|
-
lineCount,
|
|
206345
|
+
lineCount: workingLines.length,
|
|
205295
206346
|
loc: actualLoc,
|
|
205296
206347
|
modifiedTime: fileDocument.modifiedTime,
|
|
205297
206348
|
totalCharCount,
|
|
205298
206349
|
totalLineCount
|
|
205299
206350
|
};
|
|
205300
|
-
|
|
205301
|
-
|
|
205302
|
-
result.content = "This is a directory and cannot be read as plain text.";
|
|
205303
|
-
result.charCount = 0;
|
|
205304
|
-
result.lineCount = 0;
|
|
205305
|
-
result.totalCharCount = 0;
|
|
205306
|
-
result.totalLineCount = 0;
|
|
205307
|
-
}
|
|
205308
|
-
} catch {}
|
|
206351
|
+
if (truncated) result.truncated = true;
|
|
206352
|
+
if (linesTruncated > 0) result.linesTruncated = linesTruncated;
|
|
205309
206353
|
return result;
|
|
205310
206354
|
} catch (error) {
|
|
205311
|
-
return {
|
|
205312
|
-
charCount: 0,
|
|
205313
|
-
content: `Error accessing or processing file: ${error.message}`,
|
|
205314
|
-
createdTime: /* @__PURE__ */ new Date(),
|
|
205315
|
-
fileType: path.extname(filePath).toLowerCase().replace(".", "") || "unknown",
|
|
205316
|
-
filename: path.basename(filePath),
|
|
205317
|
-
lineCount: 0,
|
|
205318
|
-
loc: [0, 0],
|
|
205319
|
-
modifiedTime: /* @__PURE__ */ new Date(),
|
|
205320
|
-
totalCharCount: 0,
|
|
205321
|
-
totalLineCount: 0
|
|
205322
|
-
};
|
|
206355
|
+
return buildErrorResult(filePath, `Error accessing or processing file: ${error.message}`);
|
|
205323
206356
|
}
|
|
205324
206357
|
}
|
|
205325
206358
|
|
|
@@ -205328,9 +206361,10 @@ async function readLocalFile({ path: rawPath, loc, fullContent }) {
|
|
|
205328
206361
|
async function searchLocalFiles({ keywords, directory, contentContains, limit = 30 }) {
|
|
205329
206362
|
try {
|
|
205330
206363
|
const cwd = expandTilde(directory) || process.cwd();
|
|
206364
|
+
const wantsHidden = keywords.startsWith(".");
|
|
205331
206365
|
let results = (await (0, import_out.default)(`**/*${keywords}*`, {
|
|
205332
206366
|
cwd,
|
|
205333
|
-
dot:
|
|
206367
|
+
dot: wantsHidden,
|
|
205334
206368
|
ignore: ["**/node_modules/**", "**/.git/**"]
|
|
205335
206369
|
})).map((f) => ({
|
|
205336
206370
|
name: path.basename(f),
|
|
@@ -205471,7 +206505,7 @@ async function runCommand$1({ command, cwd, description, env: extraEnv, run_in_b
|
|
|
205471
206505
|
cwd,
|
|
205472
206506
|
timeout
|
|
205473
206507
|
});
|
|
205474
|
-
const effectiveTimeout = Math.min(Math.max(timeout, 1e3),
|
|
206508
|
+
const effectiveTimeout = Math.min(Math.max(timeout, 1e3), 8e5);
|
|
205475
206509
|
const shellConfig = getShellConfig(command);
|
|
205476
206510
|
const childEnv = extraEnv ? {
|
|
205477
206511
|
...process.env,
|
|
@@ -205571,6 +206605,239 @@ async function runCommand$1({ command, cwd, description, env: extraEnv, run_in_b
|
|
|
205571
206605
|
}
|
|
205572
206606
|
}
|
|
205573
206607
|
|
|
206608
|
+
//#endregion
|
|
206609
|
+
//#region src/daemon/taskRegistry.ts
|
|
206610
|
+
function getRegistryPath() {
|
|
206611
|
+
return path.join(os.homedir(), ".lobehub", "task-registry.json");
|
|
206612
|
+
}
|
|
206613
|
+
function readRegistry() {
|
|
206614
|
+
try {
|
|
206615
|
+
return JSON.parse(fs.readFileSync(getRegistryPath(), "utf8"));
|
|
206616
|
+
} catch {
|
|
206617
|
+
return {};
|
|
206618
|
+
}
|
|
206619
|
+
}
|
|
206620
|
+
function writeRegistry(entries) {
|
|
206621
|
+
const dir = path.dirname(getRegistryPath());
|
|
206622
|
+
fs.mkdirSync(dir, {
|
|
206623
|
+
mode: 448,
|
|
206624
|
+
recursive: true
|
|
206625
|
+
});
|
|
206626
|
+
fs.writeFileSync(getRegistryPath(), JSON.stringify(entries, null, 2), { mode: 384 });
|
|
206627
|
+
}
|
|
206628
|
+
function saveTask(entry) {
|
|
206629
|
+
const registry = readRegistry();
|
|
206630
|
+
registry[entry.taskId] = entry;
|
|
206631
|
+
writeRegistry(registry);
|
|
206632
|
+
}
|
|
206633
|
+
function getTask(taskId) {
|
|
206634
|
+
return readRegistry()[taskId];
|
|
206635
|
+
}
|
|
206636
|
+
function removeTask(taskId) {
|
|
206637
|
+
const registry = readRegistry();
|
|
206638
|
+
delete registry[taskId];
|
|
206639
|
+
writeRegistry(registry);
|
|
206640
|
+
}
|
|
206641
|
+
|
|
206642
|
+
//#endregion
|
|
206643
|
+
//#region src/tools/heteroTask.ts
|
|
206644
|
+
const DEFAULT_HERMES_PORT = 3456;
|
|
206645
|
+
/** Resolve the absolute path to the `lh` binary to avoid PATH issues in child processes. */
|
|
206646
|
+
function resolveLhPath() {
|
|
206647
|
+
try {
|
|
206648
|
+
return execFileSync("which", ["lh"], { encoding: "utf8" }).trim();
|
|
206649
|
+
} catch {
|
|
206650
|
+
return "lh";
|
|
206651
|
+
}
|
|
206652
|
+
}
|
|
206653
|
+
/**
|
|
206654
|
+
* Check whether an openclaw session already exists for the given topicId.
|
|
206655
|
+
* The session key format is `agent:<agentId>:explicit:<sessionId>`.
|
|
206656
|
+
* Returns false on any error so that callers default to injecting the full protocol.
|
|
206657
|
+
*/
|
|
206658
|
+
function openclawSessionExists(agentId, topicId) {
|
|
206659
|
+
try {
|
|
206660
|
+
const raw = execFileSync("openclaw", [
|
|
206661
|
+
"sessions",
|
|
206662
|
+
"--agent",
|
|
206663
|
+
agentId,
|
|
206664
|
+
"--json"
|
|
206665
|
+
], { encoding: "utf8" });
|
|
206666
|
+
const data = JSON.parse(raw);
|
|
206667
|
+
const expectedKey = `agent:${agentId}:explicit:${topicId}`;
|
|
206668
|
+
return data.sessions?.some((s) => s.key === expectedKey) ?? false;
|
|
206669
|
+
} catch {
|
|
206670
|
+
return false;
|
|
206671
|
+
}
|
|
206672
|
+
}
|
|
206673
|
+
function getHermesPort() {
|
|
206674
|
+
const env = process.env.HERMES_GATEWAY_PORT;
|
|
206675
|
+
if (env) {
|
|
206676
|
+
const parsed = Number.parseInt(env, 10);
|
|
206677
|
+
if (!Number.isNaN(parsed)) return parsed;
|
|
206678
|
+
}
|
|
206679
|
+
return DEFAULT_HERMES_PORT;
|
|
206680
|
+
}
|
|
206681
|
+
async function isHermesGatewayRunning(port) {
|
|
206682
|
+
try {
|
|
206683
|
+
return (await fetch(`http://localhost:${port}/health`)).ok;
|
|
206684
|
+
} catch {
|
|
206685
|
+
return false;
|
|
206686
|
+
}
|
|
206687
|
+
}
|
|
206688
|
+
async function startHermesGateway(port) {
|
|
206689
|
+
spawn("hermes", ["gateway", "start"], {
|
|
206690
|
+
detached: true,
|
|
206691
|
+
env: { ...process.env },
|
|
206692
|
+
stdio: "ignore"
|
|
206693
|
+
}).unref();
|
|
206694
|
+
const deadline = Date.now() + 1e4;
|
|
206695
|
+
while (Date.now() < deadline) {
|
|
206696
|
+
await new Promise((r) => setTimeout(r, 500));
|
|
206697
|
+
if (await isHermesGatewayRunning(port)) return;
|
|
206698
|
+
}
|
|
206699
|
+
throw new Error(`Hermes gateway did not start within 10s on port ${port}`);
|
|
206700
|
+
}
|
|
206701
|
+
async function sendAutoNotify(topicId, taskId, text, agentId) {
|
|
206702
|
+
try {
|
|
206703
|
+
await (await getTrpcClient()).agentNotify.notify.mutate({
|
|
206704
|
+
agentId,
|
|
206705
|
+
content: text,
|
|
206706
|
+
role: "assistant",
|
|
206707
|
+
topicId
|
|
206708
|
+
});
|
|
206709
|
+
} catch (err) {
|
|
206710
|
+
log$7.error("Failed to send auto-notify:", err instanceof Error ? err.message : String(err));
|
|
206711
|
+
}
|
|
206712
|
+
}
|
|
206713
|
+
/**
|
|
206714
|
+
* Build the notify protocol injected into the first message of a new hetero-agent session.
|
|
206715
|
+
* Tells the agent how to push updates back to the LobeHub user via `lh notify`.
|
|
206716
|
+
*/
|
|
206717
|
+
function buildNotifyProtocol(lhPath, topicId) {
|
|
206718
|
+
return `## Context: This task was dispatched by LobeHub
|
|
206719
|
+
|
|
206720
|
+
This conversation / task was sent to you by the **LobeHub platform** on behalf of a user. You are running as a background agent; the user is waiting for your response inside the LobeHub chat interface.
|
|
206721
|
+
|
|
206722
|
+
**When to call notify**: any time you have something meaningful to tell the user — a key finding, a decision you made, a result, a question, or your final answer. Think of it as speaking directly to the user in the chat window.
|
|
206723
|
+
|
|
206724
|
+
**What to hide**: internal work details such as tool call sequences, file reads, intermediate command output, retries, or low-level reasoning steps. The user cares about outcomes and insights, not your step-by-step mechanics.
|
|
206725
|
+
|
|
206726
|
+
## Sending messages back to the user
|
|
206727
|
+
|
|
206728
|
+
Use the \`${lhPath} notify\` command. All your updates appear as a **single message bubble** in the UI — create it once and update it in place.\n\n**Step 1 — Open the bubble on your first meaningful update** (captures the messageId):\n\`\`\`\nMSG_ID=$(${lhPath} notify --topic ${topicId} --role assistant --content "Starting..." --json | grep -o '"messageId":"[^"]*"' | cut -d'"' -f4)\n\`\`\`\n\n**Step 2 — Update the same bubble as you make progress**:\n\`\`\`\n${lhPath} notify --topic ${topicId} --role assistant --message-id "$MSG_ID" --content "Still working..."\n\`\`\`\n\n**Step 3 — Replace with your complete, final response when done**:\n\`\`\`\n${lhPath} notify --topic ${topicId} --role assistant --message-id "$MSG_ID" --content "<your full response here>"\n\`\`\`\n\nRules:\n- Always use \`--json\` on the first call and capture \`messageId\` from the output.\n- Always pass \`--message-id\` on every subsequent call so updates overwrite the same bubble.\n- Write what matters to the user — not implementation steps or internal tool calls.\n- Call notify at least once when the task is done, even if there were no intermediate updates.`;
|
|
206729
|
+
}
|
|
206730
|
+
async function runHeteroTask(params) {
|
|
206731
|
+
const { agentId, agentType, cwd, operationId, prompt, taskId, topicId } = params;
|
|
206732
|
+
const workDir = cwd || process.cwd();
|
|
206733
|
+
const lhPath = resolveLhPath();
|
|
206734
|
+
if (agentType === "openclaw") {
|
|
206735
|
+
const openclawAgent = process.env.OPENCLAW_AGENT_ID ?? "main";
|
|
206736
|
+
const child = spawn("openclaw", [
|
|
206737
|
+
"agent",
|
|
206738
|
+
"--agent",
|
|
206739
|
+
openclawAgent,
|
|
206740
|
+
"--session-id",
|
|
206741
|
+
topicId,
|
|
206742
|
+
"--message",
|
|
206743
|
+
!openclawSessionExists(openclawAgent, topicId) ? `${prompt}\n\n${buildNotifyProtocol(lhPath, topicId)}` : prompt,
|
|
206744
|
+
"--local"
|
|
206745
|
+
], {
|
|
206746
|
+
cwd: workDir,
|
|
206747
|
+
detached: true,
|
|
206748
|
+
env: { ...process.env },
|
|
206749
|
+
stdio: "ignore"
|
|
206750
|
+
});
|
|
206751
|
+
const pid = child.pid;
|
|
206752
|
+
if (pid === void 0) throw new Error("Failed to get PID for openclaw process");
|
|
206753
|
+
child.unref();
|
|
206754
|
+
saveTask({
|
|
206755
|
+
agentId,
|
|
206756
|
+
agentType,
|
|
206757
|
+
operationId,
|
|
206758
|
+
pid,
|
|
206759
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
206760
|
+
taskId,
|
|
206761
|
+
topicId
|
|
206762
|
+
});
|
|
206763
|
+
log$7.info(`OpenClaw task started: taskId=${taskId} pid=${pid} agent=${openclawAgent}`);
|
|
206764
|
+
child.on("close", (code, signal) => {
|
|
206765
|
+
removeTask(taskId);
|
|
206766
|
+
if (code !== 0 || signal !== null) sendAutoNotify(topicId, taskId, signal ? `Task cancelled (signal: ${signal})` : `Task failed (exit code: ${code})`, agentId);
|
|
206767
|
+
});
|
|
206768
|
+
return JSON.stringify({
|
|
206769
|
+
pid,
|
|
206770
|
+
taskId
|
|
206771
|
+
});
|
|
206772
|
+
}
|
|
206773
|
+
if (agentType === "hermes") {
|
|
206774
|
+
const port = getHermesPort();
|
|
206775
|
+
if (!await isHermesGatewayRunning(port)) {
|
|
206776
|
+
log$7.info(`Hermes gateway not running on port ${port}, starting...`);
|
|
206777
|
+
await startHermesGateway(port);
|
|
206778
|
+
}
|
|
206779
|
+
const res = await fetch(`http://localhost:${port}/message`, {
|
|
206780
|
+
body: JSON.stringify({
|
|
206781
|
+
content: prompt,
|
|
206782
|
+
operationId
|
|
206783
|
+
}),
|
|
206784
|
+
headers: { "Content-Type": "application/json" },
|
|
206785
|
+
method: "POST"
|
|
206786
|
+
});
|
|
206787
|
+
if (!res.ok) throw new Error(`Hermes gateway returned ${res.status}: ${await res.text()}`);
|
|
206788
|
+
saveTask({
|
|
206789
|
+
agentId,
|
|
206790
|
+
agentType,
|
|
206791
|
+
operationId,
|
|
206792
|
+
pid: 0,
|
|
206793
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
206794
|
+
taskId,
|
|
206795
|
+
topicId
|
|
206796
|
+
});
|
|
206797
|
+
log$7.info(`Hermes task dispatched: taskId=${taskId} operationId=${operationId}`);
|
|
206798
|
+
return JSON.stringify({
|
|
206799
|
+
operationId,
|
|
206800
|
+
taskId
|
|
206801
|
+
});
|
|
206802
|
+
}
|
|
206803
|
+
throw new Error(`Unsupported agentType: ${agentType}`);
|
|
206804
|
+
}
|
|
206805
|
+
async function cancelHeteroTask(params) {
|
|
206806
|
+
const { signal = "SIGINT", taskId } = params;
|
|
206807
|
+
const entry = getTask(taskId);
|
|
206808
|
+
if (!entry) return JSON.stringify({
|
|
206809
|
+
message: `No task found with taskId: ${taskId}`,
|
|
206810
|
+
success: false
|
|
206811
|
+
});
|
|
206812
|
+
if (entry.agentType === "hermes") {
|
|
206813
|
+
const port = getHermesPort();
|
|
206814
|
+
try {
|
|
206815
|
+
await fetch(`http://localhost:${port}/stop`, {
|
|
206816
|
+
body: JSON.stringify({ operationId: entry.operationId }),
|
|
206817
|
+
headers: { "Content-Type": "application/json" },
|
|
206818
|
+
method: "POST"
|
|
206819
|
+
});
|
|
206820
|
+
} catch (err) {
|
|
206821
|
+
log$7.warn(`Failed to send /stop to Hermes gateway: ${err instanceof Error ? err.message : String(err)}`);
|
|
206822
|
+
}
|
|
206823
|
+
removeTask(taskId);
|
|
206824
|
+
await sendAutoNotify(entry.topicId, taskId, "Task cancelled", entry.agentId);
|
|
206825
|
+
return JSON.stringify({ taskId });
|
|
206826
|
+
}
|
|
206827
|
+
try {
|
|
206828
|
+
process.kill(entry.pid, signal);
|
|
206829
|
+
} catch (err) {
|
|
206830
|
+
log$7.warn(`Failed to send ${signal} to pid ${entry.pid}: ${err instanceof Error ? err.message : String(err)}`);
|
|
206831
|
+
removeTask(taskId);
|
|
206832
|
+
await sendAutoNotify(entry.topicId, taskId, "Task already completed or cancelled", entry.agentId);
|
|
206833
|
+
}
|
|
206834
|
+
return JSON.stringify({
|
|
206835
|
+
pid: entry.pid,
|
|
206836
|
+
signal,
|
|
206837
|
+
taskId
|
|
206838
|
+
});
|
|
206839
|
+
}
|
|
206840
|
+
|
|
205574
206841
|
//#endregion
|
|
205575
206842
|
//#region src/tools/shell.ts
|
|
205576
206843
|
const processManager = new ShellProcessManager();
|
|
@@ -205593,6 +206860,7 @@ async function killCommand(params) {
|
|
|
205593
206860
|
//#endregion
|
|
205594
206861
|
//#region src/tools/index.ts
|
|
205595
206862
|
const methodMap = {
|
|
206863
|
+
cancelHeteroTask,
|
|
205596
206864
|
editFile: editLocalFile,
|
|
205597
206865
|
getCommandOutput,
|
|
205598
206866
|
globFiles: globLocalFiles,
|
|
@@ -205601,6 +206869,7 @@ const methodMap = {
|
|
|
205601
206869
|
listFiles: listLocalFiles,
|
|
205602
206870
|
readFile: readLocalFile,
|
|
205603
206871
|
runCommand,
|
|
206872
|
+
runHeteroTask,
|
|
205604
206873
|
searchFiles: searchLocalFiles,
|
|
205605
206874
|
writeFile: writeLocalFile,
|
|
205606
206875
|
editLocalFile,
|
|
@@ -205939,141 +207208,6 @@ function collectSystemInfo() {
|
|
|
205939
207208
|
};
|
|
205940
207209
|
}
|
|
205941
207210
|
|
|
205942
|
-
//#endregion
|
|
205943
|
-
//#region src/commands/cron.ts
|
|
205944
|
-
function registerCronCommand(program) {
|
|
205945
|
-
const cron = program.command("cron").description("Manage agent cron jobs");
|
|
205946
|
-
cron.command("list").description("List cron jobs").option("--agent-id <id>", "Filter by agent ID").option("--enabled", "Only show enabled jobs").option("--disabled", "Only show disabled jobs").option("-L, --limit <n>", "Page size", "20").option("--offset <n>", "Offset", "0").option("--json [fields]", "Output JSON, optionally specify fields (comma-separated)").action(async (options) => {
|
|
205947
|
-
const client = await getTrpcClient();
|
|
205948
|
-
const input = {};
|
|
205949
|
-
if (options.agentId) input.agentId = options.agentId;
|
|
205950
|
-
if (options.enabled) input.enabled = true;
|
|
205951
|
-
if (options.disabled) input.enabled = false;
|
|
205952
|
-
if (options.limit) input.limit = Number.parseInt(options.limit, 10);
|
|
205953
|
-
if (options.offset) input.offset = Number.parseInt(options.offset, 10);
|
|
205954
|
-
const items = (await client.agentCronJob.list.query(input)).data ?? [];
|
|
205955
|
-
if (options.json !== void 0) {
|
|
205956
|
-
outputJson(items, typeof options.json === "string" ? options.json : void 0);
|
|
205957
|
-
return;
|
|
205958
|
-
}
|
|
205959
|
-
if (items.length === 0) {
|
|
205960
|
-
console.log("No cron jobs found.");
|
|
205961
|
-
return;
|
|
205962
|
-
}
|
|
205963
|
-
printTable(items.map((j) => [
|
|
205964
|
-
j.id || "",
|
|
205965
|
-
truncate(j.name || "", 30),
|
|
205966
|
-
j.schedule || "",
|
|
205967
|
-
j.enabled ? import_picocolors.default.green("enabled") : import_picocolors.default.dim("disabled"),
|
|
205968
|
-
`${j.executionCount ?? 0}/${j.maxExecutions ?? "∞"}`,
|
|
205969
|
-
j.updatedAt ? timeAgo(j.updatedAt) : ""
|
|
205970
|
-
]), [
|
|
205971
|
-
"ID",
|
|
205972
|
-
"NAME",
|
|
205973
|
-
"SCHEDULE",
|
|
205974
|
-
"STATUS",
|
|
205975
|
-
"EXECUTIONS",
|
|
205976
|
-
"UPDATED"
|
|
205977
|
-
]);
|
|
205978
|
-
});
|
|
205979
|
-
cron.command("view <id>").description("View cron job details").option("--json [fields]", "Output JSON").action(async (id, options) => {
|
|
205980
|
-
const job = (await (await getTrpcClient()).agentCronJob.findById.query({ id })).data;
|
|
205981
|
-
if (options.json !== void 0) {
|
|
205982
|
-
outputJson(job, typeof options.json === "string" ? options.json : void 0);
|
|
205983
|
-
return;
|
|
205984
|
-
}
|
|
205985
|
-
if (!job) {
|
|
205986
|
-
log$7.error("Cron job not found.");
|
|
205987
|
-
process.exit(1);
|
|
205988
|
-
}
|
|
205989
|
-
console.log(`${import_picocolors.default.bold("ID:")} ${job.id}`);
|
|
205990
|
-
console.log(`${import_picocolors.default.bold("Name:")} ${job.name || ""}`);
|
|
205991
|
-
console.log(`${import_picocolors.default.bold("Agent ID:")} ${job.agentId || ""}`);
|
|
205992
|
-
console.log(`${import_picocolors.default.bold("Schedule:")} ${job.schedule || ""}`);
|
|
205993
|
-
console.log(`${import_picocolors.default.bold("Status:")} ${job.enabled ? import_picocolors.default.green("enabled") : import_picocolors.default.dim("disabled")}`);
|
|
205994
|
-
console.log(`${import_picocolors.default.bold("Executions:")} ${job.executionCount ?? 0}/${job.maxExecutions ?? "∞"}`);
|
|
205995
|
-
if (job.prompt) console.log(`${import_picocolors.default.bold("Prompt:")} ${truncate(job.prompt, 80)}`);
|
|
205996
|
-
if (job.createdAt) console.log(`${import_picocolors.default.bold("Created:")} ${timeAgo(job.createdAt)}`);
|
|
205997
|
-
if (job.updatedAt) console.log(`${import_picocolors.default.bold("Updated:")} ${timeAgo(job.updatedAt)}`);
|
|
205998
|
-
});
|
|
205999
|
-
cron.command("create").description("Create a cron job").requiredOption("--agent-id <id>", "Agent ID").requiredOption("-s, --schedule <cron>", "Cron schedule expression").option("-n, --name <name>", "Job name").option("-p, --prompt <prompt>", "Prompt text").option("--max-executions <n>", "Maximum number of executions").option("--json", "Output JSON").action(async (options) => {
|
|
206000
|
-
const client = await getTrpcClient();
|
|
206001
|
-
const input = {
|
|
206002
|
-
agentId: options.agentId,
|
|
206003
|
-
cronPattern: options.schedule
|
|
206004
|
-
};
|
|
206005
|
-
if (options.name) input.name = options.name;
|
|
206006
|
-
if (options.prompt) input.content = options.prompt;
|
|
206007
|
-
if (options.maxExecutions) input.maxExecutions = Number.parseInt(options.maxExecutions, 10);
|
|
206008
|
-
const result = await client.agentCronJob.create.mutate(input);
|
|
206009
|
-
if (options.json) {
|
|
206010
|
-
console.log(JSON.stringify(result, null, 2));
|
|
206011
|
-
return;
|
|
206012
|
-
}
|
|
206013
|
-
const data = result.data;
|
|
206014
|
-
console.log(`${import_picocolors.default.green("✓")} Created cron job ${import_picocolors.default.bold(data?.id || "")}`);
|
|
206015
|
-
});
|
|
206016
|
-
cron.command("edit <id>").description("Update a cron job").option("-n, --name <name>", "Job name").option("-s, --schedule <cron>", "Cron schedule expression").option("-p, --prompt <prompt>", "Prompt text").option("--max-executions <n>", "Maximum number of executions").option("--enable", "Enable the job").option("--disable", "Disable the job").action(async (id, options) => {
|
|
206017
|
-
const data = {};
|
|
206018
|
-
if (options.name) data.name = options.name;
|
|
206019
|
-
if (options.schedule) data.cronPattern = options.schedule;
|
|
206020
|
-
if (options.prompt) data.content = options.prompt;
|
|
206021
|
-
if (options.maxExecutions) data.maxExecutions = Number.parseInt(options.maxExecutions, 10);
|
|
206022
|
-
if (options.enable) data.enabled = true;
|
|
206023
|
-
if (options.disable) data.enabled = false;
|
|
206024
|
-
if (Object.keys(data).length === 0) {
|
|
206025
|
-
log$7.error("No changes specified. Use --name, --schedule, --prompt, --enable, or --disable.");
|
|
206026
|
-
process.exit(1);
|
|
206027
|
-
}
|
|
206028
|
-
await (await getTrpcClient()).agentCronJob.update.mutate({
|
|
206029
|
-
data,
|
|
206030
|
-
id
|
|
206031
|
-
});
|
|
206032
|
-
console.log(`${import_picocolors.default.green("✓")} Updated cron job ${import_picocolors.default.bold(id)}`);
|
|
206033
|
-
});
|
|
206034
|
-
cron.command("delete <id>").description("Delete a cron job").option("--yes", "Skip confirmation prompt").action(async (id, options) => {
|
|
206035
|
-
if (!options.yes) {
|
|
206036
|
-
if (!await confirm("Are you sure you want to delete this cron job?")) {
|
|
206037
|
-
console.log("Cancelled.");
|
|
206038
|
-
return;
|
|
206039
|
-
}
|
|
206040
|
-
}
|
|
206041
|
-
await (await getTrpcClient()).agentCronJob.delete.mutate({ id });
|
|
206042
|
-
console.log(`${import_picocolors.default.green("✓")} Deleted cron job ${import_picocolors.default.bold(id)}`);
|
|
206043
|
-
});
|
|
206044
|
-
cron.command("toggle <ids...>").description("Batch enable or disable cron jobs").option("--enable", "Enable the jobs").option("--disable", "Disable the jobs").action(async (ids, options) => {
|
|
206045
|
-
if (!options.enable && !options.disable) {
|
|
206046
|
-
log$7.error("Specify --enable or --disable.");
|
|
206047
|
-
process.exit(1);
|
|
206048
|
-
}
|
|
206049
|
-
const enabled = !!options.enable;
|
|
206050
|
-
const count = (await (await getTrpcClient()).agentCronJob.batchUpdateStatus.mutate({
|
|
206051
|
-
enabled,
|
|
206052
|
-
ids
|
|
206053
|
-
})).data?.updatedCount ?? ids.length;
|
|
206054
|
-
console.log(`${import_picocolors.default.green("✓")} ${enabled ? "Enabled" : "Disabled"} ${count} cron job(s)`);
|
|
206055
|
-
});
|
|
206056
|
-
cron.command("reset <id>").description("Reset execution count for a cron job").option("--max <n>", "Set new max executions").action(async (id, options) => {
|
|
206057
|
-
const client = await getTrpcClient();
|
|
206058
|
-
const input = { id };
|
|
206059
|
-
if (options.max) input.newMaxExecutions = Number.parseInt(options.max, 10);
|
|
206060
|
-
await client.agentCronJob.resetExecutions.mutate(input);
|
|
206061
|
-
console.log(`${import_picocolors.default.green("✓")} Reset execution count for ${import_picocolors.default.bold(id)}`);
|
|
206062
|
-
});
|
|
206063
|
-
cron.command("stats").description("Get cron job execution statistics").option("--json", "Output JSON").action(async (options) => {
|
|
206064
|
-
const stats = (await (await getTrpcClient()).agentCronJob.getStats.query()).data;
|
|
206065
|
-
if (options.json) {
|
|
206066
|
-
console.log(JSON.stringify(stats, null, 2));
|
|
206067
|
-
return;
|
|
206068
|
-
}
|
|
206069
|
-
if (!stats) {
|
|
206070
|
-
console.log("No statistics available.");
|
|
206071
|
-
return;
|
|
206072
|
-
}
|
|
206073
|
-
for (const [key, value] of Object.entries(stats)) console.log(`${import_picocolors.default.bold(key + ":")} ${value}`);
|
|
206074
|
-
});
|
|
206075
|
-
}
|
|
206076
|
-
|
|
206077
207211
|
//#endregion
|
|
206078
207212
|
//#region src/commands/device.ts
|
|
206079
207213
|
function registerDeviceCommand(program) {
|
|
@@ -207203,6 +208337,58 @@ function colorStatus(status) {
|
|
|
207203
208337
|
* regardless of whether they share a constant.
|
|
207204
208338
|
*/
|
|
207205
208339
|
const CC_TODO_WRITE_TOOL_NAME = "TodoWrite";
|
|
208340
|
+
/**
|
|
208341
|
+
* CC 2.1.143+ replaced the declarative {@link CC_TODO_WRITE_TOOL_NAME} with
|
|
208342
|
+
* an imperative trio: one task per `TaskCreate` (server-assigned numeric id),
|
|
208343
|
+
* field-merge mutations via `TaskUpdate`, and `TaskList` as the only
|
|
208344
|
+
* full-state read. The adapter accumulates these into a per-session map and
|
|
208345
|
+
* synthesizes the shared `pluginState.todos` shape on each task-tool
|
|
208346
|
+
* tool_result so the existing TodoProgress UI keeps working.
|
|
208347
|
+
*
|
|
208348
|
+
* The old TodoWrite path stays alongside — resumed sessions started on an
|
|
208349
|
+
* older CC may still emit it, and CC's recent SDK reminder text doesn't
|
|
208350
|
+
* forbid the model from using TodoWrite if it really wants to.
|
|
208351
|
+
*/
|
|
208352
|
+
const CC_TASK_CREATE_TOOL_NAME = "TaskCreate";
|
|
208353
|
+
const CC_TASK_UPDATE_TOOL_NAME = "TaskUpdate";
|
|
208354
|
+
const CC_TASK_LIST_TOOL_NAME = "TaskList";
|
|
208355
|
+
/**
|
|
208356
|
+
* tool_result confirmation emitted by CC for a successful `TaskCreate`.
|
|
208357
|
+
* Observed shape on CC 2.1.143: `Task #1 created successfully: <subject>`.
|
|
208358
|
+
* The numeric id is the only place we can read the CC-assigned handle —
|
|
208359
|
+
* `TaskCreate.input` itself does not echo it.
|
|
208360
|
+
*/
|
|
208361
|
+
const TASK_CREATE_RESULT_PATTERN = /^Task #(\d+) created successfully/;
|
|
208362
|
+
/**
|
|
208363
|
+
* tool_result confirmation emitted by CC for a successful `TaskUpdate`.
|
|
208364
|
+
* Suffix varies (`status`, blank if no field changed, etc.); we only need
|
|
208365
|
+
* the id to confirm the mutation landed — the field deltas are already
|
|
208366
|
+
* carried by the cached `TaskUpdate.input`.
|
|
208367
|
+
*/
|
|
208368
|
+
const TASK_UPDATE_RESULT_PATTERN = /^Updated task #\d+/;
|
|
208369
|
+
/**
|
|
208370
|
+
* One line of `TaskList`'s plain-text output: `#1 [in_progress] read hosts`.
|
|
208371
|
+
* Used as the resume reconciliation path — when this adapter joins a CC
|
|
208372
|
+
* session mid-stream and missed earlier Create / Update events, parsing
|
|
208373
|
+
* TaskList rebuilds id / subject / status. `activeForm` and `description`
|
|
208374
|
+
* cannot be recovered (CC omits them) so resumed in_progress tasks fall
|
|
208375
|
+
* back to the subject text, same as TodoWrite's content-fallback.
|
|
208376
|
+
*/
|
|
208377
|
+
const TASK_LIST_LINE_PATTERN = /^#(\d+) \[(pending|in_progress|completed)\] (.+)$/;
|
|
208378
|
+
/**
|
|
208379
|
+
* Tool name CC sees for the LobeHub-hosted MCP `ask_user_question` server.
|
|
208380
|
+
* Source of truth lives in `../askUser/constants.ts`; replicated here as a
|
|
208381
|
+
* literal so the adapter compiles in browser bundles without dragging in
|
|
208382
|
+
* any of the askUser package's runtime (node:http, MCP SDK, etc.) by
|
|
208383
|
+
* accident. Keep in sync.
|
|
208384
|
+
*/
|
|
208385
|
+
const ASK_USER_MCP_TOOL_NAME = "mcp__lobe_cc__ask_user_question";
|
|
208386
|
+
/**
|
|
208387
|
+
* apiName the adapter rewrites the MCP tool to so the renderer routes on
|
|
208388
|
+
* a stable key, not the wire-prefixed MCP name. Source of truth same as
|
|
208389
|
+
* above.
|
|
208390
|
+
*/
|
|
208391
|
+
const ASK_USER_API_NAME = "askUserQuestion";
|
|
207206
208392
|
const CLAUDE_CODE_CLI_INSTALL_DOCS_URL = "https://docs.anthropic.com/en/docs/claude-code/setup";
|
|
207207
208393
|
const CLI_AUTH_REQUIRED_PATTERNS = [
|
|
207208
208394
|
/failed to authenticate/i,
|
|
@@ -207260,24 +208446,40 @@ const getRateLimitTerminalError = (result, rateLimitInfo, apiErrorStatus) => {
|
|
|
207260
208446
|
stderr: rawMessage
|
|
207261
208447
|
};
|
|
207262
208448
|
};
|
|
208449
|
+
const toSynthesizedStatus = (status) => status === "in_progress" ? "processing" : status === "pending" ? "todo" : "completed";
|
|
208450
|
+
const synthesizeTodoWritePluginState = (args) => {
|
|
208451
|
+
return { todos: {
|
|
208452
|
+
items: (args.todos || []).map((todo) => {
|
|
208453
|
+
const text = todo.status === "in_progress" ? todo.activeForm || todo.content : todo.content;
|
|
208454
|
+
return {
|
|
208455
|
+
status: toSynthesizedStatus(todo.status),
|
|
208456
|
+
text
|
|
208457
|
+
};
|
|
208458
|
+
}),
|
|
208459
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
208460
|
+
} };
|
|
208461
|
+
};
|
|
207263
208462
|
/**
|
|
207264
|
-
*
|
|
207265
|
-
*
|
|
207266
|
-
*
|
|
207267
|
-
*
|
|
207268
|
-
*
|
|
207269
|
-
*
|
|
208463
|
+
* Snapshot the running `claudeCodeTasks` accumulator into the shared
|
|
208464
|
+
* `pluginState.todos` shape. Sorted by numeric id so the rendered order
|
|
208465
|
+
* matches CC's own TaskList output (insertion order = id order in practice,
|
|
208466
|
+
* but TaskUpdate can rearrange status without rearranging ids). Carries the
|
|
208467
|
+
* `id` per item so the TaskUpdate inspector can resolve `args.taskId` →
|
|
208468
|
+
* subject text without falling back to a `#N` label.
|
|
207270
208469
|
*
|
|
207271
|
-
*
|
|
207272
|
-
*
|
|
207273
|
-
*
|
|
208470
|
+
* Text resolution mirrors {@link synthesizeTodoWritePluginState}: use
|
|
208471
|
+
* `activeForm` while in progress so the spinner reads "Running tests"
|
|
208472
|
+
* rather than "Run tests"; fall back to `subject` whenever activeForm is
|
|
208473
|
+
* missing (TaskList-reconciled entries, or a TaskCreate that omitted it).
|
|
207274
208474
|
*/
|
|
207275
|
-
const
|
|
208475
|
+
const synthesizeTaskPluginState = (tasks) => {
|
|
207276
208476
|
return { todos: {
|
|
207277
|
-
items: (
|
|
208477
|
+
items: [...tasks.entries()].sort(([a], [b]) => Number(a) - Number(b)).map(([id, entry]) => {
|
|
208478
|
+
const text = entry.status === "in_progress" ? entry.activeForm || entry.subject : entry.subject;
|
|
207278
208479
|
return {
|
|
207279
|
-
|
|
207280
|
-
|
|
208480
|
+
id,
|
|
208481
|
+
status: toSynthesizedStatus(entry.status),
|
|
208482
|
+
text
|
|
207281
208483
|
};
|
|
207282
208484
|
}),
|
|
207283
208485
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -207338,10 +208540,10 @@ var ClaudeCodeAdapter = class {
|
|
|
207338
208540
|
* authoritative usage on `message_delta`.
|
|
207339
208541
|
*/
|
|
207340
208542
|
currentStreamEventModel;
|
|
207341
|
-
/**
|
|
207342
|
-
|
|
207343
|
-
/**
|
|
207344
|
-
|
|
208543
|
+
/** Cumulative text streamed via partial-message deltas, keyed by message.id. */
|
|
208544
|
+
streamedTextByMessageId = /* @__PURE__ */ new Map();
|
|
208545
|
+
/** Cumulative thinking streamed via partial-message deltas, keyed by message.id. */
|
|
208546
|
+
streamedThinkingByMessageId = /* @__PURE__ */ new Map();
|
|
207345
208547
|
/**
|
|
207346
208548
|
* Cumulative tool_use blocks per message.id. CC streams each tool_use in
|
|
207347
208549
|
* its OWN assistant event, and the handler's in-memory assistant.tools
|
|
@@ -207359,6 +208561,34 @@ var ClaudeCodeAdapter = class {
|
|
|
207359
208561
|
*/
|
|
207360
208562
|
todoWriteInputs = /* @__PURE__ */ new Map();
|
|
207361
208563
|
/**
|
|
208564
|
+
* Cached `TaskCreate.input` keyed by `tool_use.id`. Drained in `handleUser`
|
|
208565
|
+
* once the matching tool_result lands: at that point we parse the
|
|
208566
|
+
* CC-assigned numeric id from `Task #N created successfully` and push the
|
|
208567
|
+
* cached fields into {@link claudeCodeTasks}. Cleared even on error to
|
|
208568
|
+
* keep long sessions bounded — failed creates never reach the accumulator.
|
|
208569
|
+
*/
|
|
208570
|
+
taskCreateInputs = /* @__PURE__ */ new Map();
|
|
208571
|
+
/**
|
|
208572
|
+
* Cached `TaskUpdate.input` keyed by `tool_use.id`. Drained on
|
|
208573
|
+
* tool_result; on success the cached fields merge into the targeted entry
|
|
208574
|
+
* in {@link claudeCodeTasks}. `status: 'deleted'` removes the entry.
|
|
208575
|
+
*/
|
|
208576
|
+
taskUpdateInputs = /* @__PURE__ */ new Map();
|
|
208577
|
+
/**
|
|
208578
|
+
* Tool_use ids of `TaskList` calls awaiting their tool_result. Used to
|
|
208579
|
+
* dispatch the reconciliation parser without re-checking the tool name on
|
|
208580
|
+
* every user event. `TaskList.input` is empty so no payload to cache.
|
|
208581
|
+
*/
|
|
208582
|
+
pendingTaskListCalls = /* @__PURE__ */ new Set();
|
|
208583
|
+
/**
|
|
208584
|
+
* Adapter's running mirror of CC's task list, keyed by the CC-assigned
|
|
208585
|
+
* numeric task id. Survives across `result` events because CC keeps the
|
|
208586
|
+
* task list alive between turns within one session; cleared only when
|
|
208587
|
+
* the adapter is destroyed. This is what `synthesizeTaskPluginState`
|
|
208588
|
+
* snapshots on each task-tool tool_result.
|
|
208589
|
+
*/
|
|
208590
|
+
claudeCodeTasks = /* @__PURE__ */ new Map();
|
|
208591
|
+
/**
|
|
207362
208592
|
* Cached inputs for main-agent tool_uses keyed by their tool_use.id.
|
|
207363
208593
|
* Populated for every main-agent tool_use (not just `Task`) because
|
|
207364
208594
|
* CC uses multiple tool names for subagent delegation — real traces
|
|
@@ -207379,6 +208609,63 @@ var ClaudeCodeAdapter = class {
|
|
|
207379
208609
|
* recreate the Thread on every chunk.
|
|
207380
208610
|
*/
|
|
207381
208611
|
announcedSpawns = /* @__PURE__ */ new Set();
|
|
208612
|
+
/**
|
|
208613
|
+
* Tool name keyed by main-agent `tool_use.id`. Used to label the
|
|
208614
|
+
* resulting {@link ExternalSignalContext} when a Monitor-style task
|
|
208615
|
+
* fires a callback turn.
|
|
208616
|
+
*
|
|
208617
|
+
* Populated for every main-agent tool_use; subagent inner tools are
|
|
208618
|
+
* excluded because their tool_results route through `subagent.parentToolCallId`,
|
|
208619
|
+
* not the main-agent signal detector.
|
|
208620
|
+
*/
|
|
208621
|
+
mainToolNamesById = /* @__PURE__ */ new Map();
|
|
208622
|
+
/**
|
|
208623
|
+
* Active CC tasks (long-running tools registered via `system task_started`).
|
|
208624
|
+
* Keyed by `task_id`, carries the originating `tool_use_id`, the resolved
|
|
208625
|
+
* tool name, and a counter incremented for each signal callback turn
|
|
208626
|
+
* the adapter attributes to this task.
|
|
208627
|
+
*
|
|
208628
|
+
* A task lives from `task_started` until `task_notification` /
|
|
208629
|
+
* `task_completed`. While alive, any `message_start` that opens a turn
|
|
208630
|
+
* WITHOUT a preceding `user` event is a signal callback and gets tagged.
|
|
208631
|
+
*/
|
|
208632
|
+
activeTasks = /* @__PURE__ */ new Map();
|
|
208633
|
+
/**
|
|
208634
|
+
* True after a `user` event has been seen but the next turn hasn't yet
|
|
208635
|
+
* opened (`message_start` not yet fired). Carries the "this next turn
|
|
208636
|
+
* is a natural follow-up to a tool_result, not a signal callback"
|
|
208637
|
+
* intent across the gap between the tool_result event and the
|
|
208638
|
+
* resulting assistant turn.
|
|
208639
|
+
*
|
|
208640
|
+
* Reset to `false` once a `message_start` consumes it. After that, any
|
|
208641
|
+
* further `message_start` that opens while {@link activeTasks} is
|
|
208642
|
+
* non-empty is treated as a signal callback (CC re-invoked the LLM
|
|
208643
|
+
* because a long-running tool pushed an update).
|
|
208644
|
+
*/
|
|
208645
|
+
hasUnhandledUserInput = false;
|
|
208646
|
+
/**
|
|
208647
|
+
* {@link ExternalSignalContext} to attach to the NEXT `stream_start(newStep)`.
|
|
208648
|
+
*
|
|
208649
|
+
* Armed by `message_start` when {@link hasUnhandledUserInput} is false
|
|
208650
|
+
* AND {@link activeTasks} is non-empty — i.e. CC opened a new turn
|
|
208651
|
+
* without fresh user input while a long-running tool is alive. Cleared
|
|
208652
|
+
* on the next `tool_use` (LLM is back on the main chain).
|
|
208653
|
+
*/
|
|
208654
|
+
pendingExternalSignal;
|
|
208655
|
+
/**
|
|
208656
|
+
* Source-tool lineage of the most recently completed long-running task,
|
|
208657
|
+
* waiting to be stamped on the post-task summary turn with
|
|
208658
|
+
* `type: 'task-completion'`.
|
|
208659
|
+
*
|
|
208660
|
+
* Armed when `system task_notification` ends an active task; consumed
|
|
208661
|
+
* by the NEXT `message_start` that takes the natural-turn branch
|
|
208662
|
+
* (no other active task triggering a callback). Cleared on `result`
|
|
208663
|
+
* so it never leaks across LLM runs.
|
|
208664
|
+
*
|
|
208665
|
+
* Lets the renderer keep the summary inside the same AssistantGroup as
|
|
208666
|
+
* the preceding callbacks instead of letting it spawn a separate group.
|
|
208667
|
+
*/
|
|
208668
|
+
pendingTaskCompletion;
|
|
207382
208669
|
adapt(raw) {
|
|
207383
208670
|
if (!raw || typeof raw !== "object") return [];
|
|
207384
208671
|
switch (raw.type) {
|
|
@@ -207400,6 +208687,25 @@ var ClaudeCodeAdapter = class {
|
|
|
207400
208687
|
return events;
|
|
207401
208688
|
}
|
|
207402
208689
|
handleSystem(raw) {
|
|
208690
|
+
if (raw.subtype === "task_started" && raw.task_id && raw.tool_use_id) {
|
|
208691
|
+
const toolUseId = raw.tool_use_id;
|
|
208692
|
+
this.activeTasks.set(raw.task_id, {
|
|
208693
|
+
callbackCount: 0,
|
|
208694
|
+
sourceToolName: this.mainToolNamesById.get(toolUseId) ?? "unknown",
|
|
208695
|
+
toolUseId
|
|
208696
|
+
});
|
|
208697
|
+
return [];
|
|
208698
|
+
}
|
|
208699
|
+
if (raw.subtype === "task_notification" && raw.task_id) {
|
|
208700
|
+
const ending = this.activeTasks.get(raw.task_id);
|
|
208701
|
+
if (ending) this.pendingTaskCompletion = {
|
|
208702
|
+
sourceToolCallId: ending.toolUseId,
|
|
208703
|
+
sourceToolName: ending.sourceToolName
|
|
208704
|
+
};
|
|
208705
|
+
this.activeTasks.delete(raw.task_id);
|
|
208706
|
+
return [];
|
|
208707
|
+
}
|
|
208708
|
+
if (raw.subtype === "task_updated") return [];
|
|
207403
208709
|
if (raw.subtype !== "init") return [];
|
|
207404
208710
|
this.sessionId = raw.session_id;
|
|
207405
208711
|
this.started = true;
|
|
@@ -207428,9 +208734,10 @@ var ClaudeCodeAdapter = class {
|
|
|
207428
208734
|
case "thinking":
|
|
207429
208735
|
if (block.thinking) reasoningParts.push(block.thinking);
|
|
207430
208736
|
break;
|
|
207431
|
-
case "tool_use":
|
|
208737
|
+
case "tool_use": {
|
|
208738
|
+
const apiName = block.name === ASK_USER_MCP_TOOL_NAME ? ASK_USER_API_NAME : block.name;
|
|
207432
208739
|
newToolCalls.push({
|
|
207433
|
-
apiName
|
|
208740
|
+
apiName,
|
|
207434
208741
|
arguments: JSON.stringify(block.input || {}),
|
|
207435
208742
|
id: block.id,
|
|
207436
208743
|
identifier: "claude-code",
|
|
@@ -207438,19 +208745,29 @@ var ClaudeCodeAdapter = class {
|
|
|
207438
208745
|
});
|
|
207439
208746
|
this.pendingToolCalls.add(block.id);
|
|
207440
208747
|
if (block.input) this.mainToolInputsById.set(block.id, block.input);
|
|
208748
|
+
if (block.name) this.mainToolNamesById.set(block.id, block.name);
|
|
207441
208749
|
if (block.name === CC_TODO_WRITE_TOOL_NAME && block.input) this.todoWriteInputs.set(block.id, block.input);
|
|
208750
|
+
if (block.name === CC_TASK_CREATE_TOOL_NAME && block.input) this.taskCreateInputs.set(block.id, block.input);
|
|
208751
|
+
if (block.name === CC_TASK_UPDATE_TOOL_NAME && block.input) this.taskUpdateInputs.set(block.id, block.input);
|
|
208752
|
+
if (block.name === CC_TASK_LIST_TOOL_NAME) this.pendingTaskListCalls.add(block.id);
|
|
207442
208753
|
break;
|
|
208754
|
+
}
|
|
207443
208755
|
}
|
|
207444
|
-
|
|
207445
|
-
const
|
|
207446
|
-
|
|
208756
|
+
if (newToolCalls.length > 0) this.pendingExternalSignal = void 0;
|
|
208757
|
+
const textCompletion = this.getTrailingCompletion(messageId, textParts.join(""), this.streamedTextByMessageId);
|
|
208758
|
+
const thinkingCompletion = this.getTrailingCompletion(messageId, reasoningParts.join(""), this.streamedThinkingByMessageId);
|
|
208759
|
+
if (textCompletion) events.push(this.makeChunkEvent({
|
|
207447
208760
|
chunkType: "text",
|
|
207448
|
-
content:
|
|
208761
|
+
content: textCompletion
|
|
207449
208762
|
}));
|
|
207450
|
-
if (
|
|
208763
|
+
if (thinkingCompletion) events.push(this.makeChunkEvent({
|
|
207451
208764
|
chunkType: "reasoning",
|
|
207452
|
-
reasoning:
|
|
208765
|
+
reasoning: thinkingCompletion
|
|
207453
208766
|
}));
|
|
208767
|
+
if (messageId) this.clearStreamedBuffers(messageId, {
|
|
208768
|
+
thinking: reasoningParts.length > 0,
|
|
208769
|
+
text: textParts.length > 0
|
|
208770
|
+
});
|
|
207454
208771
|
events.push(...this.emitToolChunk(newToolCalls, messageId));
|
|
207455
208772
|
return events;
|
|
207456
208773
|
}
|
|
@@ -207500,9 +208817,10 @@ var ClaudeCodeAdapter = class {
|
|
|
207500
208817
|
case "thinking":
|
|
207501
208818
|
if (block.thinking) reasoningParts.push(block.thinking);
|
|
207502
208819
|
break;
|
|
207503
|
-
case "tool_use":
|
|
208820
|
+
case "tool_use": {
|
|
208821
|
+
const apiName = block.name === ASK_USER_MCP_TOOL_NAME ? ASK_USER_API_NAME : block.name;
|
|
207504
208822
|
newToolCalls.push({
|
|
207505
|
-
apiName
|
|
208823
|
+
apiName,
|
|
207506
208824
|
arguments: JSON.stringify(block.input || {}),
|
|
207507
208825
|
id: block.id,
|
|
207508
208826
|
identifier: "claude-code",
|
|
@@ -207511,6 +208829,7 @@ var ClaudeCodeAdapter = class {
|
|
|
207511
208829
|
this.pendingToolCalls.add(block.id);
|
|
207512
208830
|
if (block.name === CC_TODO_WRITE_TOOL_NAME && block.input) this.todoWriteInputs.set(block.id, block.input);
|
|
207513
208831
|
break;
|
|
208832
|
+
}
|
|
207514
208833
|
}
|
|
207515
208834
|
const events = [];
|
|
207516
208835
|
if (textParts.length > 0) events.push(this.makeChunkEvent({
|
|
@@ -207597,6 +208916,7 @@ var ClaudeCodeAdapter = class {
|
|
|
207597
208916
|
if (block.type !== "tool_result") continue;
|
|
207598
208917
|
const toolCallId = block.tool_use_id;
|
|
207599
208918
|
if (!toolCallId) continue;
|
|
208919
|
+
if (!subagentCtx) this.hasUnhandledUserInput = true;
|
|
207600
208920
|
const resultContent = typeof block.content === "string" ? block.content : Array.isArray(block.content) ? block.content.map((c) => {
|
|
207601
208921
|
if (c?.type === "tool_reference" && c.tool_name) return c.tool_name;
|
|
207602
208922
|
if (c?.type === "image") return `[Image: ${c.source?.media_type || "image"}]`;
|
|
@@ -207604,7 +208924,9 @@ var ClaudeCodeAdapter = class {
|
|
|
207604
208924
|
}).filter(Boolean).join("\n") : JSON.stringify(block.content || "");
|
|
207605
208925
|
const cachedTodoArgs = this.todoWriteInputs.get(toolCallId);
|
|
207606
208926
|
if (cachedTodoArgs) this.todoWriteInputs.delete(toolCallId);
|
|
207607
|
-
const
|
|
208927
|
+
const todoWritePluginState = cachedTodoArgs && !block.is_error ? synthesizeTodoWritePluginState(cachedTodoArgs) : void 0;
|
|
208928
|
+
const taskPluginState = subagentCtx === void 0 ? this.applyTaskToolResult(toolCallId, !!block.is_error, resultContent) : void 0;
|
|
208929
|
+
const pluginState = todoWritePluginState ?? taskPluginState;
|
|
207608
208930
|
events.push(this.makeEvent("tool_result", {
|
|
207609
208931
|
content: resultContent,
|
|
207610
208932
|
isError: !!block.is_error,
|
|
@@ -207623,6 +208945,68 @@ var ClaudeCodeAdapter = class {
|
|
|
207623
208945
|
}
|
|
207624
208946
|
return events;
|
|
207625
208947
|
}
|
|
208948
|
+
/**
|
|
208949
|
+
* Apply a Task* tool_result to the running {@link claudeCodeTasks}
|
|
208950
|
+
* accumulator and return a fresh synthesized `pluginState.todos` snapshot.
|
|
208951
|
+
* Returns `undefined` if the tool_result was for a non-Task tool, was an
|
|
208952
|
+
* error, or carried no state change (the snapshot is identical to the
|
|
208953
|
+
* pre-call one — but we still emit it so the UI re-syncs).
|
|
208954
|
+
*
|
|
208955
|
+
* Drain the input caches even on error to keep long sessions bounded;
|
|
208956
|
+
* the accumulator itself only mutates on success so a failed TaskUpdate
|
|
208957
|
+
* doesn't leak partial state into the rendered todo list.
|
|
208958
|
+
*/
|
|
208959
|
+
applyTaskToolResult(toolCallId, isError, resultContent) {
|
|
208960
|
+
const cachedCreate = this.taskCreateInputs.get(toolCallId);
|
|
208961
|
+
if (cachedCreate) this.taskCreateInputs.delete(toolCallId);
|
|
208962
|
+
const cachedUpdate = this.taskUpdateInputs.get(toolCallId);
|
|
208963
|
+
if (cachedUpdate) this.taskUpdateInputs.delete(toolCallId);
|
|
208964
|
+
const wasTaskList = this.pendingTaskListCalls.has(toolCallId);
|
|
208965
|
+
if (wasTaskList) this.pendingTaskListCalls.delete(toolCallId);
|
|
208966
|
+
if (!cachedCreate && !cachedUpdate && !wasTaskList) return void 0;
|
|
208967
|
+
if (isError) return void 0;
|
|
208968
|
+
if (cachedCreate) {
|
|
208969
|
+
const match = TASK_CREATE_RESULT_PATTERN.exec(resultContent);
|
|
208970
|
+
if (match) {
|
|
208971
|
+
const taskId = match[1];
|
|
208972
|
+
this.claudeCodeTasks.set(taskId, {
|
|
208973
|
+
activeForm: cachedCreate.activeForm,
|
|
208974
|
+
description: cachedCreate.description,
|
|
208975
|
+
status: "pending",
|
|
208976
|
+
subject: cachedCreate.subject
|
|
208977
|
+
});
|
|
208978
|
+
}
|
|
208979
|
+
} else if (cachedUpdate) {
|
|
208980
|
+
if (!TASK_UPDATE_RESULT_PATTERN.test(resultContent)) return void 0;
|
|
208981
|
+
if (cachedUpdate.status === "deleted") this.claudeCodeTasks.delete(cachedUpdate.taskId);
|
|
208982
|
+
else {
|
|
208983
|
+
const next = this.claudeCodeTasks.get(cachedUpdate.taskId) ?? {
|
|
208984
|
+
status: "pending",
|
|
208985
|
+
subject: cachedUpdate.subject ?? `Task #${cachedUpdate.taskId}`
|
|
208986
|
+
};
|
|
208987
|
+
if (cachedUpdate.status) next.status = cachedUpdate.status;
|
|
208988
|
+
if (cachedUpdate.subject !== void 0) next.subject = cachedUpdate.subject;
|
|
208989
|
+
if (cachedUpdate.description !== void 0) next.description = cachedUpdate.description;
|
|
208990
|
+
if (cachedUpdate.activeForm !== void 0) next.activeForm = cachedUpdate.activeForm;
|
|
208991
|
+
this.claudeCodeTasks.set(cachedUpdate.taskId, next);
|
|
208992
|
+
}
|
|
208993
|
+
} else if (wasTaskList) for (const rawLine of resultContent.split("\n")) {
|
|
208994
|
+
const line = rawLine.trim();
|
|
208995
|
+
if (!line) continue;
|
|
208996
|
+
const m = TASK_LIST_LINE_PATTERN.exec(line);
|
|
208997
|
+
if (!m) continue;
|
|
208998
|
+
const [, taskId, status, subject] = m;
|
|
208999
|
+
const existing = this.claudeCodeTasks.get(taskId);
|
|
209000
|
+
if (existing) {
|
|
209001
|
+
existing.status = status;
|
|
209002
|
+
existing.subject = subject;
|
|
209003
|
+
} else this.claudeCodeTasks.set(taskId, {
|
|
209004
|
+
status,
|
|
209005
|
+
subject
|
|
209006
|
+
});
|
|
209007
|
+
}
|
|
209008
|
+
return synthesizeTaskPluginState(this.claudeCodeTasks);
|
|
209009
|
+
}
|
|
207626
209010
|
handleResult(raw) {
|
|
207627
209011
|
const events = [];
|
|
207628
209012
|
const usage = toUsageData$1(raw.usage);
|
|
@@ -207638,6 +209022,9 @@ var ClaudeCodeAdapter = class {
|
|
|
207638
209022
|
message: resultMessage
|
|
207639
209023
|
}) : this.makeEvent("agent_runtime_end", {});
|
|
207640
209024
|
this.pendingRateLimitInfo = void 0;
|
|
209025
|
+
this.streamedTextByMessageId.clear();
|
|
209026
|
+
this.streamedThinkingByMessageId.clear();
|
|
209027
|
+
this.pendingTaskCompletion = void 0;
|
|
207641
209028
|
return [
|
|
207642
209029
|
...events,
|
|
207643
209030
|
this.makeEvent("stream_end", {}),
|
|
@@ -207669,14 +209056,14 @@ var ClaudeCodeAdapter = class {
|
|
|
207669
209056
|
if (!delta) return [];
|
|
207670
209057
|
const msgId = this.currentStreamEventMessageId;
|
|
207671
209058
|
if (delta.type === "text_delta" && delta.text) {
|
|
207672
|
-
if (msgId) this.
|
|
209059
|
+
if (msgId) this.streamedTextByMessageId.set(msgId, `${this.streamedTextByMessageId.get(msgId) ?? ""}${delta.text}`);
|
|
207673
209060
|
return [this.makeChunkEvent({
|
|
207674
209061
|
chunkType: "text",
|
|
207675
209062
|
content: delta.text
|
|
207676
209063
|
})];
|
|
207677
209064
|
}
|
|
207678
209065
|
if (delta.type === "thinking_delta" && delta.thinking) {
|
|
207679
|
-
if (msgId) this.
|
|
209066
|
+
if (msgId) this.streamedThinkingByMessageId.set(msgId, `${this.streamedThinkingByMessageId.get(msgId) ?? ""}${delta.thinking}`);
|
|
207680
209067
|
return [this.makeChunkEvent({
|
|
207681
209068
|
chunkType: "reasoning",
|
|
207682
209069
|
reasoning: delta.thinking
|
|
@@ -207727,12 +209114,44 @@ var ClaudeCodeAdapter = class {
|
|
|
207727
209114
|
}
|
|
207728
209115
|
this.currentMessageId = messageId;
|
|
207729
209116
|
this.stepIndex++;
|
|
209117
|
+
if (!this.hasUnhandledUserInput && this.activeTasks.size > 0) {
|
|
209118
|
+
const lastTaskKey = [...this.activeTasks.keys()].at(-1);
|
|
209119
|
+
const task = this.activeTasks.get(lastTaskKey);
|
|
209120
|
+
task.callbackCount += 1;
|
|
209121
|
+
this.pendingExternalSignal = {
|
|
209122
|
+
sequence: task.callbackCount,
|
|
209123
|
+
sourceToolCallId: task.toolUseId,
|
|
209124
|
+
sourceToolName: task.sourceToolName,
|
|
209125
|
+
type: "tool-stdout"
|
|
209126
|
+
};
|
|
209127
|
+
} else if (this.pendingTaskCompletion) {
|
|
209128
|
+
this.pendingExternalSignal = {
|
|
209129
|
+
sourceToolCallId: this.pendingTaskCompletion.sourceToolCallId,
|
|
209130
|
+
sourceToolName: this.pendingTaskCompletion.sourceToolName,
|
|
209131
|
+
type: "task-completion"
|
|
209132
|
+
};
|
|
209133
|
+
this.pendingTaskCompletion = void 0;
|
|
209134
|
+
} else this.pendingExternalSignal = void 0;
|
|
209135
|
+
this.hasUnhandledUserInput = false;
|
|
207730
209136
|
return [this.makeEvent("stream_end", {}), this.makeEvent("stream_start", {
|
|
209137
|
+
externalSignal: this.pendingExternalSignal,
|
|
207731
209138
|
model,
|
|
207732
209139
|
newStep: true,
|
|
207733
209140
|
provider: "claude-code"
|
|
207734
209141
|
})];
|
|
207735
209142
|
}
|
|
209143
|
+
getTrailingCompletion(messageId, fullContent, streamedByMessageId) {
|
|
209144
|
+
if (!fullContent) return;
|
|
209145
|
+
if (!messageId) return fullContent;
|
|
209146
|
+
const streamed = streamedByMessageId.get(messageId);
|
|
209147
|
+
if (!streamed) return fullContent;
|
|
209148
|
+
if (fullContent === streamed) return;
|
|
209149
|
+
if (fullContent.startsWith(streamed)) return fullContent.slice(streamed.length) || void 0;
|
|
209150
|
+
}
|
|
209151
|
+
clearStreamedBuffers(messageId, modes) {
|
|
209152
|
+
if (modes.text) this.streamedTextByMessageId.delete(messageId);
|
|
209153
|
+
if (modes.thinking) this.streamedThinkingByMessageId.delete(messageId);
|
|
209154
|
+
}
|
|
207736
209155
|
makeEvent(type, data) {
|
|
207737
209156
|
return {
|
|
207738
209157
|
data,
|
|
@@ -208373,6 +209792,142 @@ var AgentStreamPipeline = class {
|
|
|
208373
209792
|
}
|
|
208374
209793
|
};
|
|
208375
209794
|
|
|
209795
|
+
//#endregion
|
|
209796
|
+
//#region ../../packages/heterogeneous-agents/src/spawn/cliSpawn.ts
|
|
209797
|
+
const WINDOWS_EXE_EXT_PATTERN = /\.exe$/i;
|
|
209798
|
+
const WINDOWS_NODE_EXE_PATTERN = /(?:^|[\\/])node(?:\.exe)?$/i;
|
|
209799
|
+
const isWindows = () => platform() === "win32";
|
|
209800
|
+
const isPathLikeCommand = (command) => path.win32.isAbsolute(command) || path.posix.isAbsolute(command) || /[\\/]/.test(command);
|
|
209801
|
+
const fileExists = async (filePath) => {
|
|
209802
|
+
try {
|
|
209803
|
+
await access(filePath);
|
|
209804
|
+
return true;
|
|
209805
|
+
} catch {
|
|
209806
|
+
return false;
|
|
209807
|
+
}
|
|
209808
|
+
};
|
|
209809
|
+
const execFileString = async (command, args) => new Promise((resolve, reject) => {
|
|
209810
|
+
execFile(command, args, {
|
|
209811
|
+
timeout: 3e3,
|
|
209812
|
+
windowsHide: true
|
|
209813
|
+
}, (error, stdout) => {
|
|
209814
|
+
if (error) {
|
|
209815
|
+
reject(error);
|
|
209816
|
+
return;
|
|
209817
|
+
}
|
|
209818
|
+
resolve(stdout.toString());
|
|
209819
|
+
});
|
|
209820
|
+
});
|
|
209821
|
+
const pickWindowsExecutable = (candidates) => candidates.find((candidate) => WINDOWS_EXE_EXT_PATTERN.test(candidate));
|
|
209822
|
+
const pickWindowsNodeExecutable = (candidates) => candidates.find((candidate) => WINDOWS_EXE_EXT_PATTERN.test(candidate) && WINDOWS_NODE_EXE_PATTERN.test(candidate));
|
|
209823
|
+
const joinShimRelativePath = (shimPath, relativePath) => path.win32.join(path.win32.dirname(shimPath), ...relativePath.replaceAll("\\", "/").split("/").filter(Boolean));
|
|
209824
|
+
const resolveShimPathToken = (shimPath, token) => {
|
|
209825
|
+
const trimmedToken = token.trim().replaceAll(/^['"]|['"]$/g, "");
|
|
209826
|
+
const lowerToken = trimmedToken.toLowerCase();
|
|
209827
|
+
if (lowerToken.startsWith("$basedir")) return joinShimRelativePath(shimPath, trimmedToken.slice(8).replace(/^[\\/]/, ""));
|
|
209828
|
+
if (lowerToken.startsWith("%dp0%")) return joinShimRelativePath(shimPath, trimmedToken.slice(5).replace(/^[\\/]/, ""));
|
|
209829
|
+
if (path.win32.isAbsolute(trimmedToken)) return trimmedToken;
|
|
209830
|
+
if (/[\\/]/.test(trimmedToken)) return joinShimRelativePath(shimPath, trimmedToken);
|
|
209831
|
+
};
|
|
209832
|
+
const getExistingShimPathToken = async (shimPath, token) => {
|
|
209833
|
+
const resolvedPath = resolveShimPathToken(shimPath, token);
|
|
209834
|
+
if (!resolvedPath) return;
|
|
209835
|
+
return await fileExists(resolvedPath) ? resolvedPath : void 0;
|
|
209836
|
+
};
|
|
209837
|
+
const resolveWindowsNodeCommand = async (shimPath) => {
|
|
209838
|
+
const localNodePath = path.win32.join(path.win32.dirname(shimPath), "node.exe");
|
|
209839
|
+
if (await fileExists(localNodePath)) return localNodePath;
|
|
209840
|
+
try {
|
|
209841
|
+
return pickWindowsNodeExecutable((await execFileString("where", ["node"])).split(/\r?\n/).map((line) => line.trim()).filter(Boolean));
|
|
209842
|
+
} catch {
|
|
209843
|
+
return;
|
|
209844
|
+
}
|
|
209845
|
+
};
|
|
209846
|
+
const getNodeCommand = async (shimPath, token) => {
|
|
209847
|
+
const trimmedToken = token.trim().replaceAll(/^['"]|['"]$/g, "");
|
|
209848
|
+
if (/^node(?:\.exe)?$/i.test(trimmedToken) || /^%_prog%$/i.test(trimmedToken)) return resolveWindowsNodeCommand(shimPath);
|
|
209849
|
+
const resolvedPath = await getExistingShimPathToken(shimPath, trimmedToken);
|
|
209850
|
+
if (!resolvedPath) return;
|
|
209851
|
+
return WINDOWS_NODE_EXE_PATTERN.test(resolvedPath) ? resolvedPath : void 0;
|
|
209852
|
+
};
|
|
209853
|
+
const getNodeScriptTarget = async (shimPath, nodeToken, scriptToken) => {
|
|
209854
|
+
const command = await getNodeCommand(shimPath, nodeToken);
|
|
209855
|
+
if (!command) return;
|
|
209856
|
+
const scriptPath = await getExistingShimPathToken(shimPath, scriptToken);
|
|
209857
|
+
if (!scriptPath) return;
|
|
209858
|
+
return {
|
|
209859
|
+
argsPrefix: [scriptPath],
|
|
209860
|
+
command
|
|
209861
|
+
};
|
|
209862
|
+
};
|
|
209863
|
+
const inferWindowsNodeScriptFromShim = async (shimPath, source) => {
|
|
209864
|
+
for (const pattern of [
|
|
209865
|
+
/exec\s+"(\$basedir[^"]*node(?:\.exe)?)"\s+"([^"]+)"/i,
|
|
209866
|
+
/exec\s+(node(?:\.exe)?)\s+"([^"]+)"/i,
|
|
209867
|
+
/"(%dp0%[^"]*node(?:\.exe)?)"\s+"([^"]+)"/i,
|
|
209868
|
+
/"(%_prog%)"\s+"([^"]+)"/i,
|
|
209869
|
+
[/(?:^|\r?\n)\s*(node(?:\.exe)?)\s+"([^"]+)"/i, "node"]
|
|
209870
|
+
]) {
|
|
209871
|
+
const regex = Array.isArray(pattern) ? pattern[0] : pattern;
|
|
209872
|
+
const match = source.match(regex);
|
|
209873
|
+
if (!match) continue;
|
|
209874
|
+
const nodeToken = Array.isArray(pattern) ? pattern[1] : match[1];
|
|
209875
|
+
const scriptToken = Array.isArray(pattern) ? match[2] : match[2];
|
|
209876
|
+
if (!nodeToken || !scriptToken) continue;
|
|
209877
|
+
const target = await getNodeScriptTarget(shimPath, nodeToken, scriptToken);
|
|
209878
|
+
if (target) return target;
|
|
209879
|
+
}
|
|
209880
|
+
};
|
|
209881
|
+
const inferWindowsExecutableFromShim = async (shimPath, source) => {
|
|
209882
|
+
const matches = [...source.matchAll(/\$basedir[\\/]([^"\s]+?\.exe)/gi), ...source.matchAll(/%dp0%\\([^"\r\n]+?\.exe)/gi)];
|
|
209883
|
+
for (const match of matches) {
|
|
209884
|
+
const relativePath = match[1]?.replaceAll("\\", "/");
|
|
209885
|
+
if (!relativePath || WINDOWS_NODE_EXE_PATTERN.test(relativePath)) continue;
|
|
209886
|
+
const command = joinShimRelativePath(shimPath, relativePath);
|
|
209887
|
+
if (await fileExists(command)) return { command };
|
|
209888
|
+
}
|
|
209889
|
+
};
|
|
209890
|
+
const inferWindowsNpmShimTarget = async (shimPath) => {
|
|
209891
|
+
if (WINDOWS_EXE_EXT_PATTERN.test(shimPath)) return { command: shimPath };
|
|
209892
|
+
if (!await fileExists(shimPath)) return;
|
|
209893
|
+
try {
|
|
209894
|
+
const source = await readFile(shimPath, "utf8");
|
|
209895
|
+
return await inferWindowsNodeScriptFromShim(shimPath, source) ?? await inferWindowsExecutableFromShim(shimPath, source);
|
|
209896
|
+
} catch {
|
|
209897
|
+
return;
|
|
209898
|
+
}
|
|
209899
|
+
};
|
|
209900
|
+
const resolveWindowsBareCommand = async (command) => {
|
|
209901
|
+
try {
|
|
209902
|
+
const candidates = (await execFileString("where", [command])).split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
|
|
209903
|
+
const executable = pickWindowsExecutable(candidates);
|
|
209904
|
+
if (executable) return { command: executable };
|
|
209905
|
+
for (const candidate of candidates) {
|
|
209906
|
+
const target = await inferWindowsNpmShimTarget(candidate);
|
|
209907
|
+
if (target) return target;
|
|
209908
|
+
}
|
|
209909
|
+
return;
|
|
209910
|
+
} catch {
|
|
209911
|
+
return;
|
|
209912
|
+
}
|
|
209913
|
+
};
|
|
209914
|
+
const resolveCliSpawnPlan = async (command, args) => {
|
|
209915
|
+
const trimmedCommand = command.trim();
|
|
209916
|
+
if (!isWindows() || !trimmedCommand) return {
|
|
209917
|
+
args,
|
|
209918
|
+
command
|
|
209919
|
+
};
|
|
209920
|
+
const target = isPathLikeCommand(trimmedCommand) ? await inferWindowsNpmShimTarget(trimmedCommand) : await resolveWindowsBareCommand(trimmedCommand);
|
|
209921
|
+
if (!target) return {
|
|
209922
|
+
args,
|
|
209923
|
+
command
|
|
209924
|
+
};
|
|
209925
|
+
return {
|
|
209926
|
+
args: [...target.argsPrefix ?? [], ...args],
|
|
209927
|
+
command: target.command
|
|
209928
|
+
};
|
|
209929
|
+
};
|
|
209930
|
+
|
|
208376
209931
|
//#endregion
|
|
208377
209932
|
//#region ../../packages/heterogeneous-agents/src/spawn/input/normalizeImage.ts
|
|
208378
209933
|
const PNG_SIG = Buffer.from([
|
|
@@ -208615,6 +210170,19 @@ const buildAgentInput = async (agentType, prompt, options = {}) => {
|
|
|
208615
210170
|
|
|
208616
210171
|
//#endregion
|
|
208617
210172
|
//#region ../../packages/heterogeneous-agents/src/spawn/spawnAgent.ts
|
|
210173
|
+
/**
|
|
210174
|
+
* Invariant Claude Code CLI flags shared by every spawn site (desktop driver,
|
|
210175
|
+
* `lh hetero exec`). Permission mode and `--include-partial-messages` vary by
|
|
210176
|
+
* caller — the desktop UI wants live deltas + user-mode bypassPermissions, the
|
|
210177
|
+
* sandbox CLI may run as root and skip partials — so they're composed on top
|
|
210178
|
+
* of this base.
|
|
210179
|
+
*
|
|
210180
|
+
* `AskUserQuestion` is disabled because CC's CLI self-injects an
|
|
210181
|
+
* `is_error: "Answer questions?"` tool_result in `-p` mode before the host
|
|
210182
|
+
* can surface the questions, so the model falls back to plain-text prompting
|
|
210183
|
+
* anyway. Remove this once a local MCP-backed replacement is wired to
|
|
210184
|
+
* LobeHub's intervention UI.
|
|
210185
|
+
*/
|
|
208618
210186
|
const CLAUDE_CODE_BASE_ARGS = [
|
|
208619
210187
|
"-p",
|
|
208620
210188
|
"--input-format",
|
|
@@ -208622,7 +210190,8 @@ const CLAUDE_CODE_BASE_ARGS = [
|
|
|
208622
210190
|
"--output-format",
|
|
208623
210191
|
"stream-json",
|
|
208624
210192
|
"--verbose",
|
|
208625
|
-
"--
|
|
210193
|
+
"--disallowedTools",
|
|
210194
|
+
"AskUserQuestion"
|
|
208626
210195
|
];
|
|
208627
210196
|
const isRunningAsRoot = () => process.getuid?.() === 0;
|
|
208628
210197
|
const CLAUDE_CODE_PERMISSION_ARGS = () => isRunningAsRoot() ? [
|
|
@@ -208636,14 +210205,15 @@ const CODEX_REQUIRED_ARGS = [
|
|
|
208636
210205
|
"--skip-git-repo-check",
|
|
208637
210206
|
"--full-auto"
|
|
208638
210207
|
];
|
|
208639
|
-
const buildClaudeCodeArgs = (
|
|
210208
|
+
const buildClaudeCodeArgs = ({ extraArgs, includePartialMessages, inputArgs, resumeSessionId }) => [
|
|
208640
210209
|
...CLAUDE_CODE_BASE_ARGS,
|
|
210210
|
+
...includePartialMessages ? ["--include-partial-messages"] : [],
|
|
208641
210211
|
...CLAUDE_CODE_PERMISSION_ARGS(),
|
|
208642
210212
|
...resumeSessionId ? ["--resume", resumeSessionId] : [],
|
|
208643
210213
|
...inputArgs,
|
|
208644
210214
|
...extraArgs
|
|
208645
210215
|
];
|
|
208646
|
-
const buildCodexArgs = (
|
|
210216
|
+
const buildCodexArgs = ({ extraArgs, inputArgs, resumeSessionId }) => resumeSessionId ? [
|
|
208647
210217
|
"exec",
|
|
208648
210218
|
"resume",
|
|
208649
210219
|
...CODEX_REQUIRED_ARGS,
|
|
@@ -208657,11 +210227,11 @@ const buildCodexArgs = (resumeSessionId, inputArgs, extraArgs) => resumeSessionI
|
|
|
208657
210227
|
...inputArgs,
|
|
208658
210228
|
...extraArgs
|
|
208659
210229
|
];
|
|
208660
|
-
const buildSpawnArgs = (
|
|
208661
|
-
switch (agentType) {
|
|
208662
|
-
case "claude-code": return buildClaudeCodeArgs(
|
|
208663
|
-
case "codex": return buildCodexArgs(
|
|
208664
|
-
default: throw new Error(`spawnAgent: unsupported agent type "${agentType}"`);
|
|
210230
|
+
const buildSpawnArgs = (params) => {
|
|
210231
|
+
switch (params.agentType) {
|
|
210232
|
+
case "claude-code": return buildClaudeCodeArgs(params);
|
|
210233
|
+
case "codex": return buildCodexArgs(params);
|
|
210234
|
+
default: throw new Error(`spawnAgent: unsupported agent type "${params.agentType}"`);
|
|
208665
210235
|
}
|
|
208666
210236
|
};
|
|
208667
210237
|
const defaultCommand = (agentType) => agentType === "codex" ? "codex" : "claude";
|
|
@@ -208698,8 +210268,17 @@ const killProcessTree = (proc, signal) => {
|
|
|
208698
210268
|
const spawnAgent = async (options) => {
|
|
208699
210269
|
const command = options.command || defaultCommand(options.agentType);
|
|
208700
210270
|
const inputPlan = await buildAgentInput(options.agentType, options.prompt, options.inputOptions);
|
|
208701
|
-
const
|
|
208702
|
-
|
|
210271
|
+
const args = buildSpawnArgs({
|
|
210272
|
+
agentType: options.agentType,
|
|
210273
|
+
extraArgs: options.extraArgs ?? [],
|
|
210274
|
+
includePartialMessages: options.includePartialMessages ?? false,
|
|
210275
|
+
inputArgs: inputPlan.args,
|
|
210276
|
+
resumeSessionId: options.resumeSessionId
|
|
210277
|
+
});
|
|
210278
|
+
const cwd = options.cwd || process.cwd();
|
|
210279
|
+
const cliSpawnPlan = await resolveCliSpawnPlan(command, args);
|
|
210280
|
+
const proc = spawn(cliSpawnPlan.command, cliSpawnPlan.args, {
|
|
210281
|
+
cwd,
|
|
208703
210282
|
detached: process.platform !== "win32",
|
|
208704
210283
|
env: {
|
|
208705
210284
|
...process.env,
|
|
@@ -208711,6 +210290,13 @@ const spawnAgent = async (options) => {
|
|
|
208711
210290
|
"pipe"
|
|
208712
210291
|
]
|
|
208713
210292
|
});
|
|
210293
|
+
const exit = new Promise((resolve, reject) => {
|
|
210294
|
+
proc.on("exit", (code, signal) => resolve({
|
|
210295
|
+
code,
|
|
210296
|
+
signal
|
|
210297
|
+
}));
|
|
210298
|
+
proc.on("error", (err) => reject(err));
|
|
210299
|
+
});
|
|
208714
210300
|
if (proc.stdin) proc.stdin.write(inputPlan.stdin, () => {
|
|
208715
210301
|
proc.stdin?.end();
|
|
208716
210302
|
});
|
|
@@ -208786,13 +210372,7 @@ const spawnAgent = async (options) => {
|
|
|
208786
210372
|
}
|
|
208787
210373
|
} };
|
|
208788
210374
|
} },
|
|
208789
|
-
exit
|
|
208790
|
-
proc.on("exit", (code, signal) => resolve({
|
|
208791
|
-
code,
|
|
208792
|
-
signal
|
|
208793
|
-
}));
|
|
208794
|
-
proc.on("error", (err) => reject(err));
|
|
208795
|
-
}),
|
|
210375
|
+
exit,
|
|
208796
210376
|
kill: (signal = "SIGINT") => killProcessTree(proc, signal),
|
|
208797
210377
|
pid: proc.pid,
|
|
208798
210378
|
get sessionId() {
|
|
@@ -210657,6 +212237,7 @@ function registerMigrateCommand(program) {
|
|
|
210657
212237
|
|
|
210658
212238
|
//#endregion
|
|
210659
212239
|
//#region src/commands/model.ts
|
|
212240
|
+
const isVisibleModel = (model) => model.visible !== false;
|
|
210660
212241
|
function registerModelCommand(program) {
|
|
210661
212242
|
const model = program.command("model").description("Manage AI models");
|
|
210662
212243
|
model.command("list <providerId>").description("List models for a provider").option("-L, --limit <n>", "Maximum number of items", "50").option("--enabled", "Only show enabled models").option("--type <type>", "Filter by model type (chat|embedding|tts|stt|image|video|text2music|realtime)").option("--json [fields]", "Output JSON, optionally specify fields (comma-separated)").action(async (providerId, options) => {
|
|
@@ -210666,7 +212247,7 @@ function registerModelCommand(program) {
|
|
|
210666
212247
|
if (options.enabled) input.enabled = true;
|
|
210667
212248
|
if (options.type) input.type = options.type;
|
|
210668
212249
|
const result = await client.aiModel.getAiProviderModelList.query(input);
|
|
210669
|
-
let items = Array.isArray(result) ? result : result.items ?? [];
|
|
212250
|
+
let items = (Array.isArray(result) ? result : result.items ?? []).filter(isVisibleModel);
|
|
210670
212251
|
if (options.type) items = items.filter((m) => m.type === options.type);
|
|
210671
212252
|
if (options.json !== void 0) {
|
|
210672
212253
|
const fields = typeof options.json === "string" ? options.json : void 0;
|
|
@@ -210834,13 +212415,16 @@ function registerModelCommand(program) {
|
|
|
210834
212415
|
//#endregion
|
|
210835
212416
|
//#region src/commands/notify.ts
|
|
210836
212417
|
function registerNotifyCommand(program) {
|
|
210837
|
-
program.command("notify").description("Send a callback message to a topic and trigger the agent to process it").requiredOption("--topic <topicId>", "Target topic ID").requiredOption("-c, --content <content>", "Message content").option("--agent-id <agentId>", "Agent ID (overrides topic default)").option("--thread-id <threadId>", "Thread ID for threaded conversations").option("--json", "Output JSON").action(async (options) => {
|
|
210838
|
-
log$7.debug("notify: topic=%s, agentId=%s", options.topic, options.agentId);
|
|
212418
|
+
program.command("notify").description("Send a callback message to a topic and trigger the agent to process it").requiredOption("--topic <topicId>", "Target topic ID").requiredOption("-c, --content <content>", "Message content").option("--agent-id <agentId>", "Agent ID (overrides topic default)").option("--thread-id <threadId>", "Thread ID for threaded conversations").option("--role <role>", "Message role: user (default, triggers agent reply) | assistant (writes directly as agent message)", "user").option("--message-id <messageId>", "When --role assistant: update an existing message instead of creating a new one (keeps a single bubble)").option("--continue", "When --role assistant: trigger a follow-up agent turn after writing the message").option("--json", "Output JSON").action(async (options) => {
|
|
212419
|
+
log$7.debug("notify: topic=%s, agentId=%s, role=%s, messageId=%s", options.topic, options.agentId, options.role, options.messageId);
|
|
210839
212420
|
const client = await getTrpcClient();
|
|
210840
212421
|
try {
|
|
210841
212422
|
const result = await client.agentNotify.notify.mutate({
|
|
210842
212423
|
agentId: options.agentId,
|
|
210843
212424
|
content: options.content,
|
|
212425
|
+
continue: options.continue,
|
|
212426
|
+
messageId: options.messageId,
|
|
212427
|
+
role: options.role,
|
|
210844
212428
|
threadId: options.threadId,
|
|
210845
212429
|
topicId: options.topic
|
|
210846
212430
|
});
|
|
@@ -212067,7 +213651,7 @@ function registerReviewCommands(task) {
|
|
|
212067
213651
|
"CONFIG"
|
|
212068
213652
|
]);
|
|
212069
213653
|
});
|
|
212070
|
-
rc.command("add <id>").description("Add a review rubric").requiredOption("-n, --name <name>", "Rubric name (e.g. \"
|
|
213654
|
+
rc.command("add <id>").description("Add a review rubric").requiredOption("-n, --name <name>", "Rubric name (e.g. \"Content Accuracy\")").option("--type <type>", "Rubric type (default: llm-rubric)", "llm-rubric").option("-t, --threshold <n>", "Pass threshold 0-100 (converted to 0-1)").option("-d, --description <text>", "Criteria description (for llm-rubric type)").option("--value <value>", "Expected value (for contains/equals type)").option("--pattern <pattern>", "Regex pattern (for regex type)").option("-w, --weight <n>", "Weight for scoring (default: 1)").option("--recursive", "Add to all subtasks as well").action(async (id, options) => {
|
|
212071
213655
|
const client = await getTrpcClient();
|
|
212072
213656
|
const buildConfig = () => {
|
|
212073
213657
|
switch (options.type) {
|
|
@@ -212964,7 +214548,6 @@ function createProgram() {
|
|
|
212964
214548
|
registerAgentCommand(program);
|
|
212965
214549
|
registerAgentGroupCommand(program);
|
|
212966
214550
|
registerBotCommand(program);
|
|
212967
|
-
registerCronCommand(program);
|
|
212968
214551
|
registerGenerateCommand(program);
|
|
212969
214552
|
registerFileCommand(program);
|
|
212970
214553
|
registerHeteroCommand(program);
|