@mushinjs/plugin-console 0.2.0-next.20261112165327 → 0.2.0-next.20261113105814
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/LICENSE +20 -20
- package/dist/auth.d.ts +9 -3
- package/dist/auth.js +14 -12
- package/dist/auth.js.map +1 -1
- package/dist/index.d.ts +7 -5
- package/dist/index.js +27 -23
- package/dist/index.js.map +1 -1
- package/dist/static.js.map +1 -1
- package/package.json +9 -9
package/LICENSE
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2026 Setobox
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Setobox
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
21
|
SOFTWARE.
|
package/dist/auth.d.ts
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
-
/**
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
/**
|
|
2
|
+
* The admin token lives in the instance's `.env` as `MUSHIN_ADMIN_TOKEN`, next to the keys it protects,
|
|
3
|
+
* so it can be shown again on every start. Set in the shell instead, it overrides the file and stays fixed.
|
|
4
|
+
*/
|
|
5
|
+
export declare const ADMIN_TOKEN_ENV = "MUSHIN_ADMIN_TOKEN";
|
|
6
|
+
/** Replaces the admin token in an instance's `.env` and returns it. A running instance picks it up on the next sign-in. */
|
|
7
|
+
export declare function resetAdminToken(baseDir: string): Promise<string>;
|
|
8
|
+
/** The admin token in an instance's `.env`, if there is one. */
|
|
9
|
+
export declare function readAdminToken(baseDir: string): string | undefined;
|
package/dist/auth.js
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { EnvFile } from "@mushinjs/core";
|
|
2
2
|
import { randomBytes, timingSafeEqual } from "node:crypto";
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
// src/auth.ts
|
|
5
|
-
/**
|
|
5
|
+
/**
|
|
6
|
+
* The admin token lives in the instance's `.env` as `MUSHIN_ADMIN_TOKEN`, next to the keys it protects,
|
|
7
|
+
* so it can be shown again on every start. Set in the shell instead, it overrides the file and stays fixed.
|
|
8
|
+
*/
|
|
9
|
+
const ADMIN_TOKEN_ENV = "MUSHIN_ADMIN_TOKEN";
|
|
10
|
+
/** Storage key of the auth record of older versions. */
|
|
6
11
|
const AUTH_KEY = "auth";
|
|
7
12
|
function generateToken() {
|
|
8
13
|
return randomBytes(24).toString("base64url");
|
|
@@ -17,19 +22,16 @@ function tokensEqual(supplied, expected) {
|
|
|
17
22
|
const b = Buffer.from(expected);
|
|
18
23
|
return a.length === b.length && timingSafeEqual(a, b);
|
|
19
24
|
}
|
|
20
|
-
/** Replaces the admin token in
|
|
21
|
-
async function resetAdminToken(
|
|
25
|
+
/** Replaces the admin token in an instance's `.env` and returns it. A running instance picks it up on the next sign-in. */
|
|
26
|
+
async function resetAdminToken(baseDir) {
|
|
22
27
|
const token = generateToken();
|
|
23
|
-
await
|
|
24
|
-
token,
|
|
25
|
-
createdAt: Date.now()
|
|
26
|
-
});
|
|
28
|
+
await new EnvFile(join(baseDir, ".env")).set(ADMIN_TOKEN_ENV, token);
|
|
27
29
|
return token;
|
|
28
30
|
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
return
|
|
31
|
+
/** The admin token in an instance's `.env`, if there is one. */
|
|
32
|
+
function readAdminToken(baseDir) {
|
|
33
|
+
return new EnvFile(join(baseDir, ".env")).loadSync().get("MUSHIN_ADMIN_TOKEN") || void 0;
|
|
32
34
|
}
|
|
33
|
-
export { AUTH_KEY, generateToken, isAuthRecord, readAdminToken, resetAdminToken, tokensEqual };
|
|
35
|
+
export { ADMIN_TOKEN_ENV, AUTH_KEY, generateToken, isAuthRecord, readAdminToken, resetAdminToken, tokensEqual };
|
|
34
36
|
|
|
35
37
|
//# sourceMappingURL=auth.js.map
|
package/dist/auth.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.js","names":[],"sources":["../src/auth.ts"],"sourcesContent":["import { randomBytes, timingSafeEqual } from 'node:crypto'\
|
|
1
|
+
{"version":3,"file":"auth.js","names":[],"sources":["../src/auth.ts"],"sourcesContent":["import { randomBytes, timingSafeEqual } from 'node:crypto'\nimport { join } from 'node:path'\nimport { EnvFile } from '@mushinjs/core'\n\n/**\n * The admin token lives in the instance's `.env` as `MUSHIN_ADMIN_TOKEN`, next to the keys it protects,\n * so it can be shown again on every start. Set in the shell instead, it overrides the file and stays fixed.\n */\nexport const ADMIN_TOKEN_ENV = 'MUSHIN_ADMIN_TOKEN'\n\n/** What older versions stored in `data/core/auth.json` (before that, `data/auth.json`). */\nexport interface AuthRecord {\n token: string\n createdAt: number\n}\n\n/** Storage key of the auth record of older versions. */\nexport const AUTH_KEY = 'auth'\n\nexport function generateToken() {\n return randomBytes(24).toString('base64url')\n}\n\n/** Older builds stored only a hash, which cannot be shown again; those records are replaced. */\nexport function isAuthRecord(value: unknown): value is AuthRecord {\n return typeof (value as AuthRecord | undefined)?.token === 'string' && (value as AuthRecord).token.length >= 16\n}\n\nexport function tokensEqual(supplied: string | null | undefined, expected: string | undefined) {\n if (!supplied || !expected) return false\n const a = Buffer.from(supplied)\n const b = Buffer.from(expected)\n return a.length === b.length && timingSafeEqual(a, b)\n}\n\n/** Replaces the admin token in an instance's `.env` and returns it. A running instance picks it up on the next sign-in. */\nexport async function resetAdminToken(baseDir: string) {\n const token = generateToken()\n await new EnvFile(join(baseDir, '.env')).set(ADMIN_TOKEN_ENV, token)\n return token\n}\n\n/** The admin token in an instance's `.env`, if there is one. */\nexport function readAdminToken(baseDir: string) {\n return new EnvFile(join(baseDir, '.env')).loadSync().get(ADMIN_TOKEN_ENV) || undefined\n}\n"],"mappings":";;;;;;;;AAQA,MAAa,kBAAkB;;AAS/B,MAAa,WAAW;AAExB,SAAgB,gBAAgB;CAC9B,OAAO,YAAY,EAAE,CAAC,CAAC,SAAS,WAAW;AAC7C;;AAGA,SAAgB,aAAa,OAAqC;CAChE,OAAO,OAAQ,OAAkC,UAAU,YAAa,MAAqB,MAAM,UAAU;AAC/G;AAEA,SAAgB,YAAY,UAAqC,UAA8B;CAC7F,IAAI,CAAC,YAAY,CAAC,UAAU,OAAO;CACnC,MAAM,IAAI,OAAO,KAAK,QAAQ;CAC9B,MAAM,IAAI,OAAO,KAAK,QAAQ;CAC9B,OAAO,EAAE,WAAW,EAAE,UAAU,gBAAgB,GAAG,CAAC;AACtD;;AAGA,eAAsB,gBAAgB,SAAiB;CACrD,MAAM,QAAQ,cAAc;CAC5B,MAAM,IAAI,QAAQ,KAAK,SAAS,MAAM,CAAC,CAAC,CAAC,IAAI,iBAAiB,KAAK;CACnE,OAAO;AACT;;AAGA,SAAgB,eAAe,SAAiB;CAC9C,OAAO,IAAI,QAAQ,KAAK,SAAS,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,IAAA,oBAAmB,KAAK,KAAA;AAC/E"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { readAdminToken, resetAdminToken } from "./auth.js";
|
|
1
|
+
import { ADMIN_TOKEN_ENV, readAdminToken, resetAdminToken } from "./auth.js";
|
|
2
2
|
import { Context, LogTag, Service, z } from "@mushinjs/core";
|
|
3
3
|
declare module '@mushinjs/core' {
|
|
4
4
|
interface Context {
|
|
@@ -38,9 +38,11 @@ export declare class Console extends Service {
|
|
|
38
38
|
}, z.core.$strip>;
|
|
39
39
|
private home;
|
|
40
40
|
private clients;
|
|
41
|
-
|
|
41
|
+
/** The token in .env. */
|
|
42
|
+
private token?;
|
|
42
43
|
private reloadedAt;
|
|
43
|
-
|
|
44
|
+
/** A token set in the shell, which overrides .env and cannot be reset. */
|
|
45
|
+
private envToken?;
|
|
44
46
|
/** Set once the dashboard is being served. */
|
|
45
47
|
ready?: SignInInfo;
|
|
46
48
|
constructor(ctx: Context, config: {
|
|
@@ -49,7 +51,7 @@ export declare class Console extends Service {
|
|
|
49
51
|
[Service.init](): AsyncGenerator<() => void, void, unknown>;
|
|
50
52
|
private ensureToken;
|
|
51
53
|
private verify;
|
|
52
|
-
/** Verifies a token; on a miss, re-reads
|
|
54
|
+
/** Verifies a token; on a miss, re-reads .env so `mushin token reset` works without a restart. */
|
|
53
55
|
private authenticate;
|
|
54
56
|
private authorize;
|
|
55
57
|
private isBotLog;
|
|
@@ -59,4 +61,4 @@ export declare class Console extends Service {
|
|
|
59
61
|
private relay;
|
|
60
62
|
private defineActions;
|
|
61
63
|
}
|
|
62
|
-
export { Console as default, readAdminToken, resetAdminToken };
|
|
64
|
+
export { ADMIN_TOKEN_ENV, Console as default, readAdminToken, resetAdminToken };
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AUTH_KEY, generateToken, isAuthRecord, readAdminToken, resetAdminToken, tokensEqual } from "./auth.js";
|
|
1
|
+
import { ADMIN_TOKEN_ENV, AUTH_KEY, generateToken, isAuthRecord, readAdminToken, resetAdminToken, tokensEqual } from "./auth.js";
|
|
2
2
|
import { MISSING_BUILD_PAGE, dashboardRoot, readStatic } from "./static.js";
|
|
3
3
|
import { LOG_TAGS, Logger, MushinError, Service, field, logTag, randomId, toErrorPayload, z } from "@mushinjs/core";
|
|
4
4
|
// src/index.ts
|
|
@@ -60,9 +60,11 @@ var Console = class extends Service {
|
|
|
60
60
|
} })) });
|
|
61
61
|
home;
|
|
62
62
|
clients = /* @__PURE__ */ new Map();
|
|
63
|
-
|
|
63
|
+
/** The token in .env. */
|
|
64
|
+
token;
|
|
64
65
|
reloadedAt = 0;
|
|
65
|
-
|
|
66
|
+
/** A token set in the shell, which overrides .env and cannot be reset. */
|
|
67
|
+
envToken;
|
|
66
68
|
/** Set once the dashboard is being served. */
|
|
67
69
|
ready;
|
|
68
70
|
constructor(ctx, config) {
|
|
@@ -147,7 +149,7 @@ var Console = class extends Service {
|
|
|
147
149
|
for (const name of FORWARDED_EVENTS) yield this.home.on(name, (data) => this.broadcast(name, data));
|
|
148
150
|
this.ready = {
|
|
149
151
|
url: devDashboard ?? server.baseUrl,
|
|
150
|
-
token: this.envToken ?? this.
|
|
152
|
+
token: this.envToken ?? this.token,
|
|
151
153
|
fromEnv: !!this.envToken
|
|
152
154
|
};
|
|
153
155
|
this.home.emit("console/ready", this.ready);
|
|
@@ -163,36 +165,38 @@ var Console = class extends Service {
|
|
|
163
165
|
};
|
|
164
166
|
}
|
|
165
167
|
async ensureToken() {
|
|
166
|
-
|
|
167
|
-
|
|
168
|
+
const { storage } = this.home;
|
|
169
|
+
const logger = this.home.logger("console");
|
|
170
|
+
if (storage.secrets.fromShell("MUSHIN_ADMIN_TOKEN")) {
|
|
171
|
+
this.envToken = process.env[ADMIN_TOKEN_ENV];
|
|
172
|
+
logger.info("using the admin token from %s", ADMIN_TOKEN_ENV);
|
|
168
173
|
return;
|
|
169
174
|
}
|
|
170
|
-
const
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
175
|
+
const legacy = await storage.get(AUTH_KEY).catch(() => void 0);
|
|
176
|
+
this.token = storage.secrets.get("MUSHIN_ADMIN_TOKEN") || void 0;
|
|
177
|
+
if (!this.token) {
|
|
178
|
+
this.token = isAuthRecord(legacy) ? legacy.token : generateToken();
|
|
179
|
+
await storage.secrets.set(ADMIN_TOKEN_ENV, this.token);
|
|
180
|
+
logger.info(isAuthRecord(legacy) ? "moved the admin token to .env" : "created the admin token in .env");
|
|
174
181
|
}
|
|
175
|
-
|
|
176
|
-
token: generateToken(),
|
|
177
|
-
createdAt: Date.now()
|
|
178
|
-
};
|
|
179
|
-
await this.home.storage.set(AUTH_KEY, this.auth);
|
|
180
|
-
this.home.logger("console").info(stored ? "replaced an unreadable admin token record" : "created the admin token");
|
|
182
|
+
if (legacy !== void 0) await storage.delete(AUTH_KEY);
|
|
181
183
|
}
|
|
182
184
|
verify(token) {
|
|
183
|
-
return tokensEqual(token, this.envToken ?? this.
|
|
185
|
+
return tokensEqual(token, this.envToken ?? this.token);
|
|
184
186
|
}
|
|
185
|
-
/** Verifies a token; on a miss, re-reads
|
|
187
|
+
/** Verifies a token; on a miss, re-reads .env so `mushin token reset` works without a restart. */
|
|
186
188
|
async authenticate(token) {
|
|
187
189
|
if (this.verify(token)) return true;
|
|
188
190
|
if (this.envToken || !token || Date.now() - this.reloadedAt < 1e3) return false;
|
|
189
191
|
this.reloadedAt = Date.now();
|
|
190
|
-
const
|
|
191
|
-
|
|
192
|
-
|
|
192
|
+
const { secrets } = this.home.storage;
|
|
193
|
+
await secrets.reload().catch(() => {});
|
|
194
|
+
const stored = secrets.get(ADMIN_TOKEN_ENV);
|
|
195
|
+
if (!stored || stored === this.token) return false;
|
|
196
|
+
this.token = stored;
|
|
193
197
|
if (this.ready) this.ready = {
|
|
194
198
|
...this.ready,
|
|
195
|
-
token: stored
|
|
199
|
+
token: stored
|
|
196
200
|
};
|
|
197
201
|
return this.verify(token);
|
|
198
202
|
}
|
|
@@ -404,6 +408,6 @@ async function readBody(req) {
|
|
|
404
408
|
throw new MushinError("invalid_input", "Request body must be JSON");
|
|
405
409
|
}
|
|
406
410
|
}
|
|
407
|
-
export { Console, Console as default, name, readAdminToken, resetAdminToken };
|
|
411
|
+
export { ADMIN_TOKEN_ENV, Console, Console as default, name, readAdminToken, resetAdminToken };
|
|
408
412
|
|
|
409
413
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["import type { WebSocket } from 'ws'\r\nimport {\r\n type ActionCaller, Context, type Exporter, field, Logger, LOG_TAGS, type LogTag, logTag, type Message, MushinError, randomId, Service, toErrorPayload, z,\r\n} from '@mushinjs/core'\r\nimport type {} from '@cordisjs/plugin-server'\r\nimport type {} from '@mushinjs/plugin-agent'\r\nimport type {} from '@mushinjs/plugin-bots'\r\nimport type {} from '@mushinjs/plugin-flows'\r\nimport type {} from '@mushinjs/plugin-providers'\r\nimport type {} from '@mushinjs/plugin-stats'\r\nimport type {} from '@mushinjs/plugin-storage'\r\nimport { AUTH_KEY, type AuthRecord, generateToken, isAuthRecord, tokensEqual } from './auth'\r\nimport { dashboardRoot, MISSING_BUILD_PAGE, readStatic } from './static'\r\n\r\nexport { readAdminToken, resetAdminToken } from './auth'\r\n\r\ndeclare module '@mushinjs/core' {\r\n interface Context {\r\n console: Console\r\n }\r\n\r\n interface Events {\r\n 'console/ready'(info: SignInInfo): void\r\n }\r\n}\r\n\r\n/** What the launcher prints so the user can sign in. Never sent through the logger. */\r\nexport interface SignInInfo {\r\n url: string\r\n token: string\r\n fromEnv: boolean\r\n}\r\n\r\nexport const name = 'console'\r\n\r\ninterface Client {\r\n id: string\r\n socket: WebSocket\r\n authed: boolean\r\n page?: string\r\n lastActive: number\r\n calls: Map<string, { resolve: (value: unknown) => void; reject: (error: Error) => void; timer: NodeJS.Timeout }>\r\n}\r\n\r\nexport interface LogLine {\r\n sn: number\r\n ts: number\r\n type: string\r\n name: string\r\n /** Source category from the logger name, for the dashboard's quick filters. */\r\n tag: LogTag\r\n text: string\r\n}\r\n\r\nconst CLIENT_CALL_TIMEOUT = 15_000\r\n/** Client actions that wait for the user get longer. */\r\nconst CLIENT_CALL_TIMEOUTS: Record<string, number> = { 'ui.ask': 15 * 60_000 }\r\n\r\n/** Events forwarded to every signed-in dashboard. */\r\nconst FORWARDED_EVENTS = [\r\n 'actions/changed', 'manager/changed', 'providers/changed',\r\n 'bots/changed', 'bots/status', 'bots/message', 'bots/log', 'agent/event', 'agent/changed',\r\n 'flows/changed', 'flows/run', 'flows/step', 'flows/tests', 'flows/activity', 'flows/paused', 'stats/sample', 'permissions/changed', 'personas/changed',\r\n 'knowledge/changed', 'knowledge/progress',\r\n] as const\r\n\r\nconst plainExporter: Exporter = { colors: false, export() {} }\r\n\r\nfunction formatLog(message: Message): LogLine {\r\n return {\r\n sn: message.sn,\r\n ts: message.ts,\r\n type: message.type,\r\n name: message.name,\r\n tag: logTag(message.name),\r\n text: Logger.format(plainExporter, message),\r\n }\r\n}\r\n\r\n/**\r\n * Serves the dashboard, its HTTP API (one generic action endpoint) and a WebSocket for live events.\r\n * Every request needs the admin token.\r\n */\r\nexport class Console extends Service {\r\n static inject = ['server', 'actions', 'storage']\r\n static Config = z.object({\r\n path: z.string().default('/api').meta(field('API path', 'Base path of the HTTP API', {\r\n 'zh-CN': { title: 'API 路径', description: 'HTTP API 的基础路径' },\r\n })),\r\n })\r\n\r\n private home: Context\r\n private clients = new Map<string, Client>()\r\n private auth?: AuthRecord\r\n private reloadedAt = 0\r\n private envToken = process.env.MUSHIN_ADMIN_TOKEN\r\n /** Set once the dashboard is being served. */\r\n ready?: SignInInfo\r\n\r\n constructor(ctx: Context, public config: { path: string }) {\r\n super(ctx, 'console')\r\n this.home = ctx\r\n }\r\n\r\n async *[Service.init]() {\r\n const devDashboard = process.env.MUSHIN_DEV_DASHBOARD\r\n await this.ensureToken()\r\n const { server, actions } = this.home\r\n const base = this.config.path\r\n\r\n yield actions.setClientRelay((name, input, caller) => this.relay(name, input, caller))\r\n yield* this.defineActions()\r\n\r\n // Health and login are the only routes that work without a token.\r\n server.get(`${base}/health`, async (_req, res) => {\r\n res.json({ ok: true })\r\n })\r\n\r\n server.post(`${base}/login`, async (req, res) => {\r\n const body = await readBody(req).catch(() => undefined)\r\n if (!(await this.authenticate(typeof body?.token === 'string' ? body.token.trim() : undefined))) {\r\n res.status = 401\r\n res.json({ ok: false, error: { code: 'unauthorized', message: 'Wrong admin token' } })\r\n return\r\n }\r\n res.json({ ok: true })\r\n })\r\n\r\n server.get(`${base}/actions`, async (req, res) => {\r\n if (!(await this.authorize(req, res))) return\r\n res.json({ ok: true, result: actions.list() })\r\n })\r\n\r\n server.post(`${base}/actions/:name`, async (req, res) => {\r\n if (!(await this.authorize(req, res))) return\r\n try {\r\n const body = await readBody(req)\r\n const result = await actions.call(req.params.name, body ?? {}, { kind: 'api' })\r\n res.json({ ok: true, result: result ?? null })\r\n } catch (error) {\r\n res.status = error instanceof MushinError ? error.status : 500\r\n if (!(error instanceof MushinError)) this.home.logger('console').warn(error)\r\n res.json({ ok: false, error: toErrorPayload(error) })\r\n }\r\n })\r\n\r\n server.ws(`${base}/ws`, async (_req, accept) => {\r\n this.attach(await accept())\r\n })\r\n\r\n // Everything outside the API is the dashboard (with SPA fallback).\r\n const root = dashboardRoot()\r\n server.get(/^(?!\\/api(?:\\/|$)).*/, async (req, res) => {\r\n if (devDashboard) {\r\n res.status = 302\r\n res.headers.set('location', `${devDashboard}${req.url}`)\r\n res.headers.set('cache-control', 'no-store')\r\n res.text('')\r\n return\r\n }\r\n const file = root ? await readStatic(root, req.path) : undefined\r\n if (!file) {\r\n res.headers.set('content-type', 'text/html; charset=utf-8')\r\n res.status = root ? 404 : 200\r\n res.text(root ? 'Not found' : MISSING_BUILD_PAGE)\r\n return\r\n }\r\n res.status = 200\r\n res.headers.set('content-type', file.type)\r\n res.headers.set('cache-control', file.immutable ? 'public, max-age=31536000, immutable' : 'no-cache')\r\n res.bytes(new Uint8Array(file.body))\r\n })\r\n\r\n for (const name of FORWARDED_EVENTS) {\r\n yield this.home.on(name, (data: unknown) => this.broadcast(name, data))\r\n }\r\n\r\n // The launcher prints the sign-in link from this event; the token never goes to the log buffer,\r\n // which the dashboard and the Agent can read.\r\n this.ready = { url: devDashboard ?? server.baseUrl, token: this.envToken ?? this.auth!.token, fromEnv: !!this.envToken }\r\n this.home.emit('console/ready', this.ready)\r\n this.home.logger('console').info('dashboard at %s', this.ready.url)\r\n yield this.home.logger.exporter({\r\n colors: false,\r\n // Lines written inside a bot go to that bot's own log (`bots/log`), not the global one.\r\n export: (message) => {\r\n if (!this.isBotLog(message)) this.broadcast('log', formatLog(message))\r\n },\r\n })\r\n yield () => {\r\n for (const client of this.clients.values()) client.socket.close(1001, 'console stopped')\r\n }\r\n }\r\n\r\n private async ensureToken() {\r\n if (this.envToken) {\r\n this.home.logger('console').info('using the admin token from MUSHIN_ADMIN_TOKEN')\r\n return\r\n }\r\n const stored = await this.home.storage.get<unknown>(AUTH_KEY)\r\n if (isAuthRecord(stored)) {\r\n this.auth = stored\r\n return\r\n }\r\n // No record yet, or one from an older build that kept only a hash: make a token that can be shown.\r\n this.auth = { token: generateToken(), createdAt: Date.now() }\r\n await this.home.storage.set(AUTH_KEY, this.auth)\r\n this.home.logger('console').info(stored ? 'replaced an unreadable admin token record' : 'created the admin token')\r\n }\r\n\r\n private verify(token: string | null | undefined) {\r\n return tokensEqual(token, this.envToken ?? this.auth?.token)\r\n }\r\n\r\n /** Verifies a token; on a miss, re-reads data/auth.json so `mushin token reset` works without a restart. */\r\n private async authenticate(token: string | null | undefined) {\r\n if (this.verify(token)) return true\r\n if (this.envToken || !token || Date.now() - this.reloadedAt < 1000) return false\r\n this.reloadedAt = Date.now()\r\n const stored = await this.home.storage.get<unknown>(AUTH_KEY).catch(() => undefined)\r\n if (!isAuthRecord(stored) || stored.token === this.auth?.token) return false\r\n this.auth = stored\r\n if (this.ready) this.ready = { ...this.ready, token: stored.token }\r\n return this.verify(token)\r\n }\r\n\r\n private async authorize(req: { headers: Headers }, res: { status: number; json(data: unknown): unknown }) {\r\n const header = req.headers.get('authorization') ?? ''\r\n const token = header.replace(/^Bearer\\s+/i, '')\r\n if (await this.authenticate(token)) return true\r\n res.status = 401\r\n res.json({ ok: false, error: { code: 'unauthorized', message: 'Sign in with the admin token' } })\r\n return false\r\n }\r\n\r\n private isBotLog(message: Message) {\r\n return this.home.get('bots')?.isBotLog(message) ?? false\r\n }\r\n\r\n broadcast(name: string, data: unknown) {\r\n const frame = JSON.stringify({ type: 'event', name, data })\r\n for (const client of this.clients.values()) {\r\n if (client.authed && client.socket.readyState === 1) client.socket.send(frame)\r\n }\r\n }\r\n\r\n private attach(socket: WebSocket) {\r\n const client: Client = { id: randomId(), socket, authed: false, lastActive: Date.now(), calls: new Map() }\r\n this.clients.set(client.id, client)\r\n const send = (data: unknown) => socket.readyState === 1 && socket.send(JSON.stringify(data))\r\n const authTimer = setTimeout(() => {\r\n if (!client.authed) socket.close(4401, 'authentication timeout')\r\n }, 10_000)\r\n\r\n socket.on('close', () => {\r\n clearTimeout(authTimer)\r\n this.clients.delete(client.id)\r\n for (const call of client.calls.values()) {\r\n clearTimeout(call.timer)\r\n call.reject(new MushinError('unavailable', 'The dashboard tab was closed'))\r\n }\r\n })\r\n\r\n // Messages can arrive while the token is still being checked (the tab reports its page as soon\r\n // as the socket opens); they wait for the check instead of failing it.\r\n let authenticating: Promise<boolean> | undefined\r\n socket.on('message', async (raw) => {\r\n let message: any\r\n try {\r\n message = JSON.parse(raw.toString())\r\n } catch {\r\n return\r\n }\r\n client.lastActive = Date.now()\r\n if (!client.authed) {\r\n if (message.type === 'auth' && !authenticating) {\r\n authenticating = this.authenticate(message.token)\r\n if (await authenticating) {\r\n client.authed = true\r\n client.page = message.page\r\n clearTimeout(authTimer)\r\n send({ type: 'ready', client: client.id })\r\n } else {\r\n socket.close(4401, 'wrong admin token')\r\n }\r\n return\r\n }\r\n if (!authenticating) {\r\n socket.close(4401, 'authenticate first')\r\n return\r\n }\r\n if (!await authenticating) return\r\n }\r\n switch (message.type) {\r\n case 'page':\r\n client.page = typeof message.path === 'string' ? message.path : undefined\r\n return\r\n case 'call': {\r\n const caller: ActionCaller = { kind: 'api', client: client.id }\r\n try {\r\n const input = message.action === 'agent.send' ? { page: client.page, ...message.input } : message.input\r\n const result = await this.home.actions.call(String(message.action), input ?? {}, caller)\r\n send({ type: 'result', id: message.id, ok: true, result: result ?? null })\r\n } catch (error) {\r\n send({ type: 'result', id: message.id, ok: false, error: toErrorPayload(error) })\r\n }\r\n return\r\n }\r\n case 'client-result': {\r\n const call = client.calls.get(String(message.id))\r\n if (!call) return\r\n client.calls.delete(String(message.id))\r\n clearTimeout(call.timer)\r\n if (message.ok) call.resolve(message.result ?? { ok: true })\r\n else call.reject(new MushinError('failed', message.error?.message ?? 'The dashboard could not run this action'))\r\n }\r\n }\r\n })\r\n }\r\n\r\n /** Runs a client action in the caller's dashboard tab, or the most recently active one. */\r\n private relay(name: string, input: unknown, caller: ActionCaller) {\r\n let client = caller.client ? this.clients.get(caller.client) : undefined\r\n if (!client?.authed) {\r\n client = [...this.clients.values()].filter((item) => item.authed).sort((a, b) => b.lastActive - a.lastActive)[0]\r\n }\r\n if (!client) return Promise.reject(new MushinError('unavailable', 'No dashboard is open'))\r\n const target = client\r\n const id = randomId()\r\n return new Promise<unknown>((resolve, reject) => {\r\n const timer = setTimeout(() => {\r\n target.calls.delete(id)\r\n reject(new MushinError('unavailable', 'The dashboard did not answer'))\r\n }, CLIENT_CALL_TIMEOUTS[name] ?? CLIENT_CALL_TIMEOUT)\r\n target.calls.set(id, { resolve, reject, timer })\r\n target.socket.send(JSON.stringify({ type: 'client-call', id, action: name, input }))\r\n })\r\n }\r\n\r\n private *defineActions() {\r\n const actions = this.home.actions\r\n\r\n yield actions.define({\r\n name: 'ui.navigate',\r\n description: 'Open a dashboard page in the user\\'s browser, e.g. /bots/<bot id>, /flows/<flow id> or /providers.',\r\n risk: 'read',\r\n client: true,\r\n input: z.object({ path: z.string().regex(/^\\//).describe('Dashboard path starting with /') }),\r\n })\r\n\r\n yield actions.define({\r\n name: 'ui.highlight',\r\n description: 'Point at a control on the current dashboard page. Targets: providers.add, bots.add, bot.connection, bot.plugins, bot.runs, bot.live, bot.logs, flows.add, flows.bots, flows.save, flows.test, agent.input.',\r\n risk: 'read',\r\n client: true,\r\n input: z.object({\r\n target: z.string().describe('Highlight target id'),\r\n message: z.string().optional().describe('Short hint shown next to the control'),\r\n }),\r\n })\r\n\r\n yield actions.define({\r\n name: 'ui.ask',\r\n description: 'Ask the user a multiple-choice question in the Agent panel and wait for the answer. Use it when a choice is theirs to make (which bot, which provider, whether to overwrite). Returns { choice, label } or { other } when they type their own answer.',\r\n risk: 'read',\r\n client: true,\r\n input: z.object({\r\n question: z.string().min(1).max(300),\r\n options: z.array(z.object({\r\n label: z.string().min(1).max(80),\r\n description: z.string().max(200).optional(),\r\n })).min(2).max(6),\r\n recommended: z.number().int().min(0).optional().describe('Index of the option you recommend'),\r\n allowOther: z.boolean().optional().describe('Let the user type their own answer'),\r\n }),\r\n })\r\n\r\n yield actions.define({\r\n name: 'system.info',\r\n description: 'Server address and runtime versions. The address is what NapCat should use in reverse mode.',\r\n risk: 'read',\r\n input: z.object({}),\r\n handler: () => ({\r\n baseUrl: this.home.server.baseUrl,\r\n node: process.version,\r\n platform: process.platform,\r\n uptime: Math.round(process.uptime()),\r\n }),\r\n })\r\n\r\n yield actions.define({\r\n name: 'logs.recent',\r\n description: 'Recent log lines of Mushin.js itself, oldest first, each with a source tag. Lines written inside a bot are in bots.logs instead.',\r\n risk: 'read',\r\n input: z.object({\r\n limit: z.number().int().min(1).max(1000).optional(),\r\n tag: z.enum(LOG_TAGS).optional().describe('Only lines from this source: plugin, bot, adapter, flow, provider, agent, system or other'),\r\n }),\r\n handler: ({ limit, tag }) => this.home.logger.buffer\r\n .filter((message) => !this.isBotLog(message) && (!tag || logTag(message.name) === tag))\r\n .slice(-(limit ?? 200))\r\n .map(formatLog),\r\n })\r\n }\r\n}\r\n\r\nasync function readBody(req: { text(): Promise<string> }) {\r\n const text = await req.text()\r\n if (!text) return undefined\r\n try {\r\n return JSON.parse(text)\r\n } catch {\r\n throw new MushinError('invalid_input', 'Request body must be JSON')\r\n }\r\n}\r\n\r\nexport default Console\r\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["import type { WebSocket } from 'ws'\nimport {\n type ActionCaller, Context, type Exporter, field, Logger, LOG_TAGS, type LogTag, logTag, type Message, MushinError, randomId, Service, toErrorPayload, z,\n} from '@mushinjs/core'\nimport type {} from '@cordisjs/plugin-server'\nimport type {} from '@mushinjs/plugin-agent'\nimport type {} from '@mushinjs/plugin-bots'\nimport type {} from '@mushinjs/plugin-flows'\nimport type {} from '@mushinjs/plugin-providers'\nimport type {} from '@mushinjs/plugin-stats'\nimport type {} from '@mushinjs/plugin-storage'\nimport { ADMIN_TOKEN_ENV, AUTH_KEY, generateToken, isAuthRecord, tokensEqual } from './auth'\nimport { dashboardRoot, MISSING_BUILD_PAGE, readStatic } from './static'\n\nexport { ADMIN_TOKEN_ENV, readAdminToken, resetAdminToken } from './auth'\n\ndeclare module '@mushinjs/core' {\n interface Context {\n console: Console\n }\n\n interface Events {\n 'console/ready'(info: SignInInfo): void\n }\n}\n\n/** What the launcher prints so the user can sign in. Never sent through the logger. */\nexport interface SignInInfo {\n url: string\n token: string\n fromEnv: boolean\n}\n\nexport const name = 'console'\n\ninterface Client {\n id: string\n socket: WebSocket\n authed: boolean\n page?: string\n lastActive: number\n calls: Map<string, { resolve: (value: unknown) => void; reject: (error: Error) => void; timer: NodeJS.Timeout }>\n}\n\nexport interface LogLine {\n sn: number\n ts: number\n type: string\n name: string\n /** Source category from the logger name, for the dashboard's quick filters. */\n tag: LogTag\n text: string\n}\n\nconst CLIENT_CALL_TIMEOUT = 15_000\n/** Client actions that wait for the user get longer. */\nconst CLIENT_CALL_TIMEOUTS: Record<string, number> = { 'ui.ask': 15 * 60_000 }\n\n/** Events forwarded to every signed-in dashboard. */\nconst FORWARDED_EVENTS = [\n 'actions/changed', 'manager/changed', 'providers/changed',\n 'bots/changed', 'bots/status', 'bots/message', 'bots/log', 'agent/event', 'agent/changed',\n 'flows/changed', 'flows/run', 'flows/step', 'flows/tests', 'flows/activity', 'flows/paused', 'stats/sample', 'permissions/changed', 'personas/changed',\n 'knowledge/changed', 'knowledge/progress',\n] as const\n\nconst plainExporter: Exporter = { colors: false, export() {} }\n\nfunction formatLog(message: Message): LogLine {\n return {\n sn: message.sn,\n ts: message.ts,\n type: message.type,\n name: message.name,\n tag: logTag(message.name),\n text: Logger.format(plainExporter, message),\n }\n}\n\n/**\n * Serves the dashboard, its HTTP API (one generic action endpoint) and a WebSocket for live events.\n * Every request needs the admin token.\n */\nexport class Console extends Service {\n static inject = ['server', 'actions', 'storage']\n static Config = z.object({\n path: z.string().default('/api').meta(field('API path', 'Base path of the HTTP API', {\n 'zh-CN': { title: 'API 路径', description: 'HTTP API 的基础路径' },\n })),\n })\n\n private home: Context\n private clients = new Map<string, Client>()\n /** The token in .env. */\n private token?: string\n private reloadedAt = 0\n /** A token set in the shell, which overrides .env and cannot be reset. */\n private envToken?: string\n /** Set once the dashboard is being served. */\n ready?: SignInInfo\n\n constructor(ctx: Context, public config: { path: string }) {\n super(ctx, 'console')\n this.home = ctx\n }\n\n async *[Service.init]() {\n const devDashboard = process.env.MUSHIN_DEV_DASHBOARD\n await this.ensureToken()\n const { server, actions } = this.home\n const base = this.config.path\n\n yield actions.setClientRelay((name, input, caller) => this.relay(name, input, caller))\n yield* this.defineActions()\n\n // Health and login are the only routes that work without a token.\n server.get(`${base}/health`, async (_req, res) => {\n res.json({ ok: true })\n })\n\n server.post(`${base}/login`, async (req, res) => {\n const body = await readBody(req).catch(() => undefined)\n if (!(await this.authenticate(typeof body?.token === 'string' ? body.token.trim() : undefined))) {\n res.status = 401\n res.json({ ok: false, error: { code: 'unauthorized', message: 'Wrong admin token' } })\n return\n }\n res.json({ ok: true })\n })\n\n server.get(`${base}/actions`, async (req, res) => {\n if (!(await this.authorize(req, res))) return\n res.json({ ok: true, result: actions.list() })\n })\n\n server.post(`${base}/actions/:name`, async (req, res) => {\n if (!(await this.authorize(req, res))) return\n try {\n const body = await readBody(req)\n const result = await actions.call(req.params.name, body ?? {}, { kind: 'api' })\n res.json({ ok: true, result: result ?? null })\n } catch (error) {\n res.status = error instanceof MushinError ? error.status : 500\n if (!(error instanceof MushinError)) this.home.logger('console').warn(error)\n res.json({ ok: false, error: toErrorPayload(error) })\n }\n })\n\n server.ws(`${base}/ws`, async (_req, accept) => {\n this.attach(await accept())\n })\n\n // Everything outside the API is the dashboard (with SPA fallback).\n const root = dashboardRoot()\n server.get(/^(?!\\/api(?:\\/|$)).*/, async (req, res) => {\n if (devDashboard) {\n res.status = 302\n res.headers.set('location', `${devDashboard}${req.url}`)\n res.headers.set('cache-control', 'no-store')\n res.text('')\n return\n }\n const file = root ? await readStatic(root, req.path) : undefined\n if (!file) {\n res.headers.set('content-type', 'text/html; charset=utf-8')\n res.status = root ? 404 : 200\n res.text(root ? 'Not found' : MISSING_BUILD_PAGE)\n return\n }\n res.status = 200\n res.headers.set('content-type', file.type)\n res.headers.set('cache-control', file.immutable ? 'public, max-age=31536000, immutable' : 'no-cache')\n res.bytes(new Uint8Array(file.body))\n })\n\n for (const name of FORWARDED_EVENTS) {\n yield this.home.on(name, (data: unknown) => this.broadcast(name, data))\n }\n\n // The launcher prints the sign-in link from this event; the token never goes to the log buffer,\n // which the dashboard and the Agent can read.\n this.ready = { url: devDashboard ?? server.baseUrl, token: this.envToken ?? this.token!, fromEnv: !!this.envToken }\n this.home.emit('console/ready', this.ready)\n this.home.logger('console').info('dashboard at %s', this.ready.url)\n yield this.home.logger.exporter({\n colors: false,\n // Lines written inside a bot go to that bot's own log (`bots/log`), not the global one.\n export: (message) => {\n if (!this.isBotLog(message)) this.broadcast('log', formatLog(message))\n },\n })\n yield () => {\n for (const client of this.clients.values()) client.socket.close(1001, 'console stopped')\n }\n }\n\n private async ensureToken() {\n const { storage } = this.home\n const logger = this.home.logger('console')\n if (storage.secrets.fromShell(ADMIN_TOKEN_ENV)) {\n this.envToken = process.env[ADMIN_TOKEN_ENV]\n logger.info('using the admin token from %s', ADMIN_TOKEN_ENV)\n return\n }\n // Older versions kept the token in data/core/auth.json (or only a hash of it, which cannot be shown).\n const legacy = await storage.get<unknown>(AUTH_KEY).catch(() => undefined)\n this.token = storage.secrets.get(ADMIN_TOKEN_ENV) || undefined\n if (!this.token) {\n this.token = isAuthRecord(legacy) ? legacy.token : generateToken()\n await storage.secrets.set(ADMIN_TOKEN_ENV, this.token)\n logger.info(isAuthRecord(legacy) ? 'moved the admin token to .env' : 'created the admin token in .env')\n }\n if (legacy !== undefined) await storage.delete(AUTH_KEY)\n }\n\n private verify(token: string | null | undefined) {\n return tokensEqual(token, this.envToken ?? this.token)\n }\n\n /** Verifies a token; on a miss, re-reads .env so `mushin token reset` works without a restart. */\n private async authenticate(token: string | null | undefined) {\n if (this.verify(token)) return true\n if (this.envToken || !token || Date.now() - this.reloadedAt < 1000) return false\n this.reloadedAt = Date.now()\n const { secrets } = this.home.storage\n await secrets.reload().catch(() => {})\n const stored = secrets.get(ADMIN_TOKEN_ENV)\n if (!stored || stored === this.token) return false\n this.token = stored\n if (this.ready) this.ready = { ...this.ready, token: stored }\n return this.verify(token)\n }\n\n private async authorize(req: { headers: Headers }, res: { status: number; json(data: unknown): unknown }) {\n const header = req.headers.get('authorization') ?? ''\n const token = header.replace(/^Bearer\\s+/i, '')\n if (await this.authenticate(token)) return true\n res.status = 401\n res.json({ ok: false, error: { code: 'unauthorized', message: 'Sign in with the admin token' } })\n return false\n }\n\n private isBotLog(message: Message) {\n return this.home.get('bots')?.isBotLog(message) ?? false\n }\n\n broadcast(name: string, data: unknown) {\n const frame = JSON.stringify({ type: 'event', name, data })\n for (const client of this.clients.values()) {\n if (client.authed && client.socket.readyState === 1) client.socket.send(frame)\n }\n }\n\n private attach(socket: WebSocket) {\n const client: Client = { id: randomId(), socket, authed: false, lastActive: Date.now(), calls: new Map() }\n this.clients.set(client.id, client)\n const send = (data: unknown) => socket.readyState === 1 && socket.send(JSON.stringify(data))\n const authTimer = setTimeout(() => {\n if (!client.authed) socket.close(4401, 'authentication timeout')\n }, 10_000)\n\n socket.on('close', () => {\n clearTimeout(authTimer)\n this.clients.delete(client.id)\n for (const call of client.calls.values()) {\n clearTimeout(call.timer)\n call.reject(new MushinError('unavailable', 'The dashboard tab was closed'))\n }\n })\n\n // Messages can arrive while the token is still being checked (the tab reports its page as soon\n // as the socket opens); they wait for the check instead of failing it.\n let authenticating: Promise<boolean> | undefined\n socket.on('message', async (raw) => {\n let message: any\n try {\n message = JSON.parse(raw.toString())\n } catch {\n return\n }\n client.lastActive = Date.now()\n if (!client.authed) {\n if (message.type === 'auth' && !authenticating) {\n authenticating = this.authenticate(message.token)\n if (await authenticating) {\n client.authed = true\n client.page = message.page\n clearTimeout(authTimer)\n send({ type: 'ready', client: client.id })\n } else {\n socket.close(4401, 'wrong admin token')\n }\n return\n }\n if (!authenticating) {\n socket.close(4401, 'authenticate first')\n return\n }\n if (!await authenticating) return\n }\n switch (message.type) {\n case 'page':\n client.page = typeof message.path === 'string' ? message.path : undefined\n return\n case 'call': {\n const caller: ActionCaller = { kind: 'api', client: client.id }\n try {\n const input = message.action === 'agent.send' ? { page: client.page, ...message.input } : message.input\n const result = await this.home.actions.call(String(message.action), input ?? {}, caller)\n send({ type: 'result', id: message.id, ok: true, result: result ?? null })\n } catch (error) {\n send({ type: 'result', id: message.id, ok: false, error: toErrorPayload(error) })\n }\n return\n }\n case 'client-result': {\n const call = client.calls.get(String(message.id))\n if (!call) return\n client.calls.delete(String(message.id))\n clearTimeout(call.timer)\n if (message.ok) call.resolve(message.result ?? { ok: true })\n else call.reject(new MushinError('failed', message.error?.message ?? 'The dashboard could not run this action'))\n }\n }\n })\n }\n\n /** Runs a client action in the caller's dashboard tab, or the most recently active one. */\n private relay(name: string, input: unknown, caller: ActionCaller) {\n let client = caller.client ? this.clients.get(caller.client) : undefined\n if (!client?.authed) {\n client = [...this.clients.values()].filter((item) => item.authed).sort((a, b) => b.lastActive - a.lastActive)[0]\n }\n if (!client) return Promise.reject(new MushinError('unavailable', 'No dashboard is open'))\n const target = client\n const id = randomId()\n return new Promise<unknown>((resolve, reject) => {\n const timer = setTimeout(() => {\n target.calls.delete(id)\n reject(new MushinError('unavailable', 'The dashboard did not answer'))\n }, CLIENT_CALL_TIMEOUTS[name] ?? CLIENT_CALL_TIMEOUT)\n target.calls.set(id, { resolve, reject, timer })\n target.socket.send(JSON.stringify({ type: 'client-call', id, action: name, input }))\n })\n }\n\n private *defineActions() {\n const actions = this.home.actions\n\n yield actions.define({\n name: 'ui.navigate',\n description: 'Open a dashboard page in the user\\'s browser, e.g. /bots/<bot id>, /flows/<flow id> or /providers.',\n risk: 'read',\n client: true,\n input: z.object({ path: z.string().regex(/^\\//).describe('Dashboard path starting with /') }),\n })\n\n yield actions.define({\n name: 'ui.highlight',\n description: 'Point at a control on the current dashboard page. Targets: providers.add, bots.add, bot.connection, bot.plugins, bot.runs, bot.live, bot.logs, flows.add, flows.bots, flows.save, flows.test, agent.input.',\n risk: 'read',\n client: true,\n input: z.object({\n target: z.string().describe('Highlight target id'),\n message: z.string().optional().describe('Short hint shown next to the control'),\n }),\n })\n\n yield actions.define({\n name: 'ui.ask',\n description: 'Ask the user a multiple-choice question in the Agent panel and wait for the answer. Use it when a choice is theirs to make (which bot, which provider, whether to overwrite). Returns { choice, label } or { other } when they type their own answer.',\n risk: 'read',\n client: true,\n input: z.object({\n question: z.string().min(1).max(300),\n options: z.array(z.object({\n label: z.string().min(1).max(80),\n description: z.string().max(200).optional(),\n })).min(2).max(6),\n recommended: z.number().int().min(0).optional().describe('Index of the option you recommend'),\n allowOther: z.boolean().optional().describe('Let the user type their own answer'),\n }),\n })\n\n yield actions.define({\n name: 'system.info',\n description: 'Server address and runtime versions. The address is what NapCat should use in reverse mode.',\n risk: 'read',\n input: z.object({}),\n handler: () => ({\n baseUrl: this.home.server.baseUrl,\n node: process.version,\n platform: process.platform,\n uptime: Math.round(process.uptime()),\n }),\n })\n\n yield actions.define({\n name: 'logs.recent',\n description: 'Recent log lines of Mushin.js itself, oldest first, each with a source tag. Lines written inside a bot are in bots.logs instead.',\n risk: 'read',\n input: z.object({\n limit: z.number().int().min(1).max(1000).optional(),\n tag: z.enum(LOG_TAGS).optional().describe('Only lines from this source: plugin, bot, adapter, flow, provider, agent, system or other'),\n }),\n handler: ({ limit, tag }) => this.home.logger.buffer\n .filter((message) => !this.isBotLog(message) && (!tag || logTag(message.name) === tag))\n .slice(-(limit ?? 200))\n .map(formatLog),\n })\n }\n}\n\nasync function readBody(req: { text(): Promise<string> }) {\n const text = await req.text()\n if (!text) return undefined\n try {\n return JSON.parse(text)\n } catch {\n throw new MushinError('invalid_input', 'Request body must be JSON')\n }\n}\n\nexport default Console\n"],"mappings":"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"}
|
package/dist/static.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"static.js","names":[],"sources":["../src/static.ts"],"sourcesContent":["import { readFile, stat } from 'node:fs/promises'\
|
|
1
|
+
{"version":3,"file":"static.js","names":[],"sources":["../src/static.ts"],"sourcesContent":["import { readFile, stat } from 'node:fs/promises'\nimport { createRequire } from 'node:module'\nimport { dirname, extname, join, normalize, sep } from 'node:path'\n\nconst types: Record<string, string> = {\n '.html': 'text/html; charset=utf-8',\n '.js': 'text/javascript; charset=utf-8',\n '.mjs': 'text/javascript; charset=utf-8',\n '.css': 'text/css; charset=utf-8',\n '.json': 'application/json',\n '.svg': 'image/svg+xml',\n '.png': 'image/png',\n '.jpg': 'image/jpeg',\n '.webp': 'image/webp',\n '.ico': 'image/x-icon',\n '.woff2': 'font/woff2',\n '.woff': 'font/woff',\n '.txt': 'text/plain; charset=utf-8',\n '.map': 'application/json',\n}\n\nexport function dashboardRoot() {\n try {\n const require = createRequire(import.meta.url)\n return join(dirname(require.resolve('@mushinjs/dashboard/package.json')), 'dist')\n } catch {\n return undefined\n }\n}\n\nexport interface StaticFile {\n body: Buffer\n type: string\n immutable: boolean\n}\n\n/** Resolves a request path to a file under `root`; unknown paths fall back to index.html for the SPA router. */\nexport async function readStatic(root: string, path: string): Promise<StaticFile | undefined> {\n let decoded: string\n try {\n decoded = decodeURIComponent(path)\n } catch {\n return\n }\n const relative = normalize(decoded).replace(/^([/\\\\])+/, '')\n const file = join(root, relative)\n if (file !== root && !file.startsWith(root + sep)) return\n try {\n if ((await stat(file)).isFile()) {\n return {\n body: await readFile(file),\n type: types[extname(file)] ?? 'application/octet-stream',\n immutable: relative.startsWith('assets' + sep) || relative.startsWith('assets/'),\n }\n }\n } catch {}\n if (extname(relative)) return\n try {\n return { body: await readFile(join(root, 'index.html')), type: types['.html'], immutable: false }\n } catch {}\n}\n\nexport const MISSING_BUILD_PAGE = `<!doctype html>\n<html lang=\"en\"><head><meta charset=\"utf-8\"><title>Mushin.js</title>\n<style>body{font-family:system-ui,sans-serif;max-width:560px;margin:80px auto;padding:0 16px;line-height:1.6;color:#222}code{background:#f2f2f2;padding:2px 6px;border-radius:4px}</style>\n</head><body><h1>Mushin.js is running</h1>\n<p>The dashboard has not been built yet. Run <code>pnpm build</code> in the Mushin.js folder, or start the dev server with <code>pnpm dev</code>.</p>\n</body></html>`\n"],"mappings":";;;;AAIA,MAAM,QAAgC;CACpC,SAAS;CACT,OAAO;CACP,QAAQ;CACR,QAAQ;CACR,SAAS;CACT,QAAQ;CACR,QAAQ;CACR,QAAQ;CACR,SAAS;CACT,QAAQ;CACR,UAAU;CACV,SAAS;CACT,QAAQ;CACR,QAAQ;AACV;AAEA,SAAgB,gBAAgB;CAC9B,IAAI;EACF,MAAM,UAAU,cAAc,YAAY,GAAG;EAC7C,OAAO,KAAK,QAAQ,QAAQ,QAAQ,kCAAkC,CAAC,GAAG,MAAM;CAClF,QAAQ;EACN;CACF;AACF;;AASA,eAAsB,WAAW,MAAc,MAA+C;CAC5F,IAAI;CACJ,IAAI;EACF,UAAU,mBAAmB,IAAI;CACnC,QAAQ;EACN;CACF;CACA,MAAM,WAAW,UAAU,OAAO,CAAC,CAAC,QAAQ,aAAa,EAAE;CAC3D,MAAM,OAAO,KAAK,MAAM,QAAQ;CAChC,IAAI,SAAS,QAAQ,CAAC,KAAK,WAAW,OAAO,GAAG,GAAG;CACnD,IAAI;EACF,KAAK,MAAM,KAAK,IAAI,EAAA,CAAG,OAAO,GAC5B,OAAO;GACL,MAAM,MAAM,SAAS,IAAI;GACzB,MAAM,MAAM,QAAQ,IAAI,MAAM;GAC9B,WAAW,SAAS,WAAW,WAAW,GAAG,KAAK,SAAS,WAAW,SAAS;EACjF;CAEJ,QAAQ,CAAC;CACT,IAAI,QAAQ,QAAQ,GAAG;CACvB,IAAI;EACF,OAAO;GAAE,MAAM,MAAM,SAAS,KAAK,MAAM,YAAY,CAAC;GAAG,MAAM,MAAM;GAAU,WAAW;EAAM;CAClG,QAAQ,CAAC;AACX;AAEA,MAAa,qBAAqB"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mushinjs/plugin-console",
|
|
3
|
-
"version": "0.2.0-next.
|
|
3
|
+
"version": "0.2.0-next.20261113105814",
|
|
4
4
|
"description": "HTTP API, WebSocket and dashboard hosting",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -56,15 +56,15 @@
|
|
|
56
56
|
}
|
|
57
57
|
},
|
|
58
58
|
"dependencies": {
|
|
59
|
-
"@mushinjs/core": "0.2.0-next.
|
|
60
|
-
"@mushinjs/plugin-storage": "0.2.0-next.
|
|
61
|
-
"@mushinjs/plugin-bots": "0.2.0-next.
|
|
62
|
-
"@mushinjs/plugin-agent": "0.2.0-next.
|
|
59
|
+
"@mushinjs/core": "0.2.0-next.20261113105814",
|
|
60
|
+
"@mushinjs/plugin-storage": "0.2.0-next.20261113105814",
|
|
61
|
+
"@mushinjs/plugin-bots": "0.2.0-next.20261113105814",
|
|
62
|
+
"@mushinjs/plugin-agent": "0.2.0-next.20261113105814",
|
|
63
63
|
"@cordisjs/plugin-server": "1.7.0",
|
|
64
64
|
"ws": "^8.22.0",
|
|
65
|
-
"@mushinjs/dashboard": "0.2.0-next.
|
|
66
|
-
"@mushinjs/plugin-providers": "0.2.0-next.
|
|
67
|
-
"@mushinjs/plugin-flows": "0.2.0-next.
|
|
68
|
-
"@mushinjs/plugin-stats": "0.2.0-next.
|
|
65
|
+
"@mushinjs/dashboard": "0.2.0-next.20261113105814",
|
|
66
|
+
"@mushinjs/plugin-providers": "0.2.0-next.20261113105814",
|
|
67
|
+
"@mushinjs/plugin-flows": "0.2.0-next.20261113105814",
|
|
68
|
+
"@mushinjs/plugin-stats": "0.2.0-next.20261113105814"
|
|
69
69
|
}
|
|
70
70
|
}
|