honeydo 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -0
- package/package.json +1 -1
- package/packages/cli/dist/index.js +0 -0
- package/packages/doubao/dist/cli.js +0 -0
- package/packages/gcli/dist/cli.d.ts +262 -6
- package/packages/gcli/dist/cli.js +1504 -73
- package/packages/gcli/dist/cli.js.map +1 -1
- package/packages/lmedia/dist/index.js +532 -132
- package/packages/lmedia/python/turbo_merge.py +239 -0
- package/packages/lmedia/python/video_gen.py +31 -0
- package/packages/minimax/dist/cli.js +0 -0
package/README.md
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
# honeydo
|
|
2
2
|
|
|
3
|
+
[](https://www.npmjs.com/package/honeydo)
|
|
4
|
+
[](./LICENSE)
|
|
5
|
+
|
|
3
6
|
> Honey, do everything. 🍯
|
|
4
7
|
|
|
5
8
|
One CLI for every AI capability — **chat, vision, image, video, sound effects, TTS** — cloud and local, built for AI agents.
|
package/package.json
CHANGED
|
File without changes
|
|
File without changes
|
|
@@ -47,7 +47,28 @@ export declare const LAST_PROVIDER_PATH: string;
|
|
|
47
47
|
* best-effort — the quota subtitle is an optimization, never an error.
|
|
48
48
|
*/
|
|
49
49
|
export declare const QUOTA_CACHE_PATH: string;
|
|
50
|
-
|
|
50
|
+
/** hermes 主配置文件(model 段 + providers 段是本子命令的编辑目标)。 */
|
|
51
|
+
export declare const HERMES_CONFIG_PATH: string;
|
|
52
|
+
/** hermes env 文件(provider API key 的落点;全程保持 0o600)。 */
|
|
53
|
+
export declare const HERMES_ENV_PATH: string;
|
|
54
|
+
/** hermes cron 任务定义(重 pin 只读它;改动经 `hermes cron edit` 下发)。 */
|
|
55
|
+
export declare const HERMES_CRON_JOBS_PATH: string;
|
|
56
|
+
/** hermes 会话库(session_model_usage 表在此 —— 实测在顶层 state.db)。 */
|
|
57
|
+
export declare const HERMES_STATE_DB_PATH: string;
|
|
58
|
+
/** cc-switch name → hermes provider 字段映射(keyEnv/modelOverride)。 */
|
|
59
|
+
export declare const HERMES_PROVIDERS_REGISTRY_PATH: string;
|
|
60
|
+
/** 上一次切换的一跳记录(0o600,含旧 token 的 prevValue)。 */
|
|
61
|
+
export declare const HERMES_STATE_PATH: string;
|
|
62
|
+
/** hermes providers 条目的 transport 常量(cc-switch 只提供 anthropic 兼容端点)。 */
|
|
63
|
+
export declare const HERMES_TRANSPORT = "anthropic_messages";
|
|
64
|
+
/** 切换后验证 ping 的硬超时。 */
|
|
65
|
+
export declare const HERMES_VERIFY_TIMEOUT_MS = 60000;
|
|
66
|
+
/**
|
|
67
|
+
* 内置 seed(防推导造出与现有配置平行的条目):键是 cc-switch provider 名,
|
|
68
|
+
* 值是 hermes 侧已手工建立好的 id/keyEnv。
|
|
69
|
+
*/
|
|
70
|
+
export declare const HERMES_PROVIDER_SEEDS: HermesProviderRegistry;
|
|
71
|
+
export type Subcommand = "agy" | "claude" | "api" | "hermes";
|
|
51
72
|
export type SubcommandResult = {
|
|
52
73
|
subcommand: Subcommand | undefined;
|
|
53
74
|
rest: string[];
|
|
@@ -95,6 +116,66 @@ export type RawProvider = {
|
|
|
95
116
|
name: string;
|
|
96
117
|
settingsConfig: unknown;
|
|
97
118
|
};
|
|
119
|
+
/** registry 单条:cc-switch name → hermes providers 条目字段。 */
|
|
120
|
+
export type HermesProviderRegistryEntry = {
|
|
121
|
+
id: string;
|
|
122
|
+
keyEnv: string;
|
|
123
|
+
modelOverride?: string;
|
|
124
|
+
};
|
|
125
|
+
/** `~/.config/gcli/hermes-providers.json` 的形状(损坏/缺失视为空)。 */
|
|
126
|
+
export type HermesProviderRegistry = Record<string, HermesProviderRegistryEntry>;
|
|
127
|
+
/** 一跳切换的一端(from/to 同构)。 */
|
|
128
|
+
export type HermesModelPoint = {
|
|
129
|
+
id: string;
|
|
130
|
+
model: string;
|
|
131
|
+
base_url: string;
|
|
132
|
+
};
|
|
133
|
+
/** 一条被重 pin 的 cron job 的旧 pin(rollback 回放用)。 */
|
|
134
|
+
export type CronRepinTarget = {
|
|
135
|
+
jobId: string;
|
|
136
|
+
prevProvider: string;
|
|
137
|
+
prevModel: string | null;
|
|
138
|
+
};
|
|
139
|
+
/** `~/.config/gcli/hermes-state.json`(0o600,env.prevValue 含旧 token)。 */
|
|
140
|
+
export type HermesStateFile = {
|
|
141
|
+
lastSwitch: {
|
|
142
|
+
ts: number;
|
|
143
|
+
ccName: string;
|
|
144
|
+
to: HermesModelPoint;
|
|
145
|
+
from: HermesModelPoint;
|
|
146
|
+
configBackup: string;
|
|
147
|
+
env: {
|
|
148
|
+
key: string;
|
|
149
|
+
prevValue: string | null;
|
|
150
|
+
};
|
|
151
|
+
cronRepinned: CronRepinTarget[];
|
|
152
|
+
};
|
|
153
|
+
};
|
|
154
|
+
/** editHermesConfig 的编辑指令。 */
|
|
155
|
+
export type HermesConfigEdit = {
|
|
156
|
+
model: {
|
|
157
|
+
default: string;
|
|
158
|
+
provider: string;
|
|
159
|
+
base_url: string;
|
|
160
|
+
};
|
|
161
|
+
provider: {
|
|
162
|
+
id: string;
|
|
163
|
+
name: string;
|
|
164
|
+
base_url: string;
|
|
165
|
+
transport: string;
|
|
166
|
+
key_env: string;
|
|
167
|
+
default_model: string;
|
|
168
|
+
};
|
|
169
|
+
};
|
|
170
|
+
/** parseHermesConfig 的读取结果(status / 冲突检测共用)。 */
|
|
171
|
+
export type HermesConfigInfo = {
|
|
172
|
+
model: {
|
|
173
|
+
default?: string;
|
|
174
|
+
provider?: string;
|
|
175
|
+
base_url?: string;
|
|
176
|
+
};
|
|
177
|
+
providerIds: string[];
|
|
178
|
+
};
|
|
98
179
|
export type ProviderLookup = {
|
|
99
180
|
ok: true;
|
|
100
181
|
providers: RawProvider[];
|
|
@@ -166,6 +247,29 @@ export type RunDeps = {
|
|
|
166
247
|
* after an arrow-key picker confirm, before any backend spawn.
|
|
167
248
|
*/
|
|
168
249
|
writeLastProvider: (name: string) => Promise<void>;
|
|
250
|
+
/** Spawn `hermes <args>`(cron edit / gateway / -z ping),照 runClaude 的 timeout/SIGTERM 契约。 */
|
|
251
|
+
runHermes: (args: string[], timeoutMs?: number) => Promise<SpawnResult>;
|
|
252
|
+
/** 读 UTF-8 文本文件;缺失/不可读 → undefined(不 throw)。 */
|
|
253
|
+
readTextFile: (path: string) => Promise<string | undefined>;
|
|
254
|
+
/** 原子写(tmp + rename);mode 缺省时沿用目标文件现有权限(不存在则 0o600)。 */
|
|
255
|
+
writeTextFileAtomic: (path: string, text: string, mode?: number) => Promise<{
|
|
256
|
+
ok: true;
|
|
257
|
+
} | {
|
|
258
|
+
ok: false;
|
|
259
|
+
error: string;
|
|
260
|
+
}>;
|
|
261
|
+
/** 整文件复制(备份/整文件恢复)。 */
|
|
262
|
+
copyFile: (src: string, dest: string) => Promise<{
|
|
263
|
+
ok: true;
|
|
264
|
+
} | {
|
|
265
|
+
ok: false;
|
|
266
|
+
error: string;
|
|
267
|
+
}>;
|
|
268
|
+
/** 查 state.db session_model_usage 最新行(best-effort 验证);任何失败 → undefined。 */
|
|
269
|
+
queryLastSessionModel: () => Promise<{
|
|
270
|
+
model: string;
|
|
271
|
+
provider: string;
|
|
272
|
+
} | undefined>;
|
|
169
273
|
};
|
|
170
274
|
export type RunOutcome = {
|
|
171
275
|
exitCode: number;
|
|
@@ -331,6 +435,17 @@ export interface ApiRequest {
|
|
|
331
435
|
prompt: string;
|
|
332
436
|
stream: boolean;
|
|
333
437
|
timeoutMs: number;
|
|
438
|
+
/**
|
|
439
|
+
* Explicit thinking control: "off" sends {type:"disabled"}, "on" sends
|
|
440
|
+
* {type:"enabled", budget_tokens: floor(maxTokens/2)}. Undefined (= CLI
|
|
441
|
+
* `--thinking auto`) omits the field entirely and keeps the endpoint's
|
|
442
|
+
* default — which on bigmodel GLM endpoints means thinking ON, where
|
|
443
|
+
* thinking shares the max_tokens budget with text (small budgets can end
|
|
444
|
+
* up thinking-only, no text block).
|
|
445
|
+
*/
|
|
446
|
+
thinking?: "off" | "on";
|
|
447
|
+
/** Transient-failure retries (network error / 408/429/5xx / malformed JSON / empty body). 0 = single attempt. */
|
|
448
|
+
retries?: number;
|
|
334
449
|
}
|
|
335
450
|
/**
|
|
336
451
|
* Build the HTTP body for an anthropic-compatible /v1/messages request.
|
|
@@ -340,9 +455,12 @@ export interface ApiRequest {
|
|
|
340
455
|
* high-quality output in ~45s vs claude-agent's 53min). We rely on a sufficient
|
|
341
456
|
* --max-tokens budget (default 80000) to cover both thinking and text, not on
|
|
342
457
|
* disabling thinking. Disabling it would discard the very capability we chose
|
|
343
|
-
* k3 for.
|
|
458
|
+
* k3 for. (`--thinking auto`, the CLI default, preserves this omitted-field
|
|
459
|
+
* behaviour. bigmodel GLM endpoints default thinking ON and share max_tokens
|
|
460
|
+
* between thinking and text — small explicit budgets can end up thinking-only;
|
|
461
|
+
* --thinking off/on send explicit disabled/enabled for that case.)
|
|
344
462
|
*/
|
|
345
|
-
export declare function buildApiBody(req: Pick<ApiRequest, "model" | "maxTokens" | "prompt" | "stream">): Record<string, unknown>;
|
|
463
|
+
export declare function buildApiBody(req: Pick<ApiRequest, "model" | "maxTokens" | "prompt" | "stream" | "thinking">): Record<string, unknown>;
|
|
346
464
|
/**
|
|
347
465
|
* Build the full URL for the messages endpoint. The cc-switch base_url is
|
|
348
466
|
* stored with a trailing slash (e.g. `https://api.kimi.com/coding/`); we
|
|
@@ -368,6 +486,131 @@ export declare function extractTextDelta(line: string): string | null;
|
|
|
368
486
|
* we concatenate every text block in order.
|
|
369
487
|
*/
|
|
370
488
|
export declare function extractNonStreamText(body: unknown): string;
|
|
489
|
+
/** What a single SSE `data:` line contributes to the stream aggregation. */
|
|
490
|
+
export interface SseLineMeta {
|
|
491
|
+
/** Text fragment for content_block_delta/text_delta events, else null. */
|
|
492
|
+
text: string | null;
|
|
493
|
+
/** Characters contributed by a thinking_delta (thinking models spend the
|
|
494
|
+
* shared max_tokens budget here before any text is emitted). */
|
|
495
|
+
thinkingChars: number;
|
|
496
|
+
/** stop_reason carried by a message_delta event (e.g. "max_tokens"). */
|
|
497
|
+
stopReason?: string;
|
|
498
|
+
/** True when the line was a `data:` payload that JSON-parsed (any event type). */
|
|
499
|
+
parsed: boolean;
|
|
500
|
+
}
|
|
501
|
+
/**
|
|
502
|
+
* SSE line → structured meta (text delta + thinking volume + stop_reason).
|
|
503
|
+
*
|
|
504
|
+
* One JSON.parse per line feeding both the text aggregation and the "why did a
|
|
505
|
+
* stream produce no text" diagnosis (thinking-only streams that end in
|
|
506
|
+
* stop_reason=max_tokens are an endpoint budget issue, not a transport one).
|
|
507
|
+
* Non-`data:` lines / malformed JSON → empty meta.
|
|
508
|
+
*/
|
|
509
|
+
export declare function extractSseLineMeta(line: string): SseLineMeta;
|
|
510
|
+
/** Non-streaming body → why extractNonStreamText found no text. */
|
|
511
|
+
export interface NoTextBodyInfo {
|
|
512
|
+
/** content block type names in order, comma-joined ("" = no content at all). */
|
|
513
|
+
blocks: string;
|
|
514
|
+
sawThinking: boolean;
|
|
515
|
+
stopReason?: string;
|
|
516
|
+
}
|
|
517
|
+
/**
|
|
518
|
+
* Inspect a 200 non-streaming messages body that yielded no text, so the error
|
|
519
|
+
* can say WHY (thinking-only under a starved max_tokens budget vs an empty
|
|
520
|
+
* content array from a flaky gateway — the former is deterministic, the latter
|
|
521
|
+
* is worth retrying).
|
|
522
|
+
*/
|
|
523
|
+
export declare function describeNoTextBody(body: unknown): NoTextBodyInfo;
|
|
524
|
+
/** HTTP statuses worth an automatic retry (transient server/gateway trouble). */
|
|
525
|
+
export declare const API_RETRYABLE_STATUS: Set<number>;
|
|
526
|
+
/** Parsed options for the hermes subcommand (strict: unknown flags error). */
|
|
527
|
+
export interface ParsedHermesArgs {
|
|
528
|
+
/** Positional: provider name, or a reserved word (status/rollback). */
|
|
529
|
+
provider?: string;
|
|
530
|
+
model?: string;
|
|
531
|
+
dryRun: boolean;
|
|
532
|
+
verify: boolean;
|
|
533
|
+
keepOnFail: boolean;
|
|
534
|
+
help: boolean;
|
|
535
|
+
}
|
|
536
|
+
export type ParseHermesResult = ParsedHermesArgs | {
|
|
537
|
+
error: string;
|
|
538
|
+
};
|
|
539
|
+
/**
|
|
540
|
+
* Strip a trailing context-window marker like `[1M]` (a claude-agent
|
|
541
|
+
* convention cc-switch stores in model names; raw APIs reject it).
|
|
542
|
+
* Only a suffix anchored at the very end is removed.
|
|
543
|
+
*/
|
|
544
|
+
export declare function stripContextSuffix(model: string): string;
|
|
545
|
+
/**
|
|
546
|
+
* Parse argv for `gcli hermes ...`. Strict like parseApiArgs (unknown flags
|
|
547
|
+
* are exit-2 errors — there is no backend to forward to). `--no-verify` is
|
|
548
|
+
* accepted via allowNegative. At most one positional.
|
|
549
|
+
*/
|
|
550
|
+
export declare function parseHermesArgs(argv: string[]): ParseHermesResult;
|
|
551
|
+
/**
|
|
552
|
+
* Derive a hermes provider id from a cc-switch display name: lowercase,
|
|
553
|
+
* non-[a-z0-9] runs folded to `-`, leading/trailing dashes trimmed.
|
|
554
|
+
* May return "" for names with no alphanumerics (caller treats as error).
|
|
555
|
+
*/
|
|
556
|
+
export declare function deriveHermesId(ccName: string): string;
|
|
557
|
+
/** Derive the .env key name from a hermes provider id: UPPER + `_API_KEY`. */
|
|
558
|
+
export declare function deriveKeyEnv(id: string): string;
|
|
559
|
+
/** Quote a scalar for our line-level YAML writer ('' escapes a single quote). */
|
|
560
|
+
export declare function quoteYamlScalar(value: string): string;
|
|
561
|
+
/**
|
|
562
|
+
* Targeted line-level YAML editor for hermes' config.yaml (zero-dep: no yaml
|
|
563
|
+
* lib guaranteed). Only the top-level `model:` (3 keys) and `providers:`
|
|
564
|
+
* (one entry) sections are touched; everything else is byte-preserved.
|
|
565
|
+
* Missing sections / unexpected nesting → {error}, never a guess (callers
|
|
566
|
+
* must not write on error). Idempotent by construction.
|
|
567
|
+
*/
|
|
568
|
+
export declare function editHermesConfig(text: string, edit: HermesConfigEdit): {
|
|
569
|
+
text: string;
|
|
570
|
+
} | {
|
|
571
|
+
error: string;
|
|
572
|
+
};
|
|
573
|
+
/**
|
|
574
|
+
* Read (not edit) the parts of config.yaml the hermes backend needs: the
|
|
575
|
+
* current model section values and the list of providers entry ids.
|
|
576
|
+
* Missing model: section or unparseable model lines → {error}.
|
|
577
|
+
*/
|
|
578
|
+
export declare function parseHermesConfig(text: string): {
|
|
579
|
+
info: HermesConfigInfo;
|
|
580
|
+
} | {
|
|
581
|
+
error: string;
|
|
582
|
+
};
|
|
583
|
+
/**
|
|
584
|
+
* Upsert one `KEY=value` line in a .env text (pure). Only active lines
|
|
585
|
+
* (`^KEY=`) match — commented-out occurrences are left alone; comments and
|
|
586
|
+
* blank lines are byte-preserved. value=null deletes the line(s); a missing
|
|
587
|
+
* key with a non-null value is appended at the end.
|
|
588
|
+
*/
|
|
589
|
+
export declare function upsertEnvLines(text: string, key: string, value: string | null): string;
|
|
590
|
+
/**
|
|
591
|
+
* Plan cron re-pins: jobs that are enabled AND pinned to `oldProvider`
|
|
592
|
+
* (jobs.json shape `{jobs: [...]}`, a bare array tolerated). Malformed input
|
|
593
|
+
* yields an empty plan, never a throw.
|
|
594
|
+
*/
|
|
595
|
+
export declare function buildCronRepinPlan(jobsJson: unknown, oldProvider: string): CronRepinTarget[];
|
|
596
|
+
/**
|
|
597
|
+
* Parse the provider registry (`hermes-providers.json`). Corrupt/missing
|
|
598
|
+
* content is best-effort → empty registry; entries lacking id/keyEnv are
|
|
599
|
+
* dropped.
|
|
600
|
+
*/
|
|
601
|
+
export declare function parseHermesRegistry(text: string): HermesProviderRegistry;
|
|
602
|
+
export declare function serializeHermesRegistry(reg: HermesProviderRegistry): string;
|
|
603
|
+
/**
|
|
604
|
+
* Parse `hermes-state.json` STRICTLY (畸形 → {error},不猜): it carries the
|
|
605
|
+
* previous token and the rollback plan, so a half-broken state must not be
|
|
606
|
+
* acted on.
|
|
607
|
+
*/
|
|
608
|
+
export declare function parseHermesStateFile(text: string): {
|
|
609
|
+
state: HermesStateFile;
|
|
610
|
+
} | {
|
|
611
|
+
error: string;
|
|
612
|
+
};
|
|
613
|
+
export declare function serializeHermesStateFile(s: HermesStateFile): string;
|
|
371
614
|
export interface ParsedArgs {
|
|
372
615
|
prompt?: string;
|
|
373
616
|
model?: string;
|
|
@@ -399,6 +642,10 @@ export interface ParsedApiArgs {
|
|
|
399
642
|
maxTokens: number;
|
|
400
643
|
timeoutMs: number;
|
|
401
644
|
stream: boolean;
|
|
645
|
+
/** `auto` (default) omits the thinking field; `off`/`on` send it explicitly. */
|
|
646
|
+
thinking: "auto" | "off" | "on";
|
|
647
|
+
/** Transient-failure retries (default 1; 0 disables). */
|
|
648
|
+
retries: number;
|
|
402
649
|
version: boolean;
|
|
403
650
|
help: boolean;
|
|
404
651
|
/** --cwd was supplied (warned + ignored by the api backend). */
|
|
@@ -431,12 +678,21 @@ export declare function runClaudeInteractive(args: string[], cwd?: string): Prom
|
|
|
431
678
|
*
|
|
432
679
|
* - stream=true: reads the SSE body chunk-by-chunk, decodes UTF-8, splits on
|
|
433
680
|
* newlines, and aggregates `text_delta` payloads into stdout. Two clocks
|
|
434
|
-
* guard against hangs: an idle timer (reset on
|
|
435
|
-
*
|
|
436
|
-
* AbortController → exit 1
|
|
681
|
+
* guard against hangs: an idle timer (reset on EVERY received chunk —
|
|
682
|
+
* thinking models can emit long non-text stretches) and an absolute timer
|
|
683
|
+
* (timeoutMs). Either firing aborts the fetch via AbortController → exit 1
|
|
684
|
+
* with a timeout message.
|
|
437
685
|
* - stream=false: awaits the full JSON body and extracts `content[].text`,
|
|
438
686
|
* then applies the 50k-char truncation.
|
|
439
687
|
*
|
|
688
|
+
* TRANSIENT failures are retried automatically (default 1 retry, `--retry N`):
|
|
689
|
+
* network-level fetch errors, HTTP 408/429/5xx, malformed JSON, empty-content
|
|
690
|
+
* 200 bodies, and streams that die or deliver no SSE events at all. Each retry
|
|
691
|
+
* note lands on stderr (stdout stays pipe-clean), so a recovered attempt still
|
|
692
|
+
* leaves a trace. Deterministic failures are NOT retried — timeouts, 4xx, and
|
|
693
|
+
* thinking-only responses (retrying cannot fix a max_tokens budget starved by
|
|
694
|
+
* endpoint-default thinking); those errors carry a diagnosis instead.
|
|
695
|
+
*
|
|
440
696
|
* HTTP errors (non-2xx, network failure, abort) → exit 1 with diagnostics on
|
|
441
697
|
* stderr; stdout stays empty so the caller's empty-output guard still works.
|
|
442
698
|
*/
|