chatccc 0.2.270 → 0.2.276
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 +16 -10
- package/config.sample.json +4 -3
- package/deepccc-agent/README.md +147 -61
- package/deepccc-agent/package.json +5 -2
- package/dist/deepccc-agent/src/attachments.js +192 -0
- package/dist/deepccc-agent/src/cli.js +59 -13
- package/dist/deepccc-agent/src/config.js +57 -4
- package/dist/deepccc-agent/src/context.js +299 -16
- package/dist/deepccc-agent/src/file-tools.js +33 -0
- package/dist/deepccc-agent/src/index.js +68 -21
- package/dist/deepccc-agent/src/tool-protocol.js +14 -3
- package/dist/deepccc-agent/src/web-entry.js +72 -0
- package/dist/deepccc-agent/src/web-page.js +414 -0
- package/dist/deepccc-agent/src/web-runtime.js +331 -0
- package/dist/deepccc-agent/src/web-server.js +476 -0
- package/dist/deepccc-agent/src/web-session-store.js +162 -0
- package/dist/deepccc-agent/src/web-tool-presentation.js +123 -0
- package/dist/src/adapters/ccc-adapter.js +5 -1
- package/dist/src/agent-capability-grants.js +26 -0
- package/dist/src/agent-delegate-task.js +5 -2
- package/dist/src/agent-file-rpc.js +6 -1
- package/dist/src/agent-image-rpc.js +6 -1
- package/dist/src/agent-team/application/task-execution-service.js +330 -97
- package/dist/src/agent-team/domain/task-run.js +14 -1
- package/dist/src/agent-team/infrastructure/task-execution-runtime.js +7 -2
- package/dist/src/agent-team/main-agent-bootstrap.js +24 -1
- package/dist/src/agent-team/repositories/json-task-run-repository.js +22 -4
- package/dist/src/agent-team/web/agent-team-page.js +14 -7
- package/dist/src/cards.js +7 -4
- package/dist/src/config.js +12 -0
- package/dist/src/im-skills.js +9 -2
- package/dist/src/orchestrator.js +117 -29
- package/dist/src/safe-maintenance.js +4 -1
- package/dist/src/session-name.js +15 -0
- package/dist/src/session.js +54 -9
- package/dist/src/web-ui.js +76 -32
- package/im-skills/feishu-skill/receive-send-file.md +3 -2
- package/im-skills/feishu-skill/receive-send-image.md +3 -2
- package/im-skills/feishu-skill/send-file.mjs +6 -5
- package/im-skills/feishu-skill/send-image.mjs +6 -5
- package/im-skills/feishu-skill/skill.md +4 -2
- package/package.json +1 -1
package/dist/src/session.js
CHANGED
|
@@ -14,7 +14,8 @@ import { createCccAdapter } from "./adapters/ccc-adapter.js";
|
|
|
14
14
|
import { createDshAdapter } from "./adapters/dsh-adapter.js";
|
|
15
15
|
import { killProcessTree } from "./adapters/proc-tree-kill.js";
|
|
16
16
|
import { resourceMonitor, registerProcess, unregisterProcess } from "./adapters/resource-monitor.js";
|
|
17
|
-
import { buildImSkillsPromptCached, exportSkillSubDocs } from "./im-skills.js";
|
|
17
|
+
import { buildImSkillsPromptCached, exportSkillSubDocs, sessionImSkillsCacheDir, } from "./im-skills.js";
|
|
18
|
+
import { clearAgentCapabilityGrants, issueAgentCapabilityGrant, } from "./agent-capability-grants.js";
|
|
18
19
|
import { hasResponseStalled, observeResponseProgress } from "./response-stall.js";
|
|
19
20
|
import { classifyTerminalError, formatTerminalErrorNotice, formatTerminalErrorReason, } from "./terminal-error.js";
|
|
20
21
|
import { MAX_PROCESSED, clearFeishuMessageLedgerMemory, processedMessages, } from "./feishu-message-ingress.js";
|
|
@@ -384,6 +385,7 @@ export function resetState() {
|
|
|
384
385
|
sessionModelOverrides.clear();
|
|
385
386
|
sessionEffortOverrides.clear();
|
|
386
387
|
sessionFastModeOverrides.clear();
|
|
388
|
+
clearAgentCapabilityGrants();
|
|
387
389
|
adapterCache.clear();
|
|
388
390
|
stopUnifiedDisplayLoop();
|
|
389
391
|
console.log(`[${ts()}] [RESET] State cleared (dedup + active sessions + bindings)`);
|
|
@@ -508,11 +510,17 @@ export function setSessionFastModeOverride(sessionId, fastMode) {
|
|
|
508
510
|
sessionFastModeOverrides.set(sessionId, fastMode);
|
|
509
511
|
adapterCache.clear();
|
|
510
512
|
}
|
|
513
|
+
function buildAdapterCacheKey(tool, model, effort, fastMode) {
|
|
514
|
+
const base = `${tool}:${model}:${effort}:${fastMode ? "fast" : "default"}`;
|
|
515
|
+
return tool === "ccc"
|
|
516
|
+
? `${base}:${config.ccc.maxOutputTokens ?? "inherit"}`
|
|
517
|
+
: base;
|
|
518
|
+
}
|
|
511
519
|
export function getAdapterForTool(tool, sessionId) {
|
|
512
520
|
const effectiveModel = getEffectiveModelForTool(tool, sessionId);
|
|
513
521
|
const effectiveEffort = getEffectiveEffortForTool(tool, sessionId);
|
|
514
522
|
const effectiveFastMode = getEffectiveFastModeForTool(tool, sessionId);
|
|
515
|
-
const cacheKey =
|
|
523
|
+
const cacheKey = buildAdapterCacheKey(tool, effectiveModel || "", effectiveEffort || "", effectiveFastMode);
|
|
516
524
|
const cached = adapterCache.get(cacheKey);
|
|
517
525
|
if (cached)
|
|
518
526
|
return cached;
|
|
@@ -535,6 +543,9 @@ export function getAdapterForTool(tool, sessionId) {
|
|
|
535
543
|
compactionTimeoutMs: config.ccc.compactionTimeoutMs,
|
|
536
544
|
contextWindow: config.ccc.contextWindow,
|
|
537
545
|
...(effectiveEffort ? { effort: effectiveEffort } : {}),
|
|
546
|
+
...(config.ccc.maxOutputTokens !== null
|
|
547
|
+
? { maxOutputTokens: config.ccc.maxOutputTokens }
|
|
548
|
+
: {}),
|
|
538
549
|
// 留空("")不传 → ChatSession 跟随 DeepCCC 内核配置(~/.deepccc/config.json 或 DEEPCCC_PROVIDER)
|
|
539
550
|
...(config.ccc.provider ? { provider: config.ccc.provider } : {}),
|
|
540
551
|
...(config.ccc.subModel ? { subModel: config.ccc.subModel } : {}),
|
|
@@ -593,6 +604,22 @@ export async function saveSessionTool(sessionId, tool, chatName) {
|
|
|
593
604
|
tool,
|
|
594
605
|
createdAt: existing?.createdAt ?? Date.now(),
|
|
595
606
|
...(mergedChatName ? { chatName: mergedChatName } : {}),
|
|
607
|
+
...(existing?.displayTitle ? { displayTitle: existing.displayTitle } : {}),
|
|
608
|
+
...(existing?.pinned ? { pinned: true } : {}),
|
|
609
|
+
...(existing?.archivedAt ? { archivedAt: existing.archivedAt } : {}),
|
|
610
|
+
};
|
|
611
|
+
await saveSessionTools(data);
|
|
612
|
+
}
|
|
613
|
+
export async function saveSessionPresentation(sessionId, patch) {
|
|
614
|
+
const data = await loadSessionTools();
|
|
615
|
+
const existing = data[sessionId];
|
|
616
|
+
if (!existing)
|
|
617
|
+
return;
|
|
618
|
+
data[sessionId] = {
|
|
619
|
+
...existing,
|
|
620
|
+
...(patch.displayTitle !== undefined ? { displayTitle: patch.displayTitle } : {}),
|
|
621
|
+
...(patch.pinned !== undefined ? { pinned: patch.pinned } : {}),
|
|
622
|
+
...(patch.archivedAt === null ? { archivedAt: undefined } : patch.archivedAt !== undefined ? { archivedAt: patch.archivedAt } : {}),
|
|
596
623
|
};
|
|
597
624
|
await saveSessionTools(data);
|
|
598
625
|
}
|
|
@@ -665,6 +692,9 @@ export async function recordSessionRegistry(update) {
|
|
|
665
692
|
tool: update.tool,
|
|
666
693
|
chatType: update.chatType ?? existing?.chatType,
|
|
667
694
|
chatName: update.chatName ?? existing?.chatName ?? "",
|
|
695
|
+
displayTitle: update.displayTitle ?? existing?.displayTitle,
|
|
696
|
+
pinned: update.pinned ?? existing?.pinned,
|
|
697
|
+
archivedAt: update.archivedAt === null ? undefined : update.archivedAt ?? existing?.archivedAt,
|
|
668
698
|
namePolicy: update.namePolicy ?? existing?.namePolicy,
|
|
669
699
|
turnCount: update.turnCount ?? existing?.turnCount ?? 0,
|
|
670
700
|
lastContextTokens: update.lastContextTokens ?? existing?.lastContextTokens ?? 0,
|
|
@@ -800,7 +830,7 @@ export function accumulateBlockContent(block, state, toolCallMap) {
|
|
|
800
830
|
}
|
|
801
831
|
}
|
|
802
832
|
export async function switchChatBinding(args) {
|
|
803
|
-
const { chatId, chatType, oldSessionId, newSessionId, tool, chatName, namePolicy, newDescription, initialTurnCount = 0, initialContextTokens = 0, updateChatInfoFn, } = args;
|
|
833
|
+
const { chatId, chatType, oldSessionId, newSessionId, tool, chatName, namePolicy, displayTitle, pinned, archivedAt, newDescription, initialTurnCount = 0, initialContextTokens = 0, updateChatInfoFn, } = args;
|
|
804
834
|
// Step 1: 群聊场景先调用飞书 API(不可逆操作放最前)。
|
|
805
835
|
// 私聊跳过——p2p chatId 调 updateChatInfo 必然失败。
|
|
806
836
|
if (chatType !== "p2p") {
|
|
@@ -838,6 +868,9 @@ export async function switchChatBinding(args) {
|
|
|
838
868
|
chatType,
|
|
839
869
|
chatName,
|
|
840
870
|
namePolicy,
|
|
871
|
+
displayTitle,
|
|
872
|
+
pinned,
|
|
873
|
+
archivedAt,
|
|
841
874
|
turnCount: initialTurnCount,
|
|
842
875
|
lastContextTokens: initialContextTokens,
|
|
843
876
|
startTime: now,
|
|
@@ -983,16 +1016,18 @@ export async function runAgentSession(sessionId, userText, platform, _chatId, ms
|
|
|
983
1016
|
if (tid)
|
|
984
1017
|
logTrace(tid, "SESSION_START", { sessionId, tool, cwd, turn: (sessionInfoMap.get(_chatId)?.turnCount ?? 0) + 1 });
|
|
985
1018
|
console.log(`[${ts()}] Running ${adapter.displayName} session: ${sessionId} (${formatToolConfigForLog(tool, info?.model, sessionId)}, cwd=${cwd})`);
|
|
986
|
-
//
|
|
1019
|
+
// 会话专属目录防止并发覆盖;capability grant 防止跨会话投递。
|
|
987
1020
|
const feishuSkillDir = join(PROJECT_ROOT, "im-skills", "feishu-skill");
|
|
988
1021
|
const wechatImageSkillDir = join(PROJECT_ROOT, "im-skills", "wechat-image-skill");
|
|
989
1022
|
const wechatFileSkillDir = join(PROJECT_ROOT, "im-skills", "wechat-file-skill");
|
|
990
1023
|
const wechatVideoSkillDir = join(PROJECT_ROOT, "im-skills", "wechat-video-skill");
|
|
991
|
-
const imSkillsCacheDir = join(USER_DATA_DIR, "im-skills");
|
|
1024
|
+
const imSkillsCacheDir = sessionImSkillsCacheDir(join(USER_DATA_DIR, "im-skills"), sessionId);
|
|
1025
|
+
const agentCapabilityGrant = issueAgentCapabilityGrant(sessionId);
|
|
992
1026
|
const skillVariables = {
|
|
993
1027
|
cwd,
|
|
994
1028
|
session_id: sessionId,
|
|
995
1029
|
open_id: options.initiatorOpenId,
|
|
1030
|
+
agent_capability_grant: agentCapabilityGrant,
|
|
996
1031
|
im_skills_cache_dir: imSkillsCacheDir,
|
|
997
1032
|
delegate_task_url: `http://127.0.0.1:${CHATCCC_PORT}/api/agent/delegate-task`,
|
|
998
1033
|
set_cwd_url: `http://127.0.0.1:${CHATCCC_PORT}/api/agent/set-cwd`,
|
|
@@ -2132,7 +2167,7 @@ export async function getSessionStatus(chatId) {
|
|
|
2132
2167
|
accumulatedLength,
|
|
2133
2168
|
};
|
|
2134
2169
|
}
|
|
2135
|
-
export async function getAllSessionsStatus() {
|
|
2170
|
+
export async function getAllSessionsStatus(options = {}) {
|
|
2136
2171
|
const registry = await loadSessionRegistry();
|
|
2137
2172
|
const registryEntries = Object.values(registry)
|
|
2138
2173
|
.filter((record) => record.chatId && record.sessionId && record.tool)
|
|
@@ -2150,6 +2185,9 @@ export async function getAllSessionsStatus() {
|
|
|
2150
2185
|
sessionId,
|
|
2151
2186
|
tool: record.tool,
|
|
2152
2187
|
chatName: record.chatName ?? "",
|
|
2188
|
+
displayTitle: record.displayTitle ?? "",
|
|
2189
|
+
pinned: record.pinned ?? false,
|
|
2190
|
+
archivedAt: record.archivedAt,
|
|
2153
2191
|
turnCount: 0,
|
|
2154
2192
|
lastContextTokens: 0,
|
|
2155
2193
|
startTime: active?.startTime ?? createdAt,
|
|
@@ -2158,9 +2196,13 @@ export async function getAllSessionsStatus() {
|
|
|
2158
2196
|
sortTime: active?.startTime ?? createdAt,
|
|
2159
2197
|
};
|
|
2160
2198
|
});
|
|
2199
|
+
const query = options.query?.trim().toLowerCase() ?? "";
|
|
2161
2200
|
const entries = [...registryEntries, ...orphanEntries]
|
|
2162
|
-
.
|
|
2163
|
-
.
|
|
2201
|
+
.filter((entry) => options.archivedOnly ? !!entry.archivedAt : options.includeArchived ? true : !entry.archivedAt)
|
|
2202
|
+
.filter((entry) => !query || [entry.displayTitle, entry.chatName, entry.sessionId, entry.tool]
|
|
2203
|
+
.some((value) => String(value ?? "").toLowerCase().includes(query)))
|
|
2204
|
+
.sort((a, b) => Number(!!b.pinned) - Number(!!a.pinned) || b.sortTime - a.sortTime)
|
|
2205
|
+
.slice(0, options.limit ?? 20);
|
|
2164
2206
|
// 并行解析每个 session 的 model/effort(cursor 涉及异步 store IO)
|
|
2165
2207
|
return Promise.all(entries.map(async (info) => {
|
|
2166
2208
|
const { model, effort } = await resolveModelEffort(info.tool, info.sessionId);
|
|
@@ -2169,6 +2211,9 @@ export async function getAllSessionsStatus() {
|
|
|
2169
2211
|
chatType: info.chatType,
|
|
2170
2212
|
sessionId: info.sessionId,
|
|
2171
2213
|
chatName: info.chatName || "",
|
|
2214
|
+
displayTitle: info.displayTitle || "",
|
|
2215
|
+
pinned: info.pinned ?? false,
|
|
2216
|
+
...(info.archivedAt ? { archivedAt: info.archivedAt } : {}),
|
|
2172
2217
|
active: !!activePrompts.get(info.sessionId) &&
|
|
2173
2218
|
!activePrompts.get(info.sessionId)?.stopped &&
|
|
2174
2219
|
!activePrompts.get(info.sessionId)?.abnormalExit,
|
|
@@ -2192,7 +2237,7 @@ export function _setAdapterForToolForTest(tool, adapter) {
|
|
|
2192
2237
|
const effective = getEffectiveModelForTool(tool);
|
|
2193
2238
|
const effort = getEffectiveEffortForTool(tool);
|
|
2194
2239
|
const fastMode = getEffectiveFastModeForTool(tool);
|
|
2195
|
-
adapterCache.set(
|
|
2240
|
+
adapterCache.set(buildAdapterCacheKey(tool, effective || "", effort || "", fastMode), adapter);
|
|
2196
2241
|
if (effective)
|
|
2197
2242
|
adapterCache.set(`${tool}:${effective}`, adapter);
|
|
2198
2243
|
}
|
package/dist/src/web-ui.js
CHANGED
|
@@ -496,6 +496,14 @@ export function unflattenConfig(flat) {
|
|
|
496
496
|
result.ccc = result.ccc || {};
|
|
497
497
|
result.ccc.effort = val;
|
|
498
498
|
}
|
|
499
|
+
else if (key === "CHATCCC_CCC_MAX_OUTPUT_TOKENS") {
|
|
500
|
+
result.ccc = result.ccc || {};
|
|
501
|
+
const raw = String(val ?? "").trim();
|
|
502
|
+
const parsed = Number(raw);
|
|
503
|
+
result.ccc.maxOutputTokens = raw && Number.isInteger(parsed) && parsed > 0
|
|
504
|
+
? parsed
|
|
505
|
+
: null;
|
|
506
|
+
}
|
|
499
507
|
else if (key === "CHATCCC_CCC_PROVIDER") {
|
|
500
508
|
result.ccc = result.ccc || {};
|
|
501
509
|
result.ccc.provider = val;
|
|
@@ -684,8 +692,9 @@ header{background:#0f172a;color:#fff;padding:16px 24px;display:flex;align-items:
|
|
|
684
692
|
header h1{font-size:20px;font-weight:600}
|
|
685
693
|
header .badge{font-size:13px;padding:4px 12px;border-radius:12px;font-weight:500}
|
|
686
694
|
.header-actions{display:flex;align-items:center;gap:12px}
|
|
687
|
-
.agent-team-entry{display:inline-flex;align-items:center;gap:8px;padding:9px 16px;border:1px solid rgba(255,255,255,.2);border-radius:999px;background:linear-gradient(135deg,#6366f1,#8b5cf6 55%,#d946ef);color:#fff;text-decoration:none;font-size:14px;font-weight:700;letter-spacing:.01em;box-shadow:0 8px 24px rgba(99,102,241,.38);transition:transform .18s ease,box-shadow .18s ease,filter .18s ease}
|
|
688
|
-
.
|
|
695
|
+
.agent-team-entry,.deepccc-web-entry{display:inline-flex;align-items:center;gap:8px;padding:9px 16px;border:1px solid rgba(255,255,255,.2);border-radius:999px;background:linear-gradient(135deg,#6366f1,#8b5cf6 55%,#d946ef);color:#fff;text-decoration:none;font-size:14px;font-weight:700;letter-spacing:.01em;box-shadow:0 8px 24px rgba(99,102,241,.38);transition:transform .18s ease,box-shadow .18s ease,filter .18s ease}
|
|
696
|
+
.deepccc-web-entry{background:linear-gradient(135deg,#171a23,#413a76);box-shadow:0 8px 24px rgba(42,38,78,.38)}
|
|
697
|
+
.agent-team-entry:hover,.deepccc-web-entry:hover{transform:translateY(-2px);box-shadow:0 12px 30px rgba(139,92,246,.52);filter:saturate(1.16)}
|
|
689
698
|
.agent-team-entry:focus-visible{outline:3px solid rgba(196,181,253,.65);outline-offset:3px}
|
|
690
699
|
.agent-team-entry .agent-team-icon{font-size:16px;line-height:1}
|
|
691
700
|
.badge-running{background:#16a34a;color:#fff}
|
|
@@ -761,14 +770,15 @@ header .badge{font-size:13px;padding:4px 12px;border-radius:12px;font-weight:500
|
|
|
761
770
|
@keyframes slideIn{from{transform:translateX(100%);opacity:0}to{transform:translateX(0);opacity:1}}
|
|
762
771
|
.spinner{display:inline-block;width:14px;height:14px;border:2px solid rgba(255,255,255,.3);border-top-color:#fff;border-radius:50%;animation:spin .6s linear infinite}
|
|
763
772
|
@keyframes spin{to{transform:rotate(360deg)}}
|
|
764
|
-
@media(max-width:520px){header{padding:12px 14px}.header-actions{gap:8px}.agent-team-entry{padding:8px 11px;font-size:13px}header .badge{padding:4px 8px}}
|
|
773
|
+
@media(max-width:520px){header{padding:12px 14px}.header-actions{gap:8px}.agent-team-entry,.deepccc-web-entry{padding:8px 11px;font-size:13px}header .badge{padding:4px 8px}.deepccc-web-entry span:last-child{display:none}}
|
|
765
774
|
</style>
|
|
766
775
|
</head>
|
|
767
776
|
<body>
|
|
768
777
|
<header>
|
|
769
778
|
<h1>ChatCCC</h1>
|
|
770
|
-
<div class="header-actions">
|
|
771
|
-
<
|
|
779
|
+
<div class="header-actions">
|
|
780
|
+
<button type="button" class="deepccc-web-entry" onclick="openDeepCccWeb()"><span aria-hidden="true">D</span><span>DeepCCC Web</span></button>
|
|
781
|
+
<a href="/agent-team" class="agent-team-entry"><span class="agent-team-icon" aria-hidden="true">✦</span>Agent Team <span aria-hidden="true">→</span></a>
|
|
772
782
|
<span id="header-badge" class="badge badge-stopped">未启动</span>
|
|
773
783
|
</div>
|
|
774
784
|
</header>
|
|
@@ -931,19 +941,25 @@ header .badge{font-size:13px;padding:4px 12px;border-radius:12px;font-weight:500
|
|
|
931
941
|
</select>
|
|
932
942
|
<div class="hint">开启时追加 DeepCCC 共同作者,不替换你的 Git Author。</div>
|
|
933
943
|
</div>
|
|
934
|
-
<div class="form-group">
|
|
935
|
-
<label>Effort(推理强度,选填)</label>
|
|
936
|
-
<select id="field-CHATCCC_CCC_EFFORT">
|
|
937
|
-
<option value=""
|
|
938
|
-
<option value="none">none - 直接作答,最省 token</option>
|
|
944
|
+
<div class="form-group">
|
|
945
|
+
<label>Effort(推理强度,选填)</label>
|
|
946
|
+
<select id="field-CHATCCC_CCC_EFFORT">
|
|
947
|
+
<option value="">跟随 DeepCCC 内核配置(默认)</option>
|
|
948
|
+
<option value="none">none - 直接作答,最省 token</option>
|
|
939
949
|
<option value="minimal">minimal</option>
|
|
940
950
|
<option value="low">low</option>
|
|
941
951
|
<option value="medium">medium</option>
|
|
942
952
|
<option value="high">high</option>
|
|
943
953
|
<option value="xhigh">xhigh</option>
|
|
944
|
-
<option value="max">max - 最强推理</option>
|
|
945
|
-
</select>
|
|
946
|
-
|
|
954
|
+
<option value="max">max - 最强推理</option>
|
|
955
|
+
</select>
|
|
956
|
+
<div class="hint">留空时读取 ~/.deepccc/config.json 或 DEEPCCC_EFFORT;DeepCCC 也留空时使用模型服务端默认值。</div>
|
|
957
|
+
</div>
|
|
958
|
+
<div class="form-group">
|
|
959
|
+
<label>最大输出 Token(选填)</label>
|
|
960
|
+
<input type="number" id="field-CHATCCC_CCC_MAX_OUTPUT_TOKENS" min="1" step="1" placeholder="留空跟随 DeepCCC 内核配置">
|
|
961
|
+
<div class="hint">限制主对话单次输出长度。留空时读取 ~/.deepccc/config.json 或 DEEPCCC_MAX_OUTPUT_TOKENS;DeepCCC 也未配置时使用模型服务端默认值。</div>
|
|
962
|
+
</div>
|
|
947
963
|
<div class="form-group">
|
|
948
964
|
<label>上下文窗口(模型最大上下文)</label>
|
|
949
965
|
<select id="field-CHATCCC_CCC_CONTEXT_WINDOW" onchange="onContextWindowPresetChange('field-', this.value)">
|
|
@@ -1276,10 +1292,12 @@ header .badge{font-size:13px;padding:4px 12px;border-radius:12px;font-weight:500
|
|
|
1276
1292
|
<div class="config-row"><span class="key">API Key</span><span class="val" id="cfg-CCC_API_KEY">-</span></div>
|
|
1277
1293
|
<div class="config-row"><span class="key">Base URL</span><span class="val" id="cfg-CCC_BASE_URL">-</span></div>
|
|
1278
1294
|
<div class="config-row"><span class="key">API 协议</span><span class="val" id="cfg-CCC_PROVIDER">-</span></div>
|
|
1279
|
-
<div class="config-row"><span class="key">模型</span><span class="val" id="cfg-CCC_MODEL">-</span></div>
|
|
1280
|
-
<div class="config-row"><span class="key">子模型</span><span class="val" id="cfg-CCC_SUB_MODEL">-</span></div>
|
|
1281
|
-
<div class="config-row"><span class="key">备选模型</span><span class="val" id="cfg-CCC_ALTERNATIVE_MODEL">-</span></div>
|
|
1282
|
-
<div class="config-row"><span class="key">
|
|
1295
|
+
<div class="config-row"><span class="key">模型</span><span class="val" id="cfg-CCC_MODEL">-</span></div>
|
|
1296
|
+
<div class="config-row"><span class="key">子模型</span><span class="val" id="cfg-CCC_SUB_MODEL">-</span></div>
|
|
1297
|
+
<div class="config-row"><span class="key">备选模型</span><span class="val" id="cfg-CCC_ALTERNATIVE_MODEL">-</span></div>
|
|
1298
|
+
<div class="config-row"><span class="key">Effort</span><span class="val" id="cfg-CCC_EFFORT">-</span></div>
|
|
1299
|
+
<div class="config-row"><span class="key">最大输出 Token</span><span class="val" id="cfg-CCC_MAX_OUTPUT_TOKENS">-</span></div>
|
|
1300
|
+
<div class="config-row"><span class="key">Git 共同作者</span><span class="val" id="cfg-CCC_GIT_COAUTHOR">-</span></div>
|
|
1283
1301
|
<label class="agent-default-row" style="margin-top:10px"><input type="checkbox" id="dash-default-ccc" onchange="setDashboardDefaultAgent('ccc', this.checked)"> 设为默认 Agent</label>
|
|
1284
1302
|
<div class="hint" style="margin-top:6px;line-height:1.6">备选模型仅加入 /model 人工切换列表;保存后下一条消息或下个新会话生效。</div>
|
|
1285
1303
|
<button class="btn btn-outline" style="margin-top:8px" onclick="editSection('ccc')">编辑</button>
|
|
@@ -1330,7 +1348,7 @@ const AGENT_FIELDS = {
|
|
|
1330
1348
|
claude: ['CHATCCC_ANTHROPIC_MODEL','CHATCCC_ANTHROPIC_SUBAGENT_MODEL','CHATCCC_ANTHROPIC_EFFORT','CHATCCC_ANTHROPIC_API_KEY','CHATCCC_ANTHROPIC_BASE_URL','CHATCCC_ANTHROPIC_MAX_TURN'],
|
|
1331
1349
|
cursor: ['CHATCCC_CURSOR_PATH','CHATCCC_CURSOR_MODEL','CHATCCC_CURSOR_ALTERNATIVE_MODEL','CHATCCC_CURSOR_AVATAR_BATTERY_MODE','CHATCCC_CURSOR_ON_DEMAND_MONTHLY_BUDGET'],
|
|
1332
1350
|
codex: ['CHATCCC_CODEX_PATH','CHATCCC_CODEX_MODEL','CHATCCC_CODEX_ALTERNATIVE_MODEL','CHATCCC_CODEX_EFFORT','CHATCCC_CODEX_FAST_MODE'],
|
|
1333
|
-
ccc: ['CHATCCC_CCC_API_KEY','CHATCCC_CCC_BASE_URL','CHATCCC_CCC_MODEL','CHATCCC_CCC_SUB_MODEL','CHATCCC_CCC_ALTERNATIVE_MODEL','CHATCCC_CCC_EFFORT','CHATCCC_CCC_PROVIDER','CHATCCC_CCC_GIT_COAUTHOR','CHATCCC_CCC_CONTEXT_WINDOW'],
|
|
1351
|
+
ccc: ['CHATCCC_CCC_API_KEY','CHATCCC_CCC_BASE_URL','CHATCCC_CCC_MODEL','CHATCCC_CCC_SUB_MODEL','CHATCCC_CCC_ALTERNATIVE_MODEL','CHATCCC_CCC_EFFORT','CHATCCC_CCC_MAX_OUTPUT_TOKENS','CHATCCC_CCC_PROVIDER','CHATCCC_CCC_GIT_COAUTHOR','CHATCCC_CCC_CONTEXT_WINDOW'],
|
|
1334
1352
|
dsh: ['CHATCCC_DSH_API_KEY','CHATCCC_DSH_BASE_URL','CHATCCC_DSH_MODEL','CHATCCC_DSH_SUB_MODEL','CHATCCC_DSH_ALTERNATIVE_MODEL','CHATCCC_DSH_PROVIDER','CHATCCC_DSH_MAX_TOKENS']
|
|
1335
1353
|
};
|
|
1336
1354
|
const FEISHU_FIELDS = ['CHATCCC_APP_ID','CHATCCC_APP_SECRET'];
|
|
@@ -1727,7 +1745,8 @@ function renderStep2() {
|
|
|
1727
1745
|
prefillNested('field-CHATCCC_CCC_MODEL', c.ccc.model);
|
|
1728
1746
|
prefillNested('field-CHATCCC_CCC_SUB_MODEL', c.ccc.subModel);
|
|
1729
1747
|
prefillNested('field-CHATCCC_CCC_ALTERNATIVE_MODEL', c.ccc.alternativeModel);
|
|
1730
|
-
prefillNested('field-CHATCCC_CCC_EFFORT', c.ccc.effort);
|
|
1748
|
+
prefillNested('field-CHATCCC_CCC_EFFORT', c.ccc.effort);
|
|
1749
|
+
prefillNested('field-CHATCCC_CCC_MAX_OUTPUT_TOKENS', c.ccc.maxOutputTokens);
|
|
1731
1750
|
prefillContextWindow('field-', c.ccc.contextWindow);
|
|
1732
1751
|
}
|
|
1733
1752
|
if (c.dsh) {
|
|
@@ -1935,7 +1954,8 @@ function renderStep3() {
|
|
|
1935
1954
|
lines.push('<div class="config-row"><span class="key">模型</span><span class="val">' + (vars.CHATCCC_CCC_MODEL || '(留空)') + '</span></div>');
|
|
1936
1955
|
lines.push('<div class="config-row"><span class="key">子模型</span><span class="val">' + (vars.CHATCCC_CCC_SUB_MODEL || '(留空,跟随主模型)') + '</span></div>');
|
|
1937
1956
|
lines.push('<div class="config-row"><span class="key">备选模型</span><span class="val">' + (vars.CHATCCC_CCC_ALTERNATIVE_MODEL || '(留空)') + '</span></div>');
|
|
1938
|
-
lines.push('<div class="config-row"><span class="key">Effort</span><span class="val">' + (vars.CHATCCC_CCC_EFFORT || '(留空)') + '</span></div>');
|
|
1957
|
+
lines.push('<div class="config-row"><span class="key">Effort</span><span class="val">' + (vars.CHATCCC_CCC_EFFORT || '(留空)') + '</span></div>');
|
|
1958
|
+
lines.push('<div class="config-row"><span class="key">最大输出 Token</span><span class="val">' + (vars.CHATCCC_CCC_MAX_OUTPUT_TOKENS || '(跟随 DeepCCC)') + '</span></div>');
|
|
1939
1959
|
lines.push('<div class="config-row"><span class="key">上下文窗口</span><span class="val">' + contextWindowTokensLabel(vars.CHATCCC_CCC_CONTEXT_WINDOW || 1048576) + '</span></div>');
|
|
1940
1960
|
} else if (t === 'dsh') {
|
|
1941
1961
|
lines.push('<h4 style="margin:10px 0 4px;color:#334155">DeepSeek Harness</h4>');
|
|
@@ -2164,9 +2184,13 @@ function updateDashboardUI() {
|
|
|
2164
2184
|
document.getElementById('cfg-CCC_API_KEY').textContent = (c.ccc && c.ccc.DEEPSEEK_API_KEY) ? '***已设置***' : '(留空)';
|
|
2165
2185
|
document.getElementById('cfg-CCC_BASE_URL').textContent = (c.ccc && c.ccc.DEEPSEEK_BASE_URL) || '(留空)';
|
|
2166
2186
|
document.getElementById('cfg-CCC_PROVIDER').textContent = (c.ccc && c.ccc.provider) ? c.ccc.provider : '(跟随 DeepCCC 内核配置)';
|
|
2167
|
-
document.getElementById('cfg-CCC_MODEL').textContent = (c.ccc && c.ccc.model) || '(留空)';
|
|
2168
|
-
document.getElementById('cfg-CCC_SUB_MODEL').textContent = (c.ccc && c.ccc.subModel) || '(留空,跟随主模型)';
|
|
2169
|
-
document.getElementById('cfg-CCC_ALTERNATIVE_MODEL').textContent = (c.ccc && c.ccc.alternativeModel) || '(留空)';
|
|
2187
|
+
document.getElementById('cfg-CCC_MODEL').textContent = (c.ccc && c.ccc.model) || '(留空)';
|
|
2188
|
+
document.getElementById('cfg-CCC_SUB_MODEL').textContent = (c.ccc && c.ccc.subModel) || '(留空,跟随主模型)';
|
|
2189
|
+
document.getElementById('cfg-CCC_ALTERNATIVE_MODEL').textContent = (c.ccc && c.ccc.alternativeModel) || '(留空)';
|
|
2190
|
+
document.getElementById('cfg-CCC_EFFORT').textContent = (c.ccc && c.ccc.effort) || '(跟随 DeepCCC 内核配置)';
|
|
2191
|
+
document.getElementById('cfg-CCC_MAX_OUTPUT_TOKENS').textContent = c.ccc && c.ccc.maxOutputTokens
|
|
2192
|
+
? String(c.ccc.maxOutputTokens)
|
|
2193
|
+
: '(跟随 DeepCCC 内核配置)';
|
|
2170
2194
|
document.getElementById('cfg-CCC_GIT_COAUTHOR').textContent = !c.ccc || c.ccc.gitCoAuthor == null
|
|
2171
2195
|
? '跟随 DeepCCC 全局设置(缺省开启)'
|
|
2172
2196
|
: (c.ccc.gitCoAuthor ? '强制开启' : '强制关闭');
|
|
@@ -2255,7 +2279,7 @@ function editSection(section) {
|
|
|
2255
2279
|
'CHATCCC_CCC_API_KEY': 'API Key', 'CHATCCC_CCC_BASE_URL': 'Base URL',
|
|
2256
2280
|
'CHATCCC_CCC_PROVIDER': 'API 协议(选填)',
|
|
2257
2281
|
'CHATCCC_CCC_GIT_COAUTHOR': 'Git 提交共同作者',
|
|
2258
|
-
'CHATCCC_CCC_MODEL': '模型', 'CHATCCC_CCC_SUB_MODEL': '子模型', 'CHATCCC_CCC_ALTERNATIVE_MODEL': '备选模型', 'CHATCCC_CCC_EFFORT': 'Effort', 'CHATCCC_CCC_CONTEXT_WINDOW': '上下文窗口',
|
|
2282
|
+
'CHATCCC_CCC_MODEL': '模型', 'CHATCCC_CCC_SUB_MODEL': '子模型', 'CHATCCC_CCC_ALTERNATIVE_MODEL': '备选模型', 'CHATCCC_CCC_EFFORT': 'Effort', 'CHATCCC_CCC_MAX_OUTPUT_TOKENS': '最大输出 Token', 'CHATCCC_CCC_CONTEXT_WINDOW': '上下文窗口',
|
|
2259
2283
|
'CHATCCC_DSH_API_KEY': 'API Key', 'CHATCCC_DSH_BASE_URL': 'Base URL', 'CHATCCC_DSH_MODEL': '模型', 'CHATCCC_DSH_SUB_MODEL': '子模型', 'CHATCCC_DSH_ALTERNATIVE_MODEL': '备选模型', 'CHATCCC_DSH_PROVIDER': 'Provider 路由', 'CHATCCC_DSH_MAX_TOKENS': '单次最大输出 Tokens'
|
|
2260
2284
|
};
|
|
2261
2285
|
var hintMap = {
|
|
@@ -2263,7 +2287,9 @@ function editSection(section) {
|
|
|
2263
2287
|
'CHATCCC_CHROME_DEVTOOLS_ENABLED': '依赖:本机 Google Chrome;ChatGPT 订阅到期查询需要在该 CDP Chrome 中登录 ChatGPT。',
|
|
2264
2288
|
'CHATCCC_CHROME_DEVTOOLS_PORT': '默认 15166,健康检查端点为 http://127.0.0.1:15166/json/version。',
|
|
2265
2289
|
'CHATCCC_CHROME_DEVTOOLS_PATH': '选填。留空时自动探测 Google Chrome。',
|
|
2266
|
-
'CHATCCC_CCC_PROVIDER': '与 Base URL 强相关:OpenAI 兼容端点选 openai;Anthropic Messages 端点选 anthropic。留空 = 跟随 DeepCCC 内核配置(~/.deepccc/config.json 或 DEEPCCC_PROVIDER),改动需重启 ChatCCC 生效。',
|
|
2290
|
+
'CHATCCC_CCC_PROVIDER': '与 Base URL 强相关:OpenAI 兼容端点选 openai;Anthropic Messages 端点选 anthropic。留空 = 跟随 DeepCCC 内核配置(~/.deepccc/config.json 或 DEEPCCC_PROVIDER),改动需重启 ChatCCC 生效。',
|
|
2291
|
+
'CHATCCC_CCC_EFFORT': '留空 = 跟随 DeepCCC 内核配置(~/.deepccc/config.json 或 DEEPCCC_EFFORT);内核也留空时使用模型服务端默认值。',
|
|
2292
|
+
'CHATCCC_CCC_MAX_OUTPUT_TOKENS': '正整数;留空 = 跟随 DeepCCC 内核配置(~/.deepccc/config.json 或 DEEPCCC_MAX_OUTPUT_TOKENS);内核也未配置时使用模型服务端默认值。',
|
|
2267
2293
|
'CHATCCC_CCC_GIT_COAUTHOR': '跟随全局时读取 ~/.deepccc/config.json 的 git.coAuthor.enabled(缺省为开启)。强制选项只影响 ChatCCC 内置 CCC Agent。',
|
|
2268
2294
|
'CHATCCC_CCC_CONTEXT_WINDOW': '压缩阈值自动 = 窗口 × 80%(超出即把较早消息压缩为摘要)。⚠️ 超过模型/服务端实际上限时请求会被 API 直接拒绝(context length exceeded),实际窗口以模型与所用服务端为准(如 litellm 代理的 max_input_tokens);单位 k = 1024 tokens,1M = 1,048,576 tokens。',
|
|
2269
2295
|
'CHATCCC_CCC_SUB_MODEL': '用于 DeepCCC 内部轻量环节(上下文压缩摘要生成、task 子代理任务)。留空 = 跟随主模型;改动需重启 ChatCCC 生效。'
|
|
@@ -2313,7 +2339,8 @@ function editSection(section) {
|
|
|
2313
2339
|
else if (key === 'CHATCCC_CCC_MODEL') val = state.config.ccc.model || '';
|
|
2314
2340
|
else if (key === 'CHATCCC_CCC_SUB_MODEL') val = state.config.ccc.subModel || '';
|
|
2315
2341
|
else if (key === 'CHATCCC_CCC_ALTERNATIVE_MODEL') val = state.config.ccc.alternativeModel || '';
|
|
2316
|
-
else if (key === 'CHATCCC_CCC_EFFORT') val = state.config.ccc.effort || '';
|
|
2342
|
+
else if (key === 'CHATCCC_CCC_EFFORT') val = state.config.ccc.effort || '';
|
|
2343
|
+
else if (key === 'CHATCCC_CCC_MAX_OUTPUT_TOKENS') val = state.config.ccc.maxOutputTokens || '';
|
|
2317
2344
|
else if (key === 'CHATCCC_CCC_CONTEXT_WINDOW') val = state.config.ccc.contextWindow || '1048576';
|
|
2318
2345
|
} else if (section === 'dsh' && state.config.dsh) {
|
|
2319
2346
|
if (key === 'CHATCCC_DSH_API_KEY') val = state.config.dsh.apiKey || '';
|
|
@@ -2378,13 +2405,15 @@ function editSection(section) {
|
|
|
2378
2405
|
var rowId = key === 'CHATCCC_CURSOR_ON_DEMAND_MONTHLY_BUDGET'
|
|
2379
2406
|
? ' id="edit-cursor-on-demand-budget-row"'
|
|
2380
2407
|
: (section === 'chromeDevtools' && key !== 'CHATCCC_CHROME_DEVTOOLS_ENABLED' ? ' id="edit-row-' + key + '"' : '');
|
|
2381
|
-
var isNumber = key === 'CHATCCC_CURSOR_ON_DEMAND_MONTHLY_BUDGET' || key === 'CHATCCC_CHROME_DEVTOOLS_PORT';
|
|
2408
|
+
var isNumber = key === 'CHATCCC_CURSOR_ON_DEMAND_MONTHLY_BUDGET' || key === 'CHATCCC_CHROME_DEVTOOLS_PORT' || key === 'CHATCCC_CCC_MAX_OUTPUT_TOKENS';
|
|
2382
2409
|
var inputType = isNumber ? 'number' : (isSecret ? 'password' : 'text');
|
|
2383
2410
|
var attrs = key === 'CHATCCC_CURSOR_ON_DEMAND_MONTHLY_BUDGET'
|
|
2384
2411
|
? ' min="1" step="1"'
|
|
2385
|
-
: key === 'CHATCCC_CHROME_DEVTOOLS_PORT'
|
|
2386
|
-
? ' min="1" max="65535" step="1" placeholder="15166"'
|
|
2387
|
-
: ''
|
|
2412
|
+
: key === 'CHATCCC_CHROME_DEVTOOLS_PORT'
|
|
2413
|
+
? ' min="1" max="65535" step="1" placeholder="15166"'
|
|
2414
|
+
: key === 'CHATCCC_CCC_MAX_OUTPUT_TOKENS'
|
|
2415
|
+
? ' min="1" step="1" placeholder="留空跟随 DeepCCC 内核配置"'
|
|
2416
|
+
: '';
|
|
2388
2417
|
html += '<div class="form-group"' + rowId + '><label>' + (labelMap[key] || key) + '</label>';
|
|
2389
2418
|
html += '<input type="' + inputType + '" id="edit-' + key + '"' + attrs + ' value="' + String(val).replace(/"/g,'"') + '">';
|
|
2390
2419
|
if (hintMap[key]) html += '<div class="hint" style="margin-top:6px;line-height:1.5">' + hintMap[key] + '</div>';
|
|
@@ -2566,8 +2595,18 @@ function installEngine(engineId) {
|
|
|
2566
2595
|
});
|
|
2567
2596
|
}
|
|
2568
2597
|
|
|
2569
|
-
// ---- Start ----
|
|
2570
|
-
|
|
2598
|
+
// ---- Start ----
|
|
2599
|
+
async function openDeepCccWeb() {
|
|
2600
|
+
try {
|
|
2601
|
+
var result = await api('/api/deepccc-web/start', 'POST');
|
|
2602
|
+
if (!result.ok) throw new Error(result.error || '启动失败');
|
|
2603
|
+
window.open(result.url, '_blank', 'noopener');
|
|
2604
|
+
} catch (error) {
|
|
2605
|
+
toast('DeepCCC Web 启动失败: ' + String(error), 'error');
|
|
2606
|
+
}
|
|
2607
|
+
}
|
|
2608
|
+
|
|
2609
|
+
init();
|
|
2571
2610
|
|
|
2572
2611
|
// 页面刷新后从持久化任务文件恢复每一步的安装进度。
|
|
2573
2612
|
setTimeout(function(){ engineRefreshStatus('claude'); engineRefreshStatus('dsh'); }, 300);
|
|
@@ -2600,6 +2639,11 @@ async function handleRequest(req, res) {
|
|
|
2600
2639
|
return handleStopService(req, res);
|
|
2601
2640
|
if (url === "/api/restart" && method === "POST")
|
|
2602
2641
|
return handleRestartService(req, res);
|
|
2642
|
+
if (url === "/api/deepccc-web/start" && method === "POST") {
|
|
2643
|
+
const { launchDeepCccWebProcess } = await import("../deepccc-agent/src/web-server.js");
|
|
2644
|
+
const handle = await launchDeepCccWebProcess({ reuseExisting: true, openBrowser: false, defaultCwd: process.cwd() });
|
|
2645
|
+
return jsonReply(res, 200, { ok: true, url: handle.url, port: handle.port, reused: handle.reused });
|
|
2646
|
+
}
|
|
2603
2647
|
if (url === "/api/validate" && method === "POST")
|
|
2604
2648
|
return handleValidate(req, res);
|
|
2605
2649
|
if (url === "/api/ilink/forget" && method === "POST")
|
|
@@ -9,7 +9,7 @@ Videos are sent as regular files (not media), which looks cleaner in Feishu.
|
|
|
9
9
|
### Script
|
|
10
10
|
|
|
11
11
|
```bash
|
|
12
|
-
node "{{send_file_script}}" --url "{{send_file_url}}" --session-id "{{session_id}}" --path "<absolute file path>" --caption "<optional caption>"
|
|
12
|
+
node "{{send_file_script}}" --url "{{send_file_url}}" --session-id "{{session_id}}" --grant "{{agent_capability_grant}}" --path "<absolute file path>" --caption "<optional caption>"
|
|
13
13
|
```
|
|
14
14
|
|
|
15
15
|
### Rules
|
|
@@ -17,6 +17,7 @@ node "{{send_file_script}}" --url "{{send_file_url}}" --session-id "{{session_id
|
|
|
17
17
|
- Use the node script above — never curl or raw HTTP.
|
|
18
18
|
- Save or choose a local file first.
|
|
19
19
|
- Use an absolute local path.
|
|
20
|
+
- Use only the session ID and capability grant shown above; they are a bound pair.
|
|
20
21
|
- Max file size: 30MB.
|
|
21
22
|
- Supported formats: .mp4 .mov .avi .mkv .webm .flv .mp3 .wav .ogg .aac .m4a .pdf .doc .docx .xls .xlsx .csv .ppt .pptx .txt .zip .tar .gz.
|
|
22
23
|
- Only send a file/video when the user asked for one or when it materially helps the answer.
|
|
@@ -61,4 +62,4 @@ If only `chat_id` and `file_key` are available:
|
|
|
61
62
|
node "{{download_video_script}}" --chat-id <chat_id> --file-key <file_key> --name <file_name>
|
|
62
63
|
```
|
|
63
64
|
|
|
64
|
-
Downloads are saved under `~/.chatccc/videos/downloads/`.
|
|
65
|
+
Downloads are saved under `~/.chatccc/videos/downloads/`.
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
### Script
|
|
8
8
|
|
|
9
9
|
```bash
|
|
10
|
-
node "{{send_image_script}}" --url "{{send_image_url}}" --session-id "{{session_id}}" --path "<absolute image path>" --caption "<optional caption>"
|
|
10
|
+
node "{{send_image_script}}" --url "{{send_image_url}}" --session-id "{{session_id}}" --grant "{{agent_capability_grant}}" --path "<absolute image path>" --caption "<optional caption>"
|
|
11
11
|
```
|
|
12
12
|
|
|
13
13
|
### Rules
|
|
@@ -15,6 +15,7 @@ node "{{send_image_script}}" --url "{{send_image_url}}" --session-id "{{session_
|
|
|
15
15
|
- Use the node script above — never curl or raw HTTP.
|
|
16
16
|
- Save or choose a local image file first.
|
|
17
17
|
- Use an absolute local path.
|
|
18
|
+
- Use only the session ID and capability grant shown above; they are a bound pair.
|
|
18
19
|
- Supported formats: .png, .jpg, .jpeg, .webp, .gif, .bmp.
|
|
19
20
|
- Max image size: 10MB.
|
|
20
21
|
- Only send an image when the user asked for one or when it materially helps the answer.
|
|
@@ -22,4 +23,4 @@ node "{{send_image_script}}" --url "{{send_image_url}}" --session-id "{{session_
|
|
|
22
23
|
|
|
23
24
|
## Receive Images
|
|
24
25
|
|
|
25
|
-
Images sent to the bot are automatically downloaded to `~/.chatccc/images/downloads/`. The message contains an `image_key` that maps to the cached file.
|
|
26
|
+
Images sent to the bot are automatically downloaded to `~/.chatccc/images/downloads/`. The message contains an `image_key` that maps to the cached file.
|
|
@@ -14,22 +14,23 @@ function parseArgs(argv) {
|
|
|
14
14
|
|
|
15
15
|
function usage() {
|
|
16
16
|
console.error(`Usage:
|
|
17
|
-
node ${basename(process.argv[1])} --url <url> --session-id <session_id> --path <absolute file path> [--caption <text>]`);
|
|
17
|
+
node ${basename(process.argv[1])} --url <url> --session-id <session_id> --grant <session_grant> --path <absolute file path> [--caption <text>]`);
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
async function main() {
|
|
21
21
|
const args = parseArgs(process.argv.slice(2));
|
|
22
22
|
const url = args.url || process.env.CHATCCC_SEND_FILE_URL;
|
|
23
|
-
const sessionId = args["session-id"] || args.session_id || process.env.CHATCCC_SESSION_ID;
|
|
23
|
+
const sessionId = args["session-id"] || args.session_id || process.env.CHATCCC_SESSION_ID;
|
|
24
|
+
const grant = args.grant || process.env.CHATCCC_AGENT_CAPABILITY_GRANT;
|
|
24
25
|
const path = args.path;
|
|
25
26
|
const caption = args.caption || "";
|
|
26
27
|
|
|
27
|
-
if (!url || !sessionId || !path) {
|
|
28
|
+
if (!url || !sessionId || !grant || !path) {
|
|
28
29
|
usage();
|
|
29
30
|
process.exit(1);
|
|
30
31
|
}
|
|
31
32
|
|
|
32
|
-
const body = Buffer.from(JSON.stringify({ session_id: sessionId, path, caption }), "utf8");
|
|
33
|
+
const body = Buffer.from(JSON.stringify({ session_id: sessionId, grant, path, caption }), "utf8");
|
|
33
34
|
const response = await fetch(url, {
|
|
34
35
|
method: "POST",
|
|
35
36
|
headers: {
|
|
@@ -48,4 +49,4 @@ async function main() {
|
|
|
48
49
|
main().catch((err) => {
|
|
49
50
|
console.error(err instanceof Error ? err.message : String(err));
|
|
50
51
|
process.exit(1);
|
|
51
|
-
});
|
|
52
|
+
});
|
|
@@ -14,22 +14,23 @@ function parseArgs(argv) {
|
|
|
14
14
|
|
|
15
15
|
function usage() {
|
|
16
16
|
console.error(`Usage:
|
|
17
|
-
node ${basename(process.argv[1])} --url <url> --session-id <session_id> --path <absolute image path> [--caption <text>]`);
|
|
17
|
+
node ${basename(process.argv[1])} --url <url> --session-id <session_id> --grant <session_grant> --path <absolute image path> [--caption <text>]`);
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
async function main() {
|
|
21
21
|
const args = parseArgs(process.argv.slice(2));
|
|
22
22
|
const url = args.url || process.env.CHATCCC_SEND_IMAGE_URL;
|
|
23
|
-
const sessionId = args["session-id"] || args.session_id || process.env.CHATCCC_SESSION_ID;
|
|
23
|
+
const sessionId = args["session-id"] || args.session_id || process.env.CHATCCC_SESSION_ID;
|
|
24
|
+
const grant = args.grant || process.env.CHATCCC_AGENT_CAPABILITY_GRANT;
|
|
24
25
|
const path = args.path;
|
|
25
26
|
const caption = args.caption || "";
|
|
26
27
|
|
|
27
|
-
if (!url || !sessionId || !path) {
|
|
28
|
+
if (!url || !sessionId || !grant || !path) {
|
|
28
29
|
usage();
|
|
29
30
|
process.exit(1);
|
|
30
31
|
}
|
|
31
32
|
|
|
32
|
-
const body = Buffer.from(JSON.stringify({ session_id: sessionId, path, caption }), "utf8");
|
|
33
|
+
const body = Buffer.from(JSON.stringify({ session_id: sessionId, grant, path, caption }), "utf8");
|
|
33
34
|
const response = await fetch(url, {
|
|
34
35
|
method: "POST",
|
|
35
36
|
headers: {
|
|
@@ -48,4 +49,4 @@ async function main() {
|
|
|
48
49
|
main().catch((err) => {
|
|
49
50
|
console.error(err instanceof Error ? err.message : String(err));
|
|
50
51
|
process.exit(1);
|
|
51
|
-
});
|
|
52
|
+
});
|
|
@@ -5,12 +5,13 @@ description: Feishu IM local skills for sending images, files, videos, and for c
|
|
|
5
5
|
|
|
6
6
|
Current working directory: {{cwd}}
|
|
7
7
|
Your session id: {{session_id}}
|
|
8
|
+
Your session capability grant: {{agent_capability_grant}}
|
|
8
9
|
Your Feishu open_id: {{open_id}}
|
|
9
10
|
|
|
10
11
|
Use local endpoints instead of calling Feishu Open Platform directly.
|
|
11
12
|
|
|
12
|
-
- **Send images**: POST `{{send_image_url}}` with `{"session_id":"{{session_id}}","path":"<absolute path>","caption":"<optional>"}` — read `{{im_skills_cache_dir}}/feishu-skill/receive-send-image.md`
|
|
13
|
-
- **Send files/videos**: POST `{{send_file_url}}` with `{"session_id":"{{session_id}}","path":"<absolute path>","caption":"<optional>"}` — read `{{im_skills_cache_dir}}/feishu-skill/receive-send-file.md`
|
|
13
|
+
- **Send images**: POST `{{send_image_url}}` with `{"session_id":"{{session_id}}","grant":"{{agent_capability_grant}}","path":"<absolute path>","caption":"<optional>"}` — read `{{im_skills_cache_dir}}/feishu-skill/receive-send-image.md`
|
|
14
|
+
- **Send files/videos**: POST `{{send_file_url}}` with `{"session_id":"{{session_id}}","grant":"{{agent_capability_grant}}","path":"<absolute path>","caption":"<optional>"}` — read `{{im_skills_cache_dir}}/feishu-skill/receive-send-file.md`
|
|
14
15
|
- **Create a new session (新建会话)**: POST `{{delegate_task_url}}` with `{"tool":"claude|cursor|codex|ccc|dsh","cwd":"<absolute path>","open_id":"{{open_id}}","prompt":"<optional first task>"}`. This creates a new Feishu group and session, and only adds you (the requester). `tool` and `prompt` are optional; omit `prompt` to just create the session without a first task.
|
|
15
16
|
- **Set default working directory (cd / 切换目录)**: POST `{{set_cwd_url}}` with `{"session_id":"{{session_id}}","dir":"<absolute path>"}`. This sets the default directory for future new sessions only; it does not change the current session.
|
|
16
17
|
|
|
@@ -24,3 +25,4 @@ How to map user requests to these endpoints:
|
|
|
24
25
|
- when ambiguous, prefer creating a new session (the more common intent for "切换到").
|
|
25
26
|
- Directory names may be fuzzy or relative; resolve them to an absolute local path (using your file tools) before calling either endpoint.
|
|
26
27
|
- `open_id` must always be passed as exactly {{open_id}}; do not invent it.
|
|
28
|
+
- The capability grant is bound to this session. Never reuse a grant with another session ID.
|