gnosys 5.11.4 → 5.12.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +377 -5162
- package/dist/index.js +542 -244
- package/dist/lib/addCommand.d.ts +9 -0
- package/dist/lib/addCommand.js +102 -0
- package/dist/lib/addStructuredCommand.d.ts +16 -0
- package/dist/lib/addStructuredCommand.js +103 -0
- package/dist/lib/ambiguityCommand.d.ts +4 -0
- package/dist/lib/ambiguityCommand.js +36 -0
- package/dist/lib/apiKeyVault.d.ts +78 -0
- package/dist/lib/apiKeyVault.js +447 -0
- package/dist/lib/archive.js +0 -2
- package/dist/lib/askCommand.d.ts +13 -0
- package/dist/lib/askCommand.js +145 -0
- package/dist/lib/attachCommand.d.ts +17 -0
- package/dist/lib/attachCommand.js +66 -0
- package/dist/lib/attachments.d.ts +43 -2
- package/dist/lib/attachments.js +81 -2
- package/dist/lib/audioExtract.js +4 -1
- package/dist/lib/auditCommand.d.ts +7 -0
- package/dist/lib/auditCommand.js +27 -0
- package/dist/lib/backupCommand.d.ts +6 -0
- package/dist/lib/backupCommand.js +54 -0
- package/dist/lib/bootstrapCommand.d.ts +15 -0
- package/dist/lib/bootstrapCommand.js +51 -0
- package/dist/lib/briefingCommand.d.ts +7 -0
- package/dist/lib/briefingCommand.js +92 -0
- package/dist/lib/centralizeCommand.d.ts +5 -0
- package/dist/lib/centralizeCommand.js +16 -0
- package/dist/lib/chat/choose.js +2 -2
- package/dist/lib/chatCommand.d.ts +12 -0
- package/dist/lib/chatCommand.js +46 -0
- package/dist/lib/checkCommand.d.ts +4 -0
- package/dist/lib/checkCommand.js +133 -0
- package/dist/lib/clientReadOverlay.d.ts +27 -0
- package/dist/lib/clientReadOverlay.js +76 -0
- package/dist/lib/clientReadResolve.d.ts +32 -0
- package/dist/lib/clientReadResolve.js +84 -0
- package/dist/lib/commitContextCommand.d.ts +9 -0
- package/dist/lib/commitContextCommand.js +142 -0
- package/dist/lib/config.d.ts +41 -48
- package/dist/lib/config.js +58 -57
- package/dist/lib/configCommand.d.ts +10 -0
- package/dist/lib/configCommand.js +321 -0
- package/dist/lib/connectCommand.d.ts +8 -0
- package/dist/lib/connectCommand.js +19 -0
- package/dist/lib/db.d.ts +68 -1
- package/dist/lib/db.js +385 -120
- package/dist/lib/dbWrite.d.ts +1 -1
- package/dist/lib/dearchiveCommand.d.ts +7 -0
- package/dist/lib/dearchiveCommand.js +41 -0
- package/dist/lib/discoverCommand.d.ts +9 -0
- package/dist/lib/discoverCommand.js +87 -0
- package/dist/lib/doctorCommand.d.ts +6 -0
- package/dist/lib/doctorCommand.js +256 -0
- package/dist/lib/docxExtract.js +1 -1
- package/dist/lib/dream.d.ts +50 -2
- package/dist/lib/dream.js +324 -30
- package/dist/lib/dreamCommand.d.ts +10 -0
- package/dist/lib/dreamCommand.js +195 -0
- package/dist/lib/dreamLaunchd.d.ts +2 -0
- package/dist/lib/dreamLaunchd.js +72 -0
- package/dist/lib/dreamLogCommand.d.ts +10 -0
- package/dist/lib/dreamLogCommand.js +58 -0
- package/dist/lib/dreamReport.d.ts +7 -0
- package/dist/lib/dreamReport.js +114 -0
- package/dist/lib/dreamRunLog.d.ts +121 -0
- package/dist/lib/dreamRunLog.js +234 -0
- package/dist/lib/embeddings.js +3 -3
- package/dist/lib/exportCommand.d.ts +18 -0
- package/dist/lib/exportCommand.js +101 -0
- package/dist/lib/exportProject.d.ts +3 -2
- package/dist/lib/exportProject.js +2 -1
- package/dist/lib/federated.js +1 -1
- package/dist/lib/fsearchCommand.d.ts +8 -0
- package/dist/lib/fsearchCommand.js +44 -0
- package/dist/lib/graphCommand.d.ts +4 -0
- package/dist/lib/graphCommand.js +68 -0
- package/dist/lib/helperGenerateCommand.d.ts +5 -0
- package/dist/lib/helperGenerateCommand.js +27 -0
- package/dist/lib/historyCommand.d.ts +5 -0
- package/dist/lib/historyCommand.js +51 -0
- package/dist/lib/hybridSearchCommand.d.ts +12 -0
- package/dist/lib/hybridSearchCommand.js +95 -0
- package/dist/lib/importCommand.d.ts +16 -0
- package/dist/lib/importCommand.js +89 -0
- package/dist/lib/importProject.js +2 -1
- package/dist/lib/importProjectCommand.d.ts +6 -0
- package/dist/lib/importProjectCommand.js +43 -0
- package/dist/lib/ingestCommand.d.ts +13 -0
- package/dist/lib/ingestCommand.js +95 -0
- package/dist/lib/installOutput.d.ts +36 -0
- package/dist/lib/installOutput.js +55 -0
- package/dist/lib/lensCommand.d.ts +20 -0
- package/dist/lib/lensCommand.js +61 -0
- package/dist/lib/lensing.d.ts +1 -0
- package/dist/lib/lensing.js +50 -9
- package/dist/lib/linksCommand.d.ts +7 -0
- package/dist/lib/linksCommand.js +48 -0
- package/dist/lib/listCommand.d.ts +8 -0
- package/dist/lib/listCommand.js +74 -0
- package/dist/lib/llm.d.ts +1 -1
- package/dist/lib/llm.js +27 -9
- package/dist/lib/localDiskCheck.d.ts +17 -0
- package/dist/lib/localDiskCheck.js +54 -0
- package/dist/lib/lock.d.ts +1 -1
- package/dist/lib/lock.js +5 -3
- package/dist/lib/machineConfig.d.ts +11 -1
- package/dist/lib/machineConfig.js +16 -0
- package/dist/lib/machineRegistry.d.ts +61 -0
- package/dist/lib/machineRegistry.js +80 -0
- package/dist/lib/maintainCommand.d.ts +8 -0
- package/dist/lib/maintainCommand.js +34 -0
- package/dist/lib/masterLease.d.ts +20 -0
- package/dist/lib/masterLease.js +68 -0
- package/dist/lib/migrate.js +0 -1
- package/dist/lib/migrateCommand.d.ts +7 -0
- package/dist/lib/migrateCommand.js +158 -0
- package/dist/lib/migrateDbCommand.d.ts +9 -0
- package/dist/lib/migrateDbCommand.js +94 -0
- package/dist/lib/modelValidation.d.ts +5 -0
- package/dist/lib/modelValidation.js +27 -0
- package/dist/lib/multimodalIngest.js +1 -1
- package/dist/lib/openrouterTiers.d.ts +29 -0
- package/dist/lib/openrouterTiers.js +113 -0
- package/dist/lib/platform.d.ts +0 -6
- package/dist/lib/platform.js +0 -28
- package/dist/lib/prefCommand.d.ts +10 -0
- package/dist/lib/prefCommand.js +118 -0
- package/dist/lib/projectsCommand.d.ts +8 -0
- package/dist/lib/projectsCommand.js +131 -0
- package/dist/lib/readCommand.d.ts +7 -0
- package/dist/lib/readCommand.js +63 -0
- package/dist/lib/recall.d.ts +3 -0
- package/dist/lib/recall.js +19 -4
- package/dist/lib/recallCommand.d.ts +11 -0
- package/dist/lib/recallCommand.js +112 -0
- package/dist/lib/reflectCommand.d.ts +8 -0
- package/dist/lib/reflectCommand.js +61 -0
- package/dist/lib/reindexCommand.d.ts +4 -0
- package/dist/lib/reindexCommand.js +34 -0
- package/dist/lib/reindexGraphCommand.d.ts +4 -0
- package/dist/lib/reindexGraphCommand.js +12 -0
- package/dist/lib/reinforceCommand.d.ts +8 -0
- package/dist/lib/reinforceCommand.js +40 -0
- package/dist/lib/remote.d.ts +5 -1
- package/dist/lib/remote.js +5 -1
- package/dist/lib/remoteWizard.d.ts +24 -5
- package/dist/lib/remoteWizard.js +308 -319
- package/dist/lib/restoreCommand.d.ts +5 -0
- package/dist/lib/restoreCommand.js +35 -0
- package/dist/lib/rulesGen.d.ts +8 -0
- package/dist/lib/rulesGen.js +16 -0
- package/dist/lib/sandboxStartCommand.d.ts +6 -0
- package/dist/lib/sandboxStartCommand.js +25 -0
- package/dist/lib/sandboxStatusCommand.d.ts +4 -0
- package/dist/lib/sandboxStatusCommand.js +24 -0
- package/dist/lib/sandboxStopCommand.d.ts +4 -0
- package/dist/lib/sandboxStopCommand.js +21 -0
- package/dist/lib/search.d.ts +0 -2
- package/dist/lib/search.js +0 -7
- package/dist/lib/searchCommand.d.ts +9 -0
- package/dist/lib/searchCommand.js +90 -0
- package/dist/lib/semanticSearchCommand.d.ts +8 -0
- package/dist/lib/semanticSearchCommand.js +52 -0
- package/dist/lib/setup/configSetRender.js +2 -0
- package/dist/lib/setup/providerGlyphs.d.ts +19 -0
- package/dist/lib/setup/providerGlyphs.js +42 -0
- package/dist/lib/setup/remoteRender.d.ts +31 -1
- package/dist/lib/setup/remoteRender.js +95 -4
- package/dist/lib/setup/sections/providers.d.ts +17 -0
- package/dist/lib/setup/sections/providers.js +307 -0
- package/dist/lib/setup/sections/routing.d.ts +2 -6
- package/dist/lib/setup/sections/routing.js +67 -82
- package/dist/lib/setup/sections/taskRoutingEditor.d.ts +13 -0
- package/dist/lib/setup/sections/taskRoutingEditor.js +139 -0
- package/dist/lib/setup/summary.d.ts +9 -0
- package/dist/lib/setup/summary.js +51 -37
- package/dist/lib/setup/ui/header.js +0 -1
- package/dist/lib/setup.d.ts +105 -15
- package/dist/lib/setup.js +747 -287
- package/dist/lib/setupKeys.d.ts +42 -0
- package/dist/lib/setupKeys.js +564 -0
- package/dist/lib/setupRemoteCommand.d.ts +4 -0
- package/dist/lib/setupRemoteCommand.js +28 -0
- package/dist/lib/setupRemotePullCommand.d.ts +5 -0
- package/dist/lib/setupRemotePullCommand.js +52 -0
- package/dist/lib/setupRemotePushCommand.d.ts +5 -0
- package/dist/lib/setupRemotePushCommand.js +57 -0
- package/dist/lib/setupRemoteResolveCommand.d.ts +4 -0
- package/dist/lib/setupRemoteResolveCommand.js +48 -0
- package/dist/lib/setupRemoteStatusCommand.d.ts +4 -0
- package/dist/lib/setupRemoteStatusCommand.js +73 -0
- package/dist/lib/setupRemoteSyncCommand.d.ts +6 -0
- package/dist/lib/setupRemoteSyncCommand.js +65 -0
- package/dist/lib/setupSyncProjectsCommand.d.ts +4 -0
- package/dist/lib/setupSyncProjectsCommand.js +292 -0
- package/dist/lib/staleCommand.d.ts +8 -0
- package/dist/lib/staleCommand.js +34 -0
- package/dist/lib/statsCommand.d.ts +6 -0
- package/dist/lib/statsCommand.js +142 -0
- package/dist/lib/statusCommand.d.ts +18 -0
- package/dist/lib/statusCommand.js +250 -0
- package/dist/lib/storesCommand.d.ts +2 -0
- package/dist/lib/storesCommand.js +4 -0
- package/dist/lib/syncClient.d.ts +41 -0
- package/dist/lib/syncClient.js +234 -0
- package/dist/lib/syncCommand.d.ts +6 -0
- package/dist/lib/syncCommand.js +57 -0
- package/dist/lib/syncDoctorCommand.d.ts +5 -0
- package/dist/lib/syncDoctorCommand.js +100 -0
- package/dist/lib/syncIngest.d.ts +30 -0
- package/dist/lib/syncIngest.js +175 -0
- package/dist/lib/syncIngestLaunchd.d.ts +8 -0
- package/dist/lib/syncIngestLaunchd.js +93 -0
- package/dist/lib/syncIngestStartup.d.ts +5 -0
- package/dist/lib/syncIngestStartup.js +29 -0
- package/dist/lib/syncIngestSystemd.d.ts +10 -0
- package/dist/lib/syncIngestSystemd.js +97 -0
- package/dist/lib/syncIngestTimer.d.ts +8 -0
- package/dist/lib/syncIngestTimer.js +27 -0
- package/dist/lib/syncIngestTimerCommand.d.ts +7 -0
- package/dist/lib/syncIngestTimerCommand.js +83 -0
- package/dist/lib/syncLock.d.ts +6 -0
- package/dist/lib/syncLock.js +74 -0
- package/dist/lib/syncSnapshot.d.ts +32 -0
- package/dist/lib/syncSnapshot.js +188 -0
- package/dist/lib/syncStaging.d.ts +79 -0
- package/dist/lib/syncStaging.js +237 -0
- package/dist/lib/tagsAddCommand.d.ts +8 -0
- package/dist/lib/tagsAddCommand.js +18 -0
- package/dist/lib/tagsCommand.d.ts +4 -0
- package/dist/lib/tagsCommand.js +16 -0
- package/dist/lib/timelineCommand.d.ts +7 -0
- package/dist/lib/timelineCommand.js +49 -0
- package/dist/lib/traceCommand.d.ts +6 -0
- package/dist/lib/traceCommand.js +39 -0
- package/dist/lib/traverseCommand.d.ts +6 -0
- package/dist/lib/traverseCommand.js +58 -0
- package/dist/lib/updateCommand.d.ts +13 -0
- package/dist/lib/updateCommand.js +67 -0
- package/dist/lib/updateStatusCommand.d.ts +5 -0
- package/dist/lib/updateStatusCommand.js +38 -0
- package/dist/lib/webAddCommand.d.ts +8 -0
- package/dist/lib/webAddCommand.js +55 -0
- package/dist/lib/webBuildCommand.d.ts +10 -0
- package/dist/lib/webBuildCommand.js +65 -0
- package/dist/lib/webBuildIndexCommand.d.ts +8 -0
- package/dist/lib/webBuildIndexCommand.js +37 -0
- package/dist/lib/webIndex.js +0 -1
- package/dist/lib/webIngestCommand.d.ts +11 -0
- package/dist/lib/webIngestCommand.js +51 -0
- package/dist/lib/webInitCommand.d.ts +9 -0
- package/dist/lib/webInitCommand.js +167 -0
- package/dist/lib/webRemoveCommand.d.ts +5 -0
- package/dist/lib/webRemoveCommand.js +41 -0
- package/dist/lib/webStatusCommand.d.ts +5 -0
- package/dist/lib/webStatusCommand.js +94 -0
- package/dist/lib/webUpdateCommand.d.ts +7 -0
- package/dist/lib/webUpdateCommand.js +72 -0
- package/dist/lib/workingSetCommand.d.ts +6 -0
- package/dist/lib/workingSetCommand.js +37 -0
- package/dist/sandbox/client.js +1 -1
- package/dist/sandbox/manager.js +1 -14
- package/dist/sandbox/server.js +3 -5
- package/package.json +6 -2
|
@@ -0,0 +1,447 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* API key storage and resolution for Gnosys LLM providers.
|
|
3
|
+
*
|
|
4
|
+
* Keychain / env service names (also used as GNOSYS_*_KEY env vars):
|
|
5
|
+
* - Global: GNOSYS_GLOBAL_<PROVIDER>_KEY (one key for all tasks using that provider)
|
|
6
|
+
* - Provider: GNOSYS_<PROVIDER>_KEY (legacy default / fallback)
|
|
7
|
+
*/
|
|
8
|
+
import { execSync } from "child_process";
|
|
9
|
+
import dotenv from "dotenv";
|
|
10
|
+
import fsSync from "fs";
|
|
11
|
+
import os from "os";
|
|
12
|
+
import path from "path";
|
|
13
|
+
import { resolveTaskModel, getProviderModel, } from "./config.js";
|
|
14
|
+
const LEGACY_ENV = {
|
|
15
|
+
anthropic: "ANTHROPIC_API_KEY",
|
|
16
|
+
openai: "OPENAI_API_KEY",
|
|
17
|
+
groq: "GROQ_API_KEY",
|
|
18
|
+
xai: "XAI_API_KEY",
|
|
19
|
+
mistral: "MISTRAL_API_KEY",
|
|
20
|
+
openrouter: "OPENROUTER_API_KEY",
|
|
21
|
+
};
|
|
22
|
+
const CLOUD_TASKS = [
|
|
23
|
+
"structuring",
|
|
24
|
+
"synthesis",
|
|
25
|
+
"vision",
|
|
26
|
+
"transcription",
|
|
27
|
+
"chat",
|
|
28
|
+
];
|
|
29
|
+
export function providerNeedsApiKey(provider) {
|
|
30
|
+
return provider !== "ollama" && provider !== "lmstudio" && provider !== "skip";
|
|
31
|
+
}
|
|
32
|
+
function providerSlug(provider) {
|
|
33
|
+
return provider === "custom" ? "CUSTOM" : provider.toUpperCase();
|
|
34
|
+
}
|
|
35
|
+
/** Keychain service / env var name for a specific scope. */
|
|
36
|
+
export function apiKeyServiceName(provider, scope) {
|
|
37
|
+
const slug = providerSlug(provider);
|
|
38
|
+
if (scope === "global") {
|
|
39
|
+
return `GNOSYS_GLOBAL_${slug}_KEY`;
|
|
40
|
+
}
|
|
41
|
+
return `GNOSYS_${slug}_KEY`;
|
|
42
|
+
}
|
|
43
|
+
/** Lookup order: global → provider default (legacy/generic via readFirstInChain). */
|
|
44
|
+
export function apiKeyLookupChain(provider) {
|
|
45
|
+
return [
|
|
46
|
+
apiKeyServiceName(provider, "global"),
|
|
47
|
+
apiKeyServiceName(provider, "provider"),
|
|
48
|
+
];
|
|
49
|
+
}
|
|
50
|
+
function configApiKey(config, provider) {
|
|
51
|
+
switch (provider) {
|
|
52
|
+
case "anthropic":
|
|
53
|
+
return config.llm.anthropic.apiKey;
|
|
54
|
+
case "openai":
|
|
55
|
+
return config.llm.openai.apiKey;
|
|
56
|
+
case "groq":
|
|
57
|
+
return config.llm.groq.apiKey;
|
|
58
|
+
case "xai":
|
|
59
|
+
return config.llm.xai.apiKey;
|
|
60
|
+
case "mistral":
|
|
61
|
+
return config.llm.mistral.apiKey;
|
|
62
|
+
case "openrouter":
|
|
63
|
+
return config.llm.openrouter.apiKey;
|
|
64
|
+
case "custom":
|
|
65
|
+
return config.llm.custom?.apiKey;
|
|
66
|
+
default:
|
|
67
|
+
return undefined;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
function readFromKeychain(service) {
|
|
71
|
+
if (process.env.VITEST)
|
|
72
|
+
return undefined;
|
|
73
|
+
if (process.platform === "darwin") {
|
|
74
|
+
try {
|
|
75
|
+
return execSync(`security find-generic-password -a "$USER" -s "${service}" -w 2>/dev/null`, { stdio: "pipe", encoding: "utf-8", timeout: 2000 }).trim() || undefined;
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
return undefined;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
if (process.platform === "linux") {
|
|
82
|
+
try {
|
|
83
|
+
return execSync(`secret-tool lookup service gnosys account ${service} 2>/dev/null`, { stdio: "pipe", encoding: "utf-8", timeout: 2000 }).trim() || undefined;
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
return undefined;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return undefined;
|
|
90
|
+
}
|
|
91
|
+
/** Read a secret directly from the secure store, ignoring env vars. */
|
|
92
|
+
export function readSecureStoreSecret(service) {
|
|
93
|
+
return readFromKeychain(service);
|
|
94
|
+
}
|
|
95
|
+
/** Read a secret from env or secure store by service name. */
|
|
96
|
+
export function readStoredSecret(service) {
|
|
97
|
+
if (process.env[service])
|
|
98
|
+
return process.env[service];
|
|
99
|
+
return readSecureStoreSecret(service);
|
|
100
|
+
}
|
|
101
|
+
function keyLocationChain(provider) {
|
|
102
|
+
const slug = provider.toUpperCase();
|
|
103
|
+
const globalEnvVar = `GNOSYS_GLOBAL_${slug}_KEY`;
|
|
104
|
+
const providerEnvVar = `GNOSYS_${slug}_KEY`;
|
|
105
|
+
return [
|
|
106
|
+
{
|
|
107
|
+
envVarName: globalEnvVar,
|
|
108
|
+
serviceName: globalEnvVar,
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
envVarName: providerEnvVar,
|
|
112
|
+
serviceName: providerEnvVar,
|
|
113
|
+
},
|
|
114
|
+
{ envVarName: `${slug}_API_KEY` },
|
|
115
|
+
{ envVarName: "GNOSYS_LLM_API_KEY" },
|
|
116
|
+
];
|
|
117
|
+
}
|
|
118
|
+
function maskedLastFour(key) {
|
|
119
|
+
return `••••${key.trim().slice(-4)}`;
|
|
120
|
+
}
|
|
121
|
+
function readDotenvKeys() {
|
|
122
|
+
try {
|
|
123
|
+
const envPath = path.join(process.env.HOME ?? os.homedir(), ".config", "gnosys", ".env");
|
|
124
|
+
return dotenv.parse(fsSync.readFileSync(envPath, "utf-8"));
|
|
125
|
+
}
|
|
126
|
+
catch {
|
|
127
|
+
return {};
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
export function detectKeyLocation(provider) {
|
|
131
|
+
if (!providerNeedsApiKey(provider)) {
|
|
132
|
+
return { found: false, location: "none" };
|
|
133
|
+
}
|
|
134
|
+
const chain = keyLocationChain(provider);
|
|
135
|
+
const dotenvKeys = readDotenvKeys();
|
|
136
|
+
for (const { envVarName, serviceName } of chain) {
|
|
137
|
+
const envKey = process.env[envVarName]?.trim();
|
|
138
|
+
if (envKey) {
|
|
139
|
+
return {
|
|
140
|
+
found: true,
|
|
141
|
+
location: "env",
|
|
142
|
+
envVarName,
|
|
143
|
+
lastFour: maskedLastFour(envKey),
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
if (serviceName) {
|
|
147
|
+
const storedKey = readStoredSecret(serviceName)?.trim();
|
|
148
|
+
if (storedKey) {
|
|
149
|
+
return {
|
|
150
|
+
found: true,
|
|
151
|
+
location: "keychain",
|
|
152
|
+
serviceName,
|
|
153
|
+
lastFour: maskedLastFour(storedKey),
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
const dotenvKey = dotenvKeys[envVarName]?.trim();
|
|
158
|
+
if (dotenvKey) {
|
|
159
|
+
return {
|
|
160
|
+
found: true,
|
|
161
|
+
location: "dotenv",
|
|
162
|
+
envVarName,
|
|
163
|
+
lastFour: maskedLastFour(dotenvKey),
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
return { found: false, location: "none" };
|
|
168
|
+
}
|
|
169
|
+
/** First non-empty secret in the lookup chain. */
|
|
170
|
+
export function readFirstInChain(provider) {
|
|
171
|
+
for (const service of apiKeyLookupChain(provider)) {
|
|
172
|
+
const v = readStoredSecret(service);
|
|
173
|
+
if (v)
|
|
174
|
+
return v;
|
|
175
|
+
}
|
|
176
|
+
const legacy = LEGACY_ENV[provider];
|
|
177
|
+
if (legacy && process.env[legacy])
|
|
178
|
+
return process.env[legacy];
|
|
179
|
+
if (process.env.GNOSYS_LLM_API_KEY)
|
|
180
|
+
return process.env.GNOSYS_LLM_API_KEY;
|
|
181
|
+
return undefined;
|
|
182
|
+
}
|
|
183
|
+
/** Mask for display: first 4 + ellipsis + last 4 characters. */
|
|
184
|
+
export function maskKeySnippet(key) {
|
|
185
|
+
const trimmed = key.trim();
|
|
186
|
+
if (trimmed.length <= 8) {
|
|
187
|
+
return trimmed.length <= 2 ? "••" : `${trimmed.slice(0, 2)}…${trimmed.slice(-2)}`;
|
|
188
|
+
}
|
|
189
|
+
return `${trimmed.slice(0, 4)}…${trimmed.slice(-4)}`;
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Where a secret is stored (Keychain, env var name, gnosys.json, or .env file).
|
|
193
|
+
*/
|
|
194
|
+
export function detectSecretSource(service, legacyEnv) {
|
|
195
|
+
if (process.platform === "darwin" && !process.env.VITEST) {
|
|
196
|
+
try {
|
|
197
|
+
const result = execSync(`security find-generic-password -a "$USER" -s "${service}" -w 2>/dev/null`, { stdio: "pipe", encoding: "utf-8", timeout: 2000 }).trim();
|
|
198
|
+
if (result)
|
|
199
|
+
return "macOS Keychain";
|
|
200
|
+
}
|
|
201
|
+
catch {
|
|
202
|
+
// not in keychain
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
if (process.platform === "linux" && !process.env.VITEST) {
|
|
206
|
+
try {
|
|
207
|
+
const result = execSync(`secret-tool lookup service gnosys account ${service} 2>/dev/null`, { stdio: "pipe", encoding: "utf-8", timeout: 2000 }).trim();
|
|
208
|
+
if (result)
|
|
209
|
+
return "GNOME Keyring";
|
|
210
|
+
}
|
|
211
|
+
catch {
|
|
212
|
+
// not in keyring
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
if (process.env[service])
|
|
216
|
+
return `$${service}`;
|
|
217
|
+
if (legacyEnv && process.env[legacyEnv])
|
|
218
|
+
return `$${legacyEnv}`;
|
|
219
|
+
try {
|
|
220
|
+
const envPath = path.join(os.homedir(), ".config", "gnosys", ".env");
|
|
221
|
+
const content = fsSync.readFileSync(envPath, "utf-8");
|
|
222
|
+
if (content.includes(`${service}=`))
|
|
223
|
+
return "~/.config/gnosys/.env";
|
|
224
|
+
}
|
|
225
|
+
catch {
|
|
226
|
+
// no .env
|
|
227
|
+
}
|
|
228
|
+
return "";
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* List every key slot for a provider that currently holds a value.
|
|
232
|
+
*/
|
|
233
|
+
export function listStoredKeySlots(config, provider) {
|
|
234
|
+
const slots = [];
|
|
235
|
+
const fromConfig = configApiKey(config, provider);
|
|
236
|
+
if (fromConfig) {
|
|
237
|
+
slots.push({
|
|
238
|
+
service: "gnosys.json",
|
|
239
|
+
scope: "provider",
|
|
240
|
+
source: "gnosys.json (llm block)",
|
|
241
|
+
preview: maskKeySnippet(fromConfig),
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
const seen = new Set();
|
|
245
|
+
const push = (slot, raw) => {
|
|
246
|
+
if (seen.has(slot.service))
|
|
247
|
+
return;
|
|
248
|
+
seen.add(slot.service);
|
|
249
|
+
const source = slot.source || detectSecretSource(slot.service, LEGACY_ENV[provider]) || "stored";
|
|
250
|
+
slots.push({ ...slot, source, preview: maskKeySnippet(raw) });
|
|
251
|
+
};
|
|
252
|
+
const globalSvc = apiKeyServiceName(provider, "global");
|
|
253
|
+
const globalVal = readStoredSecret(globalSvc);
|
|
254
|
+
if (globalVal) {
|
|
255
|
+
push({ service: globalSvc, scope: "global", source: detectSecretSource(globalSvc) }, globalVal);
|
|
256
|
+
}
|
|
257
|
+
const providerSvc = apiKeyServiceName(provider, "provider");
|
|
258
|
+
const providerVal = readStoredSecret(providerSvc);
|
|
259
|
+
if (providerVal) {
|
|
260
|
+
push({ service: providerSvc, scope: "provider", source: detectSecretSource(providerSvc) }, providerVal);
|
|
261
|
+
}
|
|
262
|
+
const legacy = LEGACY_ENV[provider];
|
|
263
|
+
if (legacy && process.env[legacy] && !seen.has(legacy)) {
|
|
264
|
+
slots.push({
|
|
265
|
+
service: legacy,
|
|
266
|
+
scope: "provider",
|
|
267
|
+
source: `$${legacy}`,
|
|
268
|
+
preview: maskKeySnippet(process.env[legacy]),
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
if (process.env.GNOSYS_LLM_API_KEY && !seen.has("GNOSYS_LLM_API_KEY")) {
|
|
272
|
+
slots.push({
|
|
273
|
+
service: "GNOSYS_LLM_API_KEY",
|
|
274
|
+
scope: "provider",
|
|
275
|
+
source: "$GNOSYS_LLM_API_KEY",
|
|
276
|
+
preview: maskKeySnippet(process.env.GNOSYS_LLM_API_KEY),
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
return slots;
|
|
280
|
+
}
|
|
281
|
+
/** Delete a secret from Keychain / GNOME Keyring (not process env). */
|
|
282
|
+
export function deleteStoredSecret(service) {
|
|
283
|
+
if (service === "gnosys.json")
|
|
284
|
+
return false;
|
|
285
|
+
if (process.env.VITEST)
|
|
286
|
+
return false;
|
|
287
|
+
if (process.platform === "darwin") {
|
|
288
|
+
try {
|
|
289
|
+
execSync(`security delete-generic-password -a "$USER" -s "${service}" 2>/dev/null`, { stdio: "pipe" });
|
|
290
|
+
return true;
|
|
291
|
+
}
|
|
292
|
+
catch {
|
|
293
|
+
return false;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
if (process.platform === "linux") {
|
|
297
|
+
try {
|
|
298
|
+
execSync(`secret-tool clear service gnosys account ${service}`, {
|
|
299
|
+
stdio: "pipe",
|
|
300
|
+
});
|
|
301
|
+
return true;
|
|
302
|
+
}
|
|
303
|
+
catch {
|
|
304
|
+
return false;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
return false;
|
|
308
|
+
}
|
|
309
|
+
/** Resolve API key for a provider. */
|
|
310
|
+
export function getApiKeyForProviderFromConfig(config, provider) {
|
|
311
|
+
const fromConfig = configApiKey(config, provider);
|
|
312
|
+
if (fromConfig)
|
|
313
|
+
return fromConfig;
|
|
314
|
+
return readFirstInChain(provider);
|
|
315
|
+
}
|
|
316
|
+
export function writeApiKeyToKeychain(service, key) {
|
|
317
|
+
if (process.platform !== "darwin")
|
|
318
|
+
return false;
|
|
319
|
+
try {
|
|
320
|
+
execSync(`security add-generic-password -a "$USER" -s "${service}" -w "${key.replace(/"/g, '\\"')}" -U`, { stdio: "pipe" });
|
|
321
|
+
return true;
|
|
322
|
+
}
|
|
323
|
+
catch {
|
|
324
|
+
return false;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
export function writeApiKeyToSecretTool(service, key, label) {
|
|
328
|
+
if (process.platform === "darwin")
|
|
329
|
+
return false;
|
|
330
|
+
try {
|
|
331
|
+
execSync("which secret-tool", { stdio: "pipe" });
|
|
332
|
+
execSync(`printf "%s" "${key.replace(/"/g, '\\"')}" | secret-tool store --label="${label}" service gnosys account ${service}`, { stdio: "pipe", shell: "/bin/sh" });
|
|
333
|
+
return true;
|
|
334
|
+
}
|
|
335
|
+
catch {
|
|
336
|
+
return false;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
export function storeApiKeySecret(service, key, provider) {
|
|
340
|
+
if (process.platform === "darwin") {
|
|
341
|
+
return writeApiKeyToKeychain(service, key);
|
|
342
|
+
}
|
|
343
|
+
if (process.platform === "linux") {
|
|
344
|
+
return writeApiKeyToSecretTool(service, key, `Gnosys ${provider}`);
|
|
345
|
+
}
|
|
346
|
+
return false;
|
|
347
|
+
}
|
|
348
|
+
function requirementLabel(req) {
|
|
349
|
+
if (req.scope === "global") {
|
|
350
|
+
return `all tasks → ${req.provider}`;
|
|
351
|
+
}
|
|
352
|
+
return req.provider;
|
|
353
|
+
}
|
|
354
|
+
function dedupeRequirements(reqs) {
|
|
355
|
+
const seen = new Set();
|
|
356
|
+
const out = [];
|
|
357
|
+
for (const r of reqs) {
|
|
358
|
+
const id = apiKeyServiceName(r.provider, r.scope);
|
|
359
|
+
if (seen.has(id))
|
|
360
|
+
continue;
|
|
361
|
+
seen.add(id);
|
|
362
|
+
out.push(r);
|
|
363
|
+
}
|
|
364
|
+
return out;
|
|
365
|
+
}
|
|
366
|
+
/**
|
|
367
|
+
* Build key requirements from effective routing — one global key per cloud provider in use.
|
|
368
|
+
*/
|
|
369
|
+
export function buildApiKeyRequirementsFromConfig(config) {
|
|
370
|
+
const providers = new Set();
|
|
371
|
+
for (const task of CLOUD_TASKS) {
|
|
372
|
+
const { provider } = resolveTaskModel(config, task);
|
|
373
|
+
if (providerNeedsApiKey(provider)) {
|
|
374
|
+
providers.add(provider);
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
if (config.dream?.enabled) {
|
|
378
|
+
const dreamProvider = (config.dream.provider ?? "ollama");
|
|
379
|
+
if (providerNeedsApiKey(dreamProvider)) {
|
|
380
|
+
providers.add(dreamProvider);
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
if (providers.size === 0)
|
|
384
|
+
return [];
|
|
385
|
+
return dedupeRequirements([...providers].map((provider) => ({
|
|
386
|
+
provider,
|
|
387
|
+
scope: "global",
|
|
388
|
+
})));
|
|
389
|
+
}
|
|
390
|
+
/**
|
|
391
|
+
* Prompt for missing keys and store in Keychain / GNOME Keyring.
|
|
392
|
+
*/
|
|
393
|
+
export async function ensureApiKeys(rl, requirements, askInput, opts) {
|
|
394
|
+
const reqs = dedupeRequirements(requirements).filter((r) => providerNeedsApiKey(r.provider));
|
|
395
|
+
if (reqs.length === 0)
|
|
396
|
+
return 0;
|
|
397
|
+
const WARN = opts?.warn ?? "⚠";
|
|
398
|
+
const CHECK = opts?.check ?? "✓";
|
|
399
|
+
const CROSS = opts?.cross ?? "✗";
|
|
400
|
+
const DIM = opts?.dim ?? "";
|
|
401
|
+
const RESET = opts?.reset ?? "";
|
|
402
|
+
const maskKey = opts?.maskKey ?? ((k) => k.length <= 8 ? "***" : `${k.slice(0, 3)}***${k.slice(-2)}`);
|
|
403
|
+
let saved = 0;
|
|
404
|
+
console.log();
|
|
405
|
+
console.log(` ${WARN} API keys — stored in Keychain with scoped names${RESET}`);
|
|
406
|
+
console.log(` ${DIM}Global = one key for all tasks using that provider${RESET}`);
|
|
407
|
+
console.log();
|
|
408
|
+
for (const req of reqs) {
|
|
409
|
+
const service = apiKeyServiceName(req.provider, req.scope);
|
|
410
|
+
if (readStoredSecret(service)) {
|
|
411
|
+
console.log(` ${CHECK} ${requirementLabel(req)} ${DIM}(${service})${RESET}`);
|
|
412
|
+
continue;
|
|
413
|
+
}
|
|
414
|
+
const label = requirementLabel(req);
|
|
415
|
+
console.log(` ${DIM}Keychain: ${service}${RESET}`);
|
|
416
|
+
const key = await askInput(rl, ` API key for ${label}`);
|
|
417
|
+
if (!key) {
|
|
418
|
+
console.log(` ${DIM} skipped ${label}${RESET}`);
|
|
419
|
+
continue;
|
|
420
|
+
}
|
|
421
|
+
if (storeApiKeySecret(service, key, req.provider)) {
|
|
422
|
+
const store = process.platform === "darwin"
|
|
423
|
+
? "macOS Keychain"
|
|
424
|
+
: "GNOME Keyring";
|
|
425
|
+
console.log(` ${CHECK} ${label} → ${store} (${maskKey(key)})`);
|
|
426
|
+
saved++;
|
|
427
|
+
}
|
|
428
|
+
else {
|
|
429
|
+
console.log(` ${CROSS} Could not write ${service} to secure store`);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
return saved;
|
|
433
|
+
}
|
|
434
|
+
/** Effective routing map used for key collection. */
|
|
435
|
+
export function buildEffectiveRouting(config) {
|
|
436
|
+
const out = {};
|
|
437
|
+
for (const task of CLOUD_TASKS) {
|
|
438
|
+
out[task] = resolveTaskModel(config, task);
|
|
439
|
+
}
|
|
440
|
+
const dreamProvider = (config.dream?.provider ?? "ollama");
|
|
441
|
+
out.dream = {
|
|
442
|
+
provider: dreamProvider,
|
|
443
|
+
model: config.dream?.model ??
|
|
444
|
+
getProviderModel(config, dreamProvider),
|
|
445
|
+
};
|
|
446
|
+
return out;
|
|
447
|
+
}
|
package/dist/lib/archive.js
CHANGED
|
@@ -10,7 +10,6 @@
|
|
|
10
10
|
* then dearchives used memories back to active
|
|
11
11
|
*/
|
|
12
12
|
// Dynamic import — gracefully handles missing native module
|
|
13
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
14
13
|
let Database = null;
|
|
15
14
|
try {
|
|
16
15
|
Database = (await import("better-sqlite3")).default;
|
|
@@ -26,7 +25,6 @@ import { enableWAL } from "./lock.js";
|
|
|
26
25
|
import { auditLog } from "./audit.js";
|
|
27
26
|
// ─── Archive Manager ────────────────────────────────────────────────────
|
|
28
27
|
export class GnosysArchive {
|
|
29
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
30
28
|
db = null;
|
|
31
29
|
storePath;
|
|
32
30
|
available = false;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { GnosysResolver } from "./resolver.js";
|
|
2
|
+
export type AskCommandOptions = {
|
|
3
|
+
limit: string;
|
|
4
|
+
mode: string;
|
|
5
|
+
stream: boolean;
|
|
6
|
+
federated?: boolean;
|
|
7
|
+
scope?: string;
|
|
8
|
+
directory?: string;
|
|
9
|
+
json?: boolean;
|
|
10
|
+
};
|
|
11
|
+
type GetResolver = () => Promise<GnosysResolver>;
|
|
12
|
+
export declare function runAskCommand(getResolver: GetResolver, question: string, opts: AskCommandOptions): Promise<void>;
|
|
13
|
+
export {};
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { loadConfig, DEFAULT_CONFIG } from "./config.js";
|
|
2
|
+
import { GnosysSearch } from "./search.js";
|
|
3
|
+
import { getSecureStorageSetupHint } from "./platform.js";
|
|
4
|
+
function outputResult(json, data, humanFn) {
|
|
5
|
+
if (json) {
|
|
6
|
+
console.log(JSON.stringify(data, null, 2));
|
|
7
|
+
}
|
|
8
|
+
else {
|
|
9
|
+
humanFn();
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
export async function runAskCommand(getResolver, question, opts) {
|
|
13
|
+
const resolver = await getResolver();
|
|
14
|
+
const stores = resolver.getStores();
|
|
15
|
+
if (stores.length === 0) {
|
|
16
|
+
console.error("No stores found. Run gnosys init first.");
|
|
17
|
+
process.exit(1);
|
|
18
|
+
}
|
|
19
|
+
const storePath = stores[0].path;
|
|
20
|
+
let cliConfig;
|
|
21
|
+
try {
|
|
22
|
+
cliConfig = await loadConfig(storePath);
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
cliConfig = DEFAULT_CONFIG;
|
|
26
|
+
}
|
|
27
|
+
const search = new GnosysSearch(storePath);
|
|
28
|
+
search.clearIndex();
|
|
29
|
+
for (const s of stores) {
|
|
30
|
+
await search.addStoreMemories(s.store, s.label);
|
|
31
|
+
}
|
|
32
|
+
const { GnosysEmbeddings } = await import("./embeddings.js");
|
|
33
|
+
const { GnosysHybridSearch } = await import("./hybridSearch.js");
|
|
34
|
+
const { GnosysAsk } = await import("./ask.js");
|
|
35
|
+
const embeddings = new GnosysEmbeddings(storePath);
|
|
36
|
+
const hybridSearch = new GnosysHybridSearch(search, embeddings, resolver, storePath);
|
|
37
|
+
const ask = new GnosysAsk(hybridSearch, cliConfig, resolver, storePath);
|
|
38
|
+
if (!ask.isLLMAvailable) {
|
|
39
|
+
// v5.8.0 (#8): provider-aware error instead of hardcoded ANTHROPIC_API_KEY.
|
|
40
|
+
const providerName = cliConfig.llm.defaultProvider;
|
|
41
|
+
const envVarMap = {
|
|
42
|
+
anthropic: "ANTHROPIC_API_KEY",
|
|
43
|
+
openai: "OPENAI_API_KEY",
|
|
44
|
+
groq: "GROQ_API_KEY",
|
|
45
|
+
xai: "XAI_API_KEY",
|
|
46
|
+
mistral: "MISTRAL_API_KEY",
|
|
47
|
+
};
|
|
48
|
+
const envVar = envVarMap[providerName];
|
|
49
|
+
if (envVar) {
|
|
50
|
+
console.error(`No LLM provider available. Configured default is "${providerName}" but its key wasn't found. ` +
|
|
51
|
+
`Set ${envVar}, run 'gnosys setup' to store one in ${getSecureStorageSetupHint()}, or add llm.${providerName}.apiKey to gnosys.json.`);
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
console.error(`No LLM provider available. Provider "${providerName}" is not reachable. Run 'gnosys setup' to configure one.`);
|
|
55
|
+
}
|
|
56
|
+
process.exit(1);
|
|
57
|
+
}
|
|
58
|
+
// If --federated, pre-retrieve from central DB and inject as context
|
|
59
|
+
let federatedContext;
|
|
60
|
+
if (opts.federated || opts.scope) {
|
|
61
|
+
const { resolveClientRead } = await import("./clientReadResolve.js");
|
|
62
|
+
const resolved = resolveClientRead();
|
|
63
|
+
if (resolved) {
|
|
64
|
+
try {
|
|
65
|
+
if (resolved.db.isAvailable()) {
|
|
66
|
+
const { federatedSearch: fSearch, detectCurrentProject } = await import("./federated.js");
|
|
67
|
+
const projectId = await detectCurrentProject(resolved.db, opts.directory || undefined);
|
|
68
|
+
const scopeFilter = opts.scope ? opts.scope.split(",").map(s => s.trim()) : undefined;
|
|
69
|
+
const fResults = fSearch(resolved.db, question, {
|
|
70
|
+
limit: parseInt(opts.limit, 10),
|
|
71
|
+
projectId,
|
|
72
|
+
scopeFilter,
|
|
73
|
+
});
|
|
74
|
+
if (fResults.length > 0) {
|
|
75
|
+
federatedContext = fResults.map(r => {
|
|
76
|
+
const mem = resolved.db.getMemory(r.id);
|
|
77
|
+
return `## ${r.title} [scope:${r.scope}, score:${r.score.toFixed(3)}]\n${mem?.content || r.snippet}`;
|
|
78
|
+
}).join("\n\n");
|
|
79
|
+
console.error(`[federated] Found ${fResults.length} cross-scope memories as additional context`);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
catch { /* Central DB not available — fall through to normal ask */ }
|
|
84
|
+
finally {
|
|
85
|
+
resolved.release();
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
const mode = opts.mode;
|
|
90
|
+
const useStream = opts.stream !== false && !opts.json;
|
|
91
|
+
try {
|
|
92
|
+
const result = await ask.ask(question, {
|
|
93
|
+
limit: parseInt(opts.limit, 10),
|
|
94
|
+
mode,
|
|
95
|
+
stream: useStream,
|
|
96
|
+
additionalContext: federatedContext,
|
|
97
|
+
callbacks: useStream
|
|
98
|
+
? {
|
|
99
|
+
onToken: (token) => process.stdout.write(token),
|
|
100
|
+
onSearchComplete: (count, searchMode) => {
|
|
101
|
+
console.log(`\n Found ${count} relevant memories (${searchMode} search)\n`);
|
|
102
|
+
},
|
|
103
|
+
onDeepQuery: (refined) => {
|
|
104
|
+
console.log(`\n Deep query: searching for "${refined}"...\n`);
|
|
105
|
+
},
|
|
106
|
+
}
|
|
107
|
+
: undefined,
|
|
108
|
+
});
|
|
109
|
+
outputResult(!!opts.json, {
|
|
110
|
+
question,
|
|
111
|
+
answer: result.answer,
|
|
112
|
+
sources: result.sources.map((s) => ({
|
|
113
|
+
title: s.title,
|
|
114
|
+
relativePath: s.relativePath,
|
|
115
|
+
})),
|
|
116
|
+
deepQueryUsed: result.deepQueryUsed ?? false,
|
|
117
|
+
}, () => {
|
|
118
|
+
if (!useStream) {
|
|
119
|
+
console.log(result.answer);
|
|
120
|
+
}
|
|
121
|
+
if (result.sources.length > 0) {
|
|
122
|
+
console.log("\n\n--- Sources ---");
|
|
123
|
+
for (const s of result.sources) {
|
|
124
|
+
console.log(` [[${s.relativePath.split("/").pop()}]] — ${s.title}`);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
if (result.deepQueryUsed) {
|
|
128
|
+
console.log("\n(Deep query was used — a follow-up search expanded the context)");
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
if (result.sources.length > 0) {
|
|
132
|
+
const writeTarget = resolver.getWriteTarget();
|
|
133
|
+
if (writeTarget) {
|
|
134
|
+
const { GnosysMaintenanceEngine } = await import("./maintenance.js");
|
|
135
|
+
await GnosysMaintenanceEngine.reinforceBatch(writeTarget.store, result.sources.map((s) => s.relativePath)).catch(() => { });
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
catch (err) {
|
|
140
|
+
console.error(`Ask failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
141
|
+
process.exit(1);
|
|
142
|
+
}
|
|
143
|
+
search.close();
|
|
144
|
+
embeddings.close();
|
|
145
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI handlers for inline DB-blob attachments (v5.12).
|
|
3
|
+
*
|
|
4
|
+
* gnosys attach <file> --memory <id> store bytes inline on a memory
|
|
5
|
+
* gnosys get-attachment <id> [--out path] retrieve the stored bytes
|
|
6
|
+
*
|
|
7
|
+
* Inline attachments live in the memory row, so they ride the normal sync
|
|
8
|
+
* rail to other machines and a remote/dockerized server.
|
|
9
|
+
*/
|
|
10
|
+
export interface AttachCommandOptions {
|
|
11
|
+
memory: string;
|
|
12
|
+
}
|
|
13
|
+
export declare function runAttachCommand(filePath: string, opts: AttachCommandOptions): Promise<void>;
|
|
14
|
+
export interface GetAttachmentCommandOptions {
|
|
15
|
+
out?: string;
|
|
16
|
+
}
|
|
17
|
+
export declare function runGetAttachmentCommand(memoryId: string, opts: GetAttachmentCommandOptions): Promise<void>;
|