@clawos-dev/clawd 0.2.112-beta.213.6cb11a8 → 0.2.112-beta.215.872b507
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 +677 -730
- package/package.json +1 -1
- package/dist/persona-defaults/persona-app-builder/CLAUDE.md +0 -145
- package/dist/persona-defaults/persona-app-builder/extension-kit/README.md +0 -96
- package/dist/persona-defaults/persona-app-builder/extension-kit/config.env +0 -20
- package/dist/persona-defaults/persona-app-builder/extension-kit/contract/bootstrap +0 -22
- package/dist/persona-defaults/persona-app-builder/extension-kit/contract/s.yaml.tmpl +0 -54
- package/dist/persona-defaults/persona-app-builder/extension-kit/examples/nestjs-react/server/.env.example +0 -3
- package/dist/persona-defaults/persona-app-builder/extension-kit/examples/nestjs-react/server/.fcignore +0 -7
- package/dist/persona-defaults/persona-app-builder/extension-kit/examples/nestjs-react/server/nest-cli.json +0 -8
- package/dist/persona-defaults/persona-app-builder/extension-kit/examples/nestjs-react/server/package-lock.json +0 -4531
- package/dist/persona-defaults/persona-app-builder/extension-kit/examples/nestjs-react/server/package.json +0 -26
- package/dist/persona-defaults/persona-app-builder/extension-kit/examples/nestjs-react/server/src/app.module.ts +0 -14
- package/dist/persona-defaults/persona-app-builder/extension-kit/examples/nestjs-react/server/src/main.ts +0 -14
- package/dist/persona-defaults/persona-app-builder/extension-kit/examples/nestjs-react/server/src/messages/messages.controller.ts +0 -27
- package/dist/persona-defaults/persona-app-builder/extension-kit/examples/nestjs-react/server/src/messages/messages.module.ts +0 -9
- package/dist/persona-defaults/persona-app-builder/extension-kit/examples/nestjs-react/server/src/messages/messages.service.ts +0 -38
- package/dist/persona-defaults/persona-app-builder/extension-kit/examples/nestjs-react/server/src/polyfill.ts +0 -8
- package/dist/persona-defaults/persona-app-builder/extension-kit/examples/nestjs-react/server/tsconfig.json +0 -14
- package/dist/persona-defaults/persona-app-builder/extension-kit/examples/nestjs-react/web/index.html +0 -12
- package/dist/persona-defaults/persona-app-builder/extension-kit/examples/nestjs-react/web/package-lock.json +0 -1680
- package/dist/persona-defaults/persona-app-builder/extension-kit/examples/nestjs-react/web/package.json +0 -18
- package/dist/persona-defaults/persona-app-builder/extension-kit/examples/nestjs-react/web/src/App.jsx +0 -161
- package/dist/persona-defaults/persona-app-builder/extension-kit/examples/nestjs-react/web/src/main.jsx +0 -5
- package/dist/persona-defaults/persona-app-builder/extension-kit/examples/nestjs-react/web/vite.config.js +0 -31
- package/dist/persona-defaults/persona-app-builder/extension-kit/scripts/new-extension.sh +0 -49
- package/dist/persona-defaults/persona-app-builder/extension-kit/scripts/publish.sh +0 -78
- package/dist/persona-defaults/persona-app-builder/extension-kit/scripts/remove-extension.sh +0 -57
- package/dist/persona-defaults/persona-app-builder/extension-kit/scripts/verify.sh +0 -20
package/dist/cli.cjs
CHANGED
|
@@ -181,16 +181,17 @@ var init_methods = __esm({
|
|
|
181
181
|
// 无 subscription 持久化:guest 端身份完全来自文件系统 + reconciled publishedExtensions.
|
|
182
182
|
"extension.install-from-channel",
|
|
183
183
|
"extension.update-from-channel",
|
|
184
|
-
// ----
|
|
185
|
-
//
|
|
186
|
-
//
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
"
|
|
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",
|
|
190
|
+
// ---- hub:* 飞书统一身份 Phase 2 (spec §10.2) ----
|
|
191
|
+
// owner-only:公共 hub 目录 + per-hub connect token 连接(daemon 代理中心 server)。
|
|
192
|
+
// 中央字面量见 protocol/feishu-auth.ts HUB_METHODS。
|
|
193
|
+
"hub:list",
|
|
194
|
+
"hub:connect"
|
|
194
195
|
];
|
|
195
196
|
}
|
|
196
197
|
});
|
|
@@ -252,7 +253,11 @@ var init_errors = __esm({
|
|
|
252
253
|
INTERNAL: "INTERNAL",
|
|
253
254
|
UNAUTHORIZED: "UNAUTHORIZED",
|
|
254
255
|
FORBIDDEN: "FORBIDDEN",
|
|
255
|
-
INVALID_PARAM: "INVALID_PARAM"
|
|
256
|
+
INVALID_PARAM: "INVALID_PARAM",
|
|
257
|
+
// 飞书统一身份(spec 2026-06-01 决策 #9):People/远程体系强制飞书登录。
|
|
258
|
+
// daemon 进程身份未激活为飞书 unionId 时,FEISHU_GATED_METHODS 内的 RPC 一律返回此错误;
|
|
259
|
+
// UI 据此显示登录引导(区别于 UNAUTHORIZED 的越权语义)。
|
|
260
|
+
FEISHU_LOGIN_REQUIRED: "FEISHU_LOGIN_REQUIRED"
|
|
256
261
|
};
|
|
257
262
|
ClawdError = class extends Error {
|
|
258
263
|
constructor(code, message) {
|
|
@@ -682,8 +687,8 @@ var init_parseUtil = __esm({
|
|
|
682
687
|
init_errors2();
|
|
683
688
|
init_en();
|
|
684
689
|
makeIssue = (params) => {
|
|
685
|
-
const { data, path:
|
|
686
|
-
const fullPath = [...
|
|
690
|
+
const { data, path: path60, errorMaps, issueData } = params;
|
|
691
|
+
const fullPath = [...path60, ...issueData.path || []];
|
|
687
692
|
const fullIssue = {
|
|
688
693
|
...issueData,
|
|
689
694
|
path: fullPath
|
|
@@ -994,11 +999,11 @@ var init_types = __esm({
|
|
|
994
999
|
init_parseUtil();
|
|
995
1000
|
init_util();
|
|
996
1001
|
ParseInputLazyPath = class {
|
|
997
|
-
constructor(parent, value,
|
|
1002
|
+
constructor(parent, value, path60, key) {
|
|
998
1003
|
this._cachedPath = [];
|
|
999
1004
|
this.parent = parent;
|
|
1000
1005
|
this.data = value;
|
|
1001
|
-
this._path =
|
|
1006
|
+
this._path = path60;
|
|
1002
1007
|
this._key = key;
|
|
1003
1008
|
}
|
|
1004
1009
|
get path() {
|
|
@@ -4491,6 +4496,11 @@ var init_persona_schemas = __esm({
|
|
|
4491
4496
|
enabled: external_exports.boolean().optional(),
|
|
4492
4497
|
autoAllowBashIfSandboxed: external_exports.boolean().optional(),
|
|
4493
4498
|
allowUnsandboxedCommands: external_exports.boolean().optional(),
|
|
4499
|
+
// 开发型 persona 的网络域名白名单:CC 沙箱代理按 hostname 放行(spec 实测 D2)。
|
|
4500
|
+
// 缺省 = 无网络(沙箱代理对所有域名回 403 blocked-by-allowlist)
|
|
4501
|
+
network: external_exports.object({
|
|
4502
|
+
allowedDomains: external_exports.array(external_exports.string()).optional()
|
|
4503
|
+
}).optional(),
|
|
4494
4504
|
filesystem: external_exports.object({
|
|
4495
4505
|
denyRead: external_exports.array(external_exports.string()).optional(),
|
|
4496
4506
|
allowRead: external_exports.array(external_exports.string()).optional(),
|
|
@@ -4525,14 +4535,17 @@ var init_persona_schemas = __esm({
|
|
|
4525
4535
|
personality: external_exports.string().optional(),
|
|
4526
4536
|
public: external_exports.boolean().optional(),
|
|
4527
4537
|
// pass `null` to clear; daemon strips the field from PersonaFile on null
|
|
4528
|
-
iconKey: external_exports.string().nullable().optional()
|
|
4538
|
+
iconKey: external_exports.string().nullable().optional(),
|
|
4539
|
+
// 整体替换 sandbox-settings.json 的 sandbox.network.allowedDomains(不是 merge 数组)。
|
|
4540
|
+
// 窄接口设计:daemon 做 read-modify-write,owner 手改的其他 sandbox 字段不被 UI 覆盖
|
|
4541
|
+
networkAllowedDomains: external_exports.array(external_exports.string()).optional()
|
|
4529
4542
|
}).strict()
|
|
4530
4543
|
}).strict();
|
|
4531
4544
|
}
|
|
4532
4545
|
});
|
|
4533
4546
|
|
|
4534
4547
|
// ../protocol/src/schemas.ts
|
|
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,
|
|
4548
|
+
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;
|
|
4536
4549
|
var init_schemas = __esm({
|
|
4537
4550
|
"../protocol/src/schemas.ts"() {
|
|
4538
4551
|
"use strict";
|
|
@@ -4629,10 +4642,6 @@ var init_schemas = __esm({
|
|
|
4629
4642
|
// owner-mode persona session 身份标识;UI 用它在 SessionList 过滤 + jump,
|
|
4630
4643
|
// daemon 用它做 idempotent dedupe + spawn 时派生 ctx.personaMode='owner'
|
|
4631
4644
|
ownerPersonaId: external_exports.string().min(1).optional(),
|
|
4632
|
-
// app-builder Project 反向索引(spec: superpowers/specs/2026-06-01-app-builder-project-picker-design.md)。
|
|
4633
|
-
// 当此 session 绑定到某个 build 中的 project 时填写;name 与 projects/<name>/ 子目录同名,
|
|
4634
|
-
// 受 projectNameSchema regex 约束。daemon session:resume 时校验该目录存在,不存在则拒。
|
|
4635
|
-
appBuilderProject: external_exports.string().regex(/^[a-z][a-z0-9-]{0,39}$/).optional(),
|
|
4636
4645
|
// listener-mode sub-session 的原始 chatId(owner-mode 不写;listener-scope 强制写入)。
|
|
4637
4646
|
// 派生 sessionId 是单向 hash `${personaId}-${tokenHash12}-${chatHash8}`,必须保留原始 chatId
|
|
4638
4647
|
// 才能让 alice 重连同一 sub-session(持久化在 alice localStorage 之外,由 daemon push 同步)。
|
|
@@ -4905,12 +4914,7 @@ var init_schemas = __esm({
|
|
|
4905
4914
|
forkedFromSessionId: external_exports.string().min(1).optional(),
|
|
4906
4915
|
// owner-mode persona session:传此字段时 daemon 自行派生 cwd 和启动参数,
|
|
4907
4916
|
// 且按 ownerPersonaId 做 idempotent dedupe(每 persona 永远只有一个 owner session)
|
|
4908
|
-
ownerPersonaId: external_exports.string().min(1).optional()
|
|
4909
|
-
// app-builder Project 绑定(spec 2026-06-01-app-builder-project-picker-design)。
|
|
4910
|
-
// UI picker 上点 idle project / 新建 project 时传入;daemon 透传写入 SessionFile.appBuilderProject。
|
|
4911
|
-
// 跟 ownerPersonaId 同层但不互斥(典型场景两者都传:ownerPersonaId='persona-app-builder' +
|
|
4912
|
-
// appBuilderProject='<project-name>')。
|
|
4913
|
-
appBuilderProject: external_exports.string().regex(/^[a-z][a-z0-9-]{0,39}$/).optional()
|
|
4917
|
+
ownerPersonaId: external_exports.string().min(1).optional()
|
|
4914
4918
|
}).refine((args) => args.cwd != null || args.ownerPersonaId != null, {
|
|
4915
4919
|
message: "cwd \u4E0E ownerPersonaId \u81F3\u5C11\u4F20\u4E00\u4E2A"
|
|
4916
4920
|
});
|
|
@@ -4924,11 +4928,7 @@ var init_schemas = __esm({
|
|
|
4924
4928
|
effort: external_exports.string().optional(),
|
|
4925
4929
|
cwd: external_exports.string().optional(),
|
|
4926
4930
|
// 用户在 Edit modal 选择的 icon 标识;UI 端做 enum 兜底
|
|
4927
|
-
iconKey: external_exports.string().optional()
|
|
4928
|
-
// app-builder picker 在当前 session 内切 project 时写入(spec 2026-06-01-app-builder-project-picker-design)。
|
|
4929
|
-
// daemon session:update handler 检测此字段变化时停旧 project dev server + 起新 project dev server。
|
|
4930
|
-
// 设为空字符串 '' 表示解绑(picker 不暴露解绑入口,但 schema 允许)。
|
|
4931
|
-
appBuilderProject: external_exports.string().regex(/^[a-z][a-z0-9-]{0,39}$/).or(external_exports.literal("")).optional()
|
|
4931
|
+
iconKey: external_exports.string().optional()
|
|
4932
4932
|
})
|
|
4933
4933
|
});
|
|
4934
4934
|
SessionSendArgs = external_exports.object({
|
|
@@ -5125,6 +5125,12 @@ var init_schemas = __esm({
|
|
|
5125
5125
|
* 后调 capabilityManager.recordPeerHello(capId, ownerPrincipalId, displayName)
|
|
5126
5126
|
* 把 cap.peerOwnerId / firstUsedByPeerAt 字段回写,让 owner 端 UI 在 People
|
|
5127
5127
|
* tab 顶层 PeerOwner 视图 + Personas tab 二级分组拿到对端身份。
|
|
5128
|
+
*
|
|
5129
|
+
* 飞书统一身份 Phase 1 (2026-06-01):取值升级为飞书 union_id(owner 已飞书登录时;
|
|
5130
|
+
* 来自 whoami.owner.id → daemon ownerPrincipalId → owner-identity.json unionId)。
|
|
5131
|
+
* 未登录 daemon 仍为 auth.json 的 owner-<uuid>。daemon 端不验证该身份真实性
|
|
5132
|
+
* (Phase 1 信任模型,见 spec §9)。Phase 2 将以 connect token 替代(introspect
|
|
5133
|
+
* 返回身份,删除自报字段,见 spec §10)。
|
|
5128
5134
|
*/
|
|
5129
5135
|
selfPrincipalId: external_exports.string().min(1).optional(),
|
|
5130
5136
|
/**
|
|
@@ -5139,11 +5145,6 @@ var init_schemas = __esm({
|
|
|
5139
5145
|
version: external_exports.string().min(1).optional(),
|
|
5140
5146
|
protocolVersion: external_exports.number().int().nonnegative().optional()
|
|
5141
5147
|
});
|
|
5142
|
-
TunnelReadyEventSchema = external_exports.object({
|
|
5143
|
-
type: external_exports.literal("tunnel:ready"),
|
|
5144
|
-
url: external_exports.string().min(1),
|
|
5145
|
-
subdomain: external_exports.string().min(1)
|
|
5146
|
-
});
|
|
5147
5148
|
TunnelExitedEventSchema = external_exports.object({
|
|
5148
5149
|
type: external_exports.literal("tunnel:exited"),
|
|
5149
5150
|
code: external_exports.number().int().nullable(),
|
|
@@ -5185,41 +5186,6 @@ var init_schemas = __esm({
|
|
|
5185
5186
|
/** file-sharing HTTP 路由的 Authorization: Bearer token;与 WS token 解耦,HTTP 401 仅触发 ReAuth 不强踢 WS(spec §11 第 4 条) */
|
|
5186
5187
|
httpToken: external_exports.string().optional()
|
|
5187
5188
|
});
|
|
5188
|
-
PROJECT_PORT_MIN = 6173;
|
|
5189
|
-
PROJECT_PORT_MAX = 6182;
|
|
5190
|
-
projectNameRegex = /^[a-z][a-z0-9-]{0,39}$/;
|
|
5191
|
-
ProjectMetadataSchema = external_exports.object({
|
|
5192
|
-
name: external_exports.string().regex(projectNameRegex, {
|
|
5193
|
-
message: "kebab-case, \u5C0F\u5199\u5B57\u6BCD\u5F00\u5934\uFF0C\u53EA\u5141\u8BB8\u5C0F\u5199\u5B57\u6BCD / \u6570\u5B57 / -\uFF0C\u6700\u957F 40 \u5B57\u7B26"
|
|
5194
|
-
}),
|
|
5195
|
-
port: external_exports.number().int().min(PROJECT_PORT_MIN).max(PROJECT_PORT_MAX),
|
|
5196
|
-
createdAt: external_exports.string().datetime()
|
|
5197
|
-
});
|
|
5198
|
-
ProjectWithStatusSchema = ProjectMetadataSchema.extend({
|
|
5199
|
-
isRunning: external_exports.boolean(),
|
|
5200
|
-
boundSessionId: external_exports.string().nullable()
|
|
5201
|
-
});
|
|
5202
|
-
ListProjectsArgsSchema = external_exports.object({}).strict();
|
|
5203
|
-
ListProjectsResultSchema = external_exports.object({
|
|
5204
|
-
projects: external_exports.array(ProjectWithStatusSchema)
|
|
5205
|
-
}).strict();
|
|
5206
|
-
CreateProjectArgsSchema = external_exports.object({
|
|
5207
|
-
name: external_exports.string()
|
|
5208
|
-
}).strict();
|
|
5209
|
-
CreateProjectResultSchema = external_exports.object({
|
|
5210
|
-
project: ProjectMetadataSchema
|
|
5211
|
-
}).strict();
|
|
5212
|
-
DeleteProjectArgsSchema = external_exports.object({
|
|
5213
|
-
name: external_exports.string()
|
|
5214
|
-
}).strict();
|
|
5215
|
-
DeleteProjectResultSchema = external_exports.object({}).strict();
|
|
5216
|
-
UpdateProjectPortArgsSchema = external_exports.object({
|
|
5217
|
-
name: external_exports.string(),
|
|
5218
|
-
newPort: external_exports.number().int()
|
|
5219
|
-
}).strict();
|
|
5220
|
-
UpdateProjectPortResultSchema = external_exports.object({
|
|
5221
|
-
project: ProjectMetadataSchema
|
|
5222
|
-
}).strict();
|
|
5223
5189
|
}
|
|
5224
5190
|
});
|
|
5225
5191
|
|
|
@@ -5628,6 +5594,93 @@ var init_extension = __esm({
|
|
|
5628
5594
|
}
|
|
5629
5595
|
});
|
|
5630
5596
|
|
|
5597
|
+
// ../protocol/src/feishu-auth.ts
|
|
5598
|
+
var FEISHU_GATED_METHODS, HubEntrySchema, HubListResponseSchema, HubConnectArgsSchema, HubConnectResponseSchema, FeishuIdentitySchema, AuthLoginStartResponseSchema, AuthGetIdentityResponseSchema, AuthLogoutResponseSchema, AuthLoginDoneEventSchema, AuthLoginFailedEventSchema;
|
|
5599
|
+
var init_feishu_auth = __esm({
|
|
5600
|
+
"../protocol/src/feishu-auth.ts"() {
|
|
5601
|
+
"use strict";
|
|
5602
|
+
init_zod();
|
|
5603
|
+
FEISHU_GATED_METHODS = [
|
|
5604
|
+
// capability / 邀请码(生成添加码 = 把自己的身份分享出去)
|
|
5605
|
+
"capability:list",
|
|
5606
|
+
"capability:delete",
|
|
5607
|
+
"personal-cap:get",
|
|
5608
|
+
// 联系人(添加朋友 / 联系人列表 / 自动重连)
|
|
5609
|
+
"received-capability:list",
|
|
5610
|
+
"received-capability:add",
|
|
5611
|
+
"received-capability:remove",
|
|
5612
|
+
"received-capability:autoAttach",
|
|
5613
|
+
// DM / 跨用户通知
|
|
5614
|
+
"inbox:list",
|
|
5615
|
+
"inbox:markRead",
|
|
5616
|
+
"inbox:postMessage",
|
|
5617
|
+
// mirror peer sessions(远端会话镜像)
|
|
5618
|
+
"peerSession:upsert",
|
|
5619
|
+
"peerSession:remove",
|
|
5620
|
+
// Phase 2: hub 目录 + 连接(中心 server 代理,需要 owner 的 ttcJwt)
|
|
5621
|
+
"hub:list",
|
|
5622
|
+
"hub:connect"
|
|
5623
|
+
];
|
|
5624
|
+
HubEntrySchema = external_exports.object({
|
|
5625
|
+
/** hub owner 的飞书 union_id */
|
|
5626
|
+
hubId: external_exports.string().min(1),
|
|
5627
|
+
/** 显示名 */
|
|
5628
|
+
name: external_exports.string().min(1),
|
|
5629
|
+
/** ws/tunnel 地址 */
|
|
5630
|
+
url: external_exports.string().min(1)
|
|
5631
|
+
});
|
|
5632
|
+
HubListResponseSchema = external_exports.object({
|
|
5633
|
+
type: external_exports.literal("hub:list:ok"),
|
|
5634
|
+
hubs: external_exports.array(HubEntrySchema)
|
|
5635
|
+
});
|
|
5636
|
+
HubConnectArgsSchema = external_exports.object({
|
|
5637
|
+
/** 目标 hub 的 hubId(= hub owner unionId) */
|
|
5638
|
+
hubId: external_exports.string().min(1),
|
|
5639
|
+
/** 目标 hub 的 ws/tunnel 地址 */
|
|
5640
|
+
url: external_exports.string().min(1),
|
|
5641
|
+
/** 显示名(公共 hub 来自目录;个人 hub 链接可缺省,连上后用 whoami 回填) */
|
|
5642
|
+
name: external_exports.string().optional()
|
|
5643
|
+
});
|
|
5644
|
+
HubConnectResponseSchema = external_exports.object({
|
|
5645
|
+
type: external_exports.literal("hub:connect:ok"),
|
|
5646
|
+
hubId: external_exports.string(),
|
|
5647
|
+
name: external_exports.string(),
|
|
5648
|
+
url: external_exports.string()
|
|
5649
|
+
});
|
|
5650
|
+
FeishuIdentitySchema = external_exports.object({
|
|
5651
|
+
/** 飞书 union_id(租户维度,跨应用稳定)。TTC user_info 接口 data.union_id */
|
|
5652
|
+
unionId: external_exports.string().min(1),
|
|
5653
|
+
/** 飞书姓名。TTC user_info 接口 data.name */
|
|
5654
|
+
displayName: external_exports.string().min(1),
|
|
5655
|
+
/** TTC user_info 接口 data.avatar_url,可缺省 */
|
|
5656
|
+
avatarUrl: external_exports.string().optional()
|
|
5657
|
+
});
|
|
5658
|
+
AuthLoginStartResponseSchema = external_exports.object({
|
|
5659
|
+
type: external_exports.literal("auth:login:start:ok"),
|
|
5660
|
+
/** 完整 TTC 授权页 URL(含 callback_url + state + auto=1),UI 直接 window.open */
|
|
5661
|
+
authUrl: external_exports.string().min(1),
|
|
5662
|
+
/** 防 CSRF 的一次性 nonce;回调时 daemon 校验 */
|
|
5663
|
+
state: external_exports.string().min(1)
|
|
5664
|
+
});
|
|
5665
|
+
AuthGetIdentityResponseSchema = external_exports.object({
|
|
5666
|
+
type: external_exports.literal("auth:getIdentity:ok"),
|
|
5667
|
+
/** null = 未登录 */
|
|
5668
|
+
identity: FeishuIdentitySchema.nullable()
|
|
5669
|
+
});
|
|
5670
|
+
AuthLogoutResponseSchema = external_exports.object({
|
|
5671
|
+
type: external_exports.literal("auth:logout:ok")
|
|
5672
|
+
});
|
|
5673
|
+
AuthLoginDoneEventSchema = external_exports.object({
|
|
5674
|
+
type: external_exports.literal("auth:login:done"),
|
|
5675
|
+
identity: FeishuIdentitySchema
|
|
5676
|
+
});
|
|
5677
|
+
AuthLoginFailedEventSchema = external_exports.object({
|
|
5678
|
+
type: external_exports.literal("auth:login:failed"),
|
|
5679
|
+
reason: external_exports.string()
|
|
5680
|
+
});
|
|
5681
|
+
}
|
|
5682
|
+
});
|
|
5683
|
+
|
|
5631
5684
|
// ../protocol/src/runtime.ts
|
|
5632
5685
|
var init_runtime = __esm({
|
|
5633
5686
|
"../protocol/src/runtime.ts"() {
|
|
@@ -5645,6 +5698,7 @@ var init_runtime = __esm({
|
|
|
5645
5698
|
init_inbox();
|
|
5646
5699
|
init_received_capability();
|
|
5647
5700
|
init_extension();
|
|
5701
|
+
init_feishu_auth();
|
|
5648
5702
|
}
|
|
5649
5703
|
});
|
|
5650
5704
|
|
|
@@ -5919,8 +5973,8 @@ var require_req = __commonJS({
|
|
|
5919
5973
|
if (req.originalUrl) {
|
|
5920
5974
|
_req.url = req.originalUrl;
|
|
5921
5975
|
} else {
|
|
5922
|
-
const
|
|
5923
|
-
_req.url = typeof
|
|
5976
|
+
const path60 = req.path;
|
|
5977
|
+
_req.url = typeof path60 === "string" ? path60 : req.url ? req.url.path || req.url : void 0;
|
|
5924
5978
|
}
|
|
5925
5979
|
if (req.query) {
|
|
5926
5980
|
_req.query = req.query;
|
|
@@ -6085,14 +6139,14 @@ var require_redact = __commonJS({
|
|
|
6085
6139
|
}
|
|
6086
6140
|
return obj;
|
|
6087
6141
|
}
|
|
6088
|
-
function parsePath(
|
|
6142
|
+
function parsePath(path60) {
|
|
6089
6143
|
const parts = [];
|
|
6090
6144
|
let current = "";
|
|
6091
6145
|
let inBrackets = false;
|
|
6092
6146
|
let inQuotes = false;
|
|
6093
6147
|
let quoteChar = "";
|
|
6094
|
-
for (let i = 0; i <
|
|
6095
|
-
const char =
|
|
6148
|
+
for (let i = 0; i < path60.length; i++) {
|
|
6149
|
+
const char = path60[i];
|
|
6096
6150
|
if (!inBrackets && char === ".") {
|
|
6097
6151
|
if (current) {
|
|
6098
6152
|
parts.push(current);
|
|
@@ -6223,10 +6277,10 @@ var require_redact = __commonJS({
|
|
|
6223
6277
|
return current;
|
|
6224
6278
|
}
|
|
6225
6279
|
function redactPaths(obj, paths, censor, remove = false) {
|
|
6226
|
-
for (const
|
|
6227
|
-
const parts = parsePath(
|
|
6280
|
+
for (const path60 of paths) {
|
|
6281
|
+
const parts = parsePath(path60);
|
|
6228
6282
|
if (parts.includes("*")) {
|
|
6229
|
-
redactWildcardPath(obj, parts, censor,
|
|
6283
|
+
redactWildcardPath(obj, parts, censor, path60, remove);
|
|
6230
6284
|
} else {
|
|
6231
6285
|
if (remove) {
|
|
6232
6286
|
removeKey(obj, parts);
|
|
@@ -6311,8 +6365,8 @@ var require_redact = __commonJS({
|
|
|
6311
6365
|
}
|
|
6312
6366
|
} else {
|
|
6313
6367
|
if (afterWildcard.includes("*")) {
|
|
6314
|
-
const wrappedCensor = typeof censor === "function" ? (value,
|
|
6315
|
-
const fullPath = [...pathArray.slice(0, pathLength), ...
|
|
6368
|
+
const wrappedCensor = typeof censor === "function" ? (value, path60) => {
|
|
6369
|
+
const fullPath = [...pathArray.slice(0, pathLength), ...path60];
|
|
6316
6370
|
return censor(value, fullPath);
|
|
6317
6371
|
} : censor;
|
|
6318
6372
|
redactWildcardPath(current, afterWildcard, wrappedCensor, originalPath, remove);
|
|
@@ -6347,8 +6401,8 @@ var require_redact = __commonJS({
|
|
|
6347
6401
|
return null;
|
|
6348
6402
|
}
|
|
6349
6403
|
const pathStructure = /* @__PURE__ */ new Map();
|
|
6350
|
-
for (const
|
|
6351
|
-
const parts = parsePath(
|
|
6404
|
+
for (const path60 of pathsToClone) {
|
|
6405
|
+
const parts = parsePath(path60);
|
|
6352
6406
|
let current = pathStructure;
|
|
6353
6407
|
for (let i = 0; i < parts.length; i++) {
|
|
6354
6408
|
const part = parts[i];
|
|
@@ -6400,24 +6454,24 @@ var require_redact = __commonJS({
|
|
|
6400
6454
|
}
|
|
6401
6455
|
return cloneSelectively(obj, pathStructure);
|
|
6402
6456
|
}
|
|
6403
|
-
function validatePath(
|
|
6404
|
-
if (typeof
|
|
6457
|
+
function validatePath(path60) {
|
|
6458
|
+
if (typeof path60 !== "string") {
|
|
6405
6459
|
throw new Error("Paths must be (non-empty) strings");
|
|
6406
6460
|
}
|
|
6407
|
-
if (
|
|
6461
|
+
if (path60 === "") {
|
|
6408
6462
|
throw new Error("Invalid redaction path ()");
|
|
6409
6463
|
}
|
|
6410
|
-
if (
|
|
6411
|
-
throw new Error(`Invalid redaction path (${
|
|
6464
|
+
if (path60.includes("..")) {
|
|
6465
|
+
throw new Error(`Invalid redaction path (${path60})`);
|
|
6412
6466
|
}
|
|
6413
|
-
if (
|
|
6414
|
-
throw new Error(`Invalid redaction path (${
|
|
6467
|
+
if (path60.includes(",")) {
|
|
6468
|
+
throw new Error(`Invalid redaction path (${path60})`);
|
|
6415
6469
|
}
|
|
6416
6470
|
let bracketCount = 0;
|
|
6417
6471
|
let inQuotes = false;
|
|
6418
6472
|
let quoteChar = "";
|
|
6419
|
-
for (let i = 0; i <
|
|
6420
|
-
const char =
|
|
6473
|
+
for (let i = 0; i < path60.length; i++) {
|
|
6474
|
+
const char = path60[i];
|
|
6421
6475
|
if ((char === '"' || char === "'") && bracketCount > 0) {
|
|
6422
6476
|
if (!inQuotes) {
|
|
6423
6477
|
inQuotes = true;
|
|
@@ -6431,20 +6485,20 @@ var require_redact = __commonJS({
|
|
|
6431
6485
|
} else if (char === "]" && !inQuotes) {
|
|
6432
6486
|
bracketCount--;
|
|
6433
6487
|
if (bracketCount < 0) {
|
|
6434
|
-
throw new Error(`Invalid redaction path (${
|
|
6488
|
+
throw new Error(`Invalid redaction path (${path60})`);
|
|
6435
6489
|
}
|
|
6436
6490
|
}
|
|
6437
6491
|
}
|
|
6438
6492
|
if (bracketCount !== 0) {
|
|
6439
|
-
throw new Error(`Invalid redaction path (${
|
|
6493
|
+
throw new Error(`Invalid redaction path (${path60})`);
|
|
6440
6494
|
}
|
|
6441
6495
|
}
|
|
6442
6496
|
function validatePaths(paths) {
|
|
6443
6497
|
if (!Array.isArray(paths)) {
|
|
6444
6498
|
throw new TypeError("paths must be an array");
|
|
6445
6499
|
}
|
|
6446
|
-
for (const
|
|
6447
|
-
validatePath(
|
|
6500
|
+
for (const path60 of paths) {
|
|
6501
|
+
validatePath(path60);
|
|
6448
6502
|
}
|
|
6449
6503
|
}
|
|
6450
6504
|
function slowRedact(options = {}) {
|
|
@@ -6612,8 +6666,8 @@ var require_redaction = __commonJS({
|
|
|
6612
6666
|
if (shape[k2] === null) {
|
|
6613
6667
|
o[k2] = (value) => topCensor(value, [k2]);
|
|
6614
6668
|
} else {
|
|
6615
|
-
const wrappedCensor = typeof censor === "function" ? (value,
|
|
6616
|
-
return censor(value, [k2, ...
|
|
6669
|
+
const wrappedCensor = typeof censor === "function" ? (value, path60) => {
|
|
6670
|
+
return censor(value, [k2, ...path60]);
|
|
6617
6671
|
} : censor;
|
|
6618
6672
|
o[k2] = Redact({
|
|
6619
6673
|
paths: shape[k2],
|
|
@@ -6832,9 +6886,9 @@ var require_sonic_boom = __commonJS({
|
|
|
6832
6886
|
"../node_modules/.pnpm/sonic-boom@4.2.1/node_modules/sonic-boom/index.js"(exports2, module2) {
|
|
6833
6887
|
"use strict";
|
|
6834
6888
|
var fs48 = require("fs");
|
|
6835
|
-
var
|
|
6889
|
+
var EventEmitter2 = require("events");
|
|
6836
6890
|
var inherits = require("util").inherits;
|
|
6837
|
-
var
|
|
6891
|
+
var path60 = require("path");
|
|
6838
6892
|
var sleep = require_atomic_sleep();
|
|
6839
6893
|
var assert = require("assert");
|
|
6840
6894
|
var BUSY_WRITE_TIMEOUT = 100;
|
|
@@ -6888,7 +6942,7 @@ var require_sonic_boom = __commonJS({
|
|
|
6888
6942
|
const mode = sonic.mode;
|
|
6889
6943
|
if (sonic.sync) {
|
|
6890
6944
|
try {
|
|
6891
|
-
if (sonic.mkdir) fs48.mkdirSync(
|
|
6945
|
+
if (sonic.mkdir) fs48.mkdirSync(path60.dirname(file), { recursive: true });
|
|
6892
6946
|
const fd = fs48.openSync(file, flags, mode);
|
|
6893
6947
|
fileOpened(null, fd);
|
|
6894
6948
|
} catch (err) {
|
|
@@ -6896,7 +6950,7 @@ var require_sonic_boom = __commonJS({
|
|
|
6896
6950
|
throw err;
|
|
6897
6951
|
}
|
|
6898
6952
|
} else if (sonic.mkdir) {
|
|
6899
|
-
fs48.mkdir(
|
|
6953
|
+
fs48.mkdir(path60.dirname(file), { recursive: true }, (err) => {
|
|
6900
6954
|
if (err) return fileOpened(err);
|
|
6901
6955
|
fs48.open(file, flags, mode, fileOpened);
|
|
6902
6956
|
});
|
|
@@ -7069,7 +7123,7 @@ var require_sonic_boom = __commonJS({
|
|
|
7069
7123
|
sonic._asyncDrainScheduled = false;
|
|
7070
7124
|
sonic.emit("drain");
|
|
7071
7125
|
}
|
|
7072
|
-
inherits(SonicBoom,
|
|
7126
|
+
inherits(SonicBoom, EventEmitter2);
|
|
7073
7127
|
function mergeBuf(bufs, len) {
|
|
7074
7128
|
if (bufs.length === 0) {
|
|
7075
7129
|
return kEmptyBuffer;
|
|
@@ -7647,9 +7701,9 @@ var require_thread_stream = __commonJS({
|
|
|
7647
7701
|
"../node_modules/.pnpm/thread-stream@3.1.0/node_modules/thread-stream/index.js"(exports2, module2) {
|
|
7648
7702
|
"use strict";
|
|
7649
7703
|
var { version: version2 } = require_package();
|
|
7650
|
-
var { EventEmitter:
|
|
7704
|
+
var { EventEmitter: EventEmitter2 } = require("events");
|
|
7651
7705
|
var { Worker } = require("worker_threads");
|
|
7652
|
-
var { join:
|
|
7706
|
+
var { join: join11 } = require("path");
|
|
7653
7707
|
var { pathToFileURL } = require("url");
|
|
7654
7708
|
var { wait } = require_wait();
|
|
7655
7709
|
var {
|
|
@@ -7685,7 +7739,7 @@ var require_thread_stream = __commonJS({
|
|
|
7685
7739
|
function createWorker(stream, opts) {
|
|
7686
7740
|
const { filename, workerData } = opts;
|
|
7687
7741
|
const bundlerOverrides = "__bundlerPathsOverrides" in globalThis ? globalThis.__bundlerPathsOverrides : {};
|
|
7688
|
-
const toExecute = bundlerOverrides["thread-stream-worker"] ||
|
|
7742
|
+
const toExecute = bundlerOverrides["thread-stream-worker"] || join11(__dirname, "lib", "worker.js");
|
|
7689
7743
|
const worker = new Worker(toExecute, {
|
|
7690
7744
|
...opts.workerOpts,
|
|
7691
7745
|
trackUnmanagedFds: false,
|
|
@@ -7803,7 +7857,7 @@ var require_thread_stream = __commonJS({
|
|
|
7803
7857
|
stream.worker.off("exit", onWorkerExit);
|
|
7804
7858
|
destroy(stream, code !== 0 ? new Error("the worker thread exited") : null);
|
|
7805
7859
|
}
|
|
7806
|
-
var ThreadStream = class extends
|
|
7860
|
+
var ThreadStream = class extends EventEmitter2 {
|
|
7807
7861
|
constructor(opts = {}) {
|
|
7808
7862
|
super();
|
|
7809
7863
|
if (opts.bufferSize < 4) {
|
|
@@ -8071,7 +8125,7 @@ var require_transport = __commonJS({
|
|
|
8071
8125
|
"use strict";
|
|
8072
8126
|
var { createRequire } = require("module");
|
|
8073
8127
|
var getCallers = require_caller();
|
|
8074
|
-
var { join:
|
|
8128
|
+
var { join: join11, isAbsolute: isAbsolute2, sep: sep3 } = require("path");
|
|
8075
8129
|
var sleep = require_atomic_sleep();
|
|
8076
8130
|
var onExit = require_on_exit_leak_free();
|
|
8077
8131
|
var ThreadStream = require_thread_stream();
|
|
@@ -8134,7 +8188,7 @@ var require_transport = __commonJS({
|
|
|
8134
8188
|
throw new Error("only one of target or targets can be specified");
|
|
8135
8189
|
}
|
|
8136
8190
|
if (targets) {
|
|
8137
|
-
target = bundlerOverrides["pino-worker"] ||
|
|
8191
|
+
target = bundlerOverrides["pino-worker"] || join11(__dirname, "worker.js");
|
|
8138
8192
|
options.targets = targets.filter((dest) => dest.target).map((dest) => {
|
|
8139
8193
|
return {
|
|
8140
8194
|
...dest,
|
|
@@ -8152,7 +8206,7 @@ var require_transport = __commonJS({
|
|
|
8152
8206
|
});
|
|
8153
8207
|
});
|
|
8154
8208
|
} else if (pipeline3) {
|
|
8155
|
-
target = bundlerOverrides["pino-worker"] ||
|
|
8209
|
+
target = bundlerOverrides["pino-worker"] || join11(__dirname, "worker.js");
|
|
8156
8210
|
options.pipelines = [pipeline3.map((dest) => {
|
|
8157
8211
|
return {
|
|
8158
8212
|
...dest,
|
|
@@ -8174,7 +8228,7 @@ var require_transport = __commonJS({
|
|
|
8174
8228
|
return origin;
|
|
8175
8229
|
}
|
|
8176
8230
|
if (origin === "pino/file") {
|
|
8177
|
-
return
|
|
8231
|
+
return join11(__dirname, "..", "file.js");
|
|
8178
8232
|
}
|
|
8179
8233
|
let fixTarget2;
|
|
8180
8234
|
for (const filePath of callers) {
|
|
@@ -8761,7 +8815,7 @@ var require_meta = __commonJS({
|
|
|
8761
8815
|
var require_proto = __commonJS({
|
|
8762
8816
|
"../node_modules/.pnpm/pino@9.14.0/node_modules/pino/lib/proto.js"(exports2, module2) {
|
|
8763
8817
|
"use strict";
|
|
8764
|
-
var { EventEmitter:
|
|
8818
|
+
var { EventEmitter: EventEmitter2 } = require("events");
|
|
8765
8819
|
var {
|
|
8766
8820
|
lsCacheSym,
|
|
8767
8821
|
levelValSym,
|
|
@@ -8843,7 +8897,7 @@ var require_proto = __commonJS({
|
|
|
8843
8897
|
[getLevelSym]: getLevel,
|
|
8844
8898
|
[setLevelSym]: setLevel
|
|
8845
8899
|
};
|
|
8846
|
-
Object.setPrototypeOf(prototype,
|
|
8900
|
+
Object.setPrototypeOf(prototype, EventEmitter2.prototype);
|
|
8847
8901
|
module2.exports = function() {
|
|
8848
8902
|
return Object.create(prototype);
|
|
8849
8903
|
};
|
|
@@ -9164,7 +9218,7 @@ var require_safe_stable_stringify = __commonJS({
|
|
|
9164
9218
|
return circularValue;
|
|
9165
9219
|
}
|
|
9166
9220
|
let res = "";
|
|
9167
|
-
let
|
|
9221
|
+
let join11 = ",";
|
|
9168
9222
|
const originalIndentation = indentation;
|
|
9169
9223
|
if (Array.isArray(value)) {
|
|
9170
9224
|
if (value.length === 0) {
|
|
@@ -9178,7 +9232,7 @@ var require_safe_stable_stringify = __commonJS({
|
|
|
9178
9232
|
indentation += spacer;
|
|
9179
9233
|
res += `
|
|
9180
9234
|
${indentation}`;
|
|
9181
|
-
|
|
9235
|
+
join11 = `,
|
|
9182
9236
|
${indentation}`;
|
|
9183
9237
|
}
|
|
9184
9238
|
const maximumValuesToStringify = Math.min(value.length, maximumBreadth);
|
|
@@ -9186,13 +9240,13 @@ ${indentation}`;
|
|
|
9186
9240
|
for (; i < maximumValuesToStringify - 1; i++) {
|
|
9187
9241
|
const tmp2 = stringifyFnReplacer(String(i), value, stack, replacer, spacer, indentation);
|
|
9188
9242
|
res += tmp2 !== void 0 ? tmp2 : "null";
|
|
9189
|
-
res +=
|
|
9243
|
+
res += join11;
|
|
9190
9244
|
}
|
|
9191
9245
|
const tmp = stringifyFnReplacer(String(i), value, stack, replacer, spacer, indentation);
|
|
9192
9246
|
res += tmp !== void 0 ? tmp : "null";
|
|
9193
9247
|
if (value.length - 1 > maximumBreadth) {
|
|
9194
9248
|
const removedKeys = value.length - maximumBreadth - 1;
|
|
9195
|
-
res += `${
|
|
9249
|
+
res += `${join11}"... ${getItemCount(removedKeys)} not stringified"`;
|
|
9196
9250
|
}
|
|
9197
9251
|
if (spacer !== "") {
|
|
9198
9252
|
res += `
|
|
@@ -9213,7 +9267,7 @@ ${originalIndentation}`;
|
|
|
9213
9267
|
let separator = "";
|
|
9214
9268
|
if (spacer !== "") {
|
|
9215
9269
|
indentation += spacer;
|
|
9216
|
-
|
|
9270
|
+
join11 = `,
|
|
9217
9271
|
${indentation}`;
|
|
9218
9272
|
whitespace = " ";
|
|
9219
9273
|
}
|
|
@@ -9227,13 +9281,13 @@ ${indentation}`;
|
|
|
9227
9281
|
const tmp = stringifyFnReplacer(key2, value, stack, replacer, spacer, indentation);
|
|
9228
9282
|
if (tmp !== void 0) {
|
|
9229
9283
|
res += `${separator}${strEscape(key2)}:${whitespace}${tmp}`;
|
|
9230
|
-
separator =
|
|
9284
|
+
separator = join11;
|
|
9231
9285
|
}
|
|
9232
9286
|
}
|
|
9233
9287
|
if (keyLength > maximumBreadth) {
|
|
9234
9288
|
const removedKeys = keyLength - maximumBreadth;
|
|
9235
9289
|
res += `${separator}"...":${whitespace}"${getItemCount(removedKeys)} not stringified"`;
|
|
9236
|
-
separator =
|
|
9290
|
+
separator = join11;
|
|
9237
9291
|
}
|
|
9238
9292
|
if (spacer !== "" && separator.length > 1) {
|
|
9239
9293
|
res = `
|
|
@@ -9274,7 +9328,7 @@ ${originalIndentation}`;
|
|
|
9274
9328
|
}
|
|
9275
9329
|
const originalIndentation = indentation;
|
|
9276
9330
|
let res = "";
|
|
9277
|
-
let
|
|
9331
|
+
let join11 = ",";
|
|
9278
9332
|
if (Array.isArray(value)) {
|
|
9279
9333
|
if (value.length === 0) {
|
|
9280
9334
|
return "[]";
|
|
@@ -9287,7 +9341,7 @@ ${originalIndentation}`;
|
|
|
9287
9341
|
indentation += spacer;
|
|
9288
9342
|
res += `
|
|
9289
9343
|
${indentation}`;
|
|
9290
|
-
|
|
9344
|
+
join11 = `,
|
|
9291
9345
|
${indentation}`;
|
|
9292
9346
|
}
|
|
9293
9347
|
const maximumValuesToStringify = Math.min(value.length, maximumBreadth);
|
|
@@ -9295,13 +9349,13 @@ ${indentation}`;
|
|
|
9295
9349
|
for (; i < maximumValuesToStringify - 1; i++) {
|
|
9296
9350
|
const tmp2 = stringifyArrayReplacer(String(i), value[i], stack, replacer, spacer, indentation);
|
|
9297
9351
|
res += tmp2 !== void 0 ? tmp2 : "null";
|
|
9298
|
-
res +=
|
|
9352
|
+
res += join11;
|
|
9299
9353
|
}
|
|
9300
9354
|
const tmp = stringifyArrayReplacer(String(i), value[i], stack, replacer, spacer, indentation);
|
|
9301
9355
|
res += tmp !== void 0 ? tmp : "null";
|
|
9302
9356
|
if (value.length - 1 > maximumBreadth) {
|
|
9303
9357
|
const removedKeys = value.length - maximumBreadth - 1;
|
|
9304
|
-
res += `${
|
|
9358
|
+
res += `${join11}"... ${getItemCount(removedKeys)} not stringified"`;
|
|
9305
9359
|
}
|
|
9306
9360
|
if (spacer !== "") {
|
|
9307
9361
|
res += `
|
|
@@ -9314,7 +9368,7 @@ ${originalIndentation}`;
|
|
|
9314
9368
|
let whitespace = "";
|
|
9315
9369
|
if (spacer !== "") {
|
|
9316
9370
|
indentation += spacer;
|
|
9317
|
-
|
|
9371
|
+
join11 = `,
|
|
9318
9372
|
${indentation}`;
|
|
9319
9373
|
whitespace = " ";
|
|
9320
9374
|
}
|
|
@@ -9323,7 +9377,7 @@ ${indentation}`;
|
|
|
9323
9377
|
const tmp = stringifyArrayReplacer(key2, value[key2], stack, replacer, spacer, indentation);
|
|
9324
9378
|
if (tmp !== void 0) {
|
|
9325
9379
|
res += `${separator}${strEscape(key2)}:${whitespace}${tmp}`;
|
|
9326
|
-
separator =
|
|
9380
|
+
separator = join11;
|
|
9327
9381
|
}
|
|
9328
9382
|
}
|
|
9329
9383
|
if (spacer !== "" && separator.length > 1) {
|
|
@@ -9381,20 +9435,20 @@ ${originalIndentation}`;
|
|
|
9381
9435
|
indentation += spacer;
|
|
9382
9436
|
let res2 = `
|
|
9383
9437
|
${indentation}`;
|
|
9384
|
-
const
|
|
9438
|
+
const join12 = `,
|
|
9385
9439
|
${indentation}`;
|
|
9386
9440
|
const maximumValuesToStringify = Math.min(value.length, maximumBreadth);
|
|
9387
9441
|
let i = 0;
|
|
9388
9442
|
for (; i < maximumValuesToStringify - 1; i++) {
|
|
9389
9443
|
const tmp2 = stringifyIndent(String(i), value[i], stack, spacer, indentation);
|
|
9390
9444
|
res2 += tmp2 !== void 0 ? tmp2 : "null";
|
|
9391
|
-
res2 +=
|
|
9445
|
+
res2 += join12;
|
|
9392
9446
|
}
|
|
9393
9447
|
const tmp = stringifyIndent(String(i), value[i], stack, spacer, indentation);
|
|
9394
9448
|
res2 += tmp !== void 0 ? tmp : "null";
|
|
9395
9449
|
if (value.length - 1 > maximumBreadth) {
|
|
9396
9450
|
const removedKeys = value.length - maximumBreadth - 1;
|
|
9397
|
-
res2 += `${
|
|
9451
|
+
res2 += `${join12}"... ${getItemCount(removedKeys)} not stringified"`;
|
|
9398
9452
|
}
|
|
9399
9453
|
res2 += `
|
|
9400
9454
|
${originalIndentation}`;
|
|
@@ -9410,16 +9464,16 @@ ${originalIndentation}`;
|
|
|
9410
9464
|
return '"[Object]"';
|
|
9411
9465
|
}
|
|
9412
9466
|
indentation += spacer;
|
|
9413
|
-
const
|
|
9467
|
+
const join11 = `,
|
|
9414
9468
|
${indentation}`;
|
|
9415
9469
|
let res = "";
|
|
9416
9470
|
let separator = "";
|
|
9417
9471
|
let maximumPropertiesToStringify = Math.min(keyLength, maximumBreadth);
|
|
9418
9472
|
if (isTypedArrayWithEntries(value)) {
|
|
9419
|
-
res += stringifyTypedArray(value,
|
|
9473
|
+
res += stringifyTypedArray(value, join11, maximumBreadth);
|
|
9420
9474
|
keys = keys.slice(value.length);
|
|
9421
9475
|
maximumPropertiesToStringify -= value.length;
|
|
9422
|
-
separator =
|
|
9476
|
+
separator = join11;
|
|
9423
9477
|
}
|
|
9424
9478
|
if (deterministic) {
|
|
9425
9479
|
keys = sort(keys, comparator);
|
|
@@ -9430,13 +9484,13 @@ ${indentation}`;
|
|
|
9430
9484
|
const tmp = stringifyIndent(key2, value[key2], stack, spacer, indentation);
|
|
9431
9485
|
if (tmp !== void 0) {
|
|
9432
9486
|
res += `${separator}${strEscape(key2)}: ${tmp}`;
|
|
9433
|
-
separator =
|
|
9487
|
+
separator = join11;
|
|
9434
9488
|
}
|
|
9435
9489
|
}
|
|
9436
9490
|
if (keyLength > maximumBreadth) {
|
|
9437
9491
|
const removedKeys = keyLength - maximumBreadth;
|
|
9438
9492
|
res += `${separator}"...": "${getItemCount(removedKeys)} not stringified"`;
|
|
9439
|
-
separator =
|
|
9493
|
+
separator = join11;
|
|
9440
9494
|
}
|
|
9441
9495
|
if (separator !== "") {
|
|
9442
9496
|
res = `
|
|
@@ -10527,11 +10581,11 @@ var init_lib = __esm({
|
|
|
10527
10581
|
}
|
|
10528
10582
|
}
|
|
10529
10583
|
},
|
|
10530
|
-
addToPath: function addToPath(
|
|
10531
|
-
var last =
|
|
10584
|
+
addToPath: function addToPath(path60, added, removed, oldPosInc, options) {
|
|
10585
|
+
var last = path60.lastComponent;
|
|
10532
10586
|
if (last && !options.oneChangePerToken && last.added === added && last.removed === removed) {
|
|
10533
10587
|
return {
|
|
10534
|
-
oldPos:
|
|
10588
|
+
oldPos: path60.oldPos + oldPosInc,
|
|
10535
10589
|
lastComponent: {
|
|
10536
10590
|
count: last.count + 1,
|
|
10537
10591
|
added,
|
|
@@ -10541,7 +10595,7 @@ var init_lib = __esm({
|
|
|
10541
10595
|
};
|
|
10542
10596
|
} else {
|
|
10543
10597
|
return {
|
|
10544
|
-
oldPos:
|
|
10598
|
+
oldPos: path60.oldPos + oldPosInc,
|
|
10545
10599
|
lastComponent: {
|
|
10546
10600
|
count: 1,
|
|
10547
10601
|
added,
|
|
@@ -10987,10 +11041,10 @@ function attachmentToHistoryMessage(o, ts) {
|
|
|
10987
11041
|
const memories = raw.map((m2) => {
|
|
10988
11042
|
if (!m2 || typeof m2 !== "object") return null;
|
|
10989
11043
|
const rec = m2;
|
|
10990
|
-
const
|
|
11044
|
+
const path60 = typeof rec.path === "string" ? rec.path : null;
|
|
10991
11045
|
const content = typeof rec.content === "string" ? rec.content : null;
|
|
10992
|
-
if (!
|
|
10993
|
-
const entry = { path:
|
|
11046
|
+
if (!path60 || content == null) return null;
|
|
11047
|
+
const entry = { path: path60, content };
|
|
10994
11048
|
if (typeof rec.mtimeMs === "number") entry.mtimeMs = rec.mtimeMs;
|
|
10995
11049
|
return entry;
|
|
10996
11050
|
}).filter((m2) => m2 !== null);
|
|
@@ -11821,10 +11875,10 @@ function parseAttachment(obj) {
|
|
|
11821
11875
|
const memories = raw.map((m2) => {
|
|
11822
11876
|
if (!m2 || typeof m2 !== "object") return null;
|
|
11823
11877
|
const rec = m2;
|
|
11824
|
-
const
|
|
11878
|
+
const path60 = typeof rec.path === "string" ? rec.path : null;
|
|
11825
11879
|
const content = typeof rec.content === "string" ? rec.content : null;
|
|
11826
|
-
if (!
|
|
11827
|
-
const out = { path:
|
|
11880
|
+
if (!path60 || content == null) return null;
|
|
11881
|
+
const out = { path: path60, content };
|
|
11828
11882
|
if (typeof rec.mtimeMs === "number") out.mtimeMs = rec.mtimeMs;
|
|
11829
11883
|
return out;
|
|
11830
11884
|
}).filter((m2) => m2 !== null);
|
|
@@ -19318,10 +19372,10 @@ var require_extension = __commonJS({
|
|
|
19318
19372
|
var require_websocket = __commonJS({
|
|
19319
19373
|
"../node_modules/.pnpm/ws@8.20.0/node_modules/ws/lib/websocket.js"(exports2, module2) {
|
|
19320
19374
|
"use strict";
|
|
19321
|
-
var
|
|
19375
|
+
var EventEmitter2 = require("events");
|
|
19322
19376
|
var https = require("https");
|
|
19323
|
-
var
|
|
19324
|
-
var
|
|
19377
|
+
var http2 = require("http");
|
|
19378
|
+
var net2 = require("net");
|
|
19325
19379
|
var tls = require("tls");
|
|
19326
19380
|
var { randomBytes, createHash: createHash2 } = require("crypto");
|
|
19327
19381
|
var { Duplex, Readable: Readable3 } = require("stream");
|
|
@@ -19350,7 +19404,7 @@ var require_websocket = __commonJS({
|
|
|
19350
19404
|
var protocolVersions = [8, 13];
|
|
19351
19405
|
var readyStates = ["CONNECTING", "OPEN", "CLOSING", "CLOSED"];
|
|
19352
19406
|
var subprotocolRegex = /^[!#$%&'*+\-.0-9A-Z^_`|a-z~]+$/;
|
|
19353
|
-
var WebSocket2 = class _WebSocket extends
|
|
19407
|
+
var WebSocket2 = class _WebSocket extends EventEmitter2 {
|
|
19354
19408
|
/**
|
|
19355
19409
|
* Create a new `WebSocket`.
|
|
19356
19410
|
*
|
|
@@ -19854,7 +19908,7 @@ var require_websocket = __commonJS({
|
|
|
19854
19908
|
}
|
|
19855
19909
|
const defaultPort = isSecure ? 443 : 80;
|
|
19856
19910
|
const key = randomBytes(16).toString("base64");
|
|
19857
|
-
const request = isSecure ? https.request :
|
|
19911
|
+
const request = isSecure ? https.request : http2.request;
|
|
19858
19912
|
const protocolSet = /* @__PURE__ */ new Set();
|
|
19859
19913
|
let perMessageDeflate;
|
|
19860
19914
|
opts.createConnection = opts.createConnection || (isSecure ? tlsConnect : netConnect);
|
|
@@ -20055,12 +20109,12 @@ var require_websocket = __commonJS({
|
|
|
20055
20109
|
}
|
|
20056
20110
|
function netConnect(options) {
|
|
20057
20111
|
options.path = options.socketPath;
|
|
20058
|
-
return
|
|
20112
|
+
return net2.connect(options);
|
|
20059
20113
|
}
|
|
20060
20114
|
function tlsConnect(options) {
|
|
20061
20115
|
options.path = void 0;
|
|
20062
20116
|
if (!options.servername && options.servername !== "") {
|
|
20063
|
-
options.servername =
|
|
20117
|
+
options.servername = net2.isIP(options.host) ? "" : options.host;
|
|
20064
20118
|
}
|
|
20065
20119
|
return tls.connect(options);
|
|
20066
20120
|
}
|
|
@@ -20347,8 +20401,8 @@ var require_subprotocol = __commonJS({
|
|
|
20347
20401
|
var require_websocket_server = __commonJS({
|
|
20348
20402
|
"../node_modules/.pnpm/ws@8.20.0/node_modules/ws/lib/websocket-server.js"(exports2, module2) {
|
|
20349
20403
|
"use strict";
|
|
20350
|
-
var
|
|
20351
|
-
var
|
|
20404
|
+
var EventEmitter2 = require("events");
|
|
20405
|
+
var http2 = require("http");
|
|
20352
20406
|
var { Duplex } = require("stream");
|
|
20353
20407
|
var { createHash: createHash2 } = require("crypto");
|
|
20354
20408
|
var extension2 = require_extension();
|
|
@@ -20360,7 +20414,7 @@ var require_websocket_server = __commonJS({
|
|
|
20360
20414
|
var RUNNING = 0;
|
|
20361
20415
|
var CLOSING = 1;
|
|
20362
20416
|
var CLOSED = 2;
|
|
20363
|
-
var WebSocketServer2 = class extends
|
|
20417
|
+
var WebSocketServer2 = class extends EventEmitter2 {
|
|
20364
20418
|
/**
|
|
20365
20419
|
* Create a `WebSocketServer` instance.
|
|
20366
20420
|
*
|
|
@@ -20423,8 +20477,8 @@ var require_websocket_server = __commonJS({
|
|
|
20423
20477
|
);
|
|
20424
20478
|
}
|
|
20425
20479
|
if (options.port != null) {
|
|
20426
|
-
this._server =
|
|
20427
|
-
const body =
|
|
20480
|
+
this._server = http2.createServer((req, res) => {
|
|
20481
|
+
const body = http2.STATUS_CODES[426];
|
|
20428
20482
|
res.writeHead(426, {
|
|
20429
20483
|
"Content-Length": body.length,
|
|
20430
20484
|
"Content-Type": "text/plain"
|
|
@@ -20711,7 +20765,7 @@ var require_websocket_server = __commonJS({
|
|
|
20711
20765
|
this.destroy();
|
|
20712
20766
|
}
|
|
20713
20767
|
function abortHandshake(socket, code, message, headers) {
|
|
20714
|
-
message = message ||
|
|
20768
|
+
message = message || http2.STATUS_CODES[code];
|
|
20715
20769
|
headers = {
|
|
20716
20770
|
Connection: "close",
|
|
20717
20771
|
"Content-Type": "text/html",
|
|
@@ -20720,7 +20774,7 @@ var require_websocket_server = __commonJS({
|
|
|
20720
20774
|
};
|
|
20721
20775
|
socket.once("finish", socket.destroy);
|
|
20722
20776
|
socket.end(
|
|
20723
|
-
`HTTP/1.1 ${code} ${
|
|
20777
|
+
`HTTP/1.1 ${code} ${http2.STATUS_CODES[code]}\r
|
|
20724
20778
|
` + Object.keys(headers).map((h) => `${h}: ${headers[h]}`).join("\r\n") + "\r\n\r\n" + message
|
|
20725
20779
|
);
|
|
20726
20780
|
}
|
|
@@ -21023,7 +21077,7 @@ var require_BufferList = __commonJS({
|
|
|
21023
21077
|
this.head = this.tail = null;
|
|
21024
21078
|
this.length = 0;
|
|
21025
21079
|
};
|
|
21026
|
-
BufferList.prototype.join = function
|
|
21080
|
+
BufferList.prototype.join = function join11(s) {
|
|
21027
21081
|
if (this.length === 0) return "";
|
|
21028
21082
|
var p2 = this.head;
|
|
21029
21083
|
var ret = "" + p2.data;
|
|
@@ -23613,8 +23667,8 @@ var require_utils = __commonJS({
|
|
|
23613
23667
|
var result = transform[inputType][outputType](input);
|
|
23614
23668
|
return result;
|
|
23615
23669
|
};
|
|
23616
|
-
exports2.resolve = function(
|
|
23617
|
-
var parts =
|
|
23670
|
+
exports2.resolve = function(path60) {
|
|
23671
|
+
var parts = path60.split("/");
|
|
23618
23672
|
var result = [];
|
|
23619
23673
|
for (var index = 0; index < parts.length; index++) {
|
|
23620
23674
|
var part = parts[index];
|
|
@@ -29467,18 +29521,18 @@ var require_object = __commonJS({
|
|
|
29467
29521
|
var object = new ZipObject(name, zipObjectContent, o);
|
|
29468
29522
|
this.files[name] = object;
|
|
29469
29523
|
};
|
|
29470
|
-
var parentFolder = function(
|
|
29471
|
-
if (
|
|
29472
|
-
|
|
29524
|
+
var parentFolder = function(path60) {
|
|
29525
|
+
if (path60.slice(-1) === "/") {
|
|
29526
|
+
path60 = path60.substring(0, path60.length - 1);
|
|
29473
29527
|
}
|
|
29474
|
-
var lastSlash =
|
|
29475
|
-
return lastSlash > 0 ?
|
|
29528
|
+
var lastSlash = path60.lastIndexOf("/");
|
|
29529
|
+
return lastSlash > 0 ? path60.substring(0, lastSlash) : "";
|
|
29476
29530
|
};
|
|
29477
|
-
var forceTrailingSlash = function(
|
|
29478
|
-
if (
|
|
29479
|
-
|
|
29531
|
+
var forceTrailingSlash = function(path60) {
|
|
29532
|
+
if (path60.slice(-1) !== "/") {
|
|
29533
|
+
path60 += "/";
|
|
29480
29534
|
}
|
|
29481
|
-
return
|
|
29535
|
+
return path60;
|
|
29482
29536
|
};
|
|
29483
29537
|
var folderAdd = function(name, createFolders) {
|
|
29484
29538
|
createFolders = typeof createFolders !== "undefined" ? createFolders : defaults.createFolders;
|
|
@@ -30933,8 +30987,6 @@ function resolveConfig(opts) {
|
|
|
30933
30987
|
const frpcBinary = env.CLAWD_FRPC_BIN ?? null;
|
|
30934
30988
|
const rawMode = env.CLAWD_CC_MODE;
|
|
30935
30989
|
const mode = DAEMON_MODE_VALUES.includes(rawMode ?? "") ? rawMode : "sdk";
|
|
30936
|
-
const filePreviewPorts = Array.isArray(fileCfg.previewPorts) ? fileCfg.previewPorts.map((n) => Number(n)).filter((n) => Number.isInteger(n) && n > 0) : null;
|
|
30937
|
-
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;
|
|
30938
30990
|
return {
|
|
30939
30991
|
port,
|
|
30940
30992
|
host,
|
|
@@ -30946,8 +30998,7 @@ function resolveConfig(opts) {
|
|
|
30946
30998
|
authToken,
|
|
30947
30999
|
noTunnelPersist,
|
|
30948
31000
|
frpcBinary,
|
|
30949
|
-
mode
|
|
30950
|
-
previewPorts
|
|
31001
|
+
mode
|
|
30951
31002
|
};
|
|
30952
31003
|
}
|
|
30953
31004
|
var HELP_TEXT = `clawd [options]
|
|
@@ -32587,6 +32638,14 @@ function derivePersonaSpawnCwd(file, personaRoot) {
|
|
|
32587
32638
|
}
|
|
32588
32639
|
return import_node_path7.default.join(personaRoot, safeFileName(personaId));
|
|
32589
32640
|
}
|
|
32641
|
+
function deriveGuestWorkspaceCwd(personaId, capId, personaRoot) {
|
|
32642
|
+
if (!personaRoot) {
|
|
32643
|
+
throw new Error(
|
|
32644
|
+
`deriveGuestWorkspaceCwd: personaRoot missing (personaId=${personaId}, capId=${capId})`
|
|
32645
|
+
);
|
|
32646
|
+
}
|
|
32647
|
+
return import_node_path7.default.join(personaRoot, safeFileName(personaId), "workspaces", safeFileName(capId));
|
|
32648
|
+
}
|
|
32590
32649
|
function makeInitialState(file, subSessionMeta) {
|
|
32591
32650
|
return {
|
|
32592
32651
|
file,
|
|
@@ -32948,17 +33007,38 @@ var SessionManager = class {
|
|
|
32948
33007
|
create(args, creatorPrincipalId, creatorOwnerPrincipalId) {
|
|
32949
33008
|
let cwd;
|
|
32950
33009
|
if (args.ownerPersonaId) {
|
|
32951
|
-
|
|
33010
|
+
const probeScope = scopeForFile(
|
|
32952
33011
|
{
|
|
32953
33012
|
sessionId: "",
|
|
32954
33013
|
cwd: args.cwd ?? "",
|
|
32955
33014
|
tool: "claude",
|
|
32956
33015
|
ownerPersonaId: args.ownerPersonaId,
|
|
33016
|
+
creatorPrincipalId,
|
|
32957
33017
|
createdAt: "",
|
|
32958
33018
|
updatedAt: ""
|
|
32959
33019
|
},
|
|
32960
|
-
this.deps.
|
|
33020
|
+
this.deps.ownerPrincipalId
|
|
32961
33021
|
);
|
|
33022
|
+
if (probeScope.kind === "persona" && probeScope.mode === "guest") {
|
|
33023
|
+
cwd = deriveGuestWorkspaceCwd(
|
|
33024
|
+
args.ownerPersonaId,
|
|
33025
|
+
probeScope.capId,
|
|
33026
|
+
this.deps.personaRoot
|
|
33027
|
+
);
|
|
33028
|
+
import_node_fs6.default.mkdirSync(cwd, { recursive: true });
|
|
33029
|
+
} else {
|
|
33030
|
+
cwd = derivePersonaSpawnCwd(
|
|
33031
|
+
{
|
|
33032
|
+
sessionId: "",
|
|
33033
|
+
cwd: args.cwd ?? "",
|
|
33034
|
+
tool: "claude",
|
|
33035
|
+
ownerPersonaId: args.ownerPersonaId,
|
|
33036
|
+
createdAt: "",
|
|
33037
|
+
updatedAt: ""
|
|
33038
|
+
},
|
|
33039
|
+
this.deps.personaRoot
|
|
33040
|
+
);
|
|
33041
|
+
}
|
|
32962
33042
|
} else if (args.cwd) {
|
|
32963
33043
|
cwd = args.cwd;
|
|
32964
33044
|
} else {
|
|
@@ -32986,7 +33066,6 @@ var SessionManager = class {
|
|
|
32986
33066
|
iconKey: args.iconKey,
|
|
32987
33067
|
forkedFromSessionId: args.forkedFromSessionId,
|
|
32988
33068
|
ownerPersonaId: args.ownerPersonaId,
|
|
32989
|
-
appBuilderProject: args.appBuilderProject,
|
|
32990
33069
|
creatorPrincipalId,
|
|
32991
33070
|
...creatorOwnerPrincipalId ? { creatorOwnerPrincipalId } : {},
|
|
32992
33071
|
createdAt: iso,
|
|
@@ -34106,6 +34185,35 @@ var PersonaStore = class {
|
|
|
34106
34185
|
if (!fs7.existsSync(p2)) return null;
|
|
34107
34186
|
return fs7.readFileSync(p2, "utf8");
|
|
34108
34187
|
}
|
|
34188
|
+
/**
|
|
34189
|
+
* Read-modify-write 更新 sandbox.network.allowedDomains(开发型 persona 网络白名单)。
|
|
34190
|
+
*
|
|
34191
|
+
* 只动 network.allowedDomains 一个字段:sandbox-settings.json 是 owner 可手改文件
|
|
34192
|
+
* (manager.spec.ts 锁定 "updating personality does NOT overwrite a customized
|
|
34193
|
+
* sandbox-settings.json"),整文件覆盖会丢手改内容。
|
|
34194
|
+
*
|
|
34195
|
+
* 文件不存在 / JSON 损坏 → 以 DEFAULT_SETTINGS 为底重建。
|
|
34196
|
+
* (损坏文件无法做 merge,与其让 RPC 失败不如重建——读路径 readSandboxSettings
|
|
34197
|
+
* 对损坏返回 null 不修复,写路径必须产出合法文件,两者职责不同。)
|
|
34198
|
+
*/
|
|
34199
|
+
updateNetworkAllowedDomains(personaId, domains) {
|
|
34200
|
+
const p2 = this.sandboxSettingsPath(personaId);
|
|
34201
|
+
let current;
|
|
34202
|
+
try {
|
|
34203
|
+
current = JSON.parse(fs7.readFileSync(p2, "utf8"));
|
|
34204
|
+
if (!current || typeof current !== "object" || Array.isArray(current)) {
|
|
34205
|
+
current = structuredClone(DEFAULT_SETTINGS);
|
|
34206
|
+
}
|
|
34207
|
+
} catch {
|
|
34208
|
+
current = structuredClone(DEFAULT_SETTINGS);
|
|
34209
|
+
}
|
|
34210
|
+
const sandboxRaw = current.sandbox;
|
|
34211
|
+
const sandbox = sandboxRaw && typeof sandboxRaw === "object" && !Array.isArray(sandboxRaw) ? sandboxRaw : current.sandbox = {};
|
|
34212
|
+
const networkRaw = sandbox.network;
|
|
34213
|
+
const network = networkRaw && typeof networkRaw === "object" && !Array.isArray(networkRaw) ? networkRaw : sandbox.network = {};
|
|
34214
|
+
network.allowedDomains = domains;
|
|
34215
|
+
this.atomicWrite(p2, JSON.stringify(current, null, 2));
|
|
34216
|
+
}
|
|
34109
34217
|
/**
|
|
34110
34218
|
* 读 sandbox-settings.json 当前内容;文件不存在 / JSON 解析失败均返回 null。
|
|
34111
34219
|
* owner 手改文件可能写出非法形状,daemon 不强校验,UI 拿到 null → 显示空状态。
|
|
@@ -34536,6 +34644,20 @@ var PersonaManager = class {
|
|
|
34536
34644
|
this.deps.registry.set(updated);
|
|
34537
34645
|
return updated;
|
|
34538
34646
|
}
|
|
34647
|
+
/**
|
|
34648
|
+
* 更新开发型 persona 的网络域名白名单(sandbox.network.allowedDomains)。
|
|
34649
|
+
* store 层做 read-modify-write(保留 owner 手改字段);这里只负责存在性校验 +
|
|
34650
|
+
* touch updatedAt + registry 同步,与 update() 的元数据维护行为一致。
|
|
34651
|
+
*/
|
|
34652
|
+
updateNetworkAllowedDomains(personaId, domains) {
|
|
34653
|
+
const existing = this.deps.registry.get(personaId);
|
|
34654
|
+
if (!existing) throw new Error(`persona not found: ${personaId}`);
|
|
34655
|
+
this.deps.store.updateNetworkAllowedDomains(personaId, domains);
|
|
34656
|
+
const updated = { ...existing, updatedAt: Date.now() };
|
|
34657
|
+
this.deps.store.writeMeta(updated);
|
|
34658
|
+
this.deps.registry.set(updated);
|
|
34659
|
+
return updated;
|
|
34660
|
+
}
|
|
34539
34661
|
/** 读取 CLAUDE.md 内容;persona 目录或文件不存在时返回 null。透传给 handler 拼响应。 */
|
|
34540
34662
|
readPersonality(personaId) {
|
|
34541
34663
|
return this.deps.store.readPersonality(personaId);
|
|
@@ -34623,15 +34745,6 @@ var DEFAULT_PERSONAS = [
|
|
|
34623
34745
|
model: "opus",
|
|
34624
34746
|
iconKey: "assist",
|
|
34625
34747
|
public: false
|
|
34626
|
-
},
|
|
34627
|
-
{
|
|
34628
|
-
// app-builder:全栈应用搭建师,绑 extension-kit 模板 + multi-project picker
|
|
34629
|
-
// spec: superpowers/specs/2026-06-01-app-builder-project-picker-design.md
|
|
34630
|
-
personaId: "persona-app-builder",
|
|
34631
|
-
label: "App Builder",
|
|
34632
|
-
model: "opus",
|
|
34633
|
-
iconKey: "assist",
|
|
34634
|
-
public: false
|
|
34635
34748
|
}
|
|
34636
34749
|
];
|
|
34637
34750
|
function findDefaultsRoot() {
|
|
@@ -36328,63 +36441,13 @@ var import_websocket_server = __toESM(require_websocket_server(), 1);
|
|
|
36328
36441
|
var wrapper_default = import_websocket.default;
|
|
36329
36442
|
|
|
36330
36443
|
// src/transport/local-ws-server.ts
|
|
36331
|
-
var import_node_http2 = __toESM(require("http"), 1);
|
|
36332
|
-
|
|
36333
|
-
// src/transport/preview-proxy.ts
|
|
36334
36444
|
var import_node_http = __toESM(require("http"), 1);
|
|
36335
|
-
var import_node_net = __toESM(require("net"), 1);
|
|
36336
|
-
var PREVIEW_RE = /^\/preview\/(\d+)(?:\/.*)?$/;
|
|
36337
|
-
function matchPreviewPort(pathname) {
|
|
36338
|
-
const m2 = pathname.match(PREVIEW_RE);
|
|
36339
|
-
return m2 ? Number(m2[1]) : null;
|
|
36340
|
-
}
|
|
36341
|
-
function isPreviewPortAllowed(port, cfg) {
|
|
36342
|
-
return cfg.allowedPorts.includes(port);
|
|
36343
|
-
}
|
|
36344
|
-
function proxyPreviewHttp(req, res, port) {
|
|
36345
|
-
return new Promise((resolve6) => {
|
|
36346
|
-
const upstream = import_node_http.default.request(
|
|
36347
|
-
{ host: "localhost", port, method: req.method, path: req.url, headers: req.headers },
|
|
36348
|
-
(upRes) => {
|
|
36349
|
-
res.writeHead(upRes.statusCode || 502, upRes.headers);
|
|
36350
|
-
upRes.pipe(res);
|
|
36351
|
-
upRes.on("end", () => resolve6());
|
|
36352
|
-
}
|
|
36353
|
-
);
|
|
36354
|
-
upstream.on("error", (e) => {
|
|
36355
|
-
if (!res.headersSent) {
|
|
36356
|
-
res.writeHead(502, { "content-type": "text/plain; charset=utf-8" });
|
|
36357
|
-
}
|
|
36358
|
-
res.end(`preview upstream error: ${e.message}`);
|
|
36359
|
-
resolve6();
|
|
36360
|
-
});
|
|
36361
|
-
req.pipe(upstream);
|
|
36362
|
-
});
|
|
36363
|
-
}
|
|
36364
|
-
function proxyPreviewUpgrade(req, socket, head, port) {
|
|
36365
|
-
const upstream = import_node_net.default.connect(port, "localhost", () => {
|
|
36366
|
-
let raw = `${req.method} ${req.url} HTTP/1.1\r
|
|
36367
|
-
`;
|
|
36368
|
-
for (let i = 0; i < req.rawHeaders.length; i += 2) {
|
|
36369
|
-
raw += `${req.rawHeaders[i]}: ${req.rawHeaders[i + 1]}\r
|
|
36370
|
-
`;
|
|
36371
|
-
}
|
|
36372
|
-
raw += "\r\n";
|
|
36373
|
-
upstream.write(raw);
|
|
36374
|
-
if (head && head.length) upstream.write(head);
|
|
36375
|
-
upstream.pipe(socket);
|
|
36376
|
-
socket.pipe(upstream);
|
|
36377
|
-
});
|
|
36378
|
-
upstream.on("error", () => socket.destroy());
|
|
36379
|
-
socket.on("error", () => upstream.destroy());
|
|
36380
|
-
}
|
|
36381
|
-
|
|
36382
|
-
// src/transport/local-ws-server.ts
|
|
36383
36445
|
var LocalWsServer = class {
|
|
36384
36446
|
constructor(opts) {
|
|
36385
36447
|
this.opts = opts;
|
|
36386
36448
|
this.logger = opts.logger;
|
|
36387
36449
|
this.pingIntervalMs = opts.pingIntervalMs ?? 3e4;
|
|
36450
|
+
this.currentOwnerPrincipalId = opts.ownerPrincipalId;
|
|
36388
36451
|
}
|
|
36389
36452
|
opts;
|
|
36390
36453
|
wss = null;
|
|
@@ -36396,28 +36459,19 @@ var LocalWsServer = class {
|
|
|
36396
36459
|
capabilityIdToClients = /* @__PURE__ */ new Map();
|
|
36397
36460
|
logger;
|
|
36398
36461
|
pingIntervalMs;
|
|
36462
|
+
// 飞书热切换(spec 决策 #9,2026-06-01 拍板):登录后实时更新,broadcastToPrincipal
|
|
36463
|
+
// 路由用最新身份;初始值来自 opts.ownerPrincipalId
|
|
36464
|
+
currentOwnerPrincipalId;
|
|
36465
|
+
/** 飞书热切换:登录回调成功后由 daemon wiring 调用,更新 DM 路由身份 */
|
|
36466
|
+
setOwnerPrincipalId(id) {
|
|
36467
|
+
this.currentOwnerPrincipalId = id;
|
|
36468
|
+
}
|
|
36399
36469
|
async start() {
|
|
36400
36470
|
const host = this.opts.host ?? "127.0.0.1";
|
|
36401
36471
|
await new Promise((resolve6, reject) => {
|
|
36402
|
-
const httpServer =
|
|
36472
|
+
const httpServer = import_node_http.default.createServer((req, res) => this.handleHttpRequest(req, res));
|
|
36403
36473
|
const wss = new import_websocket_server.default({ noServer: true, clientTracking: true });
|
|
36404
36474
|
httpServer.on("upgrade", (req, socket, head) => {
|
|
36405
|
-
if (req.url?.startsWith("/preview/")) {
|
|
36406
|
-
const pathname = (() => {
|
|
36407
|
-
try {
|
|
36408
|
-
return new URL(req.url, "http://localhost").pathname;
|
|
36409
|
-
} catch {
|
|
36410
|
-
return "/";
|
|
36411
|
-
}
|
|
36412
|
-
})();
|
|
36413
|
-
const port = matchPreviewPort(pathname);
|
|
36414
|
-
if (port != null && (this.opts.previewPorts ?? []).includes(port)) {
|
|
36415
|
-
proxyPreviewUpgrade(req, socket, head, port);
|
|
36416
|
-
} else {
|
|
36417
|
-
socket.destroy();
|
|
36418
|
-
}
|
|
36419
|
-
return;
|
|
36420
|
-
}
|
|
36421
36475
|
wss.handleUpgrade(req, socket, head, (ws) => {
|
|
36422
36476
|
this.routeConnection(ws, req);
|
|
36423
36477
|
});
|
|
@@ -36531,7 +36585,7 @@ var LocalWsServer = class {
|
|
|
36531
36585
|
// principalId === 'cap_xxx' → 该 capability 的所有 guest ws (多端 / 多 tab)
|
|
36532
36586
|
// 用于 DM inbox:event 路由: from + to 两侧各播一次, 让双方 UI 实时看到 thread 更新.
|
|
36533
36587
|
broadcastToPrincipal(principalId, frame) {
|
|
36534
|
-
if (principalId === "owner" || principalId === this.
|
|
36588
|
+
if (principalId === "owner" || principalId === this.currentOwnerPrincipalId) {
|
|
36535
36589
|
this.broadcastToOwners(frame);
|
|
36536
36590
|
return;
|
|
36537
36591
|
}
|
|
@@ -36583,6 +36637,21 @@ var LocalWsServer = class {
|
|
|
36583
36637
|
getClientContext(clientId) {
|
|
36584
36638
|
return this.clients.get(clientId)?.ctx ?? null;
|
|
36585
36639
|
}
|
|
36640
|
+
// 飞书热切换(spec 决策 #9):身份切换后踢掉所有连接强制重连——在线连接的 ctx
|
|
36641
|
+
// (attachClientContext 时绑定)持有旧身份,重连后用新身份重建。
|
|
36642
|
+
// close code 4408(非 4401!4401 = token 撤销,客户端会停止重连):
|
|
36643
|
+
// 客户端按普通断连处理 → 自动重连 → 新身份 ctx。
|
|
36644
|
+
closeAllClients(code = 4408, reason = "IDENTITY_SWITCHED") {
|
|
36645
|
+
const ids = [...this.clients.keys()];
|
|
36646
|
+
for (const id of ids) {
|
|
36647
|
+
const c = this.clients.get(id);
|
|
36648
|
+
if (!c) continue;
|
|
36649
|
+
try {
|
|
36650
|
+
c.ws.close(code, reason);
|
|
36651
|
+
} catch {
|
|
36652
|
+
}
|
|
36653
|
+
}
|
|
36654
|
+
}
|
|
36586
36655
|
// Task 1.10 撤销级联:关闭某 capability 的所有活跃 ws 连接。close code 4401
|
|
36587
36656
|
// 让客户端识别 'capability revoked' 而非普通断连。
|
|
36588
36657
|
closeConnectionsByCapability(capabilityId, code = 4401, reason = "TOKEN_REVOKED") {
|
|
@@ -38065,13 +38134,23 @@ function createHttpRouter(deps) {
|
|
|
38065
38134
|
});
|
|
38066
38135
|
return true;
|
|
38067
38136
|
}
|
|
38068
|
-
if (url.pathname
|
|
38069
|
-
|
|
38070
|
-
|
|
38071
|
-
await proxyPreviewHttp(req, res, port);
|
|
38137
|
+
if (url.pathname === "/auth/callback") {
|
|
38138
|
+
if (req.method !== "GET") {
|
|
38139
|
+
sendJson(res, 404, { code: "NOT_FOUND", message: `no route for ${req.method} ${url.pathname}` });
|
|
38072
38140
|
return true;
|
|
38073
38141
|
}
|
|
38074
|
-
|
|
38142
|
+
if (!deps.feishuLogin) {
|
|
38143
|
+
sendJson(res, 501, { code: "NOT_IMPLEMENTED", message: "feishu login not wired" });
|
|
38144
|
+
return true;
|
|
38145
|
+
}
|
|
38146
|
+
const result = await deps.feishuLogin.handleLoginCallback({
|
|
38147
|
+
token: url.searchParams.get("token") ?? "",
|
|
38148
|
+
state: url.searchParams.get("state") ?? "",
|
|
38149
|
+
expiresAt: url.searchParams.get("expires_at")
|
|
38150
|
+
});
|
|
38151
|
+
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>`;
|
|
38152
|
+
res.writeHead(result.ok ? 200 : 400, { "Content-Type": "text/html; charset=utf-8" });
|
|
38153
|
+
res.end(html);
|
|
38075
38154
|
return true;
|
|
38076
38155
|
}
|
|
38077
38156
|
if (!url.pathname.startsWith("/session/") && !url.pathname.startsWith("/files/") && url.pathname !== "/attachments/upload" && url.pathname !== "/extensions/import") {
|
|
@@ -38309,6 +38388,9 @@ function parseUrl(rawUrl) {
|
|
|
38309
38388
|
return null;
|
|
38310
38389
|
}
|
|
38311
38390
|
}
|
|
38391
|
+
function escapeHtml(s) {
|
|
38392
|
+
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
38393
|
+
}
|
|
38312
38394
|
function sendJson(res, status, body, extraHeaders) {
|
|
38313
38395
|
res.writeHead(status, {
|
|
38314
38396
|
"Content-Type": "application/json; charset=utf-8",
|
|
@@ -39331,6 +39413,160 @@ function loadOwnerDisplayName(dataDir) {
|
|
|
39331
39413
|
return displayName;
|
|
39332
39414
|
}
|
|
39333
39415
|
|
|
39416
|
+
// src/feishu-auth/owner-identity-store.ts
|
|
39417
|
+
var import_node_fs27 = __toESM(require("fs"), 1);
|
|
39418
|
+
var import_node_path31 = __toESM(require("path"), 1);
|
|
39419
|
+
var OWNER_IDENTITY_FILE_NAME = "owner-identity.json";
|
|
39420
|
+
var OwnerIdentityStore = class {
|
|
39421
|
+
file;
|
|
39422
|
+
constructor(dataDir) {
|
|
39423
|
+
this.file = import_node_path31.default.join(dataDir, OWNER_IDENTITY_FILE_NAME);
|
|
39424
|
+
}
|
|
39425
|
+
read() {
|
|
39426
|
+
let raw;
|
|
39427
|
+
try {
|
|
39428
|
+
raw = import_node_fs27.default.readFileSync(this.file, "utf8");
|
|
39429
|
+
} catch {
|
|
39430
|
+
return null;
|
|
39431
|
+
}
|
|
39432
|
+
let parsed;
|
|
39433
|
+
try {
|
|
39434
|
+
parsed = JSON.parse(raw);
|
|
39435
|
+
} catch {
|
|
39436
|
+
return null;
|
|
39437
|
+
}
|
|
39438
|
+
const r = parsed;
|
|
39439
|
+
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) {
|
|
39440
|
+
return null;
|
|
39441
|
+
}
|
|
39442
|
+
return {
|
|
39443
|
+
identity: {
|
|
39444
|
+
unionId: r.identity.unionId,
|
|
39445
|
+
displayName: r.identity.displayName,
|
|
39446
|
+
...typeof r.identity.avatarUrl === "string" ? { avatarUrl: r.identity.avatarUrl } : {}
|
|
39447
|
+
},
|
|
39448
|
+
ttcToken: r.ttcToken,
|
|
39449
|
+
ttcTokenExpiresAt: typeof r.ttcTokenExpiresAt === "number" ? r.ttcTokenExpiresAt : null,
|
|
39450
|
+
loginAt: typeof r.loginAt === "string" ? r.loginAt : (/* @__PURE__ */ new Date(0)).toISOString()
|
|
39451
|
+
};
|
|
39452
|
+
}
|
|
39453
|
+
write(record) {
|
|
39454
|
+
import_node_fs27.default.mkdirSync(import_node_path31.default.dirname(this.file), { recursive: true });
|
|
39455
|
+
import_node_fs27.default.writeFileSync(this.file, JSON.stringify(record, null, 2), { mode: 384 });
|
|
39456
|
+
try {
|
|
39457
|
+
import_node_fs27.default.chmodSync(this.file, 384);
|
|
39458
|
+
} catch {
|
|
39459
|
+
}
|
|
39460
|
+
}
|
|
39461
|
+
clear() {
|
|
39462
|
+
try {
|
|
39463
|
+
import_node_fs27.default.unlinkSync(this.file);
|
|
39464
|
+
} catch (err) {
|
|
39465
|
+
const code = err?.code;
|
|
39466
|
+
if (code !== "ENOENT") throw err;
|
|
39467
|
+
}
|
|
39468
|
+
}
|
|
39469
|
+
};
|
|
39470
|
+
|
|
39471
|
+
// src/feishu-auth/login-flow.ts
|
|
39472
|
+
var import_node_crypto11 = __toESM(require("crypto"), 1);
|
|
39473
|
+
var STATE_TTL_MS = 5 * 60 * 1e3;
|
|
39474
|
+
var LoginFlow = class {
|
|
39475
|
+
constructor(deps) {
|
|
39476
|
+
this.deps = deps;
|
|
39477
|
+
}
|
|
39478
|
+
deps;
|
|
39479
|
+
pendingStates = /* @__PURE__ */ new Map();
|
|
39480
|
+
start() {
|
|
39481
|
+
const state = import_node_crypto11.default.randomBytes(16).toString("base64url");
|
|
39482
|
+
const now = (this.deps.now ?? Date.now)();
|
|
39483
|
+
this.pendingStates.set(state, now);
|
|
39484
|
+
this.gcExpired(now);
|
|
39485
|
+
const url = new URL("/auth/authorize", this.deps.ttcAuthBase);
|
|
39486
|
+
url.searchParams.set("callback_url", this.deps.getCallbackUrl());
|
|
39487
|
+
url.searchParams.set("state", state);
|
|
39488
|
+
url.searchParams.set("auto", "1");
|
|
39489
|
+
return { authUrl: url.toString(), state };
|
|
39490
|
+
}
|
|
39491
|
+
async handleCallback(params) {
|
|
39492
|
+
const now = (this.deps.now ?? Date.now)();
|
|
39493
|
+
const issuedAt = this.pendingStates.get(params.state);
|
|
39494
|
+
if (issuedAt === void 0) {
|
|
39495
|
+
return { ok: false, reason: "state mismatch" };
|
|
39496
|
+
}
|
|
39497
|
+
this.pendingStates.delete(params.state);
|
|
39498
|
+
if (now - issuedAt > STATE_TTL_MS) {
|
|
39499
|
+
return { ok: false, reason: "state expired" };
|
|
39500
|
+
}
|
|
39501
|
+
if (!params.token) {
|
|
39502
|
+
return { ok: false, reason: "missing token" };
|
|
39503
|
+
}
|
|
39504
|
+
let identity;
|
|
39505
|
+
try {
|
|
39506
|
+
identity = await this.deps.fetchUserInfo(params.token);
|
|
39507
|
+
} catch (err) {
|
|
39508
|
+
return { ok: false, reason: `user_info failed: ${err.message}` };
|
|
39509
|
+
}
|
|
39510
|
+
const expiresAtNum = params.expiresAt ? Number.parseInt(params.expiresAt, 10) : NaN;
|
|
39511
|
+
this.deps.store.write({
|
|
39512
|
+
identity,
|
|
39513
|
+
ttcToken: params.token,
|
|
39514
|
+
ttcTokenExpiresAt: Number.isFinite(expiresAtNum) ? expiresAtNum : null,
|
|
39515
|
+
loginAt: new Date(now).toISOString()
|
|
39516
|
+
});
|
|
39517
|
+
return { ok: true, identity };
|
|
39518
|
+
}
|
|
39519
|
+
gcExpired(now) {
|
|
39520
|
+
for (const [state, issuedAt] of this.pendingStates) {
|
|
39521
|
+
if (now - issuedAt > STATE_TTL_MS) this.pendingStates.delete(state);
|
|
39522
|
+
}
|
|
39523
|
+
}
|
|
39524
|
+
};
|
|
39525
|
+
|
|
39526
|
+
// src/feishu-auth/ttc-client.ts
|
|
39527
|
+
var TTC_HOSTS = {
|
|
39528
|
+
auth: "https://app.ttcadvisory.com",
|
|
39529
|
+
api: "https://api.ttcadvisory.com"
|
|
39530
|
+
};
|
|
39531
|
+
var TtcUserInfoError = class extends Error {
|
|
39532
|
+
constructor(code, message) {
|
|
39533
|
+
super(message);
|
|
39534
|
+
this.code = code;
|
|
39535
|
+
this.name = "TtcUserInfoError";
|
|
39536
|
+
}
|
|
39537
|
+
code;
|
|
39538
|
+
};
|
|
39539
|
+
async function fetchTtcUserInfo(opts) {
|
|
39540
|
+
const doFetch = opts.fetchImpl ?? fetch;
|
|
39541
|
+
let res;
|
|
39542
|
+
try {
|
|
39543
|
+
res = await doFetch(`${opts.apiBase}/api/user_service/v1/login/user`, {
|
|
39544
|
+
headers: { Authorization: `Bearer ${opts.token}` }
|
|
39545
|
+
});
|
|
39546
|
+
} catch (err) {
|
|
39547
|
+
throw new TtcUserInfoError("NETWORK", `TTC user_info request failed: ${err.message}`);
|
|
39548
|
+
}
|
|
39549
|
+
if (!res.ok) {
|
|
39550
|
+
if (res.status === 401) {
|
|
39551
|
+
throw new TtcUserInfoError("UNAUTHORIZED", "TTC token invalid or expired");
|
|
39552
|
+
}
|
|
39553
|
+
throw new TtcUserInfoError("API_ERROR", `TTC user_info HTTP ${res.status}`);
|
|
39554
|
+
}
|
|
39555
|
+
const body = await res.json();
|
|
39556
|
+
if (body.code !== 0) {
|
|
39557
|
+
throw new TtcUserInfoError("API_ERROR", `TTC user_info code=${body.code}: ${body.message ?? ""}`);
|
|
39558
|
+
}
|
|
39559
|
+
const d = body.data;
|
|
39560
|
+
if (!d || typeof d.union_id !== "string" || d.union_id.length === 0 || typeof d.name !== "string" || d.name.length === 0) {
|
|
39561
|
+
throw new TtcUserInfoError("BAD_RESPONSE", "TTC user_info missing union_id or name");
|
|
39562
|
+
}
|
|
39563
|
+
return {
|
|
39564
|
+
unionId: d.union_id,
|
|
39565
|
+
displayName: d.name,
|
|
39566
|
+
...typeof d.avatar_url === "string" && d.avatar_url.length > 0 ? { avatarUrl: d.avatar_url } : {}
|
|
39567
|
+
};
|
|
39568
|
+
}
|
|
39569
|
+
|
|
39334
39570
|
// src/index.ts
|
|
39335
39571
|
init_protocol();
|
|
39336
39572
|
|
|
@@ -39338,12 +39574,12 @@ init_protocol();
|
|
|
39338
39574
|
init_protocol();
|
|
39339
39575
|
|
|
39340
39576
|
// src/session/fork.ts
|
|
39341
|
-
var
|
|
39577
|
+
var import_node_fs28 = __toESM(require("fs"), 1);
|
|
39342
39578
|
var import_node_os14 = __toESM(require("os"), 1);
|
|
39343
|
-
var
|
|
39579
|
+
var import_node_path32 = __toESM(require("path"), 1);
|
|
39344
39580
|
init_claude_history();
|
|
39345
39581
|
function readJsonlEntries(file) {
|
|
39346
|
-
const raw =
|
|
39582
|
+
const raw = import_node_fs28.default.readFileSync(file, "utf8");
|
|
39347
39583
|
const out = [];
|
|
39348
39584
|
for (const line of raw.split("\n")) {
|
|
39349
39585
|
const t = line.trim();
|
|
@@ -39356,10 +39592,10 @@ function readJsonlEntries(file) {
|
|
|
39356
39592
|
return out;
|
|
39357
39593
|
}
|
|
39358
39594
|
function forkSession(input) {
|
|
39359
|
-
const baseDir = input.baseDir ??
|
|
39360
|
-
const projectDir =
|
|
39361
|
-
const sourceFile =
|
|
39362
|
-
if (!
|
|
39595
|
+
const baseDir = input.baseDir ?? import_node_path32.default.join(import_node_os14.default.homedir(), ".claude");
|
|
39596
|
+
const projectDir = import_node_path32.default.join(baseDir, "projects", cwdToHashDir(input.cwd));
|
|
39597
|
+
const sourceFile = import_node_path32.default.join(projectDir, `${input.toolSessionId}.jsonl`);
|
|
39598
|
+
if (!import_node_fs28.default.existsSync(sourceFile)) {
|
|
39363
39599
|
throw new Error(`fork: source transcript not found: ${sourceFile}`);
|
|
39364
39600
|
}
|
|
39365
39601
|
const entries = readJsonlEntries(sourceFile);
|
|
@@ -39389,9 +39625,9 @@ function forkSession(input) {
|
|
|
39389
39625
|
}
|
|
39390
39626
|
forkedLines.push(JSON.stringify(forked));
|
|
39391
39627
|
}
|
|
39392
|
-
const forkedFilePath =
|
|
39393
|
-
|
|
39394
|
-
|
|
39628
|
+
const forkedFilePath = import_node_path32.default.join(projectDir, `${forkedToolSessionId}.jsonl`);
|
|
39629
|
+
import_node_fs28.default.mkdirSync(projectDir, { recursive: true });
|
|
39630
|
+
import_node_fs28.default.writeFileSync(forkedFilePath, forkedLines.join("\n") + "\n", { mode: 384 });
|
|
39395
39631
|
return { forkedToolSessionId, forkedFilePath };
|
|
39396
39632
|
}
|
|
39397
39633
|
|
|
@@ -39491,67 +39727,7 @@ function buildSessionHandlers(deps) {
|
|
|
39491
39727
|
const update = async (frame, _client, ctx) => {
|
|
39492
39728
|
const args = SessionUpdateArgs.parse(frame);
|
|
39493
39729
|
ensureSessionAccess(ctx, args.sessionId, "send");
|
|
39494
|
-
const prevFile = manager.findOwnedSession(args.sessionId);
|
|
39495
|
-
const prevProject = prevFile?.appBuilderProject ?? null;
|
|
39496
|
-
const nextProject = args.patch.appBuilderProject ?? prevProject;
|
|
39497
39730
|
const { response, broadcast } = manager.update(args);
|
|
39498
|
-
if (args.patch.appBuilderProject !== void 0) {
|
|
39499
|
-
deps.logger?.info("session-update.appBuilderProject", {
|
|
39500
|
-
sessionId: args.sessionId,
|
|
39501
|
-
prevProject,
|
|
39502
|
-
nextProject,
|
|
39503
|
-
hasLifecycle: !!deps.devServerLifecycle,
|
|
39504
|
-
hasStore: !!deps.appBuilderStore
|
|
39505
|
-
});
|
|
39506
|
-
}
|
|
39507
|
-
if (deps.devServerLifecycle && deps.appBuilderStore && args.patch.appBuilderProject !== void 0) {
|
|
39508
|
-
const lifecycle = deps.devServerLifecycle;
|
|
39509
|
-
const projectStore = deps.appBuilderStore;
|
|
39510
|
-
const tunnelUrl = deps.getTunnelUrl?.() ?? null;
|
|
39511
|
-
try {
|
|
39512
|
-
if (prevProject && prevProject !== nextProject) {
|
|
39513
|
-
await lifecycle.stopForSession(args.sessionId);
|
|
39514
|
-
}
|
|
39515
|
-
if (nextProject && nextProject !== "") {
|
|
39516
|
-
const occupantSessionId = lifecycle.boundSessionId(nextProject);
|
|
39517
|
-
const alreadyRunningForThisSession = lifecycle.isRunning(nextProject) && occupantSessionId === args.sessionId;
|
|
39518
|
-
deps.logger?.info("session-update.maybe-start-dev-server", {
|
|
39519
|
-
sessionId: args.sessionId,
|
|
39520
|
-
projectName: nextProject,
|
|
39521
|
-
occupantSessionId,
|
|
39522
|
-
alreadyRunningForThisSession
|
|
39523
|
-
});
|
|
39524
|
-
if (!alreadyRunningForThisSession) {
|
|
39525
|
-
if (occupantSessionId && occupantSessionId !== args.sessionId) {
|
|
39526
|
-
await lifecycle.stopForSession(occupantSessionId);
|
|
39527
|
-
}
|
|
39528
|
-
const projects = await projectStore.list();
|
|
39529
|
-
const target = projects.find((p2) => p2.name === nextProject);
|
|
39530
|
-
deps.logger?.info("session-update.start-dev-server", {
|
|
39531
|
-
sessionId: args.sessionId,
|
|
39532
|
-
projectName: nextProject,
|
|
39533
|
-
targetFound: !!target,
|
|
39534
|
-
projectCwd: target ? projectStore.projectDir(nextProject) : null,
|
|
39535
|
-
tunnelUrl
|
|
39536
|
-
});
|
|
39537
|
-
if (target) {
|
|
39538
|
-
lifecycle.startForSession({
|
|
39539
|
-
sessionId: args.sessionId,
|
|
39540
|
-
projectName: nextProject,
|
|
39541
|
-
cwd: projectStore.projectDir(nextProject),
|
|
39542
|
-
port: target.port,
|
|
39543
|
-
tunnelHost: tunnelUrl ? new URL(tunnelUrl.replace(/^wss?:\/\//i, "https://")).host : null
|
|
39544
|
-
});
|
|
39545
|
-
}
|
|
39546
|
-
}
|
|
39547
|
-
}
|
|
39548
|
-
} catch (err) {
|
|
39549
|
-
deps.logger?.warn("session-update.lifecycle-failed", {
|
|
39550
|
-
sessionId: args.sessionId,
|
|
39551
|
-
error: err instanceof Error ? err.message : String(err)
|
|
39552
|
-
});
|
|
39553
|
-
}
|
|
39554
|
-
}
|
|
39555
39731
|
return { response: { type: "session:info", ...response }, broadcast };
|
|
39556
39732
|
};
|
|
39557
39733
|
const del = async (frame, _client, ctx) => {
|
|
@@ -39803,7 +39979,7 @@ function buildPermissionHandlers(deps) {
|
|
|
39803
39979
|
}
|
|
39804
39980
|
|
|
39805
39981
|
// src/handlers/history.ts
|
|
39806
|
-
var
|
|
39982
|
+
var path42 = __toESM(require("path"), 1);
|
|
39807
39983
|
init_protocol();
|
|
39808
39984
|
|
|
39809
39985
|
// src/session/recent-dirs.ts
|
|
@@ -39821,7 +39997,7 @@ function listRecentDirs(store, limit = 50) {
|
|
|
39821
39997
|
}
|
|
39822
39998
|
|
|
39823
39999
|
// src/permission/persona-paths.ts
|
|
39824
|
-
var
|
|
40000
|
+
var path41 = __toESM(require("path"), 1);
|
|
39825
40001
|
function getAllowedPersonaIds(grants, action) {
|
|
39826
40002
|
const ids = /* @__PURE__ */ new Set();
|
|
39827
40003
|
for (const g2 of grants) {
|
|
@@ -39834,26 +40010,26 @@ function getAllowedPersonaIds(grants, action) {
|
|
|
39834
40010
|
return ids;
|
|
39835
40011
|
}
|
|
39836
40012
|
function isGuestPathAllowed(grants, absPath, personaRoot, action = "read") {
|
|
39837
|
-
const root =
|
|
39838
|
-
const target =
|
|
39839
|
-
const sep3 = root.endsWith(
|
|
40013
|
+
const root = path41.resolve(personaRoot);
|
|
40014
|
+
const target = path41.resolve(absPath);
|
|
40015
|
+
const sep3 = root.endsWith(path41.sep) ? "" : path41.sep;
|
|
39840
40016
|
if (!target.startsWith(root + sep3)) return false;
|
|
39841
|
-
const rel =
|
|
40017
|
+
const rel = path41.relative(root, target);
|
|
39842
40018
|
if (!rel || rel.startsWith("..")) return false;
|
|
39843
|
-
const personaId = rel.split(
|
|
40019
|
+
const personaId = rel.split(path41.sep)[0];
|
|
39844
40020
|
if (!personaId) return false;
|
|
39845
40021
|
const allowed = getAllowedPersonaIds(grants, action);
|
|
39846
40022
|
if (allowed === "*") return true;
|
|
39847
40023
|
return allowed.has(personaId);
|
|
39848
40024
|
}
|
|
39849
40025
|
function personaIdFromPath(absPath, personaRoot) {
|
|
39850
|
-
const root =
|
|
39851
|
-
const target =
|
|
39852
|
-
const sep3 = root.endsWith(
|
|
40026
|
+
const root = path41.resolve(personaRoot);
|
|
40027
|
+
const target = path41.resolve(absPath);
|
|
40028
|
+
const sep3 = root.endsWith(path41.sep) ? "" : path41.sep;
|
|
39853
40029
|
if (!target.startsWith(root + sep3)) return null;
|
|
39854
|
-
const rel =
|
|
40030
|
+
const rel = path41.relative(root, target);
|
|
39855
40031
|
if (!rel || rel.startsWith("..")) return null;
|
|
39856
|
-
const id = rel.split(
|
|
40032
|
+
const id = rel.split(path41.sep)[0];
|
|
39857
40033
|
return id || null;
|
|
39858
40034
|
}
|
|
39859
40035
|
|
|
@@ -39878,7 +40054,7 @@ function buildHistoryHandlers(deps) {
|
|
|
39878
40054
|
if (!pid) return false;
|
|
39879
40055
|
return isGuestPathAllowed(
|
|
39880
40056
|
ctx.grants,
|
|
39881
|
-
|
|
40057
|
+
path42.join(personaRoot, pid),
|
|
39882
40058
|
personaRoot,
|
|
39883
40059
|
"read"
|
|
39884
40060
|
);
|
|
@@ -39889,7 +40065,7 @@ function buildHistoryHandlers(deps) {
|
|
|
39889
40065
|
};
|
|
39890
40066
|
const list = async (frame, _client, ctx) => {
|
|
39891
40067
|
const args = HistoryListArgs.parse(frame);
|
|
39892
|
-
assertGuestPath(ctx,
|
|
40068
|
+
assertGuestPath(ctx, path42.resolve(args.projectPath), personaRoot, "history:list");
|
|
39893
40069
|
const sessions = await history.listSessions(args);
|
|
39894
40070
|
return { response: { type: "history:list", sessions } };
|
|
39895
40071
|
};
|
|
@@ -39921,13 +40097,13 @@ function buildHistoryHandlers(deps) {
|
|
|
39921
40097
|
};
|
|
39922
40098
|
const subagents = async (frame, _client, ctx) => {
|
|
39923
40099
|
const args = HistorySubagentsArgs.parse(frame);
|
|
39924
|
-
assertGuestPath(ctx,
|
|
40100
|
+
assertGuestPath(ctx, path42.resolve(args.cwd), personaRoot, "history:subagents");
|
|
39925
40101
|
const subs = await history.listSubagents(args);
|
|
39926
40102
|
return { response: { type: "history:subagents", subagents: subs } };
|
|
39927
40103
|
};
|
|
39928
40104
|
const subagentRead = async (frame, _client, ctx) => {
|
|
39929
40105
|
const args = HistorySubagentReadArgs.parse(frame);
|
|
39930
|
-
assertGuestPath(ctx,
|
|
40106
|
+
assertGuestPath(ctx, path42.resolve(args.cwd), personaRoot, "history:subagent-read");
|
|
39931
40107
|
const res = await history.readSubagent(args);
|
|
39932
40108
|
return { response: { type: "history:subagent-read", ...res } };
|
|
39933
40109
|
};
|
|
@@ -39935,7 +40111,7 @@ function buildHistoryHandlers(deps) {
|
|
|
39935
40111
|
const dirs = listRecentDirs(store);
|
|
39936
40112
|
if (ctx?.principal.kind === "guest" && personaRoot) {
|
|
39937
40113
|
const filtered = dirs.filter(
|
|
39938
|
-
(d) => isGuestPathAllowed(ctx.grants,
|
|
40114
|
+
(d) => isGuestPathAllowed(ctx.grants, path42.resolve(d.cwd), personaRoot, "read")
|
|
39939
40115
|
);
|
|
39940
40116
|
return { response: { type: "history:recentDirs", dirs: filtered } };
|
|
39941
40117
|
}
|
|
@@ -39952,7 +40128,7 @@ function buildHistoryHandlers(deps) {
|
|
|
39952
40128
|
}
|
|
39953
40129
|
|
|
39954
40130
|
// src/handlers/workspace.ts
|
|
39955
|
-
var
|
|
40131
|
+
var path43 = __toESM(require("path"), 1);
|
|
39956
40132
|
var os15 = __toESM(require("os"), 1);
|
|
39957
40133
|
init_protocol();
|
|
39958
40134
|
init_protocol();
|
|
@@ -39993,22 +40169,22 @@ function buildWorkspaceHandlers(deps) {
|
|
|
39993
40169
|
const args = WorkspaceListArgs.parse(frame);
|
|
39994
40170
|
const isGuest = ctx?.principal.kind === "guest";
|
|
39995
40171
|
const fallbackCwd = isGuest && personaRoot ? personaRoot : os15.homedir();
|
|
39996
|
-
const resolvedCwd =
|
|
39997
|
-
const target = args.path ?
|
|
40172
|
+
const resolvedCwd = path43.resolve(args.cwd ?? fallbackCwd);
|
|
40173
|
+
const target = args.path ? path43.resolve(resolvedCwd, args.path) : resolvedCwd;
|
|
39998
40174
|
assertGuestPath2(ctx, target, personaRoot, "workspace:list");
|
|
39999
40175
|
const res = workspace.list({ ...args, cwd: resolvedCwd });
|
|
40000
40176
|
return { response: { type: "workspace:list", ...res } };
|
|
40001
40177
|
};
|
|
40002
40178
|
const read = async (frame, _client, ctx) => {
|
|
40003
40179
|
const args = WorkspaceReadArgs.parse(frame);
|
|
40004
|
-
const target =
|
|
40180
|
+
const target = path43.isAbsolute(args.path) ? path43.resolve(args.path) : path43.resolve(args.cwd, args.path);
|
|
40005
40181
|
assertGuestPath2(ctx, target, personaRoot, "workspace:read");
|
|
40006
40182
|
const res = workspace.read(args);
|
|
40007
40183
|
return { response: { type: "workspace:read", ...res } };
|
|
40008
40184
|
};
|
|
40009
40185
|
const skillsList = async (frame, _client, ctx) => {
|
|
40010
40186
|
const args = SkillsListArgs.parse(frame);
|
|
40011
|
-
const cwdAbs =
|
|
40187
|
+
const cwdAbs = path43.resolve(args.cwd);
|
|
40012
40188
|
assertGuestPath2(ctx, cwdAbs, personaRoot, "skills:list");
|
|
40013
40189
|
const list2 = skills.list(args);
|
|
40014
40190
|
if (ctx?.principal.kind === "guest" && personaRoot) {
|
|
@@ -40020,7 +40196,7 @@ function buildWorkspaceHandlers(deps) {
|
|
|
40020
40196
|
};
|
|
40021
40197
|
const agentsList = async (frame, _client, ctx) => {
|
|
40022
40198
|
const args = AgentsListArgs.parse(frame);
|
|
40023
|
-
const cwdAbs =
|
|
40199
|
+
const cwdAbs = path43.resolve(args.cwd);
|
|
40024
40200
|
assertGuestPath2(ctx, cwdAbs, personaRoot, "agents:list");
|
|
40025
40201
|
const list2 = agents.list(args);
|
|
40026
40202
|
if (ctx?.principal.kind === "guest" && personaRoot) {
|
|
@@ -40039,20 +40215,20 @@ function buildWorkspaceHandlers(deps) {
|
|
|
40039
40215
|
}
|
|
40040
40216
|
|
|
40041
40217
|
// src/handlers/git.ts
|
|
40042
|
-
var
|
|
40218
|
+
var path45 = __toESM(require("path"), 1);
|
|
40043
40219
|
init_protocol();
|
|
40044
40220
|
init_protocol();
|
|
40045
40221
|
|
|
40046
40222
|
// src/workspace/git.ts
|
|
40047
40223
|
var import_node_child_process6 = require("child_process");
|
|
40048
|
-
var
|
|
40049
|
-
var
|
|
40224
|
+
var import_node_fs29 = __toESM(require("fs"), 1);
|
|
40225
|
+
var import_node_path33 = __toESM(require("path"), 1);
|
|
40050
40226
|
var import_node_util = require("util");
|
|
40051
40227
|
var pexec = (0, import_node_util.promisify)(import_node_child_process6.execFile);
|
|
40052
40228
|
function normalizePath(p2) {
|
|
40053
|
-
const resolved =
|
|
40229
|
+
const resolved = import_node_path33.default.resolve(p2);
|
|
40054
40230
|
try {
|
|
40055
|
-
return
|
|
40231
|
+
return import_node_fs29.default.realpathSync(resolved);
|
|
40056
40232
|
} catch {
|
|
40057
40233
|
return resolved;
|
|
40058
40234
|
}
|
|
@@ -40125,7 +40301,7 @@ async function listGitBranches(cwd) {
|
|
|
40125
40301
|
// src/handlers/git.ts
|
|
40126
40302
|
function assertGuestCwd(ctx, cwd, personaRoot, method) {
|
|
40127
40303
|
if (!ctx || ctx.principal.kind !== "guest" || !personaRoot) return;
|
|
40128
|
-
if (!isGuestPathAllowed(ctx.grants,
|
|
40304
|
+
if (!isGuestPathAllowed(ctx.grants, path45.resolve(cwd), personaRoot, "read")) {
|
|
40129
40305
|
throw new ClawdError(
|
|
40130
40306
|
ERROR_CODES.UNAUTHORIZED,
|
|
40131
40307
|
`guest ${ctx.principal.id} cannot ${method} cwd ${cwd}`
|
|
@@ -40487,6 +40663,36 @@ function buildWhoamiHandler(deps) {
|
|
|
40487
40663
|
};
|
|
40488
40664
|
}
|
|
40489
40665
|
|
|
40666
|
+
// src/handlers/feishu-auth.ts
|
|
40667
|
+
function buildFeishuAuthHandlers(deps) {
|
|
40668
|
+
const loginStart = async () => {
|
|
40669
|
+
const { authUrl, state } = deps.loginFlow.start();
|
|
40670
|
+
return {
|
|
40671
|
+
response: { type: "auth:login:start:ok", authUrl, state }
|
|
40672
|
+
};
|
|
40673
|
+
};
|
|
40674
|
+
const getIdentity = async () => {
|
|
40675
|
+
const record = deps.ownerIdentityStore.read();
|
|
40676
|
+
return {
|
|
40677
|
+
response: {
|
|
40678
|
+
type: "auth:getIdentity:ok",
|
|
40679
|
+
identity: record ? record.identity : null
|
|
40680
|
+
}
|
|
40681
|
+
};
|
|
40682
|
+
};
|
|
40683
|
+
const logout = async () => {
|
|
40684
|
+
deps.ownerIdentityStore.clear();
|
|
40685
|
+
return {
|
|
40686
|
+
response: { type: "auth:logout:ok" }
|
|
40687
|
+
};
|
|
40688
|
+
};
|
|
40689
|
+
return {
|
|
40690
|
+
"auth:login:start": loginStart,
|
|
40691
|
+
"auth:getIdentity": getIdentity,
|
|
40692
|
+
"auth:logout": logout
|
|
40693
|
+
};
|
|
40694
|
+
}
|
|
40695
|
+
|
|
40490
40696
|
// src/handlers/meta.ts
|
|
40491
40697
|
var import_node_os15 = __toESM(require("os"), 1);
|
|
40492
40698
|
init_protocol();
|
|
@@ -40593,8 +40799,14 @@ function buildPersonaHandlers(deps) {
|
|
|
40593
40799
|
const update = async (frame) => {
|
|
40594
40800
|
const { type: _type, requestId: _requestId, ...rest } = frame;
|
|
40595
40801
|
const args = PersonaUpdateArgsSchema.parse(rest);
|
|
40596
|
-
const { personality, ...metaPatch } = args.patch;
|
|
40597
|
-
|
|
40802
|
+
const { personality, networkAllowedDomains, ...metaPatch } = args.patch;
|
|
40803
|
+
let persona = personaManager.update(args.personaId, metaPatch, personality);
|
|
40804
|
+
if (networkAllowedDomains !== void 0) {
|
|
40805
|
+
persona = personaManager.updateNetworkAllowedDomains(
|
|
40806
|
+
args.personaId,
|
|
40807
|
+
networkAllowedDomains
|
|
40808
|
+
);
|
|
40809
|
+
}
|
|
40598
40810
|
return { response: { type: "persona:info", ...persona } };
|
|
40599
40811
|
};
|
|
40600
40812
|
const del = async (frame) => {
|
|
@@ -40614,7 +40826,7 @@ function buildPersonaHandlers(deps) {
|
|
|
40614
40826
|
}
|
|
40615
40827
|
|
|
40616
40828
|
// src/handlers/attachment.ts
|
|
40617
|
-
var
|
|
40829
|
+
var import_node_path34 = __toESM(require("path"), 1);
|
|
40618
40830
|
init_protocol();
|
|
40619
40831
|
init_protocol();
|
|
40620
40832
|
var DEFAULT_TTL_SECONDS = 24 * 3600;
|
|
@@ -40668,12 +40880,12 @@ function buildAttachmentHandlers(deps) {
|
|
|
40668
40880
|
`session ${args.sessionId} scope unresolved`
|
|
40669
40881
|
);
|
|
40670
40882
|
}
|
|
40671
|
-
const cwdAbs =
|
|
40672
|
-
const candidateAbs =
|
|
40883
|
+
const cwdAbs = import_node_path34.default.resolve(sessionFile.cwd);
|
|
40884
|
+
const candidateAbs = import_node_path34.default.isAbsolute(args.relPath) ? import_node_path34.default.resolve(args.relPath) : import_node_path34.default.resolve(cwdAbs, args.relPath);
|
|
40673
40885
|
assertGuestAttachmentPath(ctx, candidateAbs, deps.personaRoot, "attachment.signUrl");
|
|
40674
40886
|
const entries = deps.groupFileStore.list(scope, args.sessionId);
|
|
40675
40887
|
const entry = entries.find((e) => {
|
|
40676
|
-
const storedAbs =
|
|
40888
|
+
const storedAbs = import_node_path34.default.isAbsolute(e.relPath) ? import_node_path34.default.resolve(e.relPath) : import_node_path34.default.resolve(cwdAbs, e.relPath);
|
|
40677
40889
|
return storedAbs === candidateAbs && !e.stale;
|
|
40678
40890
|
});
|
|
40679
40891
|
if (!entry) {
|
|
@@ -40697,7 +40909,7 @@ function buildAttachmentHandlers(deps) {
|
|
|
40697
40909
|
if (!ctx || ctx.principal.kind !== "guest" || !deps.personaRoot || !deps.sessionStore) return;
|
|
40698
40910
|
const f = deps.sessionStore.read(sessionId);
|
|
40699
40911
|
if (!f) return;
|
|
40700
|
-
assertGuestAttachmentPath(ctx,
|
|
40912
|
+
assertGuestAttachmentPath(ctx, import_node_path34.default.resolve(f.cwd), deps.personaRoot, method);
|
|
40701
40913
|
}
|
|
40702
40914
|
const groupAdd = async (frame, _client, ctx) => {
|
|
40703
40915
|
if (!deps.groupFileStore || !deps.getSessionScope) {
|
|
@@ -40769,19 +40981,19 @@ function buildAttachmentHandlers(deps) {
|
|
|
40769
40981
|
|
|
40770
40982
|
// src/handlers/extension.ts
|
|
40771
40983
|
var import_promises7 = __toESM(require("fs/promises"), 1);
|
|
40772
|
-
var
|
|
40984
|
+
var import_node_path39 = __toESM(require("path"), 1);
|
|
40773
40985
|
init_protocol();
|
|
40774
40986
|
|
|
40775
40987
|
// src/extension/bundle-zip.ts
|
|
40776
40988
|
var import_promises4 = __toESM(require("fs/promises"), 1);
|
|
40777
|
-
var
|
|
40778
|
-
var
|
|
40989
|
+
var import_node_path35 = __toESM(require("path"), 1);
|
|
40990
|
+
var import_node_crypto12 = __toESM(require("crypto"), 1);
|
|
40779
40991
|
var import_jszip2 = __toESM(require_lib3(), 1);
|
|
40780
40992
|
async function bundleExtensionDir(dir) {
|
|
40781
40993
|
const entries = await listFilesSorted(dir);
|
|
40782
40994
|
const zip = new import_jszip2.default();
|
|
40783
40995
|
for (const rel of entries) {
|
|
40784
|
-
const abs =
|
|
40996
|
+
const abs = import_node_path35.default.join(dir, rel);
|
|
40785
40997
|
const content = await import_promises4.default.readFile(abs);
|
|
40786
40998
|
zip.file(rel, content, { date: FIXED_DATE });
|
|
40787
40999
|
}
|
|
@@ -40790,7 +41002,7 @@ async function bundleExtensionDir(dir) {
|
|
|
40790
41002
|
compression: "DEFLATE",
|
|
40791
41003
|
compressionOptions: { level: 6 }
|
|
40792
41004
|
});
|
|
40793
|
-
const sha256 =
|
|
41005
|
+
const sha256 = import_node_crypto12.default.createHash("sha256").update(buffer).digest("hex");
|
|
40794
41006
|
return { buffer, sha256 };
|
|
40795
41007
|
}
|
|
40796
41008
|
var FIXED_DATE = /* @__PURE__ */ new Date("2020-01-01T00:00:00.000Z");
|
|
@@ -40802,7 +41014,7 @@ async function listFilesSorted(rootDir) {
|
|
|
40802
41014
|
return out;
|
|
40803
41015
|
}
|
|
40804
41016
|
async function walk(absRoot, relPrefix, out) {
|
|
40805
|
-
const dirAbs =
|
|
41017
|
+
const dirAbs = import_node_path35.default.join(absRoot, relPrefix);
|
|
40806
41018
|
const entries = await import_promises4.default.readdir(dirAbs, { withFileTypes: true });
|
|
40807
41019
|
for (const e of entries) {
|
|
40808
41020
|
if (IGNORE_BASENAMES.has(e.name)) continue;
|
|
@@ -40856,25 +41068,25 @@ function computePublishCheck(args) {
|
|
|
40856
41068
|
|
|
40857
41069
|
// src/extension/install-flow.ts
|
|
40858
41070
|
var import_promises5 = __toESM(require("fs/promises"), 1);
|
|
40859
|
-
var
|
|
41071
|
+
var import_node_path37 = __toESM(require("path"), 1);
|
|
40860
41072
|
var import_node_os17 = __toESM(require("os"), 1);
|
|
40861
|
-
var
|
|
41073
|
+
var import_node_crypto13 = __toESM(require("crypto"), 1);
|
|
40862
41074
|
var import_jszip3 = __toESM(require_lib3(), 1);
|
|
40863
41075
|
|
|
40864
41076
|
// src/extension/paths.ts
|
|
40865
41077
|
var import_node_os16 = __toESM(require("os"), 1);
|
|
40866
|
-
var
|
|
41078
|
+
var import_node_path36 = __toESM(require("path"), 1);
|
|
40867
41079
|
function clawdHomeRoot(override) {
|
|
40868
|
-
return override ?? process.env.CLAWD_HOME ??
|
|
41080
|
+
return override ?? process.env.CLAWD_HOME ?? import_node_path36.default.join(import_node_os16.default.homedir(), ".clawd");
|
|
40869
41081
|
}
|
|
40870
41082
|
function extensionsRoot(override) {
|
|
40871
|
-
return
|
|
41083
|
+
return import_node_path36.default.join(clawdHomeRoot(override), "extensions");
|
|
40872
41084
|
}
|
|
40873
41085
|
function publishedChannelsFile(override) {
|
|
40874
|
-
return
|
|
41086
|
+
return import_node_path36.default.join(clawdHomeRoot(override), "extensions-published.json");
|
|
40875
41087
|
}
|
|
40876
41088
|
function bundleCacheRoot(override) {
|
|
40877
|
-
return
|
|
41089
|
+
return import_node_path36.default.join(clawdHomeRoot(override), "extension-bundles");
|
|
40878
41090
|
}
|
|
40879
41091
|
|
|
40880
41092
|
// src/extension/install-flow.ts
|
|
@@ -40887,7 +41099,7 @@ var InstallError = class extends Error {
|
|
|
40887
41099
|
};
|
|
40888
41100
|
async function installFromChannel(args, deps) {
|
|
40889
41101
|
const { channelRef, snapshotHash, bundleZip } = args;
|
|
40890
|
-
const computed =
|
|
41102
|
+
const computed = import_node_crypto13.default.createHash("sha256").update(bundleZip).digest("hex");
|
|
40891
41103
|
if (computed !== snapshotHash) {
|
|
40892
41104
|
throw new InstallError(
|
|
40893
41105
|
"HASH_MISMATCH",
|
|
@@ -40901,7 +41113,7 @@ async function installFromChannel(args, deps) {
|
|
|
40901
41113
|
throw new InstallError("ZIP_INVALID", `failed to load zip: ${e.message}`);
|
|
40902
41114
|
}
|
|
40903
41115
|
for (const name of Object.keys(zip.files)) {
|
|
40904
|
-
if (name.includes("..") || name.startsWith("/") ||
|
|
41116
|
+
if (name.includes("..") || name.startsWith("/") || import_node_path37.default.isAbsolute(name)) {
|
|
40905
41117
|
throw new InstallError("ZIP_INVALID", `unsafe zip entry: ${name}`);
|
|
40906
41118
|
}
|
|
40907
41119
|
}
|
|
@@ -40933,7 +41145,7 @@ async function installFromChannel(args, deps) {
|
|
|
40933
41145
|
);
|
|
40934
41146
|
}
|
|
40935
41147
|
const localExtId = namespacedExtId(ownerSlug, channelRef.ownerPrincipalId);
|
|
40936
|
-
const destDir =
|
|
41148
|
+
const destDir = import_node_path37.default.join(deps.extensionsRoot, localExtId);
|
|
40937
41149
|
let destExists = false;
|
|
40938
41150
|
try {
|
|
40939
41151
|
await import_promises5.default.access(destDir);
|
|
@@ -40947,16 +41159,16 @@ async function installFromChannel(args, deps) {
|
|
|
40947
41159
|
);
|
|
40948
41160
|
}
|
|
40949
41161
|
const stage = await import_promises5.default.mkdtemp(
|
|
40950
|
-
|
|
41162
|
+
import_node_path37.default.join(import_node_os17.default.tmpdir(), `clawd-ext-install-${localExtId}-`)
|
|
40951
41163
|
);
|
|
40952
41164
|
try {
|
|
40953
41165
|
for (const [name, entry] of Object.entries(zip.files)) {
|
|
40954
|
-
const dest =
|
|
41166
|
+
const dest = import_node_path37.default.join(stage, name);
|
|
40955
41167
|
if (entry.dir) {
|
|
40956
41168
|
await import_promises5.default.mkdir(dest, { recursive: true });
|
|
40957
41169
|
continue;
|
|
40958
41170
|
}
|
|
40959
|
-
await import_promises5.default.mkdir(
|
|
41171
|
+
await import_promises5.default.mkdir(import_node_path37.default.dirname(dest), { recursive: true });
|
|
40960
41172
|
if (name === "manifest.json") {
|
|
40961
41173
|
const rewritten = { ...parsed.data, id: localExtId };
|
|
40962
41174
|
await import_promises5.default.writeFile(dest, JSON.stringify(rewritten, null, 2));
|
|
@@ -40977,9 +41189,9 @@ async function installFromChannel(args, deps) {
|
|
|
40977
41189
|
|
|
40978
41190
|
// src/extension/update-flow.ts
|
|
40979
41191
|
var import_promises6 = __toESM(require("fs/promises"), 1);
|
|
40980
|
-
var
|
|
41192
|
+
var import_node_path38 = __toESM(require("path"), 1);
|
|
40981
41193
|
var import_node_os18 = __toESM(require("os"), 1);
|
|
40982
|
-
var
|
|
41194
|
+
var import_node_crypto14 = __toESM(require("crypto"), 1);
|
|
40983
41195
|
var import_jszip4 = __toESM(require_lib3(), 1);
|
|
40984
41196
|
var UpdateError = class extends Error {
|
|
40985
41197
|
constructor(code, message) {
|
|
@@ -40994,11 +41206,11 @@ async function updateFromChannel(args, deps) {
|
|
|
40994
41206
|
channelRef.extId,
|
|
40995
41207
|
channelRef.ownerPrincipalId
|
|
40996
41208
|
);
|
|
40997
|
-
const liveDir =
|
|
41209
|
+
const liveDir = import_node_path38.default.join(deps.extensionsRoot, localExtId);
|
|
40998
41210
|
const prevDir = `${liveDir}.prev`;
|
|
40999
41211
|
let existingVersion;
|
|
41000
41212
|
try {
|
|
41001
|
-
const raw = await import_promises6.default.readFile(
|
|
41213
|
+
const raw = await import_promises6.default.readFile(import_node_path38.default.join(liveDir, "manifest.json"), "utf8");
|
|
41002
41214
|
const parsed2 = ExtensionManifestSchema.safeParse(JSON.parse(raw));
|
|
41003
41215
|
if (!parsed2.success) {
|
|
41004
41216
|
throw new UpdateError(
|
|
@@ -41017,7 +41229,7 @@ async function updateFromChannel(args, deps) {
|
|
|
41017
41229
|
if (e instanceof UpdateError) throw e;
|
|
41018
41230
|
throw e;
|
|
41019
41231
|
}
|
|
41020
|
-
const computed =
|
|
41232
|
+
const computed = import_node_crypto14.default.createHash("sha256").update(bundleZip).digest("hex");
|
|
41021
41233
|
if (computed !== snapshotHash) {
|
|
41022
41234
|
throw new UpdateError(
|
|
41023
41235
|
"HASH_MISMATCH",
|
|
@@ -41031,7 +41243,7 @@ async function updateFromChannel(args, deps) {
|
|
|
41031
41243
|
throw new UpdateError("ZIP_INVALID", `failed to load zip: ${e.message}`);
|
|
41032
41244
|
}
|
|
41033
41245
|
for (const name of Object.keys(zip.files)) {
|
|
41034
|
-
if (name.includes("..") || name.startsWith("/") ||
|
|
41246
|
+
if (name.includes("..") || name.startsWith("/") || import_node_path38.default.isAbsolute(name)) {
|
|
41035
41247
|
throw new UpdateError("ZIP_INVALID", `unsafe zip entry: ${name}`);
|
|
41036
41248
|
}
|
|
41037
41249
|
}
|
|
@@ -41066,16 +41278,16 @@ async function updateFromChannel(args, deps) {
|
|
|
41066
41278
|
await import_promises6.default.rm(prevDir, { recursive: true, force: true });
|
|
41067
41279
|
await import_promises6.default.rename(liveDir, prevDir);
|
|
41068
41280
|
const stage = await import_promises6.default.mkdtemp(
|
|
41069
|
-
|
|
41281
|
+
import_node_path38.default.join(import_node_os18.default.tmpdir(), `clawd-ext-update-${localExtId}-`)
|
|
41070
41282
|
);
|
|
41071
41283
|
try {
|
|
41072
41284
|
for (const [name, entry] of Object.entries(zip.files)) {
|
|
41073
|
-
const dest =
|
|
41285
|
+
const dest = import_node_path38.default.join(stage, name);
|
|
41074
41286
|
if (entry.dir) {
|
|
41075
41287
|
await import_promises6.default.mkdir(dest, { recursive: true });
|
|
41076
41288
|
continue;
|
|
41077
41289
|
}
|
|
41078
|
-
await import_promises6.default.mkdir(
|
|
41290
|
+
await import_promises6.default.mkdir(import_node_path38.default.dirname(dest), { recursive: true });
|
|
41079
41291
|
if (name === "manifest.json") {
|
|
41080
41292
|
const rewritten = { ...parsed.data, id: localExtId };
|
|
41081
41293
|
await import_promises6.default.writeFile(dest, JSON.stringify(rewritten, null, 2));
|
|
@@ -41168,7 +41380,7 @@ async function rewriteManifestVersion(root, extId, newVersion, previousPublished
|
|
|
41168
41380
|
);
|
|
41169
41381
|
}
|
|
41170
41382
|
}
|
|
41171
|
-
const manifestPath =
|
|
41383
|
+
const manifestPath = import_node_path39.default.join(root, extId, "manifest.json");
|
|
41172
41384
|
const manifest = await readManifest(root, extId);
|
|
41173
41385
|
const next = { ...manifest, version: newVersion };
|
|
41174
41386
|
const tmp = `${manifestPath}.tmp`;
|
|
@@ -41176,7 +41388,7 @@ async function rewriteManifestVersion(root, extId, newVersion, previousPublished
|
|
|
41176
41388
|
await import_promises7.default.rename(tmp, manifestPath);
|
|
41177
41389
|
}
|
|
41178
41390
|
async function readManifest(root, extId) {
|
|
41179
|
-
const file =
|
|
41391
|
+
const file = import_node_path39.default.join(root, extId, "manifest.json");
|
|
41180
41392
|
let raw;
|
|
41181
41393
|
try {
|
|
41182
41394
|
raw = await import_promises7.default.readFile(file, "utf8");
|
|
@@ -41267,7 +41479,7 @@ function buildExtensionHandlers(deps) {
|
|
|
41267
41479
|
};
|
|
41268
41480
|
async function buildSnapshotMeta(extId) {
|
|
41269
41481
|
const manifest = await readManifest(deps.root, extId);
|
|
41270
|
-
const { sha256, buffer } = await bundleExtensionDir(
|
|
41482
|
+
const { sha256, buffer } = await bundleExtensionDir(import_node_path39.default.join(deps.root, extId));
|
|
41271
41483
|
return { manifest, contentHash: sha256, buffer };
|
|
41272
41484
|
}
|
|
41273
41485
|
const publish = async (frame, _client, ctx) => {
|
|
@@ -41447,64 +41659,9 @@ function buildExtensionHandlers(deps) {
|
|
|
41447
41659
|
};
|
|
41448
41660
|
}
|
|
41449
41661
|
|
|
41450
|
-
// src/handlers/app-builder.ts
|
|
41451
|
-
function buildAppBuilderHandlers(deps) {
|
|
41452
|
-
const { store, deleteSessionsByProject, devServerLifecycle } = deps;
|
|
41453
|
-
const listProjects = async () => {
|
|
41454
|
-
const projects = await store.list();
|
|
41455
|
-
return {
|
|
41456
|
-
response: {
|
|
41457
|
-
projects: projects.map((p2) => ({
|
|
41458
|
-
...p2,
|
|
41459
|
-
isRunning: devServerLifecycle.isRunning(p2.name),
|
|
41460
|
-
boundSessionId: devServerLifecycle.boundSessionId(p2.name)
|
|
41461
|
-
}))
|
|
41462
|
-
}
|
|
41463
|
-
};
|
|
41464
|
-
};
|
|
41465
|
-
const createProject = async (frame) => {
|
|
41466
|
-
const name = frame.name;
|
|
41467
|
-
if (typeof name !== "string") throw new Error("createProject: name must be string");
|
|
41468
|
-
const project = await store.create(name);
|
|
41469
|
-
return { response: { project } };
|
|
41470
|
-
};
|
|
41471
|
-
const deleteProject = async (frame) => {
|
|
41472
|
-
const name = frame.name;
|
|
41473
|
-
if (typeof name !== "string") throw new Error("deleteProject: name must be string");
|
|
41474
|
-
await devServerLifecycle.stopForProject(name);
|
|
41475
|
-
await deleteSessionsByProject(name);
|
|
41476
|
-
await store.delete(name);
|
|
41477
|
-
return { response: {} };
|
|
41478
|
-
};
|
|
41479
|
-
const updateProjectPort = async (frame) => {
|
|
41480
|
-
const f = frame;
|
|
41481
|
-
if (typeof f.name !== "string") throw new Error("updateProjectPort: name must be string");
|
|
41482
|
-
if (typeof f.newPort !== "number") throw new Error("updateProjectPort: newPort must be number");
|
|
41483
|
-
const entry = devServerLifecycle.boundEntry(f.name);
|
|
41484
|
-
await devServerLifecycle.stopForProject(f.name);
|
|
41485
|
-
const project = await store.updatePort(f.name, f.newPort);
|
|
41486
|
-
if (entry) {
|
|
41487
|
-
await devServerLifecycle.restartForProjectAt({
|
|
41488
|
-
projectName: f.name,
|
|
41489
|
-
newPort: f.newPort,
|
|
41490
|
-
sessionId: entry.sessionId,
|
|
41491
|
-
cwd: entry.cwd,
|
|
41492
|
-
tunnelHost: entry.tunnelHost
|
|
41493
|
-
});
|
|
41494
|
-
}
|
|
41495
|
-
return { response: { project } };
|
|
41496
|
-
};
|
|
41497
|
-
return {
|
|
41498
|
-
"appBuilder:listProjects": listProjects,
|
|
41499
|
-
"appBuilder:createProject": createProject,
|
|
41500
|
-
"appBuilder:deleteProject": deleteProject,
|
|
41501
|
-
"appBuilder:updateProjectPort": updateProjectPort
|
|
41502
|
-
};
|
|
41503
|
-
}
|
|
41504
|
-
|
|
41505
41662
|
// src/extension/registry.ts
|
|
41506
41663
|
var import_promises8 = __toESM(require("fs/promises"), 1);
|
|
41507
|
-
var
|
|
41664
|
+
var import_node_path40 = __toESM(require("path"), 1);
|
|
41508
41665
|
async function loadAll(root) {
|
|
41509
41666
|
let entries;
|
|
41510
41667
|
try {
|
|
@@ -41517,13 +41674,13 @@ async function loadAll(root) {
|
|
|
41517
41674
|
for (const ent of entries) {
|
|
41518
41675
|
if (!ent.isDirectory()) continue;
|
|
41519
41676
|
if (ent.name.startsWith(".")) continue;
|
|
41520
|
-
records.push(await loadOne(
|
|
41677
|
+
records.push(await loadOne(import_node_path40.default.join(root, ent.name), ent.name));
|
|
41521
41678
|
}
|
|
41522
41679
|
records.sort((a, b2) => a.extId < b2.extId ? -1 : a.extId > b2.extId ? 1 : 0);
|
|
41523
41680
|
return records;
|
|
41524
41681
|
}
|
|
41525
41682
|
async function loadOne(dir, dirName) {
|
|
41526
|
-
const manifestPath =
|
|
41683
|
+
const manifestPath = import_node_path40.default.join(dir, "manifest.json");
|
|
41527
41684
|
let raw;
|
|
41528
41685
|
try {
|
|
41529
41686
|
raw = await import_promises8.default.readFile(manifestPath, "utf8");
|
|
@@ -41568,7 +41725,7 @@ async function loadOne(dir, dirName) {
|
|
|
41568
41725
|
|
|
41569
41726
|
// src/extension/uninstall.ts
|
|
41570
41727
|
var import_promises9 = __toESM(require("fs/promises"), 1);
|
|
41571
|
-
var
|
|
41728
|
+
var import_node_path41 = __toESM(require("path"), 1);
|
|
41572
41729
|
var UninstallError = class extends Error {
|
|
41573
41730
|
constructor(code, message) {
|
|
41574
41731
|
super(message);
|
|
@@ -41577,7 +41734,7 @@ var UninstallError = class extends Error {
|
|
|
41577
41734
|
code;
|
|
41578
41735
|
};
|
|
41579
41736
|
async function uninstall(deps) {
|
|
41580
|
-
const dir =
|
|
41737
|
+
const dir = import_node_path41.default.join(deps.root, deps.extId);
|
|
41581
41738
|
try {
|
|
41582
41739
|
await import_promises9.default.access(dir);
|
|
41583
41740
|
} catch {
|
|
@@ -41590,13 +41747,7 @@ async function uninstall(deps) {
|
|
|
41590
41747
|
// src/handlers/index.ts
|
|
41591
41748
|
function buildMethodHandlers(deps) {
|
|
41592
41749
|
return {
|
|
41593
|
-
...buildSessionHandlers(
|
|
41594
|
-
...deps,
|
|
41595
|
-
devServerLifecycle: deps.devServerLifecycle,
|
|
41596
|
-
appBuilderStore: deps.appBuilderStore,
|
|
41597
|
-
getTunnelUrl: deps.getTunnelUrl,
|
|
41598
|
-
logger: deps.logger
|
|
41599
|
-
}),
|
|
41750
|
+
...buildSessionHandlers(deps),
|
|
41600
41751
|
...buildPermissionHandlers(deps),
|
|
41601
41752
|
...buildHistoryHandlers(deps),
|
|
41602
41753
|
...buildWorkspaceHandlers(deps),
|
|
@@ -41629,6 +41780,7 @@ function buildMethodHandlers(deps) {
|
|
|
41629
41780
|
personaStore: deps.personaStore,
|
|
41630
41781
|
capabilityManager: deps.capabilityManager
|
|
41631
41782
|
}),
|
|
41783
|
+
...buildFeishuAuthHandlers(deps.feishuAuth),
|
|
41632
41784
|
...deps.attachment ? buildAttachmentHandlers(deps.attachment) : {},
|
|
41633
41785
|
...buildExtensionHandlers({
|
|
41634
41786
|
loadAll,
|
|
@@ -41637,237 +41789,10 @@ function buildMethodHandlers(deps) {
|
|
|
41637
41789
|
root: extensionsRoot(),
|
|
41638
41790
|
publishedChannelStore: deps.publishedChannelStore,
|
|
41639
41791
|
bundleCache: deps.bundleCache
|
|
41640
|
-
}),
|
|
41641
|
-
...buildAppBuilderHandlers({
|
|
41642
|
-
store: deps.appBuilderStore,
|
|
41643
|
-
deleteSessionsByProject: deps.deleteSessionsByProject,
|
|
41644
|
-
devServerLifecycle: deps.devServerLifecycle
|
|
41645
41792
|
})
|
|
41646
41793
|
};
|
|
41647
41794
|
}
|
|
41648
41795
|
|
|
41649
|
-
// src/app-builder/project-store.ts
|
|
41650
|
-
var import_node_fs29 = require("fs");
|
|
41651
|
-
var import_node_path41 = require("path");
|
|
41652
|
-
init_protocol();
|
|
41653
|
-
var PROJECTS_DIR = "projects";
|
|
41654
|
-
var META_FILE = ".clawd-project.json";
|
|
41655
|
-
var ProjectStore = class {
|
|
41656
|
-
/** @param personaRoot persona 目录,例如 `~/.clawd/personas/persona-app-builder/` */
|
|
41657
|
-
constructor(personaRoot) {
|
|
41658
|
-
this.personaRoot = personaRoot;
|
|
41659
|
-
}
|
|
41660
|
-
personaRoot;
|
|
41661
|
-
/** projects/<name>/.clawd-project.json 路径 */
|
|
41662
|
-
metaPath(name) {
|
|
41663
|
-
return (0, import_node_path41.join)(this.projectsRoot(), name, META_FILE);
|
|
41664
|
-
}
|
|
41665
|
-
/** projects/<name>/ 目录路径(cwd 用) */
|
|
41666
|
-
projectDir(name) {
|
|
41667
|
-
return (0, import_node_path41.join)(this.projectsRoot(), name);
|
|
41668
|
-
}
|
|
41669
|
-
projectsRoot() {
|
|
41670
|
-
return (0, import_node_path41.join)(this.personaRoot, PROJECTS_DIR);
|
|
41671
|
-
}
|
|
41672
|
-
async list() {
|
|
41673
|
-
let entries;
|
|
41674
|
-
try {
|
|
41675
|
-
entries = await import_node_fs29.promises.readdir(this.projectsRoot());
|
|
41676
|
-
} catch (err) {
|
|
41677
|
-
if (err.code === "ENOENT") return [];
|
|
41678
|
-
throw err;
|
|
41679
|
-
}
|
|
41680
|
-
const out = [];
|
|
41681
|
-
for (const name of entries) {
|
|
41682
|
-
try {
|
|
41683
|
-
const raw = await import_node_fs29.promises.readFile(this.metaPath(name), "utf8");
|
|
41684
|
-
const parsed = ProjectMetadataSchema.safeParse(JSON.parse(raw));
|
|
41685
|
-
if (parsed.success) out.push(parsed.data);
|
|
41686
|
-
} catch {
|
|
41687
|
-
}
|
|
41688
|
-
}
|
|
41689
|
-
return out.sort((a, b2) => a.name.localeCompare(b2.name));
|
|
41690
|
-
}
|
|
41691
|
-
async create(name) {
|
|
41692
|
-
const existing = await this.list();
|
|
41693
|
-
if (existing.some((p2) => p2.name === name)) {
|
|
41694
|
-
throw new Error(`project "${name}" already exists / \u5DF2\u5B58\u5728`);
|
|
41695
|
-
}
|
|
41696
|
-
const usedPorts = new Set(existing.map((p2) => p2.port));
|
|
41697
|
-
let port = -1;
|
|
41698
|
-
for (let p2 = PROJECT_PORT_MIN; p2 <= PROJECT_PORT_MAX; p2++) {
|
|
41699
|
-
if (!usedPorts.has(p2)) {
|
|
41700
|
-
port = p2;
|
|
41701
|
-
break;
|
|
41702
|
-
}
|
|
41703
|
-
}
|
|
41704
|
-
if (port < 0) {
|
|
41705
|
-
throw new Error(
|
|
41706
|
-
`port pool exhausted / \u7AEF\u53E3\u6C60\u5DF2\u6EE1\uFF08${PROJECT_PORT_MIN}-${PROJECT_PORT_MAX}\uFF09\uFF0C\u5148\u5220\u4E00\u4E2A project \u91CA\u653E\u7AEF\u53E3`
|
|
41707
|
-
);
|
|
41708
|
-
}
|
|
41709
|
-
const meta = {
|
|
41710
|
-
name,
|
|
41711
|
-
port,
|
|
41712
|
-
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
41713
|
-
};
|
|
41714
|
-
const validated = ProjectMetadataSchema.safeParse(meta);
|
|
41715
|
-
if (!validated.success) {
|
|
41716
|
-
throw new Error(`invalid name "${name}": ${validated.error.message}`);
|
|
41717
|
-
}
|
|
41718
|
-
const dir = this.projectDir(name);
|
|
41719
|
-
await import_node_fs29.promises.mkdir(dir, { recursive: true });
|
|
41720
|
-
await import_node_fs29.promises.writeFile(this.metaPath(name), JSON.stringify(meta, null, 2) + "\n", "utf8");
|
|
41721
|
-
return meta;
|
|
41722
|
-
}
|
|
41723
|
-
async delete(name) {
|
|
41724
|
-
const dir = this.projectDir(name);
|
|
41725
|
-
await import_node_fs29.promises.rm(dir, { recursive: true, force: true });
|
|
41726
|
-
}
|
|
41727
|
-
async updatePort(name, newPort) {
|
|
41728
|
-
if (newPort < PROJECT_PORT_MIN || newPort > PROJECT_PORT_MAX) {
|
|
41729
|
-
throw new Error(
|
|
41730
|
-
`port range invalid: must be ${PROJECT_PORT_MIN}-${PROJECT_PORT_MAX}, got ${newPort}`
|
|
41731
|
-
);
|
|
41732
|
-
}
|
|
41733
|
-
const list = await this.list();
|
|
41734
|
-
const target = list.find((p2) => p2.name === name);
|
|
41735
|
-
if (!target) throw new Error(`project "${name}" not found / \u4E0D\u5B58\u5728`);
|
|
41736
|
-
const conflict = list.find((p2) => p2.name !== name && p2.port === newPort);
|
|
41737
|
-
if (conflict) {
|
|
41738
|
-
throw new Error(`port ${newPort} already used / \u5DF2\u88AB project "${conflict.name}" \u5360\u7528`);
|
|
41739
|
-
}
|
|
41740
|
-
const updated = { ...target, port: newPort };
|
|
41741
|
-
await import_node_fs29.promises.writeFile(this.metaPath(name), JSON.stringify(updated, null, 2) + "\n", "utf8");
|
|
41742
|
-
return updated;
|
|
41743
|
-
}
|
|
41744
|
-
};
|
|
41745
|
-
|
|
41746
|
-
// src/app-builder/dev-server-supervisor.ts
|
|
41747
|
-
var import_node_child_process7 = require("child_process");
|
|
41748
|
-
var import_node_events2 = require("events");
|
|
41749
|
-
var DevServerSupervisor = class extends import_node_events2.EventEmitter {
|
|
41750
|
-
running = /* @__PURE__ */ new Map();
|
|
41751
|
-
// key: sessionId
|
|
41752
|
-
logger = { warn: () => {
|
|
41753
|
-
}, info: () => {
|
|
41754
|
-
} };
|
|
41755
|
-
/** daemon entry 启动后注入 logger,让 child 输出走 clawd.log */
|
|
41756
|
-
setLogger(logger) {
|
|
41757
|
-
this.logger = logger;
|
|
41758
|
-
}
|
|
41759
|
-
startForSession(args) {
|
|
41760
|
-
if (this.running.has(args.sessionId)) return;
|
|
41761
|
-
const augmentedPath = [
|
|
41762
|
-
process.env.PATH ?? "",
|
|
41763
|
-
"/opt/homebrew/bin",
|
|
41764
|
-
"/usr/local/bin",
|
|
41765
|
-
`${process.env.HOME}/.nvm/versions/node`,
|
|
41766
|
-
`${process.env.HOME}/.local/share/pnpm`
|
|
41767
|
-
].filter(Boolean).join(":");
|
|
41768
|
-
const env = {
|
|
41769
|
-
...process.env,
|
|
41770
|
-
PATH: augmentedPath,
|
|
41771
|
-
CLAWD_TUNNEL_HOST: args.tunnelHost ?? "",
|
|
41772
|
-
CLAWD_PREVIEW_PORT: String(args.port)
|
|
41773
|
-
};
|
|
41774
|
-
this.logger.info("dev-server.start", {
|
|
41775
|
-
projectName: args.projectName,
|
|
41776
|
-
port: args.port,
|
|
41777
|
-
cwd: args.cwd,
|
|
41778
|
-
sessionId: args.sessionId,
|
|
41779
|
-
tunnelHost: args.tunnelHost
|
|
41780
|
-
});
|
|
41781
|
-
const child = (0, import_node_child_process7.spawn)("pnpm", ["dev"], { cwd: args.cwd, env, stdio: "pipe", shell: true });
|
|
41782
|
-
const entry = { ...args, process: child };
|
|
41783
|
-
this.running.set(args.sessionId, entry);
|
|
41784
|
-
child.stdout?.on("data", (chunk) => {
|
|
41785
|
-
this.logger.info("dev-server.stdout", {
|
|
41786
|
-
projectName: args.projectName,
|
|
41787
|
-
port: args.port,
|
|
41788
|
-
chunk: chunk.toString().trimEnd()
|
|
41789
|
-
});
|
|
41790
|
-
});
|
|
41791
|
-
child.stderr?.on("data", (chunk) => {
|
|
41792
|
-
this.logger.warn("dev-server.stderr", {
|
|
41793
|
-
projectName: args.projectName,
|
|
41794
|
-
port: args.port,
|
|
41795
|
-
chunk: chunk.toString().trimEnd()
|
|
41796
|
-
});
|
|
41797
|
-
});
|
|
41798
|
-
child.on("error", (err) => {
|
|
41799
|
-
this.running.delete(args.sessionId);
|
|
41800
|
-
this.logger.warn("dev-server.spawn-failed", {
|
|
41801
|
-
projectName: args.projectName,
|
|
41802
|
-
port: args.port,
|
|
41803
|
-
error: err.message,
|
|
41804
|
-
code: err.code
|
|
41805
|
-
});
|
|
41806
|
-
this.emit("devServer:failed", {
|
|
41807
|
-
sessionId: args.sessionId,
|
|
41808
|
-
projectName: args.projectName,
|
|
41809
|
-
port: args.port,
|
|
41810
|
-
exitCode: null
|
|
41811
|
-
});
|
|
41812
|
-
});
|
|
41813
|
-
child.on("exit", (code) => {
|
|
41814
|
-
this.running.delete(args.sessionId);
|
|
41815
|
-
this.logger.info("dev-server.exit", { projectName: args.projectName, port: args.port, code });
|
|
41816
|
-
if (code !== 0 && code !== null) {
|
|
41817
|
-
this.emit("devServer:failed", {
|
|
41818
|
-
sessionId: args.sessionId,
|
|
41819
|
-
projectName: args.projectName,
|
|
41820
|
-
port: args.port,
|
|
41821
|
-
exitCode: code
|
|
41822
|
-
});
|
|
41823
|
-
}
|
|
41824
|
-
});
|
|
41825
|
-
}
|
|
41826
|
-
stopForSession(sessionId) {
|
|
41827
|
-
const entry = this.running.get(sessionId);
|
|
41828
|
-
if (!entry) return Promise.resolve();
|
|
41829
|
-
return new Promise((resolve6) => {
|
|
41830
|
-
entry.process.once("exit", () => {
|
|
41831
|
-
this.running.delete(sessionId);
|
|
41832
|
-
resolve6();
|
|
41833
|
-
});
|
|
41834
|
-
entry.process.kill("SIGTERM");
|
|
41835
|
-
});
|
|
41836
|
-
}
|
|
41837
|
-
async stopForProject(projectName) {
|
|
41838
|
-
const entry = [...this.running.values()].find((e) => e.projectName === projectName);
|
|
41839
|
-
if (!entry) return;
|
|
41840
|
-
await this.stopForSession(entry.sessionId);
|
|
41841
|
-
}
|
|
41842
|
-
async restartForProjectAt(args) {
|
|
41843
|
-
await this.stopForSession(args.sessionId);
|
|
41844
|
-
this.startForSession({
|
|
41845
|
-
sessionId: args.sessionId,
|
|
41846
|
-
projectName: args.projectName,
|
|
41847
|
-
port: args.newPort,
|
|
41848
|
-
cwd: args.cwd,
|
|
41849
|
-
tunnelHost: args.tunnelHost
|
|
41850
|
-
});
|
|
41851
|
-
}
|
|
41852
|
-
isRunning(projectName) {
|
|
41853
|
-
return [...this.running.values()].some((e) => e.projectName === projectName);
|
|
41854
|
-
}
|
|
41855
|
-
boundSessionId(projectName) {
|
|
41856
|
-
const entry = [...this.running.values()].find((e) => e.projectName === projectName);
|
|
41857
|
-
return entry?.sessionId ?? null;
|
|
41858
|
-
}
|
|
41859
|
-
boundEntry(projectName) {
|
|
41860
|
-
const entry = [...this.running.values()].find((e) => e.projectName === projectName);
|
|
41861
|
-
if (!entry) return null;
|
|
41862
|
-
return {
|
|
41863
|
-
sessionId: entry.sessionId,
|
|
41864
|
-
cwd: entry.cwd,
|
|
41865
|
-
port: entry.port,
|
|
41866
|
-
tunnelHost: entry.tunnelHost
|
|
41867
|
-
};
|
|
41868
|
-
}
|
|
41869
|
-
};
|
|
41870
|
-
|
|
41871
41796
|
// src/handlers/method-grants.ts
|
|
41872
41797
|
var ADMIN_ANY = {
|
|
41873
41798
|
kind: "fixed",
|
|
@@ -41998,14 +41923,15 @@ var METHOD_GRANT_MAP = {
|
|
|
41998
41923
|
// guest-self:guest 在自己 daemon 上触发安装与更新(无持久化 subscription 概念)
|
|
41999
41924
|
"extension.install-from-channel": ADMIN_ANY,
|
|
42000
41925
|
"extension.update-from-channel": ADMIN_ANY,
|
|
42001
|
-
// ----
|
|
42002
|
-
//
|
|
42003
|
-
|
|
42004
|
-
|
|
42005
|
-
"
|
|
42006
|
-
|
|
42007
|
-
|
|
42008
|
-
"
|
|
41926
|
+
// ---- auth:* 飞书统一身份 Phase 1(owner-only) ----
|
|
41927
|
+
// 登录/登出/查身份都是 owner 本机操作,guest 不可调
|
|
41928
|
+
"auth:login:start": ADMIN_ANY,
|
|
41929
|
+
"auth:getIdentity": ADMIN_ANY,
|
|
41930
|
+
"auth:logout": ADMIN_ANY,
|
|
41931
|
+
// ---- hub:* 飞书统一身份 Phase 2(owner-only) ----
|
|
41932
|
+
// hub 目录 / 连接都是 owner 本机操作(用 owner 的 ttcJwt 找中心 server),guest 不可调
|
|
41933
|
+
"hub:list": ADMIN_ANY,
|
|
41934
|
+
"hub:connect": ADMIN_ANY
|
|
42009
41935
|
};
|
|
42010
41936
|
function computeGrantForFrame(method, frame) {
|
|
42011
41937
|
const rule = METHOD_GRANT_MAP[method];
|
|
@@ -42021,12 +41947,12 @@ function computeGrantForFrame(method, frame) {
|
|
|
42021
41947
|
}
|
|
42022
41948
|
|
|
42023
41949
|
// src/extension/runtime.ts
|
|
42024
|
-
var
|
|
41950
|
+
var import_node_child_process7 = require("child_process");
|
|
42025
41951
|
var import_node_path42 = __toESM(require("path"), 1);
|
|
42026
41952
|
var import_promises10 = require("timers/promises");
|
|
42027
41953
|
|
|
42028
41954
|
// src/extension/port-allocator.ts
|
|
42029
|
-
var
|
|
41955
|
+
var import_node_net = __toESM(require("net"), 1);
|
|
42030
41956
|
var PortExhaustedError = class extends Error {
|
|
42031
41957
|
constructor(min, max) {
|
|
42032
41958
|
super(`no free port in [${min},${max}]`);
|
|
@@ -42039,7 +41965,7 @@ var PortExhaustedError = class extends Error {
|
|
|
42039
41965
|
};
|
|
42040
41966
|
function probe(port) {
|
|
42041
41967
|
return new Promise((resolve6) => {
|
|
42042
|
-
const srv =
|
|
41968
|
+
const srv = import_node_net.default.createServer();
|
|
42043
41969
|
srv.once("error", () => resolve6(false));
|
|
42044
41970
|
srv.once("listening", () => {
|
|
42045
41971
|
srv.close(() => resolve6(true));
|
|
@@ -42129,7 +42055,7 @@ var Runtime = class {
|
|
|
42129
42055
|
CLAWOS_EXT_PORT: String(port),
|
|
42130
42056
|
CLAWOS_EXT_ID: extId
|
|
42131
42057
|
};
|
|
42132
|
-
const child = (0,
|
|
42058
|
+
const child = (0, import_node_child_process7.spawn)("sh", ["-c", cmd], {
|
|
42133
42059
|
cwd: dir,
|
|
42134
42060
|
env,
|
|
42135
42061
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -42395,8 +42321,11 @@ async function startDaemon(config) {
|
|
|
42395
42321
|
} else if (config.tunnel) {
|
|
42396
42322
|
resolvedAuthToken = authFile.token;
|
|
42397
42323
|
}
|
|
42398
|
-
const
|
|
42399
|
-
const
|
|
42324
|
+
const ownerIdentityStore = new OwnerIdentityStore(config.dataDir);
|
|
42325
|
+
const feishuIdentity = ownerIdentityStore.read();
|
|
42326
|
+
let feishuActive = feishuIdentity !== null;
|
|
42327
|
+
let ownerPrincipalId = feishuIdentity?.identity.unionId ?? authFile.ownerPrincipalId;
|
|
42328
|
+
let ownerDisplayName = feishuIdentity?.identity.displayName ?? loadOwnerDisplayName(config.dataDir);
|
|
42400
42329
|
const authMode = resolvedAuthToken == null ? "none" : "first-message";
|
|
42401
42330
|
const inboxStore = new InboxStore(config.dataDir);
|
|
42402
42331
|
const inboxManager = new InboxManager(inboxStore, (_peerOwnerId, frame) => {
|
|
@@ -42438,7 +42367,9 @@ async function startDaemon(config) {
|
|
|
42438
42367
|
isOwnerToken: (x) => resolvedAuthToken != null && constantTimeEqual(x, resolvedAuthToken),
|
|
42439
42368
|
ownerPrincipalId,
|
|
42440
42369
|
ownerDisplayName,
|
|
42441
|
-
|
|
42370
|
+
// 飞书 gate(spec 决策 #9):未激活飞书身份时不接受 guest(capability token)入站——
|
|
42371
|
+
// 不传 registry 让 guest token 一律 BAD_TOKEN。owner 自己连接不受影响。
|
|
42372
|
+
...feishuActive ? { capabilityRegistry } : {},
|
|
42442
42373
|
selfPrincipalId,
|
|
42443
42374
|
selfDisplayName
|
|
42444
42375
|
});
|
|
@@ -42595,19 +42526,13 @@ async function startDaemon(config) {
|
|
|
42595
42526
|
const publishedChannelStore = new PublishedChannelStore(publishedChannelsFile());
|
|
42596
42527
|
await publishedChannelStore.load();
|
|
42597
42528
|
const bundleCache = new BundleCache(bundleCacheRoot());
|
|
42598
|
-
const
|
|
42599
|
-
|
|
42600
|
-
|
|
42601
|
-
|
|
42602
|
-
|
|
42603
|
-
|
|
42604
|
-
|
|
42605
|
-
const effectivePreviewPorts = Array.from(
|
|
42606
|
-
/* @__PURE__ */ new Set([...config.previewPorts ?? [], ...appBuilderPortRange])
|
|
42607
|
-
);
|
|
42608
|
-
const deleteSessionsByProject = async (_name) => {
|
|
42609
|
-
};
|
|
42610
|
-
const handlers = buildMethodHandlers({
|
|
42529
|
+
const loginFlow = new LoginFlow({
|
|
42530
|
+
store: ownerIdentityStore,
|
|
42531
|
+
fetchUserInfo: (ttcToken) => fetchTtcUserInfo({ apiBase: TTC_HOSTS.api, token: ttcToken }),
|
|
42532
|
+
ttcAuthBase: TTC_HOSTS.auth,
|
|
42533
|
+
getCallbackUrl: () => `http://127.0.0.1:${config.port}/auth/callback`
|
|
42534
|
+
});
|
|
42535
|
+
const makeHandlers = () => buildMethodHandlers({
|
|
42611
42536
|
manager,
|
|
42612
42537
|
workspace,
|
|
42613
42538
|
skills,
|
|
@@ -42680,12 +42605,10 @@ async function startDaemon(config) {
|
|
|
42680
42605
|
// extension sharing v1 — owner-side published-channels store.
|
|
42681
42606
|
publishedChannelStore,
|
|
42682
42607
|
bundleCache,
|
|
42683
|
-
//
|
|
42684
|
-
|
|
42685
|
-
deleteSessionsByProject,
|
|
42686
|
-
devServerLifecycle: devServerSupervisor,
|
|
42687
|
-
logger
|
|
42608
|
+
// 飞书统一身份 Phase 1:登录 RPC handlers(auth:login:start / getIdentity / logout)
|
|
42609
|
+
feishuAuth: { loginFlow, ownerIdentityStore }
|
|
42688
42610
|
});
|
|
42611
|
+
let handlers = makeHandlers();
|
|
42689
42612
|
const authResolver = new AuthContextResolver({
|
|
42690
42613
|
ownerToken: resolvedAuthToken
|
|
42691
42614
|
});
|
|
@@ -42711,8 +42634,29 @@ async function startDaemon(config) {
|
|
|
42711
42634
|
},
|
|
42712
42635
|
// POST /extensions/import lands in ~/.clawd/extensions/<id>/
|
|
42713
42636
|
extensionsRoot: () => extensionsRoot(),
|
|
42714
|
-
//
|
|
42715
|
-
|
|
42637
|
+
// 飞书登录 loopback 回调(热切换,2026-06-01 拍板):成功落盘后——
|
|
42638
|
+
// 1. 进程内实时切换身份(let binding + 值快照持有者重建)
|
|
42639
|
+
// 2. 广播 auth:login:done 给在线 owner 连接(UI 即时反馈)
|
|
42640
|
+
// 3. 踢掉所有 ws 连接(在线连接的 ctx 是旧身份)→ UI 自动重连 → 新身份 ctx →
|
|
42641
|
+
// People 立即解锁,不需要重启 daemon
|
|
42642
|
+
// 失败则广播 auth:login:failed。
|
|
42643
|
+
feishuLogin: {
|
|
42644
|
+
handleLoginCallback: async (params) => {
|
|
42645
|
+
const result = await loginFlow.handleCallback(params);
|
|
42646
|
+
if (result.ok) {
|
|
42647
|
+
feishuActive = true;
|
|
42648
|
+
ownerPrincipalId = result.identity.unionId;
|
|
42649
|
+
ownerDisplayName = result.identity.displayName;
|
|
42650
|
+
handlers = makeHandlers();
|
|
42651
|
+
wsServer?.setOwnerPrincipalId(result.identity.unionId);
|
|
42652
|
+
wsServer?.broadcastToOwners({ type: "auth:login:done", identity: result.identity });
|
|
42653
|
+
setImmediate(() => wsServer?.closeAllClients());
|
|
42654
|
+
} else {
|
|
42655
|
+
wsServer?.broadcastToOwners({ type: "auth:login:failed", reason: result.reason });
|
|
42656
|
+
}
|
|
42657
|
+
return result;
|
|
42658
|
+
}
|
|
42659
|
+
}
|
|
42716
42660
|
});
|
|
42717
42661
|
wsServer = new LocalWsServer({
|
|
42718
42662
|
host: config.host,
|
|
@@ -42751,8 +42695,6 @@ async function startDaemon(config) {
|
|
|
42751
42695
|
},
|
|
42752
42696
|
// file-sharing HTTP 路由复用 daemon 同端口(spec §5 第 3 条);router 自己处理 auth + 404
|
|
42753
42697
|
httpRequestHandler: httpRouter,
|
|
42754
|
-
// app-builder build 模式:/preview/<port>/ HMR websocket upgrade 转发的端口白名单(同 http-router 配置)
|
|
42755
|
-
previewPorts: effectivePreviewPorts,
|
|
42756
42698
|
// 订阅成功后给该 client 重放 in-flight pendingQuestions(plan: clawd-question-server-truth)。
|
|
42757
42699
|
// daemon 是 pendingQuestions 的唯一 source of truth;新 client 接入 / 刷新页面时
|
|
42758
42700
|
// 把当前所有未决 question 以 session:question 帧定向回放,让 UI 不再误显示 Ended。
|
|
@@ -42797,6 +42739,12 @@ async function startDaemon(config) {
|
|
|
42797
42739
|
const requestId = typeof frame.requestId === "string" ? frame.requestId : void 0;
|
|
42798
42740
|
const handler = handlers[type];
|
|
42799
42741
|
if (!handler) throw new ClawdError(ERROR_CODES.METHOD_NOT_IMPLEMENTED, `not implemented: ${type}`);
|
|
42742
|
+
if (!feishuActive && FEISHU_GATED_METHODS.includes(type)) {
|
|
42743
|
+
throw new ClawdError(
|
|
42744
|
+
ERROR_CODES.FEISHU_LOGIN_REQUIRED,
|
|
42745
|
+
`${type} requires feishu login (People/remote identity)`
|
|
42746
|
+
);
|
|
42747
|
+
}
|
|
42800
42748
|
const ctx = wsServer.getClientContext(client.id) ?? ownerContext(ownerPrincipalId, ownerDisplayName);
|
|
42801
42749
|
const verdict = computeGrantForFrame(type, frame);
|
|
42802
42750
|
if (verdict.kind === "check") {
|
|
@@ -42869,7 +42817,6 @@ async function startDaemon(config) {
|
|
|
42869
42817
|
stateSnapshot = { ...stateSnapshot, tunnelUrl: r.url, tunnelError: void 0 };
|
|
42870
42818
|
stateMgr.write(stateSnapshot);
|
|
42871
42819
|
currentTunnelUrl = r.url;
|
|
42872
|
-
wss.broadcastAll({ type: "tunnel:ready", url: r.url, subdomain: r.subdomain });
|
|
42873
42820
|
const connectUrl = resolvedAuthToken ? `${r.url}#token=${resolvedAuthToken}` : r.url;
|
|
42874
42821
|
const lines = [
|
|
42875
42822
|
`Tunnel: ${r.url}`,
|