@lobehub/cli 0.0.31 → 0.0.33
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 +220 -737
- package/man/man1/lh.1 +4 -1
- package/package.json +5 -5
package/dist/index.js
CHANGED
|
@@ -3,7 +3,7 @@ import { createRequire } from "node:module";
|
|
|
3
3
|
import { EventEmitter, once } from "node:events";
|
|
4
4
|
import { execFile, execFileSync, spawn } from "node:child_process";
|
|
5
5
|
import path, { basename, extname } from "node:path";
|
|
6
|
-
import fs, { constants,
|
|
6
|
+
import fs, { constants, createWriteStream, existsSync, readFileSync, statSync, writeFileSync } from "node:fs";
|
|
7
7
|
import crypto$1, { createHash, randomUUID } from "node:crypto";
|
|
8
8
|
import os, { platform, tmpdir } from "node:os";
|
|
9
9
|
import { createInterface } from "node:readline";
|
|
@@ -5679,13 +5679,48 @@ function isProcessAlive(pid) {
|
|
|
5679
5679
|
}
|
|
5680
5680
|
}
|
|
5681
5681
|
/**
|
|
5682
|
+
* Verify a live PID actually belongs to a LobeHub connect daemon.
|
|
5683
|
+
*
|
|
5684
|
+
* A bare `isProcessAlive` check is not enough: if a daemon dies without cleaning
|
|
5685
|
+
* up `daemon.pid` (crash, `kill -9`, reboot), the OS can later reuse that PID
|
|
5686
|
+
* for an unrelated process. Acting on the stale PID would let `lh logout` /
|
|
5687
|
+
* `connect stop` SIGTERM a stranger. The daemon is always spawned as
|
|
5688
|
+
* `<node> … connect … --daemon-child`, so we confirm that signature in the
|
|
5689
|
+
* process command line before trusting the PID.
|
|
5690
|
+
*
|
|
5691
|
+
* Best-effort and deliberately conservative: if the command line can't be read
|
|
5692
|
+
* (e.g. `ps` is unavailable), we return `false` so callers never kill a process
|
|
5693
|
+
* we can't positively identify.
|
|
5694
|
+
*/
|
|
5695
|
+
function isDaemonProcess(pid) {
|
|
5696
|
+
try {
|
|
5697
|
+
const command = execFileSync("ps", [
|
|
5698
|
+
"-ww",
|
|
5699
|
+
"-p",
|
|
5700
|
+
String(pid),
|
|
5701
|
+
"-o",
|
|
5702
|
+
"command="
|
|
5703
|
+
], {
|
|
5704
|
+
encoding: "utf8",
|
|
5705
|
+
stdio: [
|
|
5706
|
+
"ignore",
|
|
5707
|
+
"pipe",
|
|
5708
|
+
"ignore"
|
|
5709
|
+
]
|
|
5710
|
+
}).trim();
|
|
5711
|
+
return command.includes("--daemon-child") && command.includes("connect");
|
|
5712
|
+
} catch {
|
|
5713
|
+
return false;
|
|
5714
|
+
}
|
|
5715
|
+
}
|
|
5716
|
+
/**
|
|
5682
5717
|
* Get the PID of a running daemon, cleaning up stale PID files.
|
|
5683
5718
|
* Returns null if no daemon is running.
|
|
5684
5719
|
*/
|
|
5685
5720
|
function getRunningDaemonPid() {
|
|
5686
5721
|
const pid = readPid();
|
|
5687
5722
|
if (pid === null) return null;
|
|
5688
|
-
if (isProcessAlive(pid)) return pid;
|
|
5723
|
+
if (isProcessAlive(pid) && isDaemonProcess(pid)) return pid;
|
|
5689
5724
|
removePid();
|
|
5690
5725
|
removeStatus();
|
|
5691
5726
|
return null;
|
|
@@ -7614,8 +7649,7 @@ const HotkeyEnum = {
|
|
|
7614
7649
|
ShowApp: "showApp",
|
|
7615
7650
|
SwitchAgent: "switchAgent",
|
|
7616
7651
|
ToggleLeftPanel: "toggleLeftPanel",
|
|
7617
|
-
ToggleRightPanel: "toggleRightPanel"
|
|
7618
|
-
ToggleZenMode: "toggleZenMode"
|
|
7652
|
+
ToggleRightPanel: "toggleRightPanel"
|
|
7619
7653
|
};
|
|
7620
7654
|
const HotkeyGroupEnum = {
|
|
7621
7655
|
Conversation: "conversation",
|
|
@@ -7675,12 +7709,6 @@ const HOTKEYS_REGISTRATION = [
|
|
|
7675
7709
|
]),
|
|
7676
7710
|
scopes: [HotkeyScopeEnum.Global]
|
|
7677
7711
|
},
|
|
7678
|
-
{
|
|
7679
|
-
group: HotkeyGroupEnum.Essential,
|
|
7680
|
-
id: HotkeyEnum.ToggleZenMode,
|
|
7681
|
-
keys: combineKeys([KeyEnum.Mod, KeyEnum.Backslash]),
|
|
7682
|
-
scopes: [HotkeyScopeEnum.Chat]
|
|
7683
|
-
},
|
|
7684
7712
|
{
|
|
7685
7713
|
group: HotkeyGroupEnum.Conversation,
|
|
7686
7714
|
id: HotkeyEnum.OpenChatSettings,
|
|
@@ -8674,669 +8702,6 @@ const DEFAULT_INBOX_SESSION = merge(DEFAULT_AGENT_LOBE_SESSION, {
|
|
|
8674
8702
|
meta: { avatar: DEFAULT_INBOX_AVATAR }
|
|
8675
8703
|
});
|
|
8676
8704
|
|
|
8677
|
-
//#endregion
|
|
8678
|
-
//#region ../../packages/const/src/taskTemplate.ts
|
|
8679
|
-
const taskTemplates = [
|
|
8680
|
-
{
|
|
8681
|
-
id: "daily-topic-pick",
|
|
8682
|
-
category: "content-creation",
|
|
8683
|
-
cronPattern: "0 9 * * *",
|
|
8684
|
-
interests: ["writing"]
|
|
8685
|
-
},
|
|
8686
|
-
{
|
|
8687
|
-
id: "hot-topic-radar",
|
|
8688
|
-
category: "content-creation",
|
|
8689
|
-
cronPattern: "0 10 * * *",
|
|
8690
|
-
interests: ["writing"]
|
|
8691
|
-
},
|
|
8692
|
-
{
|
|
8693
|
-
id: "headline-inspiration",
|
|
8694
|
-
category: "content-creation",
|
|
8695
|
-
cronPattern: "0 10 * * *",
|
|
8696
|
-
interests: ["writing"]
|
|
8697
|
-
},
|
|
8698
|
-
{
|
|
8699
|
-
id: "viral-content-breakdown",
|
|
8700
|
-
category: "content-creation",
|
|
8701
|
-
cronPattern: "0 10 * * *",
|
|
8702
|
-
interests: ["writing"]
|
|
8703
|
-
},
|
|
8704
|
-
{
|
|
8705
|
-
id: "twitter-weekly-recap",
|
|
8706
|
-
category: "content-creation",
|
|
8707
|
-
cronPattern: "0 10 * * 1",
|
|
8708
|
-
interests: ["writing", "creator"],
|
|
8709
|
-
requiresSkills: [{
|
|
8710
|
-
provider: "twitter",
|
|
8711
|
-
source: "lobehub"
|
|
8712
|
-
}]
|
|
8713
|
-
},
|
|
8714
|
-
{
|
|
8715
|
-
id: "youtube-weekly-recap",
|
|
8716
|
-
category: "content-creation",
|
|
8717
|
-
cronPattern: "0 9 * * 1",
|
|
8718
|
-
interests: ["writing", "creator"],
|
|
8719
|
-
requiresSkills: [{
|
|
8720
|
-
provider: "youtube",
|
|
8721
|
-
source: "composio"
|
|
8722
|
-
}]
|
|
8723
|
-
},
|
|
8724
|
-
{
|
|
8725
|
-
id: "competitor-creator-tracking",
|
|
8726
|
-
category: "content-creation",
|
|
8727
|
-
cronPattern: "0 9 * * *",
|
|
8728
|
-
interests: ["writing", "creator"]
|
|
8729
|
-
},
|
|
8730
|
-
{
|
|
8731
|
-
id: "content-calendar-weekly",
|
|
8732
|
-
category: "content-creation",
|
|
8733
|
-
cronPattern: "0 20 * * 0",
|
|
8734
|
-
interests: ["writing", "creator"],
|
|
8735
|
-
optionalSkills: [{
|
|
8736
|
-
provider: "notion",
|
|
8737
|
-
source: "lobehub"
|
|
8738
|
-
}]
|
|
8739
|
-
},
|
|
8740
|
-
{
|
|
8741
|
-
id: "oss-intel-daily",
|
|
8742
|
-
category: "engineering",
|
|
8743
|
-
cronPattern: "0 9 * * *",
|
|
8744
|
-
icon: "github",
|
|
8745
|
-
interests: ["coding"]
|
|
8746
|
-
},
|
|
8747
|
-
{
|
|
8748
|
-
id: "repo-health-weekly",
|
|
8749
|
-
category: "engineering",
|
|
8750
|
-
cronPattern: "0 9 * * 1",
|
|
8751
|
-
interests: ["coding"],
|
|
8752
|
-
requiresSkills: [{
|
|
8753
|
-
provider: "github",
|
|
8754
|
-
source: "lobehub"
|
|
8755
|
-
}]
|
|
8756
|
-
},
|
|
8757
|
-
{
|
|
8758
|
-
id: "dependency-security-weekly",
|
|
8759
|
-
category: "engineering",
|
|
8760
|
-
cronPattern: "0 10 * * 1",
|
|
8761
|
-
interests: ["coding"],
|
|
8762
|
-
requiresSkills: [{
|
|
8763
|
-
provider: "github",
|
|
8764
|
-
source: "lobehub"
|
|
8765
|
-
}]
|
|
8766
|
-
},
|
|
8767
|
-
{
|
|
8768
|
-
id: "vercel-health-weekly",
|
|
8769
|
-
category: "engineering",
|
|
8770
|
-
cronPattern: "0 10 * * 1",
|
|
8771
|
-
interests: ["coding"],
|
|
8772
|
-
requiresSkills: [{
|
|
8773
|
-
provider: "vercel",
|
|
8774
|
-
source: "lobehub"
|
|
8775
|
-
}]
|
|
8776
|
-
},
|
|
8777
|
-
{
|
|
8778
|
-
id: "linear-sprint-daily",
|
|
8779
|
-
category: "engineering",
|
|
8780
|
-
cronPattern: "30 8 * * *",
|
|
8781
|
-
interests: ["coding", "product"],
|
|
8782
|
-
requiresSkills: [{
|
|
8783
|
-
provider: "linear",
|
|
8784
|
-
source: "lobehub"
|
|
8785
|
-
}]
|
|
8786
|
-
},
|
|
8787
|
-
{
|
|
8788
|
-
id: "tech-trend-weekly",
|
|
8789
|
-
category: "engineering",
|
|
8790
|
-
cronPattern: "0 8 * * 1",
|
|
8791
|
-
interests: ["coding"]
|
|
8792
|
-
},
|
|
8793
|
-
{
|
|
8794
|
-
id: "keyword-tech-feed",
|
|
8795
|
-
category: "engineering",
|
|
8796
|
-
cronPattern: "0 10 * * *",
|
|
8797
|
-
interests: ["coding"]
|
|
8798
|
-
},
|
|
8799
|
-
{
|
|
8800
|
-
id: "daily-design-inspiration",
|
|
8801
|
-
category: "design",
|
|
8802
|
-
cronPattern: "0 9 * * *",
|
|
8803
|
-
interests: ["design"]
|
|
8804
|
-
},
|
|
8805
|
-
{
|
|
8806
|
-
id: "design-trend-weekly",
|
|
8807
|
-
category: "design",
|
|
8808
|
-
cronPattern: "0 9 * * 1",
|
|
8809
|
-
interests: ["design"]
|
|
8810
|
-
},
|
|
8811
|
-
{
|
|
8812
|
-
id: "figma-files-cleanup",
|
|
8813
|
-
category: "design",
|
|
8814
|
-
cronPattern: "0 17 * * 5",
|
|
8815
|
-
interests: ["design"],
|
|
8816
|
-
requiresSkills: [{
|
|
8817
|
-
provider: "figma",
|
|
8818
|
-
source: "composio"
|
|
8819
|
-
}]
|
|
8820
|
-
},
|
|
8821
|
-
{
|
|
8822
|
-
id: "aigc-prompt-inspiration",
|
|
8823
|
-
category: "design",
|
|
8824
|
-
cronPattern: "0 10 * * *",
|
|
8825
|
-
interests: ["design"]
|
|
8826
|
-
},
|
|
8827
|
-
{
|
|
8828
|
-
id: "brand-watch-weekly",
|
|
8829
|
-
category: "design",
|
|
8830
|
-
cronPattern: "0 10 * * 1",
|
|
8831
|
-
interests: ["design"]
|
|
8832
|
-
},
|
|
8833
|
-
{
|
|
8834
|
-
id: "font-color-weekly",
|
|
8835
|
-
category: "design",
|
|
8836
|
-
cronPattern: "0 10 * * 3",
|
|
8837
|
-
interests: ["design"]
|
|
8838
|
-
},
|
|
8839
|
-
{
|
|
8840
|
-
id: "arxiv-curated-daily",
|
|
8841
|
-
category: "learning-research",
|
|
8842
|
-
cronPattern: "0 9 * * *",
|
|
8843
|
-
interests: ["education"]
|
|
8844
|
-
},
|
|
8845
|
-
{
|
|
8846
|
-
id: "must-read-papers-weekly",
|
|
8847
|
-
category: "learning-research",
|
|
8848
|
-
cronPattern: "0 20 * * 0",
|
|
8849
|
-
interests: ["education"]
|
|
8850
|
-
},
|
|
8851
|
-
{
|
|
8852
|
-
id: "language-morning-bite",
|
|
8853
|
-
category: "learning-research",
|
|
8854
|
-
cronPattern: "30 7 * * *",
|
|
8855
|
-
interests: ["education"]
|
|
8856
|
-
},
|
|
8857
|
-
{
|
|
8858
|
-
id: "industry-research-weekly",
|
|
8859
|
-
category: "learning-research",
|
|
8860
|
-
cronPattern: "0 9 * * 1",
|
|
8861
|
-
interests: ["education", "business"]
|
|
8862
|
-
},
|
|
8863
|
-
{
|
|
8864
|
-
id: "industry-morning-brief",
|
|
8865
|
-
category: "business",
|
|
8866
|
-
cronPattern: "0 8 * * *",
|
|
8867
|
-
interests: ["business"]
|
|
8868
|
-
},
|
|
8869
|
-
{
|
|
8870
|
-
id: "competitor-radar-daily",
|
|
8871
|
-
category: "business",
|
|
8872
|
-
cronPattern: "0 9 * * *",
|
|
8873
|
-
interests: ["business"]
|
|
8874
|
-
},
|
|
8875
|
-
{
|
|
8876
|
-
id: "funding-intel-daily",
|
|
8877
|
-
category: "business",
|
|
8878
|
-
cronPattern: "0 10 * * *",
|
|
8879
|
-
interests: ["business"]
|
|
8880
|
-
},
|
|
8881
|
-
{
|
|
8882
|
-
id: "macro-economy-weekly",
|
|
8883
|
-
category: "business",
|
|
8884
|
-
cronPattern: "0 8 * * 1",
|
|
8885
|
-
interests: ["business", "investing"]
|
|
8886
|
-
},
|
|
8887
|
-
{
|
|
8888
|
-
id: "weekly-meeting-brief",
|
|
8889
|
-
category: "business",
|
|
8890
|
-
cronPattern: "30 8 * * 1",
|
|
8891
|
-
interests: ["business"]
|
|
8892
|
-
},
|
|
8893
|
-
{
|
|
8894
|
-
id: "marketing-hot-radar",
|
|
8895
|
-
category: "marketing",
|
|
8896
|
-
cronPattern: "0 10 * * *",
|
|
8897
|
-
interests: ["marketing"]
|
|
8898
|
-
},
|
|
8899
|
-
{
|
|
8900
|
-
id: "ad-creative-inspiration",
|
|
8901
|
-
category: "marketing",
|
|
8902
|
-
cronPattern: "0 10 * * *",
|
|
8903
|
-
interests: ["marketing"]
|
|
8904
|
-
},
|
|
8905
|
-
{
|
|
8906
|
-
id: "brand-mention-daily",
|
|
8907
|
-
category: "marketing",
|
|
8908
|
-
cronPattern: "0 18 * * *",
|
|
8909
|
-
interests: ["marketing"],
|
|
8910
|
-
requiresSkills: [{
|
|
8911
|
-
provider: "twitter",
|
|
8912
|
-
source: "lobehub"
|
|
8913
|
-
}]
|
|
8914
|
-
},
|
|
8915
|
-
{
|
|
8916
|
-
id: "seo-weekly-report",
|
|
8917
|
-
category: "marketing",
|
|
8918
|
-
cronPattern: "0 9 * * 1",
|
|
8919
|
-
interests: ["marketing"]
|
|
8920
|
-
},
|
|
8921
|
-
{
|
|
8922
|
-
id: "newsletter-perf-weekly",
|
|
8923
|
-
category: "marketing",
|
|
8924
|
-
cronPattern: "0 10 * * 1",
|
|
8925
|
-
interests: ["marketing"],
|
|
8926
|
-
requiresSkills: [{
|
|
8927
|
-
provider: "gmail",
|
|
8928
|
-
source: "composio"
|
|
8929
|
-
}]
|
|
8930
|
-
},
|
|
8931
|
-
{
|
|
8932
|
-
id: "kol-collab-calendar",
|
|
8933
|
-
category: "marketing",
|
|
8934
|
-
cronPattern: "0 9 * * 1",
|
|
8935
|
-
interests: ["marketing"],
|
|
8936
|
-
requiresSkills: [{
|
|
8937
|
-
provider: "airtable",
|
|
8938
|
-
source: "composio"
|
|
8939
|
-
}]
|
|
8940
|
-
},
|
|
8941
|
-
{
|
|
8942
|
-
id: "hubspot-funnel-daily",
|
|
8943
|
-
category: "marketing",
|
|
8944
|
-
cronPattern: "0 9 * * *",
|
|
8945
|
-
interests: ["marketing", "sales"],
|
|
8946
|
-
requiresSkills: [{
|
|
8947
|
-
provider: "hubspot",
|
|
8948
|
-
source: "composio"
|
|
8949
|
-
}]
|
|
8950
|
-
},
|
|
8951
|
-
{
|
|
8952
|
-
id: "user-feedback-daily",
|
|
8953
|
-
category: "product",
|
|
8954
|
-
cronPattern: "0 9 * * *",
|
|
8955
|
-
interests: ["product"]
|
|
8956
|
-
},
|
|
8957
|
-
{
|
|
8958
|
-
id: "competitor-update-daily",
|
|
8959
|
-
category: "product",
|
|
8960
|
-
cronPattern: "0 10 * * *",
|
|
8961
|
-
interests: ["product"]
|
|
8962
|
-
},
|
|
8963
|
-
{
|
|
8964
|
-
id: "standup-brief",
|
|
8965
|
-
category: "product",
|
|
8966
|
-
cronPattern: "30 8 * * *",
|
|
8967
|
-
interests: ["product"],
|
|
8968
|
-
requiresSkills: [{
|
|
8969
|
-
provider: "linear",
|
|
8970
|
-
source: "lobehub"
|
|
8971
|
-
}]
|
|
8972
|
-
},
|
|
8973
|
-
{
|
|
8974
|
-
id: "iteration-recap-weekly",
|
|
8975
|
-
category: "product",
|
|
8976
|
-
cronPattern: "0 17 * * 5",
|
|
8977
|
-
interests: ["product"],
|
|
8978
|
-
requiresSkills: [{
|
|
8979
|
-
provider: "linear",
|
|
8980
|
-
source: "lobehub"
|
|
8981
|
-
}]
|
|
8982
|
-
},
|
|
8983
|
-
{
|
|
8984
|
-
id: "core-metric-daily",
|
|
8985
|
-
category: "product",
|
|
8986
|
-
cronPattern: "0 9 * * *",
|
|
8987
|
-
interests: ["product"]
|
|
8988
|
-
},
|
|
8989
|
-
{
|
|
8990
|
-
id: "user-interview-schedule",
|
|
8991
|
-
category: "product",
|
|
8992
|
-
cronPattern: "0 9 * * 1",
|
|
8993
|
-
interests: ["product"],
|
|
8994
|
-
requiresSkills: [{
|
|
8995
|
-
provider: "google-calendar",
|
|
8996
|
-
source: "composio"
|
|
8997
|
-
}]
|
|
8998
|
-
},
|
|
8999
|
-
{
|
|
9000
|
-
id: "prd-review-reminder",
|
|
9001
|
-
category: "product",
|
|
9002
|
-
cronPattern: "0 15 * * 5",
|
|
9003
|
-
interests: ["product"],
|
|
9004
|
-
requiresSkills: [{
|
|
9005
|
-
provider: "notion",
|
|
9006
|
-
source: "lobehub"
|
|
9007
|
-
}]
|
|
9008
|
-
},
|
|
9009
|
-
{
|
|
9010
|
-
id: "daily-followup-list",
|
|
9011
|
-
category: "sales-customer",
|
|
9012
|
-
cronPattern: "0 9 * * *",
|
|
9013
|
-
interests: ["sales"],
|
|
9014
|
-
requiresSkills: [{
|
|
9015
|
-
provider: "hubspot",
|
|
9016
|
-
source: "composio"
|
|
9017
|
-
}]
|
|
9018
|
-
},
|
|
9019
|
-
{
|
|
9020
|
-
id: "renewal-risk-weekly",
|
|
9021
|
-
category: "sales-customer",
|
|
9022
|
-
cronPattern: "0 9 * * 1",
|
|
9023
|
-
interests: ["sales"],
|
|
9024
|
-
requiresSkills: [{
|
|
9025
|
-
provider: "hubspot",
|
|
9026
|
-
source: "composio"
|
|
9027
|
-
}]
|
|
9028
|
-
},
|
|
9029
|
-
{
|
|
9030
|
-
id: "deal-pipeline-weekly",
|
|
9031
|
-
category: "sales-customer",
|
|
9032
|
-
cronPattern: "0 16 * * 5",
|
|
9033
|
-
interests: ["sales"],
|
|
9034
|
-
requiresSkills: [{
|
|
9035
|
-
provider: "hubspot",
|
|
9036
|
-
source: "composio"
|
|
9037
|
-
}]
|
|
9038
|
-
},
|
|
9039
|
-
{
|
|
9040
|
-
id: "key-account-radar",
|
|
9041
|
-
category: "sales-customer",
|
|
9042
|
-
cronPattern: "0 9 * * *",
|
|
9043
|
-
interests: ["sales"]
|
|
9044
|
-
},
|
|
9045
|
-
{
|
|
9046
|
-
id: "zendesk-ticket-daily",
|
|
9047
|
-
category: "sales-customer",
|
|
9048
|
-
cronPattern: "0 9 * * *",
|
|
9049
|
-
interests: ["sales"],
|
|
9050
|
-
requiresSkills: [{
|
|
9051
|
-
provider: "zendesk",
|
|
9052
|
-
source: "composio"
|
|
9053
|
-
}]
|
|
9054
|
-
},
|
|
9055
|
-
{
|
|
9056
|
-
id: "morning-brief",
|
|
9057
|
-
category: "operations",
|
|
9058
|
-
cronPattern: "0 8 * * *",
|
|
9059
|
-
interests: ["operations"],
|
|
9060
|
-
requiresSkills: [{
|
|
9061
|
-
provider: "google-calendar",
|
|
9062
|
-
source: "composio"
|
|
9063
|
-
}]
|
|
9064
|
-
},
|
|
9065
|
-
{
|
|
9066
|
-
id: "meeting-brief",
|
|
9067
|
-
category: "operations",
|
|
9068
|
-
cronPattern: "30 8 * * *",
|
|
9069
|
-
interests: ["operations"],
|
|
9070
|
-
requiresSkills: [{
|
|
9071
|
-
provider: "google-calendar",
|
|
9072
|
-
source: "composio"
|
|
9073
|
-
}]
|
|
9074
|
-
},
|
|
9075
|
-
{
|
|
9076
|
-
id: "calendar-conflict-check",
|
|
9077
|
-
category: "operations",
|
|
9078
|
-
cronPattern: "30 7 * * *",
|
|
9079
|
-
interests: ["operations"],
|
|
9080
|
-
requiresSkills: [{
|
|
9081
|
-
provider: "google-calendar",
|
|
9082
|
-
source: "composio"
|
|
9083
|
-
}]
|
|
9084
|
-
},
|
|
9085
|
-
{
|
|
9086
|
-
id: "friday-wrap-list",
|
|
9087
|
-
category: "operations",
|
|
9088
|
-
cronPattern: "0 16 * * 5",
|
|
9089
|
-
interests: ["operations"],
|
|
9090
|
-
requiresSkills: [{
|
|
9091
|
-
provider: "linear",
|
|
9092
|
-
source: "lobehub"
|
|
9093
|
-
}]
|
|
9094
|
-
},
|
|
9095
|
-
{
|
|
9096
|
-
id: "recruit-funnel-daily",
|
|
9097
|
-
category: "hr",
|
|
9098
|
-
cronPattern: "0 9 * * *",
|
|
9099
|
-
interests: ["hr"],
|
|
9100
|
-
requiresSkills: [{
|
|
9101
|
-
provider: "airtable",
|
|
9102
|
-
source: "composio"
|
|
9103
|
-
}]
|
|
9104
|
-
},
|
|
9105
|
-
{
|
|
9106
|
-
id: "onboarding-buddy-weekly",
|
|
9107
|
-
category: "hr",
|
|
9108
|
-
cronPattern: "0 9 * * 1",
|
|
9109
|
-
interests: ["hr"],
|
|
9110
|
-
requiresSkills: [{
|
|
9111
|
-
provider: "notion",
|
|
9112
|
-
source: "lobehub"
|
|
9113
|
-
}]
|
|
9114
|
-
},
|
|
9115
|
-
{
|
|
9116
|
-
id: "team-status-weekly",
|
|
9117
|
-
category: "hr",
|
|
9118
|
-
cronPattern: "0 9 * * 1",
|
|
9119
|
-
interests: ["hr"],
|
|
9120
|
-
requiresSkills: [{
|
|
9121
|
-
provider: "google-calendar",
|
|
9122
|
-
source: "composio"
|
|
9123
|
-
}]
|
|
9124
|
-
},
|
|
9125
|
-
{
|
|
9126
|
-
id: "precious-metals-daily",
|
|
9127
|
-
category: "finance-legal",
|
|
9128
|
-
cronPattern: "0 16 * * *",
|
|
9129
|
-
interests: ["finance-legal", "investing"]
|
|
9130
|
-
},
|
|
9131
|
-
{
|
|
9132
|
-
id: "pre-market-brief",
|
|
9133
|
-
category: "finance-legal",
|
|
9134
|
-
cronPattern: "0 9 * * *",
|
|
9135
|
-
interests: ["finance-legal", "investing"]
|
|
9136
|
-
},
|
|
9137
|
-
{
|
|
9138
|
-
id: "cashflow-weekly",
|
|
9139
|
-
category: "finance-legal",
|
|
9140
|
-
cronPattern: "0 9 * * 1",
|
|
9141
|
-
interests: ["finance-legal"],
|
|
9142
|
-
requiresSkills: [{
|
|
9143
|
-
provider: "airtable",
|
|
9144
|
-
source: "composio"
|
|
9145
|
-
}]
|
|
9146
|
-
},
|
|
9147
|
-
{
|
|
9148
|
-
id: "contract-expiry-weekly",
|
|
9149
|
-
category: "finance-legal",
|
|
9150
|
-
cronPattern: "0 9 * * 1",
|
|
9151
|
-
interests: ["finance-legal"],
|
|
9152
|
-
requiresSkills: [{
|
|
9153
|
-
provider: "notion",
|
|
9154
|
-
source: "lobehub"
|
|
9155
|
-
}]
|
|
9156
|
-
},
|
|
9157
|
-
{
|
|
9158
|
-
id: "regulation-watch-weekly",
|
|
9159
|
-
category: "finance-legal",
|
|
9160
|
-
cronPattern: "0 10 * * 1",
|
|
9161
|
-
interests: ["finance-legal"]
|
|
9162
|
-
},
|
|
9163
|
-
{
|
|
9164
|
-
id: "invoice-collection-daily",
|
|
9165
|
-
category: "finance-legal",
|
|
9166
|
-
cronPattern: "0 10 * * *",
|
|
9167
|
-
interests: ["finance-legal"],
|
|
9168
|
-
requiresSkills: [{
|
|
9169
|
-
provider: "gmail",
|
|
9170
|
-
source: "composio"
|
|
9171
|
-
}]
|
|
9172
|
-
},
|
|
9173
|
-
{
|
|
9174
|
-
id: "cross-platform-engagement-daily",
|
|
9175
|
-
category: "creator",
|
|
9176
|
-
cronPattern: "0 9 * * *",
|
|
9177
|
-
interests: ["creator"],
|
|
9178
|
-
requiresSkills: [{
|
|
9179
|
-
provider: "twitter",
|
|
9180
|
-
source: "lobehub"
|
|
9181
|
-
}]
|
|
9182
|
-
},
|
|
9183
|
-
{
|
|
9184
|
-
id: "brand-collab-weekly",
|
|
9185
|
-
category: "creator",
|
|
9186
|
-
cronPattern: "0 10 * * 1",
|
|
9187
|
-
interests: ["creator"]
|
|
9188
|
-
},
|
|
9189
|
-
{
|
|
9190
|
-
id: "follower-growth-weekly",
|
|
9191
|
-
category: "creator",
|
|
9192
|
-
cronPattern: "0 10 * * 1",
|
|
9193
|
-
interests: ["creator"],
|
|
9194
|
-
requiresSkills: [{
|
|
9195
|
-
provider: "twitter",
|
|
9196
|
-
source: "lobehub"
|
|
9197
|
-
}]
|
|
9198
|
-
},
|
|
9199
|
-
{
|
|
9200
|
-
id: "youtube-channel-weekly",
|
|
9201
|
-
category: "creator",
|
|
9202
|
-
cronPattern: "0 9 * * 1",
|
|
9203
|
-
interests: ["creator"],
|
|
9204
|
-
requiresSkills: [{
|
|
9205
|
-
provider: "youtube",
|
|
9206
|
-
source: "composio"
|
|
9207
|
-
}]
|
|
9208
|
-
},
|
|
9209
|
-
{
|
|
9210
|
-
id: "monetization-opportunity-weekly",
|
|
9211
|
-
category: "creator",
|
|
9212
|
-
cronPattern: "0 10 * * 3",
|
|
9213
|
-
interests: ["creator"]
|
|
9214
|
-
},
|
|
9215
|
-
{
|
|
9216
|
-
id: "portfolio-daily",
|
|
9217
|
-
category: "investing",
|
|
9218
|
-
cronPattern: "0 16 * * *",
|
|
9219
|
-
interests: ["investing"]
|
|
9220
|
-
},
|
|
9221
|
-
{
|
|
9222
|
-
id: "crypto-market-daily",
|
|
9223
|
-
category: "investing",
|
|
9224
|
-
cronPattern: "0 9 * * *",
|
|
9225
|
-
interests: ["investing"]
|
|
9226
|
-
},
|
|
9227
|
-
{
|
|
9228
|
-
id: "child-growth-weekly",
|
|
9229
|
-
category: "parenting",
|
|
9230
|
-
cronPattern: "0 9 * * 1",
|
|
9231
|
-
interests: ["parenting"]
|
|
9232
|
-
},
|
|
9233
|
-
{
|
|
9234
|
-
id: "child-study-weekly",
|
|
9235
|
-
category: "parenting",
|
|
9236
|
-
cronPattern: "0 20 * * 0",
|
|
9237
|
-
interests: ["parenting", "education"]
|
|
9238
|
-
},
|
|
9239
|
-
{
|
|
9240
|
-
id: "family-finance-weekly",
|
|
9241
|
-
category: "parenting",
|
|
9242
|
-
cronPattern: "0 20 * * 0",
|
|
9243
|
-
interests: ["parenting", "finance-legal"],
|
|
9244
|
-
requiresSkills: [{
|
|
9245
|
-
provider: "google-sheets",
|
|
9246
|
-
source: "composio"
|
|
9247
|
-
}]
|
|
9248
|
-
},
|
|
9249
|
-
{
|
|
9250
|
-
id: "family-task-schedule",
|
|
9251
|
-
category: "parenting",
|
|
9252
|
-
cronPattern: "0 8 * * 1",
|
|
9253
|
-
interests: ["parenting"],
|
|
9254
|
-
optionalSkills: [{
|
|
9255
|
-
provider: "google-calendar",
|
|
9256
|
-
source: "composio"
|
|
9257
|
-
}]
|
|
9258
|
-
},
|
|
9259
|
-
{
|
|
9260
|
-
id: "diet-log-companion",
|
|
9261
|
-
category: "health",
|
|
9262
|
-
cronPattern: "0 21 * * *",
|
|
9263
|
-
interests: ["health"]
|
|
9264
|
-
},
|
|
9265
|
-
{
|
|
9266
|
-
id: "podcast-new-episodes",
|
|
9267
|
-
category: "hobbies",
|
|
9268
|
-
cronPattern: "0 9 * * 1",
|
|
9269
|
-
interests: ["hobbies"]
|
|
9270
|
-
},
|
|
9271
|
-
{
|
|
9272
|
-
id: "newsletter-aggregator",
|
|
9273
|
-
category: "hobbies",
|
|
9274
|
-
cronPattern: "0 20 * * 0",
|
|
9275
|
-
interests: ["hobbies"],
|
|
9276
|
-
requiresSkills: [{
|
|
9277
|
-
provider: "gmail",
|
|
9278
|
-
source: "composio"
|
|
9279
|
-
}]
|
|
9280
|
-
},
|
|
9281
|
-
{
|
|
9282
|
-
id: "series-update-weekly",
|
|
9283
|
-
category: "hobbies",
|
|
9284
|
-
cronPattern: "0 9 * * 1",
|
|
9285
|
-
interests: ["hobbies"]
|
|
9286
|
-
},
|
|
9287
|
-
{
|
|
9288
|
-
id: "travel-inspiration-weekly",
|
|
9289
|
-
category: "hobbies",
|
|
9290
|
-
cronPattern: "0 10 * * 3",
|
|
9291
|
-
interests: ["hobbies"]
|
|
9292
|
-
},
|
|
9293
|
-
{
|
|
9294
|
-
id: "watchlist-friday",
|
|
9295
|
-
category: "hobbies",
|
|
9296
|
-
cronPattern: "0 18 * * 5",
|
|
9297
|
-
interests: ["hobbies"]
|
|
9298
|
-
},
|
|
9299
|
-
{
|
|
9300
|
-
id: "exhibition-event-weekly",
|
|
9301
|
-
category: "hobbies",
|
|
9302
|
-
cronPattern: "0 10 * * 1",
|
|
9303
|
-
interests: ["hobbies"]
|
|
9304
|
-
},
|
|
9305
|
-
{
|
|
9306
|
-
id: "daily-learning-bite",
|
|
9307
|
-
category: "personal-life",
|
|
9308
|
-
cronPattern: "30 7 * * *",
|
|
9309
|
-
interests: ["education", "personal"]
|
|
9310
|
-
},
|
|
9311
|
-
{
|
|
9312
|
-
id: "sunday-reflection",
|
|
9313
|
-
category: "personal-life",
|
|
9314
|
-
cronPattern: "0 21 * * 0",
|
|
9315
|
-
interests: ["personal"]
|
|
9316
|
-
},
|
|
9317
|
-
{
|
|
9318
|
-
id: "morning-ritual",
|
|
9319
|
-
category: "personal-life",
|
|
9320
|
-
cronPattern: "0 7 * * *",
|
|
9321
|
-
interests: ["personal"],
|
|
9322
|
-
optionalSkills: [{
|
|
9323
|
-
provider: "google-calendar",
|
|
9324
|
-
source: "composio"
|
|
9325
|
-
}]
|
|
9326
|
-
},
|
|
9327
|
-
{
|
|
9328
|
-
id: "bedtime-gratitude",
|
|
9329
|
-
category: "personal-life",
|
|
9330
|
-
cronPattern: "0 22 * * *",
|
|
9331
|
-
interests: ["personal"],
|
|
9332
|
-
optionalSkills: [{
|
|
9333
|
-
provider: "notion",
|
|
9334
|
-
source: "lobehub"
|
|
9335
|
-
}]
|
|
9336
|
-
}
|
|
9337
|
-
];
|
|
9338
|
-
const KNOWN_TASK_TEMPLATE_IDS = new Set(taskTemplates.map((t) => t.id));
|
|
9339
|
-
|
|
9340
8705
|
//#endregion
|
|
9341
8706
|
//#region node_modules/.pnpm/url-join@5.0.0/node_modules/url-join/lib/url-join.js
|
|
9342
8707
|
function normalize(strArray) {
|
|
@@ -9419,7 +8784,7 @@ const MEMORY_SEARCH_TOP_K_LIMITS = {
|
|
|
9419
8784
|
|
|
9420
8785
|
//#endregion
|
|
9421
8786
|
//#region ../../package.json
|
|
9422
|
-
var version$3 = "2.2.
|
|
8787
|
+
var version$3 = "2.2.6";
|
|
9423
8788
|
|
|
9424
8789
|
//#endregion
|
|
9425
8790
|
//#region ../../packages/const/src/version.ts
|
|
@@ -18080,11 +17445,27 @@ const expandTilde = (input) => {
|
|
|
18080
17445
|
if (input.startsWith("~/") || input.startsWith("~\\")) return path.join(os.homedir(), input.slice(2));
|
|
18081
17446
|
return input;
|
|
18082
17447
|
};
|
|
17448
|
+
/**
|
|
17449
|
+
* Resolve a filesystem path for Node fs APIs: first expand a leading `~`, then
|
|
17450
|
+
* anchor a still-relative path to `cwd` — the device-bound working directory.
|
|
17451
|
+
*
|
|
17452
|
+
* Absolute paths pass through untouched. When `cwd` is absent the behavior is
|
|
17453
|
+
* identical to {@link expandTilde}, so callers that don't carry a working
|
|
17454
|
+
* directory (e.g. desktop client-mode today) keep resolving relative paths
|
|
17455
|
+
* against the process cwd and nothing regresses. Without this, a relative path
|
|
17456
|
+
* supplied by the model resolves against the daemon's `process.cwd()` (= `/`
|
|
17457
|
+
* for a Finder/Dock-launched app) instead of the user's bound directory.
|
|
17458
|
+
*/
|
|
17459
|
+
const resolveAgainstCwd = (input, cwd) => {
|
|
17460
|
+
const expanded = expandTilde(input);
|
|
17461
|
+
if (!expanded || !cwd) return expanded;
|
|
17462
|
+
return path.isAbsolute(expanded) ? expanded : path.join(cwd, expanded);
|
|
17463
|
+
};
|
|
18083
17464
|
|
|
18084
17465
|
//#endregion
|
|
18085
17466
|
//#region ../../packages/local-file-shell/src/file/edit.ts
|
|
18086
|
-
async function editLocalFile({ file_path: rawPath, old_string, new_string, replace_all = false }) {
|
|
18087
|
-
const filePath =
|
|
17467
|
+
async function editLocalFile({ file_path: rawPath, old_string, new_string, replace_all = false, cwd }) {
|
|
17468
|
+
const filePath = resolveAgainstCwd(rawPath, cwd) ?? rawPath;
|
|
18088
17469
|
try {
|
|
18089
17470
|
const content = await readFile(filePath, "utf8");
|
|
18090
17471
|
if (!content.includes(old_string)) return {
|
|
@@ -198620,9 +198001,9 @@ const sniffBinaryFile = async (filePath) => {
|
|
|
198620
198001
|
|
|
198621
198002
|
//#endregion
|
|
198622
198003
|
//#region ../../packages/local-file-shell/src/file/list.ts
|
|
198623
|
-
async function listLocalFiles({ path: rawPath, sortBy = "modifiedTime", sortOrder = "desc", limit = 100 }, options) {
|
|
198004
|
+
async function listLocalFiles({ path: rawPath, sortBy = "modifiedTime", sortOrder = "desc", limit = 100, cwd }, options) {
|
|
198624
198005
|
const { ignoreSystemFiles = true } = options || {};
|
|
198625
|
-
const dirPath =
|
|
198006
|
+
const dirPath = resolveAgainstCwd(rawPath, cwd) ?? rawPath;
|
|
198626
198007
|
try {
|
|
198627
198008
|
const entries = await readdir(dirPath);
|
|
198628
198009
|
const results = [];
|
|
@@ -198678,12 +198059,12 @@ async function listLocalFiles({ path: rawPath, sortBy = "modifiedTime", sortOrde
|
|
|
198678
198059
|
|
|
198679
198060
|
//#endregion
|
|
198680
198061
|
//#region ../../packages/local-file-shell/src/file/move.ts
|
|
198681
|
-
async function moveLocalFiles({ items }) {
|
|
198062
|
+
async function moveLocalFiles({ items, cwd }) {
|
|
198682
198063
|
const results = [];
|
|
198683
198064
|
if (!items || items.length === 0) return [];
|
|
198684
198065
|
for (const item of items) {
|
|
198685
|
-
const sourcePath =
|
|
198686
|
-
const newPath =
|
|
198066
|
+
const sourcePath = resolveAgainstCwd(item.oldPath, cwd) ?? item.oldPath;
|
|
198067
|
+
const newPath = resolveAgainstCwd(item.newPath, cwd) ?? item.newPath;
|
|
198687
198068
|
const resultItem = {
|
|
198688
198069
|
newPath: void 0,
|
|
198689
198070
|
sourcePath,
|
|
@@ -198759,8 +198140,8 @@ const truncateLine = (line) => {
|
|
|
198759
198140
|
truncated: true
|
|
198760
198141
|
};
|
|
198761
198142
|
};
|
|
198762
|
-
async function readLocalFile({ path: rawPath, loc, fullContent }) {
|
|
198763
|
-
const filePath =
|
|
198143
|
+
async function readLocalFile({ path: rawPath, loc, fullContent, cwd }) {
|
|
198144
|
+
const filePath = resolveAgainstCwd(rawPath, cwd) ?? rawPath;
|
|
198764
198145
|
const effectiveLoc = fullContent ? void 0 : loc ?? [0, 200];
|
|
198765
198146
|
let stats;
|
|
198766
198147
|
try {
|
|
@@ -198921,7 +198302,7 @@ async function searchLocalFiles({ keywords, directory, onlyIn, contentContains,
|
|
|
198921
198302
|
|
|
198922
198303
|
//#endregion
|
|
198923
198304
|
//#region ../../packages/local-file-shell/src/file/write.ts
|
|
198924
|
-
async function writeLocalFile({ path: rawPath, content }) {
|
|
198305
|
+
async function writeLocalFile({ path: rawPath, content, cwd }) {
|
|
198925
198306
|
if (!rawPath) return {
|
|
198926
198307
|
error: "Path cannot be empty",
|
|
198927
198308
|
success: false
|
|
@@ -198930,7 +198311,7 @@ async function writeLocalFile({ path: rawPath, content }) {
|
|
|
198930
198311
|
error: "Content cannot be empty",
|
|
198931
198312
|
success: false
|
|
198932
198313
|
};
|
|
198933
|
-
const filePath =
|
|
198314
|
+
const filePath = resolveAgainstCwd(rawPath, cwd) ?? rawPath;
|
|
198934
198315
|
try {
|
|
198935
198316
|
await mkdir(path.dirname(filePath), { recursive: true });
|
|
198936
198317
|
await writeFile(filePath, content, "utf8");
|
|
@@ -207326,10 +206707,7 @@ function registerConnectCommand(program) {
|
|
|
207326
206707
|
if (options.daemon) return handleDaemonStart(options);
|
|
207327
206708
|
await runConnect(options, options.daemonChild || process.env.LOBEHUB_DAEMON === "1");
|
|
207328
206709
|
});
|
|
207329
|
-
connectCmd.command("stop").description("Stop the background daemon process").action(
|
|
207330
|
-
if (stopDaemon()) log$10.info("Daemon stopped.");
|
|
207331
|
-
else log$10.warn("No daemon is running.");
|
|
207332
|
-
});
|
|
206710
|
+
connectCmd.command("stop").description("Stop the background daemon process").action(handleStop);
|
|
207333
206711
|
connectCmd.command("status").description("Show background daemon status").action(() => {
|
|
207334
206712
|
const pid = getRunningDaemonPid();
|
|
207335
206713
|
if (pid === null) {
|
|
@@ -207368,6 +206746,11 @@ function registerConnectCommand(program) {
|
|
|
207368
206746
|
daemon: true
|
|
207369
206747
|
});
|
|
207370
206748
|
});
|
|
206749
|
+
program.command("disconnect").description("Disconnect from the device gateway (alias for `connect stop`)").action(handleStop);
|
|
206750
|
+
}
|
|
206751
|
+
function handleStop() {
|
|
206752
|
+
if (stopDaemon()) log$10.info("Daemon stopped.");
|
|
206753
|
+
else log$10.warn("No daemon is running.");
|
|
207371
206754
|
}
|
|
207372
206755
|
function handleDaemonStart(options) {
|
|
207373
206756
|
const existingPid = getRunningDaemonPid();
|
|
@@ -208245,21 +207628,27 @@ function registerEvalCommand(program) {
|
|
|
208245
207628
|
* Unknown extensions fall back to `application/octet-stream`.
|
|
208246
207629
|
*/
|
|
208247
207630
|
const MIME_MAP = {
|
|
207631
|
+
aac: "audio/aac",
|
|
208248
207632
|
csv: "text/csv",
|
|
208249
207633
|
doc: "application/msword",
|
|
208250
207634
|
docx: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
207635
|
+
flac: "audio/flac",
|
|
208251
207636
|
gif: "image/gif",
|
|
208252
207637
|
jpeg: "image/jpeg",
|
|
208253
207638
|
jpg: "image/jpeg",
|
|
208254
207639
|
json: "application/json",
|
|
207640
|
+
m4a: "audio/mp4",
|
|
208255
207641
|
md: "text/markdown",
|
|
208256
207642
|
mp3: "audio/mpeg",
|
|
208257
207643
|
mp4: "video/mp4",
|
|
207644
|
+
ogg: "audio/ogg",
|
|
208258
207645
|
pdf: "application/pdf",
|
|
208259
207646
|
png: "image/png",
|
|
208260
207647
|
pptx: "application/vnd.openxmlformats-officedocument.presentationml.presentation",
|
|
208261
207648
|
svg: "image/svg+xml",
|
|
208262
207649
|
txt: "text/plain",
|
|
207650
|
+
wav: "audio/wav",
|
|
207651
|
+
webm: "audio/webm",
|
|
208263
207652
|
webp: "image/webp",
|
|
208264
207653
|
xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
|
|
208265
207654
|
};
|
|
@@ -208505,46 +207894,128 @@ function registerFileCommand(program) {
|
|
|
208505
207894
|
|
|
208506
207895
|
//#endregion
|
|
208507
207896
|
//#region src/commands/generate/asr.ts
|
|
207897
|
+
const MAX_INLINE_AUDIO_BYTES = 3 * 1024 * 1024;
|
|
208508
207898
|
function registerAsrCommand(parent) {
|
|
208509
|
-
parent.command("asr <audio-file>").description("Convert speech to text (automatic speech recognition)").option("--model <model>", "STT model", "whisper-1").option("--language <lang>", "Language code (e.g. en, zh)").option("--json", "Output raw JSON").action(async (audioFile, options) => {
|
|
208510
|
-
|
|
207899
|
+
parent.command("asr <audio-file>").description("Convert speech to text (automatic speech recognition). Accepts a local path or a URL").option("--model <model>", "STT model", "whisper-1").option("--provider <provider>", "AI provider", "openai").option("--language <lang>", "Language code (e.g. en, zh)").option("--json", "Output raw JSON").action(async (audioFile, options) => {
|
|
207900
|
+
const isUrl = audioFile.startsWith("http://") || audioFile.startsWith("https://");
|
|
207901
|
+
if (!isUrl && !existsSync(audioFile)) {
|
|
208511
207902
|
log$10.error(`File not found: ${audioFile}`);
|
|
208512
207903
|
process.exit(1);
|
|
208513
207904
|
return;
|
|
208514
207905
|
}
|
|
208515
|
-
|
|
208516
|
-
|
|
208517
|
-
|
|
208518
|
-
|
|
208519
|
-
|
|
208520
|
-
|
|
208521
|
-
|
|
208522
|
-
|
|
208523
|
-
|
|
208524
|
-
|
|
208525
|
-
|
|
208526
|
-
|
|
208527
|
-
|
|
208528
|
-
|
|
208529
|
-
|
|
208530
|
-
|
|
207906
|
+
let localPath;
|
|
207907
|
+
let fileName;
|
|
207908
|
+
let mimeType;
|
|
207909
|
+
let size;
|
|
207910
|
+
let tempPath;
|
|
207911
|
+
try {
|
|
207912
|
+
if (isUrl) {
|
|
207913
|
+
const downloaded = await fetchAudioFromUrl(audioFile);
|
|
207914
|
+
fileName = downloaded.name;
|
|
207915
|
+
mimeType = downloaded.mimeType;
|
|
207916
|
+
size = downloaded.bytes.byteLength;
|
|
207917
|
+
tempPath = path.join(os.tmpdir(), `lh-asr-${process.pid}-${Date.now()}-${fileName}`);
|
|
207918
|
+
await writeFile(tempPath, downloaded.bytes);
|
|
207919
|
+
localPath = tempPath;
|
|
207920
|
+
} else {
|
|
207921
|
+
localPath = audioFile;
|
|
207922
|
+
fileName = path.basename(audioFile);
|
|
207923
|
+
size = statSync(audioFile).size;
|
|
207924
|
+
}
|
|
207925
|
+
} catch (error) {
|
|
207926
|
+
log$10.error(error instanceof Error ? error.message : String(error));
|
|
208531
207927
|
process.exit(1);
|
|
208532
207928
|
return;
|
|
208533
207929
|
}
|
|
208534
|
-
|
|
208535
|
-
|
|
208536
|
-
|
|
208537
|
-
|
|
208538
|
-
|
|
208539
|
-
|
|
208540
|
-
|
|
208541
|
-
|
|
207930
|
+
try {
|
|
207931
|
+
const client = await getTrpcClient();
|
|
207932
|
+
let result;
|
|
207933
|
+
if (size > MAX_INLINE_AUDIO_BYTES) {
|
|
207934
|
+
process.stderr.write(`Audio is ${(size / 1024 / 1024).toFixed(1)}MB — uploading before transcription…\n`);
|
|
207935
|
+
const record = await uploadLocalFile(client, localPath);
|
|
207936
|
+
result = await client.asr.transcribe.mutate({
|
|
207937
|
+
fileId: record.id,
|
|
207938
|
+
language: options.language,
|
|
207939
|
+
model: options.model,
|
|
207940
|
+
provider: options.provider
|
|
207941
|
+
});
|
|
207942
|
+
} else {
|
|
207943
|
+
const bytes = await readFile(localPath);
|
|
207944
|
+
result = await client.asr.transcribe.mutate({
|
|
207945
|
+
audioBase64: Buffer.from(bytes).toString("base64"),
|
|
207946
|
+
fileName,
|
|
207947
|
+
language: options.language,
|
|
207948
|
+
mimeType,
|
|
207949
|
+
model: options.model,
|
|
207950
|
+
provider: options.provider
|
|
207951
|
+
});
|
|
207952
|
+
}
|
|
207953
|
+
if (options.json) console.log(JSON.stringify(result, null, 2));
|
|
207954
|
+
else {
|
|
207955
|
+
process.stdout.write(result.text);
|
|
207956
|
+
process.stdout.write("\n");
|
|
207957
|
+
}
|
|
207958
|
+
} catch (error) {
|
|
207959
|
+
log$10.error(`ASR failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
207960
|
+
process.exit(1);
|
|
207961
|
+
} finally {
|
|
207962
|
+
if (tempPath) await rm(tempPath, { force: true }).catch(() => {});
|
|
207963
|
+
}
|
|
207964
|
+
});
|
|
207965
|
+
}
|
|
207966
|
+
const AUDIO_MIME_TO_EXT = {
|
|
207967
|
+
"audio/aac": "aac",
|
|
207968
|
+
"audio/flac": "flac",
|
|
207969
|
+
"audio/m4a": "m4a",
|
|
207970
|
+
"audio/mp3": "mp3",
|
|
207971
|
+
"audio/mp4": "m4a",
|
|
207972
|
+
"audio/mpeg": "mp3",
|
|
207973
|
+
"audio/mpga": "mp3",
|
|
207974
|
+
"audio/ogg": "ogg",
|
|
207975
|
+
"audio/opus": "ogg",
|
|
207976
|
+
"audio/wav": "wav",
|
|
207977
|
+
"audio/wave": "wav",
|
|
207978
|
+
"audio/webm": "webm",
|
|
207979
|
+
"audio/x-m4a": "m4a",
|
|
207980
|
+
"audio/x-wav": "wav"
|
|
207981
|
+
};
|
|
207982
|
+
async function fetchAudioFromUrl(url) {
|
|
207983
|
+
const res = await fetch(url);
|
|
207984
|
+
if (!res.ok) throw new Error(`Failed to download audio: ${res.status} ${res.statusText}`);
|
|
207985
|
+
const bytes = new Uint8Array(await res.arrayBuffer());
|
|
207986
|
+
const contentType = res.headers.get("content-type")?.split(";")[0]?.trim().toLowerCase();
|
|
207987
|
+
const mimeType = contentType?.startsWith("audio/") ? contentType : void 0;
|
|
207988
|
+
const name = fileNameFromContentDisposition(res.headers.get("content-disposition")) || basenameFromUrl(url) || "audio";
|
|
207989
|
+
const ext = contentType ? AUDIO_MIME_TO_EXT[contentType] : void 0;
|
|
207990
|
+
return {
|
|
207991
|
+
bytes,
|
|
207992
|
+
mimeType,
|
|
207993
|
+
name: path.extname(name) || !ext ? name : `${name}.${ext}`
|
|
207994
|
+
};
|
|
208542
207995
|
}
|
|
208543
|
-
|
|
208544
|
-
|
|
208545
|
-
const
|
|
208546
|
-
|
|
208547
|
-
|
|
207996
|
+
function fileNameFromContentDisposition(header) {
|
|
207997
|
+
if (!header) return void 0;
|
|
207998
|
+
const extended = /filename\*=\s*(?:UTF-8|ISO-8859-1)?''([^;]+)/i.exec(header);
|
|
207999
|
+
if (extended?.[1]) try {
|
|
208000
|
+
return path.basename(decodeURIComponent(extended[1].trim()));
|
|
208001
|
+
} catch {}
|
|
208002
|
+
const value = /filename=\s*"?([^";]+)"?/i.exec(header)?.[1]?.trim();
|
|
208003
|
+
return value ? path.basename(value) : void 0;
|
|
208004
|
+
}
|
|
208005
|
+
function basenameFromUrl(url) {
|
|
208006
|
+
let pathname;
|
|
208007
|
+
try {
|
|
208008
|
+
pathname = new URL(url).pathname;
|
|
208009
|
+
} catch {
|
|
208010
|
+
return;
|
|
208011
|
+
}
|
|
208012
|
+
const base = path.basename(pathname);
|
|
208013
|
+
if (!base) return void 0;
|
|
208014
|
+
try {
|
|
208015
|
+
return decodeURIComponent(base);
|
|
208016
|
+
} catch {
|
|
208017
|
+
return base;
|
|
208018
|
+
}
|
|
208548
208019
|
}
|
|
208549
208020
|
|
|
208550
208021
|
//#endregion
|
|
@@ -209020,17 +208491,26 @@ const CLI_OVERLOADED_PATTERNS = [
|
|
|
209020
208491
|
...CLI_SERVER_THROTTLE_PATTERNS
|
|
209021
208492
|
];
|
|
209022
208493
|
/**
|
|
209023
|
-
*
|
|
209024
|
-
*
|
|
209025
|
-
*
|
|
209026
|
-
*
|
|
209027
|
-
*
|
|
209028
|
-
*
|
|
209029
|
-
*
|
|
209030
|
-
*
|
|
209031
|
-
*
|
|
208494
|
+
* Discriminates a user-side plan/quota limit from everything else.
|
|
208495
|
+
*
|
|
208496
|
+
* Two signals must BOTH hold:
|
|
208497
|
+
* 1. The request was actually `status: 'rejected'`. Anthropic stamps a
|
|
208498
|
+
* `rate_limit_info` onto its events even when the request goes through
|
|
208499
|
+
* (`status: 'allowed'`) — that block is just the rolling-window metadata
|
|
208500
|
+
* (`resetsAt`, `rateLimitType`) for an *allowed* call, NOT evidence the
|
|
208501
|
+
* limit was hit. Leaning on the presence of a reset window alone made a
|
|
208502
|
+
* later unrelated terminal failure (e.g. an `ECONNRESET` network drop)
|
|
208503
|
+
* inherit the last allowed event's window and render a bogus "usage limit
|
|
208504
|
+
* reached, resets at X" guide. The `status` is the gate.
|
|
208505
|
+
* 2. A concrete reset window (`resetsAt` epoch seconds and/or a named
|
|
208506
|
+
* `rateLimitType` such as `seven_day`). A bare `rejected` with no window is
|
|
208507
|
+
* Anthropic's transient server throttle — left to the overloaded (retry)
|
|
208508
|
+
* classifier, not the usage-limit guide.
|
|
208509
|
+
*
|
|
208510
|
+
* Status codes (429 / 529) and message text are deliberately not consulted
|
|
208511
|
+
* here — only this structured signal decides the "usage limit reached" guide.
|
|
209032
208512
|
*/
|
|
209033
|
-
const isUserQuotaRateLimit = (info) => !!info && (info.resetsAt != null || info.rateLimitType != null);
|
|
208513
|
+
const isUserQuotaRateLimit = (info) => !!info && info.status === "rejected" && (info.resetsAt != null || info.rateLimitType != null);
|
|
209034
208514
|
const getCliResultMessage = (result) => {
|
|
209035
208515
|
if (typeof result === "string") return result;
|
|
209036
208516
|
if (result && typeof result === "object" && "message" in result && typeof result.message === "string") return result.message;
|
|
@@ -212466,6 +211946,7 @@ async function openBrowser(url) {
|
|
|
212466
211946
|
//#region src/commands/logout.ts
|
|
212467
211947
|
function registerLogoutCommand(program) {
|
|
212468
211948
|
program.command("logout").description("Log out and remove stored credentials").action(() => {
|
|
211949
|
+
if (stopDaemon()) log$10.info("Disconnected device daemon.");
|
|
212469
211950
|
if (clearCredentials()) log$10.info("Logged out. Credentials removed.");
|
|
212470
211951
|
else log$10.info("No credentials found. Already logged out.");
|
|
212471
211952
|
});
|
|
@@ -213544,17 +213025,19 @@ function registerMigrateCommand(program) {
|
|
|
213544
213025
|
//#endregion
|
|
213545
213026
|
//#region src/commands/model.ts
|
|
213546
213027
|
const isVisibleModel = (model) => model.visible !== false;
|
|
213028
|
+
const normalizeModelType = (type) => type === "stt" ? "asr" : type;
|
|
213547
213029
|
function registerModelCommand(program) {
|
|
213548
213030
|
const model = program.command("model").description("Manage AI models");
|
|
213549
|
-
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|
|
|
213031
|
+
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|asr|image|video|text2music|realtime)").option("--json [fields]", "Output JSON, optionally specify fields (comma-separated)").action(async (providerId, options) => {
|
|
213550
213032
|
const client = await getTrpcClient();
|
|
213033
|
+
const typeFilter = options.type ? normalizeModelType(options.type) : void 0;
|
|
213551
213034
|
const input = { id: providerId };
|
|
213552
213035
|
if (options.limit) input.limit = Number.parseInt(options.limit, 10);
|
|
213553
213036
|
if (options.enabled) input.enabled = true;
|
|
213554
|
-
if (
|
|
213037
|
+
if (typeFilter) input.type = typeFilter;
|
|
213555
213038
|
const result = await client.aiModel.getAiProviderModelList.query(input);
|
|
213556
213039
|
let items = (Array.isArray(result) ? result : result.items ?? []).filter(isVisibleModel);
|
|
213557
|
-
if (
|
|
213040
|
+
if (typeFilter) items = items.filter((m) => m.type === typeFilter);
|
|
213558
213041
|
if (options.json !== void 0) {
|
|
213559
213042
|
const fields = typeof options.json === "string" ? options.json : void 0;
|
|
213560
213043
|
outputJson(items, fields);
|
|
@@ -213595,18 +213078,18 @@ function registerModelCommand(program) {
|
|
|
213595
213078
|
if (r.enabled !== void 0) meta.push(r.enabled ? "Enabled" : "Disabled");
|
|
213596
213079
|
if (meta.length > 0) console.log(import_picocolors.default.dim(meta.join(" · ")));
|
|
213597
213080
|
});
|
|
213598
|
-
model.command("create").description("Create a new model").requiredOption("--id <id>", "Model ID").requiredOption("--provider <providerId>", "Provider ID").option("--display-name <name>", "Display name").option("--type <type>", "Model type (chat|embedding|tts|
|
|
213081
|
+
model.command("create").description("Create a new model").requiredOption("--id <id>", "Model ID").requiredOption("--provider <providerId>", "Provider ID").option("--display-name <name>", "Display name").option("--type <type>", "Model type (chat|embedding|tts|asr|image|video|text2music|realtime)", "chat").action(async (options) => {
|
|
213599
213082
|
const client = await getTrpcClient();
|
|
213600
213083
|
const input = {
|
|
213601
213084
|
id: options.id,
|
|
213602
213085
|
providerId: options.provider,
|
|
213603
|
-
type: options.type || "chat"
|
|
213086
|
+
type: normalizeModelType(options.type || "chat")
|
|
213604
213087
|
};
|
|
213605
213088
|
if (options.displayName) input.displayName = options.displayName;
|
|
213606
213089
|
const resultId = await client.aiModel.createAiModel.mutate(input);
|
|
213607
213090
|
console.log(`${import_picocolors.default.green("✓")} Created model ${import_picocolors.default.bold(resultId || options.id)}`);
|
|
213608
213091
|
});
|
|
213609
|
-
model.command("edit <id>").description("Update model info").requiredOption("--provider <providerId>", "Provider ID").option("--display-name <name>", "Display name").option("--type <type>", "Model type (chat|embedding|tts|
|
|
213092
|
+
model.command("edit <id>").description("Update model info").requiredOption("--provider <providerId>", "Provider ID").option("--display-name <name>", "Display name").option("--type <type>", "Model type (chat|embedding|tts|asr|image|video|text2music|realtime)").action(async (id, options) => {
|
|
213610
213093
|
if (!options.displayName && !options.type) {
|
|
213611
213094
|
log$10.error("No changes specified. Use --display-name or --type.");
|
|
213612
213095
|
process.exit(1);
|
|
@@ -213614,7 +213097,7 @@ function registerModelCommand(program) {
|
|
|
213614
213097
|
const client = await getTrpcClient();
|
|
213615
213098
|
const value = {};
|
|
213616
213099
|
if (options.displayName) value.displayName = options.displayName;
|
|
213617
|
-
if (options.type) value.type = options.type;
|
|
213100
|
+
if (options.type) value.type = normalizeModelType(options.type);
|
|
213618
213101
|
await client.aiModel.updateAiModel.mutate({
|
|
213619
213102
|
id,
|
|
213620
213103
|
providerId: options.provider,
|
package/man/man1/lh.1
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
.\" Code generated by `npm run man:generate`; DO NOT EDIT.
|
|
2
2
|
.\" Manual command details come from the Commander command tree.
|
|
3
|
-
.TH LH 1 "" "@lobehub/cli 0.0.
|
|
3
|
+
.TH LH 1 "" "@lobehub/cli 0.0.33" "User Commands"
|
|
4
4
|
.SH NAME
|
|
5
5
|
lh \- LobeHub CLI \- manage and connect to LobeHub services
|
|
6
6
|
.SH SYNOPSIS
|
|
@@ -41,6 +41,9 @@ Show a manual page for the CLI or a subcommand
|
|
|
41
41
|
.B connect
|
|
42
42
|
Connect to the device gateway and listen for tool calls
|
|
43
43
|
.TP
|
|
44
|
+
.B disconnect
|
|
45
|
+
Disconnect from the device gateway (alias for `connect stop`)
|
|
46
|
+
.TP
|
|
44
47
|
.B device
|
|
45
48
|
Manage connected devices
|
|
46
49
|
.TP
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lobehub/cli",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.33",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"lh": "./dist/index.js",
|
|
@@ -31,13 +31,13 @@
|
|
|
31
31
|
"tsdown": "^0.21.4",
|
|
32
32
|
"typescript": "^6.0.3",
|
|
33
33
|
"ws": "^8.18.1",
|
|
34
|
-
"@lobechat/
|
|
34
|
+
"@lobechat/device-control": "1.0.0",
|
|
35
35
|
"@lobechat/device-gateway-client": "1.0.0",
|
|
36
|
+
"@lobechat/agent-gateway-client": "1.0.0",
|
|
36
37
|
"@lobechat/heterogeneous-agents": "1.0.0",
|
|
37
|
-
"@lobechat/
|
|
38
|
-
"@lobechat/device-identity": "1.0.0",
|
|
38
|
+
"@lobechat/tool-runtime": "1.0.0",
|
|
39
39
|
"@lobechat/local-file-shell": "1.0.0",
|
|
40
|
-
"@lobechat/
|
|
40
|
+
"@lobechat/device-identity": "1.0.0"
|
|
41
41
|
},
|
|
42
42
|
"publishConfig": {
|
|
43
43
|
"access": "public",
|