@dsh-enhanced/lark-channel 0.1.48 → 0.1.53
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 +20 -8
- package/docs/rsi-setup.md +145 -12
- package/docs/setup.md +29 -14
- package/docs/supervised-growth.md +2 -0
- package/lib/adapter.d.ts +6 -0
- package/lib/adapter.d.ts.map +1 -1
- package/lib/adapter.js +79 -8
- package/lib/adapter.js.map +1 -1
- package/lib/business-cli-install.d.ts +9 -0
- package/lib/business-cli-install.d.ts.map +1 -0
- package/lib/business-cli-install.js +367 -0
- package/lib/business-cli-install.js.map +1 -0
- package/lib/business-environment.d.ts +2 -0
- package/lib/business-environment.d.ts.map +1 -0
- package/lib/business-environment.js +16 -0
- package/lib/business-environment.js.map +1 -0
- package/lib/business-skill.d.ts +32 -0
- package/lib/business-skill.d.ts.map +1 -0
- package/lib/business-skill.js +202 -0
- package/lib/business-skill.js.map +1 -0
- package/lib/launchd-control.d.ts +24 -0
- package/lib/launchd-control.d.ts.map +1 -0
- package/lib/launchd-control.js +68 -0
- package/lib/launchd-control.js.map +1 -0
- package/lib/launchd-identity.d.ts +34 -0
- package/lib/launchd-identity.d.ts.map +1 -0
- package/lib/launchd-identity.js +184 -0
- package/lib/launchd-identity.js.map +1 -0
- package/lib/launchd-target-lock.d.ts +6 -0
- package/lib/launchd-target-lock.d.ts.map +1 -0
- package/lib/launchd-target-lock.js +48 -0
- package/lib/launchd-target-lock.js.map +1 -0
- package/lib/launchd.d.ts +18 -0
- package/lib/launchd.d.ts.map +1 -1
- package/lib/launchd.js +211 -54
- package/lib/launchd.js.map +1 -1
- package/lib/rsi-authority-config.d.ts +29 -0
- package/lib/rsi-authority-config.d.ts.map +1 -0
- package/lib/rsi-authority-config.js +233 -0
- package/lib/rsi-authority-config.js.map +1 -0
- package/lib/rsi-authority-resources.d.ts +36 -0
- package/lib/rsi-authority-resources.d.ts.map +1 -0
- package/lib/rsi-authority-resources.js +139 -0
- package/lib/rsi-authority-resources.js.map +1 -0
- package/lib/rsi-authority-runtime.d.ts +50 -0
- package/lib/rsi-authority-runtime.d.ts.map +1 -0
- package/lib/rsi-authority-runtime.js +393 -0
- package/lib/rsi-authority-runtime.js.map +1 -0
- package/lib/rsi-bootstrap-manifest.d.ts +39 -0
- package/lib/rsi-bootstrap-manifest.d.ts.map +1 -0
- package/lib/rsi-bootstrap-manifest.js +345 -0
- package/lib/rsi-bootstrap-manifest.js.map +1 -0
- package/lib/rsi-bootstrap.d.ts +30 -0
- package/lib/rsi-bootstrap.d.ts.map +1 -0
- package/lib/rsi-bootstrap.js +363 -0
- package/lib/rsi-bootstrap.js.map +1 -0
- package/lib/rsi-build.d.ts +54 -0
- package/lib/rsi-build.d.ts.map +1 -0
- package/lib/rsi-build.js +478 -0
- package/lib/rsi-build.js.map +1 -0
- package/lib/rsi-creation-build.d.ts +48 -0
- package/lib/rsi-creation-build.d.ts.map +1 -0
- package/lib/rsi-creation-build.js +363 -0
- package/lib/rsi-creation-build.js.map +1 -0
- package/lib/rsi-host-authorities.d.ts +18 -0
- package/lib/rsi-host-authorities.d.ts.map +1 -0
- package/lib/rsi-host-authorities.js +129 -0
- package/lib/rsi-host-authorities.js.map +1 -0
- package/lib/rsi-host-update.d.ts +113 -0
- package/lib/rsi-host-update.d.ts.map +1 -0
- package/lib/rsi-host-update.js +316 -0
- package/lib/rsi-host-update.js.map +1 -0
- package/lib/rsi-install-inputs.d.ts +43 -0
- package/lib/rsi-install-inputs.d.ts.map +1 -0
- package/lib/rsi-install-inputs.js +342 -0
- package/lib/rsi-install-inputs.js.map +1 -0
- package/lib/rsi-install.d.ts +54 -0
- package/lib/rsi-install.d.ts.map +1 -0
- package/lib/rsi-install.js +404 -0
- package/lib/rsi-install.js.map +1 -0
- package/lib/rsi-local-activation.d.ts +78 -0
- package/lib/rsi-local-activation.d.ts.map +1 -0
- package/lib/rsi-local-activation.js +485 -0
- package/lib/rsi-local-activation.js.map +1 -0
- package/lib/rsi-local-cohort.d.ts +70 -0
- package/lib/rsi-local-cohort.d.ts.map +1 -0
- package/lib/rsi-local-cohort.js +781 -0
- package/lib/rsi-local-cohort.js.map +1 -0
- package/lib/rsi-local-install.d.ts +68 -0
- package/lib/rsi-local-install.d.ts.map +1 -0
- package/lib/rsi-local-install.js +322 -0
- package/lib/rsi-local-install.js.map +1 -0
- package/lib/rsi-local-pair-install.d.ts +38 -0
- package/lib/rsi-local-pair-install.d.ts.map +1 -0
- package/lib/rsi-local-pair-install.js +200 -0
- package/lib/rsi-local-pair-install.js.map +1 -0
- package/lib/rsi-local-profile-update.d.ts +25 -0
- package/lib/rsi-local-profile-update.d.ts.map +1 -0
- package/lib/rsi-local-profile-update.js +226 -0
- package/lib/rsi-local-profile-update.js.map +1 -0
- package/lib/rsi-local-resources.d.ts +37 -0
- package/lib/rsi-local-resources.d.ts.map +1 -0
- package/lib/rsi-local-resources.js +184 -0
- package/lib/rsi-local-resources.js.map +1 -0
- package/lib/rsi-local-roots-extension.d.ts +11 -0
- package/lib/rsi-local-roots-extension.d.ts.map +1 -0
- package/lib/rsi-local-roots-extension.js +31 -0
- package/lib/rsi-local-roots-extension.js.map +1 -0
- package/lib/rsi-local-update-build.d.ts +18 -0
- package/lib/rsi-local-update-build.d.ts.map +1 -0
- package/lib/rsi-local-update-build.js +37 -0
- package/lib/rsi-local-update-build.js.map +1 -0
- package/lib/rsi-local-update.d.ts +39 -0
- package/lib/rsi-local-update.d.ts.map +1 -0
- package/lib/rsi-local-update.js +205 -0
- package/lib/rsi-local-update.js.map +1 -0
- package/lib/rsi-memory-learning.d.ts +30 -0
- package/lib/rsi-memory-learning.d.ts.map +1 -0
- package/lib/rsi-memory-learning.js +104 -0
- package/lib/rsi-memory-learning.js.map +1 -0
- package/lib/rsi-native-install.d.ts +38 -0
- package/lib/rsi-native-install.d.ts.map +1 -0
- package/lib/rsi-native-install.js +254 -0
- package/lib/rsi-native-install.js.map +1 -0
- package/lib/rsi-native-launchd.d.ts +7 -0
- package/lib/rsi-native-launchd.d.ts.map +1 -0
- package/lib/rsi-native-launchd.js +49 -0
- package/lib/rsi-native-launchd.js.map +1 -0
- package/lib/rsi-native-profile.d.ts +64 -0
- package/lib/rsi-native-profile.d.ts.map +1 -0
- package/lib/rsi-native-profile.js +283 -0
- package/lib/rsi-native-profile.js.map +1 -0
- package/lib/rsi-native-runtime-bootstrap.d.ts +35 -0
- package/lib/rsi-native-runtime-bootstrap.d.ts.map +1 -0
- package/lib/rsi-native-runtime-bootstrap.js +385 -0
- package/lib/rsi-native-runtime-bootstrap.js.map +1 -0
- package/lib/rsi-owner-profile.d.ts +37 -0
- package/lib/rsi-owner-profile.d.ts.map +1 -0
- package/lib/rsi-owner-profile.js +178 -0
- package/lib/rsi-owner-profile.js.map +1 -0
- package/lib/rsi-plugin-creation.d.ts +32 -0
- package/lib/rsi-plugin-creation.d.ts.map +1 -0
- package/lib/rsi-plugin-creation.js +189 -0
- package/lib/rsi-plugin-creation.js.map +1 -0
- package/lib/rsi-profile.d.ts +23 -0
- package/lib/rsi-profile.d.ts.map +1 -1
- package/lib/rsi-profile.js +154 -43
- package/lib/rsi-profile.js.map +1 -1
- package/lib/rsi-release-build.d.ts +41 -0
- package/lib/rsi-release-build.d.ts.map +1 -0
- package/lib/rsi-release-build.js +223 -0
- package/lib/rsi-release-build.js.map +1 -0
- package/lib/rsi-service-environment.d.ts +6 -0
- package/lib/rsi-service-environment.d.ts.map +1 -0
- package/lib/rsi-service-environment.js +113 -0
- package/lib/rsi-service-environment.js.map +1 -0
- package/lib/rsi-service-setup.d.ts +10 -0
- package/lib/rsi-service-setup.d.ts.map +1 -0
- package/lib/rsi-service-setup.js +34 -0
- package/lib/rsi-service-setup.js.map +1 -0
- package/lib/rsi-setup.d.ts +35 -2
- package/lib/rsi-setup.d.ts.map +1 -1
- package/lib/rsi-setup.js +475 -32
- package/lib/rsi-setup.js.map +1 -1
- package/lib/rsi-source-maintenance.d.ts +25 -0
- package/lib/rsi-source-maintenance.d.ts.map +1 -0
- package/lib/rsi-source-maintenance.js +348 -0
- package/lib/rsi-source-maintenance.js.map +1 -0
- package/lib/rsi-source-update.d.ts +26 -0
- package/lib/rsi-source-update.d.ts.map +1 -0
- package/lib/rsi-source-update.js +276 -0
- package/lib/rsi-source-update.js.map +1 -0
- package/lib/rsi-source.d.ts +34 -0
- package/lib/rsi-source.d.ts.map +1 -0
- package/lib/rsi-source.js +487 -0
- package/lib/rsi-source.js.map +1 -0
- package/lib/rsi-systemd-bootstrap.d.ts +46 -0
- package/lib/rsi-systemd-bootstrap.d.ts.map +1 -0
- package/lib/rsi-systemd-bootstrap.js +271 -0
- package/lib/rsi-systemd-bootstrap.js.map +1 -0
- package/lib/sdk.d.ts.map +1 -1
- package/lib/sdk.js +26 -1
- package/lib/sdk.js.map +1 -1
- package/lib/setup-business.d.ts +49 -0
- package/lib/setup-business.d.ts.map +1 -0
- package/lib/setup-business.js +335 -0
- package/lib/setup-business.js.map +1 -0
- package/lib/setup-profile.d.ts +1 -1
- package/lib/setup-profile.d.ts.map +1 -1
- package/lib/setup-profile.js +25 -2
- package/lib/setup-profile.js.map +1 -1
- package/lib/setup.d.ts +12 -1
- package/lib/setup.d.ts.map +1 -1
- package/lib/setup.js +265 -48
- package/lib/setup.js.map +1 -1
- package/lib/systemd.d.ts +10 -0
- package/lib/systemd.d.ts.map +1 -1
- package/lib/systemd.js +23 -6
- package/lib/systemd.js.map +1 -1
- package/lib/types.d.ts +7 -0
- package/lib/types.d.ts.map +1 -1
- package/lib/types.js.map +1 -1
- package/lib/version.d.ts +1 -1
- package/lib/version.js +1 -1
- package/package.json +33 -20
package/lib/setup.js
CHANGED
|
@@ -1,11 +1,20 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
|
|
3
|
+
if (typeof path === "string" && /^\.\.?\//.test(path)) {
|
|
4
|
+
return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
|
|
5
|
+
return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
|
|
6
|
+
});
|
|
7
|
+
}
|
|
8
|
+
return path;
|
|
9
|
+
};
|
|
2
10
|
import { createHash, randomBytes } from 'node:crypto';
|
|
3
11
|
import { accessSync, constants, realpathSync } from 'node:fs';
|
|
4
12
|
import { chmod, lstat, mkdir, open, readFile, readdir, rename, rm, unlink } from 'node:fs/promises';
|
|
5
13
|
import { homedir } from 'node:os';
|
|
6
|
-
import {
|
|
14
|
+
import { createRequire } from 'node:module';
|
|
15
|
+
import { basename, delimiter, dirname, isAbsolute, join, normalize, win32 } from 'node:path';
|
|
7
16
|
import { DatabaseSync } from 'node:sqlite';
|
|
8
|
-
import { fileURLToPath } from 'node:url';
|
|
17
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
9
18
|
import { createInterface } from 'node:readline/promises';
|
|
10
19
|
import { spawnSync } from 'node:child_process';
|
|
11
20
|
import { registerApp } from '@larksuiteoapi/node-sdk';
|
|
@@ -15,6 +24,9 @@ import { createOfficialLarkTransport } from './sdk.js';
|
|
|
15
24
|
import { installDshResidentService } from './resident.js';
|
|
16
25
|
import { prepareDshSystemdUserService } from './systemd.js';
|
|
17
26
|
import { configureLarkProfilePatch, refreshLarkAgentPolicyPatch } from './setup-profile.js';
|
|
27
|
+
import { ensureLarkBusinessCli } from './business-cli-install.js';
|
|
28
|
+
import { setupLarkBusinessTools } from './setup-business.js';
|
|
29
|
+
import { reconcileLarkBusinessSkill } from './business-skill.js';
|
|
18
30
|
import { assertRawManagedProfileIntegrity, materializeManagedProfileOverride, } from './setup-materialization.js';
|
|
19
31
|
const setupKeyPattern = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/u;
|
|
20
32
|
const keychainLocatorPattern = /^[A-Za-z0-9][A-Za-z0-9/._-]{0,255}$/u;
|
|
@@ -33,13 +45,14 @@ export function parseLarkSetupArgs(argv) {
|
|
|
33
45
|
tenant: 'personal',
|
|
34
46
|
createApp: false,
|
|
35
47
|
calendarReadonly: false,
|
|
48
|
+
businessTools: true,
|
|
36
49
|
appName: 'DSH Personal Assistant',
|
|
37
50
|
timeoutMs: 300_000,
|
|
38
51
|
installServiceOnly: false,
|
|
39
52
|
refreshAgentPolicy: false,
|
|
40
53
|
manageService: true,
|
|
41
54
|
linuxCredentialProvider: 'auto',
|
|
42
|
-
agentTools: '
|
|
55
|
+
agentTools: 'auto',
|
|
43
56
|
help: false,
|
|
44
57
|
};
|
|
45
58
|
for (let index = 0; index < argv.length; index += 1) {
|
|
@@ -66,6 +79,8 @@ export function parseLarkSetupArgs(argv) {
|
|
|
66
79
|
result.createApp = true;
|
|
67
80
|
else if (option === '--calendar-readonly')
|
|
68
81
|
result.calendarReadonly = true;
|
|
82
|
+
else if (option === '--no-business-tools')
|
|
83
|
+
result.businessTools = false;
|
|
69
84
|
else if (option === '--install-service')
|
|
70
85
|
result.installServiceOnly = true;
|
|
71
86
|
else if (option === '--refresh-agent-policy')
|
|
@@ -75,11 +90,11 @@ export function parseLarkSetupArgs(argv) {
|
|
|
75
90
|
else if (option === '--linux-credential-provider') {
|
|
76
91
|
result.linuxCredentialProvider = argumentValue(argv, index++, option);
|
|
77
92
|
}
|
|
78
|
-
else if (option === '--allow-agent-tools' || option === '--disable-agent-tools') {
|
|
79
|
-
if (result.agentTools !== '
|
|
80
|
-
throw new Error('lark-channel setup: --allow-agent-tools and --
|
|
93
|
+
else if (option === '--allow-agent-tools' || option === '--disable-agent-tools' || option === '--preserve-agent-tools') {
|
|
94
|
+
if (result.agentTools !== 'auto') {
|
|
95
|
+
throw new Error('lark-channel setup: --allow-agent-tools, --disable-agent-tools and --preserve-agent-tools are mutually exclusive');
|
|
81
96
|
}
|
|
82
|
-
result.agentTools = option === '--allow-agent-tools' ? 'enable' : 'disable';
|
|
97
|
+
result.agentTools = option === '--allow-agent-tools' ? 'enable' : option === '--disable-agent-tools' ? 'disable' : 'preserve';
|
|
83
98
|
}
|
|
84
99
|
else if (option === '--app-name')
|
|
85
100
|
result.appName = argumentValue(argv, index++, option);
|
|
@@ -100,12 +115,12 @@ export function parseLarkSetupArgs(argv) {
|
|
|
100
115
|
if (!result.help && result.refreshAgentPolicy) {
|
|
101
116
|
const incompatible = [
|
|
102
117
|
'--domain', '--tenant', '--app-id', '--create-app', '--install-service',
|
|
103
|
-
'--no-service', '--linux-credential-provider', '--app-name', '--timeout-ms',
|
|
118
|
+
'--no-service', '--linux-credential-provider', '--app-name', '--timeout-ms', '--no-business-tools',
|
|
104
119
|
].find(option => argv.includes(option));
|
|
105
120
|
if (incompatible !== undefined) {
|
|
106
121
|
throw new Error(`lark-channel setup: --refresh-agent-policy cannot be combined with ${incompatible}`);
|
|
107
122
|
}
|
|
108
|
-
if (result.agentTools
|
|
123
|
+
if (result.agentTools !== 'enable' && result.agentTools !== 'disable') {
|
|
109
124
|
throw new Error('lark-channel setup: --refresh-agent-policy requires --allow-agent-tools or --disable-agent-tools');
|
|
110
125
|
}
|
|
111
126
|
}
|
|
@@ -118,10 +133,13 @@ export function parseLarkSetupArgs(argv) {
|
|
|
118
133
|
if (result.installServiceOnly && (result.createApp || result.appId !== undefined)) {
|
|
119
134
|
throw new Error('lark-channel setup: --install-service cannot be combined with application setup options');
|
|
120
135
|
}
|
|
136
|
+
if (result.installServiceOnly && !result.businessTools) {
|
|
137
|
+
throw new Error('lark-channel setup: --install-service cannot be combined with --no-business-tools');
|
|
138
|
+
}
|
|
121
139
|
if (result.installServiceOnly && argv.includes('--linux-credential-provider')) {
|
|
122
140
|
throw new Error('lark-channel setup: --install-service cannot be combined with --linux-credential-provider');
|
|
123
141
|
}
|
|
124
|
-
if (result.installServiceOnly && result.agentTools !== '
|
|
142
|
+
if (result.installServiceOnly && result.agentTools !== 'auto') {
|
|
125
143
|
throw new Error('lark-channel setup: --install-service cannot be combined with agent-tools options');
|
|
126
144
|
}
|
|
127
145
|
const appNameHasControlCharacter = [...result.appName]
|
|
@@ -151,7 +169,7 @@ export function createLarkRegistrationOptions(input) {
|
|
|
151
169
|
desc: '由 DeepSeek Harness 驱动的个人助理',
|
|
152
170
|
},
|
|
153
171
|
addons: {
|
|
154
|
-
preset: false,
|
|
172
|
+
preset: input.businessTools !== false,
|
|
155
173
|
scopes: { tenant: [
|
|
156
174
|
'application:bot.basic_info:read',
|
|
157
175
|
'im:message.group_at_msg:readonly',
|
|
@@ -289,10 +307,13 @@ Options:
|
|
|
289
307
|
--install-service Only install/restart the profile's resident service
|
|
290
308
|
--refresh-agent-policy Only refresh policy from the existing channel binding
|
|
291
309
|
--no-service Configure Lark without installing a resident service
|
|
310
|
+
--no-business-tools Only configure the message channel; skip business CLI and user authorization
|
|
292
311
|
--linux-credential-provider <mode>
|
|
293
312
|
auto, protected-file, or secret-service (default: auto)
|
|
294
313
|
--allow-agent-tools Allow mounted foreground/external Agent capabilities
|
|
295
314
|
--disable-agent-tools Remove setup-managed Agent capability rules
|
|
315
|
+
--preserve-agent-tools Preserve existing rules, including on first setup
|
|
316
|
+
Default: allow for a new owner, preserve on reconfiguration
|
|
296
317
|
--timeout-ms <ms> Owner DM wait, 30000..900000 (default: 300000)
|
|
297
318
|
-h, --help Show this help
|
|
298
319
|
|
|
@@ -1146,6 +1167,7 @@ async function registerLarkApplication(args) {
|
|
|
1146
1167
|
appName: args.appName,
|
|
1147
1168
|
...(args.appId === undefined ? {} : { appId: args.appId }),
|
|
1148
1169
|
...(args.calendarReadonly ? { calendarReadonly: true } : {}),
|
|
1170
|
+
businessTools: args.businessTools,
|
|
1149
1171
|
signal: abortController.signal,
|
|
1150
1172
|
onQRCodeReady(info) {
|
|
1151
1173
|
process.stdout.write(`\n请在飞书中打开以下链接,选择已有应用或创建新应用(${info.expireIn} 秒内有效):\n${info.url}\n\n`);
|
|
@@ -1210,7 +1232,7 @@ export function dshHomeLifecycleRendezvousPath(dshHome, uid = process.getuid?.()
|
|
|
1210
1232
|
if (!isAbsolute(dshHome))
|
|
1211
1233
|
throw new Error('lark-channel setup: DSH_HOME must be absolute');
|
|
1212
1234
|
if (!Number.isSafeInteger(uid) || uid === undefined || uid < 0) {
|
|
1213
|
-
throw new Error('lark-channel setup: cannot determine the
|
|
1235
|
+
throw new Error('lark-channel setup: cannot determine the user id for lifecycle locking');
|
|
1214
1236
|
}
|
|
1215
1237
|
const canonicalHome = normalizeSetupResourcePath(dshHome);
|
|
1216
1238
|
return `/tmp/.dsh-enhanced-lifecycle-${uid}-${textSha256(canonicalHome)}.lock`;
|
|
@@ -1226,13 +1248,15 @@ async function assertDshHomeLifecycleLockIdentity(descriptor, lockPath, uid) {
|
|
|
1226
1248
|
}
|
|
1227
1249
|
}
|
|
1228
1250
|
export async function withDshHomeLifecycleLock(dshHome, operation) {
|
|
1229
|
-
if (process.platform !== 'linux')
|
|
1251
|
+
if (process.platform !== 'linux' && process.platform !== 'darwin')
|
|
1230
1252
|
return operation();
|
|
1231
1253
|
const uid = process.getuid?.();
|
|
1232
1254
|
if (!Number.isSafeInteger(uid) || uid === undefined || uid < 0) {
|
|
1233
|
-
throw new Error('lark-channel setup: cannot determine the
|
|
1255
|
+
throw new Error('lark-channel setup: cannot determine the user id for lifecycle locking');
|
|
1234
1256
|
}
|
|
1235
|
-
|
|
1257
|
+
// macOS /tmp is a system symlink to /private/tmp. Validate the physical
|
|
1258
|
+
// sticky directory while retaining the shared rendezvous pathname.
|
|
1259
|
+
const temporaryDirectory = await lstat(process.platform === 'darwin' ? realpathSync('/tmp') : '/tmp');
|
|
1236
1260
|
if (!temporaryDirectory.isDirectory() || temporaryDirectory.isSymbolicLink()
|
|
1237
1261
|
|| temporaryDirectory.uid !== 0 || (temporaryDirectory.mode & 0o7777) !== 0o1777) {
|
|
1238
1262
|
throw new Error('lark-channel setup: lifecycle rendezvous lock requires a safe root-owned sticky /tmp');
|
|
@@ -1242,9 +1266,16 @@ export async function withDshHomeLifecycleLock(dshHome, operation) {
|
|
|
1242
1266
|
try {
|
|
1243
1267
|
descriptor = await open(lockPath, constants.O_RDWR | constants.O_CREAT | constants.O_NOFOLLOW, 0o600);
|
|
1244
1268
|
await assertDshHomeLifecycleLockIdentity(descriptor, lockPath, uid);
|
|
1245
|
-
|
|
1269
|
+
// Both helpers lock the inherited open file description. The kernel keeps
|
|
1270
|
+
// it locked until this process closes the descriptor, including on crash.
|
|
1271
|
+
const mac = process.platform === 'darwin';
|
|
1272
|
+
const acquired = spawnSync(mac ? '/usr/bin/perl' : '/usr/bin/flock', mac
|
|
1273
|
+
? ['-MFcntl=:flock', '-MErrno=EAGAIN,EWOULDBLOCK', '-e',
|
|
1274
|
+
'open(F, "<&", 3) or exit 74; flock(F, LOCK_EX|LOCK_NB) or exit(($! == EAGAIN || $! == EWOULDBLOCK) ? 75 : 74);']
|
|
1275
|
+
: ['-n', '-E', '75', '3'], {
|
|
1246
1276
|
encoding: 'utf8',
|
|
1247
1277
|
stdio: ['ignore', 'ignore', 'pipe', descriptor.fd],
|
|
1278
|
+
env: { PATH: '/usr/bin:/bin', LANG: 'C' }, timeout: 10_000,
|
|
1248
1279
|
});
|
|
1249
1280
|
if (acquired.status === 75)
|
|
1250
1281
|
throw new Error(`lark-channel setup: DSH_HOME lifecycle is busy: ${lockPath}`);
|
|
@@ -1881,6 +1912,40 @@ function effectiveProfileHasEnabledLarkChannel(effectiveProfile, profile) {
|
|
|
1881
1912
|
throw new Error(`lark-channel setup: profile ${profile} has an invalid Lark config`);
|
|
1882
1913
|
return config.get('enabled') !== false;
|
|
1883
1914
|
}
|
|
1915
|
+
function effectiveDeliveryConsumerDatabasePaths(effectiveProfile, dshHome, profile) {
|
|
1916
|
+
const document = parseDocument(effectiveProfile, { uniqueKeys: true });
|
|
1917
|
+
if (document.errors.length > 0 || !isSeq(document.contents)) {
|
|
1918
|
+
throw new Error(`lark-channel setup: cannot verify effective Delivery consumers in profile ${profile}`);
|
|
1919
|
+
}
|
|
1920
|
+
const paths = [];
|
|
1921
|
+
for (const row of document.contents.items) {
|
|
1922
|
+
if (!isMap(row))
|
|
1923
|
+
continue;
|
|
1924
|
+
const id = row.get('id');
|
|
1925
|
+
const name = row.get('name');
|
|
1926
|
+
if (id !== 'dsh-enhanced-assistant-delivery' && name !== '@dsh-enhanced/assistant-delivery')
|
|
1927
|
+
continue;
|
|
1928
|
+
if (!setupRowIsEnabled(row))
|
|
1929
|
+
continue;
|
|
1930
|
+
if (name !== '@dsh-enhanced/assistant-delivery') {
|
|
1931
|
+
throw new Error(`lark-channel setup: profile ${profile} has an invalid effective Delivery package row`);
|
|
1932
|
+
}
|
|
1933
|
+
const config = row.get('config', true);
|
|
1934
|
+
if (!isMap(config))
|
|
1935
|
+
throw new Error(`lark-channel setup: profile ${profile} has an invalid Delivery config`);
|
|
1936
|
+
const schedulerEnabled = config.get('schedulerEnabled');
|
|
1937
|
+
if (schedulerEnabled !== undefined && typeof schedulerEnabled !== 'boolean') {
|
|
1938
|
+
throw new Error(`lark-channel setup: profile ${profile} has a non-boolean Delivery schedulerEnabled value`);
|
|
1939
|
+
}
|
|
1940
|
+
// Delivery defaults to starting its scheduler independently of the Lark
|
|
1941
|
+
// adapter. A stopped service may restart later; configuration alone only
|
|
1942
|
+
// proves that it cannot consume when the scheduler is explicitly disabled.
|
|
1943
|
+
if (schedulerEnabled === false)
|
|
1944
|
+
continue;
|
|
1945
|
+
paths.push(dshEffectiveDataPath(config.get('databasePath'), dshHome, 'assistant-delivery databasePath'));
|
|
1946
|
+
}
|
|
1947
|
+
return paths;
|
|
1948
|
+
}
|
|
1884
1949
|
function externalPrincipalKeyId(principal) {
|
|
1885
1950
|
return [principal.channel, principal.account, principal.tenant, principal.user]
|
|
1886
1951
|
.map(component => encodeURIComponent(component))
|
|
@@ -2181,7 +2246,7 @@ function assertEffectiveLarkOwnerBinding(input) {
|
|
|
2181
2246
|
throw new Error(`lark-channel setup: effective Lark binding no longer represents the ${suffix}`);
|
|
2182
2247
|
}
|
|
2183
2248
|
}
|
|
2184
|
-
async function
|
|
2249
|
+
async function assertNoOtherProfileSharesDelivery(input) {
|
|
2185
2250
|
let entries;
|
|
2186
2251
|
try {
|
|
2187
2252
|
entries = await readdir(platformJoin(input.dshHome, 'profiles'), { withFileTypes: true });
|
|
@@ -2198,38 +2263,136 @@ async function assertNoOtherLarkProfileSharesDelivery(input) {
|
|
|
2198
2263
|
|| !setupKeyPattern.test(entry.name))
|
|
2199
2264
|
continue;
|
|
2200
2265
|
const patchPath = platformJoin(input.dshHome, 'profiles', entry.name, 'cordis.patch.yml');
|
|
2201
|
-
// A profile can inherit Lark
|
|
2266
|
+
// A profile can inherit Delivery or Lark from a lower bundle while its raw
|
|
2202
2267
|
// override contains no channel row. Activation and database identity must
|
|
2203
2268
|
// therefore come from effective config whenever that profile composes.
|
|
2204
2269
|
try {
|
|
2205
2270
|
await readFile(patchPath, 'utf8');
|
|
2206
2271
|
}
|
|
2207
2272
|
catch (error) {
|
|
2208
|
-
|
|
2273
|
+
// Electron can have a bundle-backed desktop profile before its optional
|
|
2274
|
+
// user patch exists. Its bundles may still own Lark or consume Delivery.
|
|
2275
|
+
if (fileSystemErrorCode(error) === 'ENOENT' && entry.name !== 'desktop')
|
|
2209
2276
|
continue;
|
|
2210
|
-
|
|
2277
|
+
if (fileSystemErrorCode(error) === 'ENOENT') {
|
|
2278
|
+
// Continue into the read-only Desktop composition below.
|
|
2279
|
+
}
|
|
2280
|
+
else {
|
|
2281
|
+
throw error;
|
|
2282
|
+
}
|
|
2211
2283
|
}
|
|
2212
2284
|
let effective;
|
|
2213
2285
|
try {
|
|
2214
2286
|
effective = await input.readEffectiveProfile(entry.name);
|
|
2215
2287
|
}
|
|
2216
2288
|
catch (error) {
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2289
|
+
if (entry.name === 'desktop'
|
|
2290
|
+
&& error instanceof Error
|
|
2291
|
+
&& error.message.includes('profile "desktop" is managed exclusively by the Electron application')) {
|
|
2292
|
+
try {
|
|
2293
|
+
effective = await readElectronDesktopEffectiveProfile(input.dshHome);
|
|
2294
|
+
}
|
|
2295
|
+
catch (compositionError) {
|
|
2296
|
+
throw new Error(`lark-channel setup: cannot verify effective Lark ownership in profile desktop or its Delivery consumers`, {
|
|
2297
|
+
cause: compositionError,
|
|
2298
|
+
});
|
|
2299
|
+
}
|
|
2300
|
+
}
|
|
2301
|
+
else {
|
|
2302
|
+
// Raw absence cannot prove absence: consumers may be inherited from a lower
|
|
2303
|
+
// bundle and a resident using the last valid composition may still run.
|
|
2304
|
+
// Without a durable ownership registry, an unverifiable real profile is
|
|
2305
|
+
// therefore a fail-closed handoff boundary.
|
|
2306
|
+
throw new Error(`lark-channel setup: cannot verify effective Lark ownership in profile ${entry.name} or its Delivery consumers`, {
|
|
2307
|
+
cause: error,
|
|
2308
|
+
});
|
|
2309
|
+
}
|
|
2224
2310
|
}
|
|
2225
|
-
if (
|
|
2226
|
-
|
|
2227
|
-
const otherDatabasePath = deliveryDatabasePathFromEffectiveProfile(effective, input.dshHome);
|
|
2228
|
-
if (normalizeSetupResourcePath(otherDatabasePath) === databasePath) {
|
|
2311
|
+
if (effectiveProfileHasEnabledLarkChannel(effective, entry.name)
|
|
2312
|
+
&& normalizeSetupResourcePath(deliveryDatabasePathFromEffectiveProfile(effective, input.dshHome)) === databasePath) {
|
|
2229
2313
|
throw new Error(`lark-channel setup: profile ${entry.name} already owns Lark for the same assistant-delivery database; `
|
|
2230
2314
|
+ 'refusing to revoke its owner from another profile');
|
|
2231
2315
|
}
|
|
2316
|
+
if (effectiveDeliveryConsumerDatabasePaths(effective, input.dshHome, entry.name)
|
|
2317
|
+
.some(path => normalizeSetupResourcePath(path) === databasePath)) {
|
|
2318
|
+
throw new Error(`lark-channel setup: profile ${entry.name} has an enabled assistant-delivery scheduler for the same database; `
|
|
2319
|
+
+ 'disable its scheduler or configure a separate database before continuing');
|
|
2320
|
+
}
|
|
2321
|
+
}
|
|
2322
|
+
}
|
|
2323
|
+
/** Electron owns `desktop`, so the DSH CLI refuses even a config dump. Compose
|
|
2324
|
+
* its existing project with the same app-boot reader as Electron, without
|
|
2325
|
+
* running the CLI or preparing (and writing to) that profile. */
|
|
2326
|
+
async function readElectronDesktopEffectiveProfile(dshHome) {
|
|
2327
|
+
const profileDir = platformJoin(dshHome, 'profiles', 'desktop');
|
|
2328
|
+
// The Electron Host resolves core bundles from its private runtime, not the
|
|
2329
|
+
// plugin project. The setup process already uses `dsh` on PATH for ordinary
|
|
2330
|
+
// config dumps, so anchor this exceptional read to that same installation.
|
|
2331
|
+
const dshManifest = await resolveSetupDshManifest();
|
|
2332
|
+
const bootEntry = createRequire(dshManifest).resolve('@deepseek-ai/dsh-app-boot');
|
|
2333
|
+
const boot = await import(__rewriteRelativeImportExtension(pathToFileURL(bootEntry).href));
|
|
2334
|
+
const loaded = boot.loadProfileDirectory('dsh desktop', profileDir, dshManifest);
|
|
2335
|
+
if (loaded.skippedBundles.length > 0) {
|
|
2336
|
+
throw new Error(`desktop profile has unreadable bundles: ${loaded.skippedBundles.map(item => item.packageName).join(', ')}`);
|
|
2337
|
+
}
|
|
2338
|
+
const layers = loaded.layers.map(layer => ({ label: layer.packageName, patches: layer.patches }));
|
|
2339
|
+
layers.push({ label: loaded.patchPath, patches: loaded.patches });
|
|
2340
|
+
const homePatchPath = platformJoin(dshHome, 'cordis.patch.yml');
|
|
2341
|
+
const homePatches = boot.loadOptionalPatches('dsh desktop', homePatchPath);
|
|
2342
|
+
if (homePatches !== undefined)
|
|
2343
|
+
layers.push({ label: homePatchPath, patches: homePatches });
|
|
2344
|
+
return boot.renderConfigDump('dsh desktop', platformJoin(profileDir, 'cordis.yml'), layers);
|
|
2345
|
+
}
|
|
2346
|
+
async function resolveSetupDshManifest() {
|
|
2347
|
+
const selectedVersion = spawnSync('dsh', ['--version'], {
|
|
2348
|
+
encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'], timeout: 10_000,
|
|
2349
|
+
});
|
|
2350
|
+
if (selectedVersion.status !== 0 || typeof selectedVersion.stdout !== 'string') {
|
|
2351
|
+
throw new Error('cannot verify the selected dsh CLI version');
|
|
2352
|
+
}
|
|
2353
|
+
const version = selectedVersion.stdout.trim();
|
|
2354
|
+
if (!/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/u.test(version)) {
|
|
2355
|
+
throw new Error('selected dsh CLI returned an invalid version');
|
|
2232
2356
|
}
|
|
2357
|
+
for (const directory of (process.env.PATH ?? '').split(delimiter)) {
|
|
2358
|
+
if (directory === '')
|
|
2359
|
+
continue;
|
|
2360
|
+
const executable = platformJoin(directory, 'dsh');
|
|
2361
|
+
try {
|
|
2362
|
+
accessSync(executable, constants.X_OK);
|
|
2363
|
+
}
|
|
2364
|
+
catch {
|
|
2365
|
+
continue;
|
|
2366
|
+
}
|
|
2367
|
+
const canonical = realpathSync(executable);
|
|
2368
|
+
const candidates = [];
|
|
2369
|
+
try {
|
|
2370
|
+
candidates.push(createRequire(canonical).resolve('@deepseek-ai/dsh/package.json'));
|
|
2371
|
+
}
|
|
2372
|
+
catch {
|
|
2373
|
+
// Electron's public `dsh` command is a shell launcher; it has no Node
|
|
2374
|
+
// package at this path. Search later PATH entries for a matching npm CLI.
|
|
2375
|
+
}
|
|
2376
|
+
for (let ancestor = dirname(canonical);;) {
|
|
2377
|
+
candidates.push(platformJoin(ancestor, 'package.json'));
|
|
2378
|
+
const parent = dirname(ancestor);
|
|
2379
|
+
if (parent === ancestor)
|
|
2380
|
+
break;
|
|
2381
|
+
ancestor = parent;
|
|
2382
|
+
}
|
|
2383
|
+
for (const manifest of candidates) {
|
|
2384
|
+
let packageJson;
|
|
2385
|
+
try {
|
|
2386
|
+
packageJson = JSON.parse(await readFile(manifest, 'utf8'));
|
|
2387
|
+
}
|
|
2388
|
+
catch {
|
|
2389
|
+
continue;
|
|
2390
|
+
}
|
|
2391
|
+
if (packageJson.name === '@deepseek-ai/dsh' && packageJson.version === version)
|
|
2392
|
+
return manifest;
|
|
2393
|
+
}
|
|
2394
|
+
}
|
|
2395
|
+
throw new Error('no npm dsh installation on PATH matches the selected CLI version');
|
|
2233
2396
|
}
|
|
2234
2397
|
async function withDeliveryOwnerSetupLock(databasePath, operation, options = {}) {
|
|
2235
2398
|
const resourcePath = normalizeSetupResourcePath(databasePath);
|
|
@@ -2406,7 +2569,7 @@ async function recoverLarkSetupJournalUnlocked(input) {
|
|
|
2406
2569
|
stagedCredential: journal.stagedCredential,
|
|
2407
2570
|
context: 'journal',
|
|
2408
2571
|
});
|
|
2409
|
-
await
|
|
2572
|
+
await assertNoOtherProfileSharesDelivery({
|
|
2410
2573
|
dshHome: input.dshHome,
|
|
2411
2574
|
profile: input.profile,
|
|
2412
2575
|
databasePath,
|
|
@@ -2588,7 +2751,7 @@ export async function executeLarkSetupProfileTransaction(input) {
|
|
|
2588
2751
|
effectiveProfile,
|
|
2589
2752
|
});
|
|
2590
2753
|
await withDeliveryOwnerSetupLock(databasePath, async () => {
|
|
2591
|
-
await
|
|
2754
|
+
await assertNoOtherProfileSharesDelivery({
|
|
2592
2755
|
dshHome: input.dshHome,
|
|
2593
2756
|
profile: input.args.profile,
|
|
2594
2757
|
databasePath,
|
|
@@ -2824,7 +2987,7 @@ export async function runLarkSetup(argv = process.argv.slice(2), runtime = {}) {
|
|
|
2824
2987
|
const effectiveProfile = await readEffectiveProfile(args.profile);
|
|
2825
2988
|
const databasePath = deliveryDatabasePathFromEffectiveProfile(effectiveProfile, dshHome);
|
|
2826
2989
|
await withDeliveryOwnerSetupLock(databasePath, async () => {
|
|
2827
|
-
await
|
|
2990
|
+
await assertNoOtherProfileSharesDelivery({
|
|
2828
2991
|
dshHome,
|
|
2829
2992
|
profile: args.profile,
|
|
2830
2993
|
databasePath,
|
|
@@ -2911,7 +3074,7 @@ export async function runLarkSetup(argv = process.argv.slice(2), runtime = {}) {
|
|
|
2911
3074
|
});
|
|
2912
3075
|
const databasePath = deliveryDatabasePathFromEffectiveProfile(await readEffectiveProfile(args.profile), dshHome);
|
|
2913
3076
|
return withDeliveryOwnerSetupLock(databasePath, async () => {
|
|
2914
|
-
await
|
|
3077
|
+
await assertNoOtherProfileSharesDelivery({
|
|
2915
3078
|
dshHome,
|
|
2916
3079
|
profile: args.profile,
|
|
2917
3080
|
databasePath,
|
|
@@ -2971,7 +3134,7 @@ export async function runLarkSetup(argv = process.argv.slice(2), runtime = {}) {
|
|
|
2971
3134
|
});
|
|
2972
3135
|
const databasePath = deliveryDatabasePathFromEffectiveProfile(await readEffectiveProfile(args.profile), dshHome);
|
|
2973
3136
|
await withDeliveryOwnerSetupLock(databasePath, async () => {
|
|
2974
|
-
await
|
|
3137
|
+
await assertNoOtherProfileSharesDelivery({
|
|
2975
3138
|
dshHome,
|
|
2976
3139
|
profile: args.profile,
|
|
2977
3140
|
databasePath,
|
|
@@ -2979,6 +3142,9 @@ export async function runLarkSetup(argv = process.argv.slice(2), runtime = {}) {
|
|
|
2979
3142
|
});
|
|
2980
3143
|
}, runtime.profileLockOptions);
|
|
2981
3144
|
}, runtime.profileLockOptions);
|
|
3145
|
+
const businessCli = args.businessTools
|
|
3146
|
+
? await (runtime.ensureBusinessCli ?? ensureLarkBusinessCli)({ dshHome })
|
|
3147
|
+
: undefined;
|
|
2982
3148
|
let appId = args.appId;
|
|
2983
3149
|
let createApp = args.createApp;
|
|
2984
3150
|
if (appId === undefined && !createApp) {
|
|
@@ -2995,9 +3161,9 @@ export async function runLarkSetup(argv = process.argv.slice(2), runtime = {}) {
|
|
|
2995
3161
|
let generatedSecret;
|
|
2996
3162
|
if (createApp) {
|
|
2997
3163
|
process.stdout.write(args.appId === undefined
|
|
2998
|
-
? '\n
|
|
2999
|
-
: `\n将通过飞书官方设备授权更新已有应用 ${args.appId}
|
|
3000
|
-
const registration = await registerLarkApplication(args);
|
|
3164
|
+
? '\n将通过飞书官方设备授权选择已有应用或创建新应用,并增量申请应用权限。App Secret 不会显示或写入 profile。\n'
|
|
3165
|
+
: `\n将通过飞书官方设备授权更新已有应用 ${args.appId},并增量申请应用权限。App Secret 不会显示或写入 profile。\n`);
|
|
3166
|
+
const registration = await (runtime.registerApplication ?? registerLarkApplication)(args);
|
|
3001
3167
|
appId = registration.client_id;
|
|
3002
3168
|
resolvedDomain = registration.user_info?.tenant_brand ?? resolvedDomain;
|
|
3003
3169
|
generatedSecret = registration.client_secret;
|
|
@@ -3014,6 +3180,7 @@ export async function runLarkSetup(argv = process.argv.slice(2), runtime = {}) {
|
|
|
3014
3180
|
throw new Error('lark-channel setup: Feishu did not return an App ID');
|
|
3015
3181
|
const configuredAppId = appId;
|
|
3016
3182
|
let installedService;
|
|
3183
|
+
let businessBinding;
|
|
3017
3184
|
await executeLarkSetupProfileTransaction({
|
|
3018
3185
|
args,
|
|
3019
3186
|
dshHome,
|
|
@@ -3049,17 +3216,38 @@ export async function runLarkSetup(argv = process.argv.slice(2), runtime = {}) {
|
|
|
3049
3216
|
: undefined);
|
|
3050
3217
|
},
|
|
3051
3218
|
readEffectiveProfile,
|
|
3219
|
+
validateProfile: validate,
|
|
3220
|
+
...runtime.ownerSetupOperations,
|
|
3052
3221
|
async discoverOwner(ownerInput) {
|
|
3053
|
-
|
|
3054
|
-
|
|
3055
|
-
|
|
3056
|
-
|
|
3057
|
-
|
|
3058
|
-
|
|
3059
|
-
|
|
3060
|
-
|
|
3222
|
+
let owner;
|
|
3223
|
+
if (runtime.discoverOwner !== undefined)
|
|
3224
|
+
owner = await runtime.discoverOwner(ownerInput);
|
|
3225
|
+
else {
|
|
3226
|
+
const phrase = `DSH-CONNECT-${randomBytes(4).toString('hex').toUpperCase()}`;
|
|
3227
|
+
const transport = createOfficialLarkTransport({
|
|
3228
|
+
appId: ownerInput.appId,
|
|
3229
|
+
appSecret: ownerInput.appSecret,
|
|
3230
|
+
domain: ownerInput.domain,
|
|
3231
|
+
handshakeTimeoutMs: 15_000,
|
|
3232
|
+
requestTimeoutMs: 30_000,
|
|
3233
|
+
imageDownloadTimeoutMs: 30_000,
|
|
3234
|
+
});
|
|
3235
|
+
owner = await discoverOwner(transport, phrase, ownerInput.account, ownerInput.tenant, ownerInput.timeoutMs);
|
|
3236
|
+
}
|
|
3237
|
+
if (args.businessTools)
|
|
3238
|
+
businessBinding = { appSecret: ownerInput.appSecret, owner };
|
|
3239
|
+
const previousDocument = parseDocument(await readEffectiveProfile(args.profile), { uniqueKeys: true });
|
|
3240
|
+
const previousRow = isSeq(previousDocument.contents)
|
|
3241
|
+
? previousDocument.contents.items.find(item => isMap(item) && item.get('id') === 'dsh-enhanced-lark-channel')
|
|
3242
|
+
: undefined;
|
|
3243
|
+
const previousConfig = isMap(previousRow) ? previousRow.get('config', true) : undefined;
|
|
3244
|
+
const previousAccount = isMap(previousConfig) ? previousConfig.get('account') : undefined;
|
|
3245
|
+
await reconcileLarkBusinessSkill({
|
|
3246
|
+
dshHome, profile: args.profile, account: args.account, appId: ownerInput.appId,
|
|
3247
|
+
ownerUserId: owner.user, enabled: args.businessTools,
|
|
3248
|
+
...(typeof previousAccount === 'string' ? { previousAccount } : {}),
|
|
3061
3249
|
});
|
|
3062
|
-
return
|
|
3250
|
+
return owner;
|
|
3063
3251
|
},
|
|
3064
3252
|
...(args.manageService ? {
|
|
3065
3253
|
async afterCommit() {
|
|
@@ -3068,6 +3256,35 @@ export async function runLarkSetup(argv = process.argv.slice(2), runtime = {}) {
|
|
|
3068
3256
|
} : {}),
|
|
3069
3257
|
},
|
|
3070
3258
|
});
|
|
3259
|
+
if (businessCli !== undefined) {
|
|
3260
|
+
const binding = businessBinding;
|
|
3261
|
+
if (binding === undefined)
|
|
3262
|
+
throw new Error('lark-channel setup: business tools require the completed owner binding');
|
|
3263
|
+
process.stdout.write('\n消息通道已配置;正在接入飞书业务工具并检查 owner 用户授权。\n');
|
|
3264
|
+
const business = await (runtime.setupBusinessTools ?? setupLarkBusinessTools)({
|
|
3265
|
+
dshHome, profile: args.profile, account: args.account, appId: configuredAppId,
|
|
3266
|
+
appSecret: binding.appSecret, domain: resolvedDomain, ownerUserId: binding.owner.user,
|
|
3267
|
+
cli: businessCli, timeoutMs: args.timeoutMs,
|
|
3268
|
+
async onAuthorization(url) {
|
|
3269
|
+
process.stdout.write(`\n请用刚才绑定的飞书账号完成业务权限授权:\n${url}\n\n`);
|
|
3270
|
+
try {
|
|
3271
|
+
process.stdout.write(`${await QRCode.toString(url, { type: 'terminal', small: true })}\n`);
|
|
3272
|
+
}
|
|
3273
|
+
catch {
|
|
3274
|
+
process.stdout.write('终端二维码生成失败,请打开上面的链接。\n');
|
|
3275
|
+
}
|
|
3276
|
+
},
|
|
3277
|
+
});
|
|
3278
|
+
businessBinding = undefined;
|
|
3279
|
+
process.stdout.write(`飞书业务工具已接入${business.reusedAuthorization ? ',复用已有 owner 授权' : ''}。技能:${business.skillPath}\n`);
|
|
3280
|
+
if (business.permissionCompletion === 'partial') {
|
|
3281
|
+
process.stdout.write(`用户登录已完成;本次申请仍有 ${business.missingScopes.length} 项权限未获批,业务工具仅使用实际已授予的权限。\n`
|
|
3282
|
+
+ `未获批权限:${business.missingScopes.join(', ')}\n`);
|
|
3283
|
+
}
|
|
3284
|
+
else if (business.permissionCompletion === 'unknown') {
|
|
3285
|
+
process.stdout.write('已验证 owner 登录;当前授权证据不足以确认完整权限清单,具体操作仍以平台当前授权为准。\n');
|
|
3286
|
+
}
|
|
3287
|
+
}
|
|
3071
3288
|
if (args.manageService) {
|
|
3072
3289
|
const service = installedService;
|
|
3073
3290
|
if (service === undefined)
|