dsh-remote-bridge 2.5.2 → 2.6.0
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.en.md +8 -7
- package/README.md +55 -9
- package/docs/api/asyncapi.yaml +1 -1
- package/docs/api/openapi.yaml +141 -2
- package/lib/client.cjs +57 -53
- package/lib/index.d.ts +138 -2
- package/lib/index.js +603 -56
- package/package.json +1 -1
package/lib/index.d.ts
CHANGED
|
@@ -242,7 +242,39 @@ declare class WorkspaceDatabase {
|
|
|
242
242
|
* 干净的办法制造这种行,而它不该走生产代码路径 —— 所以显式开一个测试专用入口,
|
|
243
243
|
* 并起一个自见其明的名字,免得有人把它当成正常 API。
|
|
244
244
|
*/
|
|
245
|
+
/**
|
|
246
|
+
* 仅供测试:把某台设备的「最后使用时间/创建时间」拨到过去。
|
|
247
|
+
*
|
|
248
|
+
* 生产路径没法造出「30 天没动过」的设备(认证一次就刷新 `last_seen_at`),而
|
|
249
|
+
* `pruneInactiveDevices` 的核心语义恰恰只在这种时间跨度上才看得见。与
|
|
250
|
+
* `markRevokedForTest` 同样的理由:显式开一个自见其明的测试入口,别让人误当 API。
|
|
251
|
+
*/
|
|
252
|
+
backdateDeviceForTest(id: string, options: {
|
|
253
|
+
lastSeenAt?: number;
|
|
254
|
+
createdAt?: number;
|
|
255
|
+
}): void;
|
|
245
256
|
markRevokedForTest(id: string): void;
|
|
257
|
+
/**
|
|
258
|
+
* 按「最后使用时间」批量清掉长期没动过的设备。
|
|
259
|
+
*
|
|
260
|
+
* 「最后使用」取 `last_seen_at`(每次令牌认证成功都会刷新),从没认证过的设备退回
|
|
261
|
+
* `created_at` —— 否则一条配好就再没开过的设备会永远躲过清理,而那恰恰是用户最想
|
|
262
|
+
* 一键清掉的那类(换手机、临时配对、旧测试探针)。
|
|
263
|
+
*
|
|
264
|
+
* 与 `purgeRevokedDevices` 的区别:后者清的是历史遗留的「标记式撤销」脏行,这里清的是
|
|
265
|
+
* **仍然有效、但已经不用了**的设备 —— 用户的原话是「一键清理多少天未使用设备」。
|
|
266
|
+
*
|
|
267
|
+
* 语义边界(故意这么定):
|
|
268
|
+
* - 只删设备行 + 级联的 `device_roots`,不碰审计(审计的 `actor_id` 不是外键);
|
|
269
|
+
* - **不删本机正在用的那台**(`keepDeviceId`):调用方就是它,删掉自己会让当前
|
|
270
|
+
* 客户端立刻 401,看起来像「点一下清理把手机踢下线了」;
|
|
271
|
+
* - 天数被夹在 1..3650,避免 `0` 或负数变成「清空所有设备」;
|
|
272
|
+
* - 返回真正删掉的条数,0 也是一次合法结果。
|
|
273
|
+
*/
|
|
274
|
+
pruneInactiveDevices(olderThanDays: number, options?: {
|
|
275
|
+
keepDeviceId?: string;
|
|
276
|
+
requestId?: string;
|
|
277
|
+
}): number;
|
|
246
278
|
purgeRevokedDevices(): number;
|
|
247
279
|
/**
|
|
248
280
|
* 删掉早于 `cutoffMs` 的幂等回放记录,返回删除行数。
|
|
@@ -303,15 +335,56 @@ declare class WorkspaceDatabase {
|
|
|
303
335
|
private hasRootColumn;
|
|
304
336
|
}
|
|
305
337
|
//#endregion
|
|
306
|
-
//#region src/host/settings-
|
|
338
|
+
//#region src/host/settings-namespace.d.ts
|
|
339
|
+
/**
|
|
340
|
+
* 内核 `settings.describe()` 的命名空间视图(DSH 0.2.x)。
|
|
341
|
+
*
|
|
342
|
+
* 字段比 2.6.0 之前多:`autoGenerate` / `applies` / `secrets` 都原样透出,
|
|
343
|
+
* 手机端要靠 `secrets[].path` 判断「哪些字段是密钥位、只读」,比按键名猜可靠。
|
|
344
|
+
*/
|
|
307
345
|
interface SettingsNamespaceView {
|
|
308
346
|
ns: string;
|
|
347
|
+
autoGenerate?: unknown;
|
|
309
348
|
schema?: unknown;
|
|
310
349
|
value: unknown;
|
|
311
350
|
base?: unknown;
|
|
312
351
|
user?: unknown;
|
|
352
|
+
applies?: unknown;
|
|
353
|
+
secrets?: SettingsNamespaceSecret[];
|
|
313
354
|
revision: number;
|
|
314
355
|
}
|
|
356
|
+
/** 一个密钥位的路径与「是否已设置」;**只有路径,没有值**。 */
|
|
357
|
+
interface SettingsNamespaceSecret {
|
|
358
|
+
path: string[];
|
|
359
|
+
set: boolean;
|
|
360
|
+
}
|
|
361
|
+
type SettingsNamespaceReason = 'HOST_READONLY' | 'HIGH_RISK_NAMESPACE';
|
|
362
|
+
/** 手机端看到的单个命名空间:内核视图 + 能不能写 + 为什么不能写。 */
|
|
363
|
+
interface SettingsNamespaceEntry {
|
|
364
|
+
ns: string;
|
|
365
|
+
revision: number;
|
|
366
|
+
schema?: unknown;
|
|
367
|
+
value: unknown;
|
|
368
|
+
base?: unknown;
|
|
369
|
+
user?: unknown;
|
|
370
|
+
autoGenerate?: unknown;
|
|
371
|
+
applies?: unknown;
|
|
372
|
+
secrets?: SettingsNamespaceSecret[];
|
|
373
|
+
writable: boolean;
|
|
374
|
+
reason?: SettingsNamespaceReason;
|
|
375
|
+
}
|
|
376
|
+
/**
|
|
377
|
+
* `GET /api/v1/settings/namespaces` 的响应。
|
|
378
|
+
*
|
|
379
|
+
* 刻意**不**透出内核的 `hasDocument`:它对手机端没有决策价值,而响应多一个字段
|
|
380
|
+
* 就等于契约多一个字段(另一侧已按 `{writable, namespaces}` 定稿)。
|
|
381
|
+
*/
|
|
382
|
+
interface SettingsNamespacesView {
|
|
383
|
+
writable: boolean;
|
|
384
|
+
namespaces: SettingsNamespaceEntry[];
|
|
385
|
+
}
|
|
386
|
+
//#endregion
|
|
387
|
+
//#region src/host/settings-adapter.d.ts
|
|
315
388
|
interface ConfigurableProvider {
|
|
316
389
|
provider: string;
|
|
317
390
|
displayName: string;
|
|
@@ -476,7 +549,31 @@ interface CustomProviderCapability {
|
|
|
476
549
|
}
|
|
477
550
|
declare class DshSettingsAdapter {
|
|
478
551
|
private readonly api;
|
|
479
|
-
|
|
552
|
+
private readonly options;
|
|
553
|
+
/**
|
|
554
|
+
* @param options.writeNamespaces 额外允许手机端写入的设置命名空间(精确 ns 或
|
|
555
|
+
* `前缀*`),与内置策略(`ui-*` / 以 `locale` 结尾)叠加;默认为空。
|
|
556
|
+
*/
|
|
557
|
+
constructor(api: SettingsApiProxy, options?: {
|
|
558
|
+
writeNamespaces?: readonly string[];
|
|
559
|
+
});
|
|
560
|
+
/**
|
|
561
|
+
* 列出全部设置命名空间(通用设置面的读侧)。
|
|
562
|
+
*
|
|
563
|
+
* 与 `listProviders()` 的区别:它不理解任何 provider 语义,原样透出内核按插件
|
|
564
|
+
* 划分的命名空间,由手机端按 `schema` 自己长表单 —— 新装插件不必等 App 发版。
|
|
565
|
+
*/
|
|
566
|
+
describeNamespaces(): Promise<SettingsNamespacesView>;
|
|
567
|
+
/**
|
|
568
|
+
* 按路径改一个命名空间(通用设置面的写侧)。
|
|
569
|
+
*
|
|
570
|
+
* 校验顺序是刻意的:ns 策略 → ops 结构 → 内核 describe(host 可写性)→ mutate。
|
|
571
|
+
* 先验策略既省一次内核往返,也保证「高危命名空间」无论 ops 怎么错都先得到 403。
|
|
572
|
+
*/
|
|
573
|
+
mutateNamespace(ns: string, ops: unknown, expectedRevision?: number): Promise<SettingsNamespaceEntry>;
|
|
574
|
+
private get writeNamespaces();
|
|
575
|
+
/** 内核 settings 服务缺失时给 503(而不是把 `TypeError` 漏成 500)。 */
|
|
576
|
+
private settingsService;
|
|
480
577
|
listProviders(): Promise<{
|
|
481
578
|
writable: boolean;
|
|
482
579
|
revisionByNamespace: Record<string, number>;
|
|
@@ -2281,6 +2378,37 @@ declare class ApiRouter {
|
|
|
2281
2378
|
handle(req: IncomingMessage, res: ServerResponse, stripPrefix?: string): Promise<void>;
|
|
2282
2379
|
private dispatch;
|
|
2283
2380
|
private dispatchManagement;
|
|
2381
|
+
/**
|
|
2382
|
+
* 打开隧道并(可选)顺带产出配置文本 —— HTTP `POST /manage/cloudflare/create` 与
|
|
2383
|
+
* IM `/tunnel` 命令**共用**这一条实现。
|
|
2384
|
+
*
|
|
2385
|
+
* 抽出来的原因:两条入口的前半段完全一致(确保 cloudflared → startQuick → 等随机域名 →
|
|
2386
|
+
* 建配置文本),差别只在「谁在调用」——HTTP 侧要先把请求体读干净(流会随时间关闭),
|
|
2387
|
+
* 命令侧没有请求体。复制一份会让「一键出令牌」在两条路径上慢慢漂移。
|
|
2388
|
+
*
|
|
2389
|
+
* @param createBody - 已解析的请求体(HTTP 侧)或 `{ configText: true }`(命令侧)。
|
|
2390
|
+
* @param requestId - 审计与配对记录共用的请求 ID。
|
|
2391
|
+
* @param actorLabel - 审计里的操作者标签:`loopback-admin`(本机管理面)或 `im-command`。
|
|
2392
|
+
* @returns 合并了「等到地址」的 state,以及(wantConfig 且拿到地址时)配置文本。
|
|
2393
|
+
*/
|
|
2394
|
+
private createTunnel;
|
|
2395
|
+
/**
|
|
2396
|
+
* IM `/tunnel` 命令的宿主侧入口:开隧道并返回一行可粘贴的配置文本。
|
|
2397
|
+
*
|
|
2398
|
+
* 与 HTTP 路径的两点差别:
|
|
2399
|
+
*
|
|
2400
|
+
* 1. **隧道已在运行时直接复用当前地址**,不重复 spawn(第二次 `startQuick` 会撞
|
|
2401
|
+
* `TUNNEL_ALREADY_RUNNING`,而用户在 IM 里连发两条是很常见的)。
|
|
2402
|
+
* 2. 配置文本的 endpoint 只用隧道地址,设备名缺省由 `buildConfigText` 决定 —— 命令
|
|
2403
|
+
* 不接受参数,所以没有 `endpointLabel` / `rootIds` 的透传面。
|
|
2404
|
+
*
|
|
2405
|
+
* @throws 当 12 秒内没拿到公网地址,或 cloudflared 下载失败时。
|
|
2406
|
+
*/
|
|
2407
|
+
openTunnelForCommand(): Promise<{
|
|
2408
|
+
url: string;
|
|
2409
|
+
text: string;
|
|
2410
|
+
deviceName: string;
|
|
2411
|
+
}>;
|
|
2284
2412
|
/**
|
|
2285
2413
|
* 隧道状态视图。
|
|
2286
2414
|
*
|
|
@@ -2560,6 +2688,14 @@ interface Config {
|
|
|
2560
2688
|
port?: number;
|
|
2561
2689
|
remoteHost?: string;
|
|
2562
2690
|
maxUploadBytes?: number;
|
|
2691
|
+
/**
|
|
2692
|
+
* 额外允许手机端写入的设置命名空间(默认空)。
|
|
2693
|
+
*
|
|
2694
|
+
* 精确 ns(`ui-chat`)或通配前缀(`llm-*`)。与内置策略叠加:`ui-*` 与以
|
|
2695
|
+
* `locale` 结尾的命名空间本来就可写;其余默认只读,因为改模型/权限等于把
|
|
2696
|
+
* 遥控器交出去(理由见 docs/project/SECURITY.md)。
|
|
2697
|
+
*/
|
|
2698
|
+
settingsWriteNamespaces?: readonly string[];
|
|
2563
2699
|
}
|
|
2564
2700
|
interface HostContext extends Dsh02HostServices {
|
|
2565
2701
|
webServer: Parameters<typeof attachEmbeddedRoutes>[0];
|