dsh-lark-bot 0.19.5 → 0.19.6
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 +10 -3
- package/README_EN.md +6 -2
- package/dist/cli.js +89 -18
- package/dist/cli.js.map +1 -1
- package/dist/plugin.js +25 -8
- package/dist/plugin.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -354,9 +354,16 @@ npx dsh-lark-bot@latest upgrade --profile dsh-lark --yes
|
|
|
354
354
|
- `--no-guardian`:跳过守护升级;
|
|
355
355
|
- **runtime profile 一致性修复**:升级后自动把 `dsh-lark-sdk` / `dsh-lark-acp` 的
|
|
356
356
|
own-package 链接重指到新版本,并当场强制刷新 runtime profile 及被链接主插件中版本陈旧或物理
|
|
357
|
-
内容损坏的 SDK server / ACP
|
|
358
|
-
|
|
359
|
-
|
|
357
|
+
内容损坏的 SDK server / ACP 依赖;修复目标以 dsh profile 中重新读取并验证的安装包为准,
|
|
358
|
+
不会链接到临时 npx worker。npm/npx 扁平依赖按 Node 的真实模块解析结果校验;pnpm 安装则先
|
|
359
|
+
解引用 profile 中的包链接,再从 `.pnpm` 物理包目录解析其依赖,避免误判缺失并强制重装;
|
|
360
|
+
- **托管环境 pnpm 一致性**:执行 `dsh plugin add` 前读取既有 profile 的
|
|
361
|
+
`node_modules/.modules.yaml`,把其中记录的精确 pnpm 版本同步到 profile `package.json`,避免
|
|
362
|
+
Corepack 因 source-managed dsh 的工作目录选择另一 pnpm 主版本并触发
|
|
363
|
+
`ERR_PNPM_UNEXPECTED_STORE`;全新 profile 不会被提前创建 manifest;
|
|
364
|
+
- **受管服务安全重载**:更新 worker 的临时 callback URL/token 与 npx/npm package-bin PATH 不会写入持久
|
|
365
|
+
service env 或 Guardian unit;Guardian 仅保留 Node 所在目录及稳定的用户/系统 PATH。只有飞书通道、
|
|
366
|
+
callback server 和 Guardian 心跳均已就绪后才确认更新成功。
|
|
360
367
|
|
|
361
368
|
无需交互确认时加 `--yes`(非交互环境不带 `--yes` 会安全中止)。其余方式:
|
|
362
369
|
|
package/README_EN.md
CHANGED
|
@@ -94,7 +94,7 @@ question-card buttons. If an existing app was created by an older flow, enable c
|
|
|
94
94
|
Console → Events & Callbacks → Callback Configuration and publish the app again. Without it, messages still work
|
|
95
95
|
while card clicks never reach the bot.
|
|
96
96
|
|
|
97
|
-
`setup` automatically: locates your local dsh → pre-approves pnpm's build policy (protobufjs) → runs the standard `dsh plugin --profile dsh-lark add dsh-lark-bot@<version>` (pinned to the running package) → installs the safety-net guardian system service. One command installs everything.
|
|
97
|
+
`setup` automatically: locates your local dsh → pre-approves pnpm's build policy (protobufjs; an existing profile is also pinned to the exact pnpm release recorded by its install metadata, preventing Corepack/store-major conflicts) → runs the standard `dsh plugin --profile dsh-lark add dsh-lark-bot@<version>` (pinned to the running package) → installs the safety-net guardian system service. A fresh profile is still initialized by dsh. One command installs everything.
|
|
98
98
|
|
|
99
99
|
> **No public IP / domain / server / tunneling required** (Feishu outbound WebSocket long connection); works on Linux / macOS / Windows.
|
|
100
100
|
> With an existing PersonalAgent app you can skip the QR step (see Configuration): `DSH_LARK_APP_ID=cli_xxx DSH_LARK_APP_SECRET=<secret> DSH_LARK_TENANT=feishu dsh --profile dsh-lark`
|
|
@@ -358,12 +358,16 @@ npx dsh-lark-bot@latest upgrade --profile dsh-lark --yes
|
|
|
358
358
|
- `--rollback`: reinstall the version recorded before the last upgrade (`~/.dsh-lark/upgrade-state.json`);
|
|
359
359
|
- `--force`: reinstall the running version when npm is unreachable (offline);
|
|
360
360
|
- `--no-guardian`: skip the guardian upgrade;
|
|
361
|
-
- **Runtime-profile consistency repair**: after
|
|
361
|
+
- **Runtime-profile consistency repair**: after `dsh plugin add`, the upgrader re-reads and verifies the exact target installed in the dsh profile, then re-points `dsh-lark-sdk` / `dsh-lark-acp` only to that stable package root—never to the transient npx worker—and refreshes stale SDK-server / ACP dependencies.
|
|
362
|
+
- **npm/npx/pnpm layout support**: npm/npx-flat dependencies use Node's actual module resolution; pnpm package links are resolved to their physical `.pnpm` target before dependency validation, avoiding false corruption reports and unnecessary forced installs.
|
|
363
|
+
- **Corepack-managed dsh compatibility**: before installing into an existing profile, the exact pnpm release from `node_modules/.modules.yaml` is persisted to `package.json#packageManager`, preventing a source-managed dsh working directory from selecting an incompatible pnpm store major.
|
|
362
364
|
|
|
363
365
|
Pass `--yes` to skip the interactive confirmation (non-interactive runs fail closed without it). Alternatives:
|
|
364
366
|
|
|
365
367
|
- Plugin: re-run `setup` (or `dsh plugin --profile <name> add dsh-lark-bot`) to pull the latest npm release.
|
|
366
368
|
- Safety-net guardian: installed / upgraded together with `upgrade` / `setup` (idempotent), or standalone via `dsh-lark-bot guardian install`.
|
|
369
|
+
- Managed reloads never persist the update worker's npx/npm package-bin paths into the service environment or
|
|
370
|
+
Guardian unit. Guardian keeps only the Node directory and stable user/system PATH entries.
|
|
367
371
|
- CLI tool (optional): `npm i -g dsh-lark-bot@latest`; not needed when using `npx`.
|
|
368
372
|
- Restart the profile after upgrading (when not using `--restart`): `dsh --profile dsh-lark`.
|
|
369
373
|
|
package/dist/cli.js
CHANGED
|
@@ -282,8 +282,7 @@ import { join as join7 } from "path";
|
|
|
282
282
|
function acpProfileRoot(home, profile, env) {
|
|
283
283
|
return join7(resolveDshHome(home, env), "profiles", profile);
|
|
284
284
|
}
|
|
285
|
-
function packageJsonFor2(profile) {
|
|
286
|
-
const own = ownPackageInfo();
|
|
285
|
+
function packageJsonFor2(profile, own = ownPackageInfo()) {
|
|
287
286
|
return `${JSON.stringify(
|
|
288
287
|
{
|
|
289
288
|
name: `dsh-profile-${profile}`,
|
|
@@ -372,12 +371,11 @@ function acpPatchYaml(provider, model) {
|
|
|
372
371
|
""
|
|
373
372
|
].join("\n");
|
|
374
373
|
}
|
|
375
|
-
function isAcpProfileReady(profileRoot) {
|
|
376
|
-
const own = ownPackageInfo();
|
|
374
|
+
function isAcpProfileReady(profileRoot, own = ownPackageInfo()) {
|
|
377
375
|
return existsSync4(join7(profileRoot, "package.json")) && existsSync4(join7(profileRoot, "cordis.yml")) && existsSync4(join7(profileRoot, "cordis.patch.yml")) && profilePackageMatches(profileRoot, ACP_PACKAGE, ACP_VERSION) && ownPackageLinked2(profileRoot, own);
|
|
378
376
|
}
|
|
379
|
-
function isAcpManagedProfileCurrent(profileRoot, provider, model) {
|
|
380
|
-
if (!isAcpProfileReady(profileRoot)) return false;
|
|
377
|
+
function isAcpManagedProfileCurrent(profileRoot, provider, model, own = ownPackageInfo()) {
|
|
378
|
+
if (!isAcpProfileReady(profileRoot, own)) return false;
|
|
381
379
|
try {
|
|
382
380
|
return readFileSync4(join7(profileRoot, "cordis.patch.yml"), "utf8") === acpPatchYaml(provider, model);
|
|
383
381
|
} catch {
|
|
@@ -423,10 +421,11 @@ ${tail}`));
|
|
|
423
421
|
}
|
|
424
422
|
async function ensureAcpProfile(options) {
|
|
425
423
|
const profile = options.profile ?? DEFAULT_ACP_PROFILE;
|
|
424
|
+
const own = options.ownPackage ?? ownPackageInfo();
|
|
426
425
|
const root = acpProfileRoot(options.home, profile, options.env);
|
|
427
426
|
const provider = options.provider?.trim();
|
|
428
427
|
const model = options.model?.trim();
|
|
429
|
-
const ready = isAcpProfileReady(root);
|
|
428
|
+
const ready = isAcpProfileReady(root, own);
|
|
430
429
|
try {
|
|
431
430
|
if (!provider || !model) {
|
|
432
431
|
throw new Error(
|
|
@@ -434,14 +433,14 @@ async function ensureAcpProfile(options) {
|
|
|
434
433
|
);
|
|
435
434
|
}
|
|
436
435
|
await mkdir4(root, { recursive: true });
|
|
437
|
-
await writeFile4(join7(root, "package.json"), packageJsonFor2(profile), "utf8");
|
|
436
|
+
await writeFile4(join7(root, "package.json"), packageJsonFor2(profile, own), "utf8");
|
|
438
437
|
await writeFile4(join7(root, "cordis.yml"), "[]\n", "utf8");
|
|
439
438
|
await writeFile4(join7(root, "cordis.patch.yml"), acpPatchYaml(provider, model), "utf8");
|
|
440
439
|
if (!ready) {
|
|
441
440
|
const install = options.install ?? runPnpmInstall2;
|
|
442
441
|
await install(root, { force: true });
|
|
443
442
|
}
|
|
444
|
-
if (!isAcpProfileReady(root)) {
|
|
443
|
+
if (!isAcpProfileReady(root, own)) {
|
|
445
444
|
return {
|
|
446
445
|
ok: false,
|
|
447
446
|
created: true,
|
|
@@ -2765,6 +2764,7 @@ init_bridge_persona();
|
|
|
2765
2764
|
init_own_package();
|
|
2766
2765
|
init_profile_package();
|
|
2767
2766
|
import { spawn as spawn3 } from "cross-spawn";
|
|
2767
|
+
import { createRequire } from "module";
|
|
2768
2768
|
import { existsSync as existsSync3, realpathSync as realpathSync2 } from "fs";
|
|
2769
2769
|
import { readFileSync as readFileSync3 } from "fs";
|
|
2770
2770
|
import { mkdir as mkdir3, writeFile as writeFile3 } from "fs/promises";
|
|
@@ -2900,7 +2900,24 @@ function isSdkManagedProfileCurrent(profileRoot, options, own = ownPackageInfo()
|
|
|
2900
2900
|
return isSdkProfileReady(profileRoot, own) && readFileIfPresent(join6(profileRoot, "cordis.patch.yml")) === patchYamlFor(options);
|
|
2901
2901
|
}
|
|
2902
2902
|
function ownSdkServerDependencyReady(own) {
|
|
2903
|
-
|
|
2903
|
+
try {
|
|
2904
|
+
const physicalOwnRoot = realpathSync2(own.root);
|
|
2905
|
+
const entry = createRequire(join6(physicalOwnRoot, "package.json")).resolve(SDK_SERVER_PACKAGE);
|
|
2906
|
+
let current = dirname5(entry);
|
|
2907
|
+
for (let depth = 0; depth < 8; depth += 1) {
|
|
2908
|
+
try {
|
|
2909
|
+
const parsed = JSON.parse(readFileSync3(join6(current, "package.json"), "utf8"));
|
|
2910
|
+
if (parsed.name === SDK_SERVER_PACKAGE) return parsed.version === SDK_SERVER_VERSION;
|
|
2911
|
+
} catch {
|
|
2912
|
+
}
|
|
2913
|
+
const parent = dirname5(current);
|
|
2914
|
+
if (parent === current) return false;
|
|
2915
|
+
current = parent;
|
|
2916
|
+
}
|
|
2917
|
+
return false;
|
|
2918
|
+
} catch {
|
|
2919
|
+
return false;
|
|
2920
|
+
}
|
|
2904
2921
|
}
|
|
2905
2922
|
function findPnpmProjectRoot(start) {
|
|
2906
2923
|
let current = start;
|
|
@@ -4141,6 +4158,20 @@ function systemdUnit(nodeBin, cliEntry, env = {}) {
|
|
|
4141
4158
|
""
|
|
4142
4159
|
].join("\n");
|
|
4143
4160
|
}
|
|
4161
|
+
function stableGuardianServicePath(nodeBin, inheritedPath = process.env.PATH) {
|
|
4162
|
+
const entries = [dirname9(nodeBin), ...inheritedPath?.split(delimiter) ?? []];
|
|
4163
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4164
|
+
return entries.filter((entry) => {
|
|
4165
|
+
if (!entry) return false;
|
|
4166
|
+
const normalized = entry.replaceAll("\\", "/");
|
|
4167
|
+
if (normalized.includes("/node_modules/.bin") || /\/_npx(?:\/|$)/.test(normalized) || /\/guardian\/update-worker\/npm-cache(?:\/|$)/.test(normalized)) {
|
|
4168
|
+
return false;
|
|
4169
|
+
}
|
|
4170
|
+
if (seen.has(entry)) return false;
|
|
4171
|
+
seen.add(entry);
|
|
4172
|
+
return true;
|
|
4173
|
+
}).join(delimiter);
|
|
4174
|
+
}
|
|
4144
4175
|
function launchdPlist(nodeBin, cliEntry, label2 = `io.dsh-lark.${GUARDIAN_LABEL}`, logPath = "/tmp/dsh-lark-guardian.log") {
|
|
4145
4176
|
return [
|
|
4146
4177
|
'<?xml version="1.0" encoding="UTF-8"?>',
|
|
@@ -4203,7 +4234,7 @@ async function installGuardian(options) {
|
|
|
4203
4234
|
await writeFile5(
|
|
4204
4235
|
unitPath,
|
|
4205
4236
|
systemdUnit(nodeBin, cliEntry, {
|
|
4206
|
-
PATH:
|
|
4237
|
+
PATH: stableGuardianServicePath(nodeBin),
|
|
4207
4238
|
DSH_LARK_GUARDIAN_DISABLED: "0"
|
|
4208
4239
|
}),
|
|
4209
4240
|
"utf8"
|
|
@@ -18513,6 +18544,7 @@ import { spawn as spawn8 } from "child_process";
|
|
|
18513
18544
|
import { mkdir as mkdir23, readFile as readFile32, writeFile as writeFile9 } from "fs/promises";
|
|
18514
18545
|
import { homedir as homedir17 } from "os";
|
|
18515
18546
|
import { join as join28 } from "path";
|
|
18547
|
+
import { parse } from "yaml";
|
|
18516
18548
|
async function runSetup(options = {}) {
|
|
18517
18549
|
const profile = options.profile ?? "dsh-lark";
|
|
18518
18550
|
const own = ownPackageInfo();
|
|
@@ -18586,6 +18618,7 @@ function runDshPlugin(bin, profile, packageSpec, env = process.env) {
|
|
|
18586
18618
|
});
|
|
18587
18619
|
}
|
|
18588
18620
|
async function approveBuilds(profileDir) {
|
|
18621
|
+
await preserveInstalledPnpmVersion(profileDir);
|
|
18589
18622
|
const workspaceFile = join28(profileDir, "pnpm-workspace.yaml");
|
|
18590
18623
|
let existing = "";
|
|
18591
18624
|
try {
|
|
@@ -18610,6 +18643,32 @@ allowBuilds:
|
|
|
18610
18643
|
}
|
|
18611
18644
|
await writeFile9(workspaceFile, existing, "utf8");
|
|
18612
18645
|
}
|
|
18646
|
+
async function preserveInstalledPnpmVersion(profileDir) {
|
|
18647
|
+
const packageFile = join28(profileDir, "package.json");
|
|
18648
|
+
const modulesFile = join28(profileDir, "node_modules", ".modules.yaml");
|
|
18649
|
+
let packageText;
|
|
18650
|
+
let modulesText;
|
|
18651
|
+
try {
|
|
18652
|
+
[packageText, modulesText] = await Promise.all([
|
|
18653
|
+
readFile32(packageFile, "utf8"),
|
|
18654
|
+
readFile32(modulesFile, "utf8")
|
|
18655
|
+
]);
|
|
18656
|
+
} catch {
|
|
18657
|
+
return;
|
|
18658
|
+
}
|
|
18659
|
+
try {
|
|
18660
|
+
const profilePackage = JSON.parse(packageText);
|
|
18661
|
+
const modules = parse(modulesText);
|
|
18662
|
+
const packageManager = modules?.packageManager;
|
|
18663
|
+
if (typeof packageManager !== "string" || !/^pnpm@\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/.test(packageManager) || profilePackage.packageManager === packageManager) {
|
|
18664
|
+
return;
|
|
18665
|
+
}
|
|
18666
|
+
profilePackage.packageManager = packageManager;
|
|
18667
|
+
await writeFile9(packageFile, `${JSON.stringify(profilePackage, null, 2)}
|
|
18668
|
+
`, "utf8");
|
|
18669
|
+
} catch {
|
|
18670
|
+
}
|
|
18671
|
+
}
|
|
18613
18672
|
|
|
18614
18673
|
// src/cli/commands/upgrade.ts
|
|
18615
18674
|
init_own_package();
|
|
@@ -18768,7 +18827,7 @@ function existingAcpRoute(profileRoot) {
|
|
|
18768
18827
|
}
|
|
18769
18828
|
}
|
|
18770
18829
|
async function repairRuntimeProfiles(options) {
|
|
18771
|
-
const own = ownPackageInfo();
|
|
18830
|
+
const own = options.ownPackage ?? ownPackageInfo();
|
|
18772
18831
|
const env = options.env ?? process.env;
|
|
18773
18832
|
const relink = options.relinkFn ?? defaultRelink;
|
|
18774
18833
|
const managedEnv = { ...env, DSH_HOME: options.dshHome };
|
|
@@ -18786,18 +18845,19 @@ async function repairRuntimeProfiles(options) {
|
|
|
18786
18845
|
{
|
|
18787
18846
|
profile: DEFAULT_SDK_PROFILE,
|
|
18788
18847
|
root: sdkRoot,
|
|
18789
|
-
isReady: isSdkManagedProfileCurrent,
|
|
18790
|
-
ensure: () => ensureSdk({ home: options.dshHome, env: managedEnv })
|
|
18848
|
+
isReady: (root) => isSdkManagedProfileCurrent(root, void 0, own),
|
|
18849
|
+
ensure: () => ensureSdk({ home: options.dshHome, env: managedEnv, ownPackage: own })
|
|
18791
18850
|
},
|
|
18792
18851
|
{
|
|
18793
18852
|
profile: DEFAULT_ACP_PROFILE,
|
|
18794
18853
|
root: acpRoot,
|
|
18795
|
-
isReady: (root) => isAcpManagedProfileCurrent(root, acpRoute.provider, acpRoute.model),
|
|
18854
|
+
isReady: (root) => isAcpManagedProfileCurrent(root, acpRoute.provider, acpRoute.model, own),
|
|
18796
18855
|
ensure: () => ensureAcp({
|
|
18797
18856
|
home: options.dshHome,
|
|
18798
18857
|
env: managedEnv,
|
|
18799
18858
|
provider: acpRoute.provider,
|
|
18800
|
-
model: acpRoute.model
|
|
18859
|
+
model: acpRoute.model,
|
|
18860
|
+
ownPackage: own
|
|
18801
18861
|
})
|
|
18802
18862
|
}
|
|
18803
18863
|
];
|
|
@@ -19014,10 +19074,21 @@ ${changeLines.join("\n")}
|
|
|
19014
19074
|
await (options.pluginSpawnFn ?? runDshPlugin)(bin, profile, target.spec);
|
|
19015
19075
|
write(out, `\u2705 \u5305\u672C\u4F53\u5DF2\u66F4\u65B0\u5230 ${target.version}\u3002
|
|
19016
19076
|
`);
|
|
19077
|
+
const installedTarget = await (options.readInstalledPackageFn ?? readInstalledPackage)(
|
|
19078
|
+
dshHome,
|
|
19079
|
+
profile,
|
|
19080
|
+
own.name
|
|
19081
|
+
);
|
|
19082
|
+
if (!installedTarget || installedTarget.name !== own.name || installedTarget.version !== target.version) {
|
|
19083
|
+
throw new Error(
|
|
19084
|
+
`dsh plugin add completed but ${own.name}@${target.version} is not installed in profile ${profile}`
|
|
19085
|
+
);
|
|
19086
|
+
}
|
|
19017
19087
|
const repair = options.repairRuntimeFn ?? repairRuntimeProfiles;
|
|
19018
19088
|
const runtimeStates = await repair({
|
|
19019
19089
|
dshHome,
|
|
19020
19090
|
env: process.env,
|
|
19091
|
+
ownPackage: installedTarget,
|
|
19021
19092
|
provider: env.provider,
|
|
19022
19093
|
model: env.model
|
|
19023
19094
|
});
|
|
@@ -19133,7 +19204,7 @@ import { join as join32 } from "path";
|
|
|
19133
19204
|
import {
|
|
19134
19205
|
createLarkChannel as createLarkChannel3
|
|
19135
19206
|
} from "@larksuite/channel";
|
|
19136
|
-
import { parse } from "yaml";
|
|
19207
|
+
import { parse as parse2 } from "yaml";
|
|
19137
19208
|
init_dsh_runtime();
|
|
19138
19209
|
|
|
19139
19210
|
// src/guardian/control.ts
|
|
@@ -20382,7 +20453,7 @@ function safeScopeForAction(event) {
|
|
|
20382
20453
|
function readDshDefaultSelection(home, env) {
|
|
20383
20454
|
try {
|
|
20384
20455
|
const file = join32(resolveDshHome(home, env), "settings.yaml");
|
|
20385
|
-
const doc =
|
|
20456
|
+
const doc = parse2(readFileSync6(file, "utf8"));
|
|
20386
20457
|
const value = doc?.["agent-default-model"];
|
|
20387
20458
|
if (typeof value === "object" && value !== null && typeof value.provider === "string" && typeof value.model === "string") {
|
|
20388
20459
|
const selection = value;
|