dexto 1.6.11 → 1.6.13

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 (73) hide show
  1. package/README.md +10 -2
  2. package/dist/analytics/events.d.ts +1 -1
  3. package/dist/analytics/events.d.ts.map +1 -1
  4. package/dist/cli/auth/api-client.d.ts +0 -2
  5. package/dist/cli/auth/api-client.d.ts.map +1 -1
  6. package/dist/cli/auth/api-client.js +7 -11
  7. package/dist/cli/auth/constants.d.ts +4 -4
  8. package/dist/cli/auth/constants.js +4 -4
  9. package/dist/cli/commands/agents/install.d.ts.map +1 -0
  10. package/dist/cli/commands/{install.js → agents/install.js} +4 -4
  11. package/dist/cli/commands/{list-agents.d.ts → agents/list.d.ts} +1 -1
  12. package/dist/cli/commands/agents/list.d.ts.map +1 -0
  13. package/dist/cli/commands/{list-agents.js → agents/list.js} +2 -2
  14. package/dist/cli/commands/agents/register.js +5 -5
  15. package/dist/cli/commands/{sync-agents.d.ts → agents/sync.d.ts} +10 -2
  16. package/dist/cli/commands/agents/sync.d.ts.map +1 -0
  17. package/dist/cli/commands/{sync-agents.js → agents/sync.js} +54 -5
  18. package/dist/cli/commands/agents/uninstall.d.ts +18 -0
  19. package/dist/cli/commands/agents/uninstall.d.ts.map +1 -0
  20. package/dist/cli/commands/agents/uninstall.js +141 -0
  21. package/dist/cli/commands/deploy/client.d.ts +44 -0
  22. package/dist/cli/commands/deploy/client.d.ts.map +1 -0
  23. package/dist/cli/commands/deploy/client.js +232 -0
  24. package/dist/cli/commands/deploy/config.d.ts +81 -0
  25. package/dist/cli/commands/deploy/config.d.ts.map +1 -0
  26. package/dist/cli/commands/deploy/config.js +144 -0
  27. package/dist/cli/commands/deploy/entry-agent.d.ts +3 -0
  28. package/dist/cli/commands/deploy/entry-agent.d.ts.map +1 -0
  29. package/dist/cli/commands/deploy/entry-agent.js +22 -0
  30. package/dist/cli/commands/deploy/index.d.ts +9 -0
  31. package/dist/cli/commands/deploy/index.d.ts.map +1 -0
  32. package/dist/cli/commands/deploy/index.js +204 -0
  33. package/dist/cli/commands/deploy/links.d.ts +3 -0
  34. package/dist/cli/commands/deploy/links.d.ts.map +1 -0
  35. package/dist/cli/commands/deploy/links.js +53 -0
  36. package/dist/cli/commands/deploy/register.d.ts +6 -0
  37. package/dist/cli/commands/deploy/register.d.ts.map +1 -0
  38. package/dist/cli/commands/deploy/register.js +75 -0
  39. package/dist/cli/commands/deploy/snapshot.d.ts +12 -0
  40. package/dist/cli/commands/deploy/snapshot.d.ts.map +1 -0
  41. package/dist/cli/commands/deploy/snapshot.js +76 -0
  42. package/dist/cli/commands/deploy/state.d.ts +21 -0
  43. package/dist/cli/commands/deploy/state.d.ts.map +1 -0
  44. package/dist/cli/commands/deploy/state.js +122 -0
  45. package/dist/cli/commands/index.d.ts +6 -4
  46. package/dist/cli/commands/index.d.ts.map +1 -1
  47. package/dist/cli/commands/index.js +6 -4
  48. package/dist/cli/commands/setup.d.ts.map +1 -1
  49. package/dist/cli/commands/setup.js +304 -31
  50. package/dist/cli/commands/uninstall.d.ts +9 -12
  51. package/dist/cli/commands/uninstall.d.ts.map +1 -1
  52. package/dist/cli/commands/uninstall.js +99 -113
  53. package/dist/cli/commands/upgrade.d.ts +15 -0
  54. package/dist/cli/commands/upgrade.d.ts.map +1 -0
  55. package/dist/cli/commands/upgrade.js +106 -0
  56. package/dist/cli/modes/cli.d.ts.map +1 -1
  57. package/dist/cli/modes/cli.js +0 -12
  58. package/dist/cli/utils/config-validation.d.ts.map +1 -1
  59. package/dist/cli/utils/config-validation.js +34 -20
  60. package/dist/cli/utils/self-management.d.ts +93 -0
  61. package/dist/cli/utils/self-management.d.ts.map +1 -0
  62. package/dist/cli/utils/self-management.js +423 -0
  63. package/dist/cli/utils/version-check.d.ts +1 -1
  64. package/dist/cli/utils/version-check.d.ts.map +1 -1
  65. package/dist/cli/utils/version-check.js +53 -19
  66. package/dist/index-main.js +59 -2
  67. package/dist/webui/assets/{index-CNiOYnOb.js → index-UDAdxmci.js} +187 -187
  68. package/dist/webui/index.html +1 -1
  69. package/package.json +13 -11
  70. package/dist/cli/commands/install.d.ts.map +0 -1
  71. package/dist/cli/commands/list-agents.d.ts.map +0 -1
  72. package/dist/cli/commands/sync-agents.d.ts.map +0 -1
  73. /package/dist/cli/commands/{install.d.ts → agents/install.d.ts} +0 -0
@@ -0,0 +1,423 @@
1
+ import { spawn } from 'child_process';
2
+ import { promises as fs } from 'fs';
3
+ import os from 'os';
4
+ import path from 'path';
5
+ import { z } from 'zod';
6
+ const DEXTO_BINARY = process.platform === 'win32' ? 'dexto.exe' : 'dexto';
7
+ const DEXTO_PATH_COMMAND = process.platform === 'win32' ? 'where' : 'which';
8
+ const DEXTO_PATH_ARGS = process.platform === 'win32' ? ['dexto'] : ['-a', 'dexto'];
9
+ const DEFAULT_NATIVE_INSTALL_URL = 'https://dexto.ai/install';
10
+ const DEFAULT_WINDOWS_INSTALL_URL = 'https://dexto.ai/install.ps1';
11
+ const InstallMetadataSchema = z
12
+ .object({
13
+ schemaVersion: z.number().int().positive().default(1),
14
+ method: z.enum(['native', 'npm']),
15
+ installedPath: z.string().min(1),
16
+ installedAt: z.string().min(1),
17
+ version: z.string().min(1),
18
+ sourceUrl: z.string().min(1).optional(),
19
+ releaseTag: z.string().min(1).optional(),
20
+ platform: z.string().min(1).optional(),
21
+ arch: z.string().min(1).optional(),
22
+ })
23
+ .strict();
24
+ export async function executeCommand(command, args, options = {}) {
25
+ const stdio = options.stdio ?? 'pipe';
26
+ return await new Promise((resolve) => {
27
+ const child = spawn(command, args, {
28
+ cwd: options.cwd,
29
+ env: options.env,
30
+ stdio,
31
+ windowsHide: true,
32
+ });
33
+ let stdout = '';
34
+ let stderr = '';
35
+ if (stdio === 'pipe') {
36
+ if (child.stdout) {
37
+ child.stdout.on('data', (chunk) => {
38
+ stdout += chunk.toString();
39
+ });
40
+ }
41
+ if (child.stderr) {
42
+ child.stderr.on('data', (chunk) => {
43
+ stderr += chunk.toString();
44
+ });
45
+ }
46
+ }
47
+ child.on('error', (error) => {
48
+ resolve({
49
+ code: -1,
50
+ stdout,
51
+ stderr: `${stderr}\n${error.message}`.trim(),
52
+ });
53
+ });
54
+ child.on('close', (code) => {
55
+ resolve({
56
+ code: code ?? -1,
57
+ stdout,
58
+ stderr,
59
+ });
60
+ });
61
+ });
62
+ }
63
+ export async function executeManagedCommand(commandSpec, options) {
64
+ if (options.dryRun) {
65
+ console.log(`[dry-run] ${commandSpec.displayCommand}`);
66
+ return;
67
+ }
68
+ const executeOptions = { stdio: 'inherit' };
69
+ if (options.cwd) {
70
+ executeOptions.cwd = options.cwd;
71
+ }
72
+ if (commandSpec.env) {
73
+ executeOptions.env = commandSpec.env;
74
+ }
75
+ const result = await executeCommand(commandSpec.command, commandSpec.args, executeOptions);
76
+ if (result.code !== 0) {
77
+ throw new Error(`Command failed: ${commandSpec.displayCommand}`);
78
+ }
79
+ }
80
+ export function getDextoHomePath() {
81
+ return path.join(os.homedir(), '.dexto');
82
+ }
83
+ export async function readInstallMetadata() {
84
+ const metadataPath = path.join(getDextoHomePath(), 'install.json');
85
+ try {
86
+ const content = await fs.readFile(metadataPath, 'utf-8');
87
+ const parsed = JSON.parse(content);
88
+ const validated = InstallMetadataSchema.safeParse(parsed);
89
+ if (!validated.success) {
90
+ return null;
91
+ }
92
+ return validated.data;
93
+ }
94
+ catch {
95
+ return null;
96
+ }
97
+ }
98
+ function normalizePathForComparison(targetPath) {
99
+ const normalized = path.normalize(targetPath);
100
+ return process.platform === 'win32' ? normalized.toLowerCase() : normalized;
101
+ }
102
+ function uniquePaths(paths) {
103
+ const seen = new Set();
104
+ const result = [];
105
+ for (const candidate of paths) {
106
+ const normalized = normalizePathForComparison(candidate);
107
+ if (seen.has(normalized)) {
108
+ continue;
109
+ }
110
+ seen.add(normalized);
111
+ result.push(path.normalize(candidate));
112
+ }
113
+ return result;
114
+ }
115
+ async function getBinaryPathsFromPath() {
116
+ const result = await executeCommand(DEXTO_PATH_COMMAND, DEXTO_PATH_ARGS);
117
+ if (result.code !== 0) {
118
+ return [];
119
+ }
120
+ const candidates = result.stdout
121
+ .split(/\r?\n/)
122
+ .map((entry) => entry.trim())
123
+ .filter((entry) => entry.length > 0);
124
+ return uniquePaths(candidates);
125
+ }
126
+ function inferInstallMethodFromPath(binaryPath) {
127
+ const normalized = normalizePathForComparison(binaryPath);
128
+ if (normalized.includes('.dexto') || normalized.includes(path.join('.local', 'bin'))) {
129
+ return 'native';
130
+ }
131
+ return 'unknown';
132
+ }
133
+ export function isProjectLocalBinaryPath(binaryPath) {
134
+ const normalized = normalizePathForSignatureMatch(binaryPath);
135
+ return (normalized.includes('/node_modules/.bin/') || normalized.includes('/node_modules/dexto/'));
136
+ }
137
+ function pathStartsWith(targetPath, candidatePrefix) {
138
+ const normalizedTarget = normalizePathForComparison(targetPath);
139
+ const normalizedPrefix = normalizePathForComparison(candidatePrefix);
140
+ return (normalizedTarget === normalizedPrefix ||
141
+ normalizedTarget.startsWith(`${normalizedPrefix}${path.sep}`));
142
+ }
143
+ function pathBasenameIsDexto(targetPath) {
144
+ const basename = path.basename(targetPath).toLowerCase();
145
+ return basename === 'dexto' || basename === 'dexto.exe' || basename === 'dexto.cmd';
146
+ }
147
+ async function detectNodePackageManagerFromPath(binaryPath) {
148
+ const npmPrefix = await executeCommand('npm', ['prefix', '-g']);
149
+ if (npmPrefix.code === 0) {
150
+ const prefix = npmPrefix.stdout.trim();
151
+ if (prefix.length > 0) {
152
+ const npmBinDir = process.platform === 'win32' ? prefix : path.join(prefix, 'bin');
153
+ if (pathStartsWith(binaryPath, npmBinDir)) {
154
+ return 'npm';
155
+ }
156
+ }
157
+ }
158
+ return null;
159
+ }
160
+ function normalizePathForSignatureMatch(targetPath) {
161
+ return normalizePathForComparison(targetPath).replace(/\\/g, '/');
162
+ }
163
+ export function detectUnsupportedPackageManagerFromPath(binaryPath) {
164
+ const normalized = normalizePathForSignatureMatch(binaryPath);
165
+ const pnpmHome = process.env.PNPM_HOME;
166
+ if (pnpmHome && pathStartsWith(binaryPath, pnpmHome)) {
167
+ return 'pnpm';
168
+ }
169
+ const bunInstall = process.env.BUN_INSTALL;
170
+ if (bunInstall && pathStartsWith(binaryPath, path.join(bunInstall, 'bin'))) {
171
+ return 'bun';
172
+ }
173
+ if (normalized.includes('/.local/share/pnpm/') ||
174
+ normalized.includes('/appdata/local/pnpm/') ||
175
+ normalized.includes('/pnpm/')) {
176
+ return 'pnpm';
177
+ }
178
+ if (normalized.includes('/.bun/bin/')) {
179
+ return 'bun';
180
+ }
181
+ return null;
182
+ }
183
+ export function buildMultipleInstallWarning(allDetectedPaths, activePath) {
184
+ if (allDetectedPaths.length <= 1) {
185
+ return null;
186
+ }
187
+ const formattedPaths = allDetectedPaths
188
+ .map((entry, index) => {
189
+ const isActive = activePath !== null &&
190
+ normalizePathForComparison(entry) === normalizePathForComparison(activePath);
191
+ const prefix = isActive || (activePath === null && index === 0) ? '*' : '-';
192
+ return `${prefix} ${entry}`;
193
+ })
194
+ .join('\n');
195
+ return [
196
+ 'Multiple dexto binaries detected in PATH:',
197
+ formattedPaths,
198
+ 'The active binary is marked with *.',
199
+ ].join('\n');
200
+ }
201
+ export async function detectInstallMethod() {
202
+ return await detectInstallMethodWithDeps();
203
+ }
204
+ export async function detectInstallMethodWithDeps(deps = {}) {
205
+ const readMetadata = deps.readMetadata ?? readInstallMetadata;
206
+ const getPathEntries = deps.getPathEntries ?? getBinaryPathsFromPath;
207
+ const detectNodeManager = deps.detectNodeManager ?? detectNodePackageManagerFromPath;
208
+ const pathExistsFn = deps.pathExists ?? pathExists;
209
+ const metadata = await readMetadata();
210
+ const allDetectedPaths = await getPathEntries();
211
+ const activePath = allDetectedPaths[0] ?? null;
212
+ async function detectMethodFromActivePath(pathEntry) {
213
+ if (isProjectLocalBinaryPath(pathEntry)) {
214
+ return 'project-local';
215
+ }
216
+ let method = inferInstallMethodFromPath(pathEntry);
217
+ if (method === 'unknown' || method === 'native') {
218
+ const pmMethod = await detectNodeManager(pathEntry);
219
+ if (pmMethod) {
220
+ method = pmMethod;
221
+ }
222
+ }
223
+ if (method === 'unknown' && pathBasenameIsDexto(pathEntry)) {
224
+ const normalizedPath = normalizePathForComparison(pathEntry);
225
+ if (normalizedPath.includes(normalizePathForComparison(path.join(os.homedir(), '.local', 'bin')))) {
226
+ method = 'native';
227
+ }
228
+ }
229
+ return method;
230
+ }
231
+ if (metadata) {
232
+ const installedPath = metadata.installedPath;
233
+ const metadataPathExists = await pathExistsFn(installedPath);
234
+ const metadataMatchesActivePath = activePath !== null &&
235
+ normalizePathForComparison(activePath) === normalizePathForComparison(installedPath);
236
+ const metadataIsTrusted = metadataPathExists && (allDetectedPaths.length === 0 || metadataMatchesActivePath);
237
+ if (!metadataIsTrusted && activePath) {
238
+ const method = await detectMethodFromActivePath(activePath);
239
+ return {
240
+ method,
241
+ source: 'heuristic',
242
+ metadata,
243
+ installedPath: activePath,
244
+ installDir: path.dirname(activePath),
245
+ allDetectedPaths,
246
+ multipleInstallWarning: buildMultipleInstallWarning(allDetectedPaths, activePath),
247
+ };
248
+ }
249
+ if (!metadataIsTrusted) {
250
+ return {
251
+ method: 'unknown',
252
+ source: 'heuristic',
253
+ metadata,
254
+ installedPath: null,
255
+ installDir: null,
256
+ allDetectedPaths,
257
+ multipleInstallWarning: null,
258
+ };
259
+ }
260
+ const installDir = path.dirname(installedPath);
261
+ return {
262
+ method: metadata.method,
263
+ source: 'metadata',
264
+ metadata,
265
+ installedPath,
266
+ installDir,
267
+ allDetectedPaths,
268
+ multipleInstallWarning: buildMultipleInstallWarning(allDetectedPaths, installedPath),
269
+ };
270
+ }
271
+ if (!activePath) {
272
+ return {
273
+ method: 'unknown',
274
+ source: 'heuristic',
275
+ metadata: null,
276
+ installedPath: null,
277
+ installDir: null,
278
+ allDetectedPaths,
279
+ multipleInstallWarning: null,
280
+ };
281
+ }
282
+ const method = await detectMethodFromActivePath(activePath);
283
+ return {
284
+ method,
285
+ source: 'heuristic',
286
+ metadata: null,
287
+ installedPath: activePath,
288
+ installDir: path.dirname(activePath),
289
+ allDetectedPaths,
290
+ multipleInstallWarning: buildMultipleInstallWarning(allDetectedPaths, activePath),
291
+ };
292
+ }
293
+ function shellEscapeForPosix(value) {
294
+ return `'${value.replace(/'/g, `'\\''`)}'`;
295
+ }
296
+ function shellEscapeForPowerShell(value) {
297
+ return `'${value.replace(/'/g, `''`)}'`;
298
+ }
299
+ export function commandDisplayWithEnvPosix(command, envOverrides) {
300
+ const parts = [];
301
+ for (const [key, value] of Object.entries(envOverrides)) {
302
+ parts.push(`${key}=${shellEscapeForPosix(value)}`);
303
+ }
304
+ if (parts.length === 0) {
305
+ return command;
306
+ }
307
+ return `${parts.join(' ')} ${command}`;
308
+ }
309
+ export function commandDisplayWithEnvPowerShell(command, envOverrides) {
310
+ const parts = [];
311
+ for (const [key, value] of Object.entries(envOverrides)) {
312
+ parts.push(`$env:${key}=${shellEscapeForPowerShell(value)};`);
313
+ }
314
+ if (parts.length === 0) {
315
+ return command;
316
+ }
317
+ return `${parts.join(' ')} ${command}`;
318
+ }
319
+ export function createNativeInstallCommand(options) {
320
+ const env = { ...process.env };
321
+ const envOverrides = {};
322
+ if (options.version) {
323
+ env.DEXTO_VERSION = options.version;
324
+ envOverrides.DEXTO_VERSION = options.version;
325
+ }
326
+ if (options.installDir) {
327
+ env.DEXTO_INSTALL_DIR = options.installDir;
328
+ envOverrides.DEXTO_INSTALL_DIR = options.installDir;
329
+ }
330
+ if (options.force) {
331
+ env.DEXTO_INSTALL_FORCE = '1';
332
+ envOverrides.DEXTO_INSTALL_FORCE = '1';
333
+ }
334
+ if (process.platform === 'win32') {
335
+ const commandText = `irm ${DEFAULT_WINDOWS_INSTALL_URL} | iex`;
336
+ return {
337
+ command: 'powershell',
338
+ args: ['-NoProfile', '-ExecutionPolicy', 'Bypass', '-Command', commandText],
339
+ env,
340
+ displayCommand: commandDisplayWithEnvPowerShell(`powershell -NoProfile -ExecutionPolicy Bypass -Command ${shellEscapeForPowerShell(commandText)}`, envOverrides),
341
+ };
342
+ }
343
+ const commandText = `curl -fsSL ${DEFAULT_NATIVE_INSTALL_URL} | bash`;
344
+ return {
345
+ command: 'bash',
346
+ args: ['-lc', commandText],
347
+ env,
348
+ displayCommand: commandDisplayWithEnvPosix(commandText, envOverrides),
349
+ };
350
+ }
351
+ export function createLegacyNpmUninstallCommand() {
352
+ return {
353
+ command: 'npm',
354
+ args: ['uninstall', '-g', 'dexto'],
355
+ displayCommand: 'npm uninstall -g dexto',
356
+ };
357
+ }
358
+ export function normalizeRequestedVersion(version) {
359
+ if (!version) {
360
+ return null;
361
+ }
362
+ const trimmed = version.trim();
363
+ if (trimmed.length === 0) {
364
+ return null;
365
+ }
366
+ return trimmed.startsWith('dexto@') ? trimmed.slice('dexto@'.length) : trimmed;
367
+ }
368
+ export async function scheduleDeferredWindowsRemoval(targetPaths) {
369
+ const uniqueTargets = uniquePaths(targetPaths);
370
+ const removalScript = uniqueTargets
371
+ .map((targetPath) => {
372
+ const escapedPath = shellEscapeForPowerShell(targetPath);
373
+ return [
374
+ `if (Test-Path -LiteralPath ${escapedPath}) {`,
375
+ `Remove-Item -LiteralPath ${escapedPath} -Recurse -Force -ErrorAction SilentlyContinue`,
376
+ '}',
377
+ ].join(' ');
378
+ })
379
+ .join('; ');
380
+ const commandText = `Start-Sleep -Seconds 2; ${removalScript}`;
381
+ await new Promise((resolve, reject) => {
382
+ const child = spawn('powershell', ['-NoProfile', '-ExecutionPolicy', 'Bypass', '-Command', commandText], {
383
+ detached: true,
384
+ stdio: 'ignore',
385
+ windowsHide: true,
386
+ });
387
+ child.once('error', reject);
388
+ child.once('spawn', () => {
389
+ child.unref();
390
+ resolve();
391
+ });
392
+ });
393
+ }
394
+ export async function pathExists(targetPath) {
395
+ try {
396
+ await fs.lstat(targetPath);
397
+ return true;
398
+ }
399
+ catch {
400
+ return false;
401
+ }
402
+ }
403
+ function hasErrorCode(error, code) {
404
+ return typeof error === 'object' && error !== null && 'code' in error && error.code === code;
405
+ }
406
+ export async function removePath(targetPath) {
407
+ try {
408
+ const stat = await fs.lstat(targetPath);
409
+ await fs.rm(targetPath, { recursive: stat.isDirectory(), force: true });
410
+ }
411
+ catch (error) {
412
+ if (hasErrorCode(error, 'ENOENT')) {
413
+ return;
414
+ }
415
+ throw error;
416
+ }
417
+ }
418
+ export function getDefaultNativeBinaryPath() {
419
+ if (process.platform === 'win32') {
420
+ return path.join(os.homedir(), '.dexto', 'bin', DEXTO_BINARY);
421
+ }
422
+ return path.join(os.homedir(), '.local', 'bin', DEXTO_BINARY);
423
+ }
@@ -37,7 +37,7 @@ export declare function checkForUpdates(currentVersion: string): Promise<UpdateI
37
37
  * displayUpdateNotification({
38
38
  * current: '1.5.4',
39
39
  * latest: '1.6.0',
40
- * updateCommand: 'npm i -g dexto'
40
+ * updateCommand: 'dexto upgrade'
41
41
  * });
42
42
  * ```
43
43
  */
@@ -1 +1 @@
1
- {"version":3,"file":"version-check.d.ts","sourceRoot":"","sources":["../../../src/cli/utils/version-check.ts"],"names":[],"mappings":"AAkBA;;GAEG;AACH,MAAM,WAAW,UAAU;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,CAAC;CACzB;AAqGD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAsB,eAAe,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CA+DxF;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,yBAAyB,CAAC,UAAU,EAAE,UAAU,GAAG,IAAI,CAatE"}
1
+ {"version":3,"file":"version-check.d.ts","sourceRoot":"","sources":["../../../src/cli/utils/version-check.ts"],"names":[],"mappings":"AAiBA;;GAEG;AACH,MAAM,WAAW,UAAU;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,CAAC;CACzB;AAkJD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAsB,eAAe,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CA6DxF;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,yBAAyB,CAAC,UAAU,EAAE,UAAU,GAAG,IAAI,CAatE"}
@@ -1,13 +1,17 @@
1
1
  // packages/cli/src/cli/utils/version-check.ts
2
2
  import { promises as fs } from 'fs';
3
+ import os from 'os';
3
4
  import path from 'path';
4
5
  import chalk from 'chalk';
5
6
  import boxen from 'boxen';
6
- import { logger } from '@dexto/core';
7
- import { getDextoGlobalPath } from '@dexto/agent-management';
8
7
  const CACHE_TTL_MS = 24 * 60 * 60 * 1000; // 24 hours
9
- const NPM_REGISTRY_URL = 'https://registry.npmjs.org/dexto/latest';
10
- const CACHE_FILE_PATH = getDextoGlobalPath('cache', 'version-check.json');
8
+ const GITHUB_RELEASES_LATEST_URL = 'https://api.github.com/repos/truffle-ai/dexto/releases/latest';
9
+ const CACHE_FILE_PATH = path.join(os.homedir(), '.dexto', 'cache', 'version-check.json');
10
+ function debugLog(message) {
11
+ if (process.env.DEXTO_DEBUG === 'true') {
12
+ console.debug(`[version-check] ${message}`);
13
+ }
14
+ }
11
15
  /**
12
16
  * Compare two semver versions.
13
17
  * Returns:
@@ -63,32 +67,62 @@ async function saveCache(cache) {
63
67
  }
64
68
  catch (error) {
65
69
  // Non-critical - just log and continue
66
- logger.debug(`Failed to save version cache: ${error instanceof Error ? error.message : String(error)}`);
70
+ debugLog(`Failed to save version cache: ${error instanceof Error ? error.message : String(error)}`);
71
+ }
72
+ }
73
+ function normalizeReleaseTag(tagName) {
74
+ const trimmed = tagName.trim();
75
+ if (trimmed.length === 0) {
76
+ return null;
77
+ }
78
+ // GitHub releases in this monorepo can include scoped package tags like
79
+ // "@dexto/tools-filesystem@1.6.10". Always extract a trailing semver.
80
+ const trailingSemverMatch = trimmed.match(/(v?\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?)$/);
81
+ if (!trailingSemverMatch) {
82
+ return null;
67
83
  }
84
+ const version = trailingSemverMatch[1]?.trim();
85
+ return version && version.length > 0 ? version : null;
86
+ }
87
+ function extractLatestVersionFromRelease(payload) {
88
+ if (!payload || typeof payload !== 'object') {
89
+ return null;
90
+ }
91
+ const tagName = Reflect.get(payload, 'tag_name');
92
+ if (typeof tagName !== 'string') {
93
+ return null;
94
+ }
95
+ return normalizeReleaseTag(tagName);
68
96
  }
69
97
  /**
70
- * Fetch latest version from npm registry
98
+ * Fetch latest version from GitHub Releases
71
99
  */
72
100
  async function fetchLatestVersion() {
73
101
  const controller = new AbortController();
74
102
  const timeoutId = setTimeout(() => controller.abort(), 5000); // 5 second timeout
75
103
  try {
76
- const response = await fetch(NPM_REGISTRY_URL, {
104
+ const response = await fetch(GITHUB_RELEASES_LATEST_URL, {
77
105
  signal: controller.signal,
78
106
  headers: {
79
- Accept: 'application/json',
107
+ Accept: 'application/vnd.github+json',
108
+ 'User-Agent': 'dexto-cli-version-check',
80
109
  },
81
110
  });
82
111
  if (!response.ok) {
83
- logger.debug(`npm registry returned status ${response.status}`);
112
+ debugLog(`GitHub releases API returned status ${response.status}`);
84
113
  return null;
85
114
  }
86
115
  const data = (await response.json());
87
- return data.version || null;
116
+ const latestVersion = extractLatestVersionFromRelease(data);
117
+ if (!latestVersion) {
118
+ debugLog('GitHub releases API response missing valid tag_name');
119
+ return null;
120
+ }
121
+ return latestVersion;
88
122
  }
89
123
  catch (error) {
90
124
  // Network errors, timeouts, etc. - silent fail
91
- logger.debug(`Failed to fetch latest version: ${error instanceof Error ? error.message : String(error)}`);
125
+ debugLog(`Failed to fetch latest version: ${error instanceof Error ? error.message : String(error)}`);
92
126
  return null;
93
127
  }
94
128
  finally {
@@ -118,7 +152,7 @@ async function fetchLatestVersion() {
118
152
  export async function checkForUpdates(currentVersion) {
119
153
  // Check if update checks are disabled
120
154
  if (process.env.DEXTO_NO_UPDATE_CHECK === 'true') {
121
- logger.debug('Version check disabled via DEXTO_NO_UPDATE_CHECK');
155
+ debugLog('Version check disabled via DEXTO_NO_UPDATE_CHECK');
122
156
  return null;
123
157
  }
124
158
  try {
@@ -128,20 +162,20 @@ export async function checkForUpdates(currentVersion) {
128
162
  if (cache && cache.currentVersion === currentVersion) {
129
163
  const cacheAge = now - cache.lastCheck;
130
164
  if (cacheAge < CACHE_TTL_MS) {
131
- logger.debug(`Using cached version info (age: ${Math.round(cacheAge / 1000 / 60)} minutes)`);
165
+ debugLog(`Using cached version info (age: ${Math.round(cacheAge / 1000 / 60)} minutes)`);
132
166
  // Return cached result if newer version exists
133
167
  if (compareSemver(cache.latestVersion, currentVersion) > 0) {
134
168
  return {
135
169
  current: currentVersion,
136
170
  latest: cache.latestVersion,
137
- updateCommand: 'npm i -g dexto',
171
+ updateCommand: 'dexto upgrade',
138
172
  };
139
173
  }
140
174
  return null;
141
175
  }
142
176
  }
143
- // Cache expired or invalid - fetch from npm
144
- logger.debug('Fetching latest version from npm registry');
177
+ // Cache expired or invalid - fetch from GitHub releases
178
+ debugLog('Fetching latest version from GitHub releases');
145
179
  const latestVersion = await fetchLatestVersion();
146
180
  if (!latestVersion) {
147
181
  return null;
@@ -158,14 +192,14 @@ export async function checkForUpdates(currentVersion) {
158
192
  return {
159
193
  current: currentVersion,
160
194
  latest: latestVersion,
161
- updateCommand: 'npm i -g dexto',
195
+ updateCommand: 'dexto upgrade',
162
196
  };
163
197
  }
164
198
  return null;
165
199
  }
166
200
  catch (error) {
167
201
  // Never fail the CLI startup due to version check errors
168
- logger.debug(`Version check error: ${error instanceof Error ? error.message : String(error)}`);
202
+ debugLog(`Version check error: ${error instanceof Error ? error.message : String(error)}`);
169
203
  return null;
170
204
  }
171
205
  }
@@ -179,7 +213,7 @@ export async function checkForUpdates(currentVersion) {
179
213
  * displayUpdateNotification({
180
214
  * current: '1.5.4',
181
215
  * latest: '1.6.0',
182
- * updateCommand: 'npm i -g dexto'
216
+ * updateCommand: 'dexto upgrade'
183
217
  * });
184
218
  * ```
185
219
  */
@@ -22,7 +22,7 @@ function readVersionFromPackageJson(packageJsonPath) {
22
22
  return undefined;
23
23
  }
24
24
  function resolveCliVersion() {
25
- // Regular installs (npm/pnpm): package.json is next to dist/.
25
+ // Regular npm installs: package.json is next to dist/.
26
26
  const scriptDir = path.dirname(fileURLToPath(import.meta.url));
27
27
  const localPackageJsonPath = path.resolve(scriptDir, '..', 'package.json');
28
28
  const localVersion = readVersionFromPackageJson(localPackageJsonPath);
@@ -58,6 +58,7 @@ import { registerMcpCommand } from './cli/commands/mcp/register.js';
58
58
  import { registerImageCommand } from './cli/commands/image/register.js';
59
59
  import { registerPluginCommand } from './cli/commands/plugin/register.js';
60
60
  import { registerAgentsCommand } from './cli/commands/agents/register.js';
61
+ import { registerDeployCommand } from './cli/commands/deploy/register.js';
61
62
  const program = new Command();
62
63
  let imageImporterConfigured = false;
63
64
  let dextoApiKeyBootstrapped = false;
@@ -146,6 +147,7 @@ program
146
147
  }
147
148
  }));
148
149
  registerImageCommand({ program });
150
+ registerDeployCommand({ program });
149
151
  // 4) `init-app` SUB-COMMAND
150
152
  program
151
153
  .command('init-app')
@@ -211,7 +213,45 @@ program
211
213
  }
212
214
  }));
213
215
  registerAgentsCommand({ program });
214
- // 7) `which` SUB-COMMAND
216
+ // 7) `upgrade` SUB-COMMAND
217
+ program
218
+ .command('upgrade [version]')
219
+ .description('Upgrade Dexto CLI (auto-migrates npm installs to native)')
220
+ .option('--dry-run', 'Print commands without executing them')
221
+ .option('--force', 'Force reinstall during upgrade')
222
+ .action(withAnalytics('upgrade', async (version, options) => {
223
+ try {
224
+ const { handleUpgradeCommand } = await import('./cli/commands/upgrade.js');
225
+ await handleUpgradeCommand(version, options);
226
+ safeExit('upgrade', 0);
227
+ }
228
+ catch (err) {
229
+ if (err instanceof ExitSignal)
230
+ throw err;
231
+ console.error(`❌ dexto upgrade command failed: ${err}`);
232
+ safeExit('upgrade', 1, 'error');
233
+ }
234
+ }));
235
+ // 8) `uninstall` SUB-COMMAND (CLI self uninstall)
236
+ program
237
+ .command('uninstall')
238
+ .description('Uninstall the Dexto CLI binary (does not uninstall agents)')
239
+ .option('--purge', 'Also remove ~/.dexto completely')
240
+ .option('--dry-run', 'Print actions without deleting files')
241
+ .action(withAnalytics('uninstall', async (options) => {
242
+ try {
243
+ const { handleUninstallCliCommand } = await import('./cli/commands/uninstall.js');
244
+ await handleUninstallCliCommand(options);
245
+ safeExit('uninstall', 0);
246
+ }
247
+ catch (err) {
248
+ if (err instanceof ExitSignal)
249
+ throw err;
250
+ console.error(`❌ dexto uninstall command failed: ${err}`);
251
+ safeExit('uninstall', 1, 'error');
252
+ }
253
+ }));
254
+ // 9) `which` SUB-COMMAND
215
255
  program
216
256
  .command('which <agent>')
217
257
  .description('Show the path to an agent')
@@ -520,6 +560,23 @@ program
520
560
  }
521
561
  // Now resolve agent (will auto-install since setup is complete)
522
562
  resolvedPath = await resolveAgentPath(opts.agent, opts.autoInstall !== false);
563
+ if (opts.interactive !== false) {
564
+ const { getBundledSyncTargetForAgentPath, shouldPromptForSync, handleSyncAgentsCommand, } = await import('./cli/commands/agents/sync.js');
565
+ const syncTarget = getBundledSyncTargetForAgentPath(resolvedPath);
566
+ if (syncTarget && (await shouldPromptForSync(resolvedPath))) {
567
+ const shouldSync = await p.confirm({
568
+ message: `Bundled agent updates available for '${syncTarget.agentId}'. Sync now?`,
569
+ initialValue: true,
570
+ });
571
+ if (!p.isCancel(shouldSync) && shouldSync) {
572
+ await handleSyncAgentsCommand({
573
+ force: true,
574
+ quiet: true,
575
+ agentIds: [syncTarget.agentId],
576
+ });
577
+ }
578
+ }
579
+ }
523
580
  }
524
581
  // Load raw config and apply CLI overrides
525
582
  const rawConfig = await loadAgentConfig(resolvedPath);