bortexcode 1.2.9 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +15 -0
  2. package/bin/bortex.js +317 -17
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -48,6 +48,7 @@ bortexcode
48
48
  bortexcode "explain this function"
49
49
  bortexcode --agent "refactor src/utils.js"
50
50
  bortexcode remote-control
51
+ bortexcode remote-control --cloud
51
52
  bortexcode --remote-control
52
53
  ```
53
54
 
@@ -67,6 +68,7 @@ Common commands:
67
68
  /llm-config sync
68
69
  /remote-control [name]
69
70
  /remote-control --lan
71
+ /remote-control --cloud
70
72
  /rc
71
73
  /exit
72
74
  ```
@@ -87,11 +89,19 @@ For phone access on the same LAN:
87
89
  bortexcode remote-control --remote-lan
88
90
  ```
89
91
 
92
+ For remote access without opening an inbound port, use the bortex.site relay:
93
+
94
+ ```bash
95
+ bortexcode remote-control --cloud --name "My Project"
96
+ bortexcode --remote-cloud "My Project"
97
+ ```
98
+
90
99
  Inside the REPL:
91
100
 
92
101
  ```text
93
102
  /remote-control My Project
94
103
  /remote-control --lan
104
+ /remote-control --cloud
95
105
  /remote-control stop
96
106
  ```
97
107
 
@@ -115,6 +125,10 @@ Inside the REPL:
115
125
  Disable startup update check
116
126
  --remote-control, --rc [name]
117
127
  Enable browser remote control
128
+ --remote-cloud, --cloud
129
+ Use bortex.site relay instead of opening a local port
130
+ --remote-relay <url>
131
+ Remote relay base URL
118
132
  --remote-lan Bind remote control to 0.0.0.0 for LAN/mobile access
119
133
  --remote-host <host>, --remote-port <port>
120
134
  Remote control bind address
@@ -141,4 +155,5 @@ BORTEX_CLI_ICONS=1
141
155
  BORTEX_NO_UPDATE_CHECK=1
142
156
  BORTEX_REMOTE_HOST
143
157
  BORTEX_REMOTE_PORT
158
+ BORTEX_REMOTE_RELAY_URL
144
159
  ```
package/bin/bortex.js CHANGED
@@ -33,6 +33,8 @@ function parseArgs(argv) {
33
33
  _apiKeyExplicit: false,
34
34
  remoteControl: false,
35
35
  remoteControlServerMode: false,
36
+ remoteControlCloud: false,
37
+ remoteControlRelayUrl: process.env.BORTEX_REMOTE_RELAY_URL || '',
36
38
  remoteControlName: '',
37
39
  remoteControlHost: process.env.BORTEX_REMOTE_HOST || '127.0.0.1',
38
40
  remoteControlPort: Number(process.env.BORTEX_REMOTE_PORT || 0) || 0,
@@ -51,6 +53,10 @@ function parseArgs(argv) {
51
53
  opts.remoteControlServerMode = true;
52
54
  continue;
53
55
  }
56
+ if ((a === 'cloud' || a === 'relay') && opts.remoteControlServerMode) {
57
+ opts.remoteControlCloud = true;
58
+ continue;
59
+ }
54
60
  if (a === '--agent' || a === '-a') {
55
61
  opts.agent = true;
56
62
  continue;
@@ -150,6 +156,28 @@ function parseArgs(argv) {
150
156
  opts.remoteControlName = a.slice('--remote-control='.length);
151
157
  continue;
152
158
  }
159
+ if (a === '--remote-cloud' || a === '--cloud' || a === '--relay') {
160
+ opts.remoteControl = true;
161
+ opts.remoteControlCloud = true;
162
+ if (argv[i + 1] && !String(argv[i + 1]).startsWith('-')) {
163
+ opts.remoteControlName = argv[i + 1];
164
+ i += 1;
165
+ }
166
+ continue;
167
+ }
168
+ if ((a === '--remote-relay' || a === '--relay-url') && argv[i + 1]) {
169
+ opts.remoteControlRelayUrl = argv[i + 1];
170
+ i += 1;
171
+ continue;
172
+ }
173
+ if (a.startsWith('--remote-relay=')) {
174
+ opts.remoteControlRelayUrl = a.slice('--remote-relay='.length);
175
+ continue;
176
+ }
177
+ if (a.startsWith('--relay-url=')) {
178
+ opts.remoteControlRelayUrl = a.slice('--relay-url='.length);
179
+ continue;
180
+ }
153
181
  if (a === '--remote-lan') {
154
182
  opts.remoteControlHost = '0.0.0.0';
155
183
  continue;
@@ -197,6 +225,7 @@ function usage() {
197
225
  console.log('Usage:');
198
226
  console.log(` ${cliName} [options] [prompt]`);
199
227
  console.log(` ${cliName} remote-control [--name <title>] [--remote-lan]`);
228
+ console.log(` ${cliName} remote-control --cloud [--name <title>]`);
200
229
  console.log(` ${cliName} --api-key <apikey>`);
201
230
  console.log(` ${cliName} "write a python function"`);
202
231
  console.log('');
@@ -218,6 +247,10 @@ function usage() {
218
247
  console.log(' Disable startup update check');
219
248
  console.log(' --remote-control, --rc [name]');
220
249
  console.log(' Enable browser remote control for this session');
250
+ console.log(' --remote-cloud, --cloud');
251
+ console.log(' Use bortex.site relay instead of opening a local port');
252
+ console.log(' --remote-relay <url>');
253
+ console.log(' Remote relay base URL (default: current --url)');
221
254
  console.log(' --remote-lan Bind remote control to 0.0.0.0 for LAN/mobile access');
222
255
  console.log(' --remote-host <host>, --remote-port <port>');
223
256
  console.log(' Remote control bind address');
@@ -230,6 +263,7 @@ function usage() {
230
263
  console.log('Environment:');
231
264
  console.log(' BORTEX_URL Server URL');
232
265
  console.log(' BORTEX_API_KEY API key');
266
+ console.log(' BORTEX_REMOTE_RELAY_URL Remote Control cloud relay URL');
233
267
  }
234
268
 
235
269
  function formatMs(ms) {
@@ -3930,6 +3964,7 @@ function printLocalHelp() {
3930
3964
  console.log(' /process-status check running processes');
3931
3965
  console.log(' /port-status check listening ports');
3932
3966
  console.log(' /remote-control [name] [--lan|--host <host>|--port <port>]');
3967
+ console.log(' /remote-control --cloud [name]');
3933
3968
  console.log(' /remote-control stop');
3934
3969
  console.log(' /diff [unstaged|staged|all]');
3935
3970
  console.log(' /stage <file>|--all');
@@ -4382,7 +4417,7 @@ async function startRemoteControlServer(opts, overrides = {}) {
4382
4417
  const token = crypto.randomBytes(24).toString('hex');
4383
4418
  const state = {
4384
4419
  id: crypto.randomUUID ? crypto.randomUUID() : `${Date.now()}-${Math.random().toString(16).slice(2)}`,
4385
- name: String(overrides.name || opts.remoteControlName || path.basename(opts.cwd || process.cwd()) || os.hostname() || 'Bortex Code').trim(),
4420
+ name: getRemoteControlDisplayName(opts, overrides),
4386
4421
  host,
4387
4422
  port,
4388
4423
  token,
@@ -4496,11 +4531,231 @@ async function stopRemoteControlServer(opts) {
4496
4531
  return true;
4497
4532
  }
4498
4533
 
4534
+ function getRemoteControlDisplayName(opts, overrides = {}) {
4535
+ return String(overrides.name || opts.remoteControlName || path.basename(opts.cwd || process.cwd()) || os.hostname() || 'Bortex Code').trim();
4536
+ }
4537
+
4538
+ function getRemoteControlStatus(opts) {
4539
+ if (opts.remoteControlCloudSession?.active) return 'cloud';
4540
+ if (opts.remoteControlSession?.server) return 'local';
4541
+ return 'off';
4542
+ }
4543
+
4544
+ function getRemoteControlRelayBase(opts, overrides = {}) {
4545
+ return String(overrides.relayUrl || opts.remoteControlRelayUrl || opts.url || 'https://bortex.site').replace(/\/+$/, '');
4546
+ }
4547
+
4548
+ function remoteControlSleep(ms) {
4549
+ return new Promise((resolve) => setTimeout(resolve, ms));
4550
+ }
4551
+
4552
+ function withRemoteControlQuery(rawUrl, baseUrl, params = {}) {
4553
+ const urlObj = new URL(rawUrl, baseUrl);
4554
+ Object.entries(params).forEach(([key, value]) => {
4555
+ if (value !== undefined && value !== null && value !== '') {
4556
+ urlObj.searchParams.set(key, String(value));
4557
+ }
4558
+ });
4559
+ return urlObj.toString();
4560
+ }
4561
+
4562
+ async function fetchRemoteControlJson(url, options = {}, timeoutMs = 30000) {
4563
+ const controller = new AbortController();
4564
+ const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
4565
+ if (typeof timeoutId.unref === 'function') timeoutId.unref();
4566
+ try {
4567
+ const res = await fetch(url, {
4568
+ ...options,
4569
+ headers: {
4570
+ Accept: 'application/json',
4571
+ ...(options.headers || {})
4572
+ },
4573
+ signal: controller.signal
4574
+ });
4575
+ const text = await res.text();
4576
+ let data = null;
4577
+ if (text) {
4578
+ try { data = JSON.parse(text); } catch (_err) { data = null; }
4579
+ }
4580
+ if (!res.ok) {
4581
+ const detail = data?.error || data?.message || text || res.statusText || 'request failed';
4582
+ throw new Error(`HTTP ${res.status}: ${detail}`);
4583
+ }
4584
+ if (data?.ok === false) {
4585
+ throw new Error(data.error || data.message || 'request failed');
4586
+ }
4587
+ return data || {};
4588
+ } finally {
4589
+ clearTimeout(timeoutId);
4590
+ }
4591
+ }
4592
+
4593
+ async function postCloudRemoteHeartbeat(state) {
4594
+ const heartbeatUrl = withRemoteControlQuery(
4595
+ state.heartbeatUrl,
4596
+ state.baseUrl,
4597
+ { agentToken: state.agentToken }
4598
+ );
4599
+ await fetchRemoteControlJson(heartbeatUrl, {
4600
+ method: 'POST',
4601
+ headers: { 'Content-Type': 'application/json' },
4602
+ body: JSON.stringify({ cwd: state.cwd, version: CLI_VERSION })
4603
+ }, 12000);
4604
+ }
4605
+
4606
+ async function postCloudRemoteResult(state, commandId, result) {
4607
+ const resultUrl = withRemoteControlQuery(
4608
+ state.resultUrl,
4609
+ state.baseUrl,
4610
+ { agentToken: state.agentToken }
4611
+ );
4612
+ await fetchRemoteControlJson(resultUrl, {
4613
+ method: 'POST',
4614
+ headers: { 'Content-Type': 'application/json' },
4615
+ body: JSON.stringify({
4616
+ commandId,
4617
+ ok: !!result.ok,
4618
+ output: String(result.output || '')
4619
+ })
4620
+ }, 35000);
4621
+ }
4622
+
4623
+ async function pollCloudRemoteControlLoop(opts, state) {
4624
+ let lastHeartbeatAt = 0;
4625
+ while (state.active) {
4626
+ let delayMs = 1200;
4627
+ try {
4628
+ const pollUrl = withRemoteControlQuery(
4629
+ state.pollUrl,
4630
+ state.baseUrl,
4631
+ { agentToken: state.agentToken, after: state.lastCommandId || 0 }
4632
+ );
4633
+ const data = await fetchRemoteControlJson(pollUrl, { method: 'GET' }, 35000);
4634
+ const commands = Array.isArray(data.commands) ? data.commands : [];
4635
+ state.lastSeenAt = Date.now();
4636
+ state.errorCount = 0;
4637
+
4638
+ for (const command of commands) {
4639
+ if (!state.active) break;
4640
+ const commandId = Number(command?.id || 0);
4641
+ if (commandId > (state.lastCommandId || 0)) state.lastCommandId = commandId;
4642
+ const text = String(command?.text || command?.prompt || '').trim();
4643
+ if (!text) continue;
4644
+ state.busy = true;
4645
+ let result;
4646
+ try {
4647
+ result = await runRemoteControlPrompt(opts, text);
4648
+ } catch (err) {
4649
+ result = { ok: false, output: err.stack || err.message || String(err) };
4650
+ } finally {
4651
+ state.busy = false;
4652
+ }
4653
+ await postCloudRemoteResult(state, commandId, result);
4654
+ }
4655
+
4656
+ if (!commands.length && Date.now() - lastHeartbeatAt > 10000) {
4657
+ lastHeartbeatAt = Date.now();
4658
+ await postCloudRemoteHeartbeat(state);
4659
+ }
4660
+ if (commands.length) delayMs = 250;
4661
+ } catch (err) {
4662
+ if (state.active) {
4663
+ state.errorCount = (state.errorCount || 0) + 1;
4664
+ const now = Date.now();
4665
+ if (!state.lastErrorAt || now - state.lastErrorAt > 15000) {
4666
+ console.error(`Cloud Remote Control error: ${err.message || String(err)}`);
4667
+ state.lastErrorAt = now;
4668
+ }
4669
+ delayMs = Math.min(5000, 1000 + state.errorCount * 500);
4670
+ }
4671
+ } finally {
4672
+ state.busy = false;
4673
+ }
4674
+ await remoteControlSleep(delayMs);
4675
+ }
4676
+ }
4677
+
4678
+ async function startCloudRemoteControlSession(opts, overrides = {}) {
4679
+ if (opts.remoteControlCloudSession?.active) {
4680
+ console.log(`Cloud Remote Control already active: ${opts.remoteControlCloudSession.url}`);
4681
+ return opts.remoteControlCloudSession;
4682
+ }
4683
+
4684
+ const baseUrl = getRemoteControlRelayBase(opts, overrides);
4685
+ const name = getRemoteControlDisplayName(opts, overrides);
4686
+ const payload = {
4687
+ name,
4688
+ cwd: opts.cwd,
4689
+ version: CLI_VERSION,
4690
+ machine: os.hostname(),
4691
+ platform: process.platform,
4692
+ node: process.version,
4693
+ mode: opts.agent ? 'agent' : 'chat'
4694
+ };
4695
+ const data = await fetchRemoteControlJson(`${baseUrl}/api/bortex-code/remote/register`, {
4696
+ method: 'POST',
4697
+ headers: { 'Content-Type': 'application/json' },
4698
+ body: JSON.stringify(payload)
4699
+ }, 30000);
4700
+
4701
+ const sessionId = String(data.sessionId || '').trim();
4702
+ const agentToken = String(data.agentToken || '').trim();
4703
+ if (!sessionId || !agentToken) {
4704
+ throw new Error('Remote relay registration did not return a session token.');
4705
+ }
4706
+
4707
+ const state = {
4708
+ active: true,
4709
+ cloud: true,
4710
+ baseUrl,
4711
+ sessionId,
4712
+ agentToken,
4713
+ clientToken: data.clientToken || '',
4714
+ name,
4715
+ cwd: opts.cwd,
4716
+ url: data.url || `${baseUrl}/bortex-code/remote?session=${encodeURIComponent(sessionId)}`,
4717
+ pollUrl: data.pollUrl || `${baseUrl}/api/bortex-code/remote/session/${encodeURIComponent(sessionId)}/poll`,
4718
+ resultUrl: data.resultUrl || `${baseUrl}/api/bortex-code/remote/session/${encodeURIComponent(sessionId)}/result`,
4719
+ heartbeatUrl: data.heartbeatUrl || `${baseUrl}/api/bortex-code/remote/session/${encodeURIComponent(sessionId)}/heartbeat`,
4720
+ lastCommandId: 0,
4721
+ startedAt: Date.now(),
4722
+ busy: false,
4723
+ errorCount: 0
4724
+ };
4725
+ state.stop = async () => {
4726
+ state.active = false;
4727
+ };
4728
+ opts.remoteControlCloudSession = state;
4729
+ state.pollPromise = pollCloudRemoteControlLoop(opts, state).catch((err) => {
4730
+ if (state.active) console.error(`Cloud Remote Control stopped unexpectedly: ${err.message || String(err)}`);
4731
+ });
4732
+
4733
+ console.log(`Cloud Remote Control active: ${state.name}`);
4734
+ console.log(`URL: ${state.url}`);
4735
+ console.log('No inbound port required. Keep this process running.');
4736
+ console.log('Security: keep this tokenized URL private. Anyone with the URL can control this Bortex Code session.');
4737
+ return state;
4738
+ }
4739
+
4740
+ async function stopCloudRemoteControlSession(opts) {
4741
+ const state = opts.remoteControlCloudSession;
4742
+ if (!state?.active) {
4743
+ console.log('Cloud Remote Control is not active.');
4744
+ return false;
4745
+ }
4746
+ await state.stop();
4747
+ opts.remoteControlCloudSession = null;
4748
+ console.log('Cloud Remote Control stopped.');
4749
+ return true;
4750
+ }
4751
+
4499
4752
  function parseRemoteControlSlashArgs(rest = [], opts = {}) {
4500
4753
  const out = {
4501
4754
  name: '',
4502
4755
  host: opts.remoteControlHost || '127.0.0.1',
4503
- port: Number(opts.remoteControlPort || 0) || 0
4756
+ port: Number(opts.remoteControlPort || 0) || 0,
4757
+ relayUrl: opts.remoteControlRelayUrl || '',
4758
+ cloud: false
4504
4759
  };
4505
4760
  const nameParts = [];
4506
4761
  for (let i = 0; i < rest.length; i += 1) {
@@ -4513,6 +4768,23 @@ function parseRemoteControlSlashArgs(rest = [], opts = {}) {
4513
4768
  out.host = '0.0.0.0';
4514
4769
  continue;
4515
4770
  }
4771
+ if (a === 'cloud' || a === '--cloud' || a === '--remote-cloud' || a === '--relay') {
4772
+ out.cloud = true;
4773
+ continue;
4774
+ }
4775
+ if ((a === '--relay-url' || a === '--remote-relay') && rest[i + 1]) {
4776
+ out.relayUrl = String(rest[i + 1]);
4777
+ i += 1;
4778
+ continue;
4779
+ }
4780
+ if (a.startsWith('--relay-url=')) {
4781
+ out.relayUrl = a.slice('--relay-url='.length);
4782
+ continue;
4783
+ }
4784
+ if (a.startsWith('--remote-relay=')) {
4785
+ out.relayUrl = a.slice('--remote-relay='.length);
4786
+ continue;
4787
+ }
4516
4788
  if ((a === '--host' || a === '--remote-host') && rest[i + 1]) {
4517
4789
  out.host = String(rest[i + 1]);
4518
4790
  i += 1;
@@ -5835,13 +6107,18 @@ async function handleLocalCommand(opts, line) {
5835
6107
  const done = todoItems.filter((t) => t.done).length;
5836
6108
  const runPending = Array.isArray(opts.runState?.steps) ? opts.runState.steps.filter((s) => s.status === 'pending').length : 0;
5837
6109
  console.log(formatModeLine(opts));
5838
- console.log(`Todo: ${done}/${todoItems.length} | Plan: ${opts.plan?.goal ? 'active' : 'none'} | Run: ${opts.runState?.goal ? `active (${runPending} pending)` : 'none'} | Remote: ${opts.remoteControlSession?.server ? 'active' : 'off'}`);
6110
+ console.log(`Todo: ${done}/${todoItems.length} | Plan: ${opts.plan?.goal ? 'active' : 'none'} | Run: ${opts.runState?.goal ? `active (${runPending} pending)` : 'none'} | Remote: ${getRemoteControlStatus(opts)}`);
5839
6111
  return { handled: true };
5840
6112
  }
5841
6113
  if (lc === '/remote-control' || lc === '/remote' || lc === '/rc') {
5842
6114
  const parsed = parseRemoteControlSlashArgs(rest, opts);
5843
- if (parsed.stop || opts.remoteControlSession?.server) {
5844
- await stopRemoteControlServer(opts);
6115
+ if (parsed.stop || opts.remoteControlSession?.server || opts.remoteControlCloudSession?.active) {
6116
+ if (opts.remoteControlSession?.server) await stopRemoteControlServer(opts);
6117
+ if (opts.remoteControlCloudSession?.active) await stopCloudRemoteControlSession(opts);
6118
+ return { handled: true };
6119
+ }
6120
+ if (parsed.cloud) {
6121
+ await startCloudRemoteControlSession(opts, parsed);
5845
6122
  return { handled: true };
5846
6123
  }
5847
6124
  await startRemoteControlServer(opts, parsed);
@@ -7010,6 +7287,7 @@ const SLASH_COMMANDS = [
7010
7287
  ['/help', 'Show command help'],
7011
7288
  ['/remote-control [name]', 'Control this local session from a browser'],
7012
7289
  ['/remote-control --lan', 'Expose Remote Control on the local network'],
7290
+ ['/remote-control --cloud', 'Control this session through bortex.site relay'],
7013
7291
  ['/rc', 'Toggle Remote Control'],
7014
7292
  ['/llm-config show', 'Show cached LLM configuration'],
7015
7293
  ['/llm-config sync', 'Sync LLM configuration from Bortex'],
@@ -7186,11 +7464,18 @@ async function runRepl(opts) {
7186
7464
  };
7187
7465
  opts._askInput = async (prompt) => new Promise((resolve) => rl.question(prompt, resolve));
7188
7466
  if (opts.remoteControl) {
7189
- await startRemoteControlServer(opts, {
7190
- name: opts.remoteControlName,
7191
- host: opts.remoteControlHost,
7192
- port: opts.remoteControlPort
7193
- });
7467
+ if (opts.remoteControlCloud) {
7468
+ await startCloudRemoteControlSession(opts, {
7469
+ name: opts.remoteControlName,
7470
+ relayUrl: opts.remoteControlRelayUrl
7471
+ });
7472
+ } else {
7473
+ await startRemoteControlServer(opts, {
7474
+ name: opts.remoteControlName,
7475
+ host: opts.remoteControlHost,
7476
+ port: opts.remoteControlPort
7477
+ });
7478
+ }
7194
7479
  }
7195
7480
 
7196
7481
  const question = () => new Promise((resolve) => {
@@ -7216,7 +7501,7 @@ async function runRepl(opts) {
7216
7501
  const done = todoItems.filter((t) => t.done).length;
7217
7502
  console.log(formatModeLine(opts));
7218
7503
  const runPending = Array.isArray(opts.runState?.steps) ? opts.runState.steps.filter((s) => s.status === 'pending').length : 0;
7219
- console.log(`Todo: ${done}/${todoItems.length} | Plan: ${opts.plan?.goal ? 'active' : 'none'} | Run: ${opts.runState?.goal ? `active (${runPending} pending)` : 'none'}`);
7504
+ console.log(`Todo: ${done}/${todoItems.length} | Plan: ${opts.plan?.goal ? 'active' : 'none'} | Run: ${opts.runState?.goal ? `active (${runPending} pending)` : 'none'} | Remote: ${getRemoteControlStatus(opts)}`);
7220
7505
  continue;
7221
7506
  }
7222
7507
  if (line === '/help') {
@@ -7266,6 +7551,9 @@ async function runRepl(opts) {
7266
7551
  if (opts.remoteControlSession?.server) {
7267
7552
  await stopRemoteControlServer(opts);
7268
7553
  }
7554
+ if (opts.remoteControlCloudSession?.active) {
7555
+ await stopCloudRemoteControlSession(opts);
7556
+ }
7269
7557
  try { saveCliWorkspaceState(opts); } catch (_err) { }
7270
7558
  delete opts._readMultiline;
7271
7559
  delete opts._askInput;
@@ -7317,18 +7605,30 @@ async function main() {
7317
7605
  }
7318
7606
 
7319
7607
  if (opts.remoteControlServerMode) {
7320
- await startRemoteControlServer(opts, {
7321
- name: opts.remoteControlName,
7322
- host: opts.remoteControlHost,
7323
- port: opts.remoteControlPort
7324
- });
7608
+ if (opts.remoteControlCloud) {
7609
+ await startCloudRemoteControlSession(opts, {
7610
+ name: opts.remoteControlName,
7611
+ relayUrl: opts.remoteControlRelayUrl
7612
+ });
7613
+ } else {
7614
+ await startRemoteControlServer(opts, {
7615
+ name: opts.remoteControlName,
7616
+ host: opts.remoteControlHost,
7617
+ port: opts.remoteControlPort
7618
+ });
7619
+ }
7325
7620
  console.log('Server mode: keep this process running. Press Ctrl+C to stop Remote Control.');
7326
7621
  await new Promise((resolve) => {
7327
7622
  let stopping = false;
7328
7623
  const stop = async () => {
7329
7624
  if (stopping) return;
7330
7625
  stopping = true;
7331
- try { await stopRemoteControlServer(opts); } catch (_err) {}
7626
+ if (opts.remoteControlSession?.server) {
7627
+ try { await stopRemoteControlServer(opts); } catch (_err) {}
7628
+ }
7629
+ if (opts.remoteControlCloudSession?.active) {
7630
+ try { await stopCloudRemoteControlSession(opts); } catch (_err) {}
7631
+ }
7332
7632
  resolve();
7333
7633
  };
7334
7634
  process.once('SIGINT', stop);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bortexcode",
3
- "version": "1.2.9",
3
+ "version": "1.3.0",
4
4
  "description": "Bortex Code CLI - AI coding assistant powered by bortex.site",
5
5
  "homepage": "https://bortex.site",
6
6
  "license": "UNLICENSED",