@clawos-dev/clawd 0.2.299 → 0.2.301

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.cjs CHANGED
@@ -6585,7 +6585,7 @@ var init_shift_internal = __esm({
6585
6585
  });
6586
6586
 
6587
6587
  // ../protocol/src/lark-bot-schemas.ts
6588
- var LarkBotPersonaArgs, LarkBotUnbindArgs, LarkBotStatusArgs, LarkBotBindManualArgs, LarkBotGroupSchema, LarkBotStatusResultSchema, LarkBotStateFrameSchema;
6588
+ var LarkBotPersonaArgs, LarkBotUnbindArgs, LarkBotStatusArgs, LarkBotProvisionArgs, LarkBotProvisionCancelArgs, LARK_BOT_PROVISION_ERROR_REASONS, LarkBotProvisionErrorReasonSchema, LarkBotBindManualArgs, LarkBotGroupSchema, LarkBotStatusResultSchema, LarkBotStateFrameSchema;
6589
6589
  var init_lark_bot_schemas = __esm({
6590
6590
  "../protocol/src/lark-bot-schemas.ts"() {
6591
6591
  "use strict";
@@ -6593,6 +6593,17 @@ var init_lark_bot_schemas = __esm({
6593
6593
  LarkBotPersonaArgs = external_exports.object({ personaId: external_exports.string().min(1) });
6594
6594
  LarkBotUnbindArgs = LarkBotPersonaArgs;
6595
6595
  LarkBotStatusArgs = LarkBotPersonaArgs;
6596
+ LarkBotProvisionArgs = LarkBotPersonaArgs;
6597
+ LarkBotProvisionCancelArgs = LarkBotPersonaArgs;
6598
+ LARK_BOT_PROVISION_ERROR_REASONS = [
6599
+ "expired",
6600
+ "cancelled",
6601
+ "access_denied",
6602
+ "lark_protocol_error",
6603
+ "cloud_unreachable",
6604
+ "internal_error"
6605
+ ];
6606
+ LarkBotProvisionErrorReasonSchema = external_exports.enum(LARK_BOT_PROVISION_ERROR_REASONS);
6596
6607
  LarkBotBindManualArgs = external_exports.object({
6597
6608
  personaId: external_exports.string().min(1),
6598
6609
  appId: external_exports.string().min(1),
@@ -6606,10 +6617,14 @@ var init_lark_bot_schemas = __esm({
6606
6617
  lastActiveAt: external_exports.number().int().optional()
6607
6618
  });
6608
6619
  LarkBotStatusResultSchema = external_exports.object({
6609
- state: external_exports.enum(["unbound", "bound", "broken"]),
6620
+ state: external_exports.enum(["unbound", "provisioning", "bound", "broken"]),
6610
6621
  appId: external_exports.string().optional(),
6611
6622
  botName: external_exports.string().optional(),
6612
6623
  brokenReason: external_exports.string().optional(),
6624
+ /** provisioning 态:二维码内容 URL(UI 本地渲染成 QR 图;spec P5 无 expiresAt/倒计时) */
6625
+ qrUrl: external_exports.string().optional(),
6626
+ /** provision 失败后的 unbound 帧携带;正常 unbound 无此字段 */
6627
+ errorReason: LarkBotProvisionErrorReasonSchema.optional(),
6613
6628
  groups: external_exports.array(LarkBotGroupSchema)
6614
6629
  });
6615
6630
  LarkBotStateFrameSchema = LarkBotStatusResultSchema.extend({
@@ -8034,8 +8049,16 @@ var init_methods = __esm({
8034
8049
  summary: "\u67E5 shift \u7684\u6267\u884C\u5386\u53F2\uFF08\u4EC5\u521B\u5EFA\u8005\uFF09",
8035
8050
  args: ShiftInternalHistoryArgs
8036
8051
  },
8052
+ "larkBot:provision": {
8053
+ summary: "\u626B\u7801\u88C5\u914D\uFF1A\u4E91\u7AEF registerApp \u521B\u5EFA PersonalAgent \u5E76\u81EA\u52A8\u7ED1\u5B9A\uFF1B\u7ACB\u5373\u8FD4\u56DE\uFF0C\u8FDB\u5EA6\u8D70 larkBot:state \u5E27\uFF1Bowner-only",
8054
+ args: LarkBotProvisionArgs
8055
+ },
8056
+ "larkBot:provisionCancel": {
8057
+ summary: "\u53D6\u6D88\u8FDB\u884C\u4E2D\u7684\u626B\u7801\u88C5\u914D\uFF1Bowner-only",
8058
+ args: LarkBotProvisionCancelArgs
8059
+ },
8037
8060
  "larkBot:bindManual": {
8038
- summary: "\u624B\u586B app_id/app_secret \u7ED1\u5B9A\u73B0\u6709\u98DE\u4E66 bot\uFF08\u552F\u4E00\u7ED1\u5B9A\u5165\u53E3\uFF09\uFF1Bowner-only",
8061
+ summary: "\u9003\u751F\u8231\uFF1A\u624B\u586B app_id/app_secret \u7ED1\u5B9A\u73B0\u6709\u98DE\u4E66 bot\uFF1Bowner-only",
8039
8062
  args: LarkBotBindManualArgs
8040
8063
  },
8041
8064
  "larkBot:unbind": {
@@ -51829,11 +51852,20 @@ function buildShiftInternalHandlers(deps) {
51829
51852
 
51830
51853
  // src/handlers/lark-bot.ts
51831
51854
  init_protocol();
51855
+ var PROVISION_ERROR_SET = new Set(LARK_BOT_PROVISION_ERROR_REASONS);
51856
+ var PROVISION_POLL_INTERVAL_MS = 2e3;
51857
+ var PROVISION_MAX_CONSECUTIVE_FAILURES = 5;
51858
+ function normalizeProvisionReason(reason) {
51859
+ return PROVISION_ERROR_SET.has(reason) ? reason : "lark_protocol_error";
51860
+ }
51832
51861
  function statusFrame(personaId2, status) {
51833
51862
  return { type: "larkBot:state", personaId: personaId2, ...status };
51834
51863
  }
51835
51864
  function buildLarkBotHandlers(deps) {
51836
51865
  const now = deps.now ?? Date.now;
51866
+ const sleep2 = deps.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
51867
+ const pollIntervalMs = deps.pollIntervalMs ?? PROVISION_POLL_INTERVAL_MS;
51868
+ const inFlight = /* @__PURE__ */ new Map();
51837
51869
  const groupsOf = (personaId2) => {
51838
51870
  const removed = new Set(deps.store.read(personaId2)?.removedChatIds ?? []);
51839
51871
  return deps.listLarkSessions(personaId2).filter((g2) => !removed.has(g2.chatId));
@@ -51841,12 +51873,120 @@ function buildLarkBotHandlers(deps) {
51841
51873
  const emit = (personaId2, status2) => {
51842
51874
  deps.broadcast(statusFrame(personaId2, status2));
51843
51875
  };
51876
+ const runProvisionPolling = async (personaId2, provisionId) => {
51877
+ let failures = 0;
51878
+ for (; ; ) {
51879
+ await sleep2(pollIntervalMs);
51880
+ const entry = inFlight.get(personaId2);
51881
+ if (!entry || entry.cancelled) return;
51882
+ let status2;
51883
+ try {
51884
+ status2 = await deps.cloud.provisionStatus(provisionId);
51885
+ failures = 0;
51886
+ } catch (err) {
51887
+ failures += 1;
51888
+ if (failures >= PROVISION_MAX_CONSECUTIVE_FAILURES) {
51889
+ inFlight.delete(personaId2);
51890
+ deps.logger?.warn(`larkBot provision poll unreachable: ${err.message}`);
51891
+ emit(personaId2, { state: "unbound", errorReason: "cloud_unreachable", groups: [] });
51892
+ return;
51893
+ }
51894
+ await sleep2(pollIntervalMs * 2 ** failures);
51895
+ continue;
51896
+ }
51897
+ const current = inFlight.get(personaId2);
51898
+ if (!current || current.cancelled) {
51899
+ if (status2.state === "success") {
51900
+ void deps.cloud.unbind(status2.appId).catch(
51901
+ (err) => deps.logger?.warn(
51902
+ `larkBot provision late-success cleanup unbind failed: ${err.message}`
51903
+ )
51904
+ );
51905
+ }
51906
+ return;
51907
+ }
51908
+ if (status2.state === "pending") continue;
51909
+ inFlight.delete(personaId2);
51910
+ if (status2.state === "success") {
51911
+ deps.store.write(personaId2, {
51912
+ appId: status2.appId,
51913
+ botName: status2.botName,
51914
+ boundAt: now()
51915
+ });
51916
+ emit(personaId2, {
51917
+ state: "bound",
51918
+ appId: status2.appId,
51919
+ botName: status2.botName,
51920
+ groups: groupsOf(personaId2)
51921
+ });
51922
+ } else {
51923
+ emit(personaId2, {
51924
+ state: "unbound",
51925
+ errorReason: normalizeProvisionReason(status2.reason),
51926
+ groups: []
51927
+ });
51928
+ }
51929
+ return;
51930
+ }
51931
+ };
51932
+ const provision = async (frame) => {
51933
+ const { personaId: personaId2 } = parseArgs2(LarkBotProvisionArgs, frame, "larkBot:provision");
51934
+ const tunnelUrl = deps.getTunnelUrl();
51935
+ if (!tunnelUrl) throw new Error("larkBot provision requires tunnel to be up");
51936
+ const ownerId = deps.getOwnerId();
51937
+ if (!ownerId) throw new Error("larkBot provision requires feishu login (owner identity)");
51938
+ if (deps.store.read(personaId2)) throw new Error(`larkBot already bound: ${personaId2}`);
51939
+ if (inFlight.has(personaId2)) throw new Error(`larkBot provision already in flight: ${personaId2}`);
51940
+ const botName = deps.getPersonaName(personaId2) ?? personaId2;
51941
+ inFlight.set(personaId2, { provisionId: "", qrUrl: "", cancelled: false });
51942
+ let provisionId;
51943
+ let qrUrl;
51944
+ try {
51945
+ ;
51946
+ ({ provisionId, qrUrl } = await deps.cloud.provisionBegin({
51947
+ personaId: personaId2,
51948
+ botName,
51949
+ tunnelUrl,
51950
+ ownerId
51951
+ }));
51952
+ } catch (err) {
51953
+ inFlight.delete(personaId2);
51954
+ throw err;
51955
+ }
51956
+ const placeholder = inFlight.get(personaId2);
51957
+ if (!placeholder || placeholder.cancelled) {
51958
+ inFlight.delete(personaId2);
51959
+ void deps.cloud.provisionCancel(provisionId).catch(
51960
+ (err) => deps.logger?.warn(`larkBot provision cancel-during-begin: ${err.message}`)
51961
+ );
51962
+ return { response: { type: "larkBot:provision:ok" } };
51963
+ }
51964
+ inFlight.set(personaId2, { provisionId, qrUrl, cancelled: false });
51965
+ emit(personaId2, { state: "provisioning", qrUrl, groups: [] });
51966
+ void runProvisionPolling(personaId2, provisionId);
51967
+ return { response: { type: "larkBot:provision:ok" } };
51968
+ };
51969
+ const provisionCancel = async (frame) => {
51970
+ const { personaId: personaId2 } = parseArgs2(LarkBotProvisionCancelArgs, frame, "larkBot:provisionCancel");
51971
+ const entry = inFlight.get(personaId2);
51972
+ if (!entry) throw new Error(`larkBot provision not in flight: ${personaId2}`);
51973
+ entry.cancelled = true;
51974
+ if (entry.provisionId) {
51975
+ await deps.cloud.provisionCancel(entry.provisionId).catch(
51976
+ (err) => deps.logger?.warn(`larkBot provisionCancel cloud failed: ${err.message}`)
51977
+ );
51978
+ inFlight.delete(personaId2);
51979
+ }
51980
+ emit(personaId2, { state: "unbound", groups: [] });
51981
+ return { response: { type: "larkBot:provisionCancel:ok" } };
51982
+ };
51844
51983
  const bindManual = async (frame) => {
51845
51984
  const { personaId: personaId2, appId, appSecret, botName } = parseArgs2(
51846
51985
  LarkBotBindManualArgs,
51847
51986
  frame,
51848
51987
  "larkBot:bindManual"
51849
51988
  );
51989
+ if (inFlight.has(personaId2)) throw new Error(`larkBot provision already in flight: ${personaId2}`);
51850
51990
  const tunnelUrl = deps.getTunnelUrl();
51851
51991
  if (!tunnelUrl) throw new Error("larkBot bind requires tunnel to be up");
51852
51992
  const ownerId = deps.getOwnerId();
@@ -51873,6 +52013,17 @@ function buildLarkBotHandlers(deps) {
51873
52013
  };
51874
52014
  const status = async (frame) => {
51875
52015
  const { personaId: personaId2 } = parseArgs2(LarkBotStatusArgs, frame, "larkBot:status");
52016
+ const flight = inFlight.get(personaId2);
52017
+ if (flight && !flight.cancelled && flight.qrUrl) {
52018
+ return {
52019
+ response: {
52020
+ type: "larkBot:status:ok",
52021
+ state: "provisioning",
52022
+ qrUrl: flight.qrUrl,
52023
+ groups: []
52024
+ }
52025
+ };
52026
+ }
51876
52027
  const meta = deps.store.read(personaId2);
51877
52028
  if (!meta) {
51878
52029
  return {
@@ -51896,6 +52047,8 @@ function buildLarkBotHandlers(deps) {
51896
52047
  return { response: { type: "larkBot:status:ok", ...result } };
51897
52048
  };
51898
52049
  return {
52050
+ "larkBot:provision": provision,
52051
+ "larkBot:provisionCancel": provisionCancel,
51899
52052
  "larkBot:bindManual": bindManual,
51900
52053
  "larkBot:unbind": unbind,
51901
52054
  "larkBot:status": status
@@ -52014,6 +52167,23 @@ function createLarkBotCloudClient(opts) {
52014
52167
  async updateTunnelUrl(tunnelUrl) {
52015
52168
  await request("PATCH", "/api/lark-bot/tunnel", { tunnelUrl });
52016
52169
  },
52170
+ async provisionBegin(args) {
52171
+ const json = await request("POST", "/api/lark-bot/provision", { ...args });
52172
+ return { provisionId: String(json.provisionId), qrUrl: String(json.qrUrl) };
52173
+ },
52174
+ async provisionStatus(provisionId) {
52175
+ const json = await request("GET", `/api/lark-bot/provision/${encodeURIComponent(provisionId)}`);
52176
+ if (json.state === "success") {
52177
+ return { state: "success", appId: String(json.appId), botName: String(json.botName) };
52178
+ }
52179
+ if (json.state === "error") {
52180
+ return { state: "error", reason: String(json.reason ?? "internal_error") };
52181
+ }
52182
+ return { state: "pending" };
52183
+ },
52184
+ async provisionCancel(provisionId) {
52185
+ await request("DELETE", `/api/lark-bot/provision/${encodeURIComponent(provisionId)}`);
52186
+ },
52017
52187
  async reply(args) {
52018
52188
  await request("POST", "/api/lark-bot/reply", { ...args });
52019
52189
  },
@@ -52065,6 +52235,7 @@ init_protocol();
52065
52235
  var ERROR_REPLY_TEXT = "\u5904\u7406\u51FA\u9519\u4E86\uFF0C\u8BF7\u91CD\u8BD5";
52066
52236
  var DEFAULT_TURN_TIMEOUT_MS = 10 * 60 * 1e3;
52067
52237
  var DEBOUNCE_MS = 1e3;
52238
+ var CONTENT_HOLD_MS = 6e4;
52068
52239
  var MAX_IMAGE_BYTES = 8 * 1024 * 1024;
52069
52240
  var EXT_BY_MIME = {
52070
52241
  "image/png": "png",
@@ -52094,6 +52265,14 @@ function createLarkChatRouter(deps) {
52094
52265
  const replyDelays = deps.replyRetryDelaysMs ?? [2e3, 8e3, 3e4];
52095
52266
  const sleep2 = deps.sleepImpl ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
52096
52267
  const debounceMs = deps.debounceMs ?? DEBOUNCE_MS;
52268
+ const contentHoldMs = deps.contentHoldMs ?? CONTENT_HOLD_MS;
52269
+ const timerMsFor = (envs) => {
52270
+ if (envs.some((e) => e.text?.trim())) return debounceMs;
52271
+ const hasContent = envs.some(
52272
+ (e) => (e.imageKeys?.length ?? 0) > 0 || (e.files?.length ?? 0) > 0
52273
+ );
52274
+ return hasContent ? contentHoldMs : debounceMs;
52275
+ };
52097
52276
  const queues = /* @__PURE__ */ new Map();
52098
52277
  const activity = /* @__PURE__ */ new Map();
52099
52278
  const pending = /* @__PURE__ */ new Map();
@@ -52270,12 +52449,12 @@ ${formatLarkSpeakerReminder(speaker)}`;
52270
52449
  if (batch) {
52271
52450
  batch.envelopes.push(env);
52272
52451
  clearTimeout(batch.timer);
52273
- batch.timer = setTimeout(() => flushPending(env.chatId), debounceMs);
52452
+ batch.timer = setTimeout(() => flushPending(env.chatId), timerMsFor(batch.envelopes));
52274
52453
  } else {
52275
52454
  pending.set(env.chatId, {
52276
52455
  envelopes: [env],
52277
52456
  senderOpenId: sender,
52278
- timer: setTimeout(() => flushPending(env.chatId), debounceMs)
52457
+ timer: setTimeout(() => flushPending(env.chatId), timerMsFor([env]))
52279
52458
  });
52280
52459
  }
52281
52460
  },
@@ -58902,6 +59081,8 @@ var METHOD_GRANT_MAP = {
58902
59081
  "shift-internal:update": ADMIN_ANY,
58903
59082
  "shift-internal:history": ADMIN_ANY,
58904
59083
  // ---- larkBot:* (LarkBotConnector 绑定/状态,owner-only) ----
59084
+ "larkBot:provision": ADMIN_ANY,
59085
+ "larkBot:provisionCancel": ADMIN_ANY,
58905
59086
  "larkBot:bindManual": ADMIN_ANY,
58906
59087
  "larkBot:unbind": ADMIN_ANY,
58907
59088
  "larkBot:status": ADMIN_ANY,
@@ -59036,7 +59217,7 @@ function computeMethodAccess(args) {
59036
59217
  }
59037
59218
 
59038
59219
  // src/version.ts
59039
- var version = "0.2.299".length > 0 ? "0.2.299" : "dev";
59220
+ var version = "0.2.301".length > 0 ? "0.2.301" : "dev";
59040
59221
 
59041
59222
  // src/cli-probe/probe.ts
59042
59223
  var fs56 = __toESM(require("fs"), 1);
@@ -21794,6 +21794,17 @@ var ShiftInternalHistoryArgs = external_exports.object({
21794
21794
  var LarkBotPersonaArgs = external_exports.object({ personaId: external_exports.string().min(1) });
21795
21795
  var LarkBotUnbindArgs = LarkBotPersonaArgs;
21796
21796
  var LarkBotStatusArgs = LarkBotPersonaArgs;
21797
+ var LarkBotProvisionArgs = LarkBotPersonaArgs;
21798
+ var LarkBotProvisionCancelArgs = LarkBotPersonaArgs;
21799
+ var LARK_BOT_PROVISION_ERROR_REASONS = [
21800
+ "expired",
21801
+ "cancelled",
21802
+ "access_denied",
21803
+ "lark_protocol_error",
21804
+ "cloud_unreachable",
21805
+ "internal_error"
21806
+ ];
21807
+ var LarkBotProvisionErrorReasonSchema = external_exports.enum(LARK_BOT_PROVISION_ERROR_REASONS);
21797
21808
  var LarkBotBindManualArgs = external_exports.object({
21798
21809
  personaId: external_exports.string().min(1),
21799
21810
  appId: external_exports.string().min(1),
@@ -21807,10 +21818,14 @@ var LarkBotGroupSchema = external_exports.object({
21807
21818
  lastActiveAt: external_exports.number().int().optional()
21808
21819
  });
21809
21820
  var LarkBotStatusResultSchema = external_exports.object({
21810
- state: external_exports.enum(["unbound", "bound", "broken"]),
21821
+ state: external_exports.enum(["unbound", "provisioning", "bound", "broken"]),
21811
21822
  appId: external_exports.string().optional(),
21812
21823
  botName: external_exports.string().optional(),
21813
21824
  brokenReason: external_exports.string().optional(),
21825
+ /** provisioning 态:二维码内容 URL(UI 本地渲染成 QR 图;spec P5 无 expiresAt/倒计时) */
21826
+ qrUrl: external_exports.string().optional(),
21827
+ /** provision 失败后的 unbound 帧携带;正常 unbound 无此字段 */
21828
+ errorReason: LarkBotProvisionErrorReasonSchema.optional(),
21814
21829
  groups: external_exports.array(LarkBotGroupSchema)
21815
21830
  });
21816
21831
  var LarkBotStateFrameSchema = LarkBotStatusResultSchema.extend({
@@ -23140,8 +23155,16 @@ var METHOD_DOCS = {
23140
23155
  summary: "\u67E5 shift \u7684\u6267\u884C\u5386\u53F2\uFF08\u4EC5\u521B\u5EFA\u8005\uFF09",
23141
23156
  args: ShiftInternalHistoryArgs
23142
23157
  },
23158
+ "larkBot:provision": {
23159
+ summary: "\u626B\u7801\u88C5\u914D\uFF1A\u4E91\u7AEF registerApp \u521B\u5EFA PersonalAgent \u5E76\u81EA\u52A8\u7ED1\u5B9A\uFF1B\u7ACB\u5373\u8FD4\u56DE\uFF0C\u8FDB\u5EA6\u8D70 larkBot:state \u5E27\uFF1Bowner-only",
23160
+ args: LarkBotProvisionArgs
23161
+ },
23162
+ "larkBot:provisionCancel": {
23163
+ summary: "\u53D6\u6D88\u8FDB\u884C\u4E2D\u7684\u626B\u7801\u88C5\u914D\uFF1Bowner-only",
23164
+ args: LarkBotProvisionCancelArgs
23165
+ },
23143
23166
  "larkBot:bindManual": {
23144
- summary: "\u624B\u586B app_id/app_secret \u7ED1\u5B9A\u73B0\u6709\u98DE\u4E66 bot\uFF08\u552F\u4E00\u7ED1\u5B9A\u5165\u53E3\uFF09\uFF1Bowner-only",
23167
+ summary: "\u9003\u751F\u8231\uFF1A\u624B\u586B app_id/app_secret \u7ED1\u5B9A\u73B0\u6709\u98DE\u4E66 bot\uFF1Bowner-only",
23145
23168
  args: LarkBotBindManualArgs
23146
23169
  },
23147
23170
  "larkBot:unbind": {