honeydo 0.1.1 → 0.1.3
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/package.json +1 -1
- package/packages/gcli/dist/cli.d.ts +266 -11
- package/packages/gcli/dist/cli.js +1527 -103
- 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/package.json
CHANGED
|
@@ -6,8 +6,9 @@
|
|
|
6
6
|
* `gcli agy ...` routes to the agy CLI (explicit subcommand required); the
|
|
7
7
|
* claude backend can switch cc-switch providers inline via
|
|
8
8
|
* `claude -p ... --settings`. Adds value over calling the backends directly:
|
|
9
|
-
* prompt via argv or stdin (`-p -`),
|
|
10
|
-
*
|
|
9
|
+
* prompt via argv or stdin (`-p -`), a hard timeout (spawn SIGTERM), explicit
|
|
10
|
+
* exit codes, and empty-output detection. Output is passed through unmodified
|
|
11
|
+
* — size limits are the endpoint's business, not ours.
|
|
11
12
|
*
|
|
12
13
|
* The claude backend resolves `--provider <name>` from the cc-switch SQLite
|
|
13
14
|
* DB (read-only) and never rewrites ~/.claude/settings.json — the provider
|
|
@@ -25,7 +26,6 @@
|
|
|
25
26
|
* backend's own timeout flag.
|
|
26
27
|
*/
|
|
27
28
|
export declare const DEFAULT_TIMEOUT_MS = 300000;
|
|
28
|
-
export declare const CHARACTER_LIMIT = 50000;
|
|
29
29
|
export declare const API_DEFAULT_MAX_TOKENS = 80000;
|
|
30
30
|
/**
|
|
31
31
|
* Path to the cc-switch SQLite database. cc-switch ships provider configs
|
|
@@ -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;
|
|
@@ -185,7 +289,6 @@ export type RunOutcome = {
|
|
|
185
289
|
* prompt="claude" — the subcommand must literally lead.
|
|
186
290
|
*/
|
|
187
291
|
export declare function parseSubcommand(argv: string[]): SubcommandResult;
|
|
188
|
-
export declare function truncate(text: string): string;
|
|
189
292
|
export interface GcliOptions {
|
|
190
293
|
/** Omit for interactive mode (no -p emitted). */
|
|
191
294
|
prompt?: string;
|
|
@@ -331,6 +434,17 @@ export interface ApiRequest {
|
|
|
331
434
|
prompt: string;
|
|
332
435
|
stream: boolean;
|
|
333
436
|
timeoutMs: number;
|
|
437
|
+
/**
|
|
438
|
+
* Explicit thinking control: "off" sends {type:"disabled"}, "on" sends
|
|
439
|
+
* {type:"enabled", budget_tokens: floor(maxTokens/2)}. Undefined (= CLI
|
|
440
|
+
* `--thinking auto`) omits the field entirely and keeps the endpoint's
|
|
441
|
+
* default — which on bigmodel GLM endpoints means thinking ON, where
|
|
442
|
+
* thinking shares the max_tokens budget with text (small budgets can end
|
|
443
|
+
* up thinking-only, no text block).
|
|
444
|
+
*/
|
|
445
|
+
thinking?: "off" | "on";
|
|
446
|
+
/** Transient-failure retries (network error / 408/429/5xx / malformed JSON / empty body). 0 = single attempt. */
|
|
447
|
+
retries?: number;
|
|
334
448
|
}
|
|
335
449
|
/**
|
|
336
450
|
* Build the HTTP body for an anthropic-compatible /v1/messages request.
|
|
@@ -340,9 +454,12 @@ export interface ApiRequest {
|
|
|
340
454
|
* high-quality output in ~45s vs claude-agent's 53min). We rely on a sufficient
|
|
341
455
|
* --max-tokens budget (default 80000) to cover both thinking and text, not on
|
|
342
456
|
* disabling thinking. Disabling it would discard the very capability we chose
|
|
343
|
-
* k3 for.
|
|
457
|
+
* k3 for. (`--thinking auto`, the CLI default, preserves this omitted-field
|
|
458
|
+
* behaviour. bigmodel GLM endpoints default thinking ON and share max_tokens
|
|
459
|
+
* between thinking and text — small explicit budgets can end up thinking-only;
|
|
460
|
+
* --thinking off/on send explicit disabled/enabled for that case.)
|
|
344
461
|
*/
|
|
345
|
-
export declare function buildApiBody(req: Pick<ApiRequest, "model" | "maxTokens" | "prompt" | "stream">): Record<string, unknown>;
|
|
462
|
+
export declare function buildApiBody(req: Pick<ApiRequest, "model" | "maxTokens" | "prompt" | "stream" | "thinking">): Record<string, unknown>;
|
|
346
463
|
/**
|
|
347
464
|
* Build the full URL for the messages endpoint. The cc-switch base_url is
|
|
348
465
|
* stored with a trailing slash (e.g. `https://api.kimi.com/coding/`); we
|
|
@@ -368,6 +485,131 @@ export declare function extractTextDelta(line: string): string | null;
|
|
|
368
485
|
* we concatenate every text block in order.
|
|
369
486
|
*/
|
|
370
487
|
export declare function extractNonStreamText(body: unknown): string;
|
|
488
|
+
/** What a single SSE `data:` line contributes to the stream aggregation. */
|
|
489
|
+
export interface SseLineMeta {
|
|
490
|
+
/** Text fragment for content_block_delta/text_delta events, else null. */
|
|
491
|
+
text: string | null;
|
|
492
|
+
/** Characters contributed by a thinking_delta (thinking models spend the
|
|
493
|
+
* shared max_tokens budget here before any text is emitted). */
|
|
494
|
+
thinkingChars: number;
|
|
495
|
+
/** stop_reason carried by a message_delta event (e.g. "max_tokens"). */
|
|
496
|
+
stopReason?: string;
|
|
497
|
+
/** True when the line was a `data:` payload that JSON-parsed (any event type). */
|
|
498
|
+
parsed: boolean;
|
|
499
|
+
}
|
|
500
|
+
/**
|
|
501
|
+
* SSE line → structured meta (text delta + thinking volume + stop_reason).
|
|
502
|
+
*
|
|
503
|
+
* One JSON.parse per line feeding both the text aggregation and the "why did a
|
|
504
|
+
* stream produce no text" diagnosis (thinking-only streams that end in
|
|
505
|
+
* stop_reason=max_tokens are an endpoint budget issue, not a transport one).
|
|
506
|
+
* Non-`data:` lines / malformed JSON → empty meta.
|
|
507
|
+
*/
|
|
508
|
+
export declare function extractSseLineMeta(line: string): SseLineMeta;
|
|
509
|
+
/** Non-streaming body → why extractNonStreamText found no text. */
|
|
510
|
+
export interface NoTextBodyInfo {
|
|
511
|
+
/** content block type names in order, comma-joined ("" = no content at all). */
|
|
512
|
+
blocks: string;
|
|
513
|
+
sawThinking: boolean;
|
|
514
|
+
stopReason?: string;
|
|
515
|
+
}
|
|
516
|
+
/**
|
|
517
|
+
* Inspect a 200 non-streaming messages body that yielded no text, so the error
|
|
518
|
+
* can say WHY (thinking-only under a starved max_tokens budget vs an empty
|
|
519
|
+
* content array from a flaky gateway — the former is deterministic, the latter
|
|
520
|
+
* is worth retrying).
|
|
521
|
+
*/
|
|
522
|
+
export declare function describeNoTextBody(body: unknown): NoTextBodyInfo;
|
|
523
|
+
/** HTTP statuses worth an automatic retry (transient server/gateway trouble). */
|
|
524
|
+
export declare const API_RETRYABLE_STATUS: Set<number>;
|
|
525
|
+
/** Parsed options for the hermes subcommand (strict: unknown flags error). */
|
|
526
|
+
export interface ParsedHermesArgs {
|
|
527
|
+
/** Positional: provider name, or a reserved word (status/rollback). */
|
|
528
|
+
provider?: string;
|
|
529
|
+
model?: string;
|
|
530
|
+
dryRun: boolean;
|
|
531
|
+
verify: boolean;
|
|
532
|
+
keepOnFail: boolean;
|
|
533
|
+
help: boolean;
|
|
534
|
+
}
|
|
535
|
+
export type ParseHermesResult = ParsedHermesArgs | {
|
|
536
|
+
error: string;
|
|
537
|
+
};
|
|
538
|
+
/**
|
|
539
|
+
* Strip a trailing context-window marker like `[1M]` (a claude-agent
|
|
540
|
+
* convention cc-switch stores in model names; raw APIs reject it).
|
|
541
|
+
* Only a suffix anchored at the very end is removed.
|
|
542
|
+
*/
|
|
543
|
+
export declare function stripContextSuffix(model: string): string;
|
|
544
|
+
/**
|
|
545
|
+
* Parse argv for `gcli hermes ...`. Strict like parseApiArgs (unknown flags
|
|
546
|
+
* are exit-2 errors — there is no backend to forward to). `--no-verify` is
|
|
547
|
+
* accepted via allowNegative. At most one positional.
|
|
548
|
+
*/
|
|
549
|
+
export declare function parseHermesArgs(argv: string[]): ParseHermesResult;
|
|
550
|
+
/**
|
|
551
|
+
* Derive a hermes provider id from a cc-switch display name: lowercase,
|
|
552
|
+
* non-[a-z0-9] runs folded to `-`, leading/trailing dashes trimmed.
|
|
553
|
+
* May return "" for names with no alphanumerics (caller treats as error).
|
|
554
|
+
*/
|
|
555
|
+
export declare function deriveHermesId(ccName: string): string;
|
|
556
|
+
/** Derive the .env key name from a hermes provider id: UPPER + `_API_KEY`. */
|
|
557
|
+
export declare function deriveKeyEnv(id: string): string;
|
|
558
|
+
/** Quote a scalar for our line-level YAML writer ('' escapes a single quote). */
|
|
559
|
+
export declare function quoteYamlScalar(value: string): string;
|
|
560
|
+
/**
|
|
561
|
+
* Targeted line-level YAML editor for hermes' config.yaml (zero-dep: no yaml
|
|
562
|
+
* lib guaranteed). Only the top-level `model:` (3 keys) and `providers:`
|
|
563
|
+
* (one entry) sections are touched; everything else is byte-preserved.
|
|
564
|
+
* Missing sections / unexpected nesting → {error}, never a guess (callers
|
|
565
|
+
* must not write on error). Idempotent by construction.
|
|
566
|
+
*/
|
|
567
|
+
export declare function editHermesConfig(text: string, edit: HermesConfigEdit): {
|
|
568
|
+
text: string;
|
|
569
|
+
} | {
|
|
570
|
+
error: string;
|
|
571
|
+
};
|
|
572
|
+
/**
|
|
573
|
+
* Read (not edit) the parts of config.yaml the hermes backend needs: the
|
|
574
|
+
* current model section values and the list of providers entry ids.
|
|
575
|
+
* Missing model: section or unparseable model lines → {error}.
|
|
576
|
+
*/
|
|
577
|
+
export declare function parseHermesConfig(text: string): {
|
|
578
|
+
info: HermesConfigInfo;
|
|
579
|
+
} | {
|
|
580
|
+
error: string;
|
|
581
|
+
};
|
|
582
|
+
/**
|
|
583
|
+
* Upsert one `KEY=value` line in a .env text (pure). Only active lines
|
|
584
|
+
* (`^KEY=`) match — commented-out occurrences are left alone; comments and
|
|
585
|
+
* blank lines are byte-preserved. value=null deletes the line(s); a missing
|
|
586
|
+
* key with a non-null value is appended at the end.
|
|
587
|
+
*/
|
|
588
|
+
export declare function upsertEnvLines(text: string, key: string, value: string | null): string;
|
|
589
|
+
/**
|
|
590
|
+
* Plan cron re-pins: jobs that are enabled AND pinned to `oldProvider`
|
|
591
|
+
* (jobs.json shape `{jobs: [...]}`, a bare array tolerated). Malformed input
|
|
592
|
+
* yields an empty plan, never a throw.
|
|
593
|
+
*/
|
|
594
|
+
export declare function buildCronRepinPlan(jobsJson: unknown, oldProvider: string): CronRepinTarget[];
|
|
595
|
+
/**
|
|
596
|
+
* Parse the provider registry (`hermes-providers.json`). Corrupt/missing
|
|
597
|
+
* content is best-effort → empty registry; entries lacking id/keyEnv are
|
|
598
|
+
* dropped.
|
|
599
|
+
*/
|
|
600
|
+
export declare function parseHermesRegistry(text: string): HermesProviderRegistry;
|
|
601
|
+
export declare function serializeHermesRegistry(reg: HermesProviderRegistry): string;
|
|
602
|
+
/**
|
|
603
|
+
* Parse `hermes-state.json` STRICTLY (畸形 → {error},不猜): it carries the
|
|
604
|
+
* previous token and the rollback plan, so a half-broken state must not be
|
|
605
|
+
* acted on.
|
|
606
|
+
*/
|
|
607
|
+
export declare function parseHermesStateFile(text: string): {
|
|
608
|
+
state: HermesStateFile;
|
|
609
|
+
} | {
|
|
610
|
+
error: string;
|
|
611
|
+
};
|
|
612
|
+
export declare function serializeHermesStateFile(s: HermesStateFile): string;
|
|
371
613
|
export interface ParsedArgs {
|
|
372
614
|
prompt?: string;
|
|
373
615
|
model?: string;
|
|
@@ -399,6 +641,10 @@ export interface ParsedApiArgs {
|
|
|
399
641
|
maxTokens: number;
|
|
400
642
|
timeoutMs: number;
|
|
401
643
|
stream: boolean;
|
|
644
|
+
/** `auto` (default) omits the thinking field; `off`/`on` send it explicitly. */
|
|
645
|
+
thinking: "auto" | "off" | "on";
|
|
646
|
+
/** Transient-failure retries (default 1; 0 disables). */
|
|
647
|
+
retries: number;
|
|
402
648
|
version: boolean;
|
|
403
649
|
help: boolean;
|
|
404
650
|
/** --cwd was supplied (warned + ignored by the api backend). */
|
|
@@ -431,11 +677,20 @@ export declare function runClaudeInteractive(args: string[], cwd?: string): Prom
|
|
|
431
677
|
*
|
|
432
678
|
* - stream=true: reads the SSE body chunk-by-chunk, decodes UTF-8, splits on
|
|
433
679
|
* newlines, and aggregates `text_delta` payloads into stdout. Two clocks
|
|
434
|
-
* guard against hangs: an idle timer (reset on
|
|
435
|
-
*
|
|
436
|
-
* AbortController → exit 1
|
|
680
|
+
* guard against hangs: an idle timer (reset on EVERY received chunk —
|
|
681
|
+
* thinking models can emit long non-text stretches) and an absolute timer
|
|
682
|
+
* (timeoutMs). Either firing aborts the fetch via AbortController → exit 1
|
|
683
|
+
* with a timeout message.
|
|
437
684
|
* - stream=false: awaits the full JSON body and extracts `content[].text`,
|
|
438
|
-
*
|
|
685
|
+
* returned as-is (no size cap — endpoint limits are the endpoint's call).
|
|
686
|
+
*
|
|
687
|
+
* TRANSIENT failures are retried automatically (default 1 retry, `--retry N`):
|
|
688
|
+
* network-level fetch errors, HTTP 408/429/5xx, malformed JSON, empty-content
|
|
689
|
+
* 200 bodies, and streams that die or deliver no SSE events at all. Each retry
|
|
690
|
+
* note lands on stderr (stdout stays pipe-clean), so a recovered attempt still
|
|
691
|
+
* leaves a trace. Deterministic failures are NOT retried — timeouts, 4xx, and
|
|
692
|
+
* thinking-only responses (retrying cannot fix a max_tokens budget starved by
|
|
693
|
+
* endpoint-default thinking); those errors carry a diagnosis instead.
|
|
439
694
|
*
|
|
440
695
|
* HTTP errors (non-2xx, network failure, abort) → exit 1 with diagnostics on
|
|
441
696
|
* stderr; stdout stays empty so the caller's empty-output guard still works.
|