@axon-assistant/core 2026.8.25 → 2026.8.26
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 +1 -0
- package/dist/Daemon.d.ts +13 -0
- package/dist/Daemon.d.ts.map +1 -1
- package/dist/cli.js +148 -64
- package/dist/index.js +51 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -32,6 +32,7 @@ axon start --host 0.0.0.0 # ещё и для устройств из се
|
|
|
32
32
|
использования.
|
|
33
33
|
|
|
34
34
|
```bash
|
|
35
|
+
axon code # новый код подключения устройства
|
|
35
36
|
axon status # запущено ли и где
|
|
36
37
|
axon secret set provider.anthropic.apiKey sk-… # ключ провайдера
|
|
37
38
|
axon devices # подключённые устройства
|
package/dist/Daemon.d.ts
CHANGED
|
@@ -57,6 +57,11 @@ export declare class Daemon {
|
|
|
57
57
|
* ответ, и мог бы показать его только целиком в самом конце.
|
|
58
58
|
*/
|
|
59
59
|
private readonly signalListeners;
|
|
60
|
+
/**
|
|
61
|
+
* Пропуск для того, кто сидит за этой машиной: им подписан запрос на новый
|
|
62
|
+
* код подключения. Живёт в файле рядом с базой, с правами 0600.
|
|
63
|
+
*/
|
|
64
|
+
private readonly controlToken;
|
|
60
65
|
/** Разрешается, когда все установленные плагины отработали запуск. */
|
|
61
66
|
pluginsReady: Promise<void>;
|
|
62
67
|
constructor(options?: DaemonOptions);
|
|
@@ -116,6 +121,14 @@ export declare class Daemon {
|
|
|
116
121
|
* же машине мог его найти, не завися от того, кто его поднял, адрес и pid
|
|
117
122
|
* кладутся в файл рядом с базой.
|
|
118
123
|
*/
|
|
124
|
+
private controlPath;
|
|
125
|
+
/**
|
|
126
|
+
* Пропуск переписывается на каждом запуске: старый файл, оставшийся от
|
|
127
|
+
* убитого процесса, не должен открывать двери в новое ядро.
|
|
128
|
+
*/
|
|
129
|
+
private writeControlToken;
|
|
130
|
+
/** Сравнение постоянного времени: пропуск подбирают по одному символу. */
|
|
131
|
+
private controlAllowed;
|
|
119
132
|
private announce;
|
|
120
133
|
private withdraw;
|
|
121
134
|
stop(): Promise<void>;
|
package/dist/Daemon.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Daemon.d.ts","sourceRoot":"","sources":["../src/Daemon.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"Daemon.d.ts","sourceRoot":"","sources":["../src/Daemon.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAKtD,OAAO,EAML,OAAO,EAER,MAAM,gBAAgB,CAAC;AAKxB,eAAO,MAAM,cAAc,QAC4C,CAAC;AAIxE,MAAM,WAAW,aAAa;IAC5B,MAAM,CAAC,EAAE,OAAO,CAAC,UAAU,CAAC,CAAC;IAC7B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,0EAA0E;IAC1E,IAAI,CAAC,EAAE,UAAU,GAAG,YAAY,CAAC;CAClC;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb;;;;;;;OAOG;IACH,GAAG,EAAE,MAAM,CAAC;IACZ,0EAA0E;IAC1E,SAAS,EAAE,MAAM,EAAE,CAAC;CACrB;AAoCD;;;;;;;;;;GAUG;AACH,qBAAa,MAAM;IA8BL,OAAO,CAAC,QAAQ,CAAC,OAAO;IA7BpC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAwB;IACjD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAiB;IACzC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAgB;IAC5C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAc;IACrC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAkB;IACtC,OAAO,CAAC,OAAO,CAA8B;IAC7C,OAAO,CAAC,QAAQ,CAAgC;IAChD,gFAAgF;IAChF,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,uEAAuE;IACvE,OAAO,CAAC,IAAI,CAA4B;IACxC;;;;;;;OAOG;IACH,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAuC;IACvE;;;OAGG;IACH,OAAO,CAAC,QAAQ,CAAC,YAAY,CAA0C;IACvE,sEAAsE;IACtE,YAAY,EAAE,OAAO,CAAC,IAAI,CAAC,CAAqB;gBAEnB,OAAO,GAAE,aAAkB;IAsClD,KAAK,IAAI,OAAO,CAAC;QAAE,OAAO,EAAE,aAAa,CAAC;QAAC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;IAqDhF;;;;;;;OAOG;YACW,YAAY;IAmC1B;;;;;OAKG;YACW,WAAW;IAkBzB;;;;;OAKG;IACG,aAAa,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,GAAG,UAAU,GAAG,QAAQ,EAAE,KAAK,EAAE,MAAM;;;;;;;;;;;;;;;;;IAoC3E,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC;IAKrC,cAAc;;;;IAOd;;;;;;;OAOG;IACH,OAAO,CAAC,WAAW;IAInB;;;OAGG;IACH,OAAO,CAAC,iBAAiB;IAKzB,0EAA0E;IAC1E,OAAO,CAAC,cAAc;IAQtB,OAAO,CAAC,QAAQ;IAmBhB,OAAO,CAAC,QAAQ;IAIV,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAoB3B,IAAI,GAAG,IAAI,MAAM,CAGhB;IAED,4EAA4E;IAC5E,OAAO,CAAC,WAAW;YAWL,SAAS;IAgGvB,OAAO,CAAC,QAAQ;IAiBhB,OAAO,CAAC,SAAS;CA+BlB"}
|
package/dist/cli.js
CHANGED
|
@@ -3424,8 +3424,8 @@ function estimateTokens(text) {
|
|
|
3424
3424
|
return 0;
|
|
3425
3425
|
let cyrillic = 0;
|
|
3426
3426
|
for (const char of text) {
|
|
3427
|
-
const
|
|
3428
|
-
if (
|
|
3427
|
+
const code3 = char.codePointAt(0);
|
|
3428
|
+
if (code3 >= 1024 && code3 <= 1279)
|
|
3429
3429
|
cyrillic++;
|
|
3430
3430
|
}
|
|
3431
3431
|
const other = text.length - cyrillic;
|
|
@@ -5161,9 +5161,9 @@ var HOST_READY = "host.ready";
|
|
|
5161
5161
|
// ../core/dist/plugins/rpc.js
|
|
5162
5162
|
var RpcError = class extends Error {
|
|
5163
5163
|
code;
|
|
5164
|
-
constructor(message,
|
|
5164
|
+
constructor(message, code3 = "plugin_error") {
|
|
5165
5165
|
super(message);
|
|
5166
|
-
this.code =
|
|
5166
|
+
this.code = code3;
|
|
5167
5167
|
this.name = "RpcError";
|
|
5168
5168
|
}
|
|
5169
5169
|
};
|
|
@@ -5545,9 +5545,9 @@ var CLIENT_INFO = { name: "axon", version: "1" };
|
|
|
5545
5545
|
var DEFAULT_TIMEOUT_MS = 6e4;
|
|
5546
5546
|
var McpError = class extends Error {
|
|
5547
5547
|
code;
|
|
5548
|
-
constructor(message,
|
|
5548
|
+
constructor(message, code3 = "mcp_error") {
|
|
5549
5549
|
super(message);
|
|
5550
|
-
this.code =
|
|
5550
|
+
this.code = code3;
|
|
5551
5551
|
this.name = "McpError";
|
|
5552
5552
|
}
|
|
5553
5553
|
};
|
|
@@ -5659,10 +5659,10 @@ var McpClient = class {
|
|
|
5659
5659
|
}
|
|
5660
5660
|
});
|
|
5661
5661
|
child.on("error", (error) => this.fail(new McpError(error.message, "spawn_failed")));
|
|
5662
|
-
child.on("exit", (
|
|
5662
|
+
child.on("exit", (code3, signal) => {
|
|
5663
5663
|
if (this.closed)
|
|
5664
5664
|
return;
|
|
5665
|
-
this.fail(new McpError(`\u0421\u0435\u0440\u0432\u0435\u0440 \u0437\u0430\u0432\u0435\u0440\u0448\u0438\u043B\u0441\u044F (${signal ??
|
|
5665
|
+
this.fail(new McpError(`\u0421\u0435\u0440\u0432\u0435\u0440 \u0437\u0430\u0432\u0435\u0440\u0448\u0438\u043B\u0441\u044F (${signal ?? code3})`, "exited"));
|
|
5666
5666
|
});
|
|
5667
5667
|
}
|
|
5668
5668
|
/** Разобрать поток stdout на строки: одна строка — одно сообщение JSON-RPC. */
|
|
@@ -6009,19 +6009,19 @@ var PluginProcess = class {
|
|
|
6009
6009
|
clearTimeout(timer);
|
|
6010
6010
|
reject(error);
|
|
6011
6011
|
});
|
|
6012
|
-
child.once("exit", (
|
|
6012
|
+
child.once("exit", (code3, signal) => {
|
|
6013
6013
|
clearTimeout(timer);
|
|
6014
|
-
reject(new Error(`\u041F\u0440\u043E\u0446\u0435\u0441\u0441 \u0437\u0430\u0432\u0435\u0440\u0448\u0438\u043B\u0441\u044F \u0434\u043E \u0433\u043E\u0442\u043E\u0432\u043D\u043E\u0441\u0442\u0438 (${signal ??
|
|
6014
|
+
reject(new Error(`\u041F\u0440\u043E\u0446\u0435\u0441\u0441 \u0437\u0430\u0432\u0435\u0440\u0448\u0438\u043B\u0441\u044F \u0434\u043E \u0433\u043E\u0442\u043E\u0432\u043D\u043E\u0441\u0442\u0438 (${signal ?? code3})`));
|
|
6015
6015
|
});
|
|
6016
6016
|
});
|
|
6017
|
-
child.once("exit", (
|
|
6017
|
+
child.once("exit", (code3, signal) => {
|
|
6018
6018
|
this.child = null;
|
|
6019
6019
|
this.ready = false;
|
|
6020
6020
|
this.peer.dispose(new RpcError("\u041F\u0440\u043E\u0446\u0435\u0441\u0441 \u043F\u043B\u0430\u0433\u0438\u043D\u0430 \u0437\u0430\u0432\u0435\u0440\u0448\u0451\u043D", "not_running"));
|
|
6021
6021
|
if (this.stopping)
|
|
6022
6022
|
return;
|
|
6023
|
-
const reason = signal ? `\u0443\u0431\u0438\u0442 \u0441\u0438\u0433\u043D\u0430\u043B\u043E\u043C ${signal}` : `\u0437\u0430\u0432\u0435\u0440\u0448\u0438\u043B\u0441\u044F \u0441 \u043A\u043E\u0434\u043E\u043C ${
|
|
6024
|
-
this.log.warn({ code:
|
|
6023
|
+
const reason = signal ? `\u0443\u0431\u0438\u0442 \u0441\u0438\u0433\u043D\u0430\u043B\u043E\u043C ${signal}` : `\u0437\u0430\u0432\u0435\u0440\u0448\u0438\u043B\u0441\u044F \u0441 \u043A\u043E\u0434\u043E\u043C ${code3}`;
|
|
6024
|
+
this.log.warn({ code: code3, signal }, "\u043F\u043B\u0430\u0433\u0438\u043D \u0443\u043F\u0430\u043B");
|
|
6025
6025
|
this.options.onExit(reason);
|
|
6026
6026
|
});
|
|
6027
6027
|
await started;
|
|
@@ -6723,9 +6723,9 @@ var LoadedPlugin = class {
|
|
|
6723
6723
|
// ../core/dist/plugins/PluginHost.js
|
|
6724
6724
|
var PluginError = class extends Error {
|
|
6725
6725
|
code;
|
|
6726
|
-
constructor(message,
|
|
6726
|
+
constructor(message, code3 = "plugin_error") {
|
|
6727
6727
|
super(message);
|
|
6728
|
-
this.code =
|
|
6728
|
+
this.code = code3;
|
|
6729
6729
|
this.name = "PluginError";
|
|
6730
6730
|
}
|
|
6731
6731
|
};
|
|
@@ -7677,9 +7677,9 @@ function dayAt(from, offsetDays, time) {
|
|
|
7677
7677
|
var TICK_MS2 = 3e4;
|
|
7678
7678
|
var RoutineError = class extends Error {
|
|
7679
7679
|
code;
|
|
7680
|
-
constructor(message,
|
|
7680
|
+
constructor(message, code3 = "routine_error") {
|
|
7681
7681
|
super(message);
|
|
7682
|
-
this.code =
|
|
7682
|
+
this.code = code3;
|
|
7683
7683
|
this.name = "RoutineError";
|
|
7684
7684
|
}
|
|
7685
7685
|
};
|
|
@@ -9571,7 +9571,7 @@ function runCommand(input) {
|
|
|
9571
9571
|
input.signal.removeEventListener("abort", onAbort);
|
|
9572
9572
|
reject(e);
|
|
9573
9573
|
});
|
|
9574
|
-
child.on("close", (
|
|
9574
|
+
child.on("close", (code3) => {
|
|
9575
9575
|
if (settled)
|
|
9576
9576
|
return;
|
|
9577
9577
|
settled = true;
|
|
@@ -9583,7 +9583,7 @@ function runCommand(input) {
|
|
|
9583
9583
|
resolve({
|
|
9584
9584
|
stdout: decodeOutput(Buffer.concat(stdout)).trimEnd(),
|
|
9585
9585
|
stderr: decodeOutput(Buffer.concat(stderr)).trimEnd(),
|
|
9586
|
-
code:
|
|
9586
|
+
code: code3
|
|
9587
9587
|
});
|
|
9588
9588
|
});
|
|
9589
9589
|
});
|
|
@@ -10100,6 +10100,7 @@ axon plugin logs ${id}
|
|
|
10100
10100
|
}
|
|
10101
10101
|
|
|
10102
10102
|
// src/Daemon.ts
|
|
10103
|
+
import crypto4 from "node:crypto";
|
|
10103
10104
|
import http from "node:http";
|
|
10104
10105
|
import fs18 from "node:fs";
|
|
10105
10106
|
import os7 from "node:os";
|
|
@@ -10128,11 +10129,11 @@ var PairingService = class {
|
|
|
10128
10129
|
fs17.rmSync(this.bootstrapPath, { force: true });
|
|
10129
10130
|
return null;
|
|
10130
10131
|
}
|
|
10131
|
-
const
|
|
10132
|
+
const code3 = generateCode();
|
|
10132
10133
|
fs17.mkdirSync(path17.dirname(this.bootstrapPath), { recursive: true });
|
|
10133
|
-
fs17.writeFileSync(this.bootstrapPath, `${
|
|
10134
|
+
fs17.writeFileSync(this.bootstrapPath, `${code3}
|
|
10134
10135
|
`, { mode: 384 });
|
|
10135
|
-
this.pending.set(
|
|
10136
|
+
this.pending.set(code3, {
|
|
10136
10137
|
name: "\u041F\u0435\u0440\u0432\u043E\u0435 \u0443\u0441\u0442\u0440\u043E\u0439\u0441\u0442\u0432\u043E",
|
|
10137
10138
|
platform: "desktop",
|
|
10138
10139
|
// Первому устройству — всё: с него настраивают остальные.
|
|
@@ -10147,24 +10148,24 @@ var PairingService = class {
|
|
|
10147
10148
|
],
|
|
10148
10149
|
expiresAt: Number.POSITIVE_INFINITY
|
|
10149
10150
|
});
|
|
10150
|
-
return
|
|
10151
|
+
return code3;
|
|
10151
10152
|
}
|
|
10152
10153
|
begin(input) {
|
|
10153
|
-
const
|
|
10154
|
-
this.pending.set(
|
|
10154
|
+
const code3 = generateCode();
|
|
10155
|
+
this.pending.set(code3, {
|
|
10155
10156
|
name: input.name,
|
|
10156
10157
|
platform: input.platform,
|
|
10157
10158
|
scopes: input.scopes,
|
|
10158
10159
|
expiresAt: Date.now() + input.ttlSeconds * 1e3
|
|
10159
10160
|
});
|
|
10160
|
-
return { code:
|
|
10161
|
+
return { code: code3, expiresInSeconds: input.ttlSeconds };
|
|
10161
10162
|
}
|
|
10162
10163
|
/** Обменять код на токен. Код одноразовый. */
|
|
10163
|
-
complete(
|
|
10164
|
+
complete(code3, deviceName) {
|
|
10164
10165
|
this.sweep();
|
|
10165
|
-
const pending = this.pending.get(
|
|
10166
|
+
const pending = this.pending.get(code3);
|
|
10166
10167
|
if (!pending) return null;
|
|
10167
|
-
this.pending.delete(
|
|
10168
|
+
this.pending.delete(code3);
|
|
10168
10169
|
const token = generateToken();
|
|
10169
10170
|
const device = this.runtime.store.pairDevice({
|
|
10170
10171
|
name: deviceName || pending.name,
|
|
@@ -10177,20 +10178,20 @@ var PairingService = class {
|
|
|
10177
10178
|
}
|
|
10178
10179
|
sweep() {
|
|
10179
10180
|
const now = Date.now();
|
|
10180
|
-
for (const [
|
|
10181
|
-
if (pending.expiresAt < now) this.pending.delete(
|
|
10181
|
+
for (const [code3, pending] of this.pending) {
|
|
10182
|
+
if (pending.expiresAt < now) this.pending.delete(code3);
|
|
10182
10183
|
}
|
|
10183
10184
|
}
|
|
10184
10185
|
};
|
|
10185
10186
|
function generateCode() {
|
|
10186
10187
|
const alphabet = "ACDEFGHJKLMNPQRTUVWXY34679";
|
|
10187
10188
|
const bytes = crypto3.randomBytes(8);
|
|
10188
|
-
let
|
|
10189
|
+
let code3 = "";
|
|
10189
10190
|
for (let i = 0; i < 8; i++) {
|
|
10190
|
-
if (i === 4)
|
|
10191
|
-
|
|
10191
|
+
if (i === 4) code3 += "-";
|
|
10192
|
+
code3 += alphabet[bytes[i] % alphabet.length];
|
|
10192
10193
|
}
|
|
10193
|
-
return
|
|
10194
|
+
return code3;
|
|
10194
10195
|
}
|
|
10195
10196
|
function authenticate(runtime, token) {
|
|
10196
10197
|
if (!token) return null;
|
|
@@ -10247,9 +10248,9 @@ var POLL_TIMEOUT_SECONDS = 25;
|
|
|
10247
10248
|
var TelegramError = class extends Error {
|
|
10248
10249
|
code;
|
|
10249
10250
|
retryAfter;
|
|
10250
|
-
constructor(message,
|
|
10251
|
+
constructor(message, code3, retryAfter2) {
|
|
10251
10252
|
super(message);
|
|
10252
|
-
this.code =
|
|
10253
|
+
this.code = code3;
|
|
10253
10254
|
this.retryAfter = retryAfter2;
|
|
10254
10255
|
this.name = "TelegramError";
|
|
10255
10256
|
}
|
|
@@ -10384,8 +10385,8 @@ function toTelegramHtml(markdown) {
|
|
|
10384
10385
|
return out.join("\n").replace(/\n{3,}/g, "\n\n").trim();
|
|
10385
10386
|
}
|
|
10386
10387
|
function closeFence(body, language) {
|
|
10387
|
-
const
|
|
10388
|
-
return language ? `<pre><code class="language-${escape(language)}">${
|
|
10388
|
+
const code3 = escape(body.join("\n"));
|
|
10389
|
+
return language ? `<pre><code class="language-${escape(language)}">${code3}</code></pre>` : `<pre>${code3}</pre>`;
|
|
10389
10390
|
}
|
|
10390
10391
|
function block(line) {
|
|
10391
10392
|
const heading = /^\s{0,3}(#{1,6})\s+(.*)$/.exec(line);
|
|
@@ -10405,10 +10406,10 @@ function block(line) {
|
|
|
10405
10406
|
return inline(line);
|
|
10406
10407
|
}
|
|
10407
10408
|
function inline(text) {
|
|
10408
|
-
const
|
|
10409
|
+
const code3 = [];
|
|
10409
10410
|
let work = text.replace(/`([^`]+)`/g, (_, body) => {
|
|
10410
|
-
|
|
10411
|
-
return `\0${
|
|
10411
|
+
code3.push(`<code>${escape(body)}</code>`);
|
|
10412
|
+
return `\0${code3.length - 1}\0`;
|
|
10412
10413
|
});
|
|
10413
10414
|
work = escape(work);
|
|
10414
10415
|
work = work.replace(/\[([^\]]+)\]\(([^)\s]+)\)/g, (_, label, href) => {
|
|
@@ -10419,7 +10420,7 @@ function inline(text) {
|
|
|
10419
10420
|
work = work.replace(/(^|[\s(])\*([^*\n]+)\*/g, "$1<i>$2</i>");
|
|
10420
10421
|
work = work.replace(/(^|[\s(])_([^_\n]+)_/g, "$1<i>$2</i>");
|
|
10421
10422
|
work = work.replace(/~~([^~]+)~~/g, "<s>$1</s>");
|
|
10422
|
-
return work.replace(/\u0000(\d+)\u0000/g, (_, index) =>
|
|
10423
|
+
return work.replace(/\u0000(\d+)\u0000/g, (_, index) => code3[Number(index)] ?? "");
|
|
10423
10424
|
}
|
|
10424
10425
|
function escape(text) {
|
|
10425
10426
|
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
@@ -10558,17 +10559,17 @@ var TelegramAdapter = class {
|
|
|
10558
10559
|
}
|
|
10559
10560
|
/** Привязка: `/start КОД` меняет код на устройство, как это делает десктоп. */
|
|
10560
10561
|
async onStart(message, text) {
|
|
10561
|
-
const
|
|
10562
|
+
const code3 = text.slice("/start".length).trim();
|
|
10562
10563
|
if (this.bindingOf(message.chat.id)) {
|
|
10563
10564
|
await this.api.sendMessage(message.chat.id, "\u0423\u0436\u0435 \u043D\u0430 \u0441\u0432\u044F\u0437\u0438. \u041F\u0438\u0448\u0438\u0442\u0435.");
|
|
10564
10565
|
return;
|
|
10565
10566
|
}
|
|
10566
|
-
if (!
|
|
10567
|
+
if (!code3) {
|
|
10567
10568
|
await this.api.sendMessage(message.chat.id, "\u041D\u0443\u0436\u0435\u043D \u043A\u043E\u0434 \u043F\u043E\u0434\u043A\u043B\u044E\u0447\u0435\u043D\u0438\u044F.\n\n\u041D\u0430\u0441\u0442\u0440\u043E\u0439\u043A\u0438 Axon \u2192 \xAB\u0423\u0441\u0442\u0440\u043E\u0439\u0441\u0442\u0432\u0430\xBB \u2192 \u0441\u043E\u0437\u0434\u0430\u0442\u044C \u043A\u043E\u0434, \u043F\u043E\u0442\u043E\u043C \u0441\u044E\u0434\u0430: <code>/start \u041A\u041E\u0414</code>", { parseMode: "HTML" });
|
|
10568
10569
|
return;
|
|
10569
10570
|
}
|
|
10570
10571
|
const name = [message.from?.first_name, message.from?.last_name].filter(Boolean).join(" ");
|
|
10571
|
-
const paired = this.deps.pair(
|
|
10572
|
+
const paired = this.deps.pair(code3.toUpperCase(), `\u0422\u0435\u043B\u0435\u0433\u0440\u0430\u043C: ${name || message.chat.id}`);
|
|
10572
10573
|
if (!paired) {
|
|
10573
10574
|
await this.api.sendMessage(message.chat.id, "\u041A\u043E\u0434 \u043D\u0435 \u043F\u043E\u0434\u043E\u0448\u0451\u043B \u2014 \u0438\u0441\u0442\u0451\u043A \u0438\u043B\u0438 \u0443\u0436\u0435 \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u043D.");
|
|
10574
10575
|
return;
|
|
@@ -11144,13 +11145,13 @@ var UserbotAuth = class {
|
|
|
11144
11145
|
* продолжение, и обращаться с ней как с отказом значит терять начатый вход
|
|
11145
11146
|
* там, где он идёт нормально.
|
|
11146
11147
|
*/
|
|
11147
|
-
async signIn(
|
|
11148
|
+
async signIn(code3) {
|
|
11148
11149
|
const pending = this.require();
|
|
11149
11150
|
try {
|
|
11150
11151
|
await pending.client.invoke(new Api2.auth.SignIn({
|
|
11151
11152
|
phoneNumber: pending.phone,
|
|
11152
11153
|
phoneCodeHash: pending.phoneCodeHash,
|
|
11153
|
-
phoneCode:
|
|
11154
|
+
phoneCode: code3.trim()
|
|
11154
11155
|
}));
|
|
11155
11156
|
} catch (error) {
|
|
11156
11157
|
if (!isPasswordNeeded(error))
|
|
@@ -11199,9 +11200,9 @@ function isPasswordNeeded(error) {
|
|
|
11199
11200
|
|
|
11200
11201
|
// src/commands.ts
|
|
11201
11202
|
var CommandError = class extends Error {
|
|
11202
|
-
constructor(
|
|
11203
|
+
constructor(code3, message) {
|
|
11203
11204
|
super(message);
|
|
11204
|
-
this.code =
|
|
11205
|
+
this.code = code3;
|
|
11205
11206
|
this.name = "CommandError";
|
|
11206
11207
|
}
|
|
11207
11208
|
};
|
|
@@ -11433,8 +11434,8 @@ var handlers = {
|
|
|
11433
11434
|
try {
|
|
11434
11435
|
return { plugin: await runtime.plugins.update(req.id) };
|
|
11435
11436
|
} catch (error) {
|
|
11436
|
-
const
|
|
11437
|
-
throw new CommandError(
|
|
11437
|
+
const code3 = error.code;
|
|
11438
|
+
throw new CommandError(code3 === "not_found" ? "not_found" : "bad_request", error.message);
|
|
11438
11439
|
}
|
|
11439
11440
|
},
|
|
11440
11441
|
"plugin.configure": async (req, { runtime }) => {
|
|
@@ -11555,8 +11556,8 @@ async function withPlugin(run2) {
|
|
|
11555
11556
|
try {
|
|
11556
11557
|
await run2();
|
|
11557
11558
|
} catch (error) {
|
|
11558
|
-
const
|
|
11559
|
-
throw new CommandError(
|
|
11559
|
+
const code3 = error.code;
|
|
11560
|
+
throw new CommandError(code3 === "not_found" ? "not_found" : "bad_request", error.message);
|
|
11560
11561
|
}
|
|
11561
11562
|
}
|
|
11562
11563
|
function requireConversation(runtime, id) {
|
|
@@ -11689,7 +11690,7 @@ var WsSession = class {
|
|
|
11689
11690
|
};
|
|
11690
11691
|
|
|
11691
11692
|
// src/Daemon.ts
|
|
11692
|
-
var DAEMON_VERSION = true ? "2026.8.
|
|
11693
|
+
var DAEMON_VERSION = true ? "2026.8.26" : "0.0.0-dev";
|
|
11693
11694
|
var MAX_UPLOAD_BYTES = 64 * 1024 * 1024;
|
|
11694
11695
|
function isWildcard(host) {
|
|
11695
11696
|
return host === "0.0.0.0" || host === "::" || host === "";
|
|
@@ -11764,6 +11765,11 @@ var Daemon = class {
|
|
|
11764
11765
|
* ответ, и мог бы показать его только целиком в самом конце.
|
|
11765
11766
|
*/
|
|
11766
11767
|
signalListeners = /* @__PURE__ */ new Set();
|
|
11768
|
+
/**
|
|
11769
|
+
* Пропуск для того, кто сидит за этой машиной: им подписан запрос на новый
|
|
11770
|
+
* код подключения. Живёт в файле рядом с базой, с правами 0600.
|
|
11771
|
+
*/
|
|
11772
|
+
controlToken = crypto4.randomBytes(24).toString("hex");
|
|
11767
11773
|
/** Разрешается, когда все установленные плагины отработали запуск. */
|
|
11768
11774
|
pluginsReady = Promise.resolve();
|
|
11769
11775
|
async start() {
|
|
@@ -11785,6 +11791,7 @@ var Daemon = class {
|
|
|
11785
11791
|
reachable: isWildcard(host) ? reachableUrls(boundPort) : []
|
|
11786
11792
|
};
|
|
11787
11793
|
const bootstrapCode = this.pairing.ensureBootstrapCode();
|
|
11794
|
+
this.writeControlToken();
|
|
11788
11795
|
this.announce();
|
|
11789
11796
|
logger.info({ url: this.address.url, coreId: this.runtime.coreId }, "\u0434\u0435\u043C\u043E\u043D \u0441\u043B\u0443\u0448\u0430\u0435\u0442");
|
|
11790
11797
|
this.pluginsReady = this.runtime.startPlugins().catch((error) => {
|
|
@@ -11818,7 +11825,7 @@ var Daemon = class {
|
|
|
11818
11825
|
const adapter = new TelegramAdapter(
|
|
11819
11826
|
{
|
|
11820
11827
|
runtime: this.runtime,
|
|
11821
|
-
pair: (
|
|
11828
|
+
pair: (code3, name) => this.pairing.complete(code3, name),
|
|
11822
11829
|
resolvePermission: (requestId, allow) => void this.permissions.resolve(requestId, allow ? "allow_once" : "deny_once"),
|
|
11823
11830
|
onSignal: (listener) => {
|
|
11824
11831
|
this.signalListeners.add(listener);
|
|
@@ -11903,6 +11910,26 @@ var Daemon = class {
|
|
|
11903
11910
|
* же машине мог его найти, не завися от того, кто его поднял, адрес и pid
|
|
11904
11911
|
* кладутся в файл рядом с базой.
|
|
11905
11912
|
*/
|
|
11913
|
+
controlPath() {
|
|
11914
|
+
return path18.join(this.runtime.config.dataDir, "control.token");
|
|
11915
|
+
}
|
|
11916
|
+
/**
|
|
11917
|
+
* Пропуск переписывается на каждом запуске: старый файл, оставшийся от
|
|
11918
|
+
* убитого процесса, не должен открывать двери в новое ядро.
|
|
11919
|
+
*/
|
|
11920
|
+
writeControlToken() {
|
|
11921
|
+
fs18.mkdirSync(this.runtime.config.dataDir, { recursive: true });
|
|
11922
|
+
fs18.writeFileSync(this.controlPath(), `${this.controlToken}
|
|
11923
|
+
`, { mode: 384 });
|
|
11924
|
+
}
|
|
11925
|
+
/** Сравнение постоянного времени: пропуск подбирают по одному символу. */
|
|
11926
|
+
controlAllowed(given) {
|
|
11927
|
+
const value = Array.isArray(given) ? given[0] : given;
|
|
11928
|
+
if (!value) return false;
|
|
11929
|
+
const a = Buffer.from(value.trim());
|
|
11930
|
+
const b = Buffer.from(this.controlToken);
|
|
11931
|
+
return a.length === b.length && crypto4.timingSafeEqual(a, b);
|
|
11932
|
+
}
|
|
11906
11933
|
announce() {
|
|
11907
11934
|
const file = path18.join(this.runtime.config.dataDir, "core.json");
|
|
11908
11935
|
fs18.writeFileSync(
|
|
@@ -11962,11 +11989,34 @@ var Daemon = class {
|
|
|
11962
11989
|
pendingPermissions: this.permissions.pendingCount
|
|
11963
11990
|
});
|
|
11964
11991
|
}
|
|
11992
|
+
if (req.method === "POST" && url.pathname === "/v1/control/pair") {
|
|
11993
|
+
if (!this.controlAllowed(req.headers["x-axon-control"])) {
|
|
11994
|
+
return respond(res, 403, { error: "forbidden" });
|
|
11995
|
+
}
|
|
11996
|
+
const body = await readJson(req);
|
|
11997
|
+
const chatOnly = body?.["scopes"] === "chat";
|
|
11998
|
+
const ttl = Number(body?.["ttlSeconds"] ?? 300);
|
|
11999
|
+
const issued = this.pairing.begin({
|
|
12000
|
+
name: typeof body?.["name"] === "string" ? body["name"] : "\u041D\u043E\u0432\u043E\u0435 \u0443\u0441\u0442\u0440\u043E\u0439\u0441\u0442\u0432\u043E",
|
|
12001
|
+
platform: "desktop",
|
|
12002
|
+
scopes: chatOnly ? ["chat.read", "chat.write", "tools.safe"] : [
|
|
12003
|
+
"chat.read",
|
|
12004
|
+
"chat.write",
|
|
12005
|
+
"tools.safe",
|
|
12006
|
+
"tools.sensitive",
|
|
12007
|
+
"tools.dangerous",
|
|
12008
|
+
"settings.write",
|
|
12009
|
+
"devices.manage"
|
|
12010
|
+
],
|
|
12011
|
+
ttlSeconds: Number.isFinite(ttl) && ttl > 0 ? Math.min(ttl, 3600) : 300
|
|
12012
|
+
});
|
|
12013
|
+
return respond(res, 200, issued);
|
|
12014
|
+
}
|
|
11965
12015
|
if (req.method === "POST" && url.pathname === "/v1/pair") {
|
|
11966
12016
|
const body = await readJson(req);
|
|
11967
|
-
const
|
|
12017
|
+
const code3 = typeof body?.["code"] === "string" ? body["code"] : "";
|
|
11968
12018
|
const name = typeof body?.["name"] === "string" ? body["name"] : void 0;
|
|
11969
|
-
const paired = this.pairing.complete(
|
|
12019
|
+
const paired = this.pairing.complete(code3.trim().toUpperCase(), name);
|
|
11970
12020
|
if (!paired) return respond(res, 403, { error: "invalid_code" });
|
|
11971
12021
|
return respond(res, 200, {
|
|
11972
12022
|
token: paired.token,
|
|
@@ -12093,6 +12143,8 @@ async function main() {
|
|
|
12093
12143
|
return await restore(args);
|
|
12094
12144
|
case "status":
|
|
12095
12145
|
return await status();
|
|
12146
|
+
case "code":
|
|
12147
|
+
return await code2(args);
|
|
12096
12148
|
case "stop":
|
|
12097
12149
|
return stop();
|
|
12098
12150
|
case "help":
|
|
@@ -12129,11 +12181,11 @@ async function start(args) {
|
|
|
12129
12181
|
try {
|
|
12130
12182
|
started = await daemon.start();
|
|
12131
12183
|
} catch (error) {
|
|
12132
|
-
const
|
|
12133
|
-
if (
|
|
12184
|
+
const code3 = error.code;
|
|
12185
|
+
if (code3 === "EADDRINUSE") {
|
|
12134
12186
|
return fail(`\u041F\u043E\u0440\u0442 ${port} \u0437\u0430\u043D\u044F\u0442. \u041C\u043E\u0436\u0435\u0442 \u0431\u044B\u0442\u044C, \u044F\u0434\u0440\u043E \u0443\u0436\u0435 \u0440\u0430\u0431\u043E\u0442\u0430\u0435\u0442: axon status`);
|
|
12135
12187
|
}
|
|
12136
|
-
if (
|
|
12188
|
+
if (code3 === "EADDRNOTAVAIL") {
|
|
12137
12189
|
return fail(
|
|
12138
12190
|
`\u0410\u0434\u0440\u0435\u0441 ${host} \u043D\u0435 \u043F\u0440\u0438\u043D\u0430\u0434\u043B\u0435\u0436\u0438\u0442 \u044D\u0442\u043E\u0439 \u043C\u0430\u0448\u0438\u043D\u0435.
|
|
12139
12191
|
\u0427\u0442\u043E\u0431\u044B \u043F\u0443\u0441\u043A\u0430\u0442\u044C \u0434\u0440\u0443\u0433\u0438\u0435 \u0443\u0441\u0442\u0440\u043E\u0439\u0441\u0442\u0432\u0430, \u0441\u043B\u0443\u0448\u0430\u044E\u0442 \u0432\u0441\u0435 \u0438\u043D\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u044B: axon start --host 0.0.0.0`
|
|
@@ -12164,6 +12216,37 @@ async function start(args) {
|
|
|
12164
12216
|
process.on("SIGINT", stop2);
|
|
12165
12217
|
process.on("SIGTERM", stop2);
|
|
12166
12218
|
}
|
|
12219
|
+
async function code2(args) {
|
|
12220
|
+
const record = readRecord();
|
|
12221
|
+
if (!record || !await alive(record.url)) {
|
|
12222
|
+
return fail("\u042F\u0434\u0440\u043E \u043D\u0435 \u0437\u0430\u043F\u0443\u0449\u0435\u043D\u043E \u2014 \u043A\u043E\u0434 \u0432\u044B\u0434\u0430\u0451\u0442 \u043E\u043D\u043E \u0441\u0430\u043C\u043E. \u0417\u0430\u043F\u0443\u0441\u0442\u0438\u0442\u0435: axon start");
|
|
12223
|
+
}
|
|
12224
|
+
const file = path19.join(resolveConfig().dataDir, "control.token");
|
|
12225
|
+
let token;
|
|
12226
|
+
try {
|
|
12227
|
+
token = fs19.readFileSync(file, "utf8").trim();
|
|
12228
|
+
} catch {
|
|
12229
|
+
return fail(`\u041D\u0435\u0442 \u0434\u043E\u0441\u0442\u0443\u043F\u0430 \u043A ${file} \u2014 \u043A\u043E\u0434 \u043C\u043E\u0436\u0435\u0442 \u0432\u044B\u0434\u0430\u0442\u044C \u0442\u043E\u043B\u044C\u043A\u043E \u0445\u043E\u0437\u044F\u0438\u043D \u044F\u0434\u0440\u0430`);
|
|
12230
|
+
}
|
|
12231
|
+
const chatOnly = args.includes("--chat");
|
|
12232
|
+
const ttl = Number(valueOf(args, "--ttl") ?? 300);
|
|
12233
|
+
const response = await fetch(`${record.url}/v1/control/pair`, {
|
|
12234
|
+
method: "POST",
|
|
12235
|
+
headers: { "Content-Type": "application/json", "x-axon-control": token },
|
|
12236
|
+
body: JSON.stringify({ scopes: chatOnly ? "chat" : "full", ttlSeconds: ttl })
|
|
12237
|
+
}).catch(() => null);
|
|
12238
|
+
if (!response?.ok) {
|
|
12239
|
+
return fail(
|
|
12240
|
+
response?.status === 403 ? "\u042F\u0434\u0440\u043E \u043D\u0435 \u043F\u0440\u0438\u043D\u044F\u043B\u043E \u043F\u0440\u043E\u043F\u0443\u0441\u043A. \u041F\u043E\u0445\u043E\u0436\u0435, \u043E\u043D\u043E \u043F\u0435\u0440\u0435\u0437\u0430\u043F\u0443\u0441\u043A\u0430\u043B\u043E\u0441\u044C \u2014 \u043F\u043E\u043F\u0440\u043E\u0431\u0443\u0439\u0442\u0435 \u0435\u0449\u0451 \u0440\u0430\u0437" : "\u042F\u0434\u0440\u043E \u043D\u0435 \u043E\u0442\u0434\u0430\u043B\u043E \u043A\u043E\u0434"
|
|
12241
|
+
);
|
|
12242
|
+
}
|
|
12243
|
+
const issued = await response.json();
|
|
12244
|
+
console.log(issued.code);
|
|
12245
|
+
console.log("");
|
|
12246
|
+
console.log(` \u0434\u0435\u0439\u0441\u0442\u0432\u0443\u0435\u0442 ${Math.round(issued.expiresInSeconds / 60)} \u043C\u0438\u043D, \u043E\u0434\u043D\u043E\u0440\u0430\u0437\u043E\u0432\u044B\u0439`);
|
|
12247
|
+
console.log(` \u043F\u0440\u0430\u0432\u0430: ${chatOnly ? "\u0442\u043E\u043B\u044C\u043A\u043E \u043F\u0435\u0440\u0435\u043F\u0438\u0441\u043A\u0430" : "\u043F\u043E\u043B\u043D\u044B\u0435"}`);
|
|
12248
|
+
console.log(` \u0430\u0434\u0440\u0435\u0441 \u0434\u043B\u044F \u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u044F: ${(record.reachable ?? [])[0] ?? record.url}`);
|
|
12249
|
+
}
|
|
12167
12250
|
function logPath() {
|
|
12168
12251
|
return path19.join(resolveConfig().dataDir, "axon.log");
|
|
12169
12252
|
}
|
|
@@ -12190,11 +12273,11 @@ async function detach(args) {
|
|
|
12190
12273
|
console.log(` \u0432\u044B\u0432\u043E\u0434: ${file}`);
|
|
12191
12274
|
console.log(" \u043E\u0441\u0442\u0430\u043D\u043E\u0432\u0438\u0442\u044C: axon stop");
|
|
12192
12275
|
const codeFile = path19.join(config.dataDir, "bootstrap.code");
|
|
12193
|
-
const
|
|
12194
|
-
if (
|
|
12276
|
+
const code3 = fs19.existsSync(codeFile) ? fs19.readFileSync(codeFile, "utf8").trim() : "";
|
|
12277
|
+
if (code3) {
|
|
12195
12278
|
console.log("");
|
|
12196
12279
|
console.log(" \u0423\u0441\u0442\u0440\u043E\u0439\u0441\u0442\u0432 \u043F\u043E\u043A\u0430 \u043D\u0435\u0442. \u041A\u043E\u0434 \u0434\u043B\u044F \u043F\u0435\u0440\u0432\u043E\u0433\u043E \u043F\u043E\u0434\u043A\u043B\u044E\u0447\u0435\u043D\u0438\u044F:");
|
|
12197
|
-
console.log(` ${
|
|
12280
|
+
console.log(` ${code3}`);
|
|
12198
12281
|
console.log("");
|
|
12199
12282
|
}
|
|
12200
12283
|
}
|
|
@@ -12460,6 +12543,7 @@ function usage() {
|
|
|
12460
12543
|
--host 0.0.0.0 \u043E\u0442\u043A\u0440\u044B\u0442\u044C \u0434\u043B\u044F \u0434\u0440\u0443\u0433\u0438\u0445 \u0443\u0441\u0442\u0440\u043E\u0439\u0441\u0442\u0432
|
|
12461
12544
|
--foreground, -f \u043D\u0435 \u0443\u0445\u043E\u0434\u0438\u0442\u044C \u0432 \u0444\u043E\u043D (systemd, docker)
|
|
12462
12545
|
axon status \u0437\u0430\u043F\u0443\u0449\u0435\u043D\u043E \u043B\u0438 \u044F\u0434\u0440\u043E \u0438 \u0433\u0434\u0435
|
|
12546
|
+
axon code [--chat] [--ttl 300] \u043D\u043E\u0432\u044B\u0439 \u043A\u043E\u0434 \u043F\u043E\u0434\u043A\u043B\u044E\u0447\u0435\u043D\u0438\u044F \u0443\u0441\u0442\u0440\u043E\u0439\u0441\u0442\u0432\u0430
|
|
12463
12547
|
axon stop \u043E\u0441\u0442\u0430\u043D\u043E\u0432\u0438\u0442\u044C \u044F\u0434\u0440\u043E
|
|
12464
12548
|
axon secret list \u043A\u0430\u043A\u0438\u0435 \u0441\u0435\u043A\u0440\u0435\u0442\u044B \u0437\u0430\u0434\u0430\u043D\u044B
|
|
12465
12549
|
axon secret get <\u043A\u043B\u044E\u0447> \u043F\u043E\u043A\u0430\u0437\u0430\u0442\u044C \u0441\u0435\u043A\u0440\u0435\u0442 \u0446\u0435\u043B\u0438\u043A\u043E\u043C
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
// src/Daemon.ts
|
|
2
|
+
import crypto4 from "node:crypto";
|
|
2
3
|
import http from "node:http";
|
|
3
4
|
import fs16 from "node:fs";
|
|
4
5
|
import os7 from "node:os";
|
|
@@ -11352,7 +11353,7 @@ var WsSession = class {
|
|
|
11352
11353
|
};
|
|
11353
11354
|
|
|
11354
11355
|
// src/Daemon.ts
|
|
11355
|
-
var DAEMON_VERSION = true ? "2026.8.
|
|
11356
|
+
var DAEMON_VERSION = true ? "2026.8.26" : "0.0.0-dev";
|
|
11356
11357
|
var MAX_UPLOAD_BYTES = 64 * 1024 * 1024;
|
|
11357
11358
|
function isWildcard(host) {
|
|
11358
11359
|
return host === "0.0.0.0" || host === "::" || host === "";
|
|
@@ -11427,6 +11428,11 @@ var Daemon = class {
|
|
|
11427
11428
|
* ответ, и мог бы показать его только целиком в самом конце.
|
|
11428
11429
|
*/
|
|
11429
11430
|
signalListeners = /* @__PURE__ */ new Set();
|
|
11431
|
+
/**
|
|
11432
|
+
* Пропуск для того, кто сидит за этой машиной: им подписан запрос на новый
|
|
11433
|
+
* код подключения. Живёт в файле рядом с базой, с правами 0600.
|
|
11434
|
+
*/
|
|
11435
|
+
controlToken = crypto4.randomBytes(24).toString("hex");
|
|
11430
11436
|
/** Разрешается, когда все установленные плагины отработали запуск. */
|
|
11431
11437
|
pluginsReady = Promise.resolve();
|
|
11432
11438
|
async start() {
|
|
@@ -11448,6 +11454,7 @@ var Daemon = class {
|
|
|
11448
11454
|
reachable: isWildcard(host) ? reachableUrls(boundPort) : []
|
|
11449
11455
|
};
|
|
11450
11456
|
const bootstrapCode = this.pairing.ensureBootstrapCode();
|
|
11457
|
+
this.writeControlToken();
|
|
11451
11458
|
this.announce();
|
|
11452
11459
|
logger.info({ url: this.address.url, coreId: this.runtime.coreId }, "\u0434\u0435\u043C\u043E\u043D \u0441\u043B\u0443\u0448\u0430\u0435\u0442");
|
|
11453
11460
|
this.pluginsReady = this.runtime.startPlugins().catch((error) => {
|
|
@@ -11566,6 +11573,26 @@ var Daemon = class {
|
|
|
11566
11573
|
* же машине мог его найти, не завися от того, кто его поднял, адрес и pid
|
|
11567
11574
|
* кладутся в файл рядом с базой.
|
|
11568
11575
|
*/
|
|
11576
|
+
controlPath() {
|
|
11577
|
+
return path16.join(this.runtime.config.dataDir, "control.token");
|
|
11578
|
+
}
|
|
11579
|
+
/**
|
|
11580
|
+
* Пропуск переписывается на каждом запуске: старый файл, оставшийся от
|
|
11581
|
+
* убитого процесса, не должен открывать двери в новое ядро.
|
|
11582
|
+
*/
|
|
11583
|
+
writeControlToken() {
|
|
11584
|
+
fs16.mkdirSync(this.runtime.config.dataDir, { recursive: true });
|
|
11585
|
+
fs16.writeFileSync(this.controlPath(), `${this.controlToken}
|
|
11586
|
+
`, { mode: 384 });
|
|
11587
|
+
}
|
|
11588
|
+
/** Сравнение постоянного времени: пропуск подбирают по одному символу. */
|
|
11589
|
+
controlAllowed(given) {
|
|
11590
|
+
const value = Array.isArray(given) ? given[0] : given;
|
|
11591
|
+
if (!value) return false;
|
|
11592
|
+
const a = Buffer.from(value.trim());
|
|
11593
|
+
const b = Buffer.from(this.controlToken);
|
|
11594
|
+
return a.length === b.length && crypto4.timingSafeEqual(a, b);
|
|
11595
|
+
}
|
|
11569
11596
|
announce() {
|
|
11570
11597
|
const file = path16.join(this.runtime.config.dataDir, "core.json");
|
|
11571
11598
|
fs16.writeFileSync(
|
|
@@ -11625,6 +11652,29 @@ var Daemon = class {
|
|
|
11625
11652
|
pendingPermissions: this.permissions.pendingCount
|
|
11626
11653
|
});
|
|
11627
11654
|
}
|
|
11655
|
+
if (req.method === "POST" && url.pathname === "/v1/control/pair") {
|
|
11656
|
+
if (!this.controlAllowed(req.headers["x-axon-control"])) {
|
|
11657
|
+
return respond(res, 403, { error: "forbidden" });
|
|
11658
|
+
}
|
|
11659
|
+
const body = await readJson(req);
|
|
11660
|
+
const chatOnly = body?.["scopes"] === "chat";
|
|
11661
|
+
const ttl = Number(body?.["ttlSeconds"] ?? 300);
|
|
11662
|
+
const issued = this.pairing.begin({
|
|
11663
|
+
name: typeof body?.["name"] === "string" ? body["name"] : "\u041D\u043E\u0432\u043E\u0435 \u0443\u0441\u0442\u0440\u043E\u0439\u0441\u0442\u0432\u043E",
|
|
11664
|
+
platform: "desktop",
|
|
11665
|
+
scopes: chatOnly ? ["chat.read", "chat.write", "tools.safe"] : [
|
|
11666
|
+
"chat.read",
|
|
11667
|
+
"chat.write",
|
|
11668
|
+
"tools.safe",
|
|
11669
|
+
"tools.sensitive",
|
|
11670
|
+
"tools.dangerous",
|
|
11671
|
+
"settings.write",
|
|
11672
|
+
"devices.manage"
|
|
11673
|
+
],
|
|
11674
|
+
ttlSeconds: Number.isFinite(ttl) && ttl > 0 ? Math.min(ttl, 3600) : 300
|
|
11675
|
+
});
|
|
11676
|
+
return respond(res, 200, issued);
|
|
11677
|
+
}
|
|
11628
11678
|
if (req.method === "POST" && url.pathname === "/v1/pair") {
|
|
11629
11679
|
const body = await readJson(req);
|
|
11630
11680
|
const code = typeof body?.["code"] === "string" ? body["code"] : "";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@axon-assistant/core",
|
|
3
|
-
"version": "2026.8.
|
|
3
|
+
"version": "2026.8.26",
|
|
4
4
|
"comment:version": "Заглушка. Настоящий номер проставляется из git-тега при публикации: см. scripts/release-npm.mjs. Версия, которую правят руками, рано или поздно расходится с тем, что опубликовано.",
|
|
5
5
|
"description": "Ядро Axon: персональный AI-агент, который живёт на вашей машине",
|
|
6
6
|
"keywords": [
|