dsh-remote-control 1.1.0 → 1.2.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/cordis.patch.yml +3 -5
- package/dist/bundle/client.cjs +415 -77
- package/dist/bundle/index.js +276 -535
- package/package.json +5 -4
package/dist/bundle/index.js
CHANGED
|
@@ -7486,10 +7486,10 @@ var require_segments = __commonJS({
|
|
|
7486
7486
|
const segs = getSegmentsFromString(data, Utils.isKanjiModeEnabled());
|
|
7487
7487
|
const nodes = buildNodes(segs);
|
|
7488
7488
|
const graph = buildGraph(nodes, version2);
|
|
7489
|
-
const
|
|
7489
|
+
const path3 = dijkstra.find_path(graph.map, "start", "end");
|
|
7490
7490
|
const optimizedSegs = [];
|
|
7491
|
-
for (let i = 1; i <
|
|
7492
|
-
optimizedSegs.push(graph.table[
|
|
7491
|
+
for (let i = 1; i < path3.length - 1; i++) {
|
|
7492
|
+
optimizedSegs.push(graph.table[path3[i]].node);
|
|
7493
7493
|
}
|
|
7494
7494
|
return exports.fromArray(mergeSegments(optimizedSegs));
|
|
7495
7495
|
};
|
|
@@ -9966,7 +9966,7 @@ var require_png2 = __commonJS({
|
|
|
9966
9966
|
});
|
|
9967
9967
|
png.pack();
|
|
9968
9968
|
};
|
|
9969
|
-
exports.renderToFile = function renderToFile(
|
|
9969
|
+
exports.renderToFile = function renderToFile(path3, qrData, options, cb) {
|
|
9970
9970
|
if (typeof cb === "undefined") {
|
|
9971
9971
|
cb = options;
|
|
9972
9972
|
options = void 0;
|
|
@@ -9977,7 +9977,7 @@ var require_png2 = __commonJS({
|
|
|
9977
9977
|
called = true;
|
|
9978
9978
|
cb.apply(null, args);
|
|
9979
9979
|
};
|
|
9980
|
-
const stream = fs.createWriteStream(
|
|
9980
|
+
const stream = fs.createWriteStream(path3);
|
|
9981
9981
|
stream.on("error", done);
|
|
9982
9982
|
stream.on("close", done);
|
|
9983
9983
|
exports.renderToFileStream(stream, qrData, options);
|
|
@@ -10039,14 +10039,14 @@ var require_utf8 = __commonJS({
|
|
|
10039
10039
|
}
|
|
10040
10040
|
return output2;
|
|
10041
10041
|
};
|
|
10042
|
-
exports.renderToFile = function renderToFile(
|
|
10042
|
+
exports.renderToFile = function renderToFile(path3, qrData, options, cb) {
|
|
10043
10043
|
if (typeof cb === "undefined") {
|
|
10044
10044
|
cb = options;
|
|
10045
10045
|
options = void 0;
|
|
10046
10046
|
}
|
|
10047
10047
|
const fs = __require("fs");
|
|
10048
10048
|
const utf82 = exports.render(qrData, options);
|
|
10049
|
-
fs.writeFile(
|
|
10049
|
+
fs.writeFile(path3, utf82, cb);
|
|
10050
10050
|
};
|
|
10051
10051
|
}
|
|
10052
10052
|
});
|
|
@@ -10167,7 +10167,7 @@ var require_svg_tag = __commonJS({
|
|
|
10167
10167
|
return str;
|
|
10168
10168
|
}
|
|
10169
10169
|
function qrToPath(data, size, margin) {
|
|
10170
|
-
let
|
|
10170
|
+
let path3 = "";
|
|
10171
10171
|
let moveBy = 0;
|
|
10172
10172
|
let newRow = false;
|
|
10173
10173
|
let lineLength = 0;
|
|
@@ -10178,19 +10178,19 @@ var require_svg_tag = __commonJS({
|
|
|
10178
10178
|
if (data[i]) {
|
|
10179
10179
|
lineLength++;
|
|
10180
10180
|
if (!(i > 0 && col > 0 && data[i - 1])) {
|
|
10181
|
-
|
|
10181
|
+
path3 += newRow ? svgCmd("M", col + margin, 0.5 + row + margin) : svgCmd("m", moveBy, 0);
|
|
10182
10182
|
moveBy = 0;
|
|
10183
10183
|
newRow = false;
|
|
10184
10184
|
}
|
|
10185
10185
|
if (!(col + 1 < size && data[i + 1])) {
|
|
10186
|
-
|
|
10186
|
+
path3 += svgCmd("h", lineLength);
|
|
10187
10187
|
lineLength = 0;
|
|
10188
10188
|
}
|
|
10189
10189
|
} else {
|
|
10190
10190
|
moveBy++;
|
|
10191
10191
|
}
|
|
10192
10192
|
}
|
|
10193
|
-
return
|
|
10193
|
+
return path3;
|
|
10194
10194
|
}
|
|
10195
10195
|
exports.render = function render(qrData, options, cb) {
|
|
10196
10196
|
const opts = Utils.getOptions(options);
|
|
@@ -10198,10 +10198,10 @@ var require_svg_tag = __commonJS({
|
|
|
10198
10198
|
const data = qrData.modules.data;
|
|
10199
10199
|
const qrcodesize = size + opts.margin * 2;
|
|
10200
10200
|
const bg = !opts.color.light.a ? "" : "<path " + getColorAttrib(opts.color.light, "fill") + ' d="M0 0h' + qrcodesize + "v" + qrcodesize + 'H0z"/>';
|
|
10201
|
-
const
|
|
10201
|
+
const path3 = "<path " + getColorAttrib(opts.color.dark, "stroke") + ' d="' + qrToPath(data, size, opts.margin) + '"/>';
|
|
10202
10202
|
const viewBox = 'viewBox="0 0 ' + qrcodesize + " " + qrcodesize + '"';
|
|
10203
10203
|
const width = !opts.width ? "" : 'width="' + opts.width + '" height="' + opts.width + '" ';
|
|
10204
|
-
const svgTag = '<svg xmlns="http://www.w3.org/2000/svg" ' + width + viewBox + ' shape-rendering="crispEdges">' + bg +
|
|
10204
|
+
const svgTag = '<svg xmlns="http://www.w3.org/2000/svg" ' + width + viewBox + ' shape-rendering="crispEdges">' + bg + path3 + "</svg>\n";
|
|
10205
10205
|
if (typeof cb === "function") {
|
|
10206
10206
|
cb(null, svgTag);
|
|
10207
10207
|
}
|
|
@@ -10215,7 +10215,7 @@ var require_svg = __commonJS({
|
|
|
10215
10215
|
"../../node_modules/.pnpm/qrcode@1.5.4/node_modules/qrcode/lib/renderer/svg.js"(exports) {
|
|
10216
10216
|
var svgTagRenderer = require_svg_tag();
|
|
10217
10217
|
exports.render = svgTagRenderer.render;
|
|
10218
|
-
exports.renderToFile = function renderToFile(
|
|
10218
|
+
exports.renderToFile = function renderToFile(path3, qrData, options, cb) {
|
|
10219
10219
|
if (typeof cb === "undefined") {
|
|
10220
10220
|
cb = options;
|
|
10221
10221
|
options = void 0;
|
|
@@ -10223,7 +10223,7 @@ var require_svg = __commonJS({
|
|
|
10223
10223
|
const fs = __require("fs");
|
|
10224
10224
|
const svgTag = exports.render(qrData, options);
|
|
10225
10225
|
const xmlStr = '<?xml version="1.0" encoding="utf-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">' + svgTag;
|
|
10226
|
-
fs.writeFile(
|
|
10226
|
+
fs.writeFile(path3, xmlStr, cb);
|
|
10227
10227
|
};
|
|
10228
10228
|
}
|
|
10229
10229
|
});
|
|
@@ -10285,7 +10285,7 @@ var require_canvas = __commonJS({
|
|
|
10285
10285
|
var require_browser = __commonJS({
|
|
10286
10286
|
"../../node_modules/.pnpm/qrcode@1.5.4/node_modules/qrcode/lib/browser.js"(exports) {
|
|
10287
10287
|
var canPromise = require_can_promise();
|
|
10288
|
-
var
|
|
10288
|
+
var QRCode2 = require_qrcode();
|
|
10289
10289
|
var CanvasRenderer = require_canvas();
|
|
10290
10290
|
var SvgRenderer = require_svg_tag();
|
|
10291
10291
|
function renderCanvas(renderFunc, canvas, text, opts, cb) {
|
|
@@ -10328,7 +10328,7 @@ var require_browser = __commonJS({
|
|
|
10328
10328
|
}
|
|
10329
10329
|
return new Promise(function(resolve, reject) {
|
|
10330
10330
|
try {
|
|
10331
|
-
const data =
|
|
10331
|
+
const data = QRCode2.create(text, opts);
|
|
10332
10332
|
resolve(renderFunc(data, canvas, opts));
|
|
10333
10333
|
} catch (e) {
|
|
10334
10334
|
reject(e);
|
|
@@ -10336,13 +10336,13 @@ var require_browser = __commonJS({
|
|
|
10336
10336
|
});
|
|
10337
10337
|
}
|
|
10338
10338
|
try {
|
|
10339
|
-
const data =
|
|
10339
|
+
const data = QRCode2.create(text, opts);
|
|
10340
10340
|
cb(null, renderFunc(data, canvas, opts));
|
|
10341
10341
|
} catch (e) {
|
|
10342
10342
|
cb(e);
|
|
10343
10343
|
}
|
|
10344
10344
|
}
|
|
10345
|
-
exports.create =
|
|
10345
|
+
exports.create = QRCode2.create;
|
|
10346
10346
|
exports.toCanvas = renderCanvas.bind(null, CanvasRenderer.render);
|
|
10347
10347
|
exports.toDataURL = renderCanvas.bind(null, CanvasRenderer.renderToDataURL);
|
|
10348
10348
|
exports.toString = renderCanvas.bind(null, function(data, _, opts) {
|
|
@@ -10355,7 +10355,7 @@ var require_browser = __commonJS({
|
|
|
10355
10355
|
var require_server = __commonJS({
|
|
10356
10356
|
"../../node_modules/.pnpm/qrcode@1.5.4/node_modules/qrcode/lib/server.js"(exports) {
|
|
10357
10357
|
var canPromise = require_can_promise();
|
|
10358
|
-
var
|
|
10358
|
+
var QRCode2 = require_qrcode();
|
|
10359
10359
|
var PngRenderer = require_png2();
|
|
10360
10360
|
var Utf8Renderer = require_utf8();
|
|
10361
10361
|
var TerminalRenderer = require_terminal2();
|
|
@@ -10381,8 +10381,8 @@ var require_server = __commonJS({
|
|
|
10381
10381
|
cb
|
|
10382
10382
|
};
|
|
10383
10383
|
}
|
|
10384
|
-
function getTypeFromFilename(
|
|
10385
|
-
return
|
|
10384
|
+
function getTypeFromFilename(path3) {
|
|
10385
|
+
return path3.slice((path3.lastIndexOf(".") - 1 >>> 0) + 2).toLowerCase();
|
|
10386
10386
|
}
|
|
10387
10387
|
function getRendererFromType(type) {
|
|
10388
10388
|
switch (type) {
|
|
@@ -10412,7 +10412,7 @@ var require_server = __commonJS({
|
|
|
10412
10412
|
if (!params.cb) {
|
|
10413
10413
|
return new Promise(function(resolve, reject) {
|
|
10414
10414
|
try {
|
|
10415
|
-
const data =
|
|
10415
|
+
const data = QRCode2.create(text, params.opts);
|
|
10416
10416
|
return renderFunc(data, params.opts, function(err, data2) {
|
|
10417
10417
|
return err ? reject(err) : resolve(data2);
|
|
10418
10418
|
});
|
|
@@ -10422,13 +10422,13 @@ var require_server = __commonJS({
|
|
|
10422
10422
|
});
|
|
10423
10423
|
}
|
|
10424
10424
|
try {
|
|
10425
|
-
const data =
|
|
10425
|
+
const data = QRCode2.create(text, params.opts);
|
|
10426
10426
|
return renderFunc(data, params.opts, params.cb);
|
|
10427
10427
|
} catch (e) {
|
|
10428
10428
|
params.cb(e);
|
|
10429
10429
|
}
|
|
10430
10430
|
}
|
|
10431
|
-
exports.create =
|
|
10431
|
+
exports.create = QRCode2.create;
|
|
10432
10432
|
exports.toCanvas = require_browser().toCanvas;
|
|
10433
10433
|
exports.toString = function toString(text, opts, cb) {
|
|
10434
10434
|
const params = checkParams(text, opts, cb);
|
|
@@ -10446,17 +10446,17 @@ var require_server = __commonJS({
|
|
|
10446
10446
|
const renderer = getRendererFromType(params.opts.type);
|
|
10447
10447
|
return render(renderer.renderToBuffer, text, params);
|
|
10448
10448
|
};
|
|
10449
|
-
exports.toFile = function toFile(
|
|
10450
|
-
if (typeof
|
|
10449
|
+
exports.toFile = function toFile(path3, text, opts, cb) {
|
|
10450
|
+
if (typeof path3 !== "string" || !(typeof text === "string" || typeof text === "object")) {
|
|
10451
10451
|
throw new Error("Invalid argument");
|
|
10452
10452
|
}
|
|
10453
10453
|
if (arguments.length < 3 && !canPromise()) {
|
|
10454
10454
|
throw new Error("Too few arguments provided");
|
|
10455
10455
|
}
|
|
10456
10456
|
const params = checkParams(text, opts, cb);
|
|
10457
|
-
const type = params.opts.type || getTypeFromFilename(
|
|
10457
|
+
const type = params.opts.type || getTypeFromFilename(path3);
|
|
10458
10458
|
const renderer = getRendererFromType(type);
|
|
10459
|
-
const renderToFile = renderer.renderToFile.bind(null,
|
|
10459
|
+
const renderToFile = renderer.renderToFile.bind(null, path3);
|
|
10460
10460
|
return render(renderToFile, text, params);
|
|
10461
10461
|
};
|
|
10462
10462
|
exports.toFileStream = function toFileStream(stream, text, opts) {
|
|
@@ -10655,7 +10655,6 @@ var init_mock_kernel = __esm({
|
|
|
10655
10655
|
});
|
|
10656
10656
|
|
|
10657
10657
|
// src/index.ts
|
|
10658
|
-
import { execFile } from "node:child_process";
|
|
10659
10658
|
import { randomBytes as randomBytes4 } from "node:crypto";
|
|
10660
10659
|
|
|
10661
10660
|
// ../../node_modules/.pnpm/dsh-remote-wire@1.0.0/node_modules/dsh-remote-wire/dist/src/crypto/bytes.js
|
|
@@ -11594,10 +11593,10 @@ function mergeDefs(...defs) {
|
|
|
11594
11593
|
function cloneDef(schema) {
|
|
11595
11594
|
return mergeDefs(schema._zod.def);
|
|
11596
11595
|
}
|
|
11597
|
-
function getElementAtPath(obj,
|
|
11598
|
-
if (!
|
|
11596
|
+
function getElementAtPath(obj, path3) {
|
|
11597
|
+
if (!path3)
|
|
11599
11598
|
return obj;
|
|
11600
|
-
return
|
|
11599
|
+
return path3.reduce((acc, key) => acc?.[key], obj);
|
|
11601
11600
|
}
|
|
11602
11601
|
function promiseAllObject(promisesObj) {
|
|
11603
11602
|
const keys = Object.keys(promisesObj);
|
|
@@ -11937,11 +11936,11 @@ function explicitlyAborted(x, startIndex = 0) {
|
|
|
11937
11936
|
}
|
|
11938
11937
|
return false;
|
|
11939
11938
|
}
|
|
11940
|
-
function prefixIssues(
|
|
11939
|
+
function prefixIssues(path3, issues) {
|
|
11941
11940
|
return issues.map((iss) => {
|
|
11942
11941
|
var _a3;
|
|
11943
11942
|
(_a3 = iss).path ?? (_a3.path = []);
|
|
11944
|
-
iss.path.unshift(
|
|
11943
|
+
iss.path.unshift(path3);
|
|
11945
11944
|
return iss;
|
|
11946
11945
|
});
|
|
11947
11946
|
}
|
|
@@ -12391,16 +12390,16 @@ function flattenError(error62, mapper = (issue2) => issue2.message) {
|
|
|
12391
12390
|
}
|
|
12392
12391
|
function formatError(error62, mapper = (issue2) => issue2.message) {
|
|
12393
12392
|
const fieldErrors = { _errors: [] };
|
|
12394
|
-
const processError = (error63,
|
|
12393
|
+
const processError = (error63, path3 = []) => {
|
|
12395
12394
|
for (const issue2 of error63.issues) {
|
|
12396
12395
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
12397
|
-
issue2.errors.map((issues) => processError({ issues }, [...
|
|
12396
|
+
issue2.errors.map((issues) => processError({ issues }, [...path3, ...issue2.path]));
|
|
12398
12397
|
} else if (issue2.code === "invalid_key") {
|
|
12399
|
-
processError({ issues: issue2.issues }, [...
|
|
12398
|
+
processError({ issues: issue2.issues }, [...path3, ...issue2.path]);
|
|
12400
12399
|
} else if (issue2.code === "invalid_element") {
|
|
12401
|
-
processError({ issues: issue2.issues }, [...
|
|
12400
|
+
processError({ issues: issue2.issues }, [...path3, ...issue2.path]);
|
|
12402
12401
|
} else {
|
|
12403
|
-
const fullpath = [...
|
|
12402
|
+
const fullpath = [...path3, ...issue2.path];
|
|
12404
12403
|
if (fullpath.length === 0) {
|
|
12405
12404
|
fieldErrors._errors.push(mapper(issue2));
|
|
12406
12405
|
} else {
|
|
@@ -12439,17 +12438,17 @@ function formatError(error62, mapper = (issue2) => issue2.message) {
|
|
|
12439
12438
|
}
|
|
12440
12439
|
function treeifyError(error62, mapper = (issue2) => issue2.message) {
|
|
12441
12440
|
const result2 = { errors: [] };
|
|
12442
|
-
const processError = (error63,
|
|
12441
|
+
const processError = (error63, path3 = []) => {
|
|
12443
12442
|
var _a3;
|
|
12444
12443
|
for (const issue2 of error63.issues) {
|
|
12445
12444
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
12446
|
-
issue2.errors.map((issues) => processError({ issues }, [...
|
|
12445
|
+
issue2.errors.map((issues) => processError({ issues }, [...path3, ...issue2.path]));
|
|
12447
12446
|
} else if (issue2.code === "invalid_key") {
|
|
12448
|
-
processError({ issues: issue2.issues }, [...
|
|
12447
|
+
processError({ issues: issue2.issues }, [...path3, ...issue2.path]);
|
|
12449
12448
|
} else if (issue2.code === "invalid_element") {
|
|
12450
|
-
processError({ issues: issue2.issues }, [...
|
|
12449
|
+
processError({ issues: issue2.issues }, [...path3, ...issue2.path]);
|
|
12451
12450
|
} else {
|
|
12452
|
-
const fullpath = [...
|
|
12451
|
+
const fullpath = [...path3, ...issue2.path];
|
|
12453
12452
|
if (fullpath.length === 0) {
|
|
12454
12453
|
result2.errors.push(mapper(issue2));
|
|
12455
12454
|
continue;
|
|
@@ -12488,8 +12487,8 @@ function treeifyError(error62, mapper = (issue2) => issue2.message) {
|
|
|
12488
12487
|
}
|
|
12489
12488
|
function toDotPath(_path) {
|
|
12490
12489
|
const segs = [];
|
|
12491
|
-
const
|
|
12492
|
-
for (const seg of
|
|
12490
|
+
const path3 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
|
|
12491
|
+
for (const seg of path3) {
|
|
12493
12492
|
if (typeof seg === "number")
|
|
12494
12493
|
segs.push(`[${seg}]`);
|
|
12495
12494
|
else if (typeof seg === "symbol")
|
|
@@ -29591,13 +29590,13 @@ function resolveRef(ref, ctx) {
|
|
|
29591
29590
|
if (!ref.startsWith("#")) {
|
|
29592
29591
|
throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
|
|
29593
29592
|
}
|
|
29594
|
-
const
|
|
29595
|
-
if (
|
|
29593
|
+
const path3 = ref.slice(1).split("/").filter(Boolean);
|
|
29594
|
+
if (path3.length === 0) {
|
|
29596
29595
|
return ctx.rootSchema;
|
|
29597
29596
|
}
|
|
29598
29597
|
const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
|
|
29599
|
-
if (
|
|
29600
|
-
const key =
|
|
29598
|
+
if (path3[0] === defsKey) {
|
|
29599
|
+
const key = path3[1] === void 0 ? void 0 : decodeJSONPointerSegment(path3[1]);
|
|
29601
29600
|
if (!key || !ctx.defs[key]) {
|
|
29602
29601
|
throw new Error(`Reference not found: ${ref}`);
|
|
29603
29602
|
}
|
|
@@ -31135,7 +31134,7 @@ var HostRuntime = class {
|
|
|
31135
31134
|
this.clock.clearTimeout(this.mergeTimer);
|
|
31136
31135
|
this.mergeTimer = void 0;
|
|
31137
31136
|
}
|
|
31138
|
-
for (const [id
|
|
31137
|
+
for (const [id] of [...this.pending]) this.settle(id, void 0);
|
|
31139
31138
|
}
|
|
31140
31139
|
/** 手机发来的命令。`conversationId` 是配对通道 id,与载荷里的 sessionId 不是一回事(F3)。 */
|
|
31141
31140
|
async handleCommand(cmd, conversationId) {
|
|
@@ -32309,17 +32308,6 @@ function createServicesKernel(services, options) {
|
|
|
32309
32308
|
const status = agent?.status;
|
|
32310
32309
|
return typeof status === "string" ? status : "idle";
|
|
32311
32310
|
}
|
|
32312
|
-
function sessionWorkspace(sessionId) {
|
|
32313
|
-
const get = fn(services.sessions, "get");
|
|
32314
|
-
if (!get || sessionId === "") return void 0;
|
|
32315
|
-
try {
|
|
32316
|
-
const session = get.call(services.sessions, sessionId);
|
|
32317
|
-
const cwd = session?.header?.cwd ?? session?.cwd;
|
|
32318
|
-
return typeof cwd === "string" && cwd !== "" ? cwd : void 0;
|
|
32319
|
-
} catch {
|
|
32320
|
-
return void 0;
|
|
32321
|
-
}
|
|
32322
|
-
}
|
|
32323
32311
|
function toSummary(record2, archived) {
|
|
32324
32312
|
const cached2 = titleCache.get(record2.id);
|
|
32325
32313
|
const running = agentStatus(record2.id) === "running";
|
|
@@ -32746,7 +32734,6 @@ function createServicesKernel(services, options) {
|
|
|
32746
32734
|
subscribe,
|
|
32747
32735
|
attachInteractionSink,
|
|
32748
32736
|
ensureRunnable,
|
|
32749
|
-
sessionWorkspace,
|
|
32750
32737
|
// 与发指令/续跑走同一条读取路径:这里原来还有一份实现,而且是 `current()` 裸调用
|
|
32751
32738
|
// (不带 receiver;真要用 this 的服务方法会当场抛),两份迟早分叉。
|
|
32752
32739
|
modelSelection: currentSelection,
|
|
@@ -32761,9 +32748,6 @@ function createServicesKernel(services, options) {
|
|
|
32761
32748
|
// 新建会话只依赖这一条。和 historyFace 同理:真机上"新建没反应"第一件要分清的事
|
|
32762
32749
|
// 是宿主没有这个能力,还是我们没调对。
|
|
32763
32750
|
createFace: typeof fn(services.sessionController?.commands, "create") === "function" ? "commands.create" : `absent (keys=${shapeOf(services.sessionController)})`.slice(0, 120),
|
|
32764
|
-
// 二维码落点依赖这一条。它缺席时 PNG 会退回 ~/.dsh/,卡片上的路径会跟着变——
|
|
32765
|
-
// 用户看到"图不在工作区"时,第一件事就是看这个字段是不是 absent。
|
|
32766
|
-
workspaceFace: typeof fn(services.sessions, "get") === "function" ? "sessions.get" : `absent (keys=${shapeOf(services.sessions)})`.slice(0, 120),
|
|
32767
32751
|
agents: typeof services.agents === "object",
|
|
32768
32752
|
agentDefaultModel: typeof services.agentDefaultModel === "object",
|
|
32769
32753
|
workspaceRegistry: typeof services.workspaceRegistry === "object",
|
|
@@ -33049,7 +33033,6 @@ function createOneShotTimers(clock) {
|
|
|
33049
33033
|
|
|
33050
33034
|
// src/shell/status.ts
|
|
33051
33035
|
import { mkdirSync, renameSync, writeFileSync } from "node:fs";
|
|
33052
|
-
import { homedir } from "node:os";
|
|
33053
33036
|
import path from "node:path";
|
|
33054
33037
|
var StatusFile = class {
|
|
33055
33038
|
constructor(file2, clock, refreshMs = 3e3) {
|
|
@@ -33061,7 +33044,6 @@ var StatusFile = class {
|
|
|
33061
33044
|
clock;
|
|
33062
33045
|
refreshMs;
|
|
33063
33046
|
timer;
|
|
33064
|
-
lastWritten = 0;
|
|
33065
33047
|
write(snapshot) {
|
|
33066
33048
|
if (!this.file) return;
|
|
33067
33049
|
try {
|
|
@@ -33075,7 +33057,6 @@ var StatusFile = class {
|
|
|
33075
33057
|
}
|
|
33076
33058
|
);
|
|
33077
33059
|
renameSync(tmp, this.file);
|
|
33078
|
-
this.lastWritten = this.clock.now();
|
|
33079
33060
|
} catch {
|
|
33080
33061
|
}
|
|
33081
33062
|
}
|
|
@@ -33104,47 +33085,10 @@ var StatusFile = class {
|
|
|
33104
33085
|
this.timer = void 0;
|
|
33105
33086
|
}
|
|
33106
33087
|
}
|
|
33107
|
-
/** 此刻是否在按节拍刷新(测试与 status 自身的 `refreshing` 字段读它)。 */
|
|
33108
|
-
get refreshing() {
|
|
33109
|
-
return this.timer !== void 0;
|
|
33110
|
-
}
|
|
33111
|
-
get path() {
|
|
33112
|
-
return this.file;
|
|
33113
|
-
}
|
|
33114
33088
|
};
|
|
33115
|
-
function pairingImagePath(statusFile, workspaceDir) {
|
|
33116
|
-
if (!statusFile) return "";
|
|
33117
|
-
if (workspaceDir) return path.join(workspaceDir, ".dsh", "pairing-qr.png");
|
|
33118
|
-
return path.join(homedir(), ".dsh", "pairing-qr.png");
|
|
33119
|
-
}
|
|
33120
|
-
function writePrivateFile(file2, data) {
|
|
33121
|
-
if (!file2) return false;
|
|
33122
|
-
try {
|
|
33123
|
-
mkdirSync(path.dirname(file2), { recursive: true });
|
|
33124
|
-
writeFileSync(file2, data, { mode: 384 });
|
|
33125
|
-
return true;
|
|
33126
|
-
} catch {
|
|
33127
|
-
return false;
|
|
33128
|
-
}
|
|
33129
|
-
}
|
|
33130
|
-
|
|
33131
|
-
// src/shell/pairing-text.ts
|
|
33132
|
-
var PAIR_UNAVAILABLE_TEXT = "\u4E2D\u7EE7\u8FD8\u6CA1\u8FDE\u4E0A\uFF0C\u6682\u65F6\u65E0\u6CD5\u914D\u5BF9\u3002\u770B status.json \u7684 relayProblem\u3002";
|
|
33133
|
-
function pairingPairText(view) {
|
|
33134
|
-
const ttlSec = Math.max(0, Math.round((view.expiresAt - view.now) / 1e3));
|
|
33135
|
-
const lines = [`\u914D\u5BF9\u7801 ${view.token} \xB7 ${ttlSec} \u79D2\u5185\u6709\u6548 \xB7 \u4E00\u6B21\u6027`, "\u5C0F\u7A0B\u5E8F\u91CC\u626B\u7801\uFF0C\u6216\u624B\u8F93\u8FD9 6 \u4F4D\u6570\u5B57\u3002"];
|
|
33136
|
-
if (view.qrImage && view.imageFile) {
|
|
33137
|
-
lines.push("", `\u4E8C\u7EF4\u7801\u4F1A\u81EA\u52A8\u5F39\u51FA\u5230\u53F3\u680F\uFF1B\u6CA1\u5F39\u51FA\u5C31\u6253\u5F00\u8FD9\u5F20 PNG \u518D\u626B\uFF1A${view.imageFile}`);
|
|
33138
|
-
} else if (view.terminalQr) {
|
|
33139
|
-
lines.push("", view.terminalQr);
|
|
33140
|
-
}
|
|
33141
|
-
lines.push("", view.qr);
|
|
33142
|
-
if (!view.qrImage) lines.push("", "\uFF08\u5982\u9700\u56FE\u7247\u7248\uFF1A\u628A patch \u91CC\u7684 qrImage \u8BBE\u4E3A true\uFF09");
|
|
33143
|
-
return lines.join("\n");
|
|
33144
|
-
}
|
|
33145
33089
|
|
|
33146
33090
|
// src/shell/config.ts
|
|
33147
|
-
import { homedir
|
|
33091
|
+
import { homedir } from "node:os";
|
|
33148
33092
|
import path2 from "node:path";
|
|
33149
33093
|
var DEFAULT_CONFIG = {
|
|
33150
33094
|
enabled: true,
|
|
@@ -33155,34 +33099,18 @@ var DEFAULT_CONFIG = {
|
|
|
33155
33099
|
hostLabel: "dsh-host",
|
|
33156
33100
|
keepAwake: { enabled: true, idleReleaseSec: 300, keepDisplay: false },
|
|
33157
33101
|
mockBridge: false,
|
|
33158
|
-
statusFile: path2.join(
|
|
33102
|
+
statusFile: path2.join(homedir(), ".dsh", "dsh-remote-control", "status.json"),
|
|
33159
33103
|
unarchiveOnPrompt: true,
|
|
33160
33104
|
pairOnStartSec: 0,
|
|
33161
33105
|
pairTtlMs: 12e4,
|
|
33162
|
-
// **默认走图片**。2026-10-02 取证推翻了原判断(原判断:文本二维码在任何等宽渲染器下
|
|
33163
|
-
// 都能扫,图片是可有可无的旁路):
|
|
33164
|
-
// · DSH 命令卡的等宽输出块是 `white-space: pre` + `line-height: 1.6`(app bundle 里
|
|
33165
|
-
// 的那条规则,实测行间留白约占行高 37%),每两行模块之间被塞进一条整行宽的缝隙,
|
|
33166
|
-
// 半块二维码被横切成条。
|
|
33167
|
-
// · 用用户真机贴出来的那段输出做受控实验:把字号行距调到 1.0 时 zxing 解得出来,
|
|
33168
|
-
// 调到 ≥1.15 就扫不出来;而 DSH 固定 1.6。也就是说"文本码能扫"在**唯一宿主**上不成立。
|
|
33169
|
-
// · 而 ANSI 反色救不了:TerminalBlock 把每行渲染成内联 span,背景只覆盖字形盒,
|
|
33170
|
-
// 不覆盖行间留白(见 dsh-client-ui-primitives 的 parseAnsiLines/renderLine)。
|
|
33171
|
-
// 图片是自包含位图矩阵,跟宿主的行高、字体、配色都无关——这才是"扫得出来"的那条路。
|
|
33172
|
-
qrImage: true,
|
|
33173
|
-
// 写图片 ≠ 替用户打开查看器:仍然默认关(原话「不要用打开一个图片的方式」)。
|
|
33174
|
-
qrOpen: false,
|
|
33175
|
-
qrAnsi: false,
|
|
33176
|
-
qrStyle: "half",
|
|
33177
33106
|
carrierGraceMs: 5e3,
|
|
33178
33107
|
takeOverQuestions: false,
|
|
33179
33108
|
approvalTimeoutSec: 180,
|
|
33180
33109
|
listingRefreshSec: 15,
|
|
33181
33110
|
conversationIdleTtlSec: 86400,
|
|
33182
|
-
//
|
|
33183
|
-
//
|
|
33184
|
-
|
|
33185
|
-
sidebarQr: { enabled: true, imageFile: path2.join(homedir2(), ".dsh", "sidebar-qr.png"), refreshMs: 2e3 }
|
|
33111
|
+
// 配对的唯一入口。关掉它 = 这台主机**没有**配对入口(`/drc pair` 与文本二维码都在
|
|
33112
|
+
// 2026-10-03 删掉了),所以 index.ts 会把它记成一条 warn 而不是安静地什么都不做。
|
|
33113
|
+
pill: { enabled: true }
|
|
33186
33114
|
};
|
|
33187
33115
|
var loopback = /^wss?:\/\/(localhost|127\.0\.0\.1|\[::1\])(:|\/|$)/;
|
|
33188
33116
|
var wsUrl = /^wss?:\/\/.+/;
|
|
@@ -33200,23 +33128,16 @@ function readConfig(injected, env = process.env) {
|
|
|
33200
33128
|
...DEFAULT_CONFIG,
|
|
33201
33129
|
...injected ?? {},
|
|
33202
33130
|
keepAwake: { ...DEFAULT_CONFIG.keepAwake, ...injected?.keepAwake ?? {} },
|
|
33203
|
-
//
|
|
33204
|
-
//
|
|
33205
|
-
|
|
33131
|
+
// 逐键合并:patch 里只写 `pill.enabled` 时不能把整个对象覆盖掉(虽然它现在只有一个键,
|
|
33132
|
+
// 但"注入对象整体替换默认对象"这个形状在 keepAwake 上已经坑过一次)。
|
|
33133
|
+
pill: { ...DEFAULT_CONFIG.pill, ...injected?.pill ?? {} }
|
|
33206
33134
|
};
|
|
33207
33135
|
const fromEnv = (name) => env[name];
|
|
33208
33136
|
if (fromEnv("DRC_SERVER_URL")) merged.serverUrl = env.DRC_SERVER_URL;
|
|
33209
33137
|
if (fromEnv("DRC_HOST_LABEL")) merged.hostLabel = env.DRC_HOST_LABEL;
|
|
33210
33138
|
if (fromEnv("DRC_HOST_ID")) merged.hostId = env.DRC_HOST_ID;
|
|
33211
33139
|
if (fromEnv("DRC_PAIR_ON_START_SEC")) merged.pairOnStartSec = envSeconds(env.DRC_PAIR_ON_START_SEC);
|
|
33212
|
-
if (fromEnv("
|
|
33213
|
-
if (fromEnv("DRC_QR_OPEN")) merged.qrOpen = envFlag(env.DRC_QR_OPEN, merged.qrOpen);
|
|
33214
|
-
if (fromEnv("DRC_QR_ANSI")) merged.qrAnsi = envFlag(env.DRC_QR_ANSI, merged.qrAnsi);
|
|
33215
|
-
if (fromEnv("DRC_SIDEBAR_QR")) merged.sidebarQr.enabled = envFlag(env.DRC_SIDEBAR_QR, merged.sidebarQr.enabled);
|
|
33216
|
-
if (fromEnv("DRC_QR_STYLE")) {
|
|
33217
|
-
const style = env.DRC_QR_STYLE;
|
|
33218
|
-
if (["ascii", "block", "half"].includes(style)) merged.qrStyle = style;
|
|
33219
|
-
}
|
|
33140
|
+
if (fromEnv("DRC_PILL")) merged.pill.enabled = envFlag(env.DRC_PILL, merged.pill.enabled);
|
|
33220
33141
|
if (fromEnv("DRC_MOCK_BRIDGE") === "1") merged.mockBridge = true;
|
|
33221
33142
|
if (fromEnv("DRC_TAKE_OVER_QUESTIONS") === "1") merged.takeOverQuestions = true;
|
|
33222
33143
|
if (merged.hostTokenEnv) merged.hostToken = env[merged.hostTokenEnv] ?? merged.hostToken;
|
|
@@ -33267,21 +33188,6 @@ function validateConfig(config2) {
|
|
|
33267
33188
|
problems.push({ level: "warn", field: "pairOnStartSec", message: "\u5FC5\u987B\u662F\u975E\u8D1F\u7684\u6709\u9650\u79D2\u6570\uFF0C\u5DF2\u6298\u56DE 0\uFF08\u5173\u95ED\u81EA\u52A8\u53D1\u7801\uFF09" });
|
|
33268
33189
|
config2.pairOnStartSec = 0;
|
|
33269
33190
|
}
|
|
33270
|
-
if (config2.qrOpen && !config2.qrImage) {
|
|
33271
|
-
problems.push({
|
|
33272
|
-
level: "warn",
|
|
33273
|
-
field: "qrOpen",
|
|
33274
|
-
message: "qrOpen \u9700\u8981 qrImage=true \u624D\u4F1A\u5199\u56FE\u7247\u6587\u4EF6\uFF1B\u73B0\u5728\u53EA\u51FA\u6587\u672C\u4E8C\u7EF4\u7801\uFF08\u6587\u672C\u7801\u5728 DSH \u547D\u4EE4\u5361\u91CC\u626B\u4E0D\u51FA\u6765\uFF0C\u89C1 config.ts \u9ED8\u8BA4\u503C\u6CE8\u91CA\uFF09"
|
|
33275
|
-
});
|
|
33276
|
-
}
|
|
33277
|
-
if (!["ascii", "block", "half"].includes(config2.qrStyle)) {
|
|
33278
|
-
problems.push({
|
|
33279
|
-
level: "warn",
|
|
33280
|
-
field: "qrStyle",
|
|
33281
|
-
message: `qrStyle \u53EA\u8BA4 ascii/block/half\uFF0C\u6536\u5230 ${JSON.stringify(config2.qrStyle)}\uFF0C\u5DF2\u9000\u56DE half`
|
|
33282
|
-
});
|
|
33283
|
-
config2.qrStyle = "half";
|
|
33284
|
-
}
|
|
33285
33191
|
if (!secondsSchema.safeParse(config2.conversationIdleTtlSec).success || config2.conversationIdleTtlSec <= 0) {
|
|
33286
33192
|
problems.push({
|
|
33287
33193
|
level: "warn",
|
|
@@ -33307,77 +33213,18 @@ function validateConfig(config2) {
|
|
|
33307
33213
|
message: "\u63D0\u95EE\u63A5\u7BA1\u4F1A**\u53D6\u4EE3\u684C\u9762 UI \u7684\u63D0\u95EE\u80FD\u529B**\uFF08ctx.userQuestions \u53EA\u5141\u8BB8\u4E00\u4E2A\u6D3B\u8DC3 provider\uFF09\uFF1B\u624B\u673A\u7AEF\u4E0D\u5728\u7EBF\u65F6\u63D0\u95EE\u4F1A\u76F4\u63A5\u5931\u8D25"
|
|
33308
33214
|
});
|
|
33309
33215
|
}
|
|
33310
|
-
if (typeof config2.sidebarQr.imageFile !== "string" || config2.sidebarQr.imageFile === "") {
|
|
33311
|
-
problems.push({
|
|
33312
|
-
level: "warn",
|
|
33313
|
-
field: "sidebarQr.imageFile",
|
|
33314
|
-
message: "\u4E3A\u7A7A\u5C31\u65E0\u5904\u843D\u515C\u5E95\u56FE\uFF0C\u5DF2\u5939\u56DE ~/.dsh/sidebar-qr.png\uFF08\u5DE5\u4F5C\u533A\u89E3\u6790\u5F97\u51FA\u6765\u65F6\u672C\u6765\u4E5F\u7528\u4E0D\u5230\u5B83\uFF09"
|
|
33315
|
-
});
|
|
33316
|
-
config2.sidebarQr.imageFile = DEFAULT_CONFIG.sidebarQr.imageFile;
|
|
33317
|
-
} else if (!path2.isAbsolute(config2.sidebarQr.imageFile)) {
|
|
33318
|
-
problems.push({
|
|
33319
|
-
level: "warn",
|
|
33320
|
-
field: "sidebarQr.imageFile",
|
|
33321
|
-
message: `\u4E0D\u662F\u7EDD\u5BF9\u8DEF\u5F84\uFF08${config2.sidebarQr.imageFile}\uFF09\uFF0C\u5DE5\u4F5C\u533A\u89E3\u6790\u4E0D\u51FA\u6765\u65F6\u4F1A\u88AB\u89E3\u6790\u5230\u8FDB\u7A0B\u5DE5\u4F5C\u76EE\u5F55\u4E0B`
|
|
33322
|
-
});
|
|
33323
|
-
}
|
|
33324
|
-
if (!secondsSchema.safeParse(config2.sidebarQr.refreshMs).success || config2.sidebarQr.refreshMs < 200) {
|
|
33325
|
-
problems.push({
|
|
33326
|
-
level: "warn",
|
|
33327
|
-
field: "sidebarQr.refreshMs",
|
|
33328
|
-
message: "\u5FC5\u987B\u662F \u2265200 \u7684\u6709\u9650\u6BEB\u79D2\u6570\uFF0C\u5DF2\u5939\u56DE 2000\uFF08\u539F\u503C " + JSON.stringify(config2.sidebarQr.refreshMs) + "\uFF09"
|
|
33329
|
-
});
|
|
33330
|
-
config2.sidebarQr.refreshMs = DEFAULT_CONFIG.sidebarQr.refreshMs;
|
|
33331
|
-
}
|
|
33332
33216
|
return problems;
|
|
33333
33217
|
}
|
|
33334
|
-
function
|
|
33218
|
+
function redact(value) {
|
|
33335
33219
|
if (value.length <= 8) return "****";
|
|
33336
33220
|
return `${value.slice(0, 4)}\u2026${value.slice(-2)}(${value.length})`;
|
|
33337
33221
|
}
|
|
33338
33222
|
|
|
33223
|
+
// src/pill/routes.ts
|
|
33224
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
33225
|
+
|
|
33339
33226
|
// src/platform/qr.ts
|
|
33340
33227
|
var import_qrcode = __toESM(require_lib(), 1);
|
|
33341
|
-
async function qrMatrix(text, ecc = "M", quietZone = 2) {
|
|
33342
|
-
const qr = import_qrcode.default.create(text, { errorCorrectionLevel: ecc });
|
|
33343
|
-
const size = qr.modules.size;
|
|
33344
|
-
const total = size + quietZone * 2;
|
|
33345
|
-
const out = [];
|
|
33346
|
-
for (let y = 0; y < total; y++) {
|
|
33347
|
-
const row = [];
|
|
33348
|
-
for (let x = 0; x < total; x++) {
|
|
33349
|
-
const mx = x - quietZone;
|
|
33350
|
-
const my = y - quietZone;
|
|
33351
|
-
const dark = mx >= 0 && mx < size && my >= 0 && my < size && Boolean(qr.modules.get(my, mx));
|
|
33352
|
-
row.push(dark);
|
|
33353
|
-
}
|
|
33354
|
-
out.push(row);
|
|
33355
|
-
}
|
|
33356
|
-
return out;
|
|
33357
|
-
}
|
|
33358
|
-
async function renderTerminalQr(text, options = {}) {
|
|
33359
|
-
const { ecc = "M", quietZone = 2, style = "ascii", ansi = false } = options;
|
|
33360
|
-
const matrix = await qrMatrix(text, ecc, quietZone);
|
|
33361
|
-
let body;
|
|
33362
|
-
if (style === "half") {
|
|
33363
|
-
const lines = [];
|
|
33364
|
-
for (let y = 0; y < matrix.length; y += 2) {
|
|
33365
|
-
const top = matrix[y];
|
|
33366
|
-
const bottom = matrix[y + 1] ?? top.map(() => false);
|
|
33367
|
-
let line = "";
|
|
33368
|
-
for (let x = 0; x < top.length; x++) {
|
|
33369
|
-
line += top[x] && bottom[x] ? "\u2588" : top[x] ? "\u2580" : bottom[x] ? "\u2584" : " ";
|
|
33370
|
-
}
|
|
33371
|
-
lines.push(line);
|
|
33372
|
-
}
|
|
33373
|
-
body = lines.join("\n");
|
|
33374
|
-
} else {
|
|
33375
|
-
const dark = style === "ascii" ? "##" : "\u2588\u2588";
|
|
33376
|
-
const light = style === "ascii" ? ".." : " ";
|
|
33377
|
-
body = matrix.map((row) => row.map((cell) => cell ? dark : light).join("")).join("\n");
|
|
33378
|
-
}
|
|
33379
|
-
return ansi ? `\x1B[30;107m${body}\x1B[0m` : body;
|
|
33380
|
-
}
|
|
33381
33228
|
async function qrPng(text, options = {}) {
|
|
33382
33229
|
return import_qrcode.default.toBuffer(text, {
|
|
33383
33230
|
errorCorrectionLevel: options.ecc ?? "M",
|
|
@@ -33387,112 +33234,18 @@ async function qrPng(text, options = {}) {
|
|
|
33387
33234
|
});
|
|
33388
33235
|
}
|
|
33389
33236
|
|
|
33390
|
-
// src/
|
|
33391
|
-
|
|
33392
|
-
|
|
33393
|
-
|
|
33394
|
-
var
|
|
33395
|
-
|
|
33396
|
-
|
|
33397
|
-
|
|
33398
|
-
|
|
33399
|
-
|
|
33400
|
-
|
|
33401
|
-
const parsed = Date.parse(value);
|
|
33402
|
-
return Number.isFinite(parsed) ? parsed : 0;
|
|
33403
|
-
}
|
|
33404
|
-
return 0;
|
|
33405
|
-
}
|
|
33406
|
-
var PairingPresenter = class {
|
|
33407
|
-
constructor(deps) {
|
|
33408
|
-
this.deps = deps;
|
|
33409
|
-
}
|
|
33410
|
-
deps;
|
|
33411
|
-
value = { state: "none" };
|
|
33412
|
-
artifact = null;
|
|
33413
|
-
rendering = false;
|
|
33414
|
-
snapshot() {
|
|
33415
|
-
return this.value;
|
|
33416
|
-
}
|
|
33417
|
-
/**
|
|
33418
|
-
* 当前这一版的 PNG 字节;没有可看的码时是 null。
|
|
33419
|
-
*
|
|
33420
|
-
* **路由必须先拿它落盘成功,再回答 `ready`**——反过来就等于把用户送到一个不存在的
|
|
33421
|
-
* 文件上(右栏会弹出一张破图,比不弹更糟)。
|
|
33422
|
-
*/
|
|
33423
|
-
current() {
|
|
33424
|
-
return this.value.state === "ready" ? this.artifact : null;
|
|
33425
|
-
}
|
|
33426
|
-
/**
|
|
33427
|
-
* 看一眼当前的码。
|
|
33428
|
-
*
|
|
33429
|
-
* 三种结果:
|
|
33430
|
-
* - 没有码 / 码已过期 → `none`(右栏不该被一张废码顶开);
|
|
33431
|
-
* - 码没变 → 只更新 `expiresAt`(同一版码的过期时间可能被服务端 TTL 改写);
|
|
33432
|
-
* - 码变了 → 渲染成功后认这一版;渲染抛错就是 `none`(宁可不弹,也不弹一张上一版的图)。
|
|
33433
|
-
*/
|
|
33434
|
-
async observe(pairing) {
|
|
33435
|
-
const qr = typeof pairing?.qr === "string" && pairing.qr !== "" ? pairing.qr : null;
|
|
33436
|
-
const expiresAt = toEpochMs(pairing?.expiresAt);
|
|
33437
|
-
if (qr === null || expiresAt <= this.deps.now()) {
|
|
33438
|
-
this.value = { state: "none" };
|
|
33439
|
-
this.artifact = null;
|
|
33440
|
-
return;
|
|
33441
|
-
}
|
|
33442
|
-
const epoch = this.deps.digest(qr);
|
|
33443
|
-
if (this.artifact?.epoch !== epoch) {
|
|
33444
|
-
if (this.rendering) return;
|
|
33445
|
-
this.rendering = true;
|
|
33446
|
-
try {
|
|
33447
|
-
const buffer = await this.deps.render(qr);
|
|
33448
|
-
this.artifact = { epoch, buffer };
|
|
33449
|
-
} catch (error62) {
|
|
33450
|
-
this.artifact = null;
|
|
33451
|
-
this.deps.log("\u4E8C\u7EF4\u7801\u6E32\u67D3\u5931\u8D25", { message: String(error62?.message ?? error62).slice(0, 200) });
|
|
33452
|
-
} finally {
|
|
33453
|
-
this.rendering = false;
|
|
33454
|
-
}
|
|
33455
|
-
}
|
|
33456
|
-
this.value = this.artifact?.epoch === epoch ? { state: "ready", epoch, expiresAt } : { state: "none" };
|
|
33457
|
-
}
|
|
33458
|
-
};
|
|
33459
|
-
function liveDeps(log) {
|
|
33460
|
-
return {
|
|
33461
|
-
now: () => Date.now(),
|
|
33462
|
-
render: (qr) => import_qrcode2.default.toBuffer(qr, { errorCorrectionLevel: "M", margin: 4, scale: 8, type: "png" }),
|
|
33463
|
-
digest: (qr) => createHash2("sha256").update(qr).digest("hex").slice(0, 16),
|
|
33464
|
-
log
|
|
33465
|
-
};
|
|
33466
|
-
}
|
|
33467
|
-
function writePrivatePng(file2, data) {
|
|
33468
|
-
if (!file2) return false;
|
|
33469
|
-
try {
|
|
33470
|
-
mkdirSync2(path3.dirname(file2), { recursive: true });
|
|
33471
|
-
const tmp = `${file2}.tmp-${process.pid}`;
|
|
33472
|
-
writeFileSync2(tmp, data, { mode: 384 });
|
|
33473
|
-
renameSync2(tmp, file2);
|
|
33474
|
-
return true;
|
|
33475
|
-
} catch {
|
|
33476
|
-
return false;
|
|
33477
|
-
}
|
|
33478
|
-
}
|
|
33479
|
-
|
|
33480
|
-
// src/presentation/address.ts
|
|
33481
|
-
var FILE_ADDRESS_PREFIX = "dsh-resource://file/";
|
|
33482
|
-
function encodeSegment(segment) {
|
|
33483
|
-
return encodeURIComponent(segment).replace(/%3A/gi, ":");
|
|
33484
|
-
}
|
|
33485
|
-
function sessionFileAddress(sessionId, filePath) {
|
|
33486
|
-
const normalized = filePath.replace(/\\/g, "/");
|
|
33487
|
-
const encoded = normalized.split("/").map(encodeSegment).join("/");
|
|
33488
|
-
return `${FILE_ADDRESS_PREFIX}session/${encodeSegment(sessionId)}/${encoded}`;
|
|
33237
|
+
// src/pill/routes.ts
|
|
33238
|
+
var PAIR_NEW_ROUTE = "/plugins/dsh-remote-control/pairing/new";
|
|
33239
|
+
var PAIR_IMAGE_ROUTE = "/plugins/dsh-remote-control/pairing.png";
|
|
33240
|
+
var PAIR_STATUS_ROUTE = "/plugins/dsh-remote-control/status";
|
|
33241
|
+
var PAIR_MARKER_HEADER = "x-drc-pair";
|
|
33242
|
+
var PAIR_MARKER_VALUE = "1";
|
|
33243
|
+
function pairingEpoch(qr) {
|
|
33244
|
+
return createHash2("sha256").update(qr).digest("hex").slice(0, 16);
|
|
33245
|
+
}
|
|
33246
|
+
async function renderPairingPng(qr) {
|
|
33247
|
+
return qrPng(qr);
|
|
33489
33248
|
}
|
|
33490
|
-
function isUsableSessionId(value) {
|
|
33491
|
-
return typeof value === "string" && /^[A-Za-z0-9._:-]{1,200}$/.test(value);
|
|
33492
|
-
}
|
|
33493
|
-
|
|
33494
|
-
// src/presentation/route.ts
|
|
33495
|
-
var PAIRING_ROUTE = "/plugins/dsh-remote-control/pairing";
|
|
33496
33249
|
var LOOPBACK_HOSTS = /* @__PURE__ */ new Set(["localhost", "127.0.0.1", "::1", "[::1]"]);
|
|
33497
33250
|
function hostnameOf(host) {
|
|
33498
33251
|
const trimmed = host.trim().toLowerCase();
|
|
@@ -33513,161 +33266,202 @@ function hostIsLoopback(host) {
|
|
|
33513
33266
|
if (host === void 0 || host.trim() === "") return false;
|
|
33514
33267
|
return LOOPBACK_HOSTS.has(hostnameOf(host));
|
|
33515
33268
|
}
|
|
33516
|
-
function
|
|
33517
|
-
if (origin === void 0) return true;
|
|
33269
|
+
function originIsLoopbackOrAbsent(origin) {
|
|
33270
|
+
if (origin === void 0 || origin.trim() === "") return true;
|
|
33271
|
+
return originLooksLikeTheHostItself(origin);
|
|
33272
|
+
}
|
|
33273
|
+
function originLooksLikeTheHostItself(origin) {
|
|
33518
33274
|
try {
|
|
33519
|
-
const
|
|
33520
|
-
return
|
|
33275
|
+
const url2 = new URL(origin);
|
|
33276
|
+
if (url2.protocol !== "http:" && url2.protocol !== "https:") return origin.trim() !== "null";
|
|
33277
|
+
return LOOPBACK_HOSTS.has(url2.hostname);
|
|
33521
33278
|
} catch {
|
|
33522
33279
|
return false;
|
|
33523
33280
|
}
|
|
33524
33281
|
}
|
|
33282
|
+
function rejectedBy(request) {
|
|
33283
|
+
if (!hostIsLoopback(request.headers.host)) return "host-not-loopback";
|
|
33284
|
+
if (request.headers[PAIR_MARKER_HEADER] !== PAIR_MARKER_VALUE) return "pair-marker-missing";
|
|
33285
|
+
const origin = request.headers.origin;
|
|
33286
|
+
if (origin !== void 0 && origin.trim() !== "" && !originLooksLikeTheHostItself(origin)) return "origin-not-trusted";
|
|
33287
|
+
return void 0;
|
|
33288
|
+
}
|
|
33525
33289
|
function json2(response, status, body) {
|
|
33526
33290
|
const payload = JSON.stringify(body);
|
|
33527
33291
|
response.writeHead(status, { "Content-Type": "application/json", "Content-Length": Buffer.byteLength(payload) });
|
|
33528
33292
|
response.end(payload);
|
|
33529
33293
|
}
|
|
33530
|
-
function
|
|
33531
|
-
return
|
|
33532
|
-
if (request.method !== "
|
|
33294
|
+
function pairNewHandler(deps) {
|
|
33295
|
+
return (request, response) => {
|
|
33296
|
+
if (request.method !== "POST") {
|
|
33533
33297
|
json2(response, 405, { error: "method not allowed" });
|
|
33534
33298
|
return;
|
|
33535
33299
|
}
|
|
33536
|
-
|
|
33537
|
-
|
|
33300
|
+
const guard = rejectedBy(request);
|
|
33301
|
+
if (guard) {
|
|
33302
|
+
deps.log("\u53D1\u7801\u8BF7\u6C42\u88AB\u5B88\u536B\u62D2", { guard });
|
|
33303
|
+
json2(response, 403, { error: "request-not-trusted", guard });
|
|
33538
33304
|
return;
|
|
33539
33305
|
}
|
|
33540
33306
|
try {
|
|
33541
|
-
const
|
|
33542
|
-
|
|
33543
|
-
|
|
33544
|
-
if (snapshot.state !== "ready") {
|
|
33545
|
-
json2(response, 200, { state: "none" });
|
|
33546
|
-
return;
|
|
33547
|
-
}
|
|
33548
|
-
if (!isUsableSessionId(sessionId)) {
|
|
33549
|
-
json2(response, 400, { state: "none", error: "session parameter required" });
|
|
33550
|
-
return;
|
|
33551
|
-
}
|
|
33552
|
-
const target = options.target(sessionId);
|
|
33553
|
-
if (!target.persist()) {
|
|
33554
|
-
json2(response, 200, { state: "none" });
|
|
33307
|
+
const pairing = deps.ensureFresh();
|
|
33308
|
+
if (!pairing) {
|
|
33309
|
+
json2(response, 200, { state: "unavailable", reason: "relay-offline" });
|
|
33555
33310
|
return;
|
|
33556
33311
|
}
|
|
33312
|
+
deps.log("\u914D\u5BF9\u7801\u7ECF\u72B6\u6001\u680F\u53D1\u51FA", { token: pairing.token });
|
|
33557
33313
|
json2(response, 200, {
|
|
33558
33314
|
state: "ready",
|
|
33559
|
-
epoch:
|
|
33560
|
-
|
|
33561
|
-
|
|
33315
|
+
epoch: pairingEpoch(pairing.qr),
|
|
33316
|
+
token: pairing.token,
|
|
33317
|
+
expiresInMs: Math.max(0, pairing.expiresAt - Date.now())
|
|
33562
33318
|
});
|
|
33563
33319
|
} catch (error62) {
|
|
33564
33320
|
json2(response, 500, { error: String(error62?.message ?? error62).slice(0, 200) });
|
|
33565
33321
|
}
|
|
33566
33322
|
};
|
|
33567
33323
|
}
|
|
33568
|
-
function
|
|
33569
|
-
return
|
|
33570
|
-
|
|
33571
|
-
|
|
33572
|
-
|
|
33573
|
-
|
|
33574
|
-
|
|
33575
|
-
|
|
33576
|
-
|
|
33577
|
-
|
|
33578
|
-
let timer;
|
|
33579
|
-
let unregister;
|
|
33580
|
-
let started = false;
|
|
33581
|
-
let stopped = false;
|
|
33582
|
-
let written = null;
|
|
33583
|
-
const targetFor = (sessionId) => {
|
|
33584
|
-
let workspace;
|
|
33324
|
+
function pairImageHandler(deps) {
|
|
33325
|
+
return async (request, response) => {
|
|
33326
|
+
if (request.method !== "GET" && request.method !== "HEAD") {
|
|
33327
|
+
json2(response, 405, { error: "method not allowed" });
|
|
33328
|
+
return;
|
|
33329
|
+
}
|
|
33330
|
+
if (!hostIsLoopback(request.headers.host) || !originIsLoopbackOrAbsent(request.headers.origin)) {
|
|
33331
|
+
json2(response, 403, { error: "request-not-trusted" });
|
|
33332
|
+
return;
|
|
33333
|
+
}
|
|
33585
33334
|
try {
|
|
33586
|
-
|
|
33335
|
+
const pairing = deps.current();
|
|
33336
|
+
if (!pairing) {
|
|
33337
|
+
response.writeHead(204);
|
|
33338
|
+
response.end();
|
|
33339
|
+
return;
|
|
33340
|
+
}
|
|
33341
|
+
const png = await renderPairingPng(pairing.qr);
|
|
33342
|
+
response.writeHead(200, {
|
|
33343
|
+
"Content-Type": "image/png",
|
|
33344
|
+
"Content-Length": png.length,
|
|
33345
|
+
// 一张码会被就地换掉(半程刷新、被手机用掉),所以绝不许缓存。
|
|
33346
|
+
// ETag 给的是这一版的哈希,pill 拿它判断要不要重画,省掉撕票式的刷新。
|
|
33347
|
+
"Cache-Control": "no-store",
|
|
33348
|
+
ETag: `"${pairingEpoch(pairing.qr)}"`
|
|
33349
|
+
});
|
|
33350
|
+
response.end(request.method === "HEAD" ? void 0 : png);
|
|
33587
33351
|
} catch (error62) {
|
|
33588
|
-
|
|
33589
|
-
workspace = void 0;
|
|
33352
|
+
json2(response, 500, { error: String(error62?.message ?? error62).slice(0, 200) });
|
|
33590
33353
|
}
|
|
33591
|
-
const file2 = workspace ? path4.join(workspace, ".dsh", "sidebar-qr.png") : config2.imageFile;
|
|
33592
|
-
return {
|
|
33593
|
-
file: file2,
|
|
33594
|
-
persist: () => {
|
|
33595
|
-
const artifact = presenter.current();
|
|
33596
|
-
if (!artifact) return false;
|
|
33597
|
-
if (written && written.epoch === artifact.epoch && written.file === file2) return true;
|
|
33598
|
-
if (!writePrivatePng(file2, artifact.buffer)) {
|
|
33599
|
-
log("\u4E8C\u7EF4\u7801\u843D\u76D8\u5931\u8D25", { file: file2 });
|
|
33600
|
-
return false;
|
|
33601
|
-
}
|
|
33602
|
-
written = { epoch: artifact.epoch, file: file2 };
|
|
33603
|
-
return true;
|
|
33604
|
-
}
|
|
33605
|
-
};
|
|
33606
33354
|
};
|
|
33607
|
-
|
|
33608
|
-
|
|
33609
|
-
|
|
33610
|
-
|
|
33611
|
-
|
|
33612
|
-
|
|
33613
|
-
|
|
33355
|
+
}
|
|
33356
|
+
function pillStatusHandler(deps) {
|
|
33357
|
+
return (request, response) => {
|
|
33358
|
+
if (request.method !== "GET" && request.method !== "HEAD") {
|
|
33359
|
+
json2(response, 405, { error: "method not allowed" });
|
|
33360
|
+
return;
|
|
33361
|
+
}
|
|
33362
|
+
if (!hostIsLoopback(request.headers.host) || !originIsLoopbackOrAbsent(request.headers.origin)) {
|
|
33363
|
+
json2(response, 403, { error: "request-not-trusted" });
|
|
33614
33364
|
return;
|
|
33615
33365
|
}
|
|
33616
33366
|
try {
|
|
33617
|
-
|
|
33367
|
+
const status = deps.status();
|
|
33368
|
+
json2(response, 200, {
|
|
33369
|
+
relay: status.relay,
|
|
33370
|
+
paired: status.paired,
|
|
33371
|
+
hostLabel: status.hostLabel,
|
|
33372
|
+
serverUrl: status.serverUrl
|
|
33373
|
+
});
|
|
33618
33374
|
} catch (error62) {
|
|
33619
|
-
|
|
33375
|
+
json2(response, 500, { error: String(error62?.message ?? error62).slice(0, 200) });
|
|
33376
|
+
}
|
|
33377
|
+
};
|
|
33378
|
+
}
|
|
33379
|
+
function registerPillRoutes(webServer, deps) {
|
|
33380
|
+
const off = [];
|
|
33381
|
+
const definitions = [
|
|
33382
|
+
{ kind: "exact", path: PAIR_NEW_ROUTE, handler: pairNewHandler(deps) },
|
|
33383
|
+
{ kind: "exact", path: PAIR_IMAGE_ROUTE, handler: pairImageHandler(deps) },
|
|
33384
|
+
{ kind: "exact", path: PAIR_STATUS_ROUTE, handler: pillStatusHandler(deps) }
|
|
33385
|
+
];
|
|
33386
|
+
try {
|
|
33387
|
+
for (const definition of definitions) off.push(webServer.register(definition));
|
|
33388
|
+
} catch (error62) {
|
|
33389
|
+
for (const unregister of off.splice(0)) {
|
|
33390
|
+
try {
|
|
33391
|
+
unregister();
|
|
33392
|
+
} catch {
|
|
33393
|
+
}
|
|
33394
|
+
}
|
|
33395
|
+
throw error62;
|
|
33396
|
+
}
|
|
33397
|
+
return () => {
|
|
33398
|
+
for (const unregister of off) {
|
|
33399
|
+
try {
|
|
33400
|
+
unregister();
|
|
33401
|
+
} catch {
|
|
33402
|
+
}
|
|
33620
33403
|
}
|
|
33621
33404
|
};
|
|
33405
|
+
}
|
|
33406
|
+
|
|
33407
|
+
// src/pill/start.ts
|
|
33408
|
+
function startPill(ctx, deps) {
|
|
33409
|
+
const { log } = deps;
|
|
33410
|
+
const probe = {};
|
|
33411
|
+
let web;
|
|
33412
|
+
let unregister;
|
|
33413
|
+
let started = false;
|
|
33414
|
+
let stopped = false;
|
|
33622
33415
|
const tryStart = () => {
|
|
33623
33416
|
if (started || stopped || !web) return;
|
|
33624
33417
|
started = true;
|
|
33625
33418
|
try {
|
|
33626
|
-
unregister =
|
|
33627
|
-
|
|
33628
|
-
|
|
33419
|
+
unregister = registerPillRoutes(web, {
|
|
33420
|
+
ensureFresh: () => deps.ensureFresh(),
|
|
33421
|
+
current: () => deps.current(),
|
|
33422
|
+
status: () => deps.status(),
|
|
33423
|
+
log
|
|
33629
33424
|
});
|
|
33630
|
-
probe.
|
|
33425
|
+
probe.routes = "registered";
|
|
33631
33426
|
} catch (error62) {
|
|
33632
|
-
probe.
|
|
33633
|
-
log("route registration failed", { message: String(error62?.message ?? error62).slice(0, 200) });
|
|
33427
|
+
probe.routes = `register threw: ${String(error62?.message ?? error62).slice(0, 80)}`;
|
|
33634
33428
|
started = false;
|
|
33429
|
+
log("pill \u8DEF\u7531\u6CE8\u518C\u5931\u8D25\uFF08\u914D\u5BF9\u94FE\u8DEF\u4E0D\u53D7\u5F71\u54CD\uFF09", {
|
|
33430
|
+
message: String(error62?.message ?? error62).slice(0, 200)
|
|
33431
|
+
});
|
|
33635
33432
|
return;
|
|
33636
33433
|
}
|
|
33637
|
-
|
|
33638
|
-
timer.unref?.();
|
|
33639
|
-
void tick();
|
|
33640
|
-
log("sidebar started", { fallbackImageFile: config2.imageFile, refreshMs: config2.refreshMs });
|
|
33434
|
+
log("pill routes started");
|
|
33641
33435
|
};
|
|
33642
|
-
const
|
|
33436
|
+
const acceptWebServer = (value, via) => {
|
|
33437
|
+
if (!value || typeof value !== "object") return false;
|
|
33438
|
+
if (typeof value.register !== "function") {
|
|
33439
|
+
probe.webServer = "object without register()";
|
|
33440
|
+
return false;
|
|
33441
|
+
}
|
|
33442
|
+
web = value;
|
|
33443
|
+
probe.webServer = via;
|
|
33444
|
+
tryStart();
|
|
33445
|
+
return true;
|
|
33446
|
+
};
|
|
33447
|
+
const pickFrom = (source) => {
|
|
33643
33448
|
if (!source || typeof source !== "object") return void 0;
|
|
33644
33449
|
const asContext = source;
|
|
33645
33450
|
if (typeof asContext.get === "function") {
|
|
33646
33451
|
try {
|
|
33647
|
-
const got = asContext.get(
|
|
33452
|
+
const got = asContext.get("webServer", true);
|
|
33648
33453
|
if (got) return got;
|
|
33649
33454
|
} catch {
|
|
33650
33455
|
}
|
|
33651
33456
|
}
|
|
33652
33457
|
try {
|
|
33653
|
-
return source[
|
|
33458
|
+
return source["webServer"];
|
|
33654
33459
|
} catch {
|
|
33655
33460
|
return void 0;
|
|
33656
33461
|
}
|
|
33657
33462
|
};
|
|
33658
|
-
|
|
33659
|
-
|
|
33660
|
-
if (typeof value.register !== "function") {
|
|
33661
|
-
probe.webServer = "object without register()";
|
|
33662
|
-
return false;
|
|
33663
|
-
}
|
|
33664
|
-
web = value;
|
|
33665
|
-
probe.webServer = via;
|
|
33666
|
-
tryStart();
|
|
33667
|
-
return true;
|
|
33668
|
-
};
|
|
33669
|
-
if (!config2.enabled) {
|
|
33670
|
-
probe.sidebar = "disabled";
|
|
33463
|
+
if (!deps.enabled) {
|
|
33464
|
+
probe.pill = "disabled";
|
|
33671
33465
|
} else {
|
|
33672
33466
|
try {
|
|
33673
33467
|
const found = ctx.get?.("webServer", true);
|
|
@@ -33679,12 +33473,9 @@ function startSidebarQr(ctx, deps) {
|
|
|
33679
33473
|
try {
|
|
33680
33474
|
ctx.inject?.(["webServer"], ((...args) => {
|
|
33681
33475
|
try {
|
|
33682
|
-
let value;
|
|
33683
33476
|
for (const candidate of [args[0], ...args, ctx]) {
|
|
33684
|
-
|
|
33685
|
-
if (value) break;
|
|
33477
|
+
if (acceptWebServer(pickFrom(candidate), "via inject")) return;
|
|
33686
33478
|
}
|
|
33687
|
-
if (acceptWebServer(value, "via inject")) return;
|
|
33688
33479
|
probe.webServer = `${probe.webServer ?? "none"} (inject fired, empty)`;
|
|
33689
33480
|
} catch (error62) {
|
|
33690
33481
|
probe["injectThrew:webServer"] = String(error62?.message ?? error62).slice(0, 160);
|
|
@@ -33697,18 +33488,21 @@ function startSidebarQr(ctx, deps) {
|
|
|
33697
33488
|
const stop = () => {
|
|
33698
33489
|
if (stopped) return;
|
|
33699
33490
|
stopped = true;
|
|
33700
|
-
|
|
33701
|
-
|
|
33702
|
-
timer = void 0;
|
|
33703
|
-
}
|
|
33491
|
+
const off = unregister;
|
|
33492
|
+
unregister = void 0;
|
|
33704
33493
|
try {
|
|
33705
|
-
|
|
33494
|
+
off?.();
|
|
33706
33495
|
} catch {
|
|
33707
33496
|
}
|
|
33708
|
-
|
|
33709
|
-
|
|
33497
|
+
if (started) log("pill routes stopped");
|
|
33498
|
+
};
|
|
33499
|
+
return {
|
|
33500
|
+
stop,
|
|
33501
|
+
probe,
|
|
33502
|
+
get available() {
|
|
33503
|
+
return started && probe.routes === "registered";
|
|
33504
|
+
}
|
|
33710
33505
|
};
|
|
33711
|
-
return { stop, probe };
|
|
33712
33506
|
}
|
|
33713
33507
|
|
|
33714
33508
|
// src/index.ts
|
|
@@ -33740,11 +33534,7 @@ function apply(ctx, injected = {}) {
|
|
|
33740
33534
|
get state() {
|
|
33741
33535
|
return handle?.state() ?? { relay: "offline", carrier: "none", reason: "not-started" };
|
|
33742
33536
|
},
|
|
33743
|
-
createPairing: () => handle?.createPairing() ?? null
|
|
33744
|
-
// presentation 那一半要按会话解析落点(右栏那张图放哪),而"会话 → 工作区"这点
|
|
33745
|
-
// 平台知识只在本插件里有。**在这里开一个口,而不是让那一半自己去探内核服务**:
|
|
33746
|
-
// 两份探测迟早分叉,而分叉的表现是"卡片说 A 地、右栏读 B 地",很难查。
|
|
33747
|
-
sessionWorkspace: (sessionId) => handle?.sessionWorkspace(sessionId)
|
|
33537
|
+
createPairing: () => handle?.createPairing() ?? null
|
|
33748
33538
|
});
|
|
33749
33539
|
} catch {
|
|
33750
33540
|
}
|
|
@@ -33807,7 +33597,7 @@ function applyInner(ctx, injected) {
|
|
|
33807
33597
|
let runtime;
|
|
33808
33598
|
let relay;
|
|
33809
33599
|
let startedCarrier = "";
|
|
33810
|
-
let
|
|
33600
|
+
let pillRoutes;
|
|
33811
33601
|
let lastRelayState = "connecting";
|
|
33812
33602
|
let lastRelayProblem;
|
|
33813
33603
|
let lastRelayGeneration = 0;
|
|
@@ -33912,13 +33702,21 @@ function applyInner(ctx, injected) {
|
|
|
33912
33702
|
// 出站计数:手机上"没收到 X"的第一现场对比点(插件没发 vs 发了但路上丢了)。
|
|
33913
33703
|
outbound: runtime?.stats ?? null,
|
|
33914
33704
|
probe,
|
|
33915
|
-
//
|
|
33916
|
-
|
|
33705
|
+
// 配对入口(那颗 pill 的三条路由)为什么没起,是"配不了对"唯一的排查入口:
|
|
33706
|
+
// 这里直接把软探测的结果带出来。
|
|
33707
|
+
pill: pillRoutes?.probe ?? { webServer: "not-started" },
|
|
33917
33708
|
// token 永远不出现真值;这条有测试锁住。
|
|
33918
33709
|
hostToken: void 0,
|
|
33919
|
-
hostTokenShape: config2.hostToken ?
|
|
33710
|
+
hostTokenShape: config2.hostToken ? redact(config2.hostToken) : void 0,
|
|
33920
33711
|
mockBridgeForced: config2.mockBridge,
|
|
33921
|
-
problems:
|
|
33712
|
+
problems: [
|
|
33713
|
+
...problems.map((problem) => `${problem.level}:${problem.field}`),
|
|
33714
|
+
// **每次快照现判,不在 apply 里 push 一次**。`webServer` 可能是 `ctx.inject` 的回调
|
|
33715
|
+
// 晚到才拿到的,apply 返回那一刻 `available` 还是 false——留在 apply 里就会写出
|
|
33716
|
+
// `routes:"registered"` 与 `problems:["warn:pill"]` 并存的假警报
|
|
33717
|
+
// (2026-10-03 真机重启后实测抓到)。为什么没挂上从同一个快照的 `pill` 探针读。
|
|
33718
|
+
...pillRoutes?.available ? [] : ["warn:pill"]
|
|
33719
|
+
],
|
|
33922
33720
|
...config2.pairOnStartSec > 0 ? { pairOnStartWarn: "status.json \u91CC\u5E26\u7740\u4ECD\u7136\u6709\u6548\u7684\u914D\u5BF9\u7801\u4E0E PSK" } : {}
|
|
33923
33721
|
};
|
|
33924
33722
|
};
|
|
@@ -33951,6 +33749,25 @@ function applyInner(ctx, injected) {
|
|
|
33951
33749
|
return created;
|
|
33952
33750
|
};
|
|
33953
33751
|
const createPairing = () => wrappedPublish();
|
|
33752
|
+
function currentPairing() {
|
|
33753
|
+
const shown = active.pairing;
|
|
33754
|
+
if (!shown) return null;
|
|
33755
|
+
if (!slots.resolveFor(shown.token)) return null;
|
|
33756
|
+
return { qr: shown.qr, token: shown.token, expiresAt: shown.expiresAt };
|
|
33757
|
+
}
|
|
33758
|
+
function ensureFreshPairing() {
|
|
33759
|
+
const fresh = currentPairing() ?? createPairing();
|
|
33760
|
+
if (!fresh) return null;
|
|
33761
|
+
return { qr: fresh.qr, token: fresh.token, expiresAt: fresh.expiresAt };
|
|
33762
|
+
}
|
|
33763
|
+
function pillStatus() {
|
|
33764
|
+
return {
|
|
33765
|
+
relay: relay ? relayState() : "idle",
|
|
33766
|
+
paired: relay?.conversationCount ?? 0,
|
|
33767
|
+
hostLabel: config2.hostLabel,
|
|
33768
|
+
serverUrl: config2.serverUrl
|
|
33769
|
+
};
|
|
33770
|
+
}
|
|
33954
33771
|
const pairingWindow = new PairingWindow(
|
|
33955
33772
|
{
|
|
33956
33773
|
clock,
|
|
@@ -33967,7 +33784,6 @@ function applyInner(ctx, injected) {
|
|
|
33967
33784
|
);
|
|
33968
33785
|
const collected = {};
|
|
33969
33786
|
const hasServices = () => typeof collected.sessions === "object" && collected.sessions !== null;
|
|
33970
|
-
const apiProxyOf = () => collected.apiProxy;
|
|
33971
33787
|
const probe = {};
|
|
33972
33788
|
const injectFired = [];
|
|
33973
33789
|
let hasGet = true;
|
|
@@ -33985,9 +33801,6 @@ function applyInner(ctx, injected) {
|
|
|
33985
33801
|
start(createServicesKernel(collected, { log, takeOverQuestions: config2.takeOverQuestions, clock }));
|
|
33986
33802
|
return;
|
|
33987
33803
|
}
|
|
33988
|
-
if (typeof ctx.get?.("apiProxy", true) === "object") {
|
|
33989
|
-
log("apiProxy present but unsupported by this build", { hint: "\u53EA\u505A\u8BCA\u65AD\u8BB0\u5F55\uFF0C\u4E0D\u63A5\u8FD9\u6761\u8F7D\u4F53" });
|
|
33990
|
-
}
|
|
33991
33804
|
};
|
|
33992
33805
|
const requiredMember = { sessions: "list", sessionQuery: "listSessions", agents: "get" };
|
|
33993
33806
|
const keysOf = (value) => {
|
|
@@ -34199,23 +34012,6 @@ function applyInner(ctx, injected) {
|
|
|
34199
34012
|
} catch {
|
|
34200
34013
|
}
|
|
34201
34014
|
}
|
|
34202
|
-
try {
|
|
34203
|
-
const proxy = ctx.get?.("apiProxy", true);
|
|
34204
|
-
if (proxy) collected.apiProxy = proxy;
|
|
34205
|
-
} catch {
|
|
34206
|
-
}
|
|
34207
|
-
try {
|
|
34208
|
-
ctx.inject?.(["apiProxy"], ((scoped) => {
|
|
34209
|
-
const value = scoped?.get?.("apiProxy", true);
|
|
34210
|
-
if (!value) return;
|
|
34211
|
-
collected.apiProxy = value;
|
|
34212
|
-
if (!decided) {
|
|
34213
|
-
oneShot.schedule(tryStart, config2.carrierGraceMs);
|
|
34214
|
-
}
|
|
34215
|
-
tryStart();
|
|
34216
|
-
}));
|
|
34217
|
-
} catch {
|
|
34218
|
-
}
|
|
34219
34015
|
tryStart();
|
|
34220
34016
|
if (!runtime && config2.mockBridge) {
|
|
34221
34017
|
decided = true;
|
|
@@ -34224,44 +34020,29 @@ function applyInner(ctx, injected) {
|
|
|
34224
34020
|
}
|
|
34225
34021
|
if (!runtime) {
|
|
34226
34022
|
status.write({ ...state(), carrier: "none", reason: "no-carrier", probe });
|
|
34227
|
-
log("no kernel carrier available", { hint: "\u9700\u8981 sessions
|
|
34023
|
+
log("no kernel carrier available", { hint: "\u9700\u8981 sessions \u670D\u52A1\uFF0C\u6216\u628A mockBridge \u6253\u5F00\u505A\u5F00\u53D1" });
|
|
34024
|
+
}
|
|
34025
|
+
try {
|
|
34026
|
+
pillRoutes = startPill(ctx, {
|
|
34027
|
+
enabled: config2.pill.enabled,
|
|
34028
|
+
// 三条路由要的三件事:幂等发码、只读看当前码、读连接状态。
|
|
34029
|
+
ensureFresh: () => ensureFreshPairing(),
|
|
34030
|
+
current: () => currentPairing(),
|
|
34031
|
+
status: () => pillStatus(),
|
|
34032
|
+
log
|
|
34033
|
+
});
|
|
34034
|
+
} catch (error62) {
|
|
34035
|
+
pillRoutes = void 0;
|
|
34036
|
+
log("pill start threw\uFF08\u914D\u5BF9\u94FE\u8DEF\u4E0D\u53D7\u5F71\u54CD\uFF09", {
|
|
34037
|
+
message: String(error62?.message ?? error62).slice(0, 200)
|
|
34038
|
+
});
|
|
34228
34039
|
}
|
|
34229
|
-
const workspaceOfInvocation = (invocation) => {
|
|
34230
|
-
const agent = invocation?.agent;
|
|
34231
|
-
const sessionId = typeof agent?.session?.id === "string" ? agent.session.id : "";
|
|
34232
|
-
if (sessionId === "") return void 0;
|
|
34233
|
-
try {
|
|
34234
|
-
return kernel?.sessionWorkspace?.(sessionId);
|
|
34235
|
-
} catch {
|
|
34236
|
-
return void 0;
|
|
34237
|
-
}
|
|
34238
|
-
};
|
|
34239
34040
|
const commandDefinition = {
|
|
34240
34041
|
name: "drc",
|
|
34241
|
-
description: "DSH \u8FDC\u7A0B\u63A7\u5236\uFF1A\
|
|
34242
|
-
input: { hint: "
|
|
34042
|
+
description: "DSH \u8FDC\u7A0B\u63A7\u5236\uFF1A\u914D\u5BF9\u8BF7\u70B9\u72B6\u6001\u680F\u7684 dsh-remote-control\uFF1B/drc status \u770B\u8FDE\u63A5\u4E0E\u95EE\u9898\uFF0C/drc unpair \u89E3\u914D",
|
|
34043
|
+
input: { hint: "status | unpair" },
|
|
34243
34044
|
handler: async (invocation) => {
|
|
34244
34045
|
const argument = String(invocation.rawInput ?? "").trim().toLowerCase();
|
|
34245
|
-
if (argument.startsWith("pair")) {
|
|
34246
|
-
const pairing = createPairing();
|
|
34247
|
-
if (!pairing) return { kind: "success", text: PAIR_UNAVAILABLE_TEXT };
|
|
34248
|
-
const workspaceDir = workspaceOfInvocation(invocation);
|
|
34249
|
-
const imageFile = config2.qrImage ? pairingImagePath(config2.statusFile, workspaceDir) : "";
|
|
34250
|
-
if (imageFile) writePairingImage(imageFile, pairing.qr, config2, log);
|
|
34251
|
-
const terminal = config2.qrImage ? void 0 : await renderTerminalQr(pairing.qr, { style: config2.qrStyle, ansi: config2.qrAnsi });
|
|
34252
|
-
return {
|
|
34253
|
-
kind: "success",
|
|
34254
|
-
text: pairingPairText({
|
|
34255
|
-
token: pairing.token,
|
|
34256
|
-
qr: pairing.qr,
|
|
34257
|
-
expiresAt: pairing.expiresAt,
|
|
34258
|
-
now: clock.now(),
|
|
34259
|
-
qrImage: config2.qrImage,
|
|
34260
|
-
imageFile,
|
|
34261
|
-
terminalQr: terminal
|
|
34262
|
-
})
|
|
34263
|
-
};
|
|
34264
|
-
}
|
|
34265
34046
|
if (argument.startsWith("unpair")) {
|
|
34266
34047
|
for (const id of relay?.conversationIds() ?? []) relay?.voidConversation(id);
|
|
34267
34048
|
return { kind: "success", text: '\u5DF2\u89E3\u9664\u6240\u6709\u914D\u5BF9\uFF1B\u624B\u673A\u7AEF\u4F1A\u663E\u793A"\u4E3B\u673A\u5DF2\u65AD\u5F00\uFF0C\u8BF7\u91CD\u65B0\u914D\u5BF9"\u3002' };
|
|
@@ -34296,39 +34077,13 @@ function applyInner(ctx, injected) {
|
|
|
34296
34077
|
if (child !== void 0) injectChildren.push(["commands", child]);
|
|
34297
34078
|
} catch {
|
|
34298
34079
|
}
|
|
34299
|
-
try {
|
|
34300
|
-
sidebar = startSidebarQr(ctx, {
|
|
34301
|
-
config: config2.sidebarQr,
|
|
34302
|
-
pairing: () => describeActivePairing(),
|
|
34303
|
-
workspaceOf(sessionId) {
|
|
34304
|
-
try {
|
|
34305
|
-
return kernel?.sessionWorkspace?.(sessionId);
|
|
34306
|
-
} catch {
|
|
34307
|
-
return void 0;
|
|
34308
|
-
}
|
|
34309
|
-
},
|
|
34310
|
-
log
|
|
34311
|
-
});
|
|
34312
|
-
} catch (error62) {
|
|
34313
|
-
sidebar = void 0;
|
|
34314
|
-
log("sidebar start threw\uFF08\u914D\u5BF9\u94FE\u8DEF\u4E0D\u53D7\u5F71\u54CD\uFF09", {
|
|
34315
|
-
message: String(error62?.message ?? error62).slice(0, 200)
|
|
34316
|
-
});
|
|
34317
|
-
}
|
|
34318
34080
|
return {
|
|
34319
34081
|
config: config2,
|
|
34320
34082
|
state,
|
|
34321
34083
|
createPairing,
|
|
34322
|
-
sessionWorkspace(sessionId) {
|
|
34323
|
-
try {
|
|
34324
|
-
return kernel?.sessionWorkspace?.(sessionId);
|
|
34325
|
-
} catch {
|
|
34326
|
-
return void 0;
|
|
34327
|
-
}
|
|
34328
|
-
},
|
|
34329
34084
|
stop() {
|
|
34330
34085
|
oneShot.cancelAll();
|
|
34331
|
-
|
|
34086
|
+
pillRoutes?.stop();
|
|
34332
34087
|
status.stop();
|
|
34333
34088
|
runtime?.stop();
|
|
34334
34089
|
relay?.stop();
|
|
@@ -34336,20 +34091,6 @@ function applyInner(ctx, injected) {
|
|
|
34336
34091
|
}
|
|
34337
34092
|
};
|
|
34338
34093
|
}
|
|
34339
|
-
function writePairingImage(file2, qrText, config2, log) {
|
|
34340
|
-
if (!config2.qrImage || !file2) return;
|
|
34341
|
-
qrPng(qrText).then((buffer) => {
|
|
34342
|
-
if (!writePrivateFile(file2, buffer)) {
|
|
34343
|
-
log("qr png write failed", { file: file2 });
|
|
34344
|
-
return;
|
|
34345
|
-
}
|
|
34346
|
-
if (!config2.qrOpen) return;
|
|
34347
|
-
const opener = process.platform === "darwin" ? "open" : process.platform === "win32" ? "cmd" : "xdg-open";
|
|
34348
|
-
const args = process.platform === "win32" ? ["/c", "start", "", file2] : [file2];
|
|
34349
|
-
execFile(opener, args, () => {
|
|
34350
|
-
});
|
|
34351
|
-
}).catch((error62) => log("qr png render failed", { message: String(error62?.message ?? error62) }));
|
|
34352
|
-
}
|
|
34353
34094
|
export {
|
|
34354
34095
|
DEFAULT_CONFIG,
|
|
34355
34096
|
apply,
|