@dexto/agent-management 1.4.0 → 1.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/dist/AgentFactory.cjs +1 -2
  2. package/dist/AgentFactory.d.ts +1 -1
  3. package/dist/AgentFactory.d.ts.map +1 -1
  4. package/dist/AgentFactory.js +1 -2
  5. package/dist/config/config-enrichment.cjs +50 -1
  6. package/dist/config/config-enrichment.d.ts +2 -2
  7. package/dist/config/config-enrichment.d.ts.map +1 -1
  8. package/dist/config/config-enrichment.js +51 -3
  9. package/dist/config/discover-prompts.cjs +13 -0
  10. package/dist/config/discover-prompts.d.ts +13 -0
  11. package/dist/config/discover-prompts.d.ts.map +1 -1
  12. package/dist/config/discover-prompts.js +12 -0
  13. package/dist/config/errors.cjs +2 -2
  14. package/dist/config/errors.js +2 -2
  15. package/dist/index.cjs +69 -0
  16. package/dist/index.d.ts +4 -3
  17. package/dist/index.d.ts.map +1 -1
  18. package/dist/index.js +72 -2
  19. package/dist/installation.cjs +0 -13
  20. package/dist/installation.d.ts +0 -2
  21. package/dist/installation.d.ts.map +1 -1
  22. package/dist/installation.js +0 -13
  23. package/dist/models/custom-models.cjs +46 -2
  24. package/dist/models/custom-models.d.ts +54 -6
  25. package/dist/models/custom-models.d.ts.map +1 -1
  26. package/dist/models/custom-models.js +45 -2
  27. package/dist/models/index.cjs +89 -0
  28. package/dist/models/index.d.ts +11 -0
  29. package/dist/models/index.d.ts.map +1 -0
  30. package/dist/models/index.js +68 -0
  31. package/dist/models/path-resolver.cjs +154 -0
  32. package/dist/models/path-resolver.d.ts +77 -0
  33. package/dist/models/path-resolver.d.ts.map +1 -0
  34. package/dist/models/path-resolver.js +108 -0
  35. package/dist/models/state-manager.cjs +220 -0
  36. package/dist/models/state-manager.d.ts +138 -0
  37. package/dist/models/state-manager.d.ts.map +1 -0
  38. package/dist/models/state-manager.js +184 -0
  39. package/dist/preferences/error-codes.cjs +2 -0
  40. package/dist/preferences/error-codes.d.ts +3 -1
  41. package/dist/preferences/error-codes.d.ts.map +1 -1
  42. package/dist/preferences/error-codes.js +2 -0
  43. package/dist/preferences/index.d.ts +1 -1
  44. package/dist/preferences/index.d.ts.map +1 -1
  45. package/dist/preferences/loader.cjs +35 -6
  46. package/dist/preferences/loader.d.ts +25 -4
  47. package/dist/preferences/loader.d.ts.map +1 -1
  48. package/dist/preferences/loader.js +35 -6
  49. package/dist/preferences/schemas.cjs +24 -3
  50. package/dist/preferences/schemas.d.ts +64 -24
  51. package/dist/preferences/schemas.d.ts.map +1 -1
  52. package/dist/preferences/schemas.js +31 -4
  53. package/dist/registry/registry.cjs +7 -43
  54. package/dist/registry/registry.d.ts +3 -6
  55. package/dist/registry/registry.d.ts.map +1 -1
  56. package/dist/registry/registry.js +7 -43
  57. package/dist/registry/types.d.ts +2 -4
  58. package/dist/registry/types.d.ts.map +1 -1
  59. package/dist/resolver.cjs +20 -20
  60. package/dist/resolver.d.ts +1 -2
  61. package/dist/resolver.d.ts.map +1 -1
  62. package/dist/resolver.js +20 -20
  63. package/dist/utils/api-key-resolver.cjs +19 -1
  64. package/dist/utils/api-key-resolver.d.ts.map +1 -1
  65. package/dist/utils/api-key-resolver.js +19 -1
  66. package/dist/utils/api-key-store.cjs +46 -0
  67. package/dist/utils/api-key-store.d.ts +27 -0
  68. package/dist/utils/api-key-store.d.ts.map +1 -1
  69. package/dist/utils/api-key-store.js +44 -0
  70. package/dist/utils/env-file.cjs +20 -68
  71. package/dist/utils/env-file.d.ts +2 -1
  72. package/dist/utils/env-file.d.ts.map +1 -1
  73. package/dist/utils/env-file.js +20 -68
  74. package/dist/writer.cjs +20 -2
  75. package/dist/writer.d.ts +1 -0
  76. package/dist/writer.d.ts.map +1 -1
  77. package/dist/writer.js +20 -2
  78. package/package.json +2 -2
@@ -0,0 +1,154 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+ var path_resolver_exports = {};
30
+ __export(path_resolver_exports, {
31
+ deleteModelDirectory: () => deleteModelDirectory,
32
+ ensureModelDirectory: () => ensureModelDirectory,
33
+ ensureModelsDirectory: () => ensureModelsDirectory,
34
+ formatSize: () => formatSize,
35
+ getModelDirectory: () => getModelDirectory,
36
+ getModelFilePath: () => getModelFilePath,
37
+ getModelFileSize: () => getModelFileSize,
38
+ getModelStatePath: () => getModelStatePath,
39
+ getModelTempDirectory: () => getModelTempDirectory,
40
+ getModelsDirectory: () => getModelsDirectory,
41
+ getModelsDiskUsage: () => getModelsDiskUsage,
42
+ listModelDirectories: () => listModelDirectories,
43
+ modelFileExists: () => modelFileExists
44
+ });
45
+ module.exports = __toCommonJS(path_resolver_exports);
46
+ var path = __toESM(require("path"), 1);
47
+ var import_fs = require("fs");
48
+ var import_os = require("os");
49
+ function getModelsDirectory() {
50
+ return path.join((0, import_os.homedir)(), ".dexto", "models");
51
+ }
52
+ function getModelFilePath(modelId, filename) {
53
+ return path.join(getModelsDirectory(), modelId, filename);
54
+ }
55
+ function getModelDirectory(modelId) {
56
+ return path.join(getModelsDirectory(), modelId);
57
+ }
58
+ function getModelStatePath() {
59
+ return path.join(getModelsDirectory(), "state.json");
60
+ }
61
+ function getModelTempDirectory() {
62
+ return path.join(getModelsDirectory(), ".tmp");
63
+ }
64
+ async function ensureModelsDirectory() {
65
+ const modelsDir = getModelsDirectory();
66
+ const tempDir = getModelTempDirectory();
67
+ await import_fs.promises.mkdir(modelsDir, { recursive: true });
68
+ await import_fs.promises.mkdir(tempDir, { recursive: true });
69
+ }
70
+ async function ensureModelDirectory(modelId) {
71
+ const modelDir = getModelDirectory(modelId);
72
+ await import_fs.promises.mkdir(modelDir, { recursive: true });
73
+ return modelDir;
74
+ }
75
+ async function modelFileExists(modelId, filename) {
76
+ const filePath = getModelFilePath(modelId, filename);
77
+ try {
78
+ await import_fs.promises.access(filePath);
79
+ return true;
80
+ } catch {
81
+ return false;
82
+ }
83
+ }
84
+ async function getModelFileSize(modelId, filename) {
85
+ const filePath = getModelFilePath(modelId, filename);
86
+ try {
87
+ const stats = await import_fs.promises.stat(filePath);
88
+ return stats.size;
89
+ } catch {
90
+ return null;
91
+ }
92
+ }
93
+ async function deleteModelDirectory(modelId) {
94
+ const modelDir = getModelDirectory(modelId);
95
+ try {
96
+ await import_fs.promises.rm(modelDir, { recursive: true, force: true });
97
+ return true;
98
+ } catch {
99
+ return false;
100
+ }
101
+ }
102
+ async function listModelDirectories() {
103
+ const modelsDir = getModelsDirectory();
104
+ try {
105
+ const entries = await import_fs.promises.readdir(modelsDir, { withFileTypes: true });
106
+ return entries.filter((e) => e.isDirectory() && !e.name.startsWith(".")).map((e) => e.name);
107
+ } catch {
108
+ return [];
109
+ }
110
+ }
111
+ async function getModelsDiskUsage() {
112
+ const modelsDir = getModelsDirectory();
113
+ async function getDirSize(dir) {
114
+ let size = 0;
115
+ try {
116
+ const entries = await import_fs.promises.readdir(dir, { withFileTypes: true });
117
+ for (const entry of entries) {
118
+ const entryPath = path.join(dir, entry.name);
119
+ if (entry.isDirectory()) {
120
+ size += await getDirSize(entryPath);
121
+ } else if (entry.isFile()) {
122
+ const stats = await import_fs.promises.stat(entryPath);
123
+ size += stats.size;
124
+ }
125
+ }
126
+ } catch {
127
+ }
128
+ return size;
129
+ }
130
+ return getDirSize(modelsDir);
131
+ }
132
+ function formatSize(bytes) {
133
+ if (bytes === 0) return "0 B";
134
+ const units = ["B", "KB", "MB", "GB", "TB"];
135
+ const k = 1024;
136
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
137
+ return `${(bytes / Math.pow(k, i)).toFixed(1)} ${units[i]}`;
138
+ }
139
+ // Annotate the CommonJS export names for ESM import in node:
140
+ 0 && (module.exports = {
141
+ deleteModelDirectory,
142
+ ensureModelDirectory,
143
+ ensureModelsDirectory,
144
+ formatSize,
145
+ getModelDirectory,
146
+ getModelFilePath,
147
+ getModelFileSize,
148
+ getModelStatePath,
149
+ getModelTempDirectory,
150
+ getModelsDirectory,
151
+ getModelsDiskUsage,
152
+ listModelDirectories,
153
+ modelFileExists
154
+ });
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Path resolver for local model storage.
3
+ *
4
+ * Models are stored globally at ~/.dexto/models/ to be shared across projects.
5
+ * This avoids duplicating large model files for each project.
6
+ */
7
+ /**
8
+ * Get the base models directory path.
9
+ * Always returns global path: ~/.dexto/models/
10
+ */
11
+ export declare function getModelsDirectory(): string;
12
+ /**
13
+ * Get the path to a specific model file.
14
+ * @param modelId Model ID from registry
15
+ * @param filename GGUF filename
16
+ */
17
+ export declare function getModelFilePath(modelId: string, filename: string): string;
18
+ /**
19
+ * Get the path to a model's directory.
20
+ * @param modelId Model ID from registry
21
+ */
22
+ export declare function getModelDirectory(modelId: string): string;
23
+ /**
24
+ * Get the path to the model state file.
25
+ * Stores download status, hashes, and usage metadata.
26
+ */
27
+ export declare function getModelStatePath(): string;
28
+ /**
29
+ * Get the path to the model download temp directory.
30
+ * Used for in-progress downloads.
31
+ */
32
+ export declare function getModelTempDirectory(): string;
33
+ /**
34
+ * Ensure the models directory and subdirectories exist.
35
+ */
36
+ export declare function ensureModelsDirectory(): Promise<void>;
37
+ /**
38
+ * Ensure a specific model's directory exists.
39
+ * @param modelId Model ID from registry
40
+ */
41
+ export declare function ensureModelDirectory(modelId: string): Promise<string>;
42
+ /**
43
+ * Check if a model file exists at the expected path.
44
+ * @param modelId Model ID from registry
45
+ * @param filename GGUF filename
46
+ */
47
+ export declare function modelFileExists(modelId: string, filename: string): Promise<boolean>;
48
+ /**
49
+ * Get file size of an installed model.
50
+ * @param modelId Model ID from registry
51
+ * @param filename GGUF filename
52
+ * @returns File size in bytes, or null if file doesn't exist
53
+ */
54
+ export declare function getModelFileSize(modelId: string, filename: string): Promise<number | null>;
55
+ /**
56
+ * Delete a model's directory and all its files.
57
+ * @param modelId Model ID to delete
58
+ * @returns True if deleted, false if not found
59
+ */
60
+ export declare function deleteModelDirectory(modelId: string): Promise<boolean>;
61
+ /**
62
+ * List all model directories in the models folder.
63
+ * @returns Array of model IDs (directory names)
64
+ */
65
+ export declare function listModelDirectories(): Promise<string[]>;
66
+ /**
67
+ * Get disk usage statistics for the models directory.
68
+ * @returns Total bytes used by models, or 0 if directory doesn't exist
69
+ */
70
+ export declare function getModelsDiskUsage(): Promise<number>;
71
+ /**
72
+ * Format bytes to human-readable string.
73
+ * @param bytes Number of bytes
74
+ * @returns Formatted string (e.g., "4.5 GB")
75
+ */
76
+ export declare function formatSize(bytes: number): string;
77
+ //# sourceMappingURL=path-resolver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"path-resolver.d.ts","sourceRoot":"","sources":["../../src/models/path-resolver.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH;;;GAGG;AACH,wBAAgB,kBAAkB,IAAI,MAAM,CAE3C;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAE1E;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAEzD;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,IAAI,MAAM,CAE1C;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,IAAI,MAAM,CAE9C;AAED;;GAEG;AACH,wBAAsB,qBAAqB,IAAI,OAAO,CAAC,IAAI,CAAC,CAM3D;AAED;;;GAGG;AACH,wBAAsB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAI3E;AAED;;;;GAIG;AACH,wBAAsB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAQzF;AAED;;;;;GAKG;AACH,wBAAsB,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAQhG;AAED;;;;GAIG;AACH,wBAAsB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAQ5E;AAED;;;GAGG;AACH,wBAAsB,oBAAoB,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,CAQ9D;AAED;;;GAGG;AACH,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,MAAM,CAAC,CAuB1D;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAQhD"}
@@ -0,0 +1,108 @@
1
+ import * as path from "path";
2
+ import { promises as fs } from "fs";
3
+ import { homedir } from "os";
4
+ function getModelsDirectory() {
5
+ return path.join(homedir(), ".dexto", "models");
6
+ }
7
+ function getModelFilePath(modelId, filename) {
8
+ return path.join(getModelsDirectory(), modelId, filename);
9
+ }
10
+ function getModelDirectory(modelId) {
11
+ return path.join(getModelsDirectory(), modelId);
12
+ }
13
+ function getModelStatePath() {
14
+ return path.join(getModelsDirectory(), "state.json");
15
+ }
16
+ function getModelTempDirectory() {
17
+ return path.join(getModelsDirectory(), ".tmp");
18
+ }
19
+ async function ensureModelsDirectory() {
20
+ const modelsDir = getModelsDirectory();
21
+ const tempDir = getModelTempDirectory();
22
+ await fs.mkdir(modelsDir, { recursive: true });
23
+ await fs.mkdir(tempDir, { recursive: true });
24
+ }
25
+ async function ensureModelDirectory(modelId) {
26
+ const modelDir = getModelDirectory(modelId);
27
+ await fs.mkdir(modelDir, { recursive: true });
28
+ return modelDir;
29
+ }
30
+ async function modelFileExists(modelId, filename) {
31
+ const filePath = getModelFilePath(modelId, filename);
32
+ try {
33
+ await fs.access(filePath);
34
+ return true;
35
+ } catch {
36
+ return false;
37
+ }
38
+ }
39
+ async function getModelFileSize(modelId, filename) {
40
+ const filePath = getModelFilePath(modelId, filename);
41
+ try {
42
+ const stats = await fs.stat(filePath);
43
+ return stats.size;
44
+ } catch {
45
+ return null;
46
+ }
47
+ }
48
+ async function deleteModelDirectory(modelId) {
49
+ const modelDir = getModelDirectory(modelId);
50
+ try {
51
+ await fs.rm(modelDir, { recursive: true, force: true });
52
+ return true;
53
+ } catch {
54
+ return false;
55
+ }
56
+ }
57
+ async function listModelDirectories() {
58
+ const modelsDir = getModelsDirectory();
59
+ try {
60
+ const entries = await fs.readdir(modelsDir, { withFileTypes: true });
61
+ return entries.filter((e) => e.isDirectory() && !e.name.startsWith(".")).map((e) => e.name);
62
+ } catch {
63
+ return [];
64
+ }
65
+ }
66
+ async function getModelsDiskUsage() {
67
+ const modelsDir = getModelsDirectory();
68
+ async function getDirSize(dir) {
69
+ let size = 0;
70
+ try {
71
+ const entries = await fs.readdir(dir, { withFileTypes: true });
72
+ for (const entry of entries) {
73
+ const entryPath = path.join(dir, entry.name);
74
+ if (entry.isDirectory()) {
75
+ size += await getDirSize(entryPath);
76
+ } else if (entry.isFile()) {
77
+ const stats = await fs.stat(entryPath);
78
+ size += stats.size;
79
+ }
80
+ }
81
+ } catch {
82
+ }
83
+ return size;
84
+ }
85
+ return getDirSize(modelsDir);
86
+ }
87
+ function formatSize(bytes) {
88
+ if (bytes === 0) return "0 B";
89
+ const units = ["B", "KB", "MB", "GB", "TB"];
90
+ const k = 1024;
91
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
92
+ return `${(bytes / Math.pow(k, i)).toFixed(1)} ${units[i]}`;
93
+ }
94
+ export {
95
+ deleteModelDirectory,
96
+ ensureModelDirectory,
97
+ ensureModelsDirectory,
98
+ formatSize,
99
+ getModelDirectory,
100
+ getModelFilePath,
101
+ getModelFileSize,
102
+ getModelStatePath,
103
+ getModelTempDirectory,
104
+ getModelsDirectory,
105
+ getModelsDiskUsage,
106
+ listModelDirectories,
107
+ modelFileExists
108
+ };
@@ -0,0 +1,220 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var state_manager_exports = {};
20
+ __export(state_manager_exports, {
21
+ addInstalledModel: () => addInstalledModel,
22
+ addToDownloadQueue: () => addToDownloadQueue,
23
+ getActiveModel: () => getActiveModel,
24
+ getActiveModelId: () => getActiveModelId,
25
+ getAllInstalledModels: () => getAllInstalledModels,
26
+ getDownloadQueue: () => getDownloadQueue,
27
+ getInstalledModel: () => getInstalledModel,
28
+ getInstalledModelCount: () => getInstalledModelCount,
29
+ getTotalInstalledSize: () => getTotalInstalledSize,
30
+ isModelInstalled: () => isModelInstalled,
31
+ loadModelState: () => loadModelState,
32
+ registerManualModel: () => registerManualModel,
33
+ removeFromDownloadQueue: () => removeFromDownloadQueue,
34
+ removeInstalledModel: () => removeInstalledModel,
35
+ saveModelState: () => saveModelState,
36
+ setActiveModel: () => setActiveModel,
37
+ syncStateWithFilesystem: () => syncStateWithFilesystem,
38
+ updateModelLastUsed: () => updateModelLastUsed
39
+ });
40
+ module.exports = __toCommonJS(state_manager_exports);
41
+ var import_fs = require("fs");
42
+ var import_path_resolver = require("./path-resolver.js");
43
+ const CURRENT_VERSION = "1.0";
44
+ function createDefaultState() {
45
+ return {
46
+ version: CURRENT_VERSION,
47
+ installed: {},
48
+ downloadQueue: []
49
+ };
50
+ }
51
+ async function loadModelState() {
52
+ const statePath = (0, import_path_resolver.getModelStatePath)();
53
+ try {
54
+ const content = await import_fs.promises.readFile(statePath, "utf-8");
55
+ const state = JSON.parse(content);
56
+ if (state.version !== CURRENT_VERSION) {
57
+ return migrateState(state);
58
+ }
59
+ return state;
60
+ } catch (error) {
61
+ if (error.code === "ENOENT") {
62
+ return createDefaultState();
63
+ }
64
+ console.warn("Invalid model state file, resetting to default");
65
+ return createDefaultState();
66
+ }
67
+ }
68
+ async function saveModelState(state) {
69
+ await (0, import_path_resolver.ensureModelsDirectory)();
70
+ const statePath = (0, import_path_resolver.getModelStatePath)();
71
+ await import_fs.promises.writeFile(statePath, JSON.stringify(state, null, 2), "utf-8");
72
+ }
73
+ function migrateState(state) {
74
+ return {
75
+ ...state,
76
+ version: CURRENT_VERSION
77
+ };
78
+ }
79
+ async function addInstalledModel(model) {
80
+ const state = await loadModelState();
81
+ state.installed[model.id] = model;
82
+ await saveModelState(state);
83
+ }
84
+ async function removeInstalledModel(modelId) {
85
+ const state = await loadModelState();
86
+ if (!state.installed[modelId]) {
87
+ return false;
88
+ }
89
+ delete state.installed[modelId];
90
+ if (state.activeModelId === modelId) {
91
+ delete state.activeModelId;
92
+ }
93
+ state.downloadQueue = state.downloadQueue.filter((id) => id !== modelId);
94
+ await saveModelState(state);
95
+ return true;
96
+ }
97
+ async function getInstalledModel(modelId) {
98
+ const state = await loadModelState();
99
+ return state.installed[modelId] ?? null;
100
+ }
101
+ async function getAllInstalledModels() {
102
+ const state = await loadModelState();
103
+ return Object.values(state.installed);
104
+ }
105
+ async function isModelInstalled(modelId) {
106
+ const model = await getInstalledModel(modelId);
107
+ if (!model) {
108
+ return false;
109
+ }
110
+ return (0, import_path_resolver.modelFileExists)(modelId, model.filename);
111
+ }
112
+ async function updateModelLastUsed(modelId) {
113
+ const state = await loadModelState();
114
+ const model = state.installed[modelId];
115
+ if (model) {
116
+ model.lastUsedAt = (/* @__PURE__ */ new Date()).toISOString();
117
+ await saveModelState(state);
118
+ }
119
+ }
120
+ async function setActiveModel(modelId) {
121
+ const state = await loadModelState();
122
+ if (modelId === void 0) {
123
+ delete state.activeModelId;
124
+ } else {
125
+ state.activeModelId = modelId;
126
+ }
127
+ await saveModelState(state);
128
+ }
129
+ async function getActiveModelId() {
130
+ const state = await loadModelState();
131
+ return state.activeModelId;
132
+ }
133
+ async function getActiveModel() {
134
+ const activeId = await getActiveModelId();
135
+ if (!activeId) {
136
+ return null;
137
+ }
138
+ return getInstalledModel(activeId);
139
+ }
140
+ async function addToDownloadQueue(modelId) {
141
+ const state = await loadModelState();
142
+ if (!state.downloadQueue.includes(modelId)) {
143
+ state.downloadQueue.push(modelId);
144
+ await saveModelState(state);
145
+ }
146
+ }
147
+ async function removeFromDownloadQueue(modelId) {
148
+ const state = await loadModelState();
149
+ state.downloadQueue = state.downloadQueue.filter((id) => id !== modelId);
150
+ await saveModelState(state);
151
+ }
152
+ async function getDownloadQueue() {
153
+ const state = await loadModelState();
154
+ return [...state.downloadQueue];
155
+ }
156
+ async function syncStateWithFilesystem() {
157
+ const state = await loadModelState();
158
+ const removed = [];
159
+ const kept = [];
160
+ for (const [modelId, model] of Object.entries(state.installed)) {
161
+ const exists = await (0, import_path_resolver.modelFileExists)(modelId, model.filename);
162
+ if (exists) {
163
+ kept.push(modelId);
164
+ } else {
165
+ removed.push(modelId);
166
+ delete state.installed[modelId];
167
+ }
168
+ }
169
+ if (state.activeModelId && removed.includes(state.activeModelId)) {
170
+ delete state.activeModelId;
171
+ }
172
+ if (removed.length > 0) {
173
+ await saveModelState(state);
174
+ }
175
+ return { removed, kept };
176
+ }
177
+ async function getTotalInstalledSize() {
178
+ const state = await loadModelState();
179
+ return Object.values(state.installed).reduce((total, model) => total + model.sizeBytes, 0);
180
+ }
181
+ async function getInstalledModelCount() {
182
+ const state = await loadModelState();
183
+ return Object.keys(state.installed).length;
184
+ }
185
+ async function registerManualModel(modelId, filename, sizeBytes, sha256) {
186
+ const filePath = (0, import_path_resolver.getModelFilePath)(modelId, filename);
187
+ const model = {
188
+ id: modelId,
189
+ filePath,
190
+ sizeBytes,
191
+ downloadedAt: (/* @__PURE__ */ new Date()).toISOString(),
192
+ source: "manual",
193
+ filename
194
+ };
195
+ if (sha256 !== void 0) {
196
+ model.sha256 = sha256;
197
+ }
198
+ await addInstalledModel(model);
199
+ }
200
+ // Annotate the CommonJS export names for ESM import in node:
201
+ 0 && (module.exports = {
202
+ addInstalledModel,
203
+ addToDownloadQueue,
204
+ getActiveModel,
205
+ getActiveModelId,
206
+ getAllInstalledModels,
207
+ getDownloadQueue,
208
+ getInstalledModel,
209
+ getInstalledModelCount,
210
+ getTotalInstalledSize,
211
+ isModelInstalled,
212
+ loadModelState,
213
+ registerManualModel,
214
+ removeFromDownloadQueue,
215
+ removeInstalledModel,
216
+ saveModelState,
217
+ setActiveModel,
218
+ syncStateWithFilesystem,
219
+ updateModelLastUsed
220
+ });
@@ -0,0 +1,138 @@
1
+ /**
2
+ * Model state manager for tracking downloaded local models.
3
+ *
4
+ * Persists model metadata to ~/.dexto/models/state.json including:
5
+ * - Which models are installed
6
+ * - File paths and sizes
7
+ * - Download timestamps
8
+ * - Usage tracking
9
+ *
10
+ * Note: ModelSource and InstalledModel types here intentionally differ from
11
+ * packages/core/src/llm/providers/local/types.ts. This package extends the core
12
+ * types with agent-management specific needs:
13
+ * - ModelSource adds 'manual' for user-placed model files
14
+ * - InstalledModel adds 'filename' for file system operations (isModelInstalled, syncStateWithFilesystem)
15
+ */
16
+ /**
17
+ * Source of the model download.
18
+ */
19
+ export type ModelSource = 'huggingface' | 'manual';
20
+ /**
21
+ * Installed model metadata.
22
+ */
23
+ export interface InstalledModel {
24
+ /** Model ID from registry */
25
+ id: string;
26
+ /** Absolute path to the .gguf file */
27
+ filePath: string;
28
+ /** File size in bytes */
29
+ sizeBytes: number;
30
+ /** When the model was downloaded (ISO timestamp) */
31
+ downloadedAt: string;
32
+ /** When the model was last used (ISO timestamp) */
33
+ lastUsedAt?: string;
34
+ /** SHA-256 hash of the file for integrity verification */
35
+ sha256?: string;
36
+ /** Source of the download */
37
+ source: ModelSource;
38
+ /** GGUF filename */
39
+ filename: string;
40
+ }
41
+ /**
42
+ * Persisted model state.
43
+ */
44
+ export interface ModelState {
45
+ /** Schema version for migrations */
46
+ version: string;
47
+ /** Map of model ID to installed model info */
48
+ installed: Record<string, InstalledModel>;
49
+ /** Currently active/selected model ID */
50
+ activeModelId?: string;
51
+ /** Queue of model IDs pending download */
52
+ downloadQueue: string[];
53
+ }
54
+ /**
55
+ * Load model state from disk.
56
+ * Returns default state if file doesn't exist.
57
+ */
58
+ export declare function loadModelState(): Promise<ModelState>;
59
+ /**
60
+ * Save model state to disk.
61
+ */
62
+ export declare function saveModelState(state: ModelState): Promise<void>;
63
+ /**
64
+ * Add an installed model to state.
65
+ *
66
+ * Note: These operations are not atomic. Ensure single-threaded access
67
+ * or implement file locking for concurrent usage scenarios.
68
+ */
69
+ export declare function addInstalledModel(model: InstalledModel): Promise<void>;
70
+ /**
71
+ * Remove an installed model from state.
72
+ *
73
+ * Note: These operations are not atomic. Ensure single-threaded access
74
+ * or implement file locking for concurrent usage scenarios.
75
+ */
76
+ export declare function removeInstalledModel(modelId: string): Promise<boolean>;
77
+ /**
78
+ * Get installed model info.
79
+ */
80
+ export declare function getInstalledModel(modelId: string): Promise<InstalledModel | null>;
81
+ /**
82
+ * Get all installed models.
83
+ */
84
+ export declare function getAllInstalledModels(): Promise<InstalledModel[]>;
85
+ /**
86
+ * Check if a model is installed.
87
+ */
88
+ export declare function isModelInstalled(modelId: string): Promise<boolean>;
89
+ /**
90
+ * Update last used timestamp for a model.
91
+ */
92
+ export declare function updateModelLastUsed(modelId: string): Promise<void>;
93
+ /**
94
+ * Set the active model.
95
+ */
96
+ export declare function setActiveModel(modelId: string | undefined): Promise<void>;
97
+ /**
98
+ * Get the active model ID.
99
+ */
100
+ export declare function getActiveModelId(): Promise<string | undefined>;
101
+ /**
102
+ * Get the active model info.
103
+ */
104
+ export declare function getActiveModel(): Promise<InstalledModel | null>;
105
+ /**
106
+ * Add a model to the download queue.
107
+ */
108
+ export declare function addToDownloadQueue(modelId: string): Promise<void>;
109
+ /**
110
+ * Remove a model from the download queue.
111
+ */
112
+ export declare function removeFromDownloadQueue(modelId: string): Promise<void>;
113
+ /**
114
+ * Get the download queue.
115
+ */
116
+ export declare function getDownloadQueue(): Promise<string[]>;
117
+ /**
118
+ * Sync state with actual filesystem.
119
+ * Removes entries for models that no longer exist on disk.
120
+ */
121
+ export declare function syncStateWithFilesystem(): Promise<{
122
+ removed: string[];
123
+ kept: string[];
124
+ }>;
125
+ /**
126
+ * Get total size of all installed models.
127
+ */
128
+ export declare function getTotalInstalledSize(): Promise<number>;
129
+ /**
130
+ * Get count of installed models.
131
+ */
132
+ export declare function getInstalledModelCount(): Promise<number>;
133
+ /**
134
+ * Register a manually added model file.
135
+ * Used when user places a GGUF file directly in the models directory.
136
+ */
137
+ export declare function registerManualModel(modelId: string, filename: string, sizeBytes: number, sha256?: string): Promise<void>;
138
+ //# sourceMappingURL=state-manager.d.ts.map