@clawos-dev/clawd 0.2.300 → 0.2.302

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,21 @@ 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
+ var PROVISION_POLL_MAX_MS = 60 * 60 * 1e3;
51859
+ function normalizeProvisionReason(reason) {
51860
+ return PROVISION_ERROR_SET.has(reason) ? reason : "lark_protocol_error";
51861
+ }
51832
51862
  function statusFrame(personaId2, status) {
51833
51863
  return { type: "larkBot:state", personaId: personaId2, ...status };
51834
51864
  }
51835
51865
  function buildLarkBotHandlers(deps) {
51836
51866
  const now = deps.now ?? Date.now;
51867
+ const sleep2 = deps.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
51868
+ const pollIntervalMs = deps.pollIntervalMs ?? PROVISION_POLL_INTERVAL_MS;
51869
+ const inFlight = /* @__PURE__ */ new Map();
51837
51870
  const groupsOf = (personaId2) => {
51838
51871
  const removed = new Set(deps.store.read(personaId2)?.removedChatIds ?? []);
51839
51872
  return deps.listLarkSessions(personaId2).filter((g2) => !removed.has(g2.chatId));
@@ -51841,12 +51874,133 @@ function buildLarkBotHandlers(deps) {
51841
51874
  const emit = (personaId2, status2) => {
51842
51875
  deps.broadcast(statusFrame(personaId2, status2));
51843
51876
  };
51877
+ const runProvisionPolling = async (personaId2, provisionId) => {
51878
+ let failures = 0;
51879
+ const startedAt = now();
51880
+ for (; ; ) {
51881
+ await sleep2(pollIntervalMs);
51882
+ const entry = inFlight.get(personaId2);
51883
+ if (!entry || entry.cancelled) return;
51884
+ if (now() - startedAt >= PROVISION_POLL_MAX_MS) {
51885
+ inFlight.delete(personaId2);
51886
+ deps.logger?.warn(`larkBot provision poll deadline exceeded: ${personaId2}`);
51887
+ emit(personaId2, { state: "unbound", errorReason: "expired", groups: [] });
51888
+ return;
51889
+ }
51890
+ let status2;
51891
+ try {
51892
+ status2 = await deps.cloud.provisionStatus(provisionId);
51893
+ failures = 0;
51894
+ } catch (err) {
51895
+ failures += 1;
51896
+ if (failures >= PROVISION_MAX_CONSECUTIVE_FAILURES) {
51897
+ inFlight.delete(personaId2);
51898
+ deps.logger?.warn(`larkBot provision poll unreachable: ${err.message}`);
51899
+ emit(personaId2, { state: "unbound", errorReason: "cloud_unreachable", groups: [] });
51900
+ return;
51901
+ }
51902
+ await sleep2(pollIntervalMs * 2 ** failures);
51903
+ continue;
51904
+ }
51905
+ const current = inFlight.get(personaId2);
51906
+ if (!current || current.cancelled) return;
51907
+ if (status2.state === "pending") continue;
51908
+ inFlight.delete(personaId2);
51909
+ if (status2.state === "success") {
51910
+ deps.store.write(personaId2, {
51911
+ appId: status2.appId,
51912
+ botName: status2.botName,
51913
+ boundAt: now()
51914
+ });
51915
+ emit(personaId2, {
51916
+ state: "bound",
51917
+ appId: status2.appId,
51918
+ botName: status2.botName,
51919
+ groups: groupsOf(personaId2)
51920
+ });
51921
+ } else {
51922
+ emit(personaId2, {
51923
+ state: "unbound",
51924
+ errorReason: normalizeProvisionReason(status2.reason),
51925
+ groups: []
51926
+ });
51927
+ }
51928
+ return;
51929
+ }
51930
+ };
51931
+ const provision = async (frame) => {
51932
+ const { personaId: personaId2 } = parseArgs2(LarkBotProvisionArgs, frame, "larkBot:provision");
51933
+ const tunnelUrl = deps.getTunnelUrl();
51934
+ if (!tunnelUrl) throw new Error("larkBot provision requires tunnel to be up");
51935
+ const ownerId = deps.getOwnerId();
51936
+ if (!ownerId) throw new Error("larkBot provision requires feishu login (owner identity)");
51937
+ if (deps.store.read(personaId2)) throw new Error(`larkBot already bound: ${personaId2}`);
51938
+ if (inFlight.has(personaId2)) throw new Error(`larkBot provision already in flight: ${personaId2}`);
51939
+ const botName = deps.getPersonaName(personaId2) ?? personaId2;
51940
+ inFlight.set(personaId2, { provisionId: "", qrUrl: "", cancelled: false });
51941
+ let provisionId;
51942
+ let qrUrl;
51943
+ try {
51944
+ ;
51945
+ ({ provisionId, qrUrl } = await deps.cloud.provisionBegin({
51946
+ personaId: personaId2,
51947
+ botName,
51948
+ tunnelUrl,
51949
+ ownerId
51950
+ }));
51951
+ } catch (err) {
51952
+ inFlight.delete(personaId2);
51953
+ throw err;
51954
+ }
51955
+ const placeholder = inFlight.get(personaId2);
51956
+ if (!placeholder || placeholder.cancelled) {
51957
+ inFlight.delete(personaId2);
51958
+ void deps.cloud.provisionCancel(provisionId).catch(
51959
+ (err) => deps.logger?.warn(`larkBot provision cancel-during-begin: ${err.message}`)
51960
+ );
51961
+ return { response: { type: "larkBot:provision:ok" } };
51962
+ }
51963
+ inFlight.set(personaId2, { provisionId, qrUrl, cancelled: false });
51964
+ emit(personaId2, { state: "provisioning", qrUrl, groups: [] });
51965
+ void runProvisionPolling(personaId2, provisionId);
51966
+ return { response: { type: "larkBot:provision:ok" } };
51967
+ };
51968
+ const provisionCancel = async (frame) => {
51969
+ const { personaId: personaId2 } = parseArgs2(LarkBotProvisionCancelArgs, frame, "larkBot:provisionCancel");
51970
+ const entry = inFlight.get(personaId2);
51971
+ if (!entry) throw new Error(`larkBot provision not in flight: ${personaId2}`);
51972
+ entry.cancelled = true;
51973
+ if (entry.provisionId) {
51974
+ const result = await deps.cloud.provisionCancel(entry.provisionId).catch((err) => {
51975
+ deps.logger?.warn(`larkBot provisionCancel cloud failed: ${err.message}`);
51976
+ return { state: "cancelled" };
51977
+ });
51978
+ inFlight.delete(personaId2);
51979
+ if (result.state === "success") {
51980
+ deps.store.write(personaId2, {
51981
+ appId: result.appId,
51982
+ botName: result.botName,
51983
+ boundAt: now()
51984
+ });
51985
+ emit(personaId2, {
51986
+ state: "bound",
51987
+ appId: result.appId,
51988
+ botName: result.botName,
51989
+ groups: groupsOf(personaId2)
51990
+ });
51991
+ return { response: { type: "larkBot:provisionCancel:ok" } };
51992
+ }
51993
+ }
51994
+ emit(personaId2, { state: "unbound", groups: [] });
51995
+ return { response: { type: "larkBot:provisionCancel:ok" } };
51996
+ };
51844
51997
  const bindManual = async (frame) => {
51845
51998
  const { personaId: personaId2, appId, appSecret, botName } = parseArgs2(
51846
51999
  LarkBotBindManualArgs,
51847
52000
  frame,
51848
52001
  "larkBot:bindManual"
51849
52002
  );
52003
+ if (inFlight.has(personaId2)) throw new Error(`larkBot provision already in flight: ${personaId2}`);
51850
52004
  const tunnelUrl = deps.getTunnelUrl();
51851
52005
  if (!tunnelUrl) throw new Error("larkBot bind requires tunnel to be up");
51852
52006
  const ownerId = deps.getOwnerId();
@@ -51873,18 +52027,53 @@ function buildLarkBotHandlers(deps) {
51873
52027
  };
51874
52028
  const status = async (frame) => {
51875
52029
  const { personaId: personaId2 } = parseArgs2(LarkBotStatusArgs, frame, "larkBot:status");
51876
- const meta = deps.store.read(personaId2);
51877
- if (!meta) {
52030
+ const flight = inFlight.get(personaId2);
52031
+ if (flight && !flight.cancelled && flight.qrUrl) {
51878
52032
  return {
51879
- response: { type: "larkBot:status:ok", state: "unbound", groups: [] }
52033
+ response: {
52034
+ type: "larkBot:status:ok",
52035
+ state: "provisioning",
52036
+ qrUrl: flight.qrUrl,
52037
+ groups: []
52038
+ }
51880
52039
  };
51881
52040
  }
52041
+ const meta = deps.store.read(personaId2);
52042
+ let cloudReachable = false;
51882
52043
  let cloudBinding;
51883
52044
  try {
51884
52045
  cloudBinding = (await deps.cloud.listBindings()).find((b2) => b2.personaId === personaId2);
52046
+ cloudReachable = true;
51885
52047
  } catch (err) {
51886
52048
  deps.logger?.warn(`larkBot status: cloud unreachable: ${err.message}`);
51887
52049
  }
52050
+ if (!meta) {
52051
+ if (cloudBinding) {
52052
+ deps.store.write(personaId2, {
52053
+ appId: cloudBinding.appId,
52054
+ botName: cloudBinding.botName,
52055
+ boundAt: now()
52056
+ });
52057
+ const broken2 = cloudBinding.status === "broken";
52058
+ const result2 = {
52059
+ state: broken2 ? "broken" : "bound",
52060
+ appId: cloudBinding.appId,
52061
+ botName: cloudBinding.botName,
52062
+ ...broken2 ? { brokenReason: cloudBinding.brokenReason ?? "unknown" } : {},
52063
+ groups: groupsOf(personaId2)
52064
+ };
52065
+ return { response: { type: "larkBot:status:ok", ...result2 } };
52066
+ }
52067
+ return {
52068
+ response: { type: "larkBot:status:ok", state: "unbound", groups: [] }
52069
+ };
52070
+ }
52071
+ if (cloudReachable && !cloudBinding) {
52072
+ deps.store.remove(personaId2);
52073
+ return {
52074
+ response: { type: "larkBot:status:ok", state: "unbound", groups: [] }
52075
+ };
52076
+ }
51888
52077
  const broken = cloudBinding?.status === "broken";
51889
52078
  const result = {
51890
52079
  state: broken ? "broken" : "bound",
@@ -51896,6 +52085,8 @@ function buildLarkBotHandlers(deps) {
51896
52085
  return { response: { type: "larkBot:status:ok", ...result } };
51897
52086
  };
51898
52087
  return {
52088
+ "larkBot:provision": provision,
52089
+ "larkBot:provisionCancel": provisionCancel,
51899
52090
  "larkBot:bindManual": bindManual,
51900
52091
  "larkBot:unbind": unbind,
51901
52092
  "larkBot:status": status
@@ -52014,6 +52205,30 @@ function createLarkBotCloudClient(opts) {
52014
52205
  async updateTunnelUrl(tunnelUrl) {
52015
52206
  await request("PATCH", "/api/lark-bot/tunnel", { tunnelUrl });
52016
52207
  },
52208
+ async provisionBegin(args) {
52209
+ const json = await request("POST", "/api/lark-bot/provision", { ...args });
52210
+ return { provisionId: String(json.provisionId), qrUrl: String(json.qrUrl) };
52211
+ },
52212
+ async provisionStatus(provisionId) {
52213
+ const json = await request("GET", `/api/lark-bot/provision/${encodeURIComponent(provisionId)}`);
52214
+ if (json.state === "success") {
52215
+ return { state: "success", appId: String(json.appId), botName: String(json.botName) };
52216
+ }
52217
+ if (json.state === "error") {
52218
+ return { state: "error", reason: String(json.reason ?? "internal_error") };
52219
+ }
52220
+ return { state: "pending" };
52221
+ },
52222
+ async provisionCancel(provisionId) {
52223
+ const json = await request(
52224
+ "DELETE",
52225
+ `/api/lark-bot/provision/${encodeURIComponent(provisionId)}`
52226
+ );
52227
+ if (json.state === "success") {
52228
+ return { state: "success", appId: String(json.appId), botName: String(json.botName) };
52229
+ }
52230
+ return { state: "cancelled" };
52231
+ },
52017
52232
  async reply(args) {
52018
52233
  await request("POST", "/api/lark-bot/reply", { ...args });
52019
52234
  },
@@ -58911,6 +59126,8 @@ var METHOD_GRANT_MAP = {
58911
59126
  "shift-internal:update": ADMIN_ANY,
58912
59127
  "shift-internal:history": ADMIN_ANY,
58913
59128
  // ---- larkBot:* (LarkBotConnector 绑定/状态,owner-only) ----
59129
+ "larkBot:provision": ADMIN_ANY,
59130
+ "larkBot:provisionCancel": ADMIN_ANY,
58914
59131
  "larkBot:bindManual": ADMIN_ANY,
58915
59132
  "larkBot:unbind": ADMIN_ANY,
58916
59133
  "larkBot:status": ADMIN_ANY,
@@ -59045,7 +59262,7 @@ function computeMethodAccess(args) {
59045
59262
  }
59046
59263
 
59047
59264
  // src/version.ts
59048
- var version = "0.2.300".length > 0 ? "0.2.300" : "dev";
59265
+ var version = "0.2.302".length > 0 ? "0.2.302" : "dev";
59049
59266
 
59050
59267
  // src/cli-probe/probe.ts
59051
59268
  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": {