@clawos-dev/clawd 0.2.221-beta.434.45d5a69 → 0.2.221
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 +320 -72
- package/dist/share-md-viewer-error.html +1 -1
- package/dist/share-md-viewer.html +1 -1
- package/dist/share-ui/assets/{guest-BA1GIgmA.js → guest-C8sBPMk6.js} +119 -119
- package/dist/share-ui/assets/guest-wEktK7-z.css +32 -0
- package/dist/share-ui/guest.html +2 -2
- package/dist/ticket/mcp-server.cjs +76 -33
- package/package.json +1 -1
- package/dist/share-ui/assets/guest-B5ChN9T3.css +0 -32
package/dist/cli.cjs
CHANGED
|
@@ -45,6 +45,9 @@ var init_methods = __esm({
|
|
|
45
45
|
"session:update",
|
|
46
46
|
"session:delete",
|
|
47
47
|
"session:send",
|
|
48
|
+
// per-turn sender 2026-07-08 spec §4.6:MCP servers (ticket / inbox ...) 走 HTTP RPC
|
|
49
|
+
// /api/rpc/session.currentTurnSender 拉当前 turn caller;WS handler 同名(loopback path 复用)
|
|
50
|
+
"session:currentTurnSender",
|
|
48
51
|
"session:stop",
|
|
49
52
|
// 温柔中断当前轮次,不杀进程(走 CC IPC control_request.subtype=interrupt)
|
|
50
53
|
"session:interrupt",
|
|
@@ -4520,6 +4523,59 @@ var init_attachment_schemas = __esm({
|
|
|
4520
4523
|
}
|
|
4521
4524
|
});
|
|
4522
4525
|
|
|
4526
|
+
// ../protocol/src/principal.ts
|
|
4527
|
+
function makeOwnerPrincipal(id, displayName, feishuUnionId) {
|
|
4528
|
+
return PrincipalSchema.parse({
|
|
4529
|
+
id,
|
|
4530
|
+
kind: "owner",
|
|
4531
|
+
displayName,
|
|
4532
|
+
...feishuUnionId ? { feishuUnionId } : {}
|
|
4533
|
+
});
|
|
4534
|
+
}
|
|
4535
|
+
function principalToSender(p2) {
|
|
4536
|
+
return {
|
|
4537
|
+
did: p2.id,
|
|
4538
|
+
displayName: p2.displayName,
|
|
4539
|
+
principal: p2.kind,
|
|
4540
|
+
...p2.feishuUnionId ? { feishuUnionId: p2.feishuUnionId } : {}
|
|
4541
|
+
};
|
|
4542
|
+
}
|
|
4543
|
+
function makeGuestSender(did, displayName, feishuUnionId) {
|
|
4544
|
+
return SenderInfoSchema.parse({
|
|
4545
|
+
did,
|
|
4546
|
+
displayName,
|
|
4547
|
+
principal: "guest",
|
|
4548
|
+
...feishuUnionId ? { feishuUnionId } : {}
|
|
4549
|
+
});
|
|
4550
|
+
}
|
|
4551
|
+
var PrincipalKindSchema, PrincipalSchema, SenderInfoSchema;
|
|
4552
|
+
var init_principal = __esm({
|
|
4553
|
+
"../protocol/src/principal.ts"() {
|
|
4554
|
+
"use strict";
|
|
4555
|
+
init_zod();
|
|
4556
|
+
PrincipalKindSchema = external_exports.enum(["owner", "guest"]);
|
|
4557
|
+
PrincipalSchema = external_exports.object({
|
|
4558
|
+
id: external_exports.string().min(1),
|
|
4559
|
+
kind: PrincipalKindSchema,
|
|
4560
|
+
displayName: external_exports.string(),
|
|
4561
|
+
/**
|
|
4562
|
+
* 飞书 union_id。owner: 本机 daemon 已飞书登录时透传自 owner-identity.json;未登录 undefined。
|
|
4563
|
+
* guest: v1 保持 undefined(connect token 当前不携带 unionId;未来若签名 payload 加字段可透传)。
|
|
4564
|
+
* 消费方(如 ticket MCP / 未来别的飞书身份挂钩场景)按 undefined 决定是否降级;本层不做 fallback。
|
|
4565
|
+
*/
|
|
4566
|
+
feishuUnionId: external_exports.string().optional()
|
|
4567
|
+
}).strict();
|
|
4568
|
+
SenderInfoSchema = external_exports.object({
|
|
4569
|
+
/** = principal.id = deviceId(决策 #16)。稳定路由 id */
|
|
4570
|
+
did: external_exports.string().min(1),
|
|
4571
|
+
displayName: external_exports.string(),
|
|
4572
|
+
/** owner 未登录 / guest 无飞书身份时缺省。消费方各自决定是否降级 */
|
|
4573
|
+
feishuUnionId: external_exports.string().optional(),
|
|
4574
|
+
principal: PrincipalKindSchema
|
|
4575
|
+
}).strict();
|
|
4576
|
+
}
|
|
4577
|
+
});
|
|
4578
|
+
|
|
4523
4579
|
// ../protocol/src/persona-schemas.ts
|
|
4524
4580
|
var PersonaFileSchema, PersonaSkillSummarySchema, PersonaPluginSummarySchema, PersonaSandboxSettingsSchema, PersonaInfoResponseSchema, PersonaCreateArgsSchema, PersonaIdArgsSchema, PersonaUpdateArgsSchema, CodexSandboxSettingsSchema;
|
|
4525
4581
|
var init_persona_schemas = __esm({
|
|
@@ -4628,6 +4684,7 @@ var init_schemas = __esm({
|
|
|
4628
4684
|
init_zod();
|
|
4629
4685
|
init_events();
|
|
4630
4686
|
init_attachment_schemas();
|
|
4687
|
+
init_principal();
|
|
4631
4688
|
init_persona_schemas();
|
|
4632
4689
|
SessionStatusSchema = external_exports.enum(SESSION_STATUS_VALUES);
|
|
4633
4690
|
UsageSchema = external_exports.object({
|
|
@@ -4894,13 +4951,18 @@ var init_schemas = __esm({
|
|
|
4894
4951
|
// 真实用户输入:daemon 通过 --replay-user-messages 拿到 stdin 回显,区分于 assistant text。
|
|
4895
4952
|
// meta 非空表示是 CC 注入的系统消息(task-notification / slash-command / system-reminder 等),
|
|
4896
4953
|
// UI 据此走低视觉权重渲染或子 agent 通知卡。
|
|
4954
|
+
//
|
|
4955
|
+
// sender(2026-07-08 spec §4.1):daemon 'send' 命令 synth 的 user_text 携带发起人 SenderInfo;
|
|
4956
|
+
// observer 回灌路径(cc jsonl)永远不带 —— cc 自己写盘不知道 daemon 层身份。
|
|
4957
|
+
// UI 端 sender 缺省 → 不渲染 displayName label;有 → 气泡下方渲染。
|
|
4897
4958
|
external_exports.object({
|
|
4898
4959
|
...ParsedEventBase,
|
|
4899
4960
|
kind: external_exports.literal("user_text"),
|
|
4900
4961
|
text: external_exports.string(),
|
|
4901
4962
|
meta: HistoryUserMetaSchema.optional(),
|
|
4902
4963
|
taskId: external_exports.string().optional(),
|
|
4903
|
-
parentToolUseId: external_exports.string().optional()
|
|
4964
|
+
parentToolUseId: external_exports.string().optional(),
|
|
4965
|
+
sender: SenderInfoSchema.optional()
|
|
4904
4966
|
}),
|
|
4905
4967
|
external_exports.object({
|
|
4906
4968
|
...ParsedEventBase,
|
|
@@ -5509,24 +5571,6 @@ var init_persona_mode = __esm({
|
|
|
5509
5571
|
}
|
|
5510
5572
|
});
|
|
5511
5573
|
|
|
5512
|
-
// ../protocol/src/principal.ts
|
|
5513
|
-
function makeOwnerPrincipal(id, displayName) {
|
|
5514
|
-
return PrincipalSchema.parse({ id, kind: "owner", displayName });
|
|
5515
|
-
}
|
|
5516
|
-
var PrincipalKindSchema, PrincipalSchema;
|
|
5517
|
-
var init_principal = __esm({
|
|
5518
|
-
"../protocol/src/principal.ts"() {
|
|
5519
|
-
"use strict";
|
|
5520
|
-
init_zod();
|
|
5521
|
-
PrincipalKindSchema = external_exports.enum(["owner", "guest"]);
|
|
5522
|
-
PrincipalSchema = external_exports.object({
|
|
5523
|
-
id: external_exports.string().min(1),
|
|
5524
|
-
kind: PrincipalKindSchema,
|
|
5525
|
-
displayName: external_exports.string()
|
|
5526
|
-
}).strict();
|
|
5527
|
-
}
|
|
5528
|
-
});
|
|
5529
|
-
|
|
5530
5574
|
// ../protocol/src/capability.ts
|
|
5531
5575
|
function stripSecretHash(cap) {
|
|
5532
5576
|
const { secretHash: _hash, ...wire } = cap;
|
|
@@ -41651,6 +41695,47 @@ function escapeAttr(v2) {
|
|
|
41651
41695
|
return v2.replace(/&/g, "&").replace(/"/g, """).replace(/</g, "<").replace(/>/g, ">");
|
|
41652
41696
|
}
|
|
41653
41697
|
|
|
41698
|
+
// src/shift/end-frame-handler.ts
|
|
41699
|
+
function makeEndFrameHandler(sessionId, onEnd) {
|
|
41700
|
+
return (frame) => {
|
|
41701
|
+
if (!frame || typeof frame !== "object") return;
|
|
41702
|
+
const f = frame;
|
|
41703
|
+
if (f.type !== "session:status") return;
|
|
41704
|
+
if (f.sessionId !== sessionId) return;
|
|
41705
|
+
if (f.status === "running-idle") {
|
|
41706
|
+
onEnd({ kind: "idle" });
|
|
41707
|
+
return;
|
|
41708
|
+
}
|
|
41709
|
+
if (f.status === "stopped") {
|
|
41710
|
+
onEnd({ kind: "stopped" });
|
|
41711
|
+
return;
|
|
41712
|
+
}
|
|
41713
|
+
if (f.status === "error") {
|
|
41714
|
+
onEnd({ kind: "error", message: f.message ?? "cc proc error" });
|
|
41715
|
+
return;
|
|
41716
|
+
}
|
|
41717
|
+
};
|
|
41718
|
+
}
|
|
41719
|
+
|
|
41720
|
+
// src/shift/end-dispatcher.ts
|
|
41721
|
+
function dispatchShiftEnd(info, settle, killImpl) {
|
|
41722
|
+
if (info.kind === "idle") {
|
|
41723
|
+
settle({ ok: true });
|
|
41724
|
+
killImpl();
|
|
41725
|
+
return;
|
|
41726
|
+
}
|
|
41727
|
+
if (info.kind === "stopped") {
|
|
41728
|
+
settle({ error: "cc exited before turn completed" });
|
|
41729
|
+
return;
|
|
41730
|
+
}
|
|
41731
|
+
if (info.kind === "error") {
|
|
41732
|
+
settle({ error: info.message });
|
|
41733
|
+
return;
|
|
41734
|
+
}
|
|
41735
|
+
const _exhaustive = info;
|
|
41736
|
+
throw new Error(`dispatchShiftEnd: unhandled kind ${JSON.stringify(_exhaustive)}`);
|
|
41737
|
+
}
|
|
41738
|
+
|
|
41654
41739
|
// src/session/runner.ts
|
|
41655
41740
|
var import_node_os4 = __toESM(require("os"), 1);
|
|
41656
41741
|
var import_node_path7 = __toESM(require("path"), 1);
|
|
@@ -41817,7 +41902,9 @@ function cloneState(s) {
|
|
|
41817
41902
|
// ReadyGate v2:readyForSend 是基础类型直接复制;pendingSend 必须浅拷贝防止
|
|
41818
41903
|
// reduce 内 push/shift 在调用方原 state 上副作用(reducer 纯函数契约)
|
|
41819
41904
|
readyForSend: s.readyForSend,
|
|
41820
|
-
pendingSend: s.pendingSend.slice()
|
|
41905
|
+
pendingSend: s.pendingSend.slice(),
|
|
41906
|
+
// per-turn sender (spec §4.3):SenderInfo 只在 case 'send' 覆盖,其它 kind 直接复用引用
|
|
41907
|
+
currentTurnSender: s.currentTurnSender
|
|
41821
41908
|
};
|
|
41822
41909
|
}
|
|
41823
41910
|
var IDLE_KILL_DELAY_MS = 6e5;
|
|
@@ -41882,8 +41969,7 @@ function buildSpawnContext(state, deps) {
|
|
|
41882
41969
|
const personaId = file.ownerPersonaId;
|
|
41883
41970
|
if (personaId) env.CLAWD_PERSONA_ID = personaId;
|
|
41884
41971
|
const dispatchMcpConfigPath2 = deps.getDispatchMcpConfigPath?.() ?? null;
|
|
41885
|
-
const
|
|
41886
|
-
const ticketMcpConfigPath2 = ticketMcpConfigPathRaw && file.ownerPersonaId === "persona-ticket-manager" ? ticketMcpConfigPathRaw : null;
|
|
41972
|
+
const ticketMcpConfigPath2 = deps.getTicketMcpConfigPath?.() ?? null;
|
|
41887
41973
|
const shiftMcpConfigPath2 = deps.getShiftMcpConfigPath?.() ?? null;
|
|
41888
41974
|
const inboxMcpConfigPath2 = deps.getInboxMcpConfigPath?.() ?? null;
|
|
41889
41975
|
const ctx = {
|
|
@@ -42157,10 +42243,12 @@ function applyCommand(state, command, deps) {
|
|
|
42157
42243
|
}
|
|
42158
42244
|
});
|
|
42159
42245
|
}
|
|
42246
|
+
next.currentTurnSender = command.senderPrincipal;
|
|
42160
42247
|
const userEvent = {
|
|
42161
42248
|
kind: "user_text",
|
|
42162
42249
|
text: command.text,
|
|
42163
|
-
uuid: deps.genUuid?.()
|
|
42250
|
+
uuid: deps.genUuid?.(),
|
|
42251
|
+
sender: command.senderPrincipal
|
|
42164
42252
|
};
|
|
42165
42253
|
const pushed = pushEventToBuffer(next, userEvent, deps);
|
|
42166
42254
|
effects.push(...pushed.effects);
|
|
@@ -42708,7 +42796,7 @@ var SessionRunner = class {
|
|
|
42708
42796
|
getDispatchMcpConfigPath: this.hooks.getDispatchMcpConfigPath,
|
|
42709
42797
|
getShiftMcpConfigPath: this.hooks.getShiftMcpConfigPath,
|
|
42710
42798
|
getInboxMcpConfigPath: this.hooks.getInboxMcpConfigPath,
|
|
42711
|
-
// Ticket MCP:透传 ticket.mcp.json
|
|
42799
|
+
// Ticket MCP:透传 ticket.mcp.json 路径闭包(所有 persona session 都挂,无 gating)
|
|
42712
42800
|
getTicketMcpConfigPath: this.hooks.getTicketMcpConfigPath,
|
|
42713
42801
|
lookupDispatchByBSessionId: this.hooks.lookupDispatchByBSessionId,
|
|
42714
42802
|
// ReadyGate v2:透传 mode 让 reducer send / ready-detected 分支决定走暂存队列还是直写
|
|
@@ -43227,7 +43315,9 @@ function makeInitialState(file, subSessionMeta) {
|
|
|
43227
43315
|
// ReadyGate v2:默认值兼容 SDK happy path(直写 stdin,不走队列)。
|
|
43228
43316
|
// TUI 模式由 reducer send 分支按 deps.mode='tui' 主动翻 readyForSend=false
|
|
43229
43317
|
readyForSend: true,
|
|
43230
|
-
pendingSend: []
|
|
43318
|
+
pendingSend: [],
|
|
43319
|
+
// per-turn sender 2026-07-08 spec §4.3:初始 null;case 'send' 首次覆盖
|
|
43320
|
+
currentTurnSender: null
|
|
43231
43321
|
};
|
|
43232
43322
|
}
|
|
43233
43323
|
var SessionManager = class {
|
|
@@ -43900,7 +43990,7 @@ var SessionManager = class {
|
|
|
43900
43990
|
]
|
|
43901
43991
|
};
|
|
43902
43992
|
}
|
|
43903
|
-
send(args) {
|
|
43993
|
+
send(args, senderPrincipal) {
|
|
43904
43994
|
const existing = this.getFile(args.sessionId);
|
|
43905
43995
|
let runner = this.runners.get(args.sessionId);
|
|
43906
43996
|
if (!runner) {
|
|
@@ -43908,10 +43998,22 @@ var SessionManager = class {
|
|
|
43908
43998
|
this.runners.set(args.sessionId, runner);
|
|
43909
43999
|
}
|
|
43910
44000
|
const { broadcast } = this.withCollector(() => {
|
|
43911
|
-
runner.input({
|
|
44001
|
+
runner.input({
|
|
44002
|
+
kind: "command",
|
|
44003
|
+
command: { kind: "send", text: args.text, senderPrincipal }
|
|
44004
|
+
});
|
|
43912
44005
|
});
|
|
43913
44006
|
return { response: { ok: true }, broadcast };
|
|
43914
44007
|
}
|
|
44008
|
+
/**
|
|
44009
|
+
* 只读拿 SessionState;session 不存在返 null(不 throw)。
|
|
44010
|
+
* handlers/session.ts:currentTurnSender 用(loopback HTTP RPC 绝对不能因不存在的 sid 崩)。
|
|
44011
|
+
* 2026-07-08 spec §4.6。
|
|
44012
|
+
*/
|
|
44013
|
+
tryGetState(sessionId) {
|
|
44014
|
+
const runner = this.runners.get(sessionId);
|
|
44015
|
+
return runner?.getState() ?? null;
|
|
44016
|
+
}
|
|
43915
44017
|
// 等子进程真退出再 ack。reducer 推 SIGKILL effect(见 reducer.ts),内核立即回收子进程,
|
|
43916
44018
|
// proc.on('exit') 几乎瞬时触发;waitUntilStopped 的 3s 超时只在子进程已是 zombie 或
|
|
43917
44019
|
// 极端 OS 异常时才会用上。这样修掉了"前端 sessionStop 还没等 CC 退就去删 worktree
|
|
@@ -44311,7 +44413,10 @@ var SessionManager = class {
|
|
|
44311
44413
|
}
|
|
44312
44414
|
const runner = this.ensureRunnerForScope(file, args.scope);
|
|
44313
44415
|
const { broadcast } = this.withCollector(() => {
|
|
44314
|
-
runner.input({
|
|
44416
|
+
runner.input({
|
|
44417
|
+
kind: "command",
|
|
44418
|
+
command: { kind: "send", text: args.text, senderPrincipal: args.senderPrincipal }
|
|
44419
|
+
});
|
|
44315
44420
|
});
|
|
44316
44421
|
return { response: { ok: true }, broadcast };
|
|
44317
44422
|
}
|
|
@@ -44455,7 +44560,27 @@ var SessionManager = class {
|
|
|
44455
44560
|
sourceJsonlPath: args.sourceJsonlPath,
|
|
44456
44561
|
dispatchId: args.dispatchId
|
|
44457
44562
|
});
|
|
44458
|
-
|
|
44563
|
+
let dispatchSender;
|
|
44564
|
+
if (args.guestPrincipalId) {
|
|
44565
|
+
dispatchSender = makeGuestSender(args.guestPrincipalId, args.guestDisplayName ?? args.guestPrincipalId);
|
|
44566
|
+
} else {
|
|
44567
|
+
if (!this.deps.ownerPrincipalId) {
|
|
44568
|
+
throw new Error(
|
|
44569
|
+
"createDispatchedSession requires ManagerDeps.ownerPrincipalId to build owner sender"
|
|
44570
|
+
);
|
|
44571
|
+
}
|
|
44572
|
+
dispatchSender = principalToSender(
|
|
44573
|
+
makeOwnerPrincipal(
|
|
44574
|
+
this.deps.ownerPrincipalId,
|
|
44575
|
+
this.deps.ownerDisplayName ?? "Owner",
|
|
44576
|
+
this.deps.ownerFeishuUnionId
|
|
44577
|
+
)
|
|
44578
|
+
);
|
|
44579
|
+
}
|
|
44580
|
+
runner.input({
|
|
44581
|
+
kind: "command",
|
|
44582
|
+
command: { kind: "send", text: taskPack, senderPrincipal: dispatchSender }
|
|
44583
|
+
});
|
|
44459
44584
|
this.deps.logger?.info("dispatch.createDispatchedSession.task-pack-sent", {
|
|
44460
44585
|
dispatchId: args.dispatchId,
|
|
44461
44586
|
bSessionId: sessionId
|
|
@@ -44470,8 +44595,9 @@ var SessionManager = class {
|
|
|
44470
44595
|
* 跟 createDispatchedSession 的差异:
|
|
44471
44596
|
* - 不带 dispatchId / source / task pack;prompt 直接当第一条 user message
|
|
44472
44597
|
* - 不调 personaDispatchManager.registerBSession
|
|
44473
|
-
* - 多返一个 ended Promise,监听 session:status
|
|
44474
|
-
*
|
|
44598
|
+
* - 多返一个 ended Promise,监听 session:status 帧翻译成 shift 业务终态 union
|
|
44599
|
+
* (ShiftSessionEnded:{ok:true} | {error:string}),不经 exit code 中转
|
|
44600
|
+
* - turn-idle 到达即视为业务完成 → settle {ok:true} + fire-and-forget kill cc
|
|
44475
44601
|
* - kill 通过 runner.input({command:{kind:'stop'}}) 触发 SIGKILL → proc-exit
|
|
44476
44602
|
*/
|
|
44477
44603
|
createShiftSession(args) {
|
|
@@ -44515,18 +44641,32 @@ var SessionManager = class {
|
|
|
44515
44641
|
unsubscribe();
|
|
44516
44642
|
resolveEnded(info);
|
|
44517
44643
|
};
|
|
44518
|
-
|
|
44519
|
-
|
|
44520
|
-
|
|
44521
|
-
|
|
44522
|
-
|
|
44523
|
-
|
|
44524
|
-
|
|
44525
|
-
|
|
44526
|
-
|
|
44527
|
-
|
|
44644
|
+
const killImpl = () => {
|
|
44645
|
+
const r = this.runners.get(sessionId);
|
|
44646
|
+
if (!r) return;
|
|
44647
|
+
r.input({ kind: "command", command: { kind: "stop" } });
|
|
44648
|
+
};
|
|
44649
|
+
unsubscribe = this.subscribe(
|
|
44650
|
+
sessionId,
|
|
44651
|
+
scope,
|
|
44652
|
+
makeEndFrameHandler(sessionId, (info) => {
|
|
44653
|
+
dispatchShiftEnd(info, settle, killImpl);
|
|
44654
|
+
})
|
|
44655
|
+
);
|
|
44528
44656
|
const runner = this.ensureRunnerForScope(file, scope);
|
|
44529
44657
|
this.deps.logger?.info("shift.createShiftSession.runner-ready", { sessionId });
|
|
44658
|
+
if (!this.deps.ownerPrincipalId) {
|
|
44659
|
+
throw new Error(
|
|
44660
|
+
"createShiftSession requires ManagerDeps.ownerPrincipalId to build owner sender"
|
|
44661
|
+
);
|
|
44662
|
+
}
|
|
44663
|
+
const shiftSender = principalToSender(
|
|
44664
|
+
makeOwnerPrincipal(
|
|
44665
|
+
this.deps.ownerPrincipalId,
|
|
44666
|
+
this.deps.ownerDisplayName ?? "Owner",
|
|
44667
|
+
this.deps.ownerFeishuUnionId
|
|
44668
|
+
)
|
|
44669
|
+
);
|
|
44530
44670
|
runner.input({
|
|
44531
44671
|
kind: "command",
|
|
44532
44672
|
command: {
|
|
@@ -44534,15 +44674,11 @@ var SessionManager = class {
|
|
|
44534
44674
|
text: wrapShiftFirePrompt({
|
|
44535
44675
|
prompt: args.prompt,
|
|
44536
44676
|
targetPersona: args.targetPersona
|
|
44537
|
-
})
|
|
44677
|
+
}),
|
|
44678
|
+
senderPrincipal: shiftSender
|
|
44538
44679
|
}
|
|
44539
44680
|
});
|
|
44540
|
-
|
|
44541
|
-
const r = this.runners.get(sessionId);
|
|
44542
|
-
if (!r) return;
|
|
44543
|
-
r.input({ kind: "command", command: { kind: "stop" } });
|
|
44544
|
-
};
|
|
44545
|
-
return { sessionId, ended, kill };
|
|
44681
|
+
return { sessionId, ended, kill: killImpl };
|
|
44546
44682
|
}
|
|
44547
44683
|
/**
|
|
44548
44684
|
* 老板插话:把 'inject-owner-text' input 喂给对应 runner,
|
|
@@ -48052,7 +48188,7 @@ function ticketMcpConfigPath(dataDir) {
|
|
|
48052
48188
|
function writeTicketMcpConfig(args) {
|
|
48053
48189
|
const cfgPath = ticketMcpConfigPath(args.dataDir);
|
|
48054
48190
|
const content = {
|
|
48055
|
-
_comment: "daemon \u542F\u52A8\u65F6\u81EA\u52A8\u751F\u6210\uFF1Bcc spawn \u901A\u8FC7 --mcp-config \u6CE8\u5165\u3002ticket-mcp-server.cjs \u662F daemon \u81EA\u5E26\u7684 stdio MCP server\uFF0CHTTP client \u8C03 clawos api /api/tickets/*\u3002env \u542B api url\u3001\u5199\u6B7B API key\
|
|
48191
|
+
_comment: "daemon \u542F\u52A8\u65F6\u81EA\u52A8\u751F\u6210\uFF1Bcc spawn \u901A\u8FC7 --mcp-config \u6CE8\u5165\u3002ticket-mcp-server.cjs \u662F daemon \u81EA\u5E26\u7684 stdio MCP server\uFF0CHTTP client \u8C03 clawos api /api/tickets/*\u3002env \u542B api url\u3001\u5199\u6B7B API key\u3001scene\uFF1Bdid/name \u6BCF\u6B21 tool call \u8D70 CLAWD_DAEMON_URL + CLAWD_SESSION_ID (cc \u5DF2\u6CE8) fetch /api/rpc/session.currentTurnSender \u62FF\u5F53\u524D turn caller (2026-07-08 spec \xA74.7)\u3002CLAWD_TICKET_LOG \u6307 server \u5199\u65E5\u5FD7\u7684 append \u6587\u4EF6\u8DEF\u5F84\u3002cc \u573A\u666F scene \u56FA\u5B9A persona\u3002",
|
|
48056
48192
|
mcpServers: {
|
|
48057
48193
|
"clawd-ticket": {
|
|
48058
48194
|
type: "stdio",
|
|
@@ -48061,8 +48197,6 @@ function writeTicketMcpConfig(args) {
|
|
|
48061
48197
|
env: {
|
|
48062
48198
|
CLAWD_TICKET_API_URL: args.apiBaseUrl,
|
|
48063
48199
|
CLAWD_TICKET_API_KEY: args.apiKey,
|
|
48064
|
-
CLAWD_TICKET_DID: args.did,
|
|
48065
|
-
CLAWD_TICKET_NAME: args.name,
|
|
48066
48200
|
CLAWD_TICKET_SCENE: "persona",
|
|
48067
48201
|
...args.logPath ? { CLAWD_TICKET_LOG: args.logPath } : {}
|
|
48068
48202
|
}
|
|
@@ -48447,15 +48581,11 @@ function createShiftSpawnFn(deps) {
|
|
|
48447
48581
|
void created.ended.then((info) => {
|
|
48448
48582
|
clearTimeout(timer);
|
|
48449
48583
|
if (settled) return;
|
|
48450
|
-
if (info
|
|
48451
|
-
settle({ kind: "error", error: info.error });
|
|
48452
|
-
return;
|
|
48453
|
-
}
|
|
48454
|
-
if (info.exitCode === 0) {
|
|
48584
|
+
if ("ok" in info) {
|
|
48455
48585
|
settle({ kind: "ok" });
|
|
48456
48586
|
return;
|
|
48457
48587
|
}
|
|
48458
|
-
settle({ kind: "error", error:
|
|
48588
|
+
settle({ kind: "error", error: info.error });
|
|
48459
48589
|
});
|
|
48460
48590
|
return { sessionId: created.sessionId, ended };
|
|
48461
48591
|
};
|
|
@@ -50177,7 +50307,12 @@ var EXPOSED_RPC_METHODS = [
|
|
|
50177
50307
|
"attachment.groupAdd",
|
|
50178
50308
|
"attachment.signUrl",
|
|
50179
50309
|
// persona 受控发 P2P IM DM(sender 由 session scope 推,driver 分级授权见 handlers/inbox.ts)
|
|
50180
|
-
"inbox.sendDm"
|
|
50310
|
+
"inbox.sendDm",
|
|
50311
|
+
// per-turn sender 2026-07-08 spec §4.1:MCP servers (ticket / inbox / ...) 每 tool call
|
|
50312
|
+
// 查当前 turn caller —— cc 长驻 + owner/guest 交替驱动同一 session 时不能靠启动时 env
|
|
50313
|
+
// 写死身份,必须运行时 fetch。header X-Clawd-Session-Id 作 session 定位,handler 内查
|
|
50314
|
+
// manager.tryGetState(sid).currentTurnSender 返 SenderInfo | null。
|
|
50315
|
+
"session.currentTurnSender"
|
|
50181
50316
|
];
|
|
50182
50317
|
async function handleRpcRequest(input) {
|
|
50183
50318
|
const { method, body, sessionId, dispatcher } = input;
|
|
@@ -50362,9 +50497,9 @@ function constantTimeEqual2(a, b2) {
|
|
|
50362
50497
|
}
|
|
50363
50498
|
|
|
50364
50499
|
// src/transport/connection-context.ts
|
|
50365
|
-
function ownerContext(ownerPrincipalId, displayName) {
|
|
50500
|
+
function ownerContext(ownerPrincipalId, displayName, feishuUnionId) {
|
|
50366
50501
|
return {
|
|
50367
|
-
principal: makeOwnerPrincipal(ownerPrincipalId, displayName),
|
|
50502
|
+
principal: makeOwnerPrincipal(ownerPrincipalId, displayName, feishuUnionId),
|
|
50368
50503
|
grants: [{ resource: { type: "*" }, actions: ["admin"] }]
|
|
50369
50504
|
};
|
|
50370
50505
|
}
|
|
@@ -50393,7 +50528,10 @@ function visitorGuestContext(p2) {
|
|
|
50393
50528
|
async function authenticate(token, deps) {
|
|
50394
50529
|
if (!token) return { ok: false, code: "NO_TOKEN" };
|
|
50395
50530
|
if (deps.isOwnerToken(token)) {
|
|
50396
|
-
return {
|
|
50531
|
+
return {
|
|
50532
|
+
ok: true,
|
|
50533
|
+
context: ownerContext(deps.ownerPrincipalId, deps.ownerDisplayName, deps.ownerFeishuUnionId)
|
|
50534
|
+
};
|
|
50397
50535
|
}
|
|
50398
50536
|
if (deps.verifyVisitorToken) {
|
|
50399
50537
|
const vr = deps.verifyVisitorToken(token);
|
|
@@ -54192,6 +54330,22 @@ var LoginFlow = class {
|
|
|
54192
54330
|
} catch (err) {
|
|
54193
54331
|
return { ok: false, reason: `user_info failed: ${err.message}` };
|
|
54194
54332
|
}
|
|
54333
|
+
if (!identity.unionId) {
|
|
54334
|
+
return { ok: false, reason: "union_id missing from TTC user_info" };
|
|
54335
|
+
}
|
|
54336
|
+
try {
|
|
54337
|
+
await this.deps.upsertBinding({
|
|
54338
|
+
deviceId: this.deps.getDeviceId(),
|
|
54339
|
+
unionId: identity.unionId,
|
|
54340
|
+
displayName: identity.displayName,
|
|
54341
|
+
avatarUrl: identity.avatarUrl ?? null
|
|
54342
|
+
});
|
|
54343
|
+
} catch (err) {
|
|
54344
|
+
return {
|
|
54345
|
+
ok: false,
|
|
54346
|
+
reason: `device_binding upsert failed: ${err.message}`
|
|
54347
|
+
};
|
|
54348
|
+
}
|
|
54195
54349
|
const expiresAtNum = params.expiresAt ? Number.parseInt(params.expiresAt, 10) : NaN;
|
|
54196
54350
|
this.deps.store.write({
|
|
54197
54351
|
identity,
|
|
@@ -54360,6 +54514,58 @@ async function centralListDevices(opts) {
|
|
|
54360
54514
|
return out;
|
|
54361
54515
|
}
|
|
54362
54516
|
|
|
54517
|
+
// src/feishu-auth/device-binding-client.ts
|
|
54518
|
+
var DeviceBindingClientError = class extends Error {
|
|
54519
|
+
constructor(code, message, cause) {
|
|
54520
|
+
super(message);
|
|
54521
|
+
this.code = code;
|
|
54522
|
+
this.cause = cause;
|
|
54523
|
+
this.name = "DeviceBindingClientError";
|
|
54524
|
+
}
|
|
54525
|
+
code;
|
|
54526
|
+
cause;
|
|
54527
|
+
};
|
|
54528
|
+
var DEFAULT_TIMEOUT_MS4 = 8e3;
|
|
54529
|
+
async function upsertDeviceBinding(opts) {
|
|
54530
|
+
const doFetch = opts.fetchImpl ?? fetch;
|
|
54531
|
+
const ac = new AbortController();
|
|
54532
|
+
const timer = setTimeout(() => ac.abort(), opts.timeoutMs ?? DEFAULT_TIMEOUT_MS4);
|
|
54533
|
+
const body = { unionId: opts.unionId };
|
|
54534
|
+
if (opts.avatarUrl != null) body.avatarUrl = opts.avatarUrl;
|
|
54535
|
+
let res;
|
|
54536
|
+
try {
|
|
54537
|
+
res = await doFetch(`${opts.apiUrl}/api/device-bindings/self`, {
|
|
54538
|
+
method: "PUT",
|
|
54539
|
+
signal: ac.signal,
|
|
54540
|
+
headers: {
|
|
54541
|
+
authorization: `Bearer ${opts.apiKey}`,
|
|
54542
|
+
"x-did": encodeURIComponent(opts.deviceId),
|
|
54543
|
+
"x-name": encodeURIComponent(opts.displayName),
|
|
54544
|
+
"x-scene": "daemon",
|
|
54545
|
+
"content-type": "application/json"
|
|
54546
|
+
},
|
|
54547
|
+
body: JSON.stringify(body)
|
|
54548
|
+
});
|
|
54549
|
+
} catch (err) {
|
|
54550
|
+
clearTimeout(timer);
|
|
54551
|
+
throw new DeviceBindingClientError(
|
|
54552
|
+
"NETWORK",
|
|
54553
|
+
`network error: ${err.message}`,
|
|
54554
|
+
err
|
|
54555
|
+
);
|
|
54556
|
+
} finally {
|
|
54557
|
+
clearTimeout(timer);
|
|
54558
|
+
}
|
|
54559
|
+
if (!res.ok) {
|
|
54560
|
+
let detail = "";
|
|
54561
|
+
try {
|
|
54562
|
+
detail = JSON.stringify(await res.json());
|
|
54563
|
+
} catch {
|
|
54564
|
+
}
|
|
54565
|
+
throw new DeviceBindingClientError("API_ERROR", `HTTP ${res.status}: ${detail}`);
|
|
54566
|
+
}
|
|
54567
|
+
}
|
|
54568
|
+
|
|
54363
54569
|
// src/feishu-auth/verify-token.ts
|
|
54364
54570
|
var crypto14 = __toESM(require("crypto"), 1);
|
|
54365
54571
|
var CONNECT_TOKEN_PREFIX = "clawdtk1";
|
|
@@ -54618,9 +54824,25 @@ function buildSessionHandlers(deps) {
|
|
|
54618
54824
|
const send = async (frame, _client, ctx) => {
|
|
54619
54825
|
const args = SessionSendArgs.parse(frame);
|
|
54620
54826
|
ensureSessionAccess(ctx, args.sessionId, "send");
|
|
54621
|
-
const
|
|
54827
|
+
const sender = ctx ? principalToSender(ctx.principal) : principalToSender(
|
|
54828
|
+
makeOwnerPrincipal(ownerPrincipalId, deps.ownerDisplayName, deps.ownerFeishuUnionId)
|
|
54829
|
+
);
|
|
54830
|
+
const { broadcast } = manager.send(args, sender);
|
|
54622
54831
|
return { response: { type: "session:send", ok: true }, broadcast };
|
|
54623
54832
|
};
|
|
54833
|
+
const currentTurnSender = async (frame, _client, ctx) => {
|
|
54834
|
+
const sid = typeof frame === "object" && frame != null && "sessionId" in frame ? String(frame.sessionId) : "";
|
|
54835
|
+
if (!sid) {
|
|
54836
|
+
return { response: { type: "session:currentTurnSender", sender: null } };
|
|
54837
|
+
}
|
|
54838
|
+
if (ctx && "principal" in ctx) {
|
|
54839
|
+
ensureSessionAccess(ctx, sid, "read");
|
|
54840
|
+
}
|
|
54841
|
+
const state = manager.tryGetState(sid);
|
|
54842
|
+
return {
|
|
54843
|
+
response: { type: "session:currentTurnSender", sender: state?.currentTurnSender ?? null }
|
|
54844
|
+
};
|
|
54845
|
+
};
|
|
54624
54846
|
const stop = async (frame, _client, ctx) => {
|
|
54625
54847
|
const args = SessionIdArgs.parse(frame);
|
|
54626
54848
|
ensureSessionAccess(ctx, args.sessionId, "send");
|
|
@@ -54823,6 +55045,7 @@ function buildSessionHandlers(deps) {
|
|
|
54823
55045
|
"session:update": update,
|
|
54824
55046
|
"session:delete": del,
|
|
54825
55047
|
"session:send": send,
|
|
55048
|
+
"session:currentTurnSender": currentTurnSender,
|
|
54826
55049
|
"session:stop": stop,
|
|
54827
55050
|
"session:interrupt": interrupt,
|
|
54828
55051
|
"session:rewind": rewind,
|
|
@@ -57891,7 +58114,14 @@ function buildAppBuilderHandlers(deps) {
|
|
|
57891
58114
|
broadcastProjectUpdated(f);
|
|
57892
58115
|
},
|
|
57893
58116
|
injectUserText: async (sessionId, text) => {
|
|
57894
|
-
const
|
|
58117
|
+
const ownerSender = principalToSender(
|
|
58118
|
+
makeOwnerPrincipal(
|
|
58119
|
+
deps.ownerPrincipalId,
|
|
58120
|
+
deps.ownerDisplayName ?? "Owner",
|
|
58121
|
+
deps.ownerFeishuUnionId
|
|
58122
|
+
)
|
|
58123
|
+
);
|
|
58124
|
+
const { broadcast } = sessionManager.send({ sessionId, text }, ownerSender);
|
|
57895
58125
|
for (const entry of broadcast) {
|
|
57896
58126
|
deps.broadcastSessionFrame(entry.frame, entry.target);
|
|
57897
58127
|
}
|
|
@@ -58190,6 +58420,9 @@ function buildMethodHandlers(deps) {
|
|
|
58190
58420
|
usersRoot: deps.usersRoot,
|
|
58191
58421
|
devServerLifecycle: deps.devServerLifecycle,
|
|
58192
58422
|
sessionManager: deps.manager,
|
|
58423
|
+
ownerPrincipalId: deps.ownerPrincipalId,
|
|
58424
|
+
ownerDisplayName: deps.ownerDisplayName,
|
|
58425
|
+
ownerFeishuUnionId: deps.ownerFeishuUnionId,
|
|
58193
58426
|
getTunnelUrl: deps.getTunnelUrl,
|
|
58194
58427
|
broadcastProjectUpdated: deps.broadcastProjectUpdated,
|
|
58195
58428
|
logger: deps.logger,
|
|
@@ -58518,6 +58751,10 @@ var METHOD_GRANT_MAP = {
|
|
|
58518
58751
|
"session:update": CAPABILITY_SCOPED,
|
|
58519
58752
|
"session:delete": CAPABILITY_SCOPED,
|
|
58520
58753
|
"session:send": CAPABILITY_SCOPED,
|
|
58754
|
+
// per-turn sender 2026-07-08 spec §4.6:dispatcher 层走 'public'(HTTP RPC path 需 dispatcher 放行);
|
|
58755
|
+
// WS path 的隐私边界(防 guest 猜 sid 枚举 owner feishuUnionId)由 handler 内 ensureSessionAccess
|
|
58756
|
+
// 显式兜底——见 handlers/session.ts:currentTurnSender。dispatcher 'public' ≠ handler 免检。
|
|
58757
|
+
"session:currentTurnSender": { kind: "public" },
|
|
58521
58758
|
"session:stop": CAPABILITY_SCOPED,
|
|
58522
58759
|
"session:interrupt": CAPABILITY_SCOPED,
|
|
58523
58760
|
"session:rewind": CAPABILITY_SCOPED,
|
|
@@ -59090,6 +59327,7 @@ async function startDaemon(config) {
|
|
|
59090
59327
|
let ownerId = feishuIdentity?.identity.ownerId ?? "";
|
|
59091
59328
|
let ownerProvider = feishuIdentity?.identity.provider ?? "";
|
|
59092
59329
|
let ownerDisplayName = feishuIdentity?.identity.displayName ?? loadOwnerDisplayName(config.dataDir);
|
|
59330
|
+
let ownerFeishuUnionId = feishuIdentity?.identity.unionId;
|
|
59093
59331
|
const authMode = resolvedAuthToken == null ? "none" : "first-message";
|
|
59094
59332
|
const inboxStore = new InboxStore(config.dataDir);
|
|
59095
59333
|
const inboxManager = new InboxManager(inboxStore, (_peerDeviceId, frame) => {
|
|
@@ -59102,6 +59340,7 @@ async function startDaemon(config) {
|
|
|
59102
59340
|
isOwnerToken: (x) => resolvedAuthToken != null && constantTimeEqual(x, resolvedAuthToken),
|
|
59103
59341
|
ownerPrincipalId,
|
|
59104
59342
|
ownerDisplayName,
|
|
59343
|
+
ownerFeishuUnionId,
|
|
59105
59344
|
// persona web 分享:访客 visitor token = daemon 自签 HMAC(无 aud),独立于飞书 connect token
|
|
59106
59345
|
// 基建——secret 恒存在(auth-store backfill),故无条件注入。验签 ok → guest ctx(persona wildcard)。
|
|
59107
59346
|
verifyVisitorToken: (t) => {
|
|
@@ -59185,7 +59424,7 @@ async function startDaemon(config) {
|
|
|
59185
59424
|
return result;
|
|
59186
59425
|
},
|
|
59187
59426
|
onAuthed: (h, ctx) => wsServer?.attachClientContext(h.id, ctx),
|
|
59188
|
-
buildOwnerContext: () => ownerContext(ownerPrincipalId, ownerDisplayName),
|
|
59427
|
+
buildOwnerContext: () => ownerContext(ownerPrincipalId, ownerDisplayName, ownerFeishuUnionId),
|
|
59189
59428
|
closeConnection: (h, code, reason) => wsServer?.closeClient(h.id, code, reason),
|
|
59190
59429
|
sendOk: (h, payload) => wsServer?.sendToClient(h.id, payload)
|
|
59191
59430
|
}) : null;
|
|
@@ -59257,18 +59496,14 @@ async function startDaemon(config) {
|
|
|
59257
59496
|
import_node_path64.default.join(here, "..", "dist", "ticket", "mcp-server.cjs")
|
|
59258
59497
|
];
|
|
59259
59498
|
const ticketServerScriptPath = ticketServerCandidates.find((p2) => import_node_fs51.default.existsSync(p2));
|
|
59260
|
-
const ticketCallerDid = feishuIdentity?.identity.unionId ?? "";
|
|
59261
|
-
const ticketCallerName = feishuIdentity?.identity.displayName ?? "";
|
|
59262
59499
|
let ticketMcpConfigPath2;
|
|
59263
|
-
if (ticketServerScriptPath
|
|
59500
|
+
if (ticketServerScriptPath) {
|
|
59264
59501
|
const ticketLogPath = import_node_path64.default.join(config.dataDir, "ticket-mcp-server.log");
|
|
59265
59502
|
ticketMcpConfigPath2 = writeTicketMcpConfig({
|
|
59266
59503
|
dataDir: config.dataDir,
|
|
59267
59504
|
serverScriptPath: ticketServerScriptPath,
|
|
59268
59505
|
apiBaseUrl: config.clawosApi ?? DEFAULT_CLAWOS_API,
|
|
59269
59506
|
apiKey: resolveClawdTicketsApiKey(),
|
|
59270
|
-
did: ticketCallerDid,
|
|
59271
|
-
name: ticketCallerName,
|
|
59272
59507
|
logPath: ticketLogPath
|
|
59273
59508
|
});
|
|
59274
59509
|
logger.info("ticket.mcp.json written", {
|
|
@@ -59278,7 +59513,7 @@ async function startDaemon(config) {
|
|
|
59278
59513
|
});
|
|
59279
59514
|
} else {
|
|
59280
59515
|
logger.warn("ticket-mcp skipped", {
|
|
59281
|
-
reason:
|
|
59516
|
+
reason: "ticket-mcp-server.cjs not found (need build)"
|
|
59282
59517
|
});
|
|
59283
59518
|
}
|
|
59284
59519
|
const shiftServerCandidates = [
|
|
@@ -59357,6 +59592,7 @@ async function startDaemon(config) {
|
|
|
59357
59592
|
personaStore,
|
|
59358
59593
|
ownerDisplayName,
|
|
59359
59594
|
ownerPrincipalId,
|
|
59595
|
+
ownerFeishuUnionId,
|
|
59360
59596
|
contactStore,
|
|
59361
59597
|
mode: config.mode,
|
|
59362
59598
|
// 单栏 refactor (spec 2026-06-02 §5.1):cc 子进程 env CLAWD_DAEMON_URL 闭包来源。
|
|
@@ -59529,7 +59765,16 @@ async function startDaemon(config) {
|
|
|
59529
59765
|
store: ownerIdentityStore,
|
|
59530
59766
|
fetchUserInfo: (ttcToken) => fetchTtcUserInfo({ apiBase: TTC_HOSTS.api, token: ttcToken }),
|
|
59531
59767
|
ttcAuthBase: TTC_HOSTS.auth,
|
|
59532
|
-
getCallbackUrl: () => `http://127.0.0.1:${config.port}/auth/callback
|
|
59768
|
+
getCallbackUrl: () => `http://127.0.0.1:${config.port}/auth/callback`,
|
|
59769
|
+
// device_bindings upsert(did ↔ 飞书 unionId 映射):
|
|
59770
|
+
// deviceId 从 auth.json 稳定读;apiUrl 走同一份 clawosApi;apiKey 复用 CLAWD_TICKETS_API_KEY。
|
|
59771
|
+
// 失败即登录失败(fail-hard,login-flow.spec §upsert 失败)。
|
|
59772
|
+
getDeviceId: () => authFile.deviceId,
|
|
59773
|
+
upsertBinding: (opts) => upsertDeviceBinding({
|
|
59774
|
+
apiUrl: config.clawosApi ?? DEFAULT_CLAWOS_API,
|
|
59775
|
+
apiKey: resolveClawdTicketsApiKey(),
|
|
59776
|
+
...opts
|
|
59777
|
+
})
|
|
59533
59778
|
});
|
|
59534
59779
|
const visitorStore = createVisitorStore({ dir: config.dataDir });
|
|
59535
59780
|
const exchangeVisitorToken = buildVisitorLogin({
|
|
@@ -59610,6 +59855,8 @@ async function startDaemon(config) {
|
|
|
59610
59855
|
ownerDisplayName,
|
|
59611
59856
|
// 决策 #16: whoami owner.id = 本机 deviceId(路由主键)+ ownerId/provider 归属人
|
|
59612
59857
|
ownerPrincipalId,
|
|
59858
|
+
// per-turn sender §4.6:send handler 无 ctx 兜底 + AppBuilder publish injectUserText 需要
|
|
59859
|
+
ownerFeishuUnionId,
|
|
59613
59860
|
ownerId,
|
|
59614
59861
|
ownerProvider,
|
|
59615
59862
|
personaStore,
|
|
@@ -59893,6 +60140,7 @@ async function startDaemon(config) {
|
|
|
59893
60140
|
ownerId = result.identity.ownerId;
|
|
59894
60141
|
ownerProvider = result.identity.provider;
|
|
59895
60142
|
ownerDisplayName = result.identity.displayName;
|
|
60143
|
+
ownerFeishuUnionId = result.identity.unionId;
|
|
59896
60144
|
handlers = makeHandlers();
|
|
59897
60145
|
wsServer?.broadcastToOwners({ type: "auth:login:done", identity: result.identity });
|
|
59898
60146
|
setImmediate(() => wsServer?.closeAllClients());
|
|
@@ -60031,7 +60279,7 @@ async function startDaemon(config) {
|
|
|
60031
60279
|
wss.onFrame(async (client, frame) => {
|
|
60032
60280
|
const type = frame.type;
|
|
60033
60281
|
const requestId = typeof frame.requestId === "string" ? frame.requestId : void 0;
|
|
60034
|
-
const ctx = wsServer.getClientContext(client.id) ?? ownerContext(ownerPrincipalId, ownerDisplayName);
|
|
60282
|
+
const ctx = wsServer.getClientContext(client.id) ?? ownerContext(ownerPrincipalId, ownerDisplayName, ownerFeishuUnionId);
|
|
60035
60283
|
const result = await dispatchRpc(type, frame, client, ctx, {
|
|
60036
60284
|
handlers,
|
|
60037
60285
|
feishuActive: () => feishuActive
|