@clawos-dev/clawd 0.2.107-beta.204.07ae519 → 0.2.108-beta.205.1e8d4c5
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 +564 -300
- package/package.json +1 -1
package/dist/cli.cjs
CHANGED
|
@@ -180,7 +180,13 @@ var init_methods = __esm({
|
|
|
180
180
|
// guest-self: guest 在自己 daemon 上做安装与更新(ADMIN_ANY,guest 是 owner of self daemon).
|
|
181
181
|
// 无 subscription 持久化:guest 端身份完全来自文件系统 + reconciled publishedExtensions.
|
|
182
182
|
"extension.install-from-channel",
|
|
183
|
-
"extension.update-from-channel"
|
|
183
|
+
"extension.update-from-channel",
|
|
184
|
+
// ---- auth:* 飞书统一身份 Phase 1 (spec 2026-06-01-feishu-identity-design) ----
|
|
185
|
+
// owner-only:daemon loopback 登录流程(auth:login:start 起 state → HTTP /auth/callback
|
|
186
|
+
// 回调落盘 → auth:login:done 事件)。中央字面量见 protocol/feishu-auth.ts FEISHU_AUTH_METHODS。
|
|
187
|
+
"auth:login:start",
|
|
188
|
+
"auth:getIdentity",
|
|
189
|
+
"auth:logout"
|
|
184
190
|
];
|
|
185
191
|
}
|
|
186
192
|
});
|
|
@@ -242,7 +248,11 @@ var init_errors = __esm({
|
|
|
242
248
|
INTERNAL: "INTERNAL",
|
|
243
249
|
UNAUTHORIZED: "UNAUTHORIZED",
|
|
244
250
|
FORBIDDEN: "FORBIDDEN",
|
|
245
|
-
INVALID_PARAM: "INVALID_PARAM"
|
|
251
|
+
INVALID_PARAM: "INVALID_PARAM",
|
|
252
|
+
// 飞书统一身份(spec 2026-06-01 决策 #9):People/远程体系强制飞书登录。
|
|
253
|
+
// daemon 进程身份未激活为飞书 unionId 时,FEISHU_GATED_METHODS 内的 RPC 一律返回此错误;
|
|
254
|
+
// UI 据此显示登录引导(区别于 UNAUTHORIZED 的越权语义)。
|
|
255
|
+
FEISHU_LOGIN_REQUIRED: "FEISHU_LOGIN_REQUIRED"
|
|
246
256
|
};
|
|
247
257
|
ClawdError = class extends Error {
|
|
248
258
|
constructor(code, message) {
|
|
@@ -672,8 +682,8 @@ var init_parseUtil = __esm({
|
|
|
672
682
|
init_errors2();
|
|
673
683
|
init_en();
|
|
674
684
|
makeIssue = (params) => {
|
|
675
|
-
const { data, path:
|
|
676
|
-
const fullPath = [...
|
|
685
|
+
const { data, path: path60, errorMaps, issueData } = params;
|
|
686
|
+
const fullPath = [...path60, ...issueData.path || []];
|
|
677
687
|
const fullIssue = {
|
|
678
688
|
...issueData,
|
|
679
689
|
path: fullPath
|
|
@@ -984,11 +994,11 @@ var init_types = __esm({
|
|
|
984
994
|
init_parseUtil();
|
|
985
995
|
init_util();
|
|
986
996
|
ParseInputLazyPath = class {
|
|
987
|
-
constructor(parent, value,
|
|
997
|
+
constructor(parent, value, path60, key) {
|
|
988
998
|
this._cachedPath = [];
|
|
989
999
|
this.parent = parent;
|
|
990
1000
|
this.data = value;
|
|
991
|
-
this._path =
|
|
1001
|
+
this._path = path60;
|
|
992
1002
|
this._key = key;
|
|
993
1003
|
}
|
|
994
1004
|
get path() {
|
|
@@ -4522,7 +4532,7 @@ var init_persona_schemas = __esm({
|
|
|
4522
4532
|
});
|
|
4523
4533
|
|
|
4524
4534
|
// ../protocol/src/schemas.ts
|
|
4525
|
-
var SessionStatusSchema, UsageSchema, ContextUsageSchema, sessionMetaShape, SessionMetaSchema, ModelInfoSchema, ModeInfoSchema, ConfigFieldSchemaSchema, CapabilitiesGetArgs, CapabilitiesResponseSchema, AllowRuleSchema, SessionFileSchema, ParsedEventBase, HistoryUserMetaSchema, SubagentToolStatsSchema, StructuredPatchHunkSchema, ToolResultExtraSchema, MemoryEntrySchema, AskQuestionOptionSchema, AskQuestionItemSchema, ParsedEventSchema, SessionCreateArgs, SessionIdArgs, SessionUpdateArgs, SessionSendArgs, SessionRewindArgs, SessionRewindResponseSchema, SessionRewindDiffArgs, RewindDiffHunkSchema, RewindDiffFileSchema, SessionRewindDiffResponseSchema, SessionRewindableMessageIdsArgs, SessionRewindableMessageIdsResponseSchema, SessionResumeArgs, SessionForkArgs, SessionForkResponseSchema, SessionObserveArgs, SessionEventsArgs, PermissionRespondArgs, HistoryListArgs, HistoryReadArgs, HistorySubagentsArgs, HistorySubagentReadArgs, WorkspaceListArgs, WorkspaceReadArgs, SkillsListArgs, SkillEntrySchema, AgentEntrySchema, AgentsListArgs, AgentsListResponseSchema, SessionSubscribeArgs, SessionPinArgs, PeerSessionUpsertArgs, PeerSessionUpsertResponseSchema, PeerSessionRemoveArgs, PeerSessionRemoveResponseSchema, SessionReorderPinsArgs, GitRootArgs, GitRootResponseSchema, GitBranchArgs, GitBranchResponseSchema, GitBranchesArgs, GitBranchesResponseSchema, HistoryRecentDirsArgs, RecentDirEntrySchema, HistoryRecentDirsResponseSchema, SessionQuestionFrameSchema, SessionQuestionClearedFrameSchema, AnswerQuestionArgs, AnswerQuestionResponseSchema, CancelQuestionArgs, CancelQuestionResponseSchema, AuthRequestFrameSchema, AuthOkFrameSchema,
|
|
4535
|
+
var SessionStatusSchema, UsageSchema, ContextUsageSchema, sessionMetaShape, SessionMetaSchema, ModelInfoSchema, ModeInfoSchema, ConfigFieldSchemaSchema, CapabilitiesGetArgs, CapabilitiesResponseSchema, AllowRuleSchema, SessionFileSchema, ParsedEventBase, HistoryUserMetaSchema, SubagentToolStatsSchema, StructuredPatchHunkSchema, ToolResultExtraSchema, MemoryEntrySchema, AskQuestionOptionSchema, AskQuestionItemSchema, ParsedEventSchema, SessionCreateArgs, SessionIdArgs, SessionUpdateArgs, SessionSendArgs, SessionRewindArgs, SessionRewindResponseSchema, SessionRewindDiffArgs, RewindDiffHunkSchema, RewindDiffFileSchema, SessionRewindDiffResponseSchema, SessionRewindableMessageIdsArgs, SessionRewindableMessageIdsResponseSchema, SessionResumeArgs, SessionForkArgs, SessionForkResponseSchema, SessionObserveArgs, SessionEventsArgs, PermissionRespondArgs, HistoryListArgs, HistoryReadArgs, HistorySubagentsArgs, HistorySubagentReadArgs, WorkspaceListArgs, WorkspaceReadArgs, SkillsListArgs, SkillEntrySchema, AgentEntrySchema, AgentsListArgs, AgentsListResponseSchema, SessionSubscribeArgs, SessionPinArgs, PeerSessionUpsertArgs, PeerSessionUpsertResponseSchema, PeerSessionRemoveArgs, PeerSessionRemoveResponseSchema, SessionReorderPinsArgs, GitRootArgs, GitRootResponseSchema, GitBranchArgs, GitBranchResponseSchema, GitBranchesArgs, GitBranchesResponseSchema, HistoryRecentDirsArgs, RecentDirEntrySchema, HistoryRecentDirsResponseSchema, SessionQuestionFrameSchema, SessionQuestionClearedFrameSchema, AnswerQuestionArgs, AnswerQuestionResponseSchema, CancelQuestionArgs, CancelQuestionResponseSchema, AuthRequestFrameSchema, AuthOkFrameSchema, TunnelExitedEventSchema, TunnelUnavailableEventSchema, InfoRunningSessionSchema, InfoResponseSchema;
|
|
4526
4536
|
var init_schemas = __esm({
|
|
4527
4537
|
"../protocol/src/schemas.ts"() {
|
|
4528
4538
|
"use strict";
|
|
@@ -5102,6 +5112,12 @@ var init_schemas = __esm({
|
|
|
5102
5112
|
* 后调 capabilityManager.recordPeerHello(capId, ownerPrincipalId, displayName)
|
|
5103
5113
|
* 把 cap.peerOwnerId / firstUsedByPeerAt 字段回写,让 owner 端 UI 在 People
|
|
5104
5114
|
* tab 顶层 PeerOwner 视图 + Personas tab 二级分组拿到对端身份。
|
|
5115
|
+
*
|
|
5116
|
+
* 飞书统一身份 Phase 1 (2026-06-01):取值升级为飞书 union_id(owner 已飞书登录时;
|
|
5117
|
+
* 来自 whoami.owner.id → daemon ownerPrincipalId → owner-identity.json unionId)。
|
|
5118
|
+
* 未登录 daemon 仍为 auth.json 的 owner-<uuid>。daemon 端不验证该身份真实性
|
|
5119
|
+
* (Phase 1 信任模型,见 spec §9)。Phase 2 将以 connect token 替代(introspect
|
|
5120
|
+
* 返回身份,删除自报字段,见 spec §10)。
|
|
5105
5121
|
*/
|
|
5106
5122
|
selfPrincipalId: external_exports.string().min(1).optional(),
|
|
5107
5123
|
/**
|
|
@@ -5116,11 +5132,6 @@ var init_schemas = __esm({
|
|
|
5116
5132
|
version: external_exports.string().min(1).optional(),
|
|
5117
5133
|
protocolVersion: external_exports.number().int().nonnegative().optional()
|
|
5118
5134
|
});
|
|
5119
|
-
TunnelReadyEventSchema = external_exports.object({
|
|
5120
|
-
type: external_exports.literal("tunnel:ready"),
|
|
5121
|
-
url: external_exports.string().min(1),
|
|
5122
|
-
subdomain: external_exports.string().min(1)
|
|
5123
|
-
});
|
|
5124
5135
|
TunnelExitedEventSchema = external_exports.object({
|
|
5125
5136
|
type: external_exports.literal("tunnel:exited"),
|
|
5126
5137
|
code: external_exports.number().int().nullable(),
|
|
@@ -5570,6 +5581,64 @@ var init_extension = __esm({
|
|
|
5570
5581
|
}
|
|
5571
5582
|
});
|
|
5572
5583
|
|
|
5584
|
+
// ../protocol/src/feishu-auth.ts
|
|
5585
|
+
var FEISHU_GATED_METHODS, FeishuIdentitySchema, AuthLoginStartResponseSchema, AuthGetIdentityResponseSchema, AuthLogoutResponseSchema, AuthLoginDoneEventSchema, AuthLoginFailedEventSchema;
|
|
5586
|
+
var init_feishu_auth = __esm({
|
|
5587
|
+
"../protocol/src/feishu-auth.ts"() {
|
|
5588
|
+
"use strict";
|
|
5589
|
+
init_zod();
|
|
5590
|
+
FEISHU_GATED_METHODS = [
|
|
5591
|
+
// capability / 邀请码(生成添加码 = 把自己的身份分享出去)
|
|
5592
|
+
"capability:list",
|
|
5593
|
+
"capability:delete",
|
|
5594
|
+
"personal-cap:get",
|
|
5595
|
+
// 联系人(添加朋友 / 联系人列表 / 自动重连)
|
|
5596
|
+
"received-capability:list",
|
|
5597
|
+
"received-capability:add",
|
|
5598
|
+
"received-capability:remove",
|
|
5599
|
+
"received-capability:autoAttach",
|
|
5600
|
+
// DM / 跨用户通知
|
|
5601
|
+
"inbox:list",
|
|
5602
|
+
"inbox:markRead",
|
|
5603
|
+
"inbox:postMessage",
|
|
5604
|
+
// mirror peer sessions(远端会话镜像)
|
|
5605
|
+
"peerSession:upsert",
|
|
5606
|
+
"peerSession:remove"
|
|
5607
|
+
];
|
|
5608
|
+
FeishuIdentitySchema = external_exports.object({
|
|
5609
|
+
/** 飞书 union_id(租户维度,跨应用稳定)。TTC user_info 接口 data.union_id */
|
|
5610
|
+
unionId: external_exports.string().min(1),
|
|
5611
|
+
/** 飞书姓名。TTC user_info 接口 data.name */
|
|
5612
|
+
displayName: external_exports.string().min(1),
|
|
5613
|
+
/** TTC user_info 接口 data.avatar_url,可缺省 */
|
|
5614
|
+
avatarUrl: external_exports.string().optional()
|
|
5615
|
+
});
|
|
5616
|
+
AuthLoginStartResponseSchema = external_exports.object({
|
|
5617
|
+
type: external_exports.literal("auth:login:start:ok"),
|
|
5618
|
+
/** 完整 TTC 授权页 URL(含 callback_url + state + auto=1),UI 直接 window.open */
|
|
5619
|
+
authUrl: external_exports.string().min(1),
|
|
5620
|
+
/** 防 CSRF 的一次性 nonce;回调时 daemon 校验 */
|
|
5621
|
+
state: external_exports.string().min(1)
|
|
5622
|
+
});
|
|
5623
|
+
AuthGetIdentityResponseSchema = external_exports.object({
|
|
5624
|
+
type: external_exports.literal("auth:getIdentity:ok"),
|
|
5625
|
+
/** null = 未登录 */
|
|
5626
|
+
identity: FeishuIdentitySchema.nullable()
|
|
5627
|
+
});
|
|
5628
|
+
AuthLogoutResponseSchema = external_exports.object({
|
|
5629
|
+
type: external_exports.literal("auth:logout:ok")
|
|
5630
|
+
});
|
|
5631
|
+
AuthLoginDoneEventSchema = external_exports.object({
|
|
5632
|
+
type: external_exports.literal("auth:login:done"),
|
|
5633
|
+
identity: FeishuIdentitySchema
|
|
5634
|
+
});
|
|
5635
|
+
AuthLoginFailedEventSchema = external_exports.object({
|
|
5636
|
+
type: external_exports.literal("auth:login:failed"),
|
|
5637
|
+
reason: external_exports.string()
|
|
5638
|
+
});
|
|
5639
|
+
}
|
|
5640
|
+
});
|
|
5641
|
+
|
|
5573
5642
|
// ../protocol/src/runtime.ts
|
|
5574
5643
|
var init_runtime = __esm({
|
|
5575
5644
|
"../protocol/src/runtime.ts"() {
|
|
@@ -5587,6 +5656,7 @@ var init_runtime = __esm({
|
|
|
5587
5656
|
init_inbox();
|
|
5588
5657
|
init_received_capability();
|
|
5589
5658
|
init_extension();
|
|
5659
|
+
init_feishu_auth();
|
|
5590
5660
|
}
|
|
5591
5661
|
});
|
|
5592
5662
|
|
|
@@ -5861,8 +5931,8 @@ var require_req = __commonJS({
|
|
|
5861
5931
|
if (req.originalUrl) {
|
|
5862
5932
|
_req.url = req.originalUrl;
|
|
5863
5933
|
} else {
|
|
5864
|
-
const
|
|
5865
|
-
_req.url = typeof
|
|
5934
|
+
const path60 = req.path;
|
|
5935
|
+
_req.url = typeof path60 === "string" ? path60 : req.url ? req.url.path || req.url : void 0;
|
|
5866
5936
|
}
|
|
5867
5937
|
if (req.query) {
|
|
5868
5938
|
_req.query = req.query;
|
|
@@ -6027,14 +6097,14 @@ var require_redact = __commonJS({
|
|
|
6027
6097
|
}
|
|
6028
6098
|
return obj;
|
|
6029
6099
|
}
|
|
6030
|
-
function parsePath(
|
|
6100
|
+
function parsePath(path60) {
|
|
6031
6101
|
const parts = [];
|
|
6032
6102
|
let current = "";
|
|
6033
6103
|
let inBrackets = false;
|
|
6034
6104
|
let inQuotes = false;
|
|
6035
6105
|
let quoteChar = "";
|
|
6036
|
-
for (let i = 0; i <
|
|
6037
|
-
const char =
|
|
6106
|
+
for (let i = 0; i < path60.length; i++) {
|
|
6107
|
+
const char = path60[i];
|
|
6038
6108
|
if (!inBrackets && char === ".") {
|
|
6039
6109
|
if (current) {
|
|
6040
6110
|
parts.push(current);
|
|
@@ -6165,10 +6235,10 @@ var require_redact = __commonJS({
|
|
|
6165
6235
|
return current;
|
|
6166
6236
|
}
|
|
6167
6237
|
function redactPaths(obj, paths, censor, remove = false) {
|
|
6168
|
-
for (const
|
|
6169
|
-
const parts = parsePath(
|
|
6238
|
+
for (const path60 of paths) {
|
|
6239
|
+
const parts = parsePath(path60);
|
|
6170
6240
|
if (parts.includes("*")) {
|
|
6171
|
-
redactWildcardPath(obj, parts, censor,
|
|
6241
|
+
redactWildcardPath(obj, parts, censor, path60, remove);
|
|
6172
6242
|
} else {
|
|
6173
6243
|
if (remove) {
|
|
6174
6244
|
removeKey(obj, parts);
|
|
@@ -6253,8 +6323,8 @@ var require_redact = __commonJS({
|
|
|
6253
6323
|
}
|
|
6254
6324
|
} else {
|
|
6255
6325
|
if (afterWildcard.includes("*")) {
|
|
6256
|
-
const wrappedCensor = typeof censor === "function" ? (value,
|
|
6257
|
-
const fullPath = [...pathArray.slice(0, pathLength), ...
|
|
6326
|
+
const wrappedCensor = typeof censor === "function" ? (value, path60) => {
|
|
6327
|
+
const fullPath = [...pathArray.slice(0, pathLength), ...path60];
|
|
6258
6328
|
return censor(value, fullPath);
|
|
6259
6329
|
} : censor;
|
|
6260
6330
|
redactWildcardPath(current, afterWildcard, wrappedCensor, originalPath, remove);
|
|
@@ -6289,8 +6359,8 @@ var require_redact = __commonJS({
|
|
|
6289
6359
|
return null;
|
|
6290
6360
|
}
|
|
6291
6361
|
const pathStructure = /* @__PURE__ */ new Map();
|
|
6292
|
-
for (const
|
|
6293
|
-
const parts = parsePath(
|
|
6362
|
+
for (const path60 of pathsToClone) {
|
|
6363
|
+
const parts = parsePath(path60);
|
|
6294
6364
|
let current = pathStructure;
|
|
6295
6365
|
for (let i = 0; i < parts.length; i++) {
|
|
6296
6366
|
const part = parts[i];
|
|
@@ -6342,24 +6412,24 @@ var require_redact = __commonJS({
|
|
|
6342
6412
|
}
|
|
6343
6413
|
return cloneSelectively(obj, pathStructure);
|
|
6344
6414
|
}
|
|
6345
|
-
function validatePath(
|
|
6346
|
-
if (typeof
|
|
6415
|
+
function validatePath(path60) {
|
|
6416
|
+
if (typeof path60 !== "string") {
|
|
6347
6417
|
throw new Error("Paths must be (non-empty) strings");
|
|
6348
6418
|
}
|
|
6349
|
-
if (
|
|
6419
|
+
if (path60 === "") {
|
|
6350
6420
|
throw new Error("Invalid redaction path ()");
|
|
6351
6421
|
}
|
|
6352
|
-
if (
|
|
6353
|
-
throw new Error(`Invalid redaction path (${
|
|
6422
|
+
if (path60.includes("..")) {
|
|
6423
|
+
throw new Error(`Invalid redaction path (${path60})`);
|
|
6354
6424
|
}
|
|
6355
|
-
if (
|
|
6356
|
-
throw new Error(`Invalid redaction path (${
|
|
6425
|
+
if (path60.includes(",")) {
|
|
6426
|
+
throw new Error(`Invalid redaction path (${path60})`);
|
|
6357
6427
|
}
|
|
6358
6428
|
let bracketCount = 0;
|
|
6359
6429
|
let inQuotes = false;
|
|
6360
6430
|
let quoteChar = "";
|
|
6361
|
-
for (let i = 0; i <
|
|
6362
|
-
const char =
|
|
6431
|
+
for (let i = 0; i < path60.length; i++) {
|
|
6432
|
+
const char = path60[i];
|
|
6363
6433
|
if ((char === '"' || char === "'") && bracketCount > 0) {
|
|
6364
6434
|
if (!inQuotes) {
|
|
6365
6435
|
inQuotes = true;
|
|
@@ -6373,20 +6443,20 @@ var require_redact = __commonJS({
|
|
|
6373
6443
|
} else if (char === "]" && !inQuotes) {
|
|
6374
6444
|
bracketCount--;
|
|
6375
6445
|
if (bracketCount < 0) {
|
|
6376
|
-
throw new Error(`Invalid redaction path (${
|
|
6446
|
+
throw new Error(`Invalid redaction path (${path60})`);
|
|
6377
6447
|
}
|
|
6378
6448
|
}
|
|
6379
6449
|
}
|
|
6380
6450
|
if (bracketCount !== 0) {
|
|
6381
|
-
throw new Error(`Invalid redaction path (${
|
|
6451
|
+
throw new Error(`Invalid redaction path (${path60})`);
|
|
6382
6452
|
}
|
|
6383
6453
|
}
|
|
6384
6454
|
function validatePaths(paths) {
|
|
6385
6455
|
if (!Array.isArray(paths)) {
|
|
6386
6456
|
throw new TypeError("paths must be an array");
|
|
6387
6457
|
}
|
|
6388
|
-
for (const
|
|
6389
|
-
validatePath(
|
|
6458
|
+
for (const path60 of paths) {
|
|
6459
|
+
validatePath(path60);
|
|
6390
6460
|
}
|
|
6391
6461
|
}
|
|
6392
6462
|
function slowRedact(options = {}) {
|
|
@@ -6554,8 +6624,8 @@ var require_redaction = __commonJS({
|
|
|
6554
6624
|
if (shape[k2] === null) {
|
|
6555
6625
|
o[k2] = (value) => topCensor(value, [k2]);
|
|
6556
6626
|
} else {
|
|
6557
|
-
const wrappedCensor = typeof censor === "function" ? (value,
|
|
6558
|
-
return censor(value, [k2, ...
|
|
6627
|
+
const wrappedCensor = typeof censor === "function" ? (value, path60) => {
|
|
6628
|
+
return censor(value, [k2, ...path60]);
|
|
6559
6629
|
} : censor;
|
|
6560
6630
|
o[k2] = Redact({
|
|
6561
6631
|
paths: shape[k2],
|
|
@@ -6773,10 +6843,10 @@ var require_atomic_sleep = __commonJS({
|
|
|
6773
6843
|
var require_sonic_boom = __commonJS({
|
|
6774
6844
|
"../node_modules/.pnpm/sonic-boom@4.2.1/node_modules/sonic-boom/index.js"(exports2, module2) {
|
|
6775
6845
|
"use strict";
|
|
6776
|
-
var
|
|
6846
|
+
var fs48 = require("fs");
|
|
6777
6847
|
var EventEmitter2 = require("events");
|
|
6778
6848
|
var inherits = require("util").inherits;
|
|
6779
|
-
var
|
|
6849
|
+
var path60 = require("path");
|
|
6780
6850
|
var sleep = require_atomic_sleep();
|
|
6781
6851
|
var assert = require("assert");
|
|
6782
6852
|
var BUSY_WRITE_TIMEOUT = 100;
|
|
@@ -6830,20 +6900,20 @@ var require_sonic_boom = __commonJS({
|
|
|
6830
6900
|
const mode = sonic.mode;
|
|
6831
6901
|
if (sonic.sync) {
|
|
6832
6902
|
try {
|
|
6833
|
-
if (sonic.mkdir)
|
|
6834
|
-
const fd =
|
|
6903
|
+
if (sonic.mkdir) fs48.mkdirSync(path60.dirname(file), { recursive: true });
|
|
6904
|
+
const fd = fs48.openSync(file, flags, mode);
|
|
6835
6905
|
fileOpened(null, fd);
|
|
6836
6906
|
} catch (err) {
|
|
6837
6907
|
fileOpened(err);
|
|
6838
6908
|
throw err;
|
|
6839
6909
|
}
|
|
6840
6910
|
} else if (sonic.mkdir) {
|
|
6841
|
-
|
|
6911
|
+
fs48.mkdir(path60.dirname(file), { recursive: true }, (err) => {
|
|
6842
6912
|
if (err) return fileOpened(err);
|
|
6843
|
-
|
|
6913
|
+
fs48.open(file, flags, mode, fileOpened);
|
|
6844
6914
|
});
|
|
6845
6915
|
} else {
|
|
6846
|
-
|
|
6916
|
+
fs48.open(file, flags, mode, fileOpened);
|
|
6847
6917
|
}
|
|
6848
6918
|
}
|
|
6849
6919
|
function SonicBoom(opts) {
|
|
@@ -6884,8 +6954,8 @@ var require_sonic_boom = __commonJS({
|
|
|
6884
6954
|
this.flush = flushBuffer;
|
|
6885
6955
|
this.flushSync = flushBufferSync;
|
|
6886
6956
|
this._actualWrite = actualWriteBuffer;
|
|
6887
|
-
fsWriteSync = () =>
|
|
6888
|
-
fsWrite = () =>
|
|
6957
|
+
fsWriteSync = () => fs48.writeSync(this.fd, this._writingBuf);
|
|
6958
|
+
fsWrite = () => fs48.write(this.fd, this._writingBuf, this.release);
|
|
6889
6959
|
} else if (contentMode === void 0 || contentMode === kContentModeUtf8) {
|
|
6890
6960
|
this._writingBuf = "";
|
|
6891
6961
|
this.write = write;
|
|
@@ -6894,15 +6964,15 @@ var require_sonic_boom = __commonJS({
|
|
|
6894
6964
|
this._actualWrite = actualWrite;
|
|
6895
6965
|
fsWriteSync = () => {
|
|
6896
6966
|
if (Buffer.isBuffer(this._writingBuf)) {
|
|
6897
|
-
return
|
|
6967
|
+
return fs48.writeSync(this.fd, this._writingBuf);
|
|
6898
6968
|
}
|
|
6899
|
-
return
|
|
6969
|
+
return fs48.writeSync(this.fd, this._writingBuf, "utf8");
|
|
6900
6970
|
};
|
|
6901
6971
|
fsWrite = () => {
|
|
6902
6972
|
if (Buffer.isBuffer(this._writingBuf)) {
|
|
6903
|
-
return
|
|
6973
|
+
return fs48.write(this.fd, this._writingBuf, this.release);
|
|
6904
6974
|
}
|
|
6905
|
-
return
|
|
6975
|
+
return fs48.write(this.fd, this._writingBuf, "utf8", this.release);
|
|
6906
6976
|
};
|
|
6907
6977
|
} else {
|
|
6908
6978
|
throw new Error(`SonicBoom supports "${kContentModeUtf8}" and "${kContentModeBuffer}", but passed ${contentMode}`);
|
|
@@ -6959,7 +7029,7 @@ var require_sonic_boom = __commonJS({
|
|
|
6959
7029
|
}
|
|
6960
7030
|
}
|
|
6961
7031
|
if (this._fsync) {
|
|
6962
|
-
|
|
7032
|
+
fs48.fsyncSync(this.fd);
|
|
6963
7033
|
}
|
|
6964
7034
|
const len = this._len;
|
|
6965
7035
|
if (this._reopening) {
|
|
@@ -7073,7 +7143,7 @@ var require_sonic_boom = __commonJS({
|
|
|
7073
7143
|
const onDrain = () => {
|
|
7074
7144
|
if (!this._fsync) {
|
|
7075
7145
|
try {
|
|
7076
|
-
|
|
7146
|
+
fs48.fsync(this.fd, (err) => {
|
|
7077
7147
|
this._flushPending = false;
|
|
7078
7148
|
cb(err);
|
|
7079
7149
|
});
|
|
@@ -7175,7 +7245,7 @@ var require_sonic_boom = __commonJS({
|
|
|
7175
7245
|
const fd = this.fd;
|
|
7176
7246
|
this.once("ready", () => {
|
|
7177
7247
|
if (fd !== this.fd) {
|
|
7178
|
-
|
|
7248
|
+
fs48.close(fd, (err) => {
|
|
7179
7249
|
if (err) {
|
|
7180
7250
|
return this.emit("error", err);
|
|
7181
7251
|
}
|
|
@@ -7224,7 +7294,7 @@ var require_sonic_boom = __commonJS({
|
|
|
7224
7294
|
buf = this._bufs[0];
|
|
7225
7295
|
}
|
|
7226
7296
|
try {
|
|
7227
|
-
const n = Buffer.isBuffer(buf) ?
|
|
7297
|
+
const n = Buffer.isBuffer(buf) ? fs48.writeSync(this.fd, buf) : fs48.writeSync(this.fd, buf, "utf8");
|
|
7228
7298
|
const releasedBufObj = releaseWritingBuf(buf, this._len, n);
|
|
7229
7299
|
buf = releasedBufObj.writingBuf;
|
|
7230
7300
|
this._len = releasedBufObj.len;
|
|
@@ -7240,7 +7310,7 @@ var require_sonic_boom = __commonJS({
|
|
|
7240
7310
|
}
|
|
7241
7311
|
}
|
|
7242
7312
|
try {
|
|
7243
|
-
|
|
7313
|
+
fs48.fsyncSync(this.fd);
|
|
7244
7314
|
} catch {
|
|
7245
7315
|
}
|
|
7246
7316
|
}
|
|
@@ -7261,7 +7331,7 @@ var require_sonic_boom = __commonJS({
|
|
|
7261
7331
|
buf = mergeBuf(this._bufs[0], this._lens[0]);
|
|
7262
7332
|
}
|
|
7263
7333
|
try {
|
|
7264
|
-
const n =
|
|
7334
|
+
const n = fs48.writeSync(this.fd, buf);
|
|
7265
7335
|
buf = buf.subarray(n);
|
|
7266
7336
|
this._len = Math.max(this._len - n, 0);
|
|
7267
7337
|
if (buf.length <= 0) {
|
|
@@ -7289,13 +7359,13 @@ var require_sonic_boom = __commonJS({
|
|
|
7289
7359
|
this._writingBuf = this._writingBuf.length ? this._writingBuf : this._bufs.shift() || "";
|
|
7290
7360
|
if (this.sync) {
|
|
7291
7361
|
try {
|
|
7292
|
-
const written = Buffer.isBuffer(this._writingBuf) ?
|
|
7362
|
+
const written = Buffer.isBuffer(this._writingBuf) ? fs48.writeSync(this.fd, this._writingBuf) : fs48.writeSync(this.fd, this._writingBuf, "utf8");
|
|
7293
7363
|
release(null, written);
|
|
7294
7364
|
} catch (err) {
|
|
7295
7365
|
release(err);
|
|
7296
7366
|
}
|
|
7297
7367
|
} else {
|
|
7298
|
-
|
|
7368
|
+
fs48.write(this.fd, this._writingBuf, release);
|
|
7299
7369
|
}
|
|
7300
7370
|
}
|
|
7301
7371
|
function actualWriteBuffer() {
|
|
@@ -7304,7 +7374,7 @@ var require_sonic_boom = __commonJS({
|
|
|
7304
7374
|
this._writingBuf = this._writingBuf.length ? this._writingBuf : mergeBuf(this._bufs.shift(), this._lens.shift());
|
|
7305
7375
|
if (this.sync) {
|
|
7306
7376
|
try {
|
|
7307
|
-
const written =
|
|
7377
|
+
const written = fs48.writeSync(this.fd, this._writingBuf);
|
|
7308
7378
|
release(null, written);
|
|
7309
7379
|
} catch (err) {
|
|
7310
7380
|
release(err);
|
|
@@ -7313,7 +7383,7 @@ var require_sonic_boom = __commonJS({
|
|
|
7313
7383
|
if (kCopyBuffer) {
|
|
7314
7384
|
this._writingBuf = Buffer.from(this._writingBuf);
|
|
7315
7385
|
}
|
|
7316
|
-
|
|
7386
|
+
fs48.write(this.fd, this._writingBuf, release);
|
|
7317
7387
|
}
|
|
7318
7388
|
}
|
|
7319
7389
|
function actualClose(sonic) {
|
|
@@ -7329,12 +7399,12 @@ var require_sonic_boom = __commonJS({
|
|
|
7329
7399
|
sonic._lens = [];
|
|
7330
7400
|
assert(typeof sonic.fd === "number", `sonic.fd must be a number, got ${typeof sonic.fd}`);
|
|
7331
7401
|
try {
|
|
7332
|
-
|
|
7402
|
+
fs48.fsync(sonic.fd, closeWrapped);
|
|
7333
7403
|
} catch {
|
|
7334
7404
|
}
|
|
7335
7405
|
function closeWrapped() {
|
|
7336
7406
|
if (sonic.fd !== 1 && sonic.fd !== 2) {
|
|
7337
|
-
|
|
7407
|
+
fs48.close(sonic.fd, done);
|
|
7338
7408
|
} else {
|
|
7339
7409
|
done();
|
|
7340
7410
|
}
|
|
@@ -10469,11 +10539,11 @@ var init_lib = __esm({
|
|
|
10469
10539
|
}
|
|
10470
10540
|
}
|
|
10471
10541
|
},
|
|
10472
|
-
addToPath: function addToPath(
|
|
10473
|
-
var last =
|
|
10542
|
+
addToPath: function addToPath(path60, added, removed, oldPosInc, options) {
|
|
10543
|
+
var last = path60.lastComponent;
|
|
10474
10544
|
if (last && !options.oneChangePerToken && last.added === added && last.removed === removed) {
|
|
10475
10545
|
return {
|
|
10476
|
-
oldPos:
|
|
10546
|
+
oldPos: path60.oldPos + oldPosInc,
|
|
10477
10547
|
lastComponent: {
|
|
10478
10548
|
count: last.count + 1,
|
|
10479
10549
|
added,
|
|
@@ -10483,7 +10553,7 @@ var init_lib = __esm({
|
|
|
10483
10553
|
};
|
|
10484
10554
|
} else {
|
|
10485
10555
|
return {
|
|
10486
|
-
oldPos:
|
|
10556
|
+
oldPos: path60.oldPos + oldPosInc,
|
|
10487
10557
|
lastComponent: {
|
|
10488
10558
|
count: 1,
|
|
10489
10559
|
added,
|
|
@@ -10929,10 +10999,10 @@ function attachmentToHistoryMessage(o, ts) {
|
|
|
10929
10999
|
const memories = raw.map((m2) => {
|
|
10930
11000
|
if (!m2 || typeof m2 !== "object") return null;
|
|
10931
11001
|
const rec = m2;
|
|
10932
|
-
const
|
|
11002
|
+
const path60 = typeof rec.path === "string" ? rec.path : null;
|
|
10933
11003
|
const content = typeof rec.content === "string" ? rec.content : null;
|
|
10934
|
-
if (!
|
|
10935
|
-
const entry = { path:
|
|
11004
|
+
if (!path60 || content == null) return null;
|
|
11005
|
+
const entry = { path: path60, content };
|
|
10936
11006
|
if (typeof rec.mtimeMs === "number") entry.mtimeMs = rec.mtimeMs;
|
|
10937
11007
|
return entry;
|
|
10938
11008
|
}).filter((m2) => m2 !== null);
|
|
@@ -11763,10 +11833,10 @@ function parseAttachment(obj) {
|
|
|
11763
11833
|
const memories = raw.map((m2) => {
|
|
11764
11834
|
if (!m2 || typeof m2 !== "object") return null;
|
|
11765
11835
|
const rec = m2;
|
|
11766
|
-
const
|
|
11836
|
+
const path60 = typeof rec.path === "string" ? rec.path : null;
|
|
11767
11837
|
const content = typeof rec.content === "string" ? rec.content : null;
|
|
11768
|
-
if (!
|
|
11769
|
-
const out = { path:
|
|
11838
|
+
if (!path60 || content == null) return null;
|
|
11839
|
+
const out = { path: path60, content };
|
|
11770
11840
|
if (typeof rec.mtimeMs === "number") out.mtimeMs = rec.mtimeMs;
|
|
11771
11841
|
return out;
|
|
11772
11842
|
}).filter((m2) => m2 !== null);
|
|
@@ -19262,8 +19332,8 @@ var require_websocket = __commonJS({
|
|
|
19262
19332
|
"use strict";
|
|
19263
19333
|
var EventEmitter2 = require("events");
|
|
19264
19334
|
var https = require("https");
|
|
19265
|
-
var
|
|
19266
|
-
var
|
|
19335
|
+
var http2 = require("http");
|
|
19336
|
+
var net2 = require("net");
|
|
19267
19337
|
var tls = require("tls");
|
|
19268
19338
|
var { randomBytes, createHash: createHash2 } = require("crypto");
|
|
19269
19339
|
var { Duplex, Readable: Readable3 } = require("stream");
|
|
@@ -19796,7 +19866,7 @@ var require_websocket = __commonJS({
|
|
|
19796
19866
|
}
|
|
19797
19867
|
const defaultPort = isSecure ? 443 : 80;
|
|
19798
19868
|
const key = randomBytes(16).toString("base64");
|
|
19799
|
-
const request = isSecure ? https.request :
|
|
19869
|
+
const request = isSecure ? https.request : http2.request;
|
|
19800
19870
|
const protocolSet = /* @__PURE__ */ new Set();
|
|
19801
19871
|
let perMessageDeflate;
|
|
19802
19872
|
opts.createConnection = opts.createConnection || (isSecure ? tlsConnect : netConnect);
|
|
@@ -19997,12 +20067,12 @@ var require_websocket = __commonJS({
|
|
|
19997
20067
|
}
|
|
19998
20068
|
function netConnect(options) {
|
|
19999
20069
|
options.path = options.socketPath;
|
|
20000
|
-
return
|
|
20070
|
+
return net2.connect(options);
|
|
20001
20071
|
}
|
|
20002
20072
|
function tlsConnect(options) {
|
|
20003
20073
|
options.path = void 0;
|
|
20004
20074
|
if (!options.servername && options.servername !== "") {
|
|
20005
|
-
options.servername =
|
|
20075
|
+
options.servername = net2.isIP(options.host) ? "" : options.host;
|
|
20006
20076
|
}
|
|
20007
20077
|
return tls.connect(options);
|
|
20008
20078
|
}
|
|
@@ -20290,7 +20360,7 @@ var require_websocket_server = __commonJS({
|
|
|
20290
20360
|
"../node_modules/.pnpm/ws@8.20.0/node_modules/ws/lib/websocket-server.js"(exports2, module2) {
|
|
20291
20361
|
"use strict";
|
|
20292
20362
|
var EventEmitter2 = require("events");
|
|
20293
|
-
var
|
|
20363
|
+
var http2 = require("http");
|
|
20294
20364
|
var { Duplex } = require("stream");
|
|
20295
20365
|
var { createHash: createHash2 } = require("crypto");
|
|
20296
20366
|
var extension2 = require_extension();
|
|
@@ -20365,8 +20435,8 @@ var require_websocket_server = __commonJS({
|
|
|
20365
20435
|
);
|
|
20366
20436
|
}
|
|
20367
20437
|
if (options.port != null) {
|
|
20368
|
-
this._server =
|
|
20369
|
-
const body =
|
|
20438
|
+
this._server = http2.createServer((req, res) => {
|
|
20439
|
+
const body = http2.STATUS_CODES[426];
|
|
20370
20440
|
res.writeHead(426, {
|
|
20371
20441
|
"Content-Length": body.length,
|
|
20372
20442
|
"Content-Type": "text/plain"
|
|
@@ -20653,7 +20723,7 @@ var require_websocket_server = __commonJS({
|
|
|
20653
20723
|
this.destroy();
|
|
20654
20724
|
}
|
|
20655
20725
|
function abortHandshake(socket, code, message, headers) {
|
|
20656
|
-
message = message ||
|
|
20726
|
+
message = message || http2.STATUS_CODES[code];
|
|
20657
20727
|
headers = {
|
|
20658
20728
|
Connection: "close",
|
|
20659
20729
|
"Content-Type": "text/html",
|
|
@@ -20662,7 +20732,7 @@ var require_websocket_server = __commonJS({
|
|
|
20662
20732
|
};
|
|
20663
20733
|
socket.once("finish", socket.destroy);
|
|
20664
20734
|
socket.end(
|
|
20665
|
-
`HTTP/1.1 ${code} ${
|
|
20735
|
+
`HTTP/1.1 ${code} ${http2.STATUS_CODES[code]}\r
|
|
20666
20736
|
` + Object.keys(headers).map((h) => `${h}: ${headers[h]}`).join("\r\n") + "\r\n\r\n" + message
|
|
20667
20737
|
);
|
|
20668
20738
|
}
|
|
@@ -23555,8 +23625,8 @@ var require_utils = __commonJS({
|
|
|
23555
23625
|
var result = transform[inputType][outputType](input);
|
|
23556
23626
|
return result;
|
|
23557
23627
|
};
|
|
23558
|
-
exports2.resolve = function(
|
|
23559
|
-
var parts =
|
|
23628
|
+
exports2.resolve = function(path60) {
|
|
23629
|
+
var parts = path60.split("/");
|
|
23560
23630
|
var result = [];
|
|
23561
23631
|
for (var index = 0; index < parts.length; index++) {
|
|
23562
23632
|
var part = parts[index];
|
|
@@ -29409,18 +29479,18 @@ var require_object = __commonJS({
|
|
|
29409
29479
|
var object = new ZipObject(name, zipObjectContent, o);
|
|
29410
29480
|
this.files[name] = object;
|
|
29411
29481
|
};
|
|
29412
|
-
var parentFolder = function(
|
|
29413
|
-
if (
|
|
29414
|
-
|
|
29482
|
+
var parentFolder = function(path60) {
|
|
29483
|
+
if (path60.slice(-1) === "/") {
|
|
29484
|
+
path60 = path60.substring(0, path60.length - 1);
|
|
29415
29485
|
}
|
|
29416
|
-
var lastSlash =
|
|
29417
|
-
return lastSlash > 0 ?
|
|
29486
|
+
var lastSlash = path60.lastIndexOf("/");
|
|
29487
|
+
return lastSlash > 0 ? path60.substring(0, lastSlash) : "";
|
|
29418
29488
|
};
|
|
29419
|
-
var forceTrailingSlash = function(
|
|
29420
|
-
if (
|
|
29421
|
-
|
|
29489
|
+
var forceTrailingSlash = function(path60) {
|
|
29490
|
+
if (path60.slice(-1) !== "/") {
|
|
29491
|
+
path60 += "/";
|
|
29422
29492
|
}
|
|
29423
|
-
return
|
|
29493
|
+
return path60;
|
|
29424
29494
|
};
|
|
29425
29495
|
var folderAdd = function(name, createFolders) {
|
|
29426
29496
|
createFolders = typeof createFolders !== "undefined" ? createFolders : defaults.createFolders;
|
|
@@ -30422,7 +30492,7 @@ var require_lib3 = __commonJS({
|
|
|
30422
30492
|
// src/run-case/recorder.ts
|
|
30423
30493
|
function startRunCaseRecorder(opts) {
|
|
30424
30494
|
const now = opts.now ?? Date.now;
|
|
30425
|
-
const dir =
|
|
30495
|
+
const dir = import_node_path46.default.dirname(opts.recordPath);
|
|
30426
30496
|
let stream = null;
|
|
30427
30497
|
let closing = false;
|
|
30428
30498
|
let closedSettled = false;
|
|
@@ -30436,8 +30506,8 @@ function startRunCaseRecorder(opts) {
|
|
|
30436
30506
|
});
|
|
30437
30507
|
const ensureStream = () => {
|
|
30438
30508
|
if (stream) return stream;
|
|
30439
|
-
|
|
30440
|
-
stream =
|
|
30509
|
+
import_node_fs31.default.mkdirSync(dir, { recursive: true });
|
|
30510
|
+
stream = import_node_fs31.default.createWriteStream(opts.recordPath, { flags: "a" });
|
|
30441
30511
|
stream.on("close", () => closedResolve());
|
|
30442
30512
|
return stream;
|
|
30443
30513
|
};
|
|
@@ -30462,12 +30532,12 @@ function startRunCaseRecorder(opts) {
|
|
|
30462
30532
|
};
|
|
30463
30533
|
return { tap, close, closed };
|
|
30464
30534
|
}
|
|
30465
|
-
var
|
|
30535
|
+
var import_node_fs31, import_node_path46;
|
|
30466
30536
|
var init_recorder = __esm({
|
|
30467
30537
|
"src/run-case/recorder.ts"() {
|
|
30468
30538
|
"use strict";
|
|
30469
|
-
|
|
30470
|
-
|
|
30539
|
+
import_node_fs31 = __toESM(require("fs"), 1);
|
|
30540
|
+
import_node_path46 = __toESM(require("path"), 1);
|
|
30471
30541
|
}
|
|
30472
30542
|
});
|
|
30473
30543
|
|
|
@@ -30510,7 +30580,7 @@ var init_wire = __esm({
|
|
|
30510
30580
|
// src/run-case/controller.ts
|
|
30511
30581
|
async function runController(opts) {
|
|
30512
30582
|
const now = opts.now ?? Date.now;
|
|
30513
|
-
const cwd = opts.cwd ?? (0,
|
|
30583
|
+
const cwd = opts.cwd ?? (0, import_node_fs32.mkdtempSync)(import_node_path47.default.join(import_node_os19.default.tmpdir(), "clawd-runcase-"));
|
|
30514
30584
|
const ownsCwd = opts.cwd === void 0;
|
|
30515
30585
|
const recorder = startRunCaseRecorder({ recordPath: opts.record, now });
|
|
30516
30586
|
const spawnCtx = { cwd };
|
|
@@ -30671,19 +30741,19 @@ async function runController(opts) {
|
|
|
30671
30741
|
if (sigintHandler) process.off("SIGINT", sigintHandler);
|
|
30672
30742
|
if (ownsCwd) {
|
|
30673
30743
|
try {
|
|
30674
|
-
(0,
|
|
30744
|
+
(0, import_node_fs32.rmSync)(cwd, { recursive: true, force: true });
|
|
30675
30745
|
} catch {
|
|
30676
30746
|
}
|
|
30677
30747
|
}
|
|
30678
30748
|
return exitCode ?? 0;
|
|
30679
30749
|
}
|
|
30680
|
-
var
|
|
30750
|
+
var import_node_fs32, import_node_os19, import_node_path47;
|
|
30681
30751
|
var init_controller = __esm({
|
|
30682
30752
|
"src/run-case/controller.ts"() {
|
|
30683
30753
|
"use strict";
|
|
30684
|
-
|
|
30754
|
+
import_node_fs32 = require("fs");
|
|
30685
30755
|
import_node_os19 = __toESM(require("os"), 1);
|
|
30686
|
-
|
|
30756
|
+
import_node_path47 = __toESM(require("path"), 1);
|
|
30687
30757
|
init_claude();
|
|
30688
30758
|
init_stdout_splitter();
|
|
30689
30759
|
init_permission_stdio();
|
|
@@ -30875,8 +30945,6 @@ function resolveConfig(opts) {
|
|
|
30875
30945
|
const frpcBinary = env.CLAWD_FRPC_BIN ?? null;
|
|
30876
30946
|
const rawMode = env.CLAWD_CC_MODE;
|
|
30877
30947
|
const mode = DAEMON_MODE_VALUES.includes(rawMode ?? "") ? rawMode : "sdk";
|
|
30878
|
-
const filePreviewPorts = Array.isArray(fileCfg.previewPorts) ? fileCfg.previewPorts.map((n) => Number(n)).filter((n) => Number.isInteger(n) && n > 0) : null;
|
|
30879
|
-
const previewPorts = env.CLAWD_PREVIEW_PORTS ? env.CLAWD_PREVIEW_PORTS.split(",").map((s) => Number(s.trim())).filter((n) => Number.isInteger(n) && n > 0) : filePreviewPorts && filePreviewPorts.length > 0 ? filePreviewPorts : null;
|
|
30880
30948
|
return {
|
|
30881
30949
|
port,
|
|
30882
30950
|
host,
|
|
@@ -30888,8 +30956,7 @@ function resolveConfig(opts) {
|
|
|
30888
30956
|
authToken,
|
|
30889
30957
|
noTunnelPersist,
|
|
30890
30958
|
frpcBinary,
|
|
30891
|
-
mode
|
|
30892
|
-
previewPorts
|
|
30959
|
+
mode
|
|
30893
30960
|
};
|
|
30894
30961
|
}
|
|
30895
30962
|
var HELP_TEXT = `clawd [options]
|
|
@@ -30918,8 +30985,8 @@ Env (advanced):
|
|
|
30918
30985
|
`;
|
|
30919
30986
|
|
|
30920
30987
|
// src/index.ts
|
|
30921
|
-
var
|
|
30922
|
-
var
|
|
30988
|
+
var import_node_path45 = __toESM(require("path"), 1);
|
|
30989
|
+
var import_node_fs30 = __toESM(require("fs"), 1);
|
|
30923
30990
|
|
|
30924
30991
|
// src/logger.ts
|
|
30925
30992
|
var import_node_fs2 = __toESM(require("fs"), 1);
|
|
@@ -36260,63 +36327,13 @@ var import_websocket_server = __toESM(require_websocket_server(), 1);
|
|
|
36260
36327
|
var wrapper_default = import_websocket.default;
|
|
36261
36328
|
|
|
36262
36329
|
// src/transport/local-ws-server.ts
|
|
36263
|
-
var import_node_http2 = __toESM(require("http"), 1);
|
|
36264
|
-
|
|
36265
|
-
// src/transport/preview-proxy.ts
|
|
36266
36330
|
var import_node_http = __toESM(require("http"), 1);
|
|
36267
|
-
var import_node_net = __toESM(require("net"), 1);
|
|
36268
|
-
var PREVIEW_RE = /^\/preview\/(\d+)(?:\/.*)?$/;
|
|
36269
|
-
function matchPreviewPort(pathname) {
|
|
36270
|
-
const m2 = pathname.match(PREVIEW_RE);
|
|
36271
|
-
return m2 ? Number(m2[1]) : null;
|
|
36272
|
-
}
|
|
36273
|
-
function isPreviewPortAllowed(port, cfg) {
|
|
36274
|
-
return cfg.allowedPorts.includes(port);
|
|
36275
|
-
}
|
|
36276
|
-
function proxyPreviewHttp(req, res, port) {
|
|
36277
|
-
return new Promise((resolve6) => {
|
|
36278
|
-
const upstream = import_node_http.default.request(
|
|
36279
|
-
{ host: "localhost", port, method: req.method, path: req.url, headers: req.headers },
|
|
36280
|
-
(upRes) => {
|
|
36281
|
-
res.writeHead(upRes.statusCode || 502, upRes.headers);
|
|
36282
|
-
upRes.pipe(res);
|
|
36283
|
-
upRes.on("end", () => resolve6());
|
|
36284
|
-
}
|
|
36285
|
-
);
|
|
36286
|
-
upstream.on("error", (e) => {
|
|
36287
|
-
if (!res.headersSent) {
|
|
36288
|
-
res.writeHead(502, { "content-type": "text/plain; charset=utf-8" });
|
|
36289
|
-
}
|
|
36290
|
-
res.end(`preview upstream error: ${e.message}`);
|
|
36291
|
-
resolve6();
|
|
36292
|
-
});
|
|
36293
|
-
req.pipe(upstream);
|
|
36294
|
-
});
|
|
36295
|
-
}
|
|
36296
|
-
function proxyPreviewUpgrade(req, socket, head, port) {
|
|
36297
|
-
const upstream = import_node_net.default.connect(port, "localhost", () => {
|
|
36298
|
-
let raw = `${req.method} ${req.url} HTTP/1.1\r
|
|
36299
|
-
`;
|
|
36300
|
-
for (let i = 0; i < req.rawHeaders.length; i += 2) {
|
|
36301
|
-
raw += `${req.rawHeaders[i]}: ${req.rawHeaders[i + 1]}\r
|
|
36302
|
-
`;
|
|
36303
|
-
}
|
|
36304
|
-
raw += "\r\n";
|
|
36305
|
-
upstream.write(raw);
|
|
36306
|
-
if (head && head.length) upstream.write(head);
|
|
36307
|
-
upstream.pipe(socket);
|
|
36308
|
-
socket.pipe(upstream);
|
|
36309
|
-
});
|
|
36310
|
-
upstream.on("error", () => socket.destroy());
|
|
36311
|
-
socket.on("error", () => upstream.destroy());
|
|
36312
|
-
}
|
|
36313
|
-
|
|
36314
|
-
// src/transport/local-ws-server.ts
|
|
36315
36331
|
var LocalWsServer = class {
|
|
36316
36332
|
constructor(opts) {
|
|
36317
36333
|
this.opts = opts;
|
|
36318
36334
|
this.logger = opts.logger;
|
|
36319
36335
|
this.pingIntervalMs = opts.pingIntervalMs ?? 3e4;
|
|
36336
|
+
this.currentOwnerPrincipalId = opts.ownerPrincipalId;
|
|
36320
36337
|
}
|
|
36321
36338
|
opts;
|
|
36322
36339
|
wss = null;
|
|
@@ -36328,28 +36345,19 @@ var LocalWsServer = class {
|
|
|
36328
36345
|
capabilityIdToClients = /* @__PURE__ */ new Map();
|
|
36329
36346
|
logger;
|
|
36330
36347
|
pingIntervalMs;
|
|
36348
|
+
// 飞书热切换(spec 决策 #9,2026-06-01 拍板):登录后实时更新,broadcastToPrincipal
|
|
36349
|
+
// 路由用最新身份;初始值来自 opts.ownerPrincipalId
|
|
36350
|
+
currentOwnerPrincipalId;
|
|
36351
|
+
/** 飞书热切换:登录回调成功后由 daemon wiring 调用,更新 DM 路由身份 */
|
|
36352
|
+
setOwnerPrincipalId(id) {
|
|
36353
|
+
this.currentOwnerPrincipalId = id;
|
|
36354
|
+
}
|
|
36331
36355
|
async start() {
|
|
36332
36356
|
const host = this.opts.host ?? "127.0.0.1";
|
|
36333
36357
|
await new Promise((resolve6, reject) => {
|
|
36334
|
-
const httpServer =
|
|
36358
|
+
const httpServer = import_node_http.default.createServer((req, res) => this.handleHttpRequest(req, res));
|
|
36335
36359
|
const wss = new import_websocket_server.default({ noServer: true, clientTracking: true });
|
|
36336
36360
|
httpServer.on("upgrade", (req, socket, head) => {
|
|
36337
|
-
if (req.url?.startsWith("/preview/")) {
|
|
36338
|
-
const pathname = (() => {
|
|
36339
|
-
try {
|
|
36340
|
-
return new URL(req.url, "http://localhost").pathname;
|
|
36341
|
-
} catch {
|
|
36342
|
-
return "/";
|
|
36343
|
-
}
|
|
36344
|
-
})();
|
|
36345
|
-
const port = matchPreviewPort(pathname);
|
|
36346
|
-
if (port != null && (this.opts.previewPorts ?? []).includes(port)) {
|
|
36347
|
-
proxyPreviewUpgrade(req, socket, head, port);
|
|
36348
|
-
} else {
|
|
36349
|
-
socket.destroy();
|
|
36350
|
-
}
|
|
36351
|
-
return;
|
|
36352
|
-
}
|
|
36353
36361
|
wss.handleUpgrade(req, socket, head, (ws) => {
|
|
36354
36362
|
this.routeConnection(ws, req);
|
|
36355
36363
|
});
|
|
@@ -36463,7 +36471,7 @@ var LocalWsServer = class {
|
|
|
36463
36471
|
// principalId === 'cap_xxx' → 该 capability 的所有 guest ws (多端 / 多 tab)
|
|
36464
36472
|
// 用于 DM inbox:event 路由: from + to 两侧各播一次, 让双方 UI 实时看到 thread 更新.
|
|
36465
36473
|
broadcastToPrincipal(principalId, frame) {
|
|
36466
|
-
if (principalId === "owner" || principalId === this.
|
|
36474
|
+
if (principalId === "owner" || principalId === this.currentOwnerPrincipalId) {
|
|
36467
36475
|
this.broadcastToOwners(frame);
|
|
36468
36476
|
return;
|
|
36469
36477
|
}
|
|
@@ -36515,6 +36523,21 @@ var LocalWsServer = class {
|
|
|
36515
36523
|
getClientContext(clientId) {
|
|
36516
36524
|
return this.clients.get(clientId)?.ctx ?? null;
|
|
36517
36525
|
}
|
|
36526
|
+
// 飞书热切换(spec 决策 #9):身份切换后踢掉所有连接强制重连——在线连接的 ctx
|
|
36527
|
+
// (attachClientContext 时绑定)持有旧身份,重连后用新身份重建。
|
|
36528
|
+
// close code 4408(非 4401!4401 = token 撤销,客户端会停止重连):
|
|
36529
|
+
// 客户端按普通断连处理 → 自动重连 → 新身份 ctx。
|
|
36530
|
+
closeAllClients(code = 4408, reason = "IDENTITY_SWITCHED") {
|
|
36531
|
+
const ids = [...this.clients.keys()];
|
|
36532
|
+
for (const id of ids) {
|
|
36533
|
+
const c = this.clients.get(id);
|
|
36534
|
+
if (!c) continue;
|
|
36535
|
+
try {
|
|
36536
|
+
c.ws.close(code, reason);
|
|
36537
|
+
} catch {
|
|
36538
|
+
}
|
|
36539
|
+
}
|
|
36540
|
+
}
|
|
36518
36541
|
// Task 1.10 撤销级联:关闭某 capability 的所有活跃 ws 连接。close code 4401
|
|
36519
36542
|
// 让客户端识别 'capability revoked' 而非普通断连。
|
|
36520
36543
|
closeConnectionsByCapability(capabilityId, code = 4401, reason = "TOKEN_REVOKED") {
|
|
@@ -37997,13 +38020,23 @@ function createHttpRouter(deps) {
|
|
|
37997
38020
|
});
|
|
37998
38021
|
return true;
|
|
37999
38022
|
}
|
|
38000
|
-
if (url.pathname
|
|
38001
|
-
|
|
38002
|
-
|
|
38003
|
-
|
|
38023
|
+
if (url.pathname === "/auth/callback") {
|
|
38024
|
+
if (req.method !== "GET") {
|
|
38025
|
+
sendJson(res, 404, { code: "NOT_FOUND", message: `no route for ${req.method} ${url.pathname}` });
|
|
38026
|
+
return true;
|
|
38027
|
+
}
|
|
38028
|
+
if (!deps.feishuLogin) {
|
|
38029
|
+
sendJson(res, 501, { code: "NOT_IMPLEMENTED", message: "feishu login not wired" });
|
|
38004
38030
|
return true;
|
|
38005
38031
|
}
|
|
38006
|
-
|
|
38032
|
+
const result = await deps.feishuLogin.handleLoginCallback({
|
|
38033
|
+
token: url.searchParams.get("token") ?? "",
|
|
38034
|
+
state: url.searchParams.get("state") ?? "",
|
|
38035
|
+
expiresAt: url.searchParams.get("expires_at")
|
|
38036
|
+
});
|
|
38037
|
+
const html = result.ok ? `<!doctype html><meta charset="utf-8"><title>clawd</title><style>body{font-family:-apple-system,sans-serif;padding:48px;color:#222}</style><h2>\u767B\u5F55\u6210\u529F\uFF0C\u53EF\u5173\u95ED\u6B64\u9875\u3002</h2><p>\u8EAB\u4EFD\uFF1A${escapeHtml(result.identity.displayName)}</p><script>setTimeout(()=>window.close(),1500)</script>` : `<!doctype html><meta charset="utf-8"><title>clawd</title><style>body{font-family:-apple-system,sans-serif;padding:48px;color:#922}</style><h2>\u767B\u5F55\u5931\u8D25</h2><p>${escapeHtml(result.reason)}</p>`;
|
|
38038
|
+
res.writeHead(result.ok ? 200 : 400, { "Content-Type": "text/html; charset=utf-8" });
|
|
38039
|
+
res.end(html);
|
|
38007
38040
|
return true;
|
|
38008
38041
|
}
|
|
38009
38042
|
if (!url.pathname.startsWith("/session/") && !url.pathname.startsWith("/files/") && url.pathname !== "/attachments/upload" && url.pathname !== "/extensions/import") {
|
|
@@ -38241,6 +38274,9 @@ function parseUrl(rawUrl) {
|
|
|
38241
38274
|
return null;
|
|
38242
38275
|
}
|
|
38243
38276
|
}
|
|
38277
|
+
function escapeHtml(s) {
|
|
38278
|
+
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
38279
|
+
}
|
|
38244
38280
|
function sendJson(res, status, body, extraHeaders) {
|
|
38245
38281
|
res.writeHead(status, {
|
|
38246
38282
|
"Content-Type": "application/json; charset=utf-8",
|
|
@@ -39263,6 +39299,160 @@ function loadOwnerDisplayName(dataDir) {
|
|
|
39263
39299
|
return displayName;
|
|
39264
39300
|
}
|
|
39265
39301
|
|
|
39302
|
+
// src/feishu-auth/owner-identity-store.ts
|
|
39303
|
+
var import_node_fs27 = __toESM(require("fs"), 1);
|
|
39304
|
+
var import_node_path31 = __toESM(require("path"), 1);
|
|
39305
|
+
var OWNER_IDENTITY_FILE_NAME = "owner-identity.json";
|
|
39306
|
+
var OwnerIdentityStore = class {
|
|
39307
|
+
file;
|
|
39308
|
+
constructor(dataDir) {
|
|
39309
|
+
this.file = import_node_path31.default.join(dataDir, OWNER_IDENTITY_FILE_NAME);
|
|
39310
|
+
}
|
|
39311
|
+
read() {
|
|
39312
|
+
let raw;
|
|
39313
|
+
try {
|
|
39314
|
+
raw = import_node_fs27.default.readFileSync(this.file, "utf8");
|
|
39315
|
+
} catch {
|
|
39316
|
+
return null;
|
|
39317
|
+
}
|
|
39318
|
+
let parsed;
|
|
39319
|
+
try {
|
|
39320
|
+
parsed = JSON.parse(raw);
|
|
39321
|
+
} catch {
|
|
39322
|
+
return null;
|
|
39323
|
+
}
|
|
39324
|
+
const r = parsed;
|
|
39325
|
+
if (!r.identity || typeof r.identity.unionId !== "string" || r.identity.unionId.length === 0 || typeof r.identity.displayName !== "string" || r.identity.displayName.length === 0 || typeof r.ttcToken !== "string" || r.ttcToken.length === 0) {
|
|
39326
|
+
return null;
|
|
39327
|
+
}
|
|
39328
|
+
return {
|
|
39329
|
+
identity: {
|
|
39330
|
+
unionId: r.identity.unionId,
|
|
39331
|
+
displayName: r.identity.displayName,
|
|
39332
|
+
...typeof r.identity.avatarUrl === "string" ? { avatarUrl: r.identity.avatarUrl } : {}
|
|
39333
|
+
},
|
|
39334
|
+
ttcToken: r.ttcToken,
|
|
39335
|
+
ttcTokenExpiresAt: typeof r.ttcTokenExpiresAt === "number" ? r.ttcTokenExpiresAt : null,
|
|
39336
|
+
loginAt: typeof r.loginAt === "string" ? r.loginAt : (/* @__PURE__ */ new Date(0)).toISOString()
|
|
39337
|
+
};
|
|
39338
|
+
}
|
|
39339
|
+
write(record) {
|
|
39340
|
+
import_node_fs27.default.mkdirSync(import_node_path31.default.dirname(this.file), { recursive: true });
|
|
39341
|
+
import_node_fs27.default.writeFileSync(this.file, JSON.stringify(record, null, 2), { mode: 384 });
|
|
39342
|
+
try {
|
|
39343
|
+
import_node_fs27.default.chmodSync(this.file, 384);
|
|
39344
|
+
} catch {
|
|
39345
|
+
}
|
|
39346
|
+
}
|
|
39347
|
+
clear() {
|
|
39348
|
+
try {
|
|
39349
|
+
import_node_fs27.default.unlinkSync(this.file);
|
|
39350
|
+
} catch (err) {
|
|
39351
|
+
const code = err?.code;
|
|
39352
|
+
if (code !== "ENOENT") throw err;
|
|
39353
|
+
}
|
|
39354
|
+
}
|
|
39355
|
+
};
|
|
39356
|
+
|
|
39357
|
+
// src/feishu-auth/login-flow.ts
|
|
39358
|
+
var import_node_crypto11 = __toESM(require("crypto"), 1);
|
|
39359
|
+
var STATE_TTL_MS = 5 * 60 * 1e3;
|
|
39360
|
+
var LoginFlow = class {
|
|
39361
|
+
constructor(deps) {
|
|
39362
|
+
this.deps = deps;
|
|
39363
|
+
}
|
|
39364
|
+
deps;
|
|
39365
|
+
pendingStates = /* @__PURE__ */ new Map();
|
|
39366
|
+
start() {
|
|
39367
|
+
const state = import_node_crypto11.default.randomBytes(16).toString("base64url");
|
|
39368
|
+
const now = (this.deps.now ?? Date.now)();
|
|
39369
|
+
this.pendingStates.set(state, now);
|
|
39370
|
+
this.gcExpired(now);
|
|
39371
|
+
const url = new URL("/auth/authorize", this.deps.ttcAuthBase);
|
|
39372
|
+
url.searchParams.set("callback_url", this.deps.getCallbackUrl());
|
|
39373
|
+
url.searchParams.set("state", state);
|
|
39374
|
+
url.searchParams.set("auto", "1");
|
|
39375
|
+
return { authUrl: url.toString(), state };
|
|
39376
|
+
}
|
|
39377
|
+
async handleCallback(params) {
|
|
39378
|
+
const now = (this.deps.now ?? Date.now)();
|
|
39379
|
+
const issuedAt = this.pendingStates.get(params.state);
|
|
39380
|
+
if (issuedAt === void 0) {
|
|
39381
|
+
return { ok: false, reason: "state mismatch" };
|
|
39382
|
+
}
|
|
39383
|
+
this.pendingStates.delete(params.state);
|
|
39384
|
+
if (now - issuedAt > STATE_TTL_MS) {
|
|
39385
|
+
return { ok: false, reason: "state expired" };
|
|
39386
|
+
}
|
|
39387
|
+
if (!params.token) {
|
|
39388
|
+
return { ok: false, reason: "missing token" };
|
|
39389
|
+
}
|
|
39390
|
+
let identity;
|
|
39391
|
+
try {
|
|
39392
|
+
identity = await this.deps.fetchUserInfo(params.token);
|
|
39393
|
+
} catch (err) {
|
|
39394
|
+
return { ok: false, reason: `user_info failed: ${err.message}` };
|
|
39395
|
+
}
|
|
39396
|
+
const expiresAtNum = params.expiresAt ? Number.parseInt(params.expiresAt, 10) : NaN;
|
|
39397
|
+
this.deps.store.write({
|
|
39398
|
+
identity,
|
|
39399
|
+
ttcToken: params.token,
|
|
39400
|
+
ttcTokenExpiresAt: Number.isFinite(expiresAtNum) ? expiresAtNum : null,
|
|
39401
|
+
loginAt: new Date(now).toISOString()
|
|
39402
|
+
});
|
|
39403
|
+
return { ok: true, identity };
|
|
39404
|
+
}
|
|
39405
|
+
gcExpired(now) {
|
|
39406
|
+
for (const [state, issuedAt] of this.pendingStates) {
|
|
39407
|
+
if (now - issuedAt > STATE_TTL_MS) this.pendingStates.delete(state);
|
|
39408
|
+
}
|
|
39409
|
+
}
|
|
39410
|
+
};
|
|
39411
|
+
|
|
39412
|
+
// src/feishu-auth/ttc-client.ts
|
|
39413
|
+
var TTC_HOSTS = {
|
|
39414
|
+
auth: "https://app.ttcadvisory.com",
|
|
39415
|
+
api: "https://api.ttcadvisory.com"
|
|
39416
|
+
};
|
|
39417
|
+
var TtcUserInfoError = class extends Error {
|
|
39418
|
+
constructor(code, message) {
|
|
39419
|
+
super(message);
|
|
39420
|
+
this.code = code;
|
|
39421
|
+
this.name = "TtcUserInfoError";
|
|
39422
|
+
}
|
|
39423
|
+
code;
|
|
39424
|
+
};
|
|
39425
|
+
async function fetchTtcUserInfo(opts) {
|
|
39426
|
+
const doFetch = opts.fetchImpl ?? fetch;
|
|
39427
|
+
let res;
|
|
39428
|
+
try {
|
|
39429
|
+
res = await doFetch(`${opts.apiBase}/api/user_service/v1/login/user`, {
|
|
39430
|
+
headers: { Authorization: `Bearer ${opts.token}` }
|
|
39431
|
+
});
|
|
39432
|
+
} catch (err) {
|
|
39433
|
+
throw new TtcUserInfoError("NETWORK", `TTC user_info request failed: ${err.message}`);
|
|
39434
|
+
}
|
|
39435
|
+
if (!res.ok) {
|
|
39436
|
+
if (res.status === 401) {
|
|
39437
|
+
throw new TtcUserInfoError("UNAUTHORIZED", "TTC token invalid or expired");
|
|
39438
|
+
}
|
|
39439
|
+
throw new TtcUserInfoError("API_ERROR", `TTC user_info HTTP ${res.status}`);
|
|
39440
|
+
}
|
|
39441
|
+
const body = await res.json();
|
|
39442
|
+
if (body.code !== 0) {
|
|
39443
|
+
throw new TtcUserInfoError("API_ERROR", `TTC user_info code=${body.code}: ${body.message ?? ""}`);
|
|
39444
|
+
}
|
|
39445
|
+
const d = body.data;
|
|
39446
|
+
if (!d || typeof d.union_id !== "string" || d.union_id.length === 0 || typeof d.name !== "string" || d.name.length === 0) {
|
|
39447
|
+
throw new TtcUserInfoError("BAD_RESPONSE", "TTC user_info missing union_id or name");
|
|
39448
|
+
}
|
|
39449
|
+
return {
|
|
39450
|
+
unionId: d.union_id,
|
|
39451
|
+
displayName: d.name,
|
|
39452
|
+
...typeof d.avatar_url === "string" && d.avatar_url.length > 0 ? { avatarUrl: d.avatar_url } : {}
|
|
39453
|
+
};
|
|
39454
|
+
}
|
|
39455
|
+
|
|
39266
39456
|
// src/index.ts
|
|
39267
39457
|
init_protocol();
|
|
39268
39458
|
|
|
@@ -39270,12 +39460,12 @@ init_protocol();
|
|
|
39270
39460
|
init_protocol();
|
|
39271
39461
|
|
|
39272
39462
|
// src/session/fork.ts
|
|
39273
|
-
var
|
|
39463
|
+
var import_node_fs28 = __toESM(require("fs"), 1);
|
|
39274
39464
|
var import_node_os14 = __toESM(require("os"), 1);
|
|
39275
|
-
var
|
|
39465
|
+
var import_node_path32 = __toESM(require("path"), 1);
|
|
39276
39466
|
init_claude_history();
|
|
39277
39467
|
function readJsonlEntries(file) {
|
|
39278
|
-
const raw =
|
|
39468
|
+
const raw = import_node_fs28.default.readFileSync(file, "utf8");
|
|
39279
39469
|
const out = [];
|
|
39280
39470
|
for (const line of raw.split("\n")) {
|
|
39281
39471
|
const t = line.trim();
|
|
@@ -39288,10 +39478,10 @@ function readJsonlEntries(file) {
|
|
|
39288
39478
|
return out;
|
|
39289
39479
|
}
|
|
39290
39480
|
function forkSession(input) {
|
|
39291
|
-
const baseDir = input.baseDir ??
|
|
39292
|
-
const projectDir =
|
|
39293
|
-
const sourceFile =
|
|
39294
|
-
if (!
|
|
39481
|
+
const baseDir = input.baseDir ?? import_node_path32.default.join(import_node_os14.default.homedir(), ".claude");
|
|
39482
|
+
const projectDir = import_node_path32.default.join(baseDir, "projects", cwdToHashDir(input.cwd));
|
|
39483
|
+
const sourceFile = import_node_path32.default.join(projectDir, `${input.toolSessionId}.jsonl`);
|
|
39484
|
+
if (!import_node_fs28.default.existsSync(sourceFile)) {
|
|
39295
39485
|
throw new Error(`fork: source transcript not found: ${sourceFile}`);
|
|
39296
39486
|
}
|
|
39297
39487
|
const entries = readJsonlEntries(sourceFile);
|
|
@@ -39321,9 +39511,9 @@ function forkSession(input) {
|
|
|
39321
39511
|
}
|
|
39322
39512
|
forkedLines.push(JSON.stringify(forked));
|
|
39323
39513
|
}
|
|
39324
|
-
const forkedFilePath =
|
|
39325
|
-
|
|
39326
|
-
|
|
39514
|
+
const forkedFilePath = import_node_path32.default.join(projectDir, `${forkedToolSessionId}.jsonl`);
|
|
39515
|
+
import_node_fs28.default.mkdirSync(projectDir, { recursive: true });
|
|
39516
|
+
import_node_fs28.default.writeFileSync(forkedFilePath, forkedLines.join("\n") + "\n", { mode: 384 });
|
|
39327
39517
|
return { forkedToolSessionId, forkedFilePath };
|
|
39328
39518
|
}
|
|
39329
39519
|
|
|
@@ -39675,7 +39865,7 @@ function buildPermissionHandlers(deps) {
|
|
|
39675
39865
|
}
|
|
39676
39866
|
|
|
39677
39867
|
// src/handlers/history.ts
|
|
39678
|
-
var
|
|
39868
|
+
var path42 = __toESM(require("path"), 1);
|
|
39679
39869
|
init_protocol();
|
|
39680
39870
|
|
|
39681
39871
|
// src/session/recent-dirs.ts
|
|
@@ -39693,7 +39883,7 @@ function listRecentDirs(store, limit = 50) {
|
|
|
39693
39883
|
}
|
|
39694
39884
|
|
|
39695
39885
|
// src/permission/persona-paths.ts
|
|
39696
|
-
var
|
|
39886
|
+
var path41 = __toESM(require("path"), 1);
|
|
39697
39887
|
function getAllowedPersonaIds(grants, action) {
|
|
39698
39888
|
const ids = /* @__PURE__ */ new Set();
|
|
39699
39889
|
for (const g2 of grants) {
|
|
@@ -39706,26 +39896,26 @@ function getAllowedPersonaIds(grants, action) {
|
|
|
39706
39896
|
return ids;
|
|
39707
39897
|
}
|
|
39708
39898
|
function isGuestPathAllowed(grants, absPath, personaRoot, action = "read") {
|
|
39709
|
-
const root =
|
|
39710
|
-
const target =
|
|
39711
|
-
const sep3 = root.endsWith(
|
|
39899
|
+
const root = path41.resolve(personaRoot);
|
|
39900
|
+
const target = path41.resolve(absPath);
|
|
39901
|
+
const sep3 = root.endsWith(path41.sep) ? "" : path41.sep;
|
|
39712
39902
|
if (!target.startsWith(root + sep3)) return false;
|
|
39713
|
-
const rel =
|
|
39903
|
+
const rel = path41.relative(root, target);
|
|
39714
39904
|
if (!rel || rel.startsWith("..")) return false;
|
|
39715
|
-
const personaId = rel.split(
|
|
39905
|
+
const personaId = rel.split(path41.sep)[0];
|
|
39716
39906
|
if (!personaId) return false;
|
|
39717
39907
|
const allowed = getAllowedPersonaIds(grants, action);
|
|
39718
39908
|
if (allowed === "*") return true;
|
|
39719
39909
|
return allowed.has(personaId);
|
|
39720
39910
|
}
|
|
39721
39911
|
function personaIdFromPath(absPath, personaRoot) {
|
|
39722
|
-
const root =
|
|
39723
|
-
const target =
|
|
39724
|
-
const sep3 = root.endsWith(
|
|
39912
|
+
const root = path41.resolve(personaRoot);
|
|
39913
|
+
const target = path41.resolve(absPath);
|
|
39914
|
+
const sep3 = root.endsWith(path41.sep) ? "" : path41.sep;
|
|
39725
39915
|
if (!target.startsWith(root + sep3)) return null;
|
|
39726
|
-
const rel =
|
|
39916
|
+
const rel = path41.relative(root, target);
|
|
39727
39917
|
if (!rel || rel.startsWith("..")) return null;
|
|
39728
|
-
const id = rel.split(
|
|
39918
|
+
const id = rel.split(path41.sep)[0];
|
|
39729
39919
|
return id || null;
|
|
39730
39920
|
}
|
|
39731
39921
|
|
|
@@ -39750,7 +39940,7 @@ function buildHistoryHandlers(deps) {
|
|
|
39750
39940
|
if (!pid) return false;
|
|
39751
39941
|
return isGuestPathAllowed(
|
|
39752
39942
|
ctx.grants,
|
|
39753
|
-
|
|
39943
|
+
path42.join(personaRoot, pid),
|
|
39754
39944
|
personaRoot,
|
|
39755
39945
|
"read"
|
|
39756
39946
|
);
|
|
@@ -39761,7 +39951,7 @@ function buildHistoryHandlers(deps) {
|
|
|
39761
39951
|
};
|
|
39762
39952
|
const list = async (frame, _client, ctx) => {
|
|
39763
39953
|
const args = HistoryListArgs.parse(frame);
|
|
39764
|
-
assertGuestPath(ctx,
|
|
39954
|
+
assertGuestPath(ctx, path42.resolve(args.projectPath), personaRoot, "history:list");
|
|
39765
39955
|
const sessions = await history.listSessions(args);
|
|
39766
39956
|
return { response: { type: "history:list", sessions } };
|
|
39767
39957
|
};
|
|
@@ -39793,13 +39983,13 @@ function buildHistoryHandlers(deps) {
|
|
|
39793
39983
|
};
|
|
39794
39984
|
const subagents = async (frame, _client, ctx) => {
|
|
39795
39985
|
const args = HistorySubagentsArgs.parse(frame);
|
|
39796
|
-
assertGuestPath(ctx,
|
|
39986
|
+
assertGuestPath(ctx, path42.resolve(args.cwd), personaRoot, "history:subagents");
|
|
39797
39987
|
const subs = await history.listSubagents(args);
|
|
39798
39988
|
return { response: { type: "history:subagents", subagents: subs } };
|
|
39799
39989
|
};
|
|
39800
39990
|
const subagentRead = async (frame, _client, ctx) => {
|
|
39801
39991
|
const args = HistorySubagentReadArgs.parse(frame);
|
|
39802
|
-
assertGuestPath(ctx,
|
|
39992
|
+
assertGuestPath(ctx, path42.resolve(args.cwd), personaRoot, "history:subagent-read");
|
|
39803
39993
|
const res = await history.readSubagent(args);
|
|
39804
39994
|
return { response: { type: "history:subagent-read", ...res } };
|
|
39805
39995
|
};
|
|
@@ -39807,7 +39997,7 @@ function buildHistoryHandlers(deps) {
|
|
|
39807
39997
|
const dirs = listRecentDirs(store);
|
|
39808
39998
|
if (ctx?.principal.kind === "guest" && personaRoot) {
|
|
39809
39999
|
const filtered = dirs.filter(
|
|
39810
|
-
(d) => isGuestPathAllowed(ctx.grants,
|
|
40000
|
+
(d) => isGuestPathAllowed(ctx.grants, path42.resolve(d.cwd), personaRoot, "read")
|
|
39811
40001
|
);
|
|
39812
40002
|
return { response: { type: "history:recentDirs", dirs: filtered } };
|
|
39813
40003
|
}
|
|
@@ -39824,7 +40014,7 @@ function buildHistoryHandlers(deps) {
|
|
|
39824
40014
|
}
|
|
39825
40015
|
|
|
39826
40016
|
// src/handlers/workspace.ts
|
|
39827
|
-
var
|
|
40017
|
+
var path43 = __toESM(require("path"), 1);
|
|
39828
40018
|
var os15 = __toESM(require("os"), 1);
|
|
39829
40019
|
init_protocol();
|
|
39830
40020
|
init_protocol();
|
|
@@ -39865,22 +40055,22 @@ function buildWorkspaceHandlers(deps) {
|
|
|
39865
40055
|
const args = WorkspaceListArgs.parse(frame);
|
|
39866
40056
|
const isGuest = ctx?.principal.kind === "guest";
|
|
39867
40057
|
const fallbackCwd = isGuest && personaRoot ? personaRoot : os15.homedir();
|
|
39868
|
-
const resolvedCwd =
|
|
39869
|
-
const target = args.path ?
|
|
40058
|
+
const resolvedCwd = path43.resolve(args.cwd ?? fallbackCwd);
|
|
40059
|
+
const target = args.path ? path43.resolve(resolvedCwd, args.path) : resolvedCwd;
|
|
39870
40060
|
assertGuestPath2(ctx, target, personaRoot, "workspace:list");
|
|
39871
40061
|
const res = workspace.list({ ...args, cwd: resolvedCwd });
|
|
39872
40062
|
return { response: { type: "workspace:list", ...res } };
|
|
39873
40063
|
};
|
|
39874
40064
|
const read = async (frame, _client, ctx) => {
|
|
39875
40065
|
const args = WorkspaceReadArgs.parse(frame);
|
|
39876
|
-
const target =
|
|
40066
|
+
const target = path43.isAbsolute(args.path) ? path43.resolve(args.path) : path43.resolve(args.cwd, args.path);
|
|
39877
40067
|
assertGuestPath2(ctx, target, personaRoot, "workspace:read");
|
|
39878
40068
|
const res = workspace.read(args);
|
|
39879
40069
|
return { response: { type: "workspace:read", ...res } };
|
|
39880
40070
|
};
|
|
39881
40071
|
const skillsList = async (frame, _client, ctx) => {
|
|
39882
40072
|
const args = SkillsListArgs.parse(frame);
|
|
39883
|
-
const cwdAbs =
|
|
40073
|
+
const cwdAbs = path43.resolve(args.cwd);
|
|
39884
40074
|
assertGuestPath2(ctx, cwdAbs, personaRoot, "skills:list");
|
|
39885
40075
|
const list2 = skills.list(args);
|
|
39886
40076
|
if (ctx?.principal.kind === "guest" && personaRoot) {
|
|
@@ -39892,7 +40082,7 @@ function buildWorkspaceHandlers(deps) {
|
|
|
39892
40082
|
};
|
|
39893
40083
|
const agentsList = async (frame, _client, ctx) => {
|
|
39894
40084
|
const args = AgentsListArgs.parse(frame);
|
|
39895
|
-
const cwdAbs =
|
|
40085
|
+
const cwdAbs = path43.resolve(args.cwd);
|
|
39896
40086
|
assertGuestPath2(ctx, cwdAbs, personaRoot, "agents:list");
|
|
39897
40087
|
const list2 = agents.list(args);
|
|
39898
40088
|
if (ctx?.principal.kind === "guest" && personaRoot) {
|
|
@@ -39911,20 +40101,20 @@ function buildWorkspaceHandlers(deps) {
|
|
|
39911
40101
|
}
|
|
39912
40102
|
|
|
39913
40103
|
// src/handlers/git.ts
|
|
39914
|
-
var
|
|
40104
|
+
var path45 = __toESM(require("path"), 1);
|
|
39915
40105
|
init_protocol();
|
|
39916
40106
|
init_protocol();
|
|
39917
40107
|
|
|
39918
40108
|
// src/workspace/git.ts
|
|
39919
40109
|
var import_node_child_process6 = require("child_process");
|
|
39920
|
-
var
|
|
39921
|
-
var
|
|
40110
|
+
var import_node_fs29 = __toESM(require("fs"), 1);
|
|
40111
|
+
var import_node_path33 = __toESM(require("path"), 1);
|
|
39922
40112
|
var import_node_util = require("util");
|
|
39923
40113
|
var pexec = (0, import_node_util.promisify)(import_node_child_process6.execFile);
|
|
39924
40114
|
function normalizePath(p2) {
|
|
39925
|
-
const resolved =
|
|
40115
|
+
const resolved = import_node_path33.default.resolve(p2);
|
|
39926
40116
|
try {
|
|
39927
|
-
return
|
|
40117
|
+
return import_node_fs29.default.realpathSync(resolved);
|
|
39928
40118
|
} catch {
|
|
39929
40119
|
return resolved;
|
|
39930
40120
|
}
|
|
@@ -39997,7 +40187,7 @@ async function listGitBranches(cwd) {
|
|
|
39997
40187
|
// src/handlers/git.ts
|
|
39998
40188
|
function assertGuestCwd(ctx, cwd, personaRoot, method) {
|
|
39999
40189
|
if (!ctx || ctx.principal.kind !== "guest" || !personaRoot) return;
|
|
40000
|
-
if (!isGuestPathAllowed(ctx.grants,
|
|
40190
|
+
if (!isGuestPathAllowed(ctx.grants, path45.resolve(cwd), personaRoot, "read")) {
|
|
40001
40191
|
throw new ClawdError(
|
|
40002
40192
|
ERROR_CODES.UNAUTHORIZED,
|
|
40003
40193
|
`guest ${ctx.principal.id} cannot ${method} cwd ${cwd}`
|
|
@@ -40359,6 +40549,36 @@ function buildWhoamiHandler(deps) {
|
|
|
40359
40549
|
};
|
|
40360
40550
|
}
|
|
40361
40551
|
|
|
40552
|
+
// src/handlers/feishu-auth.ts
|
|
40553
|
+
function buildFeishuAuthHandlers(deps) {
|
|
40554
|
+
const loginStart = async () => {
|
|
40555
|
+
const { authUrl, state } = deps.loginFlow.start();
|
|
40556
|
+
return {
|
|
40557
|
+
response: { type: "auth:login:start:ok", authUrl, state }
|
|
40558
|
+
};
|
|
40559
|
+
};
|
|
40560
|
+
const getIdentity = async () => {
|
|
40561
|
+
const record = deps.ownerIdentityStore.read();
|
|
40562
|
+
return {
|
|
40563
|
+
response: {
|
|
40564
|
+
type: "auth:getIdentity:ok",
|
|
40565
|
+
identity: record ? record.identity : null
|
|
40566
|
+
}
|
|
40567
|
+
};
|
|
40568
|
+
};
|
|
40569
|
+
const logout = async () => {
|
|
40570
|
+
deps.ownerIdentityStore.clear();
|
|
40571
|
+
return {
|
|
40572
|
+
response: { type: "auth:logout:ok" }
|
|
40573
|
+
};
|
|
40574
|
+
};
|
|
40575
|
+
return {
|
|
40576
|
+
"auth:login:start": loginStart,
|
|
40577
|
+
"auth:getIdentity": getIdentity,
|
|
40578
|
+
"auth:logout": logout
|
|
40579
|
+
};
|
|
40580
|
+
}
|
|
40581
|
+
|
|
40362
40582
|
// src/handlers/meta.ts
|
|
40363
40583
|
var import_node_os15 = __toESM(require("os"), 1);
|
|
40364
40584
|
init_protocol();
|
|
@@ -40486,7 +40706,7 @@ function buildPersonaHandlers(deps) {
|
|
|
40486
40706
|
}
|
|
40487
40707
|
|
|
40488
40708
|
// src/handlers/attachment.ts
|
|
40489
|
-
var
|
|
40709
|
+
var import_node_path34 = __toESM(require("path"), 1);
|
|
40490
40710
|
init_protocol();
|
|
40491
40711
|
init_protocol();
|
|
40492
40712
|
var DEFAULT_TTL_SECONDS = 24 * 3600;
|
|
@@ -40540,12 +40760,12 @@ function buildAttachmentHandlers(deps) {
|
|
|
40540
40760
|
`session ${args.sessionId} scope unresolved`
|
|
40541
40761
|
);
|
|
40542
40762
|
}
|
|
40543
|
-
const cwdAbs =
|
|
40544
|
-
const candidateAbs =
|
|
40763
|
+
const cwdAbs = import_node_path34.default.resolve(sessionFile.cwd);
|
|
40764
|
+
const candidateAbs = import_node_path34.default.isAbsolute(args.relPath) ? import_node_path34.default.resolve(args.relPath) : import_node_path34.default.resolve(cwdAbs, args.relPath);
|
|
40545
40765
|
assertGuestAttachmentPath(ctx, candidateAbs, deps.personaRoot, "attachment.signUrl");
|
|
40546
40766
|
const entries = deps.groupFileStore.list(scope, args.sessionId);
|
|
40547
40767
|
const entry = entries.find((e) => {
|
|
40548
|
-
const storedAbs =
|
|
40768
|
+
const storedAbs = import_node_path34.default.isAbsolute(e.relPath) ? import_node_path34.default.resolve(e.relPath) : import_node_path34.default.resolve(cwdAbs, e.relPath);
|
|
40549
40769
|
return storedAbs === candidateAbs && !e.stale;
|
|
40550
40770
|
});
|
|
40551
40771
|
if (!entry) {
|
|
@@ -40569,7 +40789,7 @@ function buildAttachmentHandlers(deps) {
|
|
|
40569
40789
|
if (!ctx || ctx.principal.kind !== "guest" || !deps.personaRoot || !deps.sessionStore) return;
|
|
40570
40790
|
const f = deps.sessionStore.read(sessionId);
|
|
40571
40791
|
if (!f) return;
|
|
40572
|
-
assertGuestAttachmentPath(ctx,
|
|
40792
|
+
assertGuestAttachmentPath(ctx, import_node_path34.default.resolve(f.cwd), deps.personaRoot, method);
|
|
40573
40793
|
}
|
|
40574
40794
|
const groupAdd = async (frame, _client, ctx) => {
|
|
40575
40795
|
if (!deps.groupFileStore || !deps.getSessionScope) {
|
|
@@ -40641,19 +40861,19 @@ function buildAttachmentHandlers(deps) {
|
|
|
40641
40861
|
|
|
40642
40862
|
// src/handlers/extension.ts
|
|
40643
40863
|
var import_promises7 = __toESM(require("fs/promises"), 1);
|
|
40644
|
-
var
|
|
40864
|
+
var import_node_path39 = __toESM(require("path"), 1);
|
|
40645
40865
|
init_protocol();
|
|
40646
40866
|
|
|
40647
40867
|
// src/extension/bundle-zip.ts
|
|
40648
40868
|
var import_promises4 = __toESM(require("fs/promises"), 1);
|
|
40649
|
-
var
|
|
40650
|
-
var
|
|
40869
|
+
var import_node_path35 = __toESM(require("path"), 1);
|
|
40870
|
+
var import_node_crypto12 = __toESM(require("crypto"), 1);
|
|
40651
40871
|
var import_jszip2 = __toESM(require_lib3(), 1);
|
|
40652
40872
|
async function bundleExtensionDir(dir) {
|
|
40653
40873
|
const entries = await listFilesSorted(dir);
|
|
40654
40874
|
const zip = new import_jszip2.default();
|
|
40655
40875
|
for (const rel of entries) {
|
|
40656
|
-
const abs =
|
|
40876
|
+
const abs = import_node_path35.default.join(dir, rel);
|
|
40657
40877
|
const content = await import_promises4.default.readFile(abs);
|
|
40658
40878
|
zip.file(rel, content, { date: FIXED_DATE });
|
|
40659
40879
|
}
|
|
@@ -40662,7 +40882,7 @@ async function bundleExtensionDir(dir) {
|
|
|
40662
40882
|
compression: "DEFLATE",
|
|
40663
40883
|
compressionOptions: { level: 6 }
|
|
40664
40884
|
});
|
|
40665
|
-
const sha256 =
|
|
40885
|
+
const sha256 = import_node_crypto12.default.createHash("sha256").update(buffer).digest("hex");
|
|
40666
40886
|
return { buffer, sha256 };
|
|
40667
40887
|
}
|
|
40668
40888
|
var FIXED_DATE = /* @__PURE__ */ new Date("2020-01-01T00:00:00.000Z");
|
|
@@ -40674,7 +40894,7 @@ async function listFilesSorted(rootDir) {
|
|
|
40674
40894
|
return out;
|
|
40675
40895
|
}
|
|
40676
40896
|
async function walk(absRoot, relPrefix, out) {
|
|
40677
|
-
const dirAbs =
|
|
40897
|
+
const dirAbs = import_node_path35.default.join(absRoot, relPrefix);
|
|
40678
40898
|
const entries = await import_promises4.default.readdir(dirAbs, { withFileTypes: true });
|
|
40679
40899
|
for (const e of entries) {
|
|
40680
40900
|
if (IGNORE_BASENAMES.has(e.name)) continue;
|
|
@@ -40728,25 +40948,25 @@ function computePublishCheck(args) {
|
|
|
40728
40948
|
|
|
40729
40949
|
// src/extension/install-flow.ts
|
|
40730
40950
|
var import_promises5 = __toESM(require("fs/promises"), 1);
|
|
40731
|
-
var
|
|
40951
|
+
var import_node_path37 = __toESM(require("path"), 1);
|
|
40732
40952
|
var import_node_os17 = __toESM(require("os"), 1);
|
|
40733
|
-
var
|
|
40953
|
+
var import_node_crypto13 = __toESM(require("crypto"), 1);
|
|
40734
40954
|
var import_jszip3 = __toESM(require_lib3(), 1);
|
|
40735
40955
|
|
|
40736
40956
|
// src/extension/paths.ts
|
|
40737
40957
|
var import_node_os16 = __toESM(require("os"), 1);
|
|
40738
|
-
var
|
|
40958
|
+
var import_node_path36 = __toESM(require("path"), 1);
|
|
40739
40959
|
function clawdHomeRoot(override) {
|
|
40740
|
-
return override ?? process.env.CLAWD_HOME ??
|
|
40960
|
+
return override ?? process.env.CLAWD_HOME ?? import_node_path36.default.join(import_node_os16.default.homedir(), ".clawd");
|
|
40741
40961
|
}
|
|
40742
40962
|
function extensionsRoot(override) {
|
|
40743
|
-
return
|
|
40963
|
+
return import_node_path36.default.join(clawdHomeRoot(override), "extensions");
|
|
40744
40964
|
}
|
|
40745
40965
|
function publishedChannelsFile(override) {
|
|
40746
|
-
return
|
|
40966
|
+
return import_node_path36.default.join(clawdHomeRoot(override), "extensions-published.json");
|
|
40747
40967
|
}
|
|
40748
40968
|
function bundleCacheRoot(override) {
|
|
40749
|
-
return
|
|
40969
|
+
return import_node_path36.default.join(clawdHomeRoot(override), "extension-bundles");
|
|
40750
40970
|
}
|
|
40751
40971
|
|
|
40752
40972
|
// src/extension/install-flow.ts
|
|
@@ -40759,7 +40979,7 @@ var InstallError = class extends Error {
|
|
|
40759
40979
|
};
|
|
40760
40980
|
async function installFromChannel(args, deps) {
|
|
40761
40981
|
const { channelRef, snapshotHash, bundleZip } = args;
|
|
40762
|
-
const computed =
|
|
40982
|
+
const computed = import_node_crypto13.default.createHash("sha256").update(bundleZip).digest("hex");
|
|
40763
40983
|
if (computed !== snapshotHash) {
|
|
40764
40984
|
throw new InstallError(
|
|
40765
40985
|
"HASH_MISMATCH",
|
|
@@ -40773,7 +40993,7 @@ async function installFromChannel(args, deps) {
|
|
|
40773
40993
|
throw new InstallError("ZIP_INVALID", `failed to load zip: ${e.message}`);
|
|
40774
40994
|
}
|
|
40775
40995
|
for (const name of Object.keys(zip.files)) {
|
|
40776
|
-
if (name.includes("..") || name.startsWith("/") ||
|
|
40996
|
+
if (name.includes("..") || name.startsWith("/") || import_node_path37.default.isAbsolute(name)) {
|
|
40777
40997
|
throw new InstallError("ZIP_INVALID", `unsafe zip entry: ${name}`);
|
|
40778
40998
|
}
|
|
40779
40999
|
}
|
|
@@ -40805,7 +41025,7 @@ async function installFromChannel(args, deps) {
|
|
|
40805
41025
|
);
|
|
40806
41026
|
}
|
|
40807
41027
|
const localExtId = namespacedExtId(ownerSlug, channelRef.ownerPrincipalId);
|
|
40808
|
-
const destDir =
|
|
41028
|
+
const destDir = import_node_path37.default.join(deps.extensionsRoot, localExtId);
|
|
40809
41029
|
let destExists = false;
|
|
40810
41030
|
try {
|
|
40811
41031
|
await import_promises5.default.access(destDir);
|
|
@@ -40819,16 +41039,16 @@ async function installFromChannel(args, deps) {
|
|
|
40819
41039
|
);
|
|
40820
41040
|
}
|
|
40821
41041
|
const stage = await import_promises5.default.mkdtemp(
|
|
40822
|
-
|
|
41042
|
+
import_node_path37.default.join(import_node_os17.default.tmpdir(), `clawd-ext-install-${localExtId}-`)
|
|
40823
41043
|
);
|
|
40824
41044
|
try {
|
|
40825
41045
|
for (const [name, entry] of Object.entries(zip.files)) {
|
|
40826
|
-
const dest =
|
|
41046
|
+
const dest = import_node_path37.default.join(stage, name);
|
|
40827
41047
|
if (entry.dir) {
|
|
40828
41048
|
await import_promises5.default.mkdir(dest, { recursive: true });
|
|
40829
41049
|
continue;
|
|
40830
41050
|
}
|
|
40831
|
-
await import_promises5.default.mkdir(
|
|
41051
|
+
await import_promises5.default.mkdir(import_node_path37.default.dirname(dest), { recursive: true });
|
|
40832
41052
|
if (name === "manifest.json") {
|
|
40833
41053
|
const rewritten = { ...parsed.data, id: localExtId };
|
|
40834
41054
|
await import_promises5.default.writeFile(dest, JSON.stringify(rewritten, null, 2));
|
|
@@ -40849,9 +41069,9 @@ async function installFromChannel(args, deps) {
|
|
|
40849
41069
|
|
|
40850
41070
|
// src/extension/update-flow.ts
|
|
40851
41071
|
var import_promises6 = __toESM(require("fs/promises"), 1);
|
|
40852
|
-
var
|
|
41072
|
+
var import_node_path38 = __toESM(require("path"), 1);
|
|
40853
41073
|
var import_node_os18 = __toESM(require("os"), 1);
|
|
40854
|
-
var
|
|
41074
|
+
var import_node_crypto14 = __toESM(require("crypto"), 1);
|
|
40855
41075
|
var import_jszip4 = __toESM(require_lib3(), 1);
|
|
40856
41076
|
var UpdateError = class extends Error {
|
|
40857
41077
|
constructor(code, message) {
|
|
@@ -40866,11 +41086,11 @@ async function updateFromChannel(args, deps) {
|
|
|
40866
41086
|
channelRef.extId,
|
|
40867
41087
|
channelRef.ownerPrincipalId
|
|
40868
41088
|
);
|
|
40869
|
-
const liveDir =
|
|
41089
|
+
const liveDir = import_node_path38.default.join(deps.extensionsRoot, localExtId);
|
|
40870
41090
|
const prevDir = `${liveDir}.prev`;
|
|
40871
41091
|
let existingVersion;
|
|
40872
41092
|
try {
|
|
40873
|
-
const raw = await import_promises6.default.readFile(
|
|
41093
|
+
const raw = await import_promises6.default.readFile(import_node_path38.default.join(liveDir, "manifest.json"), "utf8");
|
|
40874
41094
|
const parsed2 = ExtensionManifestSchema.safeParse(JSON.parse(raw));
|
|
40875
41095
|
if (!parsed2.success) {
|
|
40876
41096
|
throw new UpdateError(
|
|
@@ -40889,7 +41109,7 @@ async function updateFromChannel(args, deps) {
|
|
|
40889
41109
|
if (e instanceof UpdateError) throw e;
|
|
40890
41110
|
throw e;
|
|
40891
41111
|
}
|
|
40892
|
-
const computed =
|
|
41112
|
+
const computed = import_node_crypto14.default.createHash("sha256").update(bundleZip).digest("hex");
|
|
40893
41113
|
if (computed !== snapshotHash) {
|
|
40894
41114
|
throw new UpdateError(
|
|
40895
41115
|
"HASH_MISMATCH",
|
|
@@ -40903,7 +41123,7 @@ async function updateFromChannel(args, deps) {
|
|
|
40903
41123
|
throw new UpdateError("ZIP_INVALID", `failed to load zip: ${e.message}`);
|
|
40904
41124
|
}
|
|
40905
41125
|
for (const name of Object.keys(zip.files)) {
|
|
40906
|
-
if (name.includes("..") || name.startsWith("/") ||
|
|
41126
|
+
if (name.includes("..") || name.startsWith("/") || import_node_path38.default.isAbsolute(name)) {
|
|
40907
41127
|
throw new UpdateError("ZIP_INVALID", `unsafe zip entry: ${name}`);
|
|
40908
41128
|
}
|
|
40909
41129
|
}
|
|
@@ -40938,16 +41158,16 @@ async function updateFromChannel(args, deps) {
|
|
|
40938
41158
|
await import_promises6.default.rm(prevDir, { recursive: true, force: true });
|
|
40939
41159
|
await import_promises6.default.rename(liveDir, prevDir);
|
|
40940
41160
|
const stage = await import_promises6.default.mkdtemp(
|
|
40941
|
-
|
|
41161
|
+
import_node_path38.default.join(import_node_os18.default.tmpdir(), `clawd-ext-update-${localExtId}-`)
|
|
40942
41162
|
);
|
|
40943
41163
|
try {
|
|
40944
41164
|
for (const [name, entry] of Object.entries(zip.files)) {
|
|
40945
|
-
const dest =
|
|
41165
|
+
const dest = import_node_path38.default.join(stage, name);
|
|
40946
41166
|
if (entry.dir) {
|
|
40947
41167
|
await import_promises6.default.mkdir(dest, { recursive: true });
|
|
40948
41168
|
continue;
|
|
40949
41169
|
}
|
|
40950
|
-
await import_promises6.default.mkdir(
|
|
41170
|
+
await import_promises6.default.mkdir(import_node_path38.default.dirname(dest), { recursive: true });
|
|
40951
41171
|
if (name === "manifest.json") {
|
|
40952
41172
|
const rewritten = { ...parsed.data, id: localExtId };
|
|
40953
41173
|
await import_promises6.default.writeFile(dest, JSON.stringify(rewritten, null, 2));
|
|
@@ -41040,7 +41260,7 @@ async function rewriteManifestVersion(root, extId, newVersion, previousPublished
|
|
|
41040
41260
|
);
|
|
41041
41261
|
}
|
|
41042
41262
|
}
|
|
41043
|
-
const manifestPath =
|
|
41263
|
+
const manifestPath = import_node_path39.default.join(root, extId, "manifest.json");
|
|
41044
41264
|
const manifest = await readManifest(root, extId);
|
|
41045
41265
|
const next = { ...manifest, version: newVersion };
|
|
41046
41266
|
const tmp = `${manifestPath}.tmp`;
|
|
@@ -41048,7 +41268,7 @@ async function rewriteManifestVersion(root, extId, newVersion, previousPublished
|
|
|
41048
41268
|
await import_promises7.default.rename(tmp, manifestPath);
|
|
41049
41269
|
}
|
|
41050
41270
|
async function readManifest(root, extId) {
|
|
41051
|
-
const file =
|
|
41271
|
+
const file = import_node_path39.default.join(root, extId, "manifest.json");
|
|
41052
41272
|
let raw;
|
|
41053
41273
|
try {
|
|
41054
41274
|
raw = await import_promises7.default.readFile(file, "utf8");
|
|
@@ -41139,7 +41359,7 @@ function buildExtensionHandlers(deps) {
|
|
|
41139
41359
|
};
|
|
41140
41360
|
async function buildSnapshotMeta(extId) {
|
|
41141
41361
|
const manifest = await readManifest(deps.root, extId);
|
|
41142
|
-
const { sha256, buffer } = await bundleExtensionDir(
|
|
41362
|
+
const { sha256, buffer } = await bundleExtensionDir(import_node_path39.default.join(deps.root, extId));
|
|
41143
41363
|
return { manifest, contentHash: sha256, buffer };
|
|
41144
41364
|
}
|
|
41145
41365
|
const publish = async (frame, _client, ctx) => {
|
|
@@ -41321,7 +41541,7 @@ function buildExtensionHandlers(deps) {
|
|
|
41321
41541
|
|
|
41322
41542
|
// src/extension/registry.ts
|
|
41323
41543
|
var import_promises8 = __toESM(require("fs/promises"), 1);
|
|
41324
|
-
var
|
|
41544
|
+
var import_node_path40 = __toESM(require("path"), 1);
|
|
41325
41545
|
async function loadAll(root) {
|
|
41326
41546
|
let entries;
|
|
41327
41547
|
try {
|
|
@@ -41334,13 +41554,13 @@ async function loadAll(root) {
|
|
|
41334
41554
|
for (const ent of entries) {
|
|
41335
41555
|
if (!ent.isDirectory()) continue;
|
|
41336
41556
|
if (ent.name.startsWith(".")) continue;
|
|
41337
|
-
records.push(await loadOne(
|
|
41557
|
+
records.push(await loadOne(import_node_path40.default.join(root, ent.name), ent.name));
|
|
41338
41558
|
}
|
|
41339
41559
|
records.sort((a, b2) => a.extId < b2.extId ? -1 : a.extId > b2.extId ? 1 : 0);
|
|
41340
41560
|
return records;
|
|
41341
41561
|
}
|
|
41342
41562
|
async function loadOne(dir, dirName) {
|
|
41343
|
-
const manifestPath =
|
|
41563
|
+
const manifestPath = import_node_path40.default.join(dir, "manifest.json");
|
|
41344
41564
|
let raw;
|
|
41345
41565
|
try {
|
|
41346
41566
|
raw = await import_promises8.default.readFile(manifestPath, "utf8");
|
|
@@ -41385,7 +41605,7 @@ async function loadOne(dir, dirName) {
|
|
|
41385
41605
|
|
|
41386
41606
|
// src/extension/uninstall.ts
|
|
41387
41607
|
var import_promises9 = __toESM(require("fs/promises"), 1);
|
|
41388
|
-
var
|
|
41608
|
+
var import_node_path41 = __toESM(require("path"), 1);
|
|
41389
41609
|
var UninstallError = class extends Error {
|
|
41390
41610
|
constructor(code, message) {
|
|
41391
41611
|
super(message);
|
|
@@ -41394,7 +41614,7 @@ var UninstallError = class extends Error {
|
|
|
41394
41614
|
code;
|
|
41395
41615
|
};
|
|
41396
41616
|
async function uninstall(deps) {
|
|
41397
|
-
const dir =
|
|
41617
|
+
const dir = import_node_path41.default.join(deps.root, deps.extId);
|
|
41398
41618
|
try {
|
|
41399
41619
|
await import_promises9.default.access(dir);
|
|
41400
41620
|
} catch {
|
|
@@ -41440,6 +41660,7 @@ function buildMethodHandlers(deps) {
|
|
|
41440
41660
|
personaStore: deps.personaStore,
|
|
41441
41661
|
capabilityManager: deps.capabilityManager
|
|
41442
41662
|
}),
|
|
41663
|
+
...buildFeishuAuthHandlers(deps.feishuAuth),
|
|
41443
41664
|
...deps.attachment ? buildAttachmentHandlers(deps.attachment) : {},
|
|
41444
41665
|
...buildExtensionHandlers({
|
|
41445
41666
|
loadAll,
|
|
@@ -41581,7 +41802,12 @@ var METHOD_GRANT_MAP = {
|
|
|
41581
41802
|
"extension.fetchBundle": CAPABILITY_SCOPED,
|
|
41582
41803
|
// guest-self:guest 在自己 daemon 上触发安装与更新(无持久化 subscription 概念)
|
|
41583
41804
|
"extension.install-from-channel": ADMIN_ANY,
|
|
41584
|
-
"extension.update-from-channel": ADMIN_ANY
|
|
41805
|
+
"extension.update-from-channel": ADMIN_ANY,
|
|
41806
|
+
// ---- auth:* 飞书统一身份 Phase 1(owner-only) ----
|
|
41807
|
+
// 登录/登出/查身份都是 owner 本机操作,guest 不可调
|
|
41808
|
+
"auth:login:start": ADMIN_ANY,
|
|
41809
|
+
"auth:getIdentity": ADMIN_ANY,
|
|
41810
|
+
"auth:logout": ADMIN_ANY
|
|
41585
41811
|
};
|
|
41586
41812
|
function computeGrantForFrame(method, frame) {
|
|
41587
41813
|
const rule = METHOD_GRANT_MAP[method];
|
|
@@ -41598,11 +41824,11 @@ function computeGrantForFrame(method, frame) {
|
|
|
41598
41824
|
|
|
41599
41825
|
// src/extension/runtime.ts
|
|
41600
41826
|
var import_node_child_process7 = require("child_process");
|
|
41601
|
-
var
|
|
41827
|
+
var import_node_path42 = __toESM(require("path"), 1);
|
|
41602
41828
|
var import_promises10 = require("timers/promises");
|
|
41603
41829
|
|
|
41604
41830
|
// src/extension/port-allocator.ts
|
|
41605
|
-
var
|
|
41831
|
+
var import_node_net = __toESM(require("net"), 1);
|
|
41606
41832
|
var PortExhaustedError = class extends Error {
|
|
41607
41833
|
constructor(min, max) {
|
|
41608
41834
|
super(`no free port in [${min},${max}]`);
|
|
@@ -41615,7 +41841,7 @@ var PortExhaustedError = class extends Error {
|
|
|
41615
41841
|
};
|
|
41616
41842
|
function probe(port) {
|
|
41617
41843
|
return new Promise((resolve6) => {
|
|
41618
|
-
const srv =
|
|
41844
|
+
const srv = import_node_net.default.createServer();
|
|
41619
41845
|
srv.once("error", () => resolve6(false));
|
|
41620
41846
|
srv.once("listening", () => {
|
|
41621
41847
|
srv.close(() => resolve6(true));
|
|
@@ -41699,7 +41925,7 @@ var Runtime = class {
|
|
|
41699
41925
|
/\$CLAWOS_EXT_PORT/g,
|
|
41700
41926
|
String(port)
|
|
41701
41927
|
);
|
|
41702
|
-
const dir =
|
|
41928
|
+
const dir = import_node_path42.default.join(this.root, extId);
|
|
41703
41929
|
const env = {
|
|
41704
41930
|
...process.env,
|
|
41705
41931
|
CLAWOS_EXT_PORT: String(port),
|
|
@@ -41811,7 +42037,7 @@ ${handle.stderrTail}`
|
|
|
41811
42037
|
|
|
41812
42038
|
// src/extension/published-channels.ts
|
|
41813
42039
|
var import_promises11 = __toESM(require("fs/promises"), 1);
|
|
41814
|
-
var
|
|
42040
|
+
var import_node_path43 = __toESM(require("path"), 1);
|
|
41815
42041
|
init_zod();
|
|
41816
42042
|
var PublishedChannelsError = class extends Error {
|
|
41817
42043
|
constructor(code, message) {
|
|
@@ -41910,7 +42136,7 @@ var PublishedChannelStore = class {
|
|
|
41910
42136
|
)
|
|
41911
42137
|
};
|
|
41912
42138
|
const tmp = `${this.filePath}.tmp`;
|
|
41913
|
-
await import_promises11.default.mkdir(
|
|
42139
|
+
await import_promises11.default.mkdir(import_node_path43.default.dirname(this.filePath), { recursive: true });
|
|
41914
42140
|
await import_promises11.default.writeFile(tmp, JSON.stringify(data, null, 2), { mode: 384 });
|
|
41915
42141
|
await import_promises11.default.rename(tmp, this.filePath);
|
|
41916
42142
|
}
|
|
@@ -41918,7 +42144,7 @@ var PublishedChannelStore = class {
|
|
|
41918
42144
|
|
|
41919
42145
|
// src/extension/bundle-cache.ts
|
|
41920
42146
|
var import_promises12 = __toESM(require("fs/promises"), 1);
|
|
41921
|
-
var
|
|
42147
|
+
var import_node_path44 = __toESM(require("path"), 1);
|
|
41922
42148
|
var BundleCache = class {
|
|
41923
42149
|
constructor(rootDir) {
|
|
41924
42150
|
this.rootDir = rootDir;
|
|
@@ -41927,14 +42153,14 @@ var BundleCache = class {
|
|
|
41927
42153
|
/** Atomic write: stage tmp → rename. Caller passes the hex sha256. */
|
|
41928
42154
|
async write(snapshotHash, buffer) {
|
|
41929
42155
|
await import_promises12.default.mkdir(this.rootDir, { recursive: true });
|
|
41930
|
-
const file =
|
|
42156
|
+
const file = import_node_path44.default.join(this.rootDir, `${snapshotHash}.zip`);
|
|
41931
42157
|
const tmp = `${file}.tmp`;
|
|
41932
42158
|
await import_promises12.default.writeFile(tmp, buffer, { mode: 384 });
|
|
41933
42159
|
await import_promises12.default.rename(tmp, file);
|
|
41934
42160
|
}
|
|
41935
42161
|
/** Returns the bundle bytes, or null when the file doesn't exist. */
|
|
41936
42162
|
async read(snapshotHash) {
|
|
41937
|
-
const file =
|
|
42163
|
+
const file = import_node_path44.default.join(this.rootDir, `${snapshotHash}.zip`);
|
|
41938
42164
|
try {
|
|
41939
42165
|
return await import_promises12.default.readFile(file);
|
|
41940
42166
|
} catch (e) {
|
|
@@ -41944,7 +42170,7 @@ var BundleCache = class {
|
|
|
41944
42170
|
}
|
|
41945
42171
|
/** Idempotent — missing file is not an error. */
|
|
41946
42172
|
async delete(snapshotHash) {
|
|
41947
|
-
const file =
|
|
42173
|
+
const file = import_node_path44.default.join(this.rootDir, `${snapshotHash}.zip`);
|
|
41948
42174
|
await import_promises12.default.rm(file, { force: true });
|
|
41949
42175
|
}
|
|
41950
42176
|
};
|
|
@@ -41953,7 +42179,7 @@ var BundleCache = class {
|
|
|
41953
42179
|
async function startDaemon(config) {
|
|
41954
42180
|
const logger = createLogger({
|
|
41955
42181
|
level: config.logLevel,
|
|
41956
|
-
file:
|
|
42182
|
+
file: import_node_path45.default.join(config.dataDir, "clawd.log")
|
|
41957
42183
|
});
|
|
41958
42184
|
logger.info("starting clawd", { version, config: { port: config.port, host: config.host, dataDir: config.dataDir } });
|
|
41959
42185
|
const stateMgr = new StateFileManager({ dataDir: config.dataDir });
|
|
@@ -41971,8 +42197,11 @@ async function startDaemon(config) {
|
|
|
41971
42197
|
} else if (config.tunnel) {
|
|
41972
42198
|
resolvedAuthToken = authFile.token;
|
|
41973
42199
|
}
|
|
41974
|
-
const
|
|
41975
|
-
const
|
|
42200
|
+
const ownerIdentityStore = new OwnerIdentityStore(config.dataDir);
|
|
42201
|
+
const feishuIdentity = ownerIdentityStore.read();
|
|
42202
|
+
let feishuActive = feishuIdentity !== null;
|
|
42203
|
+
let ownerPrincipalId = feishuIdentity?.identity.unionId ?? authFile.ownerPrincipalId;
|
|
42204
|
+
let ownerDisplayName = feishuIdentity?.identity.displayName ?? loadOwnerDisplayName(config.dataDir);
|
|
41976
42205
|
const authMode = resolvedAuthToken == null ? "none" : "first-message";
|
|
41977
42206
|
const inboxStore = new InboxStore(config.dataDir);
|
|
41978
42207
|
const inboxManager = new InboxManager(inboxStore, (_peerOwnerId, frame) => {
|
|
@@ -42014,7 +42243,9 @@ async function startDaemon(config) {
|
|
|
42014
42243
|
isOwnerToken: (x) => resolvedAuthToken != null && constantTimeEqual(x, resolvedAuthToken),
|
|
42015
42244
|
ownerPrincipalId,
|
|
42016
42245
|
ownerDisplayName,
|
|
42017
|
-
|
|
42246
|
+
// 飞书 gate(spec 决策 #9):未激活飞书身份时不接受 guest(capability token)入站——
|
|
42247
|
+
// 不传 registry 让 guest token 一律 BAD_TOKEN。owner 自己连接不受影响。
|
|
42248
|
+
...feishuActive ? { capabilityRegistry } : {},
|
|
42018
42249
|
selfPrincipalId,
|
|
42019
42250
|
selfDisplayName
|
|
42020
42251
|
});
|
|
@@ -42040,7 +42271,7 @@ async function startDaemon(config) {
|
|
|
42040
42271
|
const agents = new AgentsScanner();
|
|
42041
42272
|
const history = new ClaudeHistoryReader();
|
|
42042
42273
|
let transport = null;
|
|
42043
|
-
const personaStore = new PersonaStore(
|
|
42274
|
+
const personaStore = new PersonaStore(import_node_path45.default.join(config.dataDir, "personas"));
|
|
42044
42275
|
const defaultsRoot = findDefaultsRoot();
|
|
42045
42276
|
if (defaultsRoot) {
|
|
42046
42277
|
seedDefaultPersonas({ store: personaStore, defaultsRoot, logger });
|
|
@@ -42057,7 +42288,7 @@ async function startDaemon(config) {
|
|
|
42057
42288
|
getAdapter,
|
|
42058
42289
|
historyReader: history,
|
|
42059
42290
|
dataDir: config.dataDir,
|
|
42060
|
-
personaRoot:
|
|
42291
|
+
personaRoot: import_node_path45.default.join(config.dataDir, "personas"),
|
|
42061
42292
|
personaStore,
|
|
42062
42293
|
ownerDisplayName,
|
|
42063
42294
|
ownerPrincipalId,
|
|
@@ -42081,10 +42312,10 @@ async function startDaemon(config) {
|
|
|
42081
42312
|
// 文件可能 agent 写完又被自己删(罕见),用 size=0 / fallback mime 兜底。
|
|
42082
42313
|
attachmentGroup: {
|
|
42083
42314
|
onFileEdit: (input) => {
|
|
42084
|
-
const absPath =
|
|
42315
|
+
const absPath = import_node_path45.default.isAbsolute(input.relPath) ? input.relPath : import_node_path45.default.join(input.cwd, input.relPath);
|
|
42085
42316
|
let size = 0;
|
|
42086
42317
|
try {
|
|
42087
|
-
size =
|
|
42318
|
+
size = import_node_fs30.default.statSync(absPath).size;
|
|
42088
42319
|
} catch (err) {
|
|
42089
42320
|
logger.warn("attachment.onFileEdit stat failed", {
|
|
42090
42321
|
sessionId: input.sessionId,
|
|
@@ -42171,7 +42402,13 @@ async function startDaemon(config) {
|
|
|
42171
42402
|
const publishedChannelStore = new PublishedChannelStore(publishedChannelsFile());
|
|
42172
42403
|
await publishedChannelStore.load();
|
|
42173
42404
|
const bundleCache = new BundleCache(bundleCacheRoot());
|
|
42174
|
-
const
|
|
42405
|
+
const loginFlow = new LoginFlow({
|
|
42406
|
+
store: ownerIdentityStore,
|
|
42407
|
+
fetchUserInfo: (ttcToken) => fetchTtcUserInfo({ apiBase: TTC_HOSTS.api, token: ttcToken }),
|
|
42408
|
+
ttcAuthBase: TTC_HOSTS.auth,
|
|
42409
|
+
getCallbackUrl: () => `http://127.0.0.1:${config.port}/auth/callback`
|
|
42410
|
+
});
|
|
42411
|
+
const makeHandlers = () => buildMethodHandlers({
|
|
42175
42412
|
manager,
|
|
42176
42413
|
workspace,
|
|
42177
42414
|
skills,
|
|
@@ -42210,10 +42447,10 @@ async function startDaemon(config) {
|
|
|
42210
42447
|
// 'persona/<pid>/owner',default 走 'default'。
|
|
42211
42448
|
getSessionScope: (sid) => manager.findOwnedSessionScope(sid),
|
|
42212
42449
|
// guest path guard:candidate 必须在 personaRoot 子树下
|
|
42213
|
-
personaRoot:
|
|
42450
|
+
personaRoot: import_node_path45.default.join(config.dataDir, "personas")
|
|
42214
42451
|
},
|
|
42215
42452
|
// workspace/git/history/skills/agents handler 共用的 guest path guard 锚点
|
|
42216
|
-
personaRoot:
|
|
42453
|
+
personaRoot: import_node_path45.default.join(config.dataDir, "personas"),
|
|
42217
42454
|
// capability:list / delete handler 依赖
|
|
42218
42455
|
capabilityManager,
|
|
42219
42456
|
// personal-cap:get 返回的 shareBaseUrl 用此 base URL:
|
|
@@ -42243,8 +42480,11 @@ async function startDaemon(config) {
|
|
|
42243
42480
|
extensionRuntime,
|
|
42244
42481
|
// extension sharing v1 — owner-side published-channels store.
|
|
42245
42482
|
publishedChannelStore,
|
|
42246
|
-
bundleCache
|
|
42483
|
+
bundleCache,
|
|
42484
|
+
// 飞书统一身份 Phase 1:登录 RPC handlers(auth:login:start / getIdentity / logout)
|
|
42485
|
+
feishuAuth: { loginFlow, ownerIdentityStore }
|
|
42247
42486
|
});
|
|
42487
|
+
let handlers = makeHandlers();
|
|
42248
42488
|
const authResolver = new AuthContextResolver({
|
|
42249
42489
|
ownerToken: resolvedAuthToken
|
|
42250
42490
|
});
|
|
@@ -42270,8 +42510,29 @@ async function startDaemon(config) {
|
|
|
42270
42510
|
},
|
|
42271
42511
|
// POST /extensions/import lands in ~/.clawd/extensions/<id>/
|
|
42272
42512
|
extensionsRoot: () => extensionsRoot(),
|
|
42273
|
-
//
|
|
42274
|
-
|
|
42513
|
+
// 飞书登录 loopback 回调(热切换,2026-06-01 拍板):成功落盘后——
|
|
42514
|
+
// 1. 进程内实时切换身份(let binding + 值快照持有者重建)
|
|
42515
|
+
// 2. 广播 auth:login:done 给在线 owner 连接(UI 即时反馈)
|
|
42516
|
+
// 3. 踢掉所有 ws 连接(在线连接的 ctx 是旧身份)→ UI 自动重连 → 新身份 ctx →
|
|
42517
|
+
// People 立即解锁,不需要重启 daemon
|
|
42518
|
+
// 失败则广播 auth:login:failed。
|
|
42519
|
+
feishuLogin: {
|
|
42520
|
+
handleLoginCallback: async (params) => {
|
|
42521
|
+
const result = await loginFlow.handleCallback(params);
|
|
42522
|
+
if (result.ok) {
|
|
42523
|
+
feishuActive = true;
|
|
42524
|
+
ownerPrincipalId = result.identity.unionId;
|
|
42525
|
+
ownerDisplayName = result.identity.displayName;
|
|
42526
|
+
handlers = makeHandlers();
|
|
42527
|
+
wsServer?.setOwnerPrincipalId(result.identity.unionId);
|
|
42528
|
+
wsServer?.broadcastToOwners({ type: "auth:login:done", identity: result.identity });
|
|
42529
|
+
setImmediate(() => wsServer?.closeAllClients());
|
|
42530
|
+
} else {
|
|
42531
|
+
wsServer?.broadcastToOwners({ type: "auth:login:failed", reason: result.reason });
|
|
42532
|
+
}
|
|
42533
|
+
return result;
|
|
42534
|
+
}
|
|
42535
|
+
}
|
|
42275
42536
|
});
|
|
42276
42537
|
wsServer = new LocalWsServer({
|
|
42277
42538
|
host: config.host,
|
|
@@ -42310,8 +42571,6 @@ async function startDaemon(config) {
|
|
|
42310
42571
|
},
|
|
42311
42572
|
// file-sharing HTTP 路由复用 daemon 同端口(spec §5 第 3 条);router 自己处理 auth + 404
|
|
42312
42573
|
httpRequestHandler: httpRouter,
|
|
42313
|
-
// app-builder build 模式:/preview/<port>/ HMR websocket upgrade 转发的端口白名单(同 http-router 配置)
|
|
42314
|
-
previewPorts: config.previewPorts ?? void 0,
|
|
42315
42574
|
// 订阅成功后给该 client 重放 in-flight pendingQuestions(plan: clawd-question-server-truth)。
|
|
42316
42575
|
// daemon 是 pendingQuestions 的唯一 source of truth;新 client 接入 / 刷新页面时
|
|
42317
42576
|
// 把当前所有未决 question 以 session:question 帧定向回放,让 UI 不再误显示 Ended。
|
|
@@ -42356,6 +42615,12 @@ async function startDaemon(config) {
|
|
|
42356
42615
|
const requestId = typeof frame.requestId === "string" ? frame.requestId : void 0;
|
|
42357
42616
|
const handler = handlers[type];
|
|
42358
42617
|
if (!handler) throw new ClawdError(ERROR_CODES.METHOD_NOT_IMPLEMENTED, `not implemented: ${type}`);
|
|
42618
|
+
if (!feishuActive && FEISHU_GATED_METHODS.includes(type)) {
|
|
42619
|
+
throw new ClawdError(
|
|
42620
|
+
ERROR_CODES.FEISHU_LOGIN_REQUIRED,
|
|
42621
|
+
`${type} requires feishu login (People/remote identity)`
|
|
42622
|
+
);
|
|
42623
|
+
}
|
|
42359
42624
|
const ctx = wsServer.getClientContext(client.id) ?? ownerContext(ownerPrincipalId, ownerDisplayName);
|
|
42360
42625
|
const verdict = computeGrantForFrame(type, frame);
|
|
42361
42626
|
if (verdict.kind === "check") {
|
|
@@ -42428,13 +42693,12 @@ async function startDaemon(config) {
|
|
|
42428
42693
|
stateSnapshot = { ...stateSnapshot, tunnelUrl: r.url, tunnelError: void 0 };
|
|
42429
42694
|
stateMgr.write(stateSnapshot);
|
|
42430
42695
|
currentTunnelUrl = r.url;
|
|
42431
|
-
wss.broadcastAll({ type: "tunnel:ready", url: r.url, subdomain: r.subdomain });
|
|
42432
42696
|
const connectUrl = resolvedAuthToken ? `${r.url}#token=${resolvedAuthToken}` : r.url;
|
|
42433
42697
|
const lines = [
|
|
42434
42698
|
`Tunnel: ${r.url}`,
|
|
42435
42699
|
...resolvedAuthToken ? [`Connect: ${connectUrl}`] : [],
|
|
42436
|
-
`Frpc config: ${
|
|
42437
|
-
`Frpc log: ${
|
|
42700
|
+
`Frpc config: ${import_node_path45.default.join(config.dataDir, "frpc.toml")}`,
|
|
42701
|
+
`Frpc log: ${import_node_path45.default.join(config.dataDir, "frpc.log")}`
|
|
42438
42702
|
];
|
|
42439
42703
|
const width = Math.max(...lines.map((l) => l.length));
|
|
42440
42704
|
const bar = "\u2550".repeat(width + 4);
|
|
@@ -42447,8 +42711,8 @@ ${bar}
|
|
|
42447
42711
|
|
|
42448
42712
|
`);
|
|
42449
42713
|
try {
|
|
42450
|
-
const connectPath =
|
|
42451
|
-
|
|
42714
|
+
const connectPath = import_node_path45.default.join(config.dataDir, "connect.txt");
|
|
42715
|
+
import_node_fs30.default.writeFileSync(connectPath, lines.join("\n") + "\n", { mode: 384 });
|
|
42452
42716
|
} catch {
|
|
42453
42717
|
}
|
|
42454
42718
|
} catch (err) {
|
|
@@ -42514,9 +42778,9 @@ ${bar}
|
|
|
42514
42778
|
};
|
|
42515
42779
|
}
|
|
42516
42780
|
function migrateDropPersonsDir(dataDir) {
|
|
42517
|
-
const dir =
|
|
42781
|
+
const dir = import_node_path45.default.join(dataDir, "persons");
|
|
42518
42782
|
try {
|
|
42519
|
-
|
|
42783
|
+
import_node_fs30.default.rmSync(dir, { recursive: true, force: true });
|
|
42520
42784
|
} catch {
|
|
42521
42785
|
}
|
|
42522
42786
|
}
|