@devicelink/dlink 0.2.2 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +24 -37
- package/index.js +113 -26
- package/index.js.map +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
# @devicelink/dlink
|
|
2
2
|
|
|
3
|
-
DeviceLink
|
|
4
|
-
|
|
5
|
-
Share files from your machine with AI assistants (ChatGPT, Claude, etc.) through the Model Context Protocol (MCP). Your files never leave your device — they're streamed in real-time with your explicit permission.
|
|
3
|
+
DeviceLink lets you share folders from your computer with AI assistants (like ChatGPT) safely. Nothing is uploaded to the cloud to sit on a server: files stay on your machine until you allow access, and you stay in control.
|
|
6
4
|
|
|
7
5
|
## Install
|
|
8
6
|
|
|
@@ -10,58 +8,47 @@ Share files from your machine with AI assistants (ChatGPT, Claude, etc.) through
|
|
|
10
8
|
npm install -g @devicelink/dlink
|
|
11
9
|
```
|
|
12
10
|
|
|
13
|
-
## Quick
|
|
11
|
+
## Quick start
|
|
14
12
|
|
|
15
13
|
```bash
|
|
16
14
|
# Sign in with your DeviceLink account
|
|
17
15
|
dlink signin
|
|
18
16
|
|
|
19
|
-
#
|
|
17
|
+
# Pick a folder to share
|
|
20
18
|
dlink share /path/to/project
|
|
21
19
|
|
|
22
|
-
#
|
|
20
|
+
# You're set: your AI can use that folder when you connect it through DeviceLink
|
|
23
21
|
```
|
|
24
22
|
|
|
25
23
|
## Commands
|
|
26
24
|
|
|
27
|
-
| Command |
|
|
28
|
-
|
|
29
|
-
| `dlink signin` |
|
|
30
|
-
| `dlink signout` | Sign out
|
|
31
|
-
| `dlink share <path>` | Share a
|
|
32
|
-
| `dlink status` |
|
|
33
|
-
| `dlink debug on/off` | Toggle debug logging |
|
|
34
|
-
|
|
35
|
-
## How It Works
|
|
36
|
-
|
|
37
|
-
1. **Sign in** — authenticate via Google OAuth using a device code flow
|
|
38
|
-
2. **Share directories** — choose which folders to make available
|
|
39
|
-
3. **Connect** — the agent maintains a secure WebSocket connection to the DeviceLink bridge
|
|
40
|
-
4. **Use** — AI assistants call tools (list files, read files, search, grep) through MCP
|
|
41
|
-
5. **Control** — you see every tool call in real-time and can disconnect anytime
|
|
42
|
-
|
|
43
|
-
## Security
|
|
25
|
+
| Command | What it does |
|
|
26
|
+
|---------|----------------|
|
|
27
|
+
| `dlink signin` | Sign in to DeviceLink |
|
|
28
|
+
| `dlink signout` | Sign out on this computer |
|
|
29
|
+
| `dlink share <path>` | Share a folder with your AI |
|
|
30
|
+
| `dlink status` | See whether you're connected |
|
|
44
31
|
|
|
45
|
-
|
|
46
|
-
- **Ed25519 proof-of-possession** — device identity is cryptographically verified
|
|
47
|
-
- **Encrypted credential storage** — AES-256-GCM with machine-bound keys
|
|
48
|
-
- **Shared directory boundaries** — path traversal protection, symlink escape prevention
|
|
49
|
-
- **Short-lived tokens** — access tokens expire in 30 seconds, auto-refreshed
|
|
32
|
+
## How it works
|
|
50
33
|
|
|
51
|
-
|
|
34
|
+
1. **Sign in** with your account (you'll use your browser when asked).
|
|
35
|
+
2. **Pick folders** you want the AI to be able to read.
|
|
36
|
+
3. **Stay connected** so requests can reach your computer securely.
|
|
37
|
+
4. **Use your AI** as usual: it can list and read files in the folders you shared.
|
|
38
|
+
5. **Stop anytime**: sign out or disconnect if you want access to end.
|
|
52
39
|
|
|
53
|
-
|
|
40
|
+
## Security & privacy
|
|
54
41
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
42
|
+
- Your files are not kept on our servers for browsing; access happens when you're connected and allow it.
|
|
43
|
+
- Your device is checked so only your account can use your shared folders.
|
|
44
|
+
- Sign-in details are stored encrypted on this computer.
|
|
45
|
+
- Shared folders are limited to what you picked, so the AI can't wander the rest of your disk.
|
|
46
|
+
- Access is short-lived and refreshed automatically so old keys don't stick around.
|
|
60
47
|
|
|
61
48
|
## Requirements
|
|
62
49
|
|
|
63
|
-
- Node.js 20 or
|
|
64
|
-
- Internet
|
|
50
|
+
- [Node.js](https://nodejs.org/) version 20 or newer
|
|
51
|
+
- Internet access to sign in and stay connected
|
|
65
52
|
|
|
66
53
|
## License
|
|
67
54
|
|
package/index.js
CHANGED
|
@@ -962,6 +962,13 @@ var Engine2 = class {
|
|
|
962
962
|
getBus() {
|
|
963
963
|
return this.bus;
|
|
964
964
|
}
|
|
965
|
+
hasRunningJobs() {
|
|
966
|
+
for (const [, count] of this.laneActive) {
|
|
967
|
+
if (count > 0)
|
|
968
|
+
return true;
|
|
969
|
+
}
|
|
970
|
+
return false;
|
|
971
|
+
}
|
|
965
972
|
register(jobType) {
|
|
966
973
|
const name = jobType.options.name;
|
|
967
974
|
if (this.registry.has(name)) {
|
|
@@ -1588,7 +1595,7 @@ var LoadConfigJob = class extends JobType {
|
|
|
1588
1595
|
);
|
|
1589
1596
|
}
|
|
1590
1597
|
async run(input, ctx) {
|
|
1591
|
-
ctx.notifier.info("Loading configuration...");
|
|
1598
|
+
ctx.notifier.info("Loading configuration...", { scope: "system" /* System */ });
|
|
1592
1599
|
const configLoader = ctx.engine.configurationContext.loader;
|
|
1593
1600
|
const refreshRes = await ctx.engine.secrets.get("devicePrivateKey");
|
|
1594
1601
|
configLoader.setPrivateKeyCache(refreshRes.ok && refreshRes.value ? refreshRes.value : void 0);
|
|
@@ -1611,13 +1618,14 @@ var LoadConfigJob = class extends JobType {
|
|
|
1611
1618
|
return new LoadConfigOutput(true, config);
|
|
1612
1619
|
}
|
|
1613
1620
|
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
|
|
1621
|
+
const sys = { scope: "system" /* System */ };
|
|
1622
|
+
ctx.notifier.info(`Control base URL: ${config.controlBaseUrl}`, sys);
|
|
1623
|
+
ctx.notifier.info(`Bridge WebSocket URL: ${config.bridgeWsUrl}`, sys);
|
|
1624
|
+
ctx.notifier.info(`Directories: ${config.directories.map((directory) => directory.path).join(", ")}`, sys);
|
|
1625
|
+
ctx.notifier.info(`Device: ${config.device?.id}`, sys);
|
|
1626
|
+
ctx.notifier.info(`Device name: ${config.device?.name}`, sys);
|
|
1627
|
+
ctx.notifier.info(`Device tag: ${config.device?.tag}`, sys);
|
|
1628
|
+
ctx.notifier.info(`Device keys public: ${config.device?.keys?.public ? "***" : "(none)"}`, sys);
|
|
1621
1629
|
}
|
|
1622
1630
|
};
|
|
1623
1631
|
var loadConfigJob = new LoadConfigJob();
|
|
@@ -3312,6 +3320,9 @@ var ConsoleOrchestrator = class {
|
|
|
3312
3320
|
ctx.bus.emit("runtime:debug:changed", { enabled: false });
|
|
3313
3321
|
ctx.notifier.success("Debug mode disabled");
|
|
3314
3322
|
break;
|
|
3323
|
+
case "replace" /* Replace */:
|
|
3324
|
+
ctx.bus.emit("auth:session_conflict:resolve", { choice: "USE_NEW" });
|
|
3325
|
+
break;
|
|
3315
3326
|
default:
|
|
3316
3327
|
ctx.notifier.warn(`Command not implemented: ${input.kind}`);
|
|
3317
3328
|
break;
|
|
@@ -3361,6 +3372,9 @@ var ConsoleOrchestrator = class {
|
|
|
3361
3372
|
if (cmd === "doctor") {
|
|
3362
3373
|
return { handled: true, kind: "doctor" /* Doctor */ };
|
|
3363
3374
|
}
|
|
3375
|
+
if (cmd === "replace" || cmd === "yes" || cmd === "y") {
|
|
3376
|
+
return { handled: true, kind: "replace" /* Replace */ };
|
|
3377
|
+
}
|
|
3364
3378
|
}
|
|
3365
3379
|
return { handled: false };
|
|
3366
3380
|
});
|
|
@@ -3810,7 +3824,6 @@ var WebSocketOrchestrator = class {
|
|
|
3810
3824
|
};
|
|
3811
3825
|
return {
|
|
3812
3826
|
"config:cache:validated": async (_, ctx) => initSocket("config:cache:validated", ctx),
|
|
3813
|
-
"auth:signin:completed": async (_, ctx) => initSocket("auth:signin:completed", ctx),
|
|
3814
3827
|
"auth:signout:requested": async (_, ctx) => {
|
|
3815
3828
|
ctx.notifier.info("WS: sign-out requested -> disconnecting");
|
|
3816
3829
|
this.stopped = true;
|
|
@@ -3829,7 +3842,10 @@ var WebSocketOrchestrator = class {
|
|
|
3829
3842
|
ctx.notifier.info("WS: reconnect requested by auth", { scope: "system" /* System */ });
|
|
3830
3843
|
this.retryAttempt = 0;
|
|
3831
3844
|
this.clearCountdown();
|
|
3832
|
-
this.socket
|
|
3845
|
+
if (this.socket) {
|
|
3846
|
+
this.socket.stop();
|
|
3847
|
+
this.socket = null;
|
|
3848
|
+
}
|
|
3833
3849
|
this.ensureStarted(ctx, "auth:ws:reconnect");
|
|
3834
3850
|
},
|
|
3835
3851
|
// the only sender
|
|
@@ -3848,6 +3864,11 @@ var WebSocketOrchestrator = class {
|
|
|
3848
3864
|
ctx.notifier.info(`WS: connect loop already running (reason=${reason})`, { scope: "system" /* System */ });
|
|
3849
3865
|
return;
|
|
3850
3866
|
}
|
|
3867
|
+
const ws = this.socket?.getSocket();
|
|
3868
|
+
if (ws && ws.readyState <= 1) {
|
|
3869
|
+
ctx.notifier.info(`WS: already connected/connecting, skipping (reason=${reason})`, { scope: "system" /* System */ });
|
|
3870
|
+
return;
|
|
3871
|
+
}
|
|
3851
3872
|
this.connectLoopInFlight = this.startOrJoinConnectLoop(ctx, reason).finally(() => {
|
|
3852
3873
|
this.connectLoopInFlight = null;
|
|
3853
3874
|
});
|
|
@@ -3862,32 +3883,47 @@ var WebSocketOrchestrator = class {
|
|
|
3862
3883
|
this.lastUrl = url;
|
|
3863
3884
|
ctx.notifier.info(`WS: ensure connection (reason=${reason}) url=${url}`, { scope: "system" /* System */ });
|
|
3864
3885
|
if (!this.socket) {
|
|
3865
|
-
|
|
3886
|
+
const sock = new RecoverableSocket(url, {
|
|
3866
3887
|
abortSignal: ctx.signal,
|
|
3867
3888
|
connectionTimeoutMs: 1e4,
|
|
3868
3889
|
heartbeatIntervalMs: 2e4,
|
|
3869
3890
|
pongTimeoutMs: 1e4
|
|
3870
3891
|
});
|
|
3871
|
-
this.socket
|
|
3872
|
-
|
|
3892
|
+
this.socket = sock;
|
|
3893
|
+
sock.registerOnStateChangeCallback((state) => this.handleStateChange(state, ctx));
|
|
3894
|
+
sock.registerOnErrorCallback((error) => {
|
|
3873
3895
|
ctx.notifier.error(`WS: transport error: ${error.message}`);
|
|
3874
3896
|
});
|
|
3875
|
-
|
|
3897
|
+
sock.registerOnConnectedCallback((_ws) => {
|
|
3898
|
+
if (this.socket !== sock)
|
|
3899
|
+
return;
|
|
3876
3900
|
this.retryAttempt = 0;
|
|
3877
3901
|
this.clearCountdown();
|
|
3878
|
-
ctx.notifier.success(`WS: connected (${url})`);
|
|
3902
|
+
ctx.notifier.success(ctx.flags.debug ? `WS: connected (${url})` : `WS: connected`);
|
|
3879
3903
|
ctx.bus.emit("ws:connected", { url, attempt: 0 });
|
|
3880
3904
|
});
|
|
3881
|
-
|
|
3905
|
+
sock.registerOnDisconnectedCallback((code, reasonStr) => {
|
|
3906
|
+
if (this.socket !== sock) {
|
|
3907
|
+
ctx.notifier.info(`WS: ignoring stale disconnect (code=${code})`, { scope: "system" /* System */ });
|
|
3908
|
+
return;
|
|
3909
|
+
}
|
|
3882
3910
|
ctx.notifier.warn(`WS: disconnected code=${code} reason=${reasonStr || "-"}`);
|
|
3883
3911
|
ctx.bus.emit("ws:disconnected", { code, reason: reasonStr });
|
|
3884
3912
|
if (code === 4001) {
|
|
3885
3913
|
ctx.notifier.info("WS: auth disconnect (4001) -> waiting for auth", { scope: "system" /* System */ });
|
|
3886
3914
|
return;
|
|
3887
3915
|
}
|
|
3916
|
+
if (code === 4011) {
|
|
3917
|
+
this.stopped = true;
|
|
3918
|
+
this.socket?.stop();
|
|
3919
|
+
this.socket = null;
|
|
3920
|
+
return;
|
|
3921
|
+
}
|
|
3888
3922
|
this.ensureStarted(ctx, "socket:disconnected");
|
|
3889
3923
|
});
|
|
3890
|
-
|
|
3924
|
+
sock.registerOnMessageCallback((data) => {
|
|
3925
|
+
if (this.socket !== sock)
|
|
3926
|
+
return;
|
|
3891
3927
|
const text = typeof data === "string" ? data : data.toString("utf8");
|
|
3892
3928
|
ctx.bus.emit("ws:rx", { text });
|
|
3893
3929
|
});
|
|
@@ -4558,6 +4594,7 @@ var AuthOrchestrator = class {
|
|
|
4558
4594
|
connectionId = null;
|
|
4559
4595
|
sessionId = null;
|
|
4560
4596
|
accessToken;
|
|
4597
|
+
rejectedByConflict = false;
|
|
4561
4598
|
refreshGroupKey = "auth:refresh";
|
|
4562
4599
|
refreshCycle = 0;
|
|
4563
4600
|
refreshScheduler = null;
|
|
@@ -4591,6 +4628,11 @@ var AuthOrchestrator = class {
|
|
|
4591
4628
|
"Auth: signin completed; requesting ws reconnect",
|
|
4592
4629
|
{ scope: "system" /* System */ }
|
|
4593
4630
|
);
|
|
4631
|
+
this.rejectedByConflict = false;
|
|
4632
|
+
if (this.refreshRetryScheduler) {
|
|
4633
|
+
this.refreshRetryScheduler.stop();
|
|
4634
|
+
this.refreshRetryScheduler = null;
|
|
4635
|
+
}
|
|
4594
4636
|
this.ensureAccessExpPersisted(ctx, "signin");
|
|
4595
4637
|
await this.ensureRefreshScheduling(ctx, "signin");
|
|
4596
4638
|
ctx.bus.emit("auth:ws:reconnect", void 0);
|
|
@@ -4647,6 +4689,14 @@ var AuthOrchestrator = class {
|
|
|
4647
4689
|
{ scope: "system" /* System */ }
|
|
4648
4690
|
);
|
|
4649
4691
|
this.resetConnectionState();
|
|
4692
|
+
if (p.code === 4011) {
|
|
4693
|
+
ctx.notifier.warn(
|
|
4694
|
+
'Connection rejected - another device is active on this workspace. Run "signin" to try again.'
|
|
4695
|
+
);
|
|
4696
|
+
this.rejectedByConflict = true;
|
|
4697
|
+
ctx.bus.emit("ws:disconnect", { reason: "REPLACEMENT_REJECTED" });
|
|
4698
|
+
return;
|
|
4699
|
+
}
|
|
4650
4700
|
if (p.code === 4001) {
|
|
4651
4701
|
const refreshRes = await ctx.engine.secrets.get("refreshToken");
|
|
4652
4702
|
if (!refreshRes.ok || !refreshRes.value) {
|
|
@@ -4669,7 +4719,8 @@ var AuthOrchestrator = class {
|
|
|
4669
4719
|
// --- boot path unchanged except it must schedule refresh always ---
|
|
4670
4720
|
async onEventConfigLoaded(config, ctx) {
|
|
4671
4721
|
ctx.notifier.info(
|
|
4672
|
-
"Initializing with cached configuration"
|
|
4722
|
+
"Initializing with cached configuration",
|
|
4723
|
+
{ scope: "system" /* System */ }
|
|
4673
4724
|
);
|
|
4674
4725
|
if (!config.device?.id) {
|
|
4675
4726
|
ctx.notifier.error("Device ID is not available");
|
|
@@ -4698,7 +4749,6 @@ var AuthOrchestrator = class {
|
|
|
4698
4749
|
ctx.notifier.warn("Auth: not signed in; run signin");
|
|
4699
4750
|
return;
|
|
4700
4751
|
}
|
|
4701
|
-
ctx.bus.emit("auth:ws:reconnect", void 0);
|
|
4702
4752
|
}
|
|
4703
4753
|
isAccessLikelyUsable(ctx) {
|
|
4704
4754
|
const jwt = this.accessToken;
|
|
@@ -4715,7 +4765,8 @@ var AuthOrchestrator = class {
|
|
|
4715
4765
|
async waitForApproval(api, startResponse, ctx) {
|
|
4716
4766
|
const deadline = Date.now() + startResponse.expires_in * 1e3;
|
|
4717
4767
|
ctx.notifier.info(
|
|
4718
|
-
"Waiting for approval... (streaming)"
|
|
4768
|
+
"Waiting for approval... (streaming)",
|
|
4769
|
+
{ scope: "system" /* System */ }
|
|
4719
4770
|
);
|
|
4720
4771
|
try {
|
|
4721
4772
|
return await api.deviceAuthStream({
|
|
@@ -4812,7 +4863,8 @@ var AuthOrchestrator = class {
|
|
|
4812
4863
|
}
|
|
4813
4864
|
if (streamResp.needs_proof) {
|
|
4814
4865
|
ctx.notifier.info(
|
|
4815
|
-
"Sending proof-of-possession signature..."
|
|
4866
|
+
"Sending proof-of-possession signature...",
|
|
4867
|
+
{ scope: "system" /* System */ }
|
|
4816
4868
|
);
|
|
4817
4869
|
const privKeyObj = crypto8.createPrivateKey(privateKeyPem);
|
|
4818
4870
|
const challengeBytes = Buffer.from(
|
|
@@ -4827,9 +4879,9 @@ var AuthOrchestrator = class {
|
|
|
4827
4879
|
challenge: startResponse.challenge,
|
|
4828
4880
|
signature_b64: signatureB64
|
|
4829
4881
|
});
|
|
4830
|
-
ctx.notifier.info("Proof-of-possession verified");
|
|
4882
|
+
ctx.notifier.info("Proof-of-possession verified", { scope: "system" /* System */ });
|
|
4831
4883
|
}
|
|
4832
|
-
ctx.notifier.info("Fetching tokens...");
|
|
4884
|
+
ctx.notifier.info("Fetching tokens...", { scope: "system" /* System */ });
|
|
4833
4885
|
const tokenResp = await api.deviceAuthToken({
|
|
4834
4886
|
device_code: startResponse.device_code
|
|
4835
4887
|
});
|
|
@@ -4963,6 +5015,9 @@ var AuthOrchestrator = class {
|
|
|
4963
5015
|
ctx.bus.emit("auth:session_conflict", msg);
|
|
4964
5016
|
void this.handleSessionConflict(ctx, msg);
|
|
4965
5017
|
return;
|
|
5018
|
+
case "quiesce":
|
|
5019
|
+
void this.handleQuiesce(ctx, msg);
|
|
5020
|
+
return;
|
|
4966
5021
|
default:
|
|
4967
5022
|
return;
|
|
4968
5023
|
}
|
|
@@ -4973,14 +5028,41 @@ var AuthOrchestrator = class {
|
|
|
4973
5028
|
this.conflictTimer = null;
|
|
4974
5029
|
}
|
|
4975
5030
|
this.pendingConflictRequest = msg;
|
|
5031
|
+
ctx.notifier.warn(
|
|
5032
|
+
`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.`
|
|
5033
|
+
);
|
|
4976
5034
|
const deadlineMs = Math.min(msg.deadline_ms, 15e3);
|
|
4977
5035
|
this.conflictTimer = setTimeout(() => {
|
|
4978
5036
|
this.conflictTimer = null;
|
|
4979
5037
|
if (this.pendingConflictRequest?.new_session_id === msg.new_session_id) {
|
|
5038
|
+
ctx.notifier.info("No response - keeping existing device connected.");
|
|
4980
5039
|
this.sendSessionConflictResolve(ctx, "KEEP_OLD");
|
|
4981
5040
|
}
|
|
4982
5041
|
}, deadlineMs);
|
|
4983
5042
|
}
|
|
5043
|
+
async handleQuiesce(ctx, msg) {
|
|
5044
|
+
ctx.notifier.warn(
|
|
5045
|
+
`This device is being replaced by another connection. Finishing in-flight operations...`
|
|
5046
|
+
);
|
|
5047
|
+
const hasActiveJobs = ctx.engine.hasRunningJobs?.() ?? false;
|
|
5048
|
+
if (hasActiveJobs) {
|
|
5049
|
+
const deadline = Math.min(msg.deadline_ms - 2e3, 12e3);
|
|
5050
|
+
const start = Date.now();
|
|
5051
|
+
while (Date.now() - start < deadline) {
|
|
5052
|
+
if (!(ctx.engine.hasRunningJobs?.() ?? false))
|
|
5053
|
+
break;
|
|
5054
|
+
await new Promise((r) => setTimeout(r, 500));
|
|
5055
|
+
}
|
|
5056
|
+
}
|
|
5057
|
+
const status = ctx.engine.hasRunningJobs?.() ?? false ? "BUSY" : "IDLE";
|
|
5058
|
+
this.send(ctx, {
|
|
5059
|
+
type: "quiesce_ack",
|
|
5060
|
+
session_id: msg.session_id,
|
|
5061
|
+
status
|
|
5062
|
+
});
|
|
5063
|
+
ctx.notifier.info("Device replaced by new connection. Disconnecting.");
|
|
5064
|
+
this.socket?.stop();
|
|
5065
|
+
}
|
|
4984
5066
|
sendSessionConflictResolve(ctx, choice) {
|
|
4985
5067
|
const req = this.pendingConflictRequest;
|
|
4986
5068
|
if (!req)
|
|
@@ -5242,6 +5324,10 @@ var AuthOrchestrator = class {
|
|
|
5242
5324
|
if (this.accessToken && !ctx.engine.configurationContext.configuration.tokens?.accessTokenExpSec) {
|
|
5243
5325
|
this.ensureAccessExpPersisted(ctx, reason);
|
|
5244
5326
|
}
|
|
5327
|
+
if (this.refreshRetryScheduler) {
|
|
5328
|
+
this.refreshRetryScheduler.stop();
|
|
5329
|
+
this.refreshRetryScheduler = null;
|
|
5330
|
+
}
|
|
5245
5331
|
this.scheduleSilentRefresh(ctx, reason);
|
|
5246
5332
|
}
|
|
5247
5333
|
async scheduleSilentRefresh(ctx, reason) {
|
|
@@ -7677,6 +7763,7 @@ var ToolsOrchestrator = class {
|
|
|
7677
7763
|
const availableDirectoryIds = config.directories.map((r) => r.id).join(", ") || "none";
|
|
7678
7764
|
const availableDirectoryPaths = config.directories.map((r) => r.path).join(", ") || "none";
|
|
7679
7765
|
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 */ });
|
|
7766
|
+
ctx.notifier.info(`Executing: ${toolName}`);
|
|
7680
7767
|
const toolStartTime = Date.now();
|
|
7681
7768
|
try {
|
|
7682
7769
|
const toolRegistry = new ToolExecuterRegistry(config);
|
|
@@ -7687,9 +7774,9 @@ var ToolsOrchestrator = class {
|
|
|
7687
7774
|
const timing = calculateTiming(toolStartTime);
|
|
7688
7775
|
const result = createToolResult(toolName, jobId, raw, timing.execution_sec);
|
|
7689
7776
|
if (result.ok) {
|
|
7690
|
-
ctx.notifier.success(result.toString()
|
|
7777
|
+
ctx.notifier.success(result.toString());
|
|
7691
7778
|
} else {
|
|
7692
|
-
ctx.notifier.error(result.toString()
|
|
7779
|
+
ctx.notifier.error(result.toString());
|
|
7693
7780
|
}
|
|
7694
7781
|
} catch (e) {
|
|
7695
7782
|
const toolResult = formatToolErrorResponse(jobId, toolName, e, toolStartTime);
|
|
@@ -7701,7 +7788,7 @@ var ToolsOrchestrator = class {
|
|
|
7701
7788
|
error: e instanceof Error ? { code: "INTERNAL", message: e.message } : { code: "INTERNAL", message: String(e) }
|
|
7702
7789
|
};
|
|
7703
7790
|
const result = createToolResult(toolName, jobId, errorResult, timing.execution_sec);
|
|
7704
|
-
ctx.notifier.error(result.toString()
|
|
7791
|
+
ctx.notifier.error(result.toString());
|
|
7705
7792
|
}
|
|
7706
7793
|
}
|
|
7707
7794
|
};
|