dsh-lark-bot 0.6.0 → 0.7.0
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 +38 -90
- package/SECURITY.md +2 -3
- package/cordis.patch.yml +23 -5
- package/dist/cli.js +191 -997
- package/dist/cli.js.map +1 -1
- package/dist/notify.js +2 -7
- package/dist/notify.js.map +1 -1
- package/dist/plugin.d.ts +677 -90
- package/dist/plugin.js +5544 -705
- package/dist/plugin.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -134,16 +134,29 @@ var init_dsh_runtime = __esm({
|
|
|
134
134
|
// src/adapters/dsh/own-package.ts
|
|
135
135
|
import { readFileSync } from "fs";
|
|
136
136
|
import { fileURLToPath } from "url";
|
|
137
|
-
import { join as join2 } from "path";
|
|
137
|
+
import { dirname, join as join2 } from "path";
|
|
138
138
|
function ownPackageInfo() {
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
139
|
+
return findOwnPackageRoot(dirname(fileURLToPath(import.meta.url)));
|
|
140
|
+
}
|
|
141
|
+
function findOwnPackageRoot(startDir) {
|
|
142
|
+
let dir = startDir;
|
|
143
|
+
for (let depth = 0; depth < 8; depth += 1) {
|
|
144
|
+
const pkgPath = join2(dir, "package.json");
|
|
145
|
+
try {
|
|
146
|
+
const pkg = JSON.parse(readFileSync(pkgPath, "utf8"));
|
|
147
|
+
if (typeof pkg.name === "string" && isOwnPackageName(pkg.name)) {
|
|
148
|
+
return { name: pkg.name, root: dir };
|
|
149
|
+
}
|
|
150
|
+
} catch {
|
|
151
|
+
}
|
|
152
|
+
const parent = dirname(dir);
|
|
153
|
+
if (parent === dir) break;
|
|
154
|
+
dir = parent;
|
|
145
155
|
}
|
|
146
|
-
return { name, root };
|
|
156
|
+
return { name: "dsh-lark-bot", root: process.cwd() };
|
|
157
|
+
}
|
|
158
|
+
function isOwnPackageName(name) {
|
|
159
|
+
return name === "dsh-lark-bot" || name === "dsh-feishu-bot" || /^@[^/]+\/(dsh-lark-bot|dsh-feishu-bot)$/.test(name);
|
|
147
160
|
}
|
|
148
161
|
var init_own_package = __esm({
|
|
149
162
|
"src/adapters/dsh/own-package.ts"() {
|
|
@@ -153,7 +166,8 @@ var init_own_package = __esm({
|
|
|
153
166
|
|
|
154
167
|
// src/adapters/dsh/acp-runtime.ts
|
|
155
168
|
import { spawn as spawn4 } from "child_process";
|
|
156
|
-
import { existsSync as existsSync4 } from "fs";
|
|
169
|
+
import { existsSync as existsSync4, realpathSync as realpathSync2 } from "fs";
|
|
170
|
+
import { readFileSync as readFileSync3 } from "fs";
|
|
157
171
|
import { mkdir as mkdir2, writeFile as writeFile2 } from "fs/promises";
|
|
158
172
|
import { join as join4 } from "path";
|
|
159
173
|
function acpProfileRoot(home, profile, env) {
|
|
@@ -224,7 +238,17 @@ function acpPluginInstalled(profileRoot) {
|
|
|
224
238
|
}
|
|
225
239
|
function isAcpProfileReady(profileRoot) {
|
|
226
240
|
const own = ownPackageInfo();
|
|
227
|
-
return existsSync4(join4(profileRoot, "package.json")) && existsSync4(join4(profileRoot, "cordis.yml")) && existsSync4(join4(profileRoot, "cordis.patch.yml")) && acpPluginInstalled(profileRoot) &&
|
|
241
|
+
return existsSync4(join4(profileRoot, "package.json")) && existsSync4(join4(profileRoot, "cordis.yml")) && existsSync4(join4(profileRoot, "cordis.patch.yml")) && acpPluginInstalled(profileRoot) && ownPackageLinked2(profileRoot, own.name);
|
|
242
|
+
}
|
|
243
|
+
function ownPackageLinked2(profileRoot, ownName) {
|
|
244
|
+
const linkPath = join4(profileRoot, "node_modules", ownName);
|
|
245
|
+
try {
|
|
246
|
+
const real = realpathSync2(linkPath);
|
|
247
|
+
const pkg = JSON.parse(readFileSync3(join4(real, "package.json"), "utf8"));
|
|
248
|
+
return pkg.name === ownName && pkg.dsh?.bundle?.patch !== void 0;
|
|
249
|
+
} catch {
|
|
250
|
+
return false;
|
|
251
|
+
}
|
|
228
252
|
}
|
|
229
253
|
function runPnpmInstall2(profileRoot) {
|
|
230
254
|
return new Promise((resolve5, reject) => {
|
|
@@ -633,8 +657,8 @@ var init_acp_adapter = __esm({
|
|
|
633
657
|
});
|
|
634
658
|
|
|
635
659
|
// src/cli.ts
|
|
636
|
-
import { readFileSync as
|
|
637
|
-
import { fileURLToPath as
|
|
660
|
+
import { readFileSync as readFileSync4, realpathSync as realpathSync4 } from "fs";
|
|
661
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
638
662
|
import { Command } from "commander";
|
|
639
663
|
|
|
640
664
|
// src/cli/commands/doctor.ts
|
|
@@ -1235,7 +1259,8 @@ init_dsh_compat();
|
|
|
1235
1259
|
init_dsh_runtime();
|
|
1236
1260
|
init_own_package();
|
|
1237
1261
|
import { spawn as spawn3 } from "child_process";
|
|
1238
|
-
import { existsSync as existsSync3 } from "fs";
|
|
1262
|
+
import { existsSync as existsSync3, realpathSync } from "fs";
|
|
1263
|
+
import { readFileSync as readFileSync2 } from "fs";
|
|
1239
1264
|
import { mkdir, writeFile } from "fs/promises";
|
|
1240
1265
|
import { join as join3 } from "path";
|
|
1241
1266
|
var SDK_SERVER_PACKAGE = "@deepseek-ai/dsh-sdk-jsonrpc-server";
|
|
@@ -1310,7 +1335,17 @@ function sdkServerInstalled(profileRoot) {
|
|
|
1310
1335
|
}
|
|
1311
1336
|
function isSdkProfileReady(profileRoot) {
|
|
1312
1337
|
const own = ownPackageInfo();
|
|
1313
|
-
return existsSync3(join3(profileRoot, "package.json")) && existsSync3(join3(profileRoot, "cordis.yml")) && existsSync3(join3(profileRoot, "cordis.patch.yml")) && sdkServerInstalled(profileRoot) &&
|
|
1338
|
+
return existsSync3(join3(profileRoot, "package.json")) && existsSync3(join3(profileRoot, "cordis.yml")) && existsSync3(join3(profileRoot, "cordis.patch.yml")) && sdkServerInstalled(profileRoot) && ownPackageLinked(profileRoot, own.name);
|
|
1339
|
+
}
|
|
1340
|
+
function ownPackageLinked(profileRoot, ownName) {
|
|
1341
|
+
const linkPath = join3(profileRoot, "node_modules", ownName);
|
|
1342
|
+
try {
|
|
1343
|
+
const real = realpathSync(linkPath);
|
|
1344
|
+
const pkg = JSON.parse(readFileSync2(join3(real, "package.json"), "utf8"));
|
|
1345
|
+
return pkg.name === ownName && pkg.dsh?.bundle?.patch !== void 0;
|
|
1346
|
+
} catch {
|
|
1347
|
+
return false;
|
|
1348
|
+
}
|
|
1314
1349
|
}
|
|
1315
1350
|
function runPnpmInstall(profileRoot) {
|
|
1316
1351
|
return new Promise((resolve5, reject) => {
|
|
@@ -1446,13 +1481,7 @@ function resolveAppPaths(root = defaultHome()) {
|
|
|
1446
1481
|
archivesDir: (profile) => profilePath(profile, "archives"),
|
|
1447
1482
|
workspacesFile: (profile) => profilePath(profile, "workspaces.json"),
|
|
1448
1483
|
mediaDir: (profile) => profilePath(profile, "media"),
|
|
1449
|
-
logsDir: (profile) => profilePath(profile, "logs")
|
|
1450
|
-
serviceDir: join5(root, "service"),
|
|
1451
|
-
serviceEnvFile: join5(root, "service", "service.env"),
|
|
1452
|
-
serviceMetadataFile: join5(root, "service", "service.json"),
|
|
1453
|
-
serviceLogFile: (profile) => profilePath(profile, "logs", "bot.log"),
|
|
1454
|
-
registryFile: join5(root, "registry", "processes.json"),
|
|
1455
|
-
locksDir: join5(root, "registry", "locks")
|
|
1484
|
+
logsDir: (profile) => profilePath(profile, "logs")
|
|
1456
1485
|
};
|
|
1457
1486
|
}
|
|
1458
1487
|
|
|
@@ -1612,14 +1641,14 @@ function loadRuntimeEnv(source = process.env) {
|
|
|
1612
1641
|
// src/config/profile-store.ts
|
|
1613
1642
|
import { readFile } from "fs/promises";
|
|
1614
1643
|
import { mkdir as mkdir4 } from "fs/promises";
|
|
1615
|
-
import { dirname as
|
|
1644
|
+
import { dirname as dirname3 } from "path";
|
|
1616
1645
|
|
|
1617
1646
|
// src/platform/atomic-write.ts
|
|
1618
1647
|
import { randomBytes } from "crypto";
|
|
1619
1648
|
import { mkdir as mkdir3, rename, writeFile as writeFile3 } from "fs/promises";
|
|
1620
|
-
import { dirname, join as join7 } from "path";
|
|
1649
|
+
import { dirname as dirname2, join as join7 } from "path";
|
|
1621
1650
|
async function writeFileAtomic(target, data, options = {}) {
|
|
1622
|
-
const directory =
|
|
1651
|
+
const directory = dirname2(target);
|
|
1623
1652
|
await mkdir3(directory, { recursive: true });
|
|
1624
1653
|
const temporary = join7(directory, `.${randomBytes(8).toString("hex")}.tmp`);
|
|
1625
1654
|
try {
|
|
@@ -1631,9 +1660,9 @@ async function writeFileAtomic(target, data, options = {}) {
|
|
|
1631
1660
|
}
|
|
1632
1661
|
}
|
|
1633
1662
|
async function rmSilently(path) {
|
|
1634
|
-
const { rm:
|
|
1663
|
+
const { rm: rm2 } = await import("fs/promises");
|
|
1635
1664
|
try {
|
|
1636
|
-
await
|
|
1665
|
+
await rm2(path, { force: true });
|
|
1637
1666
|
} catch {
|
|
1638
1667
|
}
|
|
1639
1668
|
}
|
|
@@ -1725,7 +1754,7 @@ var ConfigStore = class {
|
|
|
1725
1754
|
}
|
|
1726
1755
|
async persist() {
|
|
1727
1756
|
const data = this.getData();
|
|
1728
|
-
await mkdir4(
|
|
1757
|
+
await mkdir4(dirname3(this.path), { recursive: true });
|
|
1729
1758
|
await writeFileAtomic(this.path, `${JSON.stringify(data, null, 2)}
|
|
1730
1759
|
`, {
|
|
1731
1760
|
mode: 384
|
|
@@ -1857,7 +1886,7 @@ var ActiveRuns = class {
|
|
|
1857
1886
|
};
|
|
1858
1887
|
|
|
1859
1888
|
// src/config/security.ts
|
|
1860
|
-
import { realpathSync } from "fs";
|
|
1889
|
+
import { realpathSync as realpathSync3 } from "fs";
|
|
1861
1890
|
import { isIP } from "net";
|
|
1862
1891
|
import { isAbsolute, relative, resolve as resolve3, sep } from "path";
|
|
1863
1892
|
var REDACTED = "[redacted]";
|
|
@@ -1875,7 +1904,7 @@ function redactSecrets(text) {
|
|
|
1875
1904
|
}
|
|
1876
1905
|
function resolveReal(path) {
|
|
1877
1906
|
try {
|
|
1878
|
-
return
|
|
1907
|
+
return realpathSync3(path);
|
|
1879
1908
|
} catch {
|
|
1880
1909
|
return resolve3(path);
|
|
1881
1910
|
}
|
|
@@ -2959,7 +2988,7 @@ function errorMessage(error) {
|
|
|
2959
2988
|
// src/config/dsh-config.ts
|
|
2960
2989
|
import { mkdir as mkdir5, readFile as readFile3, rm, writeFile as writeFile4 } from "fs/promises";
|
|
2961
2990
|
import { homedir as homedir5 } from "os";
|
|
2962
|
-
import { dirname as
|
|
2991
|
+
import { dirname as dirname4, join as join8 } from "path";
|
|
2963
2992
|
import { Document, parseDocument } from "yaml";
|
|
2964
2993
|
init_dsh_runtime();
|
|
2965
2994
|
var DEEPSEEK_NAMESPACE = "llm-deepseek";
|
|
@@ -3315,7 +3344,7 @@ var DshProviderManager = class {
|
|
|
3315
3344
|
throw new Error(`\u975E\u6CD5\u51ED\u636E\u5F15\u7528\u540D\uFF1A${ref}\uFF08\u5E94\u4E3A POSIX \u73AF\u5883\u53D8\u91CF\u540D\uFF0C\u5982 OPENAI_API_KEY\uFF09`);
|
|
3316
3345
|
}
|
|
3317
3346
|
if (value.length === 0) throw new Error("\u51ED\u636E\u503C\u4E0D\u80FD\u4E3A\u7A7A");
|
|
3318
|
-
await mkdir5(
|
|
3347
|
+
await mkdir5(dirname4(this.credentialsFile), { recursive: true, mode: 448 });
|
|
3319
3348
|
await withFileLock(this.credentialsFile, async () => {
|
|
3320
3349
|
const text = await readOptional(this.credentialsFile);
|
|
3321
3350
|
const root = parseYamlMap(text, this.credentialsFile);
|
|
@@ -3345,7 +3374,7 @@ var DshProviderManager = class {
|
|
|
3345
3374
|
return removed;
|
|
3346
3375
|
}
|
|
3347
3376
|
async writeNamespace(ns, section) {
|
|
3348
|
-
await mkdir5(
|
|
3377
|
+
await mkdir5(dirname4(this.settingsFile), { recursive: true });
|
|
3349
3378
|
await withFileLock(this.settingsFile, async () => {
|
|
3350
3379
|
const text = await readOptional(this.settingsFile);
|
|
3351
3380
|
const root = parseYamlMap(text, this.settingsFile);
|
|
@@ -5206,7 +5235,7 @@ async function safeRemove(path) {
|
|
|
5206
5235
|
import { execFile as execFile2 } from "child_process";
|
|
5207
5236
|
import { randomBytes as randomBytes4 } from "crypto";
|
|
5208
5237
|
import { access as access2, copyFile, mkdir as mkdir8 } from "fs/promises";
|
|
5209
|
-
import { dirname as
|
|
5238
|
+
import { dirname as dirname5, join as join11 } from "path";
|
|
5210
5239
|
import { promisify as promisify2 } from "util";
|
|
5211
5240
|
var execFileAsync2 = promisify2(execFile2);
|
|
5212
5241
|
async function defaultRunGit2(args, cwd) {
|
|
@@ -5225,7 +5254,7 @@ async function exists2(path) {
|
|
|
5225
5254
|
}
|
|
5226
5255
|
}
|
|
5227
5256
|
async function defaultCopyRulesFile(source, target) {
|
|
5228
|
-
await mkdir8(
|
|
5257
|
+
await mkdir8(dirname5(target), { recursive: true });
|
|
5229
5258
|
await copyFile(source, target);
|
|
5230
5259
|
}
|
|
5231
5260
|
function slugify2(scope) {
|
|
@@ -5380,10 +5409,7 @@ async function ensureBotProfile(store, options) {
|
|
|
5380
5409
|
}
|
|
5381
5410
|
if (store.getProfile(profileName)) return true;
|
|
5382
5411
|
if (!options.allowOnboarding) {
|
|
5383
|
-
|
|
5384
|
-
"\u672A\u68C0\u6D4B\u5230\u98DE\u4E66 / Lark \u5E94\u7528\u51ED\u636E\u3002\u8BF7\u5148\u5728\u7EC8\u7AEF\u8FD0\u884C `dsh-lark-bot start` \u5B8C\u6210\u626B\u7801\u7ED1\u5B9A\u3002\n"
|
|
5385
|
-
);
|
|
5386
|
-
return false;
|
|
5412
|
+
throw new Error("\u672A\u68C0\u6D4B\u5230\u98DE\u4E66 / Lark \u5E94\u7528\u51ED\u636E\uFF0C\u4E14\u672A\u5141\u8BB8\u626B\u7801\u7ED1\u5B9A\u3002");
|
|
5387
5413
|
}
|
|
5388
5414
|
try {
|
|
5389
5415
|
const created = await onboardPersonalAgent();
|
|
@@ -5403,9 +5429,7 @@ async function ensureBotProfile(store, options) {
|
|
|
5403
5429
|
return true;
|
|
5404
5430
|
} catch (error) {
|
|
5405
5431
|
log.fail("onboarding", error);
|
|
5406
|
-
|
|
5407
|
-
process.exitCode = 1;
|
|
5408
|
-
return false;
|
|
5432
|
+
throw new Error(`\u626B\u7801\u521B\u5EFA\u5E94\u7528\u5931\u8D25\uFF1A${errorMessage2(error)}`);
|
|
5409
5433
|
}
|
|
5410
5434
|
}
|
|
5411
5435
|
function mergeStartEnv(options) {
|
|
@@ -5417,28 +5441,25 @@ function mergeStartEnv(options) {
|
|
|
5417
5441
|
...options.appSecret ? { DSH_LARK_APP_SECRET: options.appSecret } : {}
|
|
5418
5442
|
};
|
|
5419
5443
|
}
|
|
5420
|
-
async function
|
|
5421
|
-
const env =
|
|
5444
|
+
async function startBridgeEngine(options) {
|
|
5445
|
+
const env = options.env;
|
|
5422
5446
|
const paths = resolveAppPaths(env.home);
|
|
5423
|
-
const profileName = options.
|
|
5447
|
+
const profileName = options.profileName;
|
|
5424
5448
|
const configStore = new ConfigStore(paths.configFile);
|
|
5425
5449
|
await configStore.load();
|
|
5426
5450
|
const ready = await ensureBotProfile(configStore, {
|
|
5427
5451
|
env,
|
|
5428
5452
|
profileName,
|
|
5429
|
-
allowOnboarding:
|
|
5453
|
+
allowOnboarding: options.allowOnboarding
|
|
5430
5454
|
});
|
|
5431
5455
|
if (!ready) {
|
|
5432
|
-
|
|
5433
|
-
return;
|
|
5456
|
+
throw new Error("bot profile \u672A\u5C31\u7EEA\u3002");
|
|
5434
5457
|
}
|
|
5435
5458
|
const activeProfile = configStore.getProfile(profileName);
|
|
5436
5459
|
if (!activeProfile) {
|
|
5437
|
-
|
|
5438
|
-
process.exitCode = 1;
|
|
5439
|
-
return;
|
|
5460
|
+
throw new Error("\u672C\u5730\u914D\u7F6E\u8BFB\u53D6\u5931\u8D25\uFF0C\u8BF7\u68C0\u67E5\u540E\u91CD\u8BD5\u3002");
|
|
5440
5461
|
}
|
|
5441
|
-
const defaultWorkspace =
|
|
5462
|
+
const defaultWorkspace = activeProfile.workspaces.default ?? env.workspace ?? paths.profilePath(profileName, "workspace");
|
|
5442
5463
|
await mkdir9(defaultWorkspace, { recursive: true });
|
|
5443
5464
|
const sessions = new SessionStore(paths.sessionsFile(profileName));
|
|
5444
5465
|
const archiver = new SessionArchive(paths.archivesDir(profileName));
|
|
@@ -5454,19 +5475,10 @@ async function runBot(options) {
|
|
|
5454
5475
|
roleStore.load(),
|
|
5455
5476
|
scopeDirectory.load()
|
|
5456
5477
|
]);
|
|
5457
|
-
|
|
5458
|
-
|
|
5459
|
-
|
|
5460
|
-
|
|
5461
|
-
model: activeProfile.preferences.model
|
|
5462
|
-
});
|
|
5463
|
-
} catch (error) {
|
|
5464
|
-
log.fail("adapter", error);
|
|
5465
|
-
process.stderr.write(`agent adapter \u521D\u59CB\u5316\u5931\u8D25\uFF1A${errorMessage2(error)}
|
|
5466
|
-
`);
|
|
5467
|
-
process.exitCode = 1;
|
|
5468
|
-
return;
|
|
5469
|
-
}
|
|
5478
|
+
const adapter = options.adapter ?? await buildAgentAdapter(env, {
|
|
5479
|
+
stopGraceMs: activeProfile.preferences.stopGraceMs ?? env.stopGraceMs,
|
|
5480
|
+
model: activeProfile.preferences.model
|
|
5481
|
+
});
|
|
5470
5482
|
const activeRuns = new ActiveRuns();
|
|
5471
5483
|
const runPolicies = new RunPolicyStore();
|
|
5472
5484
|
const concurrencyStore = new ConcurrencyStore();
|
|
@@ -5582,7 +5594,8 @@ async function runBot(options) {
|
|
|
5582
5594
|
accessDefaultDeny: env.accessDefaultDeny,
|
|
5583
5595
|
eventFreshnessMs: env.eventFreshnessMs,
|
|
5584
5596
|
allowedUsers: activeProfile.access.allowedUsers,
|
|
5585
|
-
allowedChats: activeProfile.access.allowedChats
|
|
5597
|
+
allowedChats: activeProfile.access.allowedChats,
|
|
5598
|
+
...options.createChannel ? { createChannel: options.createChannel } : {}
|
|
5586
5599
|
};
|
|
5587
5600
|
if (activeProfile.preferences.stopGraceMs !== void 0) {
|
|
5588
5601
|
channelInput.stopGraceMs = activeProfile.preferences.stopGraceMs;
|
|
@@ -5592,24 +5605,54 @@ async function runBot(options) {
|
|
|
5592
5605
|
larkChannel = bridge.channel;
|
|
5593
5606
|
await notifyServer.start();
|
|
5594
5607
|
process.env.DSH_LARK_NOTIFY_URL = notifyServer.url ?? "";
|
|
5608
|
+
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
5595
5609
|
log.info("cli", "started", {
|
|
5596
5610
|
profile: profileName,
|
|
5597
5611
|
home: paths.root,
|
|
5598
5612
|
tenant: activeProfile.tenant,
|
|
5599
|
-
workspace: defaultWorkspace
|
|
5613
|
+
workspace: defaultWorkspace,
|
|
5614
|
+
mode: "engine"
|
|
5615
|
+
});
|
|
5616
|
+
let stopped = false;
|
|
5617
|
+
return {
|
|
5618
|
+
profile: profileName,
|
|
5619
|
+
home: paths.root,
|
|
5620
|
+
status: () => ({
|
|
5621
|
+
state: stopped ? "stopped" : "running",
|
|
5622
|
+
profile: profileName,
|
|
5623
|
+
home: paths.root,
|
|
5624
|
+
adapterId: adapter.id,
|
|
5625
|
+
startedAt,
|
|
5626
|
+
workspace: defaultWorkspace,
|
|
5627
|
+
notifyUrl: notifyServer.url
|
|
5628
|
+
}),
|
|
5629
|
+
stop: async () => {
|
|
5630
|
+
if (stopped) return;
|
|
5631
|
+
stopped = true;
|
|
5632
|
+
await notifyServer.stop();
|
|
5633
|
+
await bridge.disconnect();
|
|
5634
|
+
await adapter.dispose?.();
|
|
5635
|
+
await Promise.all([
|
|
5636
|
+
sessions.flush(),
|
|
5637
|
+
workspaces.flush(),
|
|
5638
|
+
roleStore.flush(),
|
|
5639
|
+
scopeDirectory.flush()
|
|
5640
|
+
]);
|
|
5641
|
+
}
|
|
5642
|
+
};
|
|
5643
|
+
}
|
|
5644
|
+
async function runBot(options) {
|
|
5645
|
+
const env = loadRuntimeEnv(mergeStartEnv(options));
|
|
5646
|
+
const profileName = options.profile ?? "default";
|
|
5647
|
+
const engine = await startBridgeEngine({
|
|
5648
|
+
env,
|
|
5649
|
+
profileName,
|
|
5650
|
+
allowOnboarding: false
|
|
5600
5651
|
});
|
|
5601
5652
|
process.stdout.write(`dsh-lark-bot \u5DF2\u542F\u52A8\uFF0Cprofile=${profileName}
|
|
5602
5653
|
`);
|
|
5603
5654
|
await waitForShutdown();
|
|
5604
|
-
await
|
|
5605
|
-
await bridge.disconnect();
|
|
5606
|
-
await adapter.dispose?.();
|
|
5607
|
-
await Promise.all([
|
|
5608
|
-
sessions.flush(),
|
|
5609
|
-
workspaces.flush(),
|
|
5610
|
-
roleStore.flush(),
|
|
5611
|
-
scopeDirectory.flush()
|
|
5612
|
-
]);
|
|
5655
|
+
await engine.stop();
|
|
5613
5656
|
}
|
|
5614
5657
|
function errorMessage2(error) {
|
|
5615
5658
|
return error instanceof Error ? error.message : String(error);
|
|
@@ -5626,917 +5669,88 @@ function waitForShutdown() {
|
|
|
5626
5669
|
});
|
|
5627
5670
|
}
|
|
5628
5671
|
|
|
5629
|
-
// src/
|
|
5630
|
-
|
|
5631
|
-
|
|
5632
|
-
|
|
5633
|
-
// src/service/command.ts
|
|
5634
|
-
import { execFile as execFile3 } from "child_process";
|
|
5635
|
-
import { basename, dirname as dirname5, join as join12 } from "path";
|
|
5636
|
-
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
5637
|
-
var runCommand = (command, args, options) => new Promise((resolve5) => {
|
|
5638
|
-
execFile3(
|
|
5639
|
-
command,
|
|
5640
|
-
[...args],
|
|
5641
|
-
{
|
|
5642
|
-
env: options?.env,
|
|
5643
|
-
maxBuffer: 16 * 1024 * 1024,
|
|
5644
|
-
windowsHide: true
|
|
5645
|
-
},
|
|
5646
|
-
(error, stdout, stderr) => {
|
|
5647
|
-
let code = 0;
|
|
5648
|
-
if (error) {
|
|
5649
|
-
const errorCode = error.code;
|
|
5650
|
-
code = typeof errorCode === "number" ? errorCode : 1;
|
|
5651
|
-
}
|
|
5652
|
-
resolve5({ code, stdout: stdout ?? "", stderr: stderr ?? "" });
|
|
5653
|
-
}
|
|
5654
|
-
);
|
|
5655
|
-
});
|
|
5656
|
-
function resolveCliJsPath(metaUrl = import.meta.url) {
|
|
5657
|
-
const current = fileURLToPath2(metaUrl);
|
|
5658
|
-
const name = basename(current);
|
|
5659
|
-
if (name === "cli.js" || name === "cli.mjs" || name === "cli.cjs") return current;
|
|
5660
|
-
return join12(dirname5(current), "..", "dist", "cli.js");
|
|
5661
|
-
}
|
|
5662
|
-
function slugify3(value) {
|
|
5663
|
-
const slug = value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
5664
|
-
return slug || "default";
|
|
5665
|
-
}
|
|
5666
|
-
function serviceNameFor(profile) {
|
|
5667
|
-
return profile === "default" ? "dsh-lark-bot" : `dsh-lark-bot-${slugify3(profile)}`;
|
|
5668
|
-
}
|
|
5669
|
-
function launchdLabelFor(profile) {
|
|
5670
|
-
return profile === "default" ? "io.dsh-lark-bot" : `io.dsh-lark-bot.${slugify3(profile)}`;
|
|
5671
|
-
}
|
|
5672
|
-
|
|
5673
|
-
// src/service/env-snapshot.ts
|
|
5674
|
-
import { readFile as readFile9 } from "fs/promises";
|
|
5675
|
-
var EXTRA_KEYS = ["PATH", "HOME", "DEEPSEEK_API_KEY", "DSH_HOME"];
|
|
5676
|
-
function snapshotServiceEnv(source = process.env) {
|
|
5677
|
-
const env = {};
|
|
5678
|
-
for (const [key, value] of Object.entries(source)) {
|
|
5679
|
-
if (value === void 0) continue;
|
|
5680
|
-
if (key.startsWith("DSH_LARK_") || EXTRA_KEYS.includes(key)) {
|
|
5681
|
-
env[key] = value;
|
|
5682
|
-
}
|
|
5683
|
-
}
|
|
5684
|
-
return env;
|
|
5685
|
-
}
|
|
5686
|
-
function formatEnvFile(env) {
|
|
5687
|
-
const lines = [];
|
|
5688
|
-
for (const [key, value] of Object.entries(env)) {
|
|
5689
|
-
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key)) {
|
|
5690
|
-
throw new Error(`invalid service env key: ${key}`);
|
|
5691
|
-
}
|
|
5692
|
-
if (/[\r\n]/.test(value)) {
|
|
5693
|
-
throw new Error(`service env value for ${key} contains a newline`);
|
|
5694
|
-
}
|
|
5695
|
-
const escaped = value.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
5696
|
-
lines.push(`${key}="${escaped}"`);
|
|
5697
|
-
}
|
|
5698
|
-
return `${lines.join("\n")}
|
|
5699
|
-
`;
|
|
5700
|
-
}
|
|
5701
|
-
function parseEnvFile(text) {
|
|
5702
|
-
const env = {};
|
|
5703
|
-
for (const rawLine of text.split("\n")) {
|
|
5704
|
-
const line = rawLine.trim();
|
|
5705
|
-
if (!line || line.startsWith("#") || line.startsWith(";")) continue;
|
|
5706
|
-
const eq = line.indexOf("=");
|
|
5707
|
-
if (eq <= 0) continue;
|
|
5708
|
-
const key = line.slice(0, eq).trim();
|
|
5709
|
-
let value = line.slice(eq + 1).trim();
|
|
5710
|
-
if (value.length >= 2 && value.startsWith('"') && value.endsWith('"')) {
|
|
5711
|
-
value = value.slice(1, -1).replace(/\\(.)/g, "$1");
|
|
5712
|
-
} else if (value.length >= 2 && value.startsWith("'") && value.endsWith("'")) {
|
|
5713
|
-
value = value.slice(1, -1);
|
|
5714
|
-
}
|
|
5715
|
-
env[key] = value;
|
|
5716
|
-
}
|
|
5717
|
-
return env;
|
|
5718
|
-
}
|
|
5719
|
-
async function writeServiceEnv(file, env) {
|
|
5720
|
-
await writeFileAtomic(file, formatEnvFile(env), { mode: 384 });
|
|
5721
|
-
}
|
|
5722
|
-
async function readServiceEnv(file) {
|
|
5723
|
-
try {
|
|
5724
|
-
return parseEnvFile(await readFile9(file, "utf8"));
|
|
5725
|
-
} catch (error) {
|
|
5726
|
-
if (error.code === "ENOENT") return {};
|
|
5727
|
-
throw error;
|
|
5728
|
-
}
|
|
5729
|
-
}
|
|
5730
|
-
|
|
5731
|
-
// src/service/linux-systemd.ts
|
|
5732
|
-
import { mkdir as mkdir10, stat as stat3 } from "fs/promises";
|
|
5733
|
-
import { homedir as homedir6 } from "os";
|
|
5734
|
-
import { join as join13 } from "path";
|
|
5735
|
-
function defaultSystemdUnitDir() {
|
|
5736
|
-
return join13(homedir6(), ".config", "systemd", "user");
|
|
5737
|
-
}
|
|
5738
|
-
function quoteSystemdArg(arg) {
|
|
5739
|
-
if (!/[\s"\\]/.test(arg)) return arg;
|
|
5740
|
-
return `"${arg.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
|
|
5741
|
-
}
|
|
5742
|
-
function buildSystemdUnit(spec) {
|
|
5743
|
-
const exec = [spec.nodePath, spec.cliJsPath, "run", "--profile", spec.profile].map(quoteSystemdArg).join(" ");
|
|
5744
|
-
return [
|
|
5745
|
-
"[Unit]",
|
|
5746
|
-
"Description=dsh-lark-bot bridge service",
|
|
5747
|
-
"After=network-online.target",
|
|
5748
|
-
"Wants=network-online.target",
|
|
5749
|
-
"",
|
|
5750
|
-
"[Service]",
|
|
5751
|
-
"Type=simple",
|
|
5752
|
-
`EnvironmentFile=${quoteSystemdArg(spec.envFile)}`,
|
|
5753
|
-
`ExecStart=${exec}`,
|
|
5754
|
-
"Restart=always",
|
|
5755
|
-
"RestartSec=5",
|
|
5756
|
-
"KillMode=mixed",
|
|
5757
|
-
"TimeoutStopSec=30",
|
|
5758
|
-
`StandardOutput=append:${spec.logFile}`,
|
|
5759
|
-
`StandardError=append:${spec.logFile}`,
|
|
5760
|
-
"",
|
|
5761
|
-
"[Install]",
|
|
5762
|
-
"WantedBy=default.target",
|
|
5763
|
-
""
|
|
5764
|
-
].join("\n");
|
|
5765
|
-
}
|
|
5766
|
-
async function isSystemdUserAvailable(runner = runCommand) {
|
|
5767
|
-
const result = await runner("systemctl", ["--user", "show-environment"]);
|
|
5768
|
-
return result.code === 0;
|
|
5769
|
-
}
|
|
5770
|
-
async function fileExists(path) {
|
|
5771
|
-
try {
|
|
5772
|
-
return (await stat3(path)).isFile();
|
|
5773
|
-
} catch {
|
|
5774
|
-
return false;
|
|
5775
|
-
}
|
|
5776
|
-
}
|
|
5777
|
-
function parseMainPid(value) {
|
|
5778
|
-
const raw = value?.trim();
|
|
5779
|
-
if (!raw || raw === "0" || raw === "4294967295") return void 0;
|
|
5780
|
-
const pid = Number(raw);
|
|
5781
|
-
return Number.isInteger(pid) && pid > 0 ? pid : void 0;
|
|
5782
|
-
}
|
|
5783
|
-
function parseNumber(value) {
|
|
5784
|
-
const raw = value?.trim();
|
|
5785
|
-
if (!raw) return void 0;
|
|
5786
|
-
const parsed = Number(raw);
|
|
5787
|
-
return Number.isInteger(parsed) ? parsed : void 0;
|
|
5788
|
-
}
|
|
5789
|
-
var SystemdServiceController = class {
|
|
5790
|
-
platform = "linux-systemd";
|
|
5791
|
-
run;
|
|
5792
|
-
unitDir;
|
|
5793
|
-
constructor(options = {}) {
|
|
5794
|
-
this.run = options.runCommand ?? runCommand;
|
|
5795
|
-
this.unitDir = options.userUnitDir ?? defaultSystemdUnitDir();
|
|
5796
|
-
}
|
|
5797
|
-
unitPath(spec) {
|
|
5798
|
-
return join13(this.unitDir, `${spec.serviceName}.service`);
|
|
5799
|
-
}
|
|
5800
|
-
unitName(spec) {
|
|
5801
|
-
return `${spec.serviceName}.service`;
|
|
5802
|
-
}
|
|
5803
|
-
async isActive(name) {
|
|
5804
|
-
const result = await this.run("systemctl", ["--user", "is-active", name]);
|
|
5805
|
-
return result.code === 0;
|
|
5806
|
-
}
|
|
5807
|
-
async installAndStart(spec) {
|
|
5808
|
-
await mkdir10(this.unitDir, { recursive: true });
|
|
5809
|
-
await writeFileAtomic(this.unitPath(spec), buildSystemdUnit(spec));
|
|
5810
|
-
await this.run("systemctl", ["--user", "daemon-reload"]);
|
|
5811
|
-
const name = this.unitName(spec);
|
|
5812
|
-
if (await this.isActive(name)) {
|
|
5813
|
-
await this.run("systemctl", ["--user", "restart", name]);
|
|
5814
|
-
} else {
|
|
5815
|
-
await this.run("systemctl", ["--user", "enable", "--now", name]);
|
|
5816
|
-
}
|
|
5817
|
-
}
|
|
5818
|
-
async stop(spec) {
|
|
5819
|
-
await this.run("systemctl", ["--user", "disable", "--now", this.unitName(spec)]);
|
|
5820
|
-
}
|
|
5821
|
-
async restart(spec) {
|
|
5822
|
-
await this.run("systemctl", ["--user", "daemon-reload"]);
|
|
5823
|
-
await this.run("systemctl", ["--user", "restart", this.unitName(spec)]);
|
|
5824
|
-
}
|
|
5825
|
-
async status(spec) {
|
|
5826
|
-
const name = this.unitName(spec);
|
|
5827
|
-
const unitFile = this.unitPath(spec);
|
|
5828
|
-
const installed = await fileExists(unitFile);
|
|
5829
|
-
let autostartEnabled = false;
|
|
5830
|
-
if (installed) {
|
|
5831
|
-
const enabled = await this.run("systemctl", ["--user", "is-enabled", name]);
|
|
5832
|
-
autostartEnabled = enabled.code === 0 && enabled.stdout.trim() === "enabled";
|
|
5833
|
-
}
|
|
5834
|
-
const active = await this.run("systemctl", ["--user", "is-active", name]);
|
|
5835
|
-
const isActive = active.code === 0;
|
|
5836
|
-
let detail = active.stdout.trim() || (isActive ? "active" : "inactive");
|
|
5837
|
-
let pid;
|
|
5838
|
-
let restarts;
|
|
5839
|
-
if (isActive || installed) {
|
|
5840
|
-
const show = await this.run(
|
|
5841
|
-
"systemctl",
|
|
5842
|
-
["--user", "show", name, "-p", "MainPID", "-p", "ActiveState", "-p", "NRestarts", "--value"]
|
|
5843
|
-
);
|
|
5844
|
-
const [activeState, mainPid, nRestarts] = show.stdout.trim().split("\n");
|
|
5845
|
-
if (activeState?.trim()) detail = activeState.trim();
|
|
5846
|
-
pid = parseMainPid(mainPid);
|
|
5847
|
-
restarts = parseNumber(nRestarts);
|
|
5848
|
-
}
|
|
5849
|
-
let state;
|
|
5850
|
-
if (!installed) {
|
|
5851
|
-
state = "stopped";
|
|
5852
|
-
} else if (detail === "failed") {
|
|
5853
|
-
state = "error";
|
|
5854
|
-
} else if (isActive || detail === "activating" || detail === "deactivating" || detail === "reloading") {
|
|
5855
|
-
state = "running";
|
|
5856
|
-
} else {
|
|
5857
|
-
state = "stopped";
|
|
5858
|
-
}
|
|
5859
|
-
return {
|
|
5860
|
-
name: spec.serviceName,
|
|
5861
|
-
platform: this.platform,
|
|
5862
|
-
installed,
|
|
5863
|
-
autostartEnabled,
|
|
5864
|
-
state,
|
|
5865
|
-
pid,
|
|
5866
|
-
detail,
|
|
5867
|
-
restarts
|
|
5868
|
-
};
|
|
5869
|
-
}
|
|
5870
|
-
};
|
|
5871
|
-
|
|
5872
|
-
// src/service/macos-launchd.ts
|
|
5873
|
-
import { mkdir as mkdir11, rm as rm2, stat as stat4 } from "fs/promises";
|
|
5874
|
-
import { homedir as homedir7 } from "os";
|
|
5875
|
-
import { join as join14 } from "path";
|
|
5876
|
-
function defaultLaunchAgentDir() {
|
|
5877
|
-
return join14(homedir7(), "Library", "LaunchAgents");
|
|
5878
|
-
}
|
|
5879
|
-
function xmlEscape(value) {
|
|
5880
|
-
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
5881
|
-
}
|
|
5882
|
-
function buildLaunchdPlist(spec) {
|
|
5883
|
-
const envLines = Object.entries(spec.env).filter(([, value]) => value !== void 0).map(
|
|
5884
|
-
([key, value]) => ` <key>${xmlEscape(key)}</key>
|
|
5885
|
-
<string>${xmlEscape(value)}</string>`
|
|
5886
|
-
).join("\n");
|
|
5887
|
-
return [
|
|
5888
|
-
'<?xml version="1.0" encoding="UTF-8"?>',
|
|
5889
|
-
'<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">',
|
|
5890
|
-
'<plist version="1.0">',
|
|
5891
|
-
"<dict>",
|
|
5892
|
-
" <key>Label</key>",
|
|
5893
|
-
` <string>${xmlEscape(spec.label)}</string>`,
|
|
5894
|
-
" <key>ProgramArguments</key>",
|
|
5895
|
-
" <array>",
|
|
5896
|
-
` <string>${xmlEscape(spec.nodePath)}</string>`,
|
|
5897
|
-
` <string>${xmlEscape(spec.cliJsPath)}</string>`,
|
|
5898
|
-
" <string>run</string>",
|
|
5899
|
-
" <string>--profile</string>",
|
|
5900
|
-
` <string>${xmlEscape(spec.profile)}</string>`,
|
|
5901
|
-
" </array>",
|
|
5902
|
-
" <key>RunAtLoad</key>",
|
|
5903
|
-
" <true/>",
|
|
5904
|
-
" <key>KeepAlive</key>",
|
|
5905
|
-
" <true/>",
|
|
5906
|
-
" <key>EnvironmentVariables</key>",
|
|
5907
|
-
" <dict>",
|
|
5908
|
-
envLines,
|
|
5909
|
-
" </dict>",
|
|
5910
|
-
" <key>StandardOutPath</key>",
|
|
5911
|
-
` <string>${xmlEscape(spec.logFile)}</string>`,
|
|
5912
|
-
" <key>StandardErrorPath</key>",
|
|
5913
|
-
` <string>${xmlEscape(spec.logFile)}</string>`,
|
|
5914
|
-
" <key>ProcessType</key>",
|
|
5915
|
-
" <string>Background</string>",
|
|
5916
|
-
"</dict>",
|
|
5917
|
-
"</plist>",
|
|
5918
|
-
""
|
|
5919
|
-
].join("\n");
|
|
5920
|
-
}
|
|
5921
|
-
async function fileExists2(path) {
|
|
5922
|
-
try {
|
|
5923
|
-
return (await stat4(path)).isFile();
|
|
5924
|
-
} catch {
|
|
5925
|
-
return false;
|
|
5926
|
-
}
|
|
5927
|
-
}
|
|
5928
|
-
var LaunchdServiceController = class {
|
|
5929
|
-
platform = "darwin-launchd";
|
|
5930
|
-
run;
|
|
5931
|
-
agentDir;
|
|
5932
|
-
uid;
|
|
5933
|
-
constructor(options = {}) {
|
|
5934
|
-
this.run = options.runCommand ?? runCommand;
|
|
5935
|
-
this.agentDir = options.launchAgentDir ?? defaultLaunchAgentDir();
|
|
5936
|
-
this.uid = options.uid ?? process.getuid?.() ?? -1;
|
|
5937
|
-
}
|
|
5938
|
-
domain() {
|
|
5939
|
-
return `gui/${this.uid}`;
|
|
5940
|
-
}
|
|
5941
|
-
plistPath(spec) {
|
|
5942
|
-
return join14(this.agentDir, `${spec.label}.plist`);
|
|
5943
|
-
}
|
|
5944
|
-
serviceRef(spec) {
|
|
5945
|
-
return `${this.domain()}/${spec.label}`;
|
|
5946
|
-
}
|
|
5947
|
-
async installAndStart(spec) {
|
|
5948
|
-
await mkdir11(this.agentDir, { recursive: true });
|
|
5949
|
-
await writeFileAtomic(this.plistPath(spec), buildLaunchdPlist(spec));
|
|
5950
|
-
const bootstrap = await this.run("launchctl", ["bootstrap", this.domain(), this.plistPath(spec)]);
|
|
5951
|
-
if (bootstrap.code !== 0) {
|
|
5952
|
-
await this.run("launchctl", ["kickstart", "-k", this.serviceRef(spec)]);
|
|
5953
|
-
}
|
|
5954
|
-
}
|
|
5955
|
-
async stop(spec) {
|
|
5956
|
-
await this.run("launchctl", ["bootout", this.serviceRef(spec)]);
|
|
5957
|
-
await rm2(this.plistPath(spec), { force: true });
|
|
5958
|
-
}
|
|
5959
|
-
async restart(spec) {
|
|
5960
|
-
const kickstart = await this.run("launchctl", ["kickstart", "-k", this.serviceRef(spec)]);
|
|
5961
|
-
if (kickstart.code !== 0) {
|
|
5962
|
-
await this.installAndStart(spec);
|
|
5963
|
-
}
|
|
5964
|
-
}
|
|
5965
|
-
async status(spec) {
|
|
5966
|
-
const plistExists = await fileExists2(this.plistPath(spec));
|
|
5967
|
-
const printed = await this.run("launchctl", ["print", this.serviceRef(spec)]);
|
|
5968
|
-
const loaded = printed.code === 0;
|
|
5969
|
-
let pid;
|
|
5970
|
-
let state = "stopped";
|
|
5971
|
-
let detail = loaded ? "loaded" : "not loaded";
|
|
5972
|
-
if (loaded) {
|
|
5973
|
-
const pidMatch = /pid = (\d+)/.exec(printed.stdout);
|
|
5974
|
-
if (pidMatch?.[1]) pid = Number(pidMatch[1]);
|
|
5975
|
-
if (/state = running/.test(printed.stdout)) {
|
|
5976
|
-
state = "running";
|
|
5977
|
-
detail = "running";
|
|
5978
|
-
} else if (/state = exited|state = waiting/.test(printed.stdout)) {
|
|
5979
|
-
state = "stopped";
|
|
5980
|
-
detail = /state = exited/.test(printed.stdout) ? "exited" : "waiting";
|
|
5981
|
-
}
|
|
5982
|
-
}
|
|
5983
|
-
return {
|
|
5984
|
-
name: spec.serviceName,
|
|
5985
|
-
platform: this.platform,
|
|
5986
|
-
installed: plistExists,
|
|
5987
|
-
autostartEnabled: loaded,
|
|
5988
|
-
state,
|
|
5989
|
-
pid,
|
|
5990
|
-
detail,
|
|
5991
|
-
restarts: void 0
|
|
5992
|
-
};
|
|
5993
|
-
}
|
|
5994
|
-
};
|
|
5995
|
-
|
|
5996
|
-
// src/service/portable.ts
|
|
5672
|
+
// src/cli/commands/setup.ts
|
|
5673
|
+
init_dsh_runtime();
|
|
5674
|
+
init_own_package();
|
|
5997
5675
|
import { spawn as spawn5 } from "child_process";
|
|
5998
|
-
import { mkdir as
|
|
5999
|
-
import { homedir as
|
|
6000
|
-
import { join as
|
|
6001
|
-
function
|
|
6002
|
-
|
|
6003
|
-
|
|
6004
|
-
|
|
6005
|
-
const
|
|
6006
|
-
|
|
6007
|
-
|
|
6008
|
-
|
|
6009
|
-
|
|
6010
|
-
|
|
6011
|
-
"[Desktop Entry]",
|
|
6012
|
-
"Type=Application",
|
|
6013
|
-
`Name=${spec.serviceName}`,
|
|
6014
|
-
"Comment=Keep dsh-lark-bot running in the background",
|
|
6015
|
-
`Exec=${exec}`,
|
|
6016
|
-
"Terminal=false",
|
|
6017
|
-
"X-GNOME-Autostart-enabled=true",
|
|
6018
|
-
"Hidden=false",
|
|
6019
|
-
""
|
|
6020
|
-
].join("\n");
|
|
6021
|
-
}
|
|
6022
|
-
function supervisorStatusFile(serviceDir, serviceName) {
|
|
6023
|
-
return join15(serviceDir, `supervisor-${serviceName}.json`);
|
|
6024
|
-
}
|
|
6025
|
-
function isProcessAlive(pid) {
|
|
6026
|
-
try {
|
|
6027
|
-
process.kill(pid, 0);
|
|
6028
|
-
return true;
|
|
6029
|
-
} catch (error) {
|
|
6030
|
-
return error.code === "EPERM";
|
|
6031
|
-
}
|
|
6032
|
-
}
|
|
6033
|
-
async function readSupervisorStatus(file) {
|
|
6034
|
-
try {
|
|
6035
|
-
return JSON.parse(await readFile10(file, "utf8"));
|
|
6036
|
-
} catch {
|
|
6037
|
-
return void 0;
|
|
6038
|
-
}
|
|
6039
|
-
}
|
|
6040
|
-
function defaultSpawnDetached(nodePath, args) {
|
|
6041
|
-
const child = spawn5(nodePath, [...args], {
|
|
6042
|
-
detached: true,
|
|
6043
|
-
stdio: "ignore",
|
|
6044
|
-
env: process.env
|
|
6045
|
-
});
|
|
6046
|
-
child.unref();
|
|
6047
|
-
return child;
|
|
6048
|
-
}
|
|
6049
|
-
var PortableServiceController = class {
|
|
6050
|
-
platform = "linux-portable";
|
|
6051
|
-
autostartDir;
|
|
6052
|
-
serviceDir;
|
|
6053
|
-
spawnDetached;
|
|
6054
|
-
constructor(options = {}) {
|
|
6055
|
-
this.autostartDir = options.autostartDir ?? defaultAutostartDir();
|
|
6056
|
-
this.serviceDir = options.serviceDir ?? "";
|
|
6057
|
-
this.spawnDetached = options.spawnDetached ?? defaultSpawnDetached;
|
|
6058
|
-
}
|
|
6059
|
-
desktopPath(spec) {
|
|
6060
|
-
return join15(this.autostartDir, `${spec.serviceName}.desktop`);
|
|
6061
|
-
}
|
|
6062
|
-
statusFile(spec) {
|
|
6063
|
-
return supervisorStatusFile(this.serviceDir, spec.serviceName);
|
|
6064
|
-
}
|
|
6065
|
-
async sendStopSignal(spec) {
|
|
6066
|
-
const status = await readSupervisorStatus(this.statusFile(spec));
|
|
6067
|
-
if (!status) return;
|
|
6068
|
-
try {
|
|
6069
|
-
process.kill(status.pid, "SIGTERM");
|
|
6070
|
-
} catch {
|
|
6071
|
-
return;
|
|
6072
|
-
}
|
|
6073
|
-
for (let attempt = 0; attempt < 20; attempt += 1) {
|
|
6074
|
-
await new Promise((resolvePromise) => setTimeout(resolvePromise, 250));
|
|
6075
|
-
if (!isProcessAlive(status.pid)) return;
|
|
6076
|
-
}
|
|
6077
|
-
try {
|
|
6078
|
-
process.kill(status.pid, "SIGKILL");
|
|
6079
|
-
} catch {
|
|
6080
|
-
}
|
|
6081
|
-
}
|
|
6082
|
-
async installAndStart(spec) {
|
|
6083
|
-
await this.sendStopSignal(spec);
|
|
6084
|
-
await mkdir12(this.autostartDir, { recursive: true });
|
|
6085
|
-
await writeFile6(this.desktopPath(spec), buildDesktopEntry(spec), { encoding: "utf8", mode: 420 });
|
|
6086
|
-
if (!this.serviceDir) throw new Error("serviceDir is required for the portable supervisor");
|
|
6087
|
-
await mkdir12(this.serviceDir, { recursive: true });
|
|
6088
|
-
this.spawnDetached(spec.nodePath, [spec.cliJsPath, "supervise", "--profile", spec.profile]);
|
|
6089
|
-
}
|
|
6090
|
-
async stop(spec) {
|
|
6091
|
-
await this.sendStopSignal(spec);
|
|
6092
|
-
await rm3(this.desktopPath(spec), { force: true });
|
|
6093
|
-
}
|
|
6094
|
-
async restart(spec) {
|
|
6095
|
-
await this.stop(spec);
|
|
6096
|
-
await this.installAndStart(spec);
|
|
6097
|
-
}
|
|
6098
|
-
async status(spec) {
|
|
6099
|
-
let desktopExists = false;
|
|
6100
|
-
try {
|
|
6101
|
-
desktopExists = (await stat5(this.desktopPath(spec))).isFile();
|
|
6102
|
-
} catch {
|
|
6103
|
-
desktopExists = false;
|
|
6104
|
-
}
|
|
6105
|
-
const status = await readSupervisorStatus(this.statusFile(spec));
|
|
6106
|
-
const alive = status !== void 0 && isProcessAlive(status.pid);
|
|
6107
|
-
return {
|
|
6108
|
-
name: spec.serviceName,
|
|
6109
|
-
platform: this.platform,
|
|
6110
|
-
installed: desktopExists,
|
|
6111
|
-
autostartEnabled: desktopExists,
|
|
6112
|
-
state: alive ? "running" : "stopped",
|
|
6113
|
-
pid: status?.childPid,
|
|
6114
|
-
detail: alive ? `supervisor ${status?.pid ?? "?"}` : "stopped",
|
|
6115
|
-
restarts: status?.restarts
|
|
6116
|
-
};
|
|
6117
|
-
}
|
|
6118
|
-
};
|
|
6119
|
-
|
|
6120
|
-
// src/service/windows-task.ts
|
|
6121
|
-
import { mkdir as mkdir13, writeFile as writeFile7 } from "fs/promises";
|
|
6122
|
-
import { dirname as dirname6, join as join16 } from "path";
|
|
6123
|
-
function psQuote(value) {
|
|
6124
|
-
return `'${value.replace(/'/g, "''")}'`;
|
|
6125
|
-
}
|
|
6126
|
-
function buildScheduledTaskScript(action, spec) {
|
|
6127
|
-
const vars = [
|
|
6128
|
-
`$taskName = ${psQuote(spec.serviceName)}`,
|
|
6129
|
-
`$node = ${psQuote(spec.nodePath)}`,
|
|
6130
|
-
`$cli = ${psQuote(spec.cliJsPath)}`,
|
|
6131
|
-
`$log = ${psQuote(spec.logFile)}`,
|
|
6132
|
-
`$profile = ${psQuote(spec.profile)}`,
|
|
6133
|
-
`$workDir = ${psQuote(dirname6(spec.cliJsPath))}`
|
|
6134
|
-
].join("\n");
|
|
6135
|
-
const install = [
|
|
6136
|
-
`$inner = '"' + $node + '" "' + $cli + '" run --profile "' + $profile + '" >> "' + $log + '" 2>&1'`,
|
|
6137
|
-
`$action = New-ScheduledTaskAction -Execute 'cmd.exe' -Argument ('/c ' + $inner) -WorkingDirectory $workDir`,
|
|
6138
|
-
`$trigger = New-ScheduledTaskTrigger -AtLogOn`,
|
|
6139
|
-
`$settings = New-ScheduledTaskSettingsSet -RestartCount 5 -RestartInterval (New-TimeSpan -Minutes 1) -ExecutionTimeLimit ([TimeSpan]::Zero) -MultipleInstances IgnoreNew -StartWhenAvailable -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries`,
|
|
6140
|
-
`Unregister-ScheduledTask -TaskName $taskName -Confirm:$false -ErrorAction SilentlyContinue`,
|
|
6141
|
-
`Register-ScheduledTask -TaskName $taskName -Action $action -Trigger $trigger -Settings $settings -Force`,
|
|
6142
|
-
`Enable-ScheduledTask -TaskName $taskName`,
|
|
6143
|
-
`Start-ScheduledTask -TaskName $taskName`
|
|
6144
|
-
].join("\n");
|
|
6145
|
-
const stop = [
|
|
6146
|
-
`Stop-ScheduledTask -TaskName $taskName -ErrorAction SilentlyContinue`,
|
|
6147
|
-
`Disable-ScheduledTask -TaskName $taskName -ErrorAction SilentlyContinue`
|
|
6148
|
-
].join("\n");
|
|
6149
|
-
const restart = [
|
|
6150
|
-
`Stop-ScheduledTask -TaskName $taskName -ErrorAction SilentlyContinue`,
|
|
6151
|
-
`Start-ScheduledTask -TaskName $taskName`
|
|
6152
|
-
].join("\n");
|
|
6153
|
-
const status = [
|
|
6154
|
-
`$task = Get-ScheduledTask -TaskName $taskName -ErrorAction SilentlyContinue`,
|
|
6155
|
-
`if ($null -eq $task) { Write-Output 'STATE:missing'; Write-Output 'ENABLED:false'; exit 2 }`,
|
|
6156
|
-
`$info = Get-ScheduledTaskInfo -TaskName $taskName`,
|
|
6157
|
-
`Write-Output ('STATE:' + $task.State)`,
|
|
6158
|
-
`Write-Output ('ENABLED:' + $task.Settings.Enabled)`,
|
|
6159
|
-
`Write-Output ('LAST_RESULT:' + $info.LastTaskResult)`,
|
|
6160
|
-
`Write-Output ('LAST_RUN:' + $info.LastRunTime)`
|
|
6161
|
-
].join("\n");
|
|
6162
|
-
const body = {
|
|
6163
|
-
install,
|
|
6164
|
-
stop,
|
|
6165
|
-
restart,
|
|
6166
|
-
status
|
|
6167
|
-
};
|
|
6168
|
-
return [
|
|
6169
|
-
`$ErrorActionPreference = 'Stop'`,
|
|
6170
|
-
vars,
|
|
6171
|
-
body[action],
|
|
6172
|
-
""
|
|
6173
|
-
].join("\n");
|
|
6174
|
-
}
|
|
6175
|
-
function parseTaskStatusOutput(output) {
|
|
6176
|
-
const fields = /* @__PURE__ */ new Map();
|
|
6177
|
-
for (const rawLine of output.split(/\r?\n/)) {
|
|
6178
|
-
const line = rawLine.trim();
|
|
6179
|
-
const eq = line.indexOf(":");
|
|
6180
|
-
if (eq <= 0) continue;
|
|
6181
|
-
fields.set(line.slice(0, eq).trim(), line.slice(eq + 1).trim());
|
|
6182
|
-
}
|
|
6183
|
-
const stateValue = fields.get("STATE") ?? "missing";
|
|
6184
|
-
const enabled = fields.get("ENABLED") === "True";
|
|
6185
|
-
const installed = stateValue !== "missing";
|
|
6186
|
-
let state = "unknown";
|
|
6187
|
-
if (stateValue === "Running") state = "running";
|
|
6188
|
-
else if (stateValue === "Ready" || stateValue === "Disabled") state = "stopped";
|
|
6189
|
-
else if (stateValue === "Unknown") state = "unknown";
|
|
6190
|
-
else if (stateValue === "missing") state = "stopped";
|
|
6191
|
-
const lastResultRaw = fields.get("LAST_RESULT");
|
|
6192
|
-
const lastResult = lastResultRaw !== void 0 ? Number.parseInt(lastResultRaw, 10) : void 0;
|
|
6193
|
-
const lastResultValue = lastResult !== void 0 && Number.isFinite(lastResult) ? lastResult : void 0;
|
|
6194
|
-
if (installed && lastResultValue !== void 0 && lastResultValue !== 0 && stateValue === "Ready") {
|
|
6195
|
-
state = "error";
|
|
5676
|
+
import { mkdir as mkdir10, readFile as readFile9, writeFile as writeFile6 } from "fs/promises";
|
|
5677
|
+
import { homedir as homedir6 } from "os";
|
|
5678
|
+
import { join as join12 } from "path";
|
|
5679
|
+
async function runSetup(options = {}) {
|
|
5680
|
+
const profile = options.profile ?? "dsh-lark";
|
|
5681
|
+
const own = ownPackageInfo();
|
|
5682
|
+
const packageSpec = options.packageSpec ?? process.env.DSH_LARK_SETUP_PACKAGE ?? own.name;
|
|
5683
|
+
const dshHome = options.dshHome ?? resolveDshHome(homedir6(), process.env);
|
|
5684
|
+
const bin = options.bin ?? discoverDshBin(homedir6(), process.env);
|
|
5685
|
+
if (!bin) {
|
|
5686
|
+
throw new Error(
|
|
5687
|
+
"\u672A\u627E\u5230\u672C\u673A dsh \u5B89\u88C5\uFF08`@deepseek-ai/dsh`\uFF09\u3002\u8BF7\u5148\u5B89\u88C5 DeepSeek Harness\uFF0C\u518D\u8FD0\u884C\u672C\u547D\u4EE4\u3002"
|
|
5688
|
+
);
|
|
6196
5689
|
}
|
|
6197
|
-
|
|
6198
|
-
|
|
6199
|
-
|
|
6200
|
-
|
|
6201
|
-
|
|
6202
|
-
|
|
6203
|
-
|
|
5690
|
+
const profileDir = join12(dshHome, "profiles", profile);
|
|
5691
|
+
await mkdir10(profileDir, { recursive: true });
|
|
5692
|
+
await approveBuilds(profileDir);
|
|
5693
|
+
process.stdout.write(
|
|
5694
|
+
`\u6B63\u5728\u628A ${packageSpec} \u5B89\u88C5\u5230 dsh profile \`${profile}\`\uFF08$DSH_HOME=${dshHome}\uFF09...
|
|
5695
|
+
`
|
|
5696
|
+
);
|
|
5697
|
+
await runDshPlugin(bin, profile, packageSpec);
|
|
5698
|
+
process.stdout.write(
|
|
5699
|
+
[
|
|
5700
|
+
"",
|
|
5701
|
+
"\u2705 \u5B89\u88C5\u5B8C\u6210\u3002\u542F\u52A8\u65B9\u5F0F\uFF08\u552F\u4E00\u7684\u90E8\u7F72\u8DEF\u5F84\uFF09\uFF1A",
|
|
5702
|
+
"",
|
|
5703
|
+
` dsh --profile ${profile}`,
|
|
5704
|
+
"",
|
|
5705
|
+
"\u9996\u6B21\u542F\u52A8\u4F1A\u5728\u7EC8\u7AEF\u6253\u5370\u4E8C\u7EF4\u7801\uFF0C\u7528\u98DE\u4E66 / Lark \u626B\u7801\u5B8C\u6210\u4E00\u6B21\u6027\u7ED1\u5B9A\uFF1B",
|
|
5706
|
+
"\u7ED1\u5B9A\u540E dsh \u5373\u4EE5\u6807\u51C6\u63D2\u4EF6\u65B9\u5F0F\u52A0\u8F7D dsh-lark-bot \u7684\u6865\u63A5\u5F15\u64CE\u3002",
|
|
5707
|
+
""
|
|
5708
|
+
].join("\n")
|
|
5709
|
+
);
|
|
6204
5710
|
}
|
|
6205
|
-
|
|
6206
|
-
|
|
6207
|
-
|
|
6208
|
-
|
|
6209
|
-
|
|
6210
|
-
|
|
6211
|
-
|
|
6212
|
-
|
|
6213
|
-
|
|
6214
|
-
|
|
6215
|
-
|
|
6216
|
-
async runScript(spec, action) {
|
|
6217
|
-
const script = this.scriptPath(spec, action);
|
|
6218
|
-
if (this.scriptDir) await mkdir13(this.scriptDir, { recursive: true });
|
|
6219
|
-
await writeFile7(script, buildScheduledTaskScript(action, spec), { encoding: "utf8" });
|
|
6220
|
-
const result = await this.run("powershell.exe", [
|
|
6221
|
-
"-NoProfile",
|
|
6222
|
-
"-NonInteractive",
|
|
6223
|
-
"-ExecutionPolicy",
|
|
6224
|
-
"Bypass",
|
|
6225
|
-
"-File",
|
|
6226
|
-
script
|
|
6227
|
-
]);
|
|
6228
|
-
return { result, script };
|
|
6229
|
-
}
|
|
6230
|
-
async installAndStart(spec) {
|
|
6231
|
-
const { result } = await this.runScript(spec, "install");
|
|
6232
|
-
if (result.code !== 0) {
|
|
6233
|
-
throw new Error(
|
|
6234
|
-
`\u65E0\u6CD5\u6CE8\u518C Windows \u8BA1\u5212\u4EFB\u52A1\uFF08exit ${result.code}\uFF09\uFF1A${result.stderr.trim() || result.stdout.trim() || "\u672A\u77E5\u9519\u8BEF"}`
|
|
6235
|
-
);
|
|
6236
|
-
}
|
|
6237
|
-
}
|
|
6238
|
-
async stop(spec) {
|
|
6239
|
-
const { result } = await this.runScript(spec, "stop");
|
|
6240
|
-
if (result.code !== 0) {
|
|
6241
|
-
throw new Error(
|
|
6242
|
-
`\u65E0\u6CD5\u505C\u6B62 Windows \u8BA1\u5212\u4EFB\u52A1\uFF08exit ${result.code}\uFF09\uFF1A${result.stderr.trim() || result.stdout.trim() || "\u672A\u77E5\u9519\u8BEF"}`
|
|
6243
|
-
);
|
|
6244
|
-
}
|
|
6245
|
-
}
|
|
6246
|
-
async restart(spec) {
|
|
6247
|
-
const { result } = await this.runScript(spec, "restart");
|
|
6248
|
-
if (result.code !== 0) {
|
|
6249
|
-
throw new Error(
|
|
6250
|
-
`\u65E0\u6CD5\u91CD\u542F Windows \u8BA1\u5212\u4EFB\u52A1\uFF08exit ${result.code}\uFF09\uFF1A${result.stderr.trim() || result.stdout.trim() || "\u672A\u77E5\u9519\u8BEF"}`
|
|
6251
|
-
);
|
|
6252
|
-
}
|
|
6253
|
-
}
|
|
6254
|
-
async status(spec) {
|
|
6255
|
-
const { result } = await this.runScript(spec, "status");
|
|
6256
|
-
const parsed = parseTaskStatusOutput(result.stdout);
|
|
6257
|
-
if (result.code !== 0 && result.code !== 2) {
|
|
6258
|
-
throw new Error(
|
|
6259
|
-
`\u65E0\u6CD5\u67E5\u8BE2 Windows \u8BA1\u5212\u4EFB\u52A1\uFF08exit ${result.code}\uFF09\uFF1A${result.stderr.trim() || result.stdout.trim() || "\u672A\u77E5\u9519\u8BEF"}`
|
|
6260
|
-
);
|
|
6261
|
-
}
|
|
6262
|
-
return {
|
|
6263
|
-
name: spec.serviceName,
|
|
6264
|
-
platform: this.platform,
|
|
6265
|
-
installed: parsed.installed,
|
|
6266
|
-
autostartEnabled: parsed.autostartEnabled,
|
|
6267
|
-
state: parsed.state,
|
|
6268
|
-
pid: void 0,
|
|
6269
|
-
detail: parsed.detail,
|
|
6270
|
-
restarts: void 0
|
|
6271
|
-
};
|
|
6272
|
-
}
|
|
6273
|
-
};
|
|
6274
|
-
|
|
6275
|
-
// src/service/manager.ts
|
|
6276
|
-
var ServiceManager = class {
|
|
6277
|
-
profile;
|
|
6278
|
-
sourceEnv;
|
|
6279
|
-
paths;
|
|
6280
|
-
version;
|
|
6281
|
-
controllerOverride;
|
|
6282
|
-
constructor(deps = {}) {
|
|
6283
|
-
this.profile = deps.profile ?? "default";
|
|
6284
|
-
this.sourceEnv = deps.env ?? process.env;
|
|
6285
|
-
this.paths = deps.paths ?? resolveAppPaths();
|
|
6286
|
-
this.version = deps.version ?? "0.0.0";
|
|
6287
|
-
this.controllerOverride = deps.controller;
|
|
6288
|
-
}
|
|
6289
|
-
buildSpec() {
|
|
6290
|
-
const serviceName = serviceNameFor(this.profile);
|
|
6291
|
-
return {
|
|
6292
|
-
serviceName,
|
|
6293
|
-
label: launchdLabelFor(this.profile),
|
|
6294
|
-
profile: this.profile,
|
|
6295
|
-
nodePath: process.execPath,
|
|
6296
|
-
cliJsPath: resolveCliJsPath(),
|
|
6297
|
-
envFile: this.paths.serviceEnvFile,
|
|
6298
|
-
logFile: this.paths.serviceLogFile(this.profile),
|
|
6299
|
-
env: snapshotServiceEnv(this.sourceEnv)
|
|
6300
|
-
};
|
|
6301
|
-
}
|
|
6302
|
-
async resolveController() {
|
|
6303
|
-
if (this.controllerOverride) return this.controllerOverride;
|
|
6304
|
-
if (process.platform === "darwin") return new LaunchdServiceController();
|
|
6305
|
-
if (process.platform === "win32") return new WindowsTaskServiceController({
|
|
6306
|
-
scriptDir: this.paths.serviceDir
|
|
5711
|
+
function runDshPlugin(bin, profile, packageSpec) {
|
|
5712
|
+
return new Promise((resolve5, reject) => {
|
|
5713
|
+
const child = spawn5(
|
|
5714
|
+
"node",
|
|
5715
|
+
[bin, "plugin", "--profile", profile, "add", packageSpec],
|
|
5716
|
+
{ stdio: "inherit" }
|
|
5717
|
+
);
|
|
5718
|
+
child.once("error", reject);
|
|
5719
|
+
child.once("exit", (code) => {
|
|
5720
|
+
if (code === 0) resolve5();
|
|
5721
|
+
else reject(new Error(`dsh plugin add exited with code ${String(code)}`));
|
|
6307
5722
|
});
|
|
6308
|
-
const systemd = new SystemdServiceController();
|
|
6309
|
-
if (await isSystemdUserAvailable()) return systemd;
|
|
6310
|
-
return new PortableServiceController({ serviceDir: this.paths.serviceDir });
|
|
6311
|
-
}
|
|
6312
|
-
async saveMetadata(platform) {
|
|
6313
|
-
const metadata = {
|
|
6314
|
-
schemaVersion: 1,
|
|
6315
|
-
serviceName: this.buildSpec().serviceName,
|
|
6316
|
-
profile: this.profile,
|
|
6317
|
-
platform,
|
|
6318
|
-
version: this.version,
|
|
6319
|
-
installedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
6320
|
-
};
|
|
6321
|
-
await writeFileAtomic(this.paths.serviceMetadataFile, `${JSON.stringify(metadata, null, 2)}
|
|
6322
|
-
`);
|
|
6323
|
-
}
|
|
6324
|
-
async readMetadata() {
|
|
6325
|
-
try {
|
|
6326
|
-
return JSON.parse(await readFile11(this.paths.serviceMetadataFile, "utf8"));
|
|
6327
|
-
} catch {
|
|
6328
|
-
return void 0;
|
|
6329
|
-
}
|
|
6330
|
-
}
|
|
6331
|
-
async prepareFiles(spec) {
|
|
6332
|
-
await mkdir14(dirname7(spec.logFile), { recursive: true });
|
|
6333
|
-
await mkdir14(dirname7(spec.envFile), { recursive: true });
|
|
6334
|
-
await writeServiceEnv(spec.envFile, spec.env);
|
|
6335
|
-
}
|
|
6336
|
-
async start() {
|
|
6337
|
-
const controller = await this.resolveController();
|
|
6338
|
-
const spec = this.buildSpec();
|
|
6339
|
-
await this.prepareFiles(spec);
|
|
6340
|
-
await controller.installAndStart(spec);
|
|
6341
|
-
await this.saveMetadata(controller.platform);
|
|
6342
|
-
return controller.status(spec);
|
|
6343
|
-
}
|
|
6344
|
-
async stop() {
|
|
6345
|
-
const controller = await this.resolveController();
|
|
6346
|
-
const spec = this.buildSpec();
|
|
6347
|
-
await controller.stop(spec);
|
|
6348
|
-
return controller.status(spec);
|
|
6349
|
-
}
|
|
6350
|
-
async restart() {
|
|
6351
|
-
const controller = await this.resolveController();
|
|
6352
|
-
const spec = this.buildSpec();
|
|
6353
|
-
const before = await controller.status(spec);
|
|
6354
|
-
if (!before.installed) {
|
|
6355
|
-
throw new Error("\u540E\u53F0\u670D\u52A1\u5C1A\u672A\u5B89\u88C5\uFF0C\u8BF7\u5148\u8FD0\u884C dsh-lark-bot start");
|
|
6356
|
-
}
|
|
6357
|
-
await this.prepareFiles(spec);
|
|
6358
|
-
await controller.restart(spec);
|
|
6359
|
-
await this.saveMetadata(controller.platform);
|
|
6360
|
-
return controller.status(spec);
|
|
6361
|
-
}
|
|
6362
|
-
async status() {
|
|
6363
|
-
const controller = await this.resolveController();
|
|
6364
|
-
const spec = this.buildSpec();
|
|
6365
|
-
return controller.status(spec);
|
|
6366
|
-
}
|
|
6367
|
-
};
|
|
6368
|
-
|
|
6369
|
-
// src/cli/commands/service.ts
|
|
6370
|
-
function mergeStartEnv2(options) {
|
|
6371
|
-
return {
|
|
6372
|
-
...process.env,
|
|
6373
|
-
...options.workspace ? { DSH_LARK_WORKSPACE: options.workspace } : {},
|
|
6374
|
-
...options.tenant ? { DSH_LARK_TENANT: options.tenant } : {},
|
|
6375
|
-
...options.appId ? { DSH_LARK_APP_ID: options.appId } : {},
|
|
6376
|
-
...options.appSecret ? { DSH_LARK_APP_SECRET: options.appSecret } : {}
|
|
6377
|
-
};
|
|
6378
|
-
}
|
|
6379
|
-
function createManager(options, version) {
|
|
6380
|
-
const env = loadRuntimeEnv(mergeStartEnv2(options));
|
|
6381
|
-
const paths = resolveAppPaths(env.home);
|
|
6382
|
-
return new ServiceManager({
|
|
6383
|
-
profile: options.profile ?? "default",
|
|
6384
|
-
env: mergeStartEnv2(options),
|
|
6385
|
-
paths,
|
|
6386
|
-
version
|
|
6387
5723
|
});
|
|
6388
5724
|
}
|
|
6389
|
-
function
|
|
6390
|
-
const
|
|
6391
|
-
|
|
6392
|
-
` name: ${status.name}`,
|
|
6393
|
-
` platform: ${status.platform}`,
|
|
6394
|
-
` installed: ${status.installed ? "\u662F" : "\u5426"}`,
|
|
6395
|
-
` autostart: ${status.autostartEnabled ? "\u5DF2\u542F\u7528\uFF08\u5F00\u673A\u81EA\u542F\uFF09" : "\u672A\u542F\u7528"}`,
|
|
6396
|
-
` state: ${status.state}`,
|
|
6397
|
-
` detail: ${status.detail}`,
|
|
6398
|
-
` pid: ${status.pid ?? "-"}`,
|
|
6399
|
-
` restarts: ${status.restarts ?? "-"}`
|
|
6400
|
-
];
|
|
6401
|
-
output(`${lines.join("\n")}
|
|
6402
|
-
`);
|
|
6403
|
-
}
|
|
6404
|
-
async function runServiceStart(options, deps = {}) {
|
|
6405
|
-
const version = deps.version ?? "0.0.0";
|
|
6406
|
-
const output = deps.output ?? ((text) => process.stdout.write(text));
|
|
6407
|
-
if (!deps.manager) {
|
|
6408
|
-
const env = loadRuntimeEnv(mergeStartEnv2(options));
|
|
6409
|
-
const paths = resolveAppPaths(env.home);
|
|
6410
|
-
const profileName = options.profile ?? "default";
|
|
6411
|
-
const store = new ConfigStore(paths.configFile);
|
|
6412
|
-
await store.load();
|
|
6413
|
-
const ready = await ensureBotProfile(store, {
|
|
6414
|
-
env,
|
|
6415
|
-
profileName,
|
|
6416
|
-
allowOnboarding: true
|
|
6417
|
-
});
|
|
6418
|
-
if (!ready) {
|
|
6419
|
-
process.exitCode = 1;
|
|
6420
|
-
return;
|
|
6421
|
-
}
|
|
6422
|
-
}
|
|
6423
|
-
const manager = deps.manager ?? createManager(options, version);
|
|
6424
|
-
try {
|
|
6425
|
-
const status = await manager.start();
|
|
6426
|
-
printServiceStatus(status, output);
|
|
6427
|
-
if (status.state !== "running") process.exitCode = 1;
|
|
6428
|
-
} catch (error) {
|
|
6429
|
-
output(`${error instanceof Error ? error.message : String(error)}
|
|
6430
|
-
`);
|
|
6431
|
-
process.exitCode = 1;
|
|
6432
|
-
}
|
|
6433
|
-
}
|
|
6434
|
-
async function runServiceCommand(action, options, deps = {}) {
|
|
6435
|
-
const output = deps.output ?? ((text) => process.stdout.write(text));
|
|
6436
|
-
const manager = deps.manager ?? createManager(options, deps.version ?? "0.0.0");
|
|
5725
|
+
async function approveBuilds(profileDir) {
|
|
5726
|
+
const workspaceFile = join12(profileDir, "pnpm-workspace.yaml");
|
|
5727
|
+
let existing = "";
|
|
6437
5728
|
try {
|
|
6438
|
-
|
|
6439
|
-
|
|
6440
|
-
status = await manager.status();
|
|
6441
|
-
} else if (action === "restart") {
|
|
6442
|
-
status = await manager.restart();
|
|
6443
|
-
} else {
|
|
6444
|
-
status = await manager.stop();
|
|
6445
|
-
}
|
|
6446
|
-
printServiceStatus(status, output);
|
|
6447
|
-
if (action === "status") {
|
|
6448
|
-
if (status.state !== "running") process.exitCode = 1;
|
|
6449
|
-
} else if (action === "restart") {
|
|
6450
|
-
if (status.state !== "running") process.exitCode = 1;
|
|
6451
|
-
} else if (status.state === "running") {
|
|
6452
|
-
process.exitCode = 1;
|
|
6453
|
-
}
|
|
6454
|
-
} catch (error) {
|
|
6455
|
-
output(`${error instanceof Error ? error.message : String(error)}
|
|
6456
|
-
`);
|
|
6457
|
-
process.exitCode = 1;
|
|
5729
|
+
existing = await readFile9(workspaceFile, "utf8");
|
|
5730
|
+
} catch {
|
|
6458
5731
|
}
|
|
6459
|
-
|
|
5732
|
+
const allowBuilds = [
|
|
5733
|
+
"protobufjs: true",
|
|
5734
|
+
...ownPackageInfo().name.startsWith("@") ? [] : [`${ownPackageInfo().name}: true`]
|
|
5735
|
+
].join("\n");
|
|
5736
|
+
if (!existing.includes("allowBuilds")) {
|
|
5737
|
+
existing = existing.trimEnd() ? `${existing.trimEnd()}
|
|
6460
5738
|
|
|
6461
|
-
|
|
6462
|
-
|
|
6463
|
-
|
|
6464
|
-
|
|
6465
|
-
|
|
6466
|
-
|
|
6467
|
-
|
|
6468
|
-
return Math.min(MAX_BACKOFF_MS, BASE_BACKOFF_MS * 2 ** Math.min(restarts, 3));
|
|
6469
|
-
}
|
|
6470
|
-
function waitForExit(child) {
|
|
6471
|
-
return new Promise((resolvePromise) => {
|
|
6472
|
-
const done = () => resolvePromise();
|
|
6473
|
-
child.once("exit", done);
|
|
6474
|
-
child.once("error", done);
|
|
6475
|
-
});
|
|
6476
|
-
}
|
|
6477
|
-
function sleep2(ms) {
|
|
6478
|
-
return new Promise((resolvePromise) => setTimeout(resolvePromise, ms));
|
|
6479
|
-
}
|
|
6480
|
-
async function runSupervise(options) {
|
|
6481
|
-
const env = loadRuntimeEnv(process.env);
|
|
6482
|
-
const paths = resolveAppPaths(env.home);
|
|
6483
|
-
const profile = options.profile ?? "default";
|
|
6484
|
-
const serviceName = serviceNameFor(profile);
|
|
6485
|
-
const statusFile = supervisorStatusFile(paths.serviceDir, serviceName);
|
|
6486
|
-
const logFile = paths.serviceLogFile(profile);
|
|
6487
|
-
const serviceEnv = await readServiceEnv(paths.serviceEnvFile);
|
|
6488
|
-
const childEnv = { ...process.env, ...serviceEnv };
|
|
6489
|
-
const cliJsPath = resolveCliJsPath();
|
|
6490
|
-
mkdirSync(dirname8(statusFile), { recursive: true });
|
|
6491
|
-
mkdirSync(dirname8(logFile), { recursive: true });
|
|
6492
|
-
const logFd = openSync(logFile, "a");
|
|
6493
|
-
const writeStatus = async (state, childPid, restarts2) => {
|
|
6494
|
-
const status = {
|
|
6495
|
-
pid: process.pid,
|
|
6496
|
-
childPid,
|
|
6497
|
-
state,
|
|
6498
|
-
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
6499
|
-
restarts: restarts2,
|
|
6500
|
-
profile
|
|
6501
|
-
};
|
|
6502
|
-
await writeFileAtomic(statusFile, `${JSON.stringify(status)}
|
|
5739
|
+
allowBuilds:
|
|
5740
|
+
${allowBuilds}
|
|
5741
|
+
` : `allowBuilds:
|
|
5742
|
+
${allowBuilds}
|
|
5743
|
+
`;
|
|
5744
|
+
} else if (!existing.includes("protobufjs: true")) {
|
|
5745
|
+
existing = existing.replace(/(allowBuilds:\s*\n)/, `$1 ${allowBuilds}
|
|
6503
5746
|
`);
|
|
6504
|
-
};
|
|
6505
|
-
let stopping = false;
|
|
6506
|
-
let child;
|
|
6507
|
-
const stop = () => {
|
|
6508
|
-
stopping = true;
|
|
6509
|
-
child?.kill("SIGTERM");
|
|
6510
|
-
};
|
|
6511
|
-
process.once("SIGTERM", stop);
|
|
6512
|
-
process.once("SIGINT", stop);
|
|
6513
|
-
let restarts = 0;
|
|
6514
|
-
try {
|
|
6515
|
-
while (!stopping) {
|
|
6516
|
-
await writeStatus("running", void 0, restarts);
|
|
6517
|
-
const spawned = spawn6(process.execPath, [cliJsPath, "run", "--profile", profile], {
|
|
6518
|
-
env: childEnv,
|
|
6519
|
-
stdio: ["ignore", logFd, logFd]
|
|
6520
|
-
});
|
|
6521
|
-
child = spawned;
|
|
6522
|
-
await writeStatus("running", spawned.pid, restarts);
|
|
6523
|
-
await waitForExit(spawned);
|
|
6524
|
-
child = void 0;
|
|
6525
|
-
if (stopping) break;
|
|
6526
|
-
restarts += 1;
|
|
6527
|
-
await writeStatus("restarting", void 0, restarts);
|
|
6528
|
-
const backoff = computeBackoffMs(restarts);
|
|
6529
|
-
await sleep2(backoff);
|
|
6530
|
-
}
|
|
6531
|
-
} finally {
|
|
6532
|
-
await writeStatus("stopped", void 0, restarts);
|
|
6533
|
-
closeSync(logFd);
|
|
6534
5747
|
}
|
|
5748
|
+
await writeFile6(workspaceFile, existing, "utf8");
|
|
6535
5749
|
}
|
|
6536
5750
|
|
|
6537
5751
|
// src/cli.ts
|
|
6538
5752
|
function packageVersion() {
|
|
6539
|
-
const raw =
|
|
5753
|
+
const raw = readFileSync4(new URL("../package.json", import.meta.url), "utf8");
|
|
6540
5754
|
const pkg = JSON.parse(raw);
|
|
6541
5755
|
return typeof pkg.version === "string" ? pkg.version : "0.0.0";
|
|
6542
5756
|
}
|
|
@@ -6546,39 +5760,19 @@ function addBotOptions(command) {
|
|
|
6546
5760
|
function buildProgram() {
|
|
6547
5761
|
const program = new Command();
|
|
6548
5762
|
program.name("dsh-lark-bot").description("Bridge DeepSeek Harness into Feishu / Lark").version(packageVersion(), "-v, --version");
|
|
6549
|
-
|
|
6550
|
-
|
|
6551
|
-
).action(async (opts) => {
|
|
6552
|
-
await
|
|
5763
|
+
program.command("setup").description(
|
|
5764
|
+
"Install this package as a standard dsh profile bundle (single install path)"
|
|
5765
|
+
).option("--profile <name>", "dsh profile to install into (default: dsh-lark)").action(async (opts) => {
|
|
5766
|
+
await runSetup({ ...opts.profile ? { profile: opts.profile } : {} });
|
|
6553
5767
|
});
|
|
6554
|
-
addBotOptions(program.command("status").description("Show background service status")).action(
|
|
6555
|
-
async (opts) => {
|
|
6556
|
-
await runServiceCommand("status", opts, { version: packageVersion() });
|
|
6557
|
-
}
|
|
6558
|
-
);
|
|
6559
|
-
addBotOptions(program.command("restart").description("Restart the background service")).action(
|
|
6560
|
-
async (opts) => {
|
|
6561
|
-
await runServiceCommand("restart", opts, { version: packageVersion() });
|
|
6562
|
-
}
|
|
6563
|
-
);
|
|
6564
|
-
addBotOptions(program.command("stop").description("Stop the background service and remove autostart")).action(
|
|
6565
|
-
async (opts) => {
|
|
6566
|
-
await runServiceCommand("stop", opts, { version: packageVersion() });
|
|
6567
|
-
}
|
|
6568
|
-
);
|
|
6569
5768
|
program.command("doctor").description("Run local diagnostics").option("--profile <name>", "profile name").option("--workspace <path>", "initial working directory").option("--app-id <id>", "existing Lark/Feishu app id").option("--app-secret <secret>", "existing Lark/Feishu app secret").option("--tenant <tenant>", "feishu or lark").action(async (opts) => {
|
|
6570
5769
|
await runDoctor({ ...opts, version: packageVersion() });
|
|
6571
5770
|
});
|
|
6572
5771
|
addBotOptions(
|
|
6573
|
-
program.command("run", { hidden: true }).description("Run the bridge
|
|
5772
|
+
program.command("run", { hidden: true }).description("Run the bridge engine directly (diagnostics; the dsh plugin runs it in-process)")
|
|
6574
5773
|
).action(async (opts) => {
|
|
6575
5774
|
await runBot(opts);
|
|
6576
5775
|
});
|
|
6577
|
-
addBotOptions(
|
|
6578
|
-
program.command("supervise", { hidden: true }).description("Supervisor loop for the portable background service; not for interactive use")
|
|
6579
|
-
).action(async (opts) => {
|
|
6580
|
-
await runSupervise(opts);
|
|
6581
|
-
});
|
|
6582
5776
|
return program;
|
|
6583
5777
|
}
|
|
6584
5778
|
async function main(argv = process.argv) {
|
|
@@ -6587,7 +5781,7 @@ async function main(argv = process.argv) {
|
|
|
6587
5781
|
function isDirectInvocation(entry = process.argv[1], metaUrl = import.meta.url) {
|
|
6588
5782
|
if (!entry) return false;
|
|
6589
5783
|
try {
|
|
6590
|
-
return
|
|
5784
|
+
return realpathSync4(entry) === fileURLToPath2(metaUrl);
|
|
6591
5785
|
} catch {
|
|
6592
5786
|
return false;
|
|
6593
5787
|
}
|