@michengai/dsh-automation 1.0.5 → 1.0.7
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/CHANGELOG.md +19 -29
- package/CHANGELOG.zh-CN.md +19 -29
- package/README.md +39 -0
- package/README.zh-CN.md +40 -0
- package/lib/client.js +14 -3
- package/lib/client.js.map +3 -3
- package/lib/index.js +300 -13
- package/lib/types/client/api-base.d.ts +20 -0
- package/lib/types/client/bundle-config.d.ts +59 -0
- package/lib/types/client/contracts.d.ts +1 -0
- package/lib/types/client/index.d.ts +2 -0
- package/lib/types/client/locales.d.ts +10 -0
- package/lib/types/client/plugin-update-ui.d.ts +2 -0
- package/lib/types/index.d.ts +3 -0
- package/lib/types/plugin-updater.d.ts +1 -0
- package/lib/types/settings-manager.d.ts +27 -0
- package/package.json +1 -1
package/lib/index.js
CHANGED
|
@@ -2707,10 +2707,10 @@ function isValidJWT(token, algorithm = null) {
|
|
|
2707
2707
|
const tokensParts = token.split(".");
|
|
2708
2708
|
if (tokensParts.length !== 3)
|
|
2709
2709
|
return false;
|
|
2710
|
-
const [
|
|
2711
|
-
if (!
|
|
2710
|
+
const [header3] = tokensParts;
|
|
2711
|
+
if (!header3)
|
|
2712
2712
|
return false;
|
|
2713
|
-
const parsedHeader = JSON.parse(atob(
|
|
2713
|
+
const parsedHeader = JSON.parse(atob(header3));
|
|
2714
2714
|
if ("typ" in parsedHeader && parsedHeader?.typ !== "JWT")
|
|
2715
2715
|
return false;
|
|
2716
2716
|
if (!parsedHeader.alg)
|
|
@@ -21629,8 +21629,8 @@ async function settlesWithin(promise2, timeoutMs) {
|
|
|
21629
21629
|
try {
|
|
21630
21630
|
return await Promise.race([
|
|
21631
21631
|
promise2.then(() => true, () => false),
|
|
21632
|
-
new Promise((
|
|
21633
|
-
timer = setTimeout(() =>
|
|
21632
|
+
new Promise((resolve3) => {
|
|
21633
|
+
timer = setTimeout(() => resolve3(false), timeoutMs);
|
|
21634
21634
|
})
|
|
21635
21635
|
]);
|
|
21636
21636
|
} finally {
|
|
@@ -21771,19 +21771,19 @@ async function executeAutomationRun(ctx, definition, run, config2) {
|
|
|
21771
21771
|
let timedOut = false;
|
|
21772
21772
|
let aborted2 = false;
|
|
21773
21773
|
const idle = handle.agent.whenIdle();
|
|
21774
|
-
const deadline = new Promise((
|
|
21774
|
+
const deadline = new Promise((resolve3) => {
|
|
21775
21775
|
timeout = setTimeout(() => {
|
|
21776
21776
|
timedOut = true;
|
|
21777
21777
|
handle?.agent.cancel({ kind: "hook", reason: "automation run timeout" });
|
|
21778
|
-
|
|
21778
|
+
resolve3();
|
|
21779
21779
|
}, config2.runTimeoutMs);
|
|
21780
21780
|
});
|
|
21781
|
-
const cancellation = new Promise((
|
|
21781
|
+
const cancellation = new Promise((resolve3) => {
|
|
21782
21782
|
if (config2.signal === void 0) return;
|
|
21783
21783
|
const cancel = () => {
|
|
21784
21784
|
aborted2 = true;
|
|
21785
21785
|
handle?.agent.cancel({ kind: "hook", reason: "automation service disposed" });
|
|
21786
|
-
|
|
21786
|
+
resolve3();
|
|
21787
21787
|
};
|
|
21788
21788
|
if (config2.signal.aborted) cancel();
|
|
21789
21789
|
else {
|
|
@@ -22703,8 +22703,8 @@ var AutomationService = class _AutomationService {
|
|
|
22703
22703
|
function readListedSessionId(item) {
|
|
22704
22704
|
if (typeof item !== "object" || item === null) return void 0;
|
|
22705
22705
|
const value = item;
|
|
22706
|
-
const
|
|
22707
|
-
for (const id of [
|
|
22706
|
+
const header3 = typeof value.header === "object" && value.header !== null ? value.header : void 0;
|
|
22707
|
+
for (const id of [header3?.id, value.id]) {
|
|
22708
22708
|
if (typeof id === "string" && id.trim() !== "") return id;
|
|
22709
22709
|
}
|
|
22710
22710
|
return void 0;
|
|
@@ -23822,6 +23822,13 @@ function registerPluginUpdater(ctx, options) {
|
|
|
23822
23822
|
path: options.endpoint,
|
|
23823
23823
|
handler: async (request, response) => {
|
|
23824
23824
|
try {
|
|
23825
|
+
if (options.isEnabled) {
|
|
23826
|
+
const allowed = await options.isEnabled();
|
|
23827
|
+
if (!allowed) {
|
|
23828
|
+
json3(response, 403, { ok: false, error: "\u5728\u7EBF\u66F4\u65B0\u5DF2\u88AB\u7981\u7528\u3002" });
|
|
23829
|
+
return;
|
|
23830
|
+
}
|
|
23831
|
+
}
|
|
23825
23832
|
const target = runtime(host);
|
|
23826
23833
|
if (request.method === "GET" || request.method === "HEAD") {
|
|
23827
23834
|
const payload = await status(options, target);
|
|
@@ -23871,6 +23878,275 @@ function registerPluginUpdater(ctx, options) {
|
|
|
23871
23878
|
});
|
|
23872
23879
|
}
|
|
23873
23880
|
|
|
23881
|
+
// src/settings-manager.ts
|
|
23882
|
+
import { existsSync as existsSync3, mkdirSync, readFileSync as readFileSync3, writeFileSync } from "node:fs";
|
|
23883
|
+
import { homedir as homedir3 } from "node:os";
|
|
23884
|
+
import { dirname as dirname2, isAbsolute as isAbsolute2, join as join2, resolve as resolve2 } from "node:path";
|
|
23885
|
+
var SETTINGS_API_HEADER = "x-michengai-client";
|
|
23886
|
+
var MAX_SETTINGS_BODY_BYTES = 64 * 1024;
|
|
23887
|
+
function resolveProfileDir(ctx) {
|
|
23888
|
+
try {
|
|
23889
|
+
return resolveUpdateRuntime(ctx).profileDir;
|
|
23890
|
+
} catch {
|
|
23891
|
+
const env = process.env;
|
|
23892
|
+
const home = homedir3();
|
|
23893
|
+
return resolve2(env.DSH_PROFILE_DIR ?? resolve2(home, ".dsh", "profiles", "web"));
|
|
23894
|
+
}
|
|
23895
|
+
}
|
|
23896
|
+
function header2(request, name2) {
|
|
23897
|
+
const value = request.headers?.[name2];
|
|
23898
|
+
return Array.isArray(value) ? value[0] : value;
|
|
23899
|
+
}
|
|
23900
|
+
function validateApiMutation(request) {
|
|
23901
|
+
const method = (request.method ?? "GET").toUpperCase();
|
|
23902
|
+
if (method === "GET" || method === "HEAD") return void 0;
|
|
23903
|
+
const marker = header2(request, SETTINGS_API_HEADER);
|
|
23904
|
+
if (marker !== "1") return { status: 403, error: "forbidden mutation request" };
|
|
23905
|
+
const contentType = header2(request, "content-type")?.split(";", 1)[0]?.trim().toLowerCase();
|
|
23906
|
+
if (contentType !== "application/json") return { status: 415, error: "content-type must be application/json" };
|
|
23907
|
+
return void 0;
|
|
23908
|
+
}
|
|
23909
|
+
var SettingsManager = class {
|
|
23910
|
+
file;
|
|
23911
|
+
hostUpdateDisabled;
|
|
23912
|
+
store = {};
|
|
23913
|
+
constructor(options) {
|
|
23914
|
+
this.hostUpdateDisabled = options.hostUpdateDisabled === true;
|
|
23915
|
+
if (options.stateDir !== void 0 && options.stateDir !== "") {
|
|
23916
|
+
this.file = isAbsolute2(options.stateDir) ? join2(options.stateDir, "settings.json") : resolve2(options.stateDir, "settings.json");
|
|
23917
|
+
} else {
|
|
23918
|
+
const profileDir = resolveProfileDir(options.ctx);
|
|
23919
|
+
this.file = join2(profileDir, "automation-settings.json");
|
|
23920
|
+
}
|
|
23921
|
+
this.load();
|
|
23922
|
+
}
|
|
23923
|
+
load() {
|
|
23924
|
+
if (!existsSync3(this.file)) return;
|
|
23925
|
+
try {
|
|
23926
|
+
const content = readFileSync3(this.file, "utf8");
|
|
23927
|
+
const parsed = JSON.parse(content);
|
|
23928
|
+
if (typeof parsed === "object" && parsed !== null) {
|
|
23929
|
+
const item = parsed;
|
|
23930
|
+
this.store = {
|
|
23931
|
+
sidebarEntry: typeof item.sidebarEntry === "boolean" ? item.sidebarEntry : void 0,
|
|
23932
|
+
onlineUpdate: typeof item.onlineUpdate === "boolean" ? item.onlineUpdate : void 0,
|
|
23933
|
+
hideOfficialSchedule: typeof item.hideOfficialSchedule === "boolean" ? item.hideOfficialSchedule : void 0
|
|
23934
|
+
};
|
|
23935
|
+
}
|
|
23936
|
+
} catch {
|
|
23937
|
+
this.store = {};
|
|
23938
|
+
}
|
|
23939
|
+
}
|
|
23940
|
+
flush() {
|
|
23941
|
+
try {
|
|
23942
|
+
const dir = dirname2(this.file);
|
|
23943
|
+
if (!existsSync3(dir)) {
|
|
23944
|
+
mkdirSync(dir, { recursive: true });
|
|
23945
|
+
}
|
|
23946
|
+
writeFileSync(this.file, JSON.stringify(this.store, null, 2), "utf8");
|
|
23947
|
+
} catch {
|
|
23948
|
+
}
|
|
23949
|
+
}
|
|
23950
|
+
getSidebarEntry() {
|
|
23951
|
+
return this.store.sidebarEntry !== false;
|
|
23952
|
+
}
|
|
23953
|
+
async setSidebarEntry(enabled) {
|
|
23954
|
+
if (typeof enabled !== "boolean") throw new Error("enabled \u5FC5\u987B\u662F\u5E03\u5C14\u503C");
|
|
23955
|
+
this.store.sidebarEntry = enabled;
|
|
23956
|
+
this.flush();
|
|
23957
|
+
return enabled;
|
|
23958
|
+
}
|
|
23959
|
+
getHideOfficialSchedule() {
|
|
23960
|
+
return this.store.hideOfficialSchedule ?? this.getSidebarEntry();
|
|
23961
|
+
}
|
|
23962
|
+
async setHideOfficialSchedule(enabled) {
|
|
23963
|
+
if (typeof enabled !== "boolean") throw new Error("enabled \u5FC5\u987B\u662F\u5E03\u5C14\u503C");
|
|
23964
|
+
this.store.hideOfficialSchedule = enabled;
|
|
23965
|
+
this.flush();
|
|
23966
|
+
return enabled;
|
|
23967
|
+
}
|
|
23968
|
+
isHostUpdateDisabled() {
|
|
23969
|
+
return this.hostUpdateDisabled;
|
|
23970
|
+
}
|
|
23971
|
+
isUpdateEnabled() {
|
|
23972
|
+
if (this.hostUpdateDisabled) return false;
|
|
23973
|
+
return this.store.onlineUpdate !== false;
|
|
23974
|
+
}
|
|
23975
|
+
async setOnlineUpdate(enabled) {
|
|
23976
|
+
if (typeof enabled !== "boolean") throw new Error("enabled \u5FC5\u987B\u662F\u5E03\u5C14\u503C");
|
|
23977
|
+
if (this.hostUpdateDisabled) throw new Error("\u5728\u7EBF\u66F4\u65B0\u5DF2\u88AB\u5BBF\u4E3B\u914D\u7F6E\u9501\u5B9A\u7981\u7528");
|
|
23978
|
+
this.store.onlineUpdate = enabled;
|
|
23979
|
+
this.flush();
|
|
23980
|
+
return enabled;
|
|
23981
|
+
}
|
|
23982
|
+
};
|
|
23983
|
+
function registerSettingsApi(ctx, manager) {
|
|
23984
|
+
const host = ctx;
|
|
23985
|
+
if (typeof host.webServer?.register !== "function") return () => {
|
|
23986
|
+
};
|
|
23987
|
+
const json4 = (res, status2, body) => {
|
|
23988
|
+
res.writeHead(status2, {
|
|
23989
|
+
"content-type": "application/json; charset=utf-8",
|
|
23990
|
+
"cache-control": "no-store"
|
|
23991
|
+
});
|
|
23992
|
+
res.end(JSON.stringify(body));
|
|
23993
|
+
};
|
|
23994
|
+
const checkAuthAndMutation = (request, response) => {
|
|
23995
|
+
const connection = typeof ctx.get === "function" ? ctx.get("connection") : void 0;
|
|
23996
|
+
if (typeof connection?.requestRejection === "function") {
|
|
23997
|
+
const rejection = connection.requestRejection(request);
|
|
23998
|
+
if (rejection !== void 0) {
|
|
23999
|
+
json4(response, rejection, { ok: false, error: rejection === 401 ? "\u8BF7\u5148\u767B\u5F55 DSH / Sign in to DSH first" : "\u5BBF\u4E3B\u62D2\u7EDD\u6B64\u8BF7\u6C42 / Request forbidden" });
|
|
24000
|
+
return false;
|
|
24001
|
+
}
|
|
24002
|
+
}
|
|
24003
|
+
const mutationError = validateApiMutation(request);
|
|
24004
|
+
if (mutationError !== void 0) {
|
|
24005
|
+
json4(response, mutationError.status, { ok: false, error: mutationError.error });
|
|
24006
|
+
return false;
|
|
24007
|
+
}
|
|
24008
|
+
return true;
|
|
24009
|
+
};
|
|
24010
|
+
const readBody = (req) => new Promise((resolveBody) => {
|
|
24011
|
+
const chunks = [];
|
|
24012
|
+
let received = 0;
|
|
24013
|
+
let oversized = false;
|
|
24014
|
+
req.on("data", (chunk) => {
|
|
24015
|
+
received += chunk.length;
|
|
24016
|
+
if (received > MAX_SETTINGS_BODY_BYTES) {
|
|
24017
|
+
oversized = true;
|
|
24018
|
+
return;
|
|
24019
|
+
}
|
|
24020
|
+
chunks.push(chunk);
|
|
24021
|
+
});
|
|
24022
|
+
req.on("end", () => {
|
|
24023
|
+
if (oversized) {
|
|
24024
|
+
resolveBody({ ok: false, status: 413, error: "payload too large" });
|
|
24025
|
+
return;
|
|
24026
|
+
}
|
|
24027
|
+
try {
|
|
24028
|
+
const raw = Buffer.concat(chunks).toString("utf8");
|
|
24029
|
+
resolveBody({ ok: true, data: raw ? JSON.parse(raw) : {} });
|
|
24030
|
+
} catch {
|
|
24031
|
+
resolveBody({ ok: false, status: 400, error: "invalid json" });
|
|
24032
|
+
}
|
|
24033
|
+
});
|
|
24034
|
+
req.on("error", () => resolveBody({ ok: false, status: 400, error: "read error" }));
|
|
24035
|
+
});
|
|
24036
|
+
const offSidebar = host.webServer.register({
|
|
24037
|
+
kind: "exact",
|
|
24038
|
+
path: "/api/michengai/dsh-automation/sidebar-entry",
|
|
24039
|
+
handler: async (request, response) => {
|
|
24040
|
+
if (!checkAuthAndMutation(request, response)) return;
|
|
24041
|
+
const method = request.method;
|
|
24042
|
+
if (method === "GET" || method === "HEAD") {
|
|
24043
|
+
if (method === "HEAD") {
|
|
24044
|
+
response.writeHead(200, { "cache-control": "no-store" });
|
|
24045
|
+
response.end();
|
|
24046
|
+
return;
|
|
24047
|
+
}
|
|
24048
|
+
json4(response, 200, { ok: true, enabled: manager.getSidebarEntry() });
|
|
24049
|
+
return;
|
|
24050
|
+
}
|
|
24051
|
+
if (method === "POST") {
|
|
24052
|
+
const bodyRes = await readBody(request);
|
|
24053
|
+
if (!bodyRes.ok || !bodyRes.data || typeof bodyRes.data.enabled !== "boolean") {
|
|
24054
|
+
json4(response, bodyRes.status ?? 400, { ok: false, error: bodyRes.error ?? "enabled \u5FC5\u987B\u662F\u5E03\u5C14\u503C" });
|
|
24055
|
+
return;
|
|
24056
|
+
}
|
|
24057
|
+
const enabled = await manager.setSidebarEntry(bodyRes.data.enabled);
|
|
24058
|
+
json4(response, 200, { ok: true, enabled });
|
|
24059
|
+
return;
|
|
24060
|
+
}
|
|
24061
|
+
response.writeHead(405, { allow: "GET, HEAD, POST" });
|
|
24062
|
+
response.end();
|
|
24063
|
+
}
|
|
24064
|
+
});
|
|
24065
|
+
const offUpdate = host.webServer.register({
|
|
24066
|
+
kind: "exact",
|
|
24067
|
+
path: "/api/michengai/dsh-automation/online-update",
|
|
24068
|
+
handler: async (request, response) => {
|
|
24069
|
+
if (!checkAuthAndMutation(request, response)) return;
|
|
24070
|
+
const method = request.method;
|
|
24071
|
+
if (method === "GET" || method === "HEAD") {
|
|
24072
|
+
if (method === "HEAD") {
|
|
24073
|
+
response.writeHead(200, { "cache-control": "no-store" });
|
|
24074
|
+
response.end();
|
|
24075
|
+
return;
|
|
24076
|
+
}
|
|
24077
|
+
json4(response, 200, {
|
|
24078
|
+
ok: true,
|
|
24079
|
+
enabled: manager.isUpdateEnabled(),
|
|
24080
|
+
locked: manager.isHostUpdateDisabled()
|
|
24081
|
+
});
|
|
24082
|
+
return;
|
|
24083
|
+
}
|
|
24084
|
+
if (method === "POST") {
|
|
24085
|
+
if (manager.isHostUpdateDisabled()) {
|
|
24086
|
+
json4(response, 403, { ok: false, error: "\u5728\u7EBF\u66F4\u65B0\u5DF2\u88AB\u5BBF\u4E3B\u914D\u7F6E\u9501\u5B9A\u7981\u7528" });
|
|
24087
|
+
return;
|
|
24088
|
+
}
|
|
24089
|
+
const bodyRes = await readBody(request);
|
|
24090
|
+
if (!bodyRes.ok || !bodyRes.data || typeof bodyRes.data.enabled !== "boolean") {
|
|
24091
|
+
json4(response, bodyRes.status ?? 400, { ok: false, error: bodyRes.error ?? "enabled \u5FC5\u987B\u662F\u5E03\u5C14\u503C" });
|
|
24092
|
+
return;
|
|
24093
|
+
}
|
|
24094
|
+
const enabled = await manager.setOnlineUpdate(bodyRes.data.enabled);
|
|
24095
|
+
json4(response, 200, { ok: true, enabled });
|
|
24096
|
+
return;
|
|
24097
|
+
}
|
|
24098
|
+
response.writeHead(405, { allow: "GET, HEAD, POST" });
|
|
24099
|
+
response.end();
|
|
24100
|
+
}
|
|
24101
|
+
});
|
|
24102
|
+
const offOfficial = host.webServer.register({
|
|
24103
|
+
kind: "exact",
|
|
24104
|
+
path: "/api/michengai/dsh-automation/official-schedule",
|
|
24105
|
+
handler: async (request, response) => {
|
|
24106
|
+
if (!checkAuthAndMutation(request, response)) return;
|
|
24107
|
+
const method = request.method;
|
|
24108
|
+
if (method === "GET" || method === "HEAD") {
|
|
24109
|
+
if (method === "HEAD") {
|
|
24110
|
+
response.writeHead(200, { "cache-control": "no-store" });
|
|
24111
|
+
response.end();
|
|
24112
|
+
return;
|
|
24113
|
+
}
|
|
24114
|
+
json4(response, 200, {
|
|
24115
|
+
ok: true,
|
|
24116
|
+
enabled: manager.getHideOfficialSchedule()
|
|
24117
|
+
});
|
|
24118
|
+
return;
|
|
24119
|
+
}
|
|
24120
|
+
if (method === "POST") {
|
|
24121
|
+
const bodyRes = await readBody(request);
|
|
24122
|
+
if (!bodyRes.ok || !bodyRes.data || typeof bodyRes.data.enabled !== "boolean") {
|
|
24123
|
+
json4(response, bodyRes.status ?? 400, { ok: false, error: bodyRes.error ?? "enabled \u5FC5\u987B\u662F\u5E03\u5C14\u503C" });
|
|
24124
|
+
return;
|
|
24125
|
+
}
|
|
24126
|
+
const enabled = await manager.setHideOfficialSchedule(bodyRes.data.enabled);
|
|
24127
|
+
json4(response, 200, { ok: true, enabled });
|
|
24128
|
+
return;
|
|
24129
|
+
}
|
|
24130
|
+
response.writeHead(405, { allow: "GET, HEAD, POST" });
|
|
24131
|
+
response.end();
|
|
24132
|
+
}
|
|
24133
|
+
});
|
|
24134
|
+
return () => {
|
|
24135
|
+
try {
|
|
24136
|
+
offSidebar();
|
|
24137
|
+
} catch {
|
|
24138
|
+
}
|
|
24139
|
+
try {
|
|
24140
|
+
offUpdate();
|
|
24141
|
+
} catch {
|
|
24142
|
+
}
|
|
24143
|
+
try {
|
|
24144
|
+
offOfficial();
|
|
24145
|
+
} catch {
|
|
24146
|
+
}
|
|
24147
|
+
};
|
|
24148
|
+
}
|
|
24149
|
+
|
|
23874
24150
|
// src/index.ts
|
|
23875
24151
|
var name = "dsh-automation";
|
|
23876
24152
|
var inject = [
|
|
@@ -23889,7 +24165,10 @@ var inject = [
|
|
|
23889
24165
|
var Config = z2.object({
|
|
23890
24166
|
runTimeoutMinutes: z2.number().step(1).min(1).max(1440).default(60),
|
|
23891
24167
|
misfireGraceMinutes: z2.number().step(1).min(0).max(10080).default(15),
|
|
23892
|
-
historyLimit: z2.number().step(1).min(1).max(5e3).default(200)
|
|
24168
|
+
historyLimit: z2.number().step(1).min(1).max(5e3).default(200),
|
|
24169
|
+
stateDir: z2.string(),
|
|
24170
|
+
updater: z2.boolean(),
|
|
24171
|
+
onlineUpdate: z2.boolean()
|
|
23893
24172
|
});
|
|
23894
24173
|
var MUTATING_TOOLS = /* @__PURE__ */ new Set([
|
|
23895
24174
|
"automation_create",
|
|
@@ -23915,11 +24194,19 @@ function humanApprovalReason(toolName) {
|
|
|
23915
24194
|
return toolName === "automation_delete" ? "\u6B64\u64CD\u4F5C\u4F1A\u6C38\u4E45\u5220\u9664\u81EA\u52A8\u5316\u5B9A\u4E49\u3002\u8FD0\u884C\u5386\u53F2\u4F1A\u4FDD\u7559\uFF0C\u4F46\u8BA1\u5212\u65E0\u6CD5\u81EA\u52A8\u6062\u590D\u3002" : "\u6B64\u64CD\u4F5C\u4F1A\u521B\u5EFA\u6216\u6269\u5927\u65E0\u4EBA\u503C\u5B88\u7684\u672A\u6765\u5DE5\u4F5C\u3002\u8BF7\u6838\u5BF9\u4EFB\u52A1\u8BF4\u660E\u3001\u8BA1\u5212\u3001\u5DE5\u4F5C\u533A\u548C\u6743\u9650\u8FB9\u754C\u3002";
|
|
23916
24195
|
}
|
|
23917
24196
|
async function apply(ctx, rawConfig) {
|
|
24197
|
+
const hostUpdateDisabled = rawConfig.updater === false || rawConfig.onlineUpdate === false;
|
|
24198
|
+
const settingsManager = new SettingsManager({
|
|
24199
|
+
ctx,
|
|
24200
|
+
...rawConfig.stateDir !== void 0 ? { stateDir: rawConfig.stateDir } : {},
|
|
24201
|
+
hostUpdateDisabled
|
|
24202
|
+
});
|
|
23918
24203
|
ctx.effect(() => registerPluginUpdater(ctx, {
|
|
23919
24204
|
endpoint: "/api/michengai/dsh-automation/update",
|
|
23920
24205
|
packageName: "@michengai/dsh-automation",
|
|
23921
|
-
manifestUrl: new URL("../package.json", import.meta.url)
|
|
24206
|
+
manifestUrl: new URL("../package.json", import.meta.url),
|
|
24207
|
+
isEnabled: async () => !hostUpdateDisabled && settingsManager.isUpdateEnabled()
|
|
23922
24208
|
}), "dsh-automation: plugin updater");
|
|
24209
|
+
ctx.effect(() => registerSettingsApi(ctx, settingsManager), "dsh-automation: settings api");
|
|
23923
24210
|
const config2 = rawConfig;
|
|
23924
24211
|
await ctx.effect(async () => {
|
|
23925
24212
|
let alive = true;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* API 基址解析:把插件自己的根绝对路径端点解析到页面实际所在的 base 之下。
|
|
3
|
+
*
|
|
4
|
+
* 为什么需要:DSH 可能被挂在带路径前缀的反向代理后面(例如 https://host/app/dsh/,
|
|
5
|
+
* 网关转发前剥掉前缀)。此时浏览器把根绝对路径 "/api/..." 解析到站点根,前缀丢失,
|
|
6
|
+
* 网关返回自己的 HTML 404,面板就报「服务端返回非 JSON 响应(HTTP 404)」。
|
|
7
|
+
*
|
|
8
|
+
* 为什么这么解析:宿主前端同样以页面 base 解析 API 路径(参见 dsh-client-connection 的
|
|
9
|
+
* openConnectionWebSocket 与 dsh-client-file-upload 的 new URL(path, document.baseURI)),
|
|
10
|
+
* 这里沿用同一约定——先去掉前导 "/",再用 new URL 相对解析;若直接 new URL("/api/x", base)
|
|
11
|
+
* 会丢弃 base 的路径段,前缀就没了。
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* 解析插件端点 URL。
|
|
15
|
+
*
|
|
16
|
+
* @param path 端点路径,通常是 "/api/michengai/dsh-automation/xxx" 这类根绝对路径。
|
|
17
|
+
* @returns 基于页面 base 的绝对 URL;相对路径、完整 URL、协议相对 URL、无 document
|
|
18
|
+
* 或 base 非法时原样返回,保证非浏览器环境与测试夹具行为不变。
|
|
19
|
+
*/
|
|
20
|
+
export declare function resolveApiUrl(path: string): string;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { type ReactElement } from 'react';
|
|
2
|
+
import type { ClientContext } from './contracts.js';
|
|
3
|
+
export declare const BUNDLE_KEY = "@michengai/dsh-automation";
|
|
4
|
+
export declare const SIDEBAR_PANEL_ID = "dsh-automation";
|
|
5
|
+
export declare const SIDEBAR_PANEL_ORDER = 4;
|
|
6
|
+
export declare const HIDE_OFFICIAL_SCHEDULE_STYLE_TAG = "dsh-automation-hide-official-schedule";
|
|
7
|
+
export declare const STORAGE_KEY_HIDE_OFFICIAL = "dsh-automation.hide-official-schedule";
|
|
8
|
+
export declare const STORAGE_KEY_SIDEBAR_ENTRY = "dsh-automation.sidebar-entry";
|
|
9
|
+
export declare function readCachedSidebarEntry(): boolean;
|
|
10
|
+
export declare function readCachedHideOfficial(): boolean;
|
|
11
|
+
export declare function applyHideOfficialScheduleStyle(hidden: boolean): void;
|
|
12
|
+
export declare function AutomationSidebarIcon(props: {
|
|
13
|
+
readonly size?: number;
|
|
14
|
+
}): ReactElement;
|
|
15
|
+
export interface BundleConfigCardProps {
|
|
16
|
+
readonly t: (key: string) => string;
|
|
17
|
+
readonly enabled: boolean;
|
|
18
|
+
readonly disabled?: boolean;
|
|
19
|
+
readonly onToggle: (next: boolean) => void;
|
|
20
|
+
readonly onlineUpdateEnabled?: boolean;
|
|
21
|
+
readonly onlineUpdateDisabled?: boolean;
|
|
22
|
+
readonly onlineUpdateLocked?: boolean;
|
|
23
|
+
readonly onToggleOnlineUpdate?: (next: boolean) => void;
|
|
24
|
+
readonly hideOfficialScheduleEnabled?: boolean;
|
|
25
|
+
readonly hideOfficialScheduleDisabled?: boolean;
|
|
26
|
+
readonly onToggleHideOfficialSchedule?: (next: boolean) => void;
|
|
27
|
+
}
|
|
28
|
+
export declare function AutomationBundleConfigCard(props: BundleConfigCardProps): ReactElement;
|
|
29
|
+
export interface BundleConfigWrapperProps {
|
|
30
|
+
readonly t: (key: string) => string;
|
|
31
|
+
readonly getEnabled: () => boolean;
|
|
32
|
+
readonly setSidebar: (next: boolean) => Promise<boolean>;
|
|
33
|
+
readonly getOnlineUpdateEnabled?: () => boolean;
|
|
34
|
+
readonly getOnlineUpdateLocked?: () => boolean;
|
|
35
|
+
readonly setOnlineUpdate?: (next: boolean) => Promise<boolean>;
|
|
36
|
+
readonly getHideOfficialScheduleEnabled?: () => boolean;
|
|
37
|
+
readonly setHideOfficialSchedule?: (next: boolean) => Promise<boolean>;
|
|
38
|
+
readonly subscribe: (listener: () => void) => () => void;
|
|
39
|
+
}
|
|
40
|
+
export declare function AutomationBundleConfigCardWrapper(props: BundleConfigWrapperProps): ReactElement;
|
|
41
|
+
export interface SetupSidebarEntryOptions {
|
|
42
|
+
readonly callApi: (path: string, options?: RequestInit) => Promise<any>;
|
|
43
|
+
readonly renderPanel: (ownerProps: any) => ReactElement;
|
|
44
|
+
readonly ns: string;
|
|
45
|
+
readonly injectProps?: Record<string, unknown> | (() => Record<string, unknown>);
|
|
46
|
+
readonly onOnlineUpdateChange?: (enabled: boolean) => void;
|
|
47
|
+
}
|
|
48
|
+
export interface SidebarController {
|
|
49
|
+
isEnabled(): boolean;
|
|
50
|
+
isSupported(): boolean;
|
|
51
|
+
setEnabled(next: boolean): Promise<boolean>;
|
|
52
|
+
isOnlineUpdateEnabled(): boolean;
|
|
53
|
+
isOnlineUpdateLocked(): boolean;
|
|
54
|
+
setOnlineUpdate(next: boolean): Promise<boolean>;
|
|
55
|
+
isHideOfficialScheduleEnabled(): boolean;
|
|
56
|
+
setHideOfficialSchedule(next: boolean): Promise<boolean>;
|
|
57
|
+
destroy(): void;
|
|
58
|
+
}
|
|
59
|
+
export declare function setupSidebarEntry(ctx: ClientContext, options: SetupSidebarEntryOptions): SidebarController;
|
|
@@ -2,3 +2,5 @@ import type { ClientContext } from './contracts.js';
|
|
|
2
2
|
export declare const name = "dsh-automation-client";
|
|
3
3
|
export declare const inject: string[];
|
|
4
4
|
export declare function apply(ctx: ClientContext): void;
|
|
5
|
+
export { resolveApiUrl } from './api-base.js';
|
|
6
|
+
export { setupSidebarEntry } from './bundle-config.js';
|
|
@@ -15,6 +15,16 @@ export declare const en: {
|
|
|
15
15
|
readonly "sidebar.workspaces": "Workspaces";
|
|
16
16
|
readonly "sidebar.search": "Search";
|
|
17
17
|
readonly "sidebar.clearSearch": "Clear search";
|
|
18
|
+
readonly "sidebar.entry.title": "Sidebar entry";
|
|
19
|
+
readonly "sidebar.entry.desc": "Show dedicated scheduled tasks entry in the primary sidebar";
|
|
20
|
+
readonly "sidebar.entry.toggle": "Toggle sidebar entry";
|
|
21
|
+
readonly "update.entry.title": "Online update";
|
|
22
|
+
readonly "update.entry.desc": "Check for updates and notify when new versions are available";
|
|
23
|
+
readonly "update.entry.toggle": "Toggle online update";
|
|
24
|
+
readonly "officialSchedule.entry.title": "Hide official automation entry";
|
|
25
|
+
readonly "officialSchedule.entry.desc": "Hide built-in 'Automation tasks' sidebar entry to avoid conflict. Restores automatically when disabled or uninstalled.";
|
|
26
|
+
readonly "officialSchedule.entry.toggle": "Toggle hide official automation entry";
|
|
27
|
+
readonly "error.update.disabled": "locked by host configuration";
|
|
18
28
|
readonly "header.lead": "Plan automatic tasks, or trigger them by hand. Describe the recurring work in any chat to create it quickly.";
|
|
19
29
|
readonly "header.githubProject": "GitHub";
|
|
20
30
|
readonly "header.githubFeedback": "Issues";
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { handlePluginUpdateEscape, manualPluginUpdateCommand } from './plugin-update-model.js';
|
|
2
2
|
export { handlePluginUpdateEscape, manualPluginUpdateCommand };
|
|
3
|
+
export declare const PLUGIN_UPDATE_TOGGLE_EVENT = "dsh-automation-update-toggle";
|
|
3
4
|
export type PluginUpdateUiOptions = {
|
|
4
5
|
readonly endpoint: string;
|
|
5
6
|
readonly packageName: string;
|
|
@@ -8,6 +9,7 @@ export type PluginUpdateUiOptions = {
|
|
|
8
9
|
readonly zhName: string;
|
|
9
10
|
readonly enName: string;
|
|
10
11
|
readonly createIcon: (name: PluginUpdateIconName) => HTMLElement;
|
|
12
|
+
readonly isEnabled?: () => boolean;
|
|
11
13
|
};
|
|
12
14
|
export type PluginUpdateIconName = 'refresh' | 'download' | 'copy' | 'close';
|
|
13
15
|
export declare function observePluginUpdate(options: PluginUpdateUiOptions): () => void;
|
package/lib/types/index.d.ts
CHANGED
|
@@ -6,6 +6,9 @@ export interface Config {
|
|
|
6
6
|
readonly runTimeoutMinutes?: number;
|
|
7
7
|
readonly misfireGraceMinutes?: number;
|
|
8
8
|
readonly historyLimit?: number;
|
|
9
|
+
readonly stateDir?: string;
|
|
10
|
+
readonly updater?: boolean;
|
|
11
|
+
readonly onlineUpdate?: boolean;
|
|
9
12
|
}
|
|
10
13
|
export declare const Config: any;
|
|
11
14
|
export type SessionApprovalPolicy = 'ask' | 'never';
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
2
|
+
export declare const SETTINGS_API_HEADER = "x-michengai-client";
|
|
3
|
+
export interface PersistedSettings {
|
|
4
|
+
sidebarEntry?: boolean | undefined;
|
|
5
|
+
onlineUpdate?: boolean | undefined;
|
|
6
|
+
hideOfficialSchedule?: boolean | undefined;
|
|
7
|
+
}
|
|
8
|
+
export declare class SettingsManager {
|
|
9
|
+
private readonly file;
|
|
10
|
+
private readonly hostUpdateDisabled;
|
|
11
|
+
private store;
|
|
12
|
+
constructor(options: {
|
|
13
|
+
ctx: Context;
|
|
14
|
+
stateDir?: string;
|
|
15
|
+
hostUpdateDisabled?: boolean;
|
|
16
|
+
});
|
|
17
|
+
private load;
|
|
18
|
+
private flush;
|
|
19
|
+
getSidebarEntry(): boolean;
|
|
20
|
+
setSidebarEntry(enabled: boolean): Promise<boolean>;
|
|
21
|
+
getHideOfficialSchedule(): boolean;
|
|
22
|
+
setHideOfficialSchedule(enabled: boolean): Promise<boolean>;
|
|
23
|
+
isHostUpdateDisabled(): boolean;
|
|
24
|
+
isUpdateEnabled(): boolean;
|
|
25
|
+
setOnlineUpdate(enabled: boolean): Promise<boolean>;
|
|
26
|
+
}
|
|
27
|
+
export declare function registerSettingsApi(ctx: Context, manager: SettingsManager): () => void;
|
package/package.json
CHANGED