@clawos-dev/clawd 0.2.286 → 0.2.288
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/cli.cjs
CHANGED
|
@@ -6585,15 +6585,14 @@ var init_shift_internal = __esm({
|
|
|
6585
6585
|
});
|
|
6586
6586
|
|
|
6587
6587
|
// ../protocol/src/lark-bot-schemas.ts
|
|
6588
|
-
var
|
|
6588
|
+
var LarkBotPersonaArgs, LarkBotUnbindArgs, LarkBotStatusArgs, LarkBotBindManualArgs, LarkBotGroupSchema, LarkBotStatusResultSchema, LarkBotStateFrameSchema;
|
|
6589
6589
|
var init_lark_bot_schemas = __esm({
|
|
6590
6590
|
"../protocol/src/lark-bot-schemas.ts"() {
|
|
6591
6591
|
"use strict";
|
|
6592
6592
|
init_zod();
|
|
6593
|
-
|
|
6594
|
-
|
|
6595
|
-
|
|
6596
|
-
LarkBotStatusArgs = LarkBotProvisionArgs;
|
|
6593
|
+
LarkBotPersonaArgs = external_exports.object({ personaId: external_exports.string().min(1) });
|
|
6594
|
+
LarkBotUnbindArgs = LarkBotPersonaArgs;
|
|
6595
|
+
LarkBotStatusArgs = LarkBotPersonaArgs;
|
|
6597
6596
|
LarkBotBindManualArgs = external_exports.object({
|
|
6598
6597
|
personaId: external_exports.string().min(1),
|
|
6599
6598
|
appId: external_exports.string().min(1),
|
|
@@ -6607,14 +6606,9 @@ var init_lark_bot_schemas = __esm({
|
|
|
6607
6606
|
lastActiveAt: external_exports.number().int().optional()
|
|
6608
6607
|
});
|
|
6609
6608
|
LarkBotStatusResultSchema = external_exports.object({
|
|
6610
|
-
state: external_exports.enum(["unbound", "
|
|
6609
|
+
state: external_exports.enum(["unbound", "bound", "broken"]),
|
|
6611
6610
|
appId: external_exports.string().optional(),
|
|
6612
6611
|
botName: external_exports.string().optional(),
|
|
6613
|
-
/** state='provisioning' 时的当前步骤与待用户打开的授权 URL */
|
|
6614
|
-
provisioning: external_exports.object({
|
|
6615
|
-
step: external_exports.enum(["register", "authorize"]),
|
|
6616
|
-
verificationUrl: external_exports.string().optional()
|
|
6617
|
-
}).optional(),
|
|
6618
6612
|
brokenReason: external_exports.string().optional(),
|
|
6619
6613
|
groups: external_exports.array(LarkBotGroupSchema)
|
|
6620
6614
|
});
|
|
@@ -7382,7 +7376,17 @@ var init_schemas = __esm({
|
|
|
7382
7376
|
SessionForkArgs = external_exports.object({
|
|
7383
7377
|
cwd: external_exports.string().min(1),
|
|
7384
7378
|
toolSessionId: external_exports.string().min(1),
|
|
7385
|
-
messageUuid: external_exports.string().min(1)
|
|
7379
|
+
messageUuid: external_exports.string().min(1),
|
|
7380
|
+
/**
|
|
7381
|
+
* 派生 jsonl 的落点 cwd(缺省 = 源文件所在目录,保持老行为)。
|
|
7382
|
+
*
|
|
7383
|
+
* 必须传的场景:调用方随后要用这条 transcript 建 session,而那个 session 的 cwd 与
|
|
7384
|
+
* 源 session 不同——最典型是 persona session(daemon 按 ownerPersonaId 把 cwd 强制
|
|
7385
|
+
* 派生成 persona 根)。CC `--resume` 只在**当前 cwd 的** projects 目录里找 session,
|
|
7386
|
+
* 落点与目标 cwd 分家 = 历史读不到 + resume 接不上(bug: quick-ask-blank-after-worktree)。
|
|
7387
|
+
* 正确编排:先 sessionCreate 拿到 daemon 定的真实 cwd,再以它作 targetCwd fork。
|
|
7388
|
+
*/
|
|
7389
|
+
targetCwd: external_exports.string().min(1).optional()
|
|
7386
7390
|
});
|
|
7387
7391
|
SessionForkResponseSchema = external_exports.object({
|
|
7388
7392
|
forkedToolSessionId: external_exports.string().min(1),
|
|
@@ -8028,16 +8032,8 @@ var init_methods = __esm({
|
|
|
8028
8032
|
summary: "\u67E5 shift \u7684\u6267\u884C\u5386\u53F2\uFF08\u4EC5\u521B\u5EFA\u8005\uFF09",
|
|
8029
8033
|
args: ShiftInternalHistoryArgs
|
|
8030
8034
|
},
|
|
8031
|
-
"larkBot:provision": {
|
|
8032
|
-
summary: "\u53D1\u8D77 persona \u7684\u98DE\u4E66 bot \u4E00\u952E\u88C5\u914D\uFF08\u5EFA app + \u6388 scope\uFF09\uFF0C\u8FDB\u5EA6\u4E0E\u6388\u6743 URL \u8D70 larkBot:state \u5E27\uFF1Bowner-only",
|
|
8033
|
-
args: LarkBotProvisionArgs
|
|
8034
|
-
},
|
|
8035
|
-
"larkBot:provisionCancel": {
|
|
8036
|
-
summary: "\u53D6\u6D88\u8FDB\u884C\u4E2D\u7684\u98DE\u4E66 bot \u88C5\u914D\u6D41\u7A0B\uFF1Bowner-only",
|
|
8037
|
-
args: LarkBotProvisionCancelArgs
|
|
8038
|
-
},
|
|
8039
8035
|
"larkBot:bindManual": {
|
|
8040
|
-
summary: "\u624B\u586B app_id/app_secret \
|
|
8036
|
+
summary: "\u624B\u586B app_id/app_secret \u7ED1\u5B9A\u73B0\u6709\u98DE\u4E66 bot\uFF08\u552F\u4E00\u7ED1\u5B9A\u5165\u53E3\uFF09\uFF1Bowner-only",
|
|
8041
8037
|
args: LarkBotBindManualArgs
|
|
8042
8038
|
},
|
|
8043
8039
|
"larkBot:unbind": {
|
|
@@ -8045,7 +8041,7 @@ var init_methods = __esm({
|
|
|
8045
8041
|
args: LarkBotUnbindArgs
|
|
8046
8042
|
},
|
|
8047
8043
|
"larkBot:status": {
|
|
8048
|
-
summary: "\u67E5 persona \u7684\u98DE\u4E66 bot \u72B6\u6001\uFF08unbound/
|
|
8044
|
+
summary: "\u67E5 persona \u7684\u98DE\u4E66 bot \u72B6\u6001\uFF08unbound/bound/broken + \u670D\u52A1\u4E2D\u7FA4\u6E05\u5355\uFF09\uFF1Bowner-only",
|
|
8049
8045
|
args: LarkBotStatusArgs
|
|
8050
8046
|
},
|
|
8051
8047
|
"skills:list": {
|
|
@@ -13982,6 +13978,26 @@ var init_claude_history = __esm({
|
|
|
13982
13978
|
this.projectsRoot = import_node_path5.default.join(base, "projects");
|
|
13983
13979
|
this.fileHistoryRoot = import_node_path5.default.join(base, "file-history");
|
|
13984
13980
|
}
|
|
13981
|
+
/**
|
|
13982
|
+
* [T-17] 定位一条 session 的 transcript 所在 project 目录,带 relocation 兜底。
|
|
13983
|
+
*
|
|
13984
|
+
* 常规路径:`<projectsRoot>/cwdToHashDir(cwd)`。但 CC 中途改 cwd(典型:会话里
|
|
13985
|
+
* EnterWorktree)会把 `<tsid>.jsonl` 挪到新 hash 目录,而调用方手里的 cwd 可能仍是老值——
|
|
13986
|
+
* persona session 尤其顽固:fork 出的新 session 因带 ownerPersonaId 被 manager 强制
|
|
13987
|
+
* 改写 cwd 回 persona 根(session/manager.ts derivePersonaSpawnCwd),与 jsonl 实际
|
|
13988
|
+
* 落点(源文件所在目录,见 session/fork.ts)永久分家。
|
|
13989
|
+
* 拼不出路径时按 tsid 全局兜底(与 fork.ts / observer 同款),命中则用它所在目录。
|
|
13990
|
+
*
|
|
13991
|
+
* 兜底失败仍返回按 cwd 算的目录:下游 readJsonlLines / readdir 都容忍不存在,
|
|
13992
|
+
* 保持"查无此 session 返回空"的既有语义,不抛。
|
|
13993
|
+
* bug: quick-ask-blank-after-worktree(fork 出的快速问答面板整片空白)
|
|
13994
|
+
*/
|
|
13995
|
+
resolveSessionDir(cwd, toolSessionId) {
|
|
13996
|
+
const byCwd = import_node_path5.default.join(this.projectsRoot, cwdToHashDir(cwd));
|
|
13997
|
+
if (import_node_fs6.default.existsSync(import_node_path5.default.join(byCwd, `${toolSessionId}.jsonl`))) return byCwd;
|
|
13998
|
+
const found = findTranscriptByToolSessionId(this.projectsRoot, toolSessionId);
|
|
13999
|
+
return found ? import_node_path5.default.dirname(found) : byCwd;
|
|
14000
|
+
}
|
|
13985
14001
|
async listProjects() {
|
|
13986
14002
|
let entries;
|
|
13987
14003
|
try {
|
|
@@ -14073,8 +14089,7 @@ var init_claude_history = __esm({
|
|
|
14073
14089
|
}
|
|
14074
14090
|
async read(args) {
|
|
14075
14091
|
const file = import_node_path5.default.join(
|
|
14076
|
-
this.
|
|
14077
|
-
cwdToHashDir(args.cwd),
|
|
14092
|
+
this.resolveSessionDir(args.cwd, args.toolSessionId),
|
|
14078
14093
|
`${args.toolSessionId}.jsonl`
|
|
14079
14094
|
);
|
|
14080
14095
|
const lines = readJsonlLines(file);
|
|
@@ -14110,8 +14125,7 @@ var init_claude_history = __esm({
|
|
|
14110
14125
|
// 返回 null 表示目录不存在(调用方回退旧实现);返回空数组表示目录存在但无 jsonl
|
|
14111
14126
|
listSubagentsFromDirectory(cwd, toolSessionId) {
|
|
14112
14127
|
const dir = import_node_path5.default.join(
|
|
14113
|
-
this.
|
|
14114
|
-
cwdToHashDir(cwd),
|
|
14128
|
+
this.resolveSessionDir(cwd, toolSessionId),
|
|
14115
14129
|
toolSessionId,
|
|
14116
14130
|
"subagents"
|
|
14117
14131
|
);
|
|
@@ -14145,8 +14159,7 @@ var init_claude_history = __esm({
|
|
|
14145
14159
|
}
|
|
14146
14160
|
listSubagentsFromMainJsonl(cwd, toolSessionId) {
|
|
14147
14161
|
const file = import_node_path5.default.join(
|
|
14148
|
-
this.
|
|
14149
|
-
cwdToHashDir(cwd),
|
|
14162
|
+
this.resolveSessionDir(cwd, toolSessionId),
|
|
14150
14163
|
`${toolSessionId}.jsonl`
|
|
14151
14164
|
);
|
|
14152
14165
|
const lines = readJsonlLines(file);
|
|
@@ -14180,8 +14193,7 @@ var init_claude_history = __esm({
|
|
|
14180
14193
|
// 独立文件路径:agent-<subagentId>.jsonl;文件不存在返回 null 让调用方回退旧实现
|
|
14181
14194
|
readSubagentFromFile(cwd, toolSessionId, subagentId) {
|
|
14182
14195
|
const file = import_node_path5.default.join(
|
|
14183
|
-
this.
|
|
14184
|
-
cwdToHashDir(cwd),
|
|
14196
|
+
this.resolveSessionDir(cwd, toolSessionId),
|
|
14185
14197
|
toolSessionId,
|
|
14186
14198
|
"subagents",
|
|
14187
14199
|
`agent-${subagentId}.jsonl`
|
|
@@ -14208,8 +14220,7 @@ var init_claude_history = __esm({
|
|
|
14208
14220
|
*/
|
|
14209
14221
|
readFileHistorySnapshots(args) {
|
|
14210
14222
|
const file = import_node_path5.default.join(
|
|
14211
|
-
this.
|
|
14212
|
-
cwdToHashDir(args.cwd),
|
|
14223
|
+
this.resolveSessionDir(args.cwd, args.toolSessionId),
|
|
14213
14224
|
`${args.toolSessionId}.jsonl`
|
|
14214
14225
|
);
|
|
14215
14226
|
const lines = readJsonlLines(file);
|
|
@@ -14340,8 +14351,7 @@ var init_claude_history = __esm({
|
|
|
14340
14351
|
}
|
|
14341
14352
|
readSubagentFromMainJsonl(cwd, toolSessionId, subagentId) {
|
|
14342
14353
|
const file = import_node_path5.default.join(
|
|
14343
|
-
this.
|
|
14344
|
-
cwdToHashDir(cwd),
|
|
14354
|
+
this.resolveSessionDir(cwd, toolSessionId),
|
|
14345
14355
|
`${toolSessionId}.jsonl`
|
|
14346
14356
|
);
|
|
14347
14357
|
const lines = readJsonlLines(file);
|
|
@@ -46034,6 +46044,14 @@ var SessionManager = class {
|
|
|
46034
46044
|
},
|
|
46035
46045
|
this.deps.personaRoot
|
|
46036
46046
|
);
|
|
46047
|
+
if (args.forkedFromSessionId && args.cwd && args.cwd !== cwd) {
|
|
46048
|
+
this.deps.logger?.info("session-create.fork-cwd-rewritten", {
|
|
46049
|
+
argCwd: args.cwd,
|
|
46050
|
+
derivedCwd: cwd,
|
|
46051
|
+
ownerPersonaId: args.ownerPersonaId,
|
|
46052
|
+
forkedFromSessionId: args.forkedFromSessionId
|
|
46053
|
+
});
|
|
46054
|
+
}
|
|
46037
46055
|
} else if (args.cwd) {
|
|
46038
46056
|
cwd = args.cwd;
|
|
46039
46057
|
} else {
|
|
@@ -51722,163 +51740,6 @@ function buildShiftInternalHandlers(deps) {
|
|
|
51722
51740
|
};
|
|
51723
51741
|
}
|
|
51724
51742
|
|
|
51725
|
-
// src/lark-bot-connector/provisioner.ts
|
|
51726
|
-
var LARK_BOT_V1_SCOPES = [
|
|
51727
|
-
"offline_access",
|
|
51728
|
-
"im:chat:readonly",
|
|
51729
|
-
"im:resource",
|
|
51730
|
-
"contact:user.base:readonly"
|
|
51731
|
-
];
|
|
51732
|
-
var REGISTRATION_URL = "https://accounts.feishu.cn/oauth/v1/app/registration";
|
|
51733
|
-
var DEVICE_AUTHORIZE_URL = "https://accounts.feishu.cn/oauth/v1/device/authorize";
|
|
51734
|
-
var DEVICE_TOKEN_URL = "https://open.feishu.cn/open-apis/authen/v2/oauth/token";
|
|
51735
|
-
var DEFAULT_POLL_INTERVAL_MS = 6e3;
|
|
51736
|
-
var DEFAULT_TIMEOUT_MS2 = 10 * 60 * 1e3;
|
|
51737
|
-
var SLOW_DOWN_STEP_MS = 5e3;
|
|
51738
|
-
var FlowCtx = class {
|
|
51739
|
-
fetchImpl;
|
|
51740
|
-
sleep;
|
|
51741
|
-
now;
|
|
51742
|
-
timeoutMs;
|
|
51743
|
-
intervalMs;
|
|
51744
|
-
cancelled = false;
|
|
51745
|
-
constructor(opts) {
|
|
51746
|
-
const f = opts.fetchImpl ?? globalThis.fetch;
|
|
51747
|
-
if (!f) throw new Error("fetch is not available (Node >= 18 required)");
|
|
51748
|
-
this.fetchImpl = f;
|
|
51749
|
-
this.sleep = opts.sleepImpl ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
51750
|
-
this.now = opts.nowImpl ?? Date.now;
|
|
51751
|
-
this.timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS2;
|
|
51752
|
-
this.intervalMs = opts.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
|
|
51753
|
-
}
|
|
51754
|
-
/**
|
|
51755
|
-
* RFC 8628 轮询循环:pollOnce 返回 null 表示 pending 继续;'slow_down' 加间隔;
|
|
51756
|
-
* 非 null 结果即完成。超时 / cancel reject。
|
|
51757
|
-
*/
|
|
51758
|
-
async poll(pollOnce) {
|
|
51759
|
-
const start = this.now();
|
|
51760
|
-
for (; ; ) {
|
|
51761
|
-
if (this.cancelled) throw new Error("flow cancelled");
|
|
51762
|
-
if (this.now() - start >= this.timeoutMs) throw new Error("flow timeout");
|
|
51763
|
-
const r = await pollOnce();
|
|
51764
|
-
if (r === "slow_down") {
|
|
51765
|
-
this.intervalMs += SLOW_DOWN_STEP_MS;
|
|
51766
|
-
} else if (r !== "pending") {
|
|
51767
|
-
return r;
|
|
51768
|
-
}
|
|
51769
|
-
await this.sleep(this.intervalMs);
|
|
51770
|
-
}
|
|
51771
|
-
}
|
|
51772
|
-
};
|
|
51773
|
-
var POST_TIMEOUT_MS = 15e3;
|
|
51774
|
-
async function postJson(ctx, url, body) {
|
|
51775
|
-
const ac = new AbortController();
|
|
51776
|
-
const timer = setTimeout(() => ac.abort(), POST_TIMEOUT_MS);
|
|
51777
|
-
let res;
|
|
51778
|
-
try {
|
|
51779
|
-
res = await ctx.fetchImpl(url, {
|
|
51780
|
-
method: "POST",
|
|
51781
|
-
headers: { "content-type": "application/json" },
|
|
51782
|
-
body: JSON.stringify(body),
|
|
51783
|
-
signal: ac.signal
|
|
51784
|
-
});
|
|
51785
|
-
} finally {
|
|
51786
|
-
clearTimeout(timer);
|
|
51787
|
-
}
|
|
51788
|
-
let parsed = {};
|
|
51789
|
-
try {
|
|
51790
|
-
parsed = await res.json();
|
|
51791
|
-
} catch {
|
|
51792
|
-
parsed = {};
|
|
51793
|
-
}
|
|
51794
|
-
return { status: res.status, json: parsed };
|
|
51795
|
-
}
|
|
51796
|
-
var TERMINAL_OAUTH_ERRORS = /* @__PURE__ */ new Set(["access_denied", "expired_token", "invalid_grant"]);
|
|
51797
|
-
function pollSignal(status, json, what) {
|
|
51798
|
-
if (status >= 200 && status < 300) return "done";
|
|
51799
|
-
const err = String(json.error ?? "");
|
|
51800
|
-
if (TERMINAL_OAUTH_ERRORS.has(err)) {
|
|
51801
|
-
throw new Error(`${what} rejected: ${err}`);
|
|
51802
|
-
}
|
|
51803
|
-
if (err === "authorization_pending" || status === 400) {
|
|
51804
|
-
if (err === "slow_down") return "slow_down";
|
|
51805
|
-
if (err === "authorization_pending") return "pending";
|
|
51806
|
-
return "pending";
|
|
51807
|
-
}
|
|
51808
|
-
if (status === 429) return "slow_down";
|
|
51809
|
-
throw new Error(`${what} failed: HTTP ${status} ${JSON.stringify(json).slice(0, 200)}`);
|
|
51810
|
-
}
|
|
51811
|
-
async function startAppRegistration(opts = {}) {
|
|
51812
|
-
const ctx = new FlowCtx(opts);
|
|
51813
|
-
const init = await postJson(ctx, REGISTRATION_URL, { action: "start", brand: "feishu" });
|
|
51814
|
-
const verificationUrl = String(init.json.verification_url ?? init.json.verification_uri ?? "");
|
|
51815
|
-
const flowId = String(init.json.flow_id ?? "");
|
|
51816
|
-
if (init.status < 200 || init.status >= 300 || !verificationUrl || !flowId) {
|
|
51817
|
-
throw new Error(
|
|
51818
|
-
`app registration start failed: HTTP ${init.status} ${JSON.stringify(init.json).slice(0, 200)}`
|
|
51819
|
-
);
|
|
51820
|
-
}
|
|
51821
|
-
return {
|
|
51822
|
-
verificationUrl,
|
|
51823
|
-
cancel: () => {
|
|
51824
|
-
ctx.cancelled = true;
|
|
51825
|
-
},
|
|
51826
|
-
complete: () => ctx.poll(async () => {
|
|
51827
|
-
const r = await postJson(ctx, REGISTRATION_URL, {
|
|
51828
|
-
action: "poll",
|
|
51829
|
-
brand: "feishu",
|
|
51830
|
-
flow_id: flowId
|
|
51831
|
-
});
|
|
51832
|
-
const sig = pollSignal(r.status, r.json, "app registration");
|
|
51833
|
-
if (sig !== "done") return sig;
|
|
51834
|
-
const appId = String(r.json.client_id ?? "");
|
|
51835
|
-
const appSecret = String(r.json.client_secret ?? "");
|
|
51836
|
-
if (!appId || !appSecret) {
|
|
51837
|
-
throw new Error("app registration succeeded but missing client_id or client_secret");
|
|
51838
|
-
}
|
|
51839
|
-
return { appId, appSecret };
|
|
51840
|
-
})
|
|
51841
|
-
};
|
|
51842
|
-
}
|
|
51843
|
-
async function startScopeAuthorization(args) {
|
|
51844
|
-
const ctx = new FlowCtx(args);
|
|
51845
|
-
const init = await postJson(ctx, DEVICE_AUTHORIZE_URL, {
|
|
51846
|
-
client_id: args.appId,
|
|
51847
|
-
scope: args.scopes.join(" ")
|
|
51848
|
-
});
|
|
51849
|
-
const verificationUrl = String(init.json.verification_url ?? init.json.verification_uri ?? "");
|
|
51850
|
-
const deviceCode = String(init.json.device_code ?? "");
|
|
51851
|
-
if (init.status < 200 || init.status >= 300 || !verificationUrl || !deviceCode) {
|
|
51852
|
-
throw new Error(
|
|
51853
|
-
`scope authorization start failed: HTTP ${init.status} ${JSON.stringify(init.json).slice(0, 200)}`
|
|
51854
|
-
);
|
|
51855
|
-
}
|
|
51856
|
-
return {
|
|
51857
|
-
verificationUrl,
|
|
51858
|
-
cancel: () => {
|
|
51859
|
-
ctx.cancelled = true;
|
|
51860
|
-
},
|
|
51861
|
-
complete: () => ctx.poll(async () => {
|
|
51862
|
-
const r = await postJson(ctx, DEVICE_TOKEN_URL, {
|
|
51863
|
-
grant_type: "urn:ietf:params:oauth:grant-type:device_code",
|
|
51864
|
-
client_id: args.appId,
|
|
51865
|
-
device_code: deviceCode
|
|
51866
|
-
});
|
|
51867
|
-
const sig = pollSignal(r.status, r.json, "scope authorization");
|
|
51868
|
-
if (sig !== "done") return sig;
|
|
51869
|
-
const grantedSet = new Set(
|
|
51870
|
-
String(r.json.scope ?? "").split(/\s+/).filter(Boolean)
|
|
51871
|
-
);
|
|
51872
|
-
const granted = args.scopes.filter((s) => grantedSet.has(s));
|
|
51873
|
-
const denied = args.scopes.filter((s) => !grantedSet.has(s));
|
|
51874
|
-
if (granted.length === 0) {
|
|
51875
|
-
throw new Error(`scope authorization: no scopes granted (requested: ${args.scopes.join(" ")})`);
|
|
51876
|
-
}
|
|
51877
|
-
return { granted: [...granted], denied: [...denied] };
|
|
51878
|
-
})
|
|
51879
|
-
};
|
|
51880
|
-
}
|
|
51881
|
-
|
|
51882
51743
|
// src/handlers/lark-bot.ts
|
|
51883
51744
|
init_protocol();
|
|
51884
51745
|
function statusFrame(personaId2, status) {
|
|
@@ -51886,7 +51747,6 @@ function statusFrame(personaId2, status) {
|
|
|
51886
51747
|
}
|
|
51887
51748
|
function buildLarkBotHandlers(deps) {
|
|
51888
51749
|
const now = deps.now ?? Date.now;
|
|
51889
|
-
const inFlight = /* @__PURE__ */ new Map();
|
|
51890
51750
|
const groupsOf = (personaId2) => {
|
|
51891
51751
|
const removed = new Set(deps.store.read(personaId2)?.removedChatIds ?? []);
|
|
51892
51752
|
return deps.listLarkSessions(personaId2).filter((g2) => !removed.has(g2.chatId));
|
|
@@ -51894,82 +51754,6 @@ function buildLarkBotHandlers(deps) {
|
|
|
51894
51754
|
const emit = (personaId2, status2) => {
|
|
51895
51755
|
deps.broadcast(statusFrame(personaId2, status2));
|
|
51896
51756
|
};
|
|
51897
|
-
const runProvision = async (personaId2, flight) => {
|
|
51898
|
-
const finish = () => {
|
|
51899
|
-
if (inFlight.get(personaId2) === flight) inFlight.delete(personaId2);
|
|
51900
|
-
};
|
|
51901
|
-
const tunnelUrl = deps.getTunnelUrl();
|
|
51902
|
-
const ownerId = deps.getOwnerId();
|
|
51903
|
-
if (!tunnelUrl || !ownerId) {
|
|
51904
|
-
finish();
|
|
51905
|
-
emit(personaId2, { state: "unbound", brokenReason: "tunnel or owner identity unavailable", groups: [] });
|
|
51906
|
-
return;
|
|
51907
|
-
}
|
|
51908
|
-
try {
|
|
51909
|
-
const reg = await deps.provisioner.startAppRegistration();
|
|
51910
|
-
if (flight.cancelled) return finish();
|
|
51911
|
-
flight.cancelFn = reg.cancel;
|
|
51912
|
-
emit(personaId2, {
|
|
51913
|
-
state: "provisioning",
|
|
51914
|
-
provisioning: { step: "register", verificationUrl: reg.verificationUrl },
|
|
51915
|
-
groups: []
|
|
51916
|
-
});
|
|
51917
|
-
const { appId, appSecret } = await reg.complete();
|
|
51918
|
-
if (flight.cancelled) return finish();
|
|
51919
|
-
const auth = await deps.provisioner.startScopeAuthorization({
|
|
51920
|
-
appId,
|
|
51921
|
-
scopes: LARK_BOT_V1_SCOPES
|
|
51922
|
-
});
|
|
51923
|
-
if (flight.cancelled) return finish();
|
|
51924
|
-
flight.cancelFn = auth.cancel;
|
|
51925
|
-
emit(personaId2, {
|
|
51926
|
-
state: "provisioning",
|
|
51927
|
-
provisioning: { step: "authorize", verificationUrl: auth.verificationUrl },
|
|
51928
|
-
groups: []
|
|
51929
|
-
});
|
|
51930
|
-
const { denied } = await auth.complete();
|
|
51931
|
-
if (flight.cancelled) return finish();
|
|
51932
|
-
if (denied.length > 0) {
|
|
51933
|
-
deps.logger?.warn(`larkBot provision ${personaId2}: scopes denied (accepted): ${denied.join(" ")}`);
|
|
51934
|
-
}
|
|
51935
|
-
const botName = deps.getPersonaName(personaId2) ?? personaId2;
|
|
51936
|
-
await deps.cloud.bind({ personaId: personaId2, appId, appSecret, botName, tunnelUrl, ownerId });
|
|
51937
|
-
deps.store.write(personaId2, { appId, botName, boundAt: now() });
|
|
51938
|
-
finish();
|
|
51939
|
-
emit(personaId2, { state: "bound", appId, botName, groups: groupsOf(personaId2) });
|
|
51940
|
-
} catch (err) {
|
|
51941
|
-
finish();
|
|
51942
|
-
if (flight.cancelled) return;
|
|
51943
|
-
emit(personaId2, {
|
|
51944
|
-
state: "unbound",
|
|
51945
|
-
brokenReason: err.message,
|
|
51946
|
-
groups: []
|
|
51947
|
-
});
|
|
51948
|
-
}
|
|
51949
|
-
};
|
|
51950
|
-
const provision = async (frame) => {
|
|
51951
|
-
const { personaId: personaId2 } = parseArgs2(LarkBotProvisionArgs, frame, "larkBot:provision");
|
|
51952
|
-
if (deps.store.read(personaId2)) throw new Error(`larkBot already bound: ${personaId2}`);
|
|
51953
|
-
if (inFlight.has(personaId2)) throw new Error(`larkBot provision already in progress: ${personaId2}`);
|
|
51954
|
-
if (!deps.getTunnelUrl()) throw new Error("larkBot provision requires tunnel to be up");
|
|
51955
|
-
if (!deps.getOwnerId()) throw new Error("larkBot provision requires feishu login (owner identity)");
|
|
51956
|
-
const flight = { cancelled: false, cancelFn: () => {
|
|
51957
|
-
} };
|
|
51958
|
-
inFlight.set(personaId2, flight);
|
|
51959
|
-
void runProvision(personaId2, flight);
|
|
51960
|
-
return { response: { type: "larkBot:provision:ok" } };
|
|
51961
|
-
};
|
|
51962
|
-
const provisionCancel = async (frame) => {
|
|
51963
|
-
const { personaId: personaId2 } = parseArgs2(LarkBotProvisionCancelArgs, frame, "larkBot:provisionCancel");
|
|
51964
|
-
const flight = inFlight.get(personaId2);
|
|
51965
|
-
if (flight) {
|
|
51966
|
-
flight.cancelled = true;
|
|
51967
|
-
inFlight.delete(personaId2);
|
|
51968
|
-
flight.cancelFn();
|
|
51969
|
-
}
|
|
51970
|
-
emit(personaId2, { state: "unbound", groups: [] });
|
|
51971
|
-
return { response: { type: "larkBot:provisionCancel:ok" } };
|
|
51972
|
-
};
|
|
51973
51757
|
const bindManual = async (frame) => {
|
|
51974
51758
|
const { personaId: personaId2, appId, appSecret, botName } = parseArgs2(
|
|
51975
51759
|
LarkBotBindManualArgs,
|
|
@@ -52025,8 +51809,6 @@ function buildLarkBotHandlers(deps) {
|
|
|
52025
51809
|
return { response: { type: "larkBot:status:ok", ...result } };
|
|
52026
51810
|
};
|
|
52027
51811
|
return {
|
|
52028
|
-
"larkBot:provision": provision,
|
|
52029
|
-
"larkBot:provisionCancel": provisionCancel,
|
|
52030
51812
|
"larkBot:bindManual": bindManual,
|
|
52031
51813
|
"larkBot:unbind": unbind,
|
|
52032
51814
|
"larkBot:status": status
|
|
@@ -57082,11 +56864,11 @@ var DeviceBindingClientError = class extends Error {
|
|
|
57082
56864
|
code;
|
|
57083
56865
|
cause;
|
|
57084
56866
|
};
|
|
57085
|
-
var
|
|
56867
|
+
var DEFAULT_TIMEOUT_MS2 = 8e3;
|
|
57086
56868
|
async function upsertDeviceBinding(opts) {
|
|
57087
56869
|
const doFetch = opts.fetchImpl ?? fetch;
|
|
57088
56870
|
const ac = new AbortController();
|
|
57089
|
-
const timer = setTimeout(() => ac.abort(), opts.timeoutMs ??
|
|
56871
|
+
const timer = setTimeout(() => ac.abort(), opts.timeoutMs ?? DEFAULT_TIMEOUT_MS2);
|
|
57090
56872
|
const body = { unionId: opts.unionId };
|
|
57091
56873
|
if (opts.avatarUrl != null) body.avatarUrl = opts.avatarUrl;
|
|
57092
56874
|
let res;
|
|
@@ -57279,8 +57061,9 @@ function forkSession(input) {
|
|
|
57279
57061
|
}
|
|
57280
57062
|
forkedLines.push(JSON.stringify(forked));
|
|
57281
57063
|
}
|
|
57282
|
-
const
|
|
57283
|
-
|
|
57064
|
+
const outDir = input.targetCwd ? import_node_path48.default.join(baseDir, "projects", cwdToHashDir(input.targetCwd)) : projectDir;
|
|
57065
|
+
const forkedFilePath = import_node_path48.default.join(outDir, `${forkedToolSessionId}.jsonl`);
|
|
57066
|
+
import_node_fs43.default.mkdirSync(outDir, { recursive: true });
|
|
57284
57067
|
import_node_fs43.default.writeFileSync(forkedFilePath, forkedLines.join("\n") + "\n", { mode: 384 });
|
|
57285
57068
|
return { forkedToolSessionId, forkedFilePath };
|
|
57286
57069
|
}
|
|
@@ -57505,9 +57288,25 @@ function buildSessionHandlers(deps) {
|
|
|
57505
57288
|
response: { type: "session:rewindable-message-ids", ...response }
|
|
57506
57289
|
};
|
|
57507
57290
|
};
|
|
57508
|
-
const fork = async (frame) => {
|
|
57291
|
+
const fork = async (frame, _client, ctx) => {
|
|
57509
57292
|
const args = SessionForkArgs.parse(frame);
|
|
57293
|
+
if (ctx && ctx.principal.kind === "guest") {
|
|
57294
|
+
const src = store.list().find((s) => s.toolSessionId === args.toolSessionId);
|
|
57295
|
+
const ok = src != null && canAccessPersona(ctx.grants, src.ownerPersonaId, "read") && src.creatorPrincipalId === ctx.principal.id;
|
|
57296
|
+
if (!ok) {
|
|
57297
|
+
throw new ClawdError(
|
|
57298
|
+
ERROR_CODES.UNAUTHORIZED,
|
|
57299
|
+
`principal guest:${ctx.principal.id} cannot fork tool session ${args.toolSessionId}`
|
|
57300
|
+
);
|
|
57301
|
+
}
|
|
57302
|
+
}
|
|
57510
57303
|
const result = forkSession(args);
|
|
57304
|
+
deps.logger?.info("session-fork.done", {
|
|
57305
|
+
argCwd: args.cwd,
|
|
57306
|
+
sourceToolSessionId: args.toolSessionId,
|
|
57307
|
+
forkedToolSessionId: result.forkedToolSessionId,
|
|
57308
|
+
forkedFilePath: result.forkedFilePath
|
|
57309
|
+
});
|
|
57511
57310
|
return { response: { type: "session:fork", ...result } };
|
|
57512
57311
|
};
|
|
57513
57312
|
const newSession = async (frame, _client, ctx) => {
|
|
@@ -57885,15 +57684,28 @@ function buildHistoryHandlers(deps) {
|
|
|
57885
57684
|
const messages = args.slim ? slimHistoryMessages(res.messages) : res.messages;
|
|
57886
57685
|
return { response: { type: "history:read", ...res, messages } };
|
|
57887
57686
|
};
|
|
57687
|
+
const assertGuestOwnsToolSession = (ctx, toolSessionId, method) => {
|
|
57688
|
+
if (!ctx || ctx.principal.kind !== "guest") return;
|
|
57689
|
+
const f = store.list().find((s) => s.toolSessionId === toolSessionId);
|
|
57690
|
+
const ok = f != null && canAccessPersona(ctx.grants, f.ownerPersonaId, "read") && f.creatorPrincipalId === ctx.principal.id;
|
|
57691
|
+
if (!ok) {
|
|
57692
|
+
throw new ClawdError(
|
|
57693
|
+
ERROR_CODES.UNAUTHORIZED,
|
|
57694
|
+
`principal guest:${ctx.principal.id} cannot ${method} of tool session ${toolSessionId}`
|
|
57695
|
+
);
|
|
57696
|
+
}
|
|
57697
|
+
};
|
|
57888
57698
|
const subagents = async (frame, _client, ctx) => {
|
|
57889
57699
|
const args = HistorySubagentsArgs.parse(frame);
|
|
57890
57700
|
assertGuestPath(ctx, path59.resolve(args.cwd), personaRoot, "history:subagents", usersRoot);
|
|
57701
|
+
assertGuestOwnsToolSession(ctx, args.toolSessionId, "history:subagents");
|
|
57891
57702
|
const subs = await history.listSubagents(args);
|
|
57892
57703
|
return { response: { type: "history:subagents", subagents: subs } };
|
|
57893
57704
|
};
|
|
57894
57705
|
const subagentRead = async (frame, _client, ctx) => {
|
|
57895
57706
|
const args = HistorySubagentReadArgs.parse(frame);
|
|
57896
57707
|
assertGuestPath(ctx, path59.resolve(args.cwd), personaRoot, "history:subagent-read", usersRoot);
|
|
57708
|
+
assertGuestOwnsToolSession(ctx, args.toolSessionId, "history:subagent-read");
|
|
57897
57709
|
const res = await history.readSubagent(args);
|
|
57898
57710
|
return { response: { type: "history:subagent-read", ...res } };
|
|
57899
57711
|
};
|
|
@@ -58827,9 +58639,7 @@ var METHOD_GRANT_MAP = {
|
|
|
58827
58639
|
"shift-internal:cancel": ADMIN_ANY,
|
|
58828
58640
|
"shift-internal:update": ADMIN_ANY,
|
|
58829
58641
|
"shift-internal:history": ADMIN_ANY,
|
|
58830
|
-
// ---- larkBot:* (LarkBotConnector
|
|
58831
|
-
"larkBot:provision": ADMIN_ANY,
|
|
58832
|
-
"larkBot:provisionCancel": ADMIN_ANY,
|
|
58642
|
+
// ---- larkBot:* (LarkBotConnector 绑定/状态,owner-only) ----
|
|
58833
58643
|
"larkBot:bindManual": ADMIN_ANY,
|
|
58834
58644
|
"larkBot:unbind": ADMIN_ANY,
|
|
58835
58645
|
"larkBot:status": ADMIN_ANY,
|
|
@@ -58964,7 +58774,7 @@ function computeMethodAccess(args) {
|
|
|
58964
58774
|
}
|
|
58965
58775
|
|
|
58966
58776
|
// src/version.ts
|
|
58967
|
-
var version = "0.2.
|
|
58777
|
+
var version = "0.2.288".length > 0 ? "0.2.288" : "dev";
|
|
58968
58778
|
|
|
58969
58779
|
// src/cli-probe/probe.ts
|
|
58970
58780
|
var fs55 = __toESM(require("fs"), 1);
|
|
@@ -61221,11 +61031,11 @@ init_protocol();
|
|
|
61221
61031
|
// src/peer-ops/run-command.ts
|
|
61222
61032
|
var import_node_child_process14 = require("child_process");
|
|
61223
61033
|
var import_node_os21 = __toESM(require("os"), 1);
|
|
61224
|
-
var
|
|
61034
|
+
var DEFAULT_TIMEOUT_MS3 = 3e4;
|
|
61225
61035
|
var MAX_TIMEOUT_MS = 3e5;
|
|
61226
61036
|
var MAX_OUTPUT_BYTES = 1048576;
|
|
61227
61037
|
function runCommand(command, opts = {}) {
|
|
61228
|
-
const timeoutMs = Math.min(opts.timeoutMs ??
|
|
61038
|
+
const timeoutMs = Math.min(opts.timeoutMs ?? DEFAULT_TIMEOUT_MS3, MAX_TIMEOUT_MS);
|
|
61229
61039
|
return new Promise((resolve6) => {
|
|
61230
61040
|
const child = (0, import_node_child_process14.spawn)("bash", ["-c", command], { cwd: import_node_os21.default.homedir() });
|
|
61231
61041
|
const out = [];
|
|
@@ -62988,7 +62798,6 @@ async function startDaemon(config) {
|
|
|
62988
62798
|
logger: { warn: (msg) => logger.warn(msg) }
|
|
62989
62799
|
});
|
|
62990
62800
|
const larkBotDeps = {
|
|
62991
|
-
provisioner: { startAppRegistration, startScopeAuthorization },
|
|
62992
62801
|
cloud: larkBotCloud,
|
|
62993
62802
|
store: larkBotStore,
|
|
62994
62803
|
broadcast: (frame) => wsServer?.broadcastToOwners(frame),
|
|
@@ -21791,10 +21791,9 @@ var ShiftInternalHistoryArgs = external_exports.object({
|
|
|
21791
21791
|
});
|
|
21792
21792
|
|
|
21793
21793
|
// ../protocol/src/lark-bot-schemas.ts
|
|
21794
|
-
var
|
|
21795
|
-
var
|
|
21796
|
-
var
|
|
21797
|
-
var LarkBotStatusArgs = LarkBotProvisionArgs;
|
|
21794
|
+
var LarkBotPersonaArgs = external_exports.object({ personaId: external_exports.string().min(1) });
|
|
21795
|
+
var LarkBotUnbindArgs = LarkBotPersonaArgs;
|
|
21796
|
+
var LarkBotStatusArgs = LarkBotPersonaArgs;
|
|
21798
21797
|
var LarkBotBindManualArgs = external_exports.object({
|
|
21799
21798
|
personaId: external_exports.string().min(1),
|
|
21800
21799
|
appId: external_exports.string().min(1),
|
|
@@ -21808,14 +21807,9 @@ var LarkBotGroupSchema = external_exports.object({
|
|
|
21808
21807
|
lastActiveAt: external_exports.number().int().optional()
|
|
21809
21808
|
});
|
|
21810
21809
|
var LarkBotStatusResultSchema = external_exports.object({
|
|
21811
|
-
state: external_exports.enum(["unbound", "
|
|
21810
|
+
state: external_exports.enum(["unbound", "bound", "broken"]),
|
|
21812
21811
|
appId: external_exports.string().optional(),
|
|
21813
21812
|
botName: external_exports.string().optional(),
|
|
21814
|
-
/** state='provisioning' 时的当前步骤与待用户打开的授权 URL */
|
|
21815
|
-
provisioning: external_exports.object({
|
|
21816
|
-
step: external_exports.enum(["register", "authorize"]),
|
|
21817
|
-
verificationUrl: external_exports.string().optional()
|
|
21818
|
-
}).optional(),
|
|
21819
21813
|
brokenReason: external_exports.string().optional(),
|
|
21820
21814
|
groups: external_exports.array(LarkBotGroupSchema)
|
|
21821
21815
|
});
|
|
@@ -22526,7 +22520,17 @@ var SessionResumeArgs = external_exports.object({
|
|
|
22526
22520
|
var SessionForkArgs = external_exports.object({
|
|
22527
22521
|
cwd: external_exports.string().min(1),
|
|
22528
22522
|
toolSessionId: external_exports.string().min(1),
|
|
22529
|
-
messageUuid: external_exports.string().min(1)
|
|
22523
|
+
messageUuid: external_exports.string().min(1),
|
|
22524
|
+
/**
|
|
22525
|
+
* 派生 jsonl 的落点 cwd(缺省 = 源文件所在目录,保持老行为)。
|
|
22526
|
+
*
|
|
22527
|
+
* 必须传的场景:调用方随后要用这条 transcript 建 session,而那个 session 的 cwd 与
|
|
22528
|
+
* 源 session 不同——最典型是 persona session(daemon 按 ownerPersonaId 把 cwd 强制
|
|
22529
|
+
* 派生成 persona 根)。CC `--resume` 只在**当前 cwd 的** projects 目录里找 session,
|
|
22530
|
+
* 落点与目标 cwd 分家 = 历史读不到 + resume 接不上(bug: quick-ask-blank-after-worktree)。
|
|
22531
|
+
* 正确编排:先 sessionCreate 拿到 daemon 定的真实 cwd,再以它作 targetCwd fork。
|
|
22532
|
+
*/
|
|
22533
|
+
targetCwd: external_exports.string().min(1).optional()
|
|
22530
22534
|
});
|
|
22531
22535
|
var SessionForkResponseSchema = external_exports.object({
|
|
22532
22536
|
forkedToolSessionId: external_exports.string().min(1),
|
|
@@ -23134,16 +23138,8 @@ var METHOD_DOCS = {
|
|
|
23134
23138
|
summary: "\u67E5 shift \u7684\u6267\u884C\u5386\u53F2\uFF08\u4EC5\u521B\u5EFA\u8005\uFF09",
|
|
23135
23139
|
args: ShiftInternalHistoryArgs
|
|
23136
23140
|
},
|
|
23137
|
-
"larkBot:provision": {
|
|
23138
|
-
summary: "\u53D1\u8D77 persona \u7684\u98DE\u4E66 bot \u4E00\u952E\u88C5\u914D\uFF08\u5EFA app + \u6388 scope\uFF09\uFF0C\u8FDB\u5EA6\u4E0E\u6388\u6743 URL \u8D70 larkBot:state \u5E27\uFF1Bowner-only",
|
|
23139
|
-
args: LarkBotProvisionArgs
|
|
23140
|
-
},
|
|
23141
|
-
"larkBot:provisionCancel": {
|
|
23142
|
-
summary: "\u53D6\u6D88\u8FDB\u884C\u4E2D\u7684\u98DE\u4E66 bot \u88C5\u914D\u6D41\u7A0B\uFF1Bowner-only",
|
|
23143
|
-
args: LarkBotProvisionCancelArgs
|
|
23144
|
-
},
|
|
23145
23141
|
"larkBot:bindManual": {
|
|
23146
|
-
summary: "\u624B\u586B app_id/app_secret \
|
|
23142
|
+
summary: "\u624B\u586B app_id/app_secret \u7ED1\u5B9A\u73B0\u6709\u98DE\u4E66 bot\uFF08\u552F\u4E00\u7ED1\u5B9A\u5165\u53E3\uFF09\uFF1Bowner-only",
|
|
23147
23143
|
args: LarkBotBindManualArgs
|
|
23148
23144
|
},
|
|
23149
23145
|
"larkBot:unbind": {
|
|
@@ -23151,7 +23147,7 @@ var METHOD_DOCS = {
|
|
|
23151
23147
|
args: LarkBotUnbindArgs
|
|
23152
23148
|
},
|
|
23153
23149
|
"larkBot:status": {
|
|
23154
|
-
summary: "\u67E5 persona \u7684\u98DE\u4E66 bot \u72B6\u6001\uFF08unbound/
|
|
23150
|
+
summary: "\u67E5 persona \u7684\u98DE\u4E66 bot \u72B6\u6001\uFF08unbound/bound/broken + \u670D\u52A1\u4E2D\u7FA4\u6E05\u5355\uFF09\uFF1Bowner-only",
|
|
23155
23151
|
args: LarkBotStatusArgs
|
|
23156
23152
|
},
|
|
23157
23153
|
"skills:list": {
|