ec-web 0.0.6 → 0.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/dist/fs-utils.js +89 -65
- package/dist/paths.js +1 -1
- package/dist/sources/msg.js +35 -26
- package/dist/sources/stats.js +1 -1
- package/dist/static/app.js +25 -2
- package/dist/static/index.html +1 -1
- package/package.json +1 -1
package/dist/fs-utils.js
CHANGED
|
@@ -14,72 +14,104 @@ export function encodeSegment(s) {
|
|
|
14
14
|
export function decodeDirSegment(seg) {
|
|
15
15
|
return seg.replace(/%([0-9A-Fa-f]{2})/g, (_, hex) => String.fromCharCode(parseInt(hex, 16)));
|
|
16
16
|
}
|
|
17
|
+
function encodeChannelSessionComponent(value) {
|
|
18
|
+
const encoded = encodeURIComponent(value).replace(/\*/g, '%2A');
|
|
19
|
+
return encoded.endsWith('.') ? `${encoded.slice(0, -1)}%2E` : encoded;
|
|
20
|
+
}
|
|
21
|
+
function formatChannelSessionKeyParts(channelType, channelName, channelId) {
|
|
22
|
+
if (!channelType || !channelName || !channelId)
|
|
23
|
+
throw new Error('channel session key components must not be empty');
|
|
24
|
+
const key = [channelType, channelName, channelId].map(encodeChannelSessionComponent).join('#');
|
|
25
|
+
if (Buffer.byteLength(key, 'utf8') > 255)
|
|
26
|
+
throw new Error('channel session key exceeds the 255-byte filesystem limit');
|
|
27
|
+
return key;
|
|
28
|
+
}
|
|
29
|
+
export function formatChannelSessionKey(channelType, channelId, channelKey) {
|
|
30
|
+
const parts = channelKey?.split('#');
|
|
31
|
+
const channelName = parts?.length === 3 && parts[0] === channelType && parts[2]?.trim()
|
|
32
|
+
? parts[2].trim()
|
|
33
|
+
: 'main';
|
|
34
|
+
return formatChannelSessionKeyParts(channelType, channelName, channelId);
|
|
35
|
+
}
|
|
36
|
+
export function tryParseChannelSessionKey(key) {
|
|
37
|
+
const parts = key.split('#');
|
|
38
|
+
if (parts.length !== 3 || parts.some(part => !part))
|
|
39
|
+
return undefined;
|
|
40
|
+
try {
|
|
41
|
+
const parsed = {
|
|
42
|
+
channelType: decodeURIComponent(parts[0]),
|
|
43
|
+
channelName: decodeURIComponent(parts[1]),
|
|
44
|
+
channelId: decodeURIComponent(parts[2]),
|
|
45
|
+
};
|
|
46
|
+
if (!parsed.channelType || !parsed.channelName || !parsed.channelId)
|
|
47
|
+
return undefined;
|
|
48
|
+
return formatChannelSessionKeyParts(parsed.channelType, parsed.channelName, parsed.channelId) === key
|
|
49
|
+
? parsed
|
|
50
|
+
: undefined;
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
return undefined;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
17
56
|
/** 与 evolcore cross-platform.ts encodePath 一致(CC transcript 目录名) */
|
|
18
57
|
export function encodePath(p) {
|
|
19
58
|
const isWindows = process.platform === 'win32';
|
|
20
59
|
const norm = isWindows ? p.replace(/\\/g, '/') : p;
|
|
21
60
|
return encodeURIComponent(norm).replace(/%2F/gi, '-').replace(/%3A/gi, '-').replace(/^-/, '');
|
|
22
61
|
}
|
|
62
|
+
function hasSessionPayload(dirPath) {
|
|
63
|
+
try {
|
|
64
|
+
return fs.readdirSync(dirPath, { withFileTypes: true }).some(entry => (entry.isFile() && (entry.name === 'active.json'
|
|
65
|
+
|| entry.name === 'messages.jsonl'
|
|
66
|
+
|| (entry.name.startsWith('meta_') && entry.name.endsWith('.jsonl')))) || (entry.isDirectory() && entry.name === '_threads'));
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
23
72
|
export function scanChatDirs(sessionsDir) {
|
|
24
73
|
const results = [];
|
|
25
|
-
let
|
|
74
|
+
let agents;
|
|
26
75
|
try {
|
|
27
|
-
|
|
76
|
+
agents = fs.readdirSync(sessionsDir, { withFileTypes: true });
|
|
28
77
|
}
|
|
29
78
|
catch {
|
|
30
79
|
return results;
|
|
31
80
|
}
|
|
32
|
-
for (const
|
|
33
|
-
if (!
|
|
81
|
+
for (const agent of agents) {
|
|
82
|
+
if (!agent.isDirectory())
|
|
34
83
|
continue;
|
|
35
|
-
const
|
|
36
|
-
const
|
|
37
|
-
let
|
|
84
|
+
const selfAID = decodeDirSegment(agent.name);
|
|
85
|
+
const agentSessions = path.join(sessionsDir, agent.name, 'sessions');
|
|
86
|
+
let sessions;
|
|
38
87
|
try {
|
|
39
|
-
|
|
88
|
+
sessions = fs.readdirSync(agentSessions, { withFileTypes: true });
|
|
40
89
|
}
|
|
41
90
|
catch {
|
|
42
91
|
continue;
|
|
43
92
|
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
channelId: decodeDirSegment(peerEnc.name),
|
|
64
|
-
selfAID,
|
|
65
|
-
});
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
else {
|
|
70
|
-
for (const chanEnc of level2) {
|
|
71
|
-
if (!chanEnc.isDirectory())
|
|
72
|
-
continue;
|
|
73
|
-
results.push({
|
|
74
|
-
dirPath: path.join(ctDir, chanEnc.name),
|
|
75
|
-
channelType,
|
|
76
|
-
channelId: decodeDirSegment(chanEnc.name),
|
|
77
|
-
selfAID: '',
|
|
78
|
-
});
|
|
79
|
-
}
|
|
93
|
+
for (const session of sessions) {
|
|
94
|
+
if (!session.isDirectory())
|
|
95
|
+
continue;
|
|
96
|
+
const parsed = tryParseChannelSessionKey(session.name);
|
|
97
|
+
if (!parsed)
|
|
98
|
+
continue;
|
|
99
|
+
const dirPath = path.join(agentSessions, session.name);
|
|
100
|
+
if (!hasSessionPayload(dirPath))
|
|
101
|
+
continue;
|
|
102
|
+
results.push({
|
|
103
|
+
dirPath,
|
|
104
|
+
channelType: parsed.channelType,
|
|
105
|
+
channelKey: parsed.channelType === 'daemon'
|
|
106
|
+
? 'daemon'
|
|
107
|
+
: `${parsed.channelType}#${selfAID}#${parsed.channelName}`,
|
|
108
|
+
channelId: parsed.channelId,
|
|
109
|
+
channelSessionKey: session.name,
|
|
110
|
+
selfAID,
|
|
111
|
+
});
|
|
80
112
|
}
|
|
81
113
|
}
|
|
82
|
-
return results;
|
|
114
|
+
return results.sort((left, right) => left.dirPath < right.dirPath ? -1 : left.dirPath > right.dirPath ? 1 : 0);
|
|
83
115
|
}
|
|
84
116
|
export function readJsonFile(filePath) {
|
|
85
117
|
try {
|
|
@@ -123,34 +155,26 @@ export function isTransientProtocolMessage(entry) {
|
|
|
123
155
|
return transient(entry.payloadType) || transient(entry.msgType);
|
|
124
156
|
}
|
|
125
157
|
export function getSessionsAunDir() {
|
|
126
|
-
return
|
|
158
|
+
return resolvePaths().sessionsDir;
|
|
127
159
|
}
|
|
128
160
|
export function listLocalAids(aunDir) {
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
.
|
|
132
|
-
.map(e => decodeDirSegment(e.name));
|
|
133
|
-
}
|
|
134
|
-
catch {
|
|
135
|
-
return [];
|
|
136
|
-
}
|
|
161
|
+
return [...new Set(scanChatDirs(aunDir)
|
|
162
|
+
.filter(chat => chat.channelType === 'aun')
|
|
163
|
+
.map(chat => chat.selfAID))].sort();
|
|
137
164
|
}
|
|
138
165
|
export function listPeers(aunDir, localAid) {
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
}
|
|
145
|
-
catch {
|
|
146
|
-
return [];
|
|
147
|
-
}
|
|
166
|
+
return [...new Set(scanChatDirs(aunDir)
|
|
167
|
+
.filter(chat => chat.channelType === 'aun' && chat.selfAID === localAid)
|
|
168
|
+
.map(chat => chat.channelId))].sort();
|
|
169
|
+
}
|
|
170
|
+
function aunChatDir(aunDir, localAid, peerId) {
|
|
171
|
+
return path.join(aunDir, encodeSegment(localAid), 'sessions', formatChannelSessionKey('aun', peerId, `aun#${localAid}#main`));
|
|
148
172
|
}
|
|
149
173
|
export function readMessages(aunDir, localAid, peerId) {
|
|
150
|
-
return readAllJsonlLines(path.join(aunDir,
|
|
174
|
+
return readAllJsonlLines(path.join(aunChatDir(aunDir, localAid, peerId), 'messages.jsonl')).filter(m => !isTransientProtocolMessage(m));
|
|
151
175
|
}
|
|
152
176
|
function readPeerName(aunDir, localAid, peerId) {
|
|
153
|
-
return readJsonFile(path.join(aunDir,
|
|
177
|
+
return readJsonFile(path.join(aunChatDir(aunDir, localAid, peerId), 'active.json'))?.metadata?.peerName ?? null;
|
|
154
178
|
}
|
|
155
179
|
export function loadAidInfo(aunDir, aid) {
|
|
156
180
|
const peers = listPeers(aunDir, aid);
|
package/dist/paths.js
CHANGED
|
@@ -42,7 +42,7 @@ export function resolvePaths() {
|
|
|
42
42
|
const root = resolveRoot();
|
|
43
43
|
return {
|
|
44
44
|
root,
|
|
45
|
-
sessionsDir: path.join(root, '
|
|
45
|
+
sessionsDir: path.join(root, 'agents'),
|
|
46
46
|
instanceDir: path.join(root, 'data', 'instance'),
|
|
47
47
|
dataDir: path.join(root, 'data'),
|
|
48
48
|
logs: path.join(root, 'logs'),
|
package/dist/sources/msg.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* 消息数据源:扫描
|
|
2
|
+
* 消息数据源:扫描 agents/<aid>/sessions 下所有渠道的 messages.jsonl。
|
|
3
3
|
*
|
|
4
4
|
* 左列按 agent 聚合;中列列出该 agent 下所有渠道的私聊/群聊会话。
|
|
5
5
|
* 兼容旧参数名 aid/peer:前端的 aid 实际上传 scope id。
|
|
@@ -9,7 +9,7 @@ import path from 'path';
|
|
|
9
9
|
import { ipcQuery } from '../ipc-client.js';
|
|
10
10
|
import { resolvePaths } from '../paths.js';
|
|
11
11
|
import { resolveParentDistModule, toFileUrl } from './parent-package.js';
|
|
12
|
-
import { isTransientProtocolMessage, readAllJsonlLines, readJsonFile, } from '../fs-utils.js';
|
|
12
|
+
import { decodeDirSegment, isTransientProtocolMessage, readAllJsonlLines, readJsonFile, scanChatDirs, tryParseChannelSessionKey, } from '../fs-utils.js';
|
|
13
13
|
let contextModulesPromise = null;
|
|
14
14
|
const sessionAdapterPromises = new Map();
|
|
15
15
|
let peerDisplayResolverPromise = null;
|
|
@@ -201,26 +201,10 @@ function getSessionAdapter(baseagent) {
|
|
|
201
201
|
function safeId(value) {
|
|
202
202
|
return Buffer.from(value, 'utf8').toString('base64url');
|
|
203
203
|
}
|
|
204
|
-
function findMessageFiles(root) {
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
try {
|
|
209
|
-
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
210
|
-
}
|
|
211
|
-
catch {
|
|
212
|
-
return;
|
|
213
|
-
}
|
|
214
|
-
for (const entry of entries) {
|
|
215
|
-
const full = path.join(dir, entry.name);
|
|
216
|
-
if (entry.isDirectory())
|
|
217
|
-
visit(full);
|
|
218
|
-
else if (entry.isFile() && entry.name === 'messages.jsonl')
|
|
219
|
-
out.push(full);
|
|
220
|
-
}
|
|
221
|
-
};
|
|
222
|
-
visit(root);
|
|
223
|
-
return out;
|
|
204
|
+
export function findMessageFiles(root) {
|
|
205
|
+
return scanChatDirs(root)
|
|
206
|
+
.map(chat => path.join(chat.dirPath, 'messages.jsonl'))
|
|
207
|
+
.filter(file => fs.existsSync(file));
|
|
224
208
|
}
|
|
225
209
|
function fileSignature(file) {
|
|
226
210
|
try {
|
|
@@ -488,9 +472,6 @@ export async function getAgentSelfNames() {
|
|
|
488
472
|
agentDisplayNamesCache = { expiresAt: Date.now() + 5000, names };
|
|
489
473
|
return names;
|
|
490
474
|
}
|
|
491
|
-
function decodeDirSegment(seg) {
|
|
492
|
-
return seg.replace(/%([0-9A-Fa-f]{2})/g, (_, hex) => String.fromCharCode(parseInt(hex, 16)));
|
|
493
|
-
}
|
|
494
475
|
function parseChannelKey(value) {
|
|
495
476
|
if (typeof value !== 'string')
|
|
496
477
|
return null;
|
|
@@ -515,10 +496,38 @@ function parseLegacySessionKey(value, channelType) {
|
|
|
515
496
|
return null;
|
|
516
497
|
return { type: parts[0], selfAID: parts[1], name: parts[2] || 'main' };
|
|
517
498
|
}
|
|
518
|
-
function pathIdentity(sessionsDir, dirPath) {
|
|
499
|
+
export function pathIdentity(sessionsDir, dirPath) {
|
|
519
500
|
const relParts = path.relative(sessionsDir, dirPath).split(path.sep).filter(Boolean);
|
|
520
501
|
if (!relParts.length)
|
|
521
502
|
return {};
|
|
503
|
+
// Current layout: agents/<aid>/sessions/<channelSessionKey>/
|
|
504
|
+
if (relParts.length === 3 && relParts[1] === 'sessions') {
|
|
505
|
+
const selfAID = decodeDirSegment(relParts[0]);
|
|
506
|
+
const parsed = tryParseChannelSessionKey(relParts[2]);
|
|
507
|
+
if (parsed) {
|
|
508
|
+
return {
|
|
509
|
+
channelType: parsed.channelType,
|
|
510
|
+
channel: parsed.channelType === 'daemon'
|
|
511
|
+
? 'daemon'
|
|
512
|
+
: `${parsed.channelType}#${selfAID}#${parsed.channelName}`,
|
|
513
|
+
channelId: parsed.channelId,
|
|
514
|
+
selfAID,
|
|
515
|
+
};
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
// Immediately preceding Agent layout, retained for inspecting backups.
|
|
519
|
+
if (relParts.length >= 4 && relParts[1] === 'sessions') {
|
|
520
|
+
const selfAID = decodeDirSegment(relParts[0]);
|
|
521
|
+
const channelKey = decodeDirSegment(relParts[2]);
|
|
522
|
+
const parsed = parseChannelKey(channelKey);
|
|
523
|
+
return {
|
|
524
|
+
channelType: parsed?.type || channelKey,
|
|
525
|
+
channel: channelKey,
|
|
526
|
+
channelId: decodeDirSegment(relParts[3]),
|
|
527
|
+
selfAID: parsed?.selfAID || selfAID,
|
|
528
|
+
};
|
|
529
|
+
}
|
|
530
|
+
// Pre-migration path fallback for viewing a backup directory only.
|
|
522
531
|
const firstKey = parseChannelKey(decodeDirSegment(relParts[0]));
|
|
523
532
|
if (firstKey) {
|
|
524
533
|
return {
|
package/dist/sources/stats.js
CHANGED
|
@@ -447,7 +447,7 @@ function getPeerInfo(peerKey) {
|
|
|
447
447
|
if (target.startsWith('group.')) {
|
|
448
448
|
// 群聊:读取群信息
|
|
449
449
|
const { sessionsDir } = resolvePaths();
|
|
450
|
-
const groupDir = path.join(sessionsDir, '
|
|
450
|
+
const groupDir = path.join(sessionsDir, encodeSegment(agentAid), 'sessions', encodeSegment(`aun#${agentAid}#main`), encodeSegment(target));
|
|
451
451
|
const activeJsonPath = path.join(groupDir, 'active.json');
|
|
452
452
|
if (fs.existsSync(activeJsonPath)) {
|
|
453
453
|
const activeData = JSON.parse(fs.readFileSync(activeJsonPath, 'utf-8'));
|
package/dist/static/app.js
CHANGED
|
@@ -5213,6 +5213,18 @@ const TRIGGER_CONCURRENCY_OPTIONS = [
|
|
|
5213
5213
|
['replace', () => t('triggers.concurrency.replace')],
|
|
5214
5214
|
['allow', () => t('triggers.concurrency.allow')],
|
|
5215
5215
|
];
|
|
5216
|
+
|
|
5217
|
+
function triggerCanonicalThread(thread) {
|
|
5218
|
+
return thread === 'per_run' || thread === 'by_run' ? 'by_run' : 'by_trigger';
|
|
5219
|
+
}
|
|
5220
|
+
|
|
5221
|
+
function triggerConcurrencyOptions(executionType, thread) {
|
|
5222
|
+
if (executionType === 'script') return TRIGGER_CONCURRENCY_OPTIONS;
|
|
5223
|
+
if (executionType === 'trigger_session' && triggerCanonicalThread(thread) === 'by_run') {
|
|
5224
|
+
return TRIGGER_CONCURRENCY_OPTIONS;
|
|
5225
|
+
}
|
|
5226
|
+
return TRIGGER_CONCURRENCY_OPTIONS.filter(([value]) => value !== 'allow');
|
|
5227
|
+
}
|
|
5216
5228
|
const TRIGGER_MISSED_POLICY_OPTIONS = [
|
|
5217
5229
|
['skip', () => t('triggers.missed.skip')],
|
|
5218
5230
|
['run_once', () => t('triggers.missed.run_once')],
|
|
@@ -5758,6 +5770,7 @@ function triggerOriginalEditState(t) {
|
|
|
5758
5770
|
name: String(definition?.name ?? t?.name ?? t?.label ?? ''),
|
|
5759
5771
|
enabled: triggerIsEnabled(t),
|
|
5760
5772
|
executionType,
|
|
5773
|
+
triggerThread: String(execution.thread ?? ''),
|
|
5761
5774
|
scriptPath: String(execution.script?.path ?? ''),
|
|
5762
5775
|
scriptRuntime: String(execution.script?.runtime ?? 'node'),
|
|
5763
5776
|
scriptTimeoutMs: Number(execution.script?.timeoutMs ?? 30_000),
|
|
@@ -6554,6 +6567,7 @@ function syncTriggerEditFields(ctx) {
|
|
|
6554
6567
|
const valueField = root.querySelector('#trig-edit-schedule-value-field');
|
|
6555
6568
|
const eventFields = root.querySelectorAll('.trig-event-field');
|
|
6556
6569
|
const executionType = root.querySelector('#trig-edit-type')?.value || ctx.original.executionType;
|
|
6570
|
+
const concurrencySelect = root.querySelector('#trig-edit-concurrency');
|
|
6557
6571
|
const deliverySwitch = root.querySelector('#trig-edit-delivery-enabled');
|
|
6558
6572
|
const deliverySwitchField = root.querySelector('.trig-delivery-switch');
|
|
6559
6573
|
if (executionType === 'target_session') {
|
|
@@ -6633,6 +6647,15 @@ function syncTriggerEditFields(ctx) {
|
|
|
6633
6647
|
if (scriptFeedback) scriptFeedback.hidden = executionType !== 'script' || !deliveryEnabled;
|
|
6634
6648
|
if (threadField) threadField.hidden = !showTarget || targetSession !== 'thread';
|
|
6635
6649
|
if (threadInput) threadInput.disabled = ctx.saving || !showTarget || targetSession !== 'thread';
|
|
6650
|
+
if (concurrencySelect) {
|
|
6651
|
+
const options = triggerConcurrencyOptions(executionType, ctx.original.triggerThread);
|
|
6652
|
+
const current = options.some(([value]) => value === concurrencySelect.value)
|
|
6653
|
+
? concurrencySelect.value
|
|
6654
|
+
: 'forbid';
|
|
6655
|
+
concurrencySelect.innerHTML = triggerOptionHtml(options, current, null);
|
|
6656
|
+
concurrencySelect.value = current;
|
|
6657
|
+
concurrencySelect.disabled = Boolean(ctx.saving);
|
|
6658
|
+
}
|
|
6636
6659
|
}
|
|
6637
6660
|
|
|
6638
6661
|
function readTriggerEditValues(root) {
|
|
@@ -6779,7 +6802,7 @@ function triggerDefinitionFromEdit(original, values, eventFilter, feedbackStrate
|
|
|
6779
6802
|
execution.prompt = values.prompt;
|
|
6780
6803
|
delete execution.script;
|
|
6781
6804
|
if (values.executionType === 'trigger_session') {
|
|
6782
|
-
if (previousExecutionType !== 'trigger_session' || !['per_run', 'by_trigger'].includes(execution.thread)) {
|
|
6805
|
+
if (previousExecutionType !== 'trigger_session' || !['per_run', 'by_run', 'by_trigger'].includes(execution.thread)) {
|
|
6783
6806
|
execution.thread = 'by_trigger';
|
|
6784
6807
|
}
|
|
6785
6808
|
} else {
|
|
@@ -7024,7 +7047,7 @@ function openTriggerEditDrawer(id, agent) {
|
|
|
7024
7047
|
`<div class="ag-edit-grid trig-edit-grid trig-limit-grid">` +
|
|
7025
7048
|
`<label class="ag-edit-field"><span>${t('triggers.field.maxRuns')}</span><input id="trig-edit-max-runs" type="number" min="1" step="1" value="${esc(original.maxRuns)}" placeholder="${t('common.noLimit')}"></label>` +
|
|
7026
7049
|
`<label class="ag-edit-field"><span>${t('triggers.field.maxDuration')}</span><input id="trig-edit-max-duration" type="text" autocomplete="off" spellcheck="false" value="${esc(original.maxDuration)}" placeholder="${t('triggers.placeholder.maxDuration')}"></label>` +
|
|
7027
|
-
`<label class="ag-edit-field"><span>${t('triggers.field.concurrency')}</span><select id="trig-edit-concurrency">${triggerOptionHtml(
|
|
7050
|
+
`<label class="ag-edit-field"><span>${t('triggers.field.concurrency')}</span><select id="trig-edit-concurrency">${triggerOptionHtml(triggerConcurrencyOptions(original.executionType, original.triggerThread), original.concurrency, null)}</select></label>` +
|
|
7028
7051
|
`<label class="ag-edit-field"><span>${t('triggers.field.retryAttempts')}</span><input id="trig-edit-retry-attempts" type="number" min="0" max="20" step="1" value="${esc(original.retryAttempts)}"></label>` +
|
|
7029
7052
|
`<label class="ag-edit-field"><span>${t('triggers.field.retryBackoff')}</span><input id="trig-edit-retry-backoff" type="text" autocomplete="off" spellcheck="false" value="${esc(original.retryBackoff)}" placeholder="${t('triggers.placeholder.retryBackoff')}"></label>` +
|
|
7030
7053
|
`<label class="ag-edit-field"><span>${t('triggers.field.missedPolicy')}</span><select id="trig-edit-missed-policy">${triggerOptionHtml(TRIGGER_MISSED_POLICY_OPTIONS, original.missedPolicy, null)}</select></label>` +
|
package/dist/static/index.html
CHANGED
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
|
|
49
49
|
</nav>
|
|
50
50
|
<span style="flex:1"></span>
|
|
51
|
-
2026-
|
|
51
|
+
2026-08-03 19:27
|
|
52
52
|
<span id="conn-status" class="conn-status" data-i18n="status.connecting">连接中…</span>
|
|
53
53
|
<button id="lang-btn" class="theme-btn" data-i18n-title="language.buttonTitle" title="切换语言">🌐</button>
|
|
54
54
|
<button id="theme-btn" class="theme-btn" data-i18n-title="theme.buttonTitle" title="主题">🌗</button>
|