@devicelink/dlink 0.3.5 → 0.3.10

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.
Files changed (3) hide show
  1. package/README.md +122 -10
  2. package/index.js +5 -4
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -4,11 +4,38 @@ DeviceLink lets you share folders from your computer with AI assistants - Claude
4
4
 
5
5
  ## Install
6
6
 
7
+ These commands download the installer, check it really is the installer, and only
8
+ then run it. Piping a download straight into a shell runs whatever the server
9
+ returns, including an error page.
10
+
11
+ macOS and Linux:
12
+
7
13
  ```bash
8
- npm install -g @devicelink/dlink
14
+ curl -fsSL https://devicelink.ai/install.sh -o dlink-install.sh && head -1 dlink-install.sh | grep -q '^#!/bin/sh' && sh dlink-install.sh
15
+ ```
16
+
17
+ Windows, in PowerShell (`irm` is a PowerShell command and does not exist in
18
+ Command Prompt):
19
+
20
+ ```powershell
21
+ irm https://devicelink.ai/install.ps1 -OutFile dlink-install.ps1; if ((gc dlink-install.ps1 -TotalCount 1) -match '^#Requires') { .\dlink-install.ps1 } else { 'Not the DeviceLink installer - check the URL' }
22
+ ```
23
+
24
+ From Command Prompt, run the same thing through PowerShell:
25
+
26
+ ```bat
27
+ powershell -NoProfile -Command "irm https://devicelink.ai/install.ps1 -OutFile dlink-install.ps1; if ((gc dlink-install.ps1 -TotalCount 1) -match '^#Requires') { .\dlink-install.ps1 } else { 'Not the DeviceLink installer - check the URL' }"
9
28
  ```
10
29
 
11
- Requires Node.js 20+.
30
+ This installs DeviceLink into `~/.devicelink` together with the Node runtime it needs,
31
+ so no system Node is required. Re-run the installer to upgrade; remove `~/.devicelink`
32
+ to uninstall.
33
+
34
+ If you already have Node.js 20 or later and prefer npm:
35
+
36
+ ```bash
37
+ npm install -g @devicelink/dlink
38
+ ```
12
39
 
13
40
  ## Quick start
14
41
 
@@ -18,10 +45,9 @@ Sign in at [devicelink.ai](https://devicelink.ai) with Google. A workspace is cr
18
45
 
19
46
  ### 2. Install and run the agent
20
47
 
21
- Open a terminal in the folder you want to share, then:
48
+ Open a terminal in the folder you want to share, install using either method above, then:
22
49
 
23
50
  ```bash
24
- npm install -g @devicelink/dlink
25
51
  dlink
26
52
  ```
27
53
 
@@ -44,12 +70,56 @@ Sign in with the same Google account when prompted. Then ask the AI to list your
44
70
 
45
71
  Run `dlink` to start the interactive agent. Once running, type these in the console:
46
72
 
47
- | Command | What it does |
48
- |---------|-------------|
49
- | `signin` | Sign in to DeviceLink (opens browser) |
50
- | `signout` | Sign out on this device |
51
- | `status` | Show connection status |
52
- | `stop` | Disconnect and exit |
73
+ | Command | Aliases | What it does |
74
+ |---------|---------|-------------|
75
+ | `help` | `?` | Show the command list |
76
+ | `start` | `run` | Connect the bridge |
77
+ | `stop` | | Disconnect the bridge. The agent keeps running; `start` reconnects |
78
+ | `status` | | Show device, bridge and session state |
79
+ | `doctor` | | Run local environment checks |
80
+ | `signin` | | Sign in to DeviceLink (opens browser) |
81
+ | `signout` | | Sign out and disconnect the bridge |
82
+ | `roots list` | | List the directories shared with the bridge |
83
+ | `roots add <path>` | | Share an additional directory |
84
+ | `replace` | `yes`, `y` | Take over an existing session on conflict |
85
+
86
+ ## Options
87
+
88
+ Passed when starting the agent, for example `dlink --headless --root /srv/data`.
89
+
90
+ | Option | What it does |
91
+ |--------|-------------|
92
+ | `-h, --help` | Show help and exit |
93
+ | `-v, --version` | Print the agent version and exit |
94
+ | `--headless[=true\|false]` | Run without the interactive UI |
95
+ | `--env <file>` | Load environment from a specific file |
96
+ | `--root <path>` | Share this directory with the bridge |
97
+ | `--control-url <url>` | Override the control plane base URL |
98
+ | `--bridge-ws-url <url>` | Override the bridge WebSocket URL |
99
+ | `--enrollment-file <file>` | Path to a `.devicelink` enrollment key |
100
+ | `--private-key-file <file>` | Path to the device private key |
101
+ | `--public-key-file <file>` | Path to the device public key |
102
+
103
+ ## Servers and containers
104
+
105
+ There is no console to type `signin` into on a headless machine. Use an enrollment key
106
+ instead. An organization admin issues one under Organization -> Devices, which downloads
107
+ a `.devicelink` file:
108
+
109
+ ```bash
110
+ dlink --headless --root /srv/data --enrollment-file ./enrollment-key.devicelink
111
+ ```
112
+
113
+ The device then appears under Organization -> Devices as a pending request for an admin
114
+ to approve. The key can also be supplied as the `ENROLMENT_KEY` environment variable,
115
+ either as a path or as the key itself.
116
+
117
+ ## Updates
118
+
119
+ DeviceLink can tell the agent that a newer version is available, and the agent reports
120
+ this when it connects. A new major version is never applied automatically - you are only
121
+ notified. Set `DLINK_AUTO_UPDATE=0` to be notified about every update instead of having
122
+ any of them applied.
53
123
 
54
124
  ## How it works
55
125
 
@@ -68,6 +138,48 @@ Run `dlink` to start the interactive agent. Once running, type these in the cons
68
138
  - Credentials are stored encrypted on your machine.
69
139
  - Access tokens are short-lived and refreshed automatically.
70
140
 
141
+ ## Usage data
142
+
143
+ The CLI reports usage data so we can see where setup breaks. It is on
144
+ by default. To turn it off:
145
+
146
+ ```bash
147
+ export DLINK_TELEMETRY=0 # or the cross-tool standard: DO_NOT_TRACK=1
148
+ ```
149
+
150
+ This switches off reporting from the CLI agent only. DeviceLink's servers
151
+ continue to record events about your account and devices - sessions starting and
152
+ ending, tokens refreshing, tools being called - because those are part of
153
+ running the service.
154
+
155
+ **What is sent:** a random install id (a UUID generated on first run and stored
156
+ in your config directory as `telemetry.json`), the CLI version, Node major
157
+ version, OS platform, OS major version, CPU architecture, and lifecycle events
158
+ - the agent started, config loaded, sign-in requested/completed, socket
159
+ connected/disconnected, session ready, the *name* of any tool invoked
160
+ (`fs_read`, `fs_list`, ...), and exit or crash reasons.
161
+
162
+ **What is never sent to our analytics provider:** file names, file contents,
163
+ directory paths, tool arguments, tool results, your username, your email
164
+ address, or anything about the folders you share. Your IP address is not
165
+ recorded either - events are sent with geolocation disabled and the IP field
166
+ suppressed.
167
+
168
+ **What DeviceLink itself receives:** your device name, which defaults to your
169
+ computer's hostname unless you set one. It is shown in your dashboard so you can
170
+ tell your devices apart. A machine identifier is also derived locally and used as
171
+ part of the key that encrypts your stored credentials - it stays on your machine
172
+ and is never sent to our analytics provider.
173
+
174
+ The install id is random and is not derived from your hardware, so it cannot be
175
+ linked back to your machine. Delete `telemetry.json` at any time to reset it.
176
+
177
+ **One thing it is linked to:** when you sign in from the CLI, the install id is
178
+ sent once to DeviceLink and associated with your account, so that a device
179
+ connecting and a tool running are recognised as the same person's session
180
+ rather than as two unrelated ones. Until you sign in it is not linked to
181
+ anything, and with telemetry turned off it is never sent at all.
182
+
71
183
  ## Links
72
184
 
73
185
  - Website: [devicelink.ai](https://devicelink.ai)
package/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env node
2
- import Oo from"node:crypto";var fe=class{debug;headless;headlessReason;constructor(e,t=!1,r=""){this.debug=e,this.headless=t,this.headlessReason=r}};var ce=class{name;concurrency;constructor(e,t){this.name=e,this.concurrency=t}};var he=class o{status;output;error;constructor(e,t,r){this.status=e,this.output=t,this.error=r}static success(e){return new o("success",e,null)}static failure(e){return new o("failure",null,e)}static cancelled(e){return new o("cancelled",null,e)}},ct=class{promise;resolveFn;rejectFn;constructor(){this.promise=new Promise((e,t)=>{this.resolveFn=e,this.rejectFn=t})}resolve(e){this.resolveFn(e)}reject(e){this.rejectFn(e)}};var U=class{name;lane;priority;locks;maxAttempts;cancellable;constructor(e){this.name=e.name,this.lane=e.lane,this.priority=e.priority,this.locks=e.locks,this.maxAttempts=e.maxAttempts,this.cancellable=e.cancellable}},B=class{options;constructor(e){this.options=e}},ee=class{type;input;constructor(e,t){this.type=e,this.input=t}},ut=class{bus;logger;notifier;flags;signal;engine;get headless(){return this.flags.headless}dispatchFn;constructor(e){this.bus=e.bus,this.engine=e.engine,this.logger=e.logger,this.notifier=e.notifier,this.flags=e.flags,this.signal=e.signal,this.dispatchFn=e.dispatch}dispatch(e,t){let r=t||new Be(null,null,null,null);return this.dispatchFn(e,r)}};var lt=class{ts;message;constructor(e,t){this.ts=t?.ts??Date.now(),this.message=e}},ne=class{id;ts;groupKey;kind;scope;message;category;constructor(e,t){this.id=crypto.randomUUID(),this.ts=t?.ts??Date.now(),this.kind=t?.kind??"info",this.scope=t?.scope??"default",this.message=e,this.groupKey=t?.groupKey,this.category=t?.category??[]}},dt=class{ts;jobId;jobType;state;attempt;maxAttempts;lane;priority;error;nextRunAt;constructor(e){this.ts=e.ts,this.jobId=e.jobId,this.jobType=e.jobType,this.state=e.state,this.attempt=e.attempt,this.maxAttempts=e.maxAttempts,this.lane=e.lane,this.priority=e.priority,this.error=e.error,this.nextRunAt=e.nextRunAt}};var Se=class{jobs;constructor(e){this.jobs=e}},We=class{id;jobType;inputJson;state;lane;priority;locks;attempts;maxAttempts;nextRunAt;cancellable;parentId;dedupeKey;lastError;createdAt;updatedAt;constructor(e){this.id=e.id,this.jobType=e.jobType,this.inputJson=e.inputJson,this.state=e.state,this.lane=e.lane,this.priority=e.priority,this.locks=e.locks,this.attempts=e.attempts,this.maxAttempts=e.maxAttempts,this.nextRunAt=e.nextRunAt,this.cancellable=e.cancellable,this.parentId=e.parentId,this.dedupeKey=e.dedupeKey,this.lastError=e.lastError,this.createdAt=e.createdAt,this.updatedAt=e.updatedAt}};var pt=class{state;constructor(e){this.state=e??new Se({})}async load(){return new Se(structuredClone(this.state.jobs))}async save(e){this.state=new Se(structuredClone(e.jobs))}};var Ie=class{engine;bus;logger;notifier;flags;signal;get headless(){return this.flags.headless}constructor(e){this.engine=e.engine,this.bus=e.bus,this.logger=e.logger,this.notifier=e.notifier,this.flags=e.flags,this.signal=e.signal}on(e,t){return this.bus.on(e,t)}};var ft=class{version=1;controlBaseUrl;bridgeWsUrl;directories;device;tokens;constructor(){this.directories=[]}},ht=class{_configuration;loader;get configuration(){return this._configuration}constructor(e,t){this._configuration=e,this.loader=t}apply(e){this._configuration=e}};import _e from"node:path";import sn from"node:os";import ar from"node:fs";import So from"node:path";import Eo from"env-paths";var ko="DeviceLink",_o=Eo(ko,{suffix:""});function To(){if(process.env.DEVICELINK_DATA_DIR)return process.env.DEVICELINK_DATA_DIR;let o=process.env.DEVICELINK_ENV,e=_o.config;return!o||o==="production"?e:So.join(e,o)}function me(){return To()}import cr from"dotenv";import ue from"lodash/get.js";import yt from"lodash/isArray.js";import ur from"lodash/isObject.js";import an from"lodash/isString.js";import le from"lodash/isEmpty.js";import Do from"lodash/defaultTo.js";import{createRequire as wo}from"node:module";var Ro=wo(import.meta.url),Co=Ro("node-machine-id");function mt(o=!0){try{let e=Co.machineIdSync(o);if(e&&typeof e=="string"&&e.trim().length>0)return e.trim()}catch{}}import Xr from"node:path";import Yt from"node:fs";import Qt from"node:crypto";import Xt from"lodash/get.js";import ge from"lodash/isEmpty.js";var gt=class{_notifier;argv;envFile;processEnv;configFileContent;getPrivateKey;_lastGeneratedPrivateKey;constructor(e){this._notifier=e.notifier,this.argv=e.argv,this.envFile=e.envFile,this.processEnv=e.processEnv,this.configFileContent=e.configFileContent,this.getPrivateKey=e.getPrivateKey}getLastGeneratedPrivateKey(){let e=this._lastGeneratedPrivateKey;return this._lastGeneratedPrivateKey=void 0,e}ensureDeviceKeys(){let e=this.getDevicePrivateKey(),t=this.getDevicePublicKey();if(e&&t)return{publicKey:t,privateKey:e};if(e&&!t)try{let s=Qt.createPrivateKey(e);return t=Qt.createPublicKey(s).export({format:"pem",type:"spki"}).toString("utf8"),this._notifier.info("Derived public key from private key",{scope:"system"}),{publicKey:t,privateKey:e}}catch{this._notifier.warn("Failed to derive public key from private key, generating new keypair",{scope:"system"})}t&&!e&&this._notifier.warn("Public key found but no private key, generating new keypair",{scope:"system"}),this._notifier.info("Generating new Ed25519 keypair...",{scope:"system"});let{publicKey:r,privateKey:n}=Qt.generateKeyPairSync("ed25519"),i=r.export({format:"pem",type:"spki"}).toString("utf8"),a=n.export({format:"pem",type:"pkcs8"}).toString("utf8");return this._lastGeneratedPrivateKey=a,this._notifier.info("Generated new Ed25519 keypair",{scope:"system"}),{publicKey:i,privateKey:a}}getDevicePrivateKey(){if(this.getPrivateKey){let r=this.getPrivateKey();if(r&&!ge(r))return this._notifier.info("Using private key from secret storage cache",{scope:"system"}),r}if(this.argv?.privateKeyFile){let r=this.loadKeyFromFile(this.argv.privateKeyFile);if(r)return this._notifier.info(`Found private key from --private-key-file: ${this.argv.privateKeyFile}`,{scope:"system"}),r;this._notifier.warn(`Failed to load private key from file: ${this.argv.privateKeyFile}`,{scope:"system"})}let e=this.safeGetEnvValue("DLINK_DEVICE_PRIVATE_KEY");if(e&&!ge(e))return this._notifier.info("Found private key in env file",{scope:"system"}),e;let t=this.processEnv.DLINK_DEVICE_PRIVATE_KEY;if(t&&!ge(t))return this._notifier.info("Found private key in process env",{scope:"system"}),t}getDevicePublicKey(){if(this.argv?.publicKeyFile){let n=this.loadKeyFromFile(this.argv.publicKeyFile);if(n)return this._notifier.info(`Found public key from --public-key-file: ${this.argv.publicKeyFile}`,{scope:"system"}),n;this._notifier.warn(`Failed to load public key from file: ${this.argv.publicKeyFile}`,{scope:"system"})}let e=Xt(this.safeGetConfigValue("device"),"keys.public");if(e&&!ge(String(e)))return this._notifier.info("Found public key in config file",{scope:"system"}),String(e);let t=this.safeGetEnvValue("DLINK_DEVICE_PUBLIC_KEY");if(t&&!ge(t))return this._notifier.info("Found public key in env file",{scope:"system"}),t;let r=this.processEnv.DLINK_DEVICE_PUBLIC_KEY;if(r&&!ge(r))return this._notifier.info("Found public key in process env",{scope:"system"}),r}loadKeyFromFile(e){if(!e||ge(e))return;let t=this.getApplicationConfigFolder(),r=[e,Xr.join(t,e)];for(let n of r)try{let i=Xr.resolve(n);if(Yt.existsSync(i))if(Yt.statSync(i).isFile()){let s=Yt.readFileSync(i,"utf8").trim();if(ge(s))this._notifier.warn(`Key file is empty: ${i}`,{scope:"system"});else return this._notifier.info(`Loaded key from file: ${i}`,{scope:"system"}),s}else this._notifier.warn(`Path is not a file: ${i}`,{scope:"system"})}catch(i){let a=i instanceof Error?i.message:String(i);this._notifier.warn(`Failed to read key file ${n}: ${a}`,{scope:"system"})}this._notifier.warn(`Key file not found: ${e} (tried: ${r.join(", ")})`,{scope:"system"})}safeGetEnvValue(e){try{return Xt(this.envFile,`parsed.${e}`)}catch{return}}safeGetConfigValue(e){try{return Xt(this.configFileContent,e)}catch{return}}getApplicationConfigFolder(){return me()}};import Zr from"node:crypto";function en(o){let e=o.trim();e=e.replace(/\\/g,"/"),e=e.replace(/(^\/\/+)|\/{2,}/g,(l,d)=>d?"//":"/"),e.length>1&&e.endsWith("/")&&(e=e.replace(/\/+$/g,"")),e===""&&(e="/"),e=e.replace(/^([a-zA-Z]):(?=\/|$)/,(l,d)=>`${String(d).toUpperCase()}:`);let t=e.startsWith("/")||e.startsWith("//")||/^[A-Z]:\//.test(e),r=e.startsWith("//")?"//":e.startsWith("/")?"/":"",n="",i=e.match(/^([A-Z]:)(\/|$)/);i?(n=i[1],e=e.slice(n.length),e.startsWith("/")&&(e=e.slice(1))):e=e.replace(/^\/+/,"");let a=e.split("/").filter(Boolean),s=[];for(let l of a)if(l!=="."){if(l===".."){s.length>0&&s[s.length-1]!==".."?s.pop():t||s.push("..");continue}s.push(l)}let c=s.join("/");return n?c=`${n}/${c}`.replace(/\/+$/g,""):t&&(c=`${r}${c}`.replace(/\/+$/g,"")),c=c.toLowerCase(),c}function Zt(o,e=[3,3,3,3]){let t=en(o),n=Zr.createHash("sha256").update(t,"utf8").digest("base64url").replace(/[^a-zA-Z0-9]/g,"").toLowerCase(),i=e.reduce((c,l)=>c+l,0),a=n.length>=i?n:n.padEnd(i,n),s=0;return e.map(c=>a.slice(s,s+=c)).join("-")}function er(o){let e=en(o),r=Zr.createHash("sha256").update(e,"utf8").digest("hex").slice(0,32),n=r.slice(0,8),i=r.slice(8,12),a=r.slice(12,16),s=r.slice(16,20),c=r.slice(20,32);return`${n}-${i}-${a}-${s}-${c}`}import*as Q from"node:crypto";function tr(o){let e=Q.createPublicKey(o);if(e.asymmetricKeyType!=="ed25519")throw new Error(`Unexpected key type: ${e.asymmetricKeyType}`);let t=e.export({format:"der",type:"spki"});return Q.createHash("sha256").update(t).digest("base64url").slice(0,16).toUpperCase()}function rr(o){let e=Buffer.from(`${o.deviceId}|${o.ts}|${o.nonce}`,"utf8"),t=Q.createPrivateKey(o.privateKeyPem);return Q.sign(null,e,t).toString("base64")}function nr(o){let e=Q.createHash("sha256").update(o.refreshToken,"utf8").digest("hex"),t=Buffer.from(`${o.deviceId}|${o.ts}|${o.nonce}|${e}`,"utf8"),r=Q.createPrivateKey(o.privateKeyPem);return Q.sign(null,t,r).toString("base64")}function or(o){return new Promise(e=>setTimeout(e,o))}import*as tn from"node:path";var rn=process.platform==="win32";function ir(o){let e=tn.resolve(o);return rn?e.toLowerCase():e}import*as oe from"node:path";import*as nn from"node:os";function Ee(o){return o.replace(/\\/g,"/")}function on(o){return/^[A-Za-z]:[\\/]/.test(o)||/^[A-Za-z]:$/.test(o.trim())}function Io(o){return on(o)?o.replace(/\//g,"\\"):o}function sr(o){if(o==null)return;let e=String(o).trim();if(e==="")return;if(e.startsWith("~"))return Ee(e);if(!on(e))try{let l=oe.resolve(e),d=oe.resolve(nn.homedir()),p=oe.relative(d,l);if(!p.startsWith("..")&&!oe.isAbsolute(p))return p===""?"~":"~/"+Ee(p)}catch{}let r=Io(e).match(/^([A-Za-z]:)\\(?:[Uu][Ss][Ee][Rr][Ss])\\([^\\]+)(\\.*)?$/);if(r){let l=(r[3]??"").replace(/\\/g,"/");return l?"~"+l:"~"}let n=e.match(/^\/mnt\/[a-z]\/(?:[Uu][Ss][Ee][Rr][Ss])\/[^/]+(\/.*)?$/i);if(n){let l=n[1]??"";return l?"~"+Ee(l):"~"}let i=e.match(/^\/Users\/[^/]+(\/.*)?$/);if(i){let l=i[1]??"";return l?"~"+Ee(l):"~"}let a=e.match(/^\/home\/[^/]+(\/.*)?$/);if(a){let l=a[1]??"";return l?"~"+Ee(l):"~"}let s=e.match(/^\/usr\/home\/[^/]+(\/.*)?$/);if(s){let l=s[1]??"";return l?"~"+Ee(l):"~"}let c=e.match(/^\/export\/home\/[^/]+(\/.*)?$/);if(c){let l=c[1]??"";return l?"~"+Ee(l):"~"}try{return oe.basename(oe.resolve(e))}catch{return e.split(/[/\\]/).pop()??e}}function L(o){if(o instanceof Error)return o.message;if(typeof o=="object"&&o!==null&&"message"in o){let e=o.message;if(typeof e=="string")return e}return String(o)}function ke(o){if(typeof o!="object"||o===null||!("code"in o))return;let e=o.code;return typeof e=="string"?e:void 0}var De={version:1,controlBaseUrl:"",bridgeWsUrl:"",directories:[],device:void 0},bt=class{_notifier;argv;config=De;envFile=cr.config({quiet:!0});processEnv={};productionEnv={};configFileContent={};_privateKeyCache=void 0;_privateKeyToStore=void 0;constructor(e){this._notifier=e,this.loadProductionEnv()}loadProductionEnv(){let e=[_e.resolve(_e.dirname(process.argv[1]??"."),".env.production"),_e.resolve(process.cwd(),".env.production")];for(let t of e)try{if(ar.existsSync(t)){let r=cr.config({path:t,quiet:!0});r.parsed&&(this.productionEnv=r.parsed);return}}catch{}}setPrivateKeyCache(e){this._privateKeyCache=e}getPrivateKeyToStore(){return this._privateKeyToStore}withDefaultConfig(){return this.config.version=De.version,this.config.controlBaseUrl=De.controlBaseUrl,this.config.bridgeWsUrl=De.bridgeWsUrl,this.config.directories=De.directories,this.config.device=De.device,this}withProcessEnv(){return this.processEnv={NODE_ENV:process.env.NODE_ENV,APPDATA:process.env.APPDATA,npm_package_version:process.env.npm_package_version,DLINK_DEVICE_ID:process.env.DLINK_DEVICE_ID,DLINK_DEVICE_NAME:process.env.DLINK_DEVICE_NAME,DLINK_DEVICE_TAG:process.env.DLINK_DEVICE_TAG,DLINK_CONTROL_URL:process.env.DLINK_CONTROL_URL,DLINK_BRIDGE_URL:process.env.DLINK_BRIDGE_URL,DLINK_DIRECTORIES:process.env.DLINK_DIRECTORIES},this}withArgv(e){return this.argv=e,this}withEnviromentFile(e){return this.envFile=cr.config({path:e,quiet:!0}),this}withConfigFile(e){try{let t=this.getApplicationConfigFolder(),r=_e.join(t,e);if(ar.existsSync(r)){let n=ar.readFileSync(r,"utf8"),i=JSON.parse(n);ur(i)&&!yt(i)?this.configFileContent=i:this._notifier.warn(`Config file ${e} does not contain a valid object`,{scope:"system"})}}catch(t){let r=t instanceof Error?t.message:String(t);this._notifier.warn(`Failed to load config file ${e}: ${r}`,{scope:"system"})}return this}build(){return this.config.controlBaseUrl=this.getControlBaseUrl(),this.config.bridgeWsUrl=this.getBridgeWsUrl(),this.config.directories=this.getDirectories(),this.config.device=this.getDevice(),this.config.tokens=this.getTokens(),this.config}getConfigValue(e,t){let r=this.safeGetConfigValue(e);return r===void 0?t?.defaultValue:r}firstSet(...e){return e.find(t=>t!=null&&String(t).trim()!=="")}getControlBaseUrl(){return this.firstSet(this.argv?.controlUrl,this.safeGetEnvValue("DLINK_CONTROL_URL"),this.safeGetConfigValue("controlBaseUrl"),this.processEnv.DLINK_CONTROL_URL,this.productionEnv.DLINK_CONTROL_URL)}getBridgeWsUrl(){return this.firstSet(this.argv?.bridgeWsUrl,this.safeGetEnvValue("DLINK_BRIDGE_URL"),this.safeGetConfigValue("bridgeWsUrl"),this.processEnv.DLINK_BRIDGE_URL,this.productionEnv.DLINK_BRIDGE_URL)}getDirectories(){let e=[];if(e.push(this.createDirectoryFromPath(process.cwd())),this.argv?.rootPath)try{e.push(...this.parseDirectoriesFromString(this.argv.rootPath))}catch{e.push(this.createDirectoryFromPath(this.argv.rootPath))}let t=this.safeGetEnvValue("DLINK_DIRECTORIES")??this.safeGetEnvValue("DLINK_ROOTS");if(t)try{e.push(...this.parseDirectoriesFromString(t))}catch{}let r=this.safeGetConfigValue("directories")??this.safeGetConfigValue("roots");if(r)try{e.push(...this.parseDirectoriesFromValue(r))}catch{}let n=this.processEnv.DLINK_DIRECTORIES??this.processEnv.DLINK_ROOTS;if(n)try{e.push(...this.parseDirectoriesFromString(n))}catch{}return this.deduplicateDirectories(e)}deduplicateDirectories(e){let t=new Set,r=[];for(let n of e){let i=ir(n.path);t.has(i)||(t.add(i),r.push(n))}return r}getDevice(){let e=this.getDeviceId();if(!e){this._notifier.warn("No device ID found from any source (argv, env, config, process env, or machine ID)",{scope:"system"});return}let t=this.getDeviceName();t||(t=sn.hostname()||"Unknown Device");let r=this.getDeviceTag(),n=new gt({notifier:this._notifier,argv:this.argv,envFile:this.envFile,processEnv:this.processEnv,configFileContent:this.configFileContent,getPrivateKey:()=>this._privateKeyCache}),{publicKey:i}=n.ensureDeviceKeys();this._privateKeyToStore=n.getLastGeneratedPrivateKey();let a={id:e,name:t,tag:r||"",keys:{public:i}};return this._notifier.info(`Created device: id=${a.id?.substring(0,8)}..., name=${a.name}, tag=${a.tag||"(empty)"}`,{scope:"system"}),a}getDeviceId(){this._notifier.info("Checking env file for DLINK_DEVICE_ID...",{scope:"system"});let e=this.safeGetEnvValue("DLINK_DEVICE_ID");if(e&&!le(e))return this._notifier.info(`Found device ID in env file: ${e.substring(0,8)}...`,{scope:"system"}),e;this._notifier.info("Checking config file for deviceId...",{scope:"system"});let t=this.safeGetConfigValue("deviceId")??ue(this.safeGetConfigValue("device"),"id");if(t&&!le(String(t)))return this._notifier.info(`Found device ID in config file: ${String(t).substring(0,8)}...`,{scope:"system"}),String(t);this._notifier.info("Checking process env for DLINK_DEVICE_ID...",{scope:"system"});let r=this.processEnv.DLINK_DEVICE_ID;if(r&&!le(r))return this._notifier.info(`Found device ID in process env: ${r.substring(0,8)}...`,{scope:"system"}),r;this._notifier.info("Trying machine ID as fallback...",{scope:"system"});let n=mt(!0);if(n)return this._notifier.info(`Using machine ID: ${n.substring(0,8)}...`,{scope:"system"}),n;this._notifier.error("Failed to get machine ID in getDeviceId()",{scope:"system"}),this._notifier.warn("No device ID found in any source",{scope:"system"})}getDeviceName(){let e=this.safeGetEnvValue("DLINK_DEVICE_NAME");if(e&&!le(e))return e;let t=ue(this.safeGetConfigValue("device"),"name");if(t&&!le(String(t)))return String(t);let r=this.processEnv.DLINK_DEVICE_NAME;if(r&&!le(r))return r;let n=sn.hostname();if(n&&!le(n))return n}getDeviceTag(){let e=this.safeGetEnvValue("DLINK_DEVICE_TAG");if(e)return e;let t=ue(this.safeGetConfigValue("device"),"tag");if(t)return String(t);let r=this.processEnv.DLINK_DEVICE_TAG;if(r)return r}getTokens(){let e=this.safeGetConfigValue("tokens");if(!e||!ur(e)||yt(e))return;let t=e,r={},n=ue(t,"accessTokenExpSec");if(typeof n=="number"&&(r.accessTokenExpSec=n),r.accessTokenExpSec!==void 0)return r}safeGetEnvValue(e){try{return ue(this.envFile,`parsed.${e}`)}catch{return}}safeGetConfigValue(e){try{return ue(this.configFileContent,e)}catch{return}}parseDirectoriesFromString(e){try{let t=JSON.parse(e);return this.parseDirectoriesFromValue(t)}catch{return[this.createDirectoryFromPath(e)]}}parseDirectoriesFromValue(e){if(yt(e))return e.map(r=>this.parseDirectoryFromValue(r)).filter(r=>r!==null);if(an(e))return[this.createDirectoryFromPath(e)];let t=this.parseDirectoryFromValue(e);return t?[t]:[]}parseDirectoryFromValue(e){if(an(e))return this.createDirectoryFromPath(e);if(ur(e)&&!yt(e)){let t=e,r=er(ue(t,"path")),n=ue(t,"path"),i=Do(ue(t,"name"),n?_e.basename(n):"");if(!le(r)&&!le(n)){let a=Zt(n);return{id:String(r),name:String(i),path:String(n),tag:a}}}return null}createDirectoryFromPath(e){let t=_e.resolve(e),r=er(t),n=_e.basename(t),i=Zt(t);return{id:r,name:n,path:t,tag:i}}getApplicationConfigFolder(){return me()}};var Be=class{dedupeKey;parentId;overridePriority;overrideLane;constructor(e,t,r,n){this.dedupeKey=e,this.parentId=t,this.overridePriority=r,this.overrideLane=n}},lr=class{phase;flags;constructor(e){this.phase="idle",this.flags=e??new fe(!1)}},vt=class{argv;configurationContext;bus;notifier;logger;storage;secrets;runtime;lanes;laneActive;registry;orchestrators;orchestratorSubscriptions=new Map;orchestratorAbort;jobs;queue;locksHeldBy;completions;dedupeIndex;engineAbort;jobAbort;stopping;running;wakeFn;wakePromise;constructor(e){this.argv=e.argv,this.bus=e.bus,this.logger=e.logger,this.notifier=e.notifier,this.storage=e.storage,this.secrets=e.secrets,this.runtime=new lr(e.flags),this.lanes=new Map;for(let n of e.lanes)this.lanes.set(n.name,n);this.laneActive=new Map;for(let n of e.lanes)this.laneActive.set(n.name,0);this.registry=new Map,this.orchestrators=[],this.orchestratorAbort=new AbortController,this.jobs=new Map,this.queue=[],this.locksHeldBy=new Map,this.completions=new Map,this.dedupeIndex=new Map,this.engineAbort=new AbortController,this.jobAbort=new Map,this.stopping=!1,this.running=!1,this.wakeFn=null,this.wakePromise=this.resetWake();let t=new ft,r=new bt(this.notifier);this.configurationContext=new ht(t,r)}applyConfiguration(e){this.configurationContext.apply(e)}setPhase(e){this.runtime.phase=e}getBus(){return this.bus}hasRunningJobs(){for(let[,e]of this.laneActive)if(e>0)return!0;return!1}register(e){let t=e.options.name;if(this.registry.has(t))throw new Error(`JobType already registered: ${t}`);this.registry.set(t,e)}registerOrchestrator(e){if(this.orchestrators.some(n=>n.name===e.name))throw new Error(`Orchestrator already registered: ${e.name}`);let r=new Ie({engine:this,bus:this.bus,logger:this.logger,notifier:this.notifier,flags:this.runtime.flags,signal:this.orchestratorAbort.signal});e.onRegister?.(r),this.setupOrchestratorSubscriptions(e,r),this.orchestrators.push(e)}setupOrchestratorSubscriptions(e,t){let r=e.handlers?.();if(r){let n=[];Object.keys(r).forEach(i=>{let a=r[i];a&&n.push(this.bus.on(i,s=>{Promise.resolve(a(s,t)).catch(c=>{let l=c instanceof Error?c.message:String(c);this.logger.error(`handler error (${e.name}/${String(i)}): ${l}`),this.notifier.error(`handler error (${e.name}/${String(i)}): ${l}`,{scope:"system"})})}))}),this.orchestratorSubscriptions.set(e.name,n)}}async init(){let e=await this.storage.load();for(let[t,r]of Object.entries(e.jobs))this.jobs.set(t,new We(r));for(let t of this.jobs.values())t.state==="running"&&(t.state="queued",t.nextRunAt=Date.now(),t.updatedAt=Date.now()),t.dedupeKey&&this.dedupeIndex.set(t.dedupeKey,t.id),t.state==="queued"&&this.queue.push(t.id)}async start(){if(!this.running){this.running=!0,this.runtime.phase="running",process.on("uncaughtException",e=>{this.notifier.error(`uncaughtException: ${e.stack?e.stack:String(e)}`,{scope:"system"}),this.requestStop("uncaughtException")}),process.on("unhandledRejection",e=>{this.notifier.error(`unhandledRejection: ${String(e)}`,{scope:"system"})}),this.bus.emit("engine:started",Date.now());try{await this.startOrchestratorsSafe(),await this.loopGuarded()}finally{await this.stopOrchestratorsSafe(),await this.persist(),this.bus.emit("engine:stopped",Date.now())}}}requestStop(e){if(!this.stopping){this.stopping=!0,this.runtime.phase="stopping",this.bus.emit("engine:stopping",e),this.engineAbort.abort(new Error(`Stopping: ${e}`)),this.orchestratorAbort.abort(new Error(`Stopping: ${e}`));for(let[t,r]of this.jobAbort.entries())this.jobs.get(t)?.cancellable&&r.abort(new Error("Cancelled by user"));this.wake()}}async dispatch(e,t){let r=t||new Be(null,null,null,null),n=e.type.options.name;if(!this.registry.has(n)){let p=new Error(`JobType not registered: ${n}`);return he.failure(p)}if(r.dedupeKey){let p=this.dedupeIndex.get(r.dedupeKey);if(p){let f=this.jobs.get(p);if(f&&(f.state==="queued"||f.state==="running")){let g=this.completions.get(p);if(g)return await g.promise}}}let i=this.newId(),a=new ct;this.completions.set(i,a);let s=r.overrideLane?r.overrideLane:e.type.options.lane,c=r.overridePriority!==null?r.overridePriority:e.type.options.priority,l=new We({id:i,jobType:n,inputJson:e.input===void 0?"null":JSON.stringify(e.input),state:"queued",lane:s,priority:c,locks:e.type.options.locks,attempts:0,maxAttempts:e.type.options.maxAttempts,nextRunAt:Date.now(),cancellable:e.type.options.cancellable,parentId:r.parentId,dedupeKey:r.dedupeKey,lastError:null,createdAt:Date.now(),updatedAt:Date.now()});return this.jobs.set(i,l),this.queue.push(i),r.dedupeKey&&this.dedupeIndex.set(r.dedupeKey,i),this.emitJobEvent(l,"queued",null,null),await this.persist(),this.wake(),await a.promise}async loopGuarded(){for(;!this.stopping;)try{await this.loopOnce()}catch(e){this.notifier.error(`Engine loop error: ${String(e)}`,{scope:"system"}),await new Promise(t=>setTimeout(t,250))}}async loopOnce(){let e=0;for(;;){let t=this.pickNextRunnable();if(!t)break;e+=1,this.runJobSafe(t)}e===0&&(await this.wakePromise,this.wakePromise=this.resetWake())}async runJobSafe(e){try{await this.runJobInternal(e)}catch(t){this.notifier.error(`runJobInternal crashed: ${String(t)}`,{scope:"system"});let r=this.completions.get(e);r&&r.resolve(he.failure(new Error(String(t))))}finally{this.wake()}}pickNextRunnable(){let e=Date.now(),t=null,r=Number.NEGATIVE_INFINITY,n=Number.POSITIVE_INFINITY;for(let a of this.queue){let s=this.jobs.get(a);s&&s.state==="queued"&&(this.stopping&&s.lane!=="console"||s.nextRunAt>e||this.canRunInLane(s.lane)&&this.canAcquireLocks(s)&&(s.priority>r||s.priority===r&&s.nextRunAt<n)&&(r=s.priority,n=s.nextRunAt,t=a))}if(!t)return null;let i=this.queue.indexOf(t);return i>=0&&this.queue.splice(i,1),t}canRunInLane(e){let t=this.lanes.get(e);if(!t)return!1;let r=this.laneActive.get(e);return r===void 0?!1:r<t.concurrency}canAcquireLocks(e){for(let t of e.locks){let r=this.locksHeldBy.get(t);if(r&&r!==e.id&&!(e.parentId&&r===e.parentId))return!1}return!0}acquireLocks(e){for(let t of e.locks)this.locksHeldBy.get(t)||this.locksHeldBy.set(t,e.id)}releaseLocks(e){for(let t of e.locks)this.locksHeldBy.get(t)===e.id&&this.locksHeldBy.delete(t)}async runJobInternal(e){let t=this.jobs.get(e);if(!t)return;let r=this.registry.get(t.jobType);if(!r){this.completeFailure(e,new Error(`No registered JobType: ${t.jobType}`));return}let n=this.laneActive.get(t.lane);if(n===void 0){this.completeFailure(e,new Error(`Unknown lane: ${t.lane}`));return}let i=new AbortController;if(this.jobAbort.set(e,i),t.cancellable){let s=this.engineAbort.signal;s.aborted?i.abort(s.reason instanceof Error?s.reason:new Error("Stopped")):s.addEventListener("abort",()=>i.abort(s.reason instanceof Error?s.reason:new Error("Stopped")),{once:!0})}this.laneActive.set(t.lane,n+1),this.acquireLocks(t),t.state="running",t.updatedAt=Date.now(),await this.persist(),this.emitJobEvent(t,"running",null,null);let a=new ut({engine:this,bus:this.bus,logger:this.logger,notifier:this.notifier,flags:this.runtime.flags,signal:i.signal,dispatch:async(s,c)=>this.dispatch(s,c)});try{let s=JSON.parse(t.inputJson),c=s===null?void 0:s,l=await r.run(c,a);t.state="succeeded",t.updatedAt=Date.now(),t.lastError=null,await this.persist(),this.emitJobEvent(t,"succeeded",null,null),this.completeSuccess(e,l)}catch(s){let c=s instanceof Error?s:new Error("Unknown error");if(t.attempts+=1,t.updatedAt=Date.now(),t.lastError=c.message,i.signal.aborted)t.state="failed",await this.persist(),this.emitJobEvent(t,"failed",c.message,null),this.notifier.error(`Job ${t.jobType} (${e.substring(0,8)}) cancelled: ${c.message}`),this.completeCancelled(e,c);else if(t.attempts<t.maxAttempts&&!this.stopping){let p=No(t.attempts);t.state="queued",t.nextRunAt=Date.now()+p,await this.persist(),this.queue.push(t.id),this.emitJobEvent(t,"queued",c.message,t.nextRunAt),this.notifier.warn(`Job ${t.jobType} (${e.substring(0,8)}) failed, retrying in ${p}ms (attempt ${t.attempts}/${t.maxAttempts}): ${c.message}`)}else t.state="failed",await this.persist(),this.emitJobEvent(t,"failed",c.message,null),this.notifier.error(`Job ${t.jobType} (${e.substring(0,8)}) failed after ${t.attempts} attempt(s): ${c.message}`),this.completeFailure(e,c)}finally{this.releaseLocks(t);let s=this.laneActive.get(t.lane);s!==void 0&&this.laneActive.set(t.lane,Math.max(0,s-1)),this.jobAbort.delete(e),this.wake()}}async startOrchestratorsSafe(){let e=new Ie({engine:this,bus:this.bus,logger:this.logger,notifier:this.notifier,flags:this.runtime.flags,signal:this.orchestratorAbort.signal});for(let t of this.orchestrators)Promise.resolve().then(()=>t.onStart?.(e)).then(()=>t.onStarted?.(e)).catch(r=>this.notifier.error(`Orchestrator start failed (${t.name}): ${String(r)}`,{scope:"system"}))}async stopOrchestratorsSafe(){let e=new Ie({engine:this,bus:this.bus,logger:this.logger,notifier:this.notifier,flags:this.runtime.flags,signal:this.orchestratorAbort.signal});for(let t=this.orchestrators.length-1;t>=0;t--){let r=this.orchestrators[t];try{await r.onStop?.(e),r.onStopped?.(e)}catch(i){this.notifier.error(`Orchestrator stop failed (${r.name}): ${String(i)}`,{scope:"system"})}let n=this.orchestratorSubscriptions.get(r.name);if(n){for(let i of n)i.unsubscribe();this.orchestratorSubscriptions.delete(r.name)}}}emitJobEvent(e,t,r,n){this.bus.emit("job:event",new dt({ts:Date.now(),jobId:e.id,jobType:e.jobType,state:t,attempt:e.attempts,maxAttempts:e.maxAttempts,lane:e.lane,priority:e.priority,error:r,nextRunAt:n}))}completeSuccess(e,t){let r=this.completions.get(e);r&&r.resolve(he.success(t))}completeFailure(e,t){let r=this.completions.get(e);r&&r.resolve(he.failure(t))}completeCancelled(e,t){let r=this.completions.get(e);r&&r.resolve(he.cancelled(t))}async persist(){let e={};for(let[t,r]of this.jobs.entries())e[t]=r;await this.storage.save(new Se(e))}wake(){this.wakeFn&&this.wakeFn()}resetWake(){return new Promise(e=>{this.wakeFn=e})}newId(){return Oo.randomBytes(8).toString("hex")}};function No(o){let r=Math.min(3e4,300*2**(o-1)),n=Math.floor(Math.random()*200);return r+n}var St=class{handlers;constructor(){this.handlers=new Map}on(e,t){let r=this.handlers.get(e),n=r||[];return n.push(t),this.handlers.set(e,n),{unsubscribe:()=>{let i=this.handlers.get(e);if(!i)return;let a=i.filter(s=>s!==t);this.handlers.set(e,a)}}}emit(e,t){let r=this.handlers.get(e);if(r)for(let n of r)n(t)}};import*as ye from"node:crypto";import*as be from"node:fs/promises";import*as kt from"node:path";var Po="DeviceLink-SecretStorage-v1",Je=12,Et=16,Fo=32,Mo="secrets.enc.json";function Lo(){return kt.join(me(),Mo)}function $o(o){return ye.pbkdf2Sync(o,Po,1e5,Fo,"sha256")}var pr=class{secretsPath;key;cache={};loaded=!1;constructor(e,t){this.secretsPath=e,this.key=t}async load(){if(!this.loaded){try{let e=await be.readFile(this.secretsPath,"utf8"),t=this.decrypt(e);this.cache=JSON.parse(t)}catch{this.cache={}}this.loaded=!0}}async save(){await be.mkdir(kt.dirname(this.secretsPath),{recursive:!0});let e=JSON.stringify(this.cache),t=this.encrypt(e),r=this.secretsPath+".tmp";await be.writeFile(r,t,"utf8"),await be.rename(r,this.secretsPath)}encrypt(e){let t=ye.randomBytes(Je),r=ye.createCipheriv("aes-256-gcm",this.key,t,{authTagLength:Et}),n=Buffer.concat([r.update(e,"utf8"),r.final()]),i=r.getAuthTag();return Buffer.concat([t,i,n]).toString("base64url")}decrypt(e){let t=Buffer.from(e,"base64url"),r=t.subarray(0,Je),n=t.subarray(Je,Je+Et),i=t.subarray(Je+Et),a=ye.createDecipheriv("aes-256-gcm",this.key,r,{authTagLength:Et});return a.setAuthTag(n),a.update(i)+a.final("utf8")}async get(e){await this.load();let t=this.cache[e];return t===void 0?{ok:!1}:{ok:!0,value:t}}async set(e,t){await this.load(),this.cache[e]=t,await this.save()}async delete(e){await this.load(),this.cache[e]!==void 0&&(delete this.cache[e],await this.save())}};async function cn(){let o=mt(!0);if(!o)throw new Error("Secret storage requires machine ID");let e=$o(o),t=Lo();return new pr(t,e)}var _t=class{},fr=class{ok;config;constructor(e,t){this.ok=e,this.config=t}},hr=class extends B{constructor(){super(new U({name:"load-config",lane:"main",priority:1e3,locks:["GLOBAL"],maxAttempts:3,cancellable:!0}))}async run(e,t){t.notifier.info("Loading configuration...",{scope:"system"});let r=t.engine.configurationContext.loader,n=await t.engine.secrets.get("devicePrivateKey");r.setPrivateKeyCache(n.ok&&n.value?n.value:void 0),r.withDefaultConfig(),r.withArgv(t.engine.argv),t.engine.argv.enviromentFile&&r.withEnviromentFile(t.engine.argv.enviromentFile),r.withConfigFile("config.json"),r.withProcessEnv();let i=r.build(),a=r.getPrivateKeyToStore();return a&&await t.engine.secrets.set("devicePrivateKey",a),this.updateUi(i,t),t.notifier.success("Configuration loaded"),t.engine.applyConfiguration(i),t.bus.emit("config:loaded",i),new fr(!0,i)}updateUi(e,t){let r={scope:"system"};t.notifier.info(`Control base URL: ${e.controlBaseUrl}`,r),t.notifier.info(`Bridge WebSocket URL: ${e.bridgeWsUrl}`,r),t.notifier.info(`Directories: ${e.directories.map(n=>n.path).join(", ")}`,r),t.notifier.info(`Device: ${e.device?.id}`,r),t.notifier.info(`Device name: ${e.device?.name}`,r),t.notifier.info(`Device tag: ${e.device?.tag}`,r),t.notifier.info(`Device keys public: ${e.device?.keys?.public?"***":"(none)"}`,r)}},mr=new hr;function un(o){return new ee(mr,o)}var gr=class{ok;constructor(e){this.ok=e}},yr=class extends B{constructor(){super(new U({name:"console-command",lane:"console",priority:1e3,locks:[],maxAttempts:1,cancellable:!1}))}async run(e,t){return t.notifier.info(`Executing console command: ${e.kind}`),await new Promise(r=>setTimeout(r,2e3)),t.notifier.info(`Console command executed: ${e.kind}`),t.notifier.success(`Console command executed: ${e.kind}`),new gr(!0)}},ln=new yr;var br=class{ok;constructor(e){this.ok=e}},vr=class extends B{constructor(){super(new U({name:"contact-server",lane:"main",priority:900,locks:["GLOBAL"],maxAttempts:8,cancellable:!0}))}async run(e,t){return t.notifier.info(`contact server mode=${e.mode}`,{scope:"system"}),await new Promise(r=>setTimeout(r,200)),new br(!0)}},dn=new vr;var Sr=class{ok;constructor(e){this.ok=e}},Er=class extends B{constructor(){super(new U({name:"send-server-result",lane:"io",priority:200,locks:[],maxAttempts:10,cancellable:!0}))}async run(e,t){return t.notifier.info(`send result requestId=${e.requestId}`,{scope:"system"}),await new Promise(r=>setTimeout(r,10)),new Sr(!0)}},pn=new Er;import Ko from"node:crypto";var H=class{constructor(e,t){this.context=e;this.handler=t}execute(e){return this.handler(this.context,e)}};async function ie(o,e,t){let r=`${o.configuration.controlBaseUrl}${e}`,n=await fetch(r,t);if(!n.ok){let i=await n.text().catch(s=>{let c=s instanceof Error?s.message:String(s);return o.notifier.error(`Failed to read HTTP error response body: ${c}`),""}),a=new Error(`HTTP ${n.status}: ${i}`);a.status=n.status;try{a.body=i?JSON.parse(i):void 0}catch{a.body=void 0}if(n.status===429){let s=n.headers.get("Retry-After");if(s!==null){let c=parseInt(s,10);Number.isFinite(c)&&c>=0&&(a.retryAfterSec=c)}}throw a}return n.json()}function fn(o){return new H(o,async(e,t)=>ie(e,"/api/device/authorize/start",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(t)}))}function hn(o){return new H(o,async(e,t)=>ie(e,"/api/device/authorize/prove",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(t)}))}import{z as te}from"zod";var jo=te.union([te.object({status:te.literal("pending"),needs_proof:te.boolean()}),te.object({status:te.literal("approved"),needs_proof:te.boolean()}),te.object({error:te.literal("access_denied")}),te.object({error:te.literal("expired_token")})]);function mn(o){return new H(o,async(e,t)=>{let r=new URL("/api/device/authorize/stream",e.configuration.controlBaseUrl);r.searchParams.set("device_code",t.deviceCode);let n=e.notifier,i=new AbortController,a=setTimeout(()=>{n.error("[deviceAuthStream] Timeout reached, aborting"),i.abort()},Math.max(1e3,t.timeoutMs));if(t.signal){let s=t.signal;s.addEventListener("abort",()=>{let c="reason"in s&&s.reason!=null?String(s.reason):"";n.warn(`[deviceAuthStream] External signal aborted: ${c}`),i.abort()})}try{let s=await fetch(r.toString(),{method:"GET",headers:{Accept:"text/event-stream"},signal:i.signal});if(!s.ok||!s.body){let p=await s.text().catch(f=>{let g=f instanceof Error?f.message:String(f);return n.error(`[deviceAuthStream] Failed to read response body (${s.status}): ${g}`),""});throw n.error(`[deviceAuthStream] Response error: ${s.status} - ${p}`),new Error(`device/authorize/stream failed (${s.status}): ${p}`)}let c=s.body.getReader(),l=new TextDecoder("utf-8"),d="";for(;;){let{value:p,done:f}=await c.read();if(f){n.error("[deviceAuthStream] Stream ended unexpectedly");break}for(d+=l.decode(p,{stream:!0});;){let g=d.indexOf(`
2
+ import Zi from"node:crypto";var _e=class{debug;headless;headlessReason;constructor(e,t=!1,r=""){this.debug=e,this.headless=t,this.headlessReason=r}};var ye=class{name;concurrency;constructor(e,t){this.name=e,this.concurrency=t}};var Ee=class o{status;output;error;constructor(e,t,r){this.status=e,this.output=t,this.error=r}static success(e){return new o("success",e,null)}static failure(e){return new o("failure",null,e)}static cancelled(e){return new o("cancelled",null,e)}},Et=class{promise;resolveFn;rejectFn;constructor(){this.promise=new Promise((e,t)=>{this.resolveFn=e,this.rejectFn=t})}resolve(e){this.resolveFn(e)}reject(e){this.rejectFn(e)}};var q=class{name;lane;priority;locks;maxAttempts;cancellable;constructor(e){this.name=e.name,this.lane=e.lane,this.priority=e.priority,this.locks=e.locks,this.maxAttempts=e.maxAttempts,this.cancellable=e.cancellable}},W=class{options;constructor(e){this.options=e}},ie=class{type;input;constructor(e,t){this.type=e,this.input=t}},kt=class{bus;logger;notifier;flags;signal;engine;get headless(){return this.flags.headless}dispatchFn;constructor(e){this.bus=e.bus,this.engine=e.engine,this.logger=e.logger,this.notifier=e.notifier,this.flags=e.flags,this.signal=e.signal,this.dispatchFn=e.dispatch}dispatch(e,t){let r=t||new Xe(null,null,null,null);return this.dispatchFn(e,r)}};var Tt=class{ts;message;constructor(e,t){this.ts=t?.ts??Date.now(),this.message=e}},ue=class{id;ts;groupKey;kind;scope;message;category;constructor(e,t){this.id=crypto.randomUUID(),this.ts=t?.ts??Date.now(),this.kind=t?.kind??"info",this.scope=t?.scope??"default",this.message=e,this.groupKey=t?.groupKey,this.category=t?.category??[]}},wt=class{ts;jobId;jobType;state;attempt;maxAttempts;lane;priority;error;nextRunAt;constructor(e){this.ts=e.ts,this.jobId=e.jobId,this.jobType=e.jobType,this.state=e.state,this.attempt=e.attempt,this.maxAttempts=e.maxAttempts,this.lane=e.lane,this.priority=e.priority,this.error=e.error,this.nextRunAt=e.nextRunAt}};var xe=class{jobs;constructor(e){this.jobs=e}},et=class{id;jobType;inputJson;state;lane;priority;locks;attempts;maxAttempts;nextRunAt;cancellable;parentId;dedupeKey;lastError;createdAt;updatedAt;constructor(e){this.id=e.id,this.jobType=e.jobType,this.inputJson=e.inputJson,this.state=e.state,this.lane=e.lane,this.priority=e.priority,this.locks=e.locks,this.attempts=e.attempts,this.maxAttempts=e.maxAttempts,this.nextRunAt=e.nextRunAt,this.cancellable=e.cancellable,this.parentId=e.parentId,this.dedupeKey=e.dedupeKey,this.lastError=e.lastError,this.createdAt=e.createdAt,this.updatedAt=e.updatedAt}};var Ct=class{state;constructor(e){this.state=e??new xe({})}async load(){return new xe(structuredClone(this.state.jobs))}async save(e){this.state=new xe(structuredClone(e.jobs))}};var Me=class{engine;bus;logger;notifier;flags;signal;get headless(){return this.flags.headless}constructor(e){this.engine=e.engine,this.bus=e.bus,this.logger=e.logger,this.notifier=e.notifier,this.flags=e.flags,this.signal=e.signal}on(e,t){return this.bus.on(e,t)}};var Rt=class{version=1;controlBaseUrl;bridgeWsUrl;directories;device;tokens;constructor(){this.directories=[]}},xt=class{_configuration;loader;get configuration(){return this._configuration}constructor(e,t){this._configuration=e,this.loader=t}apply(e){this._configuration=e}};import De from"node:path";import Vn from"node:os";import Fr from"node:fs";import Ri from"node:path";import xi from"env-paths";var Ai="DeviceLink",Di=xi(Ai,{suffix:""});function Ii(){if(process.env.DEVICELINK_DATA_DIR)return process.env.DEVICELINK_DATA_DIR;let o=process.env.DEVICELINK_ENV,e=Di.config;return!o||o==="production"?e:Ri.join(e,o)}function Q(){return Ii()}import Lr from"dotenv";import be from"lodash/get.js";import Ft from"lodash/isArray.js";import $r from"lodash/isObject.js";import zn from"lodash/isString.js";import ve from"lodash/isEmpty.js";import Xi from"lodash/defaultTo.js";import{createRequire as Oi}from"node:module";var Ni=Oi(import.meta.url),Pi=Ni("node-machine-id");function At(o=!0){try{let e=Pi.machineIdSync(o);if(e&&typeof e=="string"&&e.trim().length>0)return e.trim()}catch{}}import Ln from"node:path";import vr from"node:fs";import Sr from"node:crypto";import _r from"lodash/get.js";import ke from"lodash/isEmpty.js";var Dt=class{_notifier;argv;envFile;processEnv;configFileContent;getPrivateKey;_lastGeneratedPrivateKey;constructor(e){this._notifier=e.notifier,this.argv=e.argv,this.envFile=e.envFile,this.processEnv=e.processEnv,this.configFileContent=e.configFileContent,this.getPrivateKey=e.getPrivateKey}getLastGeneratedPrivateKey(){let e=this._lastGeneratedPrivateKey;return this._lastGeneratedPrivateKey=void 0,e}ensureDeviceKeys(){let e=this.getDevicePrivateKey(),t=this.getDevicePublicKey();if(e&&t)return{publicKey:t,privateKey:e};if(e&&!t)try{let s=Sr.createPrivateKey(e);return t=Sr.createPublicKey(s).export({format:"pem",type:"spki"}).toString("utf8"),this._notifier.info("Derived public key from private key",{scope:"system"}),{publicKey:t,privateKey:e}}catch{this._notifier.warn("Failed to derive public key from private key, generating new keypair",{scope:"system"})}t&&!e&&this._notifier.warn("Public key found but no private key, generating new keypair",{scope:"system"}),this._notifier.info("Generating new Ed25519 keypair...",{scope:"system"});let{publicKey:r,privateKey:n}=Sr.generateKeyPairSync("ed25519"),i=r.export({format:"pem",type:"spki"}).toString("utf8"),a=n.export({format:"pem",type:"pkcs8"}).toString("utf8");return this._lastGeneratedPrivateKey=a,this._notifier.info("Generated new Ed25519 keypair",{scope:"system"}),{publicKey:i,privateKey:a}}getDevicePrivateKey(){if(this.getPrivateKey){let r=this.getPrivateKey();if(r&&!ke(r))return this._notifier.info("Using private key from secret storage cache",{scope:"system"}),r}if(this.argv?.privateKeyFile){let r=this.loadKeyFromFile(this.argv.privateKeyFile);if(r)return this._notifier.info(`Found private key from --private-key-file: ${this.argv.privateKeyFile}`,{scope:"system"}),r;this._notifier.warn(`Failed to load private key from file: ${this.argv.privateKeyFile}`,{scope:"system"})}let e=this.safeGetEnvValue("DLINK_DEVICE_PRIVATE_KEY");if(e&&!ke(e))return this._notifier.info("Found private key in env file",{scope:"system"}),e;let t=this.processEnv.DLINK_DEVICE_PRIVATE_KEY;if(t&&!ke(t))return this._notifier.info("Found private key in process env",{scope:"system"}),t}getDevicePublicKey(){if(this.argv?.publicKeyFile){let n=this.loadKeyFromFile(this.argv.publicKeyFile);if(n)return this._notifier.info(`Found public key from --public-key-file: ${this.argv.publicKeyFile}`,{scope:"system"}),n;this._notifier.warn(`Failed to load public key from file: ${this.argv.publicKeyFile}`,{scope:"system"})}let e=_r(this.safeGetConfigValue("device"),"keys.public");if(e&&!ke(String(e)))return this._notifier.info("Found public key in config file",{scope:"system"}),String(e);let t=this.safeGetEnvValue("DLINK_DEVICE_PUBLIC_KEY");if(t&&!ke(t))return this._notifier.info("Found public key in env file",{scope:"system"}),t;let r=this.processEnv.DLINK_DEVICE_PUBLIC_KEY;if(r&&!ke(r))return this._notifier.info("Found public key in process env",{scope:"system"}),r}loadKeyFromFile(e){if(!e||ke(e))return;let t=this.getApplicationConfigFolder(),r=[e,Ln.join(t,e)];for(let n of r)try{let i=Ln.resolve(n);if(vr.existsSync(i))if(vr.statSync(i).isFile()){let s=vr.readFileSync(i,"utf8").trim();if(ke(s))this._notifier.warn(`Key file is empty: ${i}`,{scope:"system"});else return this._notifier.info(`Loaded key from file: ${i}`,{scope:"system"}),s}else this._notifier.warn(`Path is not a file: ${i}`,{scope:"system"})}catch(i){let a=i instanceof Error?i.message:String(i);this._notifier.warn(`Failed to read key file ${n}: ${a}`,{scope:"system"})}this._notifier.warn(`Key file not found: ${e} (tried: ${r.join(", ")})`,{scope:"system"})}safeGetEnvValue(e){try{return _r(this.envFile,`parsed.${e}`)}catch{return}}safeGetConfigValue(e){try{return _r(this.configFileContent,e)}catch{return}}getApplicationConfigFolder(){return Q()}};import $n from"node:crypto";function Mn(o){let e=o.trim();e=e.replace(/\\/g,"/"),e=e.replace(/(^\/\/+)|\/{2,}/g,(u,d)=>d?"//":"/"),e.length>1&&e.endsWith("/")&&(e=e.replace(/\/+$/g,"")),e===""&&(e="/"),e=e.replace(/^([a-zA-Z]):(?=\/|$)/,(u,d)=>`${String(d).toUpperCase()}:`);let t=e.startsWith("/")||e.startsWith("//")||/^[A-Z]:\//.test(e),r=e.startsWith("//")?"//":e.startsWith("/")?"/":"",n="",i=e.match(/^([A-Z]:)(\/|$)/);i?(n=i[1],e=e.slice(n.length),e.startsWith("/")&&(e=e.slice(1))):e=e.replace(/^\/+/,"");let a=e.split("/").filter(Boolean),s=[];for(let u of a)if(u!=="."){if(u===".."){s.length>0&&s[s.length-1]!==".."?s.pop():t||s.push("..");continue}s.push(u)}let c=s.join("/");return n?c=`${n}/${c}`.replace(/\/+$/g,""):t&&(c=`${r}${c}`.replace(/\/+$/g,"")),c=c.toLowerCase(),c}function tt(o,e=[3,3,3,3]){let t=Mn(o),n=$n.createHash("sha256").update(t,"utf8").digest("base64url").replace(/[^a-zA-Z0-9]/g,"").toLowerCase(),i=e.reduce((c,u)=>c+u,0),a=n.length>=i?n:n.padEnd(i,n),s=0;return e.map(c=>a.slice(s,s+=c)).join("-")}function rt(o){let e=Mn(o),r=$n.createHash("sha256").update(e,"utf8").digest("hex").slice(0,32),n=r.slice(0,8),i=r.slice(8,12),a=r.slice(12,16),s=r.slice(16,20),c=r.slice(20,32);return`${n}-${i}-${a}-${s}-${c}`}import*as re from"node:crypto";function Er(o){let e=re.createPublicKey(o);if(e.asymmetricKeyType!=="ed25519")throw new Error(`Unexpected key type: ${e.asymmetricKeyType}`);let t=e.export({format:"der",type:"spki"});return re.createHash("sha256").update(t).digest("base64url").slice(0,16).toUpperCase()}function kr(o){let e=Buffer.from(`${o.deviceId}|${o.ts}|${o.nonce}`,"utf8"),t=re.createPrivateKey(o.privateKeyPem);return re.sign(null,e,t).toString("base64")}function Tr(o){let e=re.createHash("sha256").update(o.refreshToken,"utf8").digest("hex"),t=Buffer.from(`${o.deviceId}|${o.ts}|${o.nonce}|${e}`,"utf8"),r=re.createPrivateKey(o.privateKeyPem);return re.sign(null,t,r).toString("base64")}function wr(o){return new Promise(e=>setTimeout(e,o))}import*as Un from"node:path";var jn=process.platform==="win32";function Cr(o){let e=Un.resolve(o);return jn?e.toLowerCase():e}import*as de from"node:path";import*as Kn from"node:os";function Ae(o){return o.replace(/\\/g,"/")}function Bn(o){return/^[A-Za-z]:[\\/]/.test(o)||/^[A-Za-z]:$/.test(o.trim())}function $i(o){return Bn(o)?o.replace(/\//g,"\\"):o}function Rr(o){if(o==null)return;let e=String(o).trim();if(e==="")return;if(e.startsWith("~"))return Ae(e);if(!Bn(e))try{let u=de.resolve(e),d=de.resolve(Kn.homedir()),f=de.relative(d,u);if(!f.startsWith("..")&&!de.isAbsolute(f))return f===""?"~":"~/"+Ae(f)}catch{}let r=$i(e).match(/^([A-Za-z]:)\\(?:[Uu][Ss][Ee][Rr][Ss])\\([^\\]+)(\\.*)?$/);if(r){let u=(r[3]??"").replace(/\\/g,"/");return u?"~"+u:"~"}let n=e.match(/^\/mnt\/[a-z]\/(?:[Uu][Ss][Ee][Rr][Ss])\/[^/]+(\/.*)?$/i);if(n){let u=n[1]??"";return u?"~"+Ae(u):"~"}let i=e.match(/^\/Users\/[^/]+(\/.*)?$/);if(i){let u=i[1]??"";return u?"~"+Ae(u):"~"}let a=e.match(/^\/home\/[^/]+(\/.*)?$/);if(a){let u=a[1]??"";return u?"~"+Ae(u):"~"}let s=e.match(/^\/usr\/home\/[^/]+(\/.*)?$/);if(s){let u=s[1]??"";return u?"~"+Ae(u):"~"}let c=e.match(/^\/export\/home\/[^/]+(\/.*)?$/);if(c){let u=c[1]??"";return u?"~"+Ae(u):"~"}try{return de.basename(de.resolve(e))}catch{return e.split(/[/\\]/).pop()??e}}import Mi from"lodash/isString.js";var qn="<path>",Ui=/(['"`])((?:[A-Za-z]:[\\/]|\\\\|~?\/)[^'"`\n]*)\1/g,ji=/(^|[\s'"`([{=,;])(?:\\\\[^\s'"`<>|?*\n]+|[A-Za-z]:[\\/][^\s'"`<>|?*\n]*|~?\/[^\s'"`<>|?*\n:]*\/[^\s'"`<>|?*\n:]*)/g;function xr(o){return!Mi(o)||o.length===0?o:o.replace(Ui,(e,t)=>`${t}${qn}${t}`).replace(ji,(e,t)=>`${t}${qn}`)}var Ki=/^v?(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$/;function pe(o){if(typeof o!="string")return null;let e=o.trim().match(Ki);return e?{major:Number(e[1]),minor:Number(e[2]),patch:Number(e[3]),prerelease:e[4]??null}:null}function Bi(o,e){if(o===e)return 0;if(o===null)return 1;if(e===null)return-1;let t=o.split("."),r=e.split("."),n=Math.max(t.length,r.length);for(let i=0;i<n;i+=1){let a=t[i],s=r[i];if(a===void 0)return-1;if(s===void 0)return 1;let c=/^\d+$/.test(a),u=/^\d+$/.test(s);if(c&&u){let d=Number(a)-Number(s);if(d!==0)return d<0?-1:1;continue}if(c!==u)return c?-1:1;if(a!==s)return a<s?-1:1}return 0}function It(o,e){let t=pe(o),r=pe(e);if(!t)throw new Error(`not a version: ${o}`);if(!r)throw new Error(`not a version: ${e}`);for(let i of["major","minor","patch"]){let a=t[i]-r[i];if(a!==0)return a<0?-1:1}let n=Bi(t.prerelease,r.prerelease);return n===0?0:n<0?-1:1}function Ar(o,e){let t=pe(o),r=pe(e);return!t||!r?!1:It(o,e)<0}function Dr(o,e){let t=pe(o),r=pe(e);return!t||!r?!1:t.major===r.major}import Ir from"lodash/get.js";import qi from"lodash/isObject.js";import Ot from"lodash/isString.js";function L(o){if(o instanceof Error)return o.message;let e=Ir(o,"message");return Ot(e)?e:String(o)}function se(o){if(!qi(o))return;let e=Ir(o,"code");return Ot(e)?e:void 0}function X(o){return Ot(se(o))&&Ot(Ir(o,"message"))}function $(o,e={}){return{code:se(o)??e.code??"INTERNAL",message:L(o)||e.message||"An unexpected error occurred."}}import Wi from"lodash/isBoolean.js";import Wn from"lodash/isFinite.js";import Ji from"lodash/isInteger.js";import Hn from"lodash/isPlainObject.js";import Jn from"lodash/isString.js";import Or from"lodash/isUndefined.js";import Gi from"lodash/omit.js";import Hi from"lodash/omitBy.js";import Vi from"lodash/toNumber.js";var zi=/^[+-]?(\d+\.?\d*|\.\d+)([eE][+-]?\d+)?$/;function Gn(o){return Hn(o)?o:{}}var Nt=class{_found;constructor(e){this._found=e}exists(){return!Or(this._found)}unknown(){return this._found}string(){return Jn(this._found)?this._found:null}number(){if(Wn(this._found))return this._found;if(!Jn(this._found)||!zi.test(this._found.trim()))return null;let e=Vi(this._found);return Wn(e)?e:null}integer(){let e=this.number();return e!==null&&Ji(e)?e:null}boolean(){return Wi(this._found)?this._found:this._found==="true"?!0:this._found==="false"?!1:null}object(){return Hn(this._found)?this._found:null}list(){return Array.isArray(this._found)?this._found:null}},Pt=class o{_value;constructor(e){this._value=Gn(e)}set(e,t){return Or(t)?this:new o({...this._value,[e]:t})}merge(e){return new o({...this._value,...Hi(Gn(e),Or)})}drop(e){return new o(Gi(this._value,e))}has(e){return e in this._value}property(e){return new Nt(this._value[e])}string(e){return this.property(e).string()}value(){return this._value}toJSON(){return this._value}};function Nr(o){return new Pt(o)}import Yi from"lodash/isUndefined.js";import Qi from"lodash/omitBy.js";function Pr(o){return Qi(o,Yi)}var Ue={version:1,controlBaseUrl:"",bridgeWsUrl:"",directories:[],device:void 0},Lt=class{_notifier;argv;config=Ue;envFile=Lr.config({quiet:!0});processEnv={};productionEnv={};configFileContent={};_privateKeyCache=void 0;_privateKeyToStore=void 0;constructor(e){this._notifier=e,this.loadProductionEnv()}loadProductionEnv(){let e=[De.resolve(De.dirname(process.argv[1]??"."),".env.production"),De.resolve(process.cwd(),".env.production")];for(let t of e)try{if(Fr.existsSync(t)){let r=Lr.config({path:t,quiet:!0});r.parsed&&(this.productionEnv=r.parsed);return}}catch{}}setPrivateKeyCache(e){this._privateKeyCache=e}getPrivateKeyToStore(){return this._privateKeyToStore}withDefaultConfig(){return this.config.version=Ue.version,this.config.controlBaseUrl=Ue.controlBaseUrl,this.config.bridgeWsUrl=Ue.bridgeWsUrl,this.config.directories=Ue.directories,this.config.device=Ue.device,this}withProcessEnv(){return this.processEnv={NODE_ENV:process.env.NODE_ENV,APPDATA:process.env.APPDATA,npm_package_version:process.env.npm_package_version,DLINK_DEVICE_ID:process.env.DLINK_DEVICE_ID,DLINK_DEVICE_NAME:process.env.DLINK_DEVICE_NAME,DLINK_DEVICE_TAG:process.env.DLINK_DEVICE_TAG,DLINK_CONTROL_URL:process.env.DLINK_CONTROL_URL,DLINK_BRIDGE_URL:process.env.DLINK_BRIDGE_URL,DLINK_DIRECTORIES:process.env.DLINK_DIRECTORIES},this}withArgv(e){return this.argv=e,this}withEnviromentFile(e){return this.envFile=Lr.config({path:e,quiet:!0}),this}withConfigFile(e){try{let t=this.getApplicationConfigFolder(),r=De.join(t,e);if(Fr.existsSync(r)){let n=Fr.readFileSync(r,"utf8"),i=JSON.parse(n);$r(i)&&!Ft(i)?this.configFileContent=i:this._notifier.warn(`Config file ${e} does not contain a valid object`,{scope:"system"})}}catch(t){let r=t instanceof Error?t.message:String(t);this._notifier.warn(`Failed to load config file ${e}: ${r}`,{scope:"system"})}return this}build(){return this.config.controlBaseUrl=this.getControlBaseUrl(),this.config.bridgeWsUrl=this.getBridgeWsUrl(),this.config.directories=this.getDirectories(),this.config.device=this.getDevice(),this.config.tokens=this.getTokens(),this.config}getConfigValue(e,t){let r=this.safeGetConfigValue(e);return r===void 0?t?.defaultValue:r}firstSet(...e){return e.find(t=>t!=null&&String(t).trim()!=="")}getControlBaseUrl(){return this.firstSet(this.argv?.controlUrl,this.safeGetEnvValue("DLINK_CONTROL_URL"),this.safeGetConfigValue("controlBaseUrl"),this.processEnv.DLINK_CONTROL_URL,this.productionEnv.DLINK_CONTROL_URL)}getBridgeWsUrl(){return this.firstSet(this.argv?.bridgeWsUrl,this.safeGetEnvValue("DLINK_BRIDGE_URL"),this.safeGetConfigValue("bridgeWsUrl"),this.processEnv.DLINK_BRIDGE_URL,this.productionEnv.DLINK_BRIDGE_URL)}getDirectories(){let e=[];if(e.push(this.createDirectoryFromPath(process.cwd())),this.argv?.rootPath)try{e.push(...this.parseDirectoriesFromString(this.argv.rootPath))}catch{e.push(this.createDirectoryFromPath(this.argv.rootPath))}let t=this.safeGetEnvValue("DLINK_DIRECTORIES")??this.safeGetEnvValue("DLINK_ROOTS");if(t)try{e.push(...this.parseDirectoriesFromString(t))}catch{}let r=this.safeGetConfigValue("directories")??this.safeGetConfigValue("roots");if(r)try{e.push(...this.parseDirectoriesFromValue(r))}catch{}let n=this.processEnv.DLINK_DIRECTORIES??this.processEnv.DLINK_ROOTS;if(n)try{e.push(...this.parseDirectoriesFromString(n))}catch{}return this.deduplicateDirectories(e)}deduplicateDirectories(e){let t=new Set,r=[];for(let n of e){let i=Cr(n.path);t.has(i)||(t.add(i),r.push(n))}return r}getDevice(){let e=this.getDeviceId();if(!e){this._notifier.warn("No device ID found from any source (argv, env, config, process env, or machine ID)",{scope:"system"});return}let t=this.getDeviceName();t||(t=Vn.hostname()||"Unknown Device");let r=this.getDeviceTag(),n=new Dt({notifier:this._notifier,argv:this.argv,envFile:this.envFile,processEnv:this.processEnv,configFileContent:this.configFileContent,getPrivateKey:()=>this._privateKeyCache}),{publicKey:i}=n.ensureDeviceKeys();this._privateKeyToStore=n.getLastGeneratedPrivateKey();let a={id:e,name:t,tag:r||"",keys:{public:i}};return this._notifier.info(`Created device: id=${a.id?.substring(0,8)}..., name=${a.name}, tag=${a.tag||"(empty)"}`,{scope:"system"}),a}getDeviceId(){this._notifier.info("Checking env file for DLINK_DEVICE_ID...",{scope:"system"});let e=this.safeGetEnvValue("DLINK_DEVICE_ID");if(e&&!ve(e))return this._notifier.info(`Found device ID in env file: ${e.substring(0,8)}...`,{scope:"system"}),e;this._notifier.info("Checking config file for deviceId...",{scope:"system"});let t=this.safeGetConfigValue("deviceId")??be(this.safeGetConfigValue("device"),"id");if(t&&!ve(String(t)))return this._notifier.info(`Found device ID in config file: ${String(t).substring(0,8)}...`,{scope:"system"}),String(t);this._notifier.info("Checking process env for DLINK_DEVICE_ID...",{scope:"system"});let r=this.processEnv.DLINK_DEVICE_ID;if(r&&!ve(r))return this._notifier.info(`Found device ID in process env: ${r.substring(0,8)}...`,{scope:"system"}),r;this._notifier.info("Trying machine ID as fallback...",{scope:"system"});let n=At(!0);if(n)return this._notifier.info(`Using machine ID: ${n.substring(0,8)}...`,{scope:"system"}),n;this._notifier.error("Failed to get machine ID in getDeviceId()",{scope:"system"}),this._notifier.warn("No device ID found in any source",{scope:"system"})}getDeviceName(){let e=this.safeGetEnvValue("DLINK_DEVICE_NAME");if(e&&!ve(e))return e;let t=be(this.safeGetConfigValue("device"),"name");if(t&&!ve(String(t)))return String(t);let r=this.processEnv.DLINK_DEVICE_NAME;if(r&&!ve(r))return r;let n=Vn.hostname();if(n&&!ve(n))return n}getDeviceTag(){let e=this.safeGetEnvValue("DLINK_DEVICE_TAG");if(e)return e;let t=be(this.safeGetConfigValue("device"),"tag");if(t)return String(t);let r=this.processEnv.DLINK_DEVICE_TAG;if(r)return r}getTokens(){let e=this.safeGetConfigValue("tokens");if(!e||!$r(e)||Ft(e))return;let t=e,r={},n=be(t,"accessTokenExpSec");if(typeof n=="number"&&(r.accessTokenExpSec=n),r.accessTokenExpSec!==void 0)return r}safeGetEnvValue(e){try{return be(this.envFile,`parsed.${e}`)}catch{return}}safeGetConfigValue(e){try{return be(this.configFileContent,e)}catch{return}}parseDirectoriesFromString(e){try{let t=JSON.parse(e);return this.parseDirectoriesFromValue(t)}catch{return[this.createDirectoryFromPath(e)]}}parseDirectoriesFromValue(e){if(Ft(e))return e.map(r=>this.parseDirectoryFromValue(r)).filter(r=>r!==null);if(zn(e))return[this.createDirectoryFromPath(e)];let t=this.parseDirectoryFromValue(e);return t?[t]:[]}parseDirectoryFromValue(e){if(zn(e))return this.createDirectoryFromPath(e);if($r(e)&&!Ft(e)){let t=e,r=rt(be(t,"path")),n=be(t,"path"),i=Xi(be(t,"name"),n?De.basename(n):"");if(!ve(r)&&!ve(n)){let a=tt(n);return{id:String(r),name:String(i),path:String(n),tag:a}}}return null}createDirectoryFromPath(e){let t=De.resolve(e),r=rt(t),n=De.basename(t),i=tt(t);return{id:r,name:n,path:t,tag:i}}getApplicationConfigFolder(){return Q()}};var Xe=class{dedupeKey;parentId;overridePriority;overrideLane;constructor(e,t,r,n){this.dedupeKey=e,this.parentId=t,this.overridePriority=r,this.overrideLane=n}},Mr=class{phase;flags;constructor(e){this.phase="idle",this.flags=e??new _e(!1)}},$t=class{argv;configurationContext;bus;notifier;logger;storage;secrets;runtime;lanes;laneActive;registry;orchestrators;orchestratorSubscriptions=new Map;orchestratorAbort;jobs;queue;locksHeldBy;completions;dedupeIndex;engineAbort;jobAbort;stopping;running;wakeFn;wakePromise;constructor(e){this.argv=e.argv,this.bus=e.bus,this.logger=e.logger,this.notifier=e.notifier,this.storage=e.storage,this.secrets=e.secrets,this.runtime=new Mr(e.flags),this.lanes=new Map;for(let n of e.lanes)this.lanes.set(n.name,n);this.laneActive=new Map;for(let n of e.lanes)this.laneActive.set(n.name,0);this.registry=new Map,this.orchestrators=[],this.orchestratorAbort=new AbortController,this.jobs=new Map,this.queue=[],this.locksHeldBy=new Map,this.completions=new Map,this.dedupeIndex=new Map,this.engineAbort=new AbortController,this.jobAbort=new Map,this.stopping=!1,this.running=!1,this.wakeFn=null,this.wakePromise=this.resetWake();let t=new Rt,r=new Lt(this.notifier);this.configurationContext=new xt(t,r)}applyConfiguration(e){this.configurationContext.apply(e)}setPhase(e){this.runtime.phase=e}getBus(){return this.bus}hasRunningJobs(){for(let[,e]of this.laneActive)if(e>0)return!0;return!1}register(e){let t=e.options.name;if(this.registry.has(t))throw new Error(`JobType already registered: ${t}`);this.registry.set(t,e)}registerOrchestrator(e){if(this.orchestrators.some(n=>n.name===e.name))throw new Error(`Orchestrator already registered: ${e.name}`);let r=new Me({engine:this,bus:this.bus,logger:this.logger,notifier:this.notifier,flags:this.runtime.flags,signal:this.orchestratorAbort.signal});e.onRegister?.(r),this.setupOrchestratorSubscriptions(e,r),this.orchestrators.push(e)}setupOrchestratorSubscriptions(e,t){let r=e.handlers?.();if(r){let n=[];Object.keys(r).forEach(i=>{let a=r[i];a&&n.push(this.bus.on(i,s=>{Promise.resolve(a(s,t)).catch(c=>{let u=c instanceof Error?c.message:String(c);this.logger.error(`handler error (${e.name}/${String(i)}): ${u}`),this.notifier.error(`handler error (${e.name}/${String(i)}): ${u}`,{scope:"system"})})}))}),this.orchestratorSubscriptions.set(e.name,n)}}async init(){let e=await this.storage.load();for(let[t,r]of Object.entries(e.jobs))this.jobs.set(t,new et(r));for(let t of this.jobs.values())t.state==="running"&&(t.state="queued",t.nextRunAt=Date.now(),t.updatedAt=Date.now()),t.dedupeKey&&this.dedupeIndex.set(t.dedupeKey,t.id),t.state==="queued"&&this.queue.push(t.id)}async start(){if(!this.running){this.running=!0,this.runtime.phase="running",process.on("uncaughtException",e=>{this.notifier.error(`uncaughtException: ${e.stack?e.stack:String(e)}`,{scope:"system"}),this.bus.emit("engine:crashed",{reason:"uncaughtException",detail:e instanceof Error?`${e.name}: ${e.message}`:String(e)}),this.requestStop("uncaughtException")}),process.on("unhandledRejection",e=>{this.notifier.error(`unhandledRejection: ${String(e)}`,{scope:"system"}),this.bus.emit("engine:crashed",{reason:"unhandledRejection",detail:String(e)})}),this.bus.emit("engine:started",Date.now());try{await this.startOrchestratorsSafe(),await this.loopGuarded()}finally{await this.stopOrchestratorsSafe(),await this.persist(),this.bus.emit("engine:stopped",Date.now())}}}requestStop(e){if(!this.stopping){this.stopping=!0,this.runtime.phase="stopping",this.bus.emit("engine:stopping",e),this.engineAbort.abort(new Error(`Stopping: ${e}`)),this.orchestratorAbort.abort(new Error(`Stopping: ${e}`));for(let[t,r]of this.jobAbort.entries())this.jobs.get(t)?.cancellable&&r.abort(new Error("Cancelled by user"));this.wake()}}async dispatch(e,t){let r=t||new Xe(null,null,null,null),n=e.type.options.name;if(!this.registry.has(n)){let f=new Error(`JobType not registered: ${n}`);return Ee.failure(f)}if(r.dedupeKey){let f=this.dedupeIndex.get(r.dedupeKey);if(f){let p=this.jobs.get(f);if(p&&(p.state==="queued"||p.state==="running")){let h=this.completions.get(f);if(h)return await h.promise}}}let i=this.newId(),a=new Et;this.completions.set(i,a);let s=r.overrideLane?r.overrideLane:e.type.options.lane,c=r.overridePriority!==null?r.overridePriority:e.type.options.priority,u=new et({id:i,jobType:n,inputJson:e.input===void 0?"null":JSON.stringify(e.input),state:"queued",lane:s,priority:c,locks:e.type.options.locks,attempts:0,maxAttempts:e.type.options.maxAttempts,nextRunAt:Date.now(),cancellable:e.type.options.cancellable,parentId:r.parentId,dedupeKey:r.dedupeKey,lastError:null,createdAt:Date.now(),updatedAt:Date.now()});return this.jobs.set(i,u),this.queue.push(i),r.dedupeKey&&this.dedupeIndex.set(r.dedupeKey,i),this.emitJobEvent(u,"queued",null,null),await this.persist(),this.wake(),await a.promise}async loopGuarded(){for(;!this.stopping;)try{await this.loopOnce()}catch(e){this.notifier.error(`Engine loop error: ${String(e)}`,{scope:"system"}),await new Promise(t=>setTimeout(t,250))}}async loopOnce(){let e=0;for(;;){let t=this.pickNextRunnable();if(!t)break;e+=1,this.runJobSafe(t)}e===0&&(await this.wakePromise,this.wakePromise=this.resetWake())}async runJobSafe(e){try{await this.runJobInternal(e)}catch(t){this.notifier.error(`runJobInternal crashed: ${String(t)}`,{scope:"system"});let r=this.completions.get(e);r&&r.resolve(Ee.failure(new Error(String(t))))}finally{this.wake()}}pickNextRunnable(){let e=Date.now(),t=null,r=Number.NEGATIVE_INFINITY,n=Number.POSITIVE_INFINITY;for(let a of this.queue){let s=this.jobs.get(a);s&&s.state==="queued"&&(this.stopping&&s.lane!=="console"||s.nextRunAt>e||this.canRunInLane(s.lane)&&this.canAcquireLocks(s)&&(s.priority>r||s.priority===r&&s.nextRunAt<n)&&(r=s.priority,n=s.nextRunAt,t=a))}if(!t)return null;let i=this.queue.indexOf(t);return i>=0&&this.queue.splice(i,1),t}canRunInLane(e){let t=this.lanes.get(e);if(!t)return!1;let r=this.laneActive.get(e);return r===void 0?!1:r<t.concurrency}canAcquireLocks(e){for(let t of e.locks){let r=this.locksHeldBy.get(t);if(r&&r!==e.id&&!(e.parentId&&r===e.parentId))return!1}return!0}acquireLocks(e){for(let t of e.locks)this.locksHeldBy.get(t)||this.locksHeldBy.set(t,e.id)}releaseLocks(e){for(let t of e.locks)this.locksHeldBy.get(t)===e.id&&this.locksHeldBy.delete(t)}async runJobInternal(e){let t=this.jobs.get(e);if(!t)return;let r=this.registry.get(t.jobType);if(!r){this.completeFailure(e,new Error(`No registered JobType: ${t.jobType}`));return}let n=this.laneActive.get(t.lane);if(n===void 0){this.completeFailure(e,new Error(`Unknown lane: ${t.lane}`));return}let i=new AbortController;if(this.jobAbort.set(e,i),t.cancellable){let s=this.engineAbort.signal;s.aborted?i.abort(s.reason instanceof Error?s.reason:new Error("Stopped")):s.addEventListener("abort",()=>i.abort(s.reason instanceof Error?s.reason:new Error("Stopped")),{once:!0})}this.laneActive.set(t.lane,n+1),this.acquireLocks(t),t.state="running",t.updatedAt=Date.now(),await this.persist(),this.emitJobEvent(t,"running",null,null);let a=new kt({engine:this,bus:this.bus,logger:this.logger,notifier:this.notifier,flags:this.runtime.flags,signal:i.signal,dispatch:async(s,c)=>this.dispatch(s,c)});try{let s=JSON.parse(t.inputJson),c=s===null?void 0:s,u=await r.run(c,a);t.state="succeeded",t.updatedAt=Date.now(),t.lastError=null,await this.persist(),this.emitJobEvent(t,"succeeded",null,null),this.completeSuccess(e,u)}catch(s){let c=s instanceof Error?s:new Error("Unknown error");if(t.attempts+=1,t.updatedAt=Date.now(),t.lastError=c.message,i.signal.aborted)t.state="failed",await this.persist(),this.emitJobEvent(t,"failed",c.message,null),this.notifier.error(`Job ${t.jobType} (${e.substring(0,8)}) cancelled: ${c.message}`),this.completeCancelled(e,c);else if(t.attempts<t.maxAttempts&&!this.stopping){let f=es(t.attempts);t.state="queued",t.nextRunAt=Date.now()+f,await this.persist(),this.queue.push(t.id),this.emitJobEvent(t,"queued",c.message,t.nextRunAt),this.notifier.warn(`Job ${t.jobType} (${e.substring(0,8)}) failed, retrying in ${f}ms (attempt ${t.attempts}/${t.maxAttempts}): ${c.message}`)}else t.state="failed",await this.persist(),this.emitJobEvent(t,"failed",c.message,null),this.notifier.error(`Job ${t.jobType} (${e.substring(0,8)}) failed after ${t.attempts} attempt(s): ${c.message}`),this.completeFailure(e,c)}finally{this.releaseLocks(t);let s=this.laneActive.get(t.lane);s!==void 0&&this.laneActive.set(t.lane,Math.max(0,s-1)),this.jobAbort.delete(e),this.wake()}}async startOrchestratorsSafe(){let e=new Me({engine:this,bus:this.bus,logger:this.logger,notifier:this.notifier,flags:this.runtime.flags,signal:this.orchestratorAbort.signal});for(let t of this.orchestrators)Promise.resolve().then(()=>t.onStart?.(e)).then(()=>t.onStarted?.(e)).catch(r=>this.notifier.error(`Orchestrator start failed (${t.name}): ${String(r)}`,{scope:"system"}))}async stopOrchestratorsSafe(){let e=new Me({engine:this,bus:this.bus,logger:this.logger,notifier:this.notifier,flags:this.runtime.flags,signal:this.orchestratorAbort.signal});for(let t=this.orchestrators.length-1;t>=0;t--){let r=this.orchestrators[t];try{await r.onStop?.(e),r.onStopped?.(e)}catch(i){this.notifier.error(`Orchestrator stop failed (${r.name}): ${String(i)}`,{scope:"system"})}let n=this.orchestratorSubscriptions.get(r.name);if(n){for(let i of n)i.unsubscribe();this.orchestratorSubscriptions.delete(r.name)}}}emitJobEvent(e,t,r,n){this.bus.emit("job:event",new wt({ts:Date.now(),jobId:e.id,jobType:e.jobType,state:t,attempt:e.attempts,maxAttempts:e.maxAttempts,lane:e.lane,priority:e.priority,error:r,nextRunAt:n}))}completeSuccess(e,t){let r=this.completions.get(e);r&&r.resolve(Ee.success(t))}completeFailure(e,t){let r=this.completions.get(e);r&&r.resolve(Ee.failure(t))}completeCancelled(e,t){let r=this.completions.get(e);r&&r.resolve(Ee.cancelled(t))}async persist(){let e={};for(let[t,r]of this.jobs.entries())e[t]=r;await this.storage.save(new xe(e))}wake(){this.wakeFn&&this.wakeFn()}resetWake(){return new Promise(e=>{this.wakeFn=e})}newId(){return Zi.randomBytes(8).toString("hex")}};function es(o){let r=Math.min(3e4,300*2**(o-1)),n=Math.floor(Math.random()*200);return r+n}var Mt=class{handlers;constructor(){this.handlers=new Map}on(e,t){let r=this.handlers.get(e),n=r||[];return n.push(t),this.handlers.set(e,n),{unsubscribe:()=>{let i=this.handlers.get(e);if(!i)return;let a=i.filter(s=>s!==t);this.handlers.set(e,a)}}}emit(e,t){let r=this.handlers.get(e);if(r)for(let n of r)n(t)}};import*as Te from"node:crypto";import*as we from"node:fs/promises";import*as jt from"node:path";var ts="DeviceLink-SecretStorage-v1",nt=12,Ut=16,rs=32,ns="secrets.enc.json";function os(){return jt.join(Q(),ns)}function is(o){return Te.pbkdf2Sync(o,ts,1e5,rs,"sha256")}var jr=class{secretsPath;key;cache={};loaded=!1;constructor(e,t){this.secretsPath=e,this.key=t}async load(){if(!this.loaded){try{let e=await we.readFile(this.secretsPath,"utf8"),t=this.decrypt(e);this.cache=JSON.parse(t)}catch{this.cache={}}this.loaded=!0}}async save(){await we.mkdir(jt.dirname(this.secretsPath),{recursive:!0});let e=JSON.stringify(this.cache),t=this.encrypt(e),r=this.secretsPath+".tmp";await we.writeFile(r,t,"utf8"),await we.rename(r,this.secretsPath)}encrypt(e){let t=Te.randomBytes(nt),r=Te.createCipheriv("aes-256-gcm",this.key,t,{authTagLength:Ut}),n=Buffer.concat([r.update(e,"utf8"),r.final()]),i=r.getAuthTag();return Buffer.concat([t,i,n]).toString("base64url")}decrypt(e){let t=Buffer.from(e,"base64url"),r=t.subarray(0,nt),n=t.subarray(nt,nt+Ut),i=t.subarray(nt+Ut),a=Te.createDecipheriv("aes-256-gcm",this.key,r,{authTagLength:Ut});return a.setAuthTag(n),a.update(i)+a.final("utf8")}async get(e){await this.load();let t=this.cache[e];return t===void 0?{ok:!1}:{ok:!0,value:t}}async set(e,t){await this.load(),this.cache[e]=t,await this.save()}async delete(e){await this.load(),this.cache[e]!==void 0&&(delete this.cache[e],await this.save())}};async function Yn(){let o=At(!0);if(!o)throw new Error("Secret storage requires machine ID");let e=is(o),t=os();return new jr(t,e)}var Kt=class{},Kr=class{ok;config;constructor(e,t){this.ok=e,this.config=t}},Br=class extends W{constructor(){super(new q({name:"load-config",lane:"main",priority:1e3,locks:["GLOBAL"],maxAttempts:3,cancellable:!0}))}async run(e,t){t.notifier.info("Loading configuration...",{scope:"system"});let r=t.engine.configurationContext.loader,n=await t.engine.secrets.get("devicePrivateKey");r.setPrivateKeyCache(n.ok&&n.value?n.value:void 0),r.withDefaultConfig(),r.withArgv(t.engine.argv),t.engine.argv.enviromentFile&&r.withEnviromentFile(t.engine.argv.enviromentFile),r.withConfigFile("config.json"),r.withProcessEnv();let i=r.build(),a=r.getPrivateKeyToStore();return a&&await t.engine.secrets.set("devicePrivateKey",a),this.updateUi(i,t),t.notifier.success("Configuration loaded"),t.engine.applyConfiguration(i),t.bus.emit("config:loaded",i),new Kr(!0,i)}updateUi(e,t){let r={scope:"system"};t.notifier.info(`Control base URL: ${e.controlBaseUrl}`,r),t.notifier.info(`Bridge WebSocket URL: ${e.bridgeWsUrl}`,r),t.notifier.info(`Directories: ${e.directories.map(n=>n.path).join(", ")}`,r),t.notifier.info(`Device: ${e.device?.id}`,r),t.notifier.info(`Device name: ${e.device?.name}`,r),t.notifier.info(`Device tag: ${e.device?.tag}`,r),t.notifier.info(`Device keys public: ${e.device?.keys?.public?"***":"(none)"}`,r)}},qr=new Br;function Qn(o){return new ie(qr,o)}var Wr=class{ok;constructor(e){this.ok=e}},Jr=class extends W{constructor(){super(new q({name:"console-command",lane:"console",priority:1e3,locks:[],maxAttempts:1,cancellable:!1}))}async run(e,t){return t.notifier.info(`Executing console command: ${e.kind}`),await new Promise(r=>setTimeout(r,2e3)),t.notifier.info(`Console command executed: ${e.kind}`),t.notifier.success(`Console command executed: ${e.kind}`),new Wr(!0)}},Xn=new Jr;var Gr=class{ok;constructor(e){this.ok=e}},Hr=class extends W{constructor(){super(new q({name:"contact-server",lane:"main",priority:900,locks:["GLOBAL"],maxAttempts:8,cancellable:!0}))}async run(e,t){return t.notifier.info(`contact server mode=${e.mode}`,{scope:"system"}),await new Promise(r=>setTimeout(r,200)),new Gr(!0)}},Zn=new Hr;var Vr=class{ok;constructor(e){this.ok=e}},zr=class extends W{constructor(){super(new q({name:"send-server-result",lane:"io",priority:200,locks:[],maxAttempts:10,cancellable:!0}))}async run(e,t){return t.notifier.info(`send result requestId=${e.requestId}`,{scope:"system"}),await new Promise(r=>setTimeout(r,10)),new Vr(!0)}},eo=new zr;import as from"node:crypto";import z from"lodash/get.js";import ot from"lodash/isPlainObject.js";import Ke from"lodash/isString.js";var V=class{constructor(e,t){this.context=e;this.handler=t}execute(e){return this.handler(this.context,e)}};async function fe(o,e,t){let r=`${o.configuration.controlBaseUrl}${e}`,n=await fetch(r,t);if(!n.ok){let i=await n.text().catch(s=>{let c=s instanceof Error?s.message:String(s);return o.notifier.error(`Failed to read HTTP error response body: ${c}`),""}),a=new Error(`HTTP ${n.status}: ${i}`);a.status=n.status;try{a.body=i?JSON.parse(i):void 0}catch{a.body=void 0}if(n.status===429){let s=n.headers.get("Retry-After");if(s!==null){let c=parseInt(s,10);Number.isFinite(c)&&c>=0&&(a.retryAfterSec=c)}}throw a}return n.json()}function to(o){return new V(o,async(e,t)=>fe(e,"/api/device/authorize/start",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(t)}))}function ro(o){return new V(o,async(e,t)=>fe(e,"/api/device/authorize/prove",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(t)}))}import{z as ae}from"zod";var ss=ae.union([ae.object({status:ae.literal("pending"),needs_proof:ae.boolean()}),ae.object({status:ae.literal("approved"),needs_proof:ae.boolean()}),ae.object({error:ae.literal("access_denied")}),ae.object({error:ae.literal("expired_token")})]);function no(o){return new V(o,async(e,t)=>{let r=new URL("/api/device/authorize/stream",e.configuration.controlBaseUrl);r.searchParams.set("device_code",t.deviceCode);let n=e.notifier,i=new AbortController,a=setTimeout(()=>{n.error("[deviceAuthStream] Timeout reached, aborting"),i.abort()},Math.max(1e3,t.timeoutMs));if(t.signal){let s=t.signal;s.addEventListener("abort",()=>{let c="reason"in s&&s.reason!=null?String(s.reason):"";n.warn(`[deviceAuthStream] External signal aborted: ${c}`),i.abort()})}try{let s=await fetch(r.toString(),{method:"GET",headers:{Accept:"text/event-stream"},signal:i.signal});if(!s.ok||!s.body){let f=await s.text().catch(p=>{let h=p instanceof Error?p.message:String(p);return n.error(`[deviceAuthStream] Failed to read response body (${s.status}): ${h}`),""});throw n.error(`[deviceAuthStream] Response error: ${s.status} - ${f}`),new Error(`device/authorize/stream failed (${s.status}): ${f}`)}let c=s.body.getReader(),u=new TextDecoder("utf-8"),d="";for(;;){let{value:f,done:p}=await c.read();if(p){n.error("[deviceAuthStream] Stream ended unexpectedly");break}for(d+=u.decode(f,{stream:!0});;){let h=d.indexOf(`
3
3
 
4
- `);if(g===-1)break;let m=d.slice(0,g);d=d.slice(g+2);let h=null,y=null;for(let _ of m.split(`
5
- `))_.startsWith("event:")&&(h=_.slice(6).trim()),_.startsWith("data:")&&(y=_.slice(5).trim());if(!h||!y)continue;let k;try{k=JSON.parse(y)}catch(_){n.error(`[deviceAuthStream] Failed to parse event data as JSON: ${String(_)}`);continue}let b=jo.safeParse(k);if(!b.success){n.error(`[deviceAuthStream] Event validation failed (${h}): ${String(b.error)}`);continue}let T=b.data;if(h==="approved"||h==="denied"||h==="expired")return T;if(h==="pending"){"status"in T&&T.status==="pending"&&T.needs_proof===!0&&n.warn("[deviceAuthStream] pending: needs_proof=true (unexpected before approval)");continue}n.warn(`[deviceAuthStream] Unknown event type: ${h}`)}}throw n.error("[deviceAuthStream] Stream ended unexpectedly without completion"),new Error("Device authorization stream ended without completion")}catch(s){if(s instanceof Error&&s.name==="AbortError")n.warn(`[deviceAuthStream] Aborted: ${s.message??""}`);else{let c=s instanceof Error?s.message:String(s);n.error(`[deviceAuthStream] Error: ${c}`)}throw s}finally{clearTimeout(a)}})}function gn(o){return new H(o,async(e,t)=>ie(e,"/api/device/authorize/token",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(t)}))}function yn(o){return new H(o,async(e,t)=>{let r=new AbortController,n=3e4,i=setTimeout(()=>{r.abort()},n);try{let a=await ie(e,"/api/device/authorize/refresh",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(t),signal:r.signal});return clearTimeout(i),a}catch(a){throw clearTimeout(i),a instanceof Error&&a.name==="AbortError"?new Error(`Refresh token request timed out after ${n}ms`):a}})}function bn(o){return new H(o,async(e,t)=>ie(e,"/api/device/enrollment/start",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(t)}))}var Oe=class{deviceAuthStartCall;deviceAuthProveCall;deviceAuthStreamCall;deviceAuthTokenCall;deviceAuthRefreshCall;deviceEnrollmentStartCall;constructor(e,t){let r={configuration:e,notifier:t};this.deviceAuthStartCall=fn(r),this.deviceAuthProveCall=hn(r),this.deviceAuthStreamCall=mn(r),this.deviceAuthTokenCall=gn(r),this.deviceAuthRefreshCall=yn(r),this.deviceEnrollmentStartCall=bn(r)}deviceAuthStart(e){return this.deviceAuthStartCall.execute(e)}deviceEnrollmentStart(e){return this.deviceEnrollmentStartCall.execute(e)}deviceAuthProve(e){return this.deviceAuthProveCall.execute(e)}deviceAuthStream(e){return this.deviceAuthStreamCall.execute(e)}deviceAuthToken(e){return this.deviceAuthTokenCall.execute(e)}deviceAuthRefresh(e){return this.deviceAuthRefreshCall.execute(e)}};function Uo(o){if(typeof o!="object"||o===null)return!1;let e=o;if(e.ok!==!0)return!1;let t=e.data;if(typeof t!="object"||t===null)return!1;let r=t;return r.ok===!0&&typeof r.access_token=="string"&&typeof r.refresh_token=="string"&&typeof r.expires_in_sec=="number"&&typeof r.device_id=="string"}function Bo(o){return o.ok===!0&&typeof o.access_token=="string"}var Tt=class{constructor(e){this.reason=e}},_r=["REFRESH_EXPIRED","REFRESH_REVOKED","REFRESH_NOT_FOUND","DEVICE_NOT_FOUND"],se=class{constructor(e,t,r,n,i,a,s){this.ok=e;this.accessToken=t;this.refreshToken=r;this.expiresInSec=n;this.error=i;this.retryAfterSec=a;this.errorCode=s}},kr=class extends B{constructor(){super(new U({name:"refresh-tokens",lane:"io",priority:900,locks:["AUTH_REFRESH"],maxAttempts:5,cancellable:!0}))}async run(e,t){let r=t.engine.configurationContext.configuration;if(!r.controlBaseUrl)return new se(!1,void 0,void 0,void 0,"controlBaseUrl missing");if(!r.device?.id)return new se(!1,void 0,void 0,void 0,"device.id missing");let n=await t.engine.secrets.get("devicePrivateKey");if(!n.ok||!n.value)return new se(!1,void 0,void 0,void 0,"device private key missing");let i=n.value,a=await t.engine.secrets.get("refreshToken");if(!a.ok||!a.value)return new se(!1,void 0,void 0,void 0,"refresh token missing");let s=a.value;t.notifier.info(`Refreshing tokens (job) reason=${e.reason}`,{scope:"system",category:["token-refresh"]});let c=Date.now(),l=Ko.randomBytes(16).toString("base64url"),d=nr({deviceId:r.device.id,ts:c,nonce:l,refreshToken:s,privateKeyPem:i}),p=new Oe(r,t.notifier),f;try{f=await p.deviceAuthRefresh({device_id:r.device.id,refresh_token:s,ts:c,nonce:l,sig:d})}catch(m){let h=m instanceof Error?m.message:String(m),y=m,k=y.retryAfterSec,b=y.status,T=y.body,_=T?.error,I=b===401&&_!=null&&typeof _=="object"&&_.code==="REFRESH_NOT_FOUND"?"REFRESH_NOT_FOUND":void 0;return I==="REFRESH_NOT_FOUND"?(t.notifier.warn("Refresh token not found; clearing stored refresh token and stopping automatic refresh. Please run signin.",{category:["token-refresh"]}),await t.engine.secrets.delete("refreshToken").catch(E=>{let w=E instanceof Error?E.message:String(E);t.logger.warn(`Could not clear stored refresh token: ${w}`,{category:["token-refresh"]}),t.notifier.warn(`Could not clear stored refresh token: ${w}`,{category:["token-refresh"]})}),new se(!1,void 0,void 0,void 0,"Refresh token not found",void 0,"REFRESH_NOT_FOUND")):(t.notifier.error(`Refresh tokens failed: ${h}`,{category:["token-refresh"]}),new se(!1,void 0,void 0,void 0,h,k,I))}let g=this.normalizeRefreshResponse(f);if(!g){let m=this.extractErrorMessage(f),h=this.extractErrorCode(f);return t.notifier.error(`Refresh tokens failed: ${m}`,{category:["token-refresh"]}),new se(!1,void 0,void 0,void 0,m,void 0,h)}return g.refresh_token&&await t.engine.secrets.set("refreshToken",g.refresh_token),new se(!0,g.access_token,g.refresh_token,g.expires_in_sec)}normalizeRefreshResponse(e){if(Uo(e))return e.data;if(this.looksLikeDomainRefresh(e)){let t=e;return Bo(t)?t:null}return null}looksLikeDomainRefresh(e){if(typeof e!="object"||e===null)return!1;let t=e;return t.ok===!0||t.ok===!1}extractErrorMessage(e){if(typeof e!="object"||e===null)return"Unknown error";let t=e;if(t.ok===!1){let r=t.error;if(typeof r=="object"&&r!==null){let i=r.message;if(typeof i=="string"&&i.length>0)return i}}return t.ok===!0&&"data"in t?"Bad refresh response: missing token fields":"Unknown error"}extractErrorCode(e){if(typeof e!="object"||e===null)return;let t=e;if(t.ok!==!1)return;let r=t.error;if(typeof r!="object"||r===null)return;let n=r.code;return typeof n=="string"?n:void 0}},Tr=new kr;function vn(o){return new ee(Tr,o)}var wt=class{name;filePath;buffer;timer;subs;constructor(e){this.name="telemetry-orchestrator",this.filePath=e,this.buffer=[],this.timer=null,this.subs=[]}async onStart(e){this.subs.push(e.on("log",t=>this.onLog(t))),this.subs.push(e.on("job:event",t=>this.onJob(t))),this.subs.push(e.on("notify:event",t=>this.onNotify(t)))}async onStarted(e){return Promise.resolve()}async onStop(e){for(let t of this.subs)t.unsubscribe();this.subs=[],await this.flush()}async onStopped(e){return Promise.resolve()}onLog(e){this.push({kind:"log",...e})}onJob(e){this.push({kind:"job",...e})}onNotify(e){this.push({...e,kind:"ui"})}push(e){this.buffer.push(JSON.stringify(e)),this.timer||(this.timer=setTimeout(()=>void this.flush(),1e3)),this.buffer.length>200&&this.flush()}async flush(){this.timer&&(clearTimeout(this.timer),this.timer=null),this.buffer.length}};function Sn(o,e){return e.headless?o.on("notify:event",t=>{qo(t,e.debug)}):null}function qo(o,e){if(!e&&!Wo(o))return;let t=`[agent] ${o.message}`;switch(o.kind){case"error":console.error(t);break;case"warning":console.warn(t);break;default:console.log(t);break}}function Wo(o){return o.kind==="error"||o.kind==="warning"||o.kind==="success"||o.scope==="system"}var Rt=class{name="os-signals-orchestrator";sigint;sigterm;async onStart(e){let t=r=>{e.notifier.warn(`received ${r}`,{scope:"system"}),e.engine.requestStop(r)};this.sigint=()=>t("SIGINT"),this.sigterm=()=>t("SIGTERM"),process.on("SIGINT",this.sigint),process.on("SIGTERM",this.sigterm)}async onStarted(e){return Promise.resolve()}async onStop(e){this.sigint&&process.off("SIGINT",this.sigint),this.sigterm&&process.off("SIGTERM",this.sigterm),this.sigint=void 0,this.sigterm=void 0,e.notifier.info("OS signal handlers removed",{scope:"system"})}async onStopped(e){return Promise.resolve()}};import xi from"react";import{render as Ai}from"ink";import{useEffect as bi,useCallback as Rr}from"react";import{Box as Cr,useApp as vi,useInput as Si}from"ink";import{useEffect as Jo,useState as Ho}from"react";function En(){let[o,e]=Ho(()=>{let t=process.stdout.columns||80,r=process.stdout.rows||24;return[t,r]});return Jo(()=>{let t=()=>{let r=process.stdout.columns||80,n=process.stdout.rows||24;e([r,n])};return process.stdout.on("resize",t),t(),()=>{process.stdout.off("resize",t)}},[]),o}import Rn,{useEffect as Ct,useState as Cn}from"react";import{Box as Pe,Text as N}from"ink";import{createContext as Go,useContext as Vo,useReducer as zo,useMemo as Ne,useEffect as kn,useState as Yo}from"react";import{jsx as ei}from"react/jsx-runtime";var _n=Go(null);function Qo(o,e){let t=o.messages,r=t.findIndex(n=>n.id===e.id);if(r!==-1){let n=t[r],i=[...t];return i[r]={...e,id:n.id,ts:n.ts},{...o,messages:i}}if(e.groupKey)for(let n=t.length-1;n>=0;n--){let i=t[n];if(i.groupKey===e.groupKey){let a=[...t];return a[n]={...e,id:i.id,ts:i.ts,groupKey:i.groupKey},{...o,messages:a}}}return{...o,messages:[...t.slice(-380),e]}}function Xo(o,e){switch(e.type){case"SET_INPUT":return{...o,input:e.payload};case"ADD_MESSAGE":return Qo(o,e.payload);case"SET_MESSAGES":return{...o,messages:e.payload};case"SET_ENGINE_STATE":return{...o,engineState:e.payload};case"ADD_COMMAND_TO_HISTORY":{if(!e.payload)return o;let t=o.commandHistory.length===0||o.commandHistory[o.commandHistory.length-1]!==e.payload?[...o.commandHistory,e.payload]:o.commandHistory;return{...o,commandHistory:t}}case"SET_HISTORY_INDEX":return{...o,historyIndex:e.payload};case"SET_IS_PROCESSING":return{...o,isProcessing:e.payload};case"SET_DEBUG_ENABLED":return{...o,debugEnabled:e.payload};case"SET_CONNECTION_STATUS":return{...o,connectionStatus:e.payload};case"SET_CONNECTION_RETRY":return{...o,connectionRetryInSec:e.payload};case"SET_REFRESH_COUNTDOWN":return{...o,refreshCountdownSec:e.payload};case"SET_DEVICE_ID":return{...o,deviceId:e.payload};case"RESET_INPUT":return{...o,input:""};case"RESET_HISTORY_INDEX":return{...o,historyIndex:-1};case"RESET_HISTORY_INDEX_IF_TYPING":if(o.historyIndex!==-1){let t=o.commandHistory[o.historyIndex];if(t!==void 0&&o.input!==t)return{...o,historyIndex:-1}}return o;default:return o}}var Zo={input:"",messages:[],engineState:"IDLE",commandHistory:[],historyIndex:-1,isProcessing:!1,debugEnabled:!1,connectionStatus:"disconnected",connectionRetryInSec:null,refreshCountdownSec:null,deviceId:null};function Tn({engine:o,bus:e,children:t}){let[r,n]=zo(Xo,Zo),[i,a]=Yo(()=>Object.freeze({phase:o.runtime.phase,flags:Object.freeze({debug:o.runtime.flags.debug})}));kn(()=>{e.emit("ui:initialized",Date.now())},[e]),kn(()=>{let c=E=>E==="running"?"RUNNING":E==="stopping"?"STOPPING":E==="booting"?"IDLE":"STOPPED",l=e.on("notify:event",E=>{if(n({type:"ADD_MESSAGE",payload:E}),E.groupKey?.startsWith("auth:refresh"))if(E.kind==="info"){let w=E.message.match(/refresh scheduled in (\d+)s/);if(w){let x=parseInt(w[1],10);isNaN(x)||n({type:"SET_REFRESH_COUNTDOWN",payload:x})}}else E.kind==="success"&&E.message.includes("refresh completed")&&n({type:"SET_REFRESH_COUNTDOWN",payload:null})}),d=()=>{let E=o.runtime;n({type:"SET_ENGINE_STATE",payload:c(E.phase)}),n({type:"SET_DEBUG_ENABLED",payload:E.flags.debug});let w=o.configurationContext.configuration.device?.id??null;n({type:"SET_DEVICE_ID",payload:w}),a(Object.freeze({phase:E.phase,flags:Object.freeze({debug:E.flags.debug})}))};d();let p=e.on("engine:started",()=>{n({type:"SET_ENGINE_STATE",payload:"RUNNING"}),d()}),f=e.on("engine:stopping",()=>{n({type:"SET_ENGINE_STATE",payload:"STOPPING"}),d()}),g=e.on("engine:stopped",()=>{n({type:"SET_ENGINE_STATE",payload:"STOPPED"}),d()}),m=e.on("ws:connecting",()=>{n({type:"SET_CONNECTION_STATUS",payload:"connecting"}),n({type:"SET_CONNECTION_RETRY",payload:null})}),h=e.on("ws:connected",()=>{n({type:"SET_CONNECTION_STATUS",payload:"connected"}),n({type:"SET_CONNECTION_RETRY",payload:null})}),y=e.on("ws:disconnected",()=>{n({type:"SET_CONNECTION_STATUS",payload:"disconnected"}),n({type:"SET_CONNECTION_RETRY",payload:null})}),k=e.on("ws:retrying",E=>{n({type:"SET_CONNECTION_STATUS",payload:"connecting"}),n({type:"SET_CONNECTION_RETRY",payload:E.retryInSec})}),b=e.on("runtime:debug:changed",({enabled:E})=>{n({type:"SET_DEBUG_ENABLED",payload:E});let w=o.runtime;a(Object.freeze({phase:w.phase,flags:Object.freeze({debug:E})}))}),T=e.on("config:loaded",E=>{let w=E.device?.id??null;n({type:"SET_DEVICE_ID",payload:w})}),_=new ne("Welcome to DeviceLink Agent UI",{ts:Date.now(),kind:"status"}),I=new ne('Type "help" for commands. Start bridge with: start or run',{ts:Date.now(),kind:"status"});return n({type:"SET_MESSAGES",payload:[_,I]}),()=>{l.unsubscribe(),p.unsubscribe(),f.unsubscribe(),g.unsubscribe(),m.unsubscribe(),h.unsubscribe(),y.unsubscribe(),k.unsubscribe(),b.unsubscribe(),T.unsubscribe()}},[e,o,n]);let s=Ne(()=>({state:r,dispatch:n,engine:o,bus:e,runtime:i}),[r,o,e,i]);return ei(_n.Provider,{value:s,children:t})}function Te(){let o=Vo(_n);if(!o)throw new Error("useAppContext must be used within AppContextProvider");return o}function wn(o){let{state:e}=Te(),t=c=>!(c.category?.includes("token-refresh")||c.groupKey?.startsWith("auth:refresh")&&c.kind==="info"&&c.message.includes("refresh scheduled in")),r=o?.displayFilter??t,n=Ne(()=>e.messages.filter(r),[e.messages,r]),i=Ne(()=>c=>e.messages.filter(c),[e.messages]),a=Ne(()=>c=>e.messages.filter(l=>l.kind===c),[e.messages]),s=Ne(()=>c=>e.messages.filter(l=>l.scope===c),[e.messages]);return Ne(()=>({rawMessages:e.messages,messagesToDisplay:n,getFilteredMessages:i,getMessagesByKind:a,getMessagesByScope:s}),[e.messages,n,i,a,s])}import{Fragment as Fe,jsx as P,jsxs as W}from"react/jsx-runtime";function ti(o){return o==="RUNNING"?"green":o==="STOPPING"?"yellow":o==="STOPPED"?"red":"gray"}function ri(o){return o==="connected"?"green":o==="connecting"?"yellow":"red"}function ni(o){return o==="connected"?"connected":o==="connecting"?"trying":"disconnected"}function xn(o,e){return o.length<=e?o:o.slice(0,Math.max(0,e-3))+"..."}function oi(o){let{state:e}=Te(),t="DeviceLink Agent",r=e.debugEnabled,n=e.deviceId??"not signed in",i=e.engineState,a=ti(i),s=e.isProcessing,c=e.connectionStatus,l=e.connectionRetryInSec,d=e.refreshCountdownSec,[p,f]=Cn(d),[g,m]=Cn(l);Ct(()=>{f(d)},[d]),Ct(()=>{m(l)},[l]),Ct(()=>{if(d===null||d<=0){f(null);return}f(d);let E=setInterval(()=>{f(w=>w===null||w<=1?null:w-1)},1e3);return()=>clearInterval(E)},[d]),Ct(()=>{if(l===null||l<=0){m(null);return}m(l);let E=setInterval(()=>{m(w=>w===null||w<=1?null:w-1)},1e3);return()=>clearInterval(E)},[l]);let h=ni(c),y=ri(c),k=t.length,b=30,T=o.width-k-2,_=[];if(_.push(P(N,{color:y,children:h},"conn")),g!==null&&g>0&&_.push(W(N,{color:y,children:["retry in ",g,"s"]},"retry")),p!==null&&p>0&&p<=5&&_.push(W(N,{color:"cyan",children:["refresh in ",p,"s"]},"refresh")),T>=b){let E=xn(n,Math.max(5,T-50));return W(Pe,{justifyContent:"space-between",width:o.width,children:[W(Pe,{children:[P(N,{bold:!0,children:t}),r&&W(Fe,{children:[P(N,{children:" "}),P(N,{color:"yellow",bold:!0,children:"[DEBUG]"})]})]}),W(Pe,{children:[s&&P(Fe,{children:P(N,{dimColor:!0,children:"\u23F3 "})}),P(N,{dimColor:!0,children:"device "}),W(N,{children:[E," "]}),P(N,{children:"\xB7 "}),P(N,{color:a,children:i}),_.length>0&&W(Fe,{children:[P(N,{children:" \xB7 "}),_.map((w,x)=>W(Rn.Fragment,{children:[x>0&&P(N,{children:" \xB7 "}),w]},x))]})]})]})}let I=xn(n,o.width-8);return W(Pe,{flexDirection:"column",width:o.width,children:[W(Pe,{children:[P(N,{bold:!0,children:t}),r&&W(Fe,{children:[P(N,{children:" "}),P(N,{color:"yellow",bold:!0,children:"[DEBUG]"})]})]}),W(Pe,{children:[s&&P(Fe,{children:P(N,{dimColor:!0,children:"\u23F3 "})}),P(N,{dimColor:!0,children:"device "}),W(N,{children:[I," "]}),P(N,{children:"\xB7 "}),P(N,{color:a,children:i}),_.length>0&&W(Fe,{children:[P(N,{children:" \xB7 "}),_.map((E,w)=>W(Rn.Fragment,{children:[w>0&&P(N,{children:" \xB7 "}),E]},w))]})]})]})}var An=oi;import On,{useMemo as In}from"react";import{Box as ii,Text as we}from"ink";import{jsx as Re,jsxs as Dn}from"react/jsx-runtime";function si(o){return o==="error"?"red":o==="warning"?"yellow":o==="success"?"green":o==="info"?"cyan":"white"}function ai(o){return o==="error"?"ERROR":o==="warning"?"WARN":o==="success"?"SUCCESS":o==="info"?"INFO":o==="status"?"STATUS":"INFO"}function ci(o){let e=new Date(o),t=String(e.getHours()).padStart(2,"0"),r=String(e.getMinutes()).padStart(2,"0"),n=String(e.getSeconds()).padStart(2,"0");return`${t}:${r}:${n}`}function ui(o,e){if(typeof o!="string"&&(o=String(o??"")),o.length<=e)return[o];let t=[],r="",n=o.split(/(\s+)/);for(let i of n)if((r+i).length<=e)r+=i;else{r&&t.push(r),r="";let a=i;for(;a.length>e;)t.push(a.slice(0,e)),a=a.slice(e);r=a}return r&&t.push(r),t.length>0?t:[o]}var li=On.memo(function(e){let{messagesToDisplay:t}=wn(),r=e.height??14,c=Math.max(20,e.width-9-8-2-2),l=In(()=>t.slice(-r),[t,r]),p=In(()=>l.flatMap(f=>{let g=ci(f.ts),m=ai(f.kind);return ui(f.message,c).map((y,k)=>({msg:f,lineIndex:k,time:k===0?g:"",kind:k===0?m:"",message:y,isContinuation:k>0}))}),[l,c]).slice(-r);return Re(ii,{flexDirection:"column",borderStyle:"round",borderColor:"gray",paddingX:1,height:r+2,width:e.width,children:p.length===0?Re(we,{dimColor:!0,children:"(no messages)"}):p.map(f=>Re(di,{item:f},`${f.msg.id}:${f.lineIndex}`))})},(o,e)=>o.height===e.height&&o.width===e.width),di=On.memo(function({item:e}){return Dn(we,{children:[e.time&&Dn(we,{dimColor:!0,children:[e.time," "]}),e.kind&&Re(we,{color:si(e.msg.kind,e.msg.scope),children:e.kind}),e.kind&&Re(we,{children:" "}),e.isContinuation&&Re(we,{dimColor:!0,children:" "}),Re(we,{dimColor:e.msg.scope==="system",children:e.message})]})}),Nn=li;import pi from"react";import{Box as xt,Text as wr,useFocus as fi}from"ink";import hi from"ink-text-input";import{jsx as Me,jsxs as Pn}from"react/jsx-runtime";var mi=pi.memo(function(e){let{state:t,dispatch:r}=Te(),{isFocused:n}=fi({autoFocus:!0,isActive:!t.isProcessing}),i=e.isPassphraseMode?"passphrase> ":"dlink> ",a=Math.max(10,e.width-i.length-2);return Pn(xt,{flexDirection:"column",width:e.width,children:[Pn(xt,{width:e.width,children:[Me(wr,{color:e.isPassphraseMode?"cyan":"green",children:i}),Me(xt,{width:a,children:t.isProcessing?Me(wr,{dimColor:!0,italic:!0,children:"Processing..."}):Me(hi,{value:t.input,onChange:s=>r({type:"SET_INPUT",payload:s}),onSubmit:e.onSubmit,placeholder:n?void 0:e.isPassphraseMode?"Enter passphrase...":'Type a command or "help"',mask:e.isPassphraseMode?"*":void 0},`input-${t.historyIndex}-${e.isPassphraseMode?"passphrase":"normal"}`)})]}),e.passphraseError&&Me(xt,{marginTop:1,children:Me(wr,{color:"red",children:e.passphraseError})})]})}),Fn=mi;import{Component as gi}from"react";import{Box as Le,Text as At}from"ink";import{Fragment as yi,jsx as de,jsxs as Mn}from"react/jsx-runtime";var He=class extends gi{constructor(e){super(e),this.state={hasError:!1,error:null}}static getDerivedStateFromError(e){return{hasError:!0,error:e}}componentDidCatch(e,t){this.props.onError&&this.props.onError(e,t)}render(){return this.state.hasError&&this.state.error?de(Le,{flexDirection:"column",padding:1,children:de(Le,{borderStyle:"round",borderColor:"red",paddingX:1,children:Mn(Le,{flexDirection:"column",children:[de(At,{color:"red",bold:!0,children:"\u26A0\uFE0F Application Error"}),de(Le,{height:1}),de(At,{color:"red",children:this.state.error.message}),this.state.error.stack&&Mn(yi,{children:[de(Le,{height:1}),de(At,{dimColor:!0,children:this.state.error.stack})]}),de(Le,{height:1}),de(At,{dimColor:!0,children:"Press Ctrl+C to exit"})]})})}):this.props.children}};import{jsx as ve,jsxs as ki}from"react/jsx-runtime";function Ei(){let{exit:o}=vi(),{state:e,dispatch:t,engine:r,bus:n}=Te(),[i,a]=En(),s=i,c=a,l=Rr(m=>n.emit("console:command",m),[n]),d=Math.max(5,c-5),p=Math.min(d-3,Math.max(5,Math.floor(d*.7)));Si((m,h)=>{if(h.ctrl&&m==="c"){r.requestStop("user exit");return}if(!e.isProcessing&&(h.upArrow||h.downArrow)){if(e.commandHistory.length===0)return;if(h.upArrow){let y=e.historyIndex===-1?e.commandHistory.length-1:Math.max(0,e.historyIndex-1);t({type:"SET_HISTORY_INDEX",payload:y}),t({type:"SET_INPUT",payload:e.commandHistory[y]})}else if(h.downArrow){if(e.historyIndex===-1)return;let y=e.historyIndex+1;y>=e.commandHistory.length?(t({type:"SET_HISTORY_INDEX",payload:-1}),t({type:"SET_INPUT",payload:""})):(t({type:"SET_HISTORY_INDEX",payload:y}),t({type:"SET_INPUT",payload:e.commandHistory[y]}))}}}),bi(()=>{t({type:"RESET_HISTORY_INDEX_IF_TYPING"})},[e.input,t]);let f=Rr(async m=>{let h=m.trim();t({type:"RESET_INPUT"}),t({type:"RESET_HISTORY_INDEX"}),h&&t({type:"ADD_COMMAND_TO_HISTORY",payload:h}),t({type:"SET_IS_PROCESSING",payload:!0});try{await l(h)}catch(y){let k=L(y),b=new ne(`Command failed: ${k}`,{ts:Date.now(),kind:"error"});t({type:"ADD_MESSAGE",payload:b})}finally{t({type:"SET_IS_PROCESSING",payload:!1})}},[t,o,l,r]),g=Rr((m,h)=>{let y=new ne(`UI Error: ${m.message}`,{ts:Date.now(),kind:"error"});t({type:"ADD_MESSAGE",payload:y})},[t]);return ve(He,{onError:g,children:ki(Cr,{flexDirection:"column",padding:1,width:s,children:[ve(An,{width:s-2}),ve(Cr,{height:1}),ve(Nn,{height:p,width:s-2}),ve(Cr,{height:1}),ve(Fn,{width:s-2,onSubmit:f})]})})}function xr({engine:o,bus:e}){return ve(Tn,{engine:o,bus:e,children:ve(Ei,{})})}import Ar from"node:fs";import _i from"node:os";import Ir from"node:tty";function Ti(o){let e=o.setRawMode;return typeof e!="function"||(o.setRawMode=t=>{try{return e.call(o,t)}catch(r){let n=typeof r=="object"&&r!==null&&"code"in r?r.code:void 0;if(n==="EPERM"||n==="EINVAL")return o;throw r}}),o}var Ge;function Dr(){if(Ge!==void 0)return Ge;let o;if(process.stdin.isTTY)o=process.stdin;else try{if(_i.platform()==="win32")try{let e=Ar.openSync("CONIN$","rs");o=new Ir.ReadStream(e)}catch{let e=Ar.openSync("\\\\.\\CONIN$","rs");o=new Ir.ReadStream(e)}else{let e=Ar.openSync("/dev/tty","rs");o=new Ir.ReadStream(e)}}catch{return Ge=null,null}return Ge=Ti(o),Ge}function wi(){return Dr()===null}var Ri=new Set(["1","true","yes","on"]),$n=new Set(["0","false","no","off"]);function Ln(o){let e=o.trim().toLowerCase();if(e!==""){if(Ri.has(e))return!0;if($n.has(e))return!1}}function Ci(o){for(let e of o){let t=e.trim(),r=t.toLowerCase();if(r==="--headless")return"on";if(r.startsWith("--headless=")){let n=t.slice(t.indexOf("=")+1).trim().toLowerCase();return $n.has(n)?"off":"on"}}return"unset"}function jn(o){let e=Ci(o);if(e==="on")return{headless:!0,reason:"CLI: --headless"};if(e==="off")return{headless:!1,reason:"CLI: --headless=false"};let t=process.env.HEADLESS;if(t!==void 0&&t.trim()!==""){let i=Ln(t);if(i!==void 0)return{headless:i,reason:i?"env: HEADLESS":"env: HEADLESS=false"}}let r=process.env.DLINK_HEADLESS;if(r!==void 0&&r.trim()!==""){let i=Ln(r);if(i!==void 0)return{headless:i,reason:i?"env: DLINK_HEADLESS":"env: DLINK_HEADLESS=false"}}return wi()?{headless:!0,reason:"no TTY stdin (Docker/CI/background)"}:{headless:!1,reason:"TTY available (interactive)"}}function Kn(o,e){let t=o.runtime.flags.headless?null:Dr();if(!t){o.runtime.flags.headless?console.log(`[agent] Headless UI (${o.runtime.flags.headlessReason})`):console.warn(`[agent] No TTY stdin; non-interactive boot (${o.runtime.flags.headlessReason})`),e.emit("ui:initialized",Date.now());let n=setInterval(()=>{},6e4);return new Promise(i=>{process.on("SIGTERM",()=>{clearInterval(n),i()}),process.on("SIGINT",()=>{clearInterval(n),i()})})}let{waitUntilExit:r}=Ai(xi.createElement(xr,{engine:o,bus:e}),{stdin:t});return r()}var It=class{handlers=new Map;subcommandHandlers=new Map;command(e,t){if(this.handlers.has(e))throw new Error(`Command '${e}' is already registered`);return this.handlers.set(e,t),this}subcommand(e,t,r){this.subcommandHandlers.has(e)||this.subcommandHandlers.set(e,new Map);let n=this.subcommandHandlers.get(e);if(n.has(t))throw new Error(`Subcommand '${e} ${t}' is already registered`);return n.set(t,r),this}commandStringToArgv(e){let t=[],r="",n=!1,i="";for(let a=0;a<e.length;a++){let s=e[a];s==='"'||s==="'"?n&&s===i?(t.push(r),r="",n=!1,i=""):n?r+=s:(r&&(t.push(r),r=""),n=!0,i=s):s===" "&&!n?r&&(t.push(r),r=""):r+=s}return(n||r)&&t.push(r),["node","script",...t]}parseString(e){let t=this.commandStringToArgv(e);return this.parse(t)}parse(e){let t=e.slice(2);if(t.length===0)return null;let r={command:"",args:[],options:{}},n=0;for(;n<t.length&&t[n]?.startsWith("--");)n++;if(n>=t.length||!t[n])return null;for(r.command=t[n],n++;n<t.length&&t[n]?.startsWith("--");)n++;for(n<t.length&&t[n]&&!t[n].startsWith("--")&&(r.subcommand=t[n],n++);n<t.length;){let i=t[n];if(i.startsWith("--")){let a=i.slice(2);if(a.includes("=")){let[s,c]=a.split("=",2);r.options[s]=c||!0}else{let s=t[n+1];s&&!s.startsWith("--")?(r.options[a]=s,n++):r.options[a]=!0}}else r.args.push(i);n++}return r}async execute(e){let t=this.parse(e);if(!t)return!1;if(t.subcommand){let n=this.subcommandHandlers.get(t.command);if(n){let i=n.get(t.subcommand);if(i)return await i(t),!0}}let r=this.handlers.get(t.command);return r?(await r(t),!0):!1}hasCommand(e,t){return t?this.subcommandHandlers.get(e)?.has(t)??!1:this.handlers.has(e)}getCommands(){return Array.from(this.handlers.keys())}};var Or=class{kind;payloadJson;constructor(e,t){this.kind=e,this.payloadJson=t}},Dt=class{matchers=[];register(e){return this.matchers.push(e),this}build(e){let t=[e.command];e.subcommand&&t.push(e.subcommand);for(let r of this.matchers){let n=r(t,e);if(n.handled){let i;if(n.job)i=n.job;else if(n.kind)i=this.createCommandInput(n.kind,e);else return null;return i}}return null}createCommandInput(e,t){let r={command:t.command,subcommand:t.subcommand,args:t.args,options:t.options};return new Or(e,JSON.stringify(r))}};var Ot=class{name="console-orchestrator";commandParser;commandJobBuilder;constructor(){this.commandParser=new It,this.commandJobBuilder=new Dt,this.setupCommandMappings()}onRegister(e){}async onStart(e){e.notifier.info("Starting console orchestrator (UI)",{scope:"system"}),e.on("console:command",async t=>{try{let r=this.commandParser.parseString(t);if(!r){e.notifier.warn("Invalid command format");return}let n=this.commandJobBuilder.build(r);if(!n){e.notifier.warn(`Unknown command: ${r.command}${r.subcommand?" "+r.subcommand:""}`);return}switch(n.kind){case"signin":e.bus.emit("auth:signin:requested",void 0),e.notifier.info("Sign-in requested");break;case"signout":e.bus.emit("auth:signout:requested",void 0),e.notifier.info("Sign-out requested");break;case"debug-on":e.flags.debug=!0,e.bus.emit("runtime:debug:changed",{enabled:!0}),e.notifier.success("Debug mode enabled");break;case"debug-off":e.flags.debug=!1,e.bus.emit("runtime:debug:changed",{enabled:!1}),e.notifier.success("Debug mode disabled");break;case"replace":e.bus.emit("auth:session_conflict:resolve",{choice:"USE_NEW"});break;default:e.notifier.warn(`Command not implemented: ${n.kind}`);break}}catch(r){let n=L(r);e.notifier.error(`Command failed: ${n}`,{scope:"system"})}})}async onStarted(e){e.notifier.info("Console orchestrator started",{scope:"system"}),Kn(e.engine,e.bus)}async onStop(e){e.notifier.info("UI exited, stopping engine",{scope:"system"}),e.engine&&e.engine.requestStop("UI exited")}async onStopped(e){}setupCommandMappings(){let e=this.commandJobBuilder;e.register(t=>{if(t.length===1){let r=t[0];if(r==="start"||r==="run")return{handled:!0,kind:"start"};if(r==="stop")return{handled:!0,kind:"stop"};if(r==="signin")return{handled:!0,kind:"signin"};if(r==="signout")return{handled:!0,kind:"signout"};if(r==="status")return{handled:!0,kind:"status"};if(r==="doctor")return{handled:!0,kind:"doctor"};if(r==="replace"||r==="yes"||r==="y")return{handled:!0,kind:"replace"}}return{handled:!1}}),e.register(t=>{if(t.length===2){let[r,n]=t;if(r==="roots"){if(n==="add")return{handled:!0,kind:"roots-add"};if(n==="list")return{handled:!0,kind:"roots-list"}}if(r==="debug"){if(n==="on")return{handled:!0,kind:"debug-on"};if(n==="off")return{handled:!0,kind:"debug-off"}}}return{handled:!1}}),e.register((t,r)=>{if(t.length===1&&t[0]==="debug"&&r.args.length>0){let n=r.args[0];if(n==="on")return{handled:!0,kind:"debug-on"};if(n==="off")return{handled:!0,kind:"debug-off"}}return{handled:!1}})}};import Ce from"ws";var Nt=class{ws=null;state={kind:"DISCONNECTED"};stopped=!1;connecting=!1;connId=0;connectionTimeout=null;heartbeatInterval=null;heartbeatTimeout=null;awaitingPong=!1;sendQueue=[];url;abortSignal;connectionTimeoutMs;heartbeatIntervalMs;pongTimeoutMs;maxSendQueueSize;sendQueueItemTtlMs;onStateChangeCallback;onErrorCallback;onConnectedCallback;onDisconnectedCallback;onMessageCallback;constructor(e,t={}){this.url=e,this.abortSignal=t.abortSignal,this.connectionTimeoutMs=t.connectionTimeoutMs??1e4,this.heartbeatIntervalMs=t.heartbeatIntervalMs??2e4,this.pongTimeoutMs=t.pongTimeoutMs??1e4,this.maxSendQueueSize=t.maxSendQueueSize??1e3,this.sendQueueItemTtlMs=t.sendQueueItemTtlMs??6e4,this.abortSignal&&this.abortSignal.addEventListener("abort",()=>{this.stop()},{once:!0})}registerOnStateChangeCallback(e){this.onStateChangeCallback=e}registerOnErrorCallback(e){this.onErrorCallback=e}registerOnConnectedCallback(e){this.onConnectedCallback=e}registerOnDisconnectedCallback(e){this.onDisconnectedCallback=e}registerOnMessageCallback(e){this.onMessageCallback=e}getState(){return this.state}getSocket(){return this.ws}isConnected(){return this.ws?.readyState===Ce.OPEN}async connect(){if(this.stopped)throw new Error("Socket is stopped");if(!(this.connecting||this.isConnected())){this.connecting=!0,this.updateState({kind:"CONNECTING"});try{await this.connectWebSocketOnce()}catch(e){let t=e instanceof Error?e:new Error(String(e));throw this.onErrorCallback?.(t),this.updateState({kind:"DISCONNECTED"}),t}finally{this.connecting=!1}}}stop(){this.stopped=!0,this.disconnect(!0)}reset(){this.stopped=!1}disconnect(e=!1){e&&(this.stopped=!0),this.connectionTimeout&&(clearTimeout(this.connectionTimeout),this.connectionTimeout=null),this.stopHeartbeat();let t=this.ws;this.ws=null,this.connecting=!1,t&&this.closeGracefullyThenTerminate(t,1e3,"client disconnect",2e3),this.updateState({kind:"DISCONNECTED"})}send(e,t){if(this.stopped){t?.(new Error("Socket is stopped"));return}let r=this.ws;if(r&&r.readyState===Ce.OPEN){r.send(e,i=>t?.(i??void 0));return}let n=Date.now();if(this.pruneSendQueue(n),this.sendQueue.length>=this.maxSendQueueSize){t?.(new Error(`Send queue full (max ${this.maxSendQueueSize})`));return}this.sendQueue.push({data:e,cb:t,expiresAt:n+this.sendQueueItemTtlMs})}closeGracefullyThenTerminate(e,t,r,n){try{(e.readyState===Ce.OPEN||e.readyState===Ce.CONNECTING)&&e.close(t,r)}catch{}let i=setTimeout(()=>{try{e.terminate()}catch{}},n);e.once("close",()=>clearTimeout(i))}async connectWebSocketOnce(){let e=++this.connId;return new Promise((t,r)=>{if(this.stopped){r(new Error("Socket is stopped"));return}let n=!1,i=!1,a=()=>{this.connectionTimeout&&(clearTimeout(this.connectionTimeout),this.connectionTimeout=null)},s=()=>{i||(i=!0,a(),t())},c=(d,p)=>{i||(i=!0,a(),p&&this.closeGracefullyThenTerminate(p,1e3,"connect failed",1500),r(d))},l;try{l=new Ce(this.url),this.connectionTimeout=setTimeout(()=>{c(new Error(`Connection timeout: unable to connect to ${this.url} within ${this.connectionTimeoutMs}ms`),l)},this.connectionTimeoutMs),l.on("open",()=>{if(n=!0,e!==this.connId){this.closeGracefullyThenTerminate(l,1e3,"stale connection",500);return}this.ws=l,this.retryResetOnConnect(),this.updateState({kind:"CONNECTED"}),this.onConnectedCallback?.(l),this.startHeartbeat(e),this.flushSendQueue(),s()}),l.on("error",d=>{if(e!==this.connId)return;let p=d instanceof Error?d:new Error(String(d));if(!n&&!i){c(p,l);return}this.onErrorCallback?.(p)}),l.on("close",(d,p)=>{if(e!==this.connId)return;let f=p?.toString?.()||"";if(!n&&!i){this.ws=null,this.stopHeartbeat(),this.updateState({kind:"DISCONNECTED"}),c(new Error(`WebSocket closed before connection established: ${d} ${f}`),l);return}this.ws===l&&(this.ws=null),this.stopHeartbeat(),this.updateState({kind:"DISCONNECTED"}),this.onDisconnectedCallback?.(d,f||`code ${d}`)}),l.on("message",d=>{if(e!==this.connId){this.onErrorCallback?.(new Error("Message ignored - connection ID mismatch"));return}if(this.ws!==l){this.onErrorCallback?.(new Error("Message ignored - socket mismatch"));return}this.onMessageCallback?.(d)}),l.on("pong",()=>{e===this.connId&&(this.awaitingPong=!1,this.heartbeatTimeout&&(clearTimeout(this.heartbeatTimeout),this.heartbeatTimeout=null))}),this.abortSignal&&this.abortSignal.addEventListener("abort",()=>{e===this.connId&&(this.closeGracefullyThenTerminate(l,1e3,"aborted",1e3),i||c(new Error("Connection aborted"),l))},{once:!0})}catch(d){let p=d instanceof Error?d:new Error(String(d));r(p)}})}retryResetOnConnect(){}startHeartbeat(e){this.stopHeartbeat(),this.ws&&(this.awaitingPong=!1,this.heartbeatInterval=setInterval(()=>{if(this.stopped||e!==this.connId)return;let r=this.ws;if(!(!r||r.readyState!==Ce.OPEN)){if(this.awaitingPong){this.closeGracefullyThenTerminate(r,4e3,"pong timeout",1e3);return}this.awaitingPong=!0;try{r.ping()}catch{this.closeGracefullyThenTerminate(r,1011,"ping failed",1e3);return}this.heartbeatTimeout&&clearTimeout(this.heartbeatTimeout),this.heartbeatTimeout=setTimeout(()=>{if(e!==this.connId)return;let n=this.ws;n&&this.closeGracefullyThenTerminate(n,4e3,"pong timeout",1e3)},this.pongTimeoutMs)}},this.heartbeatIntervalMs))}stopHeartbeat(){this.awaitingPong=!1,this.heartbeatInterval&&(clearInterval(this.heartbeatInterval),this.heartbeatInterval=null),this.heartbeatTimeout&&(clearTimeout(this.heartbeatTimeout),this.heartbeatTimeout=null)}flushSendQueue(){let e=this.ws;if(!e||e.readyState!==Ce.OPEN)return;let t=Date.now();this.pruneSendQueue(t);let r=this.sendQueue;this.sendQueue=[];for(let n of r)e.send(n.data,i=>n.cb?.(i??void 0))}pruneSendQueue(e){if(this.sendQueue.length===0)return;let t=[];for(let r of this.sendQueue)r.expiresAt>=e?t.push(r):r.cb?.(new Error("Send dropped: queued item expired"));this.sendQueue=t}updateState(e){this.state=e,this.onStateChangeCallback?.(e)}};var Pt=class{name="websocket-orchestrator";socket=null;stopped=!1;connectLoopInFlight=null;retryAttempt=0;countdownTimer=null;lastUrl=null;async onStart(e){e.notifier.info("WS: orchestrator started",{scope:"system"})}async onStarted(e){let t=e.engine.configurationContext.configuration;await this.isConnectableToBridge(t,e)&&this.ensureStarted(e,"onStarted")}async onStop(e){this.stopped=!0,this.clearCountdown(),this.socket&&(e.notifier.info("WS: stopping socket (onStop)",{scope:"system"}),this.socket.stop(),this.socket=null),e.notifier.info("WS: orchestrator stopped",{scope:"system"})}handlers(){let e=async(t,r)=>{if(this.stopped)return;let n=r.engine.configurationContext.configuration;await this.isConnectableToBridge(n,r)&&this.ensureStarted(r,t)};return{"config:cache:validated":async(t,r)=>e("config:cache:validated",r),"auth:signout:requested":async(t,r)=>{r.notifier.info("WS: sign-out requested -> disconnecting"),this.stopped=!0,this.clearCountdown(),this.socket?.stop(),this.socket=null},"ws:disconnect":async(t,r)=>{r.notifier.info("WS: disconnect requested",{scope:"system"}),this.stopped=!0,this.clearCountdown(),this.socket?.stop(),this.socket=null},"auth:ws:reconnect":async(t,r)=>{r.notifier.info("WS: reconnect requested by auth",{scope:"system"}),this.retryAttempt=0,this.clearCountdown(),this.stopped=!0,this.socket?.stop(),this.connectLoopInFlight&&await this.connectLoopInFlight.catch(()=>{}),this.socket=null,this.stopped=!1,this.ensureStarted(r,"auth:ws:reconnect")},"ws:tx":async(t,r)=>{let n=this.socket?.getSocket();if(!n||n.readyState!==1){r.notifier.info("WS: TX dropped (not connected)",{scope:"system"});return}n.send(t.text)}}}ensureStarted(e,t){if(this.connectLoopInFlight){e.notifier.info(`WS: connect loop already running (reason=${t})`,{scope:"system"});return}let r=this.socket?.getSocket();if(r&&r.readyState<=1){e.notifier.info(`WS: already connected/connecting, skipping (reason=${t})`,{scope:"system"});return}this.connectLoopInFlight=this.startOrJoinConnectLoop(e,t).finally(()=>{this.connectLoopInFlight=null})}async startOrJoinConnectLoop(e,t){this.stopped=!1;let r=e.engine.configurationContext.configuration;if(!await this.isConnectableToBridge(r,e))return;let i=String(r.bridgeWsUrl);if(this.lastUrl=i,e.notifier.info(`WS: ensure connection (reason=${t}) url=${i}`,{scope:"system"}),!this.socket){let a=new Nt(i,{abortSignal:e.signal,connectionTimeoutMs:1e4,heartbeatIntervalMs:2e4,pongTimeoutMs:1e4});this.socket=a,a.registerOnStateChangeCallback(s=>this.handleStateChange(s,e)),a.registerOnErrorCallback(s=>{e.notifier.error(`WS: transport error: ${s.message}`)}),a.registerOnConnectedCallback(s=>{this.socket===a&&(this.retryAttempt=0,this.clearCountdown(),e.notifier.success(e.flags.debug?`WS: connected (${i})`:"WS: connected"),e.bus.emit("ws:connected",{url:i,attempt:0}))}),a.registerOnDisconnectedCallback((s,c)=>{if(this.socket!==a){e.notifier.info(`WS: ignoring stale disconnect (code=${s})`,{scope:"system"});return}if(e.notifier.warn(`WS: disconnected code=${s} reason=${c||"-"}`),e.bus.emit("ws:disconnected",{code:s,reason:c}),this.stopped){e.notifier.info(`WS: stopped flag set, skipping auto-reconnect (code=${s})`,{scope:"system"});return}if(s===4001){e.notifier.info("WS: auth disconnect (4001) -> waiting for auth",{scope:"system"});return}if(s===4011){this.stopped=!0,this.socket?.stop(),this.socket=null;return}this.ensureStarted(e,"socket:disconnected")}),a.registerOnMessageCallback(s=>{if(this.socket!==a)return;let c=typeof s=="string"?s:s.toString("utf8");e.bus.emit("ws:rx",{text:c})})}for(;!this.stopped;)try{let a=this.retryAttempt+1;e.notifier.info(`WS: connecting attempt #${a} to ${i}`,{scope:"system",groupKey:"ws:connecting"}),e.bus.emit("ws:connecting",{url:i,attempt:a}),await this.socket.connect();return}catch(a){if(this.stopped)return;this.retryAttempt+=1;let s=this.computeBackoffMs(this.retryAttempt),c=Math.max(1,Math.ceil(s/1e3)),l=L(a);e.notifier.warn(`WS: connect failed (attempt=${this.retryAttempt}) -> retry in ${c}s (${l})`,{scope:"system",category:["websocket-connect"],groupKey:"ws:connect-retry"});let d={retryInSec:c,attempt:this.retryAttempt};e.bus.emit("ws:retrying",d),this.startCountdown(c,e),await this.interruptibleSleep(s)}}async isConnectableToBridge(e,t){if(!e.device?.id)return t.notifier.error("WS: cannot connect: device.id missing"),!1;if(!e.bridgeWsUrl)return t.notifier.error("WS: cannot connect: bridgeWsUrl missing"),!1;if(!e.device?.keys?.public)return t.notifier.error("WS: cannot connect: device public key missing"),!1;let r=await t.engine.secrets.get("refreshToken");return!!r.ok&&!!r.value?!0:(t.notifier.warn("WS: not connecting (no tokens). Run signin."),!1)}handleStateChange(e,t){e.kind==="CONNECTING"&&t.notifier.info(`WS: state CONNECTING (url=${this.lastUrl})`,{scope:"system"}),e.kind==="CONNECTED"&&t.notifier.info(`WS: state CONNECTED (url=${this.lastUrl})`,{scope:"system"}),e.kind==="DISCONNECTED"&&t.notifier.info(`WS: state DISCONNECTED (url=${this.lastUrl})`,{scope:"system"})}computeBackoffMs(e){let t=Math.min(3e4,1e3*Math.pow(2,Math.min(e-1,5))),r=t*(.2*(Math.random()*2-1));return Math.max(250,Math.floor(t+r))}startCountdown(e,t){this.clearCountdown();let r=e;this.countdownTimer=setInterval(()=>{if(r-=1,r<=0){this.clearCountdown();return}let n={retryInSec:r,attempt:this.retryAttempt};t.bus.emit("ws:retrying",n)},1e3)}clearCountdown(){this.countdownTimer&&(clearInterval(this.countdownTimer),this.countdownTimer=null)}async interruptibleSleep(e){let r=Date.now();for(;Date.now()-r<e;){if(this.stopped)return;await or(100)}}};import Un from"node:fs";import Ii from"node:path";var Ft=class{static save(e){let t=this.getApplicationConfigFolder(),r=Ii.join(t,"config.json"),n=this.stripSecrets(e);return Un.mkdirSync(t,{recursive:!0}),Un.writeFileSync(r,JSON.stringify(n,null,2),"utf8"),r}static stripSecrets(e){let t=e.device?{id:e.device.id,name:e.device.name,tag:e.device.tag,keys:{public:e.device.keys.public}}:void 0,r=e.tokens?{accessTokenExpSec:e.tokens.accessTokenExpSec}:void 0;return{...e,device:t,tokens:r}}static getApplicationConfigFolder(){return me()}};var Mt=class{name="main-orchestrator";bootStarted=!1;saveTimer=null;saveInFlight=null;latestConfigToSave=null;saveDebounceMs=500;async onStop(e){await this.flushSaveOnStop()}handlers(){return{"ui:initialized":async(e,t)=>{this.bootStarted||(this.bootStarted=!0,t.engine.setPhase("booting"),t.notifier.info("Booting..."),t.engine.dispatch(un(new _t)))},"config:save:requested":async(e,t)=>{this.latestConfigToSave=e,this.saveTimer&&clearTimeout(this.saveTimer),this.saveTimer=setTimeout(()=>{this.performSave(t)},this.saveDebounceMs)}}}async performSave(e){this.saveInFlight||(this.saveInFlight=(async()=>{for(;this.latestConfigToSave;){let t=this.latestConfigToSave;this.latestConfigToSave=null;try{let r=Ft.save(t);e.notifier.info(`Config saved to: ${r}`,{scope:"system"}),e.bus.emit("config:saved",void 0)}catch(r){let n=L(r);e.notifier.error(`Failed to save configuration: ${n}`)}}})().finally(()=>{this.saveInFlight=null}),await this.saveInFlight)}async flushSaveOnStop(){this.saveTimer&&(clearTimeout(this.saveTimer),this.saveTimer=null)}};import{decodeJwt as ds}from"jose";import Pr from"node:crypto";import{z as u}from"zod";var Di=u.object({name:u.string().min(1),description:u.string().optional(),inputSchema:u.any().optional(),outputSchema:u.any().optional(),defaults:u.record(u.string(),u.unknown()).optional(),version:u.string().optional(),errorCodes:u.array(u.string()).optional()}),Oi=u.object({id:u.string().min(1),name:u.string().min(1),path:u.string().optional(),path_hint:u.string().optional(),policy:u.any().optional()}),Ni=u.object({type:u.literal("connect"),device_id:u.string().min(1),agent_version:u.string().min(1),protocol_version:u.number().int().min(1).default(2),capabilities:u.array(u.string()).default([]),platform:u.string().optional(),hostname:u.string().optional(),device_tag:u.string().optional(),ts:u.number(),nonce:u.string().min(8),sig:u.string().min(16),access_token:u.string().optional(),refresh_token:u.string().min(20).optional(),public_key_pem:u.string().optional()}).passthrough(),Pi=u.object({type:u.literal("connect_ack"),ok:u.literal(!0),session_id:u.string().min(1),device_id:u.string().min(1),server_time:u.number(),auth:u.object({device_proof:u.literal("ok"),access_token:u.enum(["ok","expired","invalid","missing"])}),next:u.array(u.enum(["refresh_access_token","session_init"])).min(1),workspace_id:u.string().optional()}).strict(),Fi=u.object({type:u.literal("connect_ack"),ok:u.literal(!1),session_id:u.string().optional(),server_time:u.number(),error:u.object({code:u.string().min(1),message:u.string().min(1)})}).strict(),Mi=u.union([Pi,Fi]),Li=u.object({type:u.literal("refresh_access_token"),device_id:u.string().min(1),ts:u.number(),nonce:u.string().min(8),refresh_token:u.string().min(20),sig:u.string().min(16),session_id:u.string().min(1).optional()}).strict(),$i=u.object({type:u.literal("refresh_access_token_ack"),ok:u.literal(!0),access_token:u.string().min(1),refresh_token:u.string().min(20),expires_in_sec:u.number().int().positive().optional(),next:u.array(u.literal("session_init")).default(["session_init"])}).strict(),ji=u.object({type:u.literal("refresh_access_token_ack"),ok:u.literal(!1),error:u.object({code:u.string().min(1),message:u.string().min(1)})}).strict(),Ki=u.union([$i,ji]),Ui=u.object({type:u.literal("session_init"),device_id:u.string().min(1),session_id:u.string().min(1)}).strict(),Bi=u.object({type:u.literal("session_init_ack"),ok:u.literal(!0),device_id:u.string().min(1),session_id:u.string().min(1),mode:u.enum(["ACTIVE","ACTIVE_RECLAIMED","STANDBY"]),reason:u.string().optional(),server_time:u.number(),next:u.array(u.enum(["tools_publish","directories_publish"])).optional(),server_protocol_version:u.number().default(2),min_agent_version:u.string().optional(),upgrade_message:u.string().optional(),upgrade_url:u.string().optional()}).passthrough(),qi=u.object({type:u.literal("session_init_ack"),ok:u.literal(!1),device_id:u.string().min(1),server_time:u.number(),error:u.object({code:u.string().min(1),message:u.string().min(1)})}).passthrough(),Wi=u.union([Bi,qi]),Ji=u.object({type:u.literal("session_conflict"),device_id:u.string().min(1),new_session_id:u.string().min(1),max_sessions:u.number().int().positive(),active_count:u.number().int().nonnegative(),deadline_ms:u.number().int().positive(),message:u.string().min(1),choices:u.array(u.object({id:u.enum(["USE_NEW","KEEP_OLD"]),default:u.boolean().optional(),prompt:u.string().optional()})).min(1)}).strict(),Hi=u.object({type:u.literal("session_conflict_resolve"),device_id:u.string().min(1),new_session_id:u.string().min(1),choice:u.enum(["USE_NEW","KEEP_OLD"])}).strict(),Gi=u.object({type:u.literal("session_conflict_resolve_ack"),ok:u.literal(!0),device_id:u.string().min(1),choice:u.enum(["USE_NEW","KEEP_OLD"]),next:u.array(u.enum(["session_init"])).optional()}).strict(),Vi=u.object({type:u.literal("session_conflict_resolve_ack"),ok:u.literal(!1),device_id:u.string().min(1),error:u.object({code:u.string().min(1),message:u.string().min(1)})}).strict(),zi=u.union([Gi,Vi]),Yi=u.object({type:u.literal("quiesce"),session_id:u.string().min(1),reason:u.enum(["SESSION_REPLACED"]),deadline_ms:u.number().int().positive()}).strict(),Qi=u.object({type:u.literal("quiesce_ack"),session_id:u.string().min(1),status:u.enum(["IDLE","BUSY"])}).strict(),Xi=u.object({type:u.literal("tools_publish"),device_id:u.string().min(1),workspace_id:u.string().optional(),tools:u.array(Di).default([]),tools_version:u.string().optional()}).strict(),Zi=u.object({type:u.literal("tools_publish_ack"),ok:u.boolean(),device_id:u.string().min(1),tools_version:u.string().optional(),error:u.object({code:u.string().min(1),message:u.string().min(1)}).optional()}).strict(),es=u.object({type:u.literal("directories_publish"),device_id:u.string().min(1),workspace_id:u.string().optional(),directories:u.array(Oi).default([])}).strict(),ts=u.object({type:u.literal("directories_publish_ack"),ok:u.boolean(),device_id:u.string().min(1),error:u.object({code:u.string().min(1),message:u.string().min(1)}).optional()}).strict(),rs=u.object({blocked_extensions:u.array(u.string()).default([]),allowed_extensions:u.array(u.string()).default([]),blocked_patterns:u.array(u.string()).default([]),max_file_size_bytes:u.number().default(10485760)}).passthrough(),ns=u.object({workspace_id:u.string(),organization_id:u.string(),workspace_name:u.string().optional(),user_id:u.string(),user_email:u.string().optional(),connector:u.string().optional(),connector_policies:u.object({max_response_chars:u.number(),timeout_ms:u.number()}).passthrough().optional(),file_policies:rs.optional()}).passthrough(),Bn=u.object({type:u.literal("tool_call"),job_id:u.string().min(1),device_id:u.string().min(1).optional(),workspace_id:u.string().optional(),tool:u.string().min(1),args:u.record(u.string(),u.unknown()).default({}),context:ns.optional()}).passthrough(),os=u.object({type:u.enum(["error","warning","info"]),description:u.string().min(1)}),Lt=u.object({type:u.literal("tool_result"),job_id:u.string().min(1),tool:u.string().min(1),ok:u.boolean(),result:u.unknown().optional(),error:u.unknown().optional(),messages:u.array(os).optional(),timing:u.object({execution_ms:u.number(),execution_sec:u.number()}).optional()}).strict().superRefine((o,e)=>{o.ok&&o.result===void 0&&e.addIssue({code:u.ZodIssueCode.custom,message:"tool_result ok=true should include result",path:["result"]}),!o.ok&&o.error===void 0&&e.addIssue({code:u.ZodIssueCode.custom,message:"tool_result ok=false should include error",path:["error"]})}),qn=u.object({type:u.literal("disconnect_notice"),code:u.number().int(),reason:u.string(),at:u.number()}).strict(),Wn=u.object({type:u.literal("promoted_active"),workspaceId:u.string().min(1)}).strict(),Jn=u.object({type:u.literal("demoted_standby"),workspaceId:u.string().min(1)}).strict(),Xl=u.union([Ni,Li,Ui,Hi,Xi,es,Bn,Lt,Qi,qn,Wn,Jn]),$t=u.union([Mi,Ki,Wi,Ji,zi,Zi,ts,Bn,Yi,qn,Wn,Jn]);import{z as v}from"zod";var is=v.object({device_id:v.string().optional(),device_name:v.string().optional(),platform:v.string().optional(),public_key_pem:v.string().min(1)}),td=v.object({device_code:v.string(),user_code:v.string(),verification_uri:v.string(),verification_uri_complete:v.string(),expires_in:v.number(),interval:v.number(),challenge_id:v.string(),challenge:v.string()}),rd=is.extend({enrollment_jws:v.string().min(1)}),nd=v.union([v.object({status:v.literal("pending"),needs_proof:v.boolean()}),v.object({status:v.literal("approved"),needs_proof:v.boolean()}),v.object({error:v.literal("access_denied")}),v.object({error:v.literal("expired_token")})]),od=v.object({device_code:v.string(),challenge_id:v.string(),challenge:v.string(),signature_b64:v.string()}),id=v.object({ok:v.literal(!0)}),sd=v.object({device_code:v.string()}),ad=v.object({access_token:v.string(),refresh_token:v.string(),token_type:v.literal("Bearer"),expires_in:v.number(),device_id:v.string()}),cd=v.object({device_id:v.string().min(1),refresh_token:v.string().min(1),ts:v.number().int(),nonce:v.string().min(8),sig:v.string().min(16)}),ss=v.object({ok:v.literal(!0),access_token:v.string().min(10),refresh_token:v.string().min(10),expires_in_sec:v.number().int().positive(),device_id:v.string().min(1)}),as=v.object({ok:v.literal(!1),error:v.object({code:v.string(),message:v.string()})}),ud=v.union([ss,as]);function Hn(o){return{ok:!0,data:o}}function xe(o){return{ok:!1,error:o}}var Gn={invalid_enrollment_token:"Enrollment key is invalid or could not be verified (wrong controller URL, bad signature, or server signing keys do not match the key issuer).",invalid_enrollment_scope:"Enrollment key does not match this device account or organization membership.",enrollment_jwt_expired:"This enrollment key file has expired (JWT). Issue a new key under Organization \u2192 Devices and update ENROLMENT_KEY / your .devicelink file.",enrollment_ticket_invalid:"Enrollment key could not be accepted (already used, expired, or out of sync). Issue a new key under Organization \u2192 Devices.",enrollment_ticket_already_used:"This enrollment key was already used (single-use). Issue a new key under Organization \u2192 Devices.",enrollment_ticket_expired:"This enrollment key has expired. Issue a new key under Organization \u2192 Devices.",server_misconfigured:"Server cannot verify enrollment keys (OAuth keys missing on controller).",enrollment_start_failed:"Device enrollment failed. See controller logs."};function cs(o){if(typeof o!="object"||o===null)return;let e=o;if(e.body&&typeof e.body=="object"&&e.body!==null&&"error"in e.body){let t=e.body.error;if(typeof t=="string")return t}if(o instanceof Error){let t=o.message.match(/"error"\s*:\s*"([^"]+)"/);if(t?.[1])return t[1]}}function Vn(o){let e=cs(o);return e&&Gn[e]?Gn[e]:e?`Enrollment failed (${e}). ${L(o)}`:L(o)}var Ve=class{timeout=null;interval=null;totalSec=0;remainingSec=0;stepSec=1;task=null;stepCb=null;setStep(e){return this.stepSec=Math.max(1,e),this}schedule(e,t){return this.task=e,this.totalSec=t,this.remainingSec=t,this}onStep(e){return this.stepCb=e,this}start(){return this.clear(),this.remainingSec<=0?(this.runTask(),this):(this.stepCb?.(this.stepSec,this.remainingSec),this.interval=setInterval(()=>{this.remainingSec-=this.stepSec,this.remainingSec<=0?this.runTask():this.stepCb?.(this.stepSec,this.remainingSec)},this.stepSec*1e3),this)}stop(){return this.clear(),this}restart(){return this.remainingSec=this.totalSec,this.start()}runTask(){if(this.clear(),this.task)try{this.task()}catch(e){throw new Error(`Scheduler task failed: ${e}`)}}clear(){this.timeout&&(clearTimeout(this.timeout),this.timeout=null),this.interval&&(clearInterval(this.interval),this.interval=null)}};import{access as zn,readFile as us}from"node:fs/promises";import{constants as Yn}from"node:fs";function ls(o){return(o.trim().split(".")[0]??"").startsWith("eyJ")}function Nr(){let o=process.env.ENROLMENT_KEY?.trim()??process.env.DLINK_ENROLMENT_KEY?.trim();return o?ls(o)?{kind:"inline",jws:o}:{kind:"path",path:o}:{kind:"none"}}function Xn(o){let e=o?.trim();if(e)return`file:${e}`;let t=Nr();return t.kind==="none"?"none":t.kind==="inline"?"ENROLMENT_KEY(inline JWS)":`file:${t.path}`}async function Qn(o){try{let t=(await us(o,"utf8")).trim();return t?{ok:!0,jws:t}:{ok:!1,error:"empty_file"}}catch(e){return{ok:!1,error:e instanceof Error?e.message:String(e)}}}async function Zn(o){let e=o?.trim();if(e)try{return await zn(e,Yn.R_OK),!0}catch{return!1}let t=Nr();if(t.kind==="inline")return t.jws.trim().length>0;if(t.kind==="path")try{return await zn(t.path,Yn.R_OK),!0}catch{return!1}return!1}async function eo(o){let e=o?.trim();if(e)return Qn(e);let t=Nr();if(t.kind==="none")return{ok:!1,error:"not_configured"};if(t.kind==="inline"){let r=t.jws.trim();return r?{ok:!0,jws:r}:{ok:!1,error:"empty_file"}}return Qn(t.path)}var jt=class{name="auth-orchestrator";phase="IDLE";connectionId=null;sessionId=null;accessToken;rejectedByConflict=!1;refreshGroupKey="auth:refresh";refreshCycle=0;refreshScheduler=null;refreshRetryScheduler=null;lastRefreshPlan;refreshInFlight;conflictTimer=null;pendingConflictRequest=null;signinStarted=!1;async onStart(e){e.notifier.info("Auth orchestrator started",{scope:"system"})}async onStop(e){this.clearTimers(),this.resetConnectionState(),e.notifier.info("Auth orchestrator stopped",{scope:"system"})}handlers(){return{"config:loaded":this.onEventConfigLoaded.bind(this),"config:cache:validated":this.onEventConfigCacheValidated.bind(this),"auth:signin:requested":this.onEventAuthSigninRequested.bind(this),"auth:signin:completed":async(e,t)=>{t.notifier.info("Auth: signin completed; requesting ws reconnect",{scope:"system"}),this.rejectedByConflict=!1,this.refreshRetryScheduler&&(this.refreshRetryScheduler.stop(),this.refreshRetryScheduler=null),this.ensureAccessExpPersisted(t,"signin"),await this.ensureRefreshScheduling(t,"signin"),t.bus.emit("auth:ws:reconnect",void 0)},"auth:signout:requested":async(e,t)=>{t.notifier.info("Signing out\u2026"),this.clearTimers(),this.accessToken=void 0;try{await t.engine.secrets.delete("refreshToken")}catch{}let n={...t.engine.configurationContext.configuration,tokens:void 0};t.engine.applyConfiguration(n),t.bus.emit("config:save:requested",n),this.resetConnectionState(),t.engine.setPhase("idle"),t.bus.emit("auth:tokens:updated",{accessToken:"",refreshToken:"",expSec:void 0}),t.bus.emit("ws:disconnect",{reason:"signout"}),t.notifier.success("Signed out successfully")},"auth:session_conflict:resolve":async(e,t)=>{this.sendSessionConflictResolve(t,e.choice)},"ws:connected":async(e,t)=>{this.resetConnectionState();let r=await t.engine.secrets.get("refreshToken"),n=!!r.ok&&!!r.value,i=!!this.accessToken;if(t.notifier.info(`Auth: ws connected; tokens access=${i} refresh=${n}`,{scope:"system"}),!i&&!n){t.notifier.warn("Auth: no tokens; run signin");return}await this.sendConnect(t)},"ws:rx":async(e,t)=>{this.onInbound(e.text,t)},"ws:disconnected":async(e,t)=>{if(t.notifier.warn(`Auth: ws disconnected code=${e.code} reason=${e.reason??"-"}`,{scope:"system"}),this.resetConnectionState(),e.code===4011){t.notifier.warn('Connection rejected - another device is active on this workspace. Run "signin" to try again.'),this.rejectedByConflict=!0,t.bus.emit("ws:disconnect",{reason:"REPLACEMENT_REJECTED"});return}if(e.code===4001){let r=await t.engine.secrets.get("refreshToken");if(!r.ok||!r.value){t.notifier.warn("Auth: token invalid and no refresh; please sign in"),await this.teardownTokens(t,"AUTH_REQUIRED");return}await this.requestRefresh(t,{force:!0,reason:"ws_4001"})&&t.bus.emit("auth:ws:reconnect",void 0)}}}}async onEventConfigLoaded(e,t){if(t.notifier.info("Initializing with cached configuration",{scope:"system"}),!e.device?.id){t.notifier.error("Device ID is not available");return}if(!e.device.keys?.public){t.notifier.error("Device public key is not available");return}t.bus.emit("config:cache:validated",void 0)}async onEventConfigCacheValidated(e,t){this.ensureAccessExpPersisted(t,"boot"),await this.ensureRefreshScheduling(t,"boot");let r=await t.engine.secrets.get("refreshToken"),n=!!r.ok&&!!r.value,i=!!this.accessToken,a=this.isAccessLikelyUsable(t);if(t.notifier.info(`Auth: cache validated; access=${i} refresh=${n} accessOk=${a}`,{scope:"system"}),!i&&!n){if(t.headless&&await Zn(t.engine.argv.enrollmentFile)){t.notifier.info("Headless: enrollment certificate present; starting device authorization (approve the pending request under Organization \u2192 Devices).",{scope:"system"}),t.bus.emit("auth:signin:requested",void 0);return}t.notifier.warn("Auth: not signed in; run signin");return}}isAccessLikelyUsable(e){let t=this.accessToken;if(!t)return!1;let n=e.engine.configurationContext.configuration?.tokens?.accessTokenExpSec??this.tryDecodeExp(t);if(!n)return!0;let i=Math.floor(Date.now()/1e3);return n>i}async waitForApproval(e,t,r){let n=Date.now()+t.expires_in*1e3;r.notifier.info("Waiting for approval... (streaming)",{scope:"system"});try{return await e.deviceAuthStream({deviceCode:t.device_code,timeoutMs:Math.max(1e3,n-Date.now()),signal:r.signal})}catch(i){let a=i instanceof Error?i.message:String(i);return r.notifier.error(`Failed to wait for approval: ${a}`),null}}async onEventAuthSigninRequested(e,t){if(this.signinStarted){t.notifier.warn("Sign-in already in progress",{scope:"system"});return}this.signinStarted=!0;try{let r=t.engine.configurationContext.configuration;if(!r.controlBaseUrl){t.notifier.error("Control base URL is not set");return}if(!r.device?.id){t.notifier.error("Device ID is not available");return}if(!r.device.keys?.public){t.notifier.error("Device public key is not available");return}let n=await t.engine.secrets.get("devicePrivateKey");if(!n.ok||!n.value){t.notifier.error("Device private key is not available (secret storage)");return}let i=n.value,a=r.device.name,s=process.platform,c=r.device.keys.public,l=tr(c),d=new Oe(r,t.notifier);t.notifier.info("Starting device authentication...");let p=await eo(t.engine.argv.enrollmentFile),f;if(p.ok){t.notifier.info(t.engine.argv.enrollmentFile?"Using enrollment key from --enrollment-file":"Using ENROLMENT_KEY / DLINK_ENROLMENT_KEY from environment",{scope:"system"});try{f=await d.deviceEnrollmentStart({device_id:r.device.id,device_name:a,platform:s,public_key_pem:c,enrollment_jws:p.jws})}catch(b){t.notifier.error(Vn(b),{scope:"system"});return}t.notifier.info("Approve this device in the dashboard: Organization \u2192 Devices (pending), or use the verification link below.",{scope:"system"})}else{if(p.error!=="not_configured"){t.notifier.error(p.error==="empty_file"?"Enrollment key is empty":`Could not load enrollment key: ${p.error}`);return}f=await d.deviceAuthStart({device_id:r.device.id,device_name:a,platform:s,public_key_pem:c})}t.notifier.info("DeviceLink sign-in"),t.notifier.info(`User code: ${f.user_code}`),t.notifier.info(`Device fingerprint: ${l}`),t.notifier.info(`Verification URL: ${f.verification_uri_complete}`);let g=await this.waitForApproval(d,f,t);if(!g)return;if("error"in g){if(g.error==="access_denied"){t.notifier.error("Sign-in denied in browser.");return}if(g.error==="expired_token"){t.notifier.error("Sign-in expired. Run `signin` again.");return}t.notifier.error(`Unexpected error: ${g.error}`);return}if(g.status!=="approved"){t.notifier.error(`Unexpected status: ${g.status}`);return}if(g.needs_proof){t.notifier.info("Sending proof-of-possession signature...",{scope:"system"});let b=Pr.createPrivateKey(i),T=Buffer.from(f.challenge,"base64url"),I=Pr.sign(null,T,b).toString("base64");await d.deviceAuthProve({device_code:f.device_code,challenge_id:f.challenge_id,challenge:f.challenge,signature_b64:I}),t.notifier.info("Proof-of-possession verified",{scope:"system"})}t.notifier.info("Fetching tokens...",{scope:"system"});let m=await d.deviceAuthToken({device_code:f.device_code}),h="tag";await t.engine.secrets.set("refreshToken",m.refresh_token),this.accessToken=m.access_token;let y=this.tryDecodeExp(m.access_token),k={...r,controlBaseUrl:r.controlBaseUrl,device:{...r.device,id:m.device_id,name:a,tag:h,keys:{public:c}},tokens:{...r.tokens??{},accessTokenExpSec:y??void 0}};t.engine.applyConfiguration(k),t.bus.emit("config:save:requested",k),t.bus.emit("auth:signin:completed",void 0),t.notifier.success("Signed in successfully \u2705")}catch(r){let n=ke(r);if(n&&["ECONNREFUSED","ENOTFOUND","ETIMEDOUT"].includes(n)){t.notifier.error(`Network error: ${L(r)}`);return}t.notifier.error(`Sign-in failed: ${L(r)}`)}finally{this.signinStarted=!1}}async sendConnect(e){let t=e.engine.configurationContext.configuration,r=t.device,n=await e.engine.secrets.get("devicePrivateKey");if(!r?.id||!n.ok||!n.value){e.notifier.warn("Cannot connect: device not configured");return}let i=n.value,a=await e.engine.secrets.get("refreshToken"),s=a.ok?a.value:void 0,c=Date.now(),l=Pr.randomBytes(16).toString("base64url"),d=rr({deviceId:r.id,ts:c,nonce:l,privateKeyPem:i}),p={type:"connect",device_id:r.id,agent_version:String(t.version??"1"),protocol_version:2,capabilities:["file_policies","context_v2"],ts:c,nonce:l,sig:d,platform:process.platform,hostname:r.name,device_tag:r.tag,access_token:this.accessToken,refresh_token:s,public_key_pem:r.keys.public};this.phase="CONNECT_SENT",e.notifier.info("Auth: sending connect",{scope:"system"}),this.send(e,p)}async sendSessionInit(e,t){let n=e.engine.configurationContext.configuration.device;if(!n?.id||!this.sessionId){e.notifier.error("Cannot send session_init: device ID or session ID not available");return}let i={type:"session_init",device_id:n.id,session_id:this.sessionId};this.phase="WAIT_SESSION_INIT_ACK",e.notifier.info("Auth: sending session_init",{scope:"system"}),this.send(e,i)}onInbound(e,t){let r;try{r=JSON.parse(e)}catch{t.notifier.error("Unexpected message format");return}let n=$t.safeParse(r);if(!n.success){let a=n.error.message;t.notifier.error(`Unexpected message format: ${a}`);return}let i=n.data;switch(i.type){case"connect_ack":t.bus.emit("auth:connect_ack",i),this.onConnectAck(t,i);return;case"session_init_ack":t.bus.emit("auth:session_init_ack",i),this.onSessionInitAck(t,i);return;case"disconnect_notice":this.onDisconnectNotice(t,i);return;case"session_conflict":t.bus.emit("auth:session_conflict",i),this.handleSessionConflict(t,i);return;case"quiesce":this.handleQuiesce(t,i);return;default:return}}async handleSessionConflict(e,t){this.conflictTimer&&(clearTimeout(this.conflictTimer),this.conflictTimer=null),this.pendingConflictRequest=t,e.notifier.warn(`Another device is already connected to this workspace. Type "replace" to disconnect it, or this connection will be rejected in ${Math.round(t.deadline_ms/1e3)}s.`);let r=Math.min(t.deadline_ms,15e3);this.conflictTimer=setTimeout(()=>{this.conflictTimer=null,this.pendingConflictRequest?.new_session_id===t.new_session_id&&(e.notifier.info("No response - keeping existing device connected."),this.sendSessionConflictResolve(e,"KEEP_OLD"))},r)}async handleQuiesce(e,t){if(e.notifier.warn("This device is being replaced by another connection. Finishing in-flight operations..."),e.engine.hasRunningJobs?.()??!1){let i=Math.min(t.deadline_ms-2e3,12e3),a=Date.now();for(;Date.now()-a<i&&(e.engine.hasRunningJobs?.()??!1);)await new Promise(s=>setTimeout(s,500))}let n=e.engine.hasRunningJobs?.()??!1?"BUSY":"IDLE";this.send(e,{type:"quiesce_ack",session_id:t.session_id,status:n}),e.notifier.info("Device replaced by new connection. Disconnecting.")}sendSessionConflictResolve(e,t){let r=this.pendingConflictRequest;r&&(this.conflictTimer&&(clearTimeout(this.conflictTimer),this.conflictTimer=null),this.pendingConflictRequest=null,this.send(e,{type:"session_conflict_resolve",device_id:r.device_id,new_session_id:r.new_session_id,choice:t}))}async onConnectAck(e,t){if(!t.ok){e.notifier.error(`Connect rejected: ${t.error.code} ${t.error.message}`),this.phase="IDLE";return}this.sessionId=t.session_id;let r=t.auth.access_token;if(e.notifier.info(`Auth: connect_ack ok auth.access=${r} next=[${t.next.join(",")}]`,{scope:"system"}),t.next.includes("refresh_access_token")){let n=await e.engine.secrets.get("refreshToken");if(!n.ok||!n.value){e.notifier.warn("Auth: server requested refresh but no refresh token; please sign in"),await this.teardownTokens(e,"NO_REFRESH_TOKEN");return}await this.requestRefresh(e,{force:!0,reason:"connect_ack_next"})?e.bus.emit("auth:ws:reconnect",void 0):e.notifier.warn("Auth: refresh failed; please sign in");return}if(t.next.includes("session_init")){await this.sendSessionInit(e,t);return}if(r==="invalid"||r==="missing"){e.notifier.warn("Auth: access token invalid; please sign in again"),await this.teardownTokens(e,"INVALID_ACCESS");return}e.notifier.warn("Auth: connect_ack had no next; staying idle",{scope:"system"})}onSessionInitAck(e,t){if(!t.ok){e.notifier.error(`Session init failed: ${t.error.code} ${t.error.message}`),this.phase="IDLE";return}this.sessionId=t.session_id,this.phase="READY",e.notifier.success(`Session ready (${t.mode})`),this.ensureRefreshScheduling(e,"manual"),t.next&&(t.next.includes("tools_publish")&&e.bus.emit("meta:data:requested:tools",void 0),t.next.includes("directories_publish")&&e.bus.emit("meta:data:requested:directories",void 0))}onDisconnectNotice(e,t){e.notifier.warn(`Server disconnect_notice: ${t.reason} (code=${t.code})`)}async requestRefresh(e,t){return this.refreshInFlight?this.refreshInFlight:(this.refreshInFlight=(async()=>{try{return await this.withTimeout(this.dispatchRefreshJob(e,t),6e4,"Refresh operation")}catch(r){throw r instanceof Error&&r.message.includes("timed out")&&e.notifier.error(`Auth: refresh timed out: ${r.message}`,{category:["token-refresh"],groupKey:"auth:refresh-failure"}),r}finally{this.refreshInFlight=void 0}})(),this.refreshInFlight)}async withTimeout(e,t,r){return Promise.race([e,new Promise((n,i)=>{setTimeout(()=>{i(new Error(`${r} timed out after ${t}ms`))},t)})])}async dispatchRefreshJob(e,t){let r=await e.engine.secrets.get("refreshToken");if(!r.ok||!r.value)return!1;if(!t.force){let s=await this.shouldRefreshNow(e);if(!s.ok)return e.notifier.info(`Auth: skip refresh (${s.reason})`,{scope:"system",category:["token-refresh"]}),!1}e.notifier.info(`Auth: dispatch refresh job (${t.reason})`,{scope:"system",category:["token-refresh"]});let n=vn(new Tt(t.reason)),i=await e.engine.dispatch(n);if(e.notifier.info(`Auth: refresh job dispatch completed (status=${i.status})`,{scope:"system",category:["token-refresh"]}),i.status!=="success"){let s=i.error?.message??"Unknown error";e.notifier.error(`Auth: refresh job failed: ${s}`,{category:["token-refresh"],groupKey:"auth:refresh-failure"});let c=i.output;return c?.errorCode&&_r.includes(c.errorCode)?(await this.teardownTokens(e,c.errorCode),!1):(this.scheduleRefreshRetry(e,t.reason,c?.retryAfterSec),!1)}let a=i.output;if(!a.ok||!a.accessToken){let s=a.error??"Unknown refresh failure";return e.notifier.error(`Auth: refresh failed: ${s}`,{category:["token-refresh"],groupKey:"auth:refresh-failure"}),a.errorCode&&_r.includes(a.errorCode)?(await this.teardownTokens(e,a.errorCode),!1):(this.scheduleRefreshRetry(e,t.reason,a.retryAfterSec),!1)}return this.accessToken=a.accessToken,this.persistTokens(e,a.accessToken,a.expiresInSec),await this.ensureRefreshScheduling(e,"manual"),e.bus.emit("auth:tokens:updated",{accessToken:a.accessToken,refreshToken:a.refreshToken??"",expSec:e.engine.configurationContext.configuration.tokens?.accessTokenExpSec}),!0}scheduleRefreshRetry(e,t,r){this.refreshRetryScheduler&&this.refreshRetryScheduler.stop();let n=this.computeRefreshRetryMs(e,t,r),i=Math.max(1,Math.ceil(n/1e3));this.refreshRetryScheduler=new Ve().schedule(()=>{this.requestRefresh(e,{force:!0,reason:"retry"}).then(a=>{a&&e.bus.emit("auth:ws:reconnect",void 0)})},i).start()}computeRefreshRetryMs(e,t,r){let n=e.engine.configurationContext.loader,i=n.getConfigValue("DL_ACCESS_REFRESH_RETRY_AFTER_MIN_SEC",{defaultValue:1}),a=n.getConfigValue("DL_ACCESS_REFRESH_RETRY_AFTER_MAX_SEC",{defaultValue:300}),s=n.getConfigValue("DL_ACCESS_REFRESH_RETRY_BASE_SEC",{defaultValue:2}),c=n.getConfigValue("DL_ACCESS_REFRESH_RETRY_JITTER_SEC",{defaultValue:1}),l=n.getConfigValue("DL_ACCESS_REFRESH_RETRY_MAX_SEC",{defaultValue:30});if(typeof r=="number"&&Number.isFinite(r)&&r>=0){let g=Math.min(a*1e3,Math.max(i*1e3,r*1e3)),m=Math.max(1,Math.ceil(g/1e3));return e.notifier.warn(`Auth: refresh retry scheduled in ${m}s (honoring Retry-After, ${t})`,{scope:"system",category:["token-refresh"],groupKey:"auth:refresh-retry"}),g}let p=Math.min(l*1e3,s*1e3+Math.floor(Math.random()*(c+1))*1e3),f=Math.max(1,Math.ceil(p/1e3));return e.notifier.warn(`Auth: refresh retry scheduled in ${f}s (${t})`,{scope:"system",category:["token-refresh"],groupKey:"auth:refresh-retry"}),p}async ensureRefreshScheduling(e,t){let r=await e.engine.secrets.get("refreshToken");!r.ok||!r.value||(this.accessToken&&!e.engine.configurationContext.configuration.tokens?.accessTokenExpSec&&this.ensureAccessExpPersisted(e,t),this.refreshRetryScheduler&&(this.refreshRetryScheduler.stop(),this.refreshRetryScheduler=null),this.scheduleSilentRefresh(e,t))}async scheduleSilentRefresh(e,t){let r=await e.engine.secrets.get("refreshToken");if(!r.ok||!r.value)return;this.refreshScheduler&&this.refreshScheduler.stop();let n=e.engine.configurationContext.loader,i=n.getConfigValue("DL_ACCESS_REFRESH_MIN_DELAY_SEC",{defaultValue:2}),a=e.engine.configurationContext.configuration,s=this.accessToken,c=a.tokens?.accessTokenExpSec??(s?this.tryDecodeExp(s):null),l=c??null,d=this.getRefreshGroupKey(e);if(this.lastRefreshPlan&&this.lastRefreshPlan.exp===l&&this.lastRefreshPlan.groupKey===d)return;this.lastRefreshPlan={exp:l,groupKey:d};let p=b=>{let T=new Ve().schedule(()=>{this.requestRefresh(e,{force:b.force,reason:"timer"}).then(_=>{_&&e.notifier.success("Auth: refresh completed",{scope:"system",groupKey:d,category:["token-refresh"]})})},b.delaySec);b.log&&(T=T.onStep((_,I)=>b.log(I))),this.refreshScheduler=T.start()};if(!c){e.headless||e.notifier.info(`Auth: no exp; scheduling refresh in ${i}s [${t}]`,{scope:"system",groupKey:d,category:["token-refresh"]}),p({delaySec:i,force:!0});return}let f=Math.floor(Date.now()/1e3),g=n.getConfigValue("DL_ACCESS_REFRESH_MARGIN_SEC",{defaultValue:5}),m=n.getConfigValue("DL_ACCESS_REFRESH_JITTER_SEC",{defaultValue:2}),h=m>0?Math.floor(Math.random()*(m+1)):0,y=c-g-h,k=Math.max(i,y-f);e.headless||e.notifier.info(`Auth: refresh scheduled in ${k}s (exp=${c} now=${f} margin=${g} jitter=${h}) [${t}]`,{scope:"system",groupKey:d,category:["token-refresh"]}),p({delaySec:k,force:!1,log:b=>{e.headless||e.notifier.info(`Auth: refresh scheduled in ${b}s (exp=${c} now=${f} margin=${g} jitter=${h}) [${t}]`,{scope:"system",groupKey:d,category:["token-refresh"]})}})}getRefreshGroupKey(e){return this.refreshCycle++,`${this.refreshGroupKey}:${this.refreshCycle}`}async shouldRefreshNow(e){let t=await e.engine.secrets.get("refreshToken");if(!t.ok||!t.value)return{ok:!1,reason:"no_refresh_token"};let r=e.engine.configurationContext.configuration,n=this.accessToken,i=r.tokens?.accessTokenExpSec??(n?this.tryDecodeExp(n):null);if(!i)return{ok:!0,reason:"no_exp_assume_refresh"};let a=Math.floor(Date.now()/1e3),c=e.engine.configurationContext.loader.getConfigValue("DL_ACCESS_REFRESH_MARGIN_SEC",{defaultValue:300}),l=i-a;return l<=c?{ok:!0,reason:`within_margin remaining=${l}s margin=${c}s`}:{ok:!1,reason:`too_early remaining=${l}s margin=${c}s`}}ensureAccessExpPersisted(e,t){let r=e.engine.configurationContext.configuration,n=this.accessToken,i=r.tokens?.accessTokenExpSec;if(!n||typeof i=="number"&&Number.isFinite(i))return;let a=this.tryDecodeExp(n);if(!a)return;let s={...r,tokens:{...r.tokens??{},accessTokenExpSec:a}};e.engine.applyConfiguration(s),e.bus.emit("config:save:requested",s),e.notifier.info(`Auth: derived accessTokenExpSec=${a} (${t})`,{scope:"system"})}persistTokens(e,t,r){let n=e.engine.configurationContext.configuration,i=this.tryDecodeExp(t),a=typeof r=="number"&&Number.isFinite(r)?Math.floor(Date.now()/1e3)+r:void 0,s={...n,tokens:{...n.tokens??{},accessTokenExpSec:i??a}};e.engine.applyConfiguration(s),e.bus.emit("config:save:requested",s),e.notifier.info(`Auth: tokens updated exp=${s.tokens?.accessTokenExpSec??"-"}`,{scope:"system"})}async teardownTokens(e,t){this.accessToken=void 0;let n={...e.engine.configurationContext.configuration,tokens:void 0};e.engine.applyConfiguration(n),e.bus.emit("config:save:requested",n),this.clearTimers(),this.resetConnectionState(),e.bus.emit("auth:signout:requested",void 0),e.notifier.warn(`Auth: signed out (${t}). Please run signin.`)}tryDecodeExp(e){try{let t=ds(e);return typeof t.exp=="number"?t.exp:null}catch{return null}}send(e,t){e.bus.emit("ws:tx",{text:JSON.stringify(t)})}resetConnectionState(){this.connectionId=null,this.sessionId=null,this.phase="IDLE"}clearTimers(){this.refreshScheduler&&(this.refreshScheduler.stop(),this.refreshScheduler=null),this.refreshRetryScheduler&&(this.refreshRetryScheduler.stop(),this.refreshRetryScheduler=null),this.conflictTimer&&clearTimeout(this.conflictTimer),this.conflictTimer=null,this.pendingConflictRequest=null}};import Mr from"node:fs/promises";import Lr from"node:path";var J=class{config;constructor(e){this.config=e}async execute(e){try{let t=await this.executeTool(e);return Hn(t)}catch(t){if(this.isToolError(t))return xe(t);let r=t instanceof Error?t.message:"An unexpected error occurred";return xe({code:"INTERNAL",message:r})}}isToolError(e){if(typeof e!="object"||e===null||!("code"in e)||!("message"in e))return!1;let t=e;return typeof t.code=="string"&&typeof t.message=="string"}};import oo from"node:path";import to from"node:fs";function ps(o){let e=o.replace(/\\/g,"/");return e.includes("/secrets/")||e.startsWith("secrets/")?{shouldIgnore:!0,reason:"denylist:secrets",matchedRule:"secrets/"}:e.includes("/.env")||e.startsWith(".env")?{shouldIgnore:!0,reason:"denylist:env",matchedRule:".env"}:e.includes("/id_rsa")||e.endsWith("/id_rsa")?{shouldIgnore:!0,reason:"denylist:ssh",matchedRule:"id_rsa"}:e.includes("/id_ed25519")||e.endsWith("/id_ed25519")?{shouldIgnore:!0,reason:"denylist:ssh",matchedRule:"id_ed25519"}:e.endsWith(".pem")?{shouldIgnore:!0,reason:"denylist:pem",matchedRule:"*.pem"}:e.endsWith(".key")?{shouldIgnore:!0,reason:"denylist:key",matchedRule:"*.key"}:e.endsWith(".p12")?{shouldIgnore:!0,reason:"denylist:p12",matchedRule:"*.p12"}:e.endsWith(".pfx")?{shouldIgnore:!0,reason:"denylist:pfx",matchedRule:"*.pfx"}:null}var ro=[{pattern:"node_modules",type:"exact"},{pattern:".git",type:"exact"},{pattern:"dist",type:"exact"},{pattern:"build",type:"exact"},{pattern:".next",type:"exact"},{pattern:".cache",type:"exact"},{pattern:".DS_Store",type:"exact"},{pattern:"Thumbs.db",type:"exact"},{pattern:"*.tmp",type:"glob"},{pattern:"*.log",type:"glob"},{pattern:".env*",type:"glob"}];function fs(o,e,t){let r=o.directories.find(a=>a.id===e);if(!r)return ro;let n=[],i=oo.join(r.path,".devicelinkignore");if(to.existsSync(i))try{let s=to.readFileSync(i,"utf-8").split(`
6
- `).map(c=>c.trim()).filter(c=>c&&!c.startsWith("#"));for(let c of s)c.includes("*")||c.includes("?")?n.push({pattern:c,type:"glob"}):c.startsWith("/")?n.push({pattern:c.slice(1),type:"prefix"}):n.push({pattern:c,type:"exact"})}catch(a){console.warn(`[ignore] Failed to read .devicelinkignore: ${a}`)}return n.push(...ro),n}function z(o,e,t,r,n){let i=ps(o);if(i)return i;if(n&&!e){let l=oo.extname(o).toLowerCase();if(l&&n.blocked_extensions?.includes(l))return{shouldIgnore:!0,reason:"policy:blocked_extension",matchedRule:l};if(n.allowed_extensions&&n.allowed_extensions.length>0&&l&&!n.allowed_extensions.includes(l))return{shouldIgnore:!0,reason:"policy:not_in_allowlist",matchedRule:l};if(n.blocked_patterns){for(let d of n.blocked_patterns)if(no(o,d,!1))return{shouldIgnore:!0,reason:"policy:blocked_pattern",matchedRule:d}}}let a=fs(r,t.directory_id,t.workspace_id),s=o.replace(/\\/g,"/"),c=s.split("/").filter(Boolean);for(let l of a){let d=!1;if(l.type==="exact"?d=c.some(p=>p===l.pattern):l.type==="prefix"?d=s.startsWith(l.pattern):l.type==="glob"&&(!l.pattern.includes("/")&&!l.pattern.includes("*")&&!l.pattern.includes("?")?d=c.some(p=>p.toLowerCase()===l.pattern.toLowerCase()):d=no(s,l.pattern,e)),d)return{shouldIgnore:!0,matchedRule:l.pattern}}return{shouldIgnore:!1}}function no(o,e,t){let r="^",n=0;for(;n<e.length;){let i=e[n];i==="*"?n+1<e.length&&e[n+1]==="*"?(r+=".*",n+=2):(r+="[^/]*",n++):i==="?"?(r+="[^/]",n++):"\\^$|.+(){}[]".includes(i)?(r+="\\"+i,n++):(r+=i,n++)}r+="$";try{return new RegExp(r,"i").test(o)}catch{return o.toLowerCase().includes(e.toLowerCase())}}import Fr from"node:fs/promises";import re from"node:path";function io(o){let e=(o??"").trim();if(!e)return".";if(e.includes("\0"))throw A("INVALID_INPUT","Path contains null byte.");if(e.startsWith("/"))throw A("PATH_ESCAPE","Absolute paths are not allowed. Use a path relative to the root.",{hint:'Use relative paths like "src/file.ts" or "." for the root directory. Paths must be relative to the configured root.'});if(/^[a-zA-Z]:[\\/]/.test(e)||e.startsWith("\\\\")||e.startsWith("//"))throw A("PATH_ESCAPE","Absolute/drive/UNC paths are not allowed. Use a path relative to the root.",{hint:'Use relative paths like "src/file.ts" or "." for the root directory. Do not use drive letters (C:) or UNC paths (\\\\server).'});let t=e.replace(/\\/g,"/"),r=re.posix.normalize(t);if(r===".."||r.startsWith("../"))throw A("PATH_ESCAPE","Path escapes the root.",{hint:'Paths cannot use ".." to escape the configured root directory. Use paths relative to the root, like "src/file.ts" or "subdir/file.ts".'});if(/^[a-zA-Z]+:\/\//.test(r))throw A("INVALID_INPUT","URL-style paths are not allowed.",{hint:'Paths cannot use URL schemes (like "http://" or "file://"). Use simple relative paths like "src/file.ts".'});return r===""?".":r}async function X(o,e){let t=io(e),r=re.resolve(o),n=await Fr.realpath(r),i=re.resolve(n,t==="."?"":t),a=re.relative(n,i);if(a.startsWith("..")||re.isAbsolute(a))throw A("PATH_ESCAPE","Path escapes the root.",{hint:"The resolved path escapes the configured root directory. Ensure the path stays within the root boundaries."});try{let s=await Fr.realpath(i),c=re.relative(n,s);if(c.startsWith("..")||re.isAbsolute(c))throw A("PATH_ESCAPE","Symlink/junction escape detected.",{hint:"A symlink or junction in the path resolves outside the configured root directory. Ensure all symlinks stay within the root boundaries."});return{absTarget:s,rel:t}}catch(s){if(ke(s)!=="ENOENT")throw s;let c=t.split("/").filter(Boolean);for(let l=c.length;l>0;l--){let d=c.slice(0,l).join("/"),p=c.slice(l).join("/"),f=re.resolve(n,d);try{let g=await Fr.realpath(f),m=re.relative(n,g);if(m.startsWith("..")||re.isAbsolute(m))throw A("PATH_ESCAPE","Symlink/junction escape detected.",{hint:"A symlink or junction in the path resolves outside the configured root directory. Ensure all symlinks stay within the root boundaries."});return{absTarget:p?re.join(g,p):g,rel:t}}catch(g){if(ke(g)==="ENOENT")continue;throw g}}throw A("NOT_FOUND","Path not found.",{hint:"The specified path does not exist. Verify the path is correct and that the file or directory exists within the configured root."})}}function F(o){return{workspace_id:o.workspace_id,device_id:o.device_id,directory_id:o.directory_id,path:io(o.path)}}function K(o,e){let t=String(ke(o)??"");return t==="ENOENT"?$e("NOT_FOUND","Path not found.",{fs_code:t}):t==="ENOTDIR"?$e("NOT_A_DIRECTORY","Path is not a directory.",{fs_code:t}):t==="EISDIR"?$e("NOT_A_FILE","Path is a directory, not a file.",{fs_code:t}):t==="EACCES"||t==="EPERM"?$e("PERMISSION_DENIED","Permission denied.",{fs_code:t}):$e("INTERNAL",e,{fs_code:t})}function A(o,e,t){return $e(o,e,t)}function $e(o,e,t){return{code:o,message:e,details:t}}function G(o,e){if(Array.isArray(o.directories)&&o.directories.length===0)throw A("UNKNOWN_DIRECTORY","No directories configured.",{hint:"No directories configured. This may happen if the agent was restarted with different directory configuration. If you have only one directory configured, it will be used automatically.",requested_directory_id:e,available_directory_ids:o.directories.map(r=>r.id),available_paths:o.directories.map(r=>r.path),directory_count:o.directories.length});let t=o.directories.find(r=>r.id===e);if(!t&&o.directories.length===1&&(t=o.directories[0]),!t){let r=o.directories.map(a=>a.id).join(", ")||"none",n=o.directories.map(a=>a.path).join(", ")||"none",i=e===void 0?"undefined (missing)":String(e);throw A("UNKNOWN_DIRECTORY",`Unknown directory_id: ${i}`,{hint:`The directory_id "${i}" does not match any configured directory. Available directory IDs: ${r}. Available paths: ${n}. This may happen if the agent was restarted with different directory configuration. If you have only one directory configured, it will be used automatically.`,requested_directory_id:e,available_directory_ids:o.directories.map(a=>a.id),available_paths:o.directories.map(a=>a.path),directory_count:o.directories.length})}return t}function M(o,e,t,r,n){let i={operation:o,path:e.path||".",directory_id:e.directory_id,workspace_id:e.workspace_id,device_id:e.device_id,success:t,error:r?r.substring(0,200):void 0,metadata:n,timestamp:Date.now()},a=`[FS_AUDIT] ${o} ${t?"SUCCESS":"FAILED"} directory=${i.directory_id} path=${i.path}${i.workspace_id?` workspace=${i.workspace_id}`:""}${i.device_id?` device=${i.device_id}`:""}${i.error?` error=${i.error}`:""}${n?` ${JSON.stringify(n)}`:""}`}function hs(o,e,t){let r=[...o],n=t==="asc"?1:-1;return r.sort((i,a)=>{if(e==="name")return n*i.name.localeCompare(a.name);if(e==="mtime"){let s=i.mtime??0,c=a.mtime??0;return n*(s-c)}if(e==="size"){let s=i.size??0,c=a.size??0;return n*(s-c)}return 0}),r}function ms(o,e){return e==="all"?o:e==="files"?o.filter(t=>t.type==="file"||t.type==="symlink"):e==="dirs"?o.filter(t=>t.type==="dir"):o}var Kt=class extends J{async executeTool(e){let t=e.directory_id,r=G(this.config,t),{absTarget:n,rel:i}=await X(r.path,e.path),a=Math.min(Math.max(e.limit??200,1),1e3),s=Math.max(e.offset??0,0),c=e.include_hidden??!1,l=e.only??"all",d=e.sort??"name",p=e.order??"asc",f=e.detail??"stat";try{if(!(await Mr.stat(n)).isDirectory()){let C=F({workspace_id:e.workspace_id,device_id:e.device_id,directory_id:t,path:i});throw M("list",C,!1,"Path is not a directory."),A("NOT_A_DIRECTORY","Path is not a directory.",{path:i,hint:"The path must point to a directory, not a file. Use fs_read to read files, or provide a directory path for listing."})}}catch(S){if(S?.code&&S?.message&&typeof S?.code=="string"){let C=F({workspace_id:e.workspace_id,device_id:e.device_id,directory_id:t,path:i});throw M("list",C,!1,S.message),S}throw K(S,"Failed to list directory.")}let g;try{g=await Mr.readdir(n,{withFileTypes:!0})}catch(S){throw K(S,"Failed to read directory.")}let m=c?g:g.filter(S=>!S.name.startsWith(".")),h=[],y=0,k={total_entries:0,files:0,dirs:0,symlinks:0,hidden:0,ignored:0,total_file_bytes:0},b=new Map,T=e.context?e.context.file_policies:void 0,_={directory_id:t,workspace_id:e.workspace_id,directory_path:r.path};for(let S of m){let C=Lr.join(n,S.name),pe=(i==="."?S.name:Lr.posix.join(i,S.name)).replace(/\\/g,"/"),Z="other";S.isDirectory()?Z="dir":S.isFile()?Z="file":S.isSymbolicLink()&&(Z="symlink");let ot=S.name.startsWith(".");if(z(pe,S.isDirectory(),_,this.config,T).shouldIgnore){y++,k.ignored++;continue}k.total_entries++,Z==="file"?k.files++:Z==="dir"?k.dirs++:Z==="symlink"&&k.symlinks++,ot&&k.hidden++;let it,Qr;if(f==="stat"||f==="digest")try{let at=await Mr.stat(C);at.isFile()&&(it=at.size,k.total_file_bytes=(k.total_file_bytes??0)+at.size),Qr=Math.floor(at.mtimeMs/1e3)}catch{}let st=Z==="file"?Lr.extname(S.name).slice(1):void 0;st&&(f==="stat"||f==="digest")&&b.set(st,(b.get(st)??0)+1),h.push({name:S.name,path:pe,type:Z,size:it,mtime:Qr,ext:st,hidden:ot,ignored:!1})}f==="digest"&&b.size>0&&(k.by_ext=Array.from(b.entries()).map(([S,C])=>({ext:S,count:C})).sort((S,C)=>C.count-S.count).slice(0,10));let I=ms(h,l),E=hs(I,d,p),w=E.slice(s,s+a),x=s+w.length,O=x<E.length,V={count:w.length,limit:a,offset:s,has_more:O,next_offset:O?x:void 0},j=F({workspace_id:e.workspace_id,device_id:e.device_id,directory_id:t,path:i}),D={items:w,meta:V,context:j,filtered:{ignored_count:y}};return f==="digest"&&(D.summary=k),M("list",j,!0,void 0,{file_count:w.length}),D}};import so from"node:fs/promises";var je=class extends J{async executeTool(e){let t=e.directory_id,r=G(this.config,t),{absTarget:n,rel:i}=await X(r.path,e.path),a=e.context?e.context.file_policies:void 0,s={directory_id:t,workspace_id:e.workspace_id,directory_path:r.path},c=z(i,!1,s,this.config,a);if(c.shouldIgnore){let m=F({workspace_id:e.workspace_id,device_id:e.device_id,directory_id:t,path:i});throw M("read",m,!1,`Access denied: ${c.reason||c.matchedRule||"blocked"}`),A("PERMISSION_DENIED",`Access denied: ${c.reason||c.matchedRule||"blocked"}`,{path:i,reason:c.reason})}let l=Math.max(e.offset??0,0),d=Math.min(Math.max(e.limit??4e4,1),2e5),p=e.encoding??"utf-8",f;try{f=await so.stat(n)}catch(m){let h=F({workspace_id:e.workspace_id,device_id:e.device_id,directory_id:t,path:i});throw M("read",h,!1,K(m,"Failed to stat file.").message),K(m,"Failed to stat file.")}if(!f.isFile()){let m=F({workspace_id:e.workspace_id,device_id:e.device_id,directory_id:t,path:i});throw M("read",m,!1,"Path is not a file."),A("NOT_A_FILE","Path is not a file.",{path:i,hint:"The path must point to a file, not a directory. Use fs_list or fs_tree to explore directories, or provide a file path to read."})}let g;try{g=await so.open(n,"r")}catch(m){let h=F({workspace_id:e.workspace_id,device_id:e.device_id,directory_id:t,path:i});throw M("read",h,!1,K(m,"Failed to open file.").message),K(m,"Failed to open file.")}try{let m=Buffer.alloc(d),{bytesRead:h}=await g.read(m,0,d,l),k=m.subarray(0,h).toString(p),b=l+h,T=h<d,_=F({workspace_id:e.workspace_id,device_id:e.device_id,directory_id:t,path:i});return M("read",_,!0,void 0,{bytes_read:h}),{content:k,bytes_read:h,next_offset:b,eof:T,context:_}}catch(m){let h=F({workspace_id:e.workspace_id,device_id:e.device_id,directory_id:t,path:i});throw M("read",h,!1,m?.message||String(m)),m}finally{await g.close()}}};import $r from"node:fs/promises";var Ut=class extends J{async executeTool(e){let t=e.directory_id,r=G(this.config,t),{absTarget:n,rel:i}=await X(r.path,e.path),a=!1,s;try{s=await $r.readlink(n),a=!0}catch(p){if(p?.code==="ENOENT")throw K(p,"Path not found.")}let c;try{c=a?await $r.stat(n):await $r.lstat(n)}catch(p){throw K(p,"Failed to stat path.")}let l="other";c.isDirectory()?l="dir":c.isFile()?l="file":a&&(l="symlink");let d=F({workspace_id:e.workspace_id,device_id:e.device_id,directory_id:t,path:i});return M("info",d,!0),{type:l,size:c.isFile()?c.size:void 0,mtime:Math.floor(c.mtimeMs/1e3),ctime:Math.floor(c.ctimeMs/1e3),atime:Math.floor(c.atimeMs/1e3),is_symlink:a,target:a?s:void 0,context:d}}};import Kr from"node:fs/promises";import ae from"node:path";import jr from"node:fs/promises";import ao from"node:path";function gs(o,e){let t="^",r=0;for(;r<e.length;){let n=e[r];if(n==="*")r+1<e.length&&e[r+1]==="*"?(t+=".*",r+=2):(t+="[^/]*",r++);else if(n==="?")t+="[^/]",r++;else if(n==="{"){let i=e.indexOf("}",r);if(i===-1)t+="\\{",r++;else{let a=e.substring(r+1,i).split(",");t+="("+a.map(s=>s.trim().replace(/[.*+?^${}()|[\]\\]/g,"\\$&")).join("|")+")",r=i+1}}else"\\^$|.+()[]".includes(n)?(t+="\\"+n,r++):(t+=n,r++)}t+="$";try{return new RegExp(t,"i").test(o)}catch{return!1}}async function ys(o,e,t,r,n,i,a,s){let c;try{c=await jr.readFile(o,"utf-8")}catch{return 0}let l=c.split(/\r?\n/),d=0,p;if(r==="regex")try{p=new RegExp(t,n?"g":"gi")}catch{let f=t.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");p=new RegExp(f,n?"g":"gi")}else{let f=t.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");p=new RegExp(f,n?"g":"gi")}for(let f=0;f<l.length&&d<a;f++){let g=l[f];if(p.test(g)){p.lastIndex=0;let m=Math.max(0,f-i),h=Math.min(l.length-1,f+i),y=l.slice(m,h+1);s.push({path:e,line:f+1,preview:g.trim(),snippet:y}),d++}}return d}async function co(o,e,t,r,n,i,a,s,c,l,d,p,f,g,m){if(d.value>=c||p.length>=l)return;let h;try{h=await jr.readdir(o,{withFileTypes:!0})}catch{return}for(let y of h){if(d.value>=c||p.length>=l)break;let k=ao.join(o,y.name),b=e==="."?y.name:ao.posix.join(e,y.name);if(z(b,y.isDirectory(),g,m).shouldIgnore){f.value++;continue}if(y.isFile()){if(i&&!gs(b,i))continue;d.value++,await ys(k,b,r,n,a,s,l-p.length,p)}else y.isDirectory()&&await co(k,b,t,r,n,i,a,s,c,l,d,p,f,g,m)}}var Ke=class extends J{async executeTool(e){let t=e.directory_id,r=G(this.config,t),n=e.query;if(!n||n.length===0)throw A("INVALID_INPUT","Query is required.",{hint:'Query should be a search pattern to find in file contents. Use a substring (default) or regex pattern (with mode="regex"). Examples: "function", "import.*from", "TODO"'});let i=e.mode??"substring",a=e.file_glob,s=Math.min(Math.max(e.max_files??2e3,1),1e4),c=Math.min(Math.max(e.max_matches??200,1),1e3),l=Math.min(Math.max(e.context_lines??2,0),10),d=e.case_sensitive??!1,{absTarget:p,rel:f}=await X(r.path,e.path??".");if(f!==".")try{if(!(await jr.stat(p)).isDirectory())throw A("NOT_A_DIRECTORY","Grep path is not a directory.",{path:f,hint:'The grep path must be a directory. Grep searches within directories recursively. Use "." to search from the root, or provide a directory path.'})}catch(b){throw b?.code&&b?.message&&typeof b?.code=="string"?b:K(b,"Failed to stat grep path.")}let g={directory_id:t,workspace_id:e.workspace_id,directory_path:r.path},m=[],h={value:0},y={value:0};await co(p,f,r.path,n,i,a,d,l,s,c,h,m,y,g,this.config);let k=F({workspace_id:e.workspace_id,device_id:e.device_id,directory_id:t,path:f});return M("grep",k,!0,void 0,{file_count:h.value,match_count:m.length}),{matches:m,meta:{files_scanned:h.value,matches:m.length,truncated:h.value>=s||m.length>=c},context:k,filtered:{ignored_count:y.value}}}};import bs from"lodash/isEmpty.js";var Ue=class extends J{async executeTool(e){let t=e.directory_id,r=G(this.config,t),n=e.default_limit??12e3,i=e.default_encoding??"utf-8",a=[];if(e.items&&Array.isArray(e.items)&&(a=e.items),bs(a))throw A("INVALID_INPUT","fs_read_many expects { items: [{path,...}] }",{receivedKeys:Object.keys(e??{}),hint:'Provide an "items" array with at least one object containing a "path" property. Each item can optionally include "offset", "limit", and "encoding". Example: { items: [{ path: "file1.ts" }, { path: "file2.ts", limit: 5000 }] }'});let s=e.context?e.context.file_policies:void 0,c=[],l={directory_id:t,workspace_id:e.workspace_id,directory_path:r.path},d=new je(this.config);for(let f of a)try{let g=z(f.path,!1,l,this.config,s);if(g.shouldIgnore){c.push({path:f.path,content:"",bytes_read:0,truncated:!1,error:{code:"PERMISSION_DENIED",message:`Access denied: ${g.reason||g.matchedRule||"blocked"}`}});continue}let m={directory_id:t,path:f.path,workspace_id:e.workspace_id,device_id:e.device_id,offset:f.offset??0,limit:f.limit??n,encoding:f.encoding??i},h=await d.execute(m);if(!h.ok){c.push({path:f.path,content:"",bytes_read:0,truncated:!1,error:{code:h.error.code,message:h.error.message}});continue}c.push({path:f.path,content:h.data.content,bytes_read:h.data.bytes_read,truncated:!h.data.eof})}catch(g){let m=g,h=m?.code??"INTERNAL",y=m?.message??g?.message??String(g);c.push({path:f.path,content:"",bytes_read:0,truncated:!1,error:{code:h,message:y}})}let p=F({workspace_id:e.workspace_id,device_id:e.device_id,directory_id:t,path:"."});return M("read_many",p,!0,void 0,{file_count:c.length}),{files:c,context:p}}};function uo(o,e,t){if(!(e.length>=t)){if(o.type==="file"){e.push(o.path);return}if(o.children)for(let r of o.children){if(e.length>=t)break;uo(r,e,t)}}}function vs(o){let e=t=>{let r=t.toLowerCase(),n=r.split("/");return r==="readme.md"||r.endsWith("/readme.md")||r.includes("/readme.md")?0:r.endsWith(".md")?1:r.endsWith("package.json")||r.endsWith("pyproject.toml")||r.endsWith("go.mod")||r.endsWith("cargo.toml")||r.endsWith("pom.xml")||r.endsWith("build.gradle")?2:r.includes("dockerfile")||r.includes("compose")||r.endsWith(".env.example")?3:n.includes("config")||n.includes("conf")||n.includes("settings")?4:n.includes("ci")||n.includes(".github")||n.includes(".gitlab")||n.includes("jenkins")||r.includes("workflow")?5:r.endsWith(".ts")||r.endsWith(".tsx")||r.endsWith(".js")||r.endsWith(".jsx")||r.endsWith(".py")||r.endsWith(".go")||r.endsWith(".rs")||r.endsWith(".java")?6:7};return[...o].sort((t,r)=>{let n=e(t),i=e(r);return n!==i?n-i:t.localeCompare(r)})}function lo(o,e,t,r){let n={path:o.path,name:o.name,type:o.type,depth:e,size:o.size,mtime:o.mtime,ext:o.ext,hidden:o.hidden,ignored:o.ignored};if(r?.has(o.path)?n.error=r.get(o.path):o.type==="other"&&o.name==="<readdir_error>"&&(n.error={code:"INTERNAL",message:"Failed to read directory"}),t.push(n),o.children)for(let i of o.children)lo(i,e+1,t,r)}async function po(o,e,t,r,n,i,a,s,c,l,d,p,f,g,m,h,y){if(s.value>=a)return m.reason="MAX_ENTRIES",null;if(t>r)return m.reason="MAX_DEPTH",null;let k=e==="."?".":e.replace(/\\/g,"/"),b;try{b=await Kr.lstat(o)}catch(x){let O=x,V=O?.code??"INTERNAL",j=O?.message??L(x);l.total_entries++;let R={name:ae.basename(e)||e,type:"other",path:k,hidden:ae.basename(e)?.startsWith("."),ignored:!1};return h&&h.set(k,{code:V,message:`Failed to stat: ${j}`}),n==="files"||n==="dirs"?null:(s.value++,R)}let T="other";if(b.isDirectory()?T="dir":b.isFile()?T="file":b.isSymbolicLink()&&(T="symlink"),z(k,b.isDirectory(),f,g,y).shouldIgnore)return c.value++,l.ignored++,null;l.total_entries++,T==="file"?l.files++:T==="dir"?l.dirs++:T==="symlink"&&l.symlinks++;let I=ae.basename(e)?.startsWith(".");I&&l.hidden++;let E=T==="file"?ae.extname(ae.basename(e)||e).slice(1):void 0;E&&(p==="stat"||p==="digest")&&d.set(E,(d.get(E)??0)+1),T==="file"&&(p==="stat"||p==="digest")&&(l.total_file_bytes=(l.total_file_bytes??0)+(b.size??0));let w={name:ae.basename(e)||e,type:T,path:k,size:(p==="stat"||p==="digest")&&b.isFile()?b.size:void 0,mtime:p==="stat"||p==="digest"?Math.floor(b.mtimeMs/1e3):void 0,ext:E,hidden:I,ignored:!1};if(n==="files"&&T!=="file"&&T!=="symlink"||n==="dirs"&&T!=="dir")return null;if(s.value++,T==="dir"&&t<r){let x,O;try{x=await Kr.readdir(o,{withFileTypes:!0})}catch(R){let D=R,S=D?.code??"INTERNAL",C=D?.message??L(R);O={code:S,message:C},x=[]}let V=i?x:x.filter(R=>!R.name.startsWith("."));V.sort((R,D)=>{let S=R.isDirectory()?0:1,C=D.isDirectory()?0:1;return S!==C?S-C:R.name.localeCompare(D.name)});let j=[];O&&h&&h.set(k,{code:O.code,message:`Failed to read directory: ${O.message}`});for(let R of V){if(s.value>=a)break;let D=ae.join(o,R.name),S=e==="."?R.name:ae.posix.join(e,R.name),C=S.replace(/\\/g,"/");if(z(C,R.isDirectory(),f,g,y).shouldIgnore){c.value++,l.ignored++;continue}try{let pe=await po(D,S,t+1,r,n,i,a,s,c,l,d,p,f,g,m,h,y);pe&&j.push(pe)}catch(pe){let Z=pe,ot=Z?.code??"INTERNAL",Yr=Z?.message??L(pe);h&&h.set(C,{code:ot,message:`Failed to build subtree: ${Yr}`}),l.total_entries++;let it={name:ae.basename(S)||S,type:"other",path:C,hidden:ae.basename(S)?.startsWith("."),ignored:!1};if(n==="files"||n==="dirs")continue;s.value++,j.push(it)}}j.length>0&&(w.children=j)}return w}var Bt=class extends J{async executeTool(e){let t=e.directory_id,r=G(this.config,t),{absTarget:n,rel:i}=await X(r.path,e.path),a=Math.min(Math.max(e.max_depth??3,1),20),s=e.only??"all",c=e.include_hidden??!1,l=Math.min(Math.max(e.max_entries??5e3,1),2e4),d=e.detail??"stat",p=e.include_display??!1,f=Math.min(Math.max(e.max_display_rows??2e3,1),2e4);try{if(!(await Kr.stat(n)).isDirectory())throw A("NOT_A_DIRECTORY","Path is not a directory.",{path:i,hint:"The path must point to a directory to build a tree. Provide a directory path, not a file."})}catch(R){throw R?.code&&R?.message&&typeof R?.code=="string"?R:K(R,"Failed to build tree.")}let g=e.context?e.context.file_policies:void 0,m={directory_id:t,workspace_id:e.workspace_id,directory_path:r.path},h={reason:void 0},y={value:0},k={value:0},b={total_entries:0,files:0,dirs:0,symlinks:0,hidden:0,ignored:0,total_file_bytes:0},T=new Map,_=new Map,I=await po(n,i,0,a,s,c,l,y,k,b,T,d,m,this.config,h,_,g);if(!I)throw A("INTERNAL","Failed to build tree (empty result).");let E=F({workspace_id:e.workspace_id,device_id:e.device_id,directory_id:t,path:i});d==="digest"&&T.size>0&&(b.by_ext=Array.from(T.entries()).map(([R,D])=>({ext:R,count:D})).sort((R,D)=>D.count-R.count).slice(0,10));let w={tree:I,context:E,filtered:{ignored_count:k.value}};if(p){let R=[];lo(I,0,R,_),R.length>f&&(R.length=f,w.truncated=w.truncated??{reason:"MAX_ENTRIES",continuation:"Display rows truncated; reduce max_entries/max_depth or lower max_display_rows"}),w.display={rows:R}}h.reason&&(w.truncated={reason:h.reason,continuation:h.reason==="MAX_ENTRIES"?"Reduce max_entries or list this folder with fs_list instead":"Reduce max_depth or list this folder with fs_list instead"}),(d==="digest"||e.include_summary)&&(w.summary=b);let x={};if(e.grep?.query)try{let R=new Ke(this.config),D={directory_id:t,workspace_id:e.workspace_id,device_id:e.device_id,path:e.path??".",query:e.grep.query,mode:e.grep.mode??"substring",file_glob:e.grep.file_glob,max_files:e.grep.max_files??2e3,max_matches:e.grep.max_matches??200,context_lines:e.grep.context_lines??2,case_sensitive:e.grep.case_sensitive??!1},S=await R.execute(D);S.ok&&(x.grep={matches:S.data.matches,meta:S.data.meta,context:S.data.context})}catch{}let O=e.read?.paths?.filter(Boolean)??[],V=[];if(e.read?.auto){let R=Math.min(Math.max(e.read.auto.max_files??10,1),50),D=[];uo(I,D,3e3),V=vs(D).slice(0,R)}let j=Array.from(new Set([...O,...V])).slice(0,50);if(j.length>0)try{let R=Math.min(Math.max(e.read?.auto?.max_bytes_each??2e4,1),2e5),D=new Ue(this.config),S={directory_id:t,workspace_id:e.workspace_id,device_id:e.device_id,default_limit:R,default_encoding:"utf-8",items:j.map(Y=>({path:Y}))},C=await D.execute(S);C.ok&&(x.read_many={files:C.data.files.map(Y=>({path:Y.path,content:Y.content,bytes_read:Y.bytes_read,truncated:Y.truncated,error:Y.error})),context:C.data.context})}catch{}return Object.keys(x).length>0&&(w.extras=x),M("tree",E,!0,void 0,{file_count:y.value}),w}};import Ur from"node:fs/promises";import ze from"node:path";function Ss(o,e){let t="^",r=0;for(;r<e.length;){let n=e[r];if(n==="*")r+1<e.length&&e[r+1]==="*"?(t+=".*",r+=2):(t+="[^/\\\\]*",r++);else if(n==="?")t+="[^/\\\\]",r++;else if(n==="["){let i=e.indexOf("]",r);if(i===-1)t+="\\[",r++;else{let a=e.substring(r+1,i);a.startsWith("!")?t+="[^"+a.substring(1)+"]":t+="["+a+"]",r=i+1}}else"\\^$|.+(){}[]".includes(n)?(t+="\\"+n,r++):(t+=n,r++)}t+="$";try{return new RegExp(t,"i").test(o)}catch{return o.toLowerCase().includes(e.toLowerCase())}}function Es(o,e,t){return t==="glob"?Ss(o,e):o.toLowerCase().includes(e.toLowerCase())}function ks(o,e,t){let r=100;if(e){for(let n of e)if(o.path.startsWith(n)){r+=50;break}}if(t){for(let n of t)if(o.path.startsWith(n)){r-=30;break}}return o.name===o.path.split("/").pop()&&(r+=20),r}function _s(o,e){let t="^",r=0;for(;r<e.length;){let n=e[r];n==="*"?r+1<e.length&&e[r+1]==="*"?(t+=".*",r+=2):(t+="[^/]*",r++):n==="?"?(t+="[^/]",r++):"\\^$|.+(){}[]".includes(n)?(t+="\\"+n,r++):(t+=n,r++)}t+="$";try{return new RegExp(t,"i").test(o)}catch{return!1}}async function Br(o,e,t,r,n,i,a,s,c,l,d,p,f,g,m,h,y,k){if(l.length>=c)return;let b;try{b=await Ur.readdir(o,{withFileTypes:!0})}catch{return}let T=s?b:b.filter(_=>!_.name.startsWith("."));for(let _ of T){if(l.length>=c)break;let I=ze.join(o,_.name),E=e==="."?_.name:ze.posix.join(e,_.name);if(z(E,_.isDirectory(),p,f,k).shouldIgnore){d.value++;continue}if(g&&!_s(E,g)){i&&_.isDirectory()&&await Br(I,E,t,r,n,i,a,s,c,l,d,p,f,g,m,h,y,k);continue}let x=Es(_.name,r,n),O="other";if(_.isDirectory()?O="dir":_.isFile()?O="file":_.isSymbolicLink()&&(O="symlink"),x&&(a==="all"||a==="files"&&(O==="file"||O==="symlink")||a==="dirs"&&O==="dir")){let j,R;if(y!==!1)try{let C=await Ur.stat(I);j=C.isFile()?C.size:void 0,R=Math.floor(C.mtimeMs/1e3)}catch{}let D=O==="file"?ze.extname(_.name).slice(1):void 0,S=ks({name:_.name,path:E},m,h);l.push({name:_.name,path:E,type:O,size:j,mtime:R,ext:D,hidden:_.name.startsWith("."),ignored:!1,score:S,matched:"name"})}i&&O==="dir"&&await Br(I,E,t,r,n,i,a,s,c,l,d,p,f,g,m,h,y)}}var qt=class extends J{async executeTool(e){let t=e.directory_id,r=G(this.config,t),n=e.query;if(!n||n.length===0)throw A("INVALID_INPUT","Query is required.",{hint:'Query should be a pattern to match file or directory names. Use a substring (default) or glob pattern (with mode="glob"). Examples: "config", "*.ts", "test/**"'});let i=e.mode??"substring",a=e.recursive??!0,s=e.only??"all",c=e.include_hidden??!1,l=Math.min(Math.max(e.max_results??200,1),2e4),d=Math.max(e.offset??0,0),p=e.file_glob,f=e.prefer_paths,g=e.avoid_paths,m=e.include_stats!==!1,{absTarget:h,rel:y}=await X(r.path,e.path??"."),k=h,b=y,T=[];if(y!==".")try{let S=await Ur.stat(h);if(!S.isDirectory())if(S.isFile()){k=ze.dirname(h);let C=ze.posix.dirname(y);b=C==="."?".":C,T.push({type:"warning",description:`Search path "${y}" is not a directory (it's a file). Searching the directory containing it instead: "${b}"`})}else throw A("NOT_A_DIRECTORY","Search path is not a directory.",{path:y,hint:'The search path must be a directory. Search looks for files/directories matching the query within directories. Use "." to search from the root, or provide a directory path.'})}catch(S){throw S?.code&&S?.message&&typeof S?.code=="string"?S:K(S,"Failed to stat search path.")}let _=e.context?e.context.file_policies:void 0,I={directory_id:t,workspace_id:e.workspace_id,directory_path:r.path},E=[],w={value:0};await Br(k,b,r.path,n,i,a,s,c,l+d,E,w,I,this.config,p,f,g,m,_),E.sort((S,C)=>{let Y=(C.score??0)-(S.score??0);return Y!==0?Y:S.name.localeCompare(C.name)});let x=E.slice(d,d+l),O=d+x.length,V=O<E.length,j={count:x.length,limit:l,offset:d,has_more:V,next_offset:V?O:void 0},R=F({workspace_id:e.workspace_id,device_id:e.device_id,directory_id:t,path:b});M("search",R,!0,void 0,{file_count:x.length});let D={results:x,meta:j,context:R,filtered:{ignored_count:w.value}};return T.length>0&&(D.messages=T),D}};function Ts(o){if(typeof o!="object"||o===null||!("code"in o)||!("message"in o))return!1;let e=o;return typeof e.code=="string"&&typeof e.message=="string"}var Wt=class{config;executers=new Map;constructor(e){this.config=e,this.initializeExecuters()}initializeExecuters(){this.executers.set("fs_list",new Kt(this.config)),this.executers.set("fs_read",new je(this.config)),this.executers.set("fs_info",new Ut(this.config)),this.executers.set("fs_tree",new Bt(this.config)),this.executers.set("fs_search",new qt(this.config)),this.executers.set("fs_grep",new Ke(this.config)),this.executers.set("fs_read_many",new Ue(this.config))}async executeTool(e,t){let r=this.executers.get(e);if(!r)return xe({code:"UNKNOWN_TOOL",message:`Unknown tool: ${e}`,details:{toolName:e}});try{return await r.execute(t)}catch(n){return Ts(n)?xe(n):xe({code:"INTERNAL",message:n instanceof Error?n.message:"Unknown error",details:{raw:String(n)}})}}getExecuter(e){return this.executers.get(e)}hasTool(e){return this.executers.has(e)}getRegisteredTools(){return Array.from(this.executers.keys())}};import fo from"lodash/isObject.js";import Ae from"lodash/get.js";function Ye(o){let e=Date.now()-o,t=parseFloat((e/1e3).toFixed(2));return{execution_ms:e,execution_sec:t}}function ws(o){return fo(o)&&typeof Ae(o,"ok")=="boolean"}function Rs(o){let e=ws(o),t=e?!!Ae(o,"ok"):!0,r,n,i;if(t){if(e?r=Ae(o,"data")??Ae(o,"result")??o:r=o,fo(r)&&Array.isArray(Ae(r,"messages"))){i=Ae(r,"messages");let a={...r};delete a.messages,r=a}}else e?n=Ae(o,"error")??o:n=o;return{ok:t,result:r,error:n,messages:i}}function ho(o,e,t,r){let n=Ye(r),{ok:i,result:a,error:s,messages:c}=Rs(t);return{type:"tool_result",job_id:o,tool:e,ok:i,timing:n,...i?{result:a??null,...c?{messages:c}:{}}:{error:s??{code:"UNKNOWN",message:"Unknown error"},...c?{messages:c}:{}}}}function mo(o,e,t,r){let n=Ye(r),i=t instanceof Error?t.message:String(t);return{type:"tool_result",job_id:o,tool:e,ok:!1,error:i,timing:n}}var $=class{toolName;jobId;ok;elapsedSec;raw;constructor(e,t,r,n){this.toolName=e,this.jobId=t,this.ok=r.ok,this.elapsedSec=n,this.raw=r}getStatusIcon(){return this.ok?"\u2713":"\u2717"}getStatusText(){return this.ok?"SUCCESS":"FAILED"}toString(){let e=this.getStatusIcon(),t=this.getDescription();return`${e} ${this.toolName} (${t}, elapsed=${this.elapsedSec}s)`}};var Qe=class extends ${output;constructor(e,t,r,n){super(e,t,r,n),this.output=r.ok?r.data:void 0}getDescription(){if(!this.ok){let d=this.raw.error;return d&&typeof d=="object"&&"message"in d?`error: ${String(d.message)}`:"error"}if(!this.output)return"no result";let{items:e,meta:t,filtered:r,context:n}=this.output,i=e?.length??0,a=r?.ignored_count??0,s=n?.path??"unknown",c=[];i>0&&c.push(`${i} item${i!==1?"s":""}`),a>0&&c.push(`${a} ignored`),t?.has_more&&c.push("has_more");let l=c.length>0?c.join(", "):"empty";return`path=${s}, ${l}`}};var Xe=class extends ${output;constructor(e,t,r,n){super(e,t,r,n),this.output=r.ok?r.data:void 0}getDescription(){if(!this.ok){let c=this.raw.error;return c&&typeof c=="object"&&"message"in c?`error: ${String(c.message)}`:"error"}if(!this.output)return"no result";let{bytes_read:e,eof:t,context:r}=this.output,n=r?.path??"unknown",i=e??0,a=!t,s=[`path=${n}`,`${i} bytes`];return a&&s.push("truncated"),s.join(", ")}};var Ze=class extends ${output;constructor(e,t,r,n){super(e,t,r,n),this.output=r.ok?r.data:void 0}getDescription(){if(!this.ok){let a=this.raw.error;return a&&typeof a=="object"&&"message"in a?`error: ${String(a.message)}`:"error"}if(!this.output)return"no result";let{type:e,size:t,context:r}=this.output,n=r?.path??"unknown",i=t!==void 0?`${t} bytes`:"no size";return`path=${n}, type=${e}, ${i}`}};var et=class extends ${output;constructor(e,t,r,n){super(e,t,r,n),this.output=r.ok?r.data:void 0}getDescription(){if(!this.ok){let c=this.raw.error;return c&&typeof c=="object"&&"message"in c?`error: ${String(c.message)}`:"error"}if(!this.output)return"no result";let{tree:e,filtered:t,summary:r,context:n}=this.output,i=n?.path??"unknown",a=t?.ignored_count??0,s=[`path=${i}`];if(r){let{total_entries:c,files:l,dirs:d}=r;c!==void 0&&s.push(`${c} entries`),l!==void 0&&s.push(`${l} files`),d!==void 0&&s.push(`${d} dirs`)}else if(e){let c=d=>{let p=1;if(d.children)for(let f of d.children)p+=c(f);return p},l=c(e);s.push(`${l} nodes`)}return a>0&&s.push(`${a} ignored`),this.output.truncated&&s.push("truncated"),s.join(", ")}};var tt=class extends ${output;constructor(e,t,r,n){super(e,t,r,n),this.output=r.ok?r.data:void 0}getDescription(){if(!this.ok){let l=this.raw.error;return l&&typeof l=="object"&&"message"in l?`error: ${String(l.message)}`:"error"}if(!this.output)return"no result";let{results:e,meta:t,filtered:r,context:n}=this.output,i=n?.path??"unknown",a=e?.length??0,s=r?.ignored_count??0,c=[`path=${i}`,`${a} result${a!==1?"s":""}`];return s>0&&c.push(`${s} ignored`),t?.has_more&&c.push("has_more"),c.join(", ")}};var rt=class extends ${output;constructor(e,t,r,n){super(e,t,r,n),this.output=r.ok?r.data:void 0}getDescription(){if(!this.ok){let d=this.raw.error;return d&&typeof d=="object"&&"message"in d?`error: ${String(d.message)}`:"error"}if(!this.output)return"no result";let{matches:e,meta:t,filtered:r,context:n}=this.output,i=n?.path??"unknown",a=e?.length??0,s=t?.files_scanned??0,c=r?.ignored_count??0,l=[`path=${i}`,`${a} match${a!==1?"es":""}`,`${s} file${s!==1?"s":""} scanned`];return c>0&&l.push(`${c} ignored`),t?.truncated&&l.push("truncated"),l.join(", ")}};var nt=class extends ${output;constructor(e,t,r,n){super(e,t,r,n),this.output=r.ok?r.data:void 0}getDescription(){if(!this.ok){let s=this.raw.error;return s&&typeof s=="object"&&"message"in s?`error: ${String(s.message)}`:"error"}if(!this.output)return"no result";let{files:e}=this.output,t=e?.length??0,r=e?.filter(s=>!s.error).length??0,n=t-r,i=e?.reduce((s,c)=>s+(c.bytes_read??0),0)??0,a=[`${t} file${t!==1?"s":""}`,`${r} success`];return n>0&&a.push(`${n} error${n!==1?"s":""}`),i>0&&a.push(`${i} bytes`),a.join(", ")}};function qr(o,e,t,r){switch(o){case"fs_list":return new Qe(o,e,t,r);case"fs_read":return new Xe(o,e,t,r);case"fs_info":return new Ze(o,e,t,r);case"fs_tree":return new et(o,e,t,r);case"fs_search":return new tt(o,e,t,r);case"fs_grep":return new rt(o,e,t,r);case"fs_read_many":return new nt(o,e,t,r);default:{class n extends ${getDescription(){return this.ok?"completed":"failed"}}return new n(o,e,t,r)}}}var Jt=class{name="tools-orchestrator";handlers(){return{"ws:connected":async(e,t)=>{t.notifier.info(`WS connected: ${e.url}`,{scope:"system"})},"ws:rx":async(e,t)=>{try{let r=JSON.parse(e.text),n=$t.safeParse(r);if(!n.success){t.notifier.error("Unexpected message format",{scope:"system"});return}n.success&&n.data.type==="tool_call"&&await this.handleToolCall(n.data,t)}catch{t.notifier.error("Unexpected message format",{scope:"system"})}}}}async handleToolCall(e,t){let r=e.tool,n=e.job_id,i=e.args??{},a=i.directory_id,s=t.engine.configurationContext.configuration,c=s.directories.map(p=>p.id).join(", ")||"none",l=s.directories.map(p=>p.path).join(", ")||"none";t.notifier.info(`Tool call: ${r} (job=${n}, directory_id=${a||"MISSING"}, available_ids=[${c}], available_paths=[${l}], directory_count=${s.directories.length})`,{scope:"system"}),t.notifier.info(`Executing: ${r}`);let d=Date.now();try{let f=await new Wt(s).executeTool(r,i),g=ho(n,r,f,d);Lt.parse(g),t.bus.emit("ws:tx",{text:JSON.stringify(g)});let m=Ye(d),h=qr(r,n,f,m.execution_sec);h.ok?t.notifier.success(h.toString()):t.notifier.error(h.toString())}catch(p){let f=mo(n,r,p,d);Lt.parse(f),t.bus.emit("ws:tx",{text:JSON.stringify(f)});let g=Ye(d),m={ok:!1,error:p instanceof Error?{code:"INTERNAL",message:p.message}:{code:"INTERNAL",message:String(p)}},h=qr(r,n,m,g.execution_sec);t.notifier.error(h.toString())}}};var Wr=[{name:"fs_list",version:"1.0",description:"List files and folders at a relative path inside a shared folder.",inputSchema:{type:"object",properties:{directory_id:{type:"string",minLength:1},path:{type:"string",default:"."},limit:{type:"integer",minimum:1,maximum:1e3,default:200},offset:{type:"integer",minimum:0,default:0},include_hidden:{type:"boolean",default:!1},only:{type:"string",enum:["all","files","dirs"],default:"all"},sort:{type:"string",enum:["name","mtime","size"],default:"name"},order:{type:"string",enum:["asc","desc"],default:"asc"},detail:{type:"string",enum:["basic","stat","digest"],default:"stat"}},required:["path"]},errorCodes:["PATH_ESCAPE","NOT_FOUND","NOT_A_DIRECTORY","INVALID_INPUT","INTERNAL","UNKNOWN_ROOT"]},{name:"fs_read",version:"1.0",description:"Read the contents of a SINGLE text file inside a shared folder. For reading many files at once, prefer fs_read_many instead of multiple fs_read calls.",inputSchema:{type:"object",properties:{directory_id:{type:"string",minLength:1},path:{type:"string",minLength:1},offset:{type:"integer",minimum:0,default:0},limit:{type:"integer",minimum:1,maximum:2e5,default:4e4},encoding:{type:"string",default:"utf-8"}},required:["path"]},errorCodes:["PATH_ESCAPE","NOT_FOUND","NOT_A_FILE","PERMISSION_DENIED","INVALID_INPUT","INTERNAL","UNKNOWN_ROOT"]},{name:"fs_info",version:"1.0",description:"Get metadata about a file or directory (type, size, timestamps, symlink).",inputSchema:{type:"object",properties:{directory_id:{type:"string",minLength:1},path:{type:"string",minLength:1}},required:["path"]},errorCodes:["PATH_ESCAPE","NOT_FOUND","PERMISSION_DENIED","INVALID_INPUT","INTERNAL","UNKNOWN_ROOT"]},{name:"fs_tree",version:"1.0",description:"Return a directory tree from a relative path up to max_depth.",inputSchema:{type:"object",properties:{directory_id:{type:"string",minLength:1},path:{type:"string",default:"."},max_depth:{type:"integer",minimum:1,maximum:20,default:3},max_entries:{type:"integer",minimum:1,maximum:2e4,default:5e3},include_hidden:{type:"boolean",default:!1},only:{type:"string",enum:["all","files","dirs"],default:"all"},detail:{type:"string",enum:["basic","stat","digest"],default:"stat"},include_display:{type:"boolean",default:!1},max_display_rows:{type:"integer",minimum:1,maximum:2e4,default:2e3}},required:["path"]},errorCodes:["PATH_ESCAPE","NOT_FOUND","NOT_A_DIRECTORY","INVALID_INPUT","INTERNAL","UNKNOWN_ROOT"]},{name:"fs_search",version:"1.0",description:"Search for files/folders by name inside a root, optionally recursively.",inputSchema:{type:"object",properties:{directory_id:{type:"string",minLength:1},path:{type:"string",default:"."},query:{type:"string",minLength:1},mode:{type:"string",enum:["substring","glob"],default:"substring"},recursive:{type:"boolean",default:!0},include_hidden:{type:"boolean",default:!1},only:{type:"string",enum:["all","files","dirs"],default:"all"},max_results:{type:"integer",minimum:1,maximum:2e4,default:200}},required:["query"]},errorCodes:["PATH_ESCAPE","NOT_FOUND","INVALID_INPUT","INTERNAL","UNKNOWN_ROOT"]},{name:"fs_grep",version:"1.0",description:"Search for text content inside files (like grep/ripgrep).",inputSchema:{type:"object",properties:{directory_id:{type:"string",minLength:1},path:{type:"string",default:"."},query:{type:"string",minLength:1},mode:{type:"string",enum:["substring","regex"],default:"substring"},file_glob:{type:"string"},max_files:{type:"integer",minimum:1,maximum:1e4,default:2e3},max_matches:{type:"integer",minimum:1,maximum:1e3,default:200},context_lines:{type:"integer",minimum:0,maximum:10,default:2},case_sensitive:{type:"boolean",default:!1}},required:["query"]},errorCodes:["PATH_ESCAPE","NOT_FOUND","NOT_A_DIRECTORY","INVALID_INPUT","INTERNAL"]},{name:"fs_read_many",version:"1.0",description:"Read multiple files in a single call (batch read). Prefer this over multiple fs_read calls when you need to read several files\u2014fewer round-trips and better performance (up to 50 items per call).",inputSchema:{type:"object",properties:{directory_id:{type:"string",minLength:1},items:{type:"array",minItems:1,maxItems:50,items:{type:"object",properties:{path:{type:"string",minLength:1},offset:{type:"integer",minimum:0,default:0},limit:{type:"integer",minimum:1,maximum:2e5},encoding:{type:"string"}},required:["path"]}},default_limit:{type:"integer",minimum:1,maximum:2e5,default:12e3},default_encoding:{type:"string",default:"utf-8"}},required:["items"]},errorCodes:["PATH_ESCAPE","NOT_FOUND","NOT_A_FILE","INVALID_INPUT","INTERNAL"]}];var Jr=class extends B{constructor(){super(new U({name:"tools-job",lane:"worker",priority:1e3,locks:[],maxAttempts:1,cancellable:!1}))}async run(e,t){t.notifier.info("Executing supported tools job");let n=t.engine.configurationContext.configuration.device?.id;if(!n){t.notifier.error("Cannot publish tools: device ID not available");return}let i={type:"tools_publish",device_id:n,tools:Wr};t.bus.emit("ws:tx",{text:JSON.stringify(i)}),t.notifier.success(`\u2192 tools_publish (count=${Wr.length})`)}},Hr=new Jr;function go(){return new ee(Hr,void 0)}var Gr=class extends B{constructor(){super(new U({name:"directories-job",lane:"worker",priority:1e3,locks:[],maxAttempts:1,cancellable:!1}))}async run(e,t){t.notifier.info("Executing directories job");let r=t.engine.configurationContext.configuration,n=r.device?.id;if(!n){t.notifier.error("Cannot publish directories: device ID not available");return}let i=r.directories.map(s=>({id:s.id,name:s.name,path:sr(s.path)??s.name,path_hint:s.tag||void 0})),a={type:"directories_publish",device_id:n,directories:i};t.bus.emit("ws:tx",{text:JSON.stringify(a)}),t.notifier.success(`\u2192 directories_publish (dirs=${a.directories.length})`)}},Vr=new Gr;function yo(){return new ee(Vr,void 0)}var Ht=class{name="meta-data-orchestrator";onRegister(e){e.engine.register(Hr),e.engine.register(Vr)}handlers(){return{"meta:data:requested:tools":async(e,t)=>{t.notifier.info("MetaData: tools requested -> dispatching supported tools job"),t.engine.dispatch(go())},"meta:data:requested:directories":async(e,t)=>{t.notifier.info("MetaData: directories requested -> dispatching directories job"),t.engine.dispatch(yo())}}}};var Gt=class{constructor(e){this.argv=e;let t=e.map(p=>p.trim()?.toLowerCase()),r=e.map(p=>p.trim()),n=this.parseKeyValue(t,r,["--env"]);n.match&&(this.enviromentFile=n.value);let i=this.parseKeyValue(t,r,["--root"]);i.match&&(this.rootPath=i.value);let a=this.parseKeyValue(t,r,["--control-url"]);a.match&&(this.controlUrl=a.value);let s=this.parseKeyValue(t,r,["--bridge-ws-url"]);s.match&&(this.bridgeWsUrl=s.value);let c=this.parseKeyValue(t,r,["--private-key-file"]);c.match&&c.value&&(this.privateKeyFile=c.value);let l=this.parseKeyValue(t,r,["--public-key-file"]);l.match&&l.value&&(this.publicKeyFile=l.value);let d=this.parseKeyValue(t,r,["--enrollment-file"]);d.match&&d.value&&(this.enrollmentFile=d.value)}enviromentFile;rootPath;controlUrl;bridgeWsUrl;privateKeyFile;publicKeyFile;enrollmentFile;parseKeyValue(e,t,r){let n=r.map(i=>i.toLowerCase()?.trim()).filter(i=>i!==void 0);if(n.length===0)return{match:!1,value:void 0};for(let i of n){let a=e.findIndex(s=>{let c=s.toLowerCase();return c===i||c.startsWith(i+"=")});if(a!==-1){let s=t[a],c=s.toLowerCase().indexOf("=");return c!==-1?{match:!0,value:s.substring(c+1)||void 0}:{match:!0,value:t[a+1]??void 0}}}return{match:!1,value:void 0}}};var Vt=class{bus;flags;constructor(e,t){this.bus=e,this.flags=t??new fe(!1)}info(e,t){return this.push(e,t)}warn(e,t){let r=Object.assign({},t,{kind:"warning"});return this.push(e,r)}error(e,t){let r=Object.assign({},t,{kind:"error"});return this.push(e,r)}success(e,t){let r=Object.assign({},t,{kind:"success"});return this.push(e,r)}push(e,t){if((t?.scope??"default")==="system"&&!this.flags.debug&&!this.flags.headless)return null;let n=new ne(e,t);return this.bus.emit("notify:event",n),n}};var zt=class{bus;constructor(e){this.bus=e}info(e,t){this.push(e,Object.assign({},t,{level:"info"}))}warn(e,t){this.push(e,Object.assign({},t,{level:"warn"}))}error(e,t){this.push(e,Object.assign({},t,{level:"error"}))}debug(e,t){this.push(e,Object.assign({},t,{level:"debug"}))}success(e,t){this.push(e,Object.assign({},t,{level:"success"}))}push(e,t){let r=new lt(e,t);this.bus.emit("log",r)}};import zr from"dotenv";import{existsSync as Cs}from"node:fs";import{join as xs}from"node:path";function bo(o){zr.config({quiet:!0});let e=xs(process.cwd(),"apps","agent",".env");Cs(e)&&zr.config({path:e,override:!0,quiet:!0}),o.enviromentFile&&zr.config({path:o.enviromentFile,override:!0,quiet:!0})}async function As(){let o=new St,e=new pt,t=await cn(),r=new Gt(process.argv);bo(r);let n=jn(process.argv);if(n.headless){let l=Xn(r.enrollmentFile);console.log(`[agent] Headless boot (${n.reason})`),console.log(`[agent] Enrollment: ${l}`),l==="none"&&console.log("[agent] No enrollment key (set ENROLMENT_KEY or DLINK_ENROLMENT_KEY, or use --enrollment-file)")}let i=new fe(!1,n.headless,n.reason),a=new zt(o),s=new Vt(o,i);Sn(o,i);let c=new vt({bus:o,argv:r,logger:a,notifier:s,storage:e,secrets:t,flags:i,lanes:[new ce("console",1),new ce("supervisor",1),new ce("main",1),new ce("ws",1),new ce("worker",8),new ce("io",4)]});c.register(mr),c.register(Tr),c.register(ln),c.register(dn),c.register(pn),c.registerOrchestrator(new Ot),c.registerOrchestrator(new wt("./engine_state/telemetry.ndjson")),c.registerOrchestrator(new Rt),c.registerOrchestrator(new Mt),c.registerOrchestrator(new jt),c.registerOrchestrator(new Pt),c.registerOrchestrator(new Jt),c.registerOrchestrator(new Ht),await c.init(),await c.start()}As();
4
+ `);if(h===-1)break;let g=d.slice(0,h);d=d.slice(h+2);let m=null,y=null;for(let k of g.split(`
5
+ `))k.startsWith("event:")&&(m=k.slice(6).trim()),k.startsWith("data:")&&(y=k.slice(5).trim());if(!m||!y)continue;let S;try{S=JSON.parse(y)}catch(k){n.error(`[deviceAuthStream] Failed to parse event data as JSON: ${String(k)}`);continue}let b=ss.safeParse(S);if(!b.success){n.error(`[deviceAuthStream] Event validation failed (${m}): ${String(b.error)}`);continue}let E=b.data;if(m==="approved"||m==="denied"||m==="expired")return E;if(m==="pending"){"status"in E&&E.status==="pending"&&E.needs_proof===!0&&n.warn("[deviceAuthStream] pending: needs_proof=true (unexpected before approval)");continue}n.warn(`[deviceAuthStream] Unknown event type: ${m}`)}}throw n.error("[deviceAuthStream] Stream ended unexpectedly without completion"),new Error("Device authorization stream ended without completion")}catch(s){if(s instanceof Error&&s.name==="AbortError")n.warn(`[deviceAuthStream] Aborted: ${s.message??""}`);else{let c=s instanceof Error?s.message:String(s);n.error(`[deviceAuthStream] Error: ${c}`)}throw s}finally{clearTimeout(a)}})}function oo(o){return new V(o,async(e,t)=>fe(e,"/api/device/authorize/token",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(t)}))}function io(o){return new V(o,async(e,t)=>{let r=new AbortController,n=3e4,i=setTimeout(()=>{r.abort()},n);try{let a=await fe(e,"/api/device/authorize/refresh",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(t),signal:r.signal});return clearTimeout(i),a}catch(a){throw clearTimeout(i),a instanceof Error&&a.name==="AbortError"?new Error(`Refresh token request timed out after ${n}ms`):a}})}function so(o){return new V(o,async(e,t)=>fe(e,"/api/device/enrollment/start",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(t)}))}var je=class{deviceAuthStartCall;deviceAuthProveCall;deviceAuthStreamCall;deviceAuthTokenCall;deviceAuthRefreshCall;deviceEnrollmentStartCall;constructor(e,t){let r={configuration:e,notifier:t};this.deviceAuthStartCall=to(r),this.deviceAuthProveCall=ro(r),this.deviceAuthStreamCall=no(r),this.deviceAuthTokenCall=oo(r),this.deviceAuthRefreshCall=io(r),this.deviceEnrollmentStartCall=so(r)}deviceAuthStart(e){return this.deviceAuthStartCall.execute(e)}deviceEnrollmentStart(e){return this.deviceEnrollmentStartCall.execute(e)}deviceAuthProve(e){return this.deviceAuthProveCall.execute(e)}deviceAuthStream(e){return this.deviceAuthStreamCall.execute(e)}deviceAuthToken(e){return this.deviceAuthTokenCall.execute(e)}deviceAuthRefresh(e){return this.deviceAuthRefreshCall.execute(e)}};function cs(o){if(!ot(o)||z(o,"ok")!==!0)return!1;let e=z(o,"data");return ot(e)?z(e,"ok")===!0&&Ke(z(e,"access_token"))&&Ke(z(e,"refresh_token"))&&typeof z(e,"expires_in_sec")=="number"&&Ke(z(e,"device_id")):!1}function ls(o){return o.ok===!0&&typeof o.access_token=="string"}var Bt=class{constructor(e){this.reason=e}},Qr=["REFRESH_EXPIRED","REFRESH_REVOKED","REFRESH_NOT_FOUND","DEVICE_NOT_FOUND"],me=class{constructor(e,t,r,n,i,a,s,c){this.ok=e;this.accessToken=t;this.refreshToken=r;this.expiresInSec=n;this.error=i;this.retryAfterSec=a;this.errorCode=s;this.driftMs=c}},Yr=class extends W{constructor(){super(new q({name:"refresh-tokens",lane:"io",priority:900,locks:["AUTH_REFRESH"],maxAttempts:5,cancellable:!0}))}async run(e,t){let r=t.engine.configurationContext.configuration;if(!r.controlBaseUrl)return new me(!1,void 0,void 0,void 0,"controlBaseUrl missing");if(!r.device?.id)return new me(!1,void 0,void 0,void 0,"device.id missing");let n=await t.engine.secrets.get("devicePrivateKey");if(!n.ok||!n.value)return new me(!1,void 0,void 0,void 0,"device private key missing");let i=n.value,a=await t.engine.secrets.get("refreshToken");if(!a.ok||!a.value)return new me(!1,void 0,void 0,void 0,"refresh token missing");let s=a.value;t.notifier.info(`Refreshing tokens (job) reason=${e.reason}`,{scope:"system",category:["token-refresh"]});let c=Date.now(),u=as.randomBytes(16).toString("base64url"),d=Tr({deviceId:r.device.id,ts:c,nonce:u,refreshToken:s,privateKeyPem:i}),f=new je(r,t.notifier),p;try{p=await f.deviceAuthRefresh({device_id:r.device.id,refresh_token:s,ts:c,nonce:u,sig:d})}catch(g){let m=g instanceof Error?g.message:String(g),y=g,S=y.retryAfterSec,b=y.body,E=b?.error,k=E!=null&&typeof E=="object"?E.code:void 0,R=Ke(k)?k:void 0,_=E!=null&&typeof E=="object"?E.drift_ms:void 0,T=typeof _=="number"?_:void 0;return R==="REFRESH_NOT_FOUND"?(t.notifier.warn("Refresh token not found; clearing stored refresh token and stopping automatic refresh. Please run signin.",{category:["token-refresh"]}),await t.engine.secrets.delete("refreshToken").catch(x=>{let A=x instanceof Error?x.message:String(x);t.logger.warn(`Could not clear stored refresh token: ${A}`,{category:["token-refresh"]}),t.notifier.warn(`Could not clear stored refresh token: ${A}`,{category:["token-refresh"]})}),new me(!1,void 0,void 0,void 0,"Refresh token not found",void 0,"REFRESH_NOT_FOUND")):(t.notifier.error(`Refresh tokens failed: ${m}`,{category:["token-refresh"]}),new me(!1,void 0,void 0,void 0,m,S,R,T))}let h=this.normalizeRefreshResponse(p);if(!h){let g=this.extractErrorMessage(p),m=this.extractErrorCode(p),y=this.extractErrorDrift(p);return t.notifier.error(`Refresh tokens failed: ${g}`,{category:["token-refresh"]}),new me(!1,void 0,void 0,void 0,g,void 0,m,y)}return h.refresh_token&&await t.engine.secrets.set("refreshToken",h.refresh_token),new me(!0,h.access_token,h.refresh_token,h.expires_in_sec)}normalizeRefreshResponse(e){if(cs(e))return e.data;if(this.looksLikeDomainRefresh(e)){let t=e;return ls(t)?t:null}return null}looksLikeDomainRefresh(e){if(typeof e!="object"||e===null)return!1;let t=e;return t.ok===!0||t.ok===!1}extractErrorMessage(e){if(!ot(e))return"Unknown error";if(z(e,"ok")===!1){let t=z(e,"error.message");if(Ke(t)&&t.length>0)return t}return z(e,"ok")===!0&&"data"in e?"Bad refresh response: missing token fields":"Unknown error"}extractErrorDrift(e){if(!ot(e)||z(e,"ok")!==!1)return;let t=z(e,"error.drift_ms");return typeof t=="number"?t:void 0}extractErrorCode(e){if(!ot(e)||z(e,"ok")!==!1)return;let t=z(e,"error.code");return Ke(t)?t:void 0}},Xr=new Yr;function ao(o){return new ie(Xr,o)}function co(o,e){return e.headless?o.on("notify:event",t=>{us(t,e.debug)}):null}function us(o,e){if(!e&&!ds(o))return;let t=`[agent] ${o.message}`;switch(o.kind){case"error":console.error(t);break;case"warning":console.warn(t);break;default:console.log(t);break}}function ds(o){return o.kind==="error"||o.kind==="warning"||o.kind==="success"||o.scope==="system"}var qt=class{constructor(e){this.argv=e;let t=e.map(f=>f.trim()?.toLowerCase()),r=e.map(f=>f.trim()),n=this.parseKeyValue(t,r,["--env"]);n.match&&(this.enviromentFile=n.value);let i=this.parseKeyValue(t,r,["--root"]);i.match&&(this.rootPath=i.value);let a=this.parseKeyValue(t,r,["--control-url"]);a.match&&(this.controlUrl=a.value);let s=this.parseKeyValue(t,r,["--bridge-ws-url"]);s.match&&(this.bridgeWsUrl=s.value);let c=this.parseKeyValue(t,r,["--private-key-file"]);c.match&&c.value&&(this.privateKeyFile=c.value);let u=this.parseKeyValue(t,r,["--public-key-file"]);u.match&&u.value&&(this.publicKeyFile=u.value);let d=this.parseKeyValue(t,r,["--enrollment-file"]);d.match&&d.value&&(this.enrollmentFile=d.value)}enviromentFile;rootPath;controlUrl;bridgeWsUrl;privateKeyFile;publicKeyFile;enrollmentFile;parseKeyValue(e,t,r){let n=r.map(i=>i.toLowerCase()?.trim()).filter(i=>i!==void 0);if(n.length===0)return{match:!1,value:void 0};for(let i of n){let a=e.findIndex(s=>{let c=s.toLowerCase();return c===i||c.startsWith(i+"=")});if(a!==-1){let s=t[a],c=s.toLowerCase().indexOf("=");if(c!==-1)return{match:!0,value:s.substring(c+1)||void 0};let u=t[a+1];return u===void 0||u.startsWith("--")?{match:!0,value:void 0}:{match:!0,value:u}}}return{match:!1,value:void 0}}};var Wt=class{bus;flags;constructor(e,t){this.bus=e,this.flags=t??new _e(!1)}info(e,t){return this.push(e,t)}warn(e,t){let r=Object.assign({},t,{kind:"warning"});return this.push(e,r)}error(e,t){let r=Object.assign({},t,{kind:"error"});return this.push(e,r)}success(e,t){let r=Object.assign({},t,{kind:"success"});return this.push(e,r)}push(e,t){if((t?.scope??"default")==="system"&&!this.flags.debug&&!this.flags.headless)return null;let n=new ue(e,t);return this.bus.emit("notify:event",n),n}};var Jt=class{bus;constructor(e){this.bus=e}info(e,t){this.push(e,Object.assign({},t,{level:"info"}))}warn(e,t){this.push(e,Object.assign({},t,{level:"warn"}))}error(e,t){this.push(e,Object.assign({},t,{level:"error"}))}debug(e,t){this.push(e,Object.assign({},t,{level:"debug"}))}success(e,t){this.push(e,Object.assign({},t,{level:"success"}))}push(e,t){let r=new Tt(e,t);this.bus.emit("log",r)}};import en from"node:fs";import hs from"node:os";import tn from"node:tty";import ps from"lodash/toLower.js";import fs from"lodash/trim.js";var ms=new Set(["1","true","yes","on"]),Zr=new Set(["0","false","no","off"]);function Gt(o){let e=ps(fs(o));if(ms.has(e))return!0;if(Zr.has(e))return!1}function Be(o,e){return Gt(process.env[o]??"")??e}function gs(o){let e=o.setRawMode;return typeof e!="function"||(o.setRawMode=t=>{try{return e.call(o,t)}catch(r){let n=typeof r=="object"&&r!==null&&"code"in r?r.code:void 0;if(n==="EPERM"||n==="EINVAL")return o;throw r}}),o}var it;function rn(){if(it!==void 0)return it;let o;if(process.stdin.isTTY)o=process.stdin;else try{if(hs.platform()==="win32")try{let e=en.openSync("CONIN$","rs");o=new tn.ReadStream(e)}catch{let e=en.openSync("\\\\.\\CONIN$","rs");o=new tn.ReadStream(e)}else{let e=en.openSync("/dev/tty","rs");o=new tn.ReadStream(e)}}catch{return it=null,null}return it=gs(o),it}function ys(){return rn()===null}function bs(o){for(let e of o){let t=e.trim(),r=t.toLowerCase();if(r==="--headless")return"on";if(r.startsWith("--headless=")){let n=t.slice(t.indexOf("=")+1).trim().toLowerCase();return Zr.has(n)?"off":"on"}}return"unset"}function lo(o){let e=bs(o);if(e==="on")return{headless:!0,reason:"CLI: --headless"};if(e==="off")return{headless:!1,reason:"CLI: --headless=false"};let t=process.env.HEADLESS;if(t!==void 0&&t.trim()!==""){let i=Gt(t);if(i!==void 0)return{headless:i,reason:i?"env: HEADLESS":"env: HEADLESS=false"}}let r=process.env.DLINK_HEADLESS;if(r!==void 0&&r.trim()!==""){let i=Gt(r);if(i!==void 0)return{headless:i,reason:i?"env: DLINK_HEADLESS":"env: DLINK_HEADLESS=false"}}return ys()?{headless:!0,reason:"no TTY stdin (Docker/CI/background)"}:{headless:!1,reason:"TTY available (interactive)"}}import Ht from"dotenv";import{existsSync as uo}from"node:fs";import{join as vs,resolve as Ss,dirname as _s}from"node:path";function po(o){Ht.config({quiet:!0});let e=vs(process.cwd(),"apps","agent",".env");uo(e)&&Ht.config({path:e,override:!0,quiet:!0}),o.enviromentFile&&Ht.config({path:o.enviromentFile,override:!0,quiet:!0});let t=Ss(_s(process.argv[1]??"."),".env.production");uo(t)&&Ht.config({path:t,override:!1,quiet:!0})}import{access as fo,readFile as Es}from"node:fs/promises";import{constants as mo}from"node:fs";function ks(o){return(o.trim().split(".")[0]??"").startsWith("eyJ")}function nn(){let o=process.env.ENROLMENT_KEY?.trim()??process.env.DLINK_ENROLMENT_KEY?.trim();return o?ks(o)?{kind:"inline",jws:o}:{kind:"path",path:o}:{kind:"none"}}function go(o){let e=o?.trim();if(e)return`file:${e}`;let t=nn();return t.kind==="none"?"none":t.kind==="inline"?"ENROLMENT_KEY(inline JWS)":`file:${t.path}`}async function ho(o){try{let t=(await Es(o,"utf8")).trim();return t?{ok:!0,jws:t}:{ok:!1,error:"empty_file"}}catch(e){return{ok:!1,error:e instanceof Error?e.message:String(e)}}}async function yo(o){let e=o?.trim();if(e)try{return await fo(e,mo.R_OK),!0}catch{return!1}let t=nn();if(t.kind==="inline")return t.jws.trim().length>0;if(t.kind==="path")try{return await fo(t.path,mo.R_OK),!0}catch{return!1}return!1}async function bo(o){let e=o?.trim();if(e)return ho(e);let t=nn();if(t.kind==="none")return{ok:!1,error:"not_configured"};if(t.kind==="inline"){let r=t.jws.trim();return r?{ok:!0,jws:r}:{ok:!1,error:"empty_file"}}return ho(t.path)}import on from"lodash/trim.js";var vo=[{name:"help",aliases:["?"],summary:"Show this command list"},{name:"start",aliases:["run"],summary:"Connect the bridge"},{name:"stop",summary:"Disconnect the bridge"},{name:"status",summary:"Show device, bridge and session state"},{name:"doctor",summary:"Run local environment checks"},{name:"signin",summary:"Sign in to DeviceLink"},{name:"signout",summary:"Sign out and disconnect the bridge"},{name:"roots list",summary:"List the directories shared with the bridge"},{name:"roots add",args:"<path>",summary:"Share an additional directory"},{name:"debug on",summary:"Show internal system messages",hidden:!0},{name:"debug off",summary:"Hide internal system messages",hidden:!0},{name:"replace",aliases:["yes","y"],summary:"Take over an existing session on conflict"}],So=[{flag:"-h, --help",summary:"Show this help and exit"},{flag:"-v, --version",summary:"Print the agent version and exit"},{flag:"--headless",args:"[=true|false]",summary:"Run without the interactive UI"},{flag:"--env",args:"<file>",summary:"Load environment from a specific file"},{flag:"--root",args:"<path>",summary:"Share this directory with the bridge"},{flag:"--control-url",args:"<url>",summary:"Override the control plane base URL"},{flag:"--bridge-ws-url",args:"<url>",summary:"Override the bridge WebSocket URL"},{flag:"--enrollment-file",args:"<file>",summary:"Path to a .devicelink enrollment key"},{flag:"--private-key-file",args:"<file>",summary:"Path to the device private key"},{flag:"--public-key-file",args:"<file>",summary:"Path to the device public key"}];function _o(){let o=new Set;for(let e of vo){let t=e.name.split(" ")[0];t&&o.add(t);for(let r of e.aliases??[])o.add(r)}return Array.from(o)}function Eo(o){let e=new Set,t=new Set;for(let i of So){let a=!!i.args&&!i.args?.startsWith("[");for(let s of i.flag.split(",")){let c=on(s).toLowerCase();c.startsWith("-")&&(e.add(c),a&&t.add(c))}}let r=[],n=o.slice(2);for(let i=0;i<n.length;i++){let a=on(n[i]??"");if(a==="--"||!a.startsWith("-"))continue;let s=a.split("=")[0]??a;if(!e.has(s.toLowerCase())){r.push(s);continue}t.has(s.toLowerCase())&&!a.includes("=")&&i++}return r}function ko(o){for(let e of o.slice(2)){let t=on(e).toLowerCase();if(t==="--version"||t==="-v")return"version";if(t==="--help"||t==="-h")return"help"}return null}function To(o){let e=o.args?`${o.name} ${o.args}`:o.name;return!o.aliases||o.aliases.length===0?e:`${e} (${o.aliases.join(", ")})`}function sn(o){let e=o.reduce((t,r)=>Math.max(t,r.left.length),0);return o.map(t=>` ${t.left.padEnd(e)} ${t.right}`)}function wo(){return vo.filter(o=>!o.hidden)}function Co(){let o=wo().map(e=>({left:To(e),right:e.summary}));return["Commands:",...sn(o)]}function Ro(o){let e=wo().map(r=>({left:To(r),right:r.summary})),t=So.map(r=>({left:r.args?`${r.flag} ${r.args}`:r.flag,right:r.summary}));return[`DeviceLink Agent ${o}`,"","Usage: dlink [options]","","Options:",...sn(t),"","Interactive commands (type these once the agent is running):",...sn(e),""].join(`
6
+ `)}import Ts from"lodash/compact.js";import ws from"lodash/isString.js";import Cs from"lodash/trim.js";import{existsSync as Rs,readFileSync as xs}from"node:fs";import xo from"node:path";import{fileURLToPath as As}from"node:url";var an;function Ds(){try{let o=xo.dirname(As(import.meta.url)),e=xo.join(o,"package.json");if(!Rs(e))return;let t=JSON.parse(xs(e,"utf8"));if(t.name==="@devicelink/dlink"&&ws(t.version))return t.version}catch{return}}function ne(){if(an===void 0){let o=[process.env.DLINK_AGENT_VERSION,process.env.npm_package_version,Ds()].map(e=>Cs(e));an=Ts(o)[0]??"unknown"}return an}async function Ao(o){let e=ko(process.argv);if(e==="version"){console.log(ne());return}if(e==="help"){console.log(Ro(ne()));return}let t=Eo(process.argv);t.length>0&&(console.warn(`[agent] Ignoring unrecognised ${t.length===1?"option":"options"}: ${t.join(", ")}`),console.warn('[agent] Run "dlink --help" to see the supported options.'));let r=new Mt,n=new Ct,i=await Yn(),a=new qt(process.argv);po(a);let s=lo(process.argv);if(s.headless){let h=go(a.enrollmentFile);console.log(`[agent] Headless boot (${s.reason})`),console.log(`[agent] Enrollment: ${h}`),h==="none"&&console.log("[agent] No enrollment key (set ENROLMENT_KEY or DLINK_ENROLMENT_KEY, or use --enrollment-file)")}let c=new _e(!1,s.headless,s.reason),u=new Jt(r),d=new Wt(r,c);co(r,c);let f=new $t({bus:r,argv:a,logger:u,notifier:d,storage:n,secrets:i,flags:c,lanes:[new ye("console",1),new ye("supervisor",1),new ye("main",1),new ye("ws",1),new ye("worker",8),new ye("io",4)]});f.register(qr),f.register(Xr),f.register(Xn),f.register(Zn),f.register(eo);let p=o({headless:s.headless,headlessReason:s.reason});for(let h of p)f.registerOrchestrator(h);await f.init(),await f.start()}import la from"react";import{render as ua}from"ink";import{useEffect as oa,useCallback as ln}from"react";import{Box as un,useApp as ia,useInput as sa}from"ink";import{useEffect as Is,useState as Os}from"react";function Do(){let[o,e]=Os(()=>{let t=process.stdout.columns||80,r=process.stdout.rows||24;return[t,r]});return Is(()=>{let t=()=>{let r=process.stdout.columns||80,n=process.stdout.rows||24;e([r,n])};return process.stdout.on("resize",t),t(),()=>{process.stdout.off("resize",t)}},[]),o}import Fo,{useEffect as Vt,useState as Lo}from"react";import{Box as We,Text as O}from"ink";import{createContext as Ns,useContext as Ps,useReducer as Fs,useMemo as qe,useEffect as Io,useState as Ls}from"react";import{jsx as js}from"react/jsx-runtime";var Oo=Ns(null);function $s(o,e){let t=o.messages,r=t.findIndex(n=>n.id===e.id);if(r!==-1){let n=t[r],i=[...t];return i[r]={...e,id:n.id,ts:n.ts},{...o,messages:i}}if(e.groupKey)for(let n=t.length-1;n>=0;n--){let i=t[n];if(i.groupKey===e.groupKey){let a=[...t];return a[n]={...e,id:i.id,ts:i.ts,groupKey:i.groupKey},{...o,messages:a}}}return{...o,messages:[...t.slice(-380),e]}}function Ms(o,e){switch(e.type){case"SET_INPUT":return{...o,input:e.payload};case"ADD_MESSAGE":return $s(o,e.payload);case"SET_MESSAGES":return{...o,messages:e.payload};case"SET_ENGINE_STATE":return{...o,engineState:e.payload};case"ADD_COMMAND_TO_HISTORY":{if(!e.payload)return o;let t=o.commandHistory.length===0||o.commandHistory[o.commandHistory.length-1]!==e.payload?[...o.commandHistory,e.payload]:o.commandHistory;return{...o,commandHistory:t}}case"SET_HISTORY_INDEX":return{...o,historyIndex:e.payload};case"SET_IS_PROCESSING":return{...o,isProcessing:e.payload};case"SET_DEBUG_ENABLED":return{...o,debugEnabled:e.payload};case"SET_CONNECTION_STATUS":return{...o,connectionStatus:e.payload};case"SET_CONNECTION_RETRY":return{...o,connectionRetryInSec:e.payload};case"SET_REFRESH_COUNTDOWN":return{...o,refreshCountdownSec:e.payload};case"SET_DEVICE_ID":return{...o,deviceId:e.payload};case"RESET_INPUT":return{...o,input:""};case"RESET_HISTORY_INDEX":return{...o,historyIndex:-1};case"RESET_HISTORY_INDEX_IF_TYPING":if(o.historyIndex!==-1){let t=o.commandHistory[o.historyIndex];if(t!==void 0&&o.input!==t)return{...o,historyIndex:-1}}return o;default:return o}}var Us={input:"",messages:[],engineState:"IDLE",commandHistory:[],historyIndex:-1,isProcessing:!1,debugEnabled:!1,connectionStatus:"disconnected",connectionRetryInSec:null,refreshCountdownSec:null,deviceId:null};function No({engine:o,bus:e,children:t}){let[r,n]=Fs(Ms,Us),[i,a]=Ls(()=>Object.freeze({phase:o.runtime.phase,flags:Object.freeze({debug:o.runtime.flags.debug})}));Io(()=>{e.emit("ui:initialized",Date.now())},[e]),Io(()=>{let c=_=>_==="running"?"RUNNING":_==="stopping"?"STOPPING":_==="booting"?"IDLE":"STOPPED",u=e.on("notify:event",_=>{if(n({type:"ADD_MESSAGE",payload:_}),_.groupKey?.startsWith("auth:refresh"))if(_.kind==="info"){let T=_.message.match(/refresh scheduled in (\d+)s/);if(T){let x=parseInt(T[1],10);isNaN(x)||n({type:"SET_REFRESH_COUNTDOWN",payload:x})}}else _.kind==="success"&&_.message.includes("refresh completed")&&n({type:"SET_REFRESH_COUNTDOWN",payload:null})}),d=()=>{let _=o.runtime;n({type:"SET_ENGINE_STATE",payload:c(_.phase)}),n({type:"SET_DEBUG_ENABLED",payload:_.flags.debug});let T=o.configurationContext.configuration.device?.id??null;n({type:"SET_DEVICE_ID",payload:T}),a(Object.freeze({phase:_.phase,flags:Object.freeze({debug:_.flags.debug})}))};d();let f=e.on("engine:started",()=>{n({type:"SET_ENGINE_STATE",payload:"RUNNING"}),d()}),p=e.on("engine:stopping",()=>{n({type:"SET_ENGINE_STATE",payload:"STOPPING"}),d()}),h=e.on("engine:stopped",()=>{n({type:"SET_ENGINE_STATE",payload:"STOPPED"}),d()}),g=e.on("ws:connecting",()=>{n({type:"SET_CONNECTION_STATUS",payload:"connecting"}),n({type:"SET_CONNECTION_RETRY",payload:null})}),m=e.on("ws:connected",()=>{n({type:"SET_CONNECTION_STATUS",payload:"connected"}),n({type:"SET_CONNECTION_RETRY",payload:null})}),y=e.on("ws:disconnected",()=>{n({type:"SET_CONNECTION_STATUS",payload:"disconnected"}),n({type:"SET_CONNECTION_RETRY",payload:null})}),S=e.on("ws:retrying",_=>{n({type:"SET_CONNECTION_STATUS",payload:"connecting"}),n({type:"SET_CONNECTION_RETRY",payload:_.retryInSec})}),b=e.on("runtime:debug:changed",({enabled:_})=>{n({type:"SET_DEBUG_ENABLED",payload:_});let T=o.runtime;a(Object.freeze({phase:T.phase,flags:Object.freeze({debug:_})}))}),E=e.on("config:loaded",_=>{let T=_.device?.id??null;n({type:"SET_DEVICE_ID",payload:T})}),k=new ue("Welcome to DeviceLink Agent UI",{ts:Date.now(),kind:"status"}),R=new ue('Type "help" for commands. Start bridge with: start or run',{ts:Date.now(),kind:"status"});return n({type:"SET_MESSAGES",payload:[k,R]}),()=>{u.unsubscribe(),f.unsubscribe(),p.unsubscribe(),h.unsubscribe(),g.unsubscribe(),m.unsubscribe(),y.unsubscribe(),S.unsubscribe(),b.unsubscribe(),E.unsubscribe()}},[e,o,n]);let s=qe(()=>({state:r,dispatch:n,engine:o,bus:e,runtime:i}),[r,o,e,i]);return js(Oo.Provider,{value:s,children:t})}function Ie(){let o=Ps(Oo);if(!o)throw new Error("useAppContext must be used within AppContextProvider");return o}function Po(o){let{state:e}=Ie(),t=c=>!(c.category?.includes("token-refresh")||c.groupKey?.startsWith("auth:refresh")&&c.kind==="info"&&c.message.includes("refresh scheduled in")),r=o?.displayFilter??t,n=qe(()=>e.messages.filter(r),[e.messages,r]),i=qe(()=>c=>e.messages.filter(c),[e.messages]),a=qe(()=>c=>e.messages.filter(u=>u.kind===c),[e.messages]),s=qe(()=>c=>e.messages.filter(u=>u.scope===c),[e.messages]);return qe(()=>({rawMessages:e.messages,messagesToDisplay:n,getFilteredMessages:i,getMessagesByKind:a,getMessagesByScope:s}),[e.messages,n,i,a,s])}import{Fragment as Je,jsx as F,jsxs as J}from"react/jsx-runtime";function Ks(o){return o==="RUNNING"?"green":o==="STOPPING"?"yellow":o==="STOPPED"?"red":"gray"}function Bs(o){return o==="connected"?"green":o==="connecting"?"yellow":"red"}function qs(o){return o==="connected"?"connected":o==="connecting"?"trying":"disconnected"}function $o(o,e){return o.length<=e?o:o.slice(0,Math.max(0,e-3))+"..."}function Ws(o){let{state:e}=Ie(),t="DeviceLink Agent",r=e.debugEnabled,n=e.deviceId??"not signed in",i=e.engineState,a=Ks(i),s=e.isProcessing,c=e.connectionStatus,u=e.connectionRetryInSec,d=e.refreshCountdownSec,[f,p]=Lo(d),[h,g]=Lo(u);Vt(()=>{p(d)},[d]),Vt(()=>{g(u)},[u]),Vt(()=>{if(d===null||d<=0){p(null);return}p(d);let _=setInterval(()=>{p(T=>T===null||T<=1?null:T-1)},1e3);return()=>clearInterval(_)},[d]),Vt(()=>{if(u===null||u<=0){g(null);return}g(u);let _=setInterval(()=>{g(T=>T===null||T<=1?null:T-1)},1e3);return()=>clearInterval(_)},[u]);let m=qs(c),y=Bs(c),S=t.length,b=30,E=o.width-S-2,k=[];if(k.push(F(O,{color:y,children:m},"conn")),h!==null&&h>0&&k.push(J(O,{color:y,children:["retry in ",h,"s"]},"retry")),f!==null&&f>0&&f<=5&&k.push(J(O,{color:"cyan",children:["refresh in ",f,"s"]},"refresh")),E>=b){let _=$o(n,Math.max(5,E-50));return J(We,{justifyContent:"space-between",width:o.width,children:[J(We,{children:[F(O,{bold:!0,children:t}),r&&J(Je,{children:[F(O,{children:" "}),F(O,{color:"yellow",bold:!0,children:"[DEBUG]"})]})]}),J(We,{children:[s&&F(Je,{children:F(O,{dimColor:!0,children:"\u23F3 "})}),F(O,{dimColor:!0,children:"device "}),J(O,{children:[_," "]}),F(O,{children:"\xB7 "}),F(O,{color:a,children:i}),k.length>0&&J(Je,{children:[F(O,{children:" \xB7 "}),k.map((T,x)=>J(Fo.Fragment,{children:[x>0&&F(O,{children:" \xB7 "}),T]},x))]})]})]})}let R=$o(n,o.width-8);return J(We,{flexDirection:"column",width:o.width,children:[J(We,{children:[F(O,{bold:!0,children:t}),r&&J(Je,{children:[F(O,{children:" "}),F(O,{color:"yellow",bold:!0,children:"[DEBUG]"})]})]}),J(We,{children:[s&&F(Je,{children:F(O,{dimColor:!0,children:"\u23F3 "})}),F(O,{dimColor:!0,children:"device "}),J(O,{children:[R," "]}),F(O,{children:"\xB7 "}),F(O,{color:a,children:i}),k.length>0&&J(Je,{children:[F(O,{children:" \xB7 "}),k.map((_,T)=>J(Fo.Fragment,{children:[T>0&&F(O,{children:" \xB7 "}),_]},T))]})]})]})}var Mo=Ws;import Ko,{useMemo as Uo}from"react";import{Box as Js,Text as Oe}from"ink";import{jsx as Ne,jsxs as jo}from"react/jsx-runtime";function Gs(o){return o==="error"?"red":o==="warning"?"yellow":o==="success"?"green":o==="info"?"cyan":"white"}function Hs(o){return o==="error"?"ERROR":o==="warning"?"WARN":o==="success"?"SUCCESS":o==="info"?"INFO":o==="status"?"STATUS":"INFO"}function Vs(o){let e=new Date(o),t=String(e.getHours()).padStart(2,"0"),r=String(e.getMinutes()).padStart(2,"0"),n=String(e.getSeconds()).padStart(2,"0");return`${t}:${r}:${n}`}function zs(o,e){if(typeof o!="string"&&(o=String(o??"")),o.length<=e)return[o];let t=[],r="",n=o.split(/(\s+)/);for(let i of n)if((r+i).length<=e)r+=i;else{r&&t.push(r),r="";let a=i;for(;a.length>e;)t.push(a.slice(0,e)),a=a.slice(e);r=a}return r&&t.push(r),t.length>0?t:[o]}var Ys=Ko.memo(function(e){let{messagesToDisplay:t}=Po(),r=e.height??14,c=Math.max(20,e.width-9-8-2-2),u=Uo(()=>t.slice(-r),[t,r]),f=Uo(()=>u.flatMap(p=>{let h=Vs(p.ts),g=Hs(p.kind);return zs(p.message,c).map((y,S)=>({msg:p,lineIndex:S,time:S===0?h:"",kind:S===0?g:"",message:y,isContinuation:S>0}))}),[u,c]).slice(-r);return Ne(Js,{flexDirection:"column",borderStyle:"round",borderColor:"gray",paddingX:1,height:r+2,width:e.width,children:f.length===0?Ne(Oe,{dimColor:!0,children:"(no messages)"}):f.map(p=>Ne(Qs,{item:p},`${p.msg.id}:${p.lineIndex}`))})},(o,e)=>o.height===e.height&&o.width===e.width),Qs=Ko.memo(function({item:e}){return jo(Oe,{children:[e.time&&jo(Oe,{dimColor:!0,children:[e.time," "]}),e.kind&&Ne(Oe,{color:Gs(e.msg.kind),children:e.kind}),e.kind&&Ne(Oe,{children:" "}),e.isContinuation&&Ne(Oe,{dimColor:!0,children:" "}),Ne(Oe,{dimColor:e.msg.scope==="system",children:e.message})]})}),Bo=Ys;import Xs from"react";import{Box as zt,Text as cn,useFocus as Zs}from"ink";import ea from"ink-text-input";import{jsx as Ge,jsxs as qo}from"react/jsx-runtime";var ta=Xs.memo(function(e){let{state:t,dispatch:r}=Ie(),{isFocused:n}=Zs({autoFocus:!0,isActive:!t.isProcessing}),i=e.isPassphraseMode?"passphrase> ":"dlink> ",a=Math.max(10,e.width-i.length-2);return qo(zt,{flexDirection:"column",width:e.width,children:[qo(zt,{width:e.width,children:[Ge(cn,{color:e.isPassphraseMode?"cyan":"green",children:i}),Ge(zt,{width:a,children:t.isProcessing?Ge(cn,{dimColor:!0,italic:!0,children:"Processing..."}):Ge(ea,{value:t.input,onChange:s=>r({type:"SET_INPUT",payload:s}),onSubmit:e.onSubmit,placeholder:n?void 0:e.isPassphraseMode?"Enter passphrase...":'Type a command or "help"',mask:e.isPassphraseMode?"*":void 0},`input-${t.historyIndex}-${e.isPassphraseMode?"passphrase":"normal"}`)})]}),e.passphraseError&&Ge(zt,{marginTop:1,children:Ge(cn,{color:"red",children:e.passphraseError})})]})}),Wo=ta;import{Component as ra}from"react";import{Box as He,Text as Yt}from"ink";import{Fragment as na,jsx as Se,jsxs as Jo}from"react/jsx-runtime";var st=class extends ra{constructor(e){super(e),this.state={hasError:!1,error:null}}static getDerivedStateFromError(e){return{hasError:!0,error:e}}componentDidCatch(e,t){this.props.onError&&this.props.onError(e,t)}render(){return this.state.hasError&&this.state.error?Se(He,{flexDirection:"column",padding:1,children:Se(He,{borderStyle:"round",borderColor:"red",paddingX:1,children:Jo(He,{flexDirection:"column",children:[Se(Yt,{color:"red",bold:!0,children:"\u26A0\uFE0F Application Error"}),Se(He,{height:1}),Se(Yt,{color:"red",children:this.state.error.message}),this.state.error.stack&&Jo(na,{children:[Se(He,{height:1}),Se(Yt,{dimColor:!0,children:this.state.error.stack})]}),Se(He,{height:1}),Se(Yt,{dimColor:!0,children:"Press Ctrl+C to exit"})]})})}):this.props.children}};import{jsx as Ce,jsxs as ca}from"react/jsx-runtime";function aa(){let{exit:o}=ia(),{state:e,dispatch:t,engine:r,bus:n}=Ie(),[i,a]=Do(),s=i,c=a,u=ln(g=>n.emit("console:command",g),[n]),d=Math.max(5,c-5),f=Math.min(d-3,Math.max(5,Math.floor(d*.7)));sa((g,m)=>{if(m.ctrl&&g==="c"){r.requestStop("user exit");return}if(!e.isProcessing&&(m.upArrow||m.downArrow)){if(e.commandHistory.length===0)return;if(m.upArrow){let y=e.historyIndex===-1?e.commandHistory.length-1:Math.max(0,e.historyIndex-1);t({type:"SET_HISTORY_INDEX",payload:y}),t({type:"SET_INPUT",payload:e.commandHistory[y]})}else if(m.downArrow){if(e.historyIndex===-1)return;let y=e.historyIndex+1;y>=e.commandHistory.length?(t({type:"SET_HISTORY_INDEX",payload:-1}),t({type:"SET_INPUT",payload:""})):(t({type:"SET_HISTORY_INDEX",payload:y}),t({type:"SET_INPUT",payload:e.commandHistory[y]}))}}}),oa(()=>{t({type:"RESET_HISTORY_INDEX_IF_TYPING"})},[e.input,t]);let p=ln(async g=>{let m=g.trim();t({type:"RESET_INPUT"}),t({type:"RESET_HISTORY_INDEX"}),m&&t({type:"ADD_COMMAND_TO_HISTORY",payload:m}),t({type:"SET_IS_PROCESSING",payload:!0});try{await u(m)}catch(y){let S=L(y),b=new ue(`Command failed: ${S}`,{ts:Date.now(),kind:"error"});t({type:"ADD_MESSAGE",payload:b})}finally{t({type:"SET_IS_PROCESSING",payload:!1})}},[t,o,u,r]),h=ln((g,m)=>{let y=new ue(`UI Error: ${g.message}`,{ts:Date.now(),kind:"error"});t({type:"ADD_MESSAGE",payload:y})},[t]);return Ce(st,{onError:h,children:ca(un,{flexDirection:"column",padding:1,width:s,children:[Ce(Mo,{width:s-2}),Ce(un,{height:1}),Ce(Bo,{height:f,width:s-2}),Ce(un,{height:1}),Ce(Wo,{width:s-2,onSubmit:p})]})})}function dn({engine:o,bus:e}){return Ce(No,{engine:o,bus:e,children:Ce(aa,{})})}function Go(o,e){let t=o.runtime.flags;t.headless?console.log(`[agent] Headless UI (${t.headlessReason})`):console.warn(`[agent] No TTY stdin; non-interactive boot (${t.headlessReason})`),e.emit("ui:initialized",Date.now());let r=setInterval(()=>{},6e4);return new Promise(n=>{process.on("SIGTERM",()=>{clearInterval(r),n()}),process.on("SIGINT",()=>{clearInterval(r),n()})})}function Ho(o,e){let t=o.runtime.flags.headless?null:rn();if(!t)return Go(o,e);let{waitUntilExit:r}=ua(la.createElement(dn,{engine:o,bus:e}),{stdin:t});return r()}var Qt=class{name="ui-orchestrator";async onStarted(e){Ho(e.engine,e.bus)}async onStop(e){e.notifier.info("UI exited, stopping engine",{scope:"system"}),e.engine&&e.engine.requestStop("UI exited")}};import Aa from"lodash/truncate.js";import zo from"lodash/isEmpty.js";import Sa from"lodash/isString.js";import pn from"lodash/trim.js";import _a from"lodash/trimEnd.js";import Ea from"node:os";import da from"lodash/isEmpty.js";import pa from"lodash/isString.js";import{randomUUID as fa}from"node:crypto";import{existsSync as ma,mkdirSync as ha,readFileSync as ga,writeFileSync as ya}from"node:fs";import Vo from"node:path";var ba="telemetry.json";function va(){return Vo.join(Q(),ba)}function Xt(){let o=va();try{if(ma(o)){let t=JSON.parse(ga(o,"utf8"));if(pa(t.anonymous_id)&&!da(t.anonymous_id))return t.anonymous_id}}catch{}let e=fa().replace(/-/g,"");try{ha(Vo.dirname(o),{recursive:!0});let t={anonymous_id:e,created_at:new Date().toISOString()};ya(o,JSON.stringify(t,null,2),"utf8")}catch{}return e}var ka="https://eu.i.posthog.com",Ta=1e4,wa=20,Ca=500,Ra=4e3;function xa(o){return o.split(".")[0]||"unknown"}function fn(){return!Be("DLINK_TELEMETRY",!0)||Be("DO_NOT_TRACK",!1)?!1:!!Yo()}function Yo(){let o=pn(process.env.DLINK_POSTHOG_KEY);return zo(o)?void 0:o}var Zt=class{key;host;enabled;distinctId;base;queue;timer;sessionId;inFlight;constructor(){this.key=Yo(),this.host=_a(process.env.DLINK_POSTHOG_HOST||ka,"/"),this.enabled=fn(),this.distinctId=this.enabled?Xt():"disabled",this.queue=[],this.timer=null,this.sessionId=null,this.inFlight=new Set,this.base={$lib:"dlink-cli",$geoip_disable:!0,$ip:null,service:"agent",agent_version:ne(),node_version:process.versions.node.split(".")[0],os_platform:process.platform,os_release:xa(Ea.release()),arch:process.arch,is_ci:Be("CI",!1),environment:process.env.DEVICELINK_ENV??"production"}}get isEnabled(){return this.enabled}get anonymousId(){return this.distinctId}setSessionId(e){this.sessionId=Sa(e)&&!zo(pn(e))?pn(e):null}capture(e,t){if(this.enabled&&!(this.queue.length>=Ca)){if(this.queue.push({event:e,distinct_id:this.distinctId,properties:{...this.base,...this.sessionId?{$session_id:this.sessionId}:{},...t},timestamp:new Date().toISOString()}),this.queue.length>=wa){this.flush();return}this.timer||(this.timer=setTimeout(()=>void this.flush(),Ta),this.timer.unref?.())}}async flush(){if(this.timer&&(clearTimeout(this.timer),this.timer=null),!this.enabled||this.queue.length===0)return;let e=this.queue.splice(0,this.queue.length),t=this.post(e).finally(()=>this.inFlight.delete(t));this.inFlight.add(t),await t}async shutdown(){await this.flush(),await Promise.allSettled([...this.inFlight])}async post(e){if(!this.key)return;let t=new AbortController,r=setTimeout(()=>t.abort(),Ra);r.unref?.();try{await fetch(`${this.host}/batch/`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({api_key:this.key,batch:e}),signal:t.signal})}catch{}finally{clearTimeout(r)}}};var H={CLI_STARTED:"cli:session:started",CLI_EXITED:"cli:session:exited",CLI_CRASHED:"cli:session:crashed",CLI_CONFIG_LOADED:"cli:config:loaded",CLI_SIGNIN_REQUESTED:"cli:auth:signin_requested",CLI_SIGNIN_COMPLETED:"cli:auth:signin_completed",CLI_SIGNOUT:"cli:auth:signout",CLI_TOKENS_REFRESHED:"cli:auth:tokens_refreshed",CLI_WS_CONNECTED:"cli:socket:connected",CLI_WS_DISCONNECTED:"cli:socket:disconnected",CLI_WS_RETRYING:"cli:socket:retrying",CLI_SESSION_READY:"cli:socket:session_ready",CLI_SESSION_CONFLICT:"cli:socket:session_conflict",CLI_TOOL_CALL:"cli:tool:call_received"};var er=class{name="telemetry-orchestrator";client;startedAt;reachedReady;toolCalls;constructor(){this.client=new Zt,this.startedAt=Date.now(),this.reachedReady=!1,this.toolCalls=0}handlers(){return{"config:loaded":()=>{this.client.capture(H.CLI_CONFIG_LOADED)},"auth:signin:requested":()=>{this.client.capture(H.CLI_SIGNIN_REQUESTED)},"auth:signin:completed":()=>{this.client.capture(H.CLI_SIGNIN_COMPLETED,{seconds_since_start:this.elapsedSec()})},"auth:signout:requested":()=>{this.client.capture(H.CLI_SIGNOUT)},"auth:tokens:updated":()=>{this.client.capture(H.CLI_TOKENS_REFRESHED)},"ws:connected":()=>{this.client.capture(H.CLI_WS_CONNECTED)},"ws:disconnected":e=>{this.client.capture(H.CLI_WS_DISCONNECTED,{code:e.code}),this.client.setSessionId(null)},"ws:retrying":e=>{this.client.capture(H.CLI_WS_RETRYING,{attempt:e.attempt,retry_in_sec:e.retryInSec})},"auth:session_init_ack":e=>{this.reachedReady=!0,e.ok&&this.client.setSessionId(e.analytics_session_id),this.client.capture(H.CLI_SESSION_READY,{mode:e.ok?e.mode??null:null,reason:e.ok?e.reason??null:null,seconds_since_start:this.elapsedSec()})},"auth:session_conflict":()=>{this.client.capture(H.CLI_SESSION_CONFLICT)},"tool:call":e=>{this.toolCalls+=1,this.client.capture(H.CLI_TOOL_CALL,{tool:e.tool,call_index:this.toolCalls})},"engine:crashed":e=>{this.client.capture(H.CLI_CRASHED,{reason:e.reason,detail:Aa(xr(e.detail),{length:200,omission:""})||null,uptime_sec:this.elapsedSec(),reached_session_ready:this.reachedReady,tool_calls:this.toolCalls})}}}async onStart(e){this.client.capture(H.CLI_STARTED,{headless:e.headless})}async onStop(e){this.client.capture(H.CLI_EXITED,{uptime_sec:this.elapsedSec(),reached_session_ready:this.reachedReady,tool_calls:this.toolCalls}),await this.client.shutdown()}elapsedSec(){return Math.round((Date.now()-this.startedAt)/1e3)}};var tr=class{name="os-signals-orchestrator";sigint;sigterm;async onStart(e){let t=r=>{e.notifier.warn(`received ${r}`,{scope:"system"}),e.engine.requestStop(r)};this.sigint=()=>t("SIGINT"),this.sigterm=()=>t("SIGTERM"),process.on("SIGINT",this.sigint),process.on("SIGTERM",this.sigterm)}async onStarted(e){return Promise.resolve()}async onStop(e){this.sigint&&process.off("SIGINT",this.sigint),this.sigterm&&process.off("SIGTERM",this.sigterm),this.sigint=void 0,this.sigterm=void 0,e.notifier.info("OS signal handlers removed",{scope:"system"})}async onStopped(e){return Promise.resolve()}};import Pe from"node:fs";import hn from"node:path";var rr=class{handlers=new Map;subcommandHandlers=new Map;command(e,t){if(this.handlers.has(e))throw new Error(`Command '${e}' is already registered`);return this.handlers.set(e,t),this}subcommand(e,t,r){this.subcommandHandlers.has(e)||this.subcommandHandlers.set(e,new Map);let n=this.subcommandHandlers.get(e);if(n.has(t))throw new Error(`Subcommand '${e} ${t}' is already registered`);return n.set(t,r),this}commandStringToArgv(e){let t=[],r="",n=!1,i="";for(let a=0;a<e.length;a++){let s=e[a];s==='"'||s==="'"?n&&s===i?(t.push(r),r="",n=!1,i=""):n?r+=s:(r&&(t.push(r),r=""),n=!0,i=s):s===" "&&!n?r&&(t.push(r),r=""):r+=s}return(n||r)&&t.push(r),["node","script",...t]}parseString(e){let t=this.commandStringToArgv(e);return this.parse(t)}parse(e){let t=e.slice(2);if(t.length===0)return null;let r={command:"",args:[],options:{}},n=0;for(;n<t.length&&t[n]?.startsWith("--");)n++;if(n>=t.length||!t[n])return null;for(r.command=t[n],n++;n<t.length&&t[n]?.startsWith("--");)n++;for(n<t.length&&t[n]&&!t[n].startsWith("--")&&(r.subcommand=t[n],n++);n<t.length;){let i=t[n];if(i.startsWith("--")){let a=i.slice(2);if(a.includes("=")){let[s,c]=a.split("=",2);r.options[s]=c||!0}else{let s=t[n+1];s&&!s.startsWith("--")?(r.options[a]=s,n++):r.options[a]=!0}}else r.args.push(i);n++}return r}async execute(e){let t=this.parse(e);if(!t)return!1;if(t.subcommand){let n=this.subcommandHandlers.get(t.command);if(n){let i=n.get(t.subcommand);if(i)return await i(t),!0}}let r=this.handlers.get(t.command);return r?(await r(t),!0):!1}hasCommand(e,t){return t?this.subcommandHandlers.get(e)?.has(t)??!1:this.handlers.has(e)}getCommands(){return Array.from(this.handlers.keys())}};var mn=class{kind;payloadJson;constructor(e,t){this.kind=e,this.payloadJson=t}},nr=class{matchers=[];register(e){return this.matchers.push(e),this}build(e){let t=[e.command.toLowerCase()];e.subcommand&&t.push(e.subcommand.toLowerCase());for(let r of this.matchers){let n=r(t,e);if(n.handled){let i;if(n.job)i=n.job;else if(n.kind)i=this.createCommandInput(n.kind,e);else return null;return i}}return null}createCommandInput(e,t){let r={command:t.command,subcommand:t.subcommand,args:t.args,options:t.options};return new mn(e,JSON.stringify(r))}};var or=class{name="console-orchestrator";commandParser;commandJobBuilder;bridgeConnected=!1;constructor(){this.commandParser=new rr,this.commandJobBuilder=new nr,this.setupCommandMappings()}onRegister(e){}async onStart(e){e.notifier.info("Starting console orchestrator (UI)",{scope:"system"}),e.on("ws:connected",()=>{this.bridgeConnected=!0}),e.on("ws:disconnected",()=>{this.bridgeConnected=!1}),e.on("console:command",async t=>{try{let r=this.commandParser.parseString(t);if(!r){e.notifier.warn("Invalid command format");return}let n=this.commandJobBuilder.build(r);if(!n){this.reportUnknownCommand(r,e);return}switch(n.kind){case"help":for(let i of Co())e.notifier.info(i);break;case"start":this.handleStart(e);break;case"stop":this.handleStop(e);break;case"status":this.handleStatus(e);break;case"doctor":this.handleDoctor(e);break;case"roots-list":this.handleRootsList(e);break;case"roots-add":this.handleRootsAdd(r,e);break;case"signin":e.bus.emit("auth:signin:requested",void 0),e.notifier.info("Sign-in requested");break;case"signout":e.bus.emit("auth:signout:requested",void 0),e.notifier.info("Sign-out requested");break;case"debug-on":e.flags.debug=!0,e.bus.emit("runtime:debug:changed",{enabled:!0}),e.notifier.success("Debug mode enabled");break;case"debug-off":e.flags.debug=!1,e.bus.emit("runtime:debug:changed",{enabled:!1}),e.notifier.success("Debug mode disabled");break;case"replace":e.bus.emit("auth:session_conflict:resolve",{choice:"USE_NEW"}),e.notifier.info("Taking over the existing session");break;default:e.notifier.warn(`Command not implemented: ${n.kind}`);break}}catch(r){let n=L(r);e.notifier.error(`Command failed: ${n}`)}})}async onStarted(e){e.notifier.info("Console orchestrator started",{scope:"system"})}async onStopped(e){}reportUnknownCommand(e,t){let r=`${e.command}${e.subcommand?" "+e.subcommand:""}`,n=this.suggestCommand(e.command),i=n?` Did you mean "${n}"?`:' Type "help" for commands.';t.notifier.warn(`Unknown command: ${r}.${i}`)}suggestCommand(e){let t=e.toLowerCase();if(!t)return null;let r=null;for(let i of _o()){let a=this.editDistance(t,i);(r===null||a<r.distance)&&(r={name:i,distance:a})}if(!r)return null;let n=t.length<=4?1:2;return r.distance<=n?r.name:null}editDistance(e,t){let r=t.length+1,n=Array.from({length:r},(i,a)=>a);for(let i=1;i<=e.length;i++){let a=[i];for(let s=1;s<r;s++){let c=n[s-1]+(e[i-1]===t[s-1]?0:1),u=a[s-1]+1,d=n[s]+1;a[s]=Math.min(c,u,d)}n=a}return n[r-1]}isSignedIn(e){let t=e.tokens?.accessTokenExpSec;return typeof t!="number"?!1:t>Math.floor(Date.now()/1e3)}handleStart(e){let t=e.engine.configurationContext.configuration;if(!t.device?.id){e.notifier.warn("Bridge: no device identity yet. Wait for configuration to load.");return}if(!this.isSignedIn(t)){e.notifier.warn("Bridge: not signed in. Run signin first.");return}if(this.bridgeConnected){e.notifier.info("Bridge is already connected");return}e.bus.emit("auth:ws:reconnect",void 0),e.notifier.info("Bridge start requested")}handleStop(e){if(!this.bridgeConnected){e.notifier.info("Bridge is already disconnected");return}e.bus.emit("ws:disconnect",{reason:"console: stop"}),e.notifier.info("Bridge stop requested")}handleStatus(e){let t=e.engine.configurationContext.configuration,r=[["Agent",ne()],["Engine",String(e.engine.runtime.phase)],["Device",t.device?.id??"(none)"],["Device name",t.device?.name??"(none)"],["Signed in",this.isSignedIn(t)?"yes":"no"],["Bridge",this.bridgeConnected?"connected":"disconnected"],["Control URL",t.controlBaseUrl??"(unset)"],["Bridge URL",t.bridgeWsUrl??"(unset)"],["Directories",String(t.directories.length)],["Config dir",Q()]],n=r.reduce((i,[a])=>Math.max(i,a.length),0);e.notifier.info("Status:");for(let[i,a]of r)e.notifier.info(` ${i.padEnd(n)} ${a}`)}handleDoctor(e){let t=e.engine.configurationContext.configuration,r=[],n=Number.parseInt(process.versions.node.split(".")[0],10);r.push({label:"Node runtime",ok:Number.isFinite(n)&&n>=20,detail:`v${process.versions.node} (requires >= 20)`});let i=Q(),a=!1,s=i;try{Pe.mkdirSync(i,{recursive:!0}),Pe.accessSync(i,Pe.constants.W_OK),a=!0}catch(p){s=`${i} (${L(p)})`}r.push({label:"Config directory",ok:a,detail:s}),r.push({label:"Device identity",ok:!!t.device?.id&&!!t.device?.keys?.public,detail:t.device?.id?`id ${t.device.id}`:"no device id"});let c=this.isSignedIn(t);r.push({label:"Authentication",ok:c,detail:c?"access token valid":"not signed in (run signin)"}),r.push({label:"Control URL",ok:!!t.controlBaseUrl,detail:t.controlBaseUrl??"unset"}),r.push({label:"Bridge URL",ok:!!t.bridgeWsUrl,detail:t.bridgeWsUrl??"unset"}),r.push({label:"Bridge connection",ok:this.bridgeConnected,detail:this.bridgeConnected?"connected":"disconnected"});let u=t.directories.filter(p=>!Pe.existsSync(p.path));r.push({label:"Shared directories",ok:t.directories.length>0&&u.length===0,detail:t.directories.length===0?"none configured":u.length===0?`${t.directories.length} present`:`missing: ${u.map(p=>p.path).join(", ")}`});let d=r.reduce((p,h)=>Math.max(p,h.label.length),0);e.notifier.info("Doctor:");for(let p of r){let h=` ${p.ok?"PASS":"WARN"} ${p.label.padEnd(d)} ${p.detail}`;p.ok?e.notifier.success(h):e.notifier.warn(h)}let f=r.filter(p=>!p.ok).length;f===0?e.notifier.success("Doctor: all checks passed"):e.notifier.warn(`Doctor: ${f} check(s) need attention`)}handleRootsList(e){let t=e.engine.configurationContext.configuration.directories;if(t.length===0){e.notifier.info("No directories shared. Add one with: roots add <path>");return}e.notifier.info(`Shared directories (${t.length}):`);for(let r of t){let n=Pe.existsSync(r.path)?"":" (missing)";e.notifier.info(` ${r.name} ${r.path}${n}`)}}handleRootsAdd(e,t){let r=e.args[0];if(!r){t.notifier.warn("Usage: roots add <path>");return}let n=hn.resolve(r);if(!Pe.existsSync(n)){t.notifier.warn(`Directory does not exist: ${n}`);return}let i;try{i=Pe.statSync(n)}catch(u){t.notifier.warn(`Cannot read ${n}: ${L(u)}`);return}if(!i.isDirectory()){t.notifier.warn(`Not a directory: ${n}`);return}let a=t.engine.configurationContext.configuration;if(a.directories.some(u=>hn.resolve(u.path).toLowerCase()===n.toLowerCase())){t.notifier.info(`Already shared: ${n}`);return}let c={id:rt(n),name:hn.basename(n),path:n,tag:tt(n)};a.directories.push(c),t.bus.emit("config:save:requested",a),t.bus.emit("meta:data:requested:directories",void 0),t.notifier.success(`Added directory: ${n}`)}setupCommandMappings(){let e=this.commandJobBuilder;e.register(t=>{if(t.length===1){let r=t[0];if(r==="help"||r==="?")return{handled:!0,kind:"help"};if(r==="roots")return{handled:!0,kind:"roots-list"};if(r==="start"||r==="run")return{handled:!0,kind:"start"};if(r==="stop")return{handled:!0,kind:"stop"};if(r==="signin")return{handled:!0,kind:"signin"};if(r==="signout")return{handled:!0,kind:"signout"};if(r==="status")return{handled:!0,kind:"status"};if(r==="doctor")return{handled:!0,kind:"doctor"};if(r==="replace"||r==="yes"||r==="y")return{handled:!0,kind:"replace"}}return{handled:!1}}),e.register(t=>{if(t.length===2){let[r,n]=t;if(r==="roots"){if(n==="add")return{handled:!0,kind:"roots-add"};if(n==="list")return{handled:!0,kind:"roots-list"}}if(r==="debug"){if(n==="on")return{handled:!0,kind:"debug-on"};if(n==="off")return{handled:!0,kind:"debug-off"}}}return{handled:!1}}),e.register((t,r)=>{if(t.length===1&&t[0]==="debug"&&r.args.length>0){let n=r.args[0];if(n==="on")return{handled:!0,kind:"debug-on"};if(n==="off")return{handled:!0,kind:"debug-off"}}return{handled:!1}})}};import Fe from"ws";var ir=class{ws=null;state={kind:"DISCONNECTED"};stopped=!1;connecting=!1;connId=0;connectionTimeout=null;heartbeatInterval=null;heartbeatTimeout=null;awaitingPong=!1;sendQueue=[];url;abortSignal;connectionTimeoutMs;heartbeatIntervalMs;pongTimeoutMs;maxSendQueueSize;sendQueueItemTtlMs;onStateChangeCallback;onErrorCallback;onConnectedCallback;onDisconnectedCallback;onMessageCallback;constructor(e,t={}){this.url=e,this.abortSignal=t.abortSignal,this.connectionTimeoutMs=t.connectionTimeoutMs??1e4,this.heartbeatIntervalMs=t.heartbeatIntervalMs??2e4,this.pongTimeoutMs=t.pongTimeoutMs??1e4,this.maxSendQueueSize=t.maxSendQueueSize??1e3,this.sendQueueItemTtlMs=t.sendQueueItemTtlMs??6e4,this.abortSignal&&this.abortSignal.addEventListener("abort",()=>{this.stop()},{once:!0})}registerOnStateChangeCallback(e){this.onStateChangeCallback=e}registerOnErrorCallback(e){this.onErrorCallback=e}registerOnConnectedCallback(e){this.onConnectedCallback=e}registerOnDisconnectedCallback(e){this.onDisconnectedCallback=e}registerOnMessageCallback(e){this.onMessageCallback=e}getState(){return this.state}getSocket(){return this.ws}isConnected(){return this.ws?.readyState===Fe.OPEN}async connect(){if(this.stopped)throw new Error("Socket is stopped");if(!(this.connecting||this.isConnected())){this.connecting=!0,this.updateState({kind:"CONNECTING"});try{await this.connectWebSocketOnce()}catch(e){let t=e instanceof Error?e:new Error(String(e));throw this.onErrorCallback?.(t),this.updateState({kind:"DISCONNECTED"}),t}finally{this.connecting=!1}}}stop(){this.stopped=!0,this.disconnect(!0)}reset(){this.stopped=!1}disconnect(e=!1){e&&(this.stopped=!0),this.connectionTimeout&&(clearTimeout(this.connectionTimeout),this.connectionTimeout=null),this.stopHeartbeat();let t=this.ws;this.ws=null,this.connecting=!1,t&&this.closeGracefullyThenTerminate(t,1e3,"client disconnect",2e3),this.updateState({kind:"DISCONNECTED"})}send(e,t){if(this.stopped){t?.(new Error("Socket is stopped"));return}let r=this.ws;if(r&&r.readyState===Fe.OPEN){r.send(e,i=>t?.(i??void 0));return}let n=Date.now();if(this.pruneSendQueue(n),this.sendQueue.length>=this.maxSendQueueSize){t?.(new Error(`Send queue full (max ${this.maxSendQueueSize})`));return}this.sendQueue.push({data:e,cb:t,expiresAt:n+this.sendQueueItemTtlMs})}closeGracefullyThenTerminate(e,t,r,n){try{(e.readyState===Fe.OPEN||e.readyState===Fe.CONNECTING)&&e.close(t,r)}catch{}let i=setTimeout(()=>{try{e.terminate()}catch{}},n);e.once("close",()=>clearTimeout(i))}async connectWebSocketOnce(){let e=++this.connId;return new Promise((t,r)=>{if(this.stopped){r(new Error("Socket is stopped"));return}let n=!1,i=!1,a=()=>{this.connectionTimeout&&(clearTimeout(this.connectionTimeout),this.connectionTimeout=null)},s=()=>{i||(i=!0,a(),t())},c=(d,f)=>{i||(i=!0,a(),f&&this.closeGracefullyThenTerminate(f,1e3,"connect failed",1500),r(d))},u;try{u=new Fe(this.url),this.connectionTimeout=setTimeout(()=>{c(new Error(`Connection timeout: unable to connect to ${this.url} within ${this.connectionTimeoutMs}ms`),u)},this.connectionTimeoutMs),u.on("open",()=>{if(n=!0,e!==this.connId){this.closeGracefullyThenTerminate(u,1e3,"stale connection",500);return}this.ws=u,this.retryResetOnConnect(),this.updateState({kind:"CONNECTED"}),this.onConnectedCallback?.(u),this.startHeartbeat(e),this.flushSendQueue(),s()}),u.on("error",d=>{if(e!==this.connId)return;let f=d instanceof Error?d:new Error(String(d));if(!n&&!i){c(f,u);return}this.onErrorCallback?.(f)}),u.on("close",(d,f)=>{if(e!==this.connId)return;let p=f?.toString?.()||"";if(!n&&!i){this.ws=null,this.stopHeartbeat(),this.updateState({kind:"DISCONNECTED"}),c(new Error(`WebSocket closed before connection established: ${d} ${p}`),u);return}this.ws===u&&(this.ws=null),this.stopHeartbeat(),this.updateState({kind:"DISCONNECTED"}),this.onDisconnectedCallback?.(d,p||`code ${d}`)}),u.on("message",d=>{if(e!==this.connId){this.onErrorCallback?.(new Error("Message ignored - connection ID mismatch"));return}if(this.ws!==u){this.onErrorCallback?.(new Error("Message ignored - socket mismatch"));return}this.onMessageCallback?.(d)}),u.on("pong",()=>{e===this.connId&&(this.awaitingPong=!1,this.heartbeatTimeout&&(clearTimeout(this.heartbeatTimeout),this.heartbeatTimeout=null))}),this.abortSignal&&this.abortSignal.addEventListener("abort",()=>{e===this.connId&&(this.closeGracefullyThenTerminate(u,1e3,"aborted",1e3),i||c(new Error("Connection aborted"),u))},{once:!0})}catch(d){let f=d instanceof Error?d:new Error(String(d));r(f)}})}retryResetOnConnect(){}startHeartbeat(e){this.stopHeartbeat(),this.ws&&(this.awaitingPong=!1,this.heartbeatInterval=setInterval(()=>{if(this.stopped||e!==this.connId)return;let r=this.ws;if(!(!r||r.readyState!==Fe.OPEN)){if(this.awaitingPong){this.closeGracefullyThenTerminate(r,4e3,"pong timeout",1e3);return}this.awaitingPong=!0;try{r.ping()}catch{this.closeGracefullyThenTerminate(r,1011,"ping failed",1e3);return}this.heartbeatTimeout&&clearTimeout(this.heartbeatTimeout),this.heartbeatTimeout=setTimeout(()=>{if(e!==this.connId)return;let n=this.ws;n&&this.closeGracefullyThenTerminate(n,4e3,"pong timeout",1e3)},this.pongTimeoutMs)}},this.heartbeatIntervalMs))}stopHeartbeat(){this.awaitingPong=!1,this.heartbeatInterval&&(clearInterval(this.heartbeatInterval),this.heartbeatInterval=null),this.heartbeatTimeout&&(clearTimeout(this.heartbeatTimeout),this.heartbeatTimeout=null)}flushSendQueue(){let e=this.ws;if(!e||e.readyState!==Fe.OPEN)return;let t=Date.now();this.pruneSendQueue(t);let r=this.sendQueue;this.sendQueue=[];for(let n of r)e.send(n.data,i=>n.cb?.(i??void 0))}pruneSendQueue(e){if(this.sendQueue.length===0)return;let t=[];for(let r of this.sendQueue)r.expiresAt>=e?t.push(r):r.cb?.(new Error("Send dropped: queued item expired"));this.sendQueue=t}updateState(e){this.state=e,this.onStateChangeCallback?.(e)}};import{z as l}from"zod";var Da=l.object({name:l.string().min(1),description:l.string().optional(),inputSchema:l.any().optional(),outputSchema:l.any().optional(),defaults:l.record(l.string(),l.unknown()).optional(),version:l.string().optional(),errorCodes:l.array(l.string()).optional()}),Ia=l.object({id:l.string().min(1),name:l.string().min(1),path:l.string().optional(),path_hint:l.string().optional(),policy:l.any().optional()}),Oa=l.object({type:l.literal("connect"),device_id:l.string().min(1),agent_version:l.string().min(1),protocol_version:l.number().int().min(1).default(2),capabilities:l.array(l.string()).default([]),platform:l.string().optional(),hostname:l.string().optional(),device_tag:l.string().optional(),ts:l.number(),nonce:l.string().min(8),sig:l.string().min(16),access_token:l.string().optional(),refresh_token:l.string().min(20).optional(),public_key_pem:l.string().optional()}).passthrough(),Na=l.object({type:l.literal("connect_ack"),ok:l.literal(!0),session_id:l.string().min(1),device_id:l.string().min(1),server_time:l.number(),auth:l.object({device_proof:l.literal("ok"),access_token:l.enum(["ok","expired","invalid","missing"])}),next:l.array(l.enum(["refresh_access_token","session_init"])).min(1),workspace_id:l.string().optional()}).strict(),Pa=l.object({type:l.literal("connect_ack"),ok:l.literal(!1),session_id:l.string().optional(),server_time:l.number(),error:l.object({code:l.string().min(1),message:l.string().min(1)})}).strict(),Fa=l.union([Na,Pa]),La=l.object({type:l.literal("refresh_access_token"),device_id:l.string().min(1),ts:l.number(),nonce:l.string().min(8),refresh_token:l.string().min(20),sig:l.string().min(16),session_id:l.string().min(1).optional()}).strict(),$a=l.object({type:l.literal("refresh_access_token_ack"),ok:l.literal(!0),access_token:l.string().min(1),refresh_token:l.string().min(20),expires_in_sec:l.number().int().positive().optional(),next:l.array(l.literal("session_init")).default(["session_init"])}).strict(),Ma=l.object({type:l.literal("refresh_access_token_ack"),ok:l.literal(!1),error:l.object({code:l.string().min(1),message:l.string().min(1)})}).strict(),Ua=l.union([$a,Ma]),ja=l.object({type:l.literal("session_init"),device_id:l.string().min(1),session_id:l.string().min(1)}).strict(),Ka=l.object({type:l.literal("session_init_ack"),ok:l.literal(!0),device_id:l.string().min(1),session_id:l.string().min(1),mode:l.enum(["ACTIVE","ACTIVE_RECLAIMED","STANDBY"]),reason:l.string().optional(),server_time:l.number(),next:l.array(l.enum(["tools_publish","directories_publish"])).optional(),analytics_session_id:l.string().uuid().optional(),server_protocol_version:l.number().default(2),min_agent_version:l.string().optional(),target_agent_version:l.string().optional(),update_policy:l.enum(["off","notify","stage","apply_when_idle","required"]).optional(),upgrade_message:l.string().optional(),upgrade_url:l.string().optional()}).passthrough(),Ba=l.object({type:l.literal("session_init_ack"),ok:l.literal(!1),device_id:l.string().min(1),server_time:l.number(),error:l.object({code:l.string().min(1),message:l.string().min(1)}),min_agent_version:l.string().optional(),target_agent_version:l.string().optional(),update_policy:l.enum(["off","notify","stage","apply_when_idle","required"]).optional(),upgrade_message:l.string().optional(),upgrade_url:l.string().optional()}).passthrough(),qa=l.union([Ka,Ba]),Wa=l.object({type:l.literal("session_conflict"),device_id:l.string().min(1),new_session_id:l.string().min(1),max_sessions:l.number().int().positive(),active_count:l.number().int().nonnegative(),deadline_ms:l.number().int().positive(),message:l.string().min(1),choices:l.array(l.object({id:l.enum(["USE_NEW","KEEP_OLD"]),default:l.boolean().optional(),prompt:l.string().optional()})).min(1)}).strict(),Ja=l.object({type:l.literal("session_conflict_resolve"),device_id:l.string().min(1),new_session_id:l.string().min(1),choice:l.enum(["USE_NEW","KEEP_OLD"])}).strict(),Ga=l.object({type:l.literal("session_conflict_resolve_ack"),ok:l.literal(!0),device_id:l.string().min(1),choice:l.enum(["USE_NEW","KEEP_OLD"]),next:l.array(l.enum(["session_init"])).optional()}).strict(),Ha=l.object({type:l.literal("session_conflict_resolve_ack"),ok:l.literal(!1),device_id:l.string().min(1),error:l.object({code:l.string().min(1),message:l.string().min(1)})}).strict(),Va=l.union([Ga,Ha]),za=l.object({type:l.literal("quiesce"),session_id:l.string().min(1),reason:l.enum(["SESSION_REPLACED"]),deadline_ms:l.number().int().positive()}).strict(),Ya=l.object({type:l.literal("quiesce_ack"),session_id:l.string().min(1),status:l.enum(["IDLE","BUSY"])}).strict(),Qa=l.object({type:l.literal("tools_publish"),device_id:l.string().min(1),workspace_id:l.string().optional(),tools:l.array(Da).default([]),tools_version:l.string().optional()}).strict(),Xa=l.object({type:l.literal("tools_publish_ack"),ok:l.boolean(),device_id:l.string().min(1),tools_version:l.string().optional(),error:l.object({code:l.string().min(1),message:l.string().min(1)}).optional()}).strict(),Za=l.object({type:l.literal("directories_publish"),device_id:l.string().min(1),workspace_id:l.string().optional(),directories:l.array(Ia).default([])}).strict(),ec=l.object({type:l.literal("directories_publish_ack"),ok:l.boolean(),device_id:l.string().min(1),error:l.object({code:l.string().min(1),message:l.string().min(1)}).optional()}).strict(),tc=l.object({blocked_extensions:l.array(l.string()).default([]),allowed_extensions:l.array(l.string()).default([]),blocked_patterns:l.array(l.string()).default([]),max_file_size_bytes:l.number().default(10485760)}).passthrough(),rc=l.object({workspace_id:l.string(),organization_id:l.string(),workspace_name:l.string().optional(),user_id:l.string(),user_email:l.string().optional(),connector:l.string().optional(),connector_policies:l.object({max_response_chars:l.number(),timeout_ms:l.number()}).passthrough().optional(),file_policies:tc.optional()}).passthrough(),Qo=l.object({type:l.literal("tool_call"),job_id:l.string().min(1),device_id:l.string().min(1).optional(),workspace_id:l.string().optional(),tool:l.string().min(1),args:l.record(l.string(),l.unknown()).default({}),context:rc.optional()}).passthrough(),nc=l.object({type:l.enum(["error","warning","info"]),description:l.string().min(1)}),sr=l.object({type:l.literal("tool_result"),job_id:l.string().min(1),tool:l.string().min(1),ok:l.boolean(),result:l.unknown().optional(),error:l.unknown().optional(),messages:l.array(nc).optional(),timing:l.object({execution_ms:l.number(),execution_sec:l.number()}).optional()}).strict().superRefine((o,e)=>{o.ok&&o.result===void 0&&e.addIssue({code:l.ZodIssueCode.custom,message:"tool_result ok=true should include result",path:["result"]}),!o.ok&&o.error===void 0&&e.addIssue({code:l.ZodIssueCode.custom,message:"tool_result ok=false should include error",path:["error"]})}),Xo=l.object({type:l.literal("disconnect_notice"),code:l.number().int(),reason:l.string(),at:l.number()}).strict(),Zo=l.object({type:l.literal("promoted_active"),workspaceId:l.string().min(1)}).strict(),ei=l.object({type:l.literal("demoted_standby"),workspaceId:l.string().min(1)}).strict(),Ym=l.union([Oa,La,ja,Ja,Qa,Za,Qo,sr,Ya,Xo,Zo,ei]),ar=l.union([Fa,Ua,qa,Wa,Va,Xa,ec,Qo,za,Xo,Zo,ei]);import{z as v}from"zod";var oc=v.object({device_id:v.string().optional(),device_name:v.string().optional(),platform:v.string().optional(),agent_version:v.string().optional(),public_key_pem:v.string().min(1)}),Zm=v.object({device_code:v.string(),user_code:v.string(),verification_uri:v.string(),verification_uri_complete:v.string(),expires_in:v.number(),interval:v.number(),challenge_id:v.string(),challenge:v.string()}),eh=oc.extend({enrollment_jws:v.string().min(1)}),th=v.union([v.object({status:v.literal("pending"),needs_proof:v.boolean()}),v.object({status:v.literal("approved"),needs_proof:v.boolean()}),v.object({error:v.literal("access_denied")}),v.object({error:v.literal("expired_token")})]),rh=v.object({device_code:v.string(),challenge_id:v.string(),challenge:v.string(),signature_b64:v.string()}),nh=v.object({ok:v.literal(!0)}),oh=v.object({device_code:v.string(),install_id:v.string().min(8).max(64).optional()}),ih=v.object({access_token:v.string(),refresh_token:v.string(),token_type:v.literal("Bearer"),expires_in:v.number(),device_id:v.string()}),sh=v.object({device_id:v.string().min(1),refresh_token:v.string().min(1),ts:v.number().int(),nonce:v.string().min(8),sig:v.string().min(16)}),ic=v.object({ok:v.literal(!0),access_token:v.string().min(10),refresh_token:v.string().min(10),expires_in_sec:v.number().int().positive(),device_id:v.string().min(1)}),sc=v.object({ok:v.literal(!1),error:v.object({code:v.string(),message:v.string(),drift_ms:v.number().optional()})}),ah=v.union([ic,sc]);function ti(o){return{ok:!0,data:o}}function Le(o){return{ok:!1,error:o}}var cr=class{name="websocket-orchestrator";socket=null;stopped=!1;connectLoopInFlight=null;retryAttempt=0;countdownTimer=null;lastUrl=null;async onStart(e){e.notifier.info("WS: orchestrator started",{scope:"system"})}async onStarted(e){let t=e.engine.configurationContext.configuration;await this.isConnectableToBridge(t,e)&&this.ensureStarted(e,"onStarted")}async onStop(e){this.stopped=!0,this.clearCountdown(),this.socket&&(e.notifier.info("WS: stopping socket (onStop)",{scope:"system"}),this.socket.stop(),this.socket=null),e.notifier.info("WS: orchestrator stopped",{scope:"system"})}handlers(){let e=async(t,r)=>{if(this.stopped)return;let n=r.engine.configurationContext.configuration;await this.isConnectableToBridge(n,r)&&this.ensureStarted(r,t)};return{"config:cache:validated":async(t,r)=>e("config:cache:validated",r),"auth:signout:requested":async(t,r)=>{r.notifier.info("WS: sign-out requested -> disconnecting"),this.stopped=!0,this.clearCountdown(),this.socket?.stop(),this.socket=null},"ws:disconnect":async(t,r)=>{r.notifier.info("WS: disconnect requested",{scope:"system"}),this.stopped=!0,this.clearCountdown(),this.socket?.stop(),this.socket=null},"auth:ws:reconnect":async(t,r)=>{r.notifier.info("WS: reconnect requested by auth",{scope:"system"}),this.retryAttempt=0,this.clearCountdown(),this.stopped=!0,this.socket?.stop(),this.connectLoopInFlight&&await this.connectLoopInFlight.catch(()=>{}),this.socket=null,this.stopped=!1,this.ensureStarted(r,"auth:ws:reconnect")},"ws:tx":async(t,r)=>{let n=this.socket?.getSocket();if(!n||n.readyState!==1){r.notifier.info("WS: TX dropped (not connected)",{scope:"system"});return}n.send(t.text)}}}ensureStarted(e,t){if(this.connectLoopInFlight){e.notifier.info(`WS: connect loop already running (reason=${t})`,{scope:"system"});return}let r=this.socket?.getSocket();if(r&&r.readyState<=1){e.notifier.info(`WS: already connected/connecting, skipping (reason=${t})`,{scope:"system"});return}this.connectLoopInFlight=this.startOrJoinConnectLoop(e,t).finally(()=>{this.connectLoopInFlight=null})}async startOrJoinConnectLoop(e,t){this.stopped=!1;let r=e.engine.configurationContext.configuration;if(!await this.isConnectableToBridge(r,e))return;let i=String(r.bridgeWsUrl);if(this.lastUrl=i,e.notifier.info(`WS: ensure connection (reason=${t}) url=${i}`,{scope:"system"}),!this.socket){let a=new ir(i,{abortSignal:e.signal,connectionTimeoutMs:1e4,heartbeatIntervalMs:2e4,pongTimeoutMs:1e4});this.socket=a,a.registerOnStateChangeCallback(s=>this.handleStateChange(s,e)),a.registerOnErrorCallback(s=>{e.notifier.error(`WS: transport error: ${s.message}`)}),a.registerOnConnectedCallback(s=>{this.socket===a&&(this.retryAttempt=0,this.clearCountdown(),e.notifier.success(e.flags.debug?`WS: connected (${i})`:"WS: connected"),e.bus.emit("ws:connected",{url:i,attempt:0}))}),a.registerOnDisconnectedCallback((s,c)=>{if(this.socket!==a){e.notifier.info(`WS: ignoring stale disconnect (code=${s})`,{scope:"system"});return}if(e.notifier.warn(`WS: disconnected code=${s} reason=${c||"-"}`),e.bus.emit("ws:disconnected",{code:s,reason:c}),this.stopped){e.notifier.info(`WS: stopped flag set, skipping auto-reconnect (code=${s})`,{scope:"system"});return}if(s===4001){e.notifier.info("WS: auth disconnect (4001) -> waiting for auth",{scope:"system"});return}if(s===4011){this.stopped=!0,this.socket?.stop(),this.socket=null;return}if(s===4013||s===4014){e.notifier.error(`This agent is not accepted by the server (code=${s} ${c||"-"}). Upgrade with: npm i -g @devicelink/dlink`),this.stopped=!0,this.socket?.stop(),this.socket=null;return}this.ensureStarted(e,"socket:disconnected")}),a.registerOnMessageCallback(s=>{if(this.socket!==a)return;let c=typeof s=="string"?s:s.toString("utf8");e.bus.emit("ws:rx",{text:c})})}for(;!this.stopped;)try{let a=this.retryAttempt+1;e.notifier.info(`WS: connecting attempt #${a} to ${i}`,{scope:"system",groupKey:"ws:connecting"}),e.bus.emit("ws:connecting",{url:i,attempt:a}),await this.socket.connect();return}catch(a){if(this.stopped)return;this.retryAttempt+=1;let s=this.computeBackoffMs(this.retryAttempt),c=Math.max(1,Math.ceil(s/1e3)),u=L(a);e.notifier.warn(`WS: connect failed (attempt=${this.retryAttempt}) -> retry in ${c}s (${u})`,{scope:"system",category:["websocket-connect"],groupKey:"ws:connect-retry"});let d={retryInSec:c,attempt:this.retryAttempt};e.bus.emit("ws:retrying",d),this.startCountdown(c,e),await this.interruptibleSleep(s)}}async isConnectableToBridge(e,t){if(!e.device?.id)return t.notifier.error("WS: cannot connect: device.id missing"),!1;if(!e.bridgeWsUrl)return t.notifier.error("WS: cannot connect: bridgeWsUrl missing"),!1;if(!e.device?.keys?.public)return t.notifier.error("WS: cannot connect: device public key missing"),!1;let r=await t.engine.secrets.get("refreshToken");return!!r.ok&&!!r.value?!0:(t.notifier.warn("WS: not connecting (no tokens). Run signin."),!1)}handleStateChange(e,t){e.kind==="CONNECTING"&&t.notifier.info(`WS: state CONNECTING (url=${this.lastUrl})`,{scope:"system"}),e.kind==="CONNECTED"&&t.notifier.info(`WS: state CONNECTED (url=${this.lastUrl})`,{scope:"system"}),e.kind==="DISCONNECTED"&&t.notifier.info(`WS: state DISCONNECTED (url=${this.lastUrl})`,{scope:"system"})}computeBackoffMs(e){let t=Math.min(3e4,1e3*Math.pow(2,Math.min(e-1,5))),r=t*(.2*(Math.random()*2-1));return Math.max(250,Math.floor(t+r))}startCountdown(e,t){this.clearCountdown();let r=e;this.countdownTimer=setInterval(()=>{if(r-=1,r<=0){this.clearCountdown();return}let n={retryInSec:r,attempt:this.retryAttempt};t.bus.emit("ws:retrying",n)},1e3)}clearCountdown(){this.countdownTimer&&(clearInterval(this.countdownTimer),this.countdownTimer=null)}async interruptibleSleep(e){let r=Date.now();for(;Date.now()-r<e;){if(this.stopped)return;await wr(100)}}};import ri from"node:fs";import lc from"node:path";var lr=class{static save(e){let t=this.getApplicationConfigFolder(),r=lc.join(t,"config.json"),n=this.stripSecrets(e);return ri.mkdirSync(t,{recursive:!0}),ri.writeFileSync(r,JSON.stringify(n,null,2),"utf8"),r}static stripSecrets(e){let t=e.device?{id:e.device.id,name:e.device.name,tag:e.device.tag,keys:{public:e.device.keys.public}}:void 0,r=e.tokens?{accessTokenExpSec:e.tokens.accessTokenExpSec}:void 0;return{...e,device:t,tokens:r}}static getApplicationConfigFolder(){return Q()}};var ur=class{name="main-orchestrator";bootStarted=!1;saveTimer=null;saveInFlight=null;latestConfigToSave=null;saveDebounceMs=500;async onStop(e){await this.flushSaveOnStop()}handlers(){return{"ui:initialized":async(e,t)=>{this.bootStarted||(this.bootStarted=!0,t.engine.setPhase("booting"),t.notifier.info("Booting..."),t.engine.dispatch(Qn(new Kt)))},"config:save:requested":async(e,t)=>{this.latestConfigToSave=e,this.saveTimer&&clearTimeout(this.saveTimer),this.saveTimer=setTimeout(()=>{this.performSave(t)},this.saveDebounceMs)}}}async performSave(e){this.saveInFlight||(this.saveInFlight=(async()=>{for(;this.latestConfigToSave;){let t=this.latestConfigToSave;this.latestConfigToSave=null;try{let r=lr.save(t);e.notifier.info(`Config saved to: ${r}`,{scope:"system"}),e.bus.emit("config:saved",void 0)}catch(r){let n=L(r);e.notifier.error(`Failed to save configuration: ${n}`)}}})().finally(()=>{this.saveInFlight=null}),await this.saveInFlight)}async flushSaveOnStop(){this.saveTimer&&(clearTimeout(this.saveTimer),this.saveTimer=null)}};import{decodeJwt as mc}from"jose";import gn from"node:crypto";var ni={invalid_enrollment_token:"Enrollment key is invalid or could not be verified (wrong controller URL, bad signature, or server signing keys do not match the key issuer).",invalid_enrollment_scope:"Enrollment key does not match this device account or organization membership.",enrollment_jwt_expired:"This enrollment key file has expired (JWT). Issue a new key under Organization \u2192 Devices and update ENROLMENT_KEY / your .devicelink file.",enrollment_ticket_invalid:"Enrollment key could not be accepted (already used, expired, or out of sync). Issue a new key under Organization \u2192 Devices.",enrollment_ticket_already_used:"This enrollment key was already used (single-use). Issue a new key under Organization \u2192 Devices.",enrollment_ticket_expired:"This enrollment key has expired. Issue a new key under Organization \u2192 Devices.",server_misconfigured:"Server cannot verify enrollment keys (OAuth keys missing on controller).",enrollment_start_failed:"Device enrollment failed. See controller logs."};function uc(o){if(typeof o!="object"||o===null)return;let e=o;if(e.body&&typeof e.body=="object"&&e.body!==null&&"error"in e.body){let t=e.body.error;if(typeof t=="string")return t}if(o instanceof Error){let t=o.message.match(/"error"\s*:\s*"([^"]+)"/);if(t?.[1])return t[1]}}function oi(o){let e=uc(o);return e&&ni[e]?ni[e]:e?`Enrollment failed (${e}). ${L(o)}`:L(o)}var at=class{timeout=null;interval=null;totalSec=0;remainingSec=0;stepSec=1;task=null;stepCb=null;setStep(e){return this.stepSec=Math.max(1,e),this}schedule(e,t){return this.task=e,this.totalSec=t,this.remainingSec=t,this}onStep(e){return this.stepCb=e,this}start(){return this.clear(),this.remainingSec<=0?(this.runTask(),this):(this.stepCb?.(this.stepSec,this.remainingSec),this.interval=setInterval(()=>{this.remainingSec-=this.stepSec,this.remainingSec<=0?this.runTask():this.stepCb?.(this.stepSec,this.remainingSec)},this.stepSec*1e3),this)}stop(){return this.clear(),this}restart(){return this.remainingSec=this.totalSec,this.start()}runTask(){if(this.clear(),this.task)try{this.task()}catch(e){throw new Error(`Scheduler task failed: ${e}`)}}clear(){this.timeout&&(clearTimeout(this.timeout),this.timeout=null),this.interval&&(clearInterval(this.interval),this.interval=null)}};function ii(o){let{retryAfterSec:e}=o;if(typeof e=="number"&&Number.isFinite(e)&&e>=0)return{delayMs:Math.min(o.retryAfterMaxSec*1e3,Math.max(o.retryAfterMinSec*1e3,e*1e3)),honoredRetryAfter:!0};let r=Math.max(1,o.attempt),n=Math.min(r-1,o.maxDoublings),i=o.baseSec*Math.pow(2,n);return{delayMs:Math.min(o.ceilingSec*1e3,(i+o.jitterSec)*1e3),honoredRetryAfter:!1}}var dc={action:"none",target:null,belowFloor:!1,message:null,url:null};function ct(o){return{...dc,reason:o}}function pc(o){let{currentVersion:e,guidance:t,autoUpdateEnabled:r}=o;if(!t)return ct("no guidance from server");let n=t.update_policy??"off";if(n==="off")return ct("policy off");if(!pe(e))return ct("current version unreadable");let a=t.min_agent_version??null,s=t.target_agent_version??null,c=t.upgrade_message??null,u=t.upgrade_url??null,d=a!==null&&Ar(e,a),f=s??a;if(!f||!pe(f))return ct("no usable target version");if(It(e,f)>=0)return ct("already at or above the target");let p={target:f,belowFloor:d,message:c,url:u};return d?{...p,action:"required",reason:"below the minimum version"}:r?Dr(e,f)?n==="notify"?{...p,action:"notify",reason:"policy notify"}:n==="stage"?{...p,action:"stage",reason:"policy stage"}:n==="apply_when_idle"?{...p,action:"apply_when_idle",reason:"policy apply_when_idle"}:{...p,action:"required",reason:"policy required"}:{...p,action:"notify",reason:"major version change is not applied automatically"}:{...p,action:"notify",reason:"automatic updates disabled locally"}}function si(o,e){let t=pc(e);if(t.action==="none")return t;let r=fc(t,String(e.currentVersion));return t.belowFloor?o.error(r):o.warn(r),t}function fc(o,e){if(o.action==="none")return"";let t=o.target??"a newer version",r=o.belowFloor?`This agent (${e}) is below the minimum version the server accepts (${t}).`:`A newer agent is available (${e} -> ${t}).`,n=o.message?` ${o.message}`:"",i=o.url?` See ${o.url}`:"";return`${r}${n}${i}`}var ai="BAD_TS",hc=10,dr=class{name="auth-orchestrator";phase="IDLE";connectionId=null;sessionId=null;accessToken;rejectedByConflict=!1;refreshGroupKey="auth:refresh";refreshCycle=0;refreshScheduler=null;refreshRetryScheduler=null;refreshRetryAttempt=0;lastRefreshPlan;refreshInFlight;conflictTimer=null;pendingConflictRequest=null;signinStarted=!1;async onStart(e){e.notifier.info("Auth orchestrator started",{scope:"system"})}async onStop(e){this.clearTimers(),this.resetConnectionState(),e.notifier.info("Auth orchestrator stopped",{scope:"system"})}handlers(){return{"config:loaded":this.onEventConfigLoaded.bind(this),"config:cache:validated":this.onEventConfigCacheValidated.bind(this),"auth:signin:requested":this.onEventAuthSigninRequested.bind(this),"auth:signin:completed":async(e,t)=>{t.notifier.info("Auth: signin completed; requesting ws reconnect",{scope:"system"}),this.rejectedByConflict=!1,this.refreshRetryScheduler&&(this.refreshRetryScheduler.stop(),this.refreshRetryScheduler=null),this.ensureAccessExpPersisted(t,"signin"),await this.ensureRefreshScheduling(t,"signin"),t.bus.emit("auth:ws:reconnect",void 0)},"auth:signout:requested":async(e,t)=>{t.notifier.info("Signing out\u2026"),this.clearTimers(),this.accessToken=void 0;try{await t.engine.secrets.delete("refreshToken")}catch{}let n={...t.engine.configurationContext.configuration,tokens:void 0};t.engine.applyConfiguration(n),t.bus.emit("config:save:requested",n),this.resetConnectionState(),t.engine.setPhase("idle"),t.bus.emit("auth:tokens:updated",{accessToken:"",refreshToken:"",expSec:void 0}),t.bus.emit("ws:disconnect",{reason:"signout"}),t.notifier.success("Signed out successfully")},"auth:session_conflict:resolve":async(e,t)=>{this.sendSessionConflictResolve(t,e.choice)},"ws:connected":async(e,t)=>{this.resetConnectionState();let r=await t.engine.secrets.get("refreshToken"),n=!!r.ok&&!!r.value,i=!!this.accessToken;if(t.notifier.info(`Auth: ws connected; tokens access=${i} refresh=${n}`,{scope:"system"}),!i&&!n){t.notifier.warn("Auth: no tokens; run signin");return}await this.sendConnect(t)},"ws:rx":async(e,t)=>{this.onInbound(e.text,t)},"ws:disconnected":async(e,t)=>{if(t.notifier.warn(`Auth: ws disconnected code=${e.code} reason=${e.reason??"-"}`,{scope:"system"}),this.resetConnectionState(),e.code===4011){t.notifier.warn('Connection rejected - another device is active on this workspace. Run "signin" to try again.'),this.rejectedByConflict=!0,t.bus.emit("ws:disconnect",{reason:"REPLACEMENT_REJECTED"});return}if(e.code===4001){let r=await t.engine.secrets.get("refreshToken");if(!r.ok||!r.value){t.notifier.warn("Auth: token invalid and no refresh; please sign in"),await this.teardownTokens(t,"AUTH_REQUIRED");return}await this.requestRefresh(t,{force:!0,reason:"ws_4001"})&&t.bus.emit("auth:ws:reconnect",void 0)}}}}async onEventConfigLoaded(e,t){if(t.notifier.info("Initializing with cached configuration",{scope:"system"}),!e.device?.id){t.notifier.error("Device ID is not available");return}if(!e.device.keys?.public){t.notifier.error("Device public key is not available");return}t.bus.emit("config:cache:validated",void 0)}async onEventConfigCacheValidated(e,t){this.ensureAccessExpPersisted(t,"boot"),await this.ensureRefreshScheduling(t,"boot");let r=await t.engine.secrets.get("refreshToken"),n=!!r.ok&&!!r.value,i=!!this.accessToken,a=this.isAccessLikelyUsable(t);if(t.notifier.info(`Auth: cache validated; access=${i} refresh=${n} accessOk=${a}`,{scope:"system"}),!i&&!n){if(t.headless&&await yo(t.engine.argv.enrollmentFile)){t.notifier.info("Headless: enrollment certificate present; starting device authorization (approve the pending request under Organization \u2192 Devices).",{scope:"system"}),t.bus.emit("auth:signin:requested",void 0);return}t.notifier.warn("Auth: not signed in; run signin");return}}isAccessLikelyUsable(e){let t=this.accessToken;if(!t)return!1;let n=e.engine.configurationContext.configuration?.tokens?.accessTokenExpSec??this.tryDecodeExp(t);if(!n)return!0;let i=Math.floor(Date.now()/1e3);return n>i}async waitForApproval(e,t,r){let n=Date.now()+t.expires_in*1e3;r.notifier.info("Waiting for approval... (streaming)",{scope:"system"});try{return await e.deviceAuthStream({deviceCode:t.device_code,timeoutMs:Math.max(1e3,n-Date.now()),signal:r.signal})}catch(i){let a=i instanceof Error?i.message:String(i);return r.notifier.error(`Failed to wait for approval: ${a}`),null}}async onEventAuthSigninRequested(e,t){if(this.signinStarted){t.notifier.warn("Sign-in already in progress",{scope:"system"});return}this.signinStarted=!0;try{let r=t.engine.configurationContext.configuration;if(!r.controlBaseUrl){t.notifier.error("Control base URL is not set");return}if(!r.device?.id){t.notifier.error("Device ID is not available");return}if(!r.device.keys?.public){t.notifier.error("Device public key is not available");return}let n=await t.engine.secrets.get("devicePrivateKey");if(!n.ok||!n.value){t.notifier.error("Device private key is not available (secret storage)");return}let i=n.value,a=r.device.name,s=process.platform,c=r.device.keys.public,u=Er(c),d=new je(r,t.notifier);t.notifier.info("Starting device authentication...");let f=await bo(t.engine.argv.enrollmentFile),p;if(f.ok){t.notifier.info(t.engine.argv.enrollmentFile?"Using enrollment key from --enrollment-file":"Using ENROLMENT_KEY / DLINK_ENROLMENT_KEY from environment",{scope:"system"});try{p=await d.deviceEnrollmentStart({device_id:r.device.id,device_name:a,platform:s,agent_version:ne(),public_key_pem:c,enrollment_jws:f.jws})}catch(b){t.notifier.error(oi(b),{scope:"system"});return}t.notifier.info("Approve this device in the dashboard: Organization \u2192 Devices (pending), or use the verification link below.",{scope:"system"})}else{if(f.error!=="not_configured"){t.notifier.error(f.error==="empty_file"?"Enrollment key is empty":`Could not load enrollment key: ${f.error}`);return}p=await d.deviceAuthStart({device_id:r.device.id,device_name:a,platform:s,agent_version:ne(),public_key_pem:c})}t.notifier.info("DeviceLink sign-in"),t.notifier.info(`User code: ${p.user_code}`),t.notifier.info(`Device fingerprint: ${u}`),t.notifier.info(`Verification URL: ${p.verification_uri_complete}`);let h=await this.waitForApproval(d,p,t);if(!h)return;if("error"in h){if(h.error==="access_denied"){t.notifier.error("Sign-in denied in browser.");return}if(h.error==="expired_token"){t.notifier.error("Sign-in expired. Run `signin` again.");return}t.notifier.error(`Unexpected error: ${h.error}`);return}if(h.status!=="approved"){t.notifier.error(`Unexpected status: ${h.status}`);return}if(h.needs_proof){t.notifier.info("Sending proof-of-possession signature...",{scope:"system"});let b=gn.createPrivateKey(i),E=Buffer.from(p.challenge,"base64url"),R=gn.sign(null,E,b).toString("base64");await d.deviceAuthProve({device_code:p.device_code,challenge_id:p.challenge_id,challenge:p.challenge,signature_b64:R}),t.notifier.info("Proof-of-possession verified",{scope:"system"})}t.notifier.info("Fetching tokens...",{scope:"system"});let g=await d.deviceAuthToken({device_code:p.device_code,install_id:fn()?Xt():void 0}),m="tag";await t.engine.secrets.set("refreshToken",g.refresh_token),this.accessToken=g.access_token;let y=this.tryDecodeExp(g.access_token),S={...r,controlBaseUrl:r.controlBaseUrl,device:{...r.device,id:g.device_id,name:a,tag:m,keys:{public:c}},tokens:{...r.tokens??{},accessTokenExpSec:y??void 0}};t.engine.applyConfiguration(S),t.bus.emit("config:save:requested",S),t.bus.emit("auth:signin:completed",void 0),t.notifier.success("Signed in successfully \u2705")}catch(r){let n=se(r);if(n&&["ECONNREFUSED","ENOTFOUND","ETIMEDOUT"].includes(n)){t.notifier.error(`Network error: ${L(r)}`);return}t.notifier.error(`Sign-in failed: ${L(r)}`)}finally{this.signinStarted=!1}}async sendConnect(e){let r=e.engine.configurationContext.configuration.device,n=await e.engine.secrets.get("devicePrivateKey");if(!r?.id||!n.ok||!n.value){e.notifier.warn("Cannot connect: device not configured");return}let i=n.value,a=await e.engine.secrets.get("refreshToken"),s=a.ok?a.value:void 0,c=Date.now(),u=gn.randomBytes(16).toString("base64url"),d=kr({deviceId:r.id,ts:c,nonce:u,privateKeyPem:i}),f={type:"connect",device_id:r.id,agent_version:ne(),protocol_version:2,capabilities:["file_policies","context_v2"],ts:c,nonce:u,sig:d,platform:process.platform,hostname:r.name,device_tag:r.tag,access_token:this.accessToken,refresh_token:s,public_key_pem:r.keys.public};this.phase="CONNECT_SENT",e.notifier.info("Auth: sending connect",{scope:"system"}),this.send(e,f)}async sendSessionInit(e,t){let n=e.engine.configurationContext.configuration.device;if(!n?.id||!this.sessionId){e.notifier.error("Cannot send session_init: device ID or session ID not available");return}let i={type:"session_init",device_id:n.id,session_id:this.sessionId};this.phase="WAIT_SESSION_INIT_ACK",e.notifier.info("Auth: sending session_init",{scope:"system"}),this.send(e,i)}onInbound(e,t){let r;try{r=JSON.parse(e)}catch{t.notifier.error("Unexpected message format");return}let n=ar.safeParse(r);if(!n.success){let a=n.error.message;t.notifier.error(`Unexpected message format: ${a}`);return}let i=n.data;switch(i.type){case"connect_ack":t.bus.emit("auth:connect_ack",i),this.onConnectAck(t,i);return;case"session_init_ack":t.bus.emit("auth:session_init_ack",i),this.onSessionInitAck(t,i);return;case"disconnect_notice":this.onDisconnectNotice(t,i);return;case"session_conflict":t.bus.emit("auth:session_conflict",i),this.handleSessionConflict(t,i);return;case"quiesce":this.handleQuiesce(t,i);return;default:return}}async handleSessionConflict(e,t){this.conflictTimer&&(clearTimeout(this.conflictTimer),this.conflictTimer=null),this.pendingConflictRequest=t,e.notifier.warn(`Another device is already connected to this workspace. Type "replace" to disconnect it, or this connection will be rejected in ${Math.round(t.deadline_ms/1e3)}s.`);let r=Math.min(t.deadline_ms,15e3);this.conflictTimer=setTimeout(()=>{this.conflictTimer=null,this.pendingConflictRequest?.new_session_id===t.new_session_id&&(e.notifier.info("No response - keeping existing device connected."),this.sendSessionConflictResolve(e,"KEEP_OLD"))},r)}async handleQuiesce(e,t){if(e.notifier.warn("This device is being replaced by another connection. Finishing in-flight operations..."),e.engine.hasRunningJobs?.()??!1){let i=Math.min(t.deadline_ms-2e3,12e3),a=Date.now();for(;Date.now()-a<i&&(e.engine.hasRunningJobs?.()??!1);)await new Promise(s=>setTimeout(s,500))}let n=e.engine.hasRunningJobs?.()??!1?"BUSY":"IDLE";this.send(e,{type:"quiesce_ack",session_id:t.session_id,status:n}),e.notifier.info("Device replaced by new connection. Disconnecting.")}sendSessionConflictResolve(e,t){let r=this.pendingConflictRequest;r&&(this.conflictTimer&&(clearTimeout(this.conflictTimer),this.conflictTimer=null),this.pendingConflictRequest=null,this.send(e,{type:"session_conflict_resolve",device_id:r.device_id,new_session_id:r.new_session_id,choice:t}))}async onConnectAck(e,t){if(!t.ok){e.notifier.error(`Connect rejected: ${t.error.code} ${t.error.message}`),this.phase="IDLE";return}this.sessionId=t.session_id;let r=t.auth.access_token;if(e.notifier.info(`Auth: connect_ack ok auth.access=${r} next=[${t.next.join(",")}]`,{scope:"system"}),t.next.includes("refresh_access_token")){let n=await e.engine.secrets.get("refreshToken");if(!n.ok||!n.value){e.notifier.warn("Auth: server requested refresh but no refresh token; please sign in"),await this.teardownTokens(e,"NO_REFRESH_TOKEN");return}await this.requestRefresh(e,{force:!0,reason:"connect_ack_next"})?e.bus.emit("auth:ws:reconnect",void 0):e.notifier.warn("Auth: refresh failed; please sign in");return}if(t.next.includes("session_init")){await this.sendSessionInit(e,t);return}if(r==="invalid"||r==="missing"){e.notifier.warn("Auth: access token invalid; please sign in again"),await this.teardownTokens(e,"INVALID_ACCESS");return}e.notifier.warn("Auth: connect_ack had no next; staying idle",{scope:"system"})}onSessionInitAck(e,t){if(!t.ok){e.notifier.error(`Session init failed: ${t.error.code} ${t.error.message}`),t.error.code==="AGENT_BELOW_MINIMUM"&&this.surfaceUpdateGuidance(e,t),this.phase="IDLE";return}this.sessionId=t.session_id,this.phase="READY",e.notifier.success(`Session ready (${t.mode})`),this.surfaceUpdateGuidance(e,t),this.ensureRefreshScheduling(e,"manual"),t.next&&(t.next.includes("tools_publish")&&e.bus.emit("meta:data:requested:tools",void 0),t.next.includes("directories_publish")&&e.bus.emit("meta:data:requested:directories",void 0))}surfaceUpdateGuidance(e,t){return si({warn:n=>e.notifier.warn(n),error:n=>e.notifier.error(n)},{currentVersion:ne(),guidance:t,autoUpdateEnabled:Be("DLINK_AUTO_UPDATE",!0)})}onDisconnectNotice(e,t){e.notifier.warn(`Server disconnect_notice: ${t.reason} (code=${t.code})`)}async requestRefresh(e,t){return this.refreshInFlight?this.refreshInFlight:(this.refreshInFlight=(async()=>{try{return await this.withTimeout(this.dispatchRefreshJob(e,t),6e4,"Refresh operation")}catch(r){throw r instanceof Error&&r.message.includes("timed out")&&e.notifier.error(`Auth: refresh timed out: ${r.message}`,{category:["token-refresh"],groupKey:"auth:refresh-failure"}),r}finally{this.refreshInFlight=void 0}})(),this.refreshInFlight)}async withTimeout(e,t,r){return Promise.race([e,new Promise((n,i)=>{setTimeout(()=>{i(new Error(`${r} timed out after ${t}ms`))},t)})])}async dispatchRefreshJob(e,t){let r=await e.engine.secrets.get("refreshToken");if(!r.ok||!r.value)return!1;if(!t.force){let s=await this.shouldRefreshNow(e);if(!s.ok)return e.notifier.info(`Auth: skip refresh (${s.reason})`,{scope:"system",category:["token-refresh"]}),!1}e.notifier.info(`Auth: dispatch refresh job (${t.reason})`,{scope:"system",category:["token-refresh"]});let n=ao(new Bt(t.reason)),i=await e.engine.dispatch(n);if(e.notifier.info(`Auth: refresh job dispatch completed (status=${i.status})`,{scope:"system",category:["token-refresh"]}),i.status!=="success"){let s=i.error?.message??"Unknown error";e.notifier.error(`Auth: refresh job failed: ${s}`,{category:["token-refresh"],groupKey:"auth:refresh-failure"});let c=i.output;return c?.errorCode&&Qr.includes(c.errorCode)?(await this.teardownTokens(e,c.errorCode),!1):(this.scheduleRefreshRetry(e,t.reason,c?.retryAfterSec,c?.errorCode,c?.driftMs),!1)}let a=i.output;if(!a.ok||!a.accessToken){let s=a.error??"Unknown refresh failure";return e.notifier.error(`Auth: refresh failed: ${s}`,{category:["token-refresh"],groupKey:"auth:refresh-failure"}),a.errorCode&&Qr.includes(a.errorCode)?(await this.teardownTokens(e,a.errorCode),!1):(this.scheduleRefreshRetry(e,t.reason,a.retryAfterSec,a.errorCode,a.driftMs),!1)}return this.refreshRetryAttempt=0,this.accessToken=a.accessToken,this.persistTokens(e,a.accessToken,a.expiresInSec),await this.ensureRefreshScheduling(e,"manual"),e.bus.emit("auth:tokens:updated",{accessToken:a.accessToken,refreshToken:a.refreshToken??"",expSec:e.engine.configurationContext.configuration.tokens?.accessTokenExpSec}),!0}scheduleRefreshRetry(e,t,r,n,i){this.refreshRetryScheduler&&this.refreshRetryScheduler.stop(),this.refreshRetryAttempt+=1,n===ai&&this.warnAboutClockSkew(e,i);let a=this.computeRefreshRetryMs(e,t,r,n),s=Math.max(1,Math.ceil(a/1e3));this.refreshRetryScheduler=new at().schedule(()=>{this.requestRefresh(e,{force:!0,reason:"retry"}).then(c=>{c&&e.bus.emit("auth:ws:reconnect",void 0)})},s).start()}warnAboutClockSkew(e,t){let r=typeof t=="number"&&Number.isFinite(t)?`about ${Math.round(Math.abs(t)/1e3)}s`:"more than a minute";e.notifier.error(`Auth: this device\u2019s clock is ${r} away from the server, so sign-in cannot be renewed. Correct the system time, then run signin.`,{category:["token-refresh"],groupKey:"auth:clock-skew"})}computeRefreshRetryMs(e,t,r,n){let i=e.engine.configurationContext.loader,a=i.getConfigValue("DL_ACCESS_REFRESH_RETRY_AFTER_MIN_SEC",{defaultValue:1}),s=i.getConfigValue("DL_ACCESS_REFRESH_RETRY_AFTER_MAX_SEC",{defaultValue:300}),c=i.getConfigValue("DL_ACCESS_REFRESH_RETRY_BASE_SEC",{defaultValue:2}),u=i.getConfigValue("DL_ACCESS_REFRESH_RETRY_JITTER_SEC",{defaultValue:1}),d=i.getConfigValue("DL_ACCESS_REFRESH_RETRY_MAX_SEC",{defaultValue:30}),f=i.getConfigValue("DL_ACCESS_REFRESH_CLOCK_SKEW_MIN_SEC",{defaultValue:300}),p=Math.max(1,this.refreshRetryAttempt),h=n===ai?Math.max(d,f):d,{delayMs:g,honoredRetryAfter:m}=ii({attempt:p,baseSec:c,jitterSec:Math.floor(Math.random()*(u+1)),ceilingSec:h,maxDoublings:hc,retryAfterSec:r,retryAfterMinSec:a,retryAfterMaxSec:s}),y=Math.max(1,Math.ceil(g/1e3));return e.notifier.warn(m?`Auth: refresh retry scheduled in ${y}s (honoring Retry-After, ${t})`:`Auth: refresh retry scheduled in ${y}s (${t}, attempt ${p})`,{scope:"system",category:["token-refresh"],groupKey:"auth:refresh-retry"}),g}async ensureRefreshScheduling(e,t){let r=await e.engine.secrets.get("refreshToken");!r.ok||!r.value||(this.accessToken&&!e.engine.configurationContext.configuration.tokens?.accessTokenExpSec&&this.ensureAccessExpPersisted(e,t),this.refreshRetryScheduler&&(this.refreshRetryScheduler.stop(),this.refreshRetryScheduler=null),this.scheduleSilentRefresh(e,t))}async scheduleSilentRefresh(e,t){let r=await e.engine.secrets.get("refreshToken");if(!r.ok||!r.value)return;this.refreshScheduler&&this.refreshScheduler.stop();let n=e.engine.configurationContext.loader,i=n.getConfigValue("DL_ACCESS_REFRESH_MIN_DELAY_SEC",{defaultValue:2}),a=e.engine.configurationContext.configuration,s=this.accessToken,c=a.tokens?.accessTokenExpSec??(s?this.tryDecodeExp(s):null),u=c??null,d=this.getRefreshGroupKey(e);if(this.lastRefreshPlan&&this.lastRefreshPlan.exp===u&&this.lastRefreshPlan.groupKey===d)return;this.lastRefreshPlan={exp:u,groupKey:d};let f=b=>{let E=new at().schedule(()=>{this.requestRefresh(e,{force:b.force,reason:"timer"}).then(k=>{k&&e.notifier.success("Auth: refresh completed",{scope:"system",groupKey:d,category:["token-refresh"]})})},b.delaySec);b.log&&(E=E.onStep((k,R)=>b.log(R))),this.refreshScheduler=E.start()};if(!c){e.headless||e.notifier.info(`Auth: no exp; scheduling refresh in ${i}s [${t}]`,{scope:"system",groupKey:d,category:["token-refresh"]}),f({delaySec:i,force:!0});return}let p=Math.floor(Date.now()/1e3),h=n.getConfigValue("DL_ACCESS_REFRESH_MARGIN_SEC",{defaultValue:5}),g=n.getConfigValue("DL_ACCESS_REFRESH_JITTER_SEC",{defaultValue:2}),m=g>0?Math.floor(Math.random()*(g+1)):0,y=c-h-m,S=Math.max(i,y-p);e.headless||e.notifier.info(`Auth: refresh scheduled in ${S}s (exp=${c} now=${p} margin=${h} jitter=${m}) [${t}]`,{scope:"system",groupKey:d,category:["token-refresh"]}),f({delaySec:S,force:!1,log:b=>{e.headless||e.notifier.info(`Auth: refresh scheduled in ${b}s (exp=${c} now=${p} margin=${h} jitter=${m}) [${t}]`,{scope:"system",groupKey:d,category:["token-refresh"]})}})}getRefreshGroupKey(e){return this.refreshCycle++,`${this.refreshGroupKey}:${this.refreshCycle}`}async shouldRefreshNow(e){let t=await e.engine.secrets.get("refreshToken");if(!t.ok||!t.value)return{ok:!1,reason:"no_refresh_token"};let r=e.engine.configurationContext.configuration,n=this.accessToken,i=r.tokens?.accessTokenExpSec??(n?this.tryDecodeExp(n):null);if(!i)return{ok:!0,reason:"no_exp_assume_refresh"};let a=Math.floor(Date.now()/1e3),c=e.engine.configurationContext.loader.getConfigValue("DL_ACCESS_REFRESH_MARGIN_SEC",{defaultValue:300}),u=i-a;return u<=c?{ok:!0,reason:`within_margin remaining=${u}s margin=${c}s`}:{ok:!1,reason:`too_early remaining=${u}s margin=${c}s`}}ensureAccessExpPersisted(e,t){let r=e.engine.configurationContext.configuration,n=this.accessToken,i=r.tokens?.accessTokenExpSec;if(!n||typeof i=="number"&&Number.isFinite(i))return;let a=this.tryDecodeExp(n);if(!a)return;let s={...r,tokens:{...r.tokens??{},accessTokenExpSec:a}};e.engine.applyConfiguration(s),e.bus.emit("config:save:requested",s),e.notifier.info(`Auth: derived accessTokenExpSec=${a} (${t})`,{scope:"system"})}persistTokens(e,t,r){let n=e.engine.configurationContext.configuration,i=this.tryDecodeExp(t),a=typeof r=="number"&&Number.isFinite(r)?Math.floor(Date.now()/1e3)+r:void 0,s={...n,tokens:{...n.tokens??{},accessTokenExpSec:i??a}};e.engine.applyConfiguration(s),e.bus.emit("config:save:requested",s),e.notifier.info(`Auth: tokens updated exp=${s.tokens?.accessTokenExpSec??"-"}`,{scope:"system"})}async teardownTokens(e,t){this.accessToken=void 0;let n={...e.engine.configurationContext.configuration,tokens:void 0};e.engine.applyConfiguration(n),e.bus.emit("config:save:requested",n),this.clearTimers(),this.resetConnectionState(),e.bus.emit("auth:signout:requested",void 0),e.notifier.warn(`Auth: signed out (${t}). Please run signin.`)}tryDecodeExp(e){try{let t=mc(e);return typeof t.exp=="number"?t.exp:null}catch{return null}}send(e,t){e.bus.emit("ws:tx",{text:JSON.stringify(t)})}resetConnectionState(){this.connectionId=null,this.sessionId=null,this.phase="IDLE"}clearTimers(){this.refreshScheduler&&(this.refreshScheduler.stop(),this.refreshScheduler=null),this.refreshRetryScheduler&&(this.refreshRetryScheduler.stop(),this.refreshRetryScheduler=null),this.conflictTimer&&clearTimeout(this.conflictTimer),this.conflictTimer=null,this.pendingConflictRequest=null}};import vn from"node:fs/promises";import Sn from"node:path";var G=class{config;constructor(e){this.config=e}async execute(e){try{let t=await this.executeTool(e);return ti(t)}catch(t){return X(t)?Le(t):Le($(t,{message:"An unexpected error occurred"}))}}};import ui from"node:path";import ci from"node:fs";import gc from"lodash/get.js";function he(o){return gc(o,["context","file_policies"])}function yc(o){let e=o.replace(/\\/g,"/").toLowerCase();return e.includes("/secrets/")||e.startsWith("secrets/")?{shouldIgnore:!0,reason:"denylist:secrets",matchedRule:"secrets/"}:e.includes("/.env")||e.startsWith(".env")?{shouldIgnore:!0,reason:"denylist:env",matchedRule:".env"}:e.includes("/id_rsa")||e.startsWith("id_rsa")?{shouldIgnore:!0,reason:"denylist:ssh",matchedRule:"id_rsa"}:e.includes("/id_ed25519")||e.startsWith("id_ed25519")?{shouldIgnore:!0,reason:"denylist:ssh",matchedRule:"id_ed25519"}:e.endsWith(".pem")?{shouldIgnore:!0,reason:"denylist:pem",matchedRule:"*.pem"}:e.endsWith(".key")?{shouldIgnore:!0,reason:"denylist:key",matchedRule:"*.key"}:e.endsWith(".p12")?{shouldIgnore:!0,reason:"denylist:p12",matchedRule:"*.p12"}:e.endsWith(".pfx")?{shouldIgnore:!0,reason:"denylist:pfx",matchedRule:"*.pfx"}:null}var li=[{pattern:"node_modules",type:"exact"},{pattern:".git",type:"exact"},{pattern:"dist",type:"exact"},{pattern:"build",type:"exact"},{pattern:".next",type:"exact"},{pattern:".cache",type:"exact"},{pattern:".DS_Store",type:"exact"},{pattern:"Thumbs.db",type:"exact"},{pattern:"*.tmp",type:"glob"},{pattern:"*.log",type:"glob"},{pattern:".env*",type:"glob"}];function bc(o,e,t){let r=o.directories.find(a=>a.id===e);if(!r)return li;let n=[],i=ui.join(r.path,".devicelinkignore");if(ci.existsSync(i))try{let s=ci.readFileSync(i,"utf-8").split(`
7
+ `).map(c=>c.trim()).filter(c=>c&&!c.startsWith("#"));for(let c of s)c.includes("*")||c.includes("?")?n.push({pattern:c,type:"glob"}):c.startsWith("/")?n.push({pattern:c.slice(1),type:"prefix"}):n.push({pattern:c,type:"exact"})}catch(a){console.warn(`[ignore] Failed to read .devicelinkignore: ${a}`)}return n.push(...li),n}function Z(o,e,t,r,n){let i=yc(o);if(i)return i;if(n&&!e){let u=ui.extname(o).toLowerCase();if(u&&n.blocked_extensions?.includes(u))return{shouldIgnore:!0,reason:"policy:blocked_extension",matchedRule:u};if(n.allowed_extensions&&n.allowed_extensions.length>0&&u&&!n.allowed_extensions.includes(u))return{shouldIgnore:!0,reason:"policy:not_in_allowlist",matchedRule:u};if(n.blocked_patterns){for(let d of n.blocked_patterns)if(yn(o,d,!1))return{shouldIgnore:!0,reason:"policy:blocked_pattern",matchedRule:d}}}let a=bc(r,t.directory_id,t.workspace_id),s=o.replace(/\\/g,"/"),c=s.split("/").filter(Boolean);for(let u of a){let d=!1;if(u.type==="exact"?d=c.some(f=>f===u.pattern):u.type==="prefix"?d=s.startsWith(u.pattern):u.type==="glob"&&(!u.pattern.includes("/")&&!u.pattern.includes("*")&&!u.pattern.includes("?")?d=c.some(f=>f.toLowerCase()===u.pattern.toLowerCase()):u.pattern.includes("/")?d=yn(s,u.pattern,e):d=c.some(f=>yn(f,u.pattern,e))),d)return{shouldIgnore:!0,matchedRule:u.pattern}}return{shouldIgnore:!1}}function yn(o,e,t){let r="^",n=0;for(;n<e.length;){let i=e[n];i==="*"?n+1<e.length&&e[n+1]==="*"?(r+=".*",n+=2):(r+="[^/]*",n++):i==="?"?(r+="[^/]",n++):"\\^$|.+(){}[]".includes(i)?(r+="\\"+i,n++):(r+=i,n++)}r+="$";try{return new RegExp(r,"i").test(o)}catch{return o.toLowerCase().includes(e.toLowerCase())}}import bn from"node:fs/promises";import ce from"node:path";function di(o){let e=(o??"").trim();if(!e)return".";if(e.includes("\0"))throw I("INVALID_INPUT","Path contains null byte.");if(e.startsWith("/"))throw I("PATH_ESCAPE","Absolute paths are not allowed. Use a path relative to the root.",{hint:'Use relative paths like "src/file.ts" or "." for the root directory. Paths must be relative to the configured root.'});if(/^[a-zA-Z]:[\\/]/.test(e)||e.startsWith("\\\\")||e.startsWith("//"))throw I("PATH_ESCAPE","Absolute/drive/UNC paths are not allowed. Use a path relative to the root.",{hint:'Use relative paths like "src/file.ts" or "." for the root directory. Do not use drive letters (C:) or UNC paths (\\\\server).'});let t=e.replace(/\\/g,"/");if(/^[a-zA-Z]+:\/\//.test(t))throw I("INVALID_INPUT","URL-style paths are not allowed.",{hint:'Paths cannot use URL schemes (like "http://" or "file://"). Use simple relative paths like "src/file.ts".'});let r=ce.posix.normalize(t);if(r===".."||r.startsWith("../"))throw I("PATH_ESCAPE","Path escapes the root.",{hint:'Paths cannot use ".." to escape the configured root directory. Use paths relative to the root, like "src/file.ts" or "subdir/file.ts".'});return r===""?".":r}async function oe(o,e){let t=di(e),r=ce.resolve(o),n=await bn.realpath(r),i=ce.resolve(n,t==="."?"":t),a=ce.relative(n,i);if(a.startsWith("..")||ce.isAbsolute(a))throw I("PATH_ESCAPE","Path escapes the root.",{hint:"The resolved path escapes the configured root directory. Ensure the path stays within the root boundaries."});try{let s=await bn.realpath(i),c=ce.relative(n,s);if(c.startsWith("..")||ce.isAbsolute(c))throw I("PATH_ESCAPE","Symlink/junction escape detected.",{hint:"A symlink or junction in the path resolves outside the configured root directory. Ensure all symlinks stay within the root boundaries."});return{absTarget:s,rel:t}}catch(s){if(se(s)!=="ENOENT")throw s;let c=t.split("/").filter(Boolean);for(let u=c.length;u>0;u--){let d=c.slice(0,u).join("/"),f=c.slice(u).join("/"),p=ce.resolve(n,d);try{let h=await bn.realpath(p),g=ce.relative(n,h);if(g.startsWith("..")||ce.isAbsolute(g))throw I("PATH_ESCAPE","Symlink/junction escape detected.",{hint:"A symlink or junction in the path resolves outside the configured root directory. Ensure all symlinks stay within the root boundaries."});return{absTarget:f?ce.join(h,f):h,rel:t}}catch(h){if(se(h)==="ENOENT")continue;throw h}}throw I("NOT_FOUND","Path not found.",{hint:"The specified path does not exist. Verify the path is correct and that the file or directory exists within the configured root."})}}function N(o){return{workspace_id:o.workspace_id,device_id:o.device_id,directory_id:o.directory_id,path:di(o.path)}}function B(o,e){let t=String(se(o)??"");return t==="ENOENT"?Ve("NOT_FOUND","Path not found.",{fs_code:t}):t==="ENOTDIR"?Ve("NOT_A_DIRECTORY","Path is not a directory.",{fs_code:t}):t==="EISDIR"?Ve("NOT_A_FILE","Path is a directory, not a file.",{fs_code:t}):t==="EACCES"||t==="EPERM"?Ve("PERMISSION_DENIED","Permission denied.",{fs_code:t}):Ve("INTERNAL",e,{fs_code:t})}function I(o,e,t){return Ve(o,e,t)}function Ve(o,e,t){return{code:o,message:e,details:t}}function Y(o,e){if(Array.isArray(o.directories)&&o.directories.length===0)throw I("UNKNOWN_DIRECTORY","No directories configured.",{hint:"No directories configured. This may happen if the agent was restarted with different directory configuration. If you have only one directory configured, it will be used automatically.",requested_directory_id:e,available_directory_ids:o.directories.map(r=>r.id),available_paths:o.directories.map(r=>r.path),directory_count:o.directories.length});let t=o.directories.find(r=>r.id===e);if(!t&&o.directories.length===1&&(t=o.directories[0]),!t){let r=o.directories.map(a=>a.id).join(", ")||"none",n=o.directories.map(a=>a.path).join(", ")||"none",i=e===void 0?"undefined (missing)":String(e);throw I("UNKNOWN_DIRECTORY",`Unknown directory_id: ${i}`,{hint:`The directory_id "${i}" does not match any configured directory. Available directory IDs: ${r}. Available paths: ${n}. This may happen if the agent was restarted with different directory configuration. If you have only one directory configured, it will be used automatically.`,requested_directory_id:e,available_directory_ids:o.directories.map(a=>a.id),available_paths:o.directories.map(a=>a.path),directory_count:o.directories.length})}return t}function P(o,e,t,r,n){let i={operation:o,path:e.path||".",directory_id:e.directory_id,workspace_id:e.workspace_id,device_id:e.device_id,success:t,error:r?r.substring(0,200):void 0,metadata:n,timestamp:Date.now()},a=`[FS_AUDIT] ${o} ${t?"SUCCESS":"FAILED"} directory=${i.directory_id} path=${i.path}${i.workspace_id?` workspace=${i.workspace_id}`:""}${i.device_id?` device=${i.device_id}`:""}${i.error?` error=${i.error}`:""}${n?` ${JSON.stringify(n)}`:""}`}import pi from"lodash/orderBy.js";import vc from"lodash/clamp.js";import Sc from"lodash/take.js";import _c from"lodash/map.js";function Ec(o,e,t){return pi(o,[e==="name"?"name":e==="mtime"?n=>n.mtime??0:n=>n.size??0],[t])}function kc(o,e){return e==="all"?o:e==="files"?o.filter(t=>t.type==="file"||t.type==="symlink"):e==="dirs"?o.filter(t=>t.type==="dir"):o}var pr=class extends G{async executeTool(e){let t=e.directory_id,r=Y(this.config,t),{absTarget:n,rel:i}=await oe(r.path,e.path),a=vc(e.limit??200,1,1e3),s=Math.max(e.offset??0,0),c=e.include_hidden??!1,u=e.only??"all",d=e.sort??"name",f=e.order??"asc",p=e.detail??"stat";try{if(!(await vn.stat(n)).isDirectory()){let D=N({workspace_id:e.workspace_id,device_id:e.device_id,directory_id:t,path:i});throw P("list",D,!1,"Path is not a directory."),I("NOT_A_DIRECTORY","Path is not a directory.",{path:i,hint:"The path must point to a directory, not a file. Use fs_read to read files, or provide a directory path for listing."})}}catch(w){if(X(w)){let D=N({workspace_id:e.workspace_id,device_id:e.device_id,directory_id:t,path:i});throw P("list",D,!1,$(w).message),w}throw B(w,"Failed to list directory.")}let h;try{h=await vn.readdir(n,{withFileTypes:!0})}catch(w){throw B(w,"Failed to read directory.")}let g=c?h:h.filter(w=>!w.name.startsWith(".")),m=[],y=0,S={total_entries:0,files:0,dirs:0,symlinks:0,hidden:0,ignored:0,total_file_bytes:0},b=new Map,E=he(e),k={directory_id:t,workspace_id:e.workspace_id,directory_path:r.path};for(let w of g){let D=Sn.join(n,w.name),Re=(i==="."?w.name:Sn.posix.join(i,w.name)).replace(/\\/g,"/"),le="other";w.isDirectory()?le="dir":w.isFile()?le="file":w.isSymbolicLink()&&(le="symlink");let vt=w.name.startsWith(".");if(Z(Re,w.isDirectory(),k,this.config,E).shouldIgnore){y++,S.ignored++;continue}S.total_entries++,le==="file"?S.files++:le==="dir"?S.dirs++:le==="symlink"&&S.symlinks++,vt&&S.hidden++;let Pn,Fn;if(p==="stat"||p==="digest")try{let _t=await vn.stat(D);_t.isFile()&&(Pn=_t.size,S.total_file_bytes=(S.total_file_bytes??0)+_t.size),Fn=Math.floor(_t.mtimeMs/1e3)}catch{}let St=le==="file"?Sn.extname(w.name).slice(1):void 0;St&&(p==="stat"||p==="digest")&&b.set(St,(b.get(St)??0)+1),m.push({name:w.name,path:Re,type:le,size:Pn,mtime:Fn,ext:St,hidden:vt,ignored:!1})}p==="digest"&&b.size>0&&(S.by_ext=Sc(pi(_c([...b.entries()],([w,D])=>({ext:w,count:D})),["count"],["desc"]),10));let R=kc(m,u),_=Ec(R,d,f),T=_.slice(s,s+a),x=s+T.length,A=x<_.length,ee={count:T.length,limit:a,offset:s,has_more:A,next_offset:A?x:void 0},U=N({workspace_id:e.workspace_id,device_id:e.device_id,directory_id:t,path:i}),j={items:T,meta:ee,context:U,filtered:{ignored_count:y}};return p==="digest"&&(j.summary=S),P("list",U,!0,void 0,{file_count:T.length}),j}};import fi from"node:fs/promises";var ze=class extends G{async executeTool(e){let t=e.directory_id,r=Y(this.config,t),{absTarget:n,rel:i}=await oe(r.path,e.path),a=he(e),s={directory_id:t,workspace_id:e.workspace_id,directory_path:r.path},c=Z(i,!1,s,this.config,a);if(c.shouldIgnore){let m=N({workspace_id:e.workspace_id,device_id:e.device_id,directory_id:t,path:i});throw P("read",m,!1,`Access denied: ${c.reason||c.matchedRule||"blocked"}`),I("PERMISSION_DENIED",`Access denied: ${c.reason||c.matchedRule||"blocked"}`,{path:i,reason:c.reason})}let u=Math.max(e.offset??0,0),d=Math.min(Math.max(e.limit??4e4,1),2e5),f=e.encoding??"utf-8",p;try{p=await fi.stat(n)}catch(m){let y=N({workspace_id:e.workspace_id,device_id:e.device_id,directory_id:t,path:i});throw P("read",y,!1,B(m,"Failed to stat file.").message),B(m,"Failed to stat file.")}if(!p.isFile()){let m=N({workspace_id:e.workspace_id,device_id:e.device_id,directory_id:t,path:i});throw P("read",m,!1,"Path is not a file."),I("NOT_A_FILE","Path is not a file.",{path:i,hint:"The path must point to a file, not a directory. Use fs_list or fs_tree to explore directories, or provide a file path to read."})}let h=a?.max_file_size_bytes;if(typeof h=="number"&&h>0&&p.size>h){let m=N({workspace_id:e.workspace_id,device_id:e.device_id,directory_id:t,path:i});throw P("read",m,!1,`File exceeds the policy size limit (${p.size} > ${h}).`),I("PERMISSION_DENIED",`File exceeds the maximum size allowed by policy (${p.size} > ${h} bytes).`,{path:i,reason:"policy:max_file_size_bytes"})}let g;try{g=await fi.open(n,"r")}catch(m){let y=N({workspace_id:e.workspace_id,device_id:e.device_id,directory_id:t,path:i});throw P("read",y,!1,B(m,"Failed to open file.").message),B(m,"Failed to open file.")}try{let m=Buffer.alloc(d),{bytesRead:y}=await g.read(m,0,d,u),b=m.subarray(0,y).toString(f),E=u+y,k=y<d,R=N({workspace_id:e.workspace_id,device_id:e.device_id,directory_id:t,path:i});return P("read",R,!0,void 0,{bytes_read:y}),{content:b,bytes_read:y,next_offset:E,eof:k,context:R}}catch(m){let y=N({workspace_id:e.workspace_id,device_id:e.device_id,directory_id:t,path:i});throw P("read",y,!1,$(m).message),m}finally{await g.close()}}};import _n from"node:fs/promises";var fr=class extends G{async executeTool(e){let t=e.directory_id,r=Y(this.config,t),{absTarget:n,rel:i}=await oe(r.path,e.path),a=!1,s;try{s=await _n.readlink(n),a=!0}catch(f){if(se(f)==="ENOENT")throw B(f,"Path not found.")}let c;try{c=a?await _n.stat(n):await _n.lstat(n)}catch(f){throw B(f,"Failed to stat path.")}let u="other";c.isDirectory()?u="dir":c.isFile()?u="file":a&&(u="symlink");let d=N({workspace_id:e.workspace_id,device_id:e.device_id,directory_id:t,path:i});return P("info",d,!0),{type:u,size:c.isFile()?c.size:void 0,mtime:Math.floor(c.mtimeMs/1e3),ctime:Math.floor(c.ctimeMs/1e3),atime:Math.floor(c.atimeMs/1e3),is_symlink:a,target:a?s:void 0,context:d}}};import Tn from"node:fs/promises";import ge from"node:path";import kn from"node:fs/promises";import mi from"node:path";import En from"lodash/clamp.js";function Tc(o,e){let t="^",r=0;for(;r<e.length;){let n=e[r];if(n==="*")r+1<e.length&&e[r+1]==="*"?(t+=".*",r+=2):(t+="[^/]*",r++);else if(n==="?")t+="[^/]",r++;else if(n==="{"){let i=e.indexOf("}",r);if(i===-1)t+="\\{",r++;else{let a=e.substring(r+1,i).split(",");t+="("+a.map(s=>s.trim().replace(/[.*+?^${}()|[\]\\]/g,"\\$&")).join("|")+")",r=i+1}}else"\\^$|.+()[]".includes(n)?(t+="\\"+n,r++):(t+=n,r++)}t+="$";try{return new RegExp(t,"i").test(o)}catch{return!1}}async function wc(o,e,t,r,n,i,a,s){let c;try{c=await kn.readFile(o,"utf-8")}catch{return 0}let u=c.split(/\r?\n/),d=0,f;if(r==="regex")try{f=new RegExp(t,n?"g":"gi")}catch{let p=t.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");f=new RegExp(p,n?"g":"gi")}else{let p=t.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");f=new RegExp(p,n?"g":"gi")}for(let p=0;p<u.length&&d<a;p++){let h=u[p];if(f.test(h)){f.lastIndex=0;let g=Math.max(0,p-i),m=Math.min(u.length-1,p+i),y=u.slice(g,m+1);s.push({path:e,line:p+1,preview:h.trim(),snippet:y}),d++}}return d}async function hi(o,e,t,r,n,i,a,s,c,u,d,f,p,h,g){if(d.value>=c||f.length>=u)return;let m;try{m=await kn.readdir(o,{withFileTypes:!0})}catch{return}for(let y of m){if(d.value>=c||f.length>=u)break;let S=mi.join(o,y.name),b=e==="."?y.name:mi.posix.join(e,y.name);if(Z(b,y.isDirectory(),h,g).shouldIgnore){p.value++;continue}if(y.isFile()){if(i&&!Tc(b,i))continue;d.value++,await wc(S,b,r,n,a,s,u-f.length,f)}else y.isDirectory()&&await hi(S,b,t,r,n,i,a,s,c,u,d,f,p,h,g)}}var Ye=class extends G{async executeTool(e){let t=e.directory_id,r=Y(this.config,t),n=e.query;if(!n||n.length===0)throw I("INVALID_INPUT","Query is required.",{hint:'Query should be a search pattern to find in file contents. Use a substring (default) or regex pattern (with mode="regex"). Examples: "function", "import.*from", "TODO"'});let i=e.mode??"substring",a=e.file_glob,s=En(e.max_files??2e3,1,1e4),c=En(e.max_matches??200,1,1e3),u=En(e.context_lines??2,0,10),d=e.case_sensitive??!1,{absTarget:f,rel:p}=await oe(r.path,e.path??".");if(p!==".")try{if(!(await kn.stat(f)).isDirectory())throw I("NOT_A_DIRECTORY","Grep path is not a directory.",{path:p,hint:'The grep path must be a directory. Grep searches within directories recursively. Use "." to search from the root, or provide a directory path.'})}catch(b){throw X(b)?b:B(b,"Failed to stat grep path.")}let h={directory_id:t,workspace_id:e.workspace_id,directory_path:r.path},g=[],m={value:0},y={value:0};await hi(f,p,r.path,n,i,a,d,u,s,c,m,g,y,h,this.config);let S=N({workspace_id:e.workspace_id,device_id:e.device_id,directory_id:t,path:p});return P("grep",S,!0,void 0,{file_count:m.value,match_count:g.length}),{matches:g,meta:{files_scanned:m.value,matches:g.length,truncated:m.value>=s||g.length>=c},context:S,filtered:{ignored_count:y.value}}}};import Cc from"lodash/isEmpty.js";var Qe=class extends G{async executeTool(e){let t=e.directory_id,r=Y(this.config,t),n=e.default_limit??12e3,i=e.default_encoding??"utf-8",a=[];if(e.items&&Array.isArray(e.items)&&(a=e.items),Cc(a))throw I("INVALID_INPUT","fs_read_many expects { items: [{path,...}] }",{receivedKeys:Object.keys(e),hint:'Provide an "items" array with at least one object containing a "path" property. Each item can optionally include "offset", "limit", and "encoding". Example: { items: [{ path: "file1.ts" }, { path: "file2.ts", limit: 5000 }] }'});let s=he(e),c=[],u={directory_id:t,workspace_id:e.workspace_id,directory_path:r.path},d=new ze(this.config);for(let p of a)try{let h=Z(p.path,!1,u,this.config,s);if(h.shouldIgnore){c.push({path:p.path,content:"",bytes_read:0,truncated:!1,error:{code:"PERMISSION_DENIED",message:`Access denied: ${h.reason||h.matchedRule||"blocked"}`}});continue}let g={directory_id:t,path:p.path,workspace_id:e.workspace_id,device_id:e.device_id,offset:p.offset??0,limit:p.limit??n,encoding:p.encoding??i},m=await d.execute(g);if(!m.ok){c.push({path:p.path,content:"",bytes_read:0,truncated:!1,error:{code:m.error.code,message:m.error.message}});continue}c.push({path:p.path,content:m.data.content,bytes_read:m.data.bytes_read,truncated:!m.data.eof})}catch(h){let{code:g,message:m}=$(h);c.push({path:p.path,content:"",bytes_read:0,truncated:!1,error:{code:g,message:m}})}let f=N({workspace_id:e.workspace_id,device_id:e.device_id,directory_id:t,path:"."});return P("read_many",f,!0,void 0,{file_count:c.length}),{files:c,context:f}}};import lt from"lodash/clamp.js";import Rc from"lodash/compact.js";import yi from"lodash/orderBy.js";import xc from"lodash/uniq.js";import gi from"lodash/take.js";function bi(o,e,t){if(!(e.length>=t)){if(o.type==="file"){e.push(o.path);return}if(o.children)for(let r of o.children){if(e.length>=t)break;bi(r,e,t)}}}function Ac(o){let e=t=>{let r=t.toLowerCase(),n=r.split("/");return r==="readme.md"||r.endsWith("/readme.md")||r.includes("/readme.md")?0:r.endsWith(".md")?1:r.endsWith("package.json")||r.endsWith("pyproject.toml")||r.endsWith("go.mod")||r.endsWith("cargo.toml")||r.endsWith("pom.xml")||r.endsWith("build.gradle")?2:r.includes("dockerfile")||r.includes("compose")||r.endsWith(".env.example")?3:n.includes("config")||n.includes("conf")||n.includes("settings")?4:n.includes("ci")||n.includes(".github")||n.includes(".gitlab")||n.includes("jenkins")||r.includes("workflow")?5:r.endsWith(".ts")||r.endsWith(".tsx")||r.endsWith(".js")||r.endsWith(".jsx")||r.endsWith(".py")||r.endsWith(".go")||r.endsWith(".rs")||r.endsWith(".java")?6:7};return[...o].sort((t,r)=>{let n=e(t),i=e(r);return n!==i?n-i:t.localeCompare(r)})}function vi(o,e,t,r){let n={path:o.path,name:o.name,type:o.type,depth:e,size:o.size,mtime:o.mtime,ext:o.ext,hidden:o.hidden,ignored:o.ignored};if(r?.has(o.path)?n.error=r.get(o.path):o.type==="other"&&o.name==="<readdir_error>"&&(n.error={code:"INTERNAL",message:"Failed to read directory"}),t.push(n),o.children)for(let i of o.children)vi(i,e+1,t,r)}async function Si(o,e,t,r,n,i,a,s,c,u,d,f,p,h,g,m,y){if(s.value>=a)return g.reason="MAX_ENTRIES",null;if(t>r)return g.reason="MAX_DEPTH",null;let S=e==="."?".":e.replace(/\\/g,"/"),b;try{b=await Tn.lstat(o)}catch(x){let{code:A,message:ee}=$(x);u.total_entries++;let U={name:ge.basename(e)||e,type:"other",path:S,hidden:ge.basename(e)?.startsWith("."),ignored:!1};return m&&m.set(S,{code:A,message:`Failed to stat: ${ee}`}),n==="files"||n==="dirs"?null:(s.value++,U)}let E="other";if(b.isDirectory()?E="dir":b.isFile()?E="file":b.isSymbolicLink()&&(E="symlink"),Z(S,b.isDirectory(),p,h,y).shouldIgnore)return c.value++,u.ignored++,null;u.total_entries++,E==="file"?u.files++:E==="dir"?u.dirs++:E==="symlink"&&u.symlinks++;let R=ge.basename(e)?.startsWith(".");R&&u.hidden++;let _=E==="file"?ge.extname(ge.basename(e)||e).slice(1):void 0;_&&(f==="stat"||f==="digest")&&d.set(_,(d.get(_)??0)+1),E==="file"&&(f==="stat"||f==="digest")&&(u.total_file_bytes=(u.total_file_bytes??0)+(b.size??0));let T={name:ge.basename(e)||e,type:E,path:S,size:(f==="stat"||f==="digest")&&b.isFile()?b.size:void 0,mtime:f==="stat"||f==="digest"?Math.floor(b.mtimeMs/1e3):void 0,ext:_,hidden:R,ignored:!1};if(n==="files"&&E!=="file"&&E!=="symlink"||n==="dirs"&&E!=="dir")return null;if(s.value++,E==="dir"&&t<r){let x,A;try{x=await Tn.readdir(o,{withFileTypes:!0})}catch(C){A=$(C),x=[]}let ee=yi(i?x:x.filter(C=>!C.name.startsWith(".")),[C=>C.isDirectory()?0:1,"name"],["asc","asc"]),U=[];A&&m&&m.set(S,{code:A.code,message:`Failed to read directory: ${A.message}`});for(let C of ee){if(s.value>=a)break;let j=ge.join(o,C.name),w=e==="."?C.name:ge.posix.join(e,C.name),D=w.replace(/\\/g,"/");if(Z(D,C.isDirectory(),p,h,y).shouldIgnore){c.value++,u.ignored++;continue}try{let Re=await Si(j,w,t+1,r,n,i,a,s,c,u,d,f,p,h,g,m,y);Re&&U.push(Re)}catch(Re){let{code:le,message:vt}=$(Re);m&&m.set(D,{code:le,message:`Failed to build subtree: ${vt}`}),u.total_entries++;let Nn={name:ge.basename(w)||w,type:"other",path:D,hidden:ge.basename(w)?.startsWith("."),ignored:!1};if(n==="files"||n==="dirs")continue;s.value++,U.push(Nn)}}U.length>0&&(T.children=U)}return T}var mr=class extends G{async executeTool(e){let t=e.directory_id,r=Y(this.config,t),{absTarget:n,rel:i}=await oe(r.path,e.path),a=lt(e.max_depth??3,1,20),s=e.only??"all",c=e.include_hidden??!1,u=lt(e.max_entries??5e3,1,2e4),d=e.detail??"stat",f=e.include_display??!1,p=lt(e.max_display_rows??2e3,1,2e4);try{if(!(await Tn.stat(n)).isDirectory())throw I("NOT_A_DIRECTORY","Path is not a directory.",{path:i,hint:"The path must point to a directory to build a tree. Provide a directory path, not a file."})}catch(C){throw X(C)?C:B(C,"Failed to build tree.")}let h=he(e),g={directory_id:t,workspace_id:e.workspace_id,directory_path:r.path},m={reason:void 0},y={value:0},S={value:0},b={total_entries:0,files:0,dirs:0,symlinks:0,hidden:0,ignored:0,total_file_bytes:0},E=new Map,k=new Map,R=await Si(n,i,0,a,s,c,u,y,S,b,E,d,g,this.config,m,k,h);if(!R)throw I("INTERNAL","Failed to build tree (empty result).");let _=N({workspace_id:e.workspace_id,device_id:e.device_id,directory_id:t,path:i});d==="digest"&&E.size>0&&(b.by_ext=gi(yi([...E.entries()].map(([C,j])=>({ext:C,count:j})),["count"],["desc"]),10));let T={tree:R,context:_,filtered:{ignored_count:S.value}};if(f){let C=[];vi(R,0,C,k),C.length>p&&(C.length=p,T.truncated=T.truncated??{reason:"MAX_ENTRIES",continuation:"Display rows truncated; reduce max_entries/max_depth or lower max_display_rows"}),T.display={rows:C}}m.reason&&(T.truncated={reason:m.reason,continuation:m.reason==="MAX_ENTRIES"?"Reduce max_entries or list this folder with fs_list instead":"Reduce max_depth or list this folder with fs_list instead"}),(d==="digest"||e.include_summary)&&(T.summary=b);let x={};if(e.grep?.query)try{let C=new Ye(this.config),j={directory_id:t,workspace_id:e.workspace_id,device_id:e.device_id,path:e.path??".",query:e.grep.query,mode:e.grep.mode??"substring",file_glob:e.grep.file_glob,max_files:e.grep.max_files??2e3,max_matches:e.grep.max_matches??200,context_lines:e.grep.context_lines??2,case_sensitive:e.grep.case_sensitive??!1},w=await C.execute(j);w.ok&&(x.grep={matches:w.data.matches,meta:w.data.meta,context:w.data.context})}catch{}let A=Rc(e.read?.paths),ee=[];if(e.read?.auto){let C=lt(e.read.auto.max_files??10,1,50),j=[];bi(R,j,3e3),ee=Ac(j).slice(0,C)}let U=gi(xc([...A,...ee]),50);if(U.length>0)try{let C=lt(e.read?.auto?.max_bytes_each??2e4,1,2e5),j=new Qe(this.config),w={directory_id:t,workspace_id:e.workspace_id,device_id:e.device_id,default_limit:C,default_encoding:"utf-8",items:U.map(te=>({path:te}))},D=await j.execute(w);D.ok&&(x.read_many={files:D.data.files.map(te=>({path:te.path,content:te.content,bytes_read:te.bytes_read,truncated:te.truncated,error:te.error})),context:D.data.context})}catch{}return Object.keys(x).length>0&&(T.extras=x),P("tree",_,!0,void 0,{file_count:y.value}),T}};import wn from"node:fs/promises";import ut from"node:path";import Dc from"lodash/clamp.js";import Ic from"lodash/orderBy.js";function Oc(o,e){let t="^",r=0;for(;r<e.length;){let n=e[r];if(n==="*")r+1<e.length&&e[r+1]==="*"?(t+=".*",r+=2):(t+="[^/\\\\]*",r++);else if(n==="?")t+="[^/\\\\]",r++;else if(n==="["){let i=e.indexOf("]",r);if(i===-1)t+="\\[",r++;else{let a=e.substring(r+1,i);a.startsWith("!")?t+="[^"+a.substring(1)+"]":t+="["+a+"]",r=i+1}}else"\\^$|.+(){}[]".includes(n)?(t+="\\"+n,r++):(t+=n,r++)}t+="$";try{return new RegExp(t,"i").test(o)}catch{return o.toLowerCase().includes(e.toLowerCase())}}function Nc(o,e,t){return t==="glob"?Oc(o,e):o.toLowerCase().includes(e.toLowerCase())}function Pc(o,e,t){let r=100;if(e){for(let n of e)if(o.path.startsWith(n)){r+=50;break}}if(t){for(let n of t)if(o.path.startsWith(n)){r-=30;break}}return o.name===o.path.split("/").pop()&&(r+=20),r}function Fc(o,e){let t="^",r=0;for(;r<e.length;){let n=e[r];n==="*"?r+1<e.length&&e[r+1]==="*"?(t+=".*",r+=2):(t+="[^/]*",r++):n==="?"?(t+="[^/]",r++):"\\^$|.+(){}[]".includes(n)?(t+="\\"+n,r++):(t+=n,r++)}t+="$";try{return new RegExp(t,"i").test(o)}catch{return!1}}async function Cn(o,e,t,r,n,i,a,s,c,u,d,f,p,h,g,m,y,S){if(u.length>=c)return;let b;try{b=await wn.readdir(o,{withFileTypes:!0})}catch{return}let E=s?b:b.filter(k=>!k.name.startsWith("."));for(let k of E){if(u.length>=c)break;let R=ut.join(o,k.name),_=e==="."?k.name:ut.posix.join(e,k.name);if(Z(_,k.isDirectory(),f,p,S).shouldIgnore){d.value++;continue}if(h&&!Fc(_,h)){i&&k.isDirectory()&&await Cn(R,_,t,r,n,i,a,s,c,u,d,f,p,h,g,m,y,S);continue}let x=Nc(k.name,r,n),A="other";if(k.isDirectory()?A="dir":k.isFile()?A="file":k.isSymbolicLink()&&(A="symlink"),x&&(a==="all"||a==="files"&&(A==="file"||A==="symlink")||a==="dirs"&&A==="dir")){let U,C;if(y!==!1)try{let D=await wn.stat(R);U=D.isFile()?D.size:void 0,C=Math.floor(D.mtimeMs/1e3)}catch{}let j=A==="file"?ut.extname(k.name).slice(1):void 0,w=Pc({name:k.name,path:_},g,m);u.push({name:k.name,path:_,type:A,size:U,mtime:C,ext:j,hidden:k.name.startsWith("."),ignored:!1,score:w,matched:"name"})}i&&A==="dir"&&await Cn(R,_,t,r,n,i,a,s,c,u,d,f,p,h,g,m,y)}}var hr=class extends G{async executeTool(e){let t=e.directory_id,r=Y(this.config,t),n=e.query;if(!n||n.length===0)throw I("INVALID_INPUT","Query is required.",{hint:'Query should be a pattern to match file or directory names. Use a substring (default) or glob pattern (with mode="glob"). Examples: "config", "*.ts", "test/**"'});let i=e.mode??"substring",a=e.recursive??!0,s=e.only??"all",c=e.include_hidden??!1,u=Dc(e.max_results??200,1,2e4),d=Math.max(e.offset??0,0),f=e.file_glob,p=e.prefer_paths,h=e.avoid_paths,g=e.include_stats!==!1,{absTarget:m,rel:y}=await oe(r.path,e.path??"."),S=m,b=y,E=[];if(y!==".")try{let D=await wn.stat(m);if(!D.isDirectory())if(D.isFile()){S=ut.dirname(m);let te=ut.posix.dirname(y);b=te==="."?".":te,E.push({type:"warning",description:`Search path "${y}" is not a directory (it's a file). Searching the directory containing it instead: "${b}"`})}else throw I("NOT_A_DIRECTORY","Search path is not a directory.",{path:y,hint:'The search path must be a directory. Search looks for files/directories matching the query within directories. Use "." to search from the root, or provide a directory path.'})}catch(D){throw X(D)?D:B(D,"Failed to stat search path.")}let k=he(e),R={directory_id:t,workspace_id:e.workspace_id,directory_path:r.path},_=[],T={value:0};await Cn(S,b,r.path,n,i,a,s,c,u+d,_,T,R,this.config,f,p,h,g,k);let x=Ic(_,[D=>D.score??0,"name"],["desc","asc"]),A=x.slice(d,d+u),ee=d+A.length,U=ee<x.length,C={count:A.length,limit:u,offset:d,has_more:U,next_offset:U?ee:void 0},j=N({workspace_id:e.workspace_id,device_id:e.device_id,directory_id:t,path:b});P("search",j,!0,void 0,{file_count:A.length});let w={results:A,meta:C,context:j,filtered:{ignored_count:T.value}};return E.length>0&&(w.messages=E),w}};var gr=class{config;executers=new Map;constructor(e){this.config=e,this.initializeExecuters()}initializeExecuters(){this.executers.set("fs_list",new pr(this.config)),this.executers.set("fs_read",new ze(this.config)),this.executers.set("fs_info",new fr(this.config)),this.executers.set("fs_tree",new mr(this.config)),this.executers.set("fs_search",new hr(this.config)),this.executers.set("fs_grep",new Ye(this.config)),this.executers.set("fs_read_many",new Qe(this.config))}async executeTool(e,t){let r=this.executers.get(e);if(!r)return Le({code:"UNKNOWN_TOOL",message:`Unknown tool: ${e}`,details:{toolName:e}});try{return await r.execute(t)}catch(n){return X(n)?Le(n):Le({...$(n),details:{raw:String(n)}})}}getExecuter(e){return this.executers.get(e)}hasTool(e){return this.executers.has(e)}getRegisteredTools(){return Array.from(this.executers.keys())}};import _i from"lodash/isObject.js";import $e from"lodash/get.js";function dt(o){let e=Date.now()-o,t=parseFloat((e/1e3).toFixed(2));return{execution_ms:e,execution_sec:t}}function Lc(o){return _i(o)&&typeof $e(o,"ok")=="boolean"}function $c(o){let e=Lc(o),t=e?!!$e(o,"ok"):!0,r,n,i;if(t){if(e?r=$e(o,"data")??$e(o,"result")??o:r=o,_i(r)&&Array.isArray($e(r,"messages"))){i=$e(r,"messages");let a={...r};delete a.messages,r=a}}else e?n=$e(o,"error")??o:n=o;return{ok:t,result:r,error:n,messages:i}}function Ei(o,e,t,r){let n=dt(r),{ok:i,result:a,error:s,messages:c}=$c(t);return Pr({type:"tool_result",job_id:o,tool:e,ok:i,timing:n,...i?{result:a??null}:{error:s??{code:"UNKNOWN",message:"Unknown error"}},messages:c})}function ki(o,e,t,r){let n=dt(r);return{type:"tool_result",job_id:o,tool:e,ok:!1,error:$(t),timing:n}}var M=class{toolName;jobId;ok;elapsedSec;raw;constructor(e,t,r,n){this.toolName=e,this.jobId=t,this.ok=r.ok,this.elapsedSec=n,this.raw=r}getErrorDescription(){return`error: ${L(this.raw.error)}`}getStatusIcon(){return this.ok?"\u2713":"\u2717"}getStatusText(){return this.ok?"SUCCESS":"FAILED"}toString(){let e=this.getStatusIcon(),t=this.getDescription();return`${e} ${this.toolName} (${t}, elapsed=${this.elapsedSec}s)`}};var pt=class extends M{output;constructor(e,t,r,n){super(e,t,r,n),this.output=r.ok?r.data:void 0}getDescription(){if(!this.ok)return this.getErrorDescription();if(!this.output)return"no result";let{items:e,meta:t,filtered:r,context:n}=this.output,i=e?.length??0,a=r?.ignored_count??0,s=n?.path??"unknown",c=[];i>0&&c.push(`${i} item${i!==1?"s":""}`),a>0&&c.push(`${a} ignored`),t?.has_more&&c.push("has_more");let u=c.length>0?c.join(", "):"empty";return`path=${s}, ${u}`}};var ft=class extends M{output;constructor(e,t,r,n){super(e,t,r,n),this.output=r.ok?r.data:void 0}getDescription(){if(!this.ok)return this.getErrorDescription();if(!this.output)return"no result";let{bytes_read:e,eof:t,context:r}=this.output,n=r?.path??"unknown",i=e??0,a=!t,s=[`path=${n}`,`${i} bytes`];return a&&s.push("truncated"),s.join(", ")}};var mt=class extends M{output;constructor(e,t,r,n){super(e,t,r,n),this.output=r.ok?r.data:void 0}getDescription(){if(!this.ok)return this.getErrorDescription();if(!this.output)return"no result";let{type:e,size:t,context:r}=this.output,n=r?.path??"unknown",i=t!==void 0?`${t} bytes`:"no size";return`path=${n}, type=${e}, ${i}`}};var ht=class extends M{output;constructor(e,t,r,n){super(e,t,r,n),this.output=r.ok?r.data:void 0}getDescription(){if(!this.ok)return this.getErrorDescription();if(!this.output)return"no result";let{tree:e,filtered:t,summary:r,context:n}=this.output,i=n?.path??"unknown",a=t?.ignored_count??0,s=[`path=${i}`];if(r){let{total_entries:c,files:u,dirs:d}=r;c!==void 0&&s.push(`${c} entries`),u!==void 0&&s.push(`${u} files`),d!==void 0&&s.push(`${d} dirs`)}else if(e){let c=d=>{let f=1;if(d.children)for(let p of d.children)f+=c(p);return f},u=c(e);s.push(`${u} nodes`)}return a>0&&s.push(`${a} ignored`),this.output.truncated&&s.push("truncated"),s.join(", ")}};var gt=class extends M{output;constructor(e,t,r,n){super(e,t,r,n),this.output=r.ok?r.data:void 0}getDescription(){if(!this.ok)return this.getErrorDescription();if(!this.output)return"no result";let{results:e,meta:t,filtered:r,context:n}=this.output,i=n?.path??"unknown",a=e?.length??0,s=r?.ignored_count??0,c=[`path=${i}`,`${a} result${a!==1?"s":""}`];return s>0&&c.push(`${s} ignored`),t?.has_more&&c.push("has_more"),c.join(", ")}};var yt=class extends M{output;constructor(e,t,r,n){super(e,t,r,n),this.output=r.ok?r.data:void 0}getDescription(){if(!this.ok)return this.getErrorDescription();if(!this.output)return"no result";let{matches:e,meta:t,filtered:r,context:n}=this.output,i=n?.path??"unknown",a=e?.length??0,s=t?.files_scanned??0,c=r?.ignored_count??0,u=[`path=${i}`,`${a} match${a!==1?"es":""}`,`${s} file${s!==1?"s":""} scanned`];return c>0&&u.push(`${c} ignored`),t?.truncated&&u.push("truncated"),u.join(", ")}};var bt=class extends M{output;constructor(e,t,r,n){super(e,t,r,n),this.output=r.ok?r.data:void 0}getDescription(){if(!this.ok)return this.getErrorDescription();if(!this.output)return"no result";let{files:e}=this.output,t=e?.length??0,r=e?.filter(s=>!s.error).length??0,n=t-r,i=e?.reduce((s,c)=>s+(c.bytes_read??0),0)??0,a=[`${t} file${t!==1?"s":""}`,`${r} success`];return n>0&&a.push(`${n} error${n!==1?"s":""}`),i>0&&a.push(`${i} bytes`),a.join(", ")}};function Rn(o,e,t,r){switch(o){case"fs_list":return new pt(o,e,t,r);case"fs_read":return new ft(o,e,t,r);case"fs_info":return new mt(o,e,t,r);case"fs_tree":return new ht(o,e,t,r);case"fs_search":return new gt(o,e,t,r);case"fs_grep":return new yt(o,e,t,r);case"fs_read_many":return new bt(o,e,t,r);default:{class n extends M{getDescription(){return this.ok?"completed":"failed"}}return new n(o,e,t,r)}}}var yr=class{name="tools-orchestrator";handlers(){return{"ws:connected":async(e,t)=>{t.notifier.info(`WS connected: ${e.url}`,{scope:"system"})},"ws:rx":async(e,t)=>{try{let r=JSON.parse(e.text),n=ar.safeParse(r);if(!n.success){t.notifier.error("Unexpected message format",{scope:"system"});return}n.success&&n.data.type==="tool_call"&&(t.bus.emit("tool:call",n.data),await this.handleToolCall(n.data,t))}catch{t.notifier.error("Unexpected message format",{scope:"system"})}}}}async handleToolCall(e,t){let r=e.tool,n=e.job_id,i=Nr(e.args).set("workspace_id",e.workspace_id).set("device_id",e.device_id).set("context",e.context).value(),a=i.directory_id,s=t.engine.configurationContext.configuration,c=s.directories.map(f=>f.id).join(", ")||"none",u=s.directories.map(f=>f.path).join(", ")||"none";t.notifier.info(`Tool call: ${r} (job=${n}, directory_id=${a||"MISSING"}, available_ids=[${c}], available_paths=[${u}], directory_count=${s.directories.length})`,{scope:"system"}),t.notifier.info(`Executing: ${r}`);let d=Date.now();try{let p=await new gr(s).executeTool(r,i),h=Ei(n,r,p,d);sr.parse(h),t.bus.emit("ws:tx",{text:JSON.stringify(h)});let g=dt(d),m=Rn(r,n,p,g.execution_sec);m.ok?t.notifier.success(m.toString()):t.notifier.error(m.toString())}catch(f){let p=ki(n,r,f,d);sr.parse(p),t.bus.emit("ws:tx",{text:JSON.stringify(p)});let h=dt(d),g={ok:!1,error:$(f)},m=Rn(r,n,g,h.execution_sec);t.notifier.error(m.toString())}}};var xn=[{name:"fs_list",version:"1.0",description:"List files and folders at a relative path inside a shared folder.",inputSchema:{type:"object",properties:{directory_id:{type:"string",minLength:1},path:{type:"string",default:"."},limit:{type:"integer",minimum:1,maximum:1e3,default:200},offset:{type:"integer",minimum:0,default:0},include_hidden:{type:"boolean",default:!1},only:{type:"string",enum:["all","files","dirs"],default:"all"},sort:{type:"string",enum:["name","mtime","size"],default:"name"},order:{type:"string",enum:["asc","desc"],default:"asc"},detail:{type:"string",enum:["basic","stat","digest"],default:"stat"}},required:["path"]},errorCodes:["PATH_ESCAPE","NOT_FOUND","NOT_A_DIRECTORY","INVALID_INPUT","INTERNAL","UNKNOWN_ROOT"]},{name:"fs_read",version:"1.0",description:"Read the contents of a SINGLE text file inside a shared folder. For reading many files at once, prefer fs_read_many instead of multiple fs_read calls.",inputSchema:{type:"object",properties:{directory_id:{type:"string",minLength:1},path:{type:"string",minLength:1},offset:{type:"integer",minimum:0,default:0},limit:{type:"integer",minimum:1,maximum:2e5,default:4e4},encoding:{type:"string",default:"utf-8"}},required:["path"]},errorCodes:["PATH_ESCAPE","NOT_FOUND","NOT_A_FILE","PERMISSION_DENIED","INVALID_INPUT","INTERNAL","UNKNOWN_ROOT"]},{name:"fs_info",version:"1.0",description:"Get metadata about a file or directory (type, size, timestamps, symlink).",inputSchema:{type:"object",properties:{directory_id:{type:"string",minLength:1},path:{type:"string",minLength:1}},required:["path"]},errorCodes:["PATH_ESCAPE","NOT_FOUND","PERMISSION_DENIED","INVALID_INPUT","INTERNAL","UNKNOWN_ROOT"]},{name:"fs_tree",version:"1.0",description:"Return a directory tree from a relative path up to max_depth.",inputSchema:{type:"object",properties:{directory_id:{type:"string",minLength:1},path:{type:"string",default:"."},max_depth:{type:"integer",minimum:1,maximum:20,default:3},max_entries:{type:"integer",minimum:1,maximum:2e4,default:5e3},include_hidden:{type:"boolean",default:!1},only:{type:"string",enum:["all","files","dirs"],default:"all"},detail:{type:"string",enum:["basic","stat","digest"],default:"stat"},include_display:{type:"boolean",default:!1},max_display_rows:{type:"integer",minimum:1,maximum:2e4,default:2e3}},required:["path"]},errorCodes:["PATH_ESCAPE","NOT_FOUND","NOT_A_DIRECTORY","INVALID_INPUT","INTERNAL","UNKNOWN_ROOT"]},{name:"fs_search",version:"1.0",description:"Search for files/folders by name inside a root, optionally recursively.",inputSchema:{type:"object",properties:{directory_id:{type:"string",minLength:1},path:{type:"string",default:"."},query:{type:"string",minLength:1},mode:{type:"string",enum:["substring","glob"],default:"substring"},recursive:{type:"boolean",default:!0},include_hidden:{type:"boolean",default:!1},only:{type:"string",enum:["all","files","dirs"],default:"all"},max_results:{type:"integer",minimum:1,maximum:2e4,default:200}},required:["query"]},errorCodes:["PATH_ESCAPE","NOT_FOUND","INVALID_INPUT","INTERNAL","UNKNOWN_ROOT"]},{name:"fs_grep",version:"1.0",description:"Search for text content inside files (like grep/ripgrep).",inputSchema:{type:"object",properties:{directory_id:{type:"string",minLength:1},path:{type:"string",default:"."},query:{type:"string",minLength:1},mode:{type:"string",enum:["substring","regex"],default:"substring"},file_glob:{type:"string"},max_files:{type:"integer",minimum:1,maximum:1e4,default:2e3},max_matches:{type:"integer",minimum:1,maximum:1e3,default:200},context_lines:{type:"integer",minimum:0,maximum:10,default:2},case_sensitive:{type:"boolean",default:!1}},required:["query"]},errorCodes:["PATH_ESCAPE","NOT_FOUND","NOT_A_DIRECTORY","INVALID_INPUT","INTERNAL"]},{name:"fs_read_many",version:"1.0",description:"Read multiple files in a single call (batch read). Prefer this over multiple fs_read calls when you need to read several files\u2014fewer round-trips and better performance (up to 50 items per call).",inputSchema:{type:"object",properties:{directory_id:{type:"string",minLength:1},items:{type:"array",minItems:1,maxItems:50,items:{type:"object",properties:{path:{type:"string",minLength:1},offset:{type:"integer",minimum:0,default:0},limit:{type:"integer",minimum:1,maximum:2e5},encoding:{type:"string"}},required:["path"]}},default_limit:{type:"integer",minimum:1,maximum:2e5,default:12e3},default_encoding:{type:"string",default:"utf-8"}},required:["items"]},errorCodes:["PATH_ESCAPE","NOT_FOUND","NOT_A_FILE","INVALID_INPUT","INTERNAL"]}];var An=class extends W{constructor(){super(new q({name:"tools-job",lane:"worker",priority:1e3,locks:[],maxAttempts:1,cancellable:!1}))}async run(e,t){t.notifier.info("Executing supported tools job");let n=t.engine.configurationContext.configuration.device?.id;if(!n){t.notifier.error("Cannot publish tools: device ID not available");return}let i={type:"tools_publish",device_id:n,tools:xn};t.bus.emit("ws:tx",{text:JSON.stringify(i)}),t.notifier.success(`\u2192 tools_publish (count=${xn.length})`)}},Dn=new An;function Ti(){return new ie(Dn,void 0)}var In=class extends W{constructor(){super(new q({name:"directories-job",lane:"worker",priority:1e3,locks:[],maxAttempts:1,cancellable:!1}))}async run(e,t){t.notifier.info("Executing directories job");let r=t.engine.configurationContext.configuration,n=r.device?.id;if(!n){t.notifier.error("Cannot publish directories: device ID not available");return}let i=r.directories.map(s=>({id:s.id,name:s.name,path:Rr(s.path)??s.name,path_hint:s.tag||void 0})),a={type:"directories_publish",device_id:n,directories:i};t.bus.emit("ws:tx",{text:JSON.stringify(a)}),t.notifier.success(`\u2192 directories_publish (dirs=${a.directories.length})`)}},On=new In;function wi(){return new ie(On,void 0)}var br=class{name="meta-data-orchestrator";onRegister(e){e.engine.register(Dn),e.engine.register(On)}handlers(){return{"meta:data:requested:tools":async(e,t)=>{t.notifier.info("MetaData: tools requested -> dispatching supported tools job"),t.engine.dispatch(Ti())},"meta:data:requested:directories":async(e,t)=>{t.notifier.info("MetaData: directories requested -> dispatching directories job"),t.engine.dispatch(wi())}}}};Ao(()=>[new Qt,new or,new er,new tr,new ur,new dr,new cr,new yr,new br]);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@devicelink/dlink",
3
- "version": "0.3.5",
3
+ "version": "0.3.10",
4
4
  "description": "DeviceLink CLI agent — secure, local-first device access with explicit user consent",
5
5
  "private": false,
6
6
  "type": "module",