@devicelink/dlink 0.2.2 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +24 -37
- package/index.js +343 -161
- package/index.js.map +4 -4
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// agent/src/cli/core/engine.ts
|
|
4
|
-
import
|
|
4
|
+
import crypto5 from "node:crypto";
|
|
5
5
|
|
|
6
6
|
// agent/src/cli/core/types.ts
|
|
7
7
|
var RuntimeFlags = class {
|
|
@@ -48,8 +48,8 @@ var Deferred = class {
|
|
|
48
48
|
};
|
|
49
49
|
this.rejectFn = () => {
|
|
50
50
|
};
|
|
51
|
-
this.promise = new Promise((
|
|
52
|
-
this.resolveFn =
|
|
51
|
+
this.promise = new Promise((resolve3, reject) => {
|
|
52
|
+
this.resolveFn = resolve3;
|
|
53
53
|
this.rejectFn = reject;
|
|
54
54
|
});
|
|
55
55
|
}
|
|
@@ -272,8 +272,8 @@ var ConfigurationContext = class {
|
|
|
272
272
|
};
|
|
273
273
|
|
|
274
274
|
// agent/src/cli/utils/config.builder.ts
|
|
275
|
-
import
|
|
276
|
-
import
|
|
275
|
+
import path4 from "node:path";
|
|
276
|
+
import os3 from "node:os";
|
|
277
277
|
import fs2 from "node:fs";
|
|
278
278
|
import dotenv from "dotenv";
|
|
279
279
|
import get2 from "lodash/get.js";
|
|
@@ -524,8 +524,8 @@ function canonicalizePath(input) {
|
|
|
524
524
|
out = out.toLowerCase();
|
|
525
525
|
return out;
|
|
526
526
|
}
|
|
527
|
-
function getDirectoryTag(
|
|
528
|
-
const canonical = canonicalizePath(
|
|
527
|
+
function getDirectoryTag(path13, groups = [3, 3, 3, 3]) {
|
|
528
|
+
const canonical = canonicalizePath(path13);
|
|
529
529
|
const hash = crypto3.createHash("sha256").update(canonical, "utf8").digest("base64url");
|
|
530
530
|
const clean = hash.replace(/[^a-zA-Z0-9]/g, "").toLowerCase();
|
|
531
531
|
const needed = groups.reduce((a, b) => a + b, 0);
|
|
@@ -533,8 +533,8 @@ function getDirectoryTag(path11, groups = [3, 3, 3, 3]) {
|
|
|
533
533
|
let i = 0;
|
|
534
534
|
return groups.map((len) => padded.slice(i, i += len)).join("-");
|
|
535
535
|
}
|
|
536
|
-
function pathToUuidLikeSha256(
|
|
537
|
-
const canonical = canonicalizePath(
|
|
536
|
+
function pathToUuidLikeSha256(path13) {
|
|
537
|
+
const canonical = canonicalizePath(path13);
|
|
538
538
|
const hex = crypto3.createHash("sha256").update(canonical, "utf8").digest("hex");
|
|
539
539
|
const h = hex.slice(0, 32);
|
|
540
540
|
const part1 = h.slice(0, 8);
|
|
@@ -545,6 +545,118 @@ function pathToUuidLikeSha256(path11) {
|
|
|
545
545
|
return `${part1}-${part2}-${part3}-${part4}-${part5}`;
|
|
546
546
|
}
|
|
547
547
|
|
|
548
|
+
// common/src/lib/utils/crypto.utils.ts
|
|
549
|
+
import * as crypto4 from "node:crypto";
|
|
550
|
+
function fingerprintFromPublicKeyPem(publicKeyPem) {
|
|
551
|
+
const keyObj = crypto4.createPublicKey(publicKeyPem);
|
|
552
|
+
if (keyObj.asymmetricKeyType !== "ed25519") {
|
|
553
|
+
throw new Error(`Unexpected key type: ${keyObj.asymmetricKeyType}`);
|
|
554
|
+
}
|
|
555
|
+
const spkiDer = keyObj.export({ format: "der", type: "spki" });
|
|
556
|
+
const hash = crypto4.createHash("sha256").update(spkiDer).digest("base64url");
|
|
557
|
+
return hash.slice(0, 16).toUpperCase();
|
|
558
|
+
}
|
|
559
|
+
function signConnect(params) {
|
|
560
|
+
const payload = Buffer.from(`${params.deviceId}|${params.ts}|${params.nonce}`, "utf8");
|
|
561
|
+
const key = crypto4.createPrivateKey(params.privateKeyPem);
|
|
562
|
+
const sig = crypto4.sign(null, payload, key);
|
|
563
|
+
return sig.toString("base64");
|
|
564
|
+
}
|
|
565
|
+
function signRefresh(params) {
|
|
566
|
+
const hash = crypto4.createHash("sha256").update(params.refreshToken, "utf8").digest("hex");
|
|
567
|
+
const payload = Buffer.from(`${params.deviceId}|${params.ts}|${params.nonce}|${hash}`, "utf8");
|
|
568
|
+
const key = crypto4.createPrivateKey(params.privateKeyPem);
|
|
569
|
+
const sig = crypto4.sign(null, payload, key);
|
|
570
|
+
return sig.toString("base64");
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
// common/src/lib/utils/async.utils.ts
|
|
574
|
+
function sleep(ms) {
|
|
575
|
+
return new Promise((resolve3) => setTimeout(resolve3, ms));
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
// common/src/lib/utils/path-display.utils.ts
|
|
579
|
+
import * as path2 from "node:path";
|
|
580
|
+
import * as os2 from "node:os";
|
|
581
|
+
function normalizeSlashes(s) {
|
|
582
|
+
return s.replace(/\\/g, "/");
|
|
583
|
+
}
|
|
584
|
+
function looksLikeWindowsDrivePath(s) {
|
|
585
|
+
return /^[A-Za-z]:[\\/]/.test(s) || /^[A-Za-z]:$/.test(s.trim());
|
|
586
|
+
}
|
|
587
|
+
function normalizeWindowsSeparatorsForMatch(s) {
|
|
588
|
+
if (!looksLikeWindowsDrivePath(s))
|
|
589
|
+
return s;
|
|
590
|
+
return s.replace(/\//g, "\\");
|
|
591
|
+
}
|
|
592
|
+
function toTildeHomePath(absolutePath) {
|
|
593
|
+
if (absolutePath == null)
|
|
594
|
+
return void 0;
|
|
595
|
+
const raw = String(absolutePath).trim();
|
|
596
|
+
if (raw === "")
|
|
597
|
+
return void 0;
|
|
598
|
+
if (raw.startsWith("~")) {
|
|
599
|
+
return normalizeSlashes(raw);
|
|
600
|
+
}
|
|
601
|
+
if (!looksLikeWindowsDrivePath(raw)) {
|
|
602
|
+
try {
|
|
603
|
+
const abs = path2.resolve(raw);
|
|
604
|
+
const home = path2.resolve(os2.homedir());
|
|
605
|
+
const rel = path2.relative(home, abs);
|
|
606
|
+
if (!rel.startsWith("..") && !path2.isAbsolute(rel)) {
|
|
607
|
+
if (rel === "")
|
|
608
|
+
return "~";
|
|
609
|
+
return "~/" + normalizeSlashes(rel);
|
|
610
|
+
}
|
|
611
|
+
} catch {
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
const win = normalizeWindowsSeparatorsForMatch(raw);
|
|
615
|
+
const winUsers = win.match(/^([A-Za-z]:)\\(?:[Uu][Ss][Ee][Rr][Ss])\\([^\\]+)(\\.*)?$/);
|
|
616
|
+
if (winUsers) {
|
|
617
|
+
const rest = (winUsers[3] ?? "").replace(/\\/g, "/");
|
|
618
|
+
return rest ? "~" + rest : "~";
|
|
619
|
+
}
|
|
620
|
+
const wsl = raw.match(/^\/mnt\/[a-z]\/(?:[Uu][Ss][Ee][Rr][Ss])\/[^/]+(\/.*)?$/i);
|
|
621
|
+
if (wsl) {
|
|
622
|
+
const rest = wsl[1] ?? "";
|
|
623
|
+
return rest ? "~" + normalizeSlashes(rest) : "~";
|
|
624
|
+
}
|
|
625
|
+
const mac = raw.match(/^\/Users\/[^/]+(\/.*)?$/);
|
|
626
|
+
if (mac) {
|
|
627
|
+
const rest = mac[1] ?? "";
|
|
628
|
+
return rest ? "~" + normalizeSlashes(rest) : "~";
|
|
629
|
+
}
|
|
630
|
+
const lin = raw.match(/^\/home\/[^/]+(\/.*)?$/);
|
|
631
|
+
if (lin) {
|
|
632
|
+
const rest = lin[1] ?? "";
|
|
633
|
+
return rest ? "~" + normalizeSlashes(rest) : "~";
|
|
634
|
+
}
|
|
635
|
+
const fbsd = raw.match(/^\/usr\/home\/[^/]+(\/.*)?$/);
|
|
636
|
+
if (fbsd) {
|
|
637
|
+
const rest = fbsd[1] ?? "";
|
|
638
|
+
return rest ? "~" + normalizeSlashes(rest) : "~";
|
|
639
|
+
}
|
|
640
|
+
const exp = raw.match(/^\/export\/home\/[^/]+(\/.*)?$/);
|
|
641
|
+
if (exp) {
|
|
642
|
+
const rest = exp[1] ?? "";
|
|
643
|
+
return rest ? "~" + normalizeSlashes(rest) : "~";
|
|
644
|
+
}
|
|
645
|
+
try {
|
|
646
|
+
return path2.basename(path2.resolve(raw));
|
|
647
|
+
} catch {
|
|
648
|
+
return raw.split(/[/\\]/).pop() ?? raw;
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
// common/src/lib/utils/path.utils.ts
|
|
653
|
+
import * as path3 from "node:path";
|
|
654
|
+
var isCaseInsensitiveFS = process.platform === "win32";
|
|
655
|
+
function pathDedupeKey(p) {
|
|
656
|
+
const resolved = path3.resolve(p);
|
|
657
|
+
return isCaseInsensitiveFS ? resolved.toLowerCase() : resolved;
|
|
658
|
+
}
|
|
659
|
+
|
|
548
660
|
// agent/src/cli/utils/config.builder.ts
|
|
549
661
|
var DEFAULT_CONFIG = {
|
|
550
662
|
version: 1,
|
|
@@ -576,9 +688,9 @@ var ConfigLoader = class {
|
|
|
576
688
|
loadProductionEnv() {
|
|
577
689
|
const candidates = [
|
|
578
690
|
// npm install: .env.production alongside index.js
|
|
579
|
-
|
|
691
|
+
path4.resolve(path4.dirname(process.argv[1] ?? "."), ".env.production"),
|
|
580
692
|
// monorepo dev: agent/.env.production
|
|
581
|
-
|
|
693
|
+
path4.resolve(process.cwd(), ".env.production")
|
|
582
694
|
];
|
|
583
695
|
for (const candidate of candidates) {
|
|
584
696
|
try {
|
|
@@ -626,7 +738,7 @@ var ConfigLoader = class {
|
|
|
626
738
|
withConfigFile(filename) {
|
|
627
739
|
try {
|
|
628
740
|
const configFolder = this.getApplicationConfigFolder();
|
|
629
|
-
const configPath =
|
|
741
|
+
const configPath = path4.join(configFolder, filename);
|
|
630
742
|
if (fs2.existsSync(configPath)) {
|
|
631
743
|
const raw = fs2.readFileSync(configPath, "utf8");
|
|
632
744
|
const parsed = JSON.parse(raw);
|
|
@@ -664,36 +776,49 @@ var ConfigLoader = class {
|
|
|
664
776
|
return this.argv?.bridgeWsUrl ?? this.safeGetEnvValue("DLINK_BRIDGE_URL") ?? this.safeGetConfigValue("bridgeWsUrl") ?? this.processEnv["DLINK_BRIDGE_URL"] ?? this.productionEnv["DLINK_BRIDGE_URL"] ?? void 0;
|
|
665
777
|
}
|
|
666
778
|
getDirectories() {
|
|
779
|
+
const collected = [];
|
|
780
|
+
collected.push(this.createDirectoryFromPath(process.cwd()));
|
|
667
781
|
if (this.argv?.rootPath) {
|
|
668
782
|
try {
|
|
669
|
-
|
|
783
|
+
collected.push(...this.parseDirectoriesFromString(this.argv.rootPath));
|
|
670
784
|
} catch {
|
|
671
|
-
|
|
785
|
+
collected.push(this.createDirectoryFromPath(this.argv.rootPath));
|
|
672
786
|
}
|
|
673
787
|
}
|
|
674
788
|
const envDirs = this.safeGetEnvValue("DLINK_DIRECTORIES") ?? this.safeGetEnvValue("DLINK_ROOTS");
|
|
675
789
|
if (envDirs) {
|
|
676
790
|
try {
|
|
677
|
-
|
|
791
|
+
collected.push(...this.parseDirectoriesFromString(envDirs));
|
|
678
792
|
} catch {
|
|
679
793
|
}
|
|
680
794
|
}
|
|
681
795
|
const configDirs = this.safeGetConfigValue("directories") ?? this.safeGetConfigValue("roots");
|
|
682
796
|
if (configDirs) {
|
|
683
797
|
try {
|
|
684
|
-
|
|
798
|
+
collected.push(...this.parseDirectoriesFromValue(configDirs));
|
|
685
799
|
} catch {
|
|
686
800
|
}
|
|
687
801
|
}
|
|
688
802
|
const processDirs = this.processEnv["DLINK_DIRECTORIES"] ?? this.processEnv["DLINK_ROOTS"];
|
|
689
803
|
if (processDirs) {
|
|
690
804
|
try {
|
|
691
|
-
|
|
805
|
+
collected.push(...this.parseDirectoriesFromString(processDirs));
|
|
692
806
|
} catch {
|
|
693
807
|
}
|
|
694
808
|
}
|
|
695
|
-
|
|
696
|
-
|
|
809
|
+
return this.deduplicateDirectories(collected);
|
|
810
|
+
}
|
|
811
|
+
deduplicateDirectories(dirs) {
|
|
812
|
+
const seen = /* @__PURE__ */ new Set();
|
|
813
|
+
const result = [];
|
|
814
|
+
for (const dir of dirs) {
|
|
815
|
+
const key = pathDedupeKey(dir.path);
|
|
816
|
+
if (seen.has(key))
|
|
817
|
+
continue;
|
|
818
|
+
seen.add(key);
|
|
819
|
+
result.push(dir);
|
|
820
|
+
}
|
|
821
|
+
return result;
|
|
697
822
|
}
|
|
698
823
|
getDevice() {
|
|
699
824
|
const deviceId = this.getDeviceId();
|
|
@@ -703,7 +828,7 @@ var ConfigLoader = class {
|
|
|
703
828
|
}
|
|
704
829
|
let deviceName = this.getDeviceName();
|
|
705
830
|
if (!deviceName) {
|
|
706
|
-
deviceName =
|
|
831
|
+
deviceName = os3.hostname() || "Unknown Device";
|
|
707
832
|
}
|
|
708
833
|
const deviceTag = this.getDeviceTag();
|
|
709
834
|
const keyBuilder = new KeyBuilder({
|
|
@@ -767,7 +892,7 @@ var ConfigLoader = class {
|
|
|
767
892
|
const processName = this.processEnv["DLINK_DEVICE_NAME"];
|
|
768
893
|
if (processName && !isEmpty2(processName))
|
|
769
894
|
return processName;
|
|
770
|
-
const hostname =
|
|
895
|
+
const hostname = os3.hostname();
|
|
771
896
|
if (hostname && !isEmpty2(hostname))
|
|
772
897
|
return hostname;
|
|
773
898
|
return void 0;
|
|
@@ -842,7 +967,7 @@ var ConfigLoader = class {
|
|
|
842
967
|
const directoryPath = get2(obj, "path");
|
|
843
968
|
const name = defaultTo(
|
|
844
969
|
get2(obj, "name"),
|
|
845
|
-
directoryPath ?
|
|
970
|
+
directoryPath ? path4.basename(directoryPath) : ""
|
|
846
971
|
);
|
|
847
972
|
if (!isEmpty2(id) && !isEmpty2(directoryPath)) {
|
|
848
973
|
const tag = getDirectoryTag(directoryPath);
|
|
@@ -857,18 +982,18 @@ var ConfigLoader = class {
|
|
|
857
982
|
return null;
|
|
858
983
|
}
|
|
859
984
|
createDirectoryFromPath(directoryPath) {
|
|
860
|
-
const normalizedPath =
|
|
985
|
+
const normalizedPath = path4.resolve(directoryPath);
|
|
861
986
|
const id = pathToUuidLikeSha256(normalizedPath);
|
|
862
|
-
const name =
|
|
987
|
+
const name = path4.basename(normalizedPath);
|
|
863
988
|
const tag = getDirectoryTag(normalizedPath);
|
|
864
989
|
return { id, name, path: normalizedPath, tag };
|
|
865
990
|
}
|
|
866
991
|
getApplicationConfigFolder() {
|
|
867
992
|
const appData = defaultTo(
|
|
868
993
|
this.processEnv["APPDATA"],
|
|
869
|
-
|
|
994
|
+
path4.join(os3.homedir(), "AppData", "Roaming")
|
|
870
995
|
);
|
|
871
|
-
return
|
|
996
|
+
return path4.join(appData, "DeviceLink");
|
|
872
997
|
}
|
|
873
998
|
};
|
|
874
999
|
|
|
@@ -962,6 +1087,13 @@ var Engine2 = class {
|
|
|
962
1087
|
getBus() {
|
|
963
1088
|
return this.bus;
|
|
964
1089
|
}
|
|
1090
|
+
hasRunningJobs() {
|
|
1091
|
+
for (const [, count] of this.laneActive) {
|
|
1092
|
+
if (count > 0)
|
|
1093
|
+
return true;
|
|
1094
|
+
}
|
|
1095
|
+
return false;
|
|
1096
|
+
}
|
|
965
1097
|
register(jobType) {
|
|
966
1098
|
const name = jobType.options.name;
|
|
967
1099
|
if (this.registry.has(name)) {
|
|
@@ -1124,7 +1256,7 @@ var Engine2 = class {
|
|
|
1124
1256
|
await this.loopOnce();
|
|
1125
1257
|
} catch (err) {
|
|
1126
1258
|
this.notifier.error(`Engine loop error: ${String(err)}`, { scope: "system" /* System */ });
|
|
1127
|
-
await new Promise((
|
|
1259
|
+
await new Promise((resolve3) => setTimeout(resolve3, 250));
|
|
1128
1260
|
}
|
|
1129
1261
|
}
|
|
1130
1262
|
}
|
|
@@ -1407,12 +1539,12 @@ var Engine2 = class {
|
|
|
1407
1539
|
this.wakeFn();
|
|
1408
1540
|
}
|
|
1409
1541
|
resetWake() {
|
|
1410
|
-
return new Promise((
|
|
1411
|
-
this.wakeFn =
|
|
1542
|
+
return new Promise((resolve3) => {
|
|
1543
|
+
this.wakeFn = resolve3;
|
|
1412
1544
|
});
|
|
1413
1545
|
}
|
|
1414
1546
|
newId() {
|
|
1415
|
-
return
|
|
1547
|
+
return crypto5.randomBytes(8).toString("hex");
|
|
1416
1548
|
}
|
|
1417
1549
|
};
|
|
1418
1550
|
function backoffMs(attempt) {
|
|
@@ -1455,10 +1587,10 @@ var EventBus = class {
|
|
|
1455
1587
|
};
|
|
1456
1588
|
|
|
1457
1589
|
// agent/src/storage/index.ts
|
|
1458
|
-
import * as
|
|
1590
|
+
import * as crypto6 from "node:crypto";
|
|
1459
1591
|
import * as fs3 from "node:fs/promises";
|
|
1460
|
-
import * as
|
|
1461
|
-
import * as
|
|
1592
|
+
import * as path5 from "node:path";
|
|
1593
|
+
import * as os4 from "node:os";
|
|
1462
1594
|
import defaultTo2 from "lodash/defaultTo.js";
|
|
1463
1595
|
var SALT = "DeviceLink-SecretStorage-v1";
|
|
1464
1596
|
var IV_LENGTH = 12;
|
|
@@ -1468,12 +1600,12 @@ var SECRETS_FILENAME = "secrets.enc.json";
|
|
|
1468
1600
|
function getSecretsPath() {
|
|
1469
1601
|
const appData = defaultTo2(
|
|
1470
1602
|
process.env["APPDATA"],
|
|
1471
|
-
|
|
1603
|
+
path5.join(os4.homedir(), "AppData", "Roaming")
|
|
1472
1604
|
);
|
|
1473
|
-
return
|
|
1605
|
+
return path5.join(appData, "DeviceLink", SECRETS_FILENAME);
|
|
1474
1606
|
}
|
|
1475
1607
|
function deriveKey(machineId) {
|
|
1476
|
-
return
|
|
1608
|
+
return crypto6.pbkdf2Sync(
|
|
1477
1609
|
machineId,
|
|
1478
1610
|
SALT,
|
|
1479
1611
|
1e5,
|
|
@@ -1503,7 +1635,7 @@ var EncryptedFileStorage = class {
|
|
|
1503
1635
|
this.loaded = true;
|
|
1504
1636
|
}
|
|
1505
1637
|
async save() {
|
|
1506
|
-
await fs3.mkdir(
|
|
1638
|
+
await fs3.mkdir(path5.dirname(this.secretsPath), { recursive: true });
|
|
1507
1639
|
const plain = JSON.stringify(this.cache);
|
|
1508
1640
|
const encrypted = this.encrypt(plain);
|
|
1509
1641
|
const tmp = this.secretsPath + ".tmp";
|
|
@@ -1511,8 +1643,8 @@ var EncryptedFileStorage = class {
|
|
|
1511
1643
|
await fs3.rename(tmp, this.secretsPath);
|
|
1512
1644
|
}
|
|
1513
1645
|
encrypt(plain) {
|
|
1514
|
-
const iv =
|
|
1515
|
-
const cipher =
|
|
1646
|
+
const iv = crypto6.randomBytes(IV_LENGTH);
|
|
1647
|
+
const cipher = crypto6.createCipheriv("aes-256-gcm", this.key, iv, {
|
|
1516
1648
|
authTagLength: TAG_LENGTH
|
|
1517
1649
|
});
|
|
1518
1650
|
const enc = Buffer.concat([
|
|
@@ -1527,7 +1659,7 @@ var EncryptedFileStorage = class {
|
|
|
1527
1659
|
const iv = buf.subarray(0, IV_LENGTH);
|
|
1528
1660
|
const tag = buf.subarray(IV_LENGTH, IV_LENGTH + TAG_LENGTH);
|
|
1529
1661
|
const enc = buf.subarray(IV_LENGTH + TAG_LENGTH);
|
|
1530
|
-
const decipher =
|
|
1662
|
+
const decipher = crypto6.createDecipheriv("aes-256-gcm", this.key, iv, {
|
|
1531
1663
|
authTagLength: TAG_LENGTH
|
|
1532
1664
|
});
|
|
1533
1665
|
decipher.setAuthTag(tag);
|
|
@@ -1588,7 +1720,7 @@ var LoadConfigJob = class extends JobType {
|
|
|
1588
1720
|
);
|
|
1589
1721
|
}
|
|
1590
1722
|
async run(input, ctx) {
|
|
1591
|
-
ctx.notifier.info("Loading configuration...");
|
|
1723
|
+
ctx.notifier.info("Loading configuration...", { scope: "system" /* System */ });
|
|
1592
1724
|
const configLoader = ctx.engine.configurationContext.loader;
|
|
1593
1725
|
const refreshRes = await ctx.engine.secrets.get("devicePrivateKey");
|
|
1594
1726
|
configLoader.setPrivateKeyCache(refreshRes.ok && refreshRes.value ? refreshRes.value : void 0);
|
|
@@ -1611,13 +1743,14 @@ var LoadConfigJob = class extends JobType {
|
|
|
1611
1743
|
return new LoadConfigOutput(true, config);
|
|
1612
1744
|
}
|
|
1613
1745
|
updateUi(config, ctx) {
|
|
1614
|
-
|
|
1615
|
-
ctx.notifier.info(`
|
|
1616
|
-
ctx.notifier.info(`
|
|
1617
|
-
ctx.notifier.info(`
|
|
1618
|
-
ctx.notifier.info(`Device
|
|
1619
|
-
ctx.notifier.info(`Device
|
|
1620
|
-
ctx.notifier.info(`Device
|
|
1746
|
+
const sys = { scope: "system" /* System */ };
|
|
1747
|
+
ctx.notifier.info(`Control base URL: ${config.controlBaseUrl}`, sys);
|
|
1748
|
+
ctx.notifier.info(`Bridge WebSocket URL: ${config.bridgeWsUrl}`, sys);
|
|
1749
|
+
ctx.notifier.info(`Directories: ${config.directories.map((directory) => directory.path).join(", ")}`, sys);
|
|
1750
|
+
ctx.notifier.info(`Device: ${config.device?.id}`, sys);
|
|
1751
|
+
ctx.notifier.info(`Device name: ${config.device?.name}`, sys);
|
|
1752
|
+
ctx.notifier.info(`Device tag: ${config.device?.tag}`, sys);
|
|
1753
|
+
ctx.notifier.info(`Device keys public: ${config.device?.keys?.public ? "***" : "(none)"}`, sys);
|
|
1621
1754
|
}
|
|
1622
1755
|
};
|
|
1623
1756
|
var loadConfigJob = new LoadConfigJob();
|
|
@@ -1647,7 +1780,7 @@ var ConsoleCommandJob = class extends JobType {
|
|
|
1647
1780
|
}
|
|
1648
1781
|
async run(input, ctx) {
|
|
1649
1782
|
ctx.notifier.info(`Executing console command: ${input.kind}`);
|
|
1650
|
-
await new Promise((
|
|
1783
|
+
await new Promise((resolve3) => setTimeout(resolve3, 2e3));
|
|
1651
1784
|
ctx.notifier.info(`Console command executed: ${input.kind}`);
|
|
1652
1785
|
ctx.notifier.success(`Console command executed: ${input.kind}`);
|
|
1653
1786
|
return new ConsoleCommandOutput(true);
|
|
@@ -1677,7 +1810,7 @@ var ContactServerJob = class extends JobType {
|
|
|
1677
1810
|
}
|
|
1678
1811
|
async run(input, ctx) {
|
|
1679
1812
|
ctx.notifier.info(`contact server mode=${input.mode}`, { scope: "system" /* System */ });
|
|
1680
|
-
await new Promise((
|
|
1813
|
+
await new Promise((resolve3) => setTimeout(resolve3, 200));
|
|
1681
1814
|
return new ContactServerOutput(true);
|
|
1682
1815
|
}
|
|
1683
1816
|
};
|
|
@@ -1705,7 +1838,7 @@ var SendServerResultJob = class extends JobType {
|
|
|
1705
1838
|
}
|
|
1706
1839
|
async run(input, ctx) {
|
|
1707
1840
|
ctx.notifier.info(`send result requestId=${input.requestId}`, { scope: "system" /* System */ });
|
|
1708
|
-
await new Promise((
|
|
1841
|
+
await new Promise((resolve3) => setTimeout(resolve3, 10));
|
|
1709
1842
|
return new SendServerResultOutput(true);
|
|
1710
1843
|
}
|
|
1711
1844
|
};
|
|
@@ -1724,8 +1857,8 @@ var ApiCall = class {
|
|
|
1724
1857
|
return this.handler(this.context, input);
|
|
1725
1858
|
}
|
|
1726
1859
|
};
|
|
1727
|
-
async function fetchJson(context,
|
|
1728
|
-
const url = `${context.configuration.controlBaseUrl}${
|
|
1860
|
+
async function fetchJson(context, path13, init) {
|
|
1861
|
+
const url = `${context.configuration.controlBaseUrl}${path13}`;
|
|
1729
1862
|
const res = await fetch(url, init);
|
|
1730
1863
|
if (!res.ok) {
|
|
1731
1864
|
const text = await res.text().catch(() => "");
|
|
@@ -1982,36 +2115,6 @@ var Api = class {
|
|
|
1982
2115
|
}
|
|
1983
2116
|
};
|
|
1984
2117
|
|
|
1985
|
-
// common/src/lib/utils/crypto.utils.ts
|
|
1986
|
-
import * as crypto6 from "node:crypto";
|
|
1987
|
-
function fingerprintFromPublicKeyPem(publicKeyPem) {
|
|
1988
|
-
const keyObj = crypto6.createPublicKey(publicKeyPem);
|
|
1989
|
-
if (keyObj.asymmetricKeyType !== "ed25519") {
|
|
1990
|
-
throw new Error(`Unexpected key type: ${keyObj.asymmetricKeyType}`);
|
|
1991
|
-
}
|
|
1992
|
-
const spkiDer = keyObj.export({ format: "der", type: "spki" });
|
|
1993
|
-
const hash = crypto6.createHash("sha256").update(spkiDer).digest("base64url");
|
|
1994
|
-
return hash.slice(0, 16).toUpperCase();
|
|
1995
|
-
}
|
|
1996
|
-
function signConnect(params) {
|
|
1997
|
-
const payload = Buffer.from(`${params.deviceId}|${params.ts}|${params.nonce}`, "utf8");
|
|
1998
|
-
const key = crypto6.createPrivateKey(params.privateKeyPem);
|
|
1999
|
-
const sig = crypto6.sign(null, payload, key);
|
|
2000
|
-
return sig.toString("base64");
|
|
2001
|
-
}
|
|
2002
|
-
function signRefresh(params) {
|
|
2003
|
-
const hash = crypto6.createHash("sha256").update(params.refreshToken, "utf8").digest("hex");
|
|
2004
|
-
const payload = Buffer.from(`${params.deviceId}|${params.ts}|${params.nonce}|${hash}`, "utf8");
|
|
2005
|
-
const key = crypto6.createPrivateKey(params.privateKeyPem);
|
|
2006
|
-
const sig = crypto6.sign(null, payload, key);
|
|
2007
|
-
return sig.toString("base64");
|
|
2008
|
-
}
|
|
2009
|
-
|
|
2010
|
-
// common/src/lib/utils/async.utils.ts
|
|
2011
|
-
function sleep(ms) {
|
|
2012
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
2013
|
-
}
|
|
2014
|
-
|
|
2015
2118
|
// agent/src/cli/jobs/refreshTokens.ts
|
|
2016
2119
|
function isWrappedOk(x) {
|
|
2017
2120
|
if (typeof x !== "object" || x === null)
|
|
@@ -3312,6 +3415,9 @@ var ConsoleOrchestrator = class {
|
|
|
3312
3415
|
ctx.bus.emit("runtime:debug:changed", { enabled: false });
|
|
3313
3416
|
ctx.notifier.success("Debug mode disabled");
|
|
3314
3417
|
break;
|
|
3418
|
+
case "replace" /* Replace */:
|
|
3419
|
+
ctx.bus.emit("auth:session_conflict:resolve", { choice: "USE_NEW" });
|
|
3420
|
+
break;
|
|
3315
3421
|
default:
|
|
3316
3422
|
ctx.notifier.warn(`Command not implemented: ${input.kind}`);
|
|
3317
3423
|
break;
|
|
@@ -3361,6 +3467,9 @@ var ConsoleOrchestrator = class {
|
|
|
3361
3467
|
if (cmd === "doctor") {
|
|
3362
3468
|
return { handled: true, kind: "doctor" /* Doctor */ };
|
|
3363
3469
|
}
|
|
3470
|
+
if (cmd === "replace" || cmd === "yes" || cmd === "y") {
|
|
3471
|
+
return { handled: true, kind: "replace" /* Replace */ };
|
|
3472
|
+
}
|
|
3364
3473
|
}
|
|
3365
3474
|
return { handled: false };
|
|
3366
3475
|
});
|
|
@@ -3564,7 +3673,7 @@ var RecoverableSocket = class {
|
|
|
3564
3673
|
}
|
|
3565
3674
|
async connectWebSocketOnce() {
|
|
3566
3675
|
const myConnId = ++this.connId;
|
|
3567
|
-
return new Promise((
|
|
3676
|
+
return new Promise((resolve3, reject) => {
|
|
3568
3677
|
if (this.stopped) {
|
|
3569
3678
|
reject(new Error("Socket is stopped"));
|
|
3570
3679
|
return;
|
|
@@ -3582,7 +3691,7 @@ var RecoverableSocket = class {
|
|
|
3582
3691
|
return;
|
|
3583
3692
|
settled = true;
|
|
3584
3693
|
cleanup();
|
|
3585
|
-
|
|
3694
|
+
resolve3();
|
|
3586
3695
|
};
|
|
3587
3696
|
const settleReject = (error, ws2) => {
|
|
3588
3697
|
if (settled)
|
|
@@ -3810,7 +3919,6 @@ var WebSocketOrchestrator = class {
|
|
|
3810
3919
|
};
|
|
3811
3920
|
return {
|
|
3812
3921
|
"config:cache:validated": async (_, ctx) => initSocket("config:cache:validated", ctx),
|
|
3813
|
-
"auth:signin:completed": async (_, ctx) => initSocket("auth:signin:completed", ctx),
|
|
3814
3922
|
"auth:signout:requested": async (_, ctx) => {
|
|
3815
3923
|
ctx.notifier.info("WS: sign-out requested -> disconnecting");
|
|
3816
3924
|
this.stopped = true;
|
|
@@ -3829,7 +3937,10 @@ var WebSocketOrchestrator = class {
|
|
|
3829
3937
|
ctx.notifier.info("WS: reconnect requested by auth", { scope: "system" /* System */ });
|
|
3830
3938
|
this.retryAttempt = 0;
|
|
3831
3939
|
this.clearCountdown();
|
|
3832
|
-
this.socket
|
|
3940
|
+
if (this.socket) {
|
|
3941
|
+
this.socket.stop();
|
|
3942
|
+
this.socket = null;
|
|
3943
|
+
}
|
|
3833
3944
|
this.ensureStarted(ctx, "auth:ws:reconnect");
|
|
3834
3945
|
},
|
|
3835
3946
|
// the only sender
|
|
@@ -3848,6 +3959,11 @@ var WebSocketOrchestrator = class {
|
|
|
3848
3959
|
ctx.notifier.info(`WS: connect loop already running (reason=${reason})`, { scope: "system" /* System */ });
|
|
3849
3960
|
return;
|
|
3850
3961
|
}
|
|
3962
|
+
const ws = this.socket?.getSocket();
|
|
3963
|
+
if (ws && ws.readyState <= 1) {
|
|
3964
|
+
ctx.notifier.info(`WS: already connected/connecting, skipping (reason=${reason})`, { scope: "system" /* System */ });
|
|
3965
|
+
return;
|
|
3966
|
+
}
|
|
3851
3967
|
this.connectLoopInFlight = this.startOrJoinConnectLoop(ctx, reason).finally(() => {
|
|
3852
3968
|
this.connectLoopInFlight = null;
|
|
3853
3969
|
});
|
|
@@ -3862,32 +3978,47 @@ var WebSocketOrchestrator = class {
|
|
|
3862
3978
|
this.lastUrl = url;
|
|
3863
3979
|
ctx.notifier.info(`WS: ensure connection (reason=${reason}) url=${url}`, { scope: "system" /* System */ });
|
|
3864
3980
|
if (!this.socket) {
|
|
3865
|
-
|
|
3981
|
+
const sock = new RecoverableSocket(url, {
|
|
3866
3982
|
abortSignal: ctx.signal,
|
|
3867
3983
|
connectionTimeoutMs: 1e4,
|
|
3868
3984
|
heartbeatIntervalMs: 2e4,
|
|
3869
3985
|
pongTimeoutMs: 1e4
|
|
3870
3986
|
});
|
|
3871
|
-
this.socket
|
|
3872
|
-
|
|
3987
|
+
this.socket = sock;
|
|
3988
|
+
sock.registerOnStateChangeCallback((state) => this.handleStateChange(state, ctx));
|
|
3989
|
+
sock.registerOnErrorCallback((error) => {
|
|
3873
3990
|
ctx.notifier.error(`WS: transport error: ${error.message}`);
|
|
3874
3991
|
});
|
|
3875
|
-
|
|
3992
|
+
sock.registerOnConnectedCallback((_ws) => {
|
|
3993
|
+
if (this.socket !== sock)
|
|
3994
|
+
return;
|
|
3876
3995
|
this.retryAttempt = 0;
|
|
3877
3996
|
this.clearCountdown();
|
|
3878
|
-
ctx.notifier.success(`WS: connected (${url})`);
|
|
3997
|
+
ctx.notifier.success(ctx.flags.debug ? `WS: connected (${url})` : `WS: connected`);
|
|
3879
3998
|
ctx.bus.emit("ws:connected", { url, attempt: 0 });
|
|
3880
3999
|
});
|
|
3881
|
-
|
|
4000
|
+
sock.registerOnDisconnectedCallback((code, reasonStr) => {
|
|
4001
|
+
if (this.socket !== sock) {
|
|
4002
|
+
ctx.notifier.info(`WS: ignoring stale disconnect (code=${code})`, { scope: "system" /* System */ });
|
|
4003
|
+
return;
|
|
4004
|
+
}
|
|
3882
4005
|
ctx.notifier.warn(`WS: disconnected code=${code} reason=${reasonStr || "-"}`);
|
|
3883
4006
|
ctx.bus.emit("ws:disconnected", { code, reason: reasonStr });
|
|
3884
4007
|
if (code === 4001) {
|
|
3885
4008
|
ctx.notifier.info("WS: auth disconnect (4001) -> waiting for auth", { scope: "system" /* System */ });
|
|
3886
4009
|
return;
|
|
3887
4010
|
}
|
|
4011
|
+
if (code === 4011) {
|
|
4012
|
+
this.stopped = true;
|
|
4013
|
+
this.socket?.stop();
|
|
4014
|
+
this.socket = null;
|
|
4015
|
+
return;
|
|
4016
|
+
}
|
|
3888
4017
|
this.ensureStarted(ctx, "socket:disconnected");
|
|
3889
4018
|
});
|
|
3890
|
-
|
|
4019
|
+
sock.registerOnMessageCallback((data) => {
|
|
4020
|
+
if (this.socket !== sock)
|
|
4021
|
+
return;
|
|
3891
4022
|
const text = typeof data === "string" ? data : data.toString("utf8");
|
|
3892
4023
|
ctx.bus.emit("ws:rx", { text });
|
|
3893
4024
|
});
|
|
@@ -3981,8 +4112,8 @@ var WebSocketOrchestrator = class {
|
|
|
3981
4112
|
|
|
3982
4113
|
// agent/src/cli/utils/configuration.writer.ts
|
|
3983
4114
|
import fs4 from "node:fs";
|
|
3984
|
-
import
|
|
3985
|
-
import
|
|
4115
|
+
import path6 from "node:path";
|
|
4116
|
+
import os5 from "node:os";
|
|
3986
4117
|
import defaultTo3 from "lodash/defaultTo.js";
|
|
3987
4118
|
var ConfigurationWriter = class {
|
|
3988
4119
|
/**
|
|
@@ -3991,7 +4122,7 @@ var ConfigurationWriter = class {
|
|
|
3991
4122
|
*/
|
|
3992
4123
|
static save(config) {
|
|
3993
4124
|
const configDir = this.getApplicationConfigFolder();
|
|
3994
|
-
const configPath =
|
|
4125
|
+
const configPath = path6.join(configDir, "config.json");
|
|
3995
4126
|
const safe = this.stripSecrets(config);
|
|
3996
4127
|
fs4.mkdirSync(configDir, { recursive: true });
|
|
3997
4128
|
fs4.writeFileSync(configPath, JSON.stringify(safe, null, 2), "utf8");
|
|
@@ -4018,9 +4149,9 @@ var ConfigurationWriter = class {
|
|
|
4018
4149
|
static getApplicationConfigFolder() {
|
|
4019
4150
|
const appData = defaultTo3(
|
|
4020
4151
|
process.env["APPDATA"],
|
|
4021
|
-
|
|
4152
|
+
path6.join(os5.homedir(), "AppData", "Roaming")
|
|
4022
4153
|
);
|
|
4023
|
-
return
|
|
4154
|
+
return path6.join(appData, "DeviceLink");
|
|
4024
4155
|
}
|
|
4025
4156
|
};
|
|
4026
4157
|
|
|
@@ -4558,6 +4689,7 @@ var AuthOrchestrator = class {
|
|
|
4558
4689
|
connectionId = null;
|
|
4559
4690
|
sessionId = null;
|
|
4560
4691
|
accessToken;
|
|
4692
|
+
rejectedByConflict = false;
|
|
4561
4693
|
refreshGroupKey = "auth:refresh";
|
|
4562
4694
|
refreshCycle = 0;
|
|
4563
4695
|
refreshScheduler = null;
|
|
@@ -4591,6 +4723,11 @@ var AuthOrchestrator = class {
|
|
|
4591
4723
|
"Auth: signin completed; requesting ws reconnect",
|
|
4592
4724
|
{ scope: "system" /* System */ }
|
|
4593
4725
|
);
|
|
4726
|
+
this.rejectedByConflict = false;
|
|
4727
|
+
if (this.refreshRetryScheduler) {
|
|
4728
|
+
this.refreshRetryScheduler.stop();
|
|
4729
|
+
this.refreshRetryScheduler = null;
|
|
4730
|
+
}
|
|
4594
4731
|
this.ensureAccessExpPersisted(ctx, "signin");
|
|
4595
4732
|
await this.ensureRefreshScheduling(ctx, "signin");
|
|
4596
4733
|
ctx.bus.emit("auth:ws:reconnect", void 0);
|
|
@@ -4647,6 +4784,14 @@ var AuthOrchestrator = class {
|
|
|
4647
4784
|
{ scope: "system" /* System */ }
|
|
4648
4785
|
);
|
|
4649
4786
|
this.resetConnectionState();
|
|
4787
|
+
if (p.code === 4011) {
|
|
4788
|
+
ctx.notifier.warn(
|
|
4789
|
+
'Connection rejected - another device is active on this workspace. Run "signin" to try again.'
|
|
4790
|
+
);
|
|
4791
|
+
this.rejectedByConflict = true;
|
|
4792
|
+
ctx.bus.emit("ws:disconnect", { reason: "REPLACEMENT_REJECTED" });
|
|
4793
|
+
return;
|
|
4794
|
+
}
|
|
4650
4795
|
if (p.code === 4001) {
|
|
4651
4796
|
const refreshRes = await ctx.engine.secrets.get("refreshToken");
|
|
4652
4797
|
if (!refreshRes.ok || !refreshRes.value) {
|
|
@@ -4669,7 +4814,8 @@ var AuthOrchestrator = class {
|
|
|
4669
4814
|
// --- boot path unchanged except it must schedule refresh always ---
|
|
4670
4815
|
async onEventConfigLoaded(config, ctx) {
|
|
4671
4816
|
ctx.notifier.info(
|
|
4672
|
-
"Initializing with cached configuration"
|
|
4817
|
+
"Initializing with cached configuration",
|
|
4818
|
+
{ scope: "system" /* System */ }
|
|
4673
4819
|
);
|
|
4674
4820
|
if (!config.device?.id) {
|
|
4675
4821
|
ctx.notifier.error("Device ID is not available");
|
|
@@ -4698,7 +4844,6 @@ var AuthOrchestrator = class {
|
|
|
4698
4844
|
ctx.notifier.warn("Auth: not signed in; run signin");
|
|
4699
4845
|
return;
|
|
4700
4846
|
}
|
|
4701
|
-
ctx.bus.emit("auth:ws:reconnect", void 0);
|
|
4702
4847
|
}
|
|
4703
4848
|
isAccessLikelyUsable(ctx) {
|
|
4704
4849
|
const jwt = this.accessToken;
|
|
@@ -4715,7 +4860,8 @@ var AuthOrchestrator = class {
|
|
|
4715
4860
|
async waitForApproval(api, startResponse, ctx) {
|
|
4716
4861
|
const deadline = Date.now() + startResponse.expires_in * 1e3;
|
|
4717
4862
|
ctx.notifier.info(
|
|
4718
|
-
"Waiting for approval... (streaming)"
|
|
4863
|
+
"Waiting for approval... (streaming)",
|
|
4864
|
+
{ scope: "system" /* System */ }
|
|
4719
4865
|
);
|
|
4720
4866
|
try {
|
|
4721
4867
|
return await api.deviceAuthStream({
|
|
@@ -4812,7 +4958,8 @@ var AuthOrchestrator = class {
|
|
|
4812
4958
|
}
|
|
4813
4959
|
if (streamResp.needs_proof) {
|
|
4814
4960
|
ctx.notifier.info(
|
|
4815
|
-
"Sending proof-of-possession signature..."
|
|
4961
|
+
"Sending proof-of-possession signature...",
|
|
4962
|
+
{ scope: "system" /* System */ }
|
|
4816
4963
|
);
|
|
4817
4964
|
const privKeyObj = crypto8.createPrivateKey(privateKeyPem);
|
|
4818
4965
|
const challengeBytes = Buffer.from(
|
|
@@ -4827,9 +4974,9 @@ var AuthOrchestrator = class {
|
|
|
4827
4974
|
challenge: startResponse.challenge,
|
|
4828
4975
|
signature_b64: signatureB64
|
|
4829
4976
|
});
|
|
4830
|
-
ctx.notifier.info("Proof-of-possession verified");
|
|
4977
|
+
ctx.notifier.info("Proof-of-possession verified", { scope: "system" /* System */ });
|
|
4831
4978
|
}
|
|
4832
|
-
ctx.notifier.info("Fetching tokens...");
|
|
4979
|
+
ctx.notifier.info("Fetching tokens...", { scope: "system" /* System */ });
|
|
4833
4980
|
const tokenResp = await api.deviceAuthToken({
|
|
4834
4981
|
device_code: startResponse.device_code
|
|
4835
4982
|
});
|
|
@@ -4963,6 +5110,9 @@ var AuthOrchestrator = class {
|
|
|
4963
5110
|
ctx.bus.emit("auth:session_conflict", msg);
|
|
4964
5111
|
void this.handleSessionConflict(ctx, msg);
|
|
4965
5112
|
return;
|
|
5113
|
+
case "quiesce":
|
|
5114
|
+
void this.handleQuiesce(ctx, msg);
|
|
5115
|
+
return;
|
|
4966
5116
|
default:
|
|
4967
5117
|
return;
|
|
4968
5118
|
}
|
|
@@ -4973,14 +5123,41 @@ var AuthOrchestrator = class {
|
|
|
4973
5123
|
this.conflictTimer = null;
|
|
4974
5124
|
}
|
|
4975
5125
|
this.pendingConflictRequest = msg;
|
|
5126
|
+
ctx.notifier.warn(
|
|
5127
|
+
`Another device is already connected to this workspace. Type "replace" to disconnect it, or this connection will be rejected in ${Math.round(msg.deadline_ms / 1e3)}s.`
|
|
5128
|
+
);
|
|
4976
5129
|
const deadlineMs = Math.min(msg.deadline_ms, 15e3);
|
|
4977
5130
|
this.conflictTimer = setTimeout(() => {
|
|
4978
5131
|
this.conflictTimer = null;
|
|
4979
5132
|
if (this.pendingConflictRequest?.new_session_id === msg.new_session_id) {
|
|
5133
|
+
ctx.notifier.info("No response - keeping existing device connected.");
|
|
4980
5134
|
this.sendSessionConflictResolve(ctx, "KEEP_OLD");
|
|
4981
5135
|
}
|
|
4982
5136
|
}, deadlineMs);
|
|
4983
5137
|
}
|
|
5138
|
+
async handleQuiesce(ctx, msg) {
|
|
5139
|
+
ctx.notifier.warn(
|
|
5140
|
+
`This device is being replaced by another connection. Finishing in-flight operations...`
|
|
5141
|
+
);
|
|
5142
|
+
const hasActiveJobs = ctx.engine.hasRunningJobs?.() ?? false;
|
|
5143
|
+
if (hasActiveJobs) {
|
|
5144
|
+
const deadline = Math.min(msg.deadline_ms - 2e3, 12e3);
|
|
5145
|
+
const start = Date.now();
|
|
5146
|
+
while (Date.now() - start < deadline) {
|
|
5147
|
+
if (!(ctx.engine.hasRunningJobs?.() ?? false))
|
|
5148
|
+
break;
|
|
5149
|
+
await new Promise((r) => setTimeout(r, 500));
|
|
5150
|
+
}
|
|
5151
|
+
}
|
|
5152
|
+
const status = ctx.engine.hasRunningJobs?.() ?? false ? "BUSY" : "IDLE";
|
|
5153
|
+
this.send(ctx, {
|
|
5154
|
+
type: "quiesce_ack",
|
|
5155
|
+
session_id: msg.session_id,
|
|
5156
|
+
status
|
|
5157
|
+
});
|
|
5158
|
+
ctx.notifier.info("Device replaced by new connection. Disconnecting.");
|
|
5159
|
+
this.socket?.stop();
|
|
5160
|
+
}
|
|
4984
5161
|
sendSessionConflictResolve(ctx, choice) {
|
|
4985
5162
|
const req = this.pendingConflictRequest;
|
|
4986
5163
|
if (!req)
|
|
@@ -5242,6 +5419,10 @@ var AuthOrchestrator = class {
|
|
|
5242
5419
|
if (this.accessToken && !ctx.engine.configurationContext.configuration.tokens?.accessTokenExpSec) {
|
|
5243
5420
|
this.ensureAccessExpPersisted(ctx, reason);
|
|
5244
5421
|
}
|
|
5422
|
+
if (this.refreshRetryScheduler) {
|
|
5423
|
+
this.refreshRetryScheduler.stop();
|
|
5424
|
+
this.refreshRetryScheduler = null;
|
|
5425
|
+
}
|
|
5245
5426
|
this.scheduleSilentRefresh(ctx, reason);
|
|
5246
5427
|
}
|
|
5247
5428
|
async scheduleSilentRefresh(ctx, reason) {
|
|
@@ -5440,7 +5621,7 @@ var AuthOrchestrator = class {
|
|
|
5440
5621
|
|
|
5441
5622
|
// agent/src/cli/orchestrators/tools/executers/fs-list.ts
|
|
5442
5623
|
import fs7 from "node:fs/promises";
|
|
5443
|
-
import
|
|
5624
|
+
import path9 from "node:path";
|
|
5444
5625
|
|
|
5445
5626
|
// agent/src/cli/orchestrators/tools/executers/base-executer.ts
|
|
5446
5627
|
var BaseExecuter = class {
|
|
@@ -5476,7 +5657,7 @@ var BaseExecuter = class {
|
|
|
5476
5657
|
};
|
|
5477
5658
|
|
|
5478
5659
|
// agent/src/cli/orchestrators/tools/util/ignore.ts
|
|
5479
|
-
import
|
|
5660
|
+
import path7 from "node:path";
|
|
5480
5661
|
import fs5 from "node:fs";
|
|
5481
5662
|
function matchesDefaultDenylist(relPath) {
|
|
5482
5663
|
const normalized = relPath.replace(/\\/g, "/");
|
|
@@ -5527,7 +5708,7 @@ function loadIgnoreRules(cfg, directory_id, workspace_id) {
|
|
|
5527
5708
|
return DEFAULT_IGNORE_PATTERNS;
|
|
5528
5709
|
}
|
|
5529
5710
|
const rules = [];
|
|
5530
|
-
const ignoreFilePath =
|
|
5711
|
+
const ignoreFilePath = path7.join(directory.path, ".devicelinkignore");
|
|
5531
5712
|
if (fs5.existsSync(ignoreFilePath)) {
|
|
5532
5713
|
try {
|
|
5533
5714
|
const content = fs5.readFileSync(ignoreFilePath, "utf-8");
|
|
@@ -5575,7 +5756,7 @@ function shouldIgnore(relPath, isDir, ctx, cfg) {
|
|
|
5575
5756
|
}
|
|
5576
5757
|
return { shouldIgnore: false };
|
|
5577
5758
|
}
|
|
5578
|
-
function matchesGlob(
|
|
5759
|
+
function matchesGlob(path13, pattern, isDir) {
|
|
5579
5760
|
let regexStr = "^";
|
|
5580
5761
|
let i = 0;
|
|
5581
5762
|
while (i < pattern.length) {
|
|
@@ -5602,15 +5783,15 @@ function matchesGlob(path11, pattern, isDir) {
|
|
|
5602
5783
|
regexStr += "$";
|
|
5603
5784
|
try {
|
|
5604
5785
|
const regex = new RegExp(regexStr, "i");
|
|
5605
|
-
return regex.test(
|
|
5786
|
+
return regex.test(path13);
|
|
5606
5787
|
} catch {
|
|
5607
|
-
return
|
|
5788
|
+
return path13.toLowerCase().includes(pattern.toLowerCase());
|
|
5608
5789
|
}
|
|
5609
5790
|
}
|
|
5610
5791
|
|
|
5611
5792
|
// agent/src/cli/orchestrators/tools/util/_util.ts
|
|
5612
5793
|
import fs6 from "node:fs/promises";
|
|
5613
|
-
import
|
|
5794
|
+
import path8 from "node:path";
|
|
5614
5795
|
function normalizeRelPath(input) {
|
|
5615
5796
|
const raw = (input ?? "").trim();
|
|
5616
5797
|
if (!raw)
|
|
@@ -5629,7 +5810,7 @@ function normalizeRelPath(input) {
|
|
|
5629
5810
|
});
|
|
5630
5811
|
}
|
|
5631
5812
|
const posixish = raw.replace(/\\/g, "/");
|
|
5632
|
-
const norm =
|
|
5813
|
+
const norm = path8.posix.normalize(posixish);
|
|
5633
5814
|
if (norm === ".." || norm.startsWith("../")) {
|
|
5634
5815
|
throw toolErr("PATH_ESCAPE", "Path escapes the root.", {
|
|
5635
5816
|
hint: 'Paths cannot use ".." to escape the configured root directory. Use paths relative to the root, like "src/file.ts" or "subdir/file.ts".'
|
|
@@ -5644,19 +5825,19 @@ function normalizeRelPath(input) {
|
|
|
5644
5825
|
}
|
|
5645
5826
|
async function resolveInRoot(rootPath, inputRel) {
|
|
5646
5827
|
const rel = normalizeRelPath(inputRel);
|
|
5647
|
-
const absRoot =
|
|
5828
|
+
const absRoot = path8.resolve(rootPath);
|
|
5648
5829
|
const realRoot = await fs6.realpath(absRoot);
|
|
5649
|
-
const candidate =
|
|
5650
|
-
const relToRoot =
|
|
5651
|
-
if (relToRoot.startsWith("..") ||
|
|
5830
|
+
const candidate = path8.resolve(realRoot, rel === "." ? "" : rel);
|
|
5831
|
+
const relToRoot = path8.relative(realRoot, candidate);
|
|
5832
|
+
if (relToRoot.startsWith("..") || path8.isAbsolute(relToRoot)) {
|
|
5652
5833
|
throw toolErr("PATH_ESCAPE", "Path escapes the root.", {
|
|
5653
5834
|
hint: "The resolved path escapes the configured root directory. Ensure the path stays within the root boundaries."
|
|
5654
5835
|
});
|
|
5655
5836
|
}
|
|
5656
5837
|
try {
|
|
5657
5838
|
const realCandidate = await fs6.realpath(candidate);
|
|
5658
|
-
const rel2 =
|
|
5659
|
-
if (rel2.startsWith("..") ||
|
|
5839
|
+
const rel2 = path8.relative(realRoot, realCandidate);
|
|
5840
|
+
if (rel2.startsWith("..") || path8.isAbsolute(rel2)) {
|
|
5660
5841
|
throw toolErr("PATH_ESCAPE", "Symlink/junction escape detected.", {
|
|
5661
5842
|
hint: "A symlink or junction in the path resolves outside the configured root directory. Ensure all symlinks stay within the root boundaries."
|
|
5662
5843
|
});
|
|
@@ -5669,16 +5850,16 @@ async function resolveInRoot(rootPath, inputRel) {
|
|
|
5669
5850
|
for (let i = parts.length; i > 0; i--) {
|
|
5670
5851
|
const parentRel = parts.slice(0, i).join("/");
|
|
5671
5852
|
const restRel = parts.slice(i).join("/");
|
|
5672
|
-
const parentCandidate =
|
|
5853
|
+
const parentCandidate = path8.resolve(realRoot, parentRel);
|
|
5673
5854
|
try {
|
|
5674
5855
|
const realParent = await fs6.realpath(parentCandidate);
|
|
5675
|
-
const relParent =
|
|
5676
|
-
if (relParent.startsWith("..") ||
|
|
5856
|
+
const relParent = path8.relative(realRoot, realParent);
|
|
5857
|
+
if (relParent.startsWith("..") || path8.isAbsolute(relParent)) {
|
|
5677
5858
|
throw toolErr("PATH_ESCAPE", "Symlink/junction escape detected.", {
|
|
5678
5859
|
hint: "A symlink or junction in the path resolves outside the configured root directory. Ensure all symlinks stay within the root boundaries."
|
|
5679
5860
|
});
|
|
5680
5861
|
}
|
|
5681
|
-
return { absTarget: restRel ?
|
|
5862
|
+
return { absTarget: restRel ? path8.join(realParent, restRel) : realParent, rel };
|
|
5682
5863
|
} catch (err) {
|
|
5683
5864
|
if (err?.code === "ENOENT")
|
|
5684
5865
|
continue;
|
|
@@ -5860,8 +6041,8 @@ var FsListExecuter = class extends BaseExecuter {
|
|
|
5860
6041
|
directory_path: directory.path
|
|
5861
6042
|
};
|
|
5862
6043
|
for (const d of visibleDirents) {
|
|
5863
|
-
const full =
|
|
5864
|
-
const rawRelPath = rel === "." ? d.name :
|
|
6044
|
+
const full = path9.join(absTarget, d.name);
|
|
6045
|
+
const rawRelPath = rel === "." ? d.name : path9.posix.join(rel, d.name);
|
|
5865
6046
|
const entryRelPath = rawRelPath.replace(/\\/g, "/");
|
|
5866
6047
|
let type = "other";
|
|
5867
6048
|
if (d.isDirectory())
|
|
@@ -5899,7 +6080,7 @@ var FsListExecuter = class extends BaseExecuter {
|
|
|
5899
6080
|
} catch {
|
|
5900
6081
|
}
|
|
5901
6082
|
}
|
|
5902
|
-
const ext = type === "file" ?
|
|
6083
|
+
const ext = type === "file" ? path9.extname(d.name).slice(1) : void 0;
|
|
5903
6084
|
if (ext && (detail === "stat" || detail === "digest")) {
|
|
5904
6085
|
extCounts.set(ext, (extCounts.get(ext) ?? 0) + 1);
|
|
5905
6086
|
}
|
|
@@ -6111,11 +6292,11 @@ var FsInfoExecuter = class extends BaseExecuter {
|
|
|
6111
6292
|
|
|
6112
6293
|
// agent/src/cli/orchestrators/tools/executers/fs-tree.ts
|
|
6113
6294
|
import fs11 from "node:fs/promises";
|
|
6114
|
-
import
|
|
6295
|
+
import path11 from "node:path";
|
|
6115
6296
|
|
|
6116
6297
|
// agent/src/cli/orchestrators/tools/executers/fs-grep.ts
|
|
6117
6298
|
import fs10 from "node:fs/promises";
|
|
6118
|
-
import
|
|
6299
|
+
import path10 from "node:path";
|
|
6119
6300
|
function matchesFileGlob(relPath, fileGlob) {
|
|
6120
6301
|
let regexStr = "^";
|
|
6121
6302
|
let i = 0;
|
|
@@ -6211,8 +6392,8 @@ async function grepDirectory(absPath, relPath, rootPath, query, mode, fileGlob,
|
|
|
6211
6392
|
for (const entry of entries) {
|
|
6212
6393
|
if (filesScanned.value >= maxFiles || matches.length >= maxMatches)
|
|
6213
6394
|
break;
|
|
6214
|
-
const entryAbsPath =
|
|
6215
|
-
const entryRelPath = relPath === "." ? entry.name :
|
|
6395
|
+
const entryAbsPath = path10.join(absPath, entry.name);
|
|
6396
|
+
const entryRelPath = relPath === "." ? entry.name : path10.posix.join(relPath, entry.name);
|
|
6216
6397
|
const ignoreResult = shouldIgnore(entryRelPath, entry.isDirectory(), ignoreCtx, cfg);
|
|
6217
6398
|
if (ignoreResult.shouldIgnore) {
|
|
6218
6399
|
ignoredCount.value++;
|
|
@@ -6505,10 +6686,10 @@ async function buildTree(absPath, relPath, depth, maxDepth, only, includeHidden,
|
|
|
6505
6686
|
const errorMessage = toolError2?.message ?? e?.message ?? String(e) ?? "Unknown error";
|
|
6506
6687
|
summary.total_entries++;
|
|
6507
6688
|
const node2 = {
|
|
6508
|
-
name:
|
|
6689
|
+
name: path11.basename(relPath) || relPath,
|
|
6509
6690
|
type: "other",
|
|
6510
6691
|
path: entryRel,
|
|
6511
|
-
hidden:
|
|
6692
|
+
hidden: path11.basename(relPath)?.startsWith("."),
|
|
6512
6693
|
ignored: false
|
|
6513
6694
|
};
|
|
6514
6695
|
if (errorMap) {
|
|
@@ -6542,10 +6723,10 @@ async function buildTree(absPath, relPath, depth, maxDepth, only, includeHidden,
|
|
|
6542
6723
|
summary.dirs++;
|
|
6543
6724
|
else if (type === "symlink")
|
|
6544
6725
|
summary.symlinks++;
|
|
6545
|
-
const isHidden =
|
|
6726
|
+
const isHidden = path11.basename(relPath)?.startsWith(".");
|
|
6546
6727
|
if (isHidden)
|
|
6547
6728
|
summary.hidden++;
|
|
6548
|
-
const ext = type === "file" ?
|
|
6729
|
+
const ext = type === "file" ? path11.extname(path11.basename(relPath) || relPath).slice(1) : void 0;
|
|
6549
6730
|
if (ext && (detail === "stat" || detail === "digest")) {
|
|
6550
6731
|
extCounts.set(ext, (extCounts.get(ext) ?? 0) + 1);
|
|
6551
6732
|
}
|
|
@@ -6553,7 +6734,7 @@ async function buildTree(absPath, relPath, depth, maxDepth, only, includeHidden,
|
|
|
6553
6734
|
summary.total_file_bytes = (summary.total_file_bytes ?? 0) + (stat.size ?? 0);
|
|
6554
6735
|
}
|
|
6555
6736
|
const node = {
|
|
6556
|
-
name:
|
|
6737
|
+
name: path11.basename(relPath) || relPath,
|
|
6557
6738
|
type,
|
|
6558
6739
|
path: entryRel,
|
|
6559
6740
|
size: (detail === "stat" || detail === "digest") && stat.isFile() ? stat.size : void 0,
|
|
@@ -6600,8 +6781,8 @@ async function buildTree(absPath, relPath, depth, maxDepth, only, includeHidden,
|
|
|
6600
6781
|
for (const entry of filtered) {
|
|
6601
6782
|
if (currentCount.value >= maxEntries)
|
|
6602
6783
|
break;
|
|
6603
|
-
const childAbs =
|
|
6604
|
-
const childRelPath = relPath === "." ? entry.name :
|
|
6784
|
+
const childAbs = path11.join(absPath, entry.name);
|
|
6785
|
+
const childRelPath = relPath === "." ? entry.name : path11.posix.join(relPath, entry.name);
|
|
6605
6786
|
const childRel = childRelPath.replace(/\\/g, "/");
|
|
6606
6787
|
const preIgnore = shouldIgnore(childRel, entry.isDirectory(), ignoreCtx, cfg);
|
|
6607
6788
|
if (preIgnore.shouldIgnore) {
|
|
@@ -6642,10 +6823,10 @@ async function buildTree(absPath, relPath, depth, maxDepth, only, includeHidden,
|
|
|
6642
6823
|
}
|
|
6643
6824
|
summary.total_entries++;
|
|
6644
6825
|
const errorNode = {
|
|
6645
|
-
name:
|
|
6826
|
+
name: path11.basename(childRelPath) || childRelPath,
|
|
6646
6827
|
type: "other",
|
|
6647
6828
|
path: childRel,
|
|
6648
|
-
hidden:
|
|
6829
|
+
hidden: path11.basename(childRelPath)?.startsWith("."),
|
|
6649
6830
|
ignored: false
|
|
6650
6831
|
};
|
|
6651
6832
|
if (only === "files" || only === "dirs") {
|
|
@@ -6832,7 +7013,7 @@ var FsTreeExecuter = class extends BaseExecuter {
|
|
|
6832
7013
|
|
|
6833
7014
|
// agent/src/cli/orchestrators/tools/executers/fs-search.ts
|
|
6834
7015
|
import fs12 from "node:fs/promises";
|
|
6835
|
-
import
|
|
7016
|
+
import path12 from "node:path";
|
|
6836
7017
|
function matchesGlob2(name, pattern) {
|
|
6837
7018
|
let regexStr = "^";
|
|
6838
7019
|
let i = 0;
|
|
@@ -6954,8 +7135,8 @@ async function searchDirectory(absPath, relPath, rootPath, query, mode, recursiv
|
|
|
6954
7135
|
for (const entry of visibleEntries) {
|
|
6955
7136
|
if (results.length >= maxResults)
|
|
6956
7137
|
break;
|
|
6957
|
-
const entryAbsPath =
|
|
6958
|
-
const entryRelPath = relPath === "." ? entry.name :
|
|
7138
|
+
const entryAbsPath = path12.join(absPath, entry.name);
|
|
7139
|
+
const entryRelPath = relPath === "." ? entry.name : path12.posix.join(relPath, entry.name);
|
|
6959
7140
|
const ignoreResult = shouldIgnore(entryRelPath, entry.isDirectory(), ignoreCtx, cfg);
|
|
6960
7141
|
if (ignoreResult.shouldIgnore) {
|
|
6961
7142
|
ignoredCount.value++;
|
|
@@ -7005,7 +7186,7 @@ async function searchDirectory(absPath, relPath, rootPath, query, mode, recursiv
|
|
|
7005
7186
|
} catch {
|
|
7006
7187
|
}
|
|
7007
7188
|
}
|
|
7008
|
-
const ext = type === "file" ?
|
|
7189
|
+
const ext = type === "file" ? path12.extname(entry.name).slice(1) : void 0;
|
|
7009
7190
|
const score = calculateScore({ name: entry.name, path: entryRelPath }, preferPaths, avoidPaths);
|
|
7010
7191
|
results.push({
|
|
7011
7192
|
name: entry.name,
|
|
@@ -7073,8 +7254,8 @@ var FsSearchExecuter = class extends BaseExecuter {
|
|
|
7073
7254
|
const st = await fs12.stat(absTarget);
|
|
7074
7255
|
if (!st.isDirectory()) {
|
|
7075
7256
|
if (st.isFile()) {
|
|
7076
|
-
searchPath =
|
|
7077
|
-
const parentRelPath =
|
|
7257
|
+
searchPath = path12.dirname(absTarget);
|
|
7258
|
+
const parentRelPath = path12.posix.dirname(rel);
|
|
7078
7259
|
searchRelPath = parentRelPath === "." ? "." : parentRelPath;
|
|
7079
7260
|
messages.push({
|
|
7080
7261
|
type: "warning",
|
|
@@ -7369,7 +7550,7 @@ var FsListResult = class extends BaseToolResult {
|
|
|
7369
7550
|
const itemCount = items?.length ?? 0;
|
|
7370
7551
|
const totalCount = meta?.count ?? 0;
|
|
7371
7552
|
const ignoredCount = filtered?.ignored_count ?? 0;
|
|
7372
|
-
const
|
|
7553
|
+
const path13 = context?.path ?? "unknown";
|
|
7373
7554
|
const parts = [];
|
|
7374
7555
|
if (itemCount > 0) {
|
|
7375
7556
|
parts.push(`${itemCount} item${itemCount !== 1 ? "s" : ""}`);
|
|
@@ -7381,7 +7562,7 @@ var FsListResult = class extends BaseToolResult {
|
|
|
7381
7562
|
parts.push("has_more");
|
|
7382
7563
|
}
|
|
7383
7564
|
const description = parts.length > 0 ? parts.join(", ") : "empty";
|
|
7384
|
-
return `path=${
|
|
7565
|
+
return `path=${path13}, ${description}`;
|
|
7385
7566
|
}
|
|
7386
7567
|
};
|
|
7387
7568
|
|
|
@@ -7404,10 +7585,10 @@ var FsReadResult = class extends BaseToolResult {
|
|
|
7404
7585
|
return "no result";
|
|
7405
7586
|
}
|
|
7406
7587
|
const { bytes_read, eof, context } = this.output;
|
|
7407
|
-
const
|
|
7588
|
+
const path13 = context?.path ?? "unknown";
|
|
7408
7589
|
const size = bytes_read ?? 0;
|
|
7409
7590
|
const truncated = !eof;
|
|
7410
|
-
const parts = [`path=${
|
|
7591
|
+
const parts = [`path=${path13}`, `${size} bytes`];
|
|
7411
7592
|
if (truncated) {
|
|
7412
7593
|
parts.push("truncated");
|
|
7413
7594
|
}
|
|
@@ -7434,9 +7615,9 @@ var FsInfoResult = class extends BaseToolResult {
|
|
|
7434
7615
|
return "no result";
|
|
7435
7616
|
}
|
|
7436
7617
|
const { type, size, context } = this.output;
|
|
7437
|
-
const
|
|
7618
|
+
const path13 = context?.path ?? "unknown";
|
|
7438
7619
|
const sizeStr = size !== void 0 ? `${size} bytes` : "no size";
|
|
7439
|
-
return `path=${
|
|
7620
|
+
return `path=${path13}, type=${type}, ${sizeStr}`;
|
|
7440
7621
|
}
|
|
7441
7622
|
};
|
|
7442
7623
|
|
|
@@ -7459,9 +7640,9 @@ var FsTreeResult = class extends BaseToolResult {
|
|
|
7459
7640
|
return "no result";
|
|
7460
7641
|
}
|
|
7461
7642
|
const { tree, filtered, summary, context } = this.output;
|
|
7462
|
-
const
|
|
7643
|
+
const path13 = context?.path ?? "unknown";
|
|
7463
7644
|
const ignoredCount = filtered?.ignored_count ?? 0;
|
|
7464
|
-
const parts = [`path=${
|
|
7645
|
+
const parts = [`path=${path13}`];
|
|
7465
7646
|
if (summary) {
|
|
7466
7647
|
const { total_entries, files, dirs } = summary;
|
|
7467
7648
|
if (total_entries !== void 0) {
|
|
@@ -7515,10 +7696,10 @@ var FsSearchResult = class extends BaseToolResult {
|
|
|
7515
7696
|
return "no result";
|
|
7516
7697
|
}
|
|
7517
7698
|
const { results, meta, filtered, context } = this.output;
|
|
7518
|
-
const
|
|
7699
|
+
const path13 = context?.path ?? "unknown";
|
|
7519
7700
|
const resultCount = results?.length ?? 0;
|
|
7520
7701
|
const ignoredCount = filtered?.ignored_count ?? 0;
|
|
7521
|
-
const parts = [`path=${
|
|
7702
|
+
const parts = [`path=${path13}`, `${resultCount} result${resultCount !== 1 ? "s" : ""}`];
|
|
7522
7703
|
if (ignoredCount > 0) {
|
|
7523
7704
|
parts.push(`${ignoredCount} ignored`);
|
|
7524
7705
|
}
|
|
@@ -7548,12 +7729,12 @@ var FsGrepResult = class extends BaseToolResult {
|
|
|
7548
7729
|
return "no result";
|
|
7549
7730
|
}
|
|
7550
7731
|
const { matches, meta, filtered, context } = this.output;
|
|
7551
|
-
const
|
|
7732
|
+
const path13 = context?.path ?? "unknown";
|
|
7552
7733
|
const matchCount = matches?.length ?? 0;
|
|
7553
7734
|
const filesScanned = meta?.files_scanned ?? 0;
|
|
7554
7735
|
const ignoredCount = filtered?.ignored_count ?? 0;
|
|
7555
7736
|
const parts = [
|
|
7556
|
-
`path=${
|
|
7737
|
+
`path=${path13}`,
|
|
7557
7738
|
`${matchCount} match${matchCount !== 1 ? "es" : ""}`,
|
|
7558
7739
|
`${filesScanned} file${filesScanned !== 1 ? "s" : ""} scanned`
|
|
7559
7740
|
];
|
|
@@ -7677,6 +7858,7 @@ var ToolsOrchestrator = class {
|
|
|
7677
7858
|
const availableDirectoryIds = config.directories.map((r) => r.id).join(", ") || "none";
|
|
7678
7859
|
const availableDirectoryPaths = config.directories.map((r) => r.path).join(", ") || "none";
|
|
7679
7860
|
ctx.notifier.info(`Tool call: ${toolName} (job=${jobId}, directory_id=${directoryId || "MISSING"}, available_ids=[${availableDirectoryIds}], available_paths=[${availableDirectoryPaths}], directory_count=${config.directories.length})`, { scope: "system" /* System */ });
|
|
7861
|
+
ctx.notifier.info(`Executing: ${toolName}`);
|
|
7680
7862
|
const toolStartTime = Date.now();
|
|
7681
7863
|
try {
|
|
7682
7864
|
const toolRegistry = new ToolExecuterRegistry(config);
|
|
@@ -7687,9 +7869,9 @@ var ToolsOrchestrator = class {
|
|
|
7687
7869
|
const timing = calculateTiming(toolStartTime);
|
|
7688
7870
|
const result = createToolResult(toolName, jobId, raw, timing.execution_sec);
|
|
7689
7871
|
if (result.ok) {
|
|
7690
|
-
ctx.notifier.success(result.toString()
|
|
7872
|
+
ctx.notifier.success(result.toString());
|
|
7691
7873
|
} else {
|
|
7692
|
-
ctx.notifier.error(result.toString()
|
|
7874
|
+
ctx.notifier.error(result.toString());
|
|
7693
7875
|
}
|
|
7694
7876
|
} catch (e) {
|
|
7695
7877
|
const toolResult = formatToolErrorResponse(jobId, toolName, e, toolStartTime);
|
|
@@ -7701,7 +7883,7 @@ var ToolsOrchestrator = class {
|
|
|
7701
7883
|
error: e instanceof Error ? { code: "INTERNAL", message: e.message } : { code: "INTERNAL", message: String(e) }
|
|
7702
7884
|
};
|
|
7703
7885
|
const result = createToolResult(toolName, jobId, errorResult, timing.execution_sec);
|
|
7704
|
-
ctx.notifier.error(result.toString()
|
|
7886
|
+
ctx.notifier.error(result.toString());
|
|
7705
7887
|
}
|
|
7706
7888
|
}
|
|
7707
7889
|
};
|
|
@@ -7917,7 +8099,7 @@ var DirectoriesJob = class extends JobType {
|
|
|
7917
8099
|
return {
|
|
7918
8100
|
id: r.id,
|
|
7919
8101
|
name: r.name,
|
|
7920
|
-
path: r.path,
|
|
8102
|
+
path: toTildeHomePath(r.path) ?? r.name,
|
|
7921
8103
|
path_hint: r.tag || void 0
|
|
7922
8104
|
};
|
|
7923
8105
|
});
|