a2acalling 0.6.48 → 0.6.50
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/bin/cli.js +79 -2
- package/docs/plans/2026-02-16-auto-updater.md +1284 -0
- package/docs/plans/2026-02-16-e2e-test-prompt-sequence.md +3085 -0
- package/docs/plans/2026-02-17-claude-code-codex-skills.md +770 -0
- package/docs/prompts/e2e-test-agent.md +368 -0
- package/docs/protocol.md +121 -0
- package/package.json +1 -1
- package/scripts/install-skills.js +80 -0
- package/scripts/postinstall.js +12 -0
- package/src/dashboard/public/app.js +108 -1
- package/src/dashboard/public/index.html +9 -0
- package/src/dashboard/public/style.css +27 -0
- package/src/lib/claude-subagent.js +6 -5
- package/src/lib/config.js +42 -0
- package/src/lib/conversation-driver.js +74 -24
- package/src/lib/openclaw-integration.js +22 -66
- package/src/lib/runtime-adapter.js +8 -3
- package/src/lib/summary-formatter.js +168 -0
- package/src/lib/summary-prompt.js +203 -0
- package/src/lib/tokens.js +13 -1
- package/src/lib/turn-timeout.js +52 -0
- package/src/lib/update-checker.js +93 -0
- package/src/lib/update-manager.js +313 -0
- package/src/routes/a2a.js +9 -1
- package/src/routes/dashboard.js +103 -1
- package/src/server.js +130 -26
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
const { execFile } = require('child_process');
|
|
2
|
+
const { EventEmitter } = require('events');
|
|
3
|
+
const { createLogger } = require('./logger');
|
|
4
|
+
const { checkForUpdate, isSameMajor } = require('./update-checker');
|
|
5
|
+
|
|
6
|
+
const DEFAULT_INTERVAL_MS = 60 * 60 * 1000;
|
|
7
|
+
const DEFAULT_NPM_TIMEOUT_MS = 2 * 60 * 1000;
|
|
8
|
+
|
|
9
|
+
function isUpdateSafe(callMonitor) {
|
|
10
|
+
if (!callMonitor || typeof callMonitor.getActiveCount !== 'function') return true;
|
|
11
|
+
return Number(callMonitor.getActiveCount()) === 0;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function shouldApplyUpdate(currentVersion, latestVersion, options = {}) {
|
|
15
|
+
if (!latestVersion) return false;
|
|
16
|
+
if (options.allowMajor) return true;
|
|
17
|
+
return isSameMajor(currentVersion, latestVersion);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function autoUpdateDisabledByEnv() {
|
|
21
|
+
if (String(process.env.CI || '').toLowerCase() === 'true') return true;
|
|
22
|
+
if (String(process.env.A2A_AUTO_UPDATE || '').trim() === '0') return true;
|
|
23
|
+
if (String(process.env.NO_AUTO_UPDATE || '').trim() === '1') return true;
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function execFilePromise(cmd, args, options = {}) {
|
|
28
|
+
return new Promise((resolve, reject) => {
|
|
29
|
+
execFile(cmd, args, options, (err, stdout, stderr) => {
|
|
30
|
+
if (err) {
|
|
31
|
+
err.stdout = stdout;
|
|
32
|
+
err.stderr = stderr;
|
|
33
|
+
reject(err);
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
resolve({ stdout, stderr });
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
class UpdateManager extends EventEmitter {
|
|
42
|
+
constructor(options = {}) {
|
|
43
|
+
super();
|
|
44
|
+
this.currentVersion = options.currentVersion;
|
|
45
|
+
this.config = options.config || null;
|
|
46
|
+
this.logger = options.logger || createLogger({ component: 'a2a.updater' });
|
|
47
|
+
this.intervalMs = Number.isFinite(options.intervalMs) && options.intervalMs > 0
|
|
48
|
+
? options.intervalMs
|
|
49
|
+
: DEFAULT_INTERVAL_MS;
|
|
50
|
+
this.allowMajor = Boolean(options.allowMajor);
|
|
51
|
+
this.enabled = options.enabled !== false;
|
|
52
|
+
this.getCallMonitor = typeof options.getCallMonitor === 'function'
|
|
53
|
+
? options.getCallMonitor
|
|
54
|
+
: (() => null);
|
|
55
|
+
this.installTimeoutMs = Number.isFinite(options.installTimeoutMs) && options.installTimeoutMs > 0
|
|
56
|
+
? options.installTimeoutMs
|
|
57
|
+
: DEFAULT_NPM_TIMEOUT_MS;
|
|
58
|
+
this.restartFn = typeof options.restartFn === 'function' ? options.restartFn : null;
|
|
59
|
+
this._execFile = options.execFile || execFilePromise;
|
|
60
|
+
this._status = {
|
|
61
|
+
state: 'up_to_date',
|
|
62
|
+
enabled: this.enabled,
|
|
63
|
+
current_version: this.currentVersion,
|
|
64
|
+
latest_version: this.currentVersion,
|
|
65
|
+
target_version: null,
|
|
66
|
+
active_calls: 0,
|
|
67
|
+
last_checked_at: null,
|
|
68
|
+
last_success_at: null,
|
|
69
|
+
last_error: null,
|
|
70
|
+
defer_reason: null
|
|
71
|
+
};
|
|
72
|
+
this._timer = null;
|
|
73
|
+
this._running = false;
|
|
74
|
+
this._pendingManualUpdate = false;
|
|
75
|
+
|
|
76
|
+
this._loadConfig();
|
|
77
|
+
if (autoUpdateDisabledByEnv()) {
|
|
78
|
+
this.enabled = false;
|
|
79
|
+
this._status.enabled = false;
|
|
80
|
+
this.logger.info('Auto-update disabled by environment', { event: 'updater_env_disabled' });
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
_loadConfig() {
|
|
85
|
+
if (!this.config || typeof this.config.getAutoUpdate !== 'function') return;
|
|
86
|
+
const cfg = this.config.getAutoUpdate();
|
|
87
|
+
if (cfg && typeof cfg === 'object') {
|
|
88
|
+
if (typeof cfg.enabled === 'boolean') this.enabled = cfg.enabled;
|
|
89
|
+
if (Number.isFinite(cfg.intervalMs) && cfg.intervalMs > 0) this.intervalMs = cfg.intervalMs;
|
|
90
|
+
if (typeof cfg.allowMajor === 'boolean') this.allowMajor = cfg.allowMajor;
|
|
91
|
+
}
|
|
92
|
+
this._status.enabled = this.enabled;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
_persistConfigPatch(patch) {
|
|
96
|
+
if (!this.config || typeof this.config.setAutoUpdate !== 'function') return;
|
|
97
|
+
try {
|
|
98
|
+
this.config.setAutoUpdate(patch);
|
|
99
|
+
} catch (err) {
|
|
100
|
+
this.logger.warn('Failed to persist auto-update config patch', {
|
|
101
|
+
event: 'updater_config_patch_failed',
|
|
102
|
+
error: err
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
_setStatus(patch) {
|
|
108
|
+
this._status = { ...this._status, ...patch };
|
|
109
|
+
this.emit('status', this.getStatus());
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
getStatus() {
|
|
113
|
+
return {
|
|
114
|
+
...this._status,
|
|
115
|
+
enabled: this.enabled,
|
|
116
|
+
interval_ms: this.intervalMs,
|
|
117
|
+
allow_major: this.allowMajor
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
async setEnabled(enabled) {
|
|
122
|
+
this.enabled = Boolean(enabled);
|
|
123
|
+
this._setStatus({ enabled: this.enabled });
|
|
124
|
+
this._persistConfigPatch({ enabled: this.enabled });
|
|
125
|
+
if (this.enabled && !this._timer) {
|
|
126
|
+
this.start();
|
|
127
|
+
await this.triggerCheck({ reason: 'manual_enable' });
|
|
128
|
+
}
|
|
129
|
+
if (!this.enabled && this._timer) {
|
|
130
|
+
this.stop();
|
|
131
|
+
}
|
|
132
|
+
return this.getStatus();
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
start() {
|
|
136
|
+
if (this._timer) return;
|
|
137
|
+
if (!this.enabled) {
|
|
138
|
+
this.logger.info('Auto-updater disabled', { event: 'updater_disabled' });
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
this._timer = setInterval(() => {
|
|
143
|
+
this.triggerCheck({ reason: 'interval' }).catch((err) => {
|
|
144
|
+
this.logger.warn('Auto-update interval check failed', {
|
|
145
|
+
event: 'updater_interval_failed',
|
|
146
|
+
error: err
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
}, this.intervalMs);
|
|
150
|
+
|
|
151
|
+
this.logger.info('Auto-updater started', {
|
|
152
|
+
event: 'updater_started',
|
|
153
|
+
data: {
|
|
154
|
+
interval_ms: this.intervalMs,
|
|
155
|
+
allow_major: this.allowMajor
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
stop() {
|
|
161
|
+
if (!this._timer) return;
|
|
162
|
+
clearInterval(this._timer);
|
|
163
|
+
this._timer = null;
|
|
164
|
+
this.logger.info('Auto-updater stopped', { event: 'updater_stopped' });
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
async triggerCheck(options = {}) {
|
|
168
|
+
return this._runCycle({ ...options, manualUpdate: false, forceCheck: Boolean(options.forceCheck) });
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
async triggerUpdate(options = {}) {
|
|
172
|
+
this._pendingManualUpdate = true;
|
|
173
|
+
return this._runCycle({ ...options, manualUpdate: true });
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
async _runCycle(options = {}) {
|
|
177
|
+
if (this._running) {
|
|
178
|
+
return this.getStatus();
|
|
179
|
+
}
|
|
180
|
+
if (!this.enabled && !options.manualUpdate && !options.forceCheck) {
|
|
181
|
+
return this.getStatus();
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
this._running = true;
|
|
185
|
+
const nowIso = new Date().toISOString();
|
|
186
|
+
this._setStatus({
|
|
187
|
+
state: 'checking',
|
|
188
|
+
last_checked_at: nowIso,
|
|
189
|
+
last_error: null,
|
|
190
|
+
defer_reason: null
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
try {
|
|
194
|
+
const result = await checkForUpdate(this.currentVersion);
|
|
195
|
+
if (result.error) {
|
|
196
|
+
this._setStatus({
|
|
197
|
+
state: 'failed',
|
|
198
|
+
latest_version: this.currentVersion,
|
|
199
|
+
last_error: result.error
|
|
200
|
+
});
|
|
201
|
+
return this.getStatus();
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
const latest = result.latest || this.currentVersion;
|
|
205
|
+
if (!result.available) {
|
|
206
|
+
this._setStatus({
|
|
207
|
+
state: 'up_to_date',
|
|
208
|
+
latest_version: latest,
|
|
209
|
+
target_version: null
|
|
210
|
+
});
|
|
211
|
+
return this.getStatus();
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
if (!shouldApplyUpdate(this.currentVersion, latest, { allowMajor: this.allowMajor })) {
|
|
215
|
+
this._setStatus({
|
|
216
|
+
state: 'up_to_date',
|
|
217
|
+
latest_version: latest,
|
|
218
|
+
target_version: null,
|
|
219
|
+
defer_reason: 'cross_major_blocked'
|
|
220
|
+
});
|
|
221
|
+
return this.getStatus();
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
const monitor = this.getCallMonitor();
|
|
225
|
+
const activeCalls = monitor && typeof monitor.getActiveCount === 'function'
|
|
226
|
+
? Number(monitor.getActiveCount()) || 0
|
|
227
|
+
: 0;
|
|
228
|
+
if (!isUpdateSafe(monitor) && !options.force) {
|
|
229
|
+
this._setStatus({
|
|
230
|
+
state: 'waiting_for_safe_restart',
|
|
231
|
+
latest_version: latest,
|
|
232
|
+
target_version: latest,
|
|
233
|
+
active_calls: activeCalls,
|
|
234
|
+
defer_reason: 'active_calls'
|
|
235
|
+
});
|
|
236
|
+
return this.getStatus();
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
this._setStatus({
|
|
240
|
+
state: 'downloading',
|
|
241
|
+
latest_version: latest,
|
|
242
|
+
target_version: latest,
|
|
243
|
+
active_calls: activeCalls
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
await this._installVersion(latest);
|
|
247
|
+
this.currentVersion = latest;
|
|
248
|
+
this._persistConfigPatch({ lastGoodVersion: latest });
|
|
249
|
+
|
|
250
|
+
this._setStatus({
|
|
251
|
+
state: 'restarting',
|
|
252
|
+
current_version: latest,
|
|
253
|
+
latest_version: latest,
|
|
254
|
+
target_version: latest,
|
|
255
|
+
last_success_at: new Date().toISOString(),
|
|
256
|
+
last_error: null,
|
|
257
|
+
defer_reason: null,
|
|
258
|
+
active_calls: 0
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
if (this.restartFn) {
|
|
262
|
+
await this.restartFn(latest);
|
|
263
|
+
} else {
|
|
264
|
+
this._setStatus({
|
|
265
|
+
state: 'up_to_date',
|
|
266
|
+
current_version: latest,
|
|
267
|
+
latest_version: latest,
|
|
268
|
+
target_version: null
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
return this.getStatus();
|
|
273
|
+
} catch (err) {
|
|
274
|
+
const message = err && err.message ? err.message : 'update_failed';
|
|
275
|
+
this._setStatus({
|
|
276
|
+
state: 'failed',
|
|
277
|
+
last_error: message
|
|
278
|
+
});
|
|
279
|
+
this.logger.error('Auto-update failed', {
|
|
280
|
+
event: 'updater_failed',
|
|
281
|
+
error: err
|
|
282
|
+
});
|
|
283
|
+
return this.getStatus();
|
|
284
|
+
} finally {
|
|
285
|
+
this._running = false;
|
|
286
|
+
this._pendingManualUpdate = false;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
async _installVersion(version) {
|
|
291
|
+
const args = ['install', '-g', `a2acalling@${version}`];
|
|
292
|
+
this._setStatus({ state: 'applying' });
|
|
293
|
+
this.logger.info('Installing auto-update target', {
|
|
294
|
+
event: 'updater_install_start',
|
|
295
|
+
data: { version }
|
|
296
|
+
});
|
|
297
|
+
await this._execFile('npm', args, {
|
|
298
|
+
timeout: this.installTimeoutMs,
|
|
299
|
+
env: process.env
|
|
300
|
+
});
|
|
301
|
+
this.logger.info('Auto-update install complete', {
|
|
302
|
+
event: 'updater_install_done',
|
|
303
|
+
data: { version }
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
module.exports = {
|
|
309
|
+
DEFAULT_INTERVAL_MS,
|
|
310
|
+
isUpdateSafe,
|
|
311
|
+
shouldApplyUpdate,
|
|
312
|
+
UpdateManager
|
|
313
|
+
};
|
package/src/routes/a2a.js
CHANGED
|
@@ -174,17 +174,24 @@ function createRoutes(options = {}) {
|
|
|
174
174
|
maxDurationMs: options.maxDurationMs || 300000,
|
|
175
175
|
logger: logger.child({ component: 'a2a.call-monitor' })
|
|
176
176
|
});
|
|
177
|
+
if (typeof options.onCallMonitor === 'function') {
|
|
178
|
+
try {
|
|
179
|
+
options.onCallMonitor(monitor);
|
|
180
|
+
} catch (_) {}
|
|
181
|
+
}
|
|
177
182
|
|
|
178
183
|
/**
|
|
179
184
|
* GET /status
|
|
180
185
|
* Check if A2A is enabled
|
|
181
186
|
*/
|
|
182
187
|
router.get('/status', (req, res) => {
|
|
188
|
+
const activeCalls = monitor ? monitor.getActiveCount() : 0;
|
|
183
189
|
res.json({
|
|
184
190
|
a2a: true,
|
|
185
191
|
version: require('../../package.json').version,
|
|
186
192
|
capabilities: ['invoke', 'multi-turn'],
|
|
187
|
-
rate_limits: limits
|
|
193
|
+
rate_limits: limits,
|
|
194
|
+
active_calls: activeCalls
|
|
188
195
|
});
|
|
189
196
|
});
|
|
190
197
|
|
|
@@ -333,6 +340,7 @@ function createRoutes(options = {}) {
|
|
|
333
340
|
tier: validation.tier,
|
|
334
341
|
capabilities: validation.capabilities,
|
|
335
342
|
allowed_topics: validation.allowed_topics,
|
|
343
|
+
timeout_ms: validation.timeout_ms,
|
|
336
344
|
disclosure: validation.disclosure,
|
|
337
345
|
caller: sanitizedCaller,
|
|
338
346
|
conversation_id: conversation_id || `conv_${Date.now()}_${crypto.randomBytes(6).toString('hex')}`,
|
package/src/routes/dashboard.js
CHANGED
|
@@ -210,12 +210,45 @@ function buildContext(options = {}) {
|
|
|
210
210
|
config,
|
|
211
211
|
convStore,
|
|
212
212
|
callbookStore,
|
|
213
|
+
getUpdateManager: typeof options.getUpdateManager === 'function'
|
|
214
|
+
? options.getUpdateManager
|
|
215
|
+
: (() => null),
|
|
213
216
|
logger,
|
|
214
217
|
agentContext,
|
|
215
218
|
staticDir: DASHBOARD_STATIC_DIR
|
|
216
219
|
};
|
|
217
220
|
}
|
|
218
221
|
|
|
222
|
+
function resolveAutoUpdateStatus(context) {
|
|
223
|
+
const manager = context.getUpdateManager ? context.getUpdateManager() : null;
|
|
224
|
+
const config = context.config && typeof context.config.getAutoUpdate === 'function'
|
|
225
|
+
? context.config.getAutoUpdate()
|
|
226
|
+
: {
|
|
227
|
+
enabled: true,
|
|
228
|
+
intervalMs: 60 * 60 * 1000,
|
|
229
|
+
allowMajor: false,
|
|
230
|
+
lastGoodVersion: null
|
|
231
|
+
};
|
|
232
|
+
const runtime = manager && typeof manager.getStatus === 'function'
|
|
233
|
+
? manager.getStatus()
|
|
234
|
+
: null;
|
|
235
|
+
return {
|
|
236
|
+
enabled: runtime ? Boolean(runtime.enabled) : Boolean(config.enabled),
|
|
237
|
+
interval_ms: runtime && Number.isFinite(runtime.interval_ms) ? runtime.interval_ms : config.intervalMs,
|
|
238
|
+
allow_major: runtime ? Boolean(runtime.allow_major) : Boolean(config.allowMajor),
|
|
239
|
+
state: runtime ? runtime.state : 'up_to_date',
|
|
240
|
+
current_version: runtime ? runtime.current_version : require('../../package.json').version,
|
|
241
|
+
latest_version: runtime ? runtime.latest_version : null,
|
|
242
|
+
target_version: runtime ? runtime.target_version : null,
|
|
243
|
+
active_calls: runtime ? runtime.active_calls : 0,
|
|
244
|
+
last_checked_at: runtime ? runtime.last_checked_at : null,
|
|
245
|
+
last_success_at: runtime ? runtime.last_success_at : null,
|
|
246
|
+
last_error: runtime ? runtime.last_error : null,
|
|
247
|
+
defer_reason: runtime ? runtime.defer_reason : null,
|
|
248
|
+
last_good_version: config.lastGoodVersion || null
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
|
|
219
252
|
function buildContactIndex(contacts) {
|
|
220
253
|
const byName = new Map();
|
|
221
254
|
const byId = new Map();
|
|
@@ -537,7 +570,76 @@ function createDashboardApiRouter(options = {}) {
|
|
|
537
570
|
callbook: {
|
|
538
571
|
enabled: Boolean(context.callbookStore && context.callbookStore.isAvailable()),
|
|
539
572
|
device_count: Array.isArray(devices) ? devices.length : 0
|
|
540
|
-
}
|
|
573
|
+
},
|
|
574
|
+
auto_update: resolveAutoUpdateStatus(context)
|
|
575
|
+
});
|
|
576
|
+
});
|
|
577
|
+
|
|
578
|
+
router.get('/update/status', (req, res) => {
|
|
579
|
+
return res.json({
|
|
580
|
+
success: true,
|
|
581
|
+
auto_update: resolveAutoUpdateStatus(context)
|
|
582
|
+
});
|
|
583
|
+
});
|
|
584
|
+
|
|
585
|
+
router.post('/update/check', async (req, res) => {
|
|
586
|
+
const manager = context.getUpdateManager ? context.getUpdateManager() : null;
|
|
587
|
+
if (!manager || typeof manager.triggerCheck !== 'function') {
|
|
588
|
+
return res.status(503).json({
|
|
589
|
+
success: false,
|
|
590
|
+
error: 'updater_unavailable',
|
|
591
|
+
message: 'Auto-updater is not initialized for this server.'
|
|
592
|
+
});
|
|
593
|
+
}
|
|
594
|
+
await manager.triggerCheck({ reason: 'dashboard_manual_check', forceCheck: true });
|
|
595
|
+
return res.json({
|
|
596
|
+
success: true,
|
|
597
|
+
auto_update: resolveAutoUpdateStatus(context)
|
|
598
|
+
});
|
|
599
|
+
});
|
|
600
|
+
|
|
601
|
+
router.post('/update/now', async (req, res) => {
|
|
602
|
+
const manager = context.getUpdateManager ? context.getUpdateManager() : null;
|
|
603
|
+
if (!manager || typeof manager.triggerUpdate !== 'function') {
|
|
604
|
+
return res.status(503).json({
|
|
605
|
+
success: false,
|
|
606
|
+
error: 'updater_unavailable',
|
|
607
|
+
message: 'Auto-updater is not initialized for this server.'
|
|
608
|
+
});
|
|
609
|
+
}
|
|
610
|
+
const force = parseBoolean(req.body && (req.body.force !== undefined ? req.body.force : req.body.force_update));
|
|
611
|
+
await manager.triggerUpdate({
|
|
612
|
+
reason: 'dashboard_manual_update',
|
|
613
|
+
force
|
|
614
|
+
});
|
|
615
|
+
return res.json({
|
|
616
|
+
success: true,
|
|
617
|
+
auto_update: resolveAutoUpdateStatus(context)
|
|
618
|
+
});
|
|
619
|
+
});
|
|
620
|
+
|
|
621
|
+
router.put('/update/config', async (req, res) => {
|
|
622
|
+
const manager = context.getUpdateManager ? context.getUpdateManager() : null;
|
|
623
|
+
const body = req.body || {};
|
|
624
|
+
const enabled = body.enabled !== undefined ? parseBoolean(body.enabled) : undefined;
|
|
625
|
+
|
|
626
|
+
if (enabled === undefined) {
|
|
627
|
+
return res.status(400).json({
|
|
628
|
+
success: false,
|
|
629
|
+
error: 'enabled_required',
|
|
630
|
+
message: 'Pass { enabled: true|false }.'
|
|
631
|
+
});
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
if (manager && typeof manager.setEnabled === 'function') {
|
|
635
|
+
await manager.setEnabled(enabled);
|
|
636
|
+
} else if (context.config && typeof context.config.setAutoUpdate === 'function') {
|
|
637
|
+
context.config.setAutoUpdate({ enabled });
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
return res.json({
|
|
641
|
+
success: true,
|
|
642
|
+
auto_update: resolveAutoUpdateStatus(context)
|
|
541
643
|
});
|
|
542
644
|
});
|
|
543
645
|
|