vibe-coding-master 0.4.31 → 0.4.33

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/README.md CHANGED
@@ -221,7 +221,7 @@ The left sidebar is intentionally compact and collapsible:
221
221
  - `Settings`: `Theme`, `Flow pause alert`, `Try alert`, `Messages`, and `Events`.
222
222
  - `Translation`: global conversation translation, auto-send, target language, output scope, file translation, bootstrap, memory update, session status, and Translator session access.
223
223
  - `Gate Review Gates`: global gate switches for architecture plan, validation adequacy, and final diff.
224
- - `Gateway`: Weixin iLink or Lark binding, Gateway on/off, Gateway translation, QR login, and Lark pairing.
224
+ - `Gateway`: Weixin iLink or Lark binding, Gateway on/off, Gateway translation, QR login/setup, and Lark pairing.
225
225
  - `VCM Harness`: fixed-install status, bootstrap completion checks, and the bootstrap terminal when one is running.
226
226
  - `New Task`: one `task name` input.
227
227
  - `Tasks`: task list and task status.
@@ -275,14 +275,15 @@ The `Gateway` toggle is disabled until a QR login has produced a usable iLink to
275
275
 
276
276
  ### Bind Lark
277
277
 
278
- 1. Create a Lark app with bot capability, message receive events, and WebSocket event delivery.
279
- 2. Open the sidebar `Gateway` section and select `Lark`.
280
- 3. Save the Lark App ID and App Secret.
281
- 4. Click `Create Pairing Code`.
282
- 5. Send `/bind CODE` to the Lark bot before the code expires.
283
- 6. After binding succeeds, turn `Gateway` on in the sidebar.
278
+ 1. Open the sidebar `Gateway` section and select `Lark`.
279
+ 2. Click `Start QR Setup`.
280
+ 3. Scan the QR code with Lark and approve bot creation.
281
+ 4. Click `Confirm` in the setup dialog.
282
+ 5. Click `Create Pairing Code`.
283
+ 6. Send `/bind CODE` to the Lark bot before the code expires.
284
+ 7. After binding succeeds, turn `Gateway` on in the sidebar.
284
285
 
285
- Lark credentials stay in local VCM state. The App Secret is not shown again in the UI. `Reset Binding` clears the bound Lark user/chat state while keeping the saved Lark app credentials.
286
+ Lark QR setup creates/configures the bot app and stores the resulting App ID/App Secret in local VCM state. The App Secret is not shown in the UI. `Reset Binding` clears the bound Lark user/chat state while keeping the saved Lark app credentials.
286
287
 
287
288
  When Gateway is turned on, VCM automatically turns off the browser `Flow pause alert` and disables `Try alert`. Gateway becomes the notification path, so the browser should not show blocking flow-pause dialogs while the user is managing the task from the phone.
288
289
 
@@ -377,7 +378,7 @@ Typical mobile flow:
377
378
  - If the QR dialog does not appear, refresh the page and click `Start QR Login` again.
378
379
  - If the QR status stays `wait`, confirm the login on the phone and click `Confirm` again.
379
380
  - If the QR code expires, start a new QR login.
380
- - If `Gateway` cannot be enabled, bind Weixin or pair Lark first.
381
+ - If `Gateway` cannot be enabled, bind Weixin or complete Lark QR setup and pairing first.
381
382
  - If `/start` or read-only commands do not receive replies, check that the selected channel is connected and the message comes from the bound identity.
382
383
  - If PM messages or task-changing commands are rejected, check that Gateway is on.
383
384
  - If plain text cannot be sent to PM, select a project and task first, and make sure the task's PM session is running and idle.
@@ -14,6 +14,12 @@ export function registerGatewayRoutes(app, deps) {
14
14
  app.post("/api/gateway/qr/check", async (request) => {
15
15
  return deps.gatewayService.checkQrLogin(request.body);
16
16
  });
17
+ app.post("/api/gateway/lark-registration/start", async () => {
18
+ return deps.gatewayService.startLarkRegistration();
19
+ });
20
+ app.post("/api/gateway/lark-registration/check", async () => {
21
+ return deps.gatewayService.checkLarkRegistration();
22
+ });
17
23
  app.post("/api/gateway/binding/reset", async () => {
18
24
  return deps.gatewayService.resetBinding();
19
25
  });
@@ -4,7 +4,7 @@ const DEFAULT_LONG_POLL_TIMEOUT_MS = 35_000;
4
4
  export function createLarkChannel(options = {}) {
5
5
  let connection = null;
6
6
  async function ensureConnection(input) {
7
- const key = `${input.appId}:${input.appSecret}`;
7
+ const key = `${input.domain}:${input.appId}:${input.appSecret}`;
8
8
  if (connection?.key === key) {
9
9
  return connection;
10
10
  }
@@ -24,7 +24,7 @@ export function createLarkChannel(options = {}) {
24
24
  const client = new Lark.Client({
25
25
  appId: input.appId,
26
26
  appSecret: input.appSecret,
27
- domain: options.domain ?? "lark"
27
+ domain: input.domain
28
28
  });
29
29
  const dispatcher = new Lark.EventDispatcher({}).register({
30
30
  "im.message.receive_v1": async (data) => {
@@ -40,7 +40,7 @@ export function createLarkChannel(options = {}) {
40
40
  const wsClient = new Lark.WSClient({
41
41
  appId: input.appId,
42
42
  appSecret: input.appSecret,
43
- domain: options.domain ?? "lark",
43
+ domain: input.domain,
44
44
  source: "vcm-gateway",
45
45
  autoReconnect: true,
46
46
  loggerLevel: options.loggerLevel,
@@ -75,10 +75,11 @@ export function createLarkChannel(options = {}) {
75
75
  async getUpdates(input) {
76
76
  const appId = input.account.appId?.trim();
77
77
  const appSecret = input.account.appSecret?.trim();
78
+ const domain = input.account.larkDomain ?? options.domain ?? "lark";
78
79
  if (!appId || !appSecret) {
79
80
  throw new Error("Lark App ID and App Secret are required.");
80
81
  }
81
- const state = await ensureConnection({ appId, appSecret });
82
+ const state = await ensureConnection({ appId, appSecret, domain });
82
83
  await state.ready;
83
84
  if (state.error) {
84
85
  throw state.error;
@@ -97,10 +98,11 @@ export function createLarkChannel(options = {}) {
97
98
  async sendText(input) {
98
99
  const appId = input.account.appId?.trim();
99
100
  const appSecret = input.account.appSecret?.trim();
101
+ const domain = input.account.larkDomain ?? options.domain ?? "lark";
100
102
  if (!appId || !appSecret) {
101
103
  throw new Error("Lark App ID and App Secret are required.");
102
104
  }
103
- const state = await ensureConnection({ appId, appSecret });
105
+ const state = await ensureConnection({ appId, appSecret, domain });
104
106
  await state.ready;
105
107
  if (!state.client) {
106
108
  throw new Error("Lark client is not initialized.");
@@ -0,0 +1,194 @@
1
+ const ACCOUNTS_BASE_URLS = {
2
+ feishu: "https://accounts.feishu.cn",
3
+ lark: "https://accounts.larksuite.com"
4
+ };
5
+ const OPEN_BASE_URLS = {
6
+ feishu: "https://open.feishu.cn",
7
+ lark: "https://open.larksuite.com"
8
+ };
9
+ const REGISTRATION_PATH = "/oauth/v1/app/registration";
10
+ const REQUEST_TIMEOUT_MS = 10_000;
11
+ export function createLarkRegistrationClient(deps = {}) {
12
+ const fetchImpl = deps.fetch ?? fetch;
13
+ async function postRegistration(domain, body) {
14
+ const url = `${ACCOUNTS_BASE_URLS[domain]}${REGISTRATION_PATH}`;
15
+ const payload = await fetchJson(fetchImpl, url, {
16
+ method: "POST",
17
+ headers: {
18
+ "content-type": "application/x-www-form-urlencoded"
19
+ },
20
+ body: new URLSearchParams(body)
21
+ });
22
+ return payload;
23
+ }
24
+ return {
25
+ async init(domain) {
26
+ const payload = await postRegistration(domain, { action: "init" });
27
+ const methods = Array.isArray(payload.supported_auth_methods)
28
+ ? payload.supported_auth_methods.filter((value) => typeof value === "string")
29
+ : [];
30
+ if (!methods.includes("client_secret")) {
31
+ throw new Error(`Lark QR setup does not support client_secret auth. Supported methods: ${methods.join(", ") || "none"}.`);
32
+ }
33
+ },
34
+ async begin(domain) {
35
+ const payload = await postRegistration(domain, {
36
+ action: "begin",
37
+ archetype: "PersonalAgent",
38
+ auth_method: "client_secret",
39
+ request_user_info: "open_id"
40
+ });
41
+ const deviceCode = stringOrNull(payload.device_code);
42
+ if (!deviceCode) {
43
+ throw new Error("Lark QR setup did not return a device_code.");
44
+ }
45
+ const qrUrl = appendQrTrackingParams(stringOrNull(payload.verification_uri_complete) ?? "");
46
+ if (!qrUrl) {
47
+ throw new Error("Lark QR setup did not return a QR URL.");
48
+ }
49
+ return {
50
+ domain,
51
+ deviceCode,
52
+ qrUrl,
53
+ userCode: stringOrNull(payload.user_code),
54
+ intervalSeconds: positiveNumberOr(payload.interval, 5),
55
+ expiresInSeconds: positiveNumberOr(payload.expire_in, 600)
56
+ };
57
+ },
58
+ async poll(input) {
59
+ const payload = await postRegistration(input.domain, {
60
+ action: "poll",
61
+ device_code: input.deviceCode,
62
+ tp: "ob_app"
63
+ });
64
+ const userInfo = isObject(payload.user_info) ? payload.user_info : {};
65
+ const tenantBrand = stringOrNull(userInfo.tenant_brand);
66
+ const domain = tenantBrand === "lark" ? "lark" : input.domain;
67
+ const appId = stringOrNull(payload.client_id);
68
+ const appSecret = stringOrNull(payload.client_secret);
69
+ if (appId && appSecret) {
70
+ const bot = await probeBot(fetchImpl, {
71
+ appId,
72
+ appSecret,
73
+ domain
74
+ });
75
+ return {
76
+ status: "confirmed",
77
+ appId,
78
+ appSecret,
79
+ domain,
80
+ openId: stringOrNull(userInfo.open_id),
81
+ botName: bot.botName,
82
+ botOpenId: bot.botOpenId
83
+ };
84
+ }
85
+ const error = stringOrNull(payload.error);
86
+ if (error === "expired_token") {
87
+ return { status: "expired", message: "Lark QR setup expired. Start a new setup." };
88
+ }
89
+ if (error === "access_denied") {
90
+ return { status: "failed", message: "Lark QR setup was denied." };
91
+ }
92
+ return {
93
+ status: "wait",
94
+ message: error && error !== "authorization_pending" ? error : undefined
95
+ };
96
+ }
97
+ };
98
+ }
99
+ async function probeBot(fetchImpl, input) {
100
+ try {
101
+ const tokenPayload = await fetchJson(fetchImpl, `${OPEN_BASE_URLS[input.domain]}/open-apis/auth/v3/tenant_access_token/internal`, {
102
+ method: "POST",
103
+ headers: {
104
+ "content-type": "application/json"
105
+ },
106
+ body: JSON.stringify({
107
+ app_id: input.appId,
108
+ app_secret: input.appSecret
109
+ })
110
+ });
111
+ const accessToken = stringOrNull(tokenPayload.tenant_access_token);
112
+ if (!accessToken) {
113
+ return { botName: null, botOpenId: null };
114
+ }
115
+ const botPayload = await fetchJson(fetchImpl, `${OPEN_BASE_URLS[input.domain]}/open-apis/bot/v3/info`, {
116
+ headers: {
117
+ authorization: `Bearer ${accessToken}`,
118
+ "content-type": "application/json"
119
+ }
120
+ });
121
+ if (botPayload.code !== 0) {
122
+ return { botName: null, botOpenId: null };
123
+ }
124
+ const bot = isObject(botPayload.bot)
125
+ ? botPayload.bot
126
+ : isObject(botPayload.data) && isObject(botPayload.data.bot)
127
+ ? botPayload.data.bot
128
+ : {};
129
+ return {
130
+ botName: stringOrNull(bot.app_name) ?? stringOrNull(bot.bot_name),
131
+ botOpenId: stringOrNull(bot.open_id)
132
+ };
133
+ }
134
+ catch {
135
+ return { botName: null, botOpenId: null };
136
+ }
137
+ }
138
+ async function fetchJson(fetchImpl, url, init = {}) {
139
+ const controller = new AbortController();
140
+ const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
141
+ try {
142
+ const response = await fetchImpl(url, {
143
+ ...init,
144
+ signal: controller.signal
145
+ });
146
+ const text = await response.text();
147
+ if (!text) {
148
+ if (!response.ok) {
149
+ throw new Error(`${response.status} ${response.statusText}`);
150
+ }
151
+ return {};
152
+ }
153
+ const payload = JSON.parse(text);
154
+ if (!isObject(payload)) {
155
+ throw new Error("Lark setup response was not a JSON object.");
156
+ }
157
+ if (!response.ok && !payload.error) {
158
+ throw new Error(`${response.status} ${response.statusText}`);
159
+ }
160
+ return payload;
161
+ }
162
+ catch (error) {
163
+ if (error instanceof Error && error.name === "AbortError") {
164
+ throw new Error("Lark QR setup request timed out.");
165
+ }
166
+ throw error;
167
+ }
168
+ finally {
169
+ clearTimeout(timeout);
170
+ }
171
+ }
172
+ function appendQrTrackingParams(value) {
173
+ if (!value) {
174
+ return "";
175
+ }
176
+ try {
177
+ const url = new URL(value);
178
+ url.searchParams.set("from", "vcm");
179
+ url.searchParams.set("tp", "vcm");
180
+ return url.toString();
181
+ }
182
+ catch {
183
+ return `${value}${value.includes("?") ? "&" : "?"}from=vcm&tp=vcm`;
184
+ }
185
+ }
186
+ function positiveNumberOr(input, fallback) {
187
+ return typeof input === "number" && Number.isFinite(input) && input > 0 ? input : fallback;
188
+ }
189
+ function stringOrNull(input) {
190
+ return typeof input === "string" && input.trim() ? input.trim() : null;
191
+ }
192
+ function isObject(input) {
193
+ return Boolean(input && typeof input === "object" && !Array.isArray(input));
194
+ }
@@ -6,6 +6,7 @@ import { submitTerminalInput } from "../runtime/terminal-submit.js";
6
6
  import { getTaskRuntimeRepoRoot } from "../services/task-service.js";
7
7
  import { parseAssistantContent, resolveExistingClaudeTranscriptPath } from "../services/claude-transcript-service.js";
8
8
  import { parseGatewayCommand } from "./gateway-command-parser.js";
9
+ import { createLarkRegistrationClient } from "./channels/lark-registration.js";
9
10
  const QR_LOGIN_TTL_MS = 8 * 60 * 1000;
10
11
  const PAIRING_CODE_TTL_MS = 10 * 60 * 1000;
11
12
  const CLOSE_CONFIRM_TTL_MS = 10 * 60 * 1000;
@@ -24,10 +25,12 @@ const COMMANDS_ALLOWED_WHEN_DISABLED = new Set([
24
25
  ]);
25
26
  export function createGatewayService(deps) {
26
27
  const now = deps.now ?? (() => new Date().toISOString());
28
+ const larkRegistration = deps.larkRegistration ?? createLarkRegistrationClient();
27
29
  let pollAbort = null;
28
30
  let pollLoopPromise = null;
29
31
  let pollStartingPromise = null;
30
32
  let qrLogin = null;
33
+ let larkRegistrationState = null;
31
34
  let lastFailedTranslation = null;
32
35
  function isRunning() {
33
36
  return Boolean(pollAbort && !pollAbort.signal.aborted);
@@ -90,6 +93,7 @@ export function createGatewayService(deps) {
90
93
  token: settings.binding.token,
91
94
  appId: settings.binding.appId,
92
95
  appSecret: settings.binding.appSecret,
96
+ larkDomain: settings.binding.larkDomain,
93
97
  homeChatId: settings.binding.homeChatId
94
98
  };
95
99
  }
@@ -633,7 +637,7 @@ export function createGatewayService(deps) {
633
637
  const settings = await deps.settings.loadSettings();
634
638
  if (!toAccount(settings)) {
635
639
  return settings.channel === "lark"
636
- ? "Gateway is not configured. Save Lark App ID and App Secret in desktop VCM first."
640
+ ? "Gateway is not configured. Complete Lark QR Setup in desktop VCM first."
637
641
  : "Gateway is not bound. Start QR login from desktop VCM first.";
638
642
  }
639
643
  if (settings.enabled) {
@@ -758,7 +762,13 @@ export function createGatewayService(deps) {
758
762
  return deps.settings.expose(settings, isRunning());
759
763
  },
760
764
  async updateSettings(input) {
761
- let settings = await deps.settings.updateSettings(input);
765
+ const updateInput = input.channel && input.baseUrl === undefined
766
+ ? {
767
+ ...input,
768
+ baseUrl: deps.channels.get(input.channel).defaultBaseUrl
769
+ }
770
+ : input;
771
+ let settings = await deps.settings.updateSettings(updateInput);
762
772
  if (settings.enabled) {
763
773
  settings = await syncDesktopContext(settings);
764
774
  }
@@ -773,6 +783,8 @@ export function createGatewayService(deps) {
773
783
  async resetBinding() {
774
784
  await stopPolling();
775
785
  lastFailedTranslation = null;
786
+ qrLogin = null;
787
+ larkRegistrationState = null;
776
788
  const settings = await deps.settings.resetBinding();
777
789
  return deps.settings.expose(settings, isRunning());
778
790
  },
@@ -788,7 +800,7 @@ export function createGatewayService(deps) {
788
800
  if (!settings.binding.appId || !settings.binding.appSecret) {
789
801
  throw new VcmError({
790
802
  code: "GATEWAY_LARK_CONFIG_MISSING",
791
- message: "Save Lark App ID and App Secret before creating a pairing code.",
803
+ message: "Complete Lark QR Setup before creating a pairing code.",
792
804
  statusCode: 409
793
805
  });
794
806
  }
@@ -893,6 +905,95 @@ export function createGatewayService(deps) {
893
905
  loginUserId: result.loginUserId
894
906
  };
895
907
  },
908
+ async startLarkRegistration() {
909
+ await stopPolling();
910
+ const settings = await deps.settings.updateSettings({
911
+ channel: "lark",
912
+ baseUrl: deps.channels.get("lark").defaultBaseUrl
913
+ });
914
+ const domain = "lark";
915
+ await larkRegistration.init(domain);
916
+ const begin = await larkRegistration.begin(domain);
917
+ larkRegistrationState = {
918
+ domain: begin.domain,
919
+ deviceCode: begin.deviceCode,
920
+ qrUrl: begin.qrUrl,
921
+ userCode: begin.userCode,
922
+ intervalSeconds: begin.intervalSeconds,
923
+ expiresAt: new Date(Date.now() + begin.expiresInSeconds * 1000).toISOString()
924
+ };
925
+ return {
926
+ status: "wait",
927
+ qrUrl: begin.qrUrl,
928
+ userCode: begin.userCode,
929
+ intervalSeconds: begin.intervalSeconds,
930
+ expiresAt: larkRegistrationState.expiresAt
931
+ };
932
+ },
933
+ async checkLarkRegistration() {
934
+ if (!larkRegistrationState || Date.parse(larkRegistrationState.expiresAt) < Date.now()) {
935
+ larkRegistrationState = null;
936
+ return {
937
+ status: "expired",
938
+ message: "Lark QR setup expired. Start a new setup."
939
+ };
940
+ }
941
+ const result = await larkRegistration.poll({
942
+ domain: larkRegistrationState.domain,
943
+ deviceCode: larkRegistrationState.deviceCode
944
+ });
945
+ if (result.status !== "confirmed") {
946
+ if (result.status === "expired" || result.status === "failed") {
947
+ larkRegistrationState = null;
948
+ }
949
+ return {
950
+ status: result.status,
951
+ message: result.message
952
+ };
953
+ }
954
+ if (!result.appId || !result.appSecret || !result.domain) {
955
+ larkRegistrationState = null;
956
+ return {
957
+ status: "failed",
958
+ message: "Lark QR setup completed without app credentials."
959
+ };
960
+ }
961
+ const current = await deps.settings.loadSettings();
962
+ const settings = await deps.settings.saveSettings({
963
+ ...current,
964
+ channel: "lark",
965
+ binding: {
966
+ ...current.binding,
967
+ appId: result.appId,
968
+ appSecret: result.appSecret,
969
+ larkDomain: result.domain,
970
+ larkOpenId: result.openId ?? null,
971
+ larkBotName: result.botName ?? null,
972
+ larkBotOpenId: result.botOpenId ?? null,
973
+ pairingCode: null,
974
+ pairingCodeExpiresAt: null,
975
+ getUpdatesBuf: ""
976
+ },
977
+ lastPollStatus: {
978
+ state: "idle",
979
+ checkedAt: now()
980
+ },
981
+ updatedAt: now()
982
+ });
983
+ larkRegistrationState = null;
984
+ await ensurePolling();
985
+ const status = deps.settings.expose(settings, isRunning());
986
+ return {
987
+ status: "confirmed",
988
+ appIdConfigured: Boolean(result.appId),
989
+ appSecretConfigured: Boolean(result.appSecret),
990
+ larkDomain: result.domain,
991
+ larkOpenId: result.openId ?? null,
992
+ larkBotName: result.botName ?? null,
993
+ larkBotOpenId: result.botOpenId ?? null,
994
+ gatewayStatus: status
995
+ };
996
+ },
896
997
  async handlePmStop(input) {
897
998
  if (input.session.role !== "project-manager") {
898
999
  return;
@@ -45,10 +45,7 @@ export function createGatewaySettingsService(deps) {
45
45
  ...current.binding,
46
46
  baseUrl: input.baseUrl !== undefined
47
47
  ? normalizeBaseUrl(input.baseUrl, normalizeOptions.defaultBaseUrl)
48
- : current.binding.baseUrl,
49
- appId: input.larkAppId !== undefined ? normalizeNullableString(input.larkAppId) : current.binding.appId,
50
- appSecret: input.larkAppSecret !== undefined ? normalizeNullableString(input.larkAppSecret) : current.binding.appSecret,
51
- homeChatId: input.larkHomeChatId !== undefined ? normalizeNullableString(input.larkHomeChatId) : current.binding.homeChatId
48
+ : current.binding.baseUrl
52
49
  },
53
50
  updatedAt: now()
54
51
  });
@@ -64,6 +61,10 @@ export function createGatewaySettingsService(deps) {
64
61
  baseUrl: current.binding.baseUrl || normalizeOptions.defaultBaseUrl,
65
62
  appId: current.binding.appId,
66
63
  appSecret: current.binding.appSecret,
64
+ larkDomain: current.binding.larkDomain,
65
+ larkOpenId: current.binding.larkOpenId,
66
+ larkBotName: current.binding.larkBotName,
67
+ larkBotOpenId: current.binding.larkBotOpenId,
67
68
  homeChatId: current.binding.homeChatId
68
69
  },
69
70
  dedupe: {
@@ -96,6 +97,10 @@ export function createGatewaySettingsService(deps) {
96
97
  appId: settings.binding.appId,
97
98
  appIdConfigured: Boolean(settings.binding.appId),
98
99
  appSecretConfigured: Boolean(settings.binding.appSecret),
100
+ larkDomain: settings.binding.larkDomain,
101
+ larkOpenId: settings.binding.larkOpenId,
102
+ larkBotName: settings.binding.larkBotName,
103
+ larkBotOpenId: settings.binding.larkBotOpenId,
99
104
  homeChatId: settings.binding.homeChatId,
100
105
  pairingCodeExpiresAt: settings.binding.pairingCodeExpiresAt
101
106
  },
@@ -142,6 +147,10 @@ export function normalizeSettings(input, timestamp, options = {
142
147
  token: normalizeNullableString(bindingInput.token),
143
148
  appId: normalizeNullableString(bindingInput.appId),
144
149
  appSecret: normalizeNullableString(bindingInput.appSecret),
150
+ larkDomain: normalizeLarkDomain(bindingInput.larkDomain),
151
+ larkOpenId: normalizeNullableString(bindingInput.larkOpenId),
152
+ larkBotName: normalizeNullableString(bindingInput.larkBotName),
153
+ larkBotOpenId: normalizeNullableString(bindingInput.larkBotOpenId),
145
154
  homeChatId: normalizeNullableString(bindingInput.homeChatId),
146
155
  pairingCode: normalizeNullableString(bindingInput.pairingCode),
147
156
  pairingCodeExpiresAt: normalizeNullableString(bindingInput.pairingCodeExpiresAt),
@@ -177,6 +186,10 @@ function createDefaultBinding(defaultBaseUrl = DEFAULT_BASE_URL) {
177
186
  token: null,
178
187
  appId: null,
179
188
  appSecret: null,
189
+ larkDomain: null,
190
+ larkOpenId: null,
191
+ larkBotName: null,
192
+ larkBotOpenId: null,
180
193
  homeChatId: null,
181
194
  pairingCode: null,
182
195
  pairingCodeExpiresAt: null,
@@ -188,6 +201,9 @@ function createDefaultBinding(defaultBaseUrl = DEFAULT_BASE_URL) {
188
201
  function normalizeGatewayChannel(input, fallback) {
189
202
  return input === "weixin-ilink" || input === "lark" ? input : fallback;
190
203
  }
204
+ function normalizeLarkDomain(input) {
205
+ return input === "lark" || input === "feishu" ? input : null;
206
+ }
191
207
  function normalizeCloseTaskConfirmation(input) {
192
208
  if (!isObject(input)) {
193
209
  return null;